Merge mozilla-central to tracemonkey.

This commit is contained in:
Robert Sayre 2010-07-06 11:05:39 -07:00
commit f94d6c3608
315 changed files with 5283 additions and 9818 deletions

View File

@ -45,7 +45,7 @@
interface nsIAccessible;
interface nsIArray;
[scriptable, uuid(035c0c0e-41e3-4985-8ad9-d9f14cdc667a)]
[scriptable, uuid(cb0bf7b9-117e-40e2-9e46-189c3d43ce4a)]
interface nsIAccessibleTable : nsISupports
{
/**
@ -104,6 +104,17 @@ interface nsIAccessibleTable : nsISupports
*/
long getRowIndexAt(in long cellIndex);
/**
* Translate the given cell index into the corresponding row and column
* indices.
*
* @param cellIndex [in] cell index to return row and column indices for
* @param rowIndex [out] row index at the given cell index
* @param columnIndex [out] column index at the given cell index
*/
void getRowAndColumnIndicesAt(in long cellIndex,
out long rowIndex, out long columnIndex);
/**
* Return the number of columns occupied by the accessible cell at
* the specified row and column in the table. The result differs from 1 if

View File

@ -0,0 +1,169 @@
/* ***** 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 mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alexander Surkov <surkov.alexander@gmail.com> (original author)
*
* 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 ***** */
#include "AccGroupInfo.h"
AccGroupInfo::AccGroupInfo(nsAccessible* aItem, PRUint32 aRole) :
mPosInSet(0), mSetSize(0), mParent(nsnull)
{
nsAccessible* parent = aItem->GetParent();
if (!parent)
return;
PRInt32 indexInParent = aItem->GetIndexInParent();
PRInt32 level = nsAccUtils::GetARIAOrDefaultLevel(aItem);
// Compute position in set.
mPosInSet = 1;
for (PRInt32 idx = indexInParent - 1; idx >=0 ; idx--) {
nsAccessible* sibling = parent->GetChildAt(idx);
PRUint32 siblingRole = nsAccUtils::Role(sibling);
// If the sibling is separator then the group is ended.
if (siblingRole == nsIAccessibleRole::ROLE_SEPARATOR)
break;
// If sibling is not visible and hasn't the same base role.
if (BaseRole(siblingRole) != aRole ||
nsAccUtils::State(sibling) & nsIAccessibleStates::STATE_INVISIBLE)
continue;
// Check if it's hierarchical flatten structure, i.e. if the sibling
// level is lesser than this one then group is ended, if the sibling level
// is greater than this one then the group is split by some child elements
// (group will be continued).
PRInt32 siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
if (siblingLevel < level) {
mParent = sibling;
break;
}
// Skip subset.
if (siblingLevel > level)
continue;
// If the previous item in the group has calculated group information then
// build group information for this item based on found one.
if (sibling->mGroupInfo) {
mPosInSet += sibling->mGroupInfo->mPosInSet;
mParent = sibling->mGroupInfo->mParent;
mSetSize = sibling->mGroupInfo->mSetSize;
return;
}
mPosInSet++;
}
// Compute set size.
mSetSize = mPosInSet;
PRInt32 siblingCount = parent->GetChildCount();
for (PRInt32 idx = indexInParent + 1; idx < siblingCount; idx++) {
nsAccessible* sibling = parent->GetChildAt(idx);
PRUint32 siblingRole = nsAccUtils::Role(sibling);
// If the sibling is separator then the group is ended.
if (siblingRole == nsIAccessibleRole::ROLE_SEPARATOR)
break;
// If sibling is visible and has the same base role
if (BaseRole(siblingRole) != aRole ||
nsAccUtils::State(sibling) & nsIAccessibleStates::STATE_INVISIBLE)
continue;
// and check if it's hierarchical flatten structure.
PRInt32 siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
if (siblingLevel < level)
break;
// Skip subset.
if (siblingLevel > level)
continue;
// If the next item in the group has calculated group information then
// build group information for this item based on found one.
if (sibling->mGroupInfo) {
mParent = sibling->mGroupInfo->mParent;
mSetSize = sibling->mGroupInfo->mSetSize;
return;
}
mSetSize++;
}
if (mParent)
return;
// Compute parent.
PRUint32 parentRole = nsAccUtils::Role(parent);
// In the case of ARIA row in treegrid, return treegrid since ARIA
// groups aren't used to organize levels in ARIA treegrids.
if (aRole == nsIAccessibleRole::ROLE_ROW &&
parentRole == nsIAccessibleRole::ROLE_TREE_TABLE) {
mParent = parent;
return;
}
// In the case of ARIA tree, a tree can be arranged by using ARIA groups
// to organize levels. In this case the parent of the tree item will be
// a group and the previous treeitem of that should be the tree item
// parent. Or, if the parent is something other than a tree we will
// return that.
if (parentRole != nsIAccessibleRole::ROLE_GROUPING) {
mParent = parent;
return;
}
nsAccessible* parentPrevSibling = parent->GetSiblingAtOffset(-1);
PRUint32 parentPrevSiblingRole = nsAccUtils::Role(parentPrevSibling);
if (parentPrevSiblingRole == nsIAccessibleRole::ROLE_TEXT_LEAF) {
// XXX Sometimes an empty text accessible is in the hierarchy here,
// although the text does not appear to be rendered, GetRenderedText()
// says that it is so we need to skip past it to find the true
// previous sibling.
parentPrevSibling = parentPrevSibling->GetSiblingAtOffset(-1);
parentPrevSiblingRole = nsAccUtils::Role(parentPrevSibling);
}
// Previous sibling of parent group is a tree item, this is the
// conceptual tree item parent.
if (parentPrevSiblingRole == nsIAccessibleRole::ROLE_OUTLINEITEM)
mParent = parentPrevSibling;
}

View File

@ -0,0 +1,96 @@
/* ***** 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 mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alexander Surkov <surkov.alexander@gmail.com> (original author)
*
* 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 ***** */
#ifndef AccGroupInfo_h_
#define AccGroupInfo_h_
#include "nsAccessible.h"
#include "nsAccUtils.h"
/**
* Calculate and store group information.
*/
class AccGroupInfo
{
public:
AccGroupInfo(nsAccessible* aItem, PRUint32 aRole);
~AccGroupInfo() { }
PRInt32 PosInSet() const { return mPosInSet; }
PRUint32 SetSize() const { return mSetSize; }
nsAccessible* GetConceptualParent() const { return mParent; }
/**
* Create group info.
*/
static AccGroupInfo* CreateGroupInfo(nsAccessible* aAccessible)
{
PRUint32 role = nsAccUtils::Role(aAccessible);
if (role != nsIAccessibleRole::ROLE_ROW &&
role != nsIAccessibleRole::ROLE_GRID_CELL &&
role != nsIAccessibleRole::ROLE_OUTLINEITEM &&
role != nsIAccessibleRole::ROLE_OPTION &&
role != nsIAccessibleRole::ROLE_LISTITEM &&
role != nsIAccessibleRole::ROLE_MENUITEM &&
role != nsIAccessibleRole::ROLE_CHECK_MENU_ITEM &&
role != nsIAccessibleRole::ROLE_RADIO_MENU_ITEM &&
role != nsIAccessibleRole::ROLE_RADIOBUTTON &&
role != nsIAccessibleRole::ROLE_PAGETAB)
return nsnull;
AccGroupInfo* info = new AccGroupInfo(aAccessible, BaseRole(role));
return info;
}
private:
AccGroupInfo(const AccGroupInfo&);
AccGroupInfo& operator =(const AccGroupInfo&);
static PRUint32 BaseRole(PRUint32 aRole)
{
if (aRole == nsIAccessibleRole::ROLE_CHECK_MENU_ITEM ||
aRole == nsIAccessibleRole::ROLE_RADIO_MENU_ITEM)
return nsIAccessibleRole::ROLE_MENUITEM;
return aRole;
}
PRUint32 mPosInSet;
PRUint32 mSetSize;
nsAccessible* mParent;
};
#endif

View File

@ -49,6 +49,7 @@ LIBXUL_LIBRARY = 1
CPPSRCS = \
AccCollector.cpp \
AccGroupInfo.cpp \
AccIterator.cpp \
filters.cpp \
nsAccDocManager.cpp \

View File

@ -221,6 +221,34 @@ nsARIAGridAccessible::GetRowIndexAt(PRInt32 aCellIndex, PRInt32 *aRowIndex)
return NS_OK;
}
NS_IMETHODIMP
nsARIAGridAccessible::GetRowAndColumnIndicesAt(PRInt32 aCellIndex,
PRInt32* aRowIndex,
PRInt32* aColumnIndex)
{
NS_ENSURE_ARG_POINTER(aRowIndex);
*aRowIndex = -1;
NS_ENSURE_ARG_POINTER(aColumnIndex);
*aColumnIndex = -1;
if (IsDefunct())
return NS_ERROR_FAILURE;
NS_ENSURE_ARG(aCellIndex >= 0);
PRInt32 rowCount = 0;
GetRowCount(&rowCount);
PRInt32 colsCount = 0;
GetColumnCount(&colsCount);
NS_ENSURE_ARG(aCellIndex < rowCount * colsCount);
*aColumnIndex = aCellIndex % colsCount;
*aRowIndex = aCellIndex / colsCount;
return NS_OK;
}
NS_IMETHODIMP
nsARIAGridAccessible::GetColumnExtentAt(PRInt32 aRow, PRInt32 aColumn,
PRInt32 *aExtentCount)

View File

@ -367,106 +367,6 @@ nsAccUtils::GetAncestorWithRole(nsAccessible *aDescendant, PRUint32 aRole)
return nsnull;
}
void
nsAccUtils::GetARIATreeItemParent(nsIAccessible *aStartTreeItem,
nsIContent *aStartContent,
nsIAccessible **aTreeItemParentResult)
{
*aTreeItemParentResult = nsnull;
nsCOMPtr<nsIAccessible> parentAccessible;
aStartTreeItem->GetParent(getter_AddRefs(parentAccessible));
if (!parentAccessible)
return;
PRUint32 startTreeItemRole = nsAccUtils::Role(aStartTreeItem);
// Calculate tree grid row parent only if the row inside of ARIA treegrid.
if (startTreeItemRole == nsIAccessibleRole::ROLE_ROW) {
PRUint32 role = nsAccUtils::Role(parentAccessible);
if (role != nsIAccessibleRole::ROLE_TREE_TABLE)
return;
}
// This is a tree or treegrid that uses aria-level to define levels, so find
// the first previous sibling accessible where level is defined to be less
// than the current level.
nsAutoString levelStr;
if (nsAccUtils::HasDefinedARIAToken(aStartContent, nsAccessibilityAtoms::aria_level) &&
aStartContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_level, levelStr)) {
PRInt32 success;
PRInt32 level = levelStr.ToInteger(&success);
if (level > 1 && NS_SUCCEEDED(success)) {
nsCOMPtr<nsIAccessible> currentAccessible = aStartTreeItem, prevAccessible;
while (PR_TRUE) {
currentAccessible->GetPreviousSibling(getter_AddRefs(prevAccessible));
currentAccessible.swap(prevAccessible);
nsCOMPtr<nsIAccessNode> accessNode = do_QueryInterface(currentAccessible);
if (!accessNode) {
break; // Reached top of tree, no higher level found
}
PRUint32 role = nsAccUtils::Role(currentAccessible);
if (role != startTreeItemRole)
continue;
nsCOMPtr<nsIDOMNode> treeItemNode;
accessNode->GetDOMNode(getter_AddRefs(treeItemNode));
nsCOMPtr<nsIContent> treeItemContent = do_QueryInterface(treeItemNode);
if (treeItemContent &&
nsAccUtils::HasDefinedARIAToken(treeItemContent,
nsAccessibilityAtoms::aria_level) &&
treeItemContent->GetAttr(kNameSpaceID_None,
nsAccessibilityAtoms::aria_level, levelStr)) {
if (levelStr.ToInteger(&success) < level && NS_SUCCEEDED(success)) {
NS_ADDREF(*aTreeItemParentResult = currentAccessible);
return;
}
}
}
}
}
// In the case of ARIA treegrid, return its parent since ARIA group isn't
// used to organize levels in ARIA treegrids.
if (startTreeItemRole == nsIAccessibleRole::ROLE_ROW) {
NS_ADDREF(*aTreeItemParentResult = parentAccessible);
return; // The container for the tree grid rows
}
// In the case of ARIA tree, a tree can be arranged by using role="group" to
// organize levels. In this case the parent of the tree item will be a group
// and the previous sibling of that should be the tree item parent. Or, if
// the parent is something other than a tree we will return that.
PRUint32 role = nsAccUtils::Role(parentAccessible);
if (role != nsIAccessibleRole::ROLE_GROUPING) {
NS_ADDREF(*aTreeItemParentResult = parentAccessible);
return; // The container for the tree items
}
nsCOMPtr<nsIAccessible> prevAccessible;
parentAccessible->GetPreviousSibling(getter_AddRefs(prevAccessible));
if (!prevAccessible)
return;
role = nsAccUtils::Role(prevAccessible);
if (role == nsIAccessibleRole::ROLE_TEXT_LEAF) {
// XXX Sometimes an empty text accessible is in the hierarchy here,
// although the text does not appear to be rendered, GetRenderedText() says that it is
// so we need to skip past it to find the true previous sibling
nsCOMPtr<nsIAccessible> tempAccessible = prevAccessible;
tempAccessible->GetPreviousSibling(getter_AddRefs(prevAccessible));
if (!prevAccessible)
return;
role = nsAccUtils::Role(prevAccessible);
}
if (role == nsIAccessibleRole::ROLE_OUTLINEITEM) {
// Previous sibling of parent group is a tree item -- this is the conceptual tree item parent
NS_ADDREF(*aTreeItemParentResult = prevAccessible);
}
}
nsAccessible *
nsAccUtils::GetSelectableContainer(nsAccessible *aAccessible, PRUint32 aState)
{

View File

@ -198,19 +198,6 @@ public:
static nsAccessible * GetAncestorWithRole(nsAccessible *aDescendant,
PRUint32 aRole);
/**
* For an ARIA tree item , get the accessible that represents its conceptual parent.
* This method will use the correct method for the given way the tree is constructed.
* The conceptual parent is what the user sees as the parent, not the DOM or accessible parent.
* @param aStartTreeItem The tree item to get the parent for
* @param aStartTreeItemContent The content node for the tree item
* @param The tree item's parent, or null if none
*/
static void
GetARIATreeItemParent(nsIAccessible *aStartTreeItem,
nsIContent *aStartTreeItemContent,
nsIAccessible **aTreeItemParent);
/**
* Return single or multi selectable container for the given item.
*

View File

@ -131,7 +131,7 @@ void nsAccessNode::LastRelease()
NS_ASSERTION(!mWeakShell, "A Shutdown() impl forgot to call its parent's Shutdown?");
}
// ... then die.
NS_DELETEXPCOM(this);
delete this;
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -41,6 +41,7 @@
#include "nsIXBLAccessible.h"
#include "AccGroupInfo.h"
#include "AccIterator.h"
#include "nsAccUtils.h"
#include "nsARIAMap.h"
@ -2125,11 +2126,12 @@ nsAccessible::GetRelationByType(PRUint32 aRelationType,
(mRoleMapEntry->role == nsIAccessibleRole::ROLE_OUTLINEITEM ||
mRoleMapEntry->role == nsIAccessibleRole::ROLE_ROW)) {
nsCOMPtr<nsIAccessible> accTarget;
nsAccUtils::GetARIATreeItemParent(this, mContent,
getter_AddRefs(accTarget));
AccGroupInfo* groupInfo = GetGroupInfo();
if (!groupInfo)
return NS_OK_NO_RELATION_TARGET;
return nsRelUtils::AddTarget(aRelationType, aRelation, accTarget);
return nsRelUtils::AddTarget(aRelationType, aRelation,
groupInfo->GetConceptualParent());
}
// If accessible is in its own Window, or is the root of a document,
@ -3091,98 +3093,24 @@ nsAccessible::GetActionRule(PRUint32 aStates)
return eNoAction;
}
AccGroupInfo*
nsAccessible::GetGroupInfo()
{
if (mGroupInfo)
return mGroupInfo;
mGroupInfo = AccGroupInfo::CreateGroupInfo(this);
return mGroupInfo;
}
void
nsAccessible::GetPositionAndSizeInternal(PRInt32 *aPosInSet, PRInt32 *aSetSize)
{
PRUint32 role = nsAccUtils::Role(this);
if (role != nsIAccessibleRole::ROLE_LISTITEM &&
role != nsIAccessibleRole::ROLE_MENUITEM &&
role != nsIAccessibleRole::ROLE_CHECK_MENU_ITEM &&
role != nsIAccessibleRole::ROLE_RADIO_MENU_ITEM &&
role != nsIAccessibleRole::ROLE_RADIOBUTTON &&
role != nsIAccessibleRole::ROLE_PAGETAB &&
role != nsIAccessibleRole::ROLE_OPTION &&
role != nsIAccessibleRole::ROLE_OUTLINEITEM &&
role != nsIAccessibleRole::ROLE_ROW &&
role != nsIAccessibleRole::ROLE_GRID_CELL)
return;
PRUint32 baseRole = role;
if (role == nsIAccessibleRole::ROLE_CHECK_MENU_ITEM ||
role == nsIAccessibleRole::ROLE_RADIO_MENU_ITEM)
baseRole = nsIAccessibleRole::ROLE_MENUITEM;
nsAccessible* parent = GetParent();
NS_ENSURE_TRUE(parent,);
PRInt32 level = nsAccUtils::GetARIAOrDefaultLevel(this);
// Compute 'posinset'.
PRInt32 positionInGroup = 1;
for (PRInt32 idx = mIndexInParent - 1; idx >= 0; idx--) {
nsAccessible* sibling = parent->GetChildAt(idx);
PRUint32 siblingRole = nsAccUtils::Role(sibling);
// If the sibling is separator then the group is ended.
if (siblingRole == nsIAccessibleRole::ROLE_SEPARATOR)
break;
PRUint32 siblingBaseRole = siblingRole;
if (siblingRole == nsIAccessibleRole::ROLE_CHECK_MENU_ITEM ||
siblingRole == nsIAccessibleRole::ROLE_RADIO_MENU_ITEM)
siblingBaseRole = nsIAccessibleRole::ROLE_MENUITEM;
// If sibling is visible and has the same base role
if (siblingBaseRole == baseRole &&
!(nsAccUtils::State(sibling) & nsIAccessibleStates::STATE_INVISIBLE)) {
// and check if it's hierarchical flatten structure, i.e. if the sibling
// level is lesser than this one then group is ended, if the sibling level
// is greater than this one then the group is splited by some child
// elements (group will be continued).
PRInt32 siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
if (siblingLevel < level)
break;
else if (level == siblingLevel)
++ positionInGroup;
}
AccGroupInfo* groupInfo = GetGroupInfo();
if (groupInfo) {
*aPosInSet = groupInfo->PosInSet();
*aSetSize = groupInfo->SetSize();
}
// Compute 'setsize'.
PRInt32 setSize = positionInGroup;
PRInt32 siblingCount = parent->GetChildCount();
for (PRInt32 idx = mIndexInParent + 1; idx < siblingCount; idx++) {
nsAccessible* sibling = parent->GetChildAt(idx);
NS_ENSURE_TRUE(sibling,);
PRUint32 siblingRole = nsAccUtils::Role(sibling);
// If the sibling is separator then the group is ended.
if (siblingRole == nsIAccessibleRole::ROLE_SEPARATOR)
break;
PRUint32 siblingBaseRole = siblingRole;
if (siblingRole == nsIAccessibleRole::ROLE_CHECK_MENU_ITEM ||
siblingRole == nsIAccessibleRole::ROLE_RADIO_MENU_ITEM)
siblingBaseRole = nsIAccessibleRole::ROLE_MENUITEM;
// If sibling is visible and has the same base role
if (siblingBaseRole == baseRole &&
!(nsAccUtils::State(sibling) & nsIAccessibleStates::STATE_INVISIBLE)) {
// and check if it's hierarchical flatten structure.
PRInt32 siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
if (siblingLevel < level)
break;
else if (level == siblingLevel)
++ setSize;
}
}
*aPosInSet = positionInGroup;
*aSetSize = setSize;
}
PRInt32

View File

@ -52,6 +52,7 @@
#include "nsTArray.h"
#include "nsRefPtrHashtable.h"
class AccGroupInfo;
class nsAccessible;
class nsAccEvent;
struct nsRoleMapEntry;
@ -324,7 +325,12 @@ protected:
* Set accessible parent and index in parent.
*/
void BindToParent(nsAccessible* aParent, PRUint32 aIndexInParent);
void UnbindFromParent() { mParent = nsnull; mIndexInParent = -1; }
void UnbindFromParent()
{
mParent = nsnull;
mIndexInParent = -1;
mGroupInfo = nsnull;
}
/**
* Return sibling accessible at the given offset.
@ -424,6 +430,11 @@ protected:
*/
PRUint32 GetActionRule(PRUint32 aStates);
/**
* Return group info.
*/
AccGroupInfo* GetGroupInfo();
/**
* Fires platform accessible event. It's notification method only. It does
* change nothing on Gecko side. Don't use it until you're sure what you do
@ -440,6 +451,9 @@ protected:
PRBool mAreChildrenInitialized;
PRInt32 mIndexInParent;
nsAutoPtr<AccGroupInfo> mGroupInfo;
friend class AccGroupInfo;
nsRoleMapEntry *mRoleMapEntry; // Non-null indicates author-supplied role; possibly state & value as well
};

View File

@ -953,6 +953,26 @@ nsHTMLTableAccessible::GetRowIndexAt(PRInt32 aIndex, PRInt32 *aRow)
return (*aRow == -1 || column == -1) ? NS_ERROR_INVALID_ARG : NS_OK;
}
NS_IMETHODIMP
nsHTMLTableAccessible::GetRowAndColumnIndicesAt(PRInt32 aIndex,
PRInt32* aRowIdx,
PRInt32* aColumnIdx)
{
NS_ENSURE_ARG_POINTER(aRowIdx);
*aRowIdx = -1;
NS_ENSURE_ARG_POINTER(aColumnIdx);
*aColumnIdx = -1;
if (IsDefunct())
return NS_ERROR_FAILURE;
nsITableLayout* tableLayout = GetTableLayout();
if (tableLayout)
tableLayout->GetRowAndColumnByIndex(aIndex, aRowIdx, aColumnIdx);
return (*aRowIdx == -1 || *aColumnIdx == -1) ? NS_ERROR_INVALID_ARG : NS_OK;
}
NS_IMETHODIMP
nsHTMLTableAccessible::GetColumnExtentAt(PRInt32 aRowIndex,
PRInt32 aColumnIndex,

View File

@ -666,33 +666,28 @@ __try {
if (!tableAcc)
return E_FAIL;
PRInt32 row = -1;
nsresult rv = tableAcc->GetRowIndexAt(aIndex, &row);
if (NS_FAILED(rv))
return GetHRESULT(rv);
PRInt32 column = -1;
rv = tableAcc->GetColumnIndexAt(aIndex, &column);
PRInt32 rowIdx = -1, columnIdx = -1;
nsresult rv = tableAcc->GetRowAndColumnIndicesAt(aIndex, &rowIdx, &columnIdx);
if (NS_FAILED(rv))
return GetHRESULT(rv);
PRInt32 rowExtents = 0;
rv = tableAcc->GetRowExtentAt(row, column, &rowExtents);
rv = tableAcc->GetRowExtentAt(rowIdx, columnIdx, &rowExtents);
if (NS_FAILED(rv))
return GetHRESULT(rv);
PRInt32 columnExtents = 0;
rv = tableAcc->GetColumnExtentAt(row, column, &columnExtents);
rv = tableAcc->GetColumnExtentAt(rowIdx, columnIdx, &columnExtents);
if (NS_FAILED(rv))
return GetHRESULT(rv);
PRBool isSelected = PR_FALSE;
rv = tableAcc->IsCellSelected(row, column, &isSelected);
rv = tableAcc->IsCellSelected(rowIdx, columnIdx, &isSelected);
if (NS_FAILED(rv))
return GetHRESULT(rv);
*aRow = row;
*aColumn = column;
*aRow = rowIdx;
*aColumn = columnIdx;
*aRowExtents = rowExtents;
*aColumnExtents = columnExtents;
*aIsSelected = isSelected;

View File

@ -412,6 +412,28 @@ nsXULListboxAccessible::GetRowIndexAt(PRInt32 aIndex, PRInt32 *aRow)
return NS_OK;
}
NS_IMETHODIMP
nsXULListboxAccessible::GetRowAndColumnIndicesAt(PRInt32 aCellIndex,
PRInt32* aRowIndex,
PRInt32* aColumnIndex)
{
NS_ENSURE_ARG_POINTER(aRowIndex);
*aRowIndex = -1;
NS_ENSURE_ARG_POINTER(aColumnIndex);
*aColumnIndex = -1;
if (IsDefunct())
return NS_ERROR_FAILURE;
PRInt32 columnCount = 0;
nsresult rv = GetColumnCount(&columnCount);
NS_ENSURE_SUCCESS(rv, rv);
*aColumnIndex = aCellIndex % columnCount;
*aRowIndex = aCellIndex / columnCount;
return NS_OK;
}
NS_IMETHODIMP
nsXULListboxAccessible::GetColumnExtentAt(PRInt32 aRow, PRInt32 aColumn,
PRInt32 *aCellSpans)

View File

@ -411,6 +411,28 @@ nsXULTreeGridAccessible::GetRowIndexAt(PRInt32 aCellIndex, PRInt32 *aRowIndex)
return NS_OK;
}
NS_IMETHODIMP
nsXULTreeGridAccessible::GetRowAndColumnIndicesAt(PRInt32 aCellIndex,
PRInt32* aRowIndex,
PRInt32* aColumnIndex)
{
NS_ENSURE_ARG_POINTER(aRowIndex);
*aRowIndex = -1;
NS_ENSURE_ARG_POINTER(aColumnIndex);
*aColumnIndex = -1;
if (IsDefunct())
return NS_ERROR_FAILURE;
PRInt32 columnCount = 0;
nsresult rv = GetColumnCount(&columnCount);
NS_ENSURE_SUCCESS(rv, rv);
*aColumnIndex = aCellIndex % columnCount;
*aRowIndex = aCellIndex / columnCount;
return NS_OK;
}
NS_IMETHODIMP
nsXULTreeGridAccessible::GetColumnExtentAt(PRInt32 aRowIndex,
PRInt32 aColumnIndex,

View File

@ -224,7 +224,7 @@ function testTableIndexes(aIdentifier, aIdxes)
if (idx != - 1) {
// getRowAtIndex
// getRowIndexAt
var origRowIdx = rowIdx;
while (origRowIdx > 0 &&
aIdxes[rowIdx][colIdx] == aIdxes[origRowIdx - 1][colIdx])
@ -237,9 +237,9 @@ function testTableIndexes(aIdentifier, aIdxes)
}
is(obtainedRowIdx, origRowIdx,
id + ": row for index " + idx +" is not correct");
id + ": row for index " + idx + " is not correct (getRowIndexAt)");
// getColumnAtIndex
// getColumnIndexAt
var origColIdx = colIdx;
while (origColIdx > 0 &&
aIdxes[rowIdx][colIdx] == aIdxes[rowIdx][origColIdx - 1])
@ -252,7 +252,21 @@ function testTableIndexes(aIdentifier, aIdxes)
}
is(obtainedColIdx, origColIdx,
id + ": column for index " + idx +" is not correct");
id + ": column for index " + idx + " is not correct (getColumnIndexAt)");
// getRowAndColumnIndicesAt
var obtainedRowIdxObj = { }, obtainedColIdxObj = { };
try {
tableAcc.getRowAndColumnIndicesAt(idx, obtainedRowIdxObj,
obtainedColIdxObj);
} catch (e) {
ok(false, id + ": can't get row and column indices for cell index " + idx + "," + e);
}
is(obtainedRowIdxObj.value, origRowIdx,
id + ": row for index " + idx + " is not correct (getRowAndColumnIndicesAt)");
is(obtainedColIdxObj.value, origColIdx,
id + ": column for index " + idx + " is not correct (getRowAndColumnIndicesAt)");
if (cellAcc) {

View File

@ -320,8 +320,6 @@ pref("browser.tabs.loadInBackground", true);
pref("browser.tabs.opentabfor.middleclick", true);
pref("browser.tabs.loadDivertedInBackground", false);
pref("browser.tabs.loadBookmarksInBackground", false);
pref("browser.tabs.tabMinWidth", 100);
pref("browser.tabs.tabMaxWidth", 250);
pref("browser.tabs.tabClipWidth", 140);
// Where to show tab close buttons:

View File

@ -22,12 +22,18 @@ tabbrowser {
.tabbrowser-tab {
-moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tab");
}
.tabbrowser-tab:not([pinned]) {
-moz-box-flex: 100;
max-width: 250px;
min-width: 100px;
width: 0;
}
.tabbrowser-tab:not([fadein]) {
max-width: 1px !important;
min-width: 1px !important;
max-width: 1px;
min-width: 1px;
}
.tabbrowser-tab[fadein]:not([pinned]) {
@ -48,9 +54,6 @@ tabbrowser {
.tabbrowser-tab[pinned] {
position: fixed;
-moz-box-flex: 0;
min-width: 0 !important;
max-width: none !important;
}
.tabbrowser-tab[pinned] > .tab-text {
@ -81,6 +84,12 @@ toolbar[printpreview="true"] {
-moz-box-ordinal-group: 10;
}
%ifdef MENUBAR_CAN_AUTOHIDE
#main-window[inFullscreen] > #appmenu-button-container {
display: none;
}
%endif
toolbarpaletteitem[place="palette"] > toolbaritem > hbox[type="places"] {
display: none;
}

View File

@ -4727,12 +4727,12 @@ var TabsOnTop = {
#ifdef MENUBAR_CAN_AUTOHIDE
function updateAppButtonDisplay() {
var menubarHidden =
var displayAppButton = window.menubar.visible &&
document.getElementById("toolbar-menubar").getAttribute("autohide") == "true";
document.getElementById("appmenu-button-container").hidden = !menubarHidden;
document.getElementById("appmenu-button-container").hidden = !displayAppButton;
if (menubarHidden)
if (displayAppButton)
document.documentElement.setAttribute("chromemargin", "0,-1,-1,-1");
else
document.documentElement.removeAttribute("chromemargin");
@ -7772,6 +7772,10 @@ var TabContextMenu = {
Cc["@mozilla.org/browser/sessionstore;1"].
getService(Ci.nsISessionStore).
getClosedTabCount(window) == 0;
// Only one of pin/unpin should be visible
document.getElementById("context_pinTab").hidden = this.contextTab.pinned;
document.getElementById("context_unpinTab").hidden = !this.contextTab.pinned;
}
};

View File

@ -126,6 +126,12 @@
accesskey="&openTabInNewWindow.accesskey;"
tbattr="tabbrowser-multiple"
oncommand="gBrowser.replaceTabWithWindow(TabContextMenu.contextTab);"/>
<menuitem id="context_pinTab" label="&pinTab.label;"
accesskey="&pinTab.accesskey;"
oncommand="gBrowser.pinTab(TabContextMenu.contextTab);"/>
<menuitem id="context_unpinTab" label="&unpinTab.label;" hidden="true"
accesskey="&unpinTab.accesskey;"
oncommand="gBrowser.unpinTab(TabContextMenu.contextTab);"/>
<menuseparator/>
<menuitem id="context_bookmarkTab"
label="&bookmarkThisTab.label;"

View File

@ -1210,9 +1210,6 @@
t.setAttribute("label", aURI);
t.setAttribute("crop", "end");
t.style.maxWidth = this.tabContainer.mTabMaxWidth + "px";
t.style.minWidth = this.tabContainer.mTabMinWidth + "px";
t.width = 0;
t.setAttribute("validate", "never");
t.setAttribute("onerror", "this.removeAttribute('image');");
t.className = "tabbrowser-tab";
@ -2505,8 +2502,6 @@
<implementation implements="nsIDOMEventListener">
<constructor>
<![CDATA[
this.mTabMinWidth = Services.prefs.getIntPref("browser.tabs.tabMinWidth");
this.mTabMaxWidth = Services.prefs.getIntPref("browser.tabs.tabMaxWidth");
this.mTabClipWidth = Services.prefs.getIntPref("browser.tabs.tabClipWidth");
this.mCloseButtons = Services.prefs.getIntPref("browser.tabs.closeButtons");
this._closeWindowWithLastTab = Services.prefs.getBoolPref("browser.tabs.closeWindowWithLastTab");
@ -2514,9 +2509,6 @@
var tab = this.firstChild;
tab.setAttribute("label",
this.tabbrowser.mStringBundle.getString("tabs.emptyTabTitle"));
tab.style.minWidth = this.mTabMinWidth + "px";
tab.style.maxWidth = this.mTabMaxWidth + "px";
tab.width = 0;
tab.setAttribute("crop", "end");
tab.setAttribute("validate", "never");
tab.setAttribute("onerror", "this.removeAttribute('image');");

View File

@ -25,7 +25,7 @@ function test() {
function doTest() {
tabstrip.smoothScroll = false;
var tabMinWidth = gPrefService.getIntPref("browser.tabs.tabMinWidth");
var tabMinWidth = parseInt(getComputedStyle(gBrowser.selectedTab, null).minWidth);
var tabCountForOverflow = Math.ceil(width(tabstrip) / tabMinWidth * 3);
while (tabContainer.childNodes.length < tabCountForOverflow)
gBrowser.addTab("about:blank", {skipAnimation: true});

View File

@ -83,8 +83,7 @@ function test() {
let numTests = 4;
let completedTests = 0;
// access the pref service just once
let tabMinWidth = gPrefService.getIntPref("browser.tabs.tabMinWidth");
let tabMinWidth = parseInt(getComputedStyle(gBrowser.selectedTab, null).minWidth);
function runTest(testNum, totalTabs, selectedTab, shownTabs, order) {
let test = {

View File

@ -22,6 +22,10 @@
<!ENTITY closeOtherTabs.accesskey "o">
<!ENTITY openTabInNewWindow.label "Open in a New Window">
<!ENTITY openTabInNewWindow.accesskey "W">
<!ENTITY pinTab.label "Make into App Tab">
<!ENTITY pinTab.accesskey "p">
<!ENTITY unpinTab.label "Make into Normal Tab">
<!ENTITY unpinTab.accesskey "k">
<!ENTITY bookmarkThisTab.label "Bookmark This Tab">
<!ENTITY bookmarkThisTab.accesskey "B">
<!ENTITY bookmarkAllTabs.label "Bookmark All Tabs…">

View File

@ -52,7 +52,7 @@ addonInstallManage.accesskey=O
addonError-1=The add-on could not be downloaded because of a connection failure on #2.
addonError-2=The add-on from #2 could not be installed because it does not match the add-on #3 expected.
addonError-3=The add-on downloaded from #2 could not be installed because it appears to be corrupt.
addonError-4=#1 could not be installed because Firefox cannot modify the needed file.
addonError-4=#1 could not be installed because #3 cannot modify the needed file.
addonErrorIncompatible=#1 could not be installed because it is not compatible with #3 #4.
addonErrorBlocklisted=#1 could not be installed because it has a high risk of causing stability or security problems.

View File

@ -181,7 +181,7 @@ statusbarpanel#statusbar-display {
position: relative !important;
background-color: -moz-dialog !important;
}
#navigator-toolbox[tabsontop="true"] > #toolbar-menubar[autohide="true"] ~ #TabsToolbar {
#navigator-toolbox[tabsontop="true"] > #toolbar-menubar[autohide="true"] ~ #TabsToolbar:not([inFullscreen]) {
-moz-padding-start: 10em !important;
}
%ifdef WINSTRIPE_AERO

View File

@ -40,12 +40,28 @@
@import "chrome://global/skin/";
/* View buttons */
#viewGroup {
-moz-padding-start: 10px;
}
#viewGroup > radio {
list-style-image: url("chrome://browser/skin/pageInfo.png");
-moz-box-orient: vertical;
-moz-box-align: center;
-moz-appearance: none;
padding: 5px 3px 1px 3px;
margin: 0 1px;
min-width: 4.5em;
}
#viewGroup > radio:hover {
background-color: #E0E8F6;
color: black;
}
#viewGroup > radio[selected="true"] {
background-color: #C1D2EE;
color: black;
}
#topBar {

View File

@ -78,7 +78,7 @@ nsNullPrincipal::Release()
nsrefcnt count = PR_AtomicDecrement((PRInt32 *)&mJSPrincipals.refcount);
NS_LOG_RELEASE(this, count, "nsNullPrincipal");
if (count == 0) {
NS_DELETEXPCOM(this);
delete this;
}
return count;

View File

@ -166,7 +166,7 @@ nsPrincipal::Release()
nsrefcnt count = PR_AtomicDecrement((PRInt32 *)&mJSPrincipals.refcount);
NS_LOG_RELEASE(this, count, "nsPrincipal");
if (count == 0) {
NS_DELETEXPCOM(this);
delete this;
}
return count;

View File

@ -75,7 +75,7 @@ nsSystemPrincipal::Release()
nsrefcnt count = PR_AtomicDecrement((PRInt32 *)&mJSPrincipals.refcount);
NS_LOG_RELEASE(this, count, "nsSystemPrincipal");
if (count == 0) {
NS_DELETEXPCOM(this);
delete this;
}
return count;

View File

@ -51,10 +51,12 @@ function do_run_test()
let cr = Cc["@mozilla.org/chrome/chrome-registry;1"].
getService(Ci.nsIChromeRegistry);
var runtime = Components.classes["@mozilla.org/xre/app-info;1"]
.getService(Components.interfaces.nsIXULRuntime);
if (runtime.processType ==
Components.interfaces.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
// If we don't have libxul or e10s then we don't have process separation, so
// we don't need to worry about checking for new chrome.
var appInfo = Cc["@mozilla.org/xre/app-info;1"];
if (!appInfo ||
(appInfo.getService(Ci.nsIXULRuntime).processType ==
Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT)) {
cr.checkForNewChrome();
}

View File

@ -168,6 +168,7 @@ xpcshell-tests:
-I$(topsrcdir)/build \
$(testxpcsrcdir)/runxpcshelltests.py \
--symbols-path=$(DIST)/crashreporter-symbols \
$(EXTRA_TEST_ARGS) \
$(DIST)/bin/xpcshell \
$(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(MODULE)/$(dir))
@ -193,6 +194,8 @@ check-one:
--symbols-path=$(DIST)/crashreporter-symbols \
--test-path=$(SOLO_FILE) \
--profile-name=$(MOZ_APP_NAME) \
--verbose \
$(EXTRA_TEST_ARGS) \
$(DIST)/bin/xpcshell \
$(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(MODULE)/$(dir))
@ -2291,3 +2294,6 @@ CHECK_FROZEN_VARIABLES = $(foreach var,$(FREEZE_VARIABLES), \
libs export libs::
$(CHECK_FROZEN_VARIABLES)
default::
if test -d $(DIST)/bin ; then touch $(DIST)/bin/.purgecaches ; fi

View File

@ -1567,8 +1567,8 @@ if test "$GNU_CC"; then
ASFLAGS="$ASFLAGS -fPIC"
_MOZ_RTTI_FLAGS_ON=${_COMPILER_PREFIX}-frtti
_MOZ_RTTI_FLAGS_OFF=${_COMPILER_PREFIX}-fno-rtti
_MOZ_EXCEPTIONS_FLAGS_ON='-fhandle-exceptions'
_MOZ_EXCEPTIONS_FLAGS_OFF='-fno-handle-exceptions'
_MOZ_EXCEPTIONS_FLAGS_ON='-fexceptions'
_MOZ_EXCEPTIONS_FLAGS_OFF='-fno-exceptions'
# Turn on GNU specific features
# -Wall - turn on all warnings
@ -2539,8 +2539,8 @@ ia64*-hpux*)
USE_PTHREADS=1
_PEDANTIC=
LIBS="$LIBS -lsocket -lstdc++"
_DEFINES_CFLAGS='-Wp,-include -Wp,$(DEPTH)/mozilla-config.h -DMOZILLA_CLIENT -D_POSIX_C_SOURCE=199506'
_DEFINES_CXXFLAGS='-DMOZILLA_CLIENT -Wp,-include -Wp,$(DEPTH)/mozilla-config.h -D_POSIX_C_SOURCE=199506'
_DEFINES_CFLAGS='-include $(DEPTH)/mozilla-config.h -DMOZILLA_CLIENT -D_POSIX_C_SOURCE=199506'
_DEFINES_CXXFLAGS='-DMOZILLA_CLIENT -include $(DEPTH)/mozilla-config.h -D_POSIX_C_SOURCE=199506'
if test "$with_x" != "yes"
then
_PLATFORM_DEFAULT_TOOLKIT="photon"
@ -4024,32 +4024,6 @@ EOF
esac
dnl ===================================================================
dnl ========================================================
dnl By default, turn rtti and exceptions off on g++/egcs
dnl ========================================================
if test "$GNU_CXX"; then
AC_MSG_CHECKING(for C++ exceptions flag)
dnl They changed -f[no-]handle-exceptions to -f[no-]exceptions in g++ 2.8
AC_CACHE_VAL(ac_cv_cxx_exceptions_flags,
[echo "int main() { return 0; }" | cat > conftest.C
${CXX-g++} ${CXXFLAGS} -c -fno-handle-exceptions conftest.C > conftest.out 2>&1
if egrep "warning.*renamed" conftest.out >/dev/null; then
ac_cv_cxx_exceptions_flags=${_COMPILER_PREFIX}-fno-exceptions
else
ac_cv_cxx_exceptions_flags=${_COMPILER_PREFIX}-fno-handle-exceptions
fi
rm -f conftest*])
AC_MSG_RESULT($ac_cv_cxx_exceptions_flags)
_MOZ_EXCEPTIONS_FLAGS_OFF=$ac_cv_cxx_exceptions_flags
_MOZ_EXCEPTIONS_FLAGS_ON=`echo $ac_cv_cxx_exceptions_flags | sed 's|no-||'`
fi
dnl ========================================================
dnl Put your C++ language/feature checks below
dnl ========================================================
@ -6005,7 +5979,7 @@ if test -n "$MOZ_SYDNEYAUDIO"; then
linux*)
PKG_CHECK_MODULES(MOZ_ALSA, alsa, ,
[echo "$MOZ_ALSA_PKG_ERRORS"
AC_MSG_ERROR([Need alsa for Ogg or Wave decoding on Linux. Disable with --disable-ogg --disable-wave. (On Ubuntu, you might try installing the package libasound2-dev.)])])
AC_MSG_ERROR([Need alsa for Ogg, Wave or WebM decoding on Linux. Disable with --disable-ogg --disable-wave --disable-webm. (On Ubuntu, you might try installing the package libasound2-dev.)])])
;;
esac
fi
@ -7977,7 +7951,7 @@ if test "$_cpp_md_flag"; then
if test "$OS_ARCH" = "OpenVMS"; then
_DEPEND_CFLAGS='$(subst =, ,$(filter-out %/.pp,-MM=-MD=-MF=$(MDDEPDIR)/$(basename $(@F)).pp))'
else
_DEPEND_CFLAGS='$(filter-out %/.pp,-Wp,-MD,$(MDDEPDIR)/$(basename $(@F)).pp)'
_DEPEND_CFLAGS='$(filter-out %/.pp,-MD -MF $(MDDEPDIR)/$(basename $(@F)).pp)'
fi
dnl Sun Studio on Solaris use -xM instead of -MD, see config/rules.mk
if test "$SOLARIS_SUNPRO_CC"; then

View File

@ -1360,8 +1360,7 @@ nsXMLHttpRequest::GetAllResponseHeaders(char **_retval)
nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
if (httpChannel) {
nsHeaderVisitor *visitor = nsnull;
NS_NEWXPCOM(visitor, nsHeaderVisitor);
nsHeaderVisitor *visitor = new nsHeaderVisitor();
if (!visitor)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(visitor);

View File

@ -138,8 +138,8 @@ WebGLContext::SetDimensions(PRInt32 width, PRInt32 height)
// If incrementing the generation would cause overflow,
// don't allow it. Allowing this would allow us to use
// resource handles created from older context generations.
if (mGeneration + 1 == 0)
return NS_ERROR_FAILURE;
if (!(mGeneration+1).valid())
return NS_ERROR_FAILURE; // exit without changing the value of mGeneration
if (mWidth == width && mHeight == height)
return NS_OK;
@ -181,7 +181,7 @@ WebGLContext::SetDimensions(PRInt32 width, PRInt32 height)
mHeight = height;
// increment the generation number
mGeneration++;
++mGeneration;
MakeContextCurrent();

View File

@ -60,6 +60,8 @@
#include "GLContext.h"
#include "Layers.h"
#include "CheckedInt.h"
#define UNPACK_FLIP_Y_WEBGL 0x9240
#define UNPACK_PREMULTIPLY_ALPHA_WEBGL 0x9241
#define CONTEXT_LOST_WEBGL 0x9242
@ -307,7 +309,7 @@ public:
// a number that increments every time we have an event that causes
// all context resources to be lost.
PRUint32 Generation() { return mGeneration; }
PRUint32 Generation() { return mGeneration.value(); }
protected:
nsCOMPtr<nsIDOMHTMLCanvasElement> mCanvasElement;
nsHTMLCanvasElement *HTMLCanvasElement() {
@ -317,7 +319,7 @@ protected:
nsRefPtr<gl::GLContext> gl;
PRInt32 mWidth, mHeight;
PRUint32 mGeneration;
CheckedUint32 mGeneration;
PRBool mInvalidated;
@ -335,6 +337,7 @@ protected:
PRBool ValidateComparisonEnum(WebGLenum target, const char *info);
PRBool ValidateStencilOpEnum(WebGLenum action, const char *info);
PRBool ValidateFaceEnum(WebGLenum target, const char *info);
PRBool ValidateBufferUsageEnum(WebGLenum target, const char *info);
PRBool ValidateTexFormatAndType(WebGLenum format, WebGLenum type,
PRUint32 *texelSize, const char *info);
@ -705,7 +708,7 @@ public:
WebGLuint GLName() { return mName; }
const nsTArray<WebGLShader*>& AttachedShaders() const { return mAttachedShaders; }
PRBool LinkStatus() { return mLinkStatus; }
GLuint Generation() const { return mGeneration; }
PRUint32 Generation() const { return mGeneration.value(); }
void SetLinkStatus(PRBool val) { mLinkStatus = val; }
PRBool ContainsShader(WebGLShader *shader) {
@ -742,10 +745,9 @@ public:
PRBool NextGeneration()
{
GLuint nextGeneration = mGeneration + 1;
if (nextGeneration == 0)
if (!(mGeneration+1).valid())
return PR_FALSE; // must exit without changing mGeneration
mGeneration = nextGeneration;
++mGeneration;
mMapUniformLocations.Clear();
return PR_TRUE;
}
@ -770,7 +772,7 @@ protected:
PRPackedBool mLinkStatus;
nsTArray<WebGLShader*> mAttachedShaders;
nsRefPtrHashtable<nsUint32HashKey, WebGLUniformLocation> mMapUniformLocations;
GLuint mGeneration;
CheckedUint32 mGeneration;
GLint mUniformMaxNameLength;
GLint mAttribMaxNameLength;
GLint mUniformCount;
@ -864,7 +866,7 @@ public:
WebGLProgram *Program() const { return mProgram; }
GLint Location() const { return mLocation; }
GLuint ProgramGeneration() const { return mProgramGeneration; }
PRUint32 ProgramGeneration() const { return mProgramGeneration; }
// needed for our generic helpers to check nsIxxx parameters, see GetConcreteObject.
PRBool Deleted() { return PR_FALSE; }
@ -873,7 +875,7 @@ public:
NS_DECL_NSIWEBGLUNIFORMLOCATION
protected:
WebGLObjectRefPtr<WebGLProgram> mProgram;
GLuint mProgramGeneration;
PRUint32 mProgramGeneration;
GLint mLocation;
};

View File

@ -125,21 +125,6 @@ WebGLContext::Present()
return NS_ERROR_NOT_IMPLEMENTED;
}
/* long sizeInBytes (in GLenum type); */
NS_IMETHODIMP
WebGLContext::SizeInBytes(WebGLenum type, PRInt32 *retval)
{
if (type == LOCAL_GL_FLOAT) *retval = sizeof(float);
if (type == LOCAL_GL_SHORT) *retval = sizeof(short);
if (type == LOCAL_GL_UNSIGNED_SHORT) *retval = sizeof(unsigned short);
if (type == LOCAL_GL_BYTE) *retval = 1;
if (type == LOCAL_GL_UNSIGNED_BYTE) *retval = 1;
if (type == LOCAL_GL_INT) *retval = sizeof(int);
if (type == LOCAL_GL_UNSIGNED_INT) *retval = sizeof(unsigned int);
if (type == LOCAL_GL_DOUBLE) *retval = sizeof(double);
return NS_OK;
}
/* void GlActiveTexture (in GLenum texture); */
NS_IMETHODIMP
WebGLContext::ActiveTexture(WebGLenum texture)
@ -292,7 +277,7 @@ WebGLContext::BindTexture(WebGLenum target, nsIWebGLTexture *tobj)
return NS_OK;
}
GL_SAME_METHOD_4(BlendColor, BlendColor, float, float, float, float)
GL_SAME_METHOD_4(BlendColor, BlendColor, WebGLfloat, WebGLfloat, WebGLfloat, WebGLfloat)
NS_IMETHODIMP WebGLContext::BlendEquation(WebGLenum mode)
{
@ -362,6 +347,12 @@ WebGLContext::BufferData_size(WebGLenum target, WebGLsizei size, WebGLenum usage
return ErrorInvalidEnum("BufferData: invalid target");
}
if (size < 0)
return ErrorInvalidValue("bufferData: negative size");
if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
return NS_OK;
if (!boundBuffer)
return ErrorInvalidOperation("BufferData: no buffer bound!");
@ -388,6 +379,9 @@ WebGLContext::BufferData_buf(WebGLenum target, js::ArrayBuffer *wb, WebGLenum us
return ErrorInvalidEnum("BufferData: invalid target");
}
if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
return NS_OK;
if (!boundBuffer)
return ErrorInvalidOperation("BufferData: no buffer bound!");
@ -414,6 +408,9 @@ WebGLContext::BufferData_array(WebGLenum target, js::TypedArray *wa, WebGLenum u
return ErrorInvalidEnum("BufferData: invalid target");
}
if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
return NS_OK;
if (!boundBuffer)
return ErrorInvalidOperation("BufferData: no buffer bound!");
@ -449,8 +446,11 @@ WebGLContext::BufferSubData_buf(GLenum target, WebGLsizei byteOffset, js::ArrayB
if (!boundBuffer)
return ErrorInvalidOperation("BufferData: no buffer bound!");
// XXX check for overflow
if (byteOffset + wb->byteLength > boundBuffer->ByteLength())
CheckedUint32 checked_neededByteLength = CheckedUint32(byteOffset) + wb->byteLength;
if (!checked_neededByteLength.valid())
return ErrorInvalidOperation("bufferSubData: integer overflow computing the needed byte length");
if (checked_neededByteLength.value() > boundBuffer->ByteLength())
return ErrorInvalidOperation("BufferSubData: not enough data - operation requires %d bytes, but buffer only has %d bytes",
byteOffset, wb->byteLength, boundBuffer->ByteLength());
@ -479,8 +479,11 @@ WebGLContext::BufferSubData_array(WebGLenum target, WebGLsizei byteOffset, js::T
if (!boundBuffer)
return ErrorInvalidOperation("BufferData: no buffer bound!");
// XXX check for overflow
if (byteOffset + wa->byteLength > boundBuffer->ByteLength())
CheckedUint32 checked_neededByteLength = CheckedUint32(byteOffset) + wa->byteLength;
if (!checked_neededByteLength.valid())
return ErrorInvalidOperation("bufferSubData: integer overflow computing the needed byte length");
if (checked_neededByteLength.value() > boundBuffer->ByteLength())
return ErrorInvalidOperation("BufferSubData: not enough data -- operation requires %d bytes, but buffer only has %d bytes",
byteOffset, wa->byteLength, boundBuffer->ByteLength());
@ -512,15 +515,15 @@ WebGLContext::Clear(PRUint32 mask)
return NS_OK;
}
GL_SAME_METHOD_4(ClearColor, ClearColor, float, float, float, float)
GL_SAME_METHOD_4(ClearColor, ClearColor, WebGLfloat, WebGLfloat, WebGLfloat, WebGLfloat)
#ifdef USE_GLES2
GL_SAME_METHOD_1(ClearDepthf, ClearDepth, float)
GL_SAME_METHOD_1(ClearDepthf, ClearDepth, WebGLfloat)
#else
GL_SAME_METHOD_1(ClearDepth, ClearDepth, float)
GL_SAME_METHOD_1(ClearDepth, ClearDepth, WebGLfloat)
#endif
GL_SAME_METHOD_1(ClearStencil, ClearStencil, PRInt32)
GL_SAME_METHOD_1(ClearStencil, ClearStencil, WebGLint)
GL_SAME_METHOD_4(ColorMask, ColorMask, WebGLboolean, WebGLboolean, WebGLboolean, WebGLboolean)
@ -640,7 +643,16 @@ WebGLContext::CreateShader(WebGLenum type, nsIWebGLShader **retval)
return NS_OK;
}
GL_SAME_METHOD_1(CullFace, CullFace, WebGLenum)
NS_IMETHODIMP
WebGLContext::CullFace(WebGLenum face)
{
if (!ValidateFaceEnum(face, "cullFace"))
return NS_OK;
MakeContextCurrent();
gl->fCullFace(face);
return NS_OK;
}
NS_IMETHODIMP
WebGLContext::DeleteBuffer(nsIWebGLBuffer *bobj)
@ -816,9 +828,9 @@ WebGLContext::DepthFunc(WebGLenum func)
GL_SAME_METHOD_1(DepthMask, DepthMask, WebGLboolean)
#ifdef USE_GLES2
GL_SAME_METHOD_2(DepthRangef, DepthRange, float, float)
GL_SAME_METHOD_2(DepthRangef, DepthRange, WebGLfloat, WebGLfloat)
#else
GL_SAME_METHOD_2(DepthRange, DepthRange, float, float)
GL_SAME_METHOD_2(DepthRange, DepthRange, WebGLfloat, WebGLfloat)
#endif
NS_IMETHODIMP
@ -863,10 +875,12 @@ WebGLContext::DrawArrays(GLenum mode, WebGLint first, WebGLsizei count)
if (!mCurrentProgram)
return NS_OK;
if (first+count < first || first+count < count)
return ErrorInvalidOperation("DrawArrays: overflow in first+count");
CheckedInt32 checked_firstPlusCount = CheckedInt32(first) + count;
if (!ValidateBuffers(first+count))
if (!checked_firstPlusCount.valid())
return ErrorInvalidOperation("drawArrays: overflow in first+count");
if (!ValidateBuffers(checked_firstPlusCount.value()))
return ErrorInvalidOperation("DrawArrays: bound vertex attribute buffers do not have sufficient data for given first and count");
MakeContextCurrent();
@ -897,20 +911,21 @@ WebGLContext::DrawElements(WebGLenum mode, WebGLsizei count, WebGLenum type, Web
if (count < 0 || byteOffset < 0)
return ErrorInvalidValue("DrawElements: negative count or offset");
WebGLuint byteCount;
if (type == LOCAL_GL_UNSIGNED_SHORT) {
byteCount = WebGLuint(count) << 1;
if (byteCount >> 1 != WebGLuint(count))
return ErrorInvalidValue("DrawElements: overflow in byteCount");
CheckedUint32 checked_byteCount;
if (type == LOCAL_GL_UNSIGNED_SHORT) {
checked_byteCount = 2 * CheckedUint32(count);
if (byteOffset % 2 != 0)
return ErrorInvalidValue("DrawElements: invalid byteOffset for UNSIGNED_SHORT (must be a multiple of 2)");
} else if (type == LOCAL_GL_UNSIGNED_BYTE) {
byteCount = count;
checked_byteCount = count;
} else {
return ErrorInvalidEnum("DrawElements: type must be UNSIGNED_SHORT or UNSIGNED_BYTE");
}
if (!checked_byteCount.valid())
return ErrorInvalidValue("DrawElements: overflow in byteCount");
// If count is 0, there's nothing to do.
if (count == 0)
return NS_OK;
@ -923,10 +938,12 @@ WebGLContext::DrawElements(WebGLenum mode, WebGLsizei count, WebGLenum type, Web
if (!mBoundElementArrayBuffer)
return ErrorInvalidOperation("DrawElements: must have element array buffer binding");
if (byteOffset+byteCount < WebGLuint(byteOffset) || byteOffset+byteCount < byteCount)
CheckedUint32 checked_neededByteCount = checked_byteCount + byteOffset;
if (!checked_neededByteCount.valid())
return ErrorInvalidOperation("DrawElements: overflow in byteOffset+byteCount");
if (byteOffset + byteCount > mBoundElementArrayBuffer->ByteLength())
if (checked_neededByteCount.value() > mBoundElementArrayBuffer->ByteLength())
return ErrorInvalidOperation("DrawElements: bound element array buffer is too small for given count and offset");
WebGLuint maxIndex = 0;
@ -936,8 +953,13 @@ WebGLContext::DrawElements(WebGLenum mode, WebGLsizei count, WebGLenum type, Web
maxIndex = mBoundElementArrayBuffer->FindMaximum<GLubyte>(count, byteOffset);
}
// maxIndex+1 because ValidateBuffers expects the number of elements needed
if (!ValidateBuffers(maxIndex+1)) {
// maxIndex+1 because ValidateBuffers expects the number of elements needed.
// it is very important here to check tha maxIndex+1 doesn't overflow, otherwise the buffer validation is bypassed !!!
// maxIndex is a WebGLuint, ValidateBuffers takes a PRUint32, we validate maxIndex+1 as a PRUint32.
CheckedUint32 checked_neededCount = CheckedUint32(maxIndex) + 1;
if (!checked_neededCount.valid())
return ErrorInvalidOperation("drawElements: overflow in maxIndex+1");
if (!ValidateBuffers(checked_neededCount.value())) {
return ErrorInvalidOperation("DrawElements: bound vertex attribute buffers do not have sufficient "
"data for given indices from the bound element array");
}
@ -1306,14 +1328,7 @@ WebGLContext::GetParameter(PRUint32 pname, nsIVariant **retval)
break;
#define LOCAL_GL_MAX_VARYING_VECTORS 0x8dfc // not present in desktop OpenGL
// temporarily add those defs here, as they're missing from
// gfx/thebes/public/GLDefs.h
// and from
// gfx/layers/opengl/glDefs.h
// and I don't know in which of these 2 files they should go (probably we're going to
// kill one of them soon?)
#define LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125
#define LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122
case LOCAL_GL_MAX_VARYING_VECTORS:
{
#ifdef USE_GLES2
@ -2117,7 +2132,7 @@ WebGLContext::IsEnabled(WebGLenum cap, WebGLboolean *retval)
return NS_OK;
}
GL_SAME_METHOD_1(LineWidth, LineWidth, float)
GL_SAME_METHOD_1(LineWidth, LineWidth, WebGLfloat)
NS_IMETHODIMP
WebGLContext::LinkProgram(nsIWebGLProgram *pobj)
@ -2179,7 +2194,7 @@ WebGLContext::PixelStorei(WebGLenum pname, WebGLint param)
}
GL_SAME_METHOD_2(PolygonOffset, PolygonOffset, float, float)
GL_SAME_METHOD_2(PolygonOffset, PolygonOffset, WebGLfloat, WebGLfloat)
NS_IMETHODIMP
WebGLContext::ReadPixels(PRInt32 dummy)
@ -2232,16 +2247,19 @@ WebGLContext::ReadPixels_base(WebGLint x, WebGLint y, WebGLsizei width, WebGLsiz
PRUint32 packAlignment;
gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, (GLint*) &packAlignment);
PRUint32 plainRowSize = width*size;
CheckedUint32 checked_plainRowSize = CheckedUint32(width) * size;
// alignedRowSize = row size rounded up to next multiple of
// packAlignment which is a power of 2
PRUint32 alignedRowSize = (plainRowSize + packAlignment-1) &
~PRUint32(packAlignment-1);
// alignedRowSize = row size rounded up to next multiple of packAlignment
CheckedUint32 checked_alignedRowSize
= ((checked_plainRowSize + packAlignment-1) / packAlignment) * packAlignment;
PRUint32 neededByteLength = (height-1)*alignedRowSize + plainRowSize;
CheckedUint32 checked_neededByteLength
= (height-1) * checked_alignedRowSize + checked_plainRowSize;
if(neededByteLength > byteLength)
if (!checked_neededByteLength.valid())
return ErrorInvalidOperation("ReadPixels: integer overflow computing the needed buffer size");
if (checked_neededByteLength.value() > byteLength)
return ErrorInvalidOperation("ReadPixels: buffer too small");
if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, boundWidth, boundHeight)) {
@ -2277,7 +2295,14 @@ WebGLContext::ReadPixels_base(WebGLint x, WebGLint y, WebGLsizei width, WebGLsiz
GLint subrect_end_y = PR_MIN(y+height, boundHeight);
GLsizei subrect_height = subrect_end_y - subrect_y;
if (subrect_width < 0 || subrect_height < 0 ||
subrect_width > width || subrect_height)
return ErrorInvalidOperation("ReadPixels: integer overflow computing clipped rect size");
// now we know that subrect_width is in the [0..width] interval, and same for heights.
// now, same computation as above to find the size of the intermediate buffer to allocate for the subrect
// no need to check again for integer overflow here, since we already know the sizes aren't greater than before
PRUint32 subrect_plainRowSize = subrect_width * size;
PRUint32 subrect_alignedRowSize = (subrect_plainRowSize + packAlignment-1) &
~PRUint32(packAlignment-1);
@ -2286,11 +2311,13 @@ WebGLContext::ReadPixels_base(WebGLint x, WebGLint y, WebGLsizei width, WebGLsiz
// create subrect buffer, call glReadPixels, copy pixels into destination buffer, delete subrect buffer
GLubyte *subrect_data = new GLubyte[subrect_byteLength];
gl->fReadPixels(subrect_x, subrect_y, subrect_width, subrect_height, format, type, subrect_data);
// notice that this for loop terminates because we already checked that subrect_height is at most height
for (GLint y_inside_subrect = 0; y_inside_subrect < subrect_height; ++y_inside_subrect) {
GLint subrect_x_in_dest_buffer = subrect_x - x;
GLint subrect_y_in_dest_buffer = subrect_y - y;
memcpy(static_cast<GLubyte*>(data)
+ alignedRowSize * (subrect_y_in_dest_buffer + y_inside_subrect)
+ checked_alignedRowSize.value() * (subrect_y_in_dest_buffer + y_inside_subrect)
+ size * subrect_x_in_dest_buffer, // destination
subrect_data + subrect_alignedRowSize * y_inside_subrect, // source
subrect_plainRowSize); // size
@ -2353,14 +2380,19 @@ WebGLContext::ReadPixels_byteLength_old_API_deprecated(WebGLsizei width, WebGLsi
PRUint32 packAlignment;
gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, (GLint*) &packAlignment);
PRUint32 plainRowSize = width*size;
CheckedUint32 checked_plainRowSize = CheckedUint32(width) * size;
// alignedRowSize = row size rounded up to next multiple of
// packAlignment which is a power of 2
PRUint32 alignedRowSize = (plainRowSize + packAlignment-1) &
~PRUint32(packAlignment-1);
CheckedUint32 checked_alignedRowSize
= ((checked_plainRowSize + packAlignment-1) / packAlignment) * packAlignment;
*retval = (height-1)*alignedRowSize + plainRowSize;
CheckedUint32 checked_neededByteLength = (height-1)*checked_alignedRowSize + checked_plainRowSize;
if (!checked_neededByteLength.valid())
return ErrorInvalidOperation("ReadPixels: integer overflow computing the needed buffer size");
*retval = checked_neededByteLength.value();
return NS_OK;
}
@ -2395,7 +2427,7 @@ WebGLContext::RenderbufferStorage(WebGLenum target, WebGLenum internalformat, We
return NS_OK;
}
GL_SAME_METHOD_2(SampleCoverage, SampleCoverage, float, WebGLboolean)
GL_SAME_METHOD_2(SampleCoverage, SampleCoverage, WebGLfloat, WebGLboolean)
NS_IMETHODIMP
WebGLContext::Scissor(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height)
@ -3021,11 +3053,15 @@ WebGLContext::TexImage2D_base(WebGLenum target, WebGLint level, WebGLenum intern
if (!ValidateTexFormatAndType(format, type, &texelSize, "texImage2D"))
return NS_OK;
// XXX overflow!
uint32 bytesNeeded = width * height * texelSize;
CheckedUint32 checked_bytesNeeded = CheckedUint32(width) * height * texelSize;
if (!checked_bytesNeeded.valid())
return ErrorInvalidOperation("texImage2D: integer overflow computing the needed buffer size");
PRUint32 bytesNeeded = checked_bytesNeeded.value();
if (byteLength && byteLength < bytesNeeded)
return ErrorInvalidValue("TexImage2D: not enough data for operation (need %d, have %d)",
return ErrorInvalidOperation("TexImage2D: not enough data for operation (need %d, have %d)",
bytesNeeded, byteLength);
MakeContextCurrent();
@ -3154,8 +3190,13 @@ WebGLContext::TexSubImage2D_base(WebGLenum target, WebGLint level,
if (width == 0 || height == 0)
return NS_OK; // ES 2.0 says it has no effect, we better return right now
// XXX overflow!
uint32 bytesNeeded = width * height * texelSize;
CheckedUint32 checked_bytesNeeded = CheckedUint32(width) * height * texelSize;
if (!checked_bytesNeeded.valid())
return ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size");
PRUint32 bytesNeeded = checked_bytesNeeded.value();
if (byteLength < bytesNeeded)
return ErrorInvalidValue("TexSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, byteLength);

View File

@ -39,6 +39,8 @@
#include "WebGLContext.h"
#include "CheckedInt.h"
using namespace mozilla;
/*
@ -112,12 +114,18 @@ WebGLContext::ValidateBuffers(PRUint32 count)
continue;
// compute the number of bytes we actually need
WebGLuint needed = vd.byteOffset + // the base offset
vd.actualStride() * (count-1) + // to stride to the start of the last element group
vd.componentSize() * vd.size; // and the number of bytes needed for these components
CheckedUint32 checked_needed = CheckedUint32(vd.byteOffset) + // the base offset
CheckedUint32(vd.actualStride()) * (count-1) + // to stride to the start of the last element group
CheckedUint32(vd.componentSize()) * vd.size; // and the number of bytes needed for these components
if (vd.buf->ByteLength() < needed) {
LogMessage("VBO too small for bound attrib index %d: need at least %d bytes, but have only %d", i, needed, vd.buf->ByteLength());
if (!checked_needed.valid()) {
LogMessage("Integer overflow computing the size of bound vertex attrib buffer at index %d", i);
return PR_FALSE;
}
if (vd.buf->ByteLength() < checked_needed.value()) {
LogMessage("VBO too small for bound attrib index %d: need at least %d bytes, but have only %d",
i, checked_needed.value(), vd.buf->ByteLength());
return PR_FALSE;
}
}
@ -250,6 +258,19 @@ PRBool WebGLContext::ValidateFaceEnum(WebGLenum target, const char *info)
}
}
PRBool WebGLContext::ValidateBufferUsageEnum(WebGLenum target, const char *info)
{
switch (target) {
case LOCAL_GL_STREAM_DRAW:
case LOCAL_GL_STATIC_DRAW:
case LOCAL_GL_DYNAMIC_DRAW:
return PR_TRUE;
default:
ErrorInvalidEnumInfo(info);
return PR_FALSE;
}
}
PRBool WebGLContext::ValidateTexFormatAndType(WebGLenum format, WebGLenum type,
PRUint32 *texelSize, const char *info)
{

View File

@ -1581,9 +1581,9 @@ nsGenericHTMLElement::ParseTableVAlignValue(const nsAString& aString,
return aResult.ParseEnumValue(aString, kTableVAlignTable, PR_FALSE);
}
PRBool
PRBool
nsGenericHTMLElement::ParseDivAlignValue(const nsAString& aString,
nsAttrValue& aResult) const
nsAttrValue& aResult)
{
return aResult.ParseEnumValue(aString, kDivAlignTable, PR_FALSE);
}

View File

@ -253,8 +253,8 @@ public:
* @param aResult the resulting HTMLValue
* @return whether the value was parsed
*/
PRBool ParseDivAlignValue(const nsAString& aString,
nsAttrValue& aResult) const;
static PRBool ParseDivAlignValue(const nsAString& aString,
nsAttrValue& aResult);
/**
* Convert a table halign string to value (left/right/center/char/justify)

View File

@ -148,11 +148,13 @@ NS_NewHTMLNOTUSEDElement(nsINodeInfo *aNodeInfo, PRUint32 aFromParser)
}
#define HTML_TAG(_tag, _classname) NS_NewHTML##_classname##Element,
#define HTML_HTMLELEMENT_TAG(_tag) NS_NewHTMLElement,
#define HTML_OTHER(_tag) NS_NewHTMLNOTUSEDElement,
static const contentCreatorCallback sContentCreatorCallbacks[] = {
NS_NewHTMLUnknownElement,
#include "nsHTMLTagList.h"
#undef HTML_TAG
#undef HTML_HTMLELEMENT_TAG
#undef HTML_OTHER
NS_NewHTMLUnknownElement
};

View File

@ -1254,21 +1254,11 @@ nsHTMLDocument::CreateElement(const nsAString& aTagName,
nsIDOMElement** aReturn)
{
*aReturn = nsnull;
nsresult rv;
nsresult rv = nsContentUtils::CheckQName(aTagName, PR_FALSE);
if (NS_FAILED(rv))
return rv;
nsAutoString tagName(aTagName);
// if we are in quirks, allow surrounding '<' '>' for IE compat
if (mCompatMode == eCompatibility_NavQuirks &&
tagName.Length() > 2 &&
tagName.First() == '<' &&
tagName.Last() == '>') {
tagName = Substring(tagName, 1, tagName.Length() - 2);
}
rv = nsContentUtils::CheckQName(tagName, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
if (IsHTML()) {
ToLowerCase(tagName);
}

View File

@ -61,6 +61,7 @@ _TEST_FILES = test_bug1682.html \
test_bug311681.xhtml \
test_bug324378.html \
test_bug332848.xhtml \
test_bug340017.xhtml \
test_bug359657.html \
test_bug369370.html \
bug369370-popup.png \
@ -95,13 +96,13 @@ _TEST_FILES = test_bug1682.html \
test_bug481647.html \
test_bug482659.html \
test_bug486741.html \
test_bug489532.html \
test_bug497242.xhtml \
test_bug512367.html \
test_bug570375.html \
test_bug340017.xhtml \
test_bug499092.html \
bug499092.xml \
bug499092.html \
test_bug512367.html \
test_bug570376.html \
test_bug571981.html \
$(NULL)

View File

@ -0,0 +1,32 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=489532
-->
<head>
<title>Test for Bug 489532</title>
<script src="/MochiKit/packed.js"></script>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=489532">Mozilla Bug 489532</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script>
/** Test for Bug 489532 **/
try {
document.createElement("<div>");
ok(false, "Should throw.")
} catch (e) {
ok(e instanceof DOMException, "Expected DOMException.");
is(e.code, DOMException.INVALID_CHARACTER_ERR,
"Expected INVALID_CHARACTER_ERR.");
}
</script>
</pre>
</body>
</html>

View File

@ -1,16 +1,16 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=570375
https://bugzilla.mozilla.org/show_bug.cgi?id=570376
-->
<head>
<title>Test for Bug 570375</title>
<title>Test for Bug 570376</title>
<script type="application/javascript" src="/MochiKit/packed.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=570375">Mozilla Bug 570375</a>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=570376">Mozilla Bug 570376</a>
<p id="display">
<iframe id="testiframe"></iframe>
</p>
@ -19,7 +19,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=570375
<pre id="test">
<script type="application/javascript">
/** Test for Bug 570375
/** Test for Bug 570376
Don't crash loading <form><legend> with the html5 parser preffed off.
**/

View File

@ -325,6 +325,9 @@ nsSMILAnimationController::DoSample(PRBool aSkipUnchangedContainers)
mResampleNeeded = PR_FALSE;
// STEP 1: Bring model up to date
// (i) Rewind elements where necessary
// (ii) Run milestone samples
RewindElements();
DoMilestoneSamples();
// STEP 2: Sample the child time containers
@ -402,6 +405,56 @@ nsSMILAnimationController::DoSample(PRBool aSkipUnchangedContainers)
NS_ASSERTION(!mResampleNeeded, "Resample dirty flag set during sample!");
}
void
nsSMILAnimationController::RewindElements()
{
PRBool rewindNeeded = PR_FALSE;
mChildContainerTable.EnumerateEntries(RewindNeeded, &rewindNeeded);
if (!rewindNeeded)
return;
mAnimationElementTable.EnumerateEntries(RewindAnimation, nsnull);
mChildContainerTable.EnumerateEntries(ClearRewindNeeded, nsnull);
}
/*static*/ PR_CALLBACK PLDHashOperator
nsSMILAnimationController::RewindNeeded(TimeContainerPtrKey* aKey,
void* aData)
{
NS_ABORT_IF_FALSE(aData,
"Null data pointer during time container enumeration");
PRBool* rewindNeeded = static_cast<PRBool*>(aData);
nsSMILTimeContainer* container = aKey->GetKey();
if (container->NeedsRewind()) {
*rewindNeeded = PR_TRUE;
return PL_DHASH_STOP;
}
return PL_DHASH_NEXT;
}
/*static*/ PR_CALLBACK PLDHashOperator
nsSMILAnimationController::RewindAnimation(AnimationElementPtrKey* aKey,
void* aData)
{
nsISMILAnimationElement* animElem = aKey->GetKey();
nsSMILTimeContainer* timeContainer = animElem->GetTimeContainer();
if (timeContainer && timeContainer->NeedsRewind()) {
animElem->TimedElement().Rewind();
}
return PL_DHASH_NEXT;
}
/*static*/ PR_CALLBACK PLDHashOperator
nsSMILAnimationController::ClearRewindNeeded(TimeContainerPtrKey* aKey,
void* aData)
{
aKey->GetKey()->ClearNeedsRewind();
return PL_DHASH_NEXT;
}
void
nsSMILAnimationController::DoMilestoneSamples()
{
@ -542,6 +595,7 @@ nsSMILAnimationController::SampleTimeContainer(TimeContainerPtrKey* aKey,
(container->NeedsSample() || !params->mSkipUnchangedContainers)) {
container->ClearMilestones();
container->Sample();
container->MarkSeekFinished();
params->mActiveContainers->PutEntry(container);
}
@ -587,6 +641,8 @@ nsSMILAnimationController::SampleTimedElement(
nsSMILTime containerTime = timeContainer->GetCurrentTime();
NS_ABORT_IF_FALSE(!timeContainer->IsSeeking(),
"Doing a regular sample but the time container is still seeking");
aElement->TimedElement().SampleAt(containerTime);
}

View File

@ -152,11 +152,21 @@ protected:
// Sample-related callbacks and implementation helpers
virtual void DoSample();
void DoSample(PRBool aSkipUnchangedContainers);
void RewindElements();
PR_STATIC_CALLBACK(PLDHashOperator) RewindNeeded(
TimeContainerPtrKey* aKey, void* aData);
PR_STATIC_CALLBACK(PLDHashOperator) RewindAnimation(
AnimationElementPtrKey* aKey, void* aData);
PR_STATIC_CALLBACK(PLDHashOperator) ClearRewindNeeded(
TimeContainerPtrKey* aKey, void* aData);
void DoMilestoneSamples();
PR_STATIC_CALLBACK(PLDHashOperator) GetNextMilestone(
TimeContainerPtrKey* aKey, void* aData);
PR_STATIC_CALLBACK(PLDHashOperator) GetMilestoneElements(
TimeContainerPtrKey* aKey, void* aData);
PR_STATIC_CALLBACK(PLDHashOperator) SampleTimeContainer(
TimeContainerPtrKey* aKey, void* aData);
PR_STATIC_CALLBACK(PLDHashOperator) SampleAnimation(

View File

@ -74,9 +74,9 @@ nsSMILInstanceTime::nsSMILInstanceTime(const nsSMILTimeValue& aTime,
nsSMILInterval* aBaseInterval)
: mTime(aTime),
mFlags(0),
mSerial(0),
mVisited(PR_FALSE),
mChainEnd(PR_FALSE),
mFixedEndpointRefCnt(0),
mSerial(0),
mCreator(aCreator),
mBaseInterval(nsnull) // This will get set to aBaseInterval in a call to
// SetBaseInterval() at end of constructor
@ -87,7 +87,7 @@ nsSMILInstanceTime::nsSMILInstanceTime(const nsSMILTimeValue& aTime,
break;
case SOURCE_DOM:
mFlags = kClearOnReset | kFromDOM;
mFlags = kDynamic | kFromDOM;
break;
case SOURCE_SYNCBASE:
@ -95,7 +95,7 @@ nsSMILInstanceTime::nsSMILInstanceTime(const nsSMILTimeValue& aTime,
break;
case SOURCE_EVENT:
mFlags = kClearOnReset;
mFlags = kDynamic;
break;
}
@ -106,6 +106,9 @@ nsSMILInstanceTime::~nsSMILInstanceTime()
{
NS_ABORT_IF_FALSE(!mBaseInterval && !mCreator,
"Destroying instance time without first calling Unlink()");
NS_ABORT_IF_FALSE(mFixedEndpointRefCnt == 0,
"Destroying instance time that is still used as the fixed endpoint of an "
"interval");
}
void
@ -129,12 +132,9 @@ nsSMILInstanceTime::HandleChangedInterval(
"Got call to HandleChangedInterval on an independent instance time.");
NS_ABORT_IF_FALSE(mCreator, "Base interval is set but creator is not.");
if (mVisited || mChainEnd) {
// We're breaking the cycle here but we need to ensure that if we later
// receive a change notice in a different context (e.g. due to a time
// container change) that we don't end up following the chain further and so
// we set a flag to that effect.
mChainEnd = PR_TRUE;
if (mVisited) {
// Break the cycle here
Unlink();
return;
}
@ -152,20 +152,63 @@ void
nsSMILInstanceTime::HandleDeletedInterval()
{
NS_ABORT_IF_FALSE(mBaseInterval,
"Got call to HandleDeletedInterval on an independent instance time.");
NS_ABORT_IF_FALSE(mCreator, "Base interval is set but creator is not.");
"Got call to HandleDeletedInterval on an independent instance time");
NS_ABORT_IF_FALSE(mCreator, "Base interval is set but creator is not");
mBaseInterval = nsnull;
mFlags &= ~kMayUpdate; // Can't update without a base interval
nsRefPtr<nsSMILInstanceTime> deathGrip(this);
mCreator->HandleDeletedInstanceTime(*this);
mCreator = nsnull;
}
PRBool
nsSMILInstanceTime::IsDependent(const nsSMILInstanceTime& aOther) const
void
nsSMILInstanceTime::HandleFilteredInterval()
{
if (mVisited || mChainEnd)
NS_ABORT_IF_FALSE(mBaseInterval,
"Got call to HandleFilteredInterval on an independent instance time");
mBaseInterval = nsnull;
mFlags &= ~kMayUpdate; // Can't update without a base interval
mCreator = nsnull;
}
PRBool
nsSMILInstanceTime::ShouldPreserve() const
{
return mFixedEndpointRefCnt > 0 || (mFlags & kWasDynamicEndpoint);
}
void
nsSMILInstanceTime::UnmarkShouldPreserve()
{
mFlags &= ~kWasDynamicEndpoint;
}
void
nsSMILInstanceTime::AddRefFixedEndpoint()
{
NS_ABORT_IF_FALSE(mFixedEndpointRefCnt < PR_UINT16_MAX,
"Fixed endpoint reference count upper limit reached");
++mFixedEndpointRefCnt;
mFlags &= ~kMayUpdate; // Once fixed, always fixed
}
void
nsSMILInstanceTime::ReleaseFixedEndpoint()
{
NS_ABORT_IF_FALSE(mFixedEndpointRefCnt > 0, "Duplicate release");
--mFixedEndpointRefCnt;
if (mFixedEndpointRefCnt == 0 && IsDynamic()) {
mFlags |= kWasDynamicEndpoint;
}
}
PRBool
nsSMILInstanceTime::IsDependentOn(const nsSMILInstanceTime& aOther) const
{
if (mVisited)
return PR_FALSE;
const nsSMILInstanceTime* myBaseTime = GetBaseTime();
@ -177,7 +220,7 @@ nsSMILInstanceTime::IsDependent(const nsSMILInstanceTime& aOther) const
// mVisited is mutable
AutoBoolSetter setVisited(const_cast<nsSMILInstanceTime*>(this)->mVisited);
return myBaseTime->IsDependent(aOther);
return myBaseTime->IsDependentOn(aOther);
}
void

View File

@ -92,24 +92,31 @@ public:
PRBool aBeginObjectChanged,
PRBool aEndObjectChanged);
void HandleDeletedInterval();
void HandleFilteredInterval();
const nsSMILTimeValue& Time() const { return mTime; }
const nsSMILTimeValueSpec* GetCreator() const { return mCreator; }
PRBool ClearOnReset() const { return !!(mFlags & kClearOnReset); }
PRBool MayUpdate() const { return !!(mFlags & kMayUpdate); }
PRBool IsDynamic() const { return !!(mFlags & kDynamic); }
PRBool IsFixedTime() const { return !(mFlags & kMayUpdate); }
PRBool FromDOM() const { return !!(mFlags & kFromDOM); }
void MarkNoLongerUpdating() { mFlags &= ~kMayUpdate; }
PRBool ShouldPreserve() const;
void UnmarkShouldPreserve();
void AddRefFixedEndpoint();
void ReleaseFixedEndpoint();
void DependentUpdate(const nsSMILTimeValue& aNewTime)
{
NS_ABORT_IF_FALSE(MayUpdate(),
NS_ABORT_IF_FALSE(!IsFixedTime(),
"Updating an instance time that is not expected to be updated");
mTime = aNewTime;
}
PRBool IsDependent(const nsSMILInstanceTime& aOther) const;
PRBool IsDependent() const { return !!mBaseInterval; }
PRBool IsDependentOn(const nsSMILInstanceTime& aOther) const;
const nsSMILInterval* GetBaseInterval() const { return mBaseInterval; }
PRBool SameTimeAndBase(const nsSMILInstanceTime& aOther) const
{
@ -131,15 +138,16 @@ protected:
// Internal flags used to represent the behaviour of different instance times
enum {
// Indicates if this instance time should be removed when the owning timed
// element is reset. True for events and DOM calls.
kClearOnReset = 1,
// Indicates that this instance time was generated by an event or a DOM
// call. Such instance times require special handling when (i) the owning
// element is reset, and (ii) when a backwards seek is performed and the
// timing model is reconstructed.
kDynamic = 1,
// Indicates that this instance time is referred to by an
// nsSMILTimeValueSpec and as such may be updated. Such instance time should
// not be filtered out by the nsSMILTimedElement even if they appear to be
// in the past as they may be updated to a future time. Initially set for
// syncbase-generated times until they are frozen.
// in the past as they may be updated to a future time.
kMayUpdate = 2,
// Indicates that this instance time was generated from the DOM as opposed
@ -147,17 +155,33 @@ protected:
// reset we should clear all the instance times that have been generated by
// that attribute (and hence an nsSMILTimeValueSpec), but not those from the
// DOM.
kFromDOM = 4
kFromDOM = 4,
// Indicates that this instance time was used as the endpoint of an interval
// that has been filtered or removed. However, since it is a dynamic time it
// should be preserved and not filtered.
kWasDynamicEndpoint = 8
};
PRUint8 mFlags; // Combination of kClearOnReset, kMayUpdate, etc.
PRUint8 mFlags; // Combination of kDynamic, kMayUpdate, etc.
PRPackedBool mVisited; // (mutable) Cycle tracking
// Additional reference count to determine if this instance time is currently
// used as a fixed endpoint in any intervals. Instance times that are used in
// this way should not be removed when the owning nsSMILTimedElement removes
// instance times in response to a restart or in an attempt to free up memory
// by filtering out old instance times.
//
// Instance times are only shared in a few cases, namely:
// a) early ends,
// b) zero-duration intervals, and
// c) momentarily whilst establishing new intervals and updating the current
// interval
// Hence the limited range of a PRUint16 should be more than adequate.
PRUint16 mFixedEndpointRefCnt;
PRUint32 mSerial; // A serial number used by the containing class to
// specify the sort order for instance times with the
// same mTime.
PRPackedBool mVisited; // (mutable) Cycle tracking
PRPackedBool mChainEnd; // Flag to indicate that this instance time is part
// of some cyclic dependency and that in order to
// avoid infinite recursion the cycle should not be
// followed any further than this point.
nsSMILTimeValueSpec* mCreator; // The nsSMILTimeValueSpec object that created
// us. (currently only needed for syncbase

View File

@ -39,6 +39,8 @@
nsSMILInterval::nsSMILInterval()
:
mBeginFixed(PR_FALSE),
mEndFixed(PR_FALSE),
mBeginObjectChanged(PR_FALSE),
mEndObjectChanged(PR_FALSE)
{
@ -48,19 +50,28 @@ nsSMILInterval::nsSMILInterval(const nsSMILInterval& aOther)
:
mBegin(aOther.mBegin),
mEnd(aOther.mEnd),
mBeginFixed(PR_FALSE),
mEndFixed(PR_FALSE),
mBeginObjectChanged(PR_FALSE),
mEndObjectChanged(PR_FALSE)
{
NS_ABORT_IF_FALSE(aOther.mDependentTimes.IsEmpty(),
"Attempting to copy-construct an interval with dependent times, "
"this will lead to instance times being shared between intervals.");
// For the time being we don't allow intervals with fixed endpoints to be
// copied since we only ever copy-construct to establish a new current
// interval. If we ever need to copy historical intervals we may need to move
// the ReleaseFixedEndpoint calls from Unlink to the dtor.
NS_ABORT_IF_FALSE(!aOther.mBeginFixed && !aOther.mEndFixed,
"Attempting to copy-construct an interval with fixed endpoints");
}
nsSMILInterval::~nsSMILInterval()
{
NS_ABORT_IF_FALSE(mDependentTimes.IsEmpty(),
"Destroying interval without disassociating dependent instance times. "
"NotifyDeleting was not called.");
"Unlink was not called");
}
void
@ -76,12 +87,24 @@ nsSMILInterval::NotifyChanged(const nsSMILTimeContainer* aContainer)
}
void
nsSMILInterval::NotifyDeleting()
nsSMILInterval::Unlink(PRBool aFiltered)
{
for (PRInt32 i = mDependentTimes.Length() - 1; i >= 0; --i) {
mDependentTimes[i]->HandleDeletedInterval();
if (aFiltered) {
mDependentTimes[i]->HandleFilteredInterval();
} else {
mDependentTimes[i]->HandleDeletedInterval();
}
}
mDependentTimes.Clear();
if (mBegin && mBeginFixed) {
mBegin->ReleaseFixedEndpoint();
}
mBegin = nsnull;
if (mEnd && mEndFixed) {
mEnd->ReleaseFixedEndpoint();
}
mEnd = nsnull;
}
nsSMILInstanceTime*
@ -104,7 +127,9 @@ void
nsSMILInterval::SetBegin(nsSMILInstanceTime& aBegin)
{
NS_ABORT_IF_FALSE(aBegin.Time().IsResolved(),
"Attempting to set unresolved begin time on interval.");
"Attempting to set unresolved begin time on interval");
NS_ABORT_IF_FALSE(!mBeginFixed,
"Attempting to set begin time but the begin point is fixed");
if (mBegin == &aBegin)
return;
@ -116,6 +141,9 @@ nsSMILInterval::SetBegin(nsSMILInstanceTime& aBegin)
void
nsSMILInterval::SetEnd(nsSMILInstanceTime& aEnd)
{
NS_ABORT_IF_FALSE(!mEndFixed,
"Attempting to set end time but the end point is fixed");
if (mEnd == &aEnd)
return;
@ -123,6 +151,28 @@ nsSMILInterval::SetEnd(nsSMILInstanceTime& aEnd)
mEndObjectChanged = PR_TRUE;
}
void
nsSMILInterval::FixBegin()
{
NS_ABORT_IF_FALSE(mBegin && mEnd,
"Fixing begin point on un-initialized interval");
NS_ABORT_IF_FALSE(!mBeginFixed, "Duplicate calls to FixBegin()");
mBeginFixed = PR_TRUE;
mBegin->AddRefFixedEndpoint();
}
void
nsSMILInterval::FixEnd()
{
NS_ABORT_IF_FALSE(mBegin && mEnd,
"Fixing end point on un-initialized interval");
NS_ABORT_IF_FALSE(mBeginFixed,
"Fixing the end of an interval without a fixed begin");
NS_ABORT_IF_FALSE(!mEndFixed, "Duplicate calls to FixEnd()");
mEndFixed = PR_TRUE;
mEnd->AddRefFixedEndpoint();
}
void
nsSMILInterval::AddDependentTime(nsSMILInstanceTime& aTime)
{
@ -142,3 +192,19 @@ nsSMILInterval::RemoveDependentTime(const nsSMILInstanceTime& aTime)
mDependentTimes.RemoveElementSorted(&aTime);
NS_ABORT_IF_FALSE(found, "Couldn't find instance time to delete.");
}
PRBool
nsSMILInterval::IsDependencyChainLink() const
{
if (!mBegin || !mEnd)
return PR_FALSE; // Not yet initialised so it can't be part of a chain
if (mDependentTimes.IsEmpty())
return PR_FALSE; // No dependents, chain end
// So we have dependents, but we're still only a link in the chain (as opposed
// to the end of the chain) if one of our endpoints is dependent on an
// interval other than ourselves.
return (mBegin->IsDependent() && mBegin->GetBaseInterval() != this) ||
(mEnd->IsDependent() && mEnd->GetBaseInterval() != this);
}

View File

@ -57,7 +57,7 @@ public:
nsSMILInterval(const nsSMILInterval& aOther);
~nsSMILInterval();
void NotifyChanged(const nsSMILTimeContainer* aContainer);
void NotifyDeleting();
void Unlink(PRBool aFiltered = PR_FALSE);
const nsSMILInstanceTime* Begin() const
{
@ -83,37 +83,15 @@ public:
SetEnd(aEnd);
}
void FreezeBegin()
{
NS_ABORT_IF_FALSE(mBegin && mEnd,
"Freezing Begin() on un-initialized instance time");
mBegin->MarkNoLongerUpdating();
}
void FreezeEnd()
{
NS_ABORT_IF_FALSE(mBegin && mEnd,
"Freezing End() on un-initialized instance time");
NS_ABORT_IF_FALSE(!mBegin->MayUpdate(),
"Freezing the end of an interval without a fixed begin");
mEnd->MarkNoLongerUpdating();
}
// XXX Backwards seeking support (bug 492458)
void Unfreeze()
{
// XXX
UnfreezeEnd();
}
void UnfreezeEnd()
{
// XXX
}
void FixBegin();
void FixEnd();
void AddDependentTime(nsSMILInstanceTime& aTime);
void RemoveDependentTime(const nsSMILInstanceTime& aTime);
// Cue for assessing if this interval can be filtered
PRBool IsDependencyChainLink() const;
private:
nsRefPtr<nsSMILInstanceTime> mBegin;
nsRefPtr<nsSMILInstanceTime> mEnd;
@ -123,6 +101,18 @@ private:
// nsSMILInstanceTimes to notify when this interval is changed or deleted.
InstanceTimeList mDependentTimes;
// Indicates if the end points of the interval are fixed or not.
//
// Note that this is not the same as having an end point whose TIME is fixed
// (i.e. nsSMILInstanceTime::IsFixed() returns PR_TRUE). This is because it is
// possible to have an end point with a fixed TIME and yet still update the
// end point to refer to a different nsSMILInstanceTime object.
//
// However, if mBegin/EndFixed is PR_TRUE, then BOTH the nsSMILInstanceTime
// OBJECT returned for that end point and its TIME value will not change.
PRPackedBool mBeginFixed;
PRPackedBool mEndFixed;
// When change notifications are passed around the timing model we try to
// filter out all changes where there is no observable difference to an
// instance time. Changes that may produce an observable difference are:

View File

@ -46,6 +46,8 @@ nsSMILTimeContainer::nsSMILTimeContainer()
mParentOffset(0L),
mPauseStart(0L),
mNeedsPauseSample(PR_FALSE),
mNeedsRewind(PR_FALSE),
mIsSeeking(PR_FALSE),
mPauseState(PAUSE_BEGIN)
{
}
@ -145,6 +147,10 @@ nsSMILTimeContainer::GetCurrentTime() const
void
nsSMILTimeContainer::SetCurrentTime(nsSMILTime aSeekTo)
{
// SVG 1.1 doesn't specify what to do for negative times so we adopt SVGT1.2's
// behaviour of clamping negative times to 0.
aSeekTo = PR_MAX(0, aSeekTo);
// The following behaviour is consistent with:
// http://www.w3.org/2003/01/REC-SVG11-20030114-errata
// #getCurrentTime_setCurrentTime_undefined_before_document_timeline_begin
@ -152,12 +158,19 @@ nsSMILTimeContainer::SetCurrentTime(nsSMILTime aSeekTo)
// has begun we should still adjust the offset.
nsSMILTime parentTime = GetParentTime();
mParentOffset = parentTime - aSeekTo;
mIsSeeking = PR_TRUE;
if (IsPaused()) {
mNeedsPauseSample = PR_TRUE;
mPauseStart = parentTime;
}
if (aSeekTo < mCurrentTime) {
// Backwards seek
mNeedsRewind = PR_TRUE;
ClearMilestones();
}
// Force an update to the current time in case we get a call to GetCurrentTime
// before another call to Sample().
UpdateCurrentTime();

View File

@ -170,6 +170,21 @@ public:
*/
PRBool NeedsSample() const { return !mPauseState || mNeedsPauseSample; }
/*
* Indicates if the elements of this time container need to be rewound.
* This occurs during a backwards seek.
*/
PRBool NeedsRewind() const { return mNeedsRewind; }
void ClearNeedsRewind() { mNeedsRewind = PR_FALSE; }
/*
* Indicates the time container is currently processing a SetCurrentTime
* request and appropriate seek behaviour should be applied by child elements
* (e.g. not firing time events).
*/
PRBool IsSeeking() const { return mIsSeeking; }
void MarkSeekFinished() { mIsSeeking = PR_FALSE; }
/*
* Sets the parent time container.
*
@ -279,6 +294,9 @@ protected:
// Whether or not a pause sample is required
PRPackedBool mNeedsPauseSample;
PRPackedBool mNeedsRewind; // Backwards seek performed
PRPackedBool mIsSeeking; // Currently in the middle of a seek operation
// A bitfield of the pause state for all pause requests
PRUint32 mPauseState;

View File

@ -160,8 +160,8 @@ nsSMILTimeValueSpec::HandleChangedInstanceTime(
PRBool aObjectChanged)
{
// If the instance time is fixed (e.g. because it's being used as the begin
// time of an active interval) we just ignore the change.
if (!aInstanceTimeToUpdate.MayUpdate())
// time of an active or postactive interval) we just ignore the change.
if (aInstanceTimeToUpdate.IsFixedTime())
return;
nsSMILTimeValue updatedTime =

View File

@ -90,6 +90,29 @@ nsSMILTimedElement::InstanceTimeComparator::LessThan(
return cmp == 0 ? aElem1->Serial() < aElem2->Serial() : cmp < 0;
}
//----------------------------------------------------------------------
// Templated helper functions
// Selectively remove elements from an array of type
// nsTArray<nsRefPtr<nsSMILInstanceTime> > with O(n) performance.
template <class TestFunctor>
void
nsSMILTimedElement::RemoveInstanceTimes(InstanceTimeList& aArray,
TestFunctor& aTest)
{
InstanceTimeList newArray;
for (PRUint32 i = 0; i < aArray.Length(); ++i) {
nsSMILInstanceTime* item = aArray[i].get();
if (aTest(item, i)) {
item->Unlink();
} else {
newArray.AppendElement(item);
}
}
aArray.Clear();
aArray.SwapElements(newArray);
}
//----------------------------------------------------------------------
// Static members
@ -108,6 +131,12 @@ nsAttrValue::EnumTable nsSMILTimedElement::sRestartModeTable[] = {
const nsSMILMilestone nsSMILTimedElement::sMaxMilestone(LL_MAXINT, PR_FALSE);
// The thresholds at which point we start filtering intervals and instance times
// indiscriminately.
// See FilterIntervals and FilterInstanceTimes.
const PRUint8 nsSMILTimedElement::sMaxNumIntervals = 20;
const PRUint8 nsSMILTimedElement::sMaxNumInstanceTimes = 100;
//----------------------------------------------------------------------
// Ctor, dtor
@ -122,7 +151,8 @@ nsSMILTimedElement::nsSMILTimedElement()
mClient(nsnull),
mCurrentInterval(nsnull),
mPrevRegisteredMilestone(sMaxMilestone),
mElementState(STATE_STARTUP)
mElementState(STATE_STARTUP),
mSeekState(SEEK_NOT_SEEKING)
{
mSimpleDur.SetIndefinite();
mMin.SetMillis(0L);
@ -149,12 +179,12 @@ nsSMILTimedElement::~nsSMILTimedElement()
// (We shouldn't get any callbacks from this because all our instance times
// are now disassociated with any intervals)
if (mCurrentInterval) {
mCurrentInterval->NotifyDeleting();
mCurrentInterval->Unlink();
mCurrentInterval = nsnull;
}
for (PRInt32 i = mOldIntervals.Length() - 1; i >= 0; --i) {
mOldIntervals[i]->NotifyDeleting();
mOldIntervals[i]->Unlink();
}
mOldIntervals.Clear();
}
@ -332,22 +362,33 @@ nsSMILTimedElement::RemoveInstanceTime(nsSMILInstanceTime* aInstanceTime,
UpdateCurrentInterval();
}
namespace
{
class RemoveByCreator
{
public:
RemoveByCreator(const nsSMILTimeValueSpec* aCreator) : mCreator(aCreator)
{ }
PRBool operator()(nsSMILInstanceTime* aInstanceTime, PRUint32 /*aIndex*/)
{
return aInstanceTime->GetCreator() == mCreator;
}
private:
const nsSMILTimeValueSpec* mCreator;
};
}
void
nsSMILTimedElement::RemoveInstanceTimesForCreator(
const nsSMILTimeValueSpec* aCreator, PRBool aIsBegin)
{
NS_ABORT_IF_FALSE(aCreator, "Creator not set");
InstanceTimeList& instances = aIsBegin ? mBeginInstances : mEndInstances;
PRInt32 count = instances.Length();
for (PRInt32 i = count - 1; i >= 0; --i) {
nsSMILInstanceTime* instance = instances[i].get();
NS_ABORT_IF_FALSE(instance, "NULL instance in instances array");
if (instance->GetCreator() == aCreator) {
instance->Unlink();
instances.RemoveElementAt(i);
}
}
InstanceTimeList& instances = aIsBegin ? mBeginInstances : mEndInstances;
RemoveByCreator removeByCreator(aCreator);
RemoveInstanceTimes(instances, removeByCreator);
UpdateCurrentInterval();
}
@ -416,6 +457,16 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, PRBool aEndOnly)
"Got a regular sample during startup state, expected an end sample"
" instead");
PRBool finishedSeek = PR_FALSE;
if (GetTimeContainer()->IsSeeking() && mSeekState == SEEK_NOT_SEEKING) {
mSeekState = mElementState == STATE_ACTIVE ?
SEEK_FORWARD_FROM_ACTIVE :
SEEK_FORWARD_FROM_INACTIVE;
} else if (mSeekState != SEEK_NOT_SEEKING &&
!GetTimeContainer()->IsSeeking()) {
finishedSeek = PR_TRUE;
}
PRBool stateChanged;
nsSMILTimeValue sampleTime(aContainerTime);
@ -445,12 +496,7 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, PRBool aEndOnly)
stateChanged = PR_TRUE;
if (mElementState == STATE_WAITING) {
mCurrentInterval = new nsSMILInterval(firstInterval);
if (!mCurrentInterval) {
NS_WARNING("Failed to allocate memory for new interval");
mElementState = STATE_POSTACTIVE;
} else {
NotifyNewInterval();
}
NotifyNewInterval();
}
}
break;
@ -459,7 +505,7 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, PRBool aEndOnly)
{
if (mCurrentInterval->Begin()->Time() <= sampleTime) {
mElementState = STATE_ACTIVE;
mCurrentInterval->FreezeBegin();
mCurrentInterval->FixBegin();
if (HasPlayed()) {
Reset(); // Apply restart behaviour
}
@ -483,14 +529,7 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, PRBool aEndOnly)
case STATE_ACTIVE:
{
// Only apply an early end if we're not already ending.
if (mCurrentInterval->End()->Time() > sampleTime) {
nsSMILInstanceTime* earlyEnd = CheckForEarlyEnd(sampleTime);
if (earlyEnd) {
mCurrentInterval->SetEnd(*earlyEnd);
NotifyChangedInterval();
}
}
ApplyEarlyEnd(sampleTime);
if (mCurrentInterval->End()->Time() <= sampleTime) {
nsSMILInterval newInterval;
@ -501,22 +540,20 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, PRBool aEndOnly)
if (mClient) {
mClient->Inactivate(mFillMode == FILL_FREEZE);
}
mCurrentInterval->FreezeEnd();
mCurrentInterval->FixEnd();
mOldIntervals.AppendElement(mCurrentInterval.forget());
// We must update mOldIntervals before calling SampleFillValue
SampleFillValue();
if (mElementState == STATE_WAITING) {
mCurrentInterval = new nsSMILInterval(newInterval);
if (!mCurrentInterval) {
NS_WARNING("Failed to allocate memory for new interval");
mElementState = STATE_POSTACTIVE;
} else {
NotifyNewInterval();
}
NotifyNewInterval();
}
FilterHistory();
stateChanged = PR_TRUE;
} else {
nsSMILTime beginTime = mCurrentInterval->Begin()->Time().GetMillis();
NS_ASSERTION(aContainerTime >= beginTime,
"Sample time should not precede current interval");
nsSMILTime activeTime = aContainerTime - beginTime;
SampleSimpleTime(activeTime);
}
@ -536,6 +573,9 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, PRBool aEndOnly)
} while (stateChanged && (!aEndOnly || (mElementState != STATE_WAITING &&
mElementState != STATE_POSTACTIVE)));
if (finishedSeek) {
DoPostSeek();
}
RegisterMilestone();
}
@ -553,34 +593,46 @@ nsSMILTimedElement::HandleContainerTimeChange()
}
void
nsSMILTimedElement::Reset()
nsSMILTimedElement::Rewind()
{
// SMIL 3.0 section 5.4.3, 'Resetting element state':
// Any instance times associated with past Event-values, Repeat-values,
// Accesskey-values or added via DOM method calls are removed from the
// dependent begin and end instance times lists. In effect, all events and
// DOM methods calls in the past are cleared. This does not apply to an
// instance time that defines the begin of the current interval.
PRInt32 count = mBeginInstances.Length();
for (PRInt32 i = count - 1; i >= 0; --i) {
nsSMILInstanceTime* instance = mBeginInstances[i].get();
NS_ABORT_IF_FALSE(instance, "NULL instance in begin instances array");
if (instance->ClearOnReset() &&
(!mCurrentInterval || instance != mCurrentInterval->Begin())) {
instance->Unlink();
mBeginInstances.RemoveElementAt(i);
}
NS_ABORT_IF_FALSE(mAnimationElement,
"Got rewind request before being attached to an animation element");
NS_ABORT_IF_FALSE(mSeekState == SEEK_NOT_SEEKING,
"Got rewind request whilst already seeking");
mSeekState = mElementState == STATE_ACTIVE ?
SEEK_BACKWARD_FROM_ACTIVE :
SEEK_BACKWARD_FROM_INACTIVE;
// Set the STARTUP state first so that if we get any callbacks we won't waste
// time recalculating the current interval
mElementState = STATE_STARTUP;
// Clear the intervals and instance times except those instance times we can't
// regenerate (DOM calls etc.)
RewindTiming();
UnsetBeginSpec();
UnsetEndSpec();
if (mClient) {
mClient->Inactivate(PR_FALSE);
}
count = mEndInstances.Length();
for (PRInt32 j = count - 1; j >= 0; --j) {
nsSMILInstanceTime* instance = mEndInstances[j].get();
NS_ABORT_IF_FALSE(instance, "NULL instance in end instances array");
if (instance->ClearOnReset()) {
instance->Unlink();
mEndInstances.RemoveElementAt(j);
}
if (mAnimationElement->HasAnimAttr(nsGkAtoms::begin)) {
nsAutoString attValue;
mAnimationElement->GetAnimAttr(nsGkAtoms::begin, attValue);
SetBeginSpec(attValue, &mAnimationElement->Content());
}
if (mAnimationElement->HasAnimAttr(nsGkAtoms::end)) {
nsAutoString attValue;
mAnimationElement->GetAnimAttr(nsGkAtoms::end, attValue);
SetEndSpec(attValue, &mAnimationElement->Content());
}
mPrevRegisteredMilestone = sMaxMilestone;
RegisterMilestone();
}
PRBool
@ -922,16 +974,15 @@ nsSMILTimedElement::AddDependent(nsSMILTimeValueSpec& aDependent)
"nsSMILTimeValueSpec is already registered as a dependency");
mTimeDependents.PutEntry(&aDependent);
// Add old and current intervals
// Add current interval. We could add historical intervals too but that would
// cause unpredictable results since some intervals may have been filtered.
// SMIL doesn't say what to do here so for simplicity and consistency we
// simply add the current interval if there is one.
//
// It's not necessary to call SyncPauseTime since we're dealing with
// historical instance times not newly added ones.
nsSMILTimeContainer* container = GetTimeContainer();
for (PRUint32 i = 0; i < mOldIntervals.Length(); ++i) {
aDependent.HandleNewInterval(*mOldIntervals[i], container);
}
if (mCurrentInterval) {
aDependent.HandleNewInterval(*mCurrentInterval, container);
aDependent.HandleNewInterval(*mCurrentInterval, GetTimeContainer());
}
}
@ -950,7 +1001,7 @@ nsSMILTimedElement::IsTimeDependent(const nsSMILTimedElement& aOther) const
if (!thisBegin || !otherBegin)
return PR_FALSE;
return thisBegin->IsDependent(*otherBegin);
return thisBegin->IsDependentOn(*otherBegin);
}
void
@ -1054,6 +1105,18 @@ nsSMILTimedElement::SetBeginOrEndSpec(const nsAString& aSpec,
return rv;
}
namespace
{
class RemoveNonDOM
{
public:
PRBool operator()(nsSMILInstanceTime* aInstanceTime, PRUint32 /*aIndex*/)
{
return !aInstanceTime->FromDOM();
}
};
}
void
nsSMILTimedElement::ClearBeginOrEndSpecs(PRBool aIsBegin)
{
@ -1063,14 +1126,276 @@ nsSMILTimedElement::ClearBeginOrEndSpecs(PRBool aIsBegin)
// Remove only those instance times generated by the attribute, not those from
// DOM calls.
InstanceTimeList& instances = aIsBegin ? mBeginInstances : mEndInstances;
PRInt32 count = instances.Length();
for (PRInt32 i = count - 1; i >= 0; --i) {
nsSMILInstanceTime* instance = instances[i].get();
NS_ABORT_IF_FALSE(instance, "NULL instance in instances array");
if (!instance->FromDOM()) {
instance->Unlink();
instances.RemoveElementAt(i);
RemoveNonDOM removeNonDOM;
RemoveInstanceTimes(instances, removeNonDOM);
}
void
nsSMILTimedElement::RewindTiming()
{
RewindInstanceTimes(mBeginInstances);
RewindInstanceTimes(mEndInstances);
if (mCurrentInterval) {
mCurrentInterval->Unlink();
mCurrentInterval = nsnull;
}
for (PRInt32 i = mOldIntervals.Length() - 1; i >= 0; --i) {
mOldIntervals[i]->Unlink();
}
mOldIntervals.Clear();
}
namespace
{
class RemoveNonDynamic
{
public:
PRBool operator()(nsSMILInstanceTime* aInstanceTime, PRUint32 /*aIndex*/)
{
// Generally dynamically-generated instance times (DOM calls, event-based
// times) are not associated with their creator nsSMILTimeValueSpec.
// If that ever changes though we'll need to make sure to disassociate
// them here otherwise they'll get removed when we clear the set of
// nsSMILTimeValueSpecs later on.
NS_ABORT_IF_FALSE(!aInstanceTime->IsDynamic() ||
!aInstanceTime->GetCreator(),
"Instance time retained during rewind needs to be unlinked");
return !aInstanceTime->IsDynamic();
}
};
}
void
nsSMILTimedElement::RewindInstanceTimes(InstanceTimeList& aList)
{
RemoveNonDynamic removeNonDynamic;
RemoveInstanceTimes(aList, removeNonDynamic);
}
void
nsSMILTimedElement::ApplyEarlyEnd(const nsSMILTimeValue& aSampleTime)
{
// This should only be called within DoSampleAt as a helper function
NS_ABORT_IF_FALSE(mElementState == STATE_ACTIVE,
"Unexpected state to try to apply an early end");
// Only apply an early end if we're not already ending.
if (mCurrentInterval->End()->Time() > aSampleTime) {
nsSMILInstanceTime* earlyEnd = CheckForEarlyEnd(aSampleTime);
if (earlyEnd) {
if (earlyEnd->IsDependent()) {
// Generate a new instance time for the early end since the
// existing instance time is part of some dependency chain that we
// don't want to participate in.
nsRefPtr<nsSMILInstanceTime> newEarlyEnd =
new nsSMILInstanceTime(earlyEnd->Time());
mCurrentInterval->SetEnd(*newEarlyEnd);
} else {
mCurrentInterval->SetEnd(*earlyEnd);
}
NotifyChangedInterval();
}
}
}
namespace
{
class RemoveReset
{
public:
RemoveReset(const nsSMILInstanceTime* aCurrentIntervalBegin)
: mCurrentIntervalBegin(aCurrentIntervalBegin) { }
PRBool operator()(nsSMILInstanceTime* aInstanceTime, PRUint32 /*aIndex*/)
{
// SMIL 3.0 section 5.4.3, 'Resetting element state':
// Any instance times associated with past Event-values, Repeat-values,
// Accesskey-values or added via DOM method calls are removed from the
// dependent begin and end instance times lists. In effect, all events
// and DOM methods calls in the past are cleared. This does not apply to
// an instance time that defines the begin of the current interval.
return aInstanceTime->IsDynamic() &&
!aInstanceTime->ShouldPreserve() &&
(!mCurrentIntervalBegin || aInstanceTime != mCurrentIntervalBegin);
}
private:
const nsSMILInstanceTime* mCurrentIntervalBegin;
};
}
void
nsSMILTimedElement::Reset()
{
RemoveReset resetBegin(mCurrentInterval ? mCurrentInterval->Begin() : nsnull);
RemoveInstanceTimes(mBeginInstances, resetBegin);
RemoveReset resetEnd(nsnull);
RemoveInstanceTimes(mEndInstances, resetEnd);
}
void
nsSMILTimedElement::DoPostSeek()
{
// XXX When implementing TimeEvents we'll need to compare mElementState with
// mSeekState and dispatch events as follows:
// ACTIVE->INACTIVE: End event
// INACTIVE->ACTIVE: Begin event
// ACTIVE->ACTIVE: Nothing (even if they're different intervals)
// INACTIVE->INACTIVE: Nothing (even if we've skipped intervals)
// Finish backwards seek
if (mSeekState == SEEK_BACKWARD_FROM_INACTIVE ||
mSeekState == SEEK_BACKWARD_FROM_ACTIVE) {
// Previously some dynamic instance times may have been marked to be
// preserved because they were endpoints of an historic interval (which may
// or may not have been filtered). Now that we've finished a seek we should
// clear that flag for those instance times whose intervals are no longer
// historic.
UnpreserveInstanceTimes(mBeginInstances);
UnpreserveInstanceTimes(mEndInstances);
// Now that the times have been unmarked perform a reset. This might seem
// counter-intuitive when we're only doing a seek within an interval but
// SMIL seems to require this. SMIL 3.0, 'Hyperlinks and timing':
// Resolved end times associated with events, Repeat-values,
// Accesskey-values or added via DOM method calls are cleared when seeking
// to time earlier than the resolved end time.
Reset();
UpdateCurrentInterval();
}
mSeekState = SEEK_NOT_SEEKING;
}
void
nsSMILTimedElement::UnpreserveInstanceTimes(InstanceTimeList& aList)
{
const nsSMILInterval* prevInterval = GetPreviousInterval();
const nsSMILInstanceTime* cutoff = mCurrentInterval ?
mCurrentInterval->Begin() :
prevInterval ? prevInterval->Begin() : nsnull;
InstanceTimeComparator cmp;
PRUint32 count = aList.Length();
for (PRUint32 i = 0; i < count; ++i) {
nsSMILInstanceTime* instance = aList[i].get();
if (!cutoff || cmp.LessThan(cutoff, instance)) {
instance->UnmarkShouldPreserve();
}
}
}
void
nsSMILTimedElement::FilterHistory()
{
// We should filter the intervals first, since instance times still used in an
// interval won't be filtered.
FilterIntervals();
FilterInstanceTimes(mBeginInstances);
FilterInstanceTimes(mEndInstances);
}
void
nsSMILTimedElement::FilterIntervals()
{
// We can filter old intervals that:
//
// a) are not the previous interval; AND
// b) are not in the middle of a dependency chain
//
// Condition (a) is necessary since the previous interval is used for applying
// fill effects and updating the current interval.
//
// Condition (b) is necessary since even if this interval itself is not
// active, it may be part of a dependency chain that includes active
// intervals. Such chains are used to establish priorities within the
// animation sandwich.
//
// Although the above conditions allow us to safely filter intervals for most
// scenarios they do not cover all cases and there will still be scenarios
// that generate intervals indefinitely. In such a case we simply set
// a maximum number of intervals and drop any intervals beyond that threshold.
PRUint32 threshold = mOldIntervals.Length() > sMaxNumIntervals ?
mOldIntervals.Length() - sMaxNumIntervals :
0;
IntervalList filteredList;
for (PRUint32 i = 0; i < mOldIntervals.Length(); ++i)
{
nsSMILInterval* interval = mOldIntervals[i].get();
if (i + 1 < mOldIntervals.Length() /*skip previous interval*/ &&
(i < threshold || !interval->IsDependencyChainLink())) {
interval->Unlink(PR_TRUE /*filtered, not deleted*/);
} else {
filteredList.AppendElement(mOldIntervals[i].forget());
}
}
mOldIntervals.Clear();
mOldIntervals.SwapElements(filteredList);
}
namespace
{
class RemoveFiltered
{
public:
RemoveFiltered(nsSMILTimeValue aCutoff) : mCutoff(aCutoff) { }
PRBool operator()(nsSMILInstanceTime* aInstanceTime, PRUint32 /*aIndex*/)
{
// We can filter instance times that:
// a) Precede the end point of the previous interval; AND
// b) Are NOT syncbase times that might be updated to a time after the end
// point of the previous interval; AND
// c) Are NOT fixed end points in any remaining interval.
return aInstanceTime->Time() < mCutoff &&
aInstanceTime->IsFixedTime() &&
!aInstanceTime->ShouldPreserve();
}
private:
nsSMILTimeValue mCutoff;
};
class RemoveBelowThreshold
{
public:
RemoveBelowThreshold(PRUint32 aThreshold,
const nsSMILInstanceTime* aCurrentIntervalBegin)
: mThreshold(aThreshold),
mCurrentIntervalBegin(aCurrentIntervalBegin) { }
PRBool operator()(nsSMILInstanceTime* aInstanceTime, PRUint32 aIndex)
{
return aInstanceTime != mCurrentIntervalBegin && aIndex < mThreshold;
}
private:
PRUint32 mThreshold;
const nsSMILInstanceTime* mCurrentIntervalBegin;
};
}
void
nsSMILTimedElement::FilterInstanceTimes(InstanceTimeList& aList)
{
if (GetPreviousInterval()) {
RemoveFiltered removeFiltered(GetPreviousInterval()->End()->Time());
RemoveInstanceTimes(aList, removeFiltered);
}
// As with intervals it is possible to create a document that, even despite
// our most aggressive filtering, will generate instance times indefinitely
// (e.g. cyclic dependencies with TimeEvents---we can't filter such times as
// they're unpredictable due to the possibility of seeking the document which
// may prevent some events from being generated). Therefore we introduce
// a hard cutoff at which point we just drop the oldest instance times.
if (aList.Length() > sMaxNumInstanceTimes) {
PRUint32 threshold = aList.Length() - sMaxNumInstanceTimes;
// We should still preserve the current interval begin time however
const nsSMILInstanceTime* currentIntervalBegin = mCurrentInterval ?
mCurrentInterval->Begin() : nsnull;
RemoveBelowThreshold removeBelowThreshold(threshold, currentIntervalBegin);
RemoveInstanceTimes(aList, removeBelowThreshold);
}
}
@ -1119,8 +1444,6 @@ nsSMILTimedElement::GetNextInterval(const nsSMILInterval* aPrevInterval,
tempBegin = const_cast<nsSMILInstanceTime*>(aFixedBeginTime);
} else if (!mBeginSpecSet && beginAfter <= zeroTime) {
tempBegin = new nsSMILInstanceTime(nsSMILTimeValue(0));
if (!tempBegin)
return NS_ERROR_OUT_OF_MEMORY;
} else {
PRInt32 beginPos = 0;
tempBegin = GetNextGreaterOrEqual(mBeginInstances, beginAfter, beginPos);
@ -1166,8 +1489,6 @@ nsSMILTimedElement::GetNextInterval(const nsSMILInterval* aPrevInterval,
if (!tempEnd || intervalEnd != activeEnd) {
tempEnd = new nsSMILInstanceTime(activeEnd);
}
if (!tempEnd)
return NS_ERROR_OUT_OF_MEMORY;
}
NS_ABORT_IF_FALSE(tempEnd, "Failed to get end point for next interval");
@ -1333,6 +1654,9 @@ nsSMILTimedElement::ActiveTimeToSimpleTime(nsSMILTime aActiveTime,
NS_ASSERTION(mSimpleDur.IsResolved() || mSimpleDur.IsIndefinite(),
"Unresolved simple duration in ActiveTimeToSimpleTime");
NS_ASSERTION(aActiveTime >= 0, "Expecting non-negative active time");
// Note that a negative aActiveTime will give us a negative value for
// aRepeatIteration, which is bad because aRepeatIteration is unsigned
if (mSimpleDur.IsIndefinite() || mSimpleDur.GetMillis() == 0L) {
aRepeatIteration = 0;
@ -1409,10 +1733,6 @@ nsSMILTimedElement::UpdateCurrentInterval(PRBool aForceChangeNotice)
NS_ABORT_IF_FALSE(!mCurrentInterval,
"In postactive state but the interval has been set");
mCurrentInterval = new nsSMILInterval(updatedInterval);
if (!mCurrentInterval) {
NS_WARNING("Failed to allocate memory for new interval.");
return;
}
mElementState = STATE_WAITING;
NotifyNewInterval();
@ -1450,7 +1770,7 @@ nsSMILTimedElement::UpdateCurrentInterval(PRBool aForceChangeNotice)
if (mElementState == STATE_ACTIVE || mElementState == STATE_WAITING) {
mElementState = STATE_POSTACTIVE;
mCurrentInterval->NotifyDeleting();
mCurrentInterval->Unlink();
mCurrentInterval = nsnull;
}
}
@ -1477,9 +1797,9 @@ nsSMILTimedElement::SampleFillValue()
NS_ABORT_IF_FALSE(prevInterval,
"Attempting to sample fill value but there is no previous interval");
NS_ABORT_IF_FALSE(prevInterval->End()->Time().IsResolved() &&
!prevInterval->End()->MayUpdate(),
prevInterval->End()->IsFixedTime(),
"Attempting to sample fill value but the endpoint of the previous "
"interval is not resolved and frozen");
"interval is not resolved and fixed");
nsSMILTime activeTime = prevInterval->End()->Time().GetMillis() -
prevInterval->Begin()->Time().GetMillis();
@ -1508,10 +1828,6 @@ nsSMILTimedElement::AddInstanceTimeFromCurrentTime(nsSMILTime aCurrentTime,
// so we don't end up setting SOURCE_DOM for event-based times.
nsRefPtr<nsSMILInstanceTime> instanceTime =
new nsSMILInstanceTime(timeVal, nsSMILInstanceTime::SOURCE_DOM);
if (!instanceTime) {
NS_WARNING("Insufficient memory to create instance time");
return;
}
AddInstanceTime(instanceTime, aIsBegin);
}

View File

@ -229,10 +229,14 @@ public:
void HandleContainerTimeChange();
/**
* Reset the element's internal state. As described in SMILANIM 3.3.7, all
* instance times associated with DOM calls, events, etc. are cleared.
* Resets this timed element's accumulated times and intervals back to start
* up state.
*
* This is used for backwards seeking where rather than accumulating
* historical timing state and winding it back, we reset the element and seek
* forwards.
*/
void Reset();
void Rewind();
/**
* Attempts to set an attribute on this timed element.
@ -345,6 +349,10 @@ protected:
nsSMILTimeContainer* mTimeContainer;
};
// Templated helper functions
template <class TestFunctor>
void RemoveInstanceTimes(InstanceTimeList& aArray, TestFunctor& aTest);
//
// Implementation helpers
//
@ -375,8 +383,58 @@ protected:
nsIContent* aContextNode,
PRBool aIsBegin);
void ClearBeginOrEndSpecs(PRBool aIsBegin);
void RewindTiming();
void RewindInstanceTimes(InstanceTimeList& aList);
void DoSampleAt(nsSMILTime aContainerTime, PRBool aEndOnly);
/**
* Helper function to check for an early end and, if necessary, update the
* current interval accordingly.
*
* See SMIL 3.0, section 5.4.5, Element life cycle, "Active Time - Playing an
* interval" for a description of ending early.
*
* @param aSampleTime The current sample time. Early ends should only be
* applied at the last possible moment (i.e. if they are at
* or before the current sample time) and only if the
* current interval is not already ending.
*/
void ApplyEarlyEnd(const nsSMILTimeValue& aSampleTime);
/**
* Clears certain state in response to the element restarting.
*
* This state is described in SMIL 3.0, section 5.4.3, Resetting element state
*/
void Reset();
/**
* Completes a seek operation by sending appropriate events and, in the case
* of a backwards seek, updating the state of timing information that was
* previously considered historical.
*/
void DoPostSeek();
/**
* Unmarks instance times that were previously preserved because they were
* considered important historical milestones but are no longer such because
* a backwards seek has been performed.
*/
void UnpreserveInstanceTimes(InstanceTimeList& aList);
/**
* Helper function to iterate through this element's accumulated timing
* information (specifically old nsSMILIntervals and nsSMILTimeInstanceTimes)
* and discard items that are no longer needed or exceed some threshold of
* accumulated state.
*/
void FilterHistory();
// Helper functions for FilterHistory to clear old nsSMILIntervals and
// nsSMILInstanceTimes respectively.
void FilterIntervals();
void FilterInstanceTimes(InstanceTimeList& aList);
/**
* Calculates the next acceptable interval for this element after the
* specified interval, or, if no previous interval is specified, it will be
@ -483,6 +541,8 @@ protected:
IntervalList mOldIntervals;
nsSMILMilestone mPrevRegisteredMilestone;
static const nsSMILMilestone sMaxMilestone;
static const PRUint8 sMaxNumIntervals;
static const PRUint8 sMaxNumInstanceTimes;
// Set of dependent time value specs to be notified when establishing a new
// current interval. Change notifications and delete notifications are handled
@ -504,6 +564,16 @@ protected:
STATE_POSTACTIVE
};
nsSMILElementState mElementState;
enum nsSMILSeekState
{
SEEK_NOT_SEEKING,
SEEK_FORWARD_FROM_ACTIVE,
SEEK_FORWARD_FROM_INACTIVE,
SEEK_BACKWARD_FROM_ACTIVE,
SEEK_BACKWARD_FROM_INACTIVE
};
nsSMILSeekState mSeekState;
};
#endif // NS_SMILTIMEDELEMENT_H_

View File

@ -57,6 +57,7 @@ _TEST_FILES = \
test_smilAnimateMotion.xhtml \
test_smilAnimateMotionInvalidValues.xhtml \
test_smilAnimateMotionOverrideRules.xhtml \
test_smilBackwardsSeeking.xhtml \
test_smilChangeAfterFrozen.xhtml \
test_smilContainerBinding.xhtml \
test_smilCrossContainer.xhtml \

View File

@ -0,0 +1,191 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Test for backwards seeking behavior </title>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
<svg id="svg" xmlns="http://www.w3.org/2000/svg" />
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
<![CDATA[
/** Test for backwards seeking behavior **/
var gSvg = document.getElementById("svg");
SimpleTest.waitForExplicitFinish();
function main()
{
// Pause our document, so that the setCurrentTime calls are the only
// thing affecting document time
gSvg.pauseAnimations();
// We define a series of scenarios, sample times, and expected return values
// from getStartTime.
//
// Each scenario is basically a variation on the following arrangement:
//
// <svg>
// <set ... dur="1s" begin="<A-BEGIN>"/>
// <set ... dur="1s" begin="<B-BEGIN>"/>
// </svg>
//
// Each test then consists of the following:
// animA: attributes to be applied to a
// animB: attributes to be applied to b
// times: a series of triples which consist of:
// <sample time, a's expected start time, b's expected start time>
// * The sample time is the time passed to setCurrentTime and so is
// in seconds.
// * The expected start times are compared with the return value of
// getStartTime. To check for an unresolved start time where
// getStartTime would normally throw an exception use
// 'unresolved'.
// * We also allow the special notation to indicate a call to
// beginElement
// <'beginElementAt', id of animation element, offset>
//
// In the diagrams below '^' means the time before the seek and '*' is the
// seek time.
var testCases = Array();
// 0: Simple case
//
// A: +-------
// B: +------- begin: a.begin
// * ^
testCases[0] = {
'animA': {'begin':'1s', 'id':'a'},
'animB': {'begin':'a.begin'},
'times': [ [0, 1, 1],
[1, 1, 1],
[2, 'unresolved', 'unresolved'],
[0, 1, 1],
[1.5, 1, 1],
[1, 1, 1],
[2, 'unresolved', 'unresolved'] ]
};
// 1: Restored times should be live
//
// When we restore times they should be live. So we have the following
// scenario.
//
// A: +-------
// B: +------- begin: a.begin
// * ^
//
// Then we call beginElement at an earlier time which should give us the
// following.
//
// A: +-------
// B: +-------
// * ^
//
// If the times are not live however we'll end up with this
//
// A: +-------
// B: +-+-------
// * ^
testCases[1] = {
'animA': {'begin':'1s', 'id':'a', 'restart':'whenNotActive'},
'animB': {'begin':'a.begin', 'restart':'always'},
'times': [ [0, 1, 1],
[2, 'unresolved', 'unresolved'],
[0.25, 1, 1],
['beginElementAt', 'a', 0.25], // = start time of 0.5
[0.25, 0.5, 0.5],
[1, 0.5, 0.5],
[1.5, 'unresolved', 'unresolved'] ]
};
// 2: Multiple intervals A
//
// A: +- +-
// B: +- +- begin: a.begin+4s
// * ^
testCases[2] = {
'animA': {'begin':'1s; 3s', 'id':'a'},
'animB': {'begin':'a.begin+4s'},
'times': [ [0, 1, 5],
[3, 3, 5],
[6.5, 'unresolved', 7],
[4, 'unresolved', 5],
[6, 'unresolved', 7],
[2, 3, 5],
['beginElementAt', 'a', 0],
[2, 2, 5],
[5, 'unresolved', 5],
[6, 'unresolved', 6],
[7, 'unresolved', 7],
[8, 'unresolved', 'unresolved'] ]
};
for (var i = 0; i < testCases.length; i++) {
gSvg.setCurrentTime(0);
var test = testCases[i];
// Create animation elements
var animA = createAnim(test.animA);
var animB = createAnim(test.animB);
// Run samples
for (var j = 0; j < test.times.length; j++) {
var times = test.times[j];
if (times[0] == 'beginElementAt') {
var anim = getElement(times[1]);
anim.beginElementAt(times[2]);
} else {
gSvg.setCurrentTime(times[0]);
checkStartTime(animA, times[1], times[0], i, 'a');
checkStartTime(animB, times[2], times[0], i, 'b');
}
}
// Tidy up
removeElement(animA);
removeElement(animB);
}
SimpleTest.finish();
}
function createAnim(attr)
{
const svgns = "http://www.w3.org/2000/svg";
var anim = document.createElementNS(svgns, 'set');
anim.setAttribute('attributeName','x');
anim.setAttribute('to','10');
anim.setAttribute('dur','1s');
for (name in attr) {
anim.setAttribute(name, attr[name]);
}
return gSvg.appendChild(anim);
}
function checkStartTime(anim, expectedStartTime, sampleTime, caseNum, id)
{
var startTime = 'unresolved';
try {
startTime = anim.getStartTime();
} catch (e) {
if (e.code != DOMException.INVALID_STATE_ERR)
throw e;
}
var msg = "Test case " + caseNum + ", t=" + sampleTime + " animation '" +
id + "': Unexpected getStartTime:";
is(startTime, expectedStartTime, msg);
}
window.addEventListener("load", main, false);
]]>
</script>
</pre>
</body>
</html>

View File

@ -60,11 +60,11 @@ function main() {
is(anim.getStartTime(), 6);
// Rebind
// At this point all the old intervals should be re-added to anim. If they're
// not and only the current interval is added to anim we'll get a start time
// of 4s instead of 2s.
// At this point only the current interval will be re-added to anim (this is
// for consistency since old intervals may or may not have been filtered).
// Therefore the start time should be 4s instead of 2s.
circle.appendChild(anim);
is(anim.getStartTime(), 2);
is(anim.getStartTime(), 4);
SimpleTest.finish();
}

View File

@ -31,7 +31,8 @@ function main() {
// Test that seeking takes effect immediately
for (var i = 0; i < gTimes.length; i++) {
gSvg.setCurrentTime(gTimes[i]);
assertFloatsEqual(gSvg.getCurrentTime(), gTimes[i]);
// We adopt the SVGT1.2 behavior of clamping negative times to 0
assertFloatsEqual(gSvg.getCurrentTime(), Math.max(gTimes[i], 0.0));
}
// Test that seeking isn't messed up by timeouts
@ -56,7 +57,7 @@ function checkTimesAfterIndex(index) {
gSvg.setCurrentTime(gTimes[index]);
var func = function() {
assertFloatsEqual(gSvg.getCurrentTime(), gTimes[index]);
assertFloatsEqual(gSvg.getCurrentTime(), Math.max(gTimes[index], 0.0));
checkTimesAfterIndex(index + 1);
}
setTimeout(func, gWaitTime);

View File

@ -169,29 +169,35 @@ nsSVGPointList::SetValueString(const nsAString& aValue)
nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
nsCOMArray<nsIDOMSVGPoint> points;
PRBool parseError = PR_FALSE;
while (tokenizer.hasMoreTokens()) {
// Parse 2 tokens
NS_ConvertUTF16toUTF8 utf8String1(tokenizer.nextToken());
const char *token1 = utf8String1.get();
if (!tokenizer.hasMoreTokens() || // No 2nd token.
*token1 == '\0') { // 1st token is empty string.
return NS_ERROR_DOM_SYNTAX_ERR;
parseError = PR_TRUE;
break;
}
NS_ConvertUTF16toUTF8 utf8String2(tokenizer.nextToken());
const char *token2 = utf8String2.get();
if (*token2 == '\0') { // 2nd token is empty string.
return NS_ERROR_DOM_SYNTAX_ERR;
parseError = PR_TRUE;
break;
}
// Convert parsed tokens to float values.
char *end;
float x = float(PR_strtod(token1, &end));
if (*end != '\0' || !NS_FloatIsFinite(x)) {
return NS_ERROR_DOM_SYNTAX_ERR;
parseError = PR_TRUE;
break;
}
float y = float(PR_strtod(token2, &end));
if (*end != '\0' || !NS_FloatIsFinite(y)) {
return NS_ERROR_DOM_SYNTAX_ERR;
parseError = PR_TRUE;
break;
}
// Build a point from our parsed float values.
@ -201,7 +207,11 @@ nsSVGPointList::SetValueString(const nsAString& aValue)
}
if (tokenizer.lastTokenEndedWithSeparator()) { // Reject trailing comma
return NS_ERROR_DOM_SYNTAX_ERR;
parseError = PR_TRUE;
}
if (parseError) {
// XXX nsSVGUtils::ReportToConsole()
}
WillModify();

View File

@ -71,8 +71,7 @@ NS_NewXBLContentSink(nsIXMLContentSink** aResult,
{
NS_ENSURE_ARG_POINTER(aResult);
nsXBLContentSink* it;
NS_NEWXPCOM(it, nsXBLContentSink);
nsXBLContentSink* it = new nsXBLContentSink();
NS_ENSURE_TRUE(it, NS_ERROR_OUT_OF_MEMORY);
nsCOMPtr<nsIXMLContentSink> kungFuDeathGrip = it;

View File

@ -124,8 +124,7 @@ NS_NewXMLContentSink(nsIXMLContentSink** aResult,
if (nsnull == aResult) {
return NS_ERROR_NULL_POINTER;
}
nsXMLContentSink* it;
NS_NEWXPCOM(it, nsXMLContentSink);
nsXMLContentSink* it = new nsXMLContentSink();
if (nsnull == it) {
return NS_ERROR_OUT_OF_MEMORY;
}

View File

@ -36,10 +36,8 @@
*
* ***** END LICENSE BLOCK ***** */
#include "nsIServiceManager.h"
#include "mozilla/ModuleUtils.h"
#include "nsCOMPtr.h"
#include "nsIModule.h"
#include "nsIGenericFactory.h"
#include "nsMorkCID.h"
#include "nsIMdbFactoryFactory.h"
#include "mdb.h"
@ -59,16 +57,25 @@ protected:
NS_GENERIC_FACTORY_CONSTRUCTOR(nsMorkFactoryService)
static const nsModuleComponentInfo components[] =
{
{ "Mork Factory Service",
NS_MORK_CID,
NS_MORK_CONTRACTID,
nsMorkFactoryServiceConstructor
}
NS_DEFINE_NAMED_CID(NS_MORK_CID);
const mozilla::Module::CIDEntry kMorkCIDs[] = {
{ &kNS_MORK_CID, false, NULL, nsMorkFactoryServiceConstructor },
{ NULL }
};
NS_IMPL_NSGETMODULE(nsMorkModule, components)
const mozilla::Module::ContractIDEntry kMorkContracts[] = {
{ NS_MORK_CONTRACTID, &kNS_MORK_CID },
{ NULL }
};
static const mozilla::Module kMorkModule = {
mozilla::Module::kVersion,
kMorkCIDs,
kMorkContracts
};
NSMODULE_DEFN(nsMorkModule) = &kMorkModule;
NS_IMPL_ISUPPORTS1(nsMorkFactoryService, nsIMdbFactoryService)

View File

@ -43,7 +43,6 @@
#include "nsISupports.h"
class nsIURI;
class nsString;
namespace mozilla {
@ -52,7 +51,7 @@ namespace mozilla {
}
#define IHISTORY_IID \
{0x6f736049, 0x6370, 0x4376, {0xb7, 0x17, 0xfa, 0xfc, 0x0b, 0x4f, 0xd0, 0xf1}}
{0xaf27265d, 0x5672, 0x4d23, {0xa0, 0x75, 0x34, 0x8e, 0xb9, 0x73, 0x5a, 0x9a}}
class IHistory : public nsISupports
{
@ -97,50 +96,6 @@ public:
*/
NS_IMETHOD UnregisterVisitedCallback(nsIURI *aURI, dom::Link *aLink) = 0;
enum VisitFlags {
/**
* Indicates whether the URI was loaded in a top-level window.
*/
TOP_LEVEL = 1 << 0,
/**
* Indicates whether the URI was loaded as part of a permanent redirect.
*/
REDIRECT_PERMANENT = 1 << 1,
/**
* Indicates whether the URI was loaded as part of a temporary redirect.
*/
REDIRECT_TEMPORARY = 1 << 2
};
/**
* Adds a history visit for the URI.
*
* @pre aURI must not be null.
*
* @param aURI
* The URI of the page being visited.
* @param aLastVisitedURI
* The URI of the last visit in the chain.
* @param aFlags
* The VisitFlags describing this visit.
*/
NS_IMETHOD VisitURI(
nsIURI *aURI,
nsIURI *aLastVisitedURI,
PRUint32 aFlags
) = 0;
/**
* Set the title of the URI.
*
* @pre aURI must not be null.
*
* @param aURI
* The URI to set the title for.
* @param aTitle
* The title string.
*/
NS_IMETHOD SetURITitle(nsIURI* aURI, const nsAString& aTitle) = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(IHistory, IHISTORY_IID)
@ -149,11 +104,7 @@ NS_DEFINE_STATIC_IID_ACCESSOR(IHistory, IHISTORY_IID)
NS_IMETHOD RegisterVisitedCallback(nsIURI *aURI, \
mozilla::dom::Link *aContent); \
NS_IMETHOD UnregisterVisitedCallback(nsIURI *aURI, \
mozilla::dom::Link *aContent); \
NS_IMETHOD VisitURI(nsIURI *aURI, \
nsIURI *aLastVisitedURI, \
PRUint32 aFlags); \
NS_IMETHOD SetURITitle(nsIURI* aURI, const nsAString& aTitle);
mozilla::dom::Link *aContent);
} // namespace mozilla

View File

@ -112,8 +112,6 @@
#include "nsIOfflineCacheUpdate.h"
#include "nsCPrefetchService.h"
#include "nsJSON.h"
#include "IHistory.h"
#include "mozilla/Services.h"
// we want to explore making the document own the load group
// so we can associate the document URI with the load group.
@ -4704,16 +4702,11 @@ nsDocShell::SetTitle(const PRUnichar * aTitle)
treeOwnerAsWin->SetTitle(aTitle);
}
if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE) {
nsCOMPtr<IHistory> history = services::GetHistoryService();
if (history) {
history->SetURITitle(mCurrentURI, mTitle);
}
else if (mGlobalHistory) {
mGlobalHistory->SetPageTitle(mCurrentURI, nsString(mTitle));
}
if (mGlobalHistory && mCurrentURI && mLoadType != LOAD_ERROR_PAGE) {
mGlobalHistory->SetPageTitle(mCurrentURI, nsString(mTitle));
}
// Update SessionHistory with the document's title.
if (mOSHE && mLoadType != LOAD_BYPASS_HISTORY &&
mLoadType != LOAD_ERROR_PAGE) {
@ -5679,53 +5672,32 @@ nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
if (!(aStateFlags & STATE_IS_DOCUMENT))
return; // not a toplevel document
nsCOMPtr<nsIURI> oldURI, newURI;
aOldChannel->GetURI(getter_AddRefs(oldURI));
aNewChannel->GetURI(getter_AddRefs(newURI));
if (!oldURI || !newURI) {
return;
nsCOMPtr<nsIGlobalHistory3> history3(do_QueryInterface(mGlobalHistory));
nsresult result = NS_ERROR_NOT_IMPLEMENTED;
if (history3) {
// notify global history of this redirect
result = history3->AddDocumentRedirect(aOldChannel, aNewChannel,
aRedirectFlags, !IsFrame());
}
// Below a URI visit is saved (see AddURIVisit method doc).
// The visit chain looks something like:
// ...
// Site N - 1
// => Site N
// (redirect to =>) Site N + 1 (we are here!)
// Get N - 1 and transition type
nsCOMPtr<nsIURI> previousURI;
PRUint32 previousFlags = 0;
ExtractLastVisit(aOldChannel, getter_AddRefs(previousURI), &previousFlags);
if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL ||
ChannelIsPost(aOldChannel)) {
// 1. Internal redirects are ignored because they are specific to the
// channel implementation.
// 2. POSTs are not saved by global history.
//
// Regardless, we need to propagate the previous visit to the new
// channel.
SaveLastVisit(aNewChannel, previousURI, previousFlags);
}
else {
nsCOMPtr<nsIURI> referrer;
// Treat referrer as null if there is an error getting it.
(void)NS_GetReferrerFromChannel(aOldChannel,
getter_AddRefs(referrer));
// Add visit N -1 => N
AddURIVisit(oldURI, referrer, previousURI, previousFlags);
// Since N + 1 could be the final destination, we will not save N => N + 1
// here. OnNewURI will do that, so we will cache it.
SaveLastVisit(aNewChannel, oldURI, aRedirectFlags);
if (result == NS_ERROR_NOT_IMPLEMENTED) {
// when there is no GlobalHistory3, or it doesn't implement
// AddToplevelRedirect, we fall back to GlobalHistory2. Just notify
// that the redirecting page was a rePdirect so it will be link colored
// but not visible.
nsCOMPtr<nsIURI> oldURI;
aOldChannel->GetURI(getter_AddRefs(oldURI));
if (! oldURI)
return; // nothing to tell anybody about
AddToGlobalHistory(oldURI, PR_TRUE, aOldChannel);
}
// check if the new load should go through the application cache.
nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
do_QueryInterface(aNewChannel);
if (appCacheChannel) {
nsCOMPtr<nsIURI> newURI;
aNewChannel->GetURI(getter_AddRefs(newURI));
appCacheChannel->SetChooseApplicationCache(ShouldCheckAppCache(newURI));
}
@ -8965,7 +8937,7 @@ nsDocShell::OnNewURI(nsIURI * aURI, nsIChannel * aChannel, nsISupports* aOwner,
nsCOMPtr<nsIInputStream> inputStream;
if (aChannel) {
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
// Check if the HTTPChannel is hiding under a multiPartChannel
if (!httpChannel) {
GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
@ -9055,8 +9027,8 @@ nsDocShell::OnNewURI(nsIURI * aURI, nsIChannel * aChannel, nsISupports* aOwner,
nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(aChannel));
nsCOMPtr<nsISupports> cacheKey;
// Get the Cache Key and store it in SH.
if (cacheChannel)
// Get the Cache Key and store it in SH.
if (cacheChannel)
cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
// If we already have a loading history entry, store the new cache key
// in it. Otherwise, since we're doing a reload and won't be updating
@ -9078,22 +9050,10 @@ nsDocShell::OnNewURI(nsIURI * aURI, nsIChannel * aChannel, nsISupports* aOwner,
getter_AddRefs(mLSHE));
}
// Update Global history
if (aAddToGlobalHistory) {
// If this is a POST request, we do not want to include this in global
// history.
if (!ChannelIsPost(aChannel)) {
nsCOMPtr<nsIURI> previousURI;
PRUint32 previousFlags = 0;
ExtractLastVisit(aChannel, getter_AddRefs(previousURI),
&previousFlags);
nsCOMPtr<nsIURI> referrer;
// Treat referrer as null if there is an error getting it.
(void)NS_GetReferrerFromChannel(aChannel,
getter_AddRefs(referrer));
AddURIVisit(aURI, referrer, previousURI, previousFlags);
}
// Get the referrer uri from the channel
AddToGlobalHistory(aURI, PR_FALSE, aChannel);
}
}
@ -9412,7 +9372,7 @@ nsDocShell::AddState(nsIVariant *aData, const nsAString& aTitle,
SetCurrentURI(newURI, nsnull, PR_TRUE);
document->SetDocumentURI(newURI);
AddURIVisit(newURI, oldURI, oldURI, 0);
AddToGlobalHistory(newURI, PR_FALSE, oldURI);
}
else {
FireOnLocationChange(this, nsnull, mCurrentURI);
@ -10108,109 +10068,53 @@ NS_IMETHODIMP nsDocShell::MakeEditable(PRBool inWaitForUriLoad)
return mEditorData->MakeEditable(inWaitForUriLoad);
}
bool
nsDocShell::ChannelIsPost(nsIChannel* aChannel)
nsresult
nsDocShell::AddToGlobalHistory(nsIURI * aURI, PRBool aRedirect,
nsIChannel * aChannel)
{
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
if (!httpChannel) {
return false;
// If this is a POST request, we do not want to include this in global
// history, so return early.
nsCOMPtr<nsIHttpChannel> hchan(do_QueryInterface(aChannel));
if (hchan) {
nsCAutoString type;
nsresult rv = hchan->GetRequestMethod(type);
if (NS_SUCCEEDED(rv) && type.EqualsLiteral("POST"))
return NS_OK;
}
nsCAutoString method;
httpChannel->GetRequestMethod(method);
return method.Equals("POST");
nsCOMPtr<nsIURI> referrer;
if (aChannel)
NS_GetReferrerFromChannel(aChannel, getter_AddRefs(referrer));
return AddToGlobalHistory(aURI, aRedirect, referrer);
}
void
nsDocShell::ExtractLastVisit(nsIChannel* aChannel,
nsIURI** aURI,
PRUint32* aChannelRedirectFlags)
nsresult
nsDocShell::AddToGlobalHistory(nsIURI * aURI, PRBool aRedirect,
nsIURI * aReferrer)
{
nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
if (!props) {
return;
}
if (mItemType != typeContent || !mGlobalHistory)
return NS_OK;
nsresult rv = props->GetPropertyAsInterface(
NS_LITERAL_STRING("docshell.previousURI"),
NS_GET_IID(nsIURI),
reinterpret_cast<void**>(aURI)
);
PRBool visited;
nsresult rv = mGlobalHistory->IsVisited(aURI, &visited);
if (NS_FAILED(rv))
return rv;
if (NS_FAILED(rv)) {
// There is no last visit for this channel, so this must be the first
// link. Link the visit to the referrer of this request, if any.
// Treat referrer as null if there is an error getting it.
(void)NS_GetReferrerFromChannel(aChannel, aURI);
}
else {
rv = props->GetPropertyAsUint32(
NS_LITERAL_STRING("docshell.previousFlags"),
aChannelRedirectFlags
);
rv = mGlobalHistory->AddURI(aURI, aRedirect, !IsFrame(), aReferrer);
if (NS_FAILED(rv))
return rv;
NS_WARN_IF_FALSE(
NS_FAILED(rv),
"Could not fetch previous flags, URI will be treated like referrer"
);
}
}
void
nsDocShell::SaveLastVisit(nsIChannel* aChannel,
nsIURI* aURI,
PRUint32 aChannelRedirectFlags)
{
nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aChannel));
if (!props || !aURI) {
return;
}
props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.previousURI"),
aURI);
props->SetPropertyAsUint32(NS_LITERAL_STRING("docshell.previousFlags"),
aChannelRedirectFlags);
}
void
nsDocShell::AddURIVisit(nsIURI* aURI,
nsIURI* aReferrerURI,
nsIURI* aPreviousURI,
PRUint32 aChannelRedirectFlags)
{
NS_ASSERTION(aURI, "Visited URI is null!");
// Only content-type docshells save URI visits.
if (mItemType != typeContent) {
return;
}
nsCOMPtr<IHistory> history = services::GetHistoryService();
if (history) {
PRUint32 visitURIFlags = 0;
if (!IsFrame()) {
visitURIFlags |= IHistory::TOP_LEVEL;
if (!visited) {
nsCOMPtr<nsIObserverService> obsService =
mozilla::services::GetObserverService();
if (obsService) {
obsService->NotifyObservers(aURI, NS_LINK_VISITED_EVENT_TOPIC, nsnull);
}
if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_TEMPORARY) {
visitURIFlags |= IHistory::REDIRECT_TEMPORARY;
}
else if (aChannelRedirectFlags &
nsIChannelEventSink::REDIRECT_PERMANENT) {
visitURIFlags |= IHistory::REDIRECT_PERMANENT;
}
(void)history->VisitURI(aURI, aPreviousURI, visitURIFlags);
}
else if (mGlobalHistory) {
// Falls back to sync global history interface.
(void)mGlobalHistory->AddURI(aURI,
!!aChannelRedirectFlags,
!IsFrame(),
aReferrerURI);
}
return NS_OK;
}
//*****************************************************************************

View File

@ -433,77 +433,12 @@ protected:
PRUint32 aRedirectFlags,
PRUint32 aStateFlags);
/**
* Helper function that determines if channel is an HTTP POST.
*
* @param aChannel
* The channel to test
*
* @return True iff channel is an HTTP post.
*/
bool ChannelIsPost(nsIChannel* aChannel);
// Global History
/**
* Helper function that finds the last URI and its transition flags for a
* channel.
*
* This method first checks the channel's property bag to see if previous
* info has been saved. If not, it gives back the referrer of the channel.
*
* @param aChannel
* The channel we are transitioning to
* @param aURI
* Output parameter with the previous URI, not addref'd
* @param aChannelRedirectFlags
* If a redirect, output parameter with the previous redirect flags
* from nsIChannelEventSink
*/
void ExtractLastVisit(nsIChannel* aChannel,
nsIURI** aURI,
PRUint32* aChannelRedirectFlags);
/**
* Helper function that caches a URI and a transition for saving later.
*
* @param aChannel
* Channel that will have these properties saved
* @param aURI
* The URI to save for later
* @param aChannelRedirectFlags
* The nsIChannelEventSink redirect flags to save for later
*/
void SaveLastVisit(nsIChannel* aChannel,
nsIURI* aURI,
PRUint32 aChannelRedirectFlags);
/**
* Helper function for adding a URI visit using IHistory. If IHistory is
* not available, the method tries nsIGlobalHistory2.
*
* The IHistory API maintains chains of visits, tracking both HTTP referrers
* and redirects for a user session. VisitURI requires the current URI and
* the previous URI in the chain.
*
* Visits can be saved either during a redirect or when the request has
* reached its final destination. The previous URI in the visit may be
* from another redirect or it may be the referrer.
*
* @pre aURI is not null.
*
* @param aURI
* The URI that was just visited
* @param aReferrerURI
* The referrer URI of this request
* @param aPreviousURI
* The previous URI of this visit (may be the same as aReferrerURI)
* @param aChannelRedirectFlags
* For redirects, the redirect flags from nsIChannelEventSink
* (0 otherwise)
*/
void AddURIVisit(nsIURI* aURI,
nsIURI* aReferrerURI,
nsIURI* aPreviousURI,
PRUint32 aChannelRedirectFlags);
nsresult AddToGlobalHistory(nsIURI * aURI, PRBool aRedirect,
nsIChannel * aChannel);
nsresult AddToGlobalHistory(nsIURI * aURI, PRBool aRedirect,
nsIURI * aReferrer);
// Helper Routines
nsresult ConfirmRepost(PRBool * aRepost);
@ -765,9 +700,6 @@ protected:
PRInt32 mMarginWidth;
PRInt32 mMarginHeight;
// This can either be a content docshell or a chrome docshell. After
// Create() is called, the type is not expected to change.
PRInt32 mItemType;
// Index into the SHTransaction list, indicating the previous and current

View File

@ -909,8 +909,7 @@ nsClipboardDragDropHookCommand::GetCommandStateParams(const char *aCommandName,
#define NS_REGISTER_ONE_COMMAND(_cmdClass, _cmdName) \
{ \
_cmdClass* theCmd; \
NS_NEWXPCOM(theCmd, _cmdClass); \
_cmdClass* theCmd = new _cmdClass(); \
if (!theCmd) return NS_ERROR_OUT_OF_MEMORY; \
rv = inCommandTable->RegisterCommand(_cmdName, \
static_cast<nsIControllerCommand *>(theCmd)); \
@ -918,8 +917,7 @@ nsClipboardDragDropHookCommand::GetCommandStateParams(const char *aCommandName,
#define NS_REGISTER_FIRST_COMMAND(_cmdClass, _cmdName) \
{ \
_cmdClass* theCmd; \
NS_NEWXPCOM(theCmd, _cmdClass); \
_cmdClass* theCmd = new _cmdClass(); \
if (!theCmd) return NS_ERROR_OUT_OF_MEMORY; \
rv = inCommandTable->RegisterCommand(_cmdName, \
static_cast<nsIControllerCommand *>(theCmd));

View File

@ -580,7 +580,6 @@ interface nsICanvasRenderingContextWebGL : nsISupports
// METHODS
//
void present();
long sizeInBytes(in WebGLenum type);
void activeTexture(in WebGLenum texture);
void attachShader(in nsIWebGLProgram program, in nsIWebGLShader shader);

View File

@ -40,7 +40,7 @@
#include "nsIDOMSVGCSS2Properties.idl"
[scriptable, uuid(649B0B41-C5F7-4EA2-B6DF-CFFF6B6DD30A)]
[scriptable, uuid(29B6104D-933F-4DD0-8FC8-BDEC0514469D)]
interface nsIDOMNSCSS2Properties : nsIDOMSVGCSS2Properties
{
/* Non-DOM 2 extensions */
@ -282,6 +282,6 @@ interface nsIDOMNSCSS2Properties : nsIDOMSVGCSS2Properties
attribute DOMString MozTabSize;
// raises(DOMException) on setting
attribute DOMString MozResize;
attribute DOMString resize;
// raises(DOMException) on setting
};

View File

@ -311,6 +311,7 @@ TabChild::GetInterface(const nsIID & aIID, void **aSink)
NS_IMETHODIMP
TabChild::ProvideWindow(nsIDOMWindow* aParent, PRUint32 aChromeFlags,
PRBool aCalledFromJS,
PRBool aPositionSpecified, PRBool aSizeSpecified,
nsIURI* aURI, const nsAString& aName,
const nsACString& aFeatures, PRBool* aWindowIsNew,

View File

@ -44,8 +44,7 @@
#define NS_REGISTER_ONE_COMMAND(_cmdClass, _cmdName) \
{ \
_cmdClass* theCmd; \
NS_NEWXPCOM(theCmd, _cmdClass); \
_cmdClass* theCmd = new _cmdClass(); \
NS_ENSURE_TRUE(theCmd, NS_ERROR_OUT_OF_MEMORY); \
rv = inCommandTable->RegisterCommand(_cmdName, \
static_cast<nsIControllerCommand *>(theCmd)); \
@ -53,8 +52,7 @@
#define NS_REGISTER_FIRST_COMMAND(_cmdClass, _cmdName) \
{ \
_cmdClass* theCmd; \
NS_NEWXPCOM(theCmd, _cmdClass); \
_cmdClass* theCmd = new _cmdClass(); \
NS_ENSURE_TRUE(theCmd, NS_ERROR_OUT_OF_MEMORY); \
rv = inCommandTable->RegisterCommand(_cmdName, \
static_cast<nsIControllerCommand *>(theCmd));

View File

@ -84,8 +84,7 @@ nsComposeTxtSrvFilterConstructor(nsISupports *aOuter, REFNSIID aIID,
{
return NS_ERROR_NO_AGGREGATION;
}
nsComposeTxtSrvFilter * inst;
NS_NEWXPCOM(inst, nsComposeTxtSrvFilter);
nsComposeTxtSrvFilter * inst = new nsComposeTxtSrvFilter();
if (NULL == inst)
{
return NS_ERROR_OUT_OF_MEMORY;

View File

@ -47,8 +47,7 @@
#define NS_REGISTER_ONE_COMMAND(_cmdClass, _cmdName) \
{ \
_cmdClass* theCmd; \
NS_NEWXPCOM(theCmd, _cmdClass); \
_cmdClass* theCmd = new _cmdClass(); \
NS_ENSURE_TRUE(theCmd, NS_ERROR_OUT_OF_MEMORY); \
rv = inCommandTable->RegisterCommand(_cmdName, \
static_cast<nsIControllerCommand *>(theCmd)); \
@ -56,8 +55,7 @@
#define NS_REGISTER_FIRST_COMMAND(_cmdClass, _cmdName) \
{ \
_cmdClass* theCmd; \
NS_NEWXPCOM(theCmd, _cmdClass); \
_cmdClass* theCmd = new _cmdClass(); \
NS_ENSURE_TRUE(theCmd, NS_ERROR_OUT_OF_MEMORY); \
rv = inCommandTable->RegisterCommand(_cmdName, \
static_cast<nsIControllerCommand *>(theCmd));

View File

@ -70,7 +70,7 @@ nsrefcnt nsEditorTxnLog::Release(void)
{
NS_PRECONDITION(0 != mRefCnt, "dup release");
if (--mRefCnt == 0) {
NS_DELETEXPCOM(this);
delete this;
return 0;
}
return mRefCnt;

View File

@ -56,7 +56,7 @@ interface nsIURI;
* or the provider does not provide a window, the window watcher will proceed
* to actually open a new window.
*/
[scriptable, uuid(5119ac7f-81dd-4061-96a7-71f2cf5efee4)]
[scriptable, uuid(f607bd66-08e5-4d2e-ad83-9f9f3ca17658)]
interface nsIWindowProvider : nsISupports
{
/**
@ -114,6 +114,7 @@ interface nsIWindowProvider : nsISupports
*/
nsIDOMWindow provideWindow(in nsIDOMWindow aParent,
in unsigned long aChromeFlags,
in boolean aCalledFromJS,
in boolean aPositionSpecified,
in boolean aSizeSpecified,
in nsIURI aURI,

View File

@ -153,7 +153,7 @@ NS_IMETHODIMP nsWebBrowser::InternalDestroy()
if (mListenerArray) {
for (PRUint32 i = 0, end = mListenerArray->Length(); i < end; i++) {
nsWebBrowserListenerState *state = mListenerArray->ElementAt(i);
NS_DELETEXPCOM(state);
delete state;
}
delete mListenerArray;
mListenerArray = nsnull;
@ -237,14 +237,14 @@ NS_IMETHODIMP nsWebBrowser::AddWebBrowserListener(nsIWeakReference *aListener, c
// The window hasn't been created yet, so queue up the listener. They'll be
// registered when the window gets created.
nsAutoPtr<nsWebBrowserListenerState> state;
NS_NEWXPCOM(state, nsWebBrowserListenerState);
state = new nsWebBrowserListenerState();
if (!state) return NS_ERROR_OUT_OF_MEMORY;
state->mWeakPtr = aListener;
state->mID = aIID;
if (!mListenerArray) {
NS_NEWXPCOM(mListenerArray, nsTArray<nsWebBrowserListenerState*>);
mListenerArray = new nsTArray<nsWebBrowserListenerState*>();
if (!mListenerArray) {
return NS_ERROR_OUT_OF_MEMORY;
}
@ -315,9 +315,9 @@ NS_IMETHODIMP nsWebBrowser::RemoveWebBrowserListener(nsIWeakReference *aListener
if (0 >= mListenerArray->Length()) {
for (PRUint32 i = 0, end = mListenerArray->Length(); i < end; i++) {
nsWebBrowserListenerState *state = mListenerArray->ElementAt(i);
NS_DELETEXPCOM(state);
delete state;
}
NS_DELETEXPCOM(mListenerArray);
delete mListenerArray;
mListenerArray = nsnull;
}
@ -1172,9 +1172,9 @@ NS_IMETHODIMP nsWebBrowser::Create()
}
for (PRUint32 i = 0, end = mListenerArray->Length(); i < end; i++) {
nsWebBrowserListenerState *state = mListenerArray->ElementAt(i);
NS_DELETEXPCOM(state);
delete state;
}
NS_DELETEXPCOM(mListenerArray);
delete mListenerArray;
mListenerArray = nsnull;
}

View File

@ -69,7 +69,7 @@ NS_IMETHODIMP_(nsrefcnt) nsPrintProgress::Release(void)
mRefCnt = 1; /* stabilize */
/* enable this to find non-threadsafe destructors: */
/* NS_ASSERT_OWNINGTHREAD(nsPrintProgress); */
NS_DELETEXPCOM(this);
delete this;
return 0;
}
return count;

View File

@ -623,7 +623,7 @@ nsWindowWatcher::OpenWindowJSInternal(nsIDOMWindow *aParent,
NS_ASSERTION(aParent, "We've _got_ to have a parent here!");
nsCOMPtr<nsIDOMWindow> newWindow;
rv = provider->ProvideWindow(aParent, chromeFlags,
rv = provider->ProvideWindow(aParent, chromeFlags, aCalledFromJS,
sizeSpec.PositionSpecified(),
sizeSpec.SizeSpecified(),
uriToLoad, name, features, &windowIsNew,

View File

@ -58,6 +58,10 @@ _TEST_FILES = \
image1.png^headers^ \
image2.png \
image2.png^headers^ \
beltzner.jpg \
beltzner.jpg^headers^ \
damonbowling.jpg \
damonbowling.jpg^headers^ \
test1.css \
test1.css^headers^ \
test2.css \

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@ -0,0 +1,3 @@
Cache-Control: no-store
Set-Cookie: mike=beltzer

View File

@ -29,5 +29,5 @@ function test() {
}, "cookie-rejected", false);
// kick off a favicon load
PageProxySetIcon("http://example.org/tests/extensions/cookie/test/image1.png");
PageProxySetIcon("http://example.org/tests/extensions/cookie/test/damonbowling.jpg");
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@ -0,0 +1,2 @@
Cache-Control: no-store
Set-Cookie: damon=bowling

View File

@ -12,6 +12,6 @@
</script>
</head>
<body onload="window.opener.postMessage('f_lf_i msg data page', 'http://mochi.test:8888');">
<img src="http://example.org/tests/extensions/cookie/test/image1.png" onload="runTest()" />
<img src="http://example.org/tests/extensions/cookie/test/beltzner.jpg" onload="runTest()" />
</body>
</html>

View File

@ -7,9 +7,9 @@
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<!--
*5 cookies: 1+1 from file_testloadflags.js, 2 from file_loadflags_inner.html + 1 from image1.png^headers^.
*5 cookies: 1+1 from file_testloadflags.js, 2 from file_loadflags_inner.html + 1 from beltzner.jpg.
*1 load: file_loadflags_inner.html.
*2 headers: 1 for file_loadflags_inner.html + 1 for image1.png.
*2 headers: 1 for file_loadflags_inner.html + 1 for beltzner.jpg.
-->
<body onload="setupTest('http://example.org/tests/extensions/cookie/test/file_loadflags_inner.html', 'example.org', 5, 2, 2)">
<p id="display"></p>

View File

@ -86,15 +86,13 @@ mozInlineSpellCheckerConstructor(nsISupports *aOuter, REFNSIID aIID,
nsresult rv;
mozInlineSpellChecker* inst;
*aResult = NULL;
if (NULL != aOuter) {
rv = NS_ERROR_NO_AGGREGATION;
return rv;
}
NS_NEWXPCOM(inst, mozInlineSpellChecker);
mozInlineSpellChecker* inst = new mozInlineSpellChecker();
if (NULL == inst) {
rv = NS_ERROR_OUT_OF_MEMORY;
return rv;

File diff suppressed because it is too large Load Diff

View File

@ -74,8 +74,7 @@ nsScriptableRegionConstructor(nsISupports *aOuter, REFNSIID aIID, void **aResult
return rv;
}
nsCOMPtr <nsIRegion> rgn;
NS_NEWXPCOM(rgn, nsThebesRegion);
nsCOMPtr <nsIRegion> rgn = new nsThebesRegion();
nsCOMPtr<nsIScriptableRegion> scriptableRgn;
if (rgn != nsnull)
{

View File

@ -3025,6 +3025,8 @@ typedef ptrdiff_t GLintptr;
#define LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A
#define LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B
#define LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125
#define LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122
#define LOCAL_WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
#define LOCAL_WGL_DRAW_TO_WINDOW_ARB 0x2001

View File

@ -104,7 +104,7 @@ public: \
NS_LOG_RELEASE(this, count, #_class); \
if (count == 0) { \
mRefCnt = 1; /* stabilize */ \
NS_DELETEXPCOM(this); \
delete this; \
return 0; \
} \
return count; \

View File

@ -57,9 +57,7 @@ EXPORTS = \
nsICharsetDetectionAdaptor.h \
nsICharsetDetectionObserver.h \
nsICharsetDetector.h \
nsIMetaCharsetService.h \
nsIStringCharsetDetector.h \
nsIXMLEncodingService.h \
nsMetaCharsetCID.h \
nsXMLEncodingCID.h \
$(NULL)

View File

@ -1,58 +0,0 @@
/* -*- Mode: C; tab-width: 4; 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 mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* 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 ***** */
#ifndef nsIMetaCharsetService_h__
#define nsIMetaCharsetService_h__
#include "nsISupports.h"
// {218F2AC1-0A48-11d3-B3BA-00805F8A6670}
#define NS_IMETA_CHARSET_SERVICE_IID \
{ 0x218f2ac1, 0xa48, 0x11d3, { 0xb3, 0xba, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70 } }
class nsIMetaCharsetService : public nsISupports {
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMETA_CHARSET_SERVICE_IID)
NS_IMETHOD Start() = 0;
NS_IMETHOD End() = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIMetaCharsetService,
NS_IMETA_CHARSET_SERVICE_IID)
#endif // nsIMetaCharsetService_h__

View File

@ -1,59 +0,0 @@
/* -*- Mode: C; tab-width: 4; 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 mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* 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 ***** */
#ifndef nsIXMLEncodingService_h__
#define nsIXMLEncodingService_h__
#include "nsISupports.h"
// {12BB8F11-2389-11d3-B3BF-00805F8A6670}
#define NS_IXML_ENCODING_SERVICE_IID \
{ 0x12bb8f11, 0x2389, 0x11d3, { 0xb3, 0xbf, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70 } }
class nsIXMLEncodingService : public nsISupports {
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IXML_ENCODING_SERVICE_IID)
NS_IMETHOD Start() = 0;
NS_IMETHOD End() = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIXMLEncodingService,
NS_IXML_ENCODING_SERVICE_IID)
#endif // nsIXMLEncodingService_h__

View File

@ -53,9 +53,6 @@ LIBXUL_LIBRARY = 1
CPPSRCS = \
nsObserverBase.cpp \
nsXMLEncodingObserver.cpp \
nsMetaCharsetObserver.cpp \
nsDetectionAdaptor.cpp \
nsDebugDetector.cpp \
nsCyrillicDetector.cpp \
nsDocumentCharsetInfo.cpp \

View File

@ -49,22 +49,16 @@
#include "nsMetaCharsetCID.h"
#include "nsICharsetDetector.h"
#include "nsICharsetAlias.h"
#include "nsMetaCharsetObserver.h"
#include "nsDocumentCharsetInfo.h"
#include "nsXMLEncodingObserver.h"
#include "nsICharsetDetectionAdaptor.h"
#include "nsICharsetDetectionObserver.h"
#include "nsDetectionAdaptor.h"
#include "nsIStringCharsetDetector.h"
#include "nsCyrillicDetector.h"
#include "nsDocumentCharsetInfoCID.h"
#include "nsXMLEncodingCID.h"
#include "nsCharsetDetectionAdaptorCID.h"
NS_GENERIC_FACTORY_CONSTRUCTOR(nsMetaCharsetObserver)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsDocumentCharsetInfo)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsXMLEncodingObserver)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsDetectionAdaptor)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsRUProbDetector)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsUKProbDetector)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsRUStringProbDetector)

View File

@ -42,10 +42,7 @@
#include "nsCharDetConstructors.h"
NS_DEFINE_NAMED_CID(NS_META_CHARSET_CID);
NS_DEFINE_NAMED_CID(NS_DOCUMENTCHARSETINFO_CID);
NS_DEFINE_NAMED_CID(NS_XML_ENCODING_CID);
NS_DEFINE_NAMED_CID(NS_CHARSET_DETECTION_ADAPTOR_CID);
NS_DEFINE_NAMED_CID(NS_RU_PROBDETECTOR_CID);
NS_DEFINE_NAMED_CID(NS_UK_PROBDETECTOR_CID);
NS_DEFINE_NAMED_CID(NS_RU_STRING_PROBDETECTOR_CID);
@ -57,10 +54,7 @@ NS_DEFINE_NAMED_CID(NS_LASTBLKDBG_DETECTOR_CID);
#endif /* INCLUDE_DBGDETECTOR */
static const mozilla::Module::CIDEntry kChardetCIDs[] = {
{ &kNS_META_CHARSET_CID, false, NULL, nsMetaCharsetObserverConstructor },
{ &kNS_DOCUMENTCHARSETINFO_CID, false, NULL, nsDocumentCharsetInfoConstructor },
{ &kNS_XML_ENCODING_CID, false, NULL, nsXMLEncodingObserverConstructor },
{ &kNS_CHARSET_DETECTION_ADAPTOR_CID, false, NULL, nsDetectionAdaptorConstructor },
{ &kNS_RU_PROBDETECTOR_CID, false, NULL, nsRUProbDetectorConstructor },
{ &kNS_UK_PROBDETECTOR_CID, false, NULL, nsUKProbDetectorConstructor },
{ &kNS_RU_STRING_PROBDETECTOR_CID, false, NULL, nsRUStringProbDetectorConstructor },
@ -74,10 +68,7 @@ static const mozilla::Module::CIDEntry kChardetCIDs[] = {
};
static const mozilla::Module::ContractIDEntry kChardetContracts[] = {
{ NS_META_CHARSET_CONTRACTID, &kNS_META_CHARSET_CID },
{ NS_DOCUMENTCHARSETINFO_CONTRACTID, &kNS_DOCUMENTCHARSETINFO_CID },
{ NS_XML_ENCODING_CONTRACTID, &kNS_XML_ENCODING_CID },
{ NS_CHARSET_DETECTION_ADAPTOR_CONTRACTID, &kNS_CHARSET_DETECTION_ADAPTOR_CID },
{ NS_CHARSET_DETECTOR_CONTRACTID_BASE "ruprob", &kNS_RU_PROBDETECTOR_CID },
{ NS_CHARSET_DETECTOR_CONTRACTID_BASE "ukprob", &kNS_UK_PROBDETECTOR_CID },
{ NS_STRCDETECTOR_CONTRACTID_BASE "ruprob", &kNS_RU_STRING_PROBDETECTOR_CID },

View File

@ -1,171 +0,0 @@
/* -*- Mode: C; tab-width: 4; 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 mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Pierre Phaneuf <pp@ludusdesign.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 "nsString.h"
#include "plstr.h"
#include "pratom.h"
#include "nsCharDetDll.h"
#include "nsIParser.h"
#include "nsIDocument.h"
#include "nsDetectionAdaptor.h"
#include "nsIContentSink.h"
//--------------------------------------------------------------
NS_IMETHODIMP nsMyObserver::Notify(
const char* aCharset, nsDetectionConfident aConf)
{
nsresult rv = NS_OK;
if(mWeakRefParser) {
nsCAutoString existingCharset;
PRInt32 existingSource;
mWeakRefParser->GetDocumentCharset(existingCharset, existingSource);
if (existingSource >= kCharsetFromAutoDetection)
return NS_OK;
}
if(!mCharset.Equals(aCharset)) {
if(mNotifyByReload) {
rv = mWebShellSvc->StopDocumentLoad();
rv = mWebShellSvc->ReloadDocument(aCharset, kCharsetFromAutoDetection);
} else {
nsDependentCString newcharset(aCharset);
if (mWeakRefParser) {
mWeakRefParser->SetDocumentCharset(newcharset, kCharsetFromAutoDetection);
nsCOMPtr<nsIContentSink> contentSink = mWeakRefParser->GetContentSink();
if (contentSink)
contentSink->SetDocumentCharset(newcharset);
}
if(mWeakRefDocument)
mWeakRefDocument->SetDocumentCharacterSet(newcharset);
}
}
return NS_OK;
}
//--------------------------------------------------------------
NS_IMETHODIMP nsMyObserver::Init( nsIWebShellServices* aWebShellSvc,
nsIDocument* aDocument,
nsIParser* aParser,
const char* aCharset,
const char* aCommand)
{
if(aCommand) {
mCommand = aCommand;
}
if(aCharset) {
mCharset = aCharset;
}
if(aDocument) {
mWeakRefDocument = aDocument;
}
if(aParser) {
mWeakRefParser = aParser;
}
if(nsnull != aWebShellSvc)
{
mWebShellSvc = aWebShellSvc;
return NS_OK;
}
return NS_ERROR_ILLEGAL_VALUE;
}
//--------------------------------------------------------------
NS_IMPL_ISUPPORTS1 ( nsMyObserver ,nsICharsetDetectionObserver)
//--------------------------------------------------------------
nsDetectionAdaptor::nsDetectionAdaptor( void )
{
mDontFeedToDetector = PR_TRUE;
}
//--------------------------------------------------------------
nsDetectionAdaptor::~nsDetectionAdaptor()
{
}
//--------------------------------------------------------------
NS_IMPL_ISUPPORTS2 (nsDetectionAdaptor, nsIParserFilter, nsICharsetDetectionAdaptor)
//--------------------------------------------------------------
NS_IMETHODIMP nsDetectionAdaptor::Init(
nsIWebShellServices* aWebShellSvc, nsICharsetDetector *aDetector,
nsIDocument* aDocument, nsIParser* aParser, const char* aCharset,
const char* aCommand)
{
if((nsnull != aWebShellSvc) && (nsnull != aDetector) && (nsnull != aCharset))
{
nsresult rv = NS_OK;
mObserver = new nsMyObserver();
if(!mObserver)
return NS_ERROR_OUT_OF_MEMORY;
rv = mObserver->Init(aWebShellSvc, aDocument, aParser, aCharset, aCommand);
if(NS_SUCCEEDED(rv)) {
rv = aDetector->Init(mObserver.get());
if(NS_SUCCEEDED(rv)) {
mDetector = aDetector;
mDontFeedToDetector = PR_FALSE;
return NS_OK;
}
}
}
return NS_ERROR_ILLEGAL_VALUE;
}
//--------------------------------------------------------------
NS_IMETHODIMP nsDetectionAdaptor::RawBuffer
(const char * buffer, PRUint32 * buffer_length)
{
if((mDontFeedToDetector) || (!mDetector))
return NS_OK;
nsresult rv = NS_OK;
rv = mDetector->DoIt((const char*)buffer, *buffer_length, &mDontFeedToDetector);
if(mObserver)
mObserver->SetNotifyByReload(PR_TRUE);
return NS_OK;
}
//--------------------------------------------------------------
NS_IMETHODIMP nsDetectionAdaptor::Finish()
{
if((mDontFeedToDetector) || (!mDetector))
return NS_OK;
nsresult rv = NS_OK;
rv = mDetector->Done();
return NS_OK;
}

View File

@ -1,131 +0,0 @@
/* -*- Mode: C; tab-width: 4; 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 mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* 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 ***** */
#ifndef nsDetectionAdaptor_h__
#define nsDetectionAdaptor_h__
#include "nsCOMPtr.h"
#include "nsIWebShellServices.h"
#include "nsIParserFilter.h"
static NS_DEFINE_IID(kIParserFilterIID, NS_IPARSERFILTER_IID);
class nsIDocument;
class CToken;
//--------------------------------------------------------------
class nsMyObserver : public nsICharsetDetectionObserver
{
public:
NS_DECL_ISUPPORTS
public:
nsMyObserver( void )
{
mWebShellSvc = nsnull;
mNotifyByReload = PR_FALSE;
mWeakRefDocument = nsnull;
mWeakRefParser = nsnull;
}
virtual ~nsMyObserver( void )
{
// do not release nor delete mWeakRefDocument
// do not release nor delete mWeakRefParser
}
// Methods to support nsICharsetDetectionAdaptor
NS_IMETHOD Init(nsIWebShellServices* aWebShellSvc,
nsIDocument* aDocument,
nsIParser* aParser,
const char* aCharset,
const char* aCommand);
// Methods to support nsICharsetDetectionObserver
NS_IMETHOD Notify(const char* aCharset, nsDetectionConfident aConf);
void SetNotifyByReload(PRBool aByReload) { mNotifyByReload = aByReload; }
private:
nsCOMPtr<nsIWebShellServices> mWebShellSvc;
PRBool mNotifyByReload;
//The adaptor is owned by parser as filter, and adaptor owns detector,
//which in turn owns observer. Parser also live within the lifespan of
//document. The ownership tree is like:
// document->parser->adaptor->detector->observer
//We do not want to own Document & Parser to avoid ownership loop. That
//will cause memory leakage.
//If in future this chain got changed, ie. parser outlives document, or
//detector outlives parser, we might want to change weak reference here.
nsIDocument* mWeakRefDocument;
nsIParser* mWeakRefParser;
nsCAutoString mCharset;
nsCAutoString mCommand;
};
class nsDetectionAdaptor :
public nsIParserFilter,
public nsICharsetDetectionAdaptor
{
public:
NS_DECL_ISUPPORTS
public:
nsDetectionAdaptor( void );
virtual ~nsDetectionAdaptor( void );
// Methods to support nsICharsetDetectionAdaptor
NS_IMETHOD Init(nsIWebShellServices* aWebShellSvc, nsICharsetDetector *aDetector,
nsIDocument* aDocument,
nsIParser* aParser,
const char* aCharset,
const char* aCommand=nsnull);
// Methode to suppor nsIParserFilter
NS_IMETHOD RawBuffer(const char * buffer, PRUint32 * buffer_length) ;
NS_IMETHOD Finish();
// really don't care the following two, only because they are defined
// in nsIParserFilter.h
NS_IMETHOD WillAddToken(CToken & token) { return NS_OK; }
NS_IMETHOD ProcessTokens( void ) {return NS_OK;}
private:
nsCOMPtr<nsICharsetDetector> mDetector;
PRBool mDontFeedToDetector;
nsCOMPtr<nsMyObserver> mObserver;
};
#endif /* nsDetectionAdaptor_h__ */

View File

@ -1,440 +0,0 @@
/* -*- Mode: C; tab-width: 4; 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 mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* 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 "nsDeque.h"
#include "nsICharsetAlias.h"
#include "nsMetaCharsetObserver.h"
#include "nsIMetaCharsetService.h"
#include "nsIElementObserver.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsISupports.h"
#include "nsCRT.h"
#include "nsIParser.h"
#include "pratom.h"
#include "nsCharDetDll.h"
#include "nsIServiceManager.h"
#include "nsObserverBase.h"
#include "nsWeakReference.h"
#include "nsIParserService.h"
#include "nsParserCIID.h"
#include "nsMetaCharsetCID.h"
#include "nsReadableUtils.h"
#include "nsUnicharUtils.h"
static NS_DEFINE_CID(kCharsetAliasCID, NS_CHARSETALIAS_CID);
static const eHTMLTags gWatchTags[] =
{ eHTMLTag_meta,
eHTMLTag_unknown
};
//-------------------------------------------------------------------------
nsMetaCharsetObserver::nsMetaCharsetObserver()
{
bMetaCharsetObserverStarted = PR_FALSE;
nsresult res;
mAlias = nsnull;
nsCOMPtr<nsICharsetAlias> calias(do_GetService(kCharsetAliasCID, &res));
if(NS_SUCCEEDED(res)) {
mAlias = calias;
}
}
//-------------------------------------------------------------------------
nsMetaCharsetObserver::~nsMetaCharsetObserver()
{
}
//-------------------------------------------------------------------------
NS_IMPL_ADDREF ( nsMetaCharsetObserver )
NS_IMPL_RELEASE ( nsMetaCharsetObserver )
// Use the new scheme
NS_IMPL_QUERY_INTERFACE4(nsMetaCharsetObserver,
nsIElementObserver,
nsIObserver,
nsIMetaCharsetService,
nsISupportsWeakReference)
//-------------------------------------------------------------------------
NS_IMETHODIMP nsMetaCharsetObserver::Notify(
PRUint32 aDocumentID,
const PRUnichar* aTag,
PRUint32 numOfAttributes,
const PRUnichar* nameArray[],
const PRUnichar* valueArray[])
{
if(!nsDependentString(aTag).LowerCaseEqualsLiteral("meta"))
return NS_ERROR_ILLEGAL_VALUE;
else
return Notify(aDocumentID, numOfAttributes, nameArray, valueArray);
}
//-------------------------------------------------------------------------
NS_IMETHODIMP nsMetaCharsetObserver::Notify(
PRUint32 aDocumentID,
eHTMLTags aTag,
PRUint32 numOfAttributes,
const PRUnichar* nameArray[],
const PRUnichar* valueArray[])
{
if(eHTMLTag_meta != aTag)
return NS_ERROR_ILLEGAL_VALUE;
else
return Notify(aDocumentID, numOfAttributes, nameArray, valueArray);
}
NS_IMETHODIMP nsMetaCharsetObserver::Notify(
PRUint32 aDocumentID,
PRUint32 numOfAttributes,
const PRUnichar* nameArray[],
const PRUnichar* valueArray[])
{
nsDeque keys(0);
nsDeque values(0);
PRUint32 i;
for(i=0;i<numOfAttributes;i++)
{
keys.Push((void*)nameArray[i]);
values.Push((void*)valueArray[i]);
}
return NS_OK;//Notify((nsISupports*)aDocumentID, &keys, &values);
}
NS_IMETHODIMP nsMetaCharsetObserver::Notify(
nsISupports* aDocShell,
nsISupports* aChannel,
const PRUnichar* aTag,
const nsTArray<nsString>* keys,
const nsTArray<nsString>* values,
const PRUint32 aFlags)
{
nsresult result = NS_OK;
// bug 125317 - document.write content is already an unicode content.
if (!(aFlags & nsIElementObserver::IS_DOCUMENT_WRITE)) {
if(!nsDependentString(aTag).LowerCaseEqualsLiteral("meta")) {
result = NS_ERROR_ILLEGAL_VALUE;
}
else {
result = Notify(aDocShell, aChannel, keys, values);
}
}
return result;
}
#define IS_SPACE_CHARS(ch) (ch == ' ' || ch == '\b' || ch == '\r' || ch == '\n')
NS_IMETHODIMP nsMetaCharsetObserver::Notify(
nsISupports* aDocShell,
nsISupports* aChannel,
const nsTArray<nsString>* keys,
const nsTArray<nsString>* values)
{
NS_PRECONDITION(keys!=nsnull && values!=nsnull,"Need key-value pair");
PRUint32 numOfAttributes = keys->Length();
NS_ASSERTION( numOfAttributes == values->Length(), "size mismatch");
nsresult res=NS_OK;
#ifdef DEBUG
PRUnichar Uxcommand[]={'X','_','C','O','M','M','A','N','D','\0'};
PRUnichar UcharsetSource[]={'c','h','a','r','s','e','t','S','o','u','r','c','e','\0'};
PRUnichar Ucharset[]={'c','h','a','r','s','e','t','\0'};
NS_ASSERTION(numOfAttributes >= 3, "should have at least 3 private attribute");
NS_ASSERTION(0==nsCRT::strcmp(Uxcommand,(keys->ElementAt(numOfAttributes-1)).get()),"last name should be 'X_COMMAND'" );
NS_ASSERTION(0==nsCRT::strcmp(UcharsetSource,(keys->ElementAt(numOfAttributes-2)).get()),"2nd last name should be 'charsetSource'" );
NS_ASSERTION(0==nsCRT::strcmp(Ucharset,(keys->ElementAt(numOfAttributes-3)).get()),"3rd last name should be 'charset'" );
#endif
NS_ASSERTION(mAlias, "Didn't get nsICharsetAlias in constructor");
if(nsnull == mAlias)
return NS_ERROR_ABORT;
// we need at least 5 - HTTP-EQUIV, CONTENT and 3 private
if(numOfAttributes >= 5 )
{
const nsString& srcStr = values->ElementAt(numOfAttributes-2);
PRInt32 err;
PRInt32 src = srcStr.ToInteger(&err);
// if we cannot convert the string into PRInt32, return error
NS_ASSERTION(NS_SUCCEEDED(err), "cannot get charset source");
if(NS_FAILED(err))
return NS_ERROR_ILLEGAL_VALUE;
if(kCharsetFromMetaTag <= src)
return NS_OK; // current charset has higher priority. don't bother to do the following
const PRUnichar *httpEquivValue=nsnull;
const PRUnichar *contentValue=nsnull;
const PRUnichar *charsetValue=nsnull;
for (PRUint32 i = 0; i < numOfAttributes - 3; i++)
{
const PRUnichar *keyStr;
keyStr = keys->ElementAt(i).get();
//Change 3.190 in nsHTMLTokens.cpp allow ws/tab/cr/lf exist before
// and after text value, this need to be skipped before comparison
while(IS_SPACE_CHARS(*keyStr))
keyStr++;
if(Substring(keyStr, keyStr+10).LowerCaseEqualsLiteral("http-equiv"))
httpEquivValue = values->ElementAt(i).get();
else if(Substring(keyStr, keyStr+7).LowerCaseEqualsLiteral("content"))
contentValue = values->ElementAt(i).get();
else if (Substring(keyStr, keyStr+7).LowerCaseEqualsLiteral("charset"))
charsetValue = values->ElementAt(i).get();
}
NS_NAMED_LITERAL_STRING(contenttype, "Content-Type");
NS_NAMED_LITERAL_STRING(texthtml, "text/html");
if(nsnull == httpEquivValue || nsnull == contentValue)
return NS_OK;
while(IS_SPACE_CHARS(*httpEquivValue))
++httpEquivValue;
// skip opening quote
if (*httpEquivValue == '\'' || *httpEquivValue == '\"')
++httpEquivValue;
while(IS_SPACE_CHARS(*contentValue))
++contentValue;
// skip opening quote
if (*contentValue == '\'' || *contentValue == '\"')
++contentValue;
if(
Substring(httpEquivValue,
httpEquivValue+contenttype.Length()).Equals(contenttype,
nsCaseInsensitiveStringComparator())
&&
Substring(contentValue,
contentValue+texthtml.Length()).Equals(texthtml,
nsCaseInsensitiveStringComparator())
)
{
nsCAutoString newCharset;
if (nsnull == charsetValue)
{
nsAutoString contentPart1(contentValue+9); // after "text/html"
PRInt32 start = contentPart1.RFind("charset=", PR_TRUE ) ;
PRInt32 end = contentPart1.Length();
if(kNotFound != start)
{
start += 8; // 8 = "charset=".length
while (start < end && contentPart1.CharAt(start) == PRUnichar(' '))
++start;
if (start < end) {
end = contentPart1.FindCharInSet("\'\"; ", start);
if(kNotFound == end )
end = contentPart1.Length();
NS_ASSERTION(end>=start, "wrong index");
LossyCopyUTF16toASCII(Substring(contentPart1, start, end-start),
newCharset);
}
}
}
else
{
LossyCopyUTF16toASCII(nsDependentString(charsetValue), newCharset);
}
nsCAutoString charsetString;
charsetString.AssignWithConversion(values->ElementAt(numOfAttributes-3));
if (!newCharset.IsEmpty())
{
if(! newCharset.Equals(charsetString, nsCaseInsensitiveCStringComparator()))
{
PRBool same = PR_FALSE;
nsresult res2 = mAlias->Equals( newCharset, charsetString , &same);
if(NS_SUCCEEDED(res2) && (! same))
{
nsCAutoString preferred;
res2 = mAlias->GetPreferred(newCharset, preferred);
if(NS_SUCCEEDED(res2))
{
// following charset should have been detected by parser
if (!preferred.EqualsLiteral("UTF-16") &&
!preferred.EqualsLiteral("UTF-16BE") &&
!preferred.EqualsLiteral("UTF-16LE") &&
!preferred.EqualsLiteral("UTF-32") &&
!preferred.EqualsLiteral("UTF-32BE") &&
!preferred.EqualsLiteral("UTF-32LE")) {
// Propagate the error message so that the parser can
// shutdown correctly. - Ref. Bug 96440
res = NotifyDocShell(aDocShell,
aChannel,
preferred.get(),
kCharsetFromMetaTag);
}
} // if(NS_SUCCEEDED(res)
}
}
else {
res = NS_HTMLPARSER_VALID_META_CHARSET;
} // if EqualIgnoreCase
} // if !newCharset.IsEmpty()
} // if
}
else
{
nsAutoString compatCharset;
if (NS_SUCCEEDED(GetCharsetFromCompatibilityTag(keys, values, compatCharset)))
{
if (!compatCharset.IsEmpty()) {
res = NotifyDocShell(aDocShell,
aChannel,
NS_ConvertUTF16toUTF8(compatCharset).get(),
kCharsetFromMetaTag);
}
}
}
return res;
}
//-------------------------------------------------------------------------
NS_IMETHODIMP nsMetaCharsetObserver::GetCharsetFromCompatibilityTag(
const nsTArray<nsString>* keys,
const nsTArray<nsString>* values,
nsAString& aCharset)
{
if (!mAlias)
return NS_ERROR_ABORT;
aCharset.Truncate(0);
nsresult res = NS_OK;
// support for non standard case for compatibility
// e.g. <META charset="ISO-8859-1">
PRUint32 numOfAttributes = keys->Length();
if ((numOfAttributes >= 3) &&
(keys->ElementAt(0).LowerCaseEqualsLiteral("charset")))
{
const nsString& srcStr = values->ElementAt(numOfAttributes-2);
PRInt32 err;
PRInt32 src = srcStr.ToInteger(&err);
// if we cannot convert the string into PRInt32, return error
if (NS_FAILED(err))
return NS_ERROR_ILLEGAL_VALUE;
// current charset have a lower priority
if (kCharsetFromMetaTag > src)
{
nsCAutoString newCharset;
newCharset.AssignWithConversion(values->ElementAt(0).get());
nsCAutoString preferred;
res = mAlias->GetPreferred(newCharset,
preferred);
if (NS_SUCCEEDED(res))
{
// compare against the current charset,
// also some charsets which should have been found in
// the BOM detection.
const nsString& currentCharset = values->ElementAt(numOfAttributes-3);
if (!preferred.Equals(NS_LossyConvertUTF16toASCII(currentCharset)) &&
!preferred.EqualsLiteral("UTF-16") &&
!preferred.EqualsLiteral("UTF-16BE") &&
!preferred.EqualsLiteral("UTF-16LE") &&
!preferred.EqualsLiteral("UTF-32") &&
!preferred.EqualsLiteral("UTF-32BE") &&
!preferred.EqualsLiteral("UTF-32LE"))
AppendASCIItoUTF16(preferred, aCharset);
}
}
}
return res;
}
//-------------------------------------------------------------------------
NS_IMETHODIMP nsMetaCharsetObserver::Observe(nsISupports *aSubject,
const char *aTopic,
const PRUnichar *aData)
{
nsresult rv = NS_OK;
if (!nsCRT::strcmp(aTopic, "parser-service-start")) {
rv = Start();
}
return rv;
}
//-------------------------------------------------------------------------
NS_IMETHODIMP nsMetaCharsetObserver::Start()
{
nsresult rv = NS_OK;
if (!bMetaCharsetObserverStarted) {
bMetaCharsetObserverStarted = PR_TRUE;
nsCOMPtr<nsIParserService> parserService(do_GetService(NS_PARSERSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv))
return rv;
rv = parserService->RegisterObserver(this,
NS_LITERAL_STRING("text/html"),
gWatchTags);
}
return rv;
}
//-------------------------------------------------------------------------
NS_IMETHODIMP nsMetaCharsetObserver::End()
{
nsresult rv = NS_OK;
if (bMetaCharsetObserverStarted) {
bMetaCharsetObserverStarted = PR_FALSE;
nsCOMPtr<nsIParserService> parserService(do_GetService(NS_PARSERSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv))
return rv;
rv = parserService->UnregisterObserver(this, NS_LITERAL_STRING("text/html"));
}
return rv;
}
//==========================================================================

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