Merge with mozilla-central as of c5dc9d84d476, and restore obj-to-boolean fixes
Merge with mozilla-central as of c5dc9d84d476, and restore obj-to-boolean fixes
from 2b107c27dedf (accidentally backed out during an earlier merge). 22 test
suite failures for -L lc2 lc3 spidermonkey-n slow-n, probably needs some guard
work in TRY_BRANCH_AFTER_COND.
--- a/.hgignore
+++ b/.hgignore
@@ -11,23 +11,16 @@
^\.mozconfig\.out$
^configure$
^config\.cache$
^config\.log$
# Empty marker file that's generated when we check out NSS
^security/manager/\.nss\.checkout$
-# subtrees from other repositories
-^nsprpub/
-^dbm/
-^security/nss/
-^security/coreconf/
-^security/dbm/
-
# Build directories
^obj-
^objdir-
# Build directories for js shell
_DBG\.OBJ/
_OPT\.OBJ/
--- a/.hgtags
+++ b/.hgtags
@@ -1,4 +1,4 @@
df7a3c8ffeeaba229067efee5a20e21dae0dd877 MOZILLA_1_9_a4_BASE
4209e16b58411750ac73f761023e46b76b793e2c MOZILLA_1_9_a6_BASE
66a5c7bce7ee86a820d3c0d54fa07cb719be751c MOZILLA_1_9_a7_BASE
-d1a7596d788757799a1aa5f5d09eac32ff811831 tested
+caeba7562e495a9f604984df0b48b6f99bec3e2e FENNEC_M4
--- a/accessible/public/nsIAccessibleTreeCache.idl
+++ b/accessible/public/nsIAccessibleTreeCache.idl
@@ -42,17 +42,17 @@
interface nsIAccessible;
/**
* A private interface to operate with tree accessible.
*
* @status UNDER_REVIEW
*/
-[uuid(7e0f50b0-6444-4372-b00f-4ce81c6b058a)]
+[uuid(1dde5c3b-bede-43d1-aabf-dabc461113bd)]
interface nsIAccessibleTreeCache : nsISupports
{
/**
* Get tree item from cache according to row and column, create if doesn't
* exist in cache.
*
* @param aRow the given row index
* @param aColumn the given column object. If is is nsnull then primary
@@ -77,16 +77,22 @@ interface nsIAccessibleTreeCache : nsISu
* @param aStartRow row index invalidation starts from
* @param aEndRow row index invalidation ends, -1 means last row index
* @param aStartCol column index invalidation starts from
* @param aEndCol column index invalidation ends, -1 mens last column
* index
*/
void treeViewInvalidated(in long aStartRow, in long aEndRow,
in long aStartCol, in long aEndCol);
+
+ /**
+ * Invalidates children created for previous tree view.
+ */
+ void treeViewChanged();
+
};
[uuid(b71532f9-53b2-4647-a5b2-1c5f57e9aed6)]
interface nsPIAccessibleTreeItem : nsISupports
{
/**
* Get/set cached name.
*/
--- a/accessible/src/atk/nsAccessibleWrap.cpp
+++ b/accessible/src/atk/nsAccessibleWrap.cpp
@@ -366,18 +366,19 @@ void nsAccessibleWrap::SetMaiHyperlink(M
}
}
NS_IMETHODIMP nsAccessibleWrap::GetNativeInterface(void **aOutAccessible)
{
*aOutAccessible = nsnull;
if (!mAtkObject) {
- if (!IsEmbeddedObject(this)) {
- // We don't create ATK objects for nsIAccessible plain text leaves
+ if (!mWeakShell || !IsEmbeddedObject(this)) {
+ // We don't create ATK objects for node which has been shutdown, or
+ // nsIAccessible plain text leaves
return NS_ERROR_FAILURE;
}
GType type = GetMaiAtkType(CreateMaiInterfaces());
NS_ENSURE_TRUE(type, NS_ERROR_FAILURE);
mAtkObject =
reinterpret_cast<AtkObject *>
(g_object_new(type, NULL));
@@ -1096,22 +1097,28 @@ nsAccessibleWrap *GetAccessibleWrap(AtkO
}
NS_IMETHODIMP
nsAccessibleWrap::FireAccessibleEvent(nsIAccessibleEvent *aEvent)
{
nsresult rv = nsAccessible::FireAccessibleEvent(aEvent);
NS_ENSURE_SUCCESS(rv, rv);
+ return FirePlatformEvent(aEvent);
+}
+
+nsresult
+nsAccessibleWrap::FirePlatformEvent(nsIAccessibleEvent *aEvent)
+{
nsCOMPtr<nsIAccessible> accessible;
aEvent->GetAccessible(getter_AddRefs(accessible));
NS_ENSURE_TRUE(accessible, NS_ERROR_FAILURE);
PRUint32 type = 0;
- rv = aEvent->GetEventType(&type);
+ nsresult rv = aEvent->GetEventType(&type);
NS_ENSURE_SUCCESS(rv, rv);
AtkObject *atkObj = nsAccessibleWrap::GetAtkObject(accessible);
// We don't create ATK objects for nsIAccessible plain text leaves,
// just return NS_OK in such case
if (!atkObj) {
NS_ASSERTION(type == nsIAccessibleEvent::EVENT_ASYNCH_SHOW ||
--- a/accessible/src/atk/nsAccessibleWrap.h
+++ b/accessible/src/atk/nsAccessibleWrap.h
@@ -111,16 +111,18 @@ public:
static const char * ReturnString(nsAString &aString) {
static nsCString returnedString;
returnedString = NS_ConvertUTF16toUTF8(aString);
return returnedString.get();
}
protected:
+ virtual nsresult FirePlatformEvent(nsIAccessibleEvent *aEvent);
+
nsresult FireAtkStateChangeEvent(nsIAccessibleEvent *aEvent,
AtkObject *aObject);
nsresult FireAtkTextChangedEvent(nsIAccessibleEvent *aEvent,
AtkObject *aObject);
nsresult FireAtkPropChangedEvent(nsIAccessibleEvent *aEvent,
AtkObject *aObject);
nsresult FireAtkShowHideEvent(nsIAccessibleEvent *aEvent,
AtkObject *aObject, PRBool aIsAdded);
--- a/accessible/src/base/nsARIAMap.cpp
+++ b/accessible/src/base/nsARIAMap.cpp
@@ -58,16 +58,17 @@
static const nsStateMapEntry kEndEntry = {nsnull, 0, 0}; // To fill in array of state mappings
nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
{
{"alert", nsIAccessibleRole::ROLE_ALERT, eNameLabelOrTitle, eNoValue, kNoReqStates, kEndEntry},
{"alertdialog", nsIAccessibleRole::ROLE_ALERT, eNameOkFromChildren, eNoValue, kNoReqStates, kEndEntry},
{"application", nsIAccessibleRole::ROLE_APPLICATION, eNameLabelOrTitle, eNoValue, kNoReqStates, kEndEntry},
+ {"article", nsIAccessibleRole::ROLE_DOCUMENT, eNameLabelOrTitle, eNoValue, kNoReqStates, kEndEntry},
{"button", nsIAccessibleRole::ROLE_PUSHBUTTON, eNameOkFromChildren, eNoValue, kNoReqStates,
{&nsAccessibilityAtoms::aria_pressed, kBoolState, nsIAccessibleStates::STATE_PRESSED},
{&nsAccessibilityAtoms::aria_pressed, "mixed", nsIAccessibleStates::STATE_MIXED}, kEndEntry},
{"checkbox", nsIAccessibleRole::ROLE_CHECKBUTTON, eNameOkFromChildren, eNoValue, nsIAccessibleStates::STATE_CHECKABLE,
{&nsAccessibilityAtoms::aria_checked, kBoolState, nsIAccessibleStates::STATE_CHECKED},
{&nsAccessibilityAtoms::aria_checked, "mixed", nsIAccessibleStates::STATE_MIXED},
{&nsAccessibilityAtoms::aria_readonly, kBoolState, nsIAccessibleStates::STATE_READONLY}, kEndEntry},
{"columnheader", nsIAccessibleRole::ROLE_COLUMNHEADER, eNameOkFromChildren, eNoValue, kNoReqStates,
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -1323,39 +1323,36 @@ NS_IMETHODIMP nsAccessibilityService::Ge
nsCOMPtr<nsIDOMElement> element(do_QueryInterface(aNode));
if (element) {
element->GetAttribute(NS_LITERAL_STRING("type"), attrib);
if (attrib.EqualsLiteral("statusbarpanel"))
printf("## aaronl debugging attribute\n");
}
#endif
- nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
- if (content && content->Tag() == nsAccessibilityAtoms::map) {
- // Don't walk into maps, they take up no space.
- // The nsHTMLAreaAccessible's they contain are attached as
- // children of the appropriate nsHTMLImageAccessible.
- *aIsHidden = PR_TRUE;
- return NS_OK;
- }
-
// Check to see if we already have an accessible for this
// node in the cache
nsCOMPtr<nsIAccessNode> accessNode;
GetCachedAccessNode(aNode, aWeakShell, getter_AddRefs(accessNode));
nsCOMPtr<nsIAccessible> newAcc;
if (accessNode) {
- // Retrieved from cache
- // QI might not succeed if it's a node that's not accessible
+ // Retrieved from cache. QI might not succeed if it's a node that's not
+ // accessible. In this case try to create new accessible because one and
+ // the same DOM node may be accessible or not in time (for example,
+ // when it is visible or hidden).
newAcc = do_QueryInterface(accessNode);
- NS_IF_ADDREF(*aAccessible = newAcc);
- return NS_OK;
+ if (newAcc) {
+ NS_ADDREF(*aAccessible = newAcc);
+ return NS_OK;
+ }
}
+ nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
+
// No cache entry, so we must create the accessible
// Check to see if hidden first
nsCOMPtr<nsIDocument> nodeIsDoc;
if (!content) {
// This happens when we're on the document node, which will not QI to an
// nsIContent.
nodeIsDoc = do_QueryInterface(aNode);
NS_ENSURE_TRUE(nodeIsDoc, NS_ERROR_FAILURE); // No content, and not doc node
@@ -1456,31 +1453,51 @@ NS_IMETHODIMP nsAccessibilityService::Ge
*aIsHidden = PR_TRUE;
return NS_OK;
}
}
frame->GetAccessible(getter_AddRefs(newAcc));
return InitAccessible(newAcc, aAccessible, nsnull);
}
+ PRBool isHTML = content->IsNodeOfType(nsINode::eHTML);
+ if (isHTML && content->Tag() == nsAccessibilityAtoms::map) {
+ // Create hyper text accessible for HTML map if it is used to group links
+ // (see http://www.w3.org/TR/WCAG10-HTML-TECHS/#group-bypass). If the HTML
+ // map doesn't have 'name' attribute (or has empty name attribute) then we
+ // suppose it is used for links grouping. Otherwise we think it is used in
+ // conjuction with HTML image element and in this case we don't create any
+ // accessible for it and don't walk into it. The accessibles for HTML area
+ // (nsHTMLAreaAccessible) the map contains are attached as children of the
+ // appropriate accessible for HTML image (nsHTMLImageAccessible).
+ nsAutoString name;
+ content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::name, name);
+ if (!name.IsEmpty()) {
+ *aIsHidden = PR_TRUE;
+ return NS_OK;
+ }
+
+ nsresult rv = CreateHyperTextAccessible(frame, getter_AddRefs(newAcc));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
nsRoleMapEntry *roleMapEntry = nsAccUtils::GetRoleMapEntry(aNode);
if (roleMapEntry && !nsCRT::strcmp(roleMapEntry->roleString, "presentation") &&
!content->IsFocusable()) { // For presentation only
// Only create accessible for role of "presentation" if it is focusable --
// in that case we need an accessible in case it gets focused, we
// don't want focus ever to be 'lost'
return NS_OK;
}
// Elements may implement nsIAccessibleProvider via XBL. This allows them to
// say what kind of accessible to create.
nsresult rv = GetAccessibleByType(aNode, getter_AddRefs(newAcc));
NS_ENSURE_SUCCESS(rv, rv);
-
- PRBool isHTML = content->IsNodeOfType(nsINode::eHTML);
+
if (!newAcc && !isHTML) {
if (content->GetNameSpaceID() == kNameSpaceID_SVG &&
content->Tag() == nsAccessibilityAtoms::svg) {
newAcc = new nsEnumRoleAccessible(aNode, aWeakShell,
nsIAccessibleRole::ROLE_DIAGRAM);
}
else if (content->GetNameSpaceID() == kNameSpaceID_MathML &&
content->Tag() == nsAccessibilityAtoms::math) {
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -2040,16 +2040,19 @@ NS_IMETHODIMP nsAccessible::GetFinalRole
return mDOMNode ? GetRole(aRole) : NS_ERROR_FAILURE; // Node already shut down
}
NS_IMETHODIMP
nsAccessible::GetAttributes(nsIPersistentProperties **aAttributes)
{
NS_ENSURE_ARG_POINTER(aAttributes); // In/out param. Created if necessary.
+ if (IsDefunct())
+ return NS_ERROR_FAILURE;
+
nsCOMPtr<nsIContent> content = GetRoleContent(mDOMNode);
if (!content) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIPersistentProperties> attributes = *aAttributes;
if (!attributes) {
// Create only if an array wasn't already passed in
@@ -2109,46 +2112,72 @@ nsAccessible::GetAttributes(nsIPersisten
if (!nsAccUtils::HasAccGroupAttrs(attributes)) {
// The role of an accessible can be pointed by ARIA attribute but ARIA
// posinset, level, setsize may be skipped. Therefore we calculate here
// these properties to map them into description.
// If accessible is invisible we don't want to calculate group ARIA
// attributes for it.
if ((role == nsIAccessibleRole::ROLE_LISTITEM ||
- role == nsIAccessibleRole::ROLE_MENUITEM ||
- role == nsIAccessibleRole::ROLE_RADIOBUTTON ||
- role == nsIAccessibleRole::ROLE_PAGETAB ||
- role == nsIAccessibleRole::ROLE_OPTION ||
- role == nsIAccessibleRole::ROLE_RADIOBUTTON ||
- role == nsIAccessibleRole::ROLE_OUTLINEITEM) &&
+ 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_RADIOBUTTON ||
+ role == nsIAccessibleRole::ROLE_OUTLINEITEM) &&
0 == (State(this) & nsIAccessibleStates::STATE_INVISIBLE)) {
+
+ PRUint32 baseRole = role;
+ if (role == nsIAccessibleRole::ROLE_CHECK_MENU_ITEM ||
+ role == nsIAccessibleRole::ROLE_RADIO_MENU_ITEM)
+ baseRole = nsIAccessibleRole::ROLE_MENUITEM;
+
nsCOMPtr<nsIAccessible> parent = GetParent();
NS_ENSURE_TRUE(parent, NS_ERROR_FAILURE);
PRInt32 positionInGroup = 0;
PRInt32 setSize = 0;
nsCOMPtr<nsIAccessible> sibling, nextSibling;
parent->GetFirstChild(getter_AddRefs(sibling));
NS_ENSURE_TRUE(sibling, NS_ERROR_FAILURE);
PRBool foundCurrent = PR_FALSE;
- PRUint32 siblingRole;
+ PRUint32 siblingRole, siblingBaseRole;
while (sibling) {
sibling->GetFinalRole(&siblingRole);
- if (siblingRole == role &&
+
+ 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 &&
!(State(sibling) & nsIAccessibleStates::STATE_INVISIBLE)) {
++ setSize;
if (!foundCurrent) {
++ positionInGroup;
if (sibling == this)
foundCurrent = PR_TRUE;
}
}
+
+ // If the sibling is separator
+ if (siblingRole == nsIAccessibleRole::ROLE_SEPARATOR) {
+ if (foundCurrent) // the our group is ended
+ break;
+
+ // not our group, continue the searching
+ positionInGroup = 0;
+ setSize = 0;
+ }
+
sibling->GetNextSibling(getter_AddRefs(nextSibling));
sibling = nextSibling;
}
PRInt32 groupLevel = 0;
if (role == nsIAccessibleRole::ROLE_OUTLINEITEM) {
groupLevel = 1;
nsCOMPtr<nsIAccessible> nextParent;
@@ -2463,34 +2492,49 @@ nsAccessible::GetARIAState(PRUint32 *aSt
return NS_OK;
}
PRUint32 index = 0;
while (MappedAttrState(content, aState, &nsARIAMap::gWAIUnivStateMap[index])) {
++ index;
}
- if (!mRoleMapEntry)
- return NS_OK;
-
- // Once DHTML role is used, we're only readonly if DHTML readonly used
- *aState &= ~nsIAccessibleStates::STATE_READONLY;
-
- if (content->HasAttr(kNameSpaceID_None, content->GetIDAttributeName())) {
- // If has a role & ID and aria-activedescendant on the container, assume focusable
- nsIContent *ancestorContent = content;
- while ((ancestorContent = ancestorContent->GetParent()) != nsnull) {
- if (ancestorContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_activedescendant)) {
- // ancestor has activedescendant property, this content could be active
- *aState |= nsIAccessibleStates::STATE_FOCUSABLE;
- break;
+ if (mRoleMapEntry) {
+ // Once DHTML role is used, we're only readonly if DHTML readonly used
+ *aState &= ~nsIAccessibleStates::STATE_READONLY;
+
+ if (content->HasAttr(kNameSpaceID_None, content->GetIDAttributeName())) {
+ // If has a role & ID and aria-activedescendant on the container, assume focusable
+ nsIContent *ancestorContent = content;
+ while ((ancestorContent = ancestorContent->GetParent()) != nsnull) {
+ if (ancestorContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_activedescendant)) {
+ // ancestor has activedescendant property, this content could be active
+ *aState |= nsIAccessibleStates::STATE_FOCUSABLE;
+ break;
+ }
}
}
}
+ if (*aState & nsIAccessibleStates::STATE_FOCUSABLE) {
+ // Special case: aria-disabled propagates from ancestors down to any focusable descendant
+ nsIContent *ancestorContent = content;
+ while ((ancestorContent = ancestorContent->GetParent()) != nsnull) {
+ if (ancestorContent->AttrValueIs(kNameSpaceID_None, nsAccessibilityAtoms::aria_disabled,
+ nsAccessibilityAtoms::_true, eCaseMatters)) {
+ // ancestor has aria-disabled property, this is disabled
+ *aState |= nsIAccessibleStates::STATE_UNAVAILABLE;
+ break;
+ }
+ }
+ }
+
+ if (!mRoleMapEntry)
+ return NS_OK;
+
*aState |= mRoleMapEntry->state;
if (MappedAttrState(content, aState, &mRoleMapEntry->attributeMap1) &&
MappedAttrState(content, aState, &mRoleMapEntry->attributeMap2) &&
MappedAttrState(content, aState, &mRoleMapEntry->attributeMap3) &&
MappedAttrState(content, aState, &mRoleMapEntry->attributeMap4) &&
MappedAttrState(content, aState, &mRoleMapEntry->attributeMap5) &&
MappedAttrState(content, aState, &mRoleMapEntry->attributeMap6) &&
MappedAttrState(content, aState, &mRoleMapEntry->attributeMap7)) {
@@ -2654,18 +2698,21 @@ NS_IMETHODIMP
nsAccessible::GetNumActions(PRUint8 *aNumActions)
{
NS_ENSURE_ARG_POINTER(aNumActions);
*aNumActions = 0;
if (IsDefunct())
return NS_ERROR_FAILURE;
+ nsCOMPtr<nsIContent> content = GetRoleContent(mDOMNode);
+ if (!content)
+ return NS_OK;
+
// Check if it's an simple xlink.
- nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
if (nsAccUtils::IsXLink(content)) {
*aNumActions = 1;
return NS_OK;
}
// Has registered 'click' event handler.
PRBool isOnclick = nsAccUtils::HasListener(content,
NS_LITERAL_STRING("click"));
--- a/accessible/src/base/nsAccessible.h
+++ b/accessible/src/base/nsAccessible.h
@@ -241,16 +241,26 @@ protected:
*
* @param aAriaProperty - the ARIA property we're using
* @param aValue - value of the attribute
*
* @return - NS_OK_NO_ARIA_VALUE if there is no setted ARIA attribute
*/
nsresult GetAttrValue(nsIAtom *aAriaProperty, double *aValue);
+ /**
+ * Fires platform accessible event. It's notification method only. It does
+ * change nothing on Gecko side. Mostly you should use
+ * nsIAccessible::FireAccessibleEvent excepting special cases like we have
+ * in xul:tree accessible to lie to AT. Must be overridden in wrap classes.
+ *
+ * @param aEvent the accessible event to fire.
+ */
+ virtual nsresult FirePlatformEvent(nsIAccessibleEvent *aEvent) = 0;
+
// Data Members
nsCOMPtr<nsIAccessible> mParent;
nsIAccessible *mFirstChild, *mNextSibling;
nsRoleMapEntry *mRoleMapEntry; // Non-null indicates author-supplied role; possibly state & value as well
PRInt32 mAccChildCount;
};
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -80,17 +80,18 @@
PRUint32 nsDocAccessible::gLastFocusedAccessiblesState = 0;
nsIAtom *nsDocAccessible::gLastFocusedFrameType = nsnull;
//-----------------------------------------------------
// construction
//-----------------------------------------------------
nsDocAccessible::nsDocAccessible(nsIDOMNode *aDOMNode, nsIWeakReference* aShell):
nsHyperTextAccessibleWrap(aDOMNode, aShell), mWnd(nsnull),
- mScrollPositionChangedTicks(0), mIsContentLoaded(PR_FALSE), mIsLoadCompleteFired(PR_FALSE)
+ mScrollPositionChangedTicks(0), mIsContentLoaded(PR_FALSE),
+ mIsLoadCompleteFired(PR_FALSE), mInFlushPendingEvents(PR_FALSE)
{
// For GTK+ native window, we do nothing here.
if (!mDOMNode)
return;
// Because of the way document loading happens, the new nsIWidget is created before
// the old one is removed. Since it creates the nsDocAccessible, for a brief moment
// there can be 2 nsDocAccessible's for the content area, although for 2 different
@@ -511,18 +512,30 @@ NS_IMETHODIMP nsDocAccessible::GetCached
if (privateParent) {
privateParent->TestChildCache(accessible);
}
}
#endif
return NS_OK;
}
-NS_IMETHODIMP nsDocAccessible::CacheAccessNode(void *aUniqueID, nsIAccessNode *aAccessNode)
+NS_IMETHODIMP
+nsDocAccessible::CacheAccessNode(void *aUniqueID, nsIAccessNode *aAccessNode)
{
+ // If there is an access node for the given unique ID then let's shutdown it.
+ // The unique ID may be presented in the cache if originally we created
+ // access node object and then we want to create accessible object when
+ // DOM node is changed.
+ nsCOMPtr<nsIAccessNode> accessNode;
+ GetCacheEntry(mAccessNodeCache, aUniqueID, getter_AddRefs(accessNode));
+ if (accessNode) {
+ nsCOMPtr<nsPIAccessNode> prAccessNode = do_QueryInterface(accessNode);
+ prAccessNode->Shutdown();
+ }
+
PutCacheEntry(mAccessNodeCache, aUniqueID, aAccessNode);
return NS_OK;
}
NS_IMETHODIMP nsDocAccessible::GetParent(nsIAccessible **aParent)
{
// Hook up our new accessible with our parent
*aParent = nsnull;
@@ -584,17 +597,20 @@ NS_IMETHODIMP nsDocAccessible::Shutdown(
if (mFireEventTimer) {
// Doc being shut down before events fired,
mFireEventTimer->Cancel();
mFireEventTimer = nsnull;
if (mEventsToFire.Count() > 0 ) {
mEventsToFire.Clear();
// Make sure we release the kung fu death grip which is always
// there when there are still events left to be fired
- NS_RELEASE_THIS();
+ // If FlushPendingEvents() is in call stack,
+ // kung fu death grip will be released there.
+ if (!mInFlushPendingEvents)
+ NS_RELEASE_THIS();
}
}
// Remove from the cache after other parts of Shutdown(), so that Shutdown() procedures
// can find the doc or root accessible in the cache if they need it.
// We don't do this during ShutdownAccessibility() because that is already clearing the cache
if (!gIsShuttingDownApp)
gGlobalDocAccessibleCache.Remove(static_cast<void*>(kungFuDeathGripDoc));
@@ -1500,16 +1516,17 @@ nsDocAccessible::FireDelayedAccessibleEv
0, nsITimer::TYPE_ONE_SHOT);
}
return NS_OK;
}
NS_IMETHODIMP nsDocAccessible::FlushPendingEvents()
{
+ mInFlushPendingEvents = PR_TRUE;
PRUint32 length = mEventsToFire.Count();
NS_ASSERTION(length, "How did we get here without events to fire?");
nsCOMPtr<nsIPresShell> presShell = GetPresShell();
if (!presShell)
length = 0; // The doc is now shut down, don't fire events in it anymore
else
nsAccEvent::ApplyEventRules(mEventsToFire);
@@ -1523,18 +1540,18 @@ NS_IMETHODIMP nsDocAccessible::FlushPend
nsCOMPtr<nsIAccessible> accessible;
accessibleEvent->GetAccessible(getter_AddRefs(accessible));
nsCOMPtr<nsIDOMNode> domNode;
accessibleEvent->GetDOMNode(getter_AddRefs(domNode));
PRUint32 eventType = nsAccEvent::EventType(accessibleEvent);
PRBool isFromUserInput = nsAccEvent::IsFromUserInput(accessibleEvent);
if (domNode == gLastFocusedNode &&
- eventType == nsIAccessibleEvent::EVENT_ASYNCH_HIDE ||
- eventType == nsIAccessibleEvent::EVENT_ASYNCH_SHOW) {
+ (eventType == nsIAccessibleEvent::EVENT_ASYNCH_HIDE ||
+ eventType == nsIAccessibleEvent::EVENT_ASYNCH_SHOW)) {
// If frame type didn't change for this event, then we don't actually need to invalidate
// However, we only keep track of the old frame type for the focus, where it's very
// important not to destroy and recreate the accessible for minor style changes,
// such as a:focus { overflow: scroll; }
nsCOMPtr<nsIContent> focusContent(do_QueryInterface(domNode));
if (focusContent) {
nsIFrame *focusFrame = presShell->GetRealPrimaryFrameFor(focusContent);
nsIAtom *newFrameType =
@@ -1620,17 +1637,18 @@ NS_IMETHODIMP nsDocAccessible::FlushPend
// Test caret line # -- fire an EVENT_ALERT on the focused node so we can watch the
// line-number object attribute on it
nsCOMPtr<nsIAccessible> accForFocus;
GetAccService()->GetAccessibleFor(gLastFocusedNode, getter_AddRefs(accForFocus));
nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_ALERT, accForFocus);
#endif
nsCOMPtr<nsIAccessibleCaretMoveEvent> caretMoveEvent =
new nsAccCaretMoveEvent(accessible, caretOffset);
- NS_ENSURE_TRUE(caretMoveEvent, NS_ERROR_OUT_OF_MEMORY);
+ if (!caretMoveEvent)
+ break; // Out of memory, break out to release kung fu death grip
FireAccessibleEvent(caretMoveEvent);
PRInt32 selectionCount;
accessibleText->GetSelectionCount(&selectionCount);
if (selectionCount) { // There's a selection so fire selection change as well
nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED,
accessible, PR_TRUE);
@@ -1656,16 +1674,17 @@ NS_IMETHODIMP nsDocAccessible::FlushPend
}
}
mEventsToFire.Clear(); // Clear out array
NS_RELEASE_THIS(); // Release kung fu death grip
// After a flood of events, reset so that user input flag is off
nsAccEvent::ResetLastInputState();
+ mInFlushPendingEvents = PR_FALSE;
return NS_OK;
}
void nsDocAccessible::FlushEventsCallback(nsITimer *aTimer, void *aClosure)
{
nsPIAccessibleDocument *accessibleDoc = static_cast<nsPIAccessibleDocument*>(aClosure);
NS_ASSERTION(accessibleDoc, "How did we get here without an accessible document?");
if (accessibleDoc) {
--- a/accessible/src/base/nsDocAccessible.h
+++ b/accessible/src/base/nsDocAccessible.h
@@ -216,13 +216,14 @@ class nsDocAccessible : public nsHyperTe
PRUint16 mScrollPositionChangedTicks; // Used for tracking scroll events
PRPackedBool mIsContentLoaded;
PRPackedBool mIsLoadCompleteFired;
nsCOMArray<nsIAccessibleEvent> mEventsToFire;
protected:
PRBool mIsAnchor;
PRBool mIsAnchorJumped;
+ PRBool mInFlushPendingEvents;
static PRUint32 gLastFocusedAccessiblesState;
static nsIAtom *gLastFocusedFrameType;
};
#endif
--- a/accessible/src/base/nsOuterDocAccessible.cpp
+++ b/accessible/src/base/nsOuterDocAccessible.cpp
@@ -143,8 +143,40 @@ nsOuterDocAccessible::GetAttributesInter
aAttributes->GetStringProperty(NS_LITERAL_CSTRING("tag"), tag);
if (!tag.IsEmpty()) {
// We're overriding the ARIA attributes on an sub document, but we don't want to
// override the other attributes
return NS_OK;
}
return nsAccessible::GetAttributesInternal(aAttributes);
}
+
+// Internal frame, which is the doc's parent, should not have a click action
+NS_IMETHODIMP
+nsOuterDocAccessible::GetNumActions(PRUint8 *aNumActions)
+{
+ NS_ENSURE_ARG_POINTER(aNumActions);
+ *aNumActions = 0;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOuterDocAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
+{
+ aName.Truncate();
+
+ return NS_ERROR_INVALID_ARG;
+}
+
+NS_IMETHODIMP
+nsOuterDocAccessible::GetActionDescription(PRUint8 aIndex, nsAString& aDescription)
+{
+ aDescription.Truncate();
+
+ return NS_ERROR_INVALID_ARG;
+}
+
+NS_IMETHODIMP
+nsOuterDocAccessible::DoAction(PRUint8 aIndex)
+{
+ return NS_ERROR_INVALID_ARG;
+}
--- a/accessible/src/base/nsOuterDocAccessible.h
+++ b/accessible/src/base/nsOuterDocAccessible.h
@@ -53,11 +53,15 @@ class nsOuterDocAccessible : public nsAc
nsIWeakReference* aShell);
NS_IMETHOD GetRole(PRUint32 *aRole);
NS_IMETHOD GetState(PRUint32 *aState, PRUint32 *aExtraState);
NS_IMETHOD GetChildAtPoint(PRInt32 aX, PRInt32 aY,
nsIAccessible **aAccessible);
void CacheChildren();
nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
+ NS_IMETHOD GetNumActions(PRUint8 *aNumActions);
+ NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
+ NS_IMETHODIMP GetActionDescription(PRUint8 aIndex, nsAString& aDescription);
+ NS_IMETHOD DoAction(PRUint8 aIndex);
};
#endif
--- a/accessible/src/base/nsRootAccessible.cpp
+++ b/accessible/src/base/nsRootAccessible.cpp
@@ -638,28 +638,16 @@ nsresult nsRootAccessible::HandleEventWi
if (eventType.EqualsLiteral("DOMContentLoaded")) {
// Don't create the doc accessible until load scripts have a chance to set
// role attribute for <body> or <html> element, because the value of
// role attribute will be cached when the doc accessible is Init()'d
TryFireEarlyLoadEvent(aTargetNode);
return NS_OK;
}
-#ifdef MOZ_XUL
- if (eventType.EqualsLiteral("TreeViewChanged")) { // Always asynch, always from user input
- if (!isTree)
- return NS_OK;
-
- nsCOMPtr<nsIContent> treeContent = do_QueryInterface(aTargetNode);
- nsAccEvent::PrepareForEvent(aTargetNode, PR_TRUE);
- return accService->InvalidateSubtreeFor(eventShell, treeContent,
- nsIAccessibleEvent::EVENT_DOM_SIGNIFICANT_CHANGE);
- }
-#endif
-
if (eventType.EqualsLiteral("popuphiding")) {
// If accessible focus was on or inside popup that closes,
// then restore it to true current focus.
// This is the case when we've been getting DOMMenuItemActive events
// inside of a combo box that closes. The real focus is on the combo box.
// It's also the case when a popup gets focus in ATK -- when it closes
// we need to fire an event to restore focus to where it was
if (!gLastFocusedNode ||
@@ -673,21 +661,32 @@ nsresult nsRootAccessible::HandleEventWi
nsCOMPtr<nsIAccessible> accessible;
accService->GetAccessibleInShell(aTargetNode, eventShell,
getter_AddRefs(accessible));
nsCOMPtr<nsPIAccessible> privAcc(do_QueryInterface(accessible));
if (!privAcc)
return NS_OK;
#ifdef MOZ_XUL
- if (eventType.EqualsLiteral("TreeRowCountChanged"))
- return HandleTreeRowCountChangedEvent(aEvent, accessible, localName);
-
- if (eventType.EqualsLiteral("TreeInvalidated"))
- return HandleTreeInvalidatedEvent(aEvent, accessible, localName);
+ if (isTree) {
+ nsCOMPtr<nsIAccessibleTreeCache> treeAcc(do_QueryInterface(accessible));
+ NS_ASSERTION(treeAcc,
+ "Accessible for xul:tree doesn't implement nsIAccessibleTreeCache interface.");
+
+ if (treeAcc) {
+ if (eventType.EqualsLiteral("TreeViewChanged"))
+ return treeAcc->TreeViewChanged();
+
+ if (eventType.EqualsLiteral("TreeRowCountChanged"))
+ return HandleTreeRowCountChangedEvent(aEvent, treeAcc);
+
+ if (eventType.EqualsLiteral("TreeInvalidated"))
+ return HandleTreeInvalidatedEvent(aEvent, treeAcc);
+ }
+ }
#endif
if (eventType.EqualsLiteral("RadioStateChange")) {
PRUint32 state = State(accessible);
// radiogroup in prefWindow is exposed as a list,
// and panebutton is exposed as XULListitem in A11y.
// nsXULListitemAccessible::GetState uses STATE_SELECTED in this case,
@@ -1090,22 +1089,18 @@ NS_IMETHODIMP nsRootAccessible::FireDocL
mIsContentLoaded = (aEventType == nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE ||
aEventType == nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED);
return NS_OK;
}
nsresult
nsRootAccessible::HandleTreeRowCountChangedEvent(nsIDOMEvent *aEvent,
- nsIAccessible *aAccessible,
- const nsAString& aTargetName)
+ nsIAccessibleTreeCache *aAccessible)
{
- if (!aTargetName.EqualsLiteral("tree"))
- return NS_OK;
-
nsCOMPtr<nsIDOMDataContainerEvent> dataEvent(do_QueryInterface(aEvent));
if (!dataEvent)
return NS_OK;
nsCOMPtr<nsIVariant> indexVariant;
dataEvent->GetData(NS_LITERAL_STRING("index"),
getter_AddRefs(indexVariant));
if (!indexVariant)
@@ -1116,30 +1111,23 @@ nsRootAccessible::HandleTreeRowCountChan
getter_AddRefs(countVariant));
if (!countVariant)
return NS_OK;
PRInt32 index, count;
indexVariant->GetAsInt32(&index);
countVariant->GetAsInt32(&count);
- nsCOMPtr<nsIAccessibleTreeCache> treeAccCache(do_QueryInterface(aAccessible));
- NS_ENSURE_STATE(treeAccCache);
-
- return treeAccCache->InvalidateCache(index, count);
+ return aAccessible->InvalidateCache(index, count);
}
nsresult
nsRootAccessible::HandleTreeInvalidatedEvent(nsIDOMEvent *aEvent,
- nsIAccessible *aAccessible,
- const nsAString& aTargetName)
+ nsIAccessibleTreeCache *aAccessible)
{
- if (!aTargetName.EqualsLiteral("tree"))
- return NS_OK;
-
nsCOMPtr<nsIDOMDataContainerEvent> dataEvent(do_QueryInterface(aEvent));
if (!dataEvent)
return NS_OK;
PRInt32 startRow = 0, endRow = -1, startCol = 0, endCol = -1;
nsCOMPtr<nsIVariant> startRowVariant;
dataEvent->GetData(NS_LITERAL_STRING("startrow"),
@@ -1160,14 +1148,11 @@ nsRootAccessible::HandleTreeInvalidatedE
startColVariant->GetAsInt32(&startCol);
nsCOMPtr<nsIVariant> endColVariant;
dataEvent->GetData(NS_LITERAL_STRING("endcolumn"),
getter_AddRefs(endColVariant));
if (endColVariant)
endColVariant->GetAsInt32(&endCol);
- nsCOMPtr<nsIAccessibleTreeCache> treeAcc(do_QueryInterface(aAccessible));
- NS_ENSURE_STATE(treeAcc);
-
- return treeAcc->TreeViewInvalidated(startRow, endRow, startCol, endCol);
+ return aAccessible->TreeViewInvalidated(startRow, endRow, startCol, endCol);
}
--- a/accessible/src/base/nsRootAccessible.h
+++ b/accessible/src/base/nsRootAccessible.h
@@ -33,19 +33,23 @@
* 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 _nsRootAccessible_H_
#define _nsRootAccessible_H_
+#include "nsCaretAccessible.h"
#include "nsDocAccessibleWrap.h"
+
+#include "nsIAccessibleDocument.h"
+#include "nsIAccessibleTreeCache.h"
+
#include "nsHashtable.h"
-#include "nsIAccessibleDocument.h"
#include "nsCaretAccessible.h"
#include "nsIDocument.h"
#include "nsIDOMFocusListener.h"
#include "nsIDOMFormListener.h"
#include "nsIDOMXULListener.h"
#include "nsITimer.h"
#define NS_ROOTACCESSIBLE_IMPL_CID \
@@ -119,25 +123,23 @@ class nsRootAccessible : public nsDocAcc
void TryFireEarlyLoadEvent(nsIDOMNode *aDocNode);
void FireCurrentFocusEvent();
void GetChromeEventHandler(nsIDOMEventTarget **aChromeTarget);
/**
* Handles 'TreeRowCountChanged' event. Used in HandleEventWithTarget().
*/
nsresult HandleTreeRowCountChangedEvent(nsIDOMEvent *aEvent,
- nsIAccessible *aAccessible,
- const nsAString& aTargetName);
+ nsIAccessibleTreeCache *aAccessible);
/**
* Handles 'TreeInvalidated' event. Used in HandleEventWithTarget().
*/
nsresult HandleTreeInvalidatedEvent(nsIDOMEvent *aEvent,
- nsIAccessible *aAccessible,
- const nsAString& aTargetName);
+ nsIAccessibleTreeCache *aAccessible);
#ifdef MOZ_XUL
PRUint32 GetChromeFlags();
#endif
already_AddRefed<nsIDocShellTreeItem>
GetContentDocShell(nsIDocShellTreeItem *aStart);
nsRefPtr<nsCaretAccessible> mCaretAccessible;
nsCOMPtr<nsIDOMNode> mCurrentARIAMenubar;
--- a/accessible/src/html/nsHTMLLinkAccessible.cpp
+++ b/accessible/src/html/nsHTMLLinkAccessible.cpp
@@ -61,17 +61,26 @@ NS_IMETHODIMP
nsHTMLLinkAccessible::GetName(nsAString& aName)
{
aName.Truncate();
if (IsDefunct())
return NS_ERROR_FAILURE;
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
- return AppendFlatStringFromSubtree(content, &aName);
+ nsresult rv = AppendFlatStringFromSubtree(content, &aName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (aName.IsEmpty()) {
+ // Probably an image without alt or title inside, try to get the name on
+ // the link by usual way.
+ return GetHTMLName(aName, PR_FALSE);
+ }
+
+ return NS_OK;
}
NS_IMETHODIMP
nsHTMLLinkAccessible::GetRole(PRUint32 *aRole)
{
NS_ENSURE_ARG_POINTER(aRole);
*aRole = nsIAccessibleRole::ROLE_LINK;
@@ -97,19 +106,24 @@ nsHTMLLinkAccessible::GetState(PRUint32
*aState |= nsIAccessibleStates::STATE_SELECTABLE;
}
nsCOMPtr<nsILink> link = do_QueryInterface(mDOMNode);
NS_ENSURE_STATE(link);
nsLinkState linkState;
link->GetLinkState(linkState);
- if (linkState == eLinkState_NotLink) {
- // This is a named anchor, not a link with also a name attribute. bail out.
- return NS_OK;
+ if (linkState == eLinkState_NotLink || linkState == eLinkState_Unknown) {
+ // This is a either named anchor (a link with also a name attribute) or
+ // it doesn't have any attributes. Check if 'click' event handler is
+ // registered, otherwise bail out.
+ PRBool isOnclick = nsAccUtils::HasListener(content,
+ NS_LITERAL_STRING("click"));
+ if (!isOnclick)
+ return NS_OK;
}
*aState |= nsIAccessibleStates::STATE_LINKED;
if (linkState == eLinkState_Visited)
*aState |= nsIAccessibleStates::STATE_TRAVERSED;
return NS_OK;
@@ -133,35 +147,45 @@ nsHTMLLinkAccessible::GetValue(nsAString
return NS_OK;
}
NS_IMETHODIMP
nsHTMLLinkAccessible::GetNumActions(PRUint8 *aNumActions)
{
NS_ENSURE_ARG_POINTER(aNumActions);
+ if (!IsLinked())
+ return nsHyperTextAccessible::GetNumActions(aNumActions);
+
*aNumActions = 1;
return NS_OK;
}
NS_IMETHODIMP
nsHTMLLinkAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
{
+ aName.Truncate();
+
+ if (!IsLinked())
+ return nsHyperTextAccessible::GetActionName(aIndex, aName);
+
// Action 0 (default action): Jump to link
- aName.Truncate();
if (aIndex != eAction_Jump)
return NS_ERROR_INVALID_ARG;
aName.AssignLiteral("jump");
return NS_OK;
}
NS_IMETHODIMP
nsHTMLLinkAccessible::DoAction(PRUint8 aIndex)
{
+ if (!IsLinked())
+ return nsHyperTextAccessible::DoAction(aIndex);
+
// Action 0 (default action): Jump to link
if (aIndex != eAction_Jump)
return NS_ERROR_INVALID_ARG;
if (IsDefunct())
return NS_ERROR_FAILURE;
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
@@ -180,8 +204,25 @@ nsHTMLLinkAccessible::GetURI(PRInt32 aIn
if (aIndex != 0)
return NS_ERROR_INVALID_ARG;
nsCOMPtr<nsILink> link(do_QueryInterface(mDOMNode));
NS_ENSURE_STATE(link);
return link->GetHrefURI(aURI);
}
+
+////////////////////////////////////////////////////////////////////////////////
+// Protected members
+
+PRBool
+nsHTMLLinkAccessible::IsLinked()
+{
+ nsCOMPtr<nsILink> link(do_QueryInterface(mDOMNode));
+ if (!link)
+ return PR_FALSE;
+
+ nsLinkState linkState;
+ nsresult rv = link->GetLinkState(linkState);
+
+ return NS_SUCCEEDED(rv) && linkState != eLinkState_NotLink &&
+ linkState != eLinkState_Unknown;
+}
--- a/accessible/src/html/nsHTMLLinkAccessible.h
+++ b/accessible/src/html/nsHTMLLinkAccessible.h
@@ -59,11 +59,16 @@ public:
NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
NS_IMETHOD DoAction(PRUint8 aIndex);
// nsIAccessibleHyperLink
NS_IMETHOD GetURI(PRInt32 aIndex, nsIURI **aURI);
protected:
enum { eAction_Jump = 0 };
+
+ /**
+ * Returns true if the link has href attribute.
+ */
+ PRBool IsLinked();
};
#endif
--- a/accessible/src/html/nsHTMLTableAccessible.cpp
+++ b/accessible/src/html/nsHTMLTableAccessible.cpp
@@ -1166,21 +1166,23 @@ NS_IMETHODIMP nsHTMLTableAccessible::IsP
NS_ENSURE_TRUE(tableFrame , NS_ERROR_FAILURE);
nsSize tableSize = tableFrame->GetSize();
nsCOMPtr<nsIAccessibleDocument> docAccessible = GetDocAccessible();
nsCOMPtr<nsPIAccessNode> docAccessNode(do_QueryInterface(docAccessible));
NS_ENSURE_TRUE(docAccessNode, NS_ERROR_FAILURE);
nsIFrame *docFrame = docAccessNode->GetFrame();
NS_ENSURE_TRUE(docFrame , NS_ERROR_FAILURE);
nsSize docSize = docFrame->GetSize();
- PRInt32 percentageOfDocWidth = (100 * tableSize.width) / docSize.width;
- if (percentageOfDocWidth > 95) {
- // 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width
- // Probably for layout
- RETURN_LAYOUT_ANSWER(PR_TRUE, "<=4 columns, width hardcoded in pixels and 95% of document width");
+ if (docSize.width > 0) {
+ PRInt32 percentageOfDocWidth = (100 * tableSize.width) / docSize.width;
+ if (percentageOfDocWidth > 95) {
+ // 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width
+ // Probably for layout
+ RETURN_LAYOUT_ANSWER(PR_TRUE, "<=4 columns, width hardcoded in pixels and 95% of document width");
+ }
}
}
// Two column rules
if (rows * columns <= 10) {
RETURN_LAYOUT_ANSWER(PR_TRUE, "2-4 columns, 10 cells or less, non-bordered");
}
--- a/accessible/src/mac/nsAccessibleWrap.h
+++ b/accessible/src/mac/nsAccessibleWrap.h
@@ -91,17 +91,19 @@ class nsAccessibleWrap : public nsAccess
return (state & nsIAccessibleStates::STATE_HASPOPUP);
}
// return this accessible's all children, adhering to "flat" accessibles by not returning their children.
void GetUnignoredChildren(nsTArray<nsRefPtr<nsAccessibleWrap> > &aChildrenArray);
virtual already_AddRefed<nsIAccessible> GetUnignoredParent();
protected:
-
+
+ virtual nsresult FirePlatformEvent(nsIAccessibleEvent *aEvent);
+
PRBool AncestorIsFlat() {
// we don't create a native object if we're child of a "flat" accessible; for example, on OS X buttons
// shouldn't have any children, because that makes the OS confused.
//
// to maintain a scripting environment where the XPCOM accessible hierarchy look the same
// on all platforms, we still let the C++ objects be created though.
nsCOMPtr<nsIAccessible> curParent = GetParent();
--- a/accessible/src/mac/nsAccessibleWrap.mm
+++ b/accessible/src/mac/nsAccessibleWrap.mm
@@ -164,18 +164,28 @@ nsAccessibleWrap::FireAccessibleEvent(ns
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
NS_ENSURE_ARG_POINTER(aEvent);
nsresult rv = nsAccessible::FireAccessibleEvent(aEvent);
NS_ENSURE_SUCCESS(rv, rv);
+ return FirePlatformEvent(aEvent);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+nsresult
+nsAccessibleWrap::FirePlatformEvent(nsIAccessibleEvent *aEvent)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
PRUint32 eventType;
- rv = aEvent->GetEventType(&eventType);
+ nsresult rv = aEvent->GetEventType(&eventType);
NS_ENSURE_SUCCESS(rv, rv);
// ignore everything but focus-changed and value-changed events for now.
if (eventType != nsIAccessibleEvent::EVENT_FOCUS &&
eventType != nsIAccessibleEvent::EVENT_VALUE_CHANGE)
return NS_OK;
nsCOMPtr<nsIAccessible> accessible;
--- a/accessible/src/msaa/nsAccessibleWrap.cpp
+++ b/accessible/src/msaa/nsAccessibleWrap.cpp
@@ -328,16 +328,22 @@ STDMETHODIMP nsAccessibleWrap::get_accVa
*pszValue = NULL;
nsCOMPtr<nsIAccessible> xpAccessible;
GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
if (xpAccessible) {
nsAutoString value;
if (NS_FAILED(xpAccessible->GetValue(value)))
return E_FAIL;
+ // see bug 438784: Need to expose URL on doc's value attribute.
+ // For this, reverting part of fix for bug 425693 to make this MSAA method
+ // behave IAccessible2-style.
+ if (value.IsEmpty())
+ return S_FALSE;
+
*pszValue = ::SysAllocStringLen(value.get(), value.Length());
if (!*pszValue)
return E_OUTOFMEMORY;
}
} __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
return S_OK;
}
@@ -1685,16 +1691,22 @@ NS_IMETHODIMP nsAccessibleWrap::GetNativ
NS_IMETHODIMP
nsAccessibleWrap::FireAccessibleEvent(nsIAccessibleEvent *aEvent)
{
NS_ENSURE_ARG(aEvent);
nsresult rv = nsAccessible::FireAccessibleEvent(aEvent);
NS_ENSURE_SUCCESS(rv, rv);
+ return FirePlatformEvent(aEvent);
+}
+
+nsresult
+nsAccessibleWrap::FirePlatformEvent(nsIAccessibleEvent *aEvent)
+{
PRUint32 eventType = 0;
aEvent->GetEventType(&eventType);
NS_ENSURE_TRUE(eventType > 0 &&
eventType < nsIAccessibleEvent::EVENT_LAST_ENTRY,
NS_ERROR_FAILURE);
PRUint32 winLastEntry = gWinEventMap[nsIAccessibleEvent::EVENT_LAST_ENTRY];
--- a/accessible/src/msaa/nsAccessibleWrap.h
+++ b/accessible/src/msaa/nsAccessibleWrap.h
@@ -308,16 +308,18 @@ class nsAccessibleWrap : public nsAccess
// NT4 does not have the oleacc that defines these methods. So we define copies here that automatically
// load the library only if needed.
static STDMETHODIMP AccessibleObjectFromWindow(HWND hwnd,DWORD dwObjectID,REFIID riid,void **ppvObject);
static STDMETHODIMP NotifyWinEvent(DWORD event,HWND hwnd,LONG idObjectType,LONG idObject);
static IDispatch *NativeAccessible(nsIAccessible *aXPAccessible);
protected:
+ virtual nsresult FirePlatformEvent(nsIAccessibleEvent *aEvent);
+
// mEnumVARIANTPosition not the current accessible's position, but a "cursor" of
// where we are in the current list of children, with respect to
// nsIEnumVariant::Reset(), Skip() and Next().
PRUint16 mEnumVARIANTPosition;
enum navRelations {
NAVRELATION_CONTROLLED_BY = 0x1000,
NAVRELATION_CONTROLLER_FOR = 0x1001,
--- a/accessible/src/other/nsAccessibleWrap.h
+++ b/accessible/src/other/nsAccessibleWrap.h
@@ -46,11 +46,16 @@
#include "nsCOMPtr.h"
#include "nsAccessible.h"
class nsAccessibleWrap : public nsAccessible
{
public: // construction, destruction
nsAccessibleWrap(nsIDOMNode*, nsIWeakReference *aShell);
virtual ~nsAccessibleWrap();
+
+ protected:
+ virtual nsresult FirePlatformEvent(nsIAccessibleEvent *aEvent) {
+ return NS_OK;
+ }
};
#endif
--- a/accessible/src/xul/nsXULSelectAccessible.cpp
+++ b/accessible/src/xul/nsXULSelectAccessible.cpp
@@ -835,21 +835,23 @@ nsXULListitemAccessible::GetListAccessib
nsCOMPtr<nsIDOMXULSelectControlItemElement> listItem =
do_QueryInterface(mDOMNode);
if (!listItem)
return nsnull;
nsCOMPtr<nsIDOMXULSelectControlElement> list;
listItem->GetControl(getter_AddRefs(list));
- if (!list)
+
+ nsCOMPtr<nsIDOMNode> listNode(do_QueryInterface(list));
+ if (!listNode)
return nsnull;
nsIAccessible *listAcc = nsnull;
- GetAccService()->GetAccessibleInWeakShell(list, mWeakShell, &listAcc);
+ GetAccService()->GetAccessibleInWeakShell(listNode, mWeakShell, &listAcc);
return listAcc;
}
////////////////////////////////////////////////////////////////////////////////
// nsXULListitemAccessible. nsIAccessible
/**
* If there is a Listcell as a child ( not anonymous ) use it, otherwise
--- a/accessible/src/xul/nsXULTreeAccessible.cpp
+++ b/accessible/src/xul/nsXULTreeAccessible.cpp
@@ -631,32 +631,34 @@ nsXULTreeAccessible::InvalidateCache(PRI
// treeViewInvalidated(in long aStartRow, in long aEndRow,
// in long aStartCol, in long aEndCol);
NS_IMETHODIMP
nsXULTreeAccessible::TreeViewInvalidated(PRInt32 aStartRow, PRInt32 aEndRow,
PRInt32 aStartCol, PRInt32 aEndCol)
{
NS_ENSURE_TRUE(mTree && mTreeView, NS_ERROR_FAILURE);
- PRInt32 endRow = aEndRow, endCol = aEndCol;
+ PRInt32 endRow = aEndRow;
nsresult rv;
if (endRow == -1) {
PRInt32 rowCount = 0;
rv = mTreeView->GetRowCount(&rowCount);
NS_ENSURE_SUCCESS(rv, rv);
endRow = rowCount - 1;
}
nsCOMPtr<nsITreeColumns> treeColumns;
mTree->GetColumns(getter_AddRefs(treeColumns));
NS_ENSURE_STATE(treeColumns);
#ifdef MOZ_ACCESSIBILITY_ATK
+ PRInt32 endCol = aEndCol;
+
if (endCol == -1) {
PRInt32 colCount = 0;
rv = treeColumns->GetCount(&colCount);
NS_ENSURE_SUCCESS(rv, rv);
endCol = colCount - 1;
}
#else
@@ -700,26 +702,58 @@ nsXULTreeAccessible::TreeViewInvalidated
}
}
}
}
return NS_OK;
}
+// void nsIAccessibleTreeCache::treeViewChanged();
+NS_IMETHODIMP
+nsXULTreeAccessible::TreeViewChanged()
+{
+ if (!mTree)
+ return NS_ERROR_FAILURE;
+
+ // Fire only notification destroy/create events on accessible tree to lie to
+ // AT because it should be expensive to fire destroy events for each tree item
+ // in cache.
+ nsCOMPtr<nsIAccessibleEvent> eventDestroy =
+ new nsAccEvent(nsIAccessibleEvent::EVENT_DOM_DESTROY,
+ this, PR_FALSE);
+ NS_ENSURE_TRUE(eventDestroy, NS_ERROR_OUT_OF_MEMORY);
+
+ nsresult rv = FirePlatformEvent(eventDestroy);
+
+ ClearCache(*mAccessNodeCache);
+
+ mTree->GetView(getter_AddRefs(mTreeView));
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAccessibleEvent> eventCreate =
+ new nsAccEvent(nsIAccessibleEvent::EVENT_DOM_CREATE,
+ this, PR_FALSE);
+ NS_ENSURE_TRUE(eventCreate, NS_ERROR_OUT_OF_MEMORY);
+
+ return FirePlatformEvent(eventCreate);
+}
+
nsresult nsXULTreeAccessible::GetColumnCount(nsITreeBoxObject* aBoxObject, PRInt32* aCount)
{
NS_ENSURE_TRUE(aBoxObject, NS_ERROR_FAILURE);
nsCOMPtr<nsITreeColumns> treeColumns;
aBoxObject->GetColumns(getter_AddRefs(treeColumns));
NS_ENSURE_TRUE(treeColumns, NS_ERROR_FAILURE);
return treeColumns->GetCount(aCount);
}
-// ---------- nsXULTreeitemAccessible ----------
+////////////////////////////////////////////////////////////////////////////////
+// nsXULTreeitemAccessible
nsXULTreeitemAccessible::nsXULTreeitemAccessible(nsIAccessible *aParent, nsIDOMNode *aDOMNode, nsIWeakReference *aShell, PRInt32 aRow, nsITreeColumn* aColumn)
: nsLeafAccessible(aDOMNode, aShell)
{
mParent = aParent; // xxx todo: do we need this? We already have mParent on nsAccessible
nsXULTreeAccessible::GetTreeBoxObject(aDOMNode, getter_AddRefs(mTree));
if (mTree)
--- a/accessible/tests/mochitest/Makefile.in
+++ b/accessible/tests/mochitest/Makefile.in
@@ -43,26 +43,30 @@ VPATH = @srcdir@
relativesrcdir = accessible
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
_TEST_FILES =\
moz.png \
test_aria_activedescendant.html \
+ test_aria_role_article.html \
test_bug368835.xul \
test_bug420863.html \
test_groupattrs.xul \
test_table_indexes.html \
test_nsIAccessibleTable_1.html \
test_nsIAccessibleTable_2.html \
test_nsIAccessibleTable_3.html \
test_nsIAccessibleTable_4.html \
test_nsIAccessibleTable_listboxes.xul \
+ test_nsIAccessibleDocument.html \
test_nsIAccessibleHyperLink.html \
test_nsIAccessibleHyperLink.xul \
test_nsIAccessibleHyperText.html \
test_nsIAccessibleImage.html \
+ test_nsOuterDocAccessible.html \
test_bug428479.html \
+ test_bug429285.html \
$(NULL)
libs:: $(_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/test_aria_role_article.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=429666
+-->
+<head>
+ <title>Expose ROLE_DOCUMENT for ARIA landmarks that inherit from document chrome tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ var accRetrieval = Components.classes["@mozilla.org/accessibleRetrieval;1"].
+ getService(Components.interfaces.nsIAccessibleRetrieval);
+
+ // Test article exposed as document
+ var articleElement = document.getElementById("testArticle");
+ var articleAcc = null;
+ try {
+ articleAcc = accRetrieval.getAccessibleFor(articleElement);
+ } catch(e) { }
+ ok(articleAcc, "no accessible for article!");
+ if (articleAcc) {
+ is(articleAcc.finalRole,
+ Components.interfaces.nsIAccessibleRole.ROLE_DOCUMENT,
+ "Wrong role for article!");
+ is(articleAcc.name, "Test article", "Wrong name for article!");
+ }
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=429666">Mozilla Bug 429666</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ <div id="testArticle" role="article" title="Test article">
+ <p>This is a paragraph inside the article.</p>
+ </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/test_bug429285.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=429285
+-->
+<head>
+ <title>Propagate aria-disabled state to descendants chrome tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript">
+ // Mapping needed state flags for easier handling.
+ const state_disabled =
+ Components.interfaces.nsIAccessibleStates.STATE_UNAVAILABLE;
+ const state_focusable =
+ Components.interfaces.nsIAccessibleStates.STATE_FOCUSABLE;
+
+ const nsIAccessibleRetrieval = Components.interfaces.nsIAccessibleRetrieval;
+ const nsIAccessible = Components.interfaces.nsIAccessible;
+
+ var gAccRetrieval = null;
+
+ function testChildren(aID, aAcc)
+ {
+ // Check state of aAcc first.
+ var state = {}, extraState = {};
+ aAcc.getFinalState(state, extraState);
+ if (state.value & state_focusable) {
+ is(state.value & state_disabled, state_disabled,
+ "Wrong disabled state bit for " + aID + "!");
+ }
+
+ // Iterate over its children to see if they are disabled, too.
+ var children = null;
+ try {
+ children = aAcc.children;
+ } catch(e) {}
+ ok(children, "Could not get children for " + aID +"!");
+
+ if (children) {
+ for (var i=0; i<children.length; i++) {
+ var childAcc = children.queryElementAt(i, nsIAccessible);
+ // Test and recurse over its children as well.
+ testChildren(childAcc.name, childAcc);
+ }
+ }
+ }
+
+ function doTest()
+ {
+ gAccRetrieval = Components.classes["@mozilla.org/accessibleRetrieval;1"].
+ getService(nsIAccessibleRetrieval);
+
+ var groupItem = document.getElementById("group");
+ var groupAcc = null;
+ try {
+ groupAcc = gAccRetrieval.getAccessibleFor(groupItem);
+ } catch (e) {}
+ ok (groupAcc,
+ "No accessible for group element!");
+
+ if (groupAcc) {
+ var state = {}, extraState = {};
+ groupAcc.getFinalState(state, extraState);
+ is(state.value & state_disabled, state_disabled,
+ "Wrong disabled state bit for Group element!");
+ testChildren("group", groupAcc);
+ }
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=429285"
+ title="Propagate aria-disabled to descendants">
+ Mozilla Bug 429285
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="group" role="group" aria-disabled="true">
+ <button>hi</button>
+ <div tabindex="0" role="listbox" aria-activedescendant="item1">
+ <div role="option" id="item1">Item 1</div>
+ <div role="option" id="item2">Item 2</div>
+ <div role="option" id="item3">Item 3</div>
+ <div role="option" id="item4">Item 4</div>
+ </div>
+ <div role="slider" tabindex="0">A slider</div>
+ </div>
+</body>
+</html>
--- a/accessible/tests/mochitest/test_groupattrs.xul
+++ b/accessible/tests/mochitest/test_groupattrs.xul
@@ -25,29 +25,40 @@
return "";
}
}
this.mAcc = gAccService.getAccessibleFor(document.getElementById(aId));
this.mAttrs = this.mAcc.attributes;
}
+ function testGroupAtts(aID, aPosInSet, aSetSize)
+ {
+ var attrs = new accAttributes(aID);
+ is(attrs.getAttribute("posinset"), aPosInSet, "Wrong posinset on " + aID);
+ is(attrs.getAttribute("setsize"), aSetSize, "Wrong setsize on " + aID);
+ }
+
function doTest()
{
// Activate accessibility, otherwise events aren't fired.
gAccService = Components.classes["@mozilla.org/accessibleRetrieval;1"].
getService(Components.interfaces.nsIAccessibleRetrieval);
- var attrs = new accAttributes("item1");
- is(attrs.getAttribute("posinset"), "1", "Wrong posinset on item1.");
- is(attrs.getAttribute("setsize"), "2", "Wrong setsize on item1.");
+ //////////////////////////////////////////////////////////////////////////
+ // xul:listbox (bug 417317)
+ testGroupAtts("item1", "1", "2");
+ testGroupAtts("item2", "2", "2");
- attrs = new accAttributes("item2");
- is(attrs.getAttribute("posinset"), "2", "Wrong posinset on item2.");
- is(attrs.getAttribute("setsize"), "2", "Wrong setsize on item2.");
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA menu (bug 441888)
+ testGroupAtts("aria-menuitem", "1", "3");
+ testGroupAtts("aria-menuitemcheckbox", "2", "3");
+ testGroupAtts("aria-menuitemradio", "3", "3");
+ testGroupAtts("aria-menuitem2", "1", "1");
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(doTest);
]]>
</script>
@@ -64,10 +75,24 @@
<pre id="test">
</pre>
</body>
<listbox>
<listitem label="item1" id="item1"/>
<listitem label="item2" id="item2"/>
</listbox>
+
+ <vbox>
+ <description role="menuitem" id="aria-menuitem"
+ value="conventional menuitem"/>
+ <description role="menuitemcheckbox" id="aria-menuitemcheckbox"
+ value="conventional checkbox menuitem"/>
+ <description role="menuitem" hidden="true"/>
+ <description role="menuitemradio" id="aria-menuitemradio"
+ value="conventional radio menuitem"/>
+ <description role="separator"
+ value="conventional separator"/>
+ <description role="menuitem" id="aria-menuitem2"
+ value="conventional menuitem"/>
+ </vbox>
</window>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/test_nsIAccessibleDocument.html
@@ -0,0 +1,111 @@
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=441737
+-->
+<head>
+ <title>nsIAccessibleDocument chrome tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript">
+ const nsIAccessibleRetrieval = Components.interfaces.nsIAccessibleRetrieval;
+ const nsIAccessibleRole = Components.interfaces.nsIAccessibleRole;
+ const nsIAccessibleDocument = Components.interfaces.nsIAccessibleDocument;
+ const nsIDOMDocument = Components.interfaces.nsIDOMDocument;
+ const nsIDOMWindow = Components.interfaces.nsIDOMWindow;
+
+ // needed state flag
+ const state_focusable =
+ Components.interfaces.nsIAccessibleStates.STATE_FOCUSABLE;
+ const state_readonly =
+ Components.interfaces.nsIAccessibleStates.STATE_READONLY;
+
+ var gAccRetrieval = null;
+
+ function doTest()
+ {
+ gAccRetrieval = Components.classes["@mozilla.org/accessibleRetrieval;1"].
+ getService(nsIAccessibleRetrieval);
+
+ // Get accessible for body tag.
+ var docAcc = null;
+ try {
+ docAcc = gAccRetrieval.getAccessibleFor(document).
+ QueryInterface(nsIAccessibleDocument);
+ } catch(e) {}
+ ok(docAcc, "No accessible with interface for document!");
+
+ if (docAcc) {
+ // nsIAccessible
+ is(docAcc.name, "nsIAccessibleDocument chrome tests",
+ "Name for document accessible not correct!");
+ is(docAcc.role, nsIAccessibleRole.ROLE_DOCUMENT,
+ "Wrong role for document!");
+
+ // check if it is focusable, read-only.
+ var state = {}, extraState = {}
+ docAcc.getFinalState(state, extraState);
+ var desiredStates = (state_focusable | state_readonly);
+ is(state.value & desiredStates, desiredStates,
+ "Wrong state bits for document!");
+
+ // No actions wanted on doc
+ is(docAcc.numActions, 0, "Wrong number of actions for document!");
+
+ // attributes should contain tag:body
+ attributes = docAcc.attributes;
+ is(attributes.getStringProperty("tag"), "BODY",
+ "Wrong attribute on document!");
+
+ // nsIAccessibleDocument
+ is(docAcc.URL, "chrome://mochikit/content/a11y/accessible/test_nsIAccessibleDocument.html",
+ "Wrong URL for document!");
+ is(docAcc.title, "nsIAccessibleDocument chrome tests",
+ "Wrong title for document!");
+ is(docAcc.mimeType, "text/html",
+ "Wrong mime type for document!");
+ // nsDocAccessible::getDocType currently returns NS_ERROR_FAILURE.
+ // See bug 442005. After fixing, please remove this comment and
+ // uncomment the below two lines to enable the test.
+// is(docAcc.docType, "HTML",
+// "Wrong type of document!");
+
+ // Test for correct nsIDOMDocument retrieval.
+ var domDoc = null;
+ try {
+ domDoc = docAcc.document.QueryInterface(nsIDOMDocument);
+ } catch(e) {}
+ ok(domDoc, "no nsIDOMDocument for this doc accessible!");
+ is(domDoc, document, "Document nodes do not match!");
+
+ // Test for correct nsIDOMWindow retrieval.
+ var domWindow = null;
+ try {
+ domWindow = docAcc.window.QueryInterface(nsIDOMWindow);
+ } catch(e) {}
+ ok(domWindow, "no nsIDOMWindow for this doc accessible!");
+ is(domWindow, window, "Window nodes do not match!");
+ }
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=441737"
+ title="nsAccessibleDocument chrome tests">
+ Mozilla Bug 441737
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+</body>
+</html>
--- a/accessible/tests/mochitest/test_nsIAccessibleHyperLink.html
+++ b/accessible/tests/mochitest/test_nsIAccessibleHyperLink.html
@@ -6,16 +6,18 @@ https://bugzilla.mozilla.org/show_bug.cg
<head>
<title>nsIHyperLinkAccessible chrome tests</title>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript">
+ var gAccRetrieval = null;
+
function testThis(aID, aAcc, aRole, aAnchors, aName, aValid, aStartIndex,
aEndIndex)
{
is(aAcc.finalRole, aRole, "Wrong role for ID " + aID + "!");
is(aAcc.anchorCount, aAnchors, "Wrong number of anchors for ID "
+ aID + "!");
is(aAcc.getAnchor(0).name, aName, "Wrong name for ID "
+ aID + "!");
@@ -31,28 +33,56 @@ https://bugzilla.mozilla.org/show_bug.cg
{
is(aAcc.selected, aSelectedBefore,
"Wrong selected state before focus for ID " + aID + "!");
document.getElementById(aID).focus();
is(aAcc.selected, aSelectedAfter,
"Wrong seleccted state after focus for ID " + aID + "!");
}
- function testStates(aID, aAcc, aState, aExtraState, aAbsentState)
+ function testStates(aID, aAcc, aState, aExtraState, aAbsentState,
+ aShowStateDebugFlag)
{
var state = {}, extraState = {};
aAcc.getFinalState(state, extraState);
+
+ if (aShowStateDebugFlag) {
+ var list = gAccRetrieval.getStringStates(state.value, 0);
+
+ var str = "";
+ for (var i = 0; i < list.length; i++)
+ str += list.item(i) + "\n";
+
+ alert(str);
+ }
+
is(state.value & aState, aState, "Wrong state bits for ID " + aID + "!");
is(extraState.value & aExtraState, aExtraState,
"Wrong extra state bits for ID " + aID + "!");
if (aAbsentState != 0)
is(state.value & aAbsentState, 0, "state bits should not be present in ID "
+ aID + "!");
}
-
+
+ function testAction(aId, aAcc, aActionName)
+ {
+ var numActions = aActionName ? 1 : 0;
+ is(aAcc.numActions, numActions,
+ "Wrong actions number for ID " + aId);
+ try {
+ is(aAcc.getActionName(0), aActionName,
+ "Wrong action name for ID " + aId);
+ } catch (e) {
+ if (numActions)
+ ok(false, "Exception on action name getting for ID " + aId);
+ else
+ ok(true, "Correct action name for ID " + aId);
+ }
+ }
+
function doTest()
{
// Mapping needed state flags for easier handling.
const state_focusable =
Components.interfaces.nsIAccessibleStates.STATE_FOCUSABLE;
const state_focused =
Components.interfaces.nsIAccessibleStates.STATE_FOCUSED;
const state_selectable =
@@ -66,25 +96,25 @@ https://bugzilla.mozilla.org/show_bug.cg
Components.interfaces.nsIAccessibleStates.EXT_STATE_MULTI_LINE;
const ext_state_horizontal =
Components.interfaces.nsIAccessibleStates.EXT_STATE_HORIZONTAL;
const ext_state_required =
Components.interfaces.nsIAccessibleStates.STATE_REQUIRED;
const ext_state_invalid =
Components.interfaces.nsIAccessibleStates.STATE_INVALID;
- var accService = Components.classes["@mozilla.org/accessibleRetrieval;1"].
- getService(Components.interfaces.nsIAccessibleRetrieval);
+ gAccRetrieval = Components.classes["@mozilla.org/accessibleRetrieval;1"].
+ getService(Components.interfaces.nsIAccessibleRetrieval);
//////////////////////////////////////////////////////////////////////////
// normal hyperlink
var normalHyperlinkElement = document.getElementById("NormalHyperlink");
var normalHyperlinkAcc;
try {
- normalHyperlinkAcc = accService.getAccessibleFor(normalHyperlinkElement).
+ normalHyperlinkAcc = gAccRetrieval.getAccessibleFor(normalHyperlinkElement).
QueryInterface(Components.interfaces.nsIAccessibleHyperLink);
} catch(e) {
ok(normalHyperlinkAcc, "no interface for normal hyperlink!");
}
testThis("NormalHyperlink", normalHyperlinkAcc,
Components.interfaces.nsIAccessibleRole.ROLE_LINK, 1,
"Mozilla Foundation", true, 18, 19);
is(normalHyperlinkAcc.getURI(0).spec, "http://www.mozilla.org/",
@@ -97,40 +127,41 @@ https://bugzilla.mozilla.org/show_bug.cg
(state_focusable | state_focused | state_linked),
(ext_state_horizontal), (0));
//////////////////////////////////////////////////////////////////////////
// ARIA hyperlink
var ariaHyperlinkElement = document.getElementById("AriaHyperlink");
var ariaHyperlinkAcc;
try {
- ariaHyperlinkAcc = accService.getAccessibleFor(ariaHyperlinkElement).
+ ariaHyperlinkAcc = gAccRetrieval.getAccessibleFor(ariaHyperlinkElement).
QueryInterface(Components.interfaces.nsIAccessibleHyperLink);
} catch(e) {
ok(ariaHyperlinkAcc, "no interface for ARIA Hyperlink!");
}
testThis("AriaHyperlink", ariaHyperlinkAcc,
Components.interfaces.nsIAccessibleRole.ROLE_LINK, 1,
"Mozilla Foundation Home", true, 32, 33);
testStates("AriaHyperlink", ariaHyperlinkAcc,
(state_focusable | state_linked),
(ext_state_horizontal), (0));
testFocus("AriaHyperlink", ariaHyperlinkAcc, false, true);
testStates("AriaHyperlink", ariaHyperlinkAcc,
(state_focusable | state_focused | state_linked),
(ext_state_horizontal), (0));
+ testAction("AriaHyperlink", ariaHyperlinkAcc, "click");
//////////////////////////////////////////////////////////////////////////
// ARIA hyperlink with status invalid
var invalidAriaHyperlinkElement =
document.getElementById("InvalidAriaHyperlink");
var invalidAriaHyperlinkAcc;
try {
invalidAriaHyperlinkAcc =
- accService.getAccessibleFor(invalidAriaHyperlinkElement).
+ gAccRetrieval.getAccessibleFor(invalidAriaHyperlinkElement).
QueryInterface(Components.interfaces.nsIAccessibleHyperLink);
} catch(e) {
ok(invalidAriaHyperlinkAcc, "no interface for invalid ARIA hyperlink!");
}
is(invalidAriaHyperlinkAcc.valid, false, "Should not be valid!");
testStates("InvalidAriaHyperlink", invalidAriaHyperlinkAcc,
(state_linked),
(ext_state_horizontal), (state_focusable));
@@ -139,17 +170,17 @@ https://bugzilla.mozilla.org/show_bug.cg
//////////////////////////////////////////////////////////////////////////
// image map and its link children
var imageMapHyperlinkElement =
document.getElementById("imgmap");
var imageMapHyperlinkAcc;
try {
imageMapHyperlinkAcc =
- accService.getAccessibleFor(imageMapHyperlinkElement).
+ gAccRetrieval.getAccessibleFor(imageMapHyperlinkElement).
QueryInterface(Components.interfaces.nsIAccessibleHyperLink);
} catch(e) {
ok(imageMapHyperlinkAcc, "no Image Map interface!");
}
testThis("imgmap", imageMapHyperlinkAcc,
Components.interfaces.nsIAccessibleRole.ROLE_IMAGE_MAP, 2,
"b", true, 83, 84);
is(imageMapHyperlinkAcc.getURI(0).spec,
@@ -197,83 +228,212 @@ https://bugzilla.mozilla.org/show_bug.cg
(0), (0));
//////////////////////////////////////////////////////////////////////////
// empty hyperlink
var emptyLinkElement = document.getElementById("emptyLink");
var EmptyHLAcc;
try {
EmptyHLAcc =
- accService.getAccessibleFor(emptyLinkElement).
+ gAccRetrieval.getAccessibleFor(emptyLinkElement).
QueryInterface(Components.interfaces.nsIAccessibleHyperLink);
} catch (e) {
ok(EmptyHLAcc, "no interface for empty link!");
}
testThis("emptyLink", EmptyHLAcc,
Components.interfaces.nsIAccessibleRole.ROLE_LINK, 1,
- "", true, 98, 99);
+ null, true, 98, 99);
testStates("emptyLink", EmptyHLAcc,
(state_focusable | state_linked),
(ext_state_horizontal), (0));
+ testAction("emptyLink", EmptyHLAcc, "jump");
//////////////////////////////////////////////////////////////////////////
// normal hyperlink with embedded span
var hyperlinkElementWithSpan = document.getElementById("LinkWithSpan");
var hyperlinkWithSpanAcc;
try {
hyperlinkWithSpanAcc =
- accService.getAccessibleFor(hyperlinkElementWithSpan).
+ gAccRetrieval.getAccessibleFor(hyperlinkElementWithSpan).
QueryInterface(Components.interfaces.nsIAccessibleHyperLink);
} catch(e) {
ok(hyperlinkWithSpanAcc, "no interface for hyperlink with span!");
}
testThis("LinkWithSpan", hyperlinkWithSpanAcc,
Components.interfaces.nsIAccessibleRole.ROLE_LINK, 1,
"Heise Online", true, 124, 125);
is(hyperlinkWithSpanAcc.getURI(0).spec, "http://www.heise.de/",
"URI wrong for hyperlinkElementWithSpan!");
testStates("LinkWithSpan", hyperlinkWithSpanAcc,
(state_focusable | state_linked),
(ext_state_horizontal), (0));
testFocus("LinkWithSpan", hyperlinkWithSpanAcc, false, true);
testStates("LinkWithSpan", hyperlinkWithSpanAcc,
(state_focusable | state_focused | state_linked),
(ext_state_horizontal), (0));
+ testAction("LinkWithSpan", hyperlinkWithSpanAcc, "jump");
//////////////////////////////////////////////////////////////////////////
// Named anchor, should never have state_linked
var namedAnchorElement = document.getElementById("namedAnchor");
var namedAnchorAcc;
try {
- namedAnchorAcc = accService.getAccessibleFor(namedAnchorElement).
+ namedAnchorAcc = gAccRetrieval.getAccessibleFor(namedAnchorElement).
QueryInterface(Components.interfaces.nsIAccessibleHyperLink);
} catch(e) {
ok(namedAnchorAcc, "no interface for named anchor!");
}
testThis("namedAnchor", namedAnchorAcc,
Components.interfaces.nsIAccessibleRole.ROLE_LINK, 1,
"This should never be of state_linked", true, 202, 203);
testStates("namedAnchor", namedAnchorAcc,
(state_selectable),
(ext_state_horizontal), (state_focusable | state_linked));
+ testAction("namedAnchor", namedAnchorAcc, "");
+ //////////////////////////////////////////////////////////////////////////
+ // No link (hasn't any attribute), should never have state_linked
+ var noLinkElement = document.getElementById("noLink");
+ var noLinkAcc;
+ try {
+ noLinkAcc = gAccRetrieval.getAccessibleFor(noLinkElement).
+ QueryInterface(Components.interfaces.nsIAccessibleHyperLink);
+ } catch(e) {
+ ok(noLinkAcc, "no interface for named anchor!");
+ }
+ testThis("noLink", noLinkAcc,
+ Components.interfaces.nsIAccessibleRole.ROLE_LINK, 1,
+ "This should never be of state_linked", true, 262, 263);
+ testStates("noLink", noLinkAcc,
+ 0,
+ (ext_state_horizontal), (state_focusable | state_linked));
+ testAction("noLink", noLinkAcc, "");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Link with registered 'click' event, should have state_linked
+ var linkWithClickElement = document.getElementById("linkWithClick");
+ var linkWithClickAcc;
+ try {
+ linkWithClickAcc = gAccRetrieval.getAccessibleFor(linkWithClickElement).
+ QueryInterface(Components.interfaces.nsIAccessibleHyperLink);
+ } catch(e) {
+ ok(linkWithClickAcc, "no interface for named anchor!");
+ }
+ testThis("linkWithClick", linkWithClickAcc,
+ Components.interfaces.nsIAccessibleRole.ROLE_LINK, 1,
+ "This should have state_linked", true, 301, 302);
+ testStates("linkWithClick", linkWithClickAcc,
+ (state_linked),
+ (ext_state_horizontal), 0);
+ testAction("linkWithClick", linkWithClickAcc, "click");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Maps to group links (bug 431615).
+ var linksMapElement = document.getElementById("linksmap");
+ var linksMapAcc;
+ try {
+ linksMapAcc = gAccRetrieval.getAccessibleFor(linksMapElement);
+ } catch(e) { }
+
+ ok(linksMapAcc, "no accessible for map grouping links!");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Link with title attribute, no name from the subtree (bug 438325).
+ var id = "linkWithTitleNoNameFromSubtree";
+ var linkElement = document.getElementById(id);
+ var linkAcc;
+ try {
+ linkAcc = gAccRetrieval.getAccessibleFor(linkElement).
+ QueryInterface(Components.interfaces.nsIAccessibleHyperLink);
+ } catch(e) {
+ ok(linkAcc, "no interface for link with ID " + id + "!");
+ }
+ testThis(id, linkAcc,
+ Components.interfaces.nsIAccessibleRole.ROLE_LINK, 1,
+ "Link with title", true, 354, 355);
+ testStates(id, linkAcc,
+ (state_linked),
+ (ext_state_horizontal), 0);
+ testAction(id, linkAcc, "jump");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Link with title attribute, name from the subtree - onsreen name
+ // (bug 438325).
+ id = "linkWithTitleNameFromSubtree";
+ linkElement = document.getElementById(id);
+ linkAcc;
+ try {
+ linkAcc = gAccRetrieval.getAccessibleFor(linkElement).
+ QueryInterface(Components.interfaces.nsIAccessibleHyperLink);
+ } catch(e) {
+ ok(linkAcc, "no interface for link with ID " + id + "!");
+ }
+ testThis(id, linkAcc,
+ Components.interfaces.nsIAccessibleRole.ROLE_LINK, 1,
+ "the name from subtree", true, 403, 404);
+ testStates(id, linkAcc,
+ (state_linked),
+ (ext_state_horizontal), 0);
+ testAction(id, linkAcc, "jump");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Link with title attribute, name from the nested html:img (bug 438325).
+ id = "linkWithTitleNameFromImg";
+ linkElement = document.getElementById(id);
+ linkAcc;
+ try {
+ linkAcc = gAccRetrieval.getAccessibleFor(linkElement).
+ QueryInterface(Components.interfaces.nsIAccessibleHyperLink);
+ } catch(e) {
+ ok(linkAcc, "no interface for link with ID " + id + "!");
+ }
+ testThis(id, linkAcc,
+ Components.interfaces.nsIAccessibleRole.ROLE_LINK, 1,
+ "The title for link", true, 458, 459);
+ testStates(id, linkAcc,
+ (state_linked),
+ (ext_state_horizontal), 0);
+ testAction(id, linkAcc, "jump");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Link with label, no name from the subtree (bug 438325).
+ id = "linkWithLabelNoNameFromSubtree";
+ linkElement = document.getElementById(id);
+ linkAcc;
+ try {
+ linkAcc = gAccRetrieval.getAccessibleFor(linkElement).
+ QueryInterface(Components.interfaces.nsIAccessibleHyperLink);
+ } catch(e) {
+ ok(linkAcc, "no interface for link with ID " + id + "!");
+ }
+ testThis(id, linkAcc,
+ Components.interfaces.nsIAccessibleRole.ROLE_LINK, 1,
+ "Link with label and nested image:", true, 462, 463);
+ testStates(id, linkAcc,
+ (state_linked),
+ (ext_state_horizontal), 0);
+ testAction(id, linkAcc, "jump");
+
+ //////////////////////////////////////////////////////////////////////////
+ //
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=418368">Mozilla Bug 418368</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
+
<br>Simple link:<br>
<a id="NormalHyperlink" href="http://www.mozilla.org">Mozilla Foundation</a>
<br>ARIA link:<br>
<span id="AriaHyperlink" role="link"
onclick="window.open('http://www.mozilla.org/');"
tabindex="0">Mozilla Foundation Home</span>
<br>Invalid, non-focusable hyperlink:<br>
<span id="InvalidAriaHyperlink" role="link" aria-invalid="true"
@@ -288,16 +448,51 @@ https://bugzilla.mozilla.org/show_bug.cg
coords="0,0,13,14"
alt="a"
shape="rect"></area>
</map>
<img width="447" id="imgmap"
height="15"
usemap="#atoz_map"
src="http://www.bbc.co.uk/radio4/images/letters.gif"></img>
+
<br>Empty link:<br>
<a id="emptyLink" href=""><img src=""></img></a>
+
<br>Link with embedded span<br>
<a id="LinkWithSpan" href="http://www.heise.de/"><span lang="de">Heise Online</span></a>
+
<br>Named anchor, must not have "linked" state for it to be exposed correctly:<br>
<a id="namedAnchor" name="named_anchor">This should never be of state_linked</a>
+
+ <br>Link having no attributes, must not have "linked" state:</br>
+ <a id="noLink">This should never be of state_linked</a>
+
+ <br>Link with registered 'click' event: </br>
+ <a id="linkWithClick" onclick="var clicked = true;">This should have state_linked</a>
+
+ <br>Link with title attribute (no name from subtree): </br>
+ <a id="linkWithTitleNoNameFromSubtree" href="http://www.heise.de/"
+ title="Link with title"><img src=""/></a>
+
+ <br>Link with title attribute (name from subtree): </br>
+ <a id="linkWithTitleNameFromSubtree" href="http://www.heise.de/"
+ title="Link with title">the name from subtree</a>
+
+ <br>Link with title attribute (name from nested image): </br>
+ <a id="linkWithTitleNameFromImg" href="http://www.heise.de/"
+ title="Link with title"><img src="" alt="The title for link"/></a>
+
+ <br><label for="linkWithLabelNoNameFromSubtree">Link with label and nested image: </label></br>
+ <a id="linkWithLabelNoNameFromSubtree"
+ href="http://www.heise.de/"><img src=""/></a>
+
+ <br>Map that is used to group links (www.w3.org/TR/WCAG10-HTML-TECHS/#group-bypass),
+ also see the bug 431615:<br>
+ <map id="linksmap" title="Site navigation">
+ <ul>
+ <li><a href="http://mozilla.org">About the project</a></li>
+ <li><a href="http://mozilla.org">Sites and sounds</a></li>
+ </ul>
+ </map>
+
</body>
</html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/test_nsOuterDocAccessible.html
@@ -0,0 +1,106 @@
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=441519
+-->
+<head>
+ <title>nsOuterDocAccessible chrome tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript">
+ const nsIAccessibleRetrieval = Components.interfaces.nsIAccessibleRetrieval;
+ const nsIAccessibleRole = Components.interfaces.nsIAccessibleRole;
+
+ // needed state flag
+ const state_focusable =
+ Components.interfaces.nsIAccessibleStates.STATE_FOCUSABLE;
+
+ // needed error return value
+ const ns_error_invalid_arg = Components.results.NS_ERROR_INVALID_ARG;
+
+ var gAccRetrieval = null;
+
+ function doTest()
+ {
+ gAccRetrieval = Components.classes["@mozilla.org/accessibleRetrieval;1"].
+ getService(nsIAccessibleRetrieval);
+
+ // Get accessible for body tag.
+ var docAcc = null;
+ try {
+ docAcc = gAccRetrieval.getAccessibleFor(document);
+ } catch(e) {}
+ ok(docAcc, "No accessible for document!");
+
+ if (docAcc) {
+ var outerDocAcc = null;
+ try {
+ outerDocAcc = docAcc.parent;
+ } catch(e) {}
+ ok(outerDocAcc, "No internal frame parent for document!");
+
+ if (outerDocAcc) {
+ is(outerDocAcc.role, nsIAccessibleRole.ROLE_INTERNAL_FRAME,
+ "Wrong role for internal frame!");
+
+ // check if it is focusable, not desired.
+ var state = {}, extraState = {}
+ outerDocAcc.getFinalState(state, extraState);
+ is(state.value & state_focusable, 0,
+ "Wrong focusable state bit for internal frame!");
+
+ // see bug 428954: No name wanted for internal frame
+ is(outerDocAcc.name, "", "Wrong name for internal frame!");
+
+ // see bug 440770, no actions wanted on outer doc
+ is(outerDocAcc.numActions, 0,
+ "Wrong number of actions for internal frame!");
+ var actionTempStr; // not really used, just needs to receive a value
+ try {
+ actionTempStr = outerDocAcc.getActionName(0);
+ do_throw("No exception thrown for actionName!");
+ } catch(e) {
+ ok(e.result, ns_error_invalid_arg,
+ "Wrong return value for actionName call!");
+ }
+
+ try {
+ actionTempStr = outerDocAcc.getActionDescription(0);
+ do_throw("No exception thrown for actionDescription!");
+ } catch(e) {
+ ok(e.result, ns_error_invalid_arg,
+ "Wrong return value for actionDescription call!");
+ }
+
+ try {
+ outerDocAcc.doAction(0);
+ do_throw("No exception thrown for doAction!");
+ } catch(e) {
+ ok(e.result, ns_error_invalid_arg,
+ "Wrong return value for doAction call!");
+ }
+ }
+ }
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=441519"
+ title="nsOuterDocAccessible chrome tests">
+ Mozilla Bug 441519
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+</body>
+</html>
--- a/allmakefiles.sh
+++ b/allmakefiles.sh
@@ -58,16 +58,17 @@ fi
add_makefiles "
Makefile
build/Makefile
build/unix/Makefile
config/Makefile
config/autoconf.mk
config/mkdepend/Makefile
config/doxygen.cfg
+config/tests/src-simple/Makefile
probes/Makefile
extensions/Makefile
"
if [ "$MOZ_MEMORY" ]; then
add_makefiles "
memory/jemalloc/Makefile
"
--- a/browser/app/Makefile.in
+++ b/browser/app/Makefile.in
@@ -71,19 +71,16 @@ GRE_MILESTONE = $(shell $(PYTHON) $(tops
GRE_BUILDID = $(shell $(PYTHON) $(topsrcdir)/config/printconfigsetting.py $(LIBXUL_DIST)/bin/platform.ini Build BuildID)
DEFINES += -DGRE_MILESTONE=$(GRE_MILESTONE) -DGRE_BUILDID=$(GRE_BUILDID)
ifdef MOZ_MEMORY
ifneq ($(OS_ARCH),WINNT)
LIBS += -ljemalloc
endif
-ifeq ($(OS_ARCH),SunOS)
-SOLARIS_JEMALLOC_LDFLAGS = -L$(LIBXUL_DIST)/bin -lxul
-endif
endif
ifdef LIBXUL_SDK
include $(topsrcdir)/config/rules.mk
else
# Build a binary bootstrapping with XRE_main
ifeq ($(USE_SHORT_LIBNAME), 1)
@@ -158,26 +155,17 @@ NSDISTMODE = copy
include $(topsrcdir)/config/config.mk
ifdef _MSC_VER
# Always enter a Windows program through wmain, whether or not we're
# a console application.
WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup
endif
-ifndef BUILD_STATIC_LIBS
-
-ifdef NS_TRACE_MALLOC
-# when libxul is enabled, trace-malloc is part of it
-ifndef MOZ_ENABLE_LIBXUL
-EXTRA_DSO_LIBS += tracemalloc
-endif
-endif
-
-else
+ifdef BUILD_STATIC_LIBS
include $(topsrcdir)/config/static-config.mk
EXTRA_DEPS += \
$(STATIC_EXTRA_DEPS) \
$(NULL)
DEFINES += $(STATIC_DEFINES)
CPPSRCS += $(STATIC_CPPSRCS)
EXTRA_DSO_LIBS += $(STATIC_EXTRA_DSO_LIBS)
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -234,19 +234,17 @@ pref("browser.urlbar.search.timeout", 10
pref("browser.download.saveLinkAsFilenameTimeout", 1000);
pref("browser.download.useDownloadDir", true);
pref("browser.download.folderList", 0);
pref("browser.download.manager.showAlertOnComplete", true);
pref("browser.download.manager.showAlertInterval", 2000);
pref("browser.download.manager.retention", 2);
pref("browser.download.manager.showWhenStarting", true);
-pref("browser.download.manager.useWindow", true);
pref("browser.download.manager.closeWhenDone", false);
-pref("browser.download.manager.openDelay", 0);
pref("browser.download.manager.focusWhenStarting", false);
pref("browser.download.manager.flashCount", 2);
pref("browser.download.manager.addToRecentDocs", true);
pref("browser.download.manager.quitBehavior", 0);
pref("browser.download.manager.scanWhenDone", true);
pref("browser.download.manager.resumeOnWakeDelay", 10000);
// search engines URL
@@ -479,17 +477,17 @@ pref("accessibility.typeaheadfind", fals
pref("accessibility.typeaheadfind.timeout", 5000);
pref("accessibility.typeaheadfind.linksonly", false);
pref("accessibility.typeaheadfind.flashBar", 1);
// Disable the default plugin for firefox
pref("plugin.default_plugin_disabled", true);
// plugin finder service url
-pref("pfs.datasource.url", "https://pfs.mozilla.org/plugins/PluginFinderService.php?mimetype=%PLUGIN_MIMETYPE%&appID=%APP_ID%&appVersion=%APP_VERSION%&clientOS=%CLIENT_OS%&chromeLocale=%CHROME_LOCALE%");
+pref("pfs.datasource.url", "https://pfs.mozilla.org/plugins/PluginFinderService.php?mimetype=%PLUGIN_MIMETYPE%&appID=%APP_ID%&appVersion=%APP_VERSION%&clientOS=%CLIENT_OS%&chromeLocale=%CHROME_LOCALE%&appRelease=%APP_RELEASE%");
// by default we show an infobar message when pages require plugins the user has not installed
pref("plugins.hide_infobar_for_missing_plugin", false);
#ifdef XP_WIN
pref("browser.preferences.instantApply", false);
#else
pref("browser.preferences.instantApply", true);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -934,18 +934,16 @@ function delayedStartup()
// initiated by a web page script
window.addEventListener("fullscreen", onFullScreen, true);
if (gIsLoadingBlank && gURLBar && isElementVisible(gURLBar))
focusElement(gURLBar);
else
focusElement(content);
- SetPageProxyState("invalid");
-
var navToolbox = getNavToolbox();
navToolbox.customizeDone = BrowserToolboxCustomizeDone;
navToolbox.customizeChange = BrowserToolboxCustomizeChange;
// Set up Sanitize Item
gSanitizeListener = new SanitizeListener();
// Enable/Disable auto-hide tabbar
@@ -2544,23 +2542,18 @@ function FillInHTMLTooltip(tipElement)
}
return retVal;
}
var proxyIconDNDObserver = {
onDragStart: function (aEvent, aXferData, aDragAction)
{
- var value = gURLBar.value;
- // XXX - do we want to allow the user to set a blank page to their homepage?
- // if so then we want to modify this a little to set about:blank as
- // the homepage in the event of an empty urlbar.
- if (!value) return;
-
- var urlString = value + "\n" + window.content.document.title;
+ var value = content.location.href;
+ var urlString = value + "\n" + content.document.title;
var htmlString = "<a href=\"" + value + "\">" + value + "</a>";
aXferData.data = new TransferData();
aXferData.data.addDataForFlavour("text/x-moz-url", urlString);
aXferData.data.addDataForFlavour("text/unicode", value);
aXferData.data.addDataForFlavour("text/html", htmlString);
// we're copying the URL from the proxy icon, not moving
@@ -4809,36 +4802,16 @@ function asyncOpenWebPanel(event)
// you to add a sidebar panel. We support the Opera convention here. The link's
// title attribute contains the title that should be used for the sidebar panel.
PlacesUIUtils.showMinimalAddBookmarkUI(makeURI(wrapper.href),
wrapper.getAttribute("title"),
null, null, true, true);
event.preventDefault();
return false;
}
- else if (target == "_search") {
- // Used in WinIE as a way of transiently loading pages in a sidebar. We
- // mimic that WinIE functionality here and also load the page transiently.
-
- // DISALLOW_INHERIT_PRINCIPAL is used here in order to also
- // block javascript and data: links targeting the sidebar.
- try {
- const nsIScriptSecurityMan = Ci.nsIScriptSecurityManager;
- urlSecurityCheck(wrapper.href,
- wrapper.ownerDocument.nodePrincipal,
- nsIScriptSecurityMan.DISALLOW_INHERIT_PRINCIPAL);
- }
- catch(ex) {
- return false;
- }
-
- openWebPanel(gNavigatorBundle.getString("webPanels"), wrapper.href);
- event.preventDefault();
- return false;
- }
}
else {
handleLinkClick(event, wrapper.href, linkNode);
}
return true;
} else {
// Try simple XLink
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -340,16 +340,17 @@
chromedir="&locale.dir;"
onclick="getIdentityHandler().handleIdentityButtonEvent(event);"
onkeypress="getIdentityHandler().handleIdentityButtonEvent(event);">
<hbox align="center">
<stack id="page-proxy-stack"
onclick="PageProxyClickHandler(event);">
<image id="urlbar-throbber" busy="false"/>
<image id="page-proxy-favicon" validate="never"
+ pageproxystate="invalid"
ondraggesture="PageProxyDragGesture(event);"
onerror="this.removeAttribute('src');"/>
</stack>
<label id="identity-icon-label"/>
</hbox>
</box>
<hbox id="urlbar-icons">
<button type="menu"
@@ -358,29 +359,24 @@
id="feed-button"
chromedir="&locale.dir;"
onclick="return FeedHandler.onFeedButtonClick(event);">
<menupopup position="after_end"
onpopupshowing="return FeedHandler.buildFeedList(this);"
oncommand="return FeedHandler.subscribeToFeed(null, event);"
onclick="checkForMiddleClick(this, event);"/>
</button>
-#ifdef MOZ_SAFE_BROWSING
- <image id="safebrowsing-urlbar-icon" tooltiptext="&safeb.urlbaricon.tooltip;"
- level="safe"
+ <image id="star-button"
class="urlbar-icon"
- onclick="goDoCommand('safebrowsing-show-warning')"/>
-#endif
- <image id="star-button"
- class="urlbar-icon"
- onclick="PlacesStarButton.onClick(event);"/>
- <image id="go-button" chromedir="&locale.dir;"
- class="urlbar-icon"
- tooltiptext="&goEndCap.tooltip;"
- onclick="handleURLBarCommand(event);"/>
+ onclick="PlacesStarButton.onClick(event);"/>
+ <image id="go-button"
+ chromedir="&locale.dir;"
+ class="urlbar-icon"
+ tooltiptext="&goEndCap.tooltip;"
+ onclick="handleURLBarCommand(event);"/>
</hbox>
</textbox>
</toolbaritem>
<toolbaritem id="search-container" title="&searchItem.title;"
align="center" class="chromeclass-toolbar-additional"
flex="100" persist="width">
<searchbar id="searchbar" flex="1" chromedir="&locale.dir;"
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -1222,18 +1222,21 @@ nsContextMenu.prototype = {
isTargetATextBox: function(node) {
if (node instanceof HTMLInputElement)
return (node.type == "text" || node.type == "password")
return (node instanceof HTMLTextAreaElement);
},
isTargetAKeywordField: function(aNode) {
+ if (!(aNode instanceof HTMLInputElement))
+ return false;
+
var form = aNode.form;
- if (!form)
+ if (!form || aNode.type == "password")
return false;
var method = form.method.toUpperCase();
// These are the following types of forms we can create keywords for:
//
// method encoding type can create keyword
// GET * YES
--- a/browser/branding/unofficial/locales/jar.mn
+++ b/browser/branding/unofficial/locales/jar.mn
@@ -1,6 +1,7 @@
#filter substitution
@AB_CD@.jar:
% locale branding @AB_CD@ %locale/branding/
- locale/branding/brand.dtd (%brand.dtd)
-* locale/branding/brand.properties (%brand.properties)
+# Gran Paradiso branding only exists in en-US
+ locale/branding/brand.dtd (en-US/brand.dtd)
+* locale/branding/brand.properties (en-US/brand.properties)
--- a/browser/components/places/content/toolbar.xml
+++ b/browser/components/places/content/toolbar.xml
@@ -757,58 +757,67 @@
// dragging over this menu--insertion point, child index to drop
// before, and folder to drop into.
_getDropPoint: function TBV_DO_getDropPoint(event) {
// Can't drop if the toolbar isn't a folder.
var result = this._self.getResult();
if (!PlacesUtils.nodeIsFolder(result.root))
return null;
+ var isRTL = document.defaultView
+ .getComputedStyle(this._self.parentNode, "")
+ .direction == "rtl";
+
var dropPoint = { ip: null, beforeIndex: null, folderNode: null };
// Loop through all the nodes to see which one this should
// get dropped in/next to
for (var i = 0; i < this._self.childNodes.length; i++) {
var xulNode = this._self.childNodes[i];
if (PlacesUtils.nodeIsFolder(xulNode.node) &&
!PlacesUtils.nodeIsReadOnly(xulNode.node)) {
// This is a folder. If the mouse is in the left 25% of the
- // node, drop to the left of the folder. If it's in the middle
- // 50%, drop into the folder. If it's past that, drop to the right.
- if (event.clientX < xulNode.boxObject.x + (xulNode.boxObject.width * 0.25)) {
+ // node (or 25% of the right, in RTL UI), drop before the folder.
+ // If it's in the middle 50%, drop into the folder. If it's past
+ // that, drop after.
+ if ((isRTL && event.clientX > xulNode.boxObject.x + (xulNode.boxObject.width * 0.75)) ||
+ (!isRTL && event.clientX < xulNode.boxObject.x + (xulNode.boxObject.width * 0.25))) {
// Drop to the left of this folder.
dropPoint.ip =
new InsertionPoint(PlacesUtils.getConcreteItemId(result.root),
i, -1);
dropPoint.beforeIndex = i;
return dropPoint;
}
- else if (event.clientX < xulNode.boxObject.x + (xulNode.boxObject.width * 0.75)) {
+ else if ((isRTL && event.clientX > xulNode.boxObject.x + (xulNode.boxObject.width * 0.25)) ||
+ (!isRTL && event.clientX < xulNode.boxObject.x + (xulNode.boxObject.width * 0.75))) {
// Drop inside this folder.
dropPoint.ip =
new InsertionPoint(PlacesUtils.getConcreteItemId(xulNode.node),
-1, 1);
dropPoint.beforeIndex = i;
dropPoint.folderNode = xulNode;
return dropPoint;
}
}
else {
- // This is a non-folder node. If the mouse is left of the middle,
- // drop to the left of the folder. If it's right, drop to the right.
- if (event.clientX < xulNode.boxObject.x + (xulNode.boxObject.width / 2)) {
- // Drop to the left of this bookmark.
+ // This is a non-folder node. If the mouse is left (or right, in
+ // RTL UI) of the middle, drop before the folder. Otehrwise,
+ // we'll drop after
+ if ((isRTL && event.clientX > xulNode.boxObject.x + (xulNode.boxObject.width / 2)) ||
+ (!isRTL && event.clientX < xulNode.boxObject.x + (xulNode.boxObject.width / 2))) {
+ // Drop before this bookmark.
dropPoint.ip =
new InsertionPoint(PlacesUtils.getConcreteItemId(result.root),
i, -1);
dropPoint.beforeIndex = i;
return dropPoint;
}
}
}
- // Should drop to the right of the last node.
+ // Should drop after the last node.
dropPoint.ip =
new InsertionPoint(PlacesUtils.getConcreteItemId(result.root),
-1, 1);
dropPoint.beforeIndex = -1;
return dropPoint;
},
onDragStart: function TBV_DO_onDragStart(event, xferData, dragAction) {
@@ -888,22 +897,29 @@
ind.style.marginLeft = this._self.lastChild.boxObject.x +
this._self.lastChild.boxObject.width - this._self.boxObject.x - halfInd + 'px';
else
ind.style.marginLeft = this._self.childNodes[dropPoint.beforeIndex].boxObject.x -
this._self.boxObject.x - halfInd + 'px';
}
else {
halfInd = Math.floor(halfInd);
- if (dropPoint.beforeIndex == -1 || !this._self.childNodes.length)
- ind.style.marginRight = '0px';
- else
- ind.style.marginRight = (this._self.childNodes[this._self.childNodes.length - 1].boxObject.x +
- this._self.childNodes[this._self.childNodes.length - 1].boxObject.width) -
- (this._self.childNodes[dropPoint.beforeIndex].boxObject.x) - halfInd + 'px';
+ if (this._self.childNodes.length == 0)
+ ind.style.marginRight = this._self.boxObject.width + 'px';
+ else if (dropPoint.beforeIndex == -1) {
+ ind.style.marginRight = this._self.boxObject.width -
+ (this._self.childNodes[this._self.childNodes.length - 1].boxObject.x +
+ halfInd) +'px';
+ }
+ else {
+ ind.style.marginRight = this._self.boxObject.width -
+ (this._self.childNodes[dropPoint.beforeIndex].boxObject.x +
+ this._self.childNodes[dropPoint.beforeIndex].boxObject.width -
+ this._self.boxObject.x + halfInd) + 'px';
+ }
}
// Clear out old folder information
this._clearOverFolder();
}
},
onDrop: function TBV_DO_onDrop(event, dropData, session) {
var dropPoint = this._getDropPoint(event);
--- a/browser/installer/removed-files.in
+++ b/browser/installer/removed-files.in
@@ -605,20 +605,20 @@ init.d/README
redo-prebinding.sh
res/viewer.properties
res/bloatcycle.html
#endif
#ifdef XP_UNIX
#ifndef XP_MACOSX
readme.txt
chrome/icons/default/default.xpm
+dictionaries/PL.dic
+dictionaries/PL.aff
#endif
#endif
-dictionaries/PL.dic
-dictionaries/PL.aff
#ifdef XP_WIN
#ifdef MOZ_MEMORY
Microsoft.VC80.CRT.manifest
msvcm80.dll
msvcp80.dll
msvcr80.dll
#else
mozcrt19.dll
--- a/browser/locales/en-US/chrome/browser/safebrowsing/phishing-afterload-warning-message.dtd
+++ b/browser/locales/en-US/chrome/browser/safebrowsing/phishing-afterload-warning-message.dtd
@@ -10,18 +10,16 @@
<!ENTITY safeb.palm.accept.label "Get me out of here!">
<!ENTITY safeb.palm.accept.statustext "Navigate to my home page">
<!ENTITY safeb.palm.decline.label "Ignore this warning">
<!ENTITY safeb.palm.decline.statustext "Close warning" >
<!ENTITY safeb.palm.notforgery.label2 "This isn't a web forgery…">
<!ENTITY safeb.palm.report.label "Why was this site blocked?">
-<!ENTITY safeb.urlbaricon.tooltip "This page might be dangerous; click for details.">
-
<!ENTITY safeb.blocked.malware.title "Reported Attack Site!">
<!-- Localization note (safeb.blocked.malware.shortDesc) - Please don't translate the contents of the <span id="malware_sitename"/> tag. It will be replaced at runtime with a domain name (e.g. www.badsite.com) -->
<!ENTITY safeb.blocked.malware.shortDesc "This web site at <span id='malware_sitename'/> has been reported as an attack site and has been blocked based on your security preferences.">
<!ENTITY safeb.blocked.malware.longDesc "<p>Attack sites try to install programs that steal private information, use your computer to attack others, or damage your system.</p><p>Some attack sites intentionally distribute harmful software, but many are compromised without the knowledge or permission of their owners.</p>">
<!ENTITY safeb.blocked.phishing.title "Reported Web Forgery!">
<!-- Localization note (safeb.blocked.phishing.shortDesc) - Please don't translate the contents of the <span id="phishing_sitename"/> tag. It will be replaced at runtime with a domain name (e.g. www.badsite.com) -->
<!ENTITY safeb.blocked.phishing.shortDesc "This web site at <span id='phishing_sitename'/> has been reported as a web forgery and has been blocked based on your security preferences.">
--- a/browser/locales/shipped-locales
+++ b/browser/locales/shipped-locales
@@ -32,16 +32,18 @@ nb-NO
nl
nn-NO
pa-IN
pl
pt-BR
pt-PT
ro
ru
+si
sk
+sl
sq
sr
sv-SE
tr
uk
zh-CN
zh-TW
--- a/browser/themes/gnomestripe/browser/browser.css
+++ b/browser/themes/gnomestripe/browser/browser.css
@@ -1076,20 +1076,16 @@ toolbar[iconsize="small"] #paste-button[
}
/* Go button */
#go-button {
padding: 3px 2px 2px 2px;
list-style-image: url("chrome://browser/skin/Go-arrow.png");
}
-#go-button[chromedir="rtl"] {
- list-style-image: url("chrome://browser/skin/Go-arrow-rtl.png");
-}
-
/* Star button */
#star-button {
padding: 1px;
list-style-image: url("chrome://browser/skin/places/starPage.png");
}
#star-button[starred="true"] {
list-style-image: url("chrome://browser/skin/places/pageStarred.png");
--- a/browser/themes/gnomestripe/browser/engineManager.css
+++ b/browser/themes/gnomestripe/browser/engineManager.css
@@ -35,13 +35,15 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
%endif
#engineList treechildren::-moz-tree-image(engineName) {
-moz-margin-end: 4px;
-moz-margin-start: 1px;
+ width: 16px;
+ height: 16px;
}
#engineList treechildren::-moz-tree-row {
height: 20px !important;
}
--- a/browser/themes/pinstripe/browser/browser.css
+++ b/browser/themes/pinstripe/browser/browser.css
@@ -92,21 +92,23 @@
}
#main-window:not([active="true"]) .tabbrowser-tab[selected="true"] > .tab-image-middle,
#main-window:not([active="true"]) .tabbrowser-tab[selected="true"] > .tab-closebutton,
#main-window:not([active="true"]) .tabbrowser-tab[selected="true"] > .tab-close-button {
background-image: url("chrome://browser/skin/tabbrowser/tab-middle-inactive.png");
}
-#main-window:not([active="true"]) .tabbrowser-tab[selected="true"] > .tab-image-left {
+#main-window:not([active="true"]) .tabbrowser-tab[selected="true"] > .tab-image-left,
+#main-window:not([active="true"]) .tabbrowser-tab[selected="true"][chromedir="rtl"] > .tab-image-right {
background: url("chrome://browser/skin/tabbrowser/tab-left-inactive.png") no-repeat;
}
-#main-window:not([active="true"]) .tabbrowser-tab[selected="true"] > .tab-image-right {
+#main-window:not([active="true"]) .tabbrowser-tab[selected="true"] > .tab-image-right,
+#main-window:not([active="true"]) .tabbrowser-tab[selected="true"][chromedir="rtl"] > .tab-image-left {
background: url("chrome://browser/skin/tabbrowser/tab-right-inactive.png") no-repeat;
}
/* ----- SEARCH FIELD ----- */
#wrapper-search-container #searchbar html|*.textbox-input {
visibility: hidden;
}
@@ -1629,22 +1631,22 @@ toolbarbutton.chevron > .toolbarbutton-m
}
.tabbrowser-tab:not([selected="true"]):hover .tab-icon,
.tabbrowser-tab[selected="true"] .tab-icon {
opacity: 1.0;
}
.tab-text {
- font: message-box;
margin-top: 0 !important;
}
.tab-text,
.tab-text-shadow {
+ font: message-box;
font-weight: bold !important;
}
.tabbrowser-tab[busy] > .tab-icon-image,
.tabbrowser-tab[busy] > .tab-image-middle > .tab-icon > .tab-icon-image {
list-style-image: url("chrome://global/skin/icons/loading_16.png") !important;
}
--- a/browser/themes/pinstripe/browser/engineManager.css
+++ b/browser/themes/pinstripe/browser/engineManager.css
@@ -35,13 +35,15 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
%endif
#engineList treechildren::-moz-tree-image(engineName) {
-moz-margin-end: 4px;
-moz-margin-start: 1px;
+ width: 16px;
+ height: 16px;
}
#engineList treechildren::-moz-tree-row {
height: 20px !important;
}
--- a/browser/themes/winstripe/browser/browser.css
+++ b/browser/themes/winstripe/browser/browser.css
@@ -1228,20 +1228,16 @@ statusbarpanel#statusbar-display {
/* ::::: go button ::::: */
#go-button {
padding: 0 2px;
list-style-image: url("chrome://browser/skin/Go-arrow.png");
-moz-image-region: rect(0px 16px 16px 0px);
}
-#go-button[chromedir="rtl"] {
- list-style-image: url("chrome://browser/skin/Go-arrow-rtl.png");
-}
-
#go-button:hover {
-moz-image-region: rect(16px 16px 32px 0px);
}
/* star button */
#star-button {
padding: 0 2px;
list-style-image: url("chrome://browser/skin/places/bookmark.png");
--- a/browser/themes/winstripe/browser/engineManager.css
+++ b/browser/themes/winstripe/browser/engineManager.css
@@ -35,13 +35,15 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
%endif
#engineList treechildren::-moz-tree-image(engineName) {
-moz-margin-end: 4px;
-moz-margin-start: 1px;
+ width: 16px;
+ height: 16px;
}
#engineList treechildren::-moz-tree-row {
height: 20px !important;
}
--- a/browser/themes/winstripe/browser/places/organizer-aero.css
+++ b/browser/themes/winstripe/browser/places/organizer-aero.css
@@ -4,20 +4,33 @@
border-top: none;
}
#placesToolbar {
-moz-appearance: -moz-win-media-toolbox;
color: -moz-win-mediatext;
}
+#placesToolbar:-moz-system-metric(windows-default-theme) {
+ min-height: 36px;
+ padding-top: 0px;
+ padding-bottom: 0px;
+ -moz-padding-start: 6px;
+ -moz-padding-end: 8px;
+}
+
#placesMenu > menu {
color: -moz-win-mediatext;
}
+#placesMenu:-moz-system-metric(windows-default-theme) > menu > label {
+ -moz-padding-end: 15px;
+ background-image: url(chrome://browser/skin/places/dropDown.png);
+}
+
#placesView > splitter:-moz-system-metric(windows-default-theme) {
border: 0;
-moz-border-end: 1px solid;
-moz-border-right-colors: #A9B7C9;
-moz-border-left-colors: #A9B7C9;
min-width: 0;
width: 3px !important;
background-color: transparent;
--- a/browser/themes/winstripe/browser/places/organizer.css
+++ b/browser/themes/winstripe/browser/places/organizer.css
@@ -1,13 +1,13 @@
/* Toolbar */
#placesToolbar {
- border: none;
- min-height: 36px;
+ padding: 3px; /* b/f buttons have a 1px image padding */
+ -moz-padding-end: 4px;
}
/* back & forward buttons */
#back-button, #forward-button {
list-style-image: url(libraryNavigation.png);
-moz-appearance: none;
padding: 0;
border: none;
@@ -16,17 +16,16 @@
#placesToolbar > toolbarbutton > image,
#placesToolbar > toolbarbutton > label {
margin: 0;
padding: 0;
}
#back-button,
#forward-button[chromedir="rtl"] {
- -moz-margin-start: 8px;
-moz-image-region: rect(0px, 24px, 24px, 0px);
}
#back-button:not([disabled="true"]):hover,
#forward-button:not([disabled="true"]):hover[chromedir="rtl"] {
-moz-image-region: rect(24px, 24px, 48px, 0px);
}
#back-button[disabled="true"],
#forward-button[chromedir="rtl"][disabled="true"] {
@@ -34,17 +33,16 @@
}
#back-button:not([disabled="true"]):hover:active,
#forward-button:not([disabled="true"]):hover:active[chromedir="rtl"] {
-moz-image-region: rect(72px, 24px, 96px, 0px);
}
#forward-button,
#back-button[chromedir="rtl"] {
- -moz-margin-end: 8px;
-moz-image-region: rect(0px, 48px, 24px, 24px);
}
#forward-button:not([disabled="true"]):hover,
#back-button:not([disabled="true"]):hover[chromedir="rtl"] {
-moz-image-region: rect(24px, 48px, 48px, 24px);
}
#forward-button[disabled="true"],
#back-button[chromedir="rtl"][disabled="true"] {
@@ -52,16 +50,17 @@
}
#forward-button:not([disabled="true"]):hover:active,
#back-button:not([disabled="true"]):hover:active[chromedir="rtl"] {
-moz-image-region: rect(72px, 48px, 96px, 24px);
}
/* Menu */
#placesMenu {
+ -moz-margin-start: 8px;
-moz-appearance: none;
border: none;
}
#placesMenu > menu {
-moz-padding-start: 4px;
-moz-padding-end: 1px;
padding-top: 2px;
@@ -181,16 +180,17 @@
.small .button-text,
.small .button-box {
padding: 0px;
border: 0px;
}
#searchFilter {
padding: 0px;
+ margin: 0px;
}
#searchFilter .textbox-input-box {
padding: 2px 2px 3px 4px;
}
%ifdef PLACES_QUERY_BUILDER
/* Calendar */
--- a/build/Makefile.in
+++ b/build/Makefile.in
@@ -101,16 +101,17 @@ AUTOMATION_PPARGS += -DIS_CYGWIN=1
endif
_LEAKTEST_DIR = $(DEPTH)/_leaktest
_LEAKTEST_FILES = \
automation.py \
leaktest.py \
bloatcycle.html \
+ $(topsrcdir)/build/pgo/server-locations.txt \
$(NULL)
automation.py: $(topsrcdir)/build/pgo/automation.py.in
$(PYTHON) $(topsrcdir)/config/Preprocessor.py \
$(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $^ > $@
leaktest.py: leaktest.py.in
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $^ > $@
--- a/build/pgo/Makefile.in
+++ b/build/pgo/Makefile.in
@@ -48,16 +48,17 @@ include $(topsrcdir)/config/rules.mk
# Stuff to make a build with a profile
_PROFILE_DIR = $(DEPTH)/_profile/pgo
_PGO_FILES = \
automation.py \
profileserver.py \
index.html \
quit.js \
+ server-locations.txt \
$(NULL)
ifeq ($(USE_SHORT_LIBNAME), 1)
PROGRAM = $(MOZ_APP_NAME)$(BIN_SUFFIX)
else
PROGRAM = $(MOZ_APP_NAME)-bin$(BIN_SUFFIX)
endif
--- a/build/pgo/automation.py.in
+++ b/build/pgo/automation.py.in
@@ -32,90 +32,43 @@
# 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 *****
+import codecs
from datetime import datetime
import itertools
import logging
import shutil
import os
+import re
import signal
import sys
import threading
"""
Runs the browser from a script, and provides useful utilities
for setting up the browser environment.
"""
__all__ = [
"UNIXISH",
"IS_WIN32",
+ "IS_MAC",
"runApp",
"Process",
"initializeProfile",
"DIST_BIN",
"DEFAULT_APP",
]
-# Since some tests require cross-domain support in Mochitest, across ports,
-# domains, subdomains, etc. we use a proxy autoconfig hack to map a bunch of
-# servers onto localhost:8888. We have to grant them the same privileges as
-# localhost:8888 here, since the browser only knows them as the URLs they're
-# pretending to be. We also have two servers which are set up but don't have
-# privileges, for testing privilege functionality.
-#
-# These lists must be kept in sync with the following list:
-#
-# http://developer.mozilla.org/en/docs/Mochitest#How_do_I_test_issues_which_only_show_up_when_tests_are_run_across_domains.3F
-#
-servers = [
- "localhost:8888", # MUST be first -- see PAC pref-setting code
- "example.org:80",
- "test1.example.org:80",
- "test2.example.org:80",
- "sub1.test1.example.org:80",
- "sub1.test2.example.org:80",
- "sub2.test1.example.org:80",
- "sub2.test2.example.org:80",
- "example.org:8000",
- "test1.example.org:8000",
- "test2.example.org:8000",
- "sub1.test1.example.org:8000",
- "sub1.test2.example.org:8000",
- "sub2.test1.example.org:8000",
- "sub2.test2.example.org:8000",
- "example.com:80",
- "test1.example.com:80",
- "test2.example.com:80",
- "sub1.test1.example.com:80",
- "sub1.test2.example.com:80",
- "sub2.test1.example.com:80",
- "sub2.test2.example.com:80",
- "sectest1.example.org:80",
- "sub.sectest2.example.org:80",
- "sub1.xn--lt-uia.example.org:8000", # U+00E4 U+006C U+0074
- "sub2.xn--lt-uia.example.org:80", # U+00E4 U+006C U+0074
- "xn--exmple-cua.test:80",
- "sub1.xn--exmple-cua.test:80",
- "xn--hxajbheg2az3al.xn--jxalpdlp:80", # Greek IDN for example.test
- "sub1.xn--hxajbheg2az3al.xn--jxalpdlp:80",
- ]
-
-unprivilegedServers = [
- "sectest2.example.org:80",
- "sub.sectest1.example.org:80",
- ]
-
-
# These are generated in mozilla/build/Makefile.in
#expand DIST_BIN = "./" + __XPC_BIN_PATH__
#expand IS_WIN32 = len("__WIN32__") != 0
#expand IS_MAC = __IS_MAC__ != 0
#ifdef IS_CYGWIN
#expand IS_CYGWIN = __IS_CYGWIN__ == 1
#else
IS_CYGWIN = False
@@ -217,16 +170,98 @@ class Process:
except:
pass
#################
# PROFILE SETUP #
#################
+class SyntaxError(Exception):
+ "Signifies a syntax error on a particular line in server-locations.txt."
+
+ def __init__(self, lineno, msg = None):
+ self.lineno = lineno
+ self.msg = msg
+
+ def __str__(self):
+ s = "Syntax error on line " + str(self.lineno)
+ if self.msg:
+ s += ": %s." % self.msg
+ else:
+ s += "."
+ return s
+
+
+class Location:
+ "Represents a location line in server-locations.txt."
+
+ def __init__(self, scheme, host, port, options):
+ self.scheme = scheme
+ self.host = host
+ self.port = port
+ self.options = options
+
+
+def readLocations():
+ """
+ Reads the locations at which the Mochitest HTTP server is available from
+ server-locations.txt.
+ """
+
+ locationFile = codecs.open("server-locations.txt", "r", "UTF-8")
+
+ # Perhaps more detail than necessary, but it's the easiest way to make sure
+ # we get exactly the format we want. See server-locations.txt for the exact
+ # format guaranteed here.
+ lineRe = re.compile(r"^(?P<scheme>[a-z][-a-z0-9+.]*)"
+ r"://"
+ r"(?P<host>"
+ r"\d+\.\d+\.\d+\.\d+"
+ r"|"
+ r"(?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)*"
+ r"[a-z](?:[-a-z0-9]*[a-z0-9])?"
+ r")"
+ r":"
+ r"(?P<port>\d+)"
+ r"(?:"
+ r"\s+"
+ r"(?P<options>\w+(?:,\w+)*)"
+ r")?$")
+ locations = []
+ lineno = 0
+ seenPrimary = False
+ for line in locationFile:
+ lineno += 1
+ if line.startswith("#") or line == "\n":
+ continue
+
+ match = lineRe.match(line)
+ if not match:
+ raise SyntaxError(lineno)
+
+ options = match.group("options")
+ if options:
+ options = options.split(",")
+ if "primary" in options:
+ if seenPrimary:
+ raise SyntaxError(lineno, "multiple primary locations")
+ seenPrimary = True
+ else:
+ options = []
+
+ locations.append(Location(match.group("scheme"), match.group("host"),
+ match.group("port"), options))
+
+ if not seenPrimary:
+ raise SyntaxError(lineno + 1, "missing primary location")
+
+ return locations
+
+
def initializeProfile(profileDir):
"Sets up the standard testing profile."
# Start with a clean slate.
shutil.rmtree(profileDir, True)
os.mkdir(profileDir)
prefs = []
@@ -244,52 +279,59 @@ user_pref("accessibility.typeaheadfind.a
user_pref("javascript.options.showInConsole", true);
user_pref("layout.debug.enable_data_xbl", true);
user_pref("browser.EULA.override", true);
user_pref("camino.warn_when_closing", false); // Camino-only, harmless to others
"""
prefs.append(part)
- # Grant God-power to all the servers on which tests can run.
- for (i, server) in itertools.izip(itertools.count(1), servers):
+ locations = readLocations()
+
+ # Grant God-power to all the privileged servers on which tests run.
+ privileged = filter(lambda loc: "privileged" in loc.options, locations)
+ for (i, l) in itertools.izip(itertools.count(1), privileged):
part = """
user_pref("capability.principal.codebase.p%(i)d.granted",
"UniversalXPConnect UniversalBrowserRead UniversalBrowserWrite \
UniversalPreferencesRead UniversalPreferencesWrite \
UniversalFileRead");
-user_pref("capability.principal.codebase.p%(i)d.id", "http://%(server)s");
+user_pref("capability.principal.codebase.p%(i)d.id", "%(origin)s");
user_pref("capability.principal.codebase.p%(i)d.subjectName", "");
-""" % {"i": i, "server": server}
+""" % { "i": i,
+ "origin": (l.scheme + "://" + l.host + ":" + l.port) }
prefs.append(part)
- # Now add the two servers that do NOT have God-power so we can properly test
- # the granting and receiving of God-power. Strip off the first server because
- # we proxy all the others to it.
- allServers = servers[1:] + unprivilegedServers
-
-
- # Now actually create the preference to make the proxying happen.
- quotedServers = ", ".join(map(lambda x: "'" + x + "'", allServers))
+ # We need to proxy every server but the primary one.
+ origins = ["'%s://%s:%s'" % (l.scheme, l.host, l.port)
+ for l in filter(lambda l: "primary" not in l.options, locations)]
+ origins = ", ".join(origins)
pacURL = """data:text/plain,
function FindProxyForURL(url, host)
{
- var servers = [%(quotedServers)s];
- var regex = new RegExp('http://(?:[^/@]*@)?(.*?(:\\\\\\\\d+)?)/');
+ var origins = [%(origins)s];
+ var regex = new RegExp('^([a-z][-a-z0-9+.]*)' +
+ '://' +
+ '(?:[^/@]*@)?' +
+ '(.*?)' +
+ '(?::(\\\\\\\\d+))?/');
var matches = regex.exec(url);
if (!matches)
return 'DIRECT';
- var hostport = matches[1], port = matches[2];
- if (!port)
- hostport += ':80';
- if (servers.indexOf(hostport) >= 0)
+ var isHttp = matches[1] == 'http';
+ if (!matches[3])
+ matches[3] = isHttp ? '80' : '443';
+ var origin = matches[1] + '://' + matches[2] + ':' + matches[3];
+ if (origins.indexOf(origin) < 0)
+ return 'DIRECT';
+ if (isHttp)
return 'PROXY localhost:8888';
return 'DIRECT';
-}""" % {"quotedServers": quotedServers}
+}""" % { "origins": origins }
pacURL = "".join(pacURL.splitlines())
part = """
user_pref("network.proxy.type", 2);
user_pref("network.proxy.autoconfig_url", "%(pacURL)s");
user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless to others
""" % {"pacURL": pacURL}
new file mode 100644
--- /dev/null
+++ b/build/pgo/server-locations.txt
@@ -0,0 +1,116 @@
+#
+# ***** 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
+# Jeff Walden <jwalden+code@mit.edu>.
+# Portions created by the Initial Developer are Copyright (C) 2008
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# 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 *****
+
+#
+# This file defines the locations at which this HTTP server may be accessed.
+# It is referred to by the following page, so if this file moves, that page must
+# be modified accordingly:
+#
+# http://developer.mozilla.org/en/docs/Mochitest#How_do_I_test_issues_which_only_show_up_when_tests_are_run_across_domains.3F
+#
+# Empty lines and lines which begin with "#" are ignored and may be used for
+# storing comments. All other lines consist of an origin followed by whitespace
+# and a comma-separated list of options (if indeed any options are needed).
+#
+# The format of an origin is, referring to RFC 2396, a scheme (either "http" or
+# "https"), followed by "://", followed by a host, followed by ":", followed by
+# a port number. The colon and port number must be present even if the port
+# number is the default for the protocol.
+#
+# Unrecognized options are ignored. Recognized options are "primary" and
+# "privileged". "primary" denotes a location which is the canonical location of
+# the server; this location is the one assumed for requests which don't
+# otherwise identify a particular origin (e.g. HTTP/1.0 requests). "privileged"
+# denotes a location which should have the ability to request elevated
+# privileges; the default is no privileges.
+#
+
+#
+# This is the primary location from which tests run.
+#
+http://localhost:8888 primary,privileged
+
+#
+# These are a common set of prefixes scattered across one TLD with two ports and
+# another TLD on a single port.
+#
+http://example.org:80 privileged
+http://test1.example.org:80 privileged
+http://test2.example.org:80 privileged
+http://sub1.test1.example.org:80 privileged
+http://sub1.test2.example.org:80 privileged
+http://sub2.test1.example.org:80 privileged
+http://sub2.test2.example.org:80 privileged
+http://example.org:8000 privileged
+http://test1.example.org:8000 privileged
+http://test2.example.org:8000 privileged
+http://sub1.test1.example.org:8000 privileged
+http://sub1.test2.example.org:8000 privileged
+http://sub2.test1.example.org:8000 privileged
+http://sub2.test2.example.org:8000 privileged
+http://example.com:80 privileged
+http://test1.example.com:80 privileged
+http://test2.example.com:80 privileged
+http://sub1.test1.example.com:80 privileged
+http://sub1.test2.example.com:80 privileged
+http://sub2.test1.example.com:80 privileged
+http://sub2.test2.example.com:80 privileged
+
+#
+# These are subdomains of <ält.example.org>.
+#
+http://sub1.xn--lt-uia.example.org:8000 privileged
+http://sub2.xn--lt-uia.example.org:80 privileged
+http://xn--exmple-cua.test:80 privileged
+http://sub1.xn--exmple-cua.test:80 privileged
+
+#
+# These are subdomains of <παράδειγμα.δοκιμή>, the Greek IDN for example.test.
+#
+http://xn--hxajbheg2az3al.xn--jxalpdlp:80 privileged
+http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp:80 privileged
+
+#
+# These hosts are used in tests which exercise privilege-granting functionality;
+# we could reuse some of the names above, but specific names make it easier to
+# distinguish one from the other in tests (as well as what functionality is
+# being tested).
+#
+http://sectest1.example.org:80 privileged
+http://sub.sectest2.example.org:80 privileged
+http://sectest2.example.org:80
+http://sub.sectest1.example.org:80
--- a/build/unix/run-mozilla.sh
+++ b/build/unix/run-mozilla.sh
@@ -108,17 +108,17 @@ moz_get_debugger()
debuggers="ddd gdb dbx bdb"
debugger="notfound"
done="no"
for d in $debuggers
do
moz_test_binary /bin/type
if [ $? -eq 1 ]
then
- dpath=`type ${d} | awk '{print $3;}' | sed -e 's/\.$//'`
+ dpath=`LC_MESSAGES=C type ${d} | awk '{print $3;}' | sed -e 's/\.$//'`
else
dpath=`which ${d}`
fi
if [ -x "$dpath" ]
then
debugger=$dpath
break
fi
@@ -139,17 +139,17 @@ moz_run_program()
fi
##
## Use md5sum to crc a core file. If md5sum is not found on the system,
## then don't debug core files.
##
moz_test_binary /bin/type
if [ $? -eq 1 ]
then
- crc_prog=`type md5sum 2>/dev/null | awk '{print $3;}' 2>/dev/null | sed -e 's/\.$//'`
+ crc_prog=`LC_MESSAGES=C type md5sum 2>/dev/null | awk '{print $3;}' 2>/dev/null | sed -e 's/\.$//'`
else
crc_prog=`which md5sum 2>/dev/null`
fi
if [ -x "$crc_prog" ]
then
DEBUG_CORE_FILES=1
fi
if [ "$DEBUG_CORE_FILES" ]
@@ -207,17 +207,17 @@ moz_debug_program()
then
moz_bail "Cannot execute $prog."
fi
if [ -n "$moz_debugger" ]
then
moz_test_binary /bin/type
if [ $? -eq 1 ]
then
- debugger=`type $moz_debugger | awk '{print $3;}' | sed -e 's/\.$//'`
+ debugger=`LC_MESSAGES=C type $moz_debugger | awk '{print $3;}' | sed -e 's/\.$//'`
else
debugger=`which $moz_debugger`
fi
else
debugger=`moz_get_debugger`
fi
if [ -x "$debugger" ]
then
--- a/client.mk
+++ b/client.mk
@@ -38,21 +38,20 @@
#
# ***** END LICENSE BLOCK *****
# Build a mozilla application.
#
# To build a tree,
# 1. hg clone ssh://hg.mozilla.org/mozilla-central mozilla
# 2. cd mozilla
-# 3. python client.py checkout
-# 4. create your .mozconfig file with
+# 3. create your .mozconfig file with
# mk_add_options MOZ_CO_PROJECT=
# suite,browser
-# 5. gmake -f client.mk
+# 4. gmake -f client.mk
#
# Other targets (gmake -f client.mk [targets...]),
# build
# clean (realclean is now the same as clean)
# distclean
#
# See http://developer.mozilla.org/en/docs/Build_Documentation for
# more information.
@@ -284,16 +283,17 @@ EXTRA_CONFIG_DEPS := \
$(TOPSRCDIR)/nsprpub/configure: $(TOPSRCDIR)/nsprpub/configure.in $(EXTRA_CONFIG_DEPS)
@echo Generating $@ using autoconf
cd $(TOPSRCDIR)/nsprpub; $(AUTOCONF)
endif
CONFIG_STATUS_DEPS := \
$(TOPSRCDIR)/configure \
+ $(TOPSRCDIR)/allmakefiles.sh \
$(TOPSRCDIR)/.mozconfig.mk \
$(wildcard $(TOPSRCDIR)/nsprpub/configure) \
$(wildcard $(TOPSRCDIR)/directory/c-sdk/configure) \
$(wildcard $(TOPSRCDIR)/config/milestone.txt) \
$(wildcard $(TOPSRCDIR)/config/chrome-versions.sh) \
$(wildcard $(addsuffix confvars.sh,$(wildcard $(TOPSRCDIR)/*/))) \
$(NULL)
--- a/client.py
+++ b/client.py
@@ -1,24 +1,20 @@
#!/usr/bin/python
-NSPR_CO_TAG = 'NSPR_4_7_1_RTM'
-NSS_CO_TAG = 'NSS_3_12_RC3'
-
NSPR_DIRS = ('nsprpub',)
NSS_DIRS = ('dbm',
'security/nss',
'security/coreconf',
'security/dbm')
-# URL of the default hg repository to clone for Tamarin.
-DEFAULT_TAMARIN_REPO = 'http://hg.mozilla.org/tamarin-central/'
-
import os
import sys
+import datetime
+import shutil
from optparse import OptionParser
topsrcdir = os.path.dirname(__file__)
if topsrcdir == '':
topsrcdir = '.'
try:
from subprocess import check_call
@@ -42,97 +38,59 @@ def do_hg_pull(dir, repository, hg):
if not os.path.exists(fulldir):
fulldir = os.path.join(topsrcdir, dir)
check_call_noisy([hg, 'clone', repository, fulldir])
else:
cmd = [hg, 'pull', '-u', '-R', fulldir]
if repository is not None:
cmd.append(repository)
check_call_noisy(cmd)
+ check_call([hg, 'parent', '-R', fulldir,
+ '--template=Updated to revision {node}.\n'])
-def do_cvs_checkout(modules, tag, cvsroot, cvs):
- """Check out a CVS directory.
+def do_cvs_export(modules, tag, cvsroot, cvs):
+ """Check out a CVS directory without CVS metadata, using "export"
modules is a list of directories to check out, e.g. ['nsprpub']
"""
for module in modules:
+ fullpath = os.path.join(topsrcdir, module)
+ if os.path.exists(fullpath):
+ print "Removing '%s'" % fullpath
+ shutil.rmtree(fullpath)
+
(parent, leaf) = os.path.split(module)
+ print "CVS export begin: " + datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC")
check_call_noisy([cvs, '-d', cvsroot,
- 'checkout', '-P', '-r', tag, '-d', leaf,
+ 'export', '-r', tag, '-d', leaf,
'mozilla/%s' % module],
cwd=os.path.join(topsrcdir, parent))
+ print "CVS export end: " + datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC")
-o = OptionParser(usage="client.py [options] checkout")
-o.add_option("-m", "--mozilla-repo", dest="mozilla_repo",
- default=None,
- help="URL of Mozilla repository to pull from (default: use hg default in .hg/hgrc)")
+o = OptionParser(usage="client.py [options] update_nspr tagname | update_nss tagname")
o.add_option("--skip-mozilla", dest="skip_mozilla",
action="store_true", default=False,
- help="Skip pulling the Mozilla repository.")
-
-o.add_option("-t", "--tamarin-repo", dest="tamarin_repo",
- default=None,
- help="URL of Tamarin repository to pull from (default: use hg default in js/tamarin/.hg/hgrc; or if that file doesn't exist, use \"" + DEFAULT_TAMARIN_REPO + "\".)")
-o.add_option("--skip-tamarin", dest="skip_tamarin",
- action="store_true", default=False,
- help="Skip pulling the Tamarin repository.")
+ help="Obsolete")
-o.add_option("--skip-nspr", dest="skip_nspr",
- action="store_true", default=False,
- help="Skip pulling the NSPR repository.")
-o.add_option("--skip-nss", dest="skip_nss",
- action="store_true", default=False,
- help="Skip pulling the NSS repository.")
-
-o.add_option("--hg", dest="hg", default=os.environ.get('HG', 'hg'),
- help="The location of the hg binary")
o.add_option("--cvs", dest="cvs", default=os.environ.get('CVS', 'cvs'),
help="The location of the cvs binary")
o.add_option("--cvsroot", dest="cvsroot",
default=os.environ.get('CVSROOT', ':pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot'),
help="The CVSROOT (default: :pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot")
-
-def fixup_repo_options(options):
- """ Check options.mozilla_repo and options.tamarin_repo values;
- populate tamarin_repo if needed.
-
- options.mozilla_repo and options.tamarin_repo are normally None.
- This is fine-- our "hg pull" commands will omit the repo URL.
- The exception is the initial checkout, which does an "hg clone"
- for Tamarin. That command requires a repository URL.
- """
-
- if (options.mozilla_repo is None
- and not os.path.exists(os.path.join(topsrcdir, '.hg'))):
- o.print_help()
- print
- print "*** The -m option is required for the initial checkout."
- sys.exit(2)
-
- # Handle special case: initial checkout of Tamarin.
- if (options.tamarin_repo is None
- and not os.path.exists(os.path.join(topsrcdir, 'js', 'tamarin'))):
- options.tamarin_repo = DEFAULT_TAMARIN_REPO
-
try:
- (options, (action,)) = o.parse_args()
-except ValueError:
+ options, args = o.parse_args()
+ action = args[0]
+except IndexError:
o.print_help()
sys.exit(2)
-fixup_repo_options(options)
-
if action in ('checkout', 'co'):
- if not options.skip_nspr:
- do_cvs_checkout(NSPR_DIRS, NSPR_CO_TAG, options.cvsroot, options.cvs)
-
- if not options.skip_nss:
- do_cvs_checkout(NSS_DIRS, NSS_CO_TAG, options.cvsroot, options.cvs)
-
- if not options.skip_tamarin:
- do_hg_pull('js/tamarin', options.tamarin_repo, options.hg)
-
- if not options.skip_mozilla:
- do_hg_pull('.', options.mozilla_repo, options.hg)
-
+ print >>sys.stderr, "Warning: client.py checkout is obsolete."
+ pass
+elif action in ('update_nspr'):
+ tag, = args[1:]
+ do_cvs_export(NSPR_DIRS, tag, options.cvsroot, options.cvs)
+elif action in ('update_nss'):
+ tag, = args[1:]
+ do_cvs_export(NSS_DIRS, tag, options.cvsroot, options.cvs)
else:
o.print_help()
sys.exit(2)
--- a/config/Makefile.in
+++ b/config/Makefile.in
@@ -150,13 +150,23 @@ FORCE:
ifdef MKDEPEND_DIR
clean clobber realclean clobber_all::
cd $(MKDEPEND_DIR); $(MAKE) $@
endif
PYUNITS := unit-Expression.py unit-Preprocessor.py
-check::
+check:: check-preprocessor check-jar-mn
+
+check-preprocessor::
@$(EXIT_ON_ERROR) \
for test in $(PYUNITS); do \
$(PYTHON) $(srcdir)/tests/$$test ; \
done
+
+check-jar-mn::
+ make -C tests/src-simple check-jar
+ make -C tests/src-simple check-flat
+ make -C tests/src-simple check-flat USE_EXTENSION_MANIFEST=1
+ifneq ($(OS_ARCH), WINNT)
+ make -C tests/src-simple check-symlink
+endif
--- a/config/Preprocessor.py
+++ b/config/Preprocessor.py
@@ -142,17 +142,19 @@ class Preprocessor:
ln = self.context['LINE']
if self.writtenLines != ln:
self.out.write('//@line %(line)d "%(file)s"%(le)s'%{'line': ln,
'file': self.context['FILE'],
'le': self.LE})
self.writtenLines = ln
for f in self.filters:
aLine = f[1](aLine)
- aLine = aLine.rstrip('\r\n') + self.LE
+ # ensure our line ending. Only need to handle \n, as we're reading
+ # with universal line ending support, at least for files.
+ aLine = re.sub('\n', self.LE, aLine)
self.out.write(aLine)
def handleCommandLine(self, args, defaultToStdin = False):
"""
Parse a commandline into this parser.
Uses OptionParser internally, no args mean sys.argv[1:].
"""
includes = []
@@ -342,17 +344,17 @@ class Preprocessor:
if v in self.context:
return str(self.context[v])
return ''
for i in range(1, len(lst), 2):
lst[i] = vsubst(lst[i])
lst.append('\n') # add back the newline
self.write(reduce(lambda x, y: x+y, lst, ''))
def do_literal(self, args):
- self.write(args)
+ self.write(args + self.LE)
def do_filter(self, args):
filters = [f for f in args.split(' ') if hasattr(self, 'filter_' + f)]
if len(filters) == 0:
return
current = dict(self.filters)
for f in filters:
current[f] = getattr(self, 'filter_' + f)
filterNames = current.keys()
@@ -412,17 +414,17 @@ class Preprocessor:
oldWrittenLines = self.writtenLines
oldCheckLineNumbers = self.checkLineNumbers
self.checkLineNumbers = False
if isName:
try:
args = str(args)
if not os.path.isabs(args):
args = os.path.join(self.context['DIRECTORY'], args)
- args = open(args)
+ args = open(args, 'rU')
except:
raise Preprocessor.Error(self, 'FILE_NOT_FOUND', str(args))
self.checkLineNumbers = bool(re.search('\.js(?:\.in)?$', args.name))
oldFile = self.context['FILE']
oldLine = self.context['LINE']
oldDir = self.context['DIRECTORY']
if args.isatty():
# we're stdin, use '-' and '' for file and dir
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -615,18 +615,16 @@ MOZ_THUNDERBIRD = @MOZ_THUNDERBIRD@
MOZ_STANDALONE_COMPOSER= @MOZ_STANDALONE_COMPOSER@
MOZ_STATIC_MAIL_BUILD = @MOZ_STATIC_MAIL_BUILD@
MOZ_SUNBIRD = @MOZ_SUNBIRD@
MOZ_SUITE = @MOZ_SUITE@
WINCE = @WINCE@
MOZ_DISTRIBUTION_ID = @MOZ_DISTRIBUTION_ID@
-MINIMO = @MINIMO@
-
MACOS_SDK_DIR = @MACOS_SDK_DIR@
NEXT_ROOT = @NEXT_ROOT@
GCC_VERSION = @GCC_VERSION@
XCODEBUILD_VERSION= @XCODEBUILD_VERSION@
HAS_XCODE_2_1 = @HAS_XCODE_2_1@
UNIVERSAL_BINARY= @UNIVERSAL_BINARY@
HAVE_DTRACE= @HAVE_DTRACE@
--- a/config/config.mk
+++ b/config/config.mk
@@ -57,16 +57,18 @@ ifndef INCLUDED_AUTOCONF_MK
include $(DEPTH)/config/autoconf.mk
endif
ifndef INCLUDED_INSURE_MK
ifdef MOZ_INSURIFYING
include $(topsrcdir)/config/insure.mk
endif
endif
+COMMA = ,
+
# Sanity check some variables
CHECK_VARS := \
XPI_NAME \
LIBRARY_NAME \
MODULE \
DEPTH \
SHORT_LIBNAME \
XPI_PKGNAME \
@@ -405,38 +407,16 @@ DEFINES += \
$(NULL)
ifndef MOZ_NATIVE_ZLIB
DEFINES += -DZLIB_INTERNAL
endif
endif
endif
-ifdef MINIMO
-ifdef LIBXUL_LIBRARY
-DEFINES += \
- -D_IMPL_NS_COM \
- -DEXPORT_XPT_API \
- -DEXPORT_XPTC_API \
- -DEXPORT_XPTI_API \
- -D_IMPL_NS_COM_OBSOLETE \
- -D_IMPL_NS_GFX \
- -D_IMPL_NS_WIDGET \
- -DIMPL_XREAPI \
- -DIMPL_NS_NET \
- -DIMPL_THEBES \
- $(NULL)
-endif
-
-ifdef WINCE
-DEFINES += -D_NSPR_BUILD_
-endif
-
-endif
-
# Force _all_ exported methods to be |_declspec(dllexport)| when we're
# building them into the executable.
ifeq (,$(filter-out WINNT WINCE OS2, $(OS_ARCH)))
ifdef BUILD_STATIC_LIBS
DEFINES += \
-D_IMPL_NS_GFX \
-D_IMPL_NS_MSG_BASE \
@@ -517,20 +497,36 @@ INCLUDES = $(LOCAL_INCLUDES) $(REQ_INCLU
ifndef MOZILLA_INTERNAL_API
INCLUDES += -I$(LIBXUL_DIST)/sdk/include
endif
# The entire tree should be subject to static analysis using the XPCOM
# script. Additional scripts may be added by specific subdirectories.
-DEHYDRA_SCRIPTS = $(topsrcdir)/xpcom/analysis/static-checking.js
+DEHYDRA_SCRIPT = $(topsrcdir)/xpcom/analysis/static-checking.js
+
+DEHYDRA_MODULES = \
+ $(topsrcdir)/xpcom/analysis/stack.js \
+ $(NULL)
+
+TREEHYDRA_MODULES = \
+ $(topsrcdir)/xpcom/analysis/outparams.js \
+ $(NULL)
+
+DEHYDRA_ARGS = \
+ --topsrcdir=$(topsrcdir) \
+ --objdir=$(DEPTH) \
+ --dehydra-modules=$(subst $(NULL) ,$(COMMA),$(strip $(DEHYDRA_MODULES))) \
+ --treehydra-modules=$(subst $(NULL) ,$(COMMA),$(strip $(TREEHYDRA_MODULES))) \
+ $(NULL)
+
+DEHYDRA_FLAGS = -fplugin=$(DEHYDRA_PATH) -fplugin-arg="$(DEHYDRA_SCRIPT) $(DEHYDRA_ARGS)"
ifdef DEHYDRA_PATH
-DEHYDRA_FLAGS = -fplugin=$(DEHYDRA_PATH) $(foreach script,$(DEHYDRA_SCRIPTS),-fplugin-arg=$(script))
OS_CXXFLAGS += $(DEHYDRA_FLAGS)
endif
CFLAGS = $(OS_CFLAGS)
CXXFLAGS = $(OS_CXXFLAGS)
LDFLAGS = $(OS_LDFLAGS) $(MOZ_FIX_LINK_PATHS)
# Allow each module to override the *default* optimization settings
@@ -775,16 +771,17 @@ ifdef IS_COMPONENT
LDFLAGS += -IMPLIB:fake.lib
DELETE_AFTER_LINK = fake.lib fake.exp
endif
endif
#
# Include any personal overrides the user might think are needed.
#
+-include $(topsrcdir)/$(MOZ_BUILD_APP)/app-config.mk
-include $(MY_CONFIG)
######################################################################
# Now test variables that might have been set or overridden by $(MY_CONFIG).
DEFINES += -DOSTYPE=\"$(OS_CONFIG)\"
DEFINES += -DOSARCH=$(OS_ARCH)
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -1221,16 +1221,22 @@ endef
MAKE_DEPS_AUTO_CC = $(MAKE_DEPS_AUTO)
MAKE_DEPS_AUTO_CXX = $(MAKE_DEPS_AUTO)
endif # COMPILER_DEPEND
endif # MOZ_AUTO_DEPS
+ifdef MOZ_MEMORY
+ifeq ($(OS_ARCH),SunOS)
+SOLARIS_JEMALLOC_LDFLAGS = $(call EXPAND_LIBNAME_PATH,jemalloc,$(DIST)/lib)
+endif
+endif
+
# Rules for building native targets must come first because of the host_ prefix
host_%.$(OBJ_SUFFIX): %.c Makefile Makefile.in
$(REPORT_BUILD)
$(ELOG) $(HOST_CC) $(HOST_OUTOPTION)$@ -c $(HOST_CFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS)
host_%.$(OBJ_SUFFIX): %.cpp Makefile Makefile.in
$(REPORT_BUILD)
$(ELOG) $(HOST_CXX) $(HOST_OUTOPTION)$@ -c $(HOST_CXXFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS)
@@ -2040,16 +2046,17 @@ else
include $(MDDEPEND_FILES)
endif
endif
endif
endif
#############################################################################
+-include $(topsrcdir)/$(MOZ_BUILD_APP)/app-rules.mk
-include $(MY_RULES)
#
# This speeds up gmake's processing if these files don't exist.
#
$(MY_CONFIG) $(MY_RULES):
@touch $@
new file mode 100644
--- /dev/null
+++ b/config/tests/chrome.manifest.flat
@@ -0,0 +1,4 @@
+content test chrome/test/one xpcnativewrappers=no
+locale ab-X-stuff chrome/test/three
+overlay chrome://one/file.xml chrome://two/otherfile.xml
+skin test classic chrome/test/one
new file mode 100644
--- /dev/null
+++ b/config/tests/ref-simple/one/file.xml
@@ -0,0 +1,1 @@
+<?xml version="1.0"><doc/>
new file mode 100644
--- /dev/null
+++ b/config/tests/ref-simple/one/preproc
@@ -0,0 +1,2 @@
+
+This is ab-X-stuff.
new file mode 100644
--- /dev/null
+++ b/config/tests/ref-simple/one/some.css
@@ -0,0 +1,6 @@
+#div: {
+/* this is a ID rule, and should stay intact */
+}
+[lang=ab-X-stuff] {
+/* this selector should match content with lang="ab-X-stuff" */
+}
new file mode 100644
--- /dev/null
+++ b/config/tests/ref-simple/three/l10nfile.txt
@@ -0,0 +1,1 @@
+localized content
new file mode 100644
--- /dev/null
+++ b/config/tests/ref-simple/two/otherfile.xml
@@ -0,0 +1,1 @@
+<?xml version="1.0"><otherdoc/>
new file mode 100644
--- /dev/null
+++ b/config/tests/src-simple/Makefile.in
@@ -0,0 +1,73 @@
+#
+# ***** 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) 2008
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Axel Hecht <axel@pike.org>
+#
+# 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 *****
+
+DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+LOCALE_SRCDIR = $(srcdir)/l10n
+
+include $(DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+XPI_NAME = test_jar_mn
+
+DEFINES += \
+ -DAB_CD=ab-X-stuff \
+ $(NULL)
+
+MY_MANIFEST = $(if $(USE_EXTENSION_MANIFEST), $(FINAL_TARGET)/chrome.manifest, $(FINAL_TARGET)/chrome/test.manifest)
+REF_MANIFEST = $(if $(USE_EXTENSION_MANIFEST),chrome.manifest,test.manifest)
+
+check-%::
+ if test -d $(FINAL_TARGET); then rm -rf $(FINAL_TARGET); fi;
+ make realchrome MOZ_CHROME_FILE_FORMAT=$*
+ @echo "Comparing manifests..."
+ @if ! sort $(MY_MANIFEST) | diff -u $(srcdir)/../$(REF_MANIFEST).$* - ; then \
+ echo "FAIL: different content in manifest!" ; \
+ fi
+ @if [ $* == "jar" ]; then \
+ $(UNZIP) -d $(FINAL_TARGET)/chrome/test $(FINAL_TARGET)/chrome/test.jar; \
+ fi
+ @echo "Comparing packages..."
+ @if ! diff -ur $(srcdir)/../ref-simple $(FINAL_TARGET)/chrome/test ; then\
+ echo "FAIL: different content in jar!" ; \
+ fi
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/config/tests/src-simple/jar.mn
@@ -0,0 +1,22 @@
+#filter substitution
+
+test.jar:
+# test chrome with flags and path expansion
+% content test %one xpcnativewrappers=no
+# test locale with variable substitution and path expansion
+% locale @AB_CD@ %three
+# test overlays
+% overlay chrome://one/file.xml chrome://two/otherfile.xml
+# test regular file, preprocessed file, preprocessed css
+ one/file.xml (thesrcdir/file.xml)
+* one/preproc (thesrcdir/preproc.in)
+* one/some.css (thesrcdir/some.css)
+# test reference against topsrcdir
+ two/otherfile.xml (/config/tests/src-simple/thetopsrcdir/otherfile.xml)
+# test reference against localesrcdir
+ three/l10nfile.txt (%l10nfile.txt)
+
+test.jar:
+# test manifest update the locale one was already added above, add skin
+% locale @AB_CD@ %three
+% skin test classic %one
new file mode 100644
--- /dev/null
+++ b/config/tests/src-simple/l10n/l10nfile.txt
@@ -0,0 +1,1 @@
+localized content
new file mode 100644
--- /dev/null
+++ b/config/tests/src-simple/thesrcdir/file.xml
@@ -0,0 +1,1 @@
+<?xml version="1.0"><doc/>
new file mode 100644
--- /dev/null
+++ b/config/tests/src-simple/thesrcdir/preproc.in
@@ -0,0 +1,6 @@
+# This would be an processed out
+# pretty lengthy
+# license header.
+# For example.
+
+#expand This is __AB_CD__.
new file mode 100644
--- /dev/null
+++ b/config/tests/src-simple/thesrcdir/some.css
@@ -0,0 +1,6 @@
+#div: {
+/* this is a ID rule, and should stay intact */
+}
+%expand [lang=__AB_CD__] {
+/* this selector should match content with lang="ab-X-stuff" */
+}
new file mode 100644
--- /dev/null
+++ b/config/tests/src-simple/thetopsrcdir/otherfile.xml
@@ -0,0 +1,1 @@
+<?xml version="1.0"><otherdoc/>
new file mode 100644
--- /dev/null
+++ b/config/tests/test.manifest.flat
@@ -0,0 +1,4 @@
+content test test/one xpcnativewrappers=no
+locale ab-X-stuff test/three
+overlay chrome://one/file.xml chrome://two/otherfile.xml
+skin test classic test/one
new file mode 100644
--- /dev/null
+++ b/config/tests/test.manifest.jar
@@ -0,0 +1,4 @@
+content test jar:test.jar!/one xpcnativewrappers=no
+locale ab-X-stuff jar:test.jar!/three
+overlay chrome://one/file.xml chrome://two/otherfile.xml
+skin test classic jar:test.jar!/one
new file mode 100644
--- /dev/null
+++ b/config/tests/test.manifest.symlink
@@ -0,0 +1,4 @@
+content test test/one xpcnativewrappers=no
+locale ab-X-stuff test/three
+overlay chrome://one/file.xml chrome://two/otherfile.xml
+skin test classic test/one
new file mode 100644
--- /dev/null
+++ b/config/tests/unit-LineEndings.py
@@ -0,0 +1,46 @@
+import unittest
+
+from StringIO import StringIO
+import os
+import sys
+import os.path
+sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
+
+from Preprocessor import Preprocessor
+
+class TestLineEndings(unittest.TestCase):
+ """
+ Unit tests for the Context class
+ """
+
+ def setUp(self):
+ self.pp = Preprocessor()
+ self.pp.out = StringIO()
+ self.tempnam = os.tempnam('.')
+
+ def tearDown(self):
+ os.remove(self.tempnam)
+
+ def createFile(self, lineendings):
+ f = open(self.tempnam, 'wb')
+ for line, ending in zip(['a', '#literal b', 'c'], lineendings):
+ f.write(line+ending)
+ f.close()
+
+ def testMac(self):
+ self.createFile(['\x0D']*3)
+ self.pp.do_include(self.tempnam)
+ self.assertEquals(self.pp.out.getvalue(), 'a\nb\nc\n')
+
+ def testUnix(self):
+ self.createFile(['\x0A']*3)
+ self.pp.do_include(self.tempnam)
+ self.assertEquals(self.pp.out.getvalue(), 'a\nb\nc\n')
+
+ def testWindows(self):
+ self.createFile(['\x0D\x0A']*3)
+ self.pp.do_include(self.tempnam)
+ self.assertEquals(self.pp.out.getvalue(), 'a\nb\nc\n')
+
+if __name__ == '__main__':
+ unittest.main()
--- a/config/tests/unit-Preprocessor.py
+++ b/config/tests/unit-Preprocessor.py
@@ -172,16 +172,40 @@ BAR
def test_filter_attemptSubstitution(self):
f = NamedIO('filter_attemptSubstitution.in', '''#filter attemptSubstitution
P@VAR@ASS
#unfilter attemptSubstitution
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
+ def test_filter_emptyLines(self):
+ f = NamedIO('filter_emptyLines.in', '''lines with a
+
+blank line
+#filter emptyLines
+lines with
+
+no blank lines
+#unfilter emptyLines
+yet more lines with
+
+blank lines
+''')
+ self.pp.do_include(f)
+ self.assertEqual(self.pp.out.getvalue(), '''lines with a
+
+blank line
+lines with
+no blank lines
+yet more lines with
+
+blank lines
+''')
+
def test_filter_slashslash(self):
f = NamedIO('filter_slashslash.in', '''#filter slashslash
PASS//FAIL // FAIL
#unfilter slashslash
PASS // PASS
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\nPASS // PASS\n")
@@ -367,10 +391,18 @@ FAIL
PASS
#else
FAIL
#endif
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
+ def test_lineEndings(self):
+ f = NamedIO('lineEndings.in', '''first
+#literal second
+''')
+ self.pp.setLineEndings('cr')
+ self.pp.do_include(f)
+ self.assertEqual(self.pp.out.getvalue(), "first\rsecond\r")
+
if __name__ == '__main__':
unittest.main()
--- a/configure.in
+++ b/configure.in
@@ -63,16 +63,21 @@ dnl ====================================
AC_PREREQ(2.13)
AC_INIT(config/config.mk)
AC_CONFIG_AUX_DIR(${srcdir}/build/autoconf)
AC_CANONICAL_SYSTEM
TARGET_CPU="${target_cpu}"
TARGET_VENDOR="${target_vendor}"
TARGET_OS="${target_os}"
+
+MOZ_DEB_TIMESTAMP=`date +"%a, %d %b %Y %T %z" 2>&1`
+AC_SUBST(MOZ_DEB_TIMESTAMP)
+
+
dnl ========================================================
dnl =
dnl = Don't change the following two lines. Doing so breaks:
dnl =
dnl = CFLAGS="-foo" ./configure
dnl =
dnl ========================================================
CFLAGS="${CFLAGS=}"
@@ -123,17 +128,17 @@ WINDRES_VERSION=2.14.90
W32API_VERSION=3.8
GNOMEVFS_VERSION=2.0
GNOMEUI_VERSION=2.2.0
GCONF_VERSION=1.2.1
LIBGNOME_VERSION=2.0
STARTUP_NOTIFICATION_VERSION=0.8
DBUS_VERSION=0.60
LCMS_VERSION=1.17
-SQLITE_VERSION=3.5.4
+SQLITE_VERSION=3.5.9
MSMANIFEST_TOOL=
dnl Set various checks
dnl ========================================================
MISSING_X=
AC_PROG_AWK
@@ -478,16 +483,17 @@ case "$target" in
elif test $_CC_BUILD -ge 762; then
_USE_DYNAMICBASE=1
fi
AC_DEFINE(_CRT_SECURE_NO_DEPRECATE)
AC_DEFINE(_CRT_NONSTDC_NO_DEPRECATE)
elif test "$_CC_MAJOR_VERSION" = "15"; then
_CC_SUITE=9
CXXFLAGS="$CXXFLAGS -Zc:wchar_t-"
+ LDFLAGS="$LDFLAGS -MANIFESTUAC:NO"
_USE_DYNAMICBASE=1
AC_DEFINE(_CRT_SECURE_NO_WARNINGS)
AC_DEFINE(_CRT_NONSTDC_NO_WARNINGS)
else
AC_MSG_ERROR([This version of the MSVC compiler, $CC_VERSION , is unsupported.])
fi
_MOZ_RTTI_FLAGS_ON='-GR'
@@ -1663,28 +1669,35 @@ case "$target" in
LDFLAGS="$LDFLAGS -framework Cocoa"
# The ExceptionHandling framework is needed for Objective-C exception
# logging code in nsObjCExceptions.h. Currently we only use that in debug
# builds.
MOZ_DEBUG_LDFLAGS="$MOZ_DEBUG_LDFLAGS -framework ExceptionHandling"
# set MACOSX to generate lib/mac/MoreFiles/Makefile
MACOSX=1
- dnl check for the presence of the -dead_strip linker flag
- AC_MSG_CHECKING([for -dead_strip option to ld])
- _SAVE_LDFLAGS=$LDFLAGS
- LDFLAGS="$LDFLAGS -Wl,-dead_strip"
- AC_TRY_LINK(,[return 0;],_HAVE_DEAD_STRIP=1,_HAVE_DEAD_STRIP=)
- if test -n "$_HAVE_DEAD_STRIP" ; then
- AC_MSG_RESULT([yes])
- MOZ_OPTIMIZE_LDFLAGS="-Wl,-dead_strip"
+ dnl DTrace and -dead_strip don't interact well. See bug 403132.
+ dnl ===================================================================
+ if test "x$enable_dtrace" = "xyes"; then
+ echo "Skipping -dead_strip because DTrace is enabled. See bug 403132."
else
- AC_MSG_RESULT([no])
- fi
- LDFLAGS=$_SAVE_LDFLAGS
+ dnl check for the presence of the -dead_strip linker flag
+ AC_MSG_CHECKING([for -dead_strip option to ld])
+ _SAVE_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS -Wl,-dead_strip"
+ AC_TRY_LINK(,[return 0;],_HAVE_DEAD_STRIP=1,_HAVE_DEAD_STRIP=)
+ if test -n "$_HAVE_DEAD_STRIP" ; then
+ AC_MSG_RESULT([yes])
+ MOZ_OPTIMIZE_LDFLAGS="-Wl,-dead_strip"
+ else
+ AC_MSG_RESULT([no])
+ fi
+
+ LDFLAGS=$_SAVE_LDFLAGS
+ fi
;;
*-freebsd*)
if test `test -x /usr/bin/objformat && /usr/bin/objformat || echo aout` != "elf"; then
DLL_SUFFIX=".so.1.0"
DSO_LDOPTS="-shared"
fi
if test ! "$GNU_CC"; then
@@ -2114,17 +2127,17 @@ case "$target" in
fi
fi
LIBIDL_LIBS="${LIBIDL_LIBS} ${GLIB_LIBS}"
;;
esac
case "$host_os" in
- cygwin*)
+ cygwin*|msvc*|mks*)
AC_MSG_WARN([Using a cygwin build environment is unsupported. Configure cannot check for the presence of necessary headers. Please upgrade to MozillaBuild; see http://developer.mozilla.org/en/docs/Windows_Build_Prerequisites])
;;
*)
AC_CHECK_HEADERS(mmintrin.h oleacc.idl)
AC_LANG_SAVE
AC_LANG_CPLUSPLUS
@@ -2768,22 +2781,22 @@ dnl Note that we assume that mac & win32
AC_LANG_SAVE
AC_LANG_CPLUSPLUS
_SAVE_CXXFLAGS=$CXXFLAGS
CXXFLAGS="$CXXFLAGS -fshort-wchar"
AC_CACHE_CHECK(for compiler -fshort-wchar option,
ac_cv_have_usable_wchar_option_v2,
- [AC_TRY_COMPILE([#include <stddef.h>
- $configure_static_assert_macros],
- [CONFIGURE_STATIC_ASSERT(sizeof(wchar_t) == 2);
- CONFIGURE_STATIC_ASSERT((wchar_t)-1 > (wchar_t) 0)],
- ac_cv_have_usable_wchar_option_v2="yes",
- ac_cv_have_usable_wchar_option_v2="no")])
+ [AC_TRY_LINK([#include <stddef.h>
+ $configure_static_assert_macros],
+ [CONFIGURE_STATIC_ASSERT(sizeof(wchar_t) == 2);
+ CONFIGURE_STATIC_ASSERT((wchar_t)-1 > (wchar_t) 0)],
+ ac_cv_have_usable_wchar_option_v2="yes",
+ ac_cv_have_usable_wchar_option_v2="no")])
if test "$ac_cv_have_usable_wchar_option_v2" = "yes"; then
AC_DEFINE(HAVE_CPP_2BYTE_WCHAR_T)
HAVE_CPP_2BYTE_WCHAR_T=1
else
CXXFLAGS=$_SAVE_CXXFLAGS
fi
AC_LANG_RESTORE
@@ -4250,16 +4263,28 @@ MOZ_ARG_WITH_STRING(java-include-path,
JAVA_INCLUDE_PATH=$withval)
JAVA_BIN_PATH=
MOZ_ARG_WITH_STRING(java-bin-path,
[ --with-java-bin-path=dir Location of Java binaries (java, javac, jar)],
JAVA_BIN_PATH=$withval)
dnl ========================================================
+dnl Use ARM userspace kernel helpers; tell NSPR to enable
+dnl their usage and use them in spidermonkey.
+dnl ========================================================
+MOZ_ARG_WITH_BOOL(arm-kuser,
+[ --with-arm-kuser Use kuser helpers (Linux/ARM only -- requires kernel 2.6.13 or later)],
+ USE_ARM_KUSER=1,
+ USE_ARM_KUSER=)
+if test -n "$USE_ARM_KUSER"; then
+ AC_DEFINE(USE_ARM_KUSER)
+fi
+
+dnl ========================================================
dnl =
dnl = Application
dnl =
dnl ========================================================
MOZ_ARG_HEADER(Application)
BUILD_STATIC_LIBS=
@@ -4360,43 +4385,33 @@ case "$target_os" in
esac
MOZ_ARG_ENABLE_STRING(application,
[ --enable-application=APP
Options include:
suite
browser (Firefox)
mail (Thunderbird)
- minimo
composer
calendar (Sunbird)
xulrunner
camino
content/xslt (Standalone Transformiix XSLT)
netwerk (Standalone Necko)
tools/update-packaging (AUS-related packaging tools)
standalone (use this for standalone
xpcom/xpconnect or to manually drive a build)],
[ MOZ_BUILD_APP=$enableval ] )
if test "$MOZ_BUILD_APP" = "macbrowser"; then
AC_MSG_WARN([--enable-application=macbrowser is deprecated. Use --enable-application=camino.])
MOZ_BUILD_APP=camino
fi
-case "$MOZ_BUILD_APP" in
-minimo)
- MOZ_EMBEDDING_PROFILE=basic
- ;;
-
-*)
- MOZ_EMBEDDING_PROFILE=default
- ;;
-esac
-
+MOZ_EMBEDDING_PROFILE=default
MOZ_ARG_WITH_STRING(embedding-profile,
[ --with-embedding-profile=default|basic|minimal
see http://wiki.mozilla.org/Gecko:Small_Device_Support],
[ MOZ_EMBEDDING_PROFILE=$withval ])
case "$MOZ_EMBEDDING_PROFILE" in
default)
MOZ_EMBEDDING_LEVEL_DEFAULT=1
@@ -4575,20 +4590,16 @@ case "$MOZ_BUILD_APP" in
suite)
AC_DEFINE(MOZ_SUITE)
;;
browser)
AC_DEFINE(MOZ_PHOENIX)
;;
-minimo)
- AC_DEFINE(MINIMO)
- ;;
-
mail)
AC_DEFINE(MOZ_THUNDERBIRD)
;;
composer)
AC_DEFINE(MOZ_STANDALONE_COMPOSER)
;;
@@ -8258,16 +8269,19 @@ if test -z "$MOZ_NATIVE_NSPR" || test "$
ac_configure_args="$ac_configure_args --enable-optimize"
fi
if test "$OS_ARCH" = "WINNT" && test "$NS_TRACE_MALLOC"; then
ac_configure_args="$ac_configure_args --enable-debug --disable-optimize"
fi
if test -n "$HAVE_64BIT_OS"; then
ac_configure_args="$ac_configure_args --enable-64bit"
fi
+ if test -n "$USE_ARM_KUSER"; then
+ ac_configure_args="$ac_configure_args --with-arm-kuser"
+ fi
AC_OUTPUT_SUBDIRS(nsprpub)
ac_configure_args="$_SUBDIR_CONFIG_ARGS"
fi
if test -z "$MOZ_NATIVE_NSPR"; then
# Hack to deal with the fact that we use NSPR_CFLAGS everywhere
AC_MSG_WARN([Recreating autoconf.mk with updated nspr-config output])
if test ! "$VACPP" && test "$OS_ARCH" != "WINNT" && test "$OS_ARCH" != "WINCE"; then
--- a/content/base/public/Makefile.in
+++ b/content/base/public/Makefile.in
@@ -67,20 +67,21 @@ nsIPrivateDOMImplementation.h \
nsIContentSerializer.h \
nsIHTMLToTextSink.h \
nsIXPathEvaluatorInternal.h \
mozISanitizingSerializer.h \
nsCaseTreatment.h \
nsContentCID.h \
nsCopySupport.h \
nsContentCreatorFunctions.h \
+nsDOMFile.h \
nsLineBreaker.h \
+nsPresShellIterator.h \
+nsReferencedElement.h \
nsXMLNameSpaceMap.h \
-nsPresShellIterator.h \
-nsDOMFile.h \
$(NULL)
ifndef DISABLE_XFORMS_HOOKS
EXPORTS += nsIXFormsUtilityService.h
endif
SDK_XPIDLSRCS = \
nsISelection.idl \
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -364,16 +364,26 @@ public:
PRBool aTrimTrailing = PR_TRUE);
/**
* Returns true if aChar is of class Ps, Pi, Po, Pf, or Pe. (Does not
* currently handle non-BMP characters.)
*/
static PRBool IsPunctuationMark(PRUnichar aChar);
+ /*
+ * Is the character an HTML whitespace character?
+ *
+ * We define whitespace using the list in HTML5 and css3-selectors:
+ * U+0009, U+000A, U+000C, U+000D, U+0020
+ *
+ * HTML 4.01 also lists U+200B (zero-width space).
+ */
+ static PRBool IsHTMLWhitespace(PRUnichar aChar);
+
static void Shutdown();
/**
* Checks whether two nodes come from the same origin. aTrustedNode is
* considered 'safe' in that a user can operate on it and that it isn't
* a js-object that implements nsIDOMNode.
* Never call this function with the first node provided by script, it
* must always be known to be a 'real' node!
@@ -1196,16 +1206,22 @@ public:
static void HidePopupsInDocument(nsIDocument* aDocument);
/**
* Return true if aURI is a local file URI (i.e. file://).
*/
static PRBool URIIsLocalFile(nsIURI *aURI);
/**
+ * If aContent is an HTML element with a DOM level 0 'name', then
+ * return the name. Otherwise return null.
+ */
+ static nsIAtom* IsNamedItem(nsIContent* aContent);
+
+ /**
* Get the application manifest URI for this context. The manifest URI
* is specified in the manifest= attribute of the root element of the
* toplevel window.
*
* @param aWindow The context to check.
* @param aURI The manifest URI.
*/
static void GetOfflineAppManifest(nsIDOMWindow *aWindow, nsIURI **aURI);
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -92,18 +92,18 @@ class nsIDocumentObserver;
class nsBindingManager;
class nsIDOMNodeList;
class mozAutoSubtreeModified;
struct JSObject;
class nsFrameLoader;
// IID for the nsIDocument interface
#define NS_IDOCUMENT_IID \
-{ 0xc81acf0b, 0x2539, 0x47ab, \
- { 0xa6, 0x04, 0x64, 0x04, 0x07, 0x63, 0xc8, 0x3d } }
+{ 0xc45a4a53, 0x0485, 0x43d5, \
+ { 0x85, 0x95, 0x9f, 0x0b, 0xf4, 0x0d, 0xe9, 0x34 } }
// Flag for AddStyleSheet().
#define NS_STYLESHEET_FROM_CATALOG (1 << 0)
//----------------------------------------------------------------------
// Document interface. This is implemented by all document objects in
// Gecko.
@@ -256,16 +256,42 @@ public:
virtual nsresult AddCharSetObserver(nsIObserver* aObserver) = 0;
/**
* Remove a charset observer.
*/
virtual void RemoveCharSetObserver(nsIObserver* aObserver) = 0;
/**
+ * This gets fired when the element that an id refers to changes.
+ * This fires at difficult times. It is generally not safe to do anything
+ * which could modify the DOM in any way. Use
+ * nsContentUtils::AddScriptRunner.
+ * @return PR_TRUE to keep the callback in the callback set, PR_FALSE
+ * to remove it.
+ */
+ typedef PRBool (* IDTargetObserver)(nsIContent* aOldContent,
+ nsIContent* aNewContent, void* aData);
+
+ /**
+ * Add an IDTargetObserver for a specific ID. The IDTargetObserver
+ * will be fired whenever the content associated with the ID changes
+ * in the future. At most one (aObserver, aData) pair can be registered
+ * for each ID.
+ * @return the content currently associated with the ID.
+ */
+ virtual nsIContent* AddIDTargetObserver(nsIAtom* aID,
+ IDTargetObserver aObserver, void* aData) = 0;
+ /**
+ * Remove the (aObserver, aData) pair for a specific ID, if registered.
+ */
+ virtual void RemoveIDTargetObserver(nsIAtom* aID,
+ IDTargetObserver aObserver, void* aData) = 0;
+
+ /**
* Get the Content-Type of this document.
* (This will always return NS_OK, but has this signature to be compatible
* with nsIDOMNSDocument::GetContentType())
*/
NS_IMETHOD GetContentType(nsAString& aContentType) = 0;
/**
* Set the Content-Type of this document.
@@ -293,19 +319,19 @@ public:
}
/**
* Indicate the document contains bidi data.
* Currently, we cannot disable bidi, because once bidi is enabled,
* it affects a frame model irreversibly, and plays even though
* the document no longer contains bidi data.
*/
- void SetBidiEnabled(PRBool aBidiEnabled)
+ void SetBidiEnabled()
{
- mBidiEnabled = aBidiEnabled;
+ mBidiEnabled = PR_TRUE;
}
/**
* Check if the document contains (or has contained) any MathML elements.
*/
PRBool GetMathMLEnabled() const
{
return mMathMLEnabled;
new file mode 100644
--- /dev/null
+++ b/content/base/public/nsReferencedElement.h
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=78: */
+/* ***** 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.org.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Robert O'Callahan <robert@ocallahan.org>
+ *
+ * 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 NSREFERENCEDELEMENT_H_
+#define NSREFERENCEDELEMENT_H_
+
+#include "nsIContent.h"
+#include "nsIAtom.h"
+#include "nsIDocument.h"
+#include "nsThreadUtils.h"
+#include "nsAutoPtr.h"
+
+class nsIURI;
+class nsCycleCollectionCallback;
+
+/**
+ * Class to track what content is referenced by a given ID.
+ *
+ * By default this is a single-shot tracker --- i.e., when ContentChanged
+ * fires, we will automatically stop tracking. Override IsPersistent
+ * to return PR_TRUE if you want to keep tracking after the content for
+ * an ID has changed.
+ */
+class nsReferencedElement {
+public:
+ nsReferencedElement() {}
+ ~nsReferencedElement() {
+ Unlink();
+ if (mPendingNotification) {
+ mPendingNotification->Clear();
+ }
+ }
+ nsIContent* get() { return mContent; }
+
+ void Reset(nsIContent* aFrom, nsIURI* aURI, PRBool aWatch = PR_TRUE);
+ void Unlink();
+
+ void Traverse(nsCycleCollectionTraversalCallback* aCB);
+
+protected:
+ /**
+ * Override this to be notified of content changes. Don't forget
+ * to call this method to change mContent.
+ */
+ virtual void ContentChanged(nsIContent* aFrom, nsIContent* aTo) {
+ mContent = aTo;
+ }
+
+ /**
+ * Override this to convert from a single-shot notification to
+ * a persistent notification.
+ */
+ virtual PRBool IsPersistent() { return PR_FALSE; }
+
+private:
+ static PRBool Observe(nsIContent* aOldContent,
+ nsIContent* aNewContent, void* aData);
+
+ class Notification : public nsRunnable {
+ public:
+ Notification(nsReferencedElement* aTarget, nsIContent* aFrom, nsIContent* aTo)
+ : mTarget(aTarget), mFrom(aFrom), mTo(aTo) {}
+ NS_IMETHOD Run() {
+ if (mTarget) {
+ mTarget->mPendingNotification = nsnull;
+ mTarget->ContentChanged(mFrom, mTo);
+ }
+ return NS_OK;
+ }
+ void SetTo(nsIContent* aTo) { mTo = aTo; }
+ void Clear() { mTarget = nsnull; mFrom = nsnull; mTo = nsnull; }
+ private:
+ nsReferencedElement* mTarget;
+ nsCOMPtr<nsIContent> mFrom;
+ nsCOMPtr<nsIContent> mTo;
+ };
+ friend class Notification;
+
+ nsCOMPtr<nsIAtom> mWatchID;
+ nsCOMPtr<nsIDocument> mWatchDocument;
+ nsCOMPtr<nsIContent> mContent;
+ nsRefPtr<Notification> mPendingNotification;
+};
+
+#endif /*NSREFERENCEDELEMENT_H_*/
--- a/content/base/src/Makefile.in
+++ b/content/base/src/Makefile.in
@@ -147,16 +147,17 @@ CPPSRCS = \
nsNodeInfo.cpp \
nsNodeInfoManager.cpp \
nsNodeUtils.cpp \
nsObjectLoadingContent.cpp \
nsParserUtils.cpp \
nsPlainTextSerializer.cpp \
nsPropertyTable.cpp \
nsRange.cpp \
+ nsReferencedElement.cpp \
nsScriptElement.cpp \
nsScriptEventManager.cpp \
nsScriptLoader.cpp \
nsStubDocumentObserver.cpp \
nsStubImageDecoderObserver.cpp \
nsStubMutationObserver.cpp \
nsStyledElement.cpp \
nsStyleLinkElement.cpp \
--- a/content/base/src/nsAttrValue.cpp
+++ b/content/base/src/nsAttrValue.cpp
@@ -44,16 +44,17 @@
#include "nsAttrValue.h"
#include "nsIAtom.h"
#include "nsUnicharUtils.h"
#include "nsICSSStyleRule.h"
#include "nsCSSDeclaration.h"
#include "nsIHTMLDocument.h"
#include "nsIDocument.h"
#include "nsTPtrArray.h"
+#include "nsContentUtils.h"
#ifdef MOZ_SVG
#include "nsISVGValue.h"
#endif
nsTPtrArray<const nsAttrValue::EnumTable>* nsAttrValue::sEnumTableArray = nsnull;
nsAttrValue::nsAttrValue()
@@ -728,40 +729,40 @@ nsAttrValue::ParseAtom(const nsAString&
void
nsAttrValue::ParseAtomArray(const nsAString& aValue)
{
nsAString::const_iterator iter, end;
aValue.BeginReading(iter);
aValue.EndReading(end);
// skip initial whitespace
- while (iter != end && nsCRT::IsAsciiSpace(*iter)) {
+ while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
++iter;
}
if (iter == end) {
ResetIfSet();
return;
}
nsAString::const_iterator start(iter);
// get first - and often only - atom
do {
++iter;
- } while (iter != end && !nsCRT::IsAsciiSpace(*iter));
+ } while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter));
nsCOMPtr<nsIAtom> classAtom = do_GetAtom(Substring(start, iter));
if (!classAtom) {
Reset();
return;
}
// skip whitespace
- while (iter != end && nsCRT::IsAsciiSpace(*iter)) {
+ while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
++iter;
}
if (iter == end) {
// we only found one classname so don't bother storing a list
ResetIfSet();
nsIAtom* atom = nsnull;
classAtom.swap(atom);
@@ -781,27 +782,27 @@ nsAttrValue::ParseAtomArray(const nsAStr
}
// parse the rest of the classnames
do {
start = iter;
do {
++iter;
- } while (iter != end && !nsCRT::IsAsciiSpace(*iter));
+ } while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter));
classAtom = do_GetAtom(Substring(start, iter));
if (!array->AppendObject(classAtom)) {
Reset();
return;
}
// skip whitespace
- while (iter != end && nsCRT::IsAsciiSpace(*iter)) {
+ while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
++iter;
}
} while (iter != end);
return;
}
void
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -146,16 +146,19 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_
#include "nsIDOMNSUIEvent.h"
#include "nsIDOMNSEvent.h"
#include "nsIPrivateDOMEvent.h"
#include "nsXULPopupManager.h"
#include "nsIPermissionManager.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsIRunnable.h"
#include "nsDOMJSUtils.h"
+#include "nsGenericHTMLElement.h"
+#include "nsAttrValue.h"
+#include "nsReferencedElement.h"
#ifdef IBMBIDI
#include "nsIBidiKeyboard.h"
#endif
#include "nsCycleCollectionParticipant.h"
// for ReportToConsole
#include "nsIStringBundle.h"
@@ -680,16 +683,28 @@ DEFINE_CCMAP(gPuncCharsCCMap, const);
// static
PRBool
nsContentUtils::IsPunctuationMark(PRUnichar aChar)
{
return CCMAP_HAS_CHAR(gPuncCharsCCMap, aChar);
}
/* static */
+PRBool
+nsContentUtils::IsHTMLWhitespace(PRUnichar aChar)
+{
+ return aChar == PRUnichar(0x0009) ||
+ aChar == PRUnichar(0x000A) ||
+ aChar == PRUnichar(0x000C) ||
+ aChar == PRUnichar(0x000D) ||
+ aChar == PRUnichar(0x0020);
+}
+
+
+/* static */
void
nsContentUtils::GetOfflineAppManifest(nsIDOMWindow *aWindow, nsIURI **aURI)
{
nsCOMPtr<nsIDOMWindow> top;
aWindow->GetTop(getter_AddRefs(top));
if (!top) {
return;
}
@@ -3068,130 +3083,23 @@ nsContentUtils::CheckForBOM(const unsign
aCharset = "UTF-16LE";
} else {
found = PR_FALSE;
}
return found;
}
-static PRBool EqualExceptRef(nsIURL* aURL1, nsIURL* aURL2)
-{
- nsCOMPtr<nsIURI> u1;
- nsCOMPtr<nsIURI> u2;
-
- nsresult rv = aURL1->Clone(getter_AddRefs(u1));
- if (NS_SUCCEEDED(rv)) {
- rv = aURL2->Clone(getter_AddRefs(u2));
- }
- if (NS_FAILED(rv))
- return PR_FALSE;
-
- nsCOMPtr<nsIURL> url1 = do_QueryInterface(u1);
- nsCOMPtr<nsIURL> url2 = do_QueryInterface(u2);
- if (!url1 || !url2) {
- NS_WARNING("Cloning a URL produced a non-URL");
- return PR_FALSE;
- }
- url1->SetRef(EmptyCString());
- url2->SetRef(EmptyCString());
-
- PRBool equal;
- rv = url1->Equals(url2, &equal);
- return NS_SUCCEEDED(rv) && equal;
-}
-
/* static */
nsIContent*
nsContentUtils::GetReferencedElement(nsIURI* aURI, nsIContent *aFromContent)
{
- nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
- if (!url)
- return nsnull;
-
- nsCAutoString refPart;
- url->GetRef(refPart);
- // Unescape %-escapes in the reference. The result will be in the
- // origin charset of the URL, hopefully...
- NS_UnescapeURL(refPart);
-
- nsCAutoString charset;
- url->GetOriginCharset(charset);
- nsAutoString ref;
- nsresult rv = ConvertStringFromCharset(charset, refPart, ref);
- if (NS_FAILED(rv)) {
- CopyUTF8toUTF16(refPart, ref);
- }
- if (ref.IsEmpty())
- return nsnull;
-
- // Get the current document
- nsIDocument *doc = aFromContent->GetCurrentDoc();
- if (!doc)
- return nsnull;
-
- // This will be the URI of the document the content belongs to
- // (the URI of the XBL document if the content is anonymous
- // XBL content)
- nsCOMPtr<nsIURL> documentURL = do_QueryInterface(doc->GetDocumentURI());
- nsIContent* bindingParent = aFromContent->GetBindingParent();
- PRBool isXBL = PR_FALSE;
- if (bindingParent) {
- nsXBLBinding* binding = doc->BindingManager()->GetBinding(bindingParent);
- if (binding) {
- // XXX sXBL/XBL2 issue
- // If this is an anonymous XBL element then the URI is
- // relative to the binding document. A full fix requires a
- // proper XBL2 implementation but for now URIs that are
- // relative to the binding document should be resolve to the
- // copy of the target element that has been inserted into the
- // bound document.
- documentURL = do_QueryInterface(binding->PrototypeBinding()->DocURI());
- isXBL = PR_TRUE;
- }
- }
- if (!documentURL)
- return nsnull;
-
- if (!EqualExceptRef(url, documentURL)) {
- // Oops -- we don't support off-document references
- return nsnull;
- }
-
- // Get the element
- nsCOMPtr<nsIContent> content;
- if (isXBL) {
- nsCOMPtr<nsIDOMNodeList> anonymousChildren;
- doc->BindingManager()->
- GetAnonymousNodesFor(bindingParent, getter_AddRefs(anonymousChildren));
-
- if (anonymousChildren) {
- PRUint32 length;
- anonymousChildren->GetLength(&length);
- for (PRUint32 i = 0; i < length && !content; ++i) {
- nsCOMPtr<nsIDOMNode> node;
- anonymousChildren->Item(i, getter_AddRefs(node));
- nsCOMPtr<nsIContent> c = do_QueryInterface(node);
- if (c) {
- content = MatchElementId(c, ref);
- }
- }
- }
- } else {
- nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
- NS_ASSERTION(domDoc, "Content doesn't reference a dom Document");
-
- nsCOMPtr<nsIDOMElement> element;
- rv = domDoc->GetElementById(ref, getter_AddRefs(element));
- if (element) {
- content = do_QueryInterface(element);
- }
- }
-
- return content;
+ nsReferencedElement ref;
+ ref.Reset(aFromContent, aURI);
+ return ref.get();
}
/* static */
PRBool
nsContentUtils::HasNonEmptyAttr(nsIContent* aContent, PRInt32 aNameSpaceID,
nsIAtom* aName)
{
static nsIContent::AttrValuesArray strings[] = {&nsGkAtoms::_empty, nsnull};
@@ -4276,8 +4184,37 @@ nsContentUtils::URIIsLocalFile(nsIURI *a
}
/* static */
void
nsAutoGCRoot::Shutdown()
{
NS_IF_RELEASE(sJSRuntimeService);
}
+
+nsIAtom*
+nsContentUtils::IsNamedItem(nsIContent* aContent)
+{
+ // Only the content types reflected in Level 0 with a NAME
+ // attribute are registered. Images, layers and forms always get
+ // reflected up to the document. Applets and embeds only go
+ // to the closest container (which could be a form).
+ nsGenericHTMLElement* elm = nsGenericHTMLElement::FromContent(aContent);
+ if (!elm) {
+ return nsnull;
+ }
+
+ nsIAtom* tag = elm->Tag();
+ if (tag != nsGkAtoms::img &&
+ tag != nsGkAtoms::form &&
+ tag != nsGkAtoms::applet &&
+ tag != nsGkAtoms::embed &&
+ tag != nsGkAtoms::object) {
+ return nsnull;
+ }
+
+ const nsAttrValue* val = elm->GetParsedAttr(nsGkAtoms::name);
+ if (val && val->Type() == nsAttrValue::eAtom) {
+ return val->GetAtomValue();
+ }
+
+ return nsnull;
+}
--- a/content/base/src/nsDOMDocumentType.cpp
+++ b/content/base/src/nsDOMDocumentType.cpp
@@ -193,19 +193,17 @@ nsDOMDocumentType::GetSystemId(nsAString
aSystemId = mSystemId;
return NS_OK;
}
NS_IMETHODIMP
nsDOMDocumentType::GetInternalSubset(nsAString& aInternalSubset)
{
- // XXX: null string
aInternalSubset = mInternalSubset;
-
return NS_OK;
}
NS_IMETHODIMP
nsDOMDocumentType::GetNodeName(nsAString& aNodeName)
{
return mName->ToString(aNodeName);
}
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -283,16 +283,233 @@ nsUint32ToContentHashEntry::VisitContent
}
nsIContent* v = GetContent();
if (v) {
aVisitor->Visit(v);
}
}
+#define ID_NOT_IN_DOCUMENT ((nsIContent *)2)
+#define NAME_NOT_VALID ((nsBaseContentList*)1)
+
+nsIdentifierMapEntry::~nsIdentifierMapEntry()
+{
+ if (mNameContentList && mNameContentList != NAME_NOT_VALID) {
+ NS_RELEASE(mNameContentList);
+ }
+}
+
+void
+nsIdentifierMapEntry::Traverse(nsCycleCollectionTraversalCallback* aCallback)
+{
+ if (mNameContentList != NAME_NOT_VALID)
+ aCallback->NoteXPCOMChild(mNameContentList);
+
+ aCallback->NoteXPCOMChild(static_cast<nsIDOMNodeList*>(mDocAllList));
+}
+
+void
+nsIdentifierMapEntry::SetInvalidName()
+{
+ mNameContentList = NAME_NOT_VALID;
+}
+
+PRBool
+nsIdentifierMapEntry::IsInvalidName()
+{
+ return mNameContentList == NAME_NOT_VALID;
+}
+
+nsresult
+nsIdentifierMapEntry::CreateNameContentList()
+{
+ mNameContentList = new nsBaseContentList();
+ NS_ENSURE_TRUE(mNameContentList, NS_ERROR_OUT_OF_MEMORY);
+ NS_ADDREF(mNameContentList);
+ return NS_OK;
+}
+
+nsIContent*
+nsIdentifierMapEntry::GetIdContent(PRBool* aNotInDocument)
+{
+ nsIContent* c = static_cast<nsIContent*>(mIdContentList.SafeElementAt(0));
+ if (aNotInDocument) {
+ *aNotInDocument = c == ID_NOT_IN_DOCUMENT;
+ }
+ return c != ID_NOT_IN_DOCUMENT ? c : nsnull;
+}
+
+void
+nsIdentifierMapEntry::AppendAllIdContent(nsCOMArray<nsIContent>* aElements)
+{
+ for (PRInt32 i = 0; i < mIdContentList.Count(); ++i) {
+ aElements->AppendObject(static_cast<nsIContent*>(mIdContentList[i]));
+ }
+}
+
+void
+nsIdentifierMapEntry::AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
+ void* aData)
+{
+ if (!mChangeCallbacks) {
+ mChangeCallbacks = new nsTHashtable<ChangeCallbackEntry>;
+ if (!mChangeCallbacks)
+ return;
+ mChangeCallbacks->Init();
+ }
+
+ ChangeCallback cc = { aCallback, aData };
+ mChangeCallbacks->PutEntry(cc);
+}
+
+void
+nsIdentifierMapEntry::RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
+ void* aData)
+{
+ if (!mChangeCallbacks)
+ return;
+ ChangeCallback cc = { aCallback, aData };
+ mChangeCallbacks->RemoveEntry(cc);
+ if (mChangeCallbacks->Count() == 0) {
+ mChangeCallbacks = nsnull;
+ }
+}
+
+struct FireChangeArgs {
+ nsIContent* mFrom;
+ nsIContent* mTo;
+};
+
+PR_STATIC_CALLBACK(PLDHashOperator)
+FireChangeEnumerator(nsIdentifierMapEntry::ChangeCallbackEntry *aEntry, void *aArg)
+{
+ FireChangeArgs* args = static_cast<FireChangeArgs*>(aArg);
+ return aEntry->mKey.mCallback(args->mFrom, args->mTo, aEntry->mKey.mData)
+ ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
+}
+
+void
+nsIdentifierMapEntry::FireChangeCallbacks(nsIContent* aOldContent,
+ nsIContent* aNewContent)
+{
+ if (!mChangeCallbacks)
+ return;
+
+ FireChangeArgs args = { aOldContent, aNewContent };
+ mChangeCallbacks->EnumerateEntries(FireChangeEnumerator, &args);
+}
+
+PRBool
+nsIdentifierMapEntry::AddIdContent(nsIContent* aContent)
+{
+ NS_PRECONDITION(aContent, "Must have content");
+ NS_PRECONDITION(mIdContentList.IndexOf(nsnull) < 0,
+ "Why is null in our list?");
+ NS_PRECONDITION(aContent != ID_NOT_IN_DOCUMENT,
+ "Bogus content pointer");
+
+ nsIContent* currentContent = static_cast<nsIContent*>(mIdContentList.SafeElementAt(0));
+ if (currentContent == ID_NOT_IN_DOCUMENT) {
+ NS_ASSERTION(mIdContentList.Count() == 1, "Bogus count");
+ mIdContentList.ReplaceElementAt(aContent, 0);
+ FireChangeCallbacks(nsnull, aContent);
+ return PR_TRUE;
+ }
+
+ // Common case
+ if (mIdContentList.Count() == 0) {
+ if (!mIdContentList.AppendElement(aContent))
+ return PR_FALSE;
+ FireChangeCallbacks(nsnull, aContent);
+ return PR_TRUE;
+ }
+
+ // We seem to have multiple content nodes for the same id, or we're doing our
+ // top-down registration when the id table is going live. Search for the
+ // right place to insert the content.
+ PRInt32 start = 0;
+ PRInt32 end = mIdContentList.Count();
+ do {
+ NS_ASSERTION(start < end, "Bogus start/end");
+
+ PRInt32 cur = (start + end) / 2;
+ NS_ASSERTION(cur >= start && cur < end, "What happened here?");
+
+ nsIContent* curContent = static_cast<nsIContent*>(mIdContentList[cur]);
+ if (curContent == aContent) {
+ // Already in the list, so already in the right spot. Get out of here.
+ return PR_TRUE;
+ }
+
+ if (nsContentUtils::PositionIsBefore(aContent, curContent)) {
+ end = cur;
+ } else {
+ start = cur + 1;
+ }
+ } while (start != end);
+
+ if (!mIdContentList.InsertElementAt(aContent, start))
+ return PR_FALSE;
+ if (start == 0) {
+ FireChangeCallbacks(currentContent, aContent);
+ }
+ return PR_TRUE;
+}
+
+PRBool
+nsIdentifierMapEntry::RemoveIdContent(nsIContent* aContent)
+{
+ // This should only be called while the document is in an update.
+ // Assertions near the call to this method guarantee this.
+
+ // XXXbz should this ever Compact() I guess when all the content is gone
+ // we'll just get cleaned up in the natural order of things...
+ nsIContent* currentContent = static_cast<nsIContent*>(mIdContentList.SafeElementAt(0));
+ if (!mIdContentList.RemoveElement(aContent))
+ return PR_FALSE;
+ if (currentContent == aContent) {
+ FireChangeCallbacks(currentContent,
+ static_cast<nsIContent*>(mIdContentList.SafeElementAt(0)));
+ }
+ return mIdContentList.Count() == 0 && !mNameContentList && !mChangeCallbacks;
+}
+
+void
+nsIdentifierMapEntry::FlagIDNotInDocument()
+{
+ NS_ASSERTION(mIdContentList.Count() == 0,
+ "Flagging ID not in document when we have content?");
+ // Note that if this fails that's OK; this is just an optimization
+ mIdContentList.AppendElement(ID_NOT_IN_DOCUMENT);
+}
+
+void
+nsIdentifierMapEntry::AddNameContent(nsIContent* aContent)
+{
+ if (!mNameContentList || mNameContentList == NAME_NOT_VALID)
+ return;
+
+ // NOTE: this indexof is absolutely needed, since we don't flush
+ // content notifications when we do document.foo resolution. So
+ // aContent may be in our list already and just now getting notified
+ // for!
+ if (mNameContentList->IndexOf(aContent, PR_FALSE) < 0) {
+ mNameContentList->AppendElement(aContent);
+ }
+}
+
+void
+nsIdentifierMapEntry::RemoveNameContent(nsIContent* aContent)
+{
+ if (mNameContentList && mNameContentList != NAME_NOT_VALID) {
+ mNameContentList->RemoveElement(aContent);
+ }
+}
+
// Helper structs for the content->subdoc map
class SubDocMapEntry : public PLDHashEntryHdr
{
public:
// Both of these are strong references
nsIContent *mKey; // must be first, to look like PLDHashEntryStub
nsIDocument *mSubDocument;
@@ -695,19 +912,22 @@ nsDOMImplementation::CreateDocumentType(
{
*aReturn = nsnull;
nsresult rv = nsContentUtils::CheckQName(aQualifiedName);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIAtom> name = do_GetAtom(aQualifiedName);
NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY);
-
+
+ // Indicate that there is no internal subset (not just an empty one)
+ nsAutoString voidString;
+ voidString.SetIsVoid(PR_TRUE);
return NS_NewDOMDocumentType(aReturn, nsnull, mPrincipal, name, nsnull,
- nsnull, aPublicId, aSystemId, EmptyString());
+ nsnull, aPublicId, aSystemId, voidString);
}
NS_IMETHODIMP
nsDOMImplementation::CreateDocument(const nsAString& aNamespaceURI,
const nsAString& aQualifiedName,
nsIDOMDocumentType* aDoctype,
nsIDOMDocument** aReturn)
{
@@ -1004,31 +1224,42 @@ class LinkMapTraversalVisitor : public n
public:
nsCycleCollectionTraversalCallback *mCb;
virtual void Visit(nsIContent* aContent)
{
mCb->NoteXPCOMChild(aContent);
}
};
-PLDHashOperator PR_CALLBACK
+PR_STATIC_CALLBACK(PLDHashOperator)
LinkMapTraverser(nsUint32ToContentHashEntry* aEntry, void* userArg)
{
LinkMapTraversalVisitor visitor;
visitor.mCb = static_cast<nsCycleCollectionTraversalCallback*>(userArg);
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*visitor.mCb, "mLinkMap entry");
aEntry->VisitContent(&visitor);
return PL_DHASH_NEXT;
}
+PR_STATIC_CALLBACK(PLDHashOperator)
+IdentifierMapEntryTraverse(nsIdentifierMapEntry *aEntry, void *aArg)
+{
+ nsCycleCollectionTraversalCallback *cb =
+ static_cast<nsCycleCollectionTraversalCallback*>(aArg);
+ aEntry->Traverse(cb);
+ return PL_DHASH_NEXT;
+}
+
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDocument)
if (nsCCUncollectableMarker::InGeneration(tmp->GetMarkedCCGeneration())) {
return NS_OK;
}
+ tmp->mIdentifierMap.EnumerateEntries(IdentifierMapEntryTraverse, &cb);
+
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mNodeInfo)
// Traverse the mChildren nsAttrAndChildArray.
for (PRInt32 indx = PRInt32(tmp->mChildren.ChildCount()); indx > 0; --indx) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildren[i]");
cb.NoteXPCOMChild(tmp->mChildren.ChildAt(indx - 1));
}
@@ -1119,16 +1350,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
nsresult
nsDocument::Init()
{
if (mCSSLoader || mNodeInfoManager || mScriptLoader) {
return NS_ERROR_ALREADY_INITIALIZED;
}
+ mIdentifierMap.Init();
mLinkMap.Init();
mRadioGroups.Init();
// Force initialization.
nsINode::nsSlots* slots = GetSlots();
NS_ENSURE_TRUE(slots,NS_ERROR_OUT_OF_MEMORY);
// Prepend self as mutation-observer whether we need it or not (some
@@ -1213,16 +1445,17 @@ nsDocument::ResetToURI(nsIURI *aURI, nsI
if (gDocumentLeakPRLog && PR_LOG_TEST(gDocumentLeakPRLog, PR_LOG_DEBUG)) {
nsCAutoString spec;
aURI->GetSpec(spec);
PR_LogPrint("DOCUMENT %p ResetToURI %s", this, spec.get());
}
#endif
mDocumentTitle.SetIsVoid(PR_TRUE);
+ mIdentifierMap.Clear();
SetPrincipal(nsnull);
mSecurityInfo = nsnull;
mDocumentLoadGroup = nsnull;
// Delete references to sub-documents and kill the subdocument map,
// if any. It holds strong references
@@ -1522,16 +1755,195 @@ nsDocument::GetLastModified(nsAString& a
// If we for whatever reason failed to find the last modified time
// (or even the current time), fall back to what NS4.x returned.
aLastModified.Assign(NS_LITERAL_STRING("01/01/1970 00:00:00"));
}
return NS_OK;
}
+void
+nsDocument::UpdateNameTableEntry(nsIContent *aContent)
+{
+ if (!mIsRegularHTML)
+ return;
+
+ nsIAtom* name = nsContentUtils::IsNamedItem(aContent);
+ if (!name)
+ return;
+
+ nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(name);
+ if (!entry) {
+ // We're not tracking the elements with this name
+ return;
+ }
+
+ entry->AddNameContent(aContent);
+}
+
+void
+nsDocument::RemoveFromNameTable(nsIContent *aContent)
+{
+ if (!mIsRegularHTML)
+ return;
+
+ nsIAtom* name = nsContentUtils::IsNamedItem(aContent);
+ if (!name)
+ return;
+
+ nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(name);
+ if (!entry) {
+ // We're not tracking the elements with this name
+ return;
+ }
+
+ entry->RemoveNameContent(aContent);
+}
+
+void
+nsDocument::UpdateIdTableEntry(nsIContent *aContent)
+{
+ nsIAtom* id = aContent->GetID();
+ if (!id)
+ return;
+
+ PRBool liveTable = IdTableIsLive();
+ nsIdentifierMapEntry *entry =
+ liveTable ? mIdentifierMap.PutEntry(id) : mIdentifierMap.GetEntry(id);
+
+ if (entry) {
+ entry->AddIdContent(aContent);
+ }
+}
+
+void
+nsDocument::RemoveFromIdTable(nsIContent *aContent)
+{
+ nsIAtom* id = aContent->GetID();
+ if (!id)
+ return;
+
+ nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(id);
+ if (!entry)
+ return;
+
+ if (entry->RemoveIdContent(aContent)) {
+ mIdentifierMap.RemoveEntry(id);
+ }
+}
+
+void
+nsDocument::UnregisterNamedItems(nsIContent *aContent)
+{
+ if (aContent->IsNodeOfType(nsINode::eTEXT)) {
+ // Text nodes are not named items nor can they have children.
+ return;
+ }
+
+ RemoveFromNameTable(aContent);
+ RemoveFromIdTable(aContent);
+
+ PRUint32 i, count = aContent->GetChildCount();
+ for (i = 0; i < count; ++i) {
+ UnregisterNamedItems(aContent->GetChildAt(i));
+ }
+}
+
+void
+nsDocument::RegisterNamedItems(nsIContent *aContent)
+{
+ if (aContent->IsNodeOfType(nsINode::eTEXT)) {
+ // Text nodes are not named items nor can they have children.
+ return;
+ }
+
+ UpdateNameTableEntry(aContent);
+ UpdateIdTableEntry(aContent);
+
+ PRUint32 i, count = aContent->GetChildCount();
+ for (i = 0; i < count; ++i) {
+ RegisterNamedItems(aContent->GetChildAt(i));
+ }
+}
+
+void
+nsDocument::ContentAppended(nsIDocument* aDocument,
+ nsIContent* aContainer,
+ PRInt32 aNewIndexInContainer)
+{
+ NS_ASSERTION(aDocument == this, "unexpected doc");
+
+ PRUint32 count = aContainer->GetChildCount();
+ for (PRUint32 i = aNewIndexInContainer; i < count; ++i) {
+ RegisterNamedItems(aContainer->GetChildAt(i));
+ }
+}
+
+void
+nsDocument::ContentInserted(nsIDocument* aDocument,
+ nsIContent* aContainer,
+ nsIContent* aContent,
+ PRInt32 aIndexInContainer)
+{
+ NS_ASSERTION(aDocument == this, "unexpected doc");
+
+ NS_ABORT_IF_FALSE(aContent, "Null content!");
+
+ RegisterNamedItems(aContent);
+}
+
+void
+nsDocument::ContentRemoved(nsIDocument* aDocument,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ PRInt32 aIndexInContainer)
+{
+ NS_ASSERTION(aDocument == this, "unexpected doc");
+
+ NS_ABORT_IF_FALSE(aChild, "Null content!");
+
+ UnregisterNamedItems(aChild);
+}
+
+void
+nsDocument::AttributeWillChange(nsIContent* aContent, PRInt32 aNameSpaceID,
+ nsIAtom* aAttribute)
+{
+ NS_ABORT_IF_FALSE(aContent, "Null content!");
+ NS_PRECONDITION(aAttribute, "Must have an attribute that's changing!");
+
+ if (aNameSpaceID != kNameSpaceID_None)
+ return;
+ if (aAttribute == nsGkAtoms::name) {
+ RemoveFromNameTable(aContent);
+ } else if (aAttribute == aContent->GetIDAttributeName()) {
+ RemoveFromIdTable(aContent);
+ }
+}
+
+void
+nsDocument::AttributeChanged(nsIDocument* aDocument,
+ nsIContent* aContent, PRInt32 aNameSpaceID,
+ nsIAtom* aAttribute, PRInt32 aModType,
+ PRUint32 aStateMask)
+{
+ NS_ASSERTION(aDocument == this, "unexpected doc");
+
+ NS_ABORT_IF_FALSE(aContent, "Null content!");
+ NS_PRECONDITION(aAttribute, "Must have an attribute that's changing!");
+
+ if (aNameSpaceID != kNameSpaceID_None)
+ return;
+ if (aAttribute == nsGkAtoms::name) {
+ UpdateNameTableEntry(aContent);
+ } else if (aAttribute == aContent->GetIDAttributeName()) {
+ UpdateIdTableEntry(aContent);
+ }
+}
+
nsIPrincipal*
nsDocument::GetPrincipal()
{
return NodePrincipal();
}
void
nsDocument::SetPrincipal(nsIPrincipal *aNewPrincipal)
@@ -2753,32 +3165,178 @@ nsDocument::BeginLoad()
// Block onload here to prevent having to deal with blocking and
// unblocking it while we know the document is loading.
BlockOnload();
NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad, (this));
}
PRBool
-nsDocument::CheckGetElementByIdArg(const nsAString& aId)
-{
- if (aId.IsEmpty()) {
+nsDocument::CheckGetElementByIdArg(const nsIAtom* aId)
+{
+ if (aId == nsGkAtoms::_empty) {
nsContentUtils::ReportToConsole(
nsContentUtils::eDOM_PROPERTIES,
"EmptyGetElementByIdParam",
nsnull, 0,
nsnull,
EmptyString(), 0, 0,
nsIScriptError::warningFlag,
"DOM");
return PR_FALSE;
}
return PR_TRUE;
}
+static void
+MatchAllElementsId(nsIContent* aContent, nsIAtom* aId, nsIdentifierMapEntry* aEntry)
+{
+ if (aId == aContent->GetID()) {
+ aEntry->AddIdContent(aContent);
+ }
+
+ PRUint32 i, count = aContent->GetChildCount();
+ for (i = 0; i < count; i++) {
+ MatchAllElementsId(aContent->GetChildAt(i), aId, aEntry);
+ }
+}
+
+nsIdentifierMapEntry*
+nsDocument::GetElementByIdInternal(nsIAtom* aID)
+{
+ // We don't have to flush before we do the initial hashtable lookup, since if
+ // the id is already in the hashtable it couldn't have been removed without
+ // us being notified (all removals notify immediately, as far as I can tell).
+ // So do the lookup first.
+ nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(aID);
+ NS_ENSURE_TRUE(entry, nsnull);
+
+ if (entry->GetIdContent())
+ return entry;
+
+ // Now we have to flush. It could be that we have a cached "not in
+ // document" or know nothing about this ID yet but more content has been
+ // added to the document since. Note that we have to flush notifications,
+ // so that the entry will get updated properly.
+
+ // Make sure to stash away the current generation so we can check whether
+ // the table changes when we flush.
+ PRUint32 generation = mIdentifierMap.GetGeneration();
+
+ FlushPendingNotifications(Flush_ContentAndNotify);
+
+ if (generation != mIdentifierMap.GetGeneration()) {
+ // Table changed, so the entry pointer is no longer valid; look up the
+ // entry again, adding if necessary (the adding may be necessary in case
+ // the flush actually deleted entries).
+ entry = mIdentifierMap.PutEntry(aID);
+ }
+
+ PRBool isNotInDocument;
+ nsIContent *e = entry->GetIdContent(&isNotInDocument);
+ if (e || isNotInDocument)
+ return entry;
+
+ // Status of this id is unknown, search document
+ nsIContent* root = GetRootContent();
+ if (!IdTableIsLive()) {
+ if (IdTableShouldBecomeLive()) {
+ // Just make sure our table is up to date and call this method again
+ // to look up in the hashtable.
+ if (root) {
+ RegisterNamedItems(root);
+ }
+ return GetElementByIdInternal(aID);
+ }
+
+ if (root) {
+ // No-one should have registered an ID change callback yet. We don't
+ // want to fire one as a side-effect of getElementById! This shouldn't
+ // happen, since if someone called AddIDTargetObserver already for
+ // this ID, we should have filled in this entry with content or
+ // not-in-document.
+ NS_ASSERTION(!entry->HasContentChangeCallback(),
+ "No callbacks should be registered while we set up this entry");
+ MatchAllElementsId(root, aID, entry);
+ e = entry->GetIdContent();
+ }
+ }
+
+ if (!e) {
+#ifdef DEBUG
+ // No reason to call MatchElementId if !IdTableIsLive, since
+ // we'd have done just that already
+ if (IdTableIsLive() && root && aID != nsGkAtoms::_empty) {
+ nsIContent* eDebug =
+ nsContentUtils::MatchElementId(root, aID);
+ NS_ASSERTION(!eDebug,
+ "We got null for |e| but MatchElementId found something?");
+ }
+#endif
+ // There is no element with the given id in the document, cache
+ // the fact that it's not in the document
+ entry->FlagIDNotInDocument();
+ return entry;
+ }
+
+ return entry;
+}
+
+NS_IMETHODIMP
+nsDocument::GetElementById(const nsAString& aElementId,
+ nsIDOMElement** aReturn)
+{
+ NS_ENSURE_ARG_POINTER(aReturn);
+ *aReturn = nsnull;
+
+ nsCOMPtr<nsIAtom> idAtom(do_GetAtom(aElementId));
+ NS_ENSURE_TRUE(idAtom, NS_ERROR_OUT_OF_MEMORY);
+ if (!CheckGetElementByIdArg(idAtom))
+ return NS_OK;
+
+ nsIdentifierMapEntry *entry = GetElementByIdInternal(idAtom);
+ NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
+
+ PRBool isNotInDocument;
+ nsIContent *e = entry->GetIdContent(&isNotInDocument);
+ NS_ASSERTION(e || isNotInDocument, "Incomplete map entry!");
+ if (isNotInDocument)
+ return NS_OK;
+
+ return CallQueryInterface(e, aReturn);
+}
+
+nsIContent*
+nsDocument::AddIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
+ void* aData)
+{
+ if (!CheckGetElementByIdArg(aID))
+ return nsnull;
+
+ nsIdentifierMapEntry *entry = GetElementByIdInternal(aID);
+ NS_ENSURE_TRUE(entry, nsnull);
+
+ entry->AddContentChangeCallback(aObserver, aData);
+ return entry->GetIdContent();
+}
+
+void
+nsDocument::RemoveIDTargetObserver(nsIAtom* aID,
+ IDTargetObserver aObserver, void* aData)
+{
+ if (!CheckGetElementByIdArg(aID))
+ return;
+
+ nsIdentifierMapEntry *entry = GetElementByIdInternal(aID);
+ if (!entry)
+ return;
+
+ entry->RemoveContentChangeCallback(aObserver, aData);
+}
+
void
nsDocument::DispatchContentLoadedEvents()
{
// If you add early returns from this method, make sure you're
// calling UnblockOnload properly.
// Fire a DOM event notifying listeners that this document has been
// loaded (excluding images and other loads initiated by this
@@ -2877,23 +3435,16 @@ void
nsDocument::ContentStatesChanged(nsIContent* aContent1, nsIContent* aContent2,
PRInt32 aStateMask)
{
NS_DOCUMENT_NOTIFY_OBSERVERS(ContentStatesChanged,
(this, aContent1, aContent2, aStateMask));
}
void
-nsDocument::AttributeWillChange(nsIContent* aChild, PRInt32 aNameSpaceID,
- nsIAtom* aAttribute)
-{
- NS_ASSERTION(aChild, "Null child!");
-}
-
-void
nsDocument::StyleRuleChanged(nsIStyleSheet* aStyleSheet,
nsIStyleRule* aOldStyleRule,
nsIStyleRule* aNewStyleRule)
{
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleChanged,
(this, aStyleSheet,
aOldStyleRule, aNewStyleRule));
}
@@ -3209,24 +3760,16 @@ nsDocument::GetElementsByTagNameNS(const
NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY);
// transfer ref to aReturn
*aReturn = list;
return NS_OK;
}
NS_IMETHODIMP
-nsDocument::GetElementById(const nsAString & elementId,
- nsIDOMElement **_retval)
-{
- // Should be implemented by subclass
- return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
nsDocument::GetAsync(PRBool *aAsync)
{
NS_ERROR("nsDocument::GetAsync() should be overriden by subclass!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -85,16 +85,17 @@
#include "nsIScriptEventManager.h"
#include "nsILayoutHistoryState.h"
#include "nsIRequest.h"
#include "nsILoadGroup.h"
#include "nsTObserverArray.h"
#include "nsStubMutationObserver.h"
#include "nsIChannel.h"
#include "nsCycleCollectionParticipant.h"
+#include "nsContentList.h"
// Put these here so all document impls get them automatically
#include "nsHTMLStyleSheet.h"
#include "nsIHTMLCSSStyleSheet.h"
#include "nsStyleSet.h"
#include "nsXMLEventsManager.h"
#include "pldhash.h"
@@ -205,16 +206,126 @@ class nsUint32ToContentHashEntry : publi
void Destroy();
private:
const PRUint32 mValue;
/** A hash or nsIContent ptr, depending on the lower bit (0=hash, 1=ptr) */
void* mValOrHash;
};
+/**
+ * Right now our identifier map entries contain information for 'name'
+ * and 'id' mappings of a given string. This is so that
+ * nsHTMLDocument::ResolveName only has to do one hash lookup instead
+ * of two. It's not clear whether this still matters for performance.
+ *
+ * We also store the document.all result list here. This is mainly so that
+ * when all elements with the given ID are removed and we remove
+ * the ID's nsIdentifierMapEntry, the document.all result is released too.
+ * Perhaps the document.all results should have their own hashtable
+ * in nsHTMLDocument.
+ */
+class nsIdentifierMapEntry : public nsISupportsHashKey
+{
+public:
+ nsIdentifierMapEntry(const nsISupports* aKey) :
+ nsISupportsHashKey(aKey), mNameContentList(nsnull)
+ {
+ }
+ nsIdentifierMapEntry(const nsIdentifierMapEntry& aOther) :
+ nsISupportsHashKey(GetKey())
+ {
+ NS_ERROR("Should never be called");
+ }
+ ~nsIdentifierMapEntry();
+
+ void SetInvalidName();
+ PRBool IsInvalidName();
+ void AddNameContent(nsIContent* aContent);
+ void RemoveNameContent(nsIContent* aContent);
+ PRBool HasNameContentList() {
+ return mNameContentList != nsnull;
+ }
+ nsBaseContentList* GetNameContentList() {
+ return mNameContentList;
+ }
+ nsresult CreateNameContentList();
+
+ /**
+ * Returns the element if we know the element associated with this
+ * id. Otherwise returns null.
+ * @param aIsNotInDocument if non-null, we set the output to true
+ * if we know for sure the element is not in the document.
+ */
+ nsIContent* GetIdContent(PRBool* aIsNotInDocument = nsnull);
+ void AppendAllIdContent(nsCOMArray<nsIContent>* aElements);
+ /**
+ * This can fire ID change callbacks.
+ * @return true if the content could be added, false if we failed due
+ * to OOM.
+ */
+ PRBool AddIdContent(nsIContent* aContent);
+ /**
+ * This can fire ID change callbacks.
+ * @return true if this map entry should be removed
+ */
+ PRBool RemoveIdContent(nsIContent* aContent);
+ void FlagIDNotInDocument();
+
+ PRBool HasContentChangeCallback() { return mChangeCallbacks != nsnull; }
+ void AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback, void* aData);
+ void RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback, void* aData);
+
+ void Traverse(nsCycleCollectionTraversalCallback* aCallback);
+
+ void SetDocAllList(nsContentList* aContentList) { mDocAllList = aContentList; }
+ nsContentList* GetDocAllList() { return mDocAllList; }
+
+ struct ChangeCallback {
+ nsIDocument::IDTargetObserver mCallback;
+ void* mData;
+ };
+
+ struct ChangeCallbackEntry : public PLDHashEntryHdr {
+ typedef const ChangeCallback KeyType;
+ typedef const ChangeCallback* KeyTypePointer;
+
+ ChangeCallbackEntry(const ChangeCallback* key) :
+ mKey(*key) { }
+ ChangeCallbackEntry(const ChangeCallbackEntry& toCopy) :
+ mKey(toCopy.mKey) { }
+
+ KeyType GetKey() const { return mKey; }
+ PRBool KeyEquals(KeyTypePointer aKey) const {
+ return aKey->mCallback == mKey.mCallback &&
+ aKey->mData == mKey.mData;
+ }
+
+ static KeyTypePointer KeyToPointer(KeyType& aKey) { return &aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ return NS_PTR_TO_INT32(aKey->mCallback) >> 2 +
+ NS_PTR_TO_INT32(aKey->mData);
+ }
+ enum { ALLOW_MEMMOVE = PR_TRUE };
+
+ ChangeCallback mKey;
+ };
+
+private:
+ void FireChangeCallbacks(nsIContent* aOldContent, nsIContent* aNewContent);
+
+ // The single element ID_NOT_IN_DOCUMENT, or empty to indicate we
+ // don't know what element(s) have this key as an ID
+ nsSmallVoidArray mIdContentList;
+ // NAME_NOT_VALID if this id cannot be used as a 'name'
+ nsBaseContentList *mNameContentList;
+ nsRefPtr<nsContentList> mDocAllList;
+ nsAutoPtr<nsTHashtable<ChangeCallbackEntry> > mChangeCallbacks;
+};
class nsDocHeaderData
{
public:
nsDocHeaderData(nsIAtom* aField, const nsAString& aData)
: mField(aField), mData(aData), mNext(nsnull)
{
}
@@ -351,16 +462,21 @@ public:
*/
virtual nsresult AddCharSetObserver(nsIObserver* aObserver);
/**
* Remove a charset observer.
*/
virtual void RemoveCharSetObserver(nsIObserver* aObserver);
+ virtual nsIContent* AddIDTargetObserver(nsIAtom* aID,
+ IDTargetObserver aObserver, void* aData);
+ virtual void RemoveIDTargetObserver(nsIAtom* aID,
+ IDTargetObserver aObserver, void* aData);
+
/**
* Access HTTP header data (this may also get set from other sources, like
* HTML META tags).
*/
virtual void GetHeaderData(nsIAtom* aHeaderField, nsAString& aData) const;
virtual void SetHeaderData(nsIAtom* aheaderField,
const nsAString& aData);
@@ -607,16 +723,22 @@ public:
NS_DECL_NSIDOMEVENTTARGET
// nsIDOM3EventTarget
NS_DECL_NSIDOM3EVENTTARGET
// nsIDOMNSEventTarget
NS_DECL_NSIDOMNSEVENTTARGET
+ // nsIMutationObserver
+ NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
+ NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
+ NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
+ NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
+
// nsIScriptObjectPrincipal
virtual nsIPrincipal* GetPrincipal();
virtual nsresult Init();
virtual nsresult AddXMLEventsContent(nsIContent * aXMLEventsElement);
virtual nsresult CreateElem(nsIAtom *aName, nsIAtom *aPrefix,
@@ -660,22 +782,30 @@ public:
* Utility method for getElementsByClassName. aRootNode is the node (either
* document or element), which getElementsByClassName was called on.
*/
static nsresult GetElementsByClassNameHelper(nsINode* aRootNode,
const nsAString& aClasses,
nsIDOMNodeList** aReturn);
protected:
+ void RegisterNamedItems(nsIContent *aContent);
+ void UnregisterNamedItems(nsIContent *aContent);
+ void UpdateNameTableEntry(nsIContent *aContent);
+ void UpdateIdTableEntry(nsIContent *aContent);
+ void RemoveFromNameTable(nsIContent *aContent);
+ void RemoveFromIdTable(nsIContent *aContent);
+
/**
* Check that aId is not empty and log a message to the console
* service if it is.
* @returns PR_TRUE if aId looks correct, PR_FALSE otherwise.
*/
- static PRBool CheckGetElementByIdArg(const nsAString& aId);
+ static PRBool CheckGetElementByIdArg(const nsIAtom* aId);
+ nsIdentifierMapEntry* GetElementByIdInternal(nsIAtom* aID);
void DispatchContentLoadedEvents();
void InitializeFinalizeFrameLoaders();
void RetrieveRelevantHeaders(nsIChannel *aChannel);
static PRBool TryChannelCharset(nsIChannel *aChannel,
@@ -767,40 +897,68 @@ protected:
// is a weak reference to avoid leaks due to circular references.
nsWeakPtr mScopeObject;
nsCOMPtr<nsIEventListenerManager> mListenerManager;
nsCOMPtr<nsIDOMStyleSheetList> mDOMStyleSheets;
nsRefPtr<nsDOMStyleSheetSetList> mStyleSheetSetList;
nsRefPtr<nsScriptLoader> mScriptLoader;
nsDocHeaderData* mHeaderData;
+ /* mIdentifierMap works as follows for IDs:
+ * 1) Attribute changes affect the table immediately (removing and adding
+ * entries as needed).
+ * 2) Removals from the DOM affect the table immediately
+ * 3) Additions to the DOM always update existing entries, but only add new
+ * ones if IdTableIsLive() is true.
+ */
+ nsTHashtable<nsIdentifierMapEntry> mIdentifierMap;
nsClassHashtable<nsStringHashKey, nsRadioGroupStruct> mRadioGroups;
// True if the document has been detached from its content viewer.
PRPackedBool mIsGoingAway:1;
// True if our content viewer has been removed from the docshell
// (it may still be displayed, but in zombie state). Form control data
// has been saved.
PRPackedBool mRemovedFromDocShell:1;
// True if the document is being destroyed.
PRPackedBool mInDestructor:1;
// True if the document "page" is not hidden
PRPackedBool mVisible:1;
// True if document has ever had script handling object.
PRPackedBool mHasHadScriptHandlingObject:1;
+ // True if this is a regular (non-XHTML) HTML document
+ // XXXbz should this be reset if someone manually calls
+ // SetContentType() on this document?
+ PRPackedBool mIsRegularHTML:1;
PRPackedBool mHasWarnedAboutBoxObjects:1;
PRPackedBool mDelayFrameLoaderInitialization:1;
PRUint8 mXMLDeclarationBits;
PRUint8 mDefaultElementType;
+ PRBool IdTableIsLive() const {
+ // live if we've had over 63 misses
+ return (mIdMissCount & 0x40) != 0;
+ }
+ void SetIdTableLive() {
+ mIdMissCount = 0x40;
+ }
+ PRBool IdTableShouldBecomeLive() {
+ NS_ASSERTION(!IdTableIsLive(),
+ "Shouldn't be called if table is already live!");
+ ++mIdMissCount;
+ return IdTableIsLive();
+ }
+
+ PRUint8 mIdMissCount;
+
nsInterfaceHashtable<nsVoidPtrHashKey, nsPIBoxObject> *mBoxObjectTable;
nsInterfaceHashtable<nsVoidPtrHashKey, nsISupports> *mContentWrapperHash;
// The channel that got passed to StartDocumentLoad(), if any
nsCOMPtr<nsIChannel> mChannel;
nsRefPtr<nsHTMLStyleSheet> mAttrStyleSheet;
nsCOMPtr<nsIHTMLCSSStyleSheet> mStyleAttrStyleSheet;
nsRefPtr<nsXMLEventsManager> mXMLEventsManager;
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -627,17 +627,17 @@ nsGenericDOMDataNode::BindToTree(nsIDocu
// XXXbz sXBL/XBL2 issue!
// Set document
if (aDocument) {
// XXX See the comment in nsGenericElement::BindToTree
mParentPtrBits |= PARENT_BIT_INDOCUMENT;
if (mText.IsBidi()) {
- aDocument->SetBidiEnabled(PR_TRUE);
+ aDocument->SetBidiEnabled();
}
}
nsNodeUtils::ParentChainChanged(this);
UpdateEditableState();
NS_POSTCONDITION(aDocument == GetCurrentDoc(), "Bound to wrong document");
@@ -947,16 +947,159 @@ nsGenericDOMDataNode::SplitText(PRUint32
// No need to handle the case of document being the parent since text
// isn't allowed as direct child of documents
return CallQueryInterface(newContent, aReturn);
}
//----------------------------------------------------------------------
+// Implementation of the nsGenericDOMDataNode nsIDOM3Text tearoff
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsText3Tearoff)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsText3Tearoff)
+ NS_INTERFACE_MAP_ENTRY(nsIDOM3Text)
+NS_INTERFACE_MAP_END_AGGREGATED(mNode)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsText3Tearoff)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mNode)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsText3Tearoff)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mNode, nsIContent)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsText3Tearoff)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsText3Tearoff)
+
+NS_IMETHODIMP
+nsText3Tearoff::GetIsElementContentWhitespace(PRBool *aReturn)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsText3Tearoff::GetWholeText(nsAString& aWholeText)
+{
+ return mNode->GetWholeText(aWholeText);
+}
+
+NS_IMETHODIMP
+nsText3Tearoff::ReplaceWholeText(const nsAString& aContent,
+ nsIDOMText **aReturn)
+{
+ return mNode->ReplaceWholeText(PromiseFlatString(aContent), aReturn);
+}
+
+// Implementation of the nsIDOM3Text interface
+
+/* static */ PRUint32
+nsGenericDOMDataNode::FirstLogicallyAdjacentTextNode(nsIContent* aParent,
+ PRUint32 aIndex)
+{
+ while (aIndex-- > 0) {
+ nsIContent* sibling = aParent->GetChildAt(aIndex);
+ if (!sibling->IsNodeOfType(nsINode::eTEXT))
+ return aIndex + 1;
+ }
+ return 0;
+}
+
+/* static */ PRUint32
+nsGenericDOMDataNode::LastLogicallyAdjacentTextNode(nsIContent* aParent,
+ PRUint32 aIndex,
+ PRUint32 aCount)
+{
+ while (++aIndex < aCount) {
+ nsIContent* sibling = aParent->GetChildAt(aIndex);
+ if (!sibling->IsNodeOfType(nsINode::eTEXT))
+ return aIndex - 1;
+ }
+ return aCount - 1;
+}
+
+nsresult
+nsGenericDOMDataNode::GetWholeText(nsAString& aWholeText)
+{
+ nsIContent* parent = GetParent();
+
+ // Handle parent-less nodes
+ if (!parent)
+ return GetData(aWholeText);
+
+ PRUint32 index = parent->IndexOf(this);
+ PRUint32 first =
+ FirstLogicallyAdjacentTextNode(parent, index);
+ PRUint32 last =
+ LastLogicallyAdjacentTextNode(parent, index, parent->GetChildCount());
+
+ aWholeText.Truncate();
+
+ nsCOMPtr<nsIDOMText> node;
+ nsAutoString tmp;
+ do {
+ node = do_QueryInterface(parent->GetChildAt(first));
+ node->GetData(tmp);
+ aWholeText.Append(tmp);
+ } while (first++ < last);
+
+ return NS_OK;
+}
+
+nsresult
+nsGenericDOMDataNode::ReplaceWholeText(const nsAFlatString& aContent,
+ nsIDOMText **aReturn)
+{
+ // Batch possible DOMSubtreeModified events.
+ mozAutoSubtreeModified subtree(GetOwnerDoc(), nsnull);
+ mozAutoDocUpdate updateBatch(GetCurrentDoc(), UPDATE_CONTENT_MODEL, PR_TRUE);
+
+ nsCOMPtr<nsIContent> parent = GetParent();
+
+ // Handle parent-less nodes
+ if (!parent) {
+ if (aContent.IsEmpty()) {
+ *aReturn = nsnull;
+ return NS_OK;
+ }
+
+ SetText(aContent.get(), aContent.Length(), PR_TRUE);
+ return CallQueryInterface(this, aReturn);
+ }
+
+ // We don't support entity references or read-only nodes, so remove the
+ // logically adjacent text nodes (which therefore must all be siblings of
+ // this) and set this one to the provided text, if that text isn't empty.
+
+ PRUint32 index = parent->IndexOf(this);
+ PRUint32 first =
+ FirstLogicallyAdjacentTextNode(parent, index);
+ PRUint32 last =
+ LastLogicallyAdjacentTextNode(parent, index, parent->GetChildCount());
+
+ do {
+ if (last == index && !aContent.IsEmpty())
+ continue;
+
+ parent->RemoveChildAt(last, PR_TRUE);
+ } while (last-- > first);
+
+ // Empty string means we removed this node too.
+ if (aContent.IsEmpty()) {
+ *aReturn = nsnull;
+ return NS_OK;
+ }
+
+ SetText(aContent.get(), aContent.Length(), PR_TRUE);
+ return CallQueryInterface(this, aReturn);
+}
+
+//----------------------------------------------------------------------
+
// Implementation of the nsIContent interface text functions
const nsTextFragment *
nsGenericDOMDataNode::GetText()
{
return &mText;
}
@@ -1019,17 +1162,17 @@ void nsGenericDOMDataNode::SetBidiStatus
if (document && document->GetBidiEnabled()) {
// OK, we already know it's Bidi, so we won't test again
return;
}
mText.SetBidiFlag();
if (document && mText.IsBidi()) {
- document->SetBidiEnabled(PR_TRUE);
+ document->SetBidiEnabled();
}
}
already_AddRefed<nsIAtom>
nsGenericDOMDataNode::GetCurrentValueAtom()
{
nsAutoString val;
GetData(val);
--- a/content/base/src/nsGenericDOMDataNode.h
+++ b/content/base/src/nsGenericDOMDataNode.h
@@ -40,16 +40,17 @@
* nsIDOMCDATASection, and nsIDOMProcessingInstruction nodes.
*/
#ifndef nsGenericDOMDataNode_h___
#define nsGenericDOMDataNode_h___
#include "nsIDOMCharacterData.h"
#include "nsIDOMEventTarget.h"
+#include "nsIDOM3Text.h"
#include "nsTextFragment.h"
#include "nsVoidArray.h"
#include "nsDOMError.h"
#include "nsIEventListenerManager.h"
#include "nsGenericElement.h"
#include "nsCycleCollectionParticipant.h"
class nsIDOMAttr;
@@ -305,16 +306,29 @@ protected:
nsDataSlots *GetExistingDataSlots() const
{
return static_cast<nsDataSlots*>(GetExistingSlots());
}
nsresult SplitText(PRUint32 aOffset, nsIDOMText** aReturn);
+ friend class nsText3Tearoff;
+
+ static PRUint32 FirstLogicallyAdjacentTextNode(nsIContent* aParent,
+ PRUint32 aIndex);
+
+ static PRUint32 LastLogicallyAdjacentTextNode(nsIContent* aParent,
+ PRUint32 aIndex,
+ PRUint32 aCount);
+
+ nsresult GetWholeText(nsAString& aWholeText);
+
+ nsresult ReplaceWholeText(const nsAFlatString& aContent, nsIDOMText **aReturn);
+
nsresult SetTextInternal(PRUint32 aOffset, PRUint32 aCount,
const PRUnichar* aBuffer, PRUint32 aLength,
PRBool aNotify);
/**
* Method to clone this node. This needs to be overriden by all derived
* classes. If aCloneText is true the text content will be cloned too.
*
@@ -328,16 +342,37 @@ protected:
nsTextFragment mText;
private:
void SetBidiStatus();
already_AddRefed<nsIAtom> GetCurrentValueAtom();
};
+/** Tearoff class for the nsIDOM3Text portion of nsGenericDOMDataNode. */
+class nsText3Tearoff : public nsIDOM3Text
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+
+ NS_DECL_NSIDOM3TEXT
+
+ NS_DECL_CYCLE_COLLECTION_CLASS(nsText3Tearoff)
+
+ nsText3Tearoff(nsGenericDOMDataNode *aNode) : mNode(aNode)
+ {
+ }
+
+protected:
+ virtual ~nsText3Tearoff() {}
+
+private:
+ nsRefPtr<nsGenericDOMDataNode> mNode;
+};
+
//----------------------------------------------------------------------
/**
* Mostly implement the nsIDOMNode API by forwarding the methods to
* nsGenericDOMDataNode
*
* Note that classes using this macro will need to implement:
* NS_IMETHOD GetNodeType(PRUint16* aNodeType);
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -2227,62 +2227,85 @@ nsGenericElement::UnbindFromTree(PRBool
nsresult
nsGenericElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
{
return nsGenericElement::doPreHandleEvent(this, aVisitor);
}
static nsIContent*
-FindFirstNonNativeAnonymousAncestor(nsIContent* aContent)
+FindNativeAnonymousSubtreeOwner(nsIContent* aContent)
{
- while (aContent && aContent->IsNativeAnonymous()) {
- aContent = aContent->GetParent();
+ if (aContent->IsInNativeAnonymousSubtree()) {
+ PRBool isNativeAnon = PR_FALSE;
+ while (aContent && !isNativeAnon) {
+ isNativeAnon = aContent->IsNativeAnonymous();
+ aContent = aContent->GetParent();
+ }
}
return aContent;
}
nsresult
nsGenericElement::doPreHandleEvent(nsIContent* aContent,
nsEventChainPreVisitor& aVisitor)
{
//FIXME! Document how this event retargeting works, Bug 329124.
aVisitor.mCanHandle = PR_TRUE;
// Don't propagate mouseover and mouseout events when mouse is moving
// inside native anonymous content.
PRBool isAnonForEvents = aContent->IsNativeAnonymous();
- if (aVisitor.mEvent->message == NS_MOUSE_ENTER_SYNTH ||
- aVisitor.mEvent->message == NS_MOUSE_EXIT_SYNTH) {
+ if ((aVisitor.mEvent->message == NS_MOUSE_ENTER_SYNTH ||
+ aVisitor.mEvent->message == NS_MOUSE_EXIT_SYNTH) &&
+ // This is an optimization - try to stop event propagation when
+ // event has just possibly been retargeted.
+ static_cast<nsISupports*>(aContent) == aVisitor.mEvent->target) {
nsCOMPtr<nsIContent> relatedTarget =
do_QueryInterface(static_cast<nsMouseEvent*>
(aVisitor.mEvent)->relatedTarget);
if (relatedTarget &&
relatedTarget->GetOwnerDoc() == aContent->GetOwnerDoc()) {
// If current target is anonymous for events or we know that related
// target is descendant of an element which is anonymous for events,
// we may want to stop event propagation.
// If aContent is the original target, aVisitor.mRelatedTargetIsInAnon
// must be updated.
if (isAnonForEvents || aVisitor.mRelatedTargetIsInAnon ||
(aVisitor.mEvent->originalTarget == aContent &&
(aVisitor.mRelatedTargetIsInAnon =
relatedTarget->IsInNativeAnonymousSubtree()))) {
- nsIContent* nonAnon = FindFirstNonNativeAnonymousAncestor(aContent);
- if (nonAnon) {
- nsIContent* nonAnonRelated =
- FindFirstNonNativeAnonymousAncestor(relatedTarget);
- if (nonAnonRelated) {
- if (nonAnon == nonAnonRelated ||
- nsContentUtils::ContentIsDescendantOf(nonAnonRelated, nonAnon)) {
- aVisitor.mParentTarget = nsnull;
- // Event should not propagate to non-anon content.
- aVisitor.mCanHandle = isAnonForEvents;
- return NS_OK;
+ nsIContent* anonOwner = FindNativeAnonymousSubtreeOwner(aContent);
+ if (anonOwner) {
+ nsIContent* anonOwnerRelated =
+ FindNativeAnonymousSubtreeOwner(relatedTarget);
+ if (anonOwnerRelated) {
+ // Note, anonOwnerRelated may still be inside some other
+ // native anonymous subtree. The case where anonOwner is still
+ // inside native anonymous subtree will be handled when event
+ // propagates up in the DOM tree.
+ while (anonOwner != anonOwnerRelated &&
+ anonOwnerRelated->IsInNativeAnonymousSubtree()) {
+ anonOwnerRelated = FindNativeAnonymousSubtreeOwner(anonOwnerRelated);
+ }
+ if (anonOwner == anonOwnerRelated) {
+ nsCOMPtr<nsIContent> target =
+ do_QueryInterface(aVisitor.mEvent->originalTarget);
+ // Because XBL and native anon content both do event re-targeting,
+ // static_cast<nsISupports*>(aContent) == aVisitor.mEvent->target
+ // optimization may not always work. So be paranoid and make
+ // sure we never stop event propagation when we shouldn't!
+ if (relatedTarget->FindFirstNonNativeAnonymous() ==
+ target->FindFirstNonNativeAnonymous()) {
+ aVisitor.mParentTarget = nsnull;
+ // Event should not propagate to non-anon content.
+ aVisitor.mCanHandle = isAnonForEvents;
+ return NS_OK;
+ }
}
}
}
}
}
}
nsCOMPtr<nsIContent> parent = aContent->GetParent();
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -133,18 +133,20 @@ GK_ATOM(block, "block")
GK_ATOM(blockquote, "blockquote")
GK_ATOM(blur, "blur")
GK_ATOM(body, "body")
GK_ATOM(boolean, "boolean")
GK_ATOM(border, "border")
GK_ATOM(bordercolor, "bordercolor")
GK_ATOM(both, "both")
GK_ATOM(bottom, "bottom")
+GK_ATOM(bottomleft, "bottomleft")
GK_ATOM(bottommargin, "bottommargin")
GK_ATOM(bottompadding, "bottompadding")
+GK_ATOM(bottomright, "bottomright")
GK_ATOM(box, "box")
GK_ATOM(br, "br")
GK_ATOM(braille, "braille")
GK_ATOM(broadcast, "broadcast")
GK_ATOM(broadcaster, "broadcaster")
GK_ATOM(broadcasterset, "broadcasterset")
GK_ATOM(browser, "browser")
GK_ATOM(bulletList, "Bullet-list")
@@ -392,16 +394,17 @@ GK_ATOM(hspace, "hspace")
GK_ATOM(html, "html")
GK_ATOM(htmlBaseHref, "html-base-href")
GK_ATOM(htmlBaseTarget, "html-base-target")
GK_ATOM(httpEquiv, "http-equiv")
GK_ATOM(i, "i")
GK_ATOM(id, "id")
GK_ATOM(_if, "if")
GK_ATOM(iframe, "iframe")
+GK_ATOM(ignore, "ignore")
GK_ATOM(ignorecase, "ignorecase")
GK_ATOM(ignorekeys, "ignorekeys")
GK_ATOM(ilayer, "ilayer")
GK_ATOM(image, "image")
GK_ATOM(imageClickedPoint, "image-clicked-point")
GK_ATOM(img, "img")
GK_ATOM(implementation, "implementation")
GK_ATOM(implements, "implements")
@@ -509,16 +512,17 @@ GK_ATOM(mod, "mod")
GK_ATOM(mode, "mode")
GK_ATOM(modifiers, "modifiers")
GK_ATOM(mousedown, "mousedown")
GK_ATOM(mousemove, "mousemove")
GK_ATOM(mouseout, "mouseout")
GK_ATOM(mouseover, "mouseover")
GK_ATOM(mousethrough, "mousethrough")
GK_ATOM(mouseup, "mouseup")
+GK_ATOM(moz_opaque, "moz-opaque")
GK_ATOM(msthemecompatible, "msthemecompatible")
GK_ATOM(multicol, "multicol")
GK_ATOM(multiple, "multiple")
GK_ATOM(name, "name")
GK_ATOM(_namespace, "namespace")
GK_ATOM(namespaceAlias, "namespace-alias")
GK_ATOM(namespaceUri, "namespace-uri")
GK_ATOM(NaN, "NaN")
@@ -796,16 +800,17 @@ GK_ATOM(start_after, "start_after")
GK_ATOM(start_before, "start_before")
GK_ATOM(startsWith, "starts-with")
GK_ATOM(state, "state")
GK_ATOM(statedatasource, "statedatasource")
GK_ATOM(staticHint, "staticHint")
GK_ATOM(statustext, "statustext")
GK_ATOM(stop, "stop")
GK_ATOM(stretch, "stretch")
+GK_ATOM(stretch_to_fit, "stretch-to-fit")
GK_ATOM(strike, "strike")
GK_ATOM(string, "string")
GK_ATOM(stringLength, "string-length")
GK_ATOM(stripSpace, "strip-space")
GK_ATOM(strong, "strong")
GK_ATOM(style, "style")
GK_ATOM(stylesheet, "stylesheet")
GK_ATOM(stylesheetPrefix, "stylesheet-prefix")
@@ -850,18 +855,20 @@ GK_ATOM(token, "token")
GK_ATOM(tokenize, "tokenize")
GK_ATOM(toolbar, "toolbar")
GK_ATOM(toolbarbutton, "toolbarbutton")
GK_ATOM(toolbaritem, "toolbaritem")
GK_ATOM(toolbox, "toolbox")
GK_ATOM(tooltip, "tooltip")
GK_ATOM(tooltiptext, "tooltiptext")
GK_ATOM(top, "top")
+GK_ATOM(topleft, "topleft")
GK_ATOM(topmargin, "topmargin")
GK_ATOM(toppadding, "toppadding")
+GK_ATOM(topright, "topright")
GK_ATOM(tr, "tr")
GK_ATOM(trailing, "trailing")
GK_ATOM(transform, "transform")
GK_ATOM(transformiix, "transformiix")
GK_ATOM(translate, "translate")
GK_ATOM(transparent, "transparent")
GK_ATOM(tree, "tree")
GK_ATOM(treecell, "treecell")
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -757,25 +757,37 @@ nsObjectLoadingContent::EnsureInstantiat
return rv;
}
NS_IMETHODIMP
nsObjectLoadingContent::HasNewFrame(nsIObjectFrame* aFrame)
{
LOG(("OBJLC [%p]: Got frame %p (mInstantiating=%i)\n", this, aFrame,
mInstantiating));
- if (!mInstantiating && aFrame && mType == eType_Plugin) {
+
+ // "revoke" any existing instantiate event as it likely has out of
+ // date data (frame pointer etc).
+ mPendingInstantiateEvent = nsnull;
+
+ nsCOMPtr<nsIPluginInstance> instance;
+ aFrame->GetPluginInstance(*getter_AddRefs(instance));
+
+ if (instance) {
+ // The frame already has a plugin instance, that means the plugin
+ // has already been instantiated.
+
+ return NS_OK;
+ }
+
+ if (!mInstantiating && mType == eType_Plugin) {
// Asynchronously call Instantiate
// This can go away once plugin loading moves to content
// This must be done asynchronously to ensure that the frame is correctly
// initialized (has a view etc)
- // "revoke" any existing instantiate event.
- mPendingInstantiateEvent = nsnull;
-
// When in a plugin document, the document will take care of calling
// instantiate
nsCOMPtr<nsIPluginDocument> pDoc (do_QueryInterface(GetOurDocument()));
if (pDoc) {
return NS_OK;
}
nsCOMPtr<nsIRunnable> event =
--- a/content/base/src/nsObjectLoadingContent.h
+++ b/content/base/src/nsObjectLoadingContent.h
@@ -49,20 +49,20 @@
#include "nsIStreamListener.h"
#include "nsFrameLoader.h"
#include "nsIInterfaceRequestor.h"
#include "nsIChannelEventSink.h"
#include "nsIObjectLoadingContent.h"
#include "nsIRunnable.h"
#include "nsIChannelClassifier.h"
-struct nsAsyncInstantiateEvent;
-class AutoNotifier;
-class AutoFallback;
-class AutoSetInstantiatingToFalse;
+class nsAsyncInstantiateEvent;
+class AutoNotifier;
+class AutoFallback;
+class AutoSetInstantiatingToFalse;
/**
* INVARIANTS OF THIS CLASS
* - mChannel is non-null between asyncOpen and onStopRequest (NOTE: Only needs
* to be valid until onStopRequest is called on mFinalListener, not
* necessarily until the channel calls onStopRequest on us)
* - mChannel corresponds to the channel that gets passed to the
* nsIRequestObserver/nsIStreamListener methods
new file mode 100644
--- /dev/null
+++ b/content/base/src/nsReferencedElement.cpp
@@ -0,0 +1,212 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=78: */
+/* ***** 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.org.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Robert O'Callahan <robert@ocallahan.org>
+ *
+ * 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 "nsReferencedElement.h"
+#include "nsContentUtils.h"
+#include "nsIURI.h"
+#include "nsBindingManager.h"
+#include "nsIURL.h"
+#include "nsEscape.h"
+#include "nsXBLPrototypeBinding.h"
+#include "nsIDOMNode.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMElement.h"
+#include "nsCycleCollectionParticipant.h"
+
+static PRBool EqualExceptRef(nsIURL* aURL1, nsIURL* aURL2)
+{
+ nsCOMPtr<nsIURI> u1;
+ nsCOMPtr<nsIURI> u2;
+
+ nsresult rv = aURL1->Clone(getter_AddRefs(u1));
+ if (NS_SUCCEEDED(rv)) {
+ rv = aURL2->Clone(getter_AddRefs(u2));
+ }
+ if (NS_FAILED(rv))
+ return PR_FALSE;
+
+ nsCOMPtr<nsIURL> url1 = do_QueryInterface(u1);
+ nsCOMPtr<nsIURL> url2 = do_QueryInterface(u2);
+ if (!url1 || !url2) {
+ NS_WARNING("Cloning a URL produced a non-URL");
+ return PR_FALSE;
+ }
+ url1->SetRef(EmptyCString());
+ url2->SetRef(EmptyCString());
+
+ PRBool equal;
+ rv = url1->Equals(url2, &equal);
+ return NS_SUCCEEDED(rv) && equal;
+}
+
+void
+nsReferencedElement::Reset(nsIContent* aFromContent, nsIURI* aURI, PRBool aWatch)
+{
+ Unlink();
+
+ nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
+ if (!url)
+ return;
+
+ nsCAutoString refPart;
+ url->GetRef(refPart);
+ // Unescape %-escapes in the reference. The result will be in the
+ // origin charset of the URL, hopefully...
+ NS_UnescapeURL(refPart);
+
+ nsCAutoString charset;
+ url->GetOriginCharset(charset);
+ nsAutoString ref;
+ nsresult rv = nsContentUtils::ConvertStringFromCharset(charset, refPart, ref);
+ if (NS_FAILED(rv)) {
+ CopyUTF8toUTF16(refPart, ref);
+ }
+ if (ref.IsEmpty())
+ return;
+
+ // Get the current document
+ nsIDocument *doc = aFromContent->GetCurrentDoc();
+ if (!doc)
+ return;
+
+ // This will be the URI of the document the content belongs to
+ // (the URI of the XBL document if the content is anonymous
+ // XBL content)
+ nsCOMPtr<nsIURL> documentURL = do_QueryInterface(doc->GetDocumentURI());
+ nsIContent* bindingParent = aFromContent->GetBindingParent();
+ PRBool isXBL = PR_FALSE;
+ if (bindingParent) {
+ nsXBLBinding* binding = doc->BindingManager()->GetBinding(bindingParent);
+ if (binding) {
+ // XXX sXBL/XBL2 issue
+ // If this is an anonymous XBL element then the URI is
+ // relative to the binding document. A full fix requires a
+ // proper XBL2 implementation but for now URIs that are
+ // relative to the binding document should be resolve to the
+ // copy of the target element that has been inserted into the
+ // bound document.
+ documentURL = do_QueryInterface(binding->PrototypeBinding()->DocURI());
+ isXBL = PR_TRUE;
+ }
+ }
+ if (!documentURL)
+ return;
+
+ if (!EqualExceptRef(url, documentURL)) {
+ // Oops -- we don't support off-document references
+ return;
+ }
+
+ // Get the element
+ if (isXBL) {
+ nsCOMPtr<nsIDOMNodeList> anonymousChildren;
+ doc->BindingManager()->
+ GetAnonymousNodesFor(bindingParent, getter_AddRefs(anonymousChildren));
+
+ if (anonymousChildren) {
+ PRUint32 length;
+ anonymousChildren->GetLength(&length);
+ for (PRUint32 i = 0; i < length && !mContent; ++i) {
+ nsCOMPtr<nsIDOMNode> node;
+ anonymousChildren->Item(i, getter_AddRefs(node));
+ nsCOMPtr<nsIContent> c = do_QueryInterface(node);
+ if (c) {
+ mContent = nsContentUtils::MatchElementId(c, ref);
+ }
+ }
+ }
+ return;
+ }
+
+ if (aWatch) {
+ nsCOMPtr<nsIAtom> atom = do_GetAtom(ref);
+ if (!atom)
+ return;
+ atom.swap(mWatchID);
+ mWatchDocument = doc;
+ mContent = mWatchDocument->AddIDTargetObserver(mWatchID, Observe, this);
+ return;
+ }
+
+ nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
+ NS_ASSERTION(domDoc, "Content doesn't reference a dom Document");
+
+ nsCOMPtr<nsIDOMElement> element;
+ rv = domDoc->GetElementById(ref, getter_AddRefs(element));
+ if (element) {
+ mContent = do_QueryInterface(element);
+ }
+}
+
+void
+nsReferencedElement::Traverse(nsCycleCollectionTraversalCallback* aCB)
+{
+ aCB->NoteXPCOMChild(mWatchDocument);
+ aCB->NoteXPCOMChild(mContent);
+}
+
+void
+nsReferencedElement::Unlink()
+{
+ if (mWatchDocument && mWatchID) {
+ mWatchDocument->RemoveIDTargetObserver(mWatchID, Observe, this);
+ }
+ mWatchDocument = nsnull;
+ mWatchID = nsnull;
+ mContent = nsnull;
+}
+
+PRBool
+nsReferencedElement::Observe(nsIContent* aOldContent,
+ nsIContent* aNewContent, void* aData)
+{
+ nsReferencedElement* p = static_cast<nsReferencedElement*>(aData);
+ if (p->mPendingNotification) {
+ p->mPendingNotification->SetTo(aNewContent);
+ } else {
+ NS_ASSERTION(aOldContent == p->mContent, "Failed to track content!");
+ p->mPendingNotification = new Notification(p, aOldContent, aNewContent);
+ nsContentUtils::AddScriptRunner(p->mPendingNotification);
+ }
+ PRBool keepTracking = p->IsPersistent();
+ if (!keepTracking) {
+ p->mWatchDocument = nsnull;
+ p->mWatchID = nsnull;
+ }
+ return keepTracking;
+}
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -532,18 +532,22 @@ nsScriptLoader::EvaluateScript(nsScriptL
{
nsresult rv = NS_OK;
// We need a document to evaluate scripts.
if (!mDocument) {
return NS_ERROR_FAILURE;
}
- nsIScriptGlobalObject *globalObject = mDocument->GetScriptGlobalObject();
- NS_ENSURE_TRUE(globalObject, NS_ERROR_FAILURE);
+ nsPIDOMWindow *pwin = mDocument->GetInnerWindow();
+ if (!pwin || !pwin->IsInnerWindow()) {
+ return NS_ERROR_FAILURE;
+ }
+ nsCOMPtr<nsIScriptGlobalObject> globalObject = do_QueryInterface(pwin);
+ NS_ASSERTION(globalObject, "windows must be global objects");
// Get the script-type to be used by this element.
nsCOMPtr<nsIContent> scriptContent(do_QueryInterface(aRequest->mElement));
NS_ASSERTION(scriptContent, "no content - what is default script-type?");
PRUint32 stid = scriptContent ? scriptContent->GetScriptTypeID() :
nsIProgrammingLanguage::JAVASCRIPT;
// and make sure we are setup for this type of script.
rv = globalObject->EnsureScriptEnvironment(stid);
--- a/content/base/src/nsTextNode.cpp
+++ b/content/base/src/nsTextNode.cpp
@@ -36,16 +36,17 @@
* ***** END LICENSE BLOCK ***** */
/*
* Implementation of DOM Core's nsIDOMText node.
*/
#include "nsGenericDOMDataNode.h"
#include "nsIDOMText.h"
+#include "nsIDOM3Text.h"
#include "nsContentUtils.h"
#include "nsIDOMEventListener.h"
#include "nsIDOMEventTarget.h"
#include "nsIDOMMutationEvent.h"
#include "nsIAttribute.h"
#include "nsIDocument.h"
#include "nsThreadUtils.h"
@@ -177,16 +178,17 @@ NS_IMPL_ADDREF_INHERITED(nsTextNode, nsG
NS_IMPL_RELEASE_INHERITED(nsTextNode, nsGenericDOMDataNode)
// QueryInterface implementation for nsTextNode
NS_INTERFACE_MAP_BEGIN(nsTextNode)
NS_INTERFACE_MAP_ENTRY(nsIDOMNode)
NS_INTERFACE_MAP_ENTRY(nsIDOMText)
NS_INTERFACE_MAP_ENTRY(nsIDOMCharacterData)
+ NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOM3Text, new nsText3Tearoff(this))
NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(Text)
NS_INTERFACE_MAP_END_INHERITING(nsGenericDOMDataNode)
NS_IMETHODIMP
nsTextNode::GetNodeName(nsAString& aNodeName)
{
aNodeName.AssignLiteral("#text");
return NS_OK;
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -174,17 +174,21 @@ include $(topsrcdir)/config/rules.mk
file_XHR_pass3.txt^headers^ \
file_XHR_fail1.txt \
file_XHR_fail1.txt^headers^ \
test_bug428847.html \
file_bug428847-1.xhtml \
file_bug428847-2.xhtml \
test_bug425201.html \
test_bug431833.html \
- $(NULL)
+ test_bug438519.html \
+ test_text_replaceWholeText.html \
+ test_text_wholeText.html \
+ wholeTexty-helper.xml \
+ $(NULL)
libs:: $(_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
check::
@$(EXIT_ON_ERROR) \
for f in $(subst .cpp,,$(CPP_UNIT_TESTS)); do \
XPCOM_DEBUG_BREAK=stack-and-abort $(RUN_TEST_PROGRAM) $(DIST)/bin/$$f; \
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug438519.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=438519
+-->
+<head>
+ <title>Test for Bug 438519</title>
+ <script type="text/javascript" src="/MochiKit/MochiKit.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 onload="doTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=438519">Mozilla Bug 438519</a>
+<p id="display"></p>
+<div id="content" style="display:none">
+
+<iframe id="empty" src="data:text/xml,<!DOCTYPE HTML []><html></html>"></iframe>
+<iframe id="missing" src="data:text/xml,<!DOCTYPE HTML><html></html>"></iframe>
+<iframe id="entity" src="data:text/xml,<!DOCTYPE HTML [ <!ENTITY foo 'foo'> ]><html></html>"></iframe>
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 218236 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function doTest() {
+ function checkInternalSubset(id, expected) {
+ var e = document.getElementById(id);
+ is(e.contentDocument.doctype.internalSubset, expected, "checking '" + id + "'");
+ }
+
+ checkInternalSubset("empty", "");
+ checkInternalSubset("missing", null);
+ checkInternalSubset("entity", " <!ENTITY foo 'foo'> ");
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_text_replaceWholeText.html
@@ -0,0 +1,273 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=421765
+-->
+<head>
+ <title>Text.replaceWholeText tests</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>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=421765">Mozilla Bug 421765</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<iframe id="xmlDocument" src="wholeTexty-helper.xml"></iframe>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 421765 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var xmlDoc;
+
+function entity(n) { return xmlDoc.createEntityReference(n); }
+function text(t) { return document.createTextNode(t); }
+function element() { return document.createElement("div"); }
+function cdata(t)
+{
+ xmlDoc = $("xmlDocument").contentDocument;
+ // document.createCDATASection isn't implemented; clone for the win
+ var node = xmlDoc.documentElement.firstChild.cloneNode(false);
+ is(node.nodeType, Node.CDATA_SECTION_NODE,
+ "er, why isn't this a CDATA section node?");
+ node.data = t;
+ return node;
+}
+
+
+function startTests()
+{
+ var outer = element();
+ var first = text("first");
+ var second = element();
+ second.appendChild(text("element contents"));
+ outer.appendChild(first);
+ outer.appendChild(second);
+
+ is(first.wholeText, "first", "wrong initial wholeText");
+
+ is(first.replaceWholeText("start"), first,
+ "should have gotten first back");
+ is(first.data, "start", "should have modified first's data");
+ is(first.wholeText, "start", "should have gotten new wholeText");
+
+ var cdataNode = cdata("-cdata");
+ outer.insertBefore(cdataNode, second);
+
+ is(first.wholeText, "start-cdata",
+ "should have gotten first+cdataNode as wholeText");
+
+ var outer2 = outer.cloneNode(true); // save
+
+ is(first.replaceWholeText("first"), first,
+ "replaceWholeText on first returned wrong object");
+ is(first.nodeType, Node.TEXT_NODE, "node changed type?");
+ is(first.data, "first", "wrong data in first");
+ is(first.previousSibling, null, "wrong previousSibling for first");
+ is(first.nextSibling, second, "wrong nextSibling for first");
+ is(cdataNode.previousSibling, null, "wrong previousSibling for cdataNode");
+ is(cdataNode.nextSibling, null, "wrong nextSibling for cdataNode");
+
+ ok(first.replaceWholeText("") === null,
+ "empty string should cause a return of null");
+ is(first.data, "first", "wrong data after replacing with empty string");
+
+ is(outer.firstChild, second, "replaceWholeText('') removes the node");
+
+ // switcheroo, with sanity tests
+ outer = outer2;
+ is(outer.nodeType, Node.ELEMENT_NODE, "outer not element?");
+ first = outer.firstChild;
+ is(first.nodeType, Node.TEXT_NODE, "first not text?");
+ cdataNode = first.nextSibling;
+ is(cdataNode.nodeType, Node.CDATA_SECTION_NODE, "cdataNode not cdata?");
+ second = outer.lastChild;
+ is(second.nodeType, Node.ELEMENT_NODE, "second not element?");
+
+ is(cdataNode.replaceWholeText("cdata"), cdataNode,
+ "replaceWholeText on cdataNode returned wrong object");
+ is(cdataNode.nodeType, Node.CDATA_SECTION_NODE, "node changed type?");
+ is(cdataNode.nodeValue, "cdata", "wrong node value?");
+ is(cdataNode.previousSibling, null, "wrong previousSibling");
+ is(cdataNode.nextSibling, second, "wrong nextSibling");
+
+ ok(cdataNode.replaceWholeText("") === null,
+ "empty string should cause a return of null");
+ is(cdataNode.data, "cdata", "wrong data after replacing with empty string");
+ is(outer.firstChild, second, "should be no more text at start");
+}
+
+function middleTests()
+{
+ var outer = element();
+ var first = element();
+ var middle = text("middle");
+ var last = element();
+ first.appendChild(text("first element contents"));
+ last.appendChild(text("last element contents"));
+ outer.appendChild(first);
+ outer.appendChild(middle);
+ outer.appendChild(last);
+
+ is(middle.wholeText, "middle", "wrong initial wholeText");
+
+ is(middle.replaceWholeText("center"), middle,
+ "should have gotten middle back");
+ is(middle.data, "center", "should have modified middle's data");
+ is(middle.wholeText, "center", "should have gotten new wholeText");
+
+ var cdataNode = cdata("-cdata");
+ outer.insertBefore(cdataNode, last);
+
+ is(middle.wholeText, "center-cdata",
+ "should have gotten middle+cdataNode as wholeText");
+
+ var outer2 = outer.cloneNode(true); // save
+
+ is(middle.replaceWholeText("middle"), middle,
+ "replaceWholeText on middle returned wrong object");
+ is(middle.nodeType, Node.TEXT_NODE, "node changed type?");
+ is(middle.data, "middle", "wrong data in middle");
+ is(middle.previousSibling, first, "wrong previousSibling");
+ is(middle.nextSibling, last, "wrong nextSibling");
+
+ ok(middle.replaceWholeText("") === null,
+ "empty string should cause a return of null");
+ is(middle.data, "middle", "wrong data after replacing with empty string");
+
+ // switcheroo, with sanity tests
+ outer = outer2;
+ is(outer.nodeType, Node.ELEMENT_NODE, "outer not element?");
+ first = outer.firstChild;
+ is(first.nodeType, Node.ELEMENT_NODE, "first not element?");
+ middle = first.nextSibling;
+ is(middle.nodeType, Node.TEXT_NODE, "middle not text?");
+ cdataNode = middle.nextSibling;
+ is(cdataNode.nodeType, Node.CDATA_SECTION_NODE, "cdataNode not cdata?");
+ last = outer.lastChild;
+ is(last.nodeType, Node.ELEMENT_NODE, "last not element?");
+
+ is(cdataNode.replaceWholeText("cdata"), cdataNode,
+ "replaceWholeText on cdataNode returned wrong object");
+ is(cdataNode.nodeType, Node.CDATA_SECTION_NODE, "node changed type?");
+ is(cdataNode.nodeValue, "cdata", "wrong node value?");
+ is(cdataNode.previousSibling, first, "wrong previousSibling");
+ is(cdataNode.nextSibling, last, "wrong nextSibling");
+
+ ok(cdataNode.replaceWholeText("") === null,
+ "empty string should cause a return of null");
+ is(cdataNode.data, "cdata", "wrong data after replacing with empty string");
+ is(middle.wholeText, "center", "wrong wholeText after removal");
+ is(first.nextSibling, last, "wrong nextSibling");
+ is(last.previousSibling, first, "wrong previousSibling");
+}
+
+function endTests()
+{
+ var outer = element();
+ var first = element();
+ var second = text("second");
+ first.appendChild(text("element contents"));
+ outer.appendChild(first);
+ outer.appendChild(second);
+
+ is(second.wholeText, "second", "wrong initial wholeText");
+
+ is(second.replaceWholeText("end"), second,
+ "should have gotten second back");
+ is(second.data, "end", "should have modified second's data");
+ is(second.wholeText, "end", "should have gotten new wholeText");
+
+ var cdataNode = cdata("cdata-");
+ outer.insertBefore(cdataNode, second);
+
+ is(second.wholeText, "cdata-end",
+ "should have gotten cdataNode+second as wholeText");
+ is(cdataNode.wholeText, "cdata-end",
+ "should have gotten cdataNode+second as wholeText");
+
+ var outer2 = outer.cloneNode(true); // save
+
+ is(second.replaceWholeText("second"), second,
+ "replaceWholeText on second returned wrong object");
+ is(second.nodeType, Node.TEXT_NODE, "node changed type?");
+ is(second.data, "second", "wrong data in second");
+ is(second.previousSibling, first, "wrong previousSibling for second");
+ is(second.nextSibling, null, "wrong nextSibling for second");
+ is(cdataNode.previousSibling, null, "wrong previousSibling for cdataNode");
+ is(cdataNode.nextSibling, null, "wrong nextSibling for cdataNode");
+
+ ok(second.replaceWholeText("") === null,
+ "empty string should cause a return of null");
+ is(second.data, "second", "wrong data after replacing with empty string");
+
+ is(outer.lastChild, first, "replaceWholeText('') removes the node");
+
+ // switcheroo, with sanity tests
+ outer = outer2;
+ is(outer.nodeType, Node.ELEMENT_NODE, "outer not element?");
+ first = outer.firstChild;
+ is(first.nodeType, Node.ELEMENT_NODE, "first not element?");
+ cdataNode = first.nextSibling;
+ is(cdataNode.nodeType, Node.CDATA_SECTION_NODE, "cdataNode not cdata?");
+ second = outer.lastChild;
+ is(second.nodeType, Node.TEXT_NODE, "middle not text?");
+
+ is(cdataNode.replaceWholeText("cdata"), cdataNode,
+ "replaceWholeText on cdataNode returned wrong object");
+ is(cdataNode.nodeType, Node.CDATA_SECTION_NODE, "node changed type?");
+ is(cdataNode.nodeValue, "cdata", "wrong node value?");
+ is(cdataNode.previousSibling, first, "wrong previousSibling for cdataNode");
+ is(cdataNode.nextSibling, null, "wrong nextSibling for cdataNode");
+ is(second.previousSibling, null, "wrong previousSibling for second");
+ is(second.nextSibling, null, "wrong nextSibling for second");
+
+ ok(cdataNode.replaceWholeText("") === null,
+ "empty string should cause a return of null");
+ is(cdataNode.data, "cdata", "wrong data after replacing with empty string");
+ is(outer.lastChild, first, "should be no more text at end");
+}
+
+function entityTests()
+{
+ todo_isnot(entity("bar"), null,
+ "need implementation update if we ever support entity nodes!");
+
+ var root = xmlDoc.documentElement;
+ is(root.lastChild.firstChild.nodeType, Node.TEXT_NODE,
+ "uh-oh, did we start supporting entity references as nodes?");
+ is(root.lastChild.lastChild.nodeType, Node.ELEMENT_NODE,
+ "uh-oh, did we start supporting entity references as nodes?");
+
+ // If any of the above ever fails, add tests here!
+}
+
+function test()
+{
+ try
+ {
+ startTests();
+ middleTests();
+ endTests();
+ entityTests();
+ }
+ catch (e)
+ {
+ ok(false, "exception thrown: " + e);
+ }
+ finally
+ {
+ SimpleTest.finish();
+ }
+}
+
+window.addEventListener("load", test, false);
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_text_wholeText.html
@@ -0,0 +1,249 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=421765
+-->
+<head>
+ <title>Text.wholeText tests</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>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=421765">Mozilla Bug 421765</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<iframe id="xmlDocument" src="wholeTexty-helper.xml"></iframe>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 421765 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var xmlDoc;
+
+function entity(n) { return xmlDoc.createEntityReference(n); }
+function text(t) { return document.createTextNode(t); }
+function element() { return document.createElement("div"); }
+function cdata(t)
+{
+ xmlDoc = $("xmlDocument").contentDocument;
+ // document.createCDATASection isn't implemented; clone for the win
+ var node = xmlDoc.documentElement.firstChild.cloneNode(false);
+ is(node.nodeType, Node.CDATA_SECTION_NODE,
+ "er, why isn't this a CDATA section node?");
+ node.data = t;
+ return node;
+}
+
+
+function firstTests()
+{
+ var outer = element();
+ var first = text("first");
+ var second = element();
+ second.appendChild(text("element contents"));
+ outer.appendChild(first);
+ outer.appendChild(second);
+
+ is(first.wholeText, "first", "wrong wholeText for first");
+
+ var insertedText = text("-continued");
+ outer.insertBefore(insertedText, second);
+
+ is(first.wholeText, "first-continued",
+ "wrong wholeText for first after insertedText insertion");
+ is(insertedText.wholeText, "first-continued",
+ "wrong wholeText for insertedText after insertedText insertion");
+
+ var cdataNode = cdata("zero-")
+ outer.insertBefore(cdataNode, first);
+
+ is(first.wholeText, "zero-first-continued",
+ "wrong wholeText for first after cdataNode insertion");
+ is(cdataNode.wholeText, "zero-first-continued",
+ "wrong wholeText for cdataNode after cdataNode insertion");
+ is(insertedText.wholeText, "zero-first-continued",
+ "wrong wholeText for insertedText after cdataNode insertion");
+
+ outer.insertBefore(element(), first);
+
+ is(first.wholeText, "first-continued",
+ "wrong wholeText for first after element insertion");
+ is(cdataNode.wholeText, "zero-",
+ "wrong wholeText for cdataNode after element insertion");
+ is(insertedText.wholeText, "first-continued",
+ "wrong wholeText for insertedText after element insertion");
+
+ var cdataNode2 = cdata("-interrupted");
+ outer.insertBefore(cdataNode2, insertedText);
+
+ is(first.wholeText, "first-interrupted-continued",
+ "wrong wholeText for first after cdataNode2 insertion");
+ is(cdataNode2.wholeText, "first-interrupted-continued",
+ "wrong wholeText for cdataNode2 after cdataNode2 insertion");
+ is(insertedText.wholeText, "first-interrupted-continued",
+ "wrong wholeText for insertedText after cdataNode2 insertion");
+}
+
+function middleTests()
+{
+ var outer = element();
+ var first = element();
+ var last = element();
+ var middle = text("middle");
+ first.appendChild(text("first element contents"));
+ last.appendChild(text("last element contents"));
+ outer.appendChild(first);
+ outer.appendChild(middle);
+ outer.appendChild(last);
+
+ is(middle.wholeText, "middle", "wrong wholeText for middle");
+
+ var beforeMiddle = text("before-");
+ outer.insertBefore(beforeMiddle, middle);
+
+ is(middle.wholeText, "before-middle",
+ "wrong wholeText for middle after beforeMiddle insertion");
+ is(beforeMiddle.wholeText, "before-middle",
+ "wrong wholeText for beforeMiddle after beforeMiddle insertion");
+
+ var midElement = element();
+ midElement.appendChild(text("middle element"));
+ outer.insertBefore(midElement, middle);
+
+ is(middle.wholeText, "middle",
+ "wrong wholeText for middle after midElement insertion");
+ is(beforeMiddle.wholeText, "before-",
+ "wrong wholeText for beforeMiddle after midElement insertion");
+
+ var cdataNode = cdata("after");
+ outer.insertBefore(cdataNode, midElement);
+
+ is(cdataNode.wholeText, "before-after",
+ "wrong wholeText for cdataNode after cdataNode insertion");
+ is(beforeMiddle.wholeText, "before-after",
+ "wrong wholeText for beforeMiddle after cdataNode insertion");
+ is(middle.wholeText, "middle",
+ "wrong wholeText for middle after cdataNode insertion");
+
+ var cdataNode2 = cdata("before-");
+ outer.insertBefore(cdataNode2, middle);
+
+ is(cdataNode.wholeText, "before-after",
+ "wrong wholeText for cdataNode after cdataNode2 insertion");
+ is(beforeMiddle.wholeText, "before-after",
+ "wrong wholeText for beforeMiddle after cdataNode2 insertion");
+ is(cdataNode2.wholeText, "before-middle",
+ "wrong wholeText for middle after cdataNode2 insertion");
+ is(middle.wholeText, "before-middle",
+ "wrong wholeText for middle after cdataNode2 insertion");
+}
+
+function lastTests()
+{
+ var outer = element();
+ var first = element();
+ var second = text("second");
+ first.appendChild(text("element contents"));
+ outer.appendChild(first);
+ outer.appendChild(second);
+
+ is(second.wholeText, "second", "wrong wholeText for second");
+
+ var insertedText = text("before-");
+ outer.insertBefore(insertedText, second);
+
+ is(second.wholeText, "before-second",
+ "wrong wholeText for second after insertedText insertion");
+ is(insertedText.wholeText, "before-second",
+ "wrong wholeText for insertedText after insertedText insertion");
+
+ var cdataNode = cdata("zero-")
+ outer.insertBefore(cdataNode, insertedText);
+
+ is(cdataNode.wholeText, "zero-before-second",
+ "wrong wholeText for cdataNode after cdataNode insertion");
+ is(second.wholeText, "zero-before-second",
+ "wrong wholeText for second after cdataNode insertion");
+ is(insertedText.wholeText, "zero-before-second",
+ "wrong wholeText for insertedText after cdataNode insertion");
+
+ outer.insertBefore(element(), second);
+
+ is(second.wholeText, "second",
+ "wrong wholeText for second after element insertion");
+ is(cdataNode.wholeText, "zero-before-",
+ "wrong wholeText for cdataNode after element insertion");
+ is(insertedText.wholeText, "zero-before-",
+ "wrong wholeText for insertedText after element insertion");
+
+ var cdataNode2 = cdata("interrupted-");
+ outer.insertBefore(cdataNode2, insertedText);
+
+ is(second.wholeText, "second",
+ "wrong wholeText for second after cdataNode2 insertion");
+ is(cdataNode2.wholeText, "zero-interrupted-before-",
+ "wrong wholeText for cdataNode2 after cdataNode2 insertion");
+ is(insertedText.wholeText, "zero-interrupted-before-",
+ "wrong wholeText for insertedText after cdataNode2 insertion");
+}
+
+function noParentTests()
+{
+ var textNode = text("foobar");
+ is(textNode.wholeText, textNode.data,
+ "orphaned textNode should have wholeText == data");
+ is(textNode.wholeText, "foobar",
+ "orphaned textNode should have wholeText == 'foobar'");
+
+ var cdataSection = cdata("baz");
+ is(cdataSection.wholeText, cdataSection.data,
+ "orphaned cdatasection should have wholeText == data");
+ is(cdataSection.wholeText, "baz",
+ "orphaned cdatasection should have wholeText == data");
+}
+
+function entityTests()
+{
+ todo_isnot(entity("bar"), null,
+ "need implementation update if we ever support entity nodes!");
+
+ var root = xmlDoc.documentElement;
+ is(root.lastChild.firstChild.nodeType, Node.TEXT_NODE,
+ "uh-oh, did we start supporting entity references as nodes?");
+ is(root.lastChild.lastChild.nodeType, Node.ELEMENT_NODE,
+ "uh-oh, did we start supporting entity references as nodes?");
+
+ // If any of the above ever fails, add tests here!
+}
+
+function tests()
+{
+ try
+ {
+ firstTests();
+ middleTests();
+ lastTests();
+ noParentTests();
+ entityTests();
+ }
+ catch (e)
+ {
+ ok(false, "error thrown: " + e);
+ }
+ finally
+ {
+ SimpleTest.finish();
+ }
+}
+
+window.addEventListener("load", tests, false);
+</script>
+</pre>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/content/base/test/wholeTexty-helper.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<!DOCTYPE document
+ [
+ <!ENTITY foobar "baz<quux/>">
+ ]>
+<document><![CDATA[]]><entity>&foobar;</entity></document>
--- a/content/canvas/public/nsICanvasRenderingContextInternal.h
+++ b/content/canvas/public/nsICanvasRenderingContextInternal.h
@@ -72,14 +72,20 @@ public:
// composited on black.
NS_IMETHOD GetInputStream(const char *aMimeType,
const PRUnichar *aEncoderOptions,
nsIInputStream **aStream) = 0;
// If this canvas context can be represented with a simple Thebes surface,
// return the surface. Otherwise returns an error.
NS_IMETHOD GetThebesSurface(gfxASurface **surface) = 0;
+
+ // If this context is opaque, the backing store of the canvas should
+ // be created as opaque; all compositing operators should assume the
+ // dst alpha is always 1.0. If this is never called, the context
+ // defaults to false (not opaque).
+ NS_IMETHOD SetIsOpaque(PRBool isOpaque) = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsICanvasRenderingContextInternal,
NS_ICANVASRENDERINGCONTEXTINTERNAL_IID)
#endif /* nsICanvasRenderingContextInternal_h___ */
--- a/content/canvas/src/Makefile.in
+++ b/content/canvas/src/Makefile.in
@@ -81,9 +81,13 @@ endif
# we don't want the shared lib, but we want to force the creation of a static lib.
FORCE_STATIC_LIB = 1
include $(topsrcdir)/config/rules.mk
CXXFLAGS += $(MOZ_CAIRO_CFLAGS) $(TK_CFLAGS)
+INCLUDES += \
+ -I$(srcdir)/../../../layout/style \
+ $(NULL)
+
DEFINES += -D_IMPL_NS_LAYOUT
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -64,16 +64,17 @@
#include "nsIInterfaceRequestorUtils.h"
#include "nsIImage.h"
#include "nsIFrame.h"
#include "nsDOMError.h"
#include "nsIScriptError.h"
#include "nsICSSParser.h"
#include "nsICSSStyleRule.h"
+#include "nsInspectorCSSUtils.h"
#include "nsStyleSet.h"
#include "nsPrintfCString.h"
#include "nsReadableUtils.h"
#include "nsColor.h"
#include "nsIRenderingContext.h"
@@ -270,16 +271,53 @@ NS_IMPL_RELEASE(nsCanvasPattern)
NS_INTERFACE_MAP_BEGIN(nsCanvasPattern)
NS_INTERFACE_MAP_ENTRY(nsCanvasPattern)
NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasPattern)
NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(CanvasPattern)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
/**
+ ** nsTextMetrics
+ **/
+#define NS_TEXTMETRICS_PRIVATE_IID \
+ { 0xc5b1c2f9, 0xcb4f, 0x4394, { 0xaf, 0xe0, 0xc6, 0x59, 0x33, 0x80, 0x8b, 0xf3 } }
+class nsTextMetrics : public nsIDOMTextMetrics
+{
+public:
+ nsTextMetrics(float w) : width(w) { }
+
+ virtual ~nsTextMetrics() { }
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_TEXTMETRICS_PRIVATE_IID)
+
+ NS_IMETHOD GetWidth(float* w) {
+ *w = width;
+ return NS_OK;
+ }
+
+ NS_DECL_ISUPPORTS
+
+private:
+ float width;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsTextMetrics, NS_TEXTMETRICS_PRIVATE_IID)
+
+NS_IMPL_ADDREF(nsTextMetrics)
+NS_IMPL_RELEASE(nsTextMetrics)
+
+NS_INTERFACE_MAP_BEGIN(nsTextMetrics)
+ NS_INTERFACE_MAP_ENTRY(nsTextMetrics)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMTextMetrics)
+ NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(TextMetrics)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+/**
** nsCanvasRenderingContext2D
**/
class nsCanvasRenderingContext2D :
public nsIDOMCanvasRenderingContext2D,
public nsICanvasRenderingContextInternal
{
public:
nsCanvasRenderingContext2D();
@@ -291,16 +329,17 @@ public:
// nsICanvasRenderingContextInternal
NS_IMETHOD SetCanvasElement(nsICanvasElement* aParentCanvas);
NS_IMETHOD SetDimensions(PRInt32 width, PRInt32 height);
NS_IMETHOD Render(gfxContext *ctx);
NS_IMETHOD GetInputStream(const char* aMimeType,
const PRUnichar* aEncoderOptions,
nsIInputStream **aStream);
NS_IMETHOD GetThebesSurface(gfxASurface **surface);
+ NS_IMETHOD SetIsOpaque(PRBool isOpaque);
// nsISupports interface
NS_DECL_ISUPPORTS
// nsIDOMCanvasRenderingContext2D interface
NS_DECL_NSIDOMCANVASRENDERINGCONTEXT2D
protected:
@@ -329,48 +368,87 @@ protected:
// data. If forceWriteOnly is set, we force write only to be set
// and ignore aPrincipal. (This is used for when the original data came
// from a <canvas> that had write-only set.)
void DoDrawImageSecurityCheck(nsIPrincipal* aPrincipal,
PRBool forceWriteOnly);
// Member vars
PRInt32 mWidth, mHeight;
- PRBool mValid;
+ PRPackedBool mValid;
+ PRPackedBool mOpaque;
// the canvas element informs us when it's going away,
// so these are not nsCOMPtrs
nsICanvasElement* mCanvasElement;
// our CSS parser, for colors and whatnot
nsCOMPtr<nsICSSParser> mCSSParser;
// yay cairo
nsRefPtr<gfxContext> mThebesContext;
nsRefPtr<gfxASurface> mThebesSurface;
PRUint32 mSaveCount;
cairo_t *mCairo;
cairo_surface_t *mSurface;
- nsString mTextStyle;
- nsRefPtr<gfxFontGroup> mFontGroup;
+ // text
+ enum TextAlign {
+ TEXT_ALIGN_START,
+ TEXT_ALIGN_END,
+ TEXT_ALIGN_LEFT,
+ TEXT_ALIGN_RIGHT,
+ TEXT_ALIGN_CENTER
+ };
+
+ enum TextBaseline {
+ TEXT_BASELINE_TOP,
+ TEXT_BASELINE_HANGING,
+ TEXT_BASELINE_MIDDLE,
+ TEXT_BASELINE_ALPHABETIC,
+ TEXT_BASELINE_IDEOGRAPHIC,
+ TEXT_BASELINE_BOTTOM
+ };
+
gfxFontGroup *GetCurrentFontStyle();
+
+ enum TextDrawOperation {
+ TEXT_DRAW_OPERATION_FILL,
+ TEXT_DRAW_OPERATION_STROKE
+ };
+
+ /*
+ * Implementation of the fillText and strokeText functions with the
+ * operation abstracted to a flag. Follows the HTML 5 spec for rendering
+ * text. Will query for the fourth, optional argument.
+ */
+ nsresult drawText(const nsAString& text,
+ float x,
+ float y,
+ float maxWidth,
+ TextDrawOperation op);
// style handling
PRInt32 mLastStyle;
PRPackedBool mDirtyStyle[STYLE_MAX];
// state stack handling
class ContextState {
public:
- ContextState() : globalAlpha(1.0) { }
+ ContextState() : globalAlpha(1.0),
+ textAlign(TEXT_ALIGN_START),
+ textBaseline(TEXT_BASELINE_ALPHABETIC) { }
ContextState(const ContextState& other)
- : globalAlpha(other.globalAlpha)
+ : globalAlpha(other.globalAlpha),
+ font(other.font),
+ fontGroup(other.fontGroup),
+ textAlign(other.textAlign),
+ textBaseline(other.textBaseline)
{
for (int i = 0; i < STYLE_MAX; i++) {
colorStyles[i] = other.colorStyles[i];
gradientStyles[i] = other.gradientStyles[i];
patternStyles[i] = other.patternStyles[i];
}
}
@@ -386,16 +464,22 @@ protected:
}
inline void SetGradientStyle(int whichStyle, nsCanvasGradient* grad) {
gradientStyles[whichStyle] = grad;
patternStyles[whichStyle] = nsnull;
}
float globalAlpha;
+
+ nsString font;
+ nsRefPtr<gfxFontGroup> fontGroup;
+ TextAlign textAlign;
+ TextBaseline textBaseline;
+
nscolor colorStyles[STYLE_MAX];
nsCOMPtr<nsCanvasGradient> gradientStyles[STYLE_MAX];
nsCOMPtr<nsCanvasPattern> patternStyles[STYLE_MAX];
};
nsTArray<ContextState> mStyleStack;
inline ContextState& CurrentState() {
@@ -465,17 +549,17 @@ NS_NewCanvasRenderingContext2D(nsIDOMCan
if (!ctx)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*aResult = ctx);
return NS_OK;
}
nsCanvasRenderingContext2D::nsCanvasRenderingContext2D()
- : mValid(PR_FALSE), mCanvasElement(nsnull),
+ : mValid(PR_FALSE), mOpaque(PR_FALSE), mCanvasElement(nsnull),
mSaveCount(0), mCairo(nsnull), mSurface(nsnull), mStyleStack(20)
{
}
nsCanvasRenderingContext2D::~nsCanvasRenderingContext2D()
{
Destroy();
}
@@ -680,17 +764,22 @@ nsCanvasRenderingContext2D::SetDimension
{
Destroy();
mWidth = width;
mHeight = height;
// Check that the dimensions are sane
if (gfxASurface::CheckSurfaceSize(gfxIntSize(width, height), 0xffff)) {
- mThebesSurface = gfxPlatform::GetPlatform()->CreateOffscreenSurface(gfxIntSize(width, height), gfxASurface::ImageFormatARGB32);
+ gfxASurface::gfxImageFormat format = gfxASurface::ImageFormatARGB32;
+ if (mOpaque)
+ format = gfxASurface::ImageFormatRGB24;
+
+ mThebesSurface = gfxPlatform::GetPlatform()->CreateOffscreenSurface
+ (gfxIntSize(width, height), format);
if (mThebesSurface->CairoStatus() == 0) {
mThebesContext = new gfxContext(mThebesSurface);
}
}
/* Create dummy surfaces here */
if (mThebesSurface == nsnull || mThebesSurface->CairoStatus() != 0 ||
@@ -727,38 +816,63 @@ nsCanvasRenderingContext2D::SetDimension
cairo_set_miter_limit(mCairo, 10.0);
cairo_set_line_cap(mCairo, CAIRO_LINE_CAP_BUTT);
cairo_set_line_join(mCairo, CAIRO_LINE_JOIN_MITER);
cairo_new_path(mCairo);
return NS_OK;
}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2D::SetIsOpaque(PRBool isOpaque)
+{
+ if (isOpaque == mOpaque)
+ return NS_OK;
+
+ mOpaque = isOpaque;
+
+ if (mValid) {
+ /* If we've already been created, let SetDimensions take care of
+ * recreating our surface
+ */
+ return SetDimensions(mWidth, mHeight);
+ }
+
+ return NS_OK;
+}
NS_IMETHODIMP
nsCanvasRenderingContext2D::Render(gfxContext *ctx)
{
nsresult rv = NS_OK;
if (!mValid || !mSurface || !mCairo ||
cairo_surface_status(mSurface) != CAIRO_STATUS_SUCCESS ||
cairo_status(mCairo) != CAIRO_STATUS_SUCCESS)
return NS_ERROR_FAILURE;
if (!mThebesSurface)
return NS_ERROR_FAILURE;
nsRefPtr<gfxPattern> pat = new gfxPattern(mThebesSurface);
+ gfxContext::GraphicsOperator op = ctx->CurrentOperator();
+ if (mOpaque)
+ ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
+
// XXX I don't want to use PixelSnapped here, but layout doesn't guarantee
// pixel alignment for this stuff!
ctx->NewPath();
ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, mWidth, mHeight), pat);
ctx->Fill();
+ if (mOpaque)
+ ctx->SetOperator(op);
+
return rv;
}
NS_IMETHODIMP
nsCanvasRenderingContext2D::GetInputStream(const char *aMimeType,
const PRUnichar *aEncoderOptions,
nsIInputStream **aStream)
{
@@ -1451,106 +1565,544 @@ nsCanvasRenderingContext2D::Rect(float x
cairo_rectangle (mCairo, x, y, w, h);
return NS_OK;
}
//
// text
//
-NS_IMETHODIMP
-nsCanvasRenderingContext2D::SetMozTextStyle(const nsAString& textStyle)
+
+/**
+ * Helper function for SetFont that creates a style rule for the given font.
+ * @param aFont The CSS font string
+ * @param aCSSParser The CSS parser of the canvas rendering context
+ * @param aNode The canvas element
+ * @param aResult Pointer in which to place the new style rule.
+ * @remark Assumes all pointer arguments are non-null.
+ */
+static nsresult
+CreateFontStyleRule(const nsAString& aFont,
+ nsICSSParser* aCSSParser,
+ nsINode* aNode,
+ nsICSSStyleRule** aResult)
{
- if(mTextStyle.Equals(textStyle)) return NS_OK;
-
- nsCOMPtr<nsINode> elem = do_QueryInterface(mCanvasElement);
- if (!elem) {
- NS_WARNING("Canvas element must be an nsINode and non-null");
+ nsresult rv;
+
+ nsCOMPtr<nsICSSStyleRule> rule;
+ PRBool changed;
+
+ nsIPrincipal* principal = aNode->NodePrincipal();
+ nsIDocument* document = aNode->GetOwnerDoc();
+
+ nsIURI* docURL = document->GetDocumentURI();
+ nsIURI* baseURL = document->GetBaseURI();
+
+ rv = aCSSParser->ParseStyleAttribute(
+ EmptyString(),
+ docURL,
+ baseURL,
+ principal,
+ getter_AddRefs(rule));
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = aCSSParser->ParseProperty(eCSSProperty_font,
+ aFont,
+ docURL,
+ baseURL,
+ principal,
+ rule->GetDeclaration(),
+ &changed);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // set line height to normal, as per spec
+ rv = aCSSParser->ParseProperty(eCSSProperty_line_height,
+ NS_LITERAL_STRING("normal"),
+ docURL,
+ baseURL,
+ principal,
+ rule->GetDeclaration(),
+ &changed);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rule.forget(aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2D::SetFont(const nsAString& font)
+{
+ nsresult rv;
+
+ /*
+ * If font is defined with relative units (e.g. ems) and the parent
+ * style context changes in between calls, setting the font to the
+ * same value as previous could result in a different computed value,
+ * so we cannot have the optimization where we check if the new font
+ * string is equal to the old one.
+ */
+
+ nsCOMPtr<nsIContent> content = do_QueryInterface(mCanvasElement);
+ if (!content) {
+ NS_WARNING("Canvas element must be an nsIContent and non-null");
return NS_ERROR_FAILURE;
}
- nsIPrincipal* elemPrincipal = elem->NodePrincipal();
- nsIDocument* elemDocument = elem->GetOwnerDoc();
-
- if (!elemDocument || !elemPrincipal) {
- NS_WARNING("Element is missing document or principal");
- return NS_ERROR_FAILURE;
- }
-
- nsIPresShell* presShell = elemDocument->GetPrimaryShell();
+ nsIDocument* document = content->GetOwnerDoc();
+
+ nsIPresShell* presShell = document->GetPrimaryShell();
if (!presShell)
return NS_ERROR_FAILURE;
- nsIURI *docURL = elemDocument->GetDocumentURI();
- nsIURI *baseURL = elemDocument->GetBaseURI();
-
nsCString langGroup;
presShell->GetPresContext()->GetLangGroup()->ToUTF8String(langGroup);
nsCOMArray<nsIStyleRule> rules;
- PRBool changed;
nsCOMPtr<nsICSSStyleRule> rule;
- mCSSParser->ParseStyleAttribute(
- EmptyString(),
- docURL,
- baseURL,
- elemPrincipal,
- getter_AddRefs(rule));
-
- mCSSParser->ParseProperty(eCSSProperty_font,
- textStyle,
- docURL,
- baseURL,
- elemPrincipal,
- rule->GetDeclaration(),
- &changed);
+ rv = CreateFontStyleRule(font, mCSSParser.get(), content.get(), getter_AddRefs(rule));
+ if (NS_FAILED(rv))
+ return rv;
rules.AppendObject(rule);
- nsStyleSet *styleSet = presShell->StyleSet();
-
- nsRefPtr<nsStyleContext> sc = styleSet->ResolveStyleForRules(nsnull,rules);
+ nsStyleSet* styleSet = presShell->StyleSet();
+
+ // have to get a parent style context for inherit-like relative
+ // values (2em, bolder, etc.)
+ nsRefPtr<nsStyleContext> parentContext;
+
+ if (content->IsInDoc()) {
+ // inherit from the canvas element
+ parentContext = nsInspectorCSSUtils::GetStyleContextForContent(
+ content,
+ nsnull,
+ presShell);
+ } else {
+ // otherwise inherit from default (10px sans-serif)
+ nsCOMPtr<nsICSSStyleRule> parentRule;
+ rv = CreateFontStyleRule(NS_LITERAL_STRING("10px sans-serif"),
+ mCSSParser.get(),
+ content.get(),
+ getter_AddRefs(parentRule));
+ if (NS_FAILED(rv))
+ return rv;
+ nsCOMArray<nsIStyleRule> parentRules;
+ parentRules.AppendObject(parentRule);
+ parentContext = styleSet->ResolveStyleForRules(nsnull, parentRules);
+ }
+
+ if (!parentContext)
+ return NS_ERROR_FAILURE;
+
+ nsRefPtr<nsStyleContext> sc = styleSet->ResolveStyleForRules(parentContext, rules);
+ if (!sc)
+ return NS_ERROR_FAILURE;
const nsStyleFont *fontStyle = sc->GetStyleFont();
NS_ASSERTION(fontStyle, "Could not obtain font style");
PRUint32 aupdp = presShell->GetPresContext()->AppUnitsPerDevPixel();
gfxFontStyle style(fontStyle->mFont.style,
fontStyle->mFont.weight,
NSAppUnitsToFloatPixels(fontStyle->mFont.size,aupdp),
langGroup,
fontStyle->mFont.sizeAdjust,
fontStyle->mFont.systemFont,
fontStyle->mFont.familyNameQuirks);
- mFontGroup = gfxPlatform::GetPlatform()->CreateFontGroup(fontStyle->mFont.name, &style);
- NS_ASSERTION(mFontGroup, "Could not get font group");
- mTextStyle = textStyle;
+ CurrentState().fontGroup = gfxPlatform::GetPlatform()->CreateFontGroup(fontStyle->mFont.name, &style);
+ NS_ASSERTION(CurrentState().fontGroup, "Could not get font group");
+ CurrentState().font = font;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2D::GetFont(nsAString& font)
+{
+ /* will initilize the value if not set, else does nothing */
+ GetCurrentFontStyle();
+
+ font = CurrentState().font;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2D::SetTextAlign(const nsAString& ta)
+{
+ if (ta.EqualsLiteral("start"))
+ CurrentState().textAlign = TEXT_ALIGN_START;
+ else if (ta.EqualsLiteral("end"))
+ CurrentState().textAlign = TEXT_ALIGN_END;
+ else if (ta.EqualsLiteral("left"))
+ CurrentState().textAlign = TEXT_ALIGN_LEFT;
+ else if (ta.EqualsLiteral("right"))
+ CurrentState().textAlign = TEXT_ALIGN_RIGHT;
+ else if (ta.EqualsLiteral("center"))
+ CurrentState().textAlign = TEXT_ALIGN_CENTER;
+ // spec says to not throw error for invalid arg, but do it anyway
+ else
+ return NS_ERROR_INVALID_ARG;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2D::GetTextAlign(nsAString& ta)
+{
+ switch (CurrentState().textAlign)
+ {
+ case TEXT_ALIGN_START:
+ ta.AssignLiteral("start");
+ break;
+ case TEXT_ALIGN_END:
+ ta.AssignLiteral("end");
+ break;
+ case TEXT_ALIGN_LEFT:
+ ta.AssignLiteral("left");
+ break;
+ case TEXT_ALIGN_RIGHT:
+ ta.AssignLiteral("right");
+ break;
+ case TEXT_ALIGN_CENTER:
+ ta.AssignLiteral("center");
+ break;
+ default:
+ NS_ASSERTION(0, "textAlign holds invalid value");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2D::SetTextBaseline(const nsAString& tb)
+{
+ if (tb.EqualsLiteral("top"))
+ CurrentState().textBaseline = TEXT_BASELINE_TOP;
+ else if (tb.EqualsLiteral("hanging"))
+ CurrentState().textBaseline = TEXT_BASELINE_HANGING;
+ else if (tb.EqualsLiteral("middle"))
+ CurrentState().textBaseline = TEXT_BASELINE_MIDDLE;
+ else if (tb.EqualsLiteral("alphabetic"))
+ CurrentState().textBaseline = TEXT_BASELINE_ALPHABETIC;
+ else if (tb.EqualsLiteral("ideographic"))
+ CurrentState().textBaseline = TEXT_BASELINE_IDEOGRAPHIC;
+ else if (tb.EqualsLiteral("bottom"))
+ CurrentState().textBaseline = TEXT_BASELINE_BOTTOM;
+ // spec says to not throw error for invalid arg, but do it anyway
+ else
+ return NS_ERROR_INVALID_ARG;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2D::GetTextBaseline(nsAString& tb)
+{
+ switch (CurrentState().textBaseline)
+ {
+ case TEXT_BASELINE_TOP:
+ tb.AssignLiteral("top");
+ break;
+ case TEXT_BASELINE_HANGING:
+ tb.AssignLiteral("hanging");
+ break;
+ case TEXT_BASELINE_MIDDLE:
+ tb.AssignLiteral("middle");
+ break;
+ case TEXT_BASELINE_ALPHABETIC:
+ tb.AssignLiteral("alphabetic");
+ break;
+ case TEXT_BASELINE_IDEOGRAPHIC:
+ tb.AssignLiteral("ideographic");
+ break;
+ case TEXT_BASELINE_BOTTOM:
+ tb.AssignLiteral("bottom");
+ break;
+ default:
+ NS_ASSERTION(0, "textBaseline holds invalid value");
+ return NS_ERROR_FAILURE;
+ }
+
return NS_OK;
}
+/*
+ * Helper function that replaces the whitespace characters in a string
+ * with U+0020 SPACE. The whitespace characters are defined as U+0020 SPACE,
+ * U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF), U+000B LINE
+ * TABULATION, U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR).
+ * @param str The string whose whitespace characters to replace.
+ */
+static inline void
+TextReplaceWhitespaceCharacters(nsAutoString& str)
+{
+ str.ReplaceChar("\x09\x0A\x0B\x0C\x0D", PRUnichar(' '));
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2D::FillText(const nsAString& text, float x, float y, float maxWidth)
+{
+ return drawText(text, x, y, maxWidth, TEXT_DRAW_OPERATION_FILL);
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2D::StrokeText(const nsAString& text, float x, float y, float maxWidth)
+{
+ return drawText(text, x, y, maxWidth, TEXT_DRAW_OPERATION_STROKE);
+}
+
+nsresult
+nsCanvasRenderingContext2D::drawText(const nsAString& rawText,
+ float x,
+ float y,
+ float maxWidth,
+ TextDrawOperation op)
+{
+ if (!FloatValidate(x, y, maxWidth))
+ return NS_ERROR_DOM_SYNTAX_ERR;
+
+ // spec isn't clear on what should happen if maxWidth <= 0, so
+ // treat it as an invalid argument
+ // technically, 0 should be an invalid value as well, but 0 is the default
+ // arg, and there is no way to tell if the default was used
+ if (maxWidth < 0)
+ return NS_ERROR_INVALID_ARG;
+
+ gfxFontGroup* fontgrp = GetCurrentFontStyle();
+ NS_ASSERTION(fontgrp, "font group is null");
+
+ nsCOMPtr<nsIContent> content = do_QueryInterface(mCanvasElement);
+ if (!content) {
+ NS_WARNING("Canvas element must be an nsIContent and non-null");
+ return NS_ERROR_FAILURE;
+ }
+
+ nsIDocument* document = content->GetOwnerDoc();
+
+ nsIPresShell* presShell = document->GetPrimaryShell();
+ if (!presShell)
+ return NS_ERROR_FAILURE;
+
+ // replace all the whitespace characters with U+0020 SPACE
+ nsAutoString textToDraw(rawText);
+ TextReplaceWhitespaceCharacters(textToDraw);
+
+ const PRUnichar* textData;
+ textToDraw.GetData(&textData);
+
+ // for now, default to ltr if not in doc
+ PRBool isRTL = PR_FALSE;
+
+ if (content->IsInDoc()) {
+ // try to find the closest context
+ nsRefPtr<nsStyleContext> canvasStyle =
+ nsInspectorCSSUtils::GetStyleContextForContent(content,
+ nsnull,
+ presShell);
+ if (!canvasStyle)
+ return NS_ERROR_FAILURE;
+ isRTL = canvasStyle->GetStyleVisibility()->mDirection ==
+ NS_STYLE_DIRECTION_RTL;
+ }
+
+ PRUint32 textrunflags = isRTL ? gfxTextRunFactory::TEXT_IS_RTL : 0;
+
+ // app units conversion factor
+ PRUint32 aupdp;
+ GetAppUnitsValues(&aupdp, NULL);
+
+ gfxTextRunCache::AutoTextRun textRun;
+ textRun = gfxTextRunCache::MakeTextRun(textData,
+ textToDraw.Length(),
+ fontgrp,
+ mThebesContext,
+ aupdp,
+ textrunflags);
+
+ if (!textRun.get())
+ return NS_ERROR_FAILURE;
+
+ gfxPoint pt(x, y);
+
+ // get the text width
+ PRBool tightBoundingBox = PR_FALSE;
+ gfxTextRun::Metrics textRunMetrics = textRun->MeasureText(/* offset = */ 0,
+ textToDraw.Length(),
+ tightBoundingBox,
+ mThebesContext,
+ nsnull);
+ gfxFloat textWidth = textRunMetrics.mAdvanceWidth/gfxFloat(aupdp);
+
+
+ // offset pt x based on text align
+ gfxFloat anchorX;
+
+ if (CurrentState().textAlign == TEXT_ALIGN_CENTER)
+ anchorX = .5;
+ else if (CurrentState().textAlign == TEXT_ALIGN_LEFT ||
+ (!isRTL && CurrentState().textAlign == TEXT_ALIGN_START) ||
+ (isRTL && CurrentState().textAlign == TEXT_ALIGN_END))
+ anchorX = 0;
+ else
+ anchorX = 1;
+
+ if (isRTL)
+ pt.x += (1 - anchorX) * textWidth;
+ else
+ pt.x -= anchorX * textWidth;
+
+ // offset pt y based on text baseline
+ NS_ASSERTION(fontgrp->FontListLength()>0, "font group contains no fonts");
+ const gfxFont::Metrics& fontMetrics = fontgrp->GetFontAt(0)->GetMetrics();
+
+ gfxFloat anchorY;
+
+ switch (CurrentState().textBaseline)
+ {
+ case TEXT_BASELINE_TOP:
+ anchorY = fontMetrics.emAscent;
+ break;
+ case TEXT_BASELINE_HANGING:
+ anchorY = 0; // currently unavailable
+ break;
+ case TEXT_BASELINE_MIDDLE:
+ anchorY = (fontMetrics.emAscent-fontMetrics.emDescent)*.5f;
+ break;
+ case TEXT_BASELINE_ALPHABETIC:
+ anchorY = 0;
+ break;
+ case TEXT_BASELINE_IDEOGRAPHIC:
+ anchorY = 0; // currently unvailable
+ break;
+ case TEXT_BASELINE_BOTTOM:
+ anchorY = -fontMetrics.emDescent;
+ break;
+ default:
+ NS_ASSERTION(0, "mTextBaseline holds invalid value");
+ return NS_ERROR_FAILURE;
+ }
+
+ pt.y += anchorY;
+
+ // if text is over maxWidth, then scale the text horizonally such that its
+ // width is precisely maxWidth
+ if (maxWidth>0 && textWidth > maxWidth) {
+ cairo_save(mCairo);
+ // translate the anchor point to 0, then scale and translate back
+ cairo_translate(mCairo, x, 0);
+ cairo_scale(mCairo, (float)(maxWidth/textWidth), 1);
+ cairo_translate(mCairo, -x, 0);
+ }
+
+ pt.x *= aupdp;
+ pt.y *= aupdp;
+
+ // stroke or fill depending on operation
+ if (op == TEXT_DRAW_OPERATION_STROKE) {
+ cairo_save(mCairo);
+ cairo_new_path(mCairo);
+ textRun->DrawToPath(mThebesContext,
+ pt,
+ /* offset = */ 0,
+ textToDraw.Length(),
+ nsnull,
+ nsnull);
+ Stroke();
+ cairo_restore(mCairo);
+ } else {
+ NS_ASSERTION(op == TEXT_DRAW_OPERATION_FILL,
+ "operation should be FILL or STROKE");
+
+ ApplyStyle(STYLE_FILL);
+ textRun->Draw(mThebesContext,
+ pt,
+ /* offset = */ 0,
+ textToDraw.Length(),
+ nsnull,
+ nsnull,
+ nsnull);
+ }
+
+ // have to restore the context if was modified above
+ if (maxWidth>0 && textWidth > maxWidth)
+ cairo_restore(mCairo);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2D::MeasureText(const nsAString& rawText,
+ nsIDOMTextMetrics** _retval)
+{
+ // replace all the whitespace characters with U+0020 SPACE
+ nsAutoString textToMeasure(rawText);
+ TextReplaceWhitespaceCharacters(textToMeasure);
+
+ const PRUnichar* textdata;
+ textToMeasure.GetData(&textdata);
+
+ PRUint32 textrunflags = 0;
+ PRUint32 aupdp;
+ GetAppUnitsValues(&aupdp, nsnull);
+
+ gfxTextRunCache::AutoTextRun textRun;
+ textRun = gfxTextRunCache::MakeTextRun(textdata,
+ textToMeasure.Length(),
+ GetCurrentFontStyle(),
+ mThebesContext,
+ aupdp,
+ textrunflags);
+
+ if(!textRun.get())
+ return NS_ERROR_FAILURE;
+
+ PRBool tightBoundingBox = PR_FALSE;
+ gfxTextRun::Metrics metrics = textRun->MeasureText(/* offset = */ 0, textToMeasure.Length(),
+ tightBoundingBox, mThebesContext,
+ nsnull);
+
+ float textWidth = float(metrics.mAdvanceWidth/gfxFloat(aupdp));
+
+ nsTextMetrics *textMetrics = new nsTextMetrics(textWidth);
+ if (!textMetrics)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*_retval = textMetrics);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2D::SetMozTextStyle(const nsAString& textStyle)
+{
+ // font and mozTextStyle are the same value
+ return SetFont(textStyle);
+}
+
NS_IMETHODIMP
nsCanvasRenderingContext2D::GetMozTextStyle(nsAString& textStyle)
{
- textStyle = mTextStyle;
- return NS_OK;
+ // font and mozTextStyle are the same value
+ return GetFont(textStyle);
}
gfxFontGroup *nsCanvasRenderingContext2D::GetCurrentFontStyle()
{
- if(!mFontGroup)
- {
- nsString style;
- style.AssignLiteral("12pt sans-serif");
- nsresult res = SetMozTextStyle(style);
+ // use lazy initilization for the font group since it's rather expensive
+ if(!CurrentState().fontGroup) {
+ nsresult res = SetMozTextStyle(NS_LITERAL_STRING("10px sans-serif"));
NS_ASSERTION(res == NS_OK, "Default canvas font is invalid");
}
- return mFontGroup;
+
+ return CurrentState().fontGroup;
}
NS_IMETHODIMP
nsCanvasRenderingContext2D::MozDrawText(const nsAString& textToDraw)
{
const PRUnichar* textdata;
textToDraw.GetData(&textdata);
@@ -1583,40 +2135,22 @@ nsCanvasRenderingContext2D::MozDrawText(
nsnull,
nsnull);
return NS_OK;
}
NS_IMETHODIMP
nsCanvasRenderingContext2D::MozMeasureText(const nsAString& textToMeasure, float *retVal)
{
- const PRUnichar* textdata;
- textToMeasure.GetData(&textdata);
-
- PRUint32 textrunflags = 0;
- PRUint32 aupdp, aupcp;
- GetAppUnitsValues(&aupdp, &aupcp);
-
- gfxTextRunCache::AutoTextRun textRun;
- textRun = gfxTextRunCache::MakeTextRun(textdata,
- textToMeasure.Length(),
- GetCurrentFontStyle(),
- mThebesContext,
- aupdp,
- textrunflags);
-
- if(!textRun.get())
- return NS_ERROR_FAILURE;
-
- PRBool tightBoundingBox = PR_FALSE;
- gfxTextRun::Metrics metrics = textRun->MeasureText(/* offset = */ 0, textToMeasure.Length(),
- tightBoundingBox, mThebesContext,
- nsnull);
- *retVal = float(metrics.mAdvanceWidth/gfxFloat(aupcp));
- return NS_OK;
+ nsCOMPtr<nsIDOMTextMetrics> metrics;
+ nsresult rv;
+ rv = MeasureText(textToMeasure, getter_AddRefs(metrics));
+ if (NS_FAILED(rv))
+ return rv;
+ return metrics->GetWidth(retVal);
}
NS_IMETHODIMP
nsCanvasRenderingContext2D::MozPathText(const nsAString& textToPath)
{
const PRUnichar* textdata;
textToPath.GetData(&textdata);
@@ -1680,16 +2214,20 @@ nsCanvasRenderingContext2D::MozTextAlong
PathChar() : draw(PR_FALSE), angle(0.0), pos(0.0,0.0) {}
};
gfxFloat length = path->GetLength();
PRUint32 strLength = textToDraw.Length();
PathChar *cp = new PathChar[strLength];
+ if (!cp) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
gfxPoint position(0.0,0.0);
gfxFloat x = position.x;
for (PRUint32 i = 0; i < strLength; i++)
{
gfxFloat halfAdvance = textRun->GetAdvanceWidth(i, 1, nsnull) / (2.0 * aupdp);
// Check for end of path
if(x + halfAdvance > length)
--- a/content/canvas/test/Makefile.in
+++ b/content/canvas/test/Makefile.in
@@ -498,16 +498,21 @@ include $(topsrcdir)/config/rules.mk
image_green-redirect^headers^ \
image_ggrr-256x256.png \
image_yellow75.png \
image_broken.png \
image_rgrg-256x256.png \
image_red.png \
image_transparent.png \
image_green.png \
+ test_text.font.html \
+ test_text.textAlign.html \
+ test_text.textBaseline.html \
+ test_text.measure.html \
+ test_text.space.replace.html \
$(NULL)
# This one test crashes Mac for now. Bug 407104
ifneq ($(MOZ_WIDGET_TOOLKIT),cocoa)
_TEST_FILES_E += \
test_2d.gradient.empty.html \
$(NULL)
endif
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/test_text.font.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<title>Canvas test: text.font</title>
+<script src="/MochiKit/MochiKit.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+<body>
+<canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+var _deferred = false;
+
+SimpleTest.waitForExplicitFinish();
+MochiKit.DOM.addLoadEvent(function () {
+
+var canvas = document.getElementById('c');
+var ctx = canvas.getContext('2d');
+
+is(ctx.font, '10px sans-serif', "default font is not '10px sans-serif'");
+
+ctx.save();
+ctx.font = '20pt serif';
+is(ctx.font, '20pt serif', 'font getter returns incorrect value');
+
+ctx.restore();
+is(ctx.font, '10px sans-serif', 'font not being stored in the context state');
+
+if (!_deferred) SimpleTest.finish();
+});
+</script>
+
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/test_text.measure.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<title>Canvas test: text.measure</title>
+<script src="/MochiKit/MochiKit.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+<body>
+<canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+var _deferred = false;
+
+SimpleTest.waitForExplicitFinish();
+MochiKit.DOM.addLoadEvent(function () {
+
+var canvas = document.getElementById('c');
+var ctx = canvas.getContext('2d');
+
+ctx.font = "10px sans-serif";
+ctx.textAlign = "left";
+ctx.textBaseline = "top";
+
+var str = 'Test String';
+var wid = ctx.measureText(str).width;
+
+ok(wid > 0, "measureText returns nonpositive value for non-empty string");
+
+ctx.font = "20px sans-serif";
+isnot(wid, ctx.measureText(str).width, "measureText does not change with a different font size");
+
+ctx.font = "10px sans-serif";
+ctx.textAlign = "center";
+ctx.textBaseline = "alphabetic";
+
+is(wid, ctx.measureText(str).width, "measureText changes when alignement/baseline is changed");
+
+
+if (!_deferred) SimpleTest.finish();
+});
+</script>
+
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/test_text.space.replace.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<title>Canvas test: text.space.replace</title>
+<script src="/MochiKit/MochiKit.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+<body>
+<canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+var _deferred = false;
+
+SimpleTest.waitForExplicitFinish();
+MochiKit.DOM.addLoadEvent(function () {
+
+var canvas = document.getElementById('c');
+var ctx = canvas.getContext('2d');
+
+var swid = ctx.measureText(' ').width;
+ctx.font = "10px sans-serif";
+
+isnot(swid, 0.0, "measureText reutuns zero for a non-empty string");
+is(swid, ctx.measureText('\x09').width, "measureText does not replace whitespace char with a space");
+is(swid, ctx.measureText('\x0A').width, "measureText does not replace whitespace char with a space");
+is(swid, ctx.measureText('\x0B').width, "measureText does not replace whitespace char with a space");
+is(swid, ctx.measureText('\x0C').width, "measureText does not replace whitespace char with a space");
+is(swid, ctx.measureText('\x0D').width, "measureText does not replace whitespace char with a space");
+
+if (!_deferred) SimpleTest.finish();
+});
+</script>
+
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/test_text.textAlign.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<title>Canvas test: text.textAlign</title>
+<script src="/MochiKit/MochiKit.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+<body>
+<canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+var _deferred = false;
+
+SimpleTest.waitForExplicitFinish();
+MochiKit.DOM.addLoadEvent(function () {
+
+var canvas = document.getElementById('c');
+var ctx = canvas.getContext('2d');
+
+is(ctx.textAlign, 'start', "default textAlign is not 'start'");
+
+ctx.save();
+ctx.textAlign = 'end';
+is(ctx.textAlign, 'end', 'textAlign getter returns incorrect value');
+
+ctx.save();
+ctx.textAlign = 'left';
+is(ctx.textAlign, 'left', 'textAlign getter returns incorrect value');
+
+ctx.save();
+ctx.textAlign = 'center';
+is(ctx.textAlign, 'center', 'textAlign getter returns incorrect value');
+
+ctx.save();
+ctx.textAlign = 'right';
+is(ctx.textAlign, 'right', 'textAlign getter returns incorrect value');
+
+ctx.save();
+ctx.textAlign = 'start';
+is(ctx.textAlign, 'start', 'textAlign getter returns incorrect value');
+
+ctx.restore();
+is(ctx.textAlign, 'right', 'textAlign not being stored in the context state');
+
+ctx.restore();
+is(ctx.textAlign, 'center', 'textAlign not being stored in the context state');
+
+ctx.restore();
+is(ctx.textAlign, 'left', 'textAlign not being stored in the context state');
+
+ctx.restore();
+is(ctx.textAlign, 'end', 'textAlign not being stored in the context state');
+
+ctx.restore();
+is(ctx.textAlign, 'start', 'textAlign not being stored in the context state');
+
+if (!_deferred) SimpleTest.finish();
+});
+</script>
+
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/test_text.textBaseline.html
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML>
+<title>Canvas test: text.textBaseline</title>
+<script src="/MochiKit/MochiKit.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+<body>
+<canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+var _deferred = false;
+
+SimpleTest.waitForExplicitFinish();
+MochiKit.DOM.addLoadEvent(function () {
+
+var canvas = document.getElementById('c');
+var ctx = canvas.getContext('2d');
+
+is(ctx.textBaseline, 'alphabetic', "default textBaseline is not 'alphabetic'");
+
+ctx.save();
+ctx.textBaseline = 'ideographic';
+is(ctx.textBaseline, 'ideographic', 'textBaseline getter returns incorrect value');
+
+ctx.save();
+ctx.textBaseline = 'top';