зеркало из https://github.com/mozilla/pjs.git
Merge backout of bug 622072
This commit is contained in:
Коммит
434023cd79
|
@ -1272,6 +1272,27 @@ nsAccessibleWrap::FirePlatformEvent(AccEvent* aEvent)
|
|||
g_signal_emit(atkObj, id, 0);
|
||||
} break;
|
||||
|
||||
case nsIAccessibleEvent::EVENT_WINDOW_MAXIMIZE:
|
||||
{
|
||||
MAI_LOG_DEBUG(("\n\nReceived: EVENT_WINDOW_MAXIMIZE\n"));
|
||||
guint id = g_signal_lookup ("maximize", MAI_TYPE_ATK_OBJECT);
|
||||
g_signal_emit(atkObj, id, 0);
|
||||
} break;
|
||||
|
||||
case nsIAccessibleEvent::EVENT_WINDOW_MINIMIZE:
|
||||
{
|
||||
MAI_LOG_DEBUG(("\n\nReceived: EVENT_WINDOW_MINIMIZE\n"));
|
||||
guint id = g_signal_lookup ("minimize", MAI_TYPE_ATK_OBJECT);
|
||||
g_signal_emit(atkObj, id, 0);
|
||||
} break;
|
||||
|
||||
case nsIAccessibleEvent::EVENT_WINDOW_RESTORE:
|
||||
{
|
||||
MAI_LOG_DEBUG(("\n\nReceived: EVENT_WINDOW_RESTORE\n"));
|
||||
guint id = g_signal_lookup ("restore", MAI_TYPE_ATK_OBJECT);
|
||||
g_signal_emit(atkObj, id, 0);
|
||||
} break;
|
||||
|
||||
case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE:
|
||||
{
|
||||
MAI_LOG_DEBUG(("\n\nReceived: EVENT_DOCUMENT_LOAD_COMPLETE\n"));
|
||||
|
|
|
@ -218,6 +218,24 @@ mai_util_get_type(void)
|
|||
return type;
|
||||
}
|
||||
|
||||
static void
|
||||
window_added (AtkObject *atk_obj,
|
||||
guint index,
|
||||
AtkObject *child)
|
||||
{
|
||||
guint id = g_signal_lookup ("create", MAI_TYPE_ATK_OBJECT);
|
||||
g_signal_emit (child, id, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
window_removed (AtkObject *atk_obj,
|
||||
guint index,
|
||||
AtkObject *child)
|
||||
{
|
||||
guint id = g_signal_lookup ("destroy", MAI_TYPE_ATK_OBJECT);
|
||||
g_signal_emit (child, id, 0);
|
||||
}
|
||||
|
||||
/* intialize the the atk interface (function pointers) with MAI implementation.
|
||||
* When atk bridge get loaded, these interface can be used.
|
||||
*/
|
||||
|
@ -248,6 +266,10 @@ mai_util_class_init(MaiUtilClass *klass)
|
|||
|
||||
listener_list = g_hash_table_new_full(g_int_hash, g_int_equal, NULL,
|
||||
_listener_info_destroy);
|
||||
// Keep track of added/removed windows.
|
||||
AtkObject *root = atk_get_root ();
|
||||
g_signal_connect (root, "children-changed::add", (GCallback) window_added, NULL);
|
||||
g_signal_connect (root, "children-changed::remove", (GCallback) window_removed, NULL);
|
||||
}
|
||||
|
||||
static guint
|
||||
|
|
|
@ -304,10 +304,10 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
|||
if (accEvent->mEventRule != AccEvent::eDoNotEmit) {
|
||||
mDocument->ProcessPendingEvent(accEvent);
|
||||
|
||||
AccMutationEvent* showOrhideEvent = downcast_accEvent(accEvent);
|
||||
if (showOrhideEvent) {
|
||||
if (showOrhideEvent->mTextChangeEvent)
|
||||
mDocument->ProcessPendingEvent(showOrhideEvent->mTextChangeEvent);
|
||||
AccMutationEvent* showOrHideEvent = downcast_accEvent(accEvent);
|
||||
if (showOrHideEvent) {
|
||||
if (showOrHideEvent->mTextChangeEvent)
|
||||
mDocument->ProcessPendingEvent(showOrHideEvent->mTextChangeEvent);
|
||||
}
|
||||
}
|
||||
if (!mDocument)
|
||||
|
|
|
@ -70,7 +70,10 @@ TextUpdater::DoUpdate(const nsAString& aNewText, const nsAString& aOldText,
|
|||
PRUint32 aSkipStart)
|
||||
{
|
||||
nsAccessible* parent = mTextLeaf->GetParent();
|
||||
NS_ASSERTION(parent, "No parent for text leaf!");
|
||||
if (!parent) {
|
||||
NS_ERROR("No parent for text leaf!");
|
||||
return;
|
||||
}
|
||||
|
||||
mHyperText = parent->AsHyperText();
|
||||
if (!mHyperText) {
|
||||
|
|
|
@ -374,16 +374,6 @@ public:
|
|||
role != nsIAccessibleRole::ROLE_STATICTEXT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given accessible hasn't children.
|
||||
*/
|
||||
static inline PRBool IsLeaf(nsIAccessible *aAcc)
|
||||
{
|
||||
PRInt32 numChildren = 0;
|
||||
aAcc->GetChildCount(&numChildren);
|
||||
return numChildren == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given accessible can't have children. Used when exposing
|
||||
* to platform accessibility APIs, should the children be pruned off?
|
||||
|
|
|
@ -100,6 +100,7 @@
|
|||
#endif
|
||||
|
||||
#include "mozilla/FunctionTimer.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsAccessibilityService
|
||||
|
@ -275,7 +276,7 @@ nsAccessibilityService::CreateHTMLImageAccessible(nsIContent* aContent,
|
|||
if (!mapElmName.IsEmpty()) {
|
||||
if (mapElmName.CharAt(0) == '#')
|
||||
mapElmName.Cut(0,1);
|
||||
mapElm = htmlDoc->GetImageMap(mapElmName);
|
||||
mapElm = do_QueryInterface(htmlDoc->GetImageMap(mapElmName));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -537,6 +538,22 @@ nsAccessibilityService::UpdateText(nsIPresShell* aPresShell,
|
|||
document->UpdateText(aContent);
|
||||
}
|
||||
|
||||
void
|
||||
nsAccessibilityService::UpdateListBullet(nsIPresShell* aPresShell,
|
||||
nsIContent* aHTMLListItemContent,
|
||||
bool aHasBullet)
|
||||
{
|
||||
nsDocAccessible* document = GetDocAccessible(aPresShell->GetDocument());
|
||||
if (document) {
|
||||
nsAccessible* accessible = document->GetAccessible(aHTMLListItemContent);
|
||||
if (accessible) {
|
||||
nsHTMLLIAccessible* listItem = accessible->AsHTMLListItem();
|
||||
if (listItem)
|
||||
listItem->UpdateBullet(aHasBullet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsAccessibilityService::PresShellDestroyed(nsIPresShell *aPresShell)
|
||||
{
|
||||
|
|
|
@ -120,6 +120,13 @@ public:
|
|||
|
||||
virtual void UpdateText(nsIPresShell* aPresShell, nsIContent* aContent);
|
||||
|
||||
/**
|
||||
* Update list bullet accessible.
|
||||
*/
|
||||
virtual void UpdateListBullet(nsIPresShell* aPresShell,
|
||||
nsIContent* aHTMLListItemContent,
|
||||
bool aHasBullet);
|
||||
|
||||
virtual void NotifyOfAnchorJumpTo(nsIContent *aTarget);
|
||||
|
||||
virtual void PresShellDestroyed(nsIPresShell* aPresShell);
|
||||
|
|
|
@ -765,24 +765,22 @@ nsAccessible::GetFocusedChild(nsIAccessible **aFocusedChild)
|
|||
}
|
||||
|
||||
// nsAccessible::GetChildAtPoint()
|
||||
nsresult
|
||||
nsAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY, PRBool aDeepestChild,
|
||||
nsIAccessible **aChild)
|
||||
nsAccessible*
|
||||
nsAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
EWhichChildAtPoint aWhichChild)
|
||||
{
|
||||
// If we can't find the point in a child, we will return the fallback answer:
|
||||
// we return |this| if the point is within it, otherwise nsnull.
|
||||
PRInt32 x = 0, y = 0, width = 0, height = 0;
|
||||
nsresult rv = GetBounds(&x, &y, &width, &height);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
nsCOMPtr<nsIAccessible> fallbackAnswer;
|
||||
nsAccessible* fallbackAnswer = nsnull;
|
||||
if (aX >= x && aX < x + width && aY >= y && aY < y + height)
|
||||
fallbackAnswer = this;
|
||||
|
||||
if (nsAccUtils::MustPrune(this)) { // Do not dig any further
|
||||
NS_IF_ADDREF(*aChild = fallbackAnswer);
|
||||
return NS_OK;
|
||||
}
|
||||
if (nsAccUtils::MustPrune(this)) // Do not dig any further
|
||||
return fallbackAnswer;
|
||||
|
||||
// Search an accessible at the given point starting from accessible document
|
||||
// because containing block (see CSS2) for out of flow element (for example,
|
||||
|
@ -791,10 +789,10 @@ nsAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY, PRBool aDeepestChild,
|
|||
// for DOM parent but GetFrameForPoint() should be called for containing block
|
||||
// to get an out of flow element.
|
||||
nsDocAccessible *accDocument = GetDocAccessible();
|
||||
NS_ENSURE_TRUE(accDocument, NS_ERROR_FAILURE);
|
||||
NS_ENSURE_TRUE(accDocument, nsnull);
|
||||
|
||||
nsIFrame *frame = accDocument->GetFrame();
|
||||
NS_ENSURE_STATE(frame);
|
||||
NS_ENSURE_TRUE(frame, nsnull);
|
||||
|
||||
nsPresContext *presContext = frame->PresContext();
|
||||
|
||||
|
@ -806,19 +804,15 @@ nsAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY, PRBool aDeepestChild,
|
|||
nsIFrame *foundFrame = presShell->GetFrameForPoint(frame, offset);
|
||||
|
||||
nsIContent* content = nsnull;
|
||||
if (!foundFrame || !(content = foundFrame->GetContent())) {
|
||||
NS_IF_ADDREF(*aChild = fallbackAnswer);
|
||||
return NS_OK;
|
||||
}
|
||||
if (!foundFrame || !(content = foundFrame->GetContent()))
|
||||
return fallbackAnswer;
|
||||
|
||||
// Get accessible for the node with the point or the first accessible in
|
||||
// the DOM parent chain.
|
||||
nsAccessible* accessible =
|
||||
GetAccService()->GetAccessibleOrContainer(content, mWeakShell);
|
||||
if (!accessible) {
|
||||
NS_IF_ADDREF(*aChild = fallbackAnswer);
|
||||
return NS_OK;
|
||||
}
|
||||
if (!accessible)
|
||||
return fallbackAnswer;
|
||||
|
||||
if (accessible == this) {
|
||||
// Manually walk through accessible children and see if the are within this
|
||||
|
@ -836,40 +830,36 @@ nsAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY, PRBool aDeepestChild,
|
|||
aY >= childY && aY < childY + childHeight &&
|
||||
(nsAccUtils::State(child) & nsIAccessibleStates::STATE_INVISIBLE) == 0) {
|
||||
|
||||
if (aDeepestChild)
|
||||
return child->GetDeepestChildAtPoint(aX, aY, aChild);
|
||||
if (aWhichChild == eDeepestChild)
|
||||
return child->GetChildAtPoint(aX, aY, eDeepestChild);
|
||||
|
||||
NS_IF_ADDREF(*aChild = child);
|
||||
return NS_OK;
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
// The point is in this accessible but not in a child. We are allowed to
|
||||
// return |this| as the answer.
|
||||
NS_IF_ADDREF(*aChild = accessible);
|
||||
return NS_OK;
|
||||
return accessible;
|
||||
}
|
||||
|
||||
// Since DOM node of obtained accessible may be out of flow then we should
|
||||
// ensure obtained accessible is a child of this accessible.
|
||||
nsCOMPtr<nsIAccessible> parent, child(accessible);
|
||||
while (PR_TRUE) {
|
||||
child->GetParent(getter_AddRefs(parent));
|
||||
nsAccessible* child = accessible;
|
||||
while (true) {
|
||||
nsAccessible* parent = child->GetParent();
|
||||
if (!parent) {
|
||||
// Reached the top of the hierarchy. These bounds were inside an
|
||||
// accessible that is not a descendant of this one.
|
||||
NS_IF_ADDREF(*aChild = fallbackAnswer);
|
||||
return NS_OK;
|
||||
return fallbackAnswer;
|
||||
}
|
||||
|
||||
if (parent == this) {
|
||||
NS_ADDREF(*aChild = (aDeepestChild ? accessible : child));
|
||||
return NS_OK;
|
||||
}
|
||||
child.swap(parent);
|
||||
if (parent == this)
|
||||
return aWhichChild == eDeepestChild ? accessible : child;
|
||||
|
||||
child = parent;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
// nsIAccessible getChildAtPoint(in long x, in long y)
|
||||
|
@ -883,7 +873,8 @@ nsAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
|||
if (IsDefunct())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
return GetChildAtPoint(aX, aY, PR_FALSE, aAccessible);
|
||||
NS_IF_ADDREF(*aAccessible = GetChildAtPoint(aX, aY, eDirectChild));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIAccessible getDeepestChildAtPoint(in long x, in long y)
|
||||
|
@ -897,7 +888,8 @@ nsAccessible::GetDeepestChildAtPoint(PRInt32 aX, PRInt32 aY,
|
|||
if (IsDefunct())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
return GetChildAtPoint(aX, aY, PR_TRUE, aAccessible);
|
||||
NS_IF_ADDREF(*aAccessible = GetChildAtPoint(aX, aY, eDeepestChild));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void nsAccessible::GetBoundsRect(nsRect& aTotalBounds, nsIFrame** aBoundingFrame)
|
||||
|
@ -2716,7 +2708,7 @@ nsAccessible::BindToParent(nsAccessible* aParent, PRUint32 aIndexInParent)
|
|||
if (mParent) {
|
||||
if (mParent != aParent) {
|
||||
NS_ERROR("Adopting child!");
|
||||
mParent->InvalidateChildren();
|
||||
mParent->RemoveChild(this);
|
||||
} else {
|
||||
NS_ERROR("Binding to the same parent!");
|
||||
return;
|
||||
|
@ -3228,15 +3220,37 @@ nsAccessible::GetSiblingAtOffset(PRInt32 aOffset, nsresult* aError)
|
|||
nsAccessible *
|
||||
nsAccessible::GetFirstAvailableAccessible(nsINode *aStartNode) const
|
||||
{
|
||||
nsAccessible *accessible =
|
||||
nsAccessible* accessible =
|
||||
GetAccService()->GetAccessibleInWeakShell(aStartNode, mWeakShell);
|
||||
if (accessible)
|
||||
return accessible;
|
||||
|
||||
nsIContent *content = nsCoreUtils::GetRoleContent(aStartNode);
|
||||
nsAccTreeWalker walker(mWeakShell, content, PR_FALSE);
|
||||
nsRefPtr<nsAccessible> childAccessible = walker.GetNextChild();
|
||||
return childAccessible;
|
||||
nsCOMPtr<nsIDOMDocumentTraversal> trav =
|
||||
do_QueryInterface(aStartNode->GetOwnerDoc());
|
||||
NS_ENSURE_TRUE(trav, nsnull);
|
||||
|
||||
nsCOMPtr<nsIDOMNode> currentNode = do_QueryInterface(aStartNode);
|
||||
nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(GetNode()));
|
||||
nsCOMPtr<nsIDOMTreeWalker> walker;
|
||||
trav->CreateTreeWalker(rootNode,
|
||||
nsIDOMNodeFilter::SHOW_ELEMENT | nsIDOMNodeFilter::SHOW_TEXT,
|
||||
nsnull, PR_FALSE, getter_AddRefs(walker));
|
||||
NS_ENSURE_TRUE(walker, nsnull);
|
||||
|
||||
walker->SetCurrentNode(currentNode);
|
||||
while (true) {
|
||||
walker->NextNode(getter_AddRefs(currentNode));
|
||||
if (!currentNode)
|
||||
return nsnull;
|
||||
|
||||
nsCOMPtr<nsINode> node(do_QueryInterface(currentNode));
|
||||
nsAccessible* accessible =
|
||||
GetAccService()->GetAccessibleInWeakShell(node, mWeakShell);
|
||||
if (accessible)
|
||||
return accessible;
|
||||
}
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
PRBool nsAccessible::CheckVisibilityInParentChain(nsIDocument* aDocument, nsIView* aView)
|
||||
|
|
|
@ -58,6 +58,7 @@ class AccGroupInfo;
|
|||
class EmbeddedObjCollector;
|
||||
class nsAccessible;
|
||||
class nsHyperTextAccessible;
|
||||
class nsHTMLLIAccessible;
|
||||
struct nsRoleMapEntry;
|
||||
class nsTextAccessible;
|
||||
|
||||
|
@ -188,17 +189,24 @@ public:
|
|||
*/
|
||||
virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
|
||||
|
||||
/**
|
||||
* Used by GetChildAtPoint() method to get direct or deepest child at point.
|
||||
*/
|
||||
enum EWhichChildAtPoint {
|
||||
eDirectChild,
|
||||
eDeepestChild
|
||||
};
|
||||
|
||||
/**
|
||||
* Return direct or deepest child at the given point.
|
||||
*
|
||||
* @param aX [in] x coordinate relative screen
|
||||
* @param aY [in] y coordinate relative screen
|
||||
* @param aDeepestChild [in] flag points if deep child should be returned
|
||||
* @param aChild [out] found child
|
||||
* @param aX [in] x coordinate relative screen
|
||||
* @param aY [in] y coordinate relative screen
|
||||
* @param aWhichChild [in] flag points if deepest or direct child
|
||||
* should be returned
|
||||
*/
|
||||
virtual nsresult GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
PRBool aDeepestChild,
|
||||
nsIAccessible **aChild);
|
||||
virtual nsAccessible* GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
EWhichChildAtPoint aWhichChild);
|
||||
|
||||
/**
|
||||
* Return calculated group level based on accessible hierarchy.
|
||||
|
@ -364,6 +372,9 @@ public:
|
|||
inline bool IsHyperText() const { return mFlags & eHyperTextAccessible; }
|
||||
nsHyperTextAccessible* AsHyperText();
|
||||
|
||||
inline bool IsHTMLListItem() const { return mFlags & eHTMLListItemAccessible; }
|
||||
nsHTMLLIAccessible* AsHTMLListItem();
|
||||
|
||||
inline bool IsRoot() const { return mFlags & eRootAccessible; }
|
||||
nsRootAccessible* AsRoot();
|
||||
|
||||
|
@ -512,8 +523,9 @@ protected:
|
|||
enum AccessibleTypes {
|
||||
eApplicationAccessible = 1 << 2,
|
||||
eHyperTextAccessible = 1 << 3,
|
||||
eRootAccessible = 1 << 4,
|
||||
eTextLeafAccessible = 1 << 5
|
||||
eHTMLListItemAccessible = 1 << 4,
|
||||
eRootAccessible = 1 << 5,
|
||||
eTextLeafAccessible = 1 << 6
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -170,22 +170,11 @@ nsApplicationAccessible::GroupPosition(PRInt32 *aGroupLevel,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAccessible*
|
||||
nsApplicationAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
nsIAccessible **aChild)
|
||||
EWhichChildAtPoint aWhichChild)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aChild);
|
||||
*aChild = nsnull;
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsApplicationAccessible::GetDeepestChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
nsIAccessible **aChild)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aChild);
|
||||
*aChild = nsnull;
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -98,8 +98,6 @@ public:
|
|||
NS_IMETHOD GetAttributes(nsIPersistentProperties **aAttributes);
|
||||
NS_IMETHOD GroupPosition(PRInt32 *aGroupLevel, PRInt32 *aSimilarItemsInGroup,
|
||||
PRInt32 *aPositionInGroup);
|
||||
NS_IMETHOD GetChildAtPoint(PRInt32 aX, PRInt32 aY, nsIAccessible **aChild);
|
||||
NS_IMETHOD GetDeepestChildAtPoint(PRInt32 aX, PRInt32 aY, nsIAccessible **aChild);
|
||||
NS_IMETHOD GetRelationByType(PRUint32 aRelationType,
|
||||
nsIAccessibleRelation **aRelation);
|
||||
NS_IMETHOD GetRelationsCount(PRUint32 *aRelationsCount);
|
||||
|
@ -128,6 +126,8 @@ public:
|
|||
virtual nsresult GetARIAState(PRUint32 *aState, PRUint32 *aExtraState);
|
||||
virtual PRUint32 NativeRole();
|
||||
virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
|
||||
virtual nsAccessible* GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
EWhichChildAtPoint aWhichChild);
|
||||
|
||||
virtual void InvalidateChildren();
|
||||
|
||||
|
|
|
@ -67,14 +67,12 @@ NS_IMPL_ISUPPORTS_INHERITED0(nsLeafAccessible, nsAccessible)
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsLeafAccessible: nsAccessible public
|
||||
|
||||
nsresult
|
||||
nsAccessible*
|
||||
nsLeafAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
PRBool aDeepestChild,
|
||||
nsIAccessible **aChild)
|
||||
EWhichChildAtPoint aWhichChild)
|
||||
{
|
||||
// Don't walk into leaf accessibles.
|
||||
NS_ADDREF(*aChild = this);
|
||||
return NS_OK;
|
||||
return this;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -63,9 +63,8 @@ public:
|
|||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// nsAccessible
|
||||
virtual nsresult GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
PRBool aDeepestChild,
|
||||
nsIAccessible **aChild);
|
||||
virtual nsAccessible* GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
EWhichChildAtPoint aWhichChild);
|
||||
|
||||
protected:
|
||||
|
||||
|
|
|
@ -650,6 +650,11 @@ nsDocAccessible::Shutdown()
|
|||
|
||||
RemoveEventListeners();
|
||||
|
||||
// Mark the document as shutdown before AT is notified about the document
|
||||
// removal from its container (valid for root documents on ATK).
|
||||
nsCOMPtr<nsIDocument> kungFuDeathGripDoc = mDocument;
|
||||
mDocument = nsnull;
|
||||
|
||||
if (mParent) {
|
||||
nsDocAccessible* parentDocument = mParent->GetDocAccessible();
|
||||
if (parentDocument)
|
||||
|
@ -672,9 +677,6 @@ nsDocAccessible::Shutdown()
|
|||
mNodeToAccessibleMap.Clear();
|
||||
ClearCache(mAccessibleCache);
|
||||
|
||||
nsCOMPtr<nsIDocument> kungFuDeathGripDoc = mDocument;
|
||||
mDocument = nsnull;
|
||||
|
||||
nsHyperTextAccessibleWrap::Shutdown();
|
||||
|
||||
GetAccService()->NotifyOfDocumentShutdown(kungFuDeathGripDoc);
|
||||
|
@ -1229,19 +1231,17 @@ void nsDocAccessible::ContentAppended(nsIDocument *aDocument,
|
|||
{
|
||||
}
|
||||
|
||||
void nsDocAccessible::ContentStatesChanged(nsIDocument* aDocument,
|
||||
nsIContent* aContent1,
|
||||
nsIContent* aContent2,
|
||||
nsEventStates aStateMask)
|
||||
void nsDocAccessible::ContentStateChanged(nsIDocument* aDocument,
|
||||
nsIContent* aContent,
|
||||
nsEventStates aStateMask)
|
||||
{
|
||||
if (aStateMask.HasState(NS_EVENT_STATE_CHECKED)) {
|
||||
nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent1);
|
||||
nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent2);
|
||||
nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent);
|
||||
}
|
||||
|
||||
if (aStateMask.HasState(NS_EVENT_STATE_INVALID)) {
|
||||
nsRefPtr<AccEvent> event =
|
||||
new AccStateChangeEvent(aContent1, nsIAccessibleStates::STATE_INVALID,
|
||||
new AccStateChangeEvent(aContent, nsIAccessibleStates::STATE_INVALID,
|
||||
PR_FALSE, PR_TRUE);
|
||||
FireDelayedAccessibleEvent(event);
|
||||
}
|
||||
|
@ -1472,7 +1472,10 @@ nsDocAccessible::NotifyOfCachingEnd(nsAccessible* aAccessible)
|
|||
// Make sure we keep children updated. While we're inside of caching
|
||||
// loop then we must exist it with cached children.
|
||||
nsAccessible* container = GetContainerAccessible(content);
|
||||
container->UpdateChildren();
|
||||
NS_ASSERTION(container,
|
||||
"Got a referenced element that is not in document!");
|
||||
if (container)
|
||||
container->UpdateChildren();
|
||||
}
|
||||
}
|
||||
mInvalidationList.Clear();
|
||||
|
@ -1950,8 +1953,9 @@ nsDocAccessible::CacheChildrenInSubtree(nsAccessible* aRoot)
|
|||
PRUint32 count = aRoot->GetChildCount();
|
||||
for (PRUint32 idx = 0; idx < count; idx++) {
|
||||
nsAccessible* child = aRoot->GetChildAt(idx);
|
||||
NS_ASSERTION(child, "Illicit tree change while tree is created!");
|
||||
// Don't cross document boundaries.
|
||||
if (child->IsContent())
|
||||
if (child && child->IsContent())
|
||||
CacheChildrenInSubtree(child);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,32 +76,25 @@ nsOuterDocAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsAccessible*
|
||||
nsOuterDocAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
PRBool aDeepestChild,
|
||||
nsIAccessible **aChild)
|
||||
EWhichChildAtPoint aWhichChild)
|
||||
{
|
||||
PRInt32 docX = 0, docY = 0, docWidth = 0, docHeight = 0;
|
||||
nsresult rv = GetBounds(&docX, &docY, &docWidth, &docHeight);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
if (aX < docX || aX >= docX + docWidth || aY < docY || aY >= docY + docHeight)
|
||||
return NS_OK;
|
||||
return nsnull;
|
||||
|
||||
// Always return the inner doc as direct child accessible unless bounds
|
||||
// outside of it.
|
||||
nsCOMPtr<nsIAccessible> childAcc;
|
||||
rv = GetFirstChild(getter_AddRefs(childAcc));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsAccessible* child = GetChildAt(0);
|
||||
NS_ENSURE_TRUE(child, nsnull);
|
||||
|
||||
if (!childAcc)
|
||||
return NS_OK;
|
||||
|
||||
if (aDeepestChild)
|
||||
return childAcc->GetDeepestChildAtPoint(aX, aY, aChild);
|
||||
|
||||
NS_ADDREF(*aChild = childAcc);
|
||||
return NS_OK;
|
||||
if (aWhichChild = eDeepestChild)
|
||||
return child->GetChildAtPoint(aX, aY, eDeepestChild);
|
||||
return child;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
|
|
@ -70,9 +70,8 @@ public:
|
|||
virtual PRUint32 NativeRole();
|
||||
virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
|
||||
virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
|
||||
virtual nsresult GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
PRBool aDeepestChild,
|
||||
nsIAccessible **aChild);
|
||||
virtual nsAccessible* GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
EWhichChildAtPoint aWhichChild);
|
||||
|
||||
virtual void InvalidateChildren();
|
||||
virtual PRBool AppendChild(nsAccessible *aAccessible);
|
||||
|
|
|
@ -246,14 +246,12 @@ nsHTMLAreaAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState)
|
|||
return nsHTMLLinkAccessible::GetStateInternal(aState, aExtraState);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsAccessible*
|
||||
nsHTMLAreaAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
PRBool aDeepestChild,
|
||||
nsIAccessible **aChild)
|
||||
EWhichChildAtPoint aWhichChild)
|
||||
{
|
||||
// Don't walk into area accessibles.
|
||||
NS_ADDREF(*aChild = this);
|
||||
return NS_OK;
|
||||
return this;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -94,9 +94,8 @@ public:
|
|||
// nsAccessible
|
||||
virtual nsresult GetNameInternal(nsAString& aName);
|
||||
virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
|
||||
virtual nsresult GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
PRBool aDeepestChild,
|
||||
nsIAccessible **aChild);
|
||||
virtual nsAccessible* GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
EWhichChildAtPoint aWhichChild);
|
||||
|
||||
// HyperLinkAccessible
|
||||
virtual PRUint32 StartOffset();
|
||||
|
|
|
@ -254,12 +254,15 @@ nsHTMLOutputAccessible::GetAttributesInternal(nsIPersistentProperties* aAttribut
|
|||
|
||||
nsHTMLLIAccessible::
|
||||
nsHTMLLIAccessible(nsIContent* aContent, nsIWeakReference* aShell) :
|
||||
nsHyperTextAccessibleWrap(aContent, aShell)
|
||||
nsHyperTextAccessibleWrap(aContent, aShell), mBullet(nsnull)
|
||||
{
|
||||
mFlags |= eHTMLListItemAccessible;
|
||||
|
||||
nsBlockFrame* blockFrame = do_QueryFrame(GetFrame());
|
||||
if (blockFrame && !blockFrame->BulletIsEmptyExternal()) {
|
||||
mBulletAccessible = new nsHTMLListBulletAccessible(mContent, mWeakShell);
|
||||
GetDocAccessible()->BindToDocument(mBulletAccessible, nsnull);
|
||||
if (blockFrame && blockFrame->HasBullet()) {
|
||||
mBullet = new nsHTMLListBulletAccessible(mContent, mWeakShell);
|
||||
if (!GetDocAccessible()->BindToDocument(mBullet, nsnull))
|
||||
mBullet = nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -268,13 +271,9 @@ NS_IMPL_ISUPPORTS_INHERITED0(nsHTMLLIAccessible, nsHyperTextAccessible)
|
|||
void
|
||||
nsHTMLLIAccessible::Shutdown()
|
||||
{
|
||||
if (mBulletAccessible) {
|
||||
// Ensure that pointer to this is nulled out.
|
||||
mBulletAccessible->Shutdown();
|
||||
}
|
||||
mBullet = nsnull;
|
||||
|
||||
nsHyperTextAccessibleWrap::Shutdown();
|
||||
mBulletAccessible = nsnull;
|
||||
}
|
||||
|
||||
PRUint32
|
||||
|
@ -297,12 +296,11 @@ nsHTMLLIAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState)
|
|||
NS_IMETHODIMP nsHTMLLIAccessible::GetBounds(PRInt32 *x, PRInt32 *y, PRInt32 *width, PRInt32 *height)
|
||||
{
|
||||
nsresult rv = nsAccessibleWrap::GetBounds(x, y, width, height);
|
||||
if (NS_FAILED(rv) || !mBulletAccessible) {
|
||||
if (NS_FAILED(rv) || !mBullet)
|
||||
return rv;
|
||||
}
|
||||
|
||||
PRInt32 bulletX, bulletY, bulletWidth, bulletHeight;
|
||||
rv = mBulletAccessible->GetBounds(&bulletX, &bulletY, &bulletWidth, &bulletHeight);
|
||||
rv = mBullet->GetBounds(&bulletX, &bulletY, &bulletWidth, &bulletHeight);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*x = bulletX; // Move x coordinate of list item over to cover bullet as well
|
||||
|
@ -310,14 +308,41 @@ NS_IMETHODIMP nsHTMLLIAccessible::GetBounds(PRInt32 *x, PRInt32 *y, PRInt32 *wid
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLLIAccessible: public
|
||||
|
||||
void
|
||||
nsHTMLLIAccessible::UpdateBullet(bool aHasBullet)
|
||||
{
|
||||
if (aHasBullet == !!mBullet) {
|
||||
NS_NOTREACHED("Bullet and accessible are in sync already!");
|
||||
return;
|
||||
}
|
||||
|
||||
nsDocAccessible* document = GetDocAccessible();
|
||||
if (aHasBullet) {
|
||||
mBullet = new nsHTMLListBulletAccessible(mContent, mWeakShell);
|
||||
if (document->BindToDocument(mBullet, nsnull)) {
|
||||
InsertChildAt(0, mBullet);
|
||||
}
|
||||
} else {
|
||||
RemoveChild(mBullet);
|
||||
document->UnbindFromDocument(mBullet);
|
||||
mBullet = nsnull;
|
||||
}
|
||||
|
||||
// XXXtodo: fire show/hide and reorder events. That's hard to make it
|
||||
// right now because coalescence happens by DOM node.
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLLIAccessible: nsAccessible protected
|
||||
|
||||
void
|
||||
nsHTMLLIAccessible::CacheChildren()
|
||||
{
|
||||
if (mBulletAccessible)
|
||||
AppendChild(mBulletAccessible);
|
||||
if (mBullet)
|
||||
AppendChild(mBullet);
|
||||
|
||||
// Cache children from subtree.
|
||||
nsAccessibleWrap::CacheChildren();
|
||||
|
@ -331,19 +356,11 @@ nsHTMLListBulletAccessible::
|
|||
nsHTMLListBulletAccessible(nsIContent* aContent, nsIWeakReference* aShell) :
|
||||
nsLeafAccessible(aContent, aShell)
|
||||
{
|
||||
mBulletText += ' '; // Otherwise bullets are jammed up against list text
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLListBulletAccessible: nsAccessNode
|
||||
|
||||
void
|
||||
nsHTMLListBulletAccessible::Shutdown()
|
||||
{
|
||||
mBulletText.Truncate();
|
||||
nsLeafAccessible::Shutdown();
|
||||
}
|
||||
|
||||
bool
|
||||
nsHTMLListBulletAccessible::IsPrimaryForNode() const
|
||||
{
|
||||
|
@ -363,6 +380,7 @@ nsHTMLListBulletAccessible::GetName(nsAString &aName)
|
|||
|
||||
// Native anonymous content, ARIA can't be used. Get list bullet text.
|
||||
nsBlockFrame* blockFrame = do_QueryFrame(mContent->GetPrimaryFrame());
|
||||
NS_ASSERTION(blockFrame, "No frame for list item!");
|
||||
if (blockFrame) {
|
||||
blockFrame->GetBulletText(aName);
|
||||
|
||||
|
@ -394,17 +412,13 @@ void
|
|||
nsHTMLListBulletAccessible::AppendTextTo(nsAString& aText, PRUint32 aStartOffset,
|
||||
PRUint32 aLength)
|
||||
{
|
||||
nsAutoString bulletText;
|
||||
nsBlockFrame* blockFrame = do_QueryFrame(mContent->GetPrimaryFrame());
|
||||
if (blockFrame) {
|
||||
nsAutoString bulletText;
|
||||
NS_ASSERTION(blockFrame, "No frame for list item!");
|
||||
if (blockFrame)
|
||||
blockFrame->GetBulletText(bulletText);
|
||||
|
||||
PRUint32 maxLength = bulletText.Length() - aStartOffset;
|
||||
if (aLength > maxLength)
|
||||
aLength = maxLength;
|
||||
|
||||
aText += Substring(bulletText, aStartOffset, aLength);
|
||||
}
|
||||
aText.Append(Substring(bulletText, aStartOffset, aLength));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -136,7 +136,6 @@ public:
|
|||
NS_IMETHOD GetName(nsAString& aName);
|
||||
|
||||
// nsAccessNode
|
||||
virtual void Shutdown();
|
||||
virtual bool IsPrimaryForNode() const;
|
||||
|
||||
// nsAccessible
|
||||
|
@ -144,15 +143,6 @@ public:
|
|||
virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
|
||||
virtual void AppendTextTo(nsAString& aText, PRUint32 aStartOffset = 0,
|
||||
PRUint32 aLength = PR_UINT32_MAX);
|
||||
|
||||
protected:
|
||||
// XXX: Ideally we'd get the bullet text directly from the bullet frame via
|
||||
// nsBulletFrame::GetListItemText(), but we'd need an interface for getting
|
||||
// text from contentless anonymous frames. Perhaps something like
|
||||
// nsIAnonymousFrame::GetText() ? However, in practice storing the bullet text
|
||||
// here should not be a problem if we invalidate the right parts of
|
||||
// the accessibility cache when mutation events occur.
|
||||
nsString mBulletText;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -182,22 +172,32 @@ public:
|
|||
// nsISupports
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// nsIAccessible
|
||||
NS_IMETHOD GetBounds(PRInt32 *x, PRInt32 *y, PRInt32 *width, PRInt32 *height);
|
||||
|
||||
// nsAccessNode
|
||||
virtual void Shutdown();
|
||||
|
||||
// nsIAccessible
|
||||
NS_IMETHOD GetBounds(PRInt32 *x, PRInt32 *y, PRInt32 *width, PRInt32 *height);
|
||||
|
||||
// nsAccessible
|
||||
virtual PRUint32 NativeRole();
|
||||
virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
|
||||
|
||||
// nsHTMLLIAccessible
|
||||
void UpdateBullet(bool aHasBullet);
|
||||
|
||||
protected:
|
||||
// nsAccessible
|
||||
virtual void CacheChildren();
|
||||
|
||||
private:
|
||||
nsRefPtr<nsHTMLListBulletAccessible> mBulletAccessible;
|
||||
nsRefPtr<nsHTMLListBulletAccessible> mBullet;
|
||||
};
|
||||
|
||||
#endif
|
||||
inline nsHTMLLIAccessible*
|
||||
nsAccessible::AsHTMLListItem()
|
||||
{
|
||||
return mFlags & eHTMLListItemAccessible ?
|
||||
static_cast<nsHTMLLIAccessible*>(this) : nsnull;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -195,11 +195,8 @@ nsHyperTextAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState)
|
|||
*aState |= nsIAccessibleStates::STATE_READONLY;
|
||||
}
|
||||
|
||||
PRInt32 childCount;
|
||||
GetChildCount(&childCount);
|
||||
if (childCount > 0) {
|
||||
if (GetChildCount() > 0)
|
||||
*aExtraState |= nsIAccessibleStates::EXT_STATE_SELECTABLE_TEXT;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -308,7 +308,7 @@ STDMETHODIMP nsAccessNodeWrap::get_attributesForNames(
|
|||
/* [length_is][size_is][retval] */ BSTR __RPC_FAR *aAttribValues)
|
||||
{
|
||||
__try {
|
||||
if (IsDefunct() || IsDocument())
|
||||
if (IsDefunct() || !IsElement())
|
||||
return E_FAIL;
|
||||
|
||||
nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(mContent));
|
||||
|
@ -616,12 +616,7 @@ void nsAccessNodeWrap::InitAccessibility()
|
|||
|
||||
DoATSpecificProcessing();
|
||||
|
||||
// Register window class that'll be used for document accessibles associated
|
||||
// with tabs.
|
||||
if (nsWinUtils::IsWindowEmulationEnabled()) {
|
||||
nsWinUtils::RegisterNativeWindow(kClassNameTabContent);
|
||||
sHWNDCache.Init(4);
|
||||
}
|
||||
nsWinUtils::MaybeStartWindowEmulation();
|
||||
|
||||
nsAccessNode::InitXPAccessibility();
|
||||
}
|
||||
|
@ -631,10 +626,7 @@ void nsAccessNodeWrap::ShutdownAccessibility()
|
|||
NS_IF_RELEASE(gTextEvent);
|
||||
::DestroyCaret();
|
||||
|
||||
// Unregister window call that's used for document accessibles associated
|
||||
// with tabs.
|
||||
if (nsWinUtils::IsWindowEmulationEnabled())
|
||||
::UnregisterClassW(kClassNameTabContent, GetModuleHandle(NULL));
|
||||
nsWinUtils::ShutdownWindowEmulation();
|
||||
|
||||
nsAccessNode::ShutdownXPAccessibility();
|
||||
}
|
||||
|
|
|
@ -203,7 +203,7 @@ __try {
|
|||
// Return window system accessible object for root document and tab document
|
||||
// accessibles.
|
||||
if (!doc->ParentDocument() ||
|
||||
nsWinUtils::IsWindowEmulationEnabled() &&
|
||||
nsWinUtils::IsWindowEmulationStarted() &&
|
||||
nsWinUtils::IsTabDocument(doc->GetDocumentNode())) {
|
||||
HWND hwnd = static_cast<HWND>(doc->GetNativeWindow());
|
||||
if (hwnd && SUCCEEDED(AccessibleObjectFromWindow(hwnd, OBJID_WINDOW,
|
||||
|
@ -236,9 +236,7 @@ __try {
|
|||
if (nsAccUtils::MustPrune(this))
|
||||
return NS_OK;
|
||||
|
||||
PRInt32 numChildren;
|
||||
GetChildCount(&numChildren);
|
||||
*pcountChildren = numChildren;
|
||||
*pcountChildren = GetChildCount();
|
||||
} __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
|
||||
|
||||
return S_OK;
|
||||
|
@ -1014,9 +1012,7 @@ __try {
|
|||
|
||||
mEnumVARIANTPosition += aNumElements;
|
||||
|
||||
PRInt32 numChildren;
|
||||
GetChildCount(&numChildren);
|
||||
|
||||
PRInt32 numChildren = GetChildCount();
|
||||
if (mEnumVARIANTPosition > numChildren)
|
||||
{
|
||||
mEnumVARIANTPosition = numChildren;
|
||||
|
|
|
@ -255,10 +255,11 @@ STDMETHODIMP nsDocAccessibleWrap::get_accValue(
|
|||
void
|
||||
nsDocAccessibleWrap::Shutdown()
|
||||
{
|
||||
if (nsWinUtils::IsWindowEmulationEnabled()) {
|
||||
// Do window emulation specific shutdown if emulation was started.
|
||||
if (nsWinUtils::IsWindowEmulationStarted()) {
|
||||
// Destroy window created for root document.
|
||||
if (nsWinUtils::IsTabDocument(mDocument)) {
|
||||
nsAccessibleWrap::sHWNDCache.Remove(mHWND);
|
||||
sHWNDCache.Remove(mHWND);
|
||||
::DestroyWindow(static_cast<HWND>(mHWND));
|
||||
}
|
||||
|
||||
|
@ -285,14 +286,14 @@ nsDocAccessibleWrap::NotifyOfInitialUpdate()
|
|||
{
|
||||
nsDocAccessible::NotifyOfInitialUpdate();
|
||||
|
||||
if (nsWinUtils::IsWindowEmulationEnabled()) {
|
||||
if (nsWinUtils::IsWindowEmulationStarted()) {
|
||||
// Create window for tab document.
|
||||
if (nsWinUtils::IsTabDocument(mDocument)) {
|
||||
nsRootAccessible* rootDocument = RootAccessible();
|
||||
|
||||
PRBool isActive = PR_TRUE;
|
||||
PRInt32 x = CW_USEDEFAULT, y = CW_USEDEFAULT, width = 0, height = 0;
|
||||
if (nsWinUtils::IsWindowEmulationEnabled(kDolphinModuleHandle)) {
|
||||
if (nsWinUtils::IsWindowEmulationFor(kDolphinModuleHandle)) {
|
||||
GetBounds(&x, &y, &width, &height);
|
||||
PRInt32 rootX = 0, rootY = 0, rootWidth = 0, rootHeight = 0;
|
||||
rootDocument->GetBounds(&rootX, &rootY, &rootWidth, &rootHeight);
|
||||
|
@ -308,7 +309,7 @@ nsDocAccessibleWrap::NotifyOfInitialUpdate()
|
|||
mHWND = nsWinUtils::CreateNativeWindow(kClassNameTabContent, parentWnd,
|
||||
x, y, width, height, isActive);
|
||||
|
||||
nsAccessibleWrap::sHWNDCache.Put(mHWND, this);
|
||||
sHWNDCache.Put(mHWND, this);
|
||||
|
||||
} else {
|
||||
nsDocAccessible* parentDocument = ParentDocument();
|
||||
|
|
|
@ -63,7 +63,7 @@ nsRootAccessibleWrap::~nsRootAccessibleWrap()
|
|||
void
|
||||
nsRootAccessibleWrap::DocumentActivated(nsDocAccessible* aDocument)
|
||||
{
|
||||
if (nsWinUtils::IsWindowEmulationEnabled(kDolphinModuleHandle) &&
|
||||
if (nsWinUtils::IsWindowEmulationFor(kDolphinModuleHandle) &&
|
||||
nsWinUtils::IsTabDocument(aDocument->GetDocumentNode())) {
|
||||
PRUint32 count = mChildDocuments.Length();
|
||||
for (PRUint32 idx = 0; idx < count; idx++) {
|
||||
|
|
|
@ -99,6 +99,34 @@ nsWinUtils::ConvertToIA2Array(nsIArray *aGeckoArray, IUnknown ***aIA2Array,
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsWinUtils::MaybeStartWindowEmulation()
|
||||
{
|
||||
// Register window class that'll be used for document accessibles associated
|
||||
// with tabs.
|
||||
if (IsWindowEmulationFor(0)) {
|
||||
RegisterNativeWindow(kClassNameTabContent);
|
||||
nsAccessNodeWrap::sHWNDCache.Init(4);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
nsWinUtils::ShutdownWindowEmulation()
|
||||
{
|
||||
// Unregister window call that's used for document accessibles associated
|
||||
// with tabs.
|
||||
if (IsWindowEmulationFor(0))
|
||||
::UnregisterClassW(kClassNameTabContent, GetModuleHandle(NULL));
|
||||
}
|
||||
|
||||
bool
|
||||
nsWinUtils::IsWindowEmulationStarted()
|
||||
{
|
||||
return nsAccessNodeWrap::sHWNDCache.IsInitialized();
|
||||
}
|
||||
|
||||
void
|
||||
nsWinUtils::RegisterNativeWindow(LPCWSTR aWindowClass)
|
||||
{
|
||||
|
@ -146,7 +174,7 @@ nsWinUtils::HideNativeWindow(HWND aWnd)
|
|||
}
|
||||
|
||||
bool
|
||||
nsWinUtils::IsWindowEmulationEnabled(LPCWSTR kModuleHandle)
|
||||
nsWinUtils::IsWindowEmulationFor(LPCWSTR kModuleHandle)
|
||||
{
|
||||
return kModuleHandle ? ::GetModuleHandleW(kModuleHandle) :
|
||||
::GetModuleHandleW(kJAWSModuleHandle) ||
|
||||
|
|
|
@ -63,6 +63,21 @@ public:
|
|||
static HRESULT ConvertToIA2Array(nsIArray *aCollection,
|
||||
IUnknown ***aAccessibles, long *aCount);
|
||||
|
||||
/**
|
||||
* Start window emulation if presence of specific AT is detected.
|
||||
*/
|
||||
static bool MaybeStartWindowEmulation();
|
||||
|
||||
/**
|
||||
* Free resources used for window emulation.
|
||||
*/
|
||||
static void ShutdownWindowEmulation();
|
||||
|
||||
/**
|
||||
* Return true if window emulation is started.
|
||||
*/
|
||||
static bool IsWindowEmulationStarted();
|
||||
|
||||
/**
|
||||
* Helper to register window class.
|
||||
*/
|
||||
|
@ -88,7 +103,7 @@ public:
|
|||
/**
|
||||
* Return true if window emulation is enabled.
|
||||
*/
|
||||
static bool IsWindowEmulationEnabled(LPCWSTR kModuleHandle = 0);
|
||||
static bool IsWindowEmulationFor(LPCWSTR kModuleHandle);
|
||||
|
||||
/**
|
||||
* Return true if the given document node is for tab document accessible.
|
||||
|
|
|
@ -228,20 +228,19 @@ nsXULTreeAccessible::GetFocusedChild(nsIAccessible **aFocusedChild)
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsXULTreeAccessible: nsAccessible implementation (DON'T put methods here)
|
||||
|
||||
nsresult
|
||||
nsAccessible*
|
||||
nsXULTreeAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
PRBool aDeepestChild,
|
||||
nsIAccessible **aChild)
|
||||
EWhichChildAtPoint aWhichChild)
|
||||
{
|
||||
nsIFrame *frame = GetFrame();
|
||||
if (!frame)
|
||||
return NS_ERROR_FAILURE;
|
||||
return nsnull;
|
||||
|
||||
nsPresContext *presContext = frame->PresContext();
|
||||
nsCOMPtr<nsIPresShell> presShell = presContext->PresShell();
|
||||
|
||||
nsIFrame *rootFrame = presShell->GetRootFrame();
|
||||
NS_ENSURE_STATE(rootFrame);
|
||||
NS_ENSURE_TRUE(rootFrame, nsnull);
|
||||
|
||||
nsIntRect rootRect = rootFrame->GetScreenRectExternal();
|
||||
|
||||
|
@ -257,10 +256,10 @@ nsXULTreeAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
|||
// If we failed to find tree cell for the given point then it might be
|
||||
// tree columns.
|
||||
if (row == -1 || !column)
|
||||
return nsAccessibleWrap::GetChildAtPoint(aX, aY, aDeepestChild, aChild);
|
||||
return nsAccessibleWrap::GetChildAtPoint(aX, aY, aWhichChild);
|
||||
|
||||
nsAccessible *child = GetTreeItemAccessible(row);
|
||||
if (aDeepestChild && child) {
|
||||
if (aWhichChild == eDeepestChild && child) {
|
||||
// Look for accessible cell for the found item accessible.
|
||||
nsRefPtr<nsXULTreeItemAccessibleBase> treeitem = do_QueryObject(child);
|
||||
|
||||
|
@ -269,8 +268,7 @@ nsXULTreeAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
|||
child = cell;
|
||||
}
|
||||
|
||||
NS_IF_ADDREF(*aChild = child);
|
||||
return NS_OK;
|
||||
return child;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -86,9 +86,8 @@ public:
|
|||
// nsAccessible
|
||||
virtual PRUint32 NativeRole();
|
||||
virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
|
||||
virtual nsresult GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
PRBool aDeepestChild,
|
||||
nsIAccessible **aChild);
|
||||
virtual nsAccessible* GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
EWhichChildAtPoint aWhichChild);
|
||||
|
||||
virtual nsAccessible* GetChildAt(PRUint32 aIndex);
|
||||
virtual PRInt32 GetChildCount();
|
||||
|
|
|
@ -664,20 +664,19 @@ nsXULTreeGridRowAccessible::NativeRole()
|
|||
return nsIAccessibleRole::ROLE_ROW;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsAccessible*
|
||||
nsXULTreeGridRowAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
PRBool aDeepestChild,
|
||||
nsIAccessible **aChild)
|
||||
EWhichChildAtPoint aWhichChild)
|
||||
{
|
||||
nsIFrame *frame = GetFrame();
|
||||
if (!frame)
|
||||
return NS_ERROR_FAILURE;
|
||||
return nsnull;
|
||||
|
||||
nsPresContext *presContext = frame->PresContext();
|
||||
nsCOMPtr<nsIPresShell> presShell = presContext->PresShell();
|
||||
|
||||
nsIFrame *rootFrame = presShell->GetRootFrame();
|
||||
NS_ENSURE_STATE(rootFrame);
|
||||
NS_ENSURE_TRUE(rootFrame, nsnull);
|
||||
|
||||
nsIntRect rootRect = rootFrame->GetScreenRectExternal();
|
||||
|
||||
|
@ -692,10 +691,9 @@ nsXULTreeGridRowAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
|||
|
||||
// Return if we failed to find tree cell in the row for the given point.
|
||||
if (row != mRow || !column)
|
||||
return NS_OK;
|
||||
return nsnull;
|
||||
|
||||
NS_IF_ADDREF(*aChild = GetCellAccessible(column));
|
||||
return NS_OK;
|
||||
return GetCellAccessible(column);
|
||||
}
|
||||
|
||||
nsAccessible*
|
||||
|
|
|
@ -93,9 +93,8 @@ public:
|
|||
|
||||
// nsAccessible
|
||||
virtual PRUint32 NativeRole();
|
||||
virtual nsresult GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
PRBool aDeepestChild,
|
||||
nsIAccessible **aChild);
|
||||
virtual nsAccessible* GetChildAtPoint(PRInt32 aX, PRInt32 aY,
|
||||
EWhichChildAtPoint aWhichChild);
|
||||
|
||||
virtual nsAccessible* GetChildAt(PRUint32 aIndex);
|
||||
virtual PRInt32 GetChildCount();
|
||||
|
|
|
@ -64,6 +64,10 @@ const STATE_BUSY = nsIAccessibleStates.STATE_BUSY;
|
|||
|
||||
const kEmbedChar = String.fromCharCode(0xfffc);
|
||||
|
||||
const kDiscBulletText = String.fromCharCode(0x2022) + " ";
|
||||
const kCircleBulletText = String.fromCharCode(0x25e6) + " ";
|
||||
const kSquareBulletText = String.fromCharCode(0x25aa) + " ";
|
||||
|
||||
/**
|
||||
* nsIAccessibleRetrieval, initialized when test is loaded.
|
||||
*/
|
||||
|
|
|
@ -54,6 +54,7 @@ _TEST_FILES =\
|
|||
test_general.html \
|
||||
test_general.xul \
|
||||
test_link.html \
|
||||
test_list.html \
|
||||
test_markup.html \
|
||||
test_nsRootAcc.xul \
|
||||
markuprules.xml \
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<title>nsIAccessible::name calculation for HTML li</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"
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../name.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../events.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
/**
|
||||
* Alter list item numbering and change list style type.
|
||||
*/
|
||||
function bulletUpdate()
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_REORDER, getNode("list"))
|
||||
];
|
||||
|
||||
this.invoke = function bulletUpdate_invoke()
|
||||
{
|
||||
testName("li_end", "1. list end");
|
||||
|
||||
var li = document.createElement("li");
|
||||
li.setAttribute("id", "li_start");
|
||||
li.textContent = "list start";
|
||||
getNode("list").insertBefore(li, getNode("li_end"));
|
||||
}
|
||||
|
||||
this.finalCheck = function bulletUpdate_finalCheck()
|
||||
{
|
||||
testName("li_start", "1. list start");
|
||||
testName("li_end", "2. list end");
|
||||
|
||||
// change list style type
|
||||
var list = getNode("list");
|
||||
list.setAttribute("style", "list-style-type: disc;");
|
||||
getComputedStyle(list, "").color; // make style processing sync
|
||||
|
||||
testName("li_start", kDiscBulletText + "list start");
|
||||
testName("li_end", kDiscBulletText + "list end");
|
||||
}
|
||||
|
||||
this.getID = function bulletUpdate_getID()
|
||||
{
|
||||
return "Update bullet of list items";
|
||||
}
|
||||
}
|
||||
|
||||
var gQueue = null;
|
||||
function doTest()
|
||||
{
|
||||
gQueue = new eventQueue();
|
||||
gQueue.push(new bulletUpdate());
|
||||
gQueue.invoke(); // SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTest);
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=634200"
|
||||
title="crash [@ nsIFrame::GetStyleVisibility() ]">
|
||||
Mozilla Bug 634200
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<ol id="list">
|
||||
<li id="li_end">list end</li>
|
||||
</ol>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -5,6 +5,12 @@
|
|||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<style>
|
||||
h6.gencontent:before {
|
||||
content: "aga"
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript"
|
||||
|
@ -40,6 +46,16 @@
|
|||
testText(IDs, 10, 13, "e " + kEmbedChar);
|
||||
testText(IDs, 0, 13, "hello " + kEmbedChar + " see " + kEmbedChar);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// getTextAtOffset line boundary
|
||||
|
||||
testTextAtOffset(0, BOUNDARY_LINE_START, "line ", 0, 5,
|
||||
"hypertext3", kOk, kOk, kOk);
|
||||
|
||||
// XXX: see bug 638684.
|
||||
testTextAtOffset(0, BOUNDARY_LINE_START, "line ", 0, 5,
|
||||
"hypertext4", kTodo, kOk, kTodo);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// list
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -63,7 +79,14 @@
|
|||
|
||||
<a target="_blank"
|
||||
title="Fix getText"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=630001">Mozilla Bug 630001, part3</a>
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=630001">
|
||||
Bug 630001, part3
|
||||
</a>
|
||||
<a target="_blank"
|
||||
title="getTextAtOffset line boundary may return more than one line"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=638326">
|
||||
Bug 638326
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
|
@ -73,5 +96,17 @@
|
|||
<div id="hypertext2">hello <a>friend</a> see <input></div>
|
||||
<ol id="list"><li id="listitem">foo</li></ol>
|
||||
|
||||
<div id="hypertext3">line
|
||||
<!-- haha -->
|
||||
<!-- hahaha -->
|
||||
<h6>heading</h6>
|
||||
</div>
|
||||
|
||||
<div id="hypertext4">line
|
||||
<!-- haha -->
|
||||
<!-- hahaha -->
|
||||
<h6 role="presentation" class="gencontent">heading</h6>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -40,17 +40,13 @@
|
|||
|
||||
function doTest()
|
||||
{
|
||||
const discBulletText = String.fromCharCode(0x2022) + " ";
|
||||
const circleBulletText = String.fromCharCode(0x25e6) + " ";
|
||||
const squareBulletText = String.fromCharCode(0x25aa) + " ";
|
||||
|
||||
// list1
|
||||
var discAccTree = {
|
||||
role: ROLE_LIST,
|
||||
children: [
|
||||
new listItemTree(discBulletText, "Oranges"),
|
||||
new listItemTree(discBulletText, "Apples"),
|
||||
new listItemTree(discBulletText, "Bananas")
|
||||
new listItemTree(kDiscBulletText, "Oranges"),
|
||||
new listItemTree(kDiscBulletText, "Apples"),
|
||||
new listItemTree(kDiscBulletText, "Bananas")
|
||||
]
|
||||
};
|
||||
|
||||
|
@ -60,9 +56,9 @@
|
|||
var circleAccTree = {
|
||||
role: ROLE_LIST,
|
||||
children: [
|
||||
new listItemTree(circleBulletText, "Oranges"),
|
||||
new listItemTree(circleBulletText, "Apples"),
|
||||
new listItemTree(circleBulletText, "Bananas")
|
||||
new listItemTree(kCircleBulletText, "Oranges"),
|
||||
new listItemTree(kCircleBulletText, "Apples"),
|
||||
new listItemTree(kCircleBulletText, "Bananas")
|
||||
]
|
||||
};
|
||||
|
||||
|
@ -72,9 +68,9 @@
|
|||
var squareAccTree = {
|
||||
role: ROLE_LIST,
|
||||
children: [
|
||||
new listItemTree(squareBulletText, "Oranges"),
|
||||
new listItemTree(squareBulletText, "Apples"),
|
||||
new listItemTree(squareBulletText, "Bananas")
|
||||
new listItemTree(kSquareBulletText, "Oranges"),
|
||||
new listItemTree(kSquareBulletText, "Apples"),
|
||||
new listItemTree(kSquareBulletText, "Bananas")
|
||||
]
|
||||
};
|
||||
|
||||
|
@ -92,6 +88,43 @@
|
|||
|
||||
testAccessibleTree("list4", nestedAccTree);
|
||||
|
||||
// dl list
|
||||
var tree =
|
||||
{ LIST: [ // dl
|
||||
{ LISTITEM: [ // dt
|
||||
{ TEXT_LEAF: [] },
|
||||
] },
|
||||
{ PARAGRAPH: [ // dd
|
||||
{ TEXT_LEAF: [] }
|
||||
] },
|
||||
{ LISTITEM: [ // dt
|
||||
{ TEXT_LEAF: [] }
|
||||
] },
|
||||
{ PARAGRAPH: [ // dd
|
||||
{ TEXT_LEAF: [] }
|
||||
] }
|
||||
] };
|
||||
|
||||
testAccessibleTree("list5", tree);
|
||||
|
||||
// dl list inside ordered list
|
||||
tree =
|
||||
{ LIST: [ // ol
|
||||
{ LISTITEM: [ // li
|
||||
{ STATICTEXT: [ ] },
|
||||
{ LIST: [ // dl
|
||||
{ LISTITEM: [ // dt
|
||||
{ TEXT_LEAF: [] }
|
||||
] },
|
||||
{ PARAGRAPH: [ // dd
|
||||
{ TEXT_LEAF: [] }
|
||||
] }
|
||||
] }
|
||||
] }
|
||||
] };
|
||||
|
||||
testAccessibleTree("list6", tree);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
@ -111,6 +144,11 @@
|
|||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=604587">
|
||||
Mozilla Bug 604587
|
||||
</a>
|
||||
<a target="_blank"
|
||||
title="Fix list bullets for DL list (crash [@ nsBulletFrame::GetListItemText])"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=629114">
|
||||
Mozilla Bug 629114
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
|
@ -144,5 +182,18 @@
|
|||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<dl id="list5">
|
||||
<dt>item1</dt><dd>description</dd>
|
||||
<dt>item2</td><dd>description</dd>
|
||||
</dl>
|
||||
|
||||
<ol id="list6">
|
||||
<li>
|
||||
<dl id="dl">
|
||||
<dt>item1</dt><dd>description</dd>
|
||||
</dl>
|
||||
</li>
|
||||
</ol>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -48,8 +48,6 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
pref("general.startup.browser", true);
|
||||
|
||||
pref("browser.chromeURL","chrome://browser/content/");
|
||||
pref("browser.hiddenWindowChromeURL", "chrome://browser/content/hiddenWindow.xul");
|
||||
|
||||
|
@ -536,11 +534,6 @@ pref("mousewheel.withcontrolkey.action",3);
|
|||
pref("mousewheel.withcontrolkey.sysnumlines",false);
|
||||
pref("mousewheel.withcontrolkey.numlines",1);
|
||||
|
||||
pref("profile.allow_automigration", false); // setting to false bypasses automigration in the profile code
|
||||
|
||||
// Customizable toolbar stuff
|
||||
pref("custtoolbar.personal_toolbar_folder", "");
|
||||
|
||||
// pref to control the alert notification
|
||||
pref("alerts.slideIncrement", 1);
|
||||
pref("alerts.slideIncrementTime", 10);
|
||||
|
|
|
@ -5068,23 +5068,19 @@ var TabsInTitlebar = {
|
|||
let titlebar = $("titlebar");
|
||||
|
||||
if (allowed) {
|
||||
let availTop = screen.availTop;
|
||||
function top(ele) ele.boxObject.screenY - availTop;
|
||||
function bottom(ele) top(ele) + rect(ele).height;
|
||||
function rect(ele) ele.getBoundingClientRect();
|
||||
|
||||
let tabsToolbar = $("TabsToolbar");
|
||||
|
||||
let appmenuButtonBox = $("appmenu-button-container");
|
||||
let captionButtonsBox = $("titlebar-buttonbox");
|
||||
|
||||
this._sizePlaceholder("appmenu-button", rect(appmenuButtonBox).width);
|
||||
this._sizePlaceholder("caption-buttons", rect(captionButtonsBox).width);
|
||||
|
||||
let maxMargin = top(gNavToolbox);
|
||||
let tabsBottom = maxMargin + rect(tabsToolbar).height;
|
||||
let titlebarBottom = Math.max(bottom(appmenuButtonBox), bottom(captionButtonsBox));
|
||||
let distance = tabsBottom - titlebarBottom;
|
||||
titlebar.style.marginBottom = - Math.min(distance, maxMargin) + "px";
|
||||
let tabsToolbarRect = rect(tabsToolbar);
|
||||
let titlebarTop = rect($("titlebar-content")).top;
|
||||
titlebar.style.marginBottom = - Math.min(tabsToolbarRect.top - titlebarTop,
|
||||
tabsToolbarRect.height) + "px";
|
||||
|
||||
docElement.setAttribute("tabsintitlebar", "true");
|
||||
|
||||
|
|
|
@ -84,6 +84,7 @@ function GroupItem(listOfEls, options) {
|
|||
this.fadeAwayUndoButtonDuration = 300;
|
||||
|
||||
this.keepProportional = false;
|
||||
this._frozenItemSizeData = {};
|
||||
|
||||
// Double click tracker
|
||||
this._lastClick = 0;
|
||||
|
@ -194,6 +195,7 @@ function GroupItem(listOfEls, options) {
|
|||
gTabView.firstUseExperienced = true;
|
||||
})
|
||||
.focus(function() {
|
||||
self._unfreezeItemSize();
|
||||
if (!self._titleFocused) {
|
||||
(self.$title)[0].select();
|
||||
self._titleFocused = true;
|
||||
|
@ -644,6 +646,9 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
|||
this.removeAll({dontClose: true});
|
||||
GroupItems.unregister(this);
|
||||
|
||||
// remove unfreeze event handlers, if item size is frozen
|
||||
this._unfreezeItemSize({dontArrange: true});
|
||||
|
||||
let self = this;
|
||||
let destroyGroup = function () {
|
||||
iQ(self.container).remove();
|
||||
|
@ -677,6 +682,7 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
|||
// Closes the groupItem and all of its children.
|
||||
closeAll: function GroupItem_closeAll() {
|
||||
if (this._children.length > 0) {
|
||||
this._unfreezeItemSize();
|
||||
this._children.forEach(function(child) {
|
||||
iQ(child.container).hide();
|
||||
});
|
||||
|
@ -878,8 +884,10 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
|||
this.$undoContainer = iQ("<div/>")
|
||||
.addClass("undo")
|
||||
.attr("type", "button")
|
||||
.text(tabviewString("groupItem.undoCloseGroup"))
|
||||
.appendTo("body");
|
||||
iQ("<span/>")
|
||||
.text(tabviewString("groupItem.undoCloseGroup"))
|
||||
.appendTo(this.$undoContainer);
|
||||
let undoClose = iQ("<span/>")
|
||||
.addClass("close")
|
||||
.appendTo(this.$undoContainer);
|
||||
|
@ -1007,8 +1015,13 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
|||
item.groupItemData = {};
|
||||
|
||||
item.addSubscriber(this, "close", function() {
|
||||
let count = self._children.length;
|
||||
let dontArrange = self.expanded || !self.shouldStack(count);
|
||||
let dontClose = !item.closedManually && gBrowser._numPinnedTabs > 0;
|
||||
self.remove(item, { dontClose: dontClose });
|
||||
self.remove(item, {dontArrange: dontArrange, dontClose: dontClose});
|
||||
|
||||
if (dontArrange)
|
||||
self._freezeItemSize(count);
|
||||
|
||||
if (self._children.length > 0 && self._activeTab) {
|
||||
GroupItems.setActiveGroupItem(self);
|
||||
|
@ -1035,6 +1048,7 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
|||
if (!options.dontArrange)
|
||||
this.arrange({animate: !options.immediately});
|
||||
|
||||
this._unfreezeItemSize({dontArrange: true});
|
||||
this._sendToSubscribers("childAdded",{ groupItemId: this.id, item: item });
|
||||
|
||||
UI.setReorderTabsOnHide(this);
|
||||
|
@ -1105,8 +1119,10 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
|||
let closed = options.dontClose ? false : this.closeIfEmpty();
|
||||
if (closed)
|
||||
this._makeClosestTabActive();
|
||||
else if (!options.dontArrange)
|
||||
else if (!options.dontArrange) {
|
||||
this.arrange({animate: !options.immediately});
|
||||
this._unfreezeItemSize({dontArrange: true});
|
||||
}
|
||||
|
||||
this._sendToSubscribers("childRemoved",{ groupItemId: this.id, item: item });
|
||||
} catch(e) {
|
||||
|
@ -1237,6 +1253,66 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
|||
return shouldStack;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: _freezeItemSize
|
||||
// Freezes current item size (when removing a child).
|
||||
//
|
||||
// Parameters:
|
||||
// itemCount - the number of children before the last one was removed
|
||||
_freezeItemSize: function GroupItem__freezeItemSize(itemCount) {
|
||||
let data = this._frozenItemSizeData;
|
||||
|
||||
if (!data.lastItemCount) {
|
||||
let self = this;
|
||||
data.lastItemCount = itemCount;
|
||||
|
||||
// unfreeze item size when tabview is hidden
|
||||
data.onTabViewHidden = function () self._unfreezeItemSize();
|
||||
window.addEventListener('tabviewhidden', data.onTabViewHidden, false);
|
||||
|
||||
// we don't need to observe mouse movement when expanded because the
|
||||
// tray is closed when we leave it and collapse causes unfreezing
|
||||
if (self.expanded)
|
||||
return;
|
||||
|
||||
// unfreeze item size when cursor is moved out of group bounds
|
||||
data.onMouseMove = function (e) {
|
||||
let cursor = new Point(e.pageX, e.pageY);
|
||||
if (!self.bounds.contains(cursor))
|
||||
self._unfreezeItemSize();
|
||||
}
|
||||
iQ(window).mousemove(data.onMouseMove);
|
||||
}
|
||||
|
||||
this.arrange({animate: true, count: data.lastItemCount});
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: _unfreezeItemSize
|
||||
// Unfreezes and updates item size.
|
||||
//
|
||||
// Parameters:
|
||||
// options - various options (see below)
|
||||
//
|
||||
// Possible options:
|
||||
// dontArrange - do not arrange items when unfreezing
|
||||
_unfreezeItemSize: function GroupItem__unfreezeItemSize(options) {
|
||||
let data = this._frozenItemSizeData;
|
||||
if (!data.lastItemCount)
|
||||
return;
|
||||
|
||||
if (!options || !options.dontArrange)
|
||||
this.arrange({animate: true});
|
||||
|
||||
// unbind event listeners
|
||||
window.removeEventListener('tabviewhidden', data.onTabViewHidden, false);
|
||||
if (data.onMouseMove)
|
||||
iQ(window).unbind('mousemove', data.onMouseMove);
|
||||
|
||||
// reset freeze status
|
||||
this._frozenItemSizeData = {};
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: arrange
|
||||
// Lays out all of the children.
|
||||
|
@ -1561,6 +1637,7 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
|||
});
|
||||
|
||||
this.arrange({z: z + 2});
|
||||
this._unfreezeItemSize({dontArrange: true});
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1688,8 +1765,11 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
|||
// Function: setResizable
|
||||
// Sets whether the groupItem is resizable and updates the UI accordingly.
|
||||
setResizable: function GroupItem_setResizable(value, immediately) {
|
||||
var self = this;
|
||||
|
||||
this.resizeOptions.minWidth = GroupItems.minGroupWidth;
|
||||
this.resizeOptions.minHeight = GroupItems.minGroupHeight;
|
||||
this.resizeOptions.start = function () self._unfreezeItemSize();
|
||||
|
||||
if (value) {
|
||||
immediately ? this.$resizer.show() : this.$resizer.fadeIn();
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
* Aza Raskin <aza@mozilla.com>
|
||||
* Michael Yoshitaka Erlewine <mitcho@mitcho.com>
|
||||
* Sean Dunn <seanedunn@yahoo.com>
|
||||
* Tim Taubert <tim.taubert@gmx.de>
|
||||
*
|
||||
* 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
|
||||
|
@ -169,8 +170,10 @@ Item.prototype = {
|
|||
this.dragOptions = {
|
||||
cancelClass: 'close stackExpander',
|
||||
start: function(e, ui) {
|
||||
if (this.isAGroupItem)
|
||||
if (this.isAGroupItem) {
|
||||
GroupItems.setActiveGroupItem(this);
|
||||
this._unfreezeItemSize();
|
||||
}
|
||||
// if we start dragging a tab within a group, start with dropSpace on.
|
||||
else if (this.parent != null)
|
||||
this.parent._dropSpaceActive = true;
|
||||
|
@ -598,6 +601,35 @@ Item.prototype = {
|
|||
var droppables;
|
||||
var dropTarget;
|
||||
|
||||
// determine the best drop target based on the current mouse coordinates
|
||||
let determineBestDropTarget = function (e, box) {
|
||||
// drop events
|
||||
var best = {
|
||||
dropTarget: null,
|
||||
score: 0
|
||||
};
|
||||
|
||||
droppables.forEach(function(droppable) {
|
||||
var intersection = box.intersection(droppable.bounds);
|
||||
if (intersection && intersection.area() > best.score) {
|
||||
var possibleDropTarget = droppable.item;
|
||||
var accept = true;
|
||||
if (possibleDropTarget != dropTarget) {
|
||||
var dropOptions = possibleDropTarget.dropOptions;
|
||||
if (dropOptions && typeof dropOptions.accept == "function")
|
||||
accept = dropOptions.accept.apply(possibleDropTarget, [self]);
|
||||
}
|
||||
|
||||
if (accept) {
|
||||
best.dropTarget = possibleDropTarget;
|
||||
best.score = intersection.area();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return best.dropTarget;
|
||||
}
|
||||
|
||||
// ___ mousemove
|
||||
var handleMouseMove = function(e) {
|
||||
// global drag tracking
|
||||
|
@ -624,31 +656,9 @@ Item.prototype = {
|
|||
if (typeof self.dragOptions.drag == "function")
|
||||
self.dragOptions.drag.apply(self, [e]);
|
||||
|
||||
// drop events
|
||||
var best = {
|
||||
dropTarget: null,
|
||||
score: 0
|
||||
};
|
||||
let bestDropTarget = determineBestDropTarget(e, box);
|
||||
|
||||
droppables.forEach(function(droppable) {
|
||||
var intersection = box.intersection(droppable.bounds);
|
||||
if (intersection && intersection.area() > best.score) {
|
||||
var possibleDropTarget = droppable.item;
|
||||
var accept = true;
|
||||
if (possibleDropTarget != dropTarget) {
|
||||
var dropOptions = possibleDropTarget.dropOptions;
|
||||
if (dropOptions && typeof dropOptions.accept == "function")
|
||||
accept = dropOptions.accept.apply(possibleDropTarget, [self]);
|
||||
}
|
||||
|
||||
if (accept) {
|
||||
best.dropTarget = possibleDropTarget;
|
||||
best.score = intersection.area();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (best.dropTarget != dropTarget) {
|
||||
if (bestDropTarget != dropTarget) {
|
||||
var dropOptions;
|
||||
if (dropTarget) {
|
||||
dropOptions = dropTarget.dropOptions;
|
||||
|
@ -656,7 +666,7 @@ Item.prototype = {
|
|||
dropOptions.out.apply(dropTarget, [e]);
|
||||
}
|
||||
|
||||
dropTarget = best.dropTarget;
|
||||
dropTarget = bestDropTarget;
|
||||
|
||||
if (dropTarget) {
|
||||
dropOptions = dropTarget.dropOptions;
|
||||
|
@ -710,10 +720,10 @@ Item.prototype = {
|
|||
}
|
||||
|
||||
startMouse = new Point(e.pageX, e.pageY);
|
||||
startPos = self.getBounds().position();
|
||||
let bounds = self.getBounds();
|
||||
startPos = bounds.position();
|
||||
startEvent = e;
|
||||
startSent = false;
|
||||
dropTarget = null;
|
||||
|
||||
droppables = [];
|
||||
iQ('.iq-droppable').each(function(elem) {
|
||||
|
@ -726,6 +736,8 @@ Item.prototype = {
|
|||
}
|
||||
});
|
||||
|
||||
dropTarget = determineBestDropTarget(e, bounds);
|
||||
|
||||
iQ(gWindow)
|
||||
.mousemove(handleMouseMove)
|
||||
.mouseup(handleMouseUp);
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
* Aza Raskin <aza@mozilla.com>
|
||||
* Ian Gilman <ian@iangilman.com>
|
||||
* Michael Yoshitaka Erlewine <mitcho@mitcho.com>
|
||||
* Tim Taubert <tim.taubert@gmx.de>
|
||||
*
|
||||
* This file incorporates work from:
|
||||
* jQuery JavaScript Library v1.4.2: http://code.jquery.com/jquery-1.4.2.js
|
||||
|
@ -170,16 +171,22 @@ Rect.prototype = {
|
|||
|
||||
// ----------
|
||||
// Function: contains
|
||||
// Returns a boolean denoting if the given <Rect> is contained within
|
||||
// Returns a boolean denoting if the <Rect> or <Point> is contained inside
|
||||
// this rectangle.
|
||||
//
|
||||
// Paramaters
|
||||
// - A <Rect>
|
||||
contains: function Rect_contains(rect) {
|
||||
return (rect.left >= this.left &&
|
||||
rect.right <= this.right &&
|
||||
rect.top >= this.top &&
|
||||
rect.bottom <= this.bottom);
|
||||
// Parameters
|
||||
// - A <Rect> or a <Point>
|
||||
contains: function Rect_contains(a) {
|
||||
if (Utils.isPoint(a))
|
||||
return (a.x > this.left &&
|
||||
a.x < this.right &&
|
||||
a.y > this.top &&
|
||||
a.y < this.bottom);
|
||||
|
||||
return (a.left >= this.left &&
|
||||
a.right <= this.right &&
|
||||
a.top >= this.top &&
|
||||
a.bottom <= this.bottom);
|
||||
},
|
||||
|
||||
// ----------
|
||||
|
|
|
@ -721,6 +721,7 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
|||
complete();
|
||||
};
|
||||
|
||||
UI.setActiveTab(this);
|
||||
TabItems._update(this.tab, {force: true});
|
||||
|
||||
$tab.addClass("front");
|
||||
|
|
|
@ -112,11 +112,6 @@ body {
|
|||
position: absolute;
|
||||
}
|
||||
|
||||
.undo .close {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
position: absolute;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ function test() {
|
|||
waitForExplicitFinish();
|
||||
|
||||
is(getTopWin(), window, "got top window");
|
||||
is(getBoolPref("general.startup.browser", false), true, "getBoolPref");
|
||||
is(getBoolPref("browser.search.openintab", false), false, "getBoolPref");
|
||||
is(getBoolPref("this.pref.doesnt.exist", true), true, "getBoolPref fallback");
|
||||
is(getBoolPref("this.pref.doesnt.exist", false), false, "getBoolPref fallback #2");
|
||||
|
||||
|
|
|
@ -85,6 +85,7 @@ _BROWSER_FILES = \
|
|||
browser_tabview_bug608184.js \
|
||||
browser_tabview_bug608158.js \
|
||||
browser_tabview_bug608405.js \
|
||||
browser_tabview_bug610208.js \
|
||||
browser_tabview_bug610242.js \
|
||||
browser_tabview_bug612470.js \
|
||||
browser_tabview_bug613541.js \
|
||||
|
|
|
@ -4,10 +4,12 @@
|
|||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
newWindowWithTabView(onTabViewShown);
|
||||
newWindowWithTabView(part1);
|
||||
}
|
||||
|
||||
function onTabViewShown(win) {
|
||||
function part1(win) {
|
||||
registerCleanupFunction(function() win.close());
|
||||
|
||||
let contentWindow = win.document.getElementById("tab-view").contentWindow;
|
||||
is(contentWindow.GroupItems.groupItems.length, 1, "Has only one group");
|
||||
|
||||
|
@ -52,8 +54,32 @@ function onTabViewShown(win) {
|
|||
checkActive(function() {
|
||||
checkActive(function() {
|
||||
win.close();
|
||||
finish();
|
||||
newWindowWithTabView(part2);
|
||||
});
|
||||
}, 490);
|
||||
}, 10)
|
||||
}
|
||||
|
||||
function part2(win) {
|
||||
registerCleanupFunction(function() win.close());
|
||||
|
||||
let newTab = win.gBrowser.loadOneTab("about:blank", {inBackground: true});
|
||||
hideTabView(function() {
|
||||
let selectedTab = win.gBrowser.selectedTab;
|
||||
isnot(selectedTab, newTab, "They are different tabs");
|
||||
|
||||
// switch the selected tab to new tab
|
||||
win.gBrowser.selectedTab = newTab;
|
||||
|
||||
win.addEventListener("tabviewhidden", function () {
|
||||
win.removeEventListener("tabviewhidden", arguments.callee, false);
|
||||
is(win.gBrowser.selectedTab, newTab, "The seleted tab should be the same as before (new tab)");
|
||||
win.close();
|
||||
finish();
|
||||
}, false);
|
||||
// show tabview
|
||||
EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true }, win);
|
||||
// hide tabview
|
||||
EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true }, win);
|
||||
})
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ function test() {
|
|||
}
|
||||
|
||||
let testVeryQuickDragAndDrop = function () {
|
||||
let sourceGroup = cw.GroupItems.groupItems[0];
|
||||
let targetGroup = createGroupItem();
|
||||
let sourceGroup = createGroupItem();
|
||||
let targetGroup = cw.GroupItems.groupItems[0];
|
||||
|
||||
sourceGroup.pushAway(true);
|
||||
targetGroup.pushAway(true);
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
let cw;
|
||||
let win;
|
||||
let groupItem;
|
||||
|
||||
let next = function () {
|
||||
let test = tests.shift();
|
||||
|
||||
if (test) {
|
||||
test();
|
||||
return;
|
||||
}
|
||||
|
||||
win.close();
|
||||
finish();
|
||||
}
|
||||
|
||||
let prepareTest = function (testName) {
|
||||
let originalBounds = groupItem.getChild(0).getBounds();
|
||||
|
||||
let tabItem = groupItem.getChild(1);
|
||||
let bounds = tabItem.getBounds();
|
||||
tabItem.close();
|
||||
|
||||
ok(originalBounds.equals(groupItem.getChild(0).getBounds()), testName + ': tabs did not change their size');
|
||||
ok(bounds.equals(groupItem.getChild(1).getBounds()), testName + ': third tab is now on second tab\'s previous position');
|
||||
|
||||
return originalBounds;
|
||||
}
|
||||
|
||||
let cleanUpTest = function (testName, originalBounds, callback) {
|
||||
// Use setTimeout here because the groupItem.arrange() call uses
|
||||
// animation to re-arrange the tabItems.
|
||||
win.setTimeout(function () {
|
||||
ok(!originalBounds.equals(groupItem.getChild(0).getBounds()), testName + ': tabs changed their size');
|
||||
|
||||
// cleanup
|
||||
cw.GroupItems.setActiveGroupItem(groupItem);
|
||||
win.gBrowser.loadOneTab('about:blank', {inBackground: true});
|
||||
afterAllTabsLoaded(callback, win);
|
||||
}, 500);
|
||||
}
|
||||
|
||||
let tests = [];
|
||||
|
||||
// focus group title's input field to cause item arrange
|
||||
let testFocusTitle = function () {
|
||||
let originalBounds = prepareTest('testFocusTitle');
|
||||
|
||||
let target = groupItem.$titleShield[0];
|
||||
EventUtils.synthesizeMouseAtCenter(target, {}, cw);
|
||||
|
||||
cleanUpTest('testFocusTitle', originalBounds, next);
|
||||
}
|
||||
|
||||
// hide tabview to cause item arrange
|
||||
let testHideTabView = function () {
|
||||
let originalBounds = prepareTest('testHideTabView');
|
||||
|
||||
hideTabView(function () {
|
||||
cleanUpTest('testHideTabView', originalBounds, function () {
|
||||
showTabView(next, win);
|
||||
});
|
||||
}, win);
|
||||
}
|
||||
|
||||
// (undo) close a group to cause item arrange
|
||||
let testCloseGroupUndo = function () {
|
||||
let originalBounds = prepareTest('testCloseGroupUndo');
|
||||
|
||||
hideGroupItem(groupItem, function () {
|
||||
unhideGroupItem(groupItem, function () {
|
||||
cleanUpTest('testCloseGroupUndo', originalBounds, next);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// leave the group's container with the mouse to cause item arrange
|
||||
let testMouseOut = function () {
|
||||
let originalBounds = prepareTest('testMouseOut');
|
||||
let doc = cw.document.documentElement;
|
||||
let bounds = groupItem.getBounds();
|
||||
|
||||
EventUtils.synthesizeMouse(doc, bounds.right - 5, bounds.bottom - 5, {type: 'mousemove'}, cw);
|
||||
ok(originalBounds.equals(groupItem.getChild(0).getBounds()), 'testMouseOut: tabs did not change their size');
|
||||
|
||||
EventUtils.synthesizeMouse(doc, bounds.right + 1, bounds.bottom + 1, {type: 'mousemove'}, cw);
|
||||
cleanUpTest('testMouseOut', originalBounds, next);
|
||||
}
|
||||
|
||||
// sort item (drag it around) in its group to cause item arrange
|
||||
let testSortInGroup = function () {
|
||||
let originalBounds = prepareTest('testSortInGroup');
|
||||
let target = groupItem.getChild(0).container;
|
||||
|
||||
// simulate drag/drop sorting
|
||||
EventUtils.synthesizeMouse(target, 20, 20, {type: 'mousedown'}, cw);
|
||||
EventUtils.synthesizeMouse(target, 40, 20, {type: 'mousemove'}, cw);
|
||||
EventUtils.synthesizeMouse(target, 20, 20, {type: 'mouseup'}, cw);
|
||||
|
||||
cleanUpTest('testSortInGroup', originalBounds, next);
|
||||
}
|
||||
|
||||
// arrange items when the containing group is resized
|
||||
let testResizeGroup = function () {
|
||||
let originalBounds = prepareTest('testResizeGroup');
|
||||
let oldBounds = groupItem.getBounds();
|
||||
let resizer = groupItem.$resizer[0];
|
||||
|
||||
// simulate drag/drop resizing
|
||||
EventUtils.synthesizeMouse(resizer, 5, 5, {type: 'mousedown'}, cw);
|
||||
EventUtils.synthesizeMouse(resizer, 40, 20, {type: 'mousemove'}, cw);
|
||||
EventUtils.synthesizeMouse(resizer, 20, 20, {type: 'mouseup'}, cw);
|
||||
|
||||
// reset group size
|
||||
groupItem.setBounds(oldBounds);
|
||||
groupItem.setUserSize();
|
||||
|
||||
cleanUpTest('testResizeGroup', originalBounds, next);
|
||||
}
|
||||
|
||||
// make sure we don't freeze item size when removing an item from a stack
|
||||
let testRemoveWhileStacked = function () {
|
||||
let oldBounds = groupItem.getBounds();
|
||||
groupItem.setSize(150, 200, true);
|
||||
groupItem.setUserSize();
|
||||
|
||||
let originalBounds = groupItem.getChild(0).getBounds();
|
||||
ok(!groupItem._isStacked, 'testRemoveWhileStacked: group is not stacked');
|
||||
|
||||
// add a new tab to let the group stack
|
||||
win.gBrowser.loadOneTab('about:blank', {inBackground: true});
|
||||
ok(groupItem._isStacked, 'testRemoveWhileStacked: group is now stacked');
|
||||
|
||||
afterAllTabsLoaded(function () {
|
||||
groupItem.getChild(0).close();
|
||||
let bounds = groupItem.getChild(0).getBounds();
|
||||
ok(originalBounds.equals(bounds), 'testRemoveWhileStacked: tabs did not change their size');
|
||||
|
||||
// reset group size
|
||||
groupItem.setBounds(oldBounds);
|
||||
groupItem.setUserSize();
|
||||
|
||||
next();
|
||||
}, win);
|
||||
}
|
||||
|
||||
// 1) make sure item size is frozen when removing an item in expanded mode
|
||||
// 2) make sure item size stays frozen while moving the mouse in the expanded
|
||||
// layer
|
||||
let testExpandedMode = function () {
|
||||
let oldBounds = groupItem.getBounds();
|
||||
groupItem.setSize(100, 100, true);
|
||||
groupItem.setUserSize();
|
||||
|
||||
ok(groupItem._isStacked, 'testExpandedMode: group is stacked');
|
||||
|
||||
groupItem.addSubscriber(groupItem, 'expanded', function () {
|
||||
groupItem.removeSubscriber(groupItem, 'expanded');
|
||||
onExpanded();
|
||||
});
|
||||
|
||||
groupItem.addSubscriber(groupItem, 'collapsed', function () {
|
||||
groupItem.removeSubscriber(groupItem, 'collapsed');
|
||||
onCollapsed();
|
||||
});
|
||||
|
||||
let onExpanded = function () {
|
||||
let originalBounds = groupItem.getChild(0).getBounds();
|
||||
let tabItem = groupItem.getChild(1);
|
||||
let bounds = tabItem.getBounds();
|
||||
|
||||
for (let i=0; i<3; i++)
|
||||
groupItem.getChild(1).close();
|
||||
|
||||
ok(originalBounds.equals(groupItem.getChild(0).getBounds()), 'testExpandedMode: tabs did not change their size');
|
||||
|
||||
// move the mouse over the expanded layer
|
||||
let trayBounds = groupItem.expanded.bounds;
|
||||
let target = groupItem.expanded.$tray[0];
|
||||
EventUtils.synthesizeMouse(target, trayBounds.right - 5, trayBounds.bottom -5, {type: 'mousemove'}, cw);
|
||||
|
||||
ok(originalBounds.equals(groupItem.getChild(0).getBounds()), 'testExpandedMode: tabs did not change their size');
|
||||
groupItem.collapse();
|
||||
}
|
||||
|
||||
let onCollapsed = function () {
|
||||
// reset group size
|
||||
groupItem.setBounds(oldBounds);
|
||||
groupItem.setUserSize();
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
groupItem.expand();
|
||||
}
|
||||
|
||||
tests.push(testFocusTitle);
|
||||
tests.push(testHideTabView);
|
||||
tests.push(testCloseGroupUndo);
|
||||
tests.push(testMouseOut);
|
||||
tests.push(testSortInGroup);
|
||||
tests.push(testResizeGroup);
|
||||
tests.push(testRemoveWhileStacked);
|
||||
tests.push(testExpandedMode);
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
newWindowWithTabView(function (tvwin) {
|
||||
win = tvwin;
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
if (!win.closed)
|
||||
win.close();
|
||||
});
|
||||
|
||||
cw = win.TabView.getContentWindow();
|
||||
|
||||
groupItem = cw.GroupItems.groupItems[0];
|
||||
groupItem.setSize(400, 200, true);
|
||||
groupItem.setUserSize();
|
||||
|
||||
for (let i=0; i<3; i++)
|
||||
win.gBrowser.loadOneTab('about:blank', {inBackground: true});
|
||||
|
||||
afterAllTabsLoaded(next, win);
|
||||
});
|
||||
}
|
|
@ -127,9 +127,6 @@
|
|||
<preference id="security.disable_button.openDeviceManager"
|
||||
name="security.disable_button.openDeviceManager"
|
||||
type="bool"/>
|
||||
<preference id="privacy.donottrackheader.enabled"
|
||||
name="privacy.donottrackheader.enabled"
|
||||
type="bool"/>
|
||||
</preferences>
|
||||
|
||||
#ifdef HAVE_SHELL_SERVICE
|
||||
|
@ -194,10 +191,6 @@
|
|||
onsyncfrompreference="return gAdvancedPane.readCheckSpelling();"
|
||||
onsynctopreference="return gAdvancedPane.writeCheckSpelling();"
|
||||
preference="layout.spellcheckDefault"/>
|
||||
<checkbox id="privacyDoNotTrackPrefs"
|
||||
label="&doNotTrack.label;"
|
||||
accesskey="&doNotTrack.accesskey;"
|
||||
preference="privacy.donottrackheader.enabled"/>
|
||||
</groupbox>
|
||||
|
||||
#ifdef HAVE_SHELL_SERVICE
|
||||
|
|
|
@ -57,6 +57,11 @@
|
|||
helpTopic="prefs-privacy">
|
||||
|
||||
<preferences id="privacyPreferences">
|
||||
|
||||
<!-- Tracking -->
|
||||
<preference id="privacy.donottrackheader.enabled"
|
||||
name="privacy.donottrackheader.enabled"
|
||||
type="bool"/>
|
||||
|
||||
<!-- XXX button prefs -->
|
||||
<preference id="pref.privacy.disable_button.cookie_exceptions"
|
||||
|
@ -116,6 +121,16 @@
|
|||
|
||||
<script type="application/javascript" src="chrome://browser/content/preferences/privacy.js"/>
|
||||
|
||||
<!-- Tracking -->
|
||||
<groupbox id="trackingGroup">
|
||||
<caption label="&tracking.label;"/>
|
||||
|
||||
<checkbox id="privacyDoNotTrackPrefs"
|
||||
label="&doNotTrack.label;"
|
||||
accesskey="&doNotTrack.accesskey;"
|
||||
preference="privacy.donottrackheader.enabled"/>
|
||||
</groupbox>
|
||||
|
||||
<!-- History -->
|
||||
<groupbox id="historyGroup">
|
||||
<caption label="&history.label;"/>
|
||||
|
|
|
@ -81,8 +81,7 @@
|
|||
<stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/>
|
||||
|
||||
<!-- addons, forgery (phishing) UI -->
|
||||
<groupbox id="addonsPhishingGroup"
|
||||
xmlns:aaa="http://www.w3.org/2005/07/aaa">
|
||||
<groupbox id="addonsPhishingGroup">
|
||||
<hbox id="addonInstallBox">
|
||||
<checkbox id="warnAddonInstall" flex="1"
|
||||
label="&warnAddonInstall.label;"
|
||||
|
|
|
@ -467,7 +467,7 @@ PrivateBrowsingService.prototype = {
|
|||
|
||||
handle: function PBS_handle(aCmdLine) {
|
||||
if (aCmdLine.handleFlag("private", false))
|
||||
; // It has already been handled
|
||||
aCmdLine.preventDefault = true; // It has already been handled
|
||||
else if (aCmdLine.handleFlag("private-toggle", false)) {
|
||||
if (this._autoStarted) {
|
||||
throw Cr.NS_ERROR_ABORT;
|
||||
|
|
|
@ -640,6 +640,12 @@
|
|||
]]></body>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
This method overrides the autocomplete binding's openPopup (essentially
|
||||
duplicating the logic from the autocomplete popup binding's
|
||||
openAutocompletePopup method), modifying it so that the popup is aligned with
|
||||
the inner textbox, but sized to not extend beyond the search bar border.
|
||||
-->
|
||||
<method name="openPopup">
|
||||
<body><![CDATA[
|
||||
var popup = this.popup;
|
||||
|
@ -662,18 +668,26 @@
|
|||
|
||||
document.popupNode = null;
|
||||
|
||||
const isRTL = getComputedStyle(this, "").direction == "rtl";
|
||||
|
||||
var outerRect = this.getBoundingClientRect();
|
||||
var innerRect = this.inputField.getBoundingClientRect();
|
||||
var width = outerRect.right - innerRect.left;
|
||||
if (isRTL) {
|
||||
var width = innerRect.right - outerRect.left;
|
||||
} else {
|
||||
var width = outerRect.right - innerRect.left;
|
||||
}
|
||||
popup.setAttribute("width", width > 100 ? width : 100);
|
||||
|
||||
var yOffset = outerRect.bottom - innerRect.bottom;
|
||||
|
||||
// setConsumeRollupEvent() before we call openPopup(),
|
||||
// see bug #404438 for more details
|
||||
popup.popupBoxObject.setConsumeRollupEvent(
|
||||
this.consumeRollupEvent ?
|
||||
Ci.nsIPopupBoxObject.ROLLUP_CONSUME :
|
||||
Ci.nsIPopupBoxObject.ROLLUP_NO_CONSUME);
|
||||
popup.openPopup(null, "", innerRect.left, outerRect.bottom, false, false);
|
||||
popup.openPopup(this.inputField, "after_start", 0, yOffset, false, false);
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
|
|
@ -21,8 +21,6 @@
|
|||
<!ENTITY allowHWAccel.accesskey "h">
|
||||
<!ENTITY checkSpelling.label "Check my spelling as I type">
|
||||
<!ENTITY checkSpelling.accesskey "t">
|
||||
<!ENTITY doNotTrack.label "Tell web sites I do not want to be tracked">
|
||||
<!ENTITY doNotTrack.accesskey "d">
|
||||
|
||||
<!ENTITY systemDefaults.label "System Defaults">
|
||||
<!ENTITY alwaysCheckDefault.label "Always check to see if &brandShortName; is the default browser on startup"><!--XXX-->
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
<!ENTITY tracking.label "Tracking">
|
||||
|
||||
<!ENTITY doNotTrack.label "Tell web sites I do not want to be tracked">
|
||||
<!ENTITY doNotTrack.accesskey "d">
|
||||
|
||||
<!ENTITY history.label "History">
|
||||
|
||||
<!ENTITY locationBar.label "Location Bar">
|
||||
|
@ -30,7 +35,6 @@
|
|||
<!ENTITY showCookies.label "Show Cookies…">
|
||||
<!ENTITY showCookies.accesskey "S">
|
||||
|
||||
|
||||
<!ENTITY historyHeader.pre.label "&brandShortName; will:">
|
||||
<!ENTITY historyHeader.pre.accesskey "w">
|
||||
<!ENTITY historyHeader.remember.label "Remember history">
|
||||
|
|
|
@ -299,9 +299,12 @@ html[dir=rtl] .appTabTrayContainer {
|
|||
|
||||
.undo {
|
||||
background-color: rgba(0,0,0,.2);
|
||||
width: 150px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
-moz-padding-start: 5px;
|
||||
-moz-padding-end: 20px;
|
||||
width: 135px;
|
||||
line-height: 25px;
|
||||
box-shadow: 0 1px 0 rgba(255,255,255,.4), 0 1px 0 rgba(0,0,0,.3) inset;
|
||||
text-shadow: 0 1px 0 rgba(255,255,255,.2);
|
||||
color: WindowText;
|
||||
|
@ -316,14 +319,14 @@ html[dir=rtl] .appTabTrayContainer {
|
|||
}
|
||||
|
||||
.undo .close {
|
||||
top: 4px;
|
||||
left: 4px;
|
||||
top: 7px;
|
||||
right: 7px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
html[dir=rtl] .undo .close {
|
||||
left: auto;
|
||||
right: 4px;
|
||||
right: auto;
|
||||
left: 7px;
|
||||
}
|
||||
|
||||
.undo .close:hover{
|
||||
|
|
|
@ -293,9 +293,12 @@ html[dir=rtl] .appTabTrayContainer {
|
|||
|
||||
.undo {
|
||||
background-color: #A0A0A0;
|
||||
width: 150px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
-moz-padding-start: 5px;
|
||||
-moz-padding-end: 20px;
|
||||
width: 135px;
|
||||
line-height: 25px;
|
||||
box-shadow: 0px 1px 0px rgba(255,255,255,.5), 0px -1px 0px rgba(0,0,0,.24);
|
||||
text-shadow: 0px -1px 0px rgba(255,255,255,.2);
|
||||
color: rgba( 0,0,0, .8);
|
||||
|
@ -310,14 +313,14 @@ html[dir=rtl] .appTabTrayContainer {
|
|||
}
|
||||
|
||||
.undo .close {
|
||||
top: 4px;
|
||||
left: 4px;
|
||||
top: 7px;
|
||||
right: 7px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
html[dir=rtl] .undo .close {
|
||||
left: auto;
|
||||
right: 4px;
|
||||
right: auto;
|
||||
left: 7px;
|
||||
}
|
||||
|
||||
.undo .close:hover{
|
||||
|
|
|
@ -318,9 +318,12 @@ html[dir=rtl] .appTabTrayContainer {
|
|||
|
||||
.undo {
|
||||
background-color: #A0A0A0;
|
||||
width: 150px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
-moz-padding-start: 5px;
|
||||
-moz-padding-end: 20px;
|
||||
width: 135px;
|
||||
line-height: 25px;
|
||||
box-shadow: 0px 1px 0px rgba(255,255,255,.5), 0px -1px 0px rgba(0,0,0,.24);
|
||||
text-shadow: 0px -1px 0px rgba(255,255,255,.2);
|
||||
color: rgba( 0,0,0, .8);
|
||||
|
@ -335,14 +338,14 @@ html[dir=rtl] .appTabTrayContainer {
|
|||
}
|
||||
|
||||
.undo .close {
|
||||
top: 4px;
|
||||
left: 4px;
|
||||
top: 7px;
|
||||
right: 7px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
html[dir=rtl] .undo .close {
|
||||
left: auto;
|
||||
right: 4px;
|
||||
right: auto;
|
||||
left: 7px;
|
||||
}
|
||||
|
||||
.undo .close:hover{
|
||||
|
|
|
@ -57,7 +57,7 @@ interface nsIScriptSecurityManager : nsIXPCSecurityManager
|
|||
/**
|
||||
* Check that the script currently running in context "cx" can load "uri".
|
||||
*
|
||||
* Will return error code NS_ERROR_DOM_BAD_URI if the load request
|
||||
* Will return error code NS_ERROR_DOM_BAD_URI if the load request
|
||||
* should be denied.
|
||||
*
|
||||
* @param cx the JSContext of the script causing the load
|
||||
|
@ -105,7 +105,7 @@ interface nsIScriptSecurityManager : nsIXPCSecurityManager
|
|||
/**
|
||||
* Check that content with principal aPrincipal can load "uri".
|
||||
*
|
||||
* Will return error code NS_ERROR_DOM_BAD_URI if the load request
|
||||
* Will return error code NS_ERROR_DOM_BAD_URI if the load request
|
||||
* should be denied.
|
||||
*
|
||||
* @param aPrincipal the principal identifying the actor causing the load
|
||||
|
@ -113,13 +113,13 @@ interface nsIScriptSecurityManager : nsIXPCSecurityManager
|
|||
* @param flags the permission set, see above
|
||||
*/
|
||||
void checkLoadURIWithPrincipal(in nsIPrincipal aPrincipal,
|
||||
in nsIURI uri,
|
||||
in nsIURI uri,
|
||||
in unsigned long flags);
|
||||
|
||||
/**
|
||||
* Check that content from "from" can load "uri".
|
||||
*
|
||||
* Will return error code NS_ERROR_DOM_BAD_URI if the load request
|
||||
* Will return error code NS_ERROR_DOM_BAD_URI if the load request
|
||||
* should be denied.
|
||||
*
|
||||
* @param from the URI causing the load
|
||||
|
@ -128,8 +128,8 @@ interface nsIScriptSecurityManager : nsIXPCSecurityManager
|
|||
*
|
||||
* @deprecated Use checkLoadURIWithPrincipal instead of this function.
|
||||
*/
|
||||
void checkLoadURI(in nsIURI from, in nsIURI uri,
|
||||
in unsigned long flags);
|
||||
[deprecated] void checkLoadURI(in nsIURI from, in nsIURI uri,
|
||||
in unsigned long flags);
|
||||
|
||||
/**
|
||||
* Similar to checkLoadURIWithPrincipal but there are two differences:
|
||||
|
@ -141,17 +141,17 @@ interface nsIScriptSecurityManager : nsIXPCSecurityManager
|
|||
* function will return error code NS_ERROR_DOM_BAD_URI.
|
||||
*/
|
||||
void checkLoadURIStrWithPrincipal(in nsIPrincipal aPrincipal,
|
||||
in AUTF8String uri,
|
||||
in unsigned long flags);
|
||||
|
||||
in AUTF8String uri,
|
||||
in unsigned long flags);
|
||||
|
||||
/**
|
||||
* Same as CheckLoadURI but takes string arguments for ease of use
|
||||
* by scripts
|
||||
*
|
||||
* @deprecated Use checkLoadURIStrWithPrincipal instead of this function.
|
||||
*/
|
||||
void checkLoadURIStr(in AUTF8String from, in AUTF8String uri,
|
||||
in unsigned long flags);
|
||||
[deprecated] void checkLoadURIStr(in AUTF8String from, in AUTF8String uri,
|
||||
in unsigned long flags);
|
||||
|
||||
/**
|
||||
* Check that the function 'funObj' is allowed to run on 'targetObj'
|
||||
|
@ -173,10 +173,10 @@ interface nsIScriptSecurityManager : nsIXPCSecurityManager
|
|||
[noscript] boolean canExecuteScripts(in JSContextPtr cx,
|
||||
in nsIPrincipal principal);
|
||||
|
||||
///////////////// Principals ///////////////////////
|
||||
///////////////// Principals ///////////////////////
|
||||
/**
|
||||
* Return the principal of the innermost frame of the currently
|
||||
* executing script. Will return null if there is no script
|
||||
* Return the principal of the innermost frame of the currently
|
||||
* executing script. Will return null if there is no script
|
||||
* currently executing.
|
||||
*/
|
||||
[noscript] nsIPrincipal getSubjectPrincipal();
|
||||
|
@ -215,12 +215,12 @@ interface nsIScriptSecurityManager : nsIXPCSecurityManager
|
|||
*/
|
||||
[noscript] short requestCapability(in nsIPrincipal principal,
|
||||
in string capability);
|
||||
|
||||
|
||||
/**
|
||||
* Return true if the currently executing script has 'capability' enabled.
|
||||
*/
|
||||
boolean isCapabilityEnabled(in string capability);
|
||||
|
||||
|
||||
/**
|
||||
* Enable 'capability' in the innermost frame of the currently executing
|
||||
* script.
|
||||
|
@ -249,7 +249,7 @@ interface nsIScriptSecurityManager : nsIXPCSecurityManager
|
|||
// XXXbz ideally we'd pass a subjectName here too, and the nsISupports
|
||||
// cert we're enabling for...
|
||||
void setCanEnableCapability(in AUTF8String certificateFingerprint,
|
||||
in string capability,
|
||||
in string capability,
|
||||
in short canEnable);
|
||||
|
||||
///////////////////////
|
||||
|
|
|
@ -66,7 +66,7 @@ NS_IMETHODIMP_(nsrefcnt)
|
|||
nsNullPrincipal::AddRef()
|
||||
{
|
||||
NS_PRECONDITION(PRInt32(mJSPrincipals.refcount) >= 0, "illegal refcnt");
|
||||
nsrefcnt count = PR_AtomicIncrement((PRInt32 *)&mJSPrincipals.refcount);
|
||||
nsrefcnt count = PR_ATOMIC_INCREMENT(&mJSPrincipals.refcount);
|
||||
NS_LOG_ADDREF(this, count, "nsNullPrincipal", sizeof(*this));
|
||||
return count;
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ NS_IMETHODIMP_(nsrefcnt)
|
|||
nsNullPrincipal::Release()
|
||||
{
|
||||
NS_PRECONDITION(0 != mJSPrincipals.refcount, "dup release");
|
||||
nsrefcnt count = PR_AtomicDecrement((PRInt32 *)&mJSPrincipals.refcount);
|
||||
nsrefcnt count = PR_ATOMIC_DECREMENT(&mJSPrincipals.refcount);
|
||||
NS_LOG_RELEASE(this, count, "nsNullPrincipal");
|
||||
if (count == 0) {
|
||||
delete this;
|
||||
|
|
|
@ -154,7 +154,7 @@ nsPrincipal::AddRef()
|
|||
{
|
||||
NS_PRECONDITION(PRInt32(mJSPrincipals.refcount) >= 0, "illegal refcnt");
|
||||
// XXXcaa does this need to be threadsafe? See bug 143559.
|
||||
nsrefcnt count = PR_AtomicIncrement((PRInt32 *)&mJSPrincipals.refcount);
|
||||
nsrefcnt count = PR_ATOMIC_INCREMENT(&mJSPrincipals.refcount);
|
||||
NS_LOG_ADDREF(this, count, "nsPrincipal", sizeof(*this));
|
||||
return count;
|
||||
}
|
||||
|
@ -163,7 +163,7 @@ NS_IMETHODIMP_(nsrefcnt)
|
|||
nsPrincipal::Release()
|
||||
{
|
||||
NS_PRECONDITION(0 != mJSPrincipals.refcount, "dup release");
|
||||
nsrefcnt count = PR_AtomicDecrement((PRInt32 *)&mJSPrincipals.refcount);
|
||||
nsrefcnt count = PR_ATOMIC_DECREMENT(&mJSPrincipals.refcount);
|
||||
NS_LOG_RELEASE(this, count, "nsPrincipal");
|
||||
if (count == 0) {
|
||||
delete this;
|
||||
|
|
|
@ -63,7 +63,7 @@ NS_IMETHODIMP_(nsrefcnt)
|
|||
nsSystemPrincipal::AddRef()
|
||||
{
|
||||
NS_PRECONDITION(PRInt32(mJSPrincipals.refcount) >= 0, "illegal refcnt");
|
||||
nsrefcnt count = PR_AtomicIncrement((PRInt32 *)&mJSPrincipals.refcount);
|
||||
nsrefcnt count = PR_ATOMIC_INCREMENT(&mJSPrincipals.refcount);
|
||||
NS_LOG_ADDREF(this, count, "nsSystemPrincipal", sizeof(*this));
|
||||
return count;
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ NS_IMETHODIMP_(nsrefcnt)
|
|||
nsSystemPrincipal::Release()
|
||||
{
|
||||
NS_PRECONDITION(0 != mJSPrincipals.refcount, "dup release");
|
||||
nsrefcnt count = PR_AtomicDecrement((PRInt32 *)&mJSPrincipals.refcount);
|
||||
nsrefcnt count = PR_ATOMIC_DECREMENT(&mJSPrincipals.refcount);
|
||||
NS_LOG_RELEASE(this, count, "nsSystemPrincipal");
|
||||
if (count == 0) {
|
||||
delete this;
|
||||
|
|
|
@ -385,6 +385,7 @@ GNU_LD = @GNU_LD@
|
|||
GNU_CC = @GNU_CC@
|
||||
GNU_CXX = @GNU_CXX@
|
||||
HAVE_GCC3_ABI = @HAVE_GCC3_ABI@
|
||||
HAVE_OLD_CLANG = @HAVE_OLD_CLANG@
|
||||
INTEL_CC = @INTEL_CC@
|
||||
INTEL_CXX = @INTEL_CXX@
|
||||
|
||||
|
|
29
configure.in
29
configure.in
|
@ -1167,6 +1167,28 @@ fi
|
|||
|
||||
fi # COMPILE_ENVIRONMENT
|
||||
|
||||
dnl clang prior to 2.9 (including Xcode 4) does not support all the
|
||||
dnl constructs required by the libtheora inline asm. This is used to
|
||||
dnl detect and disable it
|
||||
AC_LANG_SAVE
|
||||
AC_LANG_C
|
||||
AC_TRY_COMPILE([
|
||||
#if defined(__clang__)
|
||||
# if __clang_major__<2 || (__clang_major__==2 && __clang_minor__<9)
|
||||
# error "clang older than 2.9 detected"
|
||||
# endif
|
||||
#endif
|
||||
],
|
||||
[],
|
||||
result="yes",
|
||||
result="no")
|
||||
if test "$result" = "no"; then
|
||||
AC_DEFINE(HAVE_OLD_CLANG)
|
||||
HAVE_OLD_CLANG=1
|
||||
fi
|
||||
AC_LANG_RESTORE
|
||||
AC_SUBST(HAVE_OLD_CLANG)
|
||||
|
||||
if test -n "$MAKE"; then
|
||||
if test `echo $MAKE | grep -c make.py` != 1; then
|
||||
NOT_PYMAKE=$MAKE
|
||||
|
@ -5052,13 +5074,18 @@ if test -z "$XULRUNNER_STUB_NAME"; then
|
|||
fi
|
||||
AC_SUBST(XULRUNNER_STUB_NAME)
|
||||
|
||||
AC_MSG_CHECKING([for application to build])
|
||||
if test -z "$MOZ_BUILD_APP"; then
|
||||
AC_MSG_ERROR([--enable-application=APP was not specified and is required.])
|
||||
AC_MSG_RESULT([browser])
|
||||
MOZ_BUILD_APP=browser
|
||||
else
|
||||
# We have a valid application only if it has a build.mk file in its top
|
||||
# directory.
|
||||
if test ! -f "${srcdir}/${MOZ_BUILD_APP}/build.mk" ; then
|
||||
AC_MSG_RESULT([none])
|
||||
AC_MSG_ERROR([--enable-application value not recognized (${MOZ_BUILD_APP}/build.mk does not exist).])
|
||||
else
|
||||
AC_MSG_RESULT([$MOZ_BUILD_APP])
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
|
@ -76,8 +76,8 @@ enum nsLinkState {
|
|||
|
||||
// IID for the nsIContent interface
|
||||
#define NS_ICONTENT_IID \
|
||||
{ 0x8331ca9f, 0x8717, 0x4ab4, \
|
||||
{ 0xad, 0x17, 0xb4, 0x9d, 0xdc, 0xe8, 0xb6, 0x77 } }
|
||||
{ 0x5788c9eb, 0x646a, 0x4285, \
|
||||
{ 0xa2, 0x8c, 0xde, 0x0d, 0x43, 0x6b, 0x47, 0x72 } }
|
||||
|
||||
/**
|
||||
* A node of content in a document's content model. This interface
|
||||
|
@ -906,15 +906,15 @@ public:
|
|||
*/
|
||||
virtual nsISMILAttr* GetAnimatedAttr(PRInt32 aNamespaceID, nsIAtom* aName) = 0;
|
||||
|
||||
/**
|
||||
* Get the SMIL override style for this content node. This is a style
|
||||
* declaration that is applied *after* the inline style, and it can be used
|
||||
* e.g. to store animated style values.
|
||||
*
|
||||
* Note: This method is analogous to the 'GetStyle' method in
|
||||
* nsGenericHTMLElement and nsStyledElement.
|
||||
*/
|
||||
virtual nsresult GetSMILOverrideStyle(nsIDOMCSSStyleDeclaration** aStyle) = 0;
|
||||
/**
|
||||
* Get the SMIL override style for this content node. This is a style
|
||||
* declaration that is applied *after* the inline style, and it can be used
|
||||
* e.g. to store animated style values.
|
||||
*
|
||||
* Note: This method is analogous to the 'GetStyle' method in
|
||||
* nsGenericHTMLElement and nsStyledElement.
|
||||
*/
|
||||
virtual nsIDOMCSSStyleDeclaration* GetSMILOverrideStyle() = 0;
|
||||
|
||||
/**
|
||||
* Get the SMIL override style rule for this content node. If the rule
|
||||
|
|
|
@ -35,8 +35,8 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef _nsIContentSerializer_h__
|
||||
#define _nsIContentSerializer_h__
|
||||
#ifndef nsIContentSerializer_h
|
||||
#define nsIContentSerializer_h
|
||||
|
||||
#include "nsISupports.h"
|
||||
|
||||
|
@ -44,7 +44,11 @@ class nsIContent;
|
|||
class nsIDocument;
|
||||
class nsAString;
|
||||
|
||||
/* starting interface: nsIContentSerializer */
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class Element;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#define NS_ICONTENTSERIALIZER_IID \
|
||||
{ 0xb1ee32f2, 0xb8c4, 0x49b9, \
|
||||
|
@ -77,11 +81,11 @@ class nsIContentSerializer : public nsISupports {
|
|||
NS_IMETHOD AppendDoctype(nsIContent *aDoctype,
|
||||
nsAString& aStr) = 0;
|
||||
|
||||
NS_IMETHOD AppendElementStart(nsIContent *aElement,
|
||||
nsIContent *aOriginalElement,
|
||||
NS_IMETHOD AppendElementStart(mozilla::dom::Element* aElement,
|
||||
mozilla::dom::Element* aOriginalElement,
|
||||
nsAString& aStr) = 0;
|
||||
|
||||
NS_IMETHOD AppendElementEnd(nsIContent *aElement,
|
||||
NS_IMETHOD AppendElementEnd(mozilla::dom::Element* aElement,
|
||||
nsAString& aStr) = 0;
|
||||
|
||||
NS_IMETHOD Flush(nsAString& aStr) = 0;
|
||||
|
@ -100,4 +104,4 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIContentSerializer, NS_ICONTENTSERIALIZER_IID)
|
|||
#define NS_CONTENTSERIALIZER_CONTRACTID_PREFIX \
|
||||
"@mozilla.org/layout/contentserializer;1?mimetype="
|
||||
|
||||
#endif /* __gen_nsIContentSerializer_h__ */
|
||||
#endif /* nsIContentSerializer_h */
|
||||
|
|
|
@ -123,8 +123,8 @@ class Element;
|
|||
|
||||
|
||||
#define NS_IDOCUMENT_IID \
|
||||
{ 0xc38a7935, 0xc854, 0x4df7, \
|
||||
{ 0x8f, 0xd4, 0xa2, 0x6f, 0x0d, 0x27, 0x9f, 0x31 } }
|
||||
{ 0x2c6ad63f, 0xb7b9, 0x42f8, \
|
||||
{ 0xbd, 0xde, 0x76, 0x0a, 0x83, 0xe3, 0xb0, 0x49 } }
|
||||
|
||||
// Flag for AddStyleSheet().
|
||||
#define NS_STYLESHEET_FROM_CATALOG (1 << 0)
|
||||
|
@ -757,11 +757,9 @@ public:
|
|||
virtual void SetReadyStateInternal(ReadyState rs) = 0;
|
||||
virtual ReadyState GetReadyStateEnum() = 0;
|
||||
|
||||
// notify that one or two content nodes changed state
|
||||
// either may be nsnull, but not both
|
||||
virtual void ContentStatesChanged(nsIContent* aContent1,
|
||||
nsIContent* aContent2,
|
||||
nsEventStates aStateMask) = 0;
|
||||
// notify that a content node changed state
|
||||
virtual void ContentStateChanged(nsIContent* aContent,
|
||||
nsEventStates aStateMask) = 0;
|
||||
|
||||
// Notify that a document state has changed.
|
||||
// This should only be called by callers whose state is also reflected in the
|
||||
|
|
|
@ -49,8 +49,8 @@ class nsString;
|
|||
class nsIDocument;
|
||||
|
||||
#define NS_IDOCUMENT_OBSERVER_IID \
|
||||
{ 0x3d005225, 0x210f, 0x4b07, \
|
||||
{ 0xb1, 0xd9, 0x96, 0x02, 0x05, 0x74, 0xc4, 0x37 } }
|
||||
{ 0x900bc4bc, 0x8b6c, 0x4cba, \
|
||||
{ 0x82, 0xfa, 0x56, 0x8a, 0x80, 0xff, 0xfd, 0x3e } }
|
||||
|
||||
typedef PRUint32 nsUpdateType;
|
||||
|
||||
|
@ -103,20 +103,12 @@ public:
|
|||
* added/removed from the document or the content itself changed
|
||||
* (the other notifications are used for that).
|
||||
*
|
||||
* The optional second content node is to allow optimization
|
||||
* of the case where state moves from one node to another
|
||||
* (as is likely for :focus and :hover)
|
||||
*
|
||||
* Either content node may be nsnull, but not both
|
||||
*
|
||||
* @param aDocument The document being observed
|
||||
* @param aContent1 the piece of content that changed
|
||||
* @param aContent2 optional second piece of content that changed
|
||||
* @param aContent the piece of content that changed
|
||||
*/
|
||||
virtual void ContentStatesChanged(nsIDocument* aDocument,
|
||||
nsIContent* aContent1,
|
||||
nsIContent* aContent2,
|
||||
nsEventStates aStateMask) = 0;
|
||||
virtual void ContentStateChanged(nsIDocument* aDocument,
|
||||
nsIContent* aContent,
|
||||
nsEventStates aStateMask) = 0;
|
||||
|
||||
/**
|
||||
* Notification that the state of the document has changed.
|
||||
|
@ -247,11 +239,10 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumentObserver, NS_IDOCUMENT_OBSERVER_IID)
|
|||
#define NS_DECL_NSIDOCUMENTOBSERVER_ENDLOAD \
|
||||
virtual void EndLoad(nsIDocument* aDocument);
|
||||
|
||||
#define NS_DECL_NSIDOCUMENTOBSERVER_CONTENTSTATESCHANGED \
|
||||
virtual void ContentStatesChanged(nsIDocument* aDocument, \
|
||||
nsIContent* aContent1, \
|
||||
nsIContent* aContent2, \
|
||||
nsEventStates aStateMask);
|
||||
#define NS_DECL_NSIDOCUMENTOBSERVER_CONTENTSTATECHANGED \
|
||||
virtual void ContentStateChanged(nsIDocument* aDocument, \
|
||||
nsIContent* aContent, \
|
||||
nsEventStates aStateMask);
|
||||
|
||||
#define NS_DECL_NSIDOCUMENTOBSERVER_DOCUMENTSTATESCHANGED \
|
||||
virtual void DocumentStatesChanged(nsIDocument* aDocument, \
|
||||
|
@ -293,7 +284,7 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumentObserver, NS_IDOCUMENT_OBSERVER_IID)
|
|||
NS_DECL_NSIDOCUMENTOBSERVER_ENDUPDATE \
|
||||
NS_DECL_NSIDOCUMENTOBSERVER_BEGINLOAD \
|
||||
NS_DECL_NSIDOCUMENTOBSERVER_ENDLOAD \
|
||||
NS_DECL_NSIDOCUMENTOBSERVER_CONTENTSTATESCHANGED \
|
||||
NS_DECL_NSIDOCUMENTOBSERVER_CONTENTSTATECHANGED \
|
||||
NS_DECL_NSIDOCUMENTOBSERVER_DOCUMENTSTATESCHANGED \
|
||||
NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETADDED \
|
||||
NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETREMOVED \
|
||||
|
@ -327,9 +318,8 @@ _class::EndLoad(nsIDocument* aDocument) \
|
|||
|
||||
#define NS_IMPL_NSIDOCUMENTOBSERVER_STATE_STUB(_class) \
|
||||
void \
|
||||
_class::ContentStatesChanged(nsIDocument* aDocument, \
|
||||
nsIContent* aContent1, \
|
||||
nsIContent* aContent2, \
|
||||
_class::ContentStateChanged(nsIDocument* aDocument, \
|
||||
nsIContent* aContent, \
|
||||
nsEventStates aStateMask) \
|
||||
{ \
|
||||
} \
|
||||
|
|
|
@ -109,7 +109,7 @@ interface nsIXMLHttpRequestUpload : nsIXMLHttpRequestEventTarget {
|
|||
* you're aware of all the security implications. And then think twice about
|
||||
* it.
|
||||
*/
|
||||
[scriptable, uuid(6bb91106-85f0-4d93-8cb4-e57b3d0624f2)]
|
||||
[scriptable, uuid(af62a870-820c-4981-96a3-28ab17b779e1)]
|
||||
interface nsIXMLHttpRequest : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -187,42 +187,19 @@ interface nsIXMLHttpRequest : nsISupports
|
|||
*/
|
||||
ACString getResponseHeader(in AUTF8String header);
|
||||
|
||||
/**
|
||||
* Native (non-script) method to initialize a request. Note that
|
||||
* the request is not sent until the <code>send</code> method
|
||||
* is invoked.
|
||||
*
|
||||
* If there is an "active" request (that is, if open() or openRequest() has
|
||||
* been called already), this is equivalent to calling abort().
|
||||
*
|
||||
* @param method The HTTP method, for example "POST" or "GET". Ignored
|
||||
* if the URL is not a HTTP(S) URL.
|
||||
* @param url The URL to which to send the request.
|
||||
* @param async Whether the request is synchronous or asynchronous
|
||||
* i.e. whether send returns only after the response
|
||||
* is received or if it returns immediately after
|
||||
* sending the request. In the latter case, notification
|
||||
* of completion is sent through the event listeners.
|
||||
* This argument must be true if the multipart
|
||||
* attribute has been set to true, or an exception will
|
||||
* be thrown.
|
||||
* @param user A username for authentication if necessary.
|
||||
* @param password A password for authentication if necessary.
|
||||
*/
|
||||
[noscript] void openRequest(in AUTF8String method,
|
||||
in AUTF8String url,
|
||||
in boolean async,
|
||||
in AString user,
|
||||
in AString password);
|
||||
|
||||
%{C++
|
||||
// note this is NOT virtual so this won't muck with the vtable!
|
||||
inline nsresult Open(const nsACString& method, const nsACString& url,
|
||||
PRBool async, const nsAString& user,
|
||||
const nsAString& password) {
|
||||
return Open(method, url, async, user, password, 3);
|
||||
}
|
||||
%}
|
||||
/**
|
||||
* Meant to be a script-only method for initializing a request.
|
||||
* The parameters are similar to the ones detailed in the
|
||||
* description of <code>openRequest</code>, but the last
|
||||
* 3 are optional.
|
||||
*
|
||||
* If there is an "active" request (that is, if open() or openRequest() has
|
||||
* been called already), this is equivalent to calling abort().
|
||||
* If there is an "active" request (that is, if open() has been called
|
||||
* already), this is equivalent to calling abort() and then open().
|
||||
*
|
||||
* @param method The HTTP method - either "POST" or "GET". Ignored
|
||||
* if the URL is not a HTTP URL.
|
||||
|
|
|
@ -102,7 +102,7 @@ Link::SetLinkState(nsLinkState aState)
|
|||
newLinkState == NS_EVENT_STATE_UNVISITED,
|
||||
"Unexpected state obtained from LinkState()!");
|
||||
mozAutoDocUpdate update(doc, UPDATE_CONTENT_STATE, PR_TRUE);
|
||||
doc->ContentStatesChanged(content, nsnull, oldLinkState ^ newLinkState);
|
||||
doc->ContentStateChanged(content, oldLinkState ^ newLinkState);
|
||||
}
|
||||
|
||||
nsEventStates
|
||||
|
@ -493,7 +493,7 @@ Link::ResetLinkState(bool aNotify)
|
|||
if (aNotify && doc) {
|
||||
nsEventStates changedState = NS_EVENT_STATE_VISITED ^ NS_EVENT_STATE_UNVISITED;
|
||||
MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, aNotify);
|
||||
doc->ContentStatesChanged(content, nsnull, changedState);
|
||||
doc->ContentStateChanged(content, changedState);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -151,6 +151,20 @@ CPPSRCS = \
|
|||
ThirdPartyUtil.cpp \
|
||||
$(NULL)
|
||||
|
||||
# Are we targeting x86-32 or x86-64? If so, we want to include SSE2 code for
|
||||
# nsTextFragment.cpp
|
||||
ifneq (,$(INTEL_ARCHITECTURE))
|
||||
|
||||
CPPSRCS += nsTextFragmentSSE2.cpp
|
||||
|
||||
# gcc requires -msse2 for this file since it uses SSE2 intrinsics. (See bug
|
||||
# 585538 comment 12.)
|
||||
ifdef GNU_CC
|
||||
nsTextFragmentSSE2.$(OBJ_SUFFIX): CXXFLAGS+=-msse2
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
GQI_SRCS = contentbase.gqi
|
||||
|
||||
# we don't want the shared lib, but we want to force the creation of a
|
||||
|
|
|
@ -63,8 +63,9 @@
|
|||
#include "nsIURI.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsEscape.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
||||
//#define DEBUG_BenB
|
||||
using namespace mozilla::dom;
|
||||
|
||||
static inline PRUnichar* escape(const nsString& source)
|
||||
{
|
||||
|
@ -241,8 +242,8 @@ mozSanitizingHTMLSerializer::AppendText(nsIContent* aText,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
mozSanitizingHTMLSerializer::AppendElementStart(nsIContent *aElement,
|
||||
nsIContent *aOriginalElement,
|
||||
mozSanitizingHTMLSerializer::AppendElementStart(Element* aElement,
|
||||
Element* aOriginalElement,
|
||||
nsAString& aStr)
|
||||
{
|
||||
NS_ENSURE_ARG(aElement);
|
||||
|
@ -270,7 +271,7 @@ mozSanitizingHTMLSerializer::AppendElementStart(nsIContent *aElement,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
mozSanitizingHTMLSerializer::AppendElementEnd(nsIContent *aElement,
|
||||
mozSanitizingHTMLSerializer::AppendElementEnd(Element* aElement,
|
||||
nsAString& aStr)
|
||||
{
|
||||
NS_ENSURE_ARG(aElement);
|
||||
|
|
|
@ -89,10 +89,11 @@ public:
|
|||
{ return NS_OK; }
|
||||
NS_IMETHOD AppendDoctype(nsIContent *aDoctype, nsAString& aStr)
|
||||
{ return NS_OK; }
|
||||
NS_IMETHOD AppendElementStart(nsIContent *aElement,
|
||||
nsIContent *aOriginalElement,
|
||||
nsAString& aStr);
|
||||
NS_IMETHOD AppendElementEnd(nsIContent *aElement, nsAString& aStr);
|
||||
NS_IMETHOD AppendElementStart(mozilla::dom::Element* aElement,
|
||||
mozilla::dom::Element* aOriginalElement,
|
||||
nsAString& aStr);
|
||||
NS_IMETHOD AppendElementEnd(mozilla::dom::Element* aElement,
|
||||
nsAString& aStr);
|
||||
NS_IMETHOD Flush(nsAString& aStr);
|
||||
|
||||
NS_IMETHOD AppendDocumentStart(nsIDocument *aDocument,
|
||||
|
@ -150,7 +151,7 @@ protected:
|
|||
PRUint32 mSkipLevel;
|
||||
nsHashtable mAllowedTags;
|
||||
|
||||
nsCOMPtr<nsIContent> mContent;
|
||||
nsRefPtr<mozilla::dom::Element> mContent;
|
||||
nsAString* mOutputString;
|
||||
nsIParserNode* mParserNode;
|
||||
nsCOMPtr<nsIParserService> mParserService;
|
||||
|
|
|
@ -448,6 +448,14 @@ nsContentList::nsContentList(nsINode* aRootNode,
|
|||
{
|
||||
NS_ASSERTION(mRootNode, "Must have root");
|
||||
mRootNode->AddMutationObserver(this);
|
||||
|
||||
// We only need to flush if we're in an non-HTML document, since the
|
||||
// HTML5 parser doesn't need flushing. Further, if we're not in a
|
||||
// document at all right now (in the GetCurrentDoc() sense), we're
|
||||
// not parser-created and don't need to be flushing stuff under us
|
||||
// to get our kids right.
|
||||
nsIDocument* doc = mRootNode->GetCurrentDoc();
|
||||
mFlushesNeeded = doc && !doc->IsHTML();
|
||||
}
|
||||
|
||||
nsContentList::~nsContentList()
|
||||
|
|
|
@ -54,52 +54,337 @@
|
|||
#include "nsIChannelEventSink.h"
|
||||
#include "nsIAsyncVerifyRedirectCallback.h"
|
||||
#include "nsCharSeparatedTokenizer.h"
|
||||
#include "nsXMLHttpRequest.h"
|
||||
#include "nsAsyncRedirectVerifyHelper.h"
|
||||
#include "prclist.h"
|
||||
#include "prtime.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
|
||||
#define PREFLIGHT_CACHE_SIZE 100
|
||||
|
||||
static PRBool gDisableCORS = PR_FALSE;
|
||||
static PRBool gDisableCORSPrivateData = PR_FALSE;
|
||||
|
||||
class nsChannelCanceller
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Preflight cache
|
||||
|
||||
class nsPreflightCache
|
||||
{
|
||||
public:
|
||||
nsChannelCanceller(nsIChannel* aChannel)
|
||||
: mChannel(aChannel)
|
||||
struct TokenTime
|
||||
{
|
||||
nsCString token;
|
||||
PRTime expirationTime;
|
||||
};
|
||||
|
||||
struct CacheEntry : public PRCList
|
||||
{
|
||||
CacheEntry(nsCString& aKey)
|
||||
: mKey(aKey)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsPreflightCache::CacheEntry);
|
||||
}
|
||||
|
||||
~CacheEntry()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsPreflightCache::CacheEntry);
|
||||
}
|
||||
|
||||
void PurgeExpired(PRTime now);
|
||||
PRBool CheckRequest(const nsCString& aMethod,
|
||||
const nsTArray<nsCString>& aCustomHeaders);
|
||||
|
||||
nsCString mKey;
|
||||
nsTArray<TokenTime> mMethods;
|
||||
nsTArray<TokenTime> mHeaders;
|
||||
};
|
||||
|
||||
nsPreflightCache()
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsPreflightCache);
|
||||
PR_INIT_CLIST(&mList);
|
||||
}
|
||||
~nsChannelCanceller()
|
||||
|
||||
~nsPreflightCache()
|
||||
{
|
||||
if (mChannel) {
|
||||
mChannel->Cancel(NS_ERROR_DOM_BAD_URI);
|
||||
Clear();
|
||||
MOZ_COUNT_DTOR(nsPreflightCache);
|
||||
}
|
||||
|
||||
PRBool Initialize()
|
||||
{
|
||||
return mTable.Init();
|
||||
}
|
||||
|
||||
CacheEntry* GetEntry(nsIURI* aURI, nsIPrincipal* aPrincipal,
|
||||
PRBool aWithCredentials, PRBool aCreate);
|
||||
void RemoveEntries(nsIURI* aURI, nsIPrincipal* aPrincipal);
|
||||
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
static PLDHashOperator
|
||||
RemoveExpiredEntries(const nsACString& aKey, nsAutoPtr<CacheEntry>& aValue,
|
||||
void* aUserData);
|
||||
|
||||
static PRBool GetCacheKey(nsIURI* aURI, nsIPrincipal* aPrincipal,
|
||||
PRBool aWithCredentials, nsACString& _retval);
|
||||
|
||||
nsClassHashtable<nsCStringHashKey, CacheEntry> mTable;
|
||||
PRCList mList;
|
||||
};
|
||||
|
||||
// Will be initialized in EnsurePreflightCache.
|
||||
static nsPreflightCache* sPreflightCache = nsnull;
|
||||
|
||||
static PRBool EnsurePreflightCache()
|
||||
{
|
||||
if (sPreflightCache)
|
||||
return PR_TRUE;
|
||||
|
||||
nsAutoPtr<nsPreflightCache> newCache(new nsPreflightCache());
|
||||
|
||||
if (newCache->Initialize()) {
|
||||
sPreflightCache = newCache.forget();
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
nsPreflightCache::CacheEntry::PurgeExpired(PRTime now)
|
||||
{
|
||||
PRUint32 i;
|
||||
for (i = 0; i < mMethods.Length(); ++i) {
|
||||
if (now >= mMethods[i].expirationTime) {
|
||||
mMethods.RemoveElementAt(i--);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < mHeaders.Length(); ++i) {
|
||||
if (now >= mHeaders[i].expirationTime) {
|
||||
mHeaders.RemoveElementAt(i--);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsPreflightCache::CacheEntry::CheckRequest(const nsCString& aMethod,
|
||||
const nsTArray<nsCString>& aHeaders)
|
||||
{
|
||||
PurgeExpired(PR_Now());
|
||||
|
||||
if (!aMethod.EqualsLiteral("GET") && !aMethod.EqualsLiteral("POST")) {
|
||||
PRUint32 i;
|
||||
for (i = 0; i < mMethods.Length(); ++i) {
|
||||
if (aMethod.Equals(mMethods[i].token))
|
||||
break;
|
||||
}
|
||||
if (i == mMethods.Length()) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void DontCancel()
|
||||
{
|
||||
mChannel = nsnull;
|
||||
for (PRUint32 i = 0; i < aHeaders.Length(); ++i) {
|
||||
PRUint32 j;
|
||||
for (j = 0; j < mHeaders.Length(); ++j) {
|
||||
if (aHeaders[i].Equals(mHeaders[j].token,
|
||||
nsCaseInsensitiveCStringComparator())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == mHeaders.Length()) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
nsIChannel* mChannel;
|
||||
};
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS5(nsCrossSiteListenerProxy, nsIStreamListener,
|
||||
nsPreflightCache::CacheEntry*
|
||||
nsPreflightCache::GetEntry(nsIURI* aURI,
|
||||
nsIPrincipal* aPrincipal,
|
||||
PRBool aWithCredentials,
|
||||
PRBool aCreate)
|
||||
{
|
||||
nsCString key;
|
||||
if (!GetCacheKey(aURI, aPrincipal, aWithCredentials, key)) {
|
||||
NS_WARNING("Invalid cache key!");
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
CacheEntry* entry;
|
||||
|
||||
if (mTable.Get(key, &entry)) {
|
||||
// Entry already existed so just return it. Also update the LRU list.
|
||||
|
||||
// Move to the head of the list.
|
||||
PR_REMOVE_LINK(entry);
|
||||
PR_INSERT_LINK(entry, &mList);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
if (!aCreate) {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
// This is a new entry, allocate and insert into the table now so that any
|
||||
// failures don't cause items to be removed from a full cache.
|
||||
entry = new CacheEntry(key);
|
||||
if (!entry) {
|
||||
NS_WARNING("Failed to allocate new cache entry!");
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
NS_ASSERTION(mTable.Count() <= PREFLIGHT_CACHE_SIZE,
|
||||
"Something is borked, too many entries in the cache!");
|
||||
|
||||
// Now enforce the max count.
|
||||
if (mTable.Count() == PREFLIGHT_CACHE_SIZE) {
|
||||
// Try to kick out all the expired entries.
|
||||
PRTime now = PR_Now();
|
||||
mTable.Enumerate(RemoveExpiredEntries, &now);
|
||||
|
||||
// If that didn't remove anything then kick out the least recently used
|
||||
// entry.
|
||||
if (mTable.Count() == PREFLIGHT_CACHE_SIZE) {
|
||||
CacheEntry* lruEntry = static_cast<CacheEntry*>(PR_LIST_TAIL(&mList));
|
||||
PR_REMOVE_LINK(lruEntry);
|
||||
|
||||
// This will delete 'lruEntry'.
|
||||
mTable.Remove(lruEntry->mKey);
|
||||
|
||||
NS_ASSERTION(mTable.Count() == PREFLIGHT_CACHE_SIZE - 1,
|
||||
"Somehow tried to remove an entry that was never added!");
|
||||
}
|
||||
}
|
||||
|
||||
if (!mTable.Put(key, entry)) {
|
||||
// Failed, clean up the new entry.
|
||||
delete entry;
|
||||
|
||||
NS_WARNING("Failed to add entry to the CORS preflight cache!");
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
PR_INSERT_LINK(entry, &mList);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
void
|
||||
nsPreflightCache::RemoveEntries(nsIURI* aURI, nsIPrincipal* aPrincipal)
|
||||
{
|
||||
CacheEntry* entry;
|
||||
nsCString key;
|
||||
if (GetCacheKey(aURI, aPrincipal, PR_TRUE, key) &&
|
||||
mTable.Get(key, &entry)) {
|
||||
PR_REMOVE_LINK(entry);
|
||||
mTable.Remove(key);
|
||||
}
|
||||
|
||||
if (GetCacheKey(aURI, aPrincipal, PR_FALSE, key) &&
|
||||
mTable.Get(key, &entry)) {
|
||||
PR_REMOVE_LINK(entry);
|
||||
mTable.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsPreflightCache::Clear()
|
||||
{
|
||||
PR_INIT_CLIST(&mList);
|
||||
mTable.Clear();
|
||||
}
|
||||
|
||||
/* static */ PLDHashOperator
|
||||
nsPreflightCache::RemoveExpiredEntries(const nsACString& aKey,
|
||||
nsAutoPtr<CacheEntry>& aValue,
|
||||
void* aUserData)
|
||||
{
|
||||
PRTime* now = static_cast<PRTime*>(aUserData);
|
||||
|
||||
aValue->PurgeExpired(*now);
|
||||
|
||||
if (aValue->mHeaders.IsEmpty() &&
|
||||
aValue->mMethods.IsEmpty()) {
|
||||
// Expired, remove from the list as well as the hash table.
|
||||
PR_REMOVE_LINK(aValue);
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
/* static */ PRBool
|
||||
nsPreflightCache::GetCacheKey(nsIURI* aURI,
|
||||
nsIPrincipal* aPrincipal,
|
||||
PRBool aWithCredentials,
|
||||
nsACString& _retval)
|
||||
{
|
||||
NS_ASSERTION(aURI, "Null uri!");
|
||||
NS_ASSERTION(aPrincipal, "Null principal!");
|
||||
|
||||
NS_NAMED_LITERAL_CSTRING(space, " ");
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
||||
|
||||
nsCAutoString scheme, host, port;
|
||||
if (uri) {
|
||||
uri->GetScheme(scheme);
|
||||
uri->GetHost(host);
|
||||
port.AppendInt(NS_GetRealPort(uri));
|
||||
}
|
||||
|
||||
nsCAutoString cred;
|
||||
if (aWithCredentials) {
|
||||
_retval.AssignLiteral("cred");
|
||||
}
|
||||
else {
|
||||
_retval.AssignLiteral("nocred");
|
||||
}
|
||||
|
||||
nsCAutoString spec;
|
||||
rv = aURI->GetSpec(spec);
|
||||
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
||||
|
||||
_retval.Assign(cred + space + scheme + space + host + space + port + space +
|
||||
spec);
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// nsCORSListenerProxy
|
||||
|
||||
NS_IMPL_ISUPPORTS5(nsCORSListenerProxy, nsIStreamListener,
|
||||
nsIRequestObserver, nsIChannelEventSink,
|
||||
nsIInterfaceRequestor, nsIAsyncVerifyRedirectCallback)
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsCrossSiteListenerProxy::Startup()
|
||||
nsCORSListenerProxy::Startup()
|
||||
{
|
||||
nsContentUtils::AddBoolPrefVarCache("content.cors.disable", &gDisableCORS);
|
||||
nsContentUtils::AddBoolPrefVarCache("content.cors.no_private_data", &gDisableCORSPrivateData);
|
||||
}
|
||||
|
||||
nsCrossSiteListenerProxy::nsCrossSiteListenerProxy(nsIStreamListener* aOuter,
|
||||
nsIPrincipal* aRequestingPrincipal,
|
||||
nsIChannel* aChannel,
|
||||
PRBool aWithCredentials,
|
||||
nsresult* aResult)
|
||||
/* static */
|
||||
void
|
||||
nsCORSListenerProxy::Shutdown()
|
||||
{
|
||||
delete sPreflightCache;
|
||||
sPreflightCache = nsnull;
|
||||
}
|
||||
|
||||
nsCORSListenerProxy::nsCORSListenerProxy(nsIStreamListener* aOuter,
|
||||
nsIPrincipal* aRequestingPrincipal,
|
||||
nsIChannel* aChannel,
|
||||
PRBool aWithCredentials,
|
||||
nsresult* aResult)
|
||||
: mOuterListener(aOuter),
|
||||
mRequestingPrincipal(aRequestingPrincipal),
|
||||
mWithCredentials(aWithCredentials && !gDisableCORSPrivateData),
|
||||
|
@ -118,14 +403,13 @@ nsCrossSiteListenerProxy::nsCrossSiteListenerProxy(nsIStreamListener* aOuter,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
nsCrossSiteListenerProxy::nsCrossSiteListenerProxy(nsIStreamListener* aOuter,
|
||||
nsIPrincipal* aRequestingPrincipal,
|
||||
nsIChannel* aChannel,
|
||||
PRBool aWithCredentials,
|
||||
const nsCString& aPreflightMethod,
|
||||
const nsTArray<nsCString>& aPreflightHeaders,
|
||||
nsresult* aResult)
|
||||
nsCORSListenerProxy::nsCORSListenerProxy(nsIStreamListener* aOuter,
|
||||
nsIPrincipal* aRequestingPrincipal,
|
||||
nsIChannel* aChannel,
|
||||
PRBool aWithCredentials,
|
||||
const nsCString& aPreflightMethod,
|
||||
const nsTArray<nsCString>& aPreflightHeaders,
|
||||
nsresult* aResult)
|
||||
: mOuterListener(aOuter),
|
||||
mRequestingPrincipal(aRequestingPrincipal),
|
||||
mWithCredentials(aWithCredentials && !gDisableCORSPrivateData),
|
||||
|
@ -152,19 +436,18 @@ nsCrossSiteListenerProxy::nsCrossSiteListenerProxy(nsIStreamListener* aOuter,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCrossSiteListenerProxy::OnStartRequest(nsIRequest* aRequest,
|
||||
nsISupports* aContext)
|
||||
nsCORSListenerProxy::OnStartRequest(nsIRequest* aRequest,
|
||||
nsISupports* aContext)
|
||||
{
|
||||
mRequestApproved = NS_SUCCEEDED(CheckRequestApproved(aRequest));
|
||||
if (!mRequestApproved) {
|
||||
if (nsXMLHttpRequest::sAccessControlCache) {
|
||||
if (sPreflightCache) {
|
||||
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
|
||||
if (channel) {
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
NS_GetFinalChannelURI(channel, getter_AddRefs(uri));
|
||||
if (uri) {
|
||||
nsXMLHttpRequest::sAccessControlCache->
|
||||
RemoveEntries(uri, mRequestingPrincipal);
|
||||
sPreflightCache->RemoveEntries(uri, mRequestingPrincipal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -219,7 +502,7 @@ IsValidHTTPToken(const nsCSubstring& aToken)
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsCrossSiteListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
||||
nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
||||
{
|
||||
// Check if this was actually a cross domain request
|
||||
if (!mHasBeenCrossSite) {
|
||||
|
@ -326,19 +609,19 @@ nsCrossSiteListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCrossSiteListenerProxy::OnStopRequest(nsIRequest* aRequest,
|
||||
nsISupports* aContext,
|
||||
nsresult aStatusCode)
|
||||
nsCORSListenerProxy::OnStopRequest(nsIRequest* aRequest,
|
||||
nsISupports* aContext,
|
||||
nsresult aStatusCode)
|
||||
{
|
||||
return mOuterListener->OnStopRequest(aRequest, aContext, aStatusCode);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCrossSiteListenerProxy::OnDataAvailable(nsIRequest* aRequest,
|
||||
nsISupports* aContext,
|
||||
nsIInputStream* aInputStream,
|
||||
PRUint32 aOffset,
|
||||
PRUint32 aCount)
|
||||
nsCORSListenerProxy::OnDataAvailable(nsIRequest* aRequest,
|
||||
nsISupports* aContext,
|
||||
nsIInputStream* aInputStream,
|
||||
PRUint32 aOffset,
|
||||
PRUint32 aCount)
|
||||
{
|
||||
if (!mRequestApproved) {
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
|
@ -348,7 +631,7 @@ nsCrossSiteListenerProxy::OnDataAvailable(nsIRequest* aRequest,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCrossSiteListenerProxy::GetInterface(const nsIID & aIID, void **aResult)
|
||||
nsCORSListenerProxy::GetInterface(const nsIID & aIID, void **aResult)
|
||||
{
|
||||
if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
|
||||
*aResult = static_cast<nsIChannelEventSink*>(this);
|
||||
|
@ -363,21 +646,20 @@ nsCrossSiteListenerProxy::GetInterface(const nsIID & aIID, void **aResult)
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCrossSiteListenerProxy::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
|
||||
nsIChannel *aNewChannel,
|
||||
PRUint32 aFlags,
|
||||
nsIAsyncVerifyRedirectCallback *cb)
|
||||
nsCORSListenerProxy::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
|
||||
nsIChannel *aNewChannel,
|
||||
PRUint32 aFlags,
|
||||
nsIAsyncVerifyRedirectCallback *cb)
|
||||
{
|
||||
nsresult rv;
|
||||
if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) {
|
||||
rv = CheckRequestApproved(aOldChannel);
|
||||
if (NS_FAILED(rv)) {
|
||||
if (nsXMLHttpRequest::sAccessControlCache) {
|
||||
if (sPreflightCache) {
|
||||
nsCOMPtr<nsIURI> oldURI;
|
||||
NS_GetFinalChannelURI(aOldChannel, getter_AddRefs(oldURI));
|
||||
if (oldURI) {
|
||||
nsXMLHttpRequest::sAccessControlCache->
|
||||
RemoveEntries(oldURI, mRequestingPrincipal);
|
||||
sPreflightCache->RemoveEntries(oldURI, mRequestingPrincipal);
|
||||
}
|
||||
}
|
||||
aOldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
|
||||
|
@ -408,7 +690,7 @@ nsCrossSiteListenerProxy::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCrossSiteListenerProxy::OnRedirectVerifyCallback(nsresult result)
|
||||
nsCORSListenerProxy::OnRedirectVerifyCallback(nsresult result)
|
||||
{
|
||||
NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback");
|
||||
NS_ASSERTION(mOldRedirectChannel, "mOldRedirectChannel not set in callback");
|
||||
|
@ -417,7 +699,7 @@ nsCrossSiteListenerProxy::OnRedirectVerifyCallback(nsresult result)
|
|||
if (NS_SUCCEEDED(result)) {
|
||||
nsresult rv = UpdateChannel(mNewRedirectChannel);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("nsCrossSiteListenerProxy::OnRedirectVerifyCallback: "
|
||||
NS_WARNING("nsCORSListenerProxy::OnRedirectVerifyCallback: "
|
||||
"UpdateChannel() returned failure");
|
||||
}
|
||||
result = rv;
|
||||
|
@ -435,7 +717,7 @@ nsCrossSiteListenerProxy::OnRedirectVerifyCallback(nsresult result)
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsCrossSiteListenerProxy::UpdateChannel(nsIChannel* aChannel)
|
||||
nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri, originalURI;
|
||||
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
|
||||
|
@ -517,3 +799,304 @@ nsCrossSiteListenerProxy::UpdateChannel(nsIChannel* aChannel)
|
|||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Preflight proxy
|
||||
|
||||
// Class used as streamlistener and notification callback when
|
||||
// doing the initial OPTIONS request for a CORS check
|
||||
class nsCORSPreflightListener : public nsIStreamListener,
|
||||
public nsIInterfaceRequestor,
|
||||
public nsIChannelEventSink
|
||||
{
|
||||
public:
|
||||
nsCORSPreflightListener(nsIChannel* aOuterChannel,
|
||||
nsIStreamListener* aOuterListener,
|
||||
nsISupports* aOuterContext,
|
||||
nsIPrincipal* aReferrerPrincipal,
|
||||
const nsACString& aRequestMethod,
|
||||
PRBool aWithCredentials)
|
||||
: mOuterChannel(aOuterChannel), mOuterListener(aOuterListener),
|
||||
mOuterContext(aOuterContext), mReferrerPrincipal(aReferrerPrincipal),
|
||||
mRequestMethod(aRequestMethod), mWithCredentials(aWithCredentials)
|
||||
{ }
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
NS_DECL_NSICHANNELEVENTSINK
|
||||
|
||||
private:
|
||||
void AddResultToCache(nsIRequest* aRequest);
|
||||
|
||||
nsCOMPtr<nsIChannel> mOuterChannel;
|
||||
nsCOMPtr<nsIStreamListener> mOuterListener;
|
||||
nsCOMPtr<nsISupports> mOuterContext;
|
||||
nsCOMPtr<nsIPrincipal> mReferrerPrincipal;
|
||||
nsCString mRequestMethod;
|
||||
PRBool mWithCredentials;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS4(nsCORSPreflightListener, nsIStreamListener,
|
||||
nsIRequestObserver, nsIInterfaceRequestor,
|
||||
nsIChannelEventSink)
|
||||
|
||||
void
|
||||
nsCORSPreflightListener::AddResultToCache(nsIRequest *aRequest)
|
||||
{
|
||||
nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
|
||||
NS_ASSERTION(http, "Request was not http");
|
||||
|
||||
// The "Access-Control-Max-Age" header should return an age in seconds.
|
||||
nsCAutoString headerVal;
|
||||
http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Max-Age"),
|
||||
headerVal);
|
||||
if (headerVal.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sanitize the string. We only allow 'delta-seconds' as specified by
|
||||
// http://dev.w3.org/2006/waf/access-control (digits 0-9 with no leading or
|
||||
// trailing non-whitespace characters).
|
||||
PRUint32 age = 0;
|
||||
nsCSubstring::const_char_iterator iter, end;
|
||||
headerVal.BeginReading(iter);
|
||||
headerVal.EndReading(end);
|
||||
while (iter != end) {
|
||||
if (*iter < '0' || *iter > '9') {
|
||||
return;
|
||||
}
|
||||
age = age * 10 + (*iter - '0');
|
||||
// Cap at 24 hours. This also avoids overflow
|
||||
age = NS_MIN(age, 86400U);
|
||||
++iter;
|
||||
}
|
||||
|
||||
if (!age || !EnsurePreflightCache()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// String seems fine, go ahead and cache.
|
||||
// Note that we have already checked that these headers follow the correct
|
||||
// syntax.
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
NS_GetFinalChannelURI(http, getter_AddRefs(uri));
|
||||
|
||||
// PR_Now gives microseconds
|
||||
PRTime expirationTime = PR_Now() + (PRUint64)age * PR_USEC_PER_SEC;
|
||||
|
||||
nsPreflightCache::CacheEntry* entry =
|
||||
sPreflightCache->GetEntry(uri, mReferrerPrincipal, mWithCredentials,
|
||||
PR_TRUE);
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The "Access-Control-Allow-Methods" header contains a comma separated
|
||||
// list of method names.
|
||||
http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Methods"),
|
||||
headerVal);
|
||||
|
||||
nsCCharSeparatedTokenizer methods(headerVal, ',');
|
||||
while(methods.hasMoreTokens()) {
|
||||
const nsDependentCSubstring& method = methods.nextToken();
|
||||
if (method.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
PRUint32 i;
|
||||
for (i = 0; i < entry->mMethods.Length(); ++i) {
|
||||
if (entry->mMethods[i].token.Equals(method)) {
|
||||
entry->mMethods[i].expirationTime = expirationTime;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == entry->mMethods.Length()) {
|
||||
nsPreflightCache::TokenTime* newMethod =
|
||||
entry->mMethods.AppendElement();
|
||||
if (!newMethod) {
|
||||
return;
|
||||
}
|
||||
|
||||
newMethod->token = method;
|
||||
newMethod->expirationTime = expirationTime;
|
||||
}
|
||||
}
|
||||
|
||||
// The "Access-Control-Allow-Headers" header contains a comma separated
|
||||
// list of method names.
|
||||
http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"),
|
||||
headerVal);
|
||||
|
||||
nsCCharSeparatedTokenizer headers(headerVal, ',');
|
||||
while(headers.hasMoreTokens()) {
|
||||
const nsDependentCSubstring& header = headers.nextToken();
|
||||
if (header.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
PRUint32 i;
|
||||
for (i = 0; i < entry->mHeaders.Length(); ++i) {
|
||||
if (entry->mHeaders[i].token.Equals(header)) {
|
||||
entry->mHeaders[i].expirationTime = expirationTime;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == entry->mHeaders.Length()) {
|
||||
nsPreflightCache::TokenTime* newHeader =
|
||||
entry->mHeaders.AppendElement();
|
||||
if (!newHeader) {
|
||||
return;
|
||||
}
|
||||
|
||||
newHeader->token = header;
|
||||
newHeader->expirationTime = expirationTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCORSPreflightListener::OnStartRequest(nsIRequest *aRequest,
|
||||
nsISupports *aContext)
|
||||
{
|
||||
nsresult status;
|
||||
nsresult rv = aRequest->GetStatus(&status);
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = status;
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// Everything worked, try to cache and then fire off the actual request.
|
||||
AddResultToCache(aRequest);
|
||||
|
||||
rv = mOuterChannel->AsyncOpen(mOuterListener, mOuterContext);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
mOuterChannel->Cancel(rv);
|
||||
mOuterListener->OnStartRequest(mOuterChannel, mOuterContext);
|
||||
mOuterListener->OnStopRequest(mOuterChannel, mOuterContext, rv);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCORSPreflightListener::OnStopRequest(nsIRequest *aRequest,
|
||||
nsISupports *aContext,
|
||||
nsresult aStatus)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/** nsIStreamListener methods **/
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCORSPreflightListener::OnDataAvailable(nsIRequest *aRequest,
|
||||
nsISupports *ctxt,
|
||||
nsIInputStream *inStr,
|
||||
PRUint32 sourceOffset,
|
||||
PRUint32 count)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCORSPreflightListener::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
|
||||
nsIChannel *aNewChannel,
|
||||
PRUint32 aFlags,
|
||||
nsIAsyncVerifyRedirectCallback *callback)
|
||||
{
|
||||
// Only internal redirects allowed for now.
|
||||
if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags))
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
|
||||
callback->OnRedirectVerifyCallback(NS_OK);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCORSPreflightListener::GetInterface(const nsIID & aIID, void **aResult)
|
||||
{
|
||||
return QueryInterface(aIID, aResult);
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
NS_StartCORSPreflight(nsIChannel* aRequestChannel,
|
||||
nsIStreamListener* aListener,
|
||||
nsIPrincipal* aPrincipal,
|
||||
PRBool aWithCredentials,
|
||||
nsTArray<nsCString>& aUnsafeHeaders,
|
||||
nsIChannel** aPreflightChannel)
|
||||
{
|
||||
*aPreflightChannel = nsnull;
|
||||
|
||||
nsCAutoString method;
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequestChannel));
|
||||
NS_ENSURE_TRUE(httpChannel, NS_ERROR_UNEXPECTED);
|
||||
httpChannel->GetRequestMethod(method);
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = NS_GetFinalChannelURI(aRequestChannel, getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsPreflightCache::CacheEntry* entry =
|
||||
sPreflightCache ?
|
||||
sPreflightCache->GetEntry(uri, aPrincipal, aWithCredentials, PR_FALSE) :
|
||||
nsnull;
|
||||
|
||||
if (entry && entry->CheckRequest(method, aUnsafeHeaders)) {
|
||||
// We have a cached preflight result, just start the original channel
|
||||
return aRequestChannel->AsyncOpen(aListener, nsnull);
|
||||
}
|
||||
|
||||
// Either it wasn't cached or the cached result has expired. Build a
|
||||
// channel for the OPTIONS request.
|
||||
|
||||
nsCOMPtr<nsILoadGroup> loadGroup;
|
||||
rv = aRequestChannel->GetLoadGroup(getter_AddRefs(loadGroup));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsLoadFlags loadFlags;
|
||||
rv = aRequestChannel->GetLoadFlags(&loadFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIChannel> preflightChannel;
|
||||
rv = NS_NewChannel(getter_AddRefs(preflightChannel), uri, nsnull,
|
||||
loadGroup, nsnull, loadFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIHttpChannel> preHttp = do_QueryInterface(preflightChannel);
|
||||
NS_ASSERTION(preHttp, "Failed to QI to nsIHttpChannel!");
|
||||
|
||||
rv = preHttp->SetRequestMethod(NS_LITERAL_CSTRING("OPTIONS"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Set up listener which will start the original channel
|
||||
nsCOMPtr<nsIStreamListener> preflightListener =
|
||||
new nsCORSPreflightListener(aRequestChannel, aListener, nsnull, aPrincipal,
|
||||
method, aWithCredentials);
|
||||
NS_ENSURE_TRUE(preflightListener, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
preflightListener =
|
||||
new nsCORSListenerProxy(preflightListener, aPrincipal,
|
||||
preflightChannel, aWithCredentials,
|
||||
method, aUnsafeHeaders, &rv);
|
||||
NS_ENSURE_TRUE(preflightListener, NS_ERROR_OUT_OF_MEMORY);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Start preflight
|
||||
rv = preflightChannel->AsyncOpen(preflightListener, nsnull);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Return newly created preflight channel
|
||||
preflightChannel.forget(aPreflightChannel);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,8 +35,8 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef nsCrossSiteListenerProxy_h__
|
||||
#define nsCrossSiteListenerProxy_h__
|
||||
#ifndef nsCORSListenerProxy_h__
|
||||
#define nsCORSListenerProxy_h__
|
||||
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
|
@ -55,24 +55,32 @@ class nsIPrincipal;
|
|||
extern PRBool
|
||||
IsValidHTTPToken(const nsCSubstring& aToken);
|
||||
|
||||
class nsCrossSiteListenerProxy : public nsIStreamListener,
|
||||
public nsIInterfaceRequestor,
|
||||
public nsIChannelEventSink,
|
||||
public nsIAsyncVerifyRedirectCallback
|
||||
nsresult
|
||||
NS_StartCORSPreflight(nsIChannel* aRequestChannel,
|
||||
nsIStreamListener* aListener,
|
||||
nsIPrincipal* aPrincipal,
|
||||
PRBool aWithCredentials,
|
||||
nsTArray<nsCString>& aACUnsafeHeaders,
|
||||
nsIChannel** aPreflightChannel);
|
||||
|
||||
class nsCORSListenerProxy : public nsIStreamListener,
|
||||
public nsIInterfaceRequestor,
|
||||
public nsIChannelEventSink,
|
||||
public nsIAsyncVerifyRedirectCallback
|
||||
{
|
||||
public:
|
||||
nsCrossSiteListenerProxy(nsIStreamListener* aOuter,
|
||||
nsIPrincipal* aRequestingPrincipal,
|
||||
nsIChannel* aChannel,
|
||||
PRBool aWithCredentials,
|
||||
nsresult* aResult);
|
||||
nsCrossSiteListenerProxy(nsIStreamListener* aOuter,
|
||||
nsIPrincipal* aRequestingPrincipal,
|
||||
nsIChannel* aChannel,
|
||||
PRBool aWithCredentials,
|
||||
const nsCString& aPreflightMethod,
|
||||
const nsTArray<nsCString>& aPreflightHeaders,
|
||||
nsresult* aResult);
|
||||
nsCORSListenerProxy(nsIStreamListener* aOuter,
|
||||
nsIPrincipal* aRequestingPrincipal,
|
||||
nsIChannel* aChannel,
|
||||
PRBool aWithCredentials,
|
||||
nsresult* aResult);
|
||||
nsCORSListenerProxy(nsIStreamListener* aOuter,
|
||||
nsIPrincipal* aRequestingPrincipal,
|
||||
nsIChannel* aChannel,
|
||||
PRBool aWithCredentials,
|
||||
const nsCString& aPreflightMethod,
|
||||
const nsTArray<nsCString>& aPreflightHeaders,
|
||||
nsresult* aResult);
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
|
@ -84,6 +92,8 @@ public:
|
|||
// Must be called at startup.
|
||||
static void Startup();
|
||||
|
||||
static void Shutdown();
|
||||
|
||||
private:
|
||||
nsresult UpdateChannel(nsIChannel* aChannel);
|
||||
nsresult CheckRequestApproved(nsIRequest* aRequest);
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
|
||||
#include "nsDOMAttributeMap.h"
|
||||
#include "nsDOMAttribute.h"
|
||||
#include "nsIDOM3Document.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsGenericElement.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsIDocument.h"
|
||||
|
@ -298,7 +298,7 @@ nsDOMAttributeMap::SetNamedItemInternal(nsIDOMNode *aNode,
|
|||
}
|
||||
|
||||
if (!mContent->HasSameOwnerDoc(iAttribute)) {
|
||||
nsCOMPtr<nsIDOM3Document> domDoc =
|
||||
nsCOMPtr<nsIDOMDocument> domDoc =
|
||||
do_QueryInterface(mContent->GetOwnerDoc(), &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
|
|
@ -4266,11 +4266,10 @@ nsDocument::EndLoad()
|
|||
}
|
||||
|
||||
void
|
||||
nsDocument::ContentStatesChanged(nsIContent* aContent1, nsIContent* aContent2,
|
||||
nsEventStates aStateMask)
|
||||
nsDocument::ContentStateChanged(nsIContent* aContent, nsEventStates aStateMask)
|
||||
{
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(ContentStatesChanged,
|
||||
(this, aContent1, aContent2, aStateMask));
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(ContentStateChanged,
|
||||
(this, aContent, aStateMask));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -52,7 +52,6 @@
|
|||
#include "nsTArray.h"
|
||||
#include "nsHashSets.h"
|
||||
#include "nsIDOMXMLDocument.h"
|
||||
#include "nsIDOM3Document.h"
|
||||
#include "nsIDOMDocumentView.h"
|
||||
#include "nsIDOMDocumentXBL.h"
|
||||
#include "nsIDOMNSDocument.h"
|
||||
|
@ -509,7 +508,6 @@ class nsDocument : public nsIDocument,
|
|||
public nsIDOMDocumentRange,
|
||||
public nsIDOMDocumentTraversal,
|
||||
public nsIDOMDocumentXBL,
|
||||
public nsIDOM3Document,
|
||||
public nsSupportsWeakReference,
|
||||
public nsIDOMEventTarget,
|
||||
public nsIDOM3EventTarget,
|
||||
|
@ -708,9 +706,8 @@ public:
|
|||
virtual void SetReadyStateInternal(ReadyState rs);
|
||||
virtual ReadyState GetReadyStateEnum();
|
||||
|
||||
virtual void ContentStatesChanged(nsIContent* aContent1,
|
||||
nsIContent* aContent2,
|
||||
nsEventStates aStateMask);
|
||||
virtual void ContentStateChanged(nsIContent* aContent,
|
||||
nsEventStates aStateMask);
|
||||
virtual void DocumentStatesChanged(nsEventStates aStateMask);
|
||||
|
||||
virtual void StyleRuleChanged(nsIStyleSheet* aStyleSheet,
|
||||
|
@ -805,9 +802,6 @@ public:
|
|||
// nsIDOMDocument
|
||||
NS_DECL_NSIDOMDOCUMENT
|
||||
|
||||
// nsIDOM3Document
|
||||
NS_DECL_NSIDOM3DOCUMENT
|
||||
|
||||
// nsIDOMXMLDocument
|
||||
NS_DECL_NSIDOMXMLDOCUMENT
|
||||
|
||||
|
@ -1279,7 +1273,6 @@ protected:
|
|||
NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, nsIDOMDocumentTraversal, \
|
||||
nsDocument) \
|
||||
NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, nsIDOMEventTarget, nsDocument) \
|
||||
NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, nsIDOMNode, nsDocument) \
|
||||
NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, nsIDOM3Document, nsDocument)
|
||||
NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, nsIDOMNode, nsDocument)
|
||||
|
||||
#endif /* nsDocument_h___ */
|
||||
|
|
|
@ -422,7 +422,7 @@ nsDocumentEncoder::SerializeNodeEnd(nsINode* aNode,
|
|||
return NS_OK;
|
||||
|
||||
if (aNode->IsElement()) {
|
||||
mSerializer->AppendElementEnd(static_cast<nsIContent*>(aNode), aStr);
|
||||
mSerializer->AppendElementEnd(aNode->AsElement(), aStr);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -1115,11 +1115,10 @@ nsGenericDOMDataNode::WalkContentStyleRules(nsRuleWalker* aRuleWalker)
|
|||
}
|
||||
|
||||
#ifdef MOZ_SMIL
|
||||
nsresult
|
||||
nsGenericDOMDataNode::GetSMILOverrideStyle(nsIDOMCSSStyleDeclaration** aStyle)
|
||||
nsIDOMCSSStyleDeclaration*
|
||||
nsGenericDOMDataNode::GetSMILOverrideStyle()
|
||||
{
|
||||
*aStyle = nsnull;
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
css::StyleRule*
|
||||
|
|
|
@ -234,7 +234,7 @@ public:
|
|||
{
|
||||
return nsnull;
|
||||
}
|
||||
virtual nsresult GetSMILOverrideStyle(nsIDOMCSSStyleDeclaration** aStyle);
|
||||
virtual nsIDOMCSSStyleDeclaration* GetSMILOverrideStyle();
|
||||
virtual mozilla::css::StyleRule* GetSMILOverrideStyleRule();
|
||||
virtual nsresult SetSMILOverrideStyleRule(mozilla::css::StyleRule* aStyleRule,
|
||||
PRBool aNotify);
|
||||
|
|
|
@ -3349,19 +3349,16 @@ nsGenericElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker)
|
|||
}
|
||||
|
||||
#ifdef MOZ_SMIL
|
||||
nsresult
|
||||
nsGenericElement::GetSMILOverrideStyle(nsIDOMCSSStyleDeclaration** aStyle)
|
||||
nsIDOMCSSStyleDeclaration*
|
||||
nsGenericElement::GetSMILOverrideStyle()
|
||||
{
|
||||
nsGenericElement::nsDOMSlots *slots = DOMSlots();
|
||||
|
||||
if (!slots->mSMILOverrideStyle) {
|
||||
slots->mSMILOverrideStyle = new nsDOMCSSAttributeDeclaration(this, PR_TRUE);
|
||||
NS_ENSURE_TRUE(slots->mSMILOverrideStyle, NS_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
// Why bother with QI?
|
||||
NS_ADDREF(*aStyle = slots->mSMILOverrideStyle);
|
||||
return NS_OK;
|
||||
return slots->mSMILOverrideStyle;
|
||||
}
|
||||
|
||||
css::StyleRule*
|
||||
|
@ -3564,7 +3561,7 @@ nsINode::doInsertChildAt(nsIContent* aKid, PRUint32 aIndex,
|
|||
rv = kid->GetNodeType(&nodeType);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIDOM3Document> domDoc = do_QueryInterface(GetOwnerDoc());
|
||||
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(GetOwnerDoc());
|
||||
|
||||
// DocumentType nodes are the only nodes that can have a null
|
||||
// ownerDocument according to the DOM spec, and we need to allow
|
||||
|
@ -4075,7 +4072,7 @@ nsINode::ReplaceOrInsertBefore(PRBool aReplace, nsINode* aNewChild,
|
|||
if (!HasSameOwnerDoc(newContent) &&
|
||||
(nodeType != nsIDOMNode::DOCUMENT_TYPE_NODE ||
|
||||
newContent->GetOwnerDoc())) {
|
||||
nsCOMPtr<nsIDOM3Document> domDoc = do_QueryInterface(doc);
|
||||
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
|
||||
|
||||
if (domDoc) {
|
||||
nsresult rv;
|
||||
|
@ -4766,7 +4763,7 @@ nsGenericElement::SetAttrAndNotify(PRInt32 aNamespaceID,
|
|||
stateMask ^= IntrinsicState();
|
||||
if (document && !stateMask.IsEmpty()) {
|
||||
MOZ_AUTO_DOC_UPDATE(document, UPDATE_CONTENT_STATE, aNotify);
|
||||
document->ContentStatesChanged(this, nsnull, stateMask);
|
||||
document->ContentStateChanged(this, stateMask);
|
||||
}
|
||||
nsNodeUtils::AttributeChanged(this, aNamespaceID, aName, aModType);
|
||||
}
|
||||
|
@ -5013,7 +5010,7 @@ nsGenericElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
|
|||
stateMask ^= IntrinsicState();
|
||||
if (document && !stateMask.IsEmpty()) {
|
||||
MOZ_AUTO_DOC_UPDATE(document, UPDATE_CONTENT_STATE, aNotify);
|
||||
document->ContentStatesChanged(this, nsnull, stateMask);
|
||||
document->ContentStateChanged(this, stateMask);
|
||||
}
|
||||
nsNodeUtils::AttributeChanged(this, aNameSpaceID, aName,
|
||||
nsIDOMMutationEvent::REMOVAL);
|
||||
|
@ -5486,8 +5483,7 @@ nsGenericElement::GetLinkTarget(nsAString& aTarget)
|
|||
static nsresult
|
||||
ParseSelectorList(nsINode* aNode,
|
||||
const nsAString& aSelectorString,
|
||||
nsCSSSelectorList** aSelectorList,
|
||||
nsPresContext** aPresContext)
|
||||
nsCSSSelectorList** aSelectorList)
|
||||
{
|
||||
NS_ENSURE_ARG(aNode);
|
||||
|
||||
|
@ -5518,117 +5514,9 @@ ParseSelectorList(nsINode* aNode,
|
|||
} while (*slot);
|
||||
*aSelectorList = selectorList;
|
||||
|
||||
// It's not strictly necessary to have a prescontext here, but it's
|
||||
// a bit of an optimization for various stuff.
|
||||
*aPresContext = nsnull;
|
||||
nsIPresShell* shell = doc->GetShell();
|
||||
if (shell) {
|
||||
*aPresContext = shell->GetPresContext();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback to be called as we iterate over the tree and match elements. If
|
||||
* the callbacks returns false, the iteration should be stopped.
|
||||
*/
|
||||
typedef PRBool
|
||||
(* ElementMatchedCallback)(nsIContent* aMatchingElement, void* aClosure);
|
||||
|
||||
// returning false means stop iteration
|
||||
static PRBool
|
||||
TryMatchingElementsInSubtree(nsINode* aRoot,
|
||||
RuleProcessorData* aParentData,
|
||||
nsPresContext* aPresContext,
|
||||
nsCSSSelectorList* aSelectorList,
|
||||
ElementMatchedCallback aCallback,
|
||||
void* aClosure)
|
||||
{
|
||||
/* To improve the performance of '+' and '~' combinators and the :nth-*
|
||||
* selectors, we keep track of the immediately previous sibling data. That's
|
||||
* cheaper than heap-allocating all the datas and keeping track of them all,
|
||||
* and helps a good bit in the common cases. We also keep track of the whole
|
||||
* parent data chain, since we have those Around anyway */
|
||||
union { char c[2 * sizeof(RuleProcessorData)]; void *p; } databuf;
|
||||
RuleProcessorData* prevSibling = nsnull;
|
||||
RuleProcessorData* data = reinterpret_cast<RuleProcessorData*>(databuf.c);
|
||||
|
||||
PRBool continueIteration = PR_TRUE;
|
||||
for (nsINode::ChildIterator iter(aRoot); !iter.IsDone(); iter.Next()) {
|
||||
nsIContent* kid = iter;
|
||||
if (!kid->IsElement()) {
|
||||
continue;
|
||||
}
|
||||
/* See whether we match */
|
||||
new (data) RuleProcessorData(aPresContext, kid->AsElement(), nsnull);
|
||||
NS_ASSERTION(!data->mParentData, "Shouldn't happen");
|
||||
NS_ASSERTION(!data->mPreviousSiblingData, "Shouldn't happen");
|
||||
data->mParentData = aParentData;
|
||||
data->mPreviousSiblingData = prevSibling;
|
||||
|
||||
if (nsCSSRuleProcessor::SelectorListMatches(*data, aSelectorList)) {
|
||||
continueIteration = (*aCallback)(kid, aClosure);
|
||||
}
|
||||
|
||||
if (continueIteration) {
|
||||
continueIteration =
|
||||
TryMatchingElementsInSubtree(kid, data, aPresContext, aSelectorList,
|
||||
aCallback, aClosure);
|
||||
}
|
||||
|
||||
/* Clear out the parent and previous sibling data if we set them, so that
|
||||
* ~RuleProcessorData won't try to delete a placement-new'd object. Make
|
||||
* sure this happens before our possible early break. Note that we can
|
||||
* have null aParentData but non-null data->mParentData if we're scoped to
|
||||
* an element. However, prevSibling and data->mPreviousSiblingData must
|
||||
* always match.
|
||||
*/
|
||||
NS_ASSERTION(!aParentData || data->mParentData == aParentData,
|
||||
"Unexpected parent");
|
||||
NS_ASSERTION(data->mPreviousSiblingData == prevSibling,
|
||||
"Unexpected prev sibling");
|
||||
data->mPreviousSiblingData = nsnull;
|
||||
if (prevSibling) {
|
||||
if (aParentData) {
|
||||
prevSibling->mParentData = nsnull;
|
||||
}
|
||||
prevSibling->~RuleProcessorData();
|
||||
} else {
|
||||
/* This is the first time through, so point |prevSibling| to the location
|
||||
we want to have |data| end up pointing to. */
|
||||
prevSibling = data + 1;
|
||||
}
|
||||
|
||||
/* Now swap |prevSibling| and |data|. Again, before the early break */
|
||||
RuleProcessorData* temp = prevSibling;
|
||||
prevSibling = data;
|
||||
data = temp;
|
||||
if (!continueIteration) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (prevSibling) {
|
||||
if (aParentData) {
|
||||
prevSibling->mParentData = nsnull;
|
||||
}
|
||||
/* Make sure to clean this up */
|
||||
prevSibling->~RuleProcessorData();
|
||||
}
|
||||
|
||||
return continueIteration;
|
||||
}
|
||||
|
||||
static PRBool
|
||||
FindFirstMatchingElement(nsIContent* aMatchingElement,
|
||||
void* aClosure)
|
||||
{
|
||||
NS_PRECONDITION(aMatchingElement && aClosure, "How did that happen?");
|
||||
nsIContent** slot = static_cast<nsIContent**>(aClosure);
|
||||
*slot = aMatchingElement;
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsIContent*
|
||||
nsGenericElement::doQuerySelector(nsINode* aRoot, const nsAString& aSelector,
|
||||
|
@ -5637,26 +5525,25 @@ nsGenericElement::doQuerySelector(nsINode* aRoot, const nsAString& aSelector,
|
|||
NS_PRECONDITION(aResult, "Null out param?");
|
||||
|
||||
nsAutoPtr<nsCSSSelectorList> selectorList;
|
||||
nsPresContext* presContext;
|
||||
*aResult = ParseSelectorList(aRoot, aSelector,
|
||||
getter_Transfers(selectorList),
|
||||
&presContext);
|
||||
getter_Transfers(selectorList));
|
||||
NS_ENSURE_SUCCESS(*aResult, nsnull);
|
||||
|
||||
nsIContent* foundElement = nsnull;
|
||||
TryMatchingElementsInSubtree(aRoot, nsnull, presContext, selectorList,
|
||||
FindFirstMatchingElement, &foundElement);
|
||||
TreeMatchContext matchingContext(PR_FALSE,
|
||||
nsRuleWalker::eRelevantLinkUnvisited,
|
||||
aRoot->GetOwnerDoc());
|
||||
for (nsIContent* cur = aRoot->GetFirstChild();
|
||||
cur;
|
||||
cur = cur->GetNextNode(aRoot)) {
|
||||
if (cur->IsElement() &&
|
||||
nsCSSRuleProcessor::SelectorListMatches(cur->AsElement(),
|
||||
matchingContext,
|
||||
selectorList)) {
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
|
||||
return foundElement;
|
||||
}
|
||||
|
||||
static PRBool
|
||||
AppendAllMatchingElements(nsIContent* aMatchingElement,
|
||||
void* aClosure)
|
||||
{
|
||||
NS_PRECONDITION(aMatchingElement && aClosure, "How did that happen?");
|
||||
static_cast<nsBaseContentList*>(aClosure)->AppendElement(aMatchingElement);
|
||||
return PR_TRUE;
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
@ -5672,14 +5559,23 @@ nsGenericElement::doQuerySelectorAll(nsINode* aRoot,
|
|||
NS_ADDREF(*aReturn = contentList);
|
||||
|
||||
nsAutoPtr<nsCSSSelectorList> selectorList;
|
||||
nsPresContext* presContext;
|
||||
nsresult rv = ParseSelectorList(aRoot, aSelector,
|
||||
getter_Transfers(selectorList),
|
||||
&presContext);
|
||||
getter_Transfers(selectorList));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
TryMatchingElementsInSubtree(aRoot, nsnull, presContext, selectorList,
|
||||
AppendAllMatchingElements, contentList);
|
||||
TreeMatchContext matchingContext(PR_FALSE,
|
||||
nsRuleWalker::eRelevantLinkUnvisited,
|
||||
aRoot->GetOwnerDoc());
|
||||
for (nsIContent* cur = aRoot->GetFirstChild();
|
||||
cur;
|
||||
cur = cur->GetNextNode(aRoot)) {
|
||||
if (cur->IsElement() &&
|
||||
nsCSSRuleProcessor::SelectorListMatches(cur->AsElement(),
|
||||
matchingContext,
|
||||
selectorList)) {
|
||||
contentList->AppendElement(cur);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -5688,15 +5584,16 @@ PRBool
|
|||
nsGenericElement::MozMatchesSelector(const nsAString& aSelector, nsresult* aResult)
|
||||
{
|
||||
nsAutoPtr<nsCSSSelectorList> selectorList;
|
||||
nsPresContext* presContext;
|
||||
PRBool matches = PR_FALSE;
|
||||
|
||||
*aResult = ParseSelectorList(this, aSelector, getter_Transfers(selectorList),
|
||||
&presContext);
|
||||
*aResult = ParseSelectorList(this, aSelector, getter_Transfers(selectorList));
|
||||
|
||||
if (NS_SUCCEEDED(*aResult)) {
|
||||
RuleProcessorData data(presContext, this, nsnull);
|
||||
matches = nsCSSRuleProcessor::SelectorListMatches(data, selectorList);
|
||||
TreeMatchContext matchingContext(PR_FALSE,
|
||||
nsRuleWalker::eRelevantLinkUnvisited,
|
||||
GetOwnerDoc());
|
||||
matches = nsCSSRuleProcessor::SelectorListMatches(this, matchingContext,
|
||||
selectorList);
|
||||
}
|
||||
|
||||
return matches;
|
||||
|
|
|
@ -455,7 +455,7 @@ public:
|
|||
{
|
||||
return nsnull;
|
||||
}
|
||||
virtual nsresult GetSMILOverrideStyle(nsIDOMCSSStyleDeclaration** aStyle);
|
||||
virtual nsIDOMCSSStyleDeclaration* GetSMILOverrideStyle();
|
||||
virtual mozilla::css::StyleRule* GetSMILOverrideStyleRule();
|
||||
virtual nsresult SetSMILOverrideStyleRule(mozilla::css::StyleRule* aStyleRule,
|
||||
PRBool aNotify);
|
||||
|
|
|
@ -72,6 +72,9 @@
|
|||
#include "nsIEditorDocShell.h"
|
||||
#include "nsIEditor.h"
|
||||
#include "nsIHTMLEditor.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
static const PRInt32 kLongLineLen = 128;
|
||||
|
||||
|
@ -228,8 +231,8 @@ nsHTMLContentSerializer::SerializeHTMLAttributes(nsIContent* aContent,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLContentSerializer::AppendElementStart(nsIContent *aElement,
|
||||
nsIContent *aOriginalElement,
|
||||
nsHTMLContentSerializer::AppendElementStart(Element* aElement,
|
||||
Element* aOriginalElement,
|
||||
nsAString& aStr)
|
||||
{
|
||||
NS_ENSURE_ARG(aElement);
|
||||
|
@ -342,7 +345,7 @@ nsHTMLContentSerializer::AppendElementStart(nsIContent *aElement,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLContentSerializer::AppendElementEnd(nsIContent *aElement,
|
||||
nsHTMLContentSerializer::AppendElementEnd(Element* aElement,
|
||||
nsAString& aStr)
|
||||
{
|
||||
NS_ENSURE_ARG(aElement);
|
||||
|
|
|
@ -57,11 +57,11 @@ class nsHTMLContentSerializer : public nsXHTMLContentSerializer {
|
|||
nsHTMLContentSerializer();
|
||||
virtual ~nsHTMLContentSerializer();
|
||||
|
||||
NS_IMETHOD AppendElementStart(nsIContent *aElement,
|
||||
nsIContent *aOriginalElement,
|
||||
NS_IMETHOD AppendElementStart(mozilla::dom::Element* aElement,
|
||||
mozilla::dom::Element* aOriginalElement,
|
||||
nsAString& aStr);
|
||||
|
||||
NS_IMETHOD AppendElementEnd(nsIContent *aElement,
|
||||
|
||||
NS_IMETHOD AppendElementEnd(mozilla::dom::Element* aElement,
|
||||
nsAString& aStr);
|
||||
|
||||
NS_IMETHOD AppendDocumentStart(nsIDocument *aDocument,
|
||||
|
|
|
@ -829,7 +829,7 @@ nsImageLoadingContent::UpdateImageState(PRBool aNotify)
|
|||
nsEventStates changedBits = oldState ^ ImageState();
|
||||
if (!changedBits.IsEmpty()) {
|
||||
mozAutoDocUpdate upd(doc, UPDATE_CONTENT_STATE, PR_TRUE);
|
||||
doc->ContentStatesChanged(thisContent, nsnull, changedBits);
|
||||
doc->ContentStateChanged(thisContent, changedBits);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1657,7 +1657,7 @@ nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType,
|
|||
|
||||
{
|
||||
mozAutoDocUpdate upd(doc, UPDATE_CONTENT_STATE, PR_TRUE);
|
||||
doc->ContentStatesChanged(thisContent, nsnull, changedBits);
|
||||
doc->ContentStateChanged(thisContent, changedBits);
|
||||
}
|
||||
if (aSync) {
|
||||
// Make sure that frames are actually constructed, and do it after
|
||||
|
|
|
@ -57,6 +57,9 @@
|
|||
#include "nsUnicharUtils.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsIParserService.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
#define PREF_STRUCTS "converter.html2txt.structs"
|
||||
#define PREF_HEADER_STRATEGY "converter.html2txt.header_strategy"
|
||||
|
@ -381,8 +384,8 @@ nsPlainTextSerializer::AppendCDATASection(nsIContent* aCDATASection,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPlainTextSerializer::AppendElementStart(nsIContent *aElement,
|
||||
nsIContent *aOriginalElement,
|
||||
nsPlainTextSerializer::AppendElementStart(Element* aElement,
|
||||
Element* aOriginalElement,
|
||||
nsAString& aStr)
|
||||
{
|
||||
NS_ENSURE_ARG(aElement);
|
||||
|
@ -414,7 +417,7 @@ nsPlainTextSerializer::AppendElementStart(nsIContent *aElement,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPlainTextSerializer::AppendElementEnd(nsIContent *aElement,
|
||||
nsPlainTextSerializer::AppendElementEnd(Element* aElement,
|
||||
nsAString& aStr)
|
||||
{
|
||||
NS_ENSURE_ARG(aElement);
|
||||
|
|
|
@ -56,6 +56,12 @@
|
|||
#include "nsIDocumentEncoder.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class Element;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
class nsPlainTextSerializer : public nsIContentSerializer,
|
||||
public nsIHTMLContentSink,
|
||||
public nsIHTMLToTextSink
|
||||
|
@ -84,10 +90,10 @@ public:
|
|||
PRInt32 aEndOffset, nsAString& aStr) { return NS_OK; }
|
||||
NS_IMETHOD AppendDoctype(nsIContent *aDoctype,
|
||||
nsAString& aStr) { return NS_OK; }
|
||||
NS_IMETHOD AppendElementStart(nsIContent *aElement,
|
||||
nsIContent *aOriginalElement,
|
||||
NS_IMETHOD AppendElementStart(mozilla::dom::Element* aElement,
|
||||
mozilla::dom::Element* aOriginalElement,
|
||||
nsAString& aStr);
|
||||
NS_IMETHOD AppendElementEnd(nsIContent *aElement,
|
||||
NS_IMETHOD AppendElementEnd(mozilla::dom::Element* aElement,
|
||||
nsAString& aStr);
|
||||
NS_IMETHOD Flush(nsAString& aStr);
|
||||
|
||||
|
@ -235,7 +241,7 @@ protected:
|
|||
section.
|
||||
mHeaderCounter[1] for <h1> etc. */
|
||||
|
||||
nsCOMPtr<nsIContent> mContent;
|
||||
nsRefPtr<mozilla::dom::Element> mContent;
|
||||
|
||||
// For handling table rows
|
||||
nsAutoTArray<PRPackedBool, 8> mHasWrittenCellsForRow;
|
||||
|
|
|
@ -220,8 +220,8 @@ nsSyncLoader::LoadDocument(nsIChannel* aChannel,
|
|||
}
|
||||
|
||||
if (aLoaderPrincipal) {
|
||||
listener = new nsCrossSiteListenerProxy(listener, aLoaderPrincipal,
|
||||
mChannel, PR_FALSE, &rv);
|
||||
listener = new nsCORSListenerProxy(listener, aLoaderPrincipal,
|
||||
mChannel, PR_FALSE, &rv);
|
||||
NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include "nsBidiUtils.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "nsUTF8Utils.h"
|
||||
#include "mozilla/SSE.h"
|
||||
|
||||
#define TEXTFRAG_WHITE_AFTER_NEWLINE 50
|
||||
#define TEXTFRAG_MAX_NEWLINES 7
|
||||
|
@ -144,6 +145,69 @@ nsTextFragment::operator=(const nsTextFragment& aOther)
|
|||
return *this;
|
||||
}
|
||||
|
||||
static inline PRBool
|
||||
Is8BitUnvectorized(const PRUnichar *str, const PRUnichar *end)
|
||||
{
|
||||
#if PR_BYTES_PER_WORD == 4
|
||||
const size_t mask = 0xff00ff00;
|
||||
const PRUint32 alignMask = 0x3;
|
||||
const PRUint32 numUnicharsPerWord = 2;
|
||||
#elif PR_BYTES_PER_WORD == 8
|
||||
const size_t mask = 0xff00ff00ff00ff00;
|
||||
const PRUint32 alignMask = 0x7;
|
||||
const PRUint32 numUnicharsPerWord = 4;
|
||||
#else
|
||||
#error Unknown platform!
|
||||
#endif
|
||||
|
||||
const PRInt32 len = end - str;
|
||||
PRInt32 i = 0;
|
||||
|
||||
// Align ourselves to a word boundary.
|
||||
PRInt32 alignLen =
|
||||
PR_MIN(len, PRInt32(((-NS_PTR_TO_UINT32(str)) & alignMask) / sizeof(PRUnichar)));
|
||||
for (; i < alignLen; i++) {
|
||||
if (str[i] > 255)
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
// Check one word at a time.
|
||||
const PRInt32 wordWalkEnd = ((len - i) / numUnicharsPerWord) * numUnicharsPerWord;
|
||||
for (; i < wordWalkEnd; i += numUnicharsPerWord) {
|
||||
const size_t word = *reinterpret_cast<const size_t*>(str + i);
|
||||
if (word & mask)
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
// Take care of the remainder one character at a time.
|
||||
for (; i < len; i++) {
|
||||
if (str[i] > 255)
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
#ifdef MOZILLA_MAY_SUPPORT_SSE2
|
||||
namespace mozilla {
|
||||
namespace SSE2 {
|
||||
PRBool Is8Bit(const PRUnichar *str, const PRUnichar *end);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline PRBool
|
||||
Is8Bit(const PRUnichar *str, const PRUnichar *end)
|
||||
{
|
||||
#ifdef MOZILLA_MAY_SUPPORT_SSE2
|
||||
if (mozilla::supports_sse2()) {
|
||||
return mozilla::SSE2::Is8Bit(str, end);
|
||||
}
|
||||
#endif
|
||||
|
||||
return Is8BitUnvectorized(str, end);
|
||||
}
|
||||
|
||||
void
|
||||
nsTextFragment::SetTo(const PRUnichar* aBuffer, PRInt32 aLength)
|
||||
{
|
||||
|
@ -203,14 +267,7 @@ nsTextFragment::SetTo(const PRUnichar* aBuffer, PRInt32 aLength)
|
|||
}
|
||||
|
||||
// See if we need to store the data in ucs2 or not
|
||||
PRBool need2 = PR_FALSE;
|
||||
while (ucp < uend) {
|
||||
PRUnichar ch = *ucp++;
|
||||
if (ch >= 256) {
|
||||
need2 = PR_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
PRBool need2 = !Is8Bit(ucp, uend);
|
||||
|
||||
if (need2) {
|
||||
// Use ucs2 storage because we have to
|
||||
|
@ -227,9 +284,7 @@ nsTextFragment::SetTo(const PRUnichar* aBuffer, PRInt32 aLength)
|
|||
}
|
||||
|
||||
// Copy data
|
||||
// Use the same copying code we use elsewhere; it's likely to be
|
||||
// carefully tuned.
|
||||
LossyConvertEncoding<PRUnichar, char> converter(buff);
|
||||
LossyConvertEncoding16to8 converter(buff);
|
||||
copy_string(aBuffer, aBuffer+aLength, converter);
|
||||
m1b = buff;
|
||||
}
|
||||
|
@ -260,9 +315,8 @@ nsTextFragment::CopyTo(PRUnichar *aDest, PRInt32 aOffset, PRInt32 aCount)
|
|||
} else {
|
||||
const char *cp = m1b + aOffset;
|
||||
const char *end = cp + aCount;
|
||||
while (cp < end) {
|
||||
*aDest++ = (unsigned char)(*cp++);
|
||||
}
|
||||
LossyConvertEncoding8to16 converter(aDest);
|
||||
copy_string(cp, end, converter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -296,18 +350,7 @@ nsTextFragment::Append(const PRUnichar* aBuffer, PRUint32 aLength)
|
|||
|
||||
// Current string is a 1-byte string, check if the new data fits in one byte too.
|
||||
|
||||
const PRUnichar* ucp = aBuffer;
|
||||
const PRUnichar* uend = ucp + aLength;
|
||||
PRBool need2 = PR_FALSE;
|
||||
while (ucp < uend) {
|
||||
PRUnichar ch = *ucp++;
|
||||
if (ch >= 256) {
|
||||
need2 = PR_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (need2) {
|
||||
if (!Is8Bit(aBuffer, aBuffer + aLength)) {
|
||||
// The old data was 1-byte, but the new is not so we have to expand it
|
||||
// all to 2-byte
|
||||
PRUnichar* buff = (PRUnichar*)nsMemory::Alloc((mState.mLength + aLength) *
|
||||
|
@ -316,11 +359,10 @@ nsTextFragment::Append(const PRUnichar* aBuffer, PRUint32 aLength)
|
|||
return;
|
||||
}
|
||||
|
||||
// Copy data
|
||||
for (PRUint32 i = 0; i < mState.mLength; ++i) {
|
||||
buff[i] = (unsigned char)m1b[i];
|
||||
}
|
||||
|
||||
// Copy data into buff
|
||||
LossyConvertEncoding8to16 converter(buff);
|
||||
copy_string(m1b, m1b+mState.mLength, converter);
|
||||
|
||||
memcpy(buff + mState.mLength, aBuffer, aLength * sizeof(PRUnichar));
|
||||
|
||||
mState.mLength += aLength;
|
||||
|
@ -354,10 +396,10 @@ nsTextFragment::Append(const PRUnichar* aBuffer, PRUint32 aLength)
|
|||
memcpy(buff, m1b, mState.mLength);
|
||||
mState.mInHeap = PR_TRUE;
|
||||
}
|
||||
|
||||
for (PRUint32 i = 0; i < aLength; ++i) {
|
||||
buff[mState.mLength + i] = (char)aBuffer[i];
|
||||
}
|
||||
|
||||
// Copy aBuffer into buff.
|
||||
LossyConvertEncoding16to8 converter(buff + mState.mLength);
|
||||
copy_string(aBuffer, aBuffer + aLength, converter);
|
||||
|
||||
m1b = buff;
|
||||
mState.mLength += aLength;
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
// This file should only be compiled if you're on x86 or x86_64. Additionally,
|
||||
// you'll need to compile this file with -msse2 if you're using gcc.
|
||||
|
||||
#include <emmintrin.h>
|
||||
#include "nscore.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace SSE2 {
|
||||
|
||||
static inline bool
|
||||
is_zero (__m128i x)
|
||||
{
|
||||
return
|
||||
_mm_movemask_epi8(_mm_cmpeq_epi8(x, _mm_setzero_si128())) == 0xffff;
|
||||
}
|
||||
|
||||
PRBool
|
||||
Is8Bit(const PRUnichar *str, const PRUnichar *end)
|
||||
{
|
||||
const PRUint32 numUnicharsPerVector = 8;
|
||||
|
||||
#if PR_BYTES_PER_WORD == 4
|
||||
const size_t mask = 0xff00ff00;
|
||||
const PRUint32 numUnicharsPerWord = 2;
|
||||
#elif PR_BYTES_PER_WORD == 8
|
||||
const size_t mask = 0xff00ff00ff00ff00;
|
||||
const PRUint32 numUnicharsPerWord = 4;
|
||||
#else
|
||||
#error Unknown platform!
|
||||
#endif
|
||||
|
||||
const PRInt32 len = end - str;
|
||||
PRInt32 i = 0;
|
||||
|
||||
// Align ourselves to a 16-byte boundary, as required by _mm_load_si128
|
||||
// (i.e. MOVDQA).
|
||||
PRInt32 alignLen =
|
||||
PR_MIN(len, PRInt32(((-NS_PTR_TO_UINT32(str)) & 0xf) / sizeof(PRUnichar)));
|
||||
for (; i < alignLen; i++) {
|
||||
if (str[i] > 255)
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
// Check one XMM register (16 bytes) at a time.
|
||||
const PRInt32 vectWalkEnd = ((len - i) / numUnicharsPerVector) * numUnicharsPerVector;
|
||||
__m128i vectmask = _mm_set1_epi16(0xff00);
|
||||
for(; i < vectWalkEnd; i += numUnicharsPerVector) {
|
||||
const __m128i vect = *reinterpret_cast<const __m128i*>(str + i);
|
||||
if (!is_zero(_mm_and_si128(vect, vectmask)))
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
// Check one word at a time.
|
||||
const PRInt32 wordWalkEnd = ((len - i) / numUnicharsPerWord) * numUnicharsPerWord;
|
||||
for(; i < wordWalkEnd; i += numUnicharsPerWord) {
|
||||
const size_t word = *reinterpret_cast<const size_t*>(str + i);
|
||||
if (word & mask)
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
// Take care of the remainder one character at a time.
|
||||
for (; i < len; i++) {
|
||||
if (str[i] > 255) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
} // namespace SSE2
|
||||
} // namespace mozilla
|
|
@ -64,6 +64,9 @@
|
|||
#include "nsContentUtils.h"
|
||||
#include "nsAttrName.h"
|
||||
#include "nsILineBreaker.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
static const char kMozStr[] = "moz";
|
||||
|
||||
|
@ -915,8 +918,8 @@ nsXMLContentSerializer::SerializeAttributes(nsIContent* aContent,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXMLContentSerializer::AppendElementStart(nsIContent *aElement,
|
||||
nsIContent *aOriginalElement,
|
||||
nsXMLContentSerializer::AppendElementStart(Element* aElement,
|
||||
Element* aOriginalElement,
|
||||
nsAString& aStr)
|
||||
{
|
||||
NS_ENSURE_ARG(aElement);
|
||||
|
@ -1016,7 +1019,7 @@ nsXMLContentSerializer::AppendEndOfElementStart(nsIContent *aOriginalElement,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXMLContentSerializer::AppendElementEnd(nsIContent *aElement,
|
||||
nsXMLContentSerializer::AppendElementEnd(Element* aElement,
|
||||
nsAString& aStr)
|
||||
{
|
||||
NS_ENSURE_ARG(aElement);
|
||||
|
|
|
@ -88,11 +88,11 @@ class nsXMLContentSerializer : public nsIContentSerializer {
|
|||
NS_IMETHOD AppendDoctype(nsIContent *aDoctype,
|
||||
nsAString& aStr);
|
||||
|
||||
NS_IMETHOD AppendElementStart(nsIContent *aElement,
|
||||
nsIContent *aOriginalElement,
|
||||
NS_IMETHOD AppendElementStart(mozilla::dom::Element* aElement,
|
||||
mozilla::dom::Element* aOriginalElement,
|
||||
nsAString& aStr);
|
||||
|
||||
NS_IMETHOD AppendElementEnd(nsIContent *aElement,
|
||||
|
||||
NS_IMETHOD AppendElementEnd(mozilla::dom::Element* aElement,
|
||||
nsAString& aStr);
|
||||
|
||||
NS_IMETHOD Flush(nsAString& aStr) { return NS_OK; }
|
||||
|
|
|
@ -87,7 +87,6 @@
|
|||
#include "nsCrossSiteListenerProxy.h"
|
||||
#include "nsDOMError.h"
|
||||
#include "nsIHTMLDocument.h"
|
||||
#include "nsIDOM3Document.h"
|
||||
#include "nsIMultiPartChannel.h"
|
||||
#include "nsIScriptObjectPrincipal.h"
|
||||
#include "nsIStorageStream.h"
|
||||
|
@ -146,8 +145,6 @@
|
|||
XML_HTTP_REQUEST_SENT | \
|
||||
XML_HTTP_REQUEST_STOPPED)
|
||||
|
||||
#define ACCESS_CONTROL_CACHE_SIZE 100
|
||||
|
||||
#define NS_BADCERTHANDLER_CONTRACTID \
|
||||
"@mozilla.org/content/xmlhttprequest-bad-cert-handler;1"
|
||||
|
||||
|
@ -286,225 +283,6 @@ nsMultipartProxyListener::OnDataAvailable(nsIRequest *aRequest,
|
|||
count);
|
||||
}
|
||||
|
||||
// Class used as streamlistener and notification callback when
|
||||
// doing the initial GET request for an access-control check
|
||||
class nsACProxyListener : public nsIStreamListener,
|
||||
public nsIInterfaceRequestor,
|
||||
public nsIChannelEventSink
|
||||
{
|
||||
public:
|
||||
nsACProxyListener(nsIChannel* aOuterChannel,
|
||||
nsIStreamListener* aOuterListener,
|
||||
nsISupports* aOuterContext,
|
||||
nsIPrincipal* aReferrerPrincipal,
|
||||
const nsACString& aRequestMethod,
|
||||
PRBool aWithCredentials)
|
||||
: mOuterChannel(aOuterChannel), mOuterListener(aOuterListener),
|
||||
mOuterContext(aOuterContext), mReferrerPrincipal(aReferrerPrincipal),
|
||||
mRequestMethod(aRequestMethod), mWithCredentials(aWithCredentials)
|
||||
{ }
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
NS_DECL_NSICHANNELEVENTSINK
|
||||
|
||||
private:
|
||||
void AddResultToCache(nsIRequest* aRequest);
|
||||
|
||||
nsCOMPtr<nsIChannel> mOuterChannel;
|
||||
nsCOMPtr<nsIStreamListener> mOuterListener;
|
||||
nsCOMPtr<nsISupports> mOuterContext;
|
||||
nsCOMPtr<nsIPrincipal> mReferrerPrincipal;
|
||||
nsCString mRequestMethod;
|
||||
PRBool mWithCredentials;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS4(nsACProxyListener, nsIStreamListener, nsIRequestObserver,
|
||||
nsIInterfaceRequestor, nsIChannelEventSink)
|
||||
|
||||
void
|
||||
nsACProxyListener::AddResultToCache(nsIRequest *aRequest)
|
||||
{
|
||||
nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
|
||||
NS_ASSERTION(http, "Request was not http");
|
||||
|
||||
// The "Access-Control-Max-Age" header should return an age in seconds.
|
||||
nsCAutoString headerVal;
|
||||
http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Max-Age"),
|
||||
headerVal);
|
||||
if (headerVal.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sanitize the string. We only allow 'delta-seconds' as specified by
|
||||
// http://dev.w3.org/2006/waf/access-control (digits 0-9 with no leading or
|
||||
// trailing non-whitespace characters).
|
||||
PRUint32 age = 0;
|
||||
nsCSubstring::const_char_iterator iter, end;
|
||||
headerVal.BeginReading(iter);
|
||||
headerVal.EndReading(end);
|
||||
while (iter != end) {
|
||||
if (*iter < '0' || *iter > '9') {
|
||||
return;
|
||||
}
|
||||
age = age * 10 + (*iter - '0');
|
||||
// Cap at 24 hours. This also avoids overflow
|
||||
age = NS_MIN(age, 86400U);
|
||||
++iter;
|
||||
}
|
||||
|
||||
if (!age || !nsXMLHttpRequest::EnsureACCache()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// String seems fine, go ahead and cache.
|
||||
// Note that we have already checked that these headers follow the correct
|
||||
// syntax.
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
NS_GetFinalChannelURI(http, getter_AddRefs(uri));
|
||||
|
||||
// PR_Now gives microseconds
|
||||
PRTime expirationTime = PR_Now() + (PRUint64)age * PR_USEC_PER_SEC;
|
||||
|
||||
nsAccessControlLRUCache::CacheEntry* entry =
|
||||
nsXMLHttpRequest::sAccessControlCache->
|
||||
GetEntry(uri, mReferrerPrincipal, mWithCredentials, PR_TRUE);
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The "Access-Control-Allow-Methods" header contains a comma separated
|
||||
// list of method names.
|
||||
http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Methods"),
|
||||
headerVal);
|
||||
|
||||
nsCCharSeparatedTokenizer methods(headerVal, ',');
|
||||
while(methods.hasMoreTokens()) {
|
||||
const nsDependentCSubstring& method = methods.nextToken();
|
||||
if (method.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
PRUint32 i;
|
||||
for (i = 0; i < entry->mMethods.Length(); ++i) {
|
||||
if (entry->mMethods[i].token.Equals(method)) {
|
||||
entry->mMethods[i].expirationTime = expirationTime;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == entry->mMethods.Length()) {
|
||||
nsAccessControlLRUCache::TokenTime* newMethod =
|
||||
entry->mMethods.AppendElement();
|
||||
if (!newMethod) {
|
||||
return;
|
||||
}
|
||||
|
||||
newMethod->token = method;
|
||||
newMethod->expirationTime = expirationTime;
|
||||
}
|
||||
}
|
||||
|
||||
// The "Access-Control-Allow-Headers" header contains a comma separated
|
||||
// list of method names.
|
||||
http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"),
|
||||
headerVal);
|
||||
|
||||
nsCCharSeparatedTokenizer headers(headerVal, ',');
|
||||
while(headers.hasMoreTokens()) {
|
||||
const nsDependentCSubstring& header = headers.nextToken();
|
||||
if (header.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
PRUint32 i;
|
||||
for (i = 0; i < entry->mHeaders.Length(); ++i) {
|
||||
if (entry->mHeaders[i].token.Equals(header)) {
|
||||
entry->mHeaders[i].expirationTime = expirationTime;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == entry->mHeaders.Length()) {
|
||||
nsAccessControlLRUCache::TokenTime* newHeader =
|
||||
entry->mHeaders.AppendElement();
|
||||
if (!newHeader) {
|
||||
return;
|
||||
}
|
||||
|
||||
newHeader->token = header;
|
||||
newHeader->expirationTime = expirationTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsACProxyListener::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
|
||||
{
|
||||
nsresult status;
|
||||
nsresult rv = aRequest->GetStatus(&status);
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = status;
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// Everything worked, try to cache and then fire off the actual request.
|
||||
AddResultToCache(aRequest);
|
||||
|
||||
rv = mOuterChannel->AsyncOpen(mOuterListener, mOuterContext);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
mOuterChannel->Cancel(rv);
|
||||
mOuterListener->OnStartRequest(mOuterChannel, mOuterContext);
|
||||
mOuterListener->OnStopRequest(mOuterChannel, mOuterContext, rv);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsACProxyListener::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
|
||||
nsresult aStatus)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/** nsIStreamListener methods **/
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsACProxyListener::OnDataAvailable(nsIRequest *aRequest,
|
||||
nsISupports *ctxt,
|
||||
nsIInputStream *inStr,
|
||||
PRUint32 sourceOffset,
|
||||
PRUint32 count)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsACProxyListener::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
|
||||
nsIChannel *aNewChannel,
|
||||
PRUint32 aFlags,
|
||||
nsIAsyncVerifyRedirectCallback *callback)
|
||||
{
|
||||
// Only internal redirects allowed for now.
|
||||
if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags))
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
|
||||
callback->OnRedirectVerifyCallback(NS_OK);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsACProxyListener::GetInterface(const nsIID & aIID, void **aResult)
|
||||
{
|
||||
return QueryInterface(aIID, aResult);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsXHREventTarget)
|
||||
|
@ -633,219 +411,11 @@ NS_INTERFACE_MAP_END_INHERITING(nsXHREventTarget)
|
|||
NS_IMPL_ADDREF_INHERITED(nsXMLHttpRequestUpload, nsXHREventTarget)
|
||||
NS_IMPL_RELEASE_INHERITED(nsXMLHttpRequestUpload, nsXHREventTarget)
|
||||
|
||||
void
|
||||
nsAccessControlLRUCache::CacheEntry::PurgeExpired(PRTime now)
|
||||
{
|
||||
PRUint32 i;
|
||||
for (i = 0; i < mMethods.Length(); ++i) {
|
||||
if (now >= mMethods[i].expirationTime) {
|
||||
mMethods.RemoveElementAt(i--);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < mHeaders.Length(); ++i) {
|
||||
if (now >= mHeaders[i].expirationTime) {
|
||||
mHeaders.RemoveElementAt(i--);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsAccessControlLRUCache::CacheEntry::CheckRequest(const nsCString& aMethod,
|
||||
const nsTArray<nsCString>& aHeaders)
|
||||
{
|
||||
PurgeExpired(PR_Now());
|
||||
|
||||
if (!aMethod.EqualsLiteral("GET") && !aMethod.EqualsLiteral("POST")) {
|
||||
PRUint32 i;
|
||||
for (i = 0; i < mMethods.Length(); ++i) {
|
||||
if (aMethod.Equals(mMethods[i].token))
|
||||
break;
|
||||
}
|
||||
if (i == mMethods.Length()) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
for (PRUint32 i = 0; i < aHeaders.Length(); ++i) {
|
||||
PRUint32 j;
|
||||
for (j = 0; j < mHeaders.Length(); ++j) {
|
||||
if (aHeaders[i].Equals(mHeaders[j].token,
|
||||
nsCaseInsensitiveCStringComparator())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == mHeaders.Length()) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
nsAccessControlLRUCache::CacheEntry*
|
||||
nsAccessControlLRUCache::GetEntry(nsIURI* aURI,
|
||||
nsIPrincipal* aPrincipal,
|
||||
PRBool aWithCredentials,
|
||||
PRBool aCreate)
|
||||
{
|
||||
nsCString key;
|
||||
if (!GetCacheKey(aURI, aPrincipal, aWithCredentials, key)) {
|
||||
NS_WARNING("Invalid cache key!");
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
CacheEntry* entry;
|
||||
|
||||
if (mTable.Get(key, &entry)) {
|
||||
// Entry already existed so just return it. Also update the LRU list.
|
||||
|
||||
// Move to the head of the list.
|
||||
PR_REMOVE_LINK(entry);
|
||||
PR_INSERT_LINK(entry, &mList);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
if (!aCreate) {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
// This is a new entry, allocate and insert into the table now so that any
|
||||
// failures don't cause items to be removed from a full cache.
|
||||
entry = new CacheEntry(key);
|
||||
if (!entry) {
|
||||
NS_WARNING("Failed to allocate new cache entry!");
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
if (!mTable.Put(key, entry)) {
|
||||
// Failed, clean up the new entry.
|
||||
delete entry;
|
||||
|
||||
NS_WARNING("Failed to add entry to the access control cache!");
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
PR_INSERT_LINK(entry, &mList);
|
||||
|
||||
NS_ASSERTION(mTable.Count() <= ACCESS_CONTROL_CACHE_SIZE + 1,
|
||||
"Something is borked, too many entries in the cache!");
|
||||
|
||||
// Now enforce the max count.
|
||||
if (mTable.Count() > ACCESS_CONTROL_CACHE_SIZE) {
|
||||
// Try to kick out all the expired entries.
|
||||
PRTime now = PR_Now();
|
||||
mTable.Enumerate(RemoveExpiredEntries, &now);
|
||||
|
||||
// If that didn't remove anything then kick out the least recently used
|
||||
// entry.
|
||||
if (mTable.Count() > ACCESS_CONTROL_CACHE_SIZE) {
|
||||
CacheEntry* lruEntry = static_cast<CacheEntry*>(PR_LIST_TAIL(&mList));
|
||||
PR_REMOVE_LINK(lruEntry);
|
||||
|
||||
// This will delete 'lruEntry'.
|
||||
mTable.Remove(lruEntry->mKey);
|
||||
|
||||
NS_ASSERTION(mTable.Count() == ACCESS_CONTROL_CACHE_SIZE,
|
||||
"Somehow tried to remove an entry that was never added!");
|
||||
}
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
void
|
||||
nsAccessControlLRUCache::RemoveEntries(nsIURI* aURI, nsIPrincipal* aPrincipal)
|
||||
{
|
||||
CacheEntry* entry;
|
||||
nsCString key;
|
||||
if (GetCacheKey(aURI, aPrincipal, PR_TRUE, key) &&
|
||||
mTable.Get(key, &entry)) {
|
||||
PR_REMOVE_LINK(entry);
|
||||
mTable.Remove(key);
|
||||
}
|
||||
|
||||
if (GetCacheKey(aURI, aPrincipal, PR_FALSE, key) &&
|
||||
mTable.Get(key, &entry)) {
|
||||
PR_REMOVE_LINK(entry);
|
||||
mTable.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsAccessControlLRUCache::Clear()
|
||||
{
|
||||
PR_INIT_CLIST(&mList);
|
||||
mTable.Clear();
|
||||
}
|
||||
|
||||
/* static */ PLDHashOperator
|
||||
nsAccessControlLRUCache::RemoveExpiredEntries(const nsACString& aKey,
|
||||
nsAutoPtr<CacheEntry>& aValue,
|
||||
void* aUserData)
|
||||
{
|
||||
PRTime* now = static_cast<PRTime*>(aUserData);
|
||||
|
||||
aValue->PurgeExpired(*now);
|
||||
|
||||
if (aValue->mHeaders.IsEmpty() &&
|
||||
aValue->mHeaders.IsEmpty()) {
|
||||
// Expired, remove from the list as well as the hash table.
|
||||
PR_REMOVE_LINK(aValue);
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
/* static */ PRBool
|
||||
nsAccessControlLRUCache::GetCacheKey(nsIURI* aURI,
|
||||
nsIPrincipal* aPrincipal,
|
||||
PRBool aWithCredentials,
|
||||
nsACString& _retval)
|
||||
{
|
||||
NS_ASSERTION(aURI, "Null uri!");
|
||||
NS_ASSERTION(aPrincipal, "Null principal!");
|
||||
|
||||
NS_NAMED_LITERAL_CSTRING(space, " ");
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
||||
|
||||
nsCAutoString scheme, host, port;
|
||||
if (uri) {
|
||||
uri->GetScheme(scheme);
|
||||
uri->GetHost(host);
|
||||
port.AppendInt(NS_GetRealPort(uri));
|
||||
}
|
||||
|
||||
nsCAutoString cred;
|
||||
if (aWithCredentials) {
|
||||
_retval.AssignLiteral("cred");
|
||||
}
|
||||
else {
|
||||
_retval.AssignLiteral("nocred");
|
||||
}
|
||||
|
||||
nsCAutoString spec;
|
||||
rv = aURI->GetSpec(spec);
|
||||
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
||||
|
||||
_retval.Assign(cred + space + scheme + space + host + space + port + space +
|
||||
spec);
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
/////////////////////////////////////////////
|
||||
|
||||
// Will be initialized in nsXMLHttpRequest::EnsureACCache.
|
||||
nsAccessControlLRUCache* nsXMLHttpRequest::sAccessControlCache = nsnull;
|
||||
|
||||
nsXMLHttpRequest::nsXMLHttpRequest()
|
||||
: mRequestObserver(nsnull), mState(XML_HTTP_REQUEST_UNINITIALIZED),
|
||||
mUploadTransferred(0), mUploadTotal(0), mUploadComplete(PR_TRUE),
|
||||
|
@ -985,7 +555,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXMLHttpRequest,
|
|||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChannel)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mReadRequest)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mResponseXML)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mACGetChannel)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCORSPreflightChannel)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnUploadProgressListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnReadystatechangeListener)
|
||||
|
@ -1006,7 +576,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXMLHttpRequest,
|
|||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChannel)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mReadRequest)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mResponseXML)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mACGetChannel)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCORSPreflightChannel)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnUploadProgressListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnReadystatechangeListener)
|
||||
|
@ -1363,8 +933,8 @@ nsXMLHttpRequest::Abort()
|
|||
if (mChannel) {
|
||||
mChannel->Cancel(NS_BINDING_ABORTED);
|
||||
}
|
||||
if (mACGetChannel) {
|
||||
mACGetChannel->Cancel(NS_BINDING_ABORTED);
|
||||
if (mCORSPreflightChannel) {
|
||||
mCORSPreflightChannel->Cancel(NS_BINDING_ABORTED);
|
||||
}
|
||||
mResponseXML = nsnull;
|
||||
PRUint32 responseLength = mResponseBody.Length();
|
||||
|
@ -1390,7 +960,7 @@ nsXMLHttpRequest::Abort()
|
|||
}
|
||||
|
||||
// The ChangeState call above calls onreadystatechange handlers which
|
||||
// if they load a new url will cause nsXMLHttpRequest::OpenRequest to clear
|
||||
// if they load a new url will cause nsXMLHttpRequest::Open to clear
|
||||
// the abort state bit. If this occurs we're not uninitialized (bug 361773).
|
||||
if (mState & XML_HTTP_REQUEST_ABORTED) {
|
||||
ChangeState(XML_HTTP_REQUEST_UNINITIALIZED, PR_FALSE); // IE seems to do it
|
||||
|
@ -1655,7 +1225,7 @@ nsXMLHttpRequest::CheckChannelForCrossSiteRequest(nsIChannel* aChannel)
|
|||
|
||||
nsCAutoString method;
|
||||
httpChannel->GetRequestMethod(method);
|
||||
if (!mACUnsafeHeaders.IsEmpty() ||
|
||||
if (!mCORSUnsafeHeaders.IsEmpty() ||
|
||||
HasListenersFor(NS_LITERAL_STRING(UPLOADPROGRESS_STR)) ||
|
||||
(mUpload && mUpload->HasListeners()) ||
|
||||
(!method.LowerCaseEqualsLiteral("get") &&
|
||||
|
@ -1667,16 +1237,18 @@ nsXMLHttpRequest::CheckChannelForCrossSiteRequest(nsIChannel* aChannel)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
/* noscript void openRequest (in AUTF8String method, in AUTF8String url, in boolean async, in AString user, in AString password); */
|
||||
NS_IMETHODIMP
|
||||
nsXMLHttpRequest::OpenRequest(const nsACString& method,
|
||||
const nsACString& url,
|
||||
PRBool async,
|
||||
const nsAString& user,
|
||||
const nsAString& password)
|
||||
nsXMLHttpRequest::Open(const nsACString& method, const nsACString& url,
|
||||
PRBool async, const nsAString& user,
|
||||
const nsAString& password, PRUint8 optional_argc)
|
||||
{
|
||||
NS_ENSURE_ARG(!method.IsEmpty());
|
||||
|
||||
if (!optional_argc) {
|
||||
// No optional arguments were passed in. Default async to true.
|
||||
async = PR_TRUE;
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
// Disallow HTTP/1.1 TRACE method (see bug 302489)
|
||||
|
@ -1806,20 +1378,6 @@ nsXMLHttpRequest::OpenRequest(const nsACString& method,
|
|||
return rv;
|
||||
}
|
||||
|
||||
/* void open (in AUTF8String method, in AUTF8String url); */
|
||||
NS_IMETHODIMP
|
||||
nsXMLHttpRequest::Open(const nsACString& method, const nsACString& url,
|
||||
PRBool async, const nsAString& user,
|
||||
const nsAString& password, PRUint8 optional_argc)
|
||||
{
|
||||
if (!optional_argc) {
|
||||
// No optional arguments were passed in. Default async to true.
|
||||
async = PR_TRUE;
|
||||
}
|
||||
|
||||
return OpenRequest(method, url, async, user, password);
|
||||
}
|
||||
|
||||
/*
|
||||
* "Copy" from a stream.
|
||||
*/
|
||||
|
@ -2279,13 +1837,10 @@ GetRequestBody(nsIVariant* aBody, nsIInputStream** aResult,
|
|||
nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(supports);
|
||||
if (doc) {
|
||||
aContentType.AssignLiteral("application/xml");
|
||||
nsCOMPtr<nsIDOM3Document> dom3doc = do_QueryInterface(doc);
|
||||
if (dom3doc) {
|
||||
nsAutoString inputEncoding;
|
||||
dom3doc->GetInputEncoding(inputEncoding);
|
||||
if (!DOMStringIsNull(inputEncoding)) {
|
||||
CopyUTF16toUTF8(inputEncoding, aCharset);
|
||||
}
|
||||
nsAutoString inputEncoding;
|
||||
doc->GetInputEncoding(inputEncoding);
|
||||
if (!DOMStringIsNull(inputEncoding)) {
|
||||
CopyUTF16toUTF8(inputEncoding, aCharset);
|
||||
}
|
||||
|
||||
// Serialize to a stream so that the encoding used will
|
||||
|
@ -2571,7 +2126,7 @@ nsXMLHttpRequest::Send(nsIVariant *aBody)
|
|||
if (!contentType.LowerCaseEqualsLiteral("text/plain") &&
|
||||
!contentType.LowerCaseEqualsLiteral("application/x-www-form-urlencoded") &&
|
||||
!contentType.LowerCaseEqualsLiteral("multipart/form-data")) {
|
||||
mACUnsafeHeaders.AppendElement(NS_LITERAL_CSTRING("Content-Type"));
|
||||
mCORSUnsafeHeaders.AppendElement(NS_LITERAL_CSTRING("Content-Type"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2588,41 +2143,6 @@ nsXMLHttpRequest::Send(nsIVariant *aBody)
|
|||
|
||||
PRBool withCredentials = !!(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS);
|
||||
|
||||
// If so, set up the preflight
|
||||
if (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT) {
|
||||
// Check to see if this initial OPTIONS request has already been cached
|
||||
// in our special Access Control Cache.
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = NS_GetFinalChannelURI(mChannel, getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAccessControlLRUCache::CacheEntry* entry =
|
||||
sAccessControlCache ?
|
||||
sAccessControlCache->GetEntry(uri, mPrincipal, withCredentials, PR_FALSE) :
|
||||
nsnull;
|
||||
|
||||
if (!entry || !entry->CheckRequest(method, mACUnsafeHeaders)) {
|
||||
// Either it wasn't cached or the cached result has expired. Build a
|
||||
// channel for the OPTIONS request.
|
||||
nsCOMPtr<nsILoadGroup> loadGroup;
|
||||
GetLoadGroup(getter_AddRefs(loadGroup));
|
||||
|
||||
nsLoadFlags loadFlags;
|
||||
rv = mChannel->GetLoadFlags(&loadFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = NS_NewChannel(getter_AddRefs(mACGetChannel), uri, nsnull,
|
||||
loadGroup, nsnull, loadFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIHttpChannel> acHttp = do_QueryInterface(mACGetChannel);
|
||||
NS_ASSERTION(acHttp, "Failed to QI to nsIHttpChannel!");
|
||||
|
||||
rv = acHttp->SetRequestMethod(NS_LITERAL_CSTRING("OPTIONS"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
||||
// Hook us up to listen to redirects and the like
|
||||
mChannel->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
|
||||
mChannel->SetNotificationCallbacks(this);
|
||||
|
@ -2637,10 +2157,10 @@ nsXMLHttpRequest::Send(nsIVariant *aBody)
|
|||
}
|
||||
|
||||
if (!IsSystemXHR()) {
|
||||
// Always create a nsCrossSiteListenerProxy here even if it's
|
||||
// Always create a nsCORSListenerProxy here even if it's
|
||||
// a same-origin request right now, since it could be redirected.
|
||||
listener = new nsCrossSiteListenerProxy(listener, mPrincipal, mChannel,
|
||||
withCredentials, &rv);
|
||||
listener = new nsCORSListenerProxy(listener, mPrincipal, mChannel,
|
||||
withCredentials, &rv);
|
||||
NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
@ -2661,10 +2181,6 @@ nsXMLHttpRequest::Send(nsIVariant *aBody)
|
|||
else if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
|
||||
AddLoadFlags(mChannel,
|
||||
nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
|
||||
if (mACGetChannel) {
|
||||
AddLoadFlags(mACGetChannel,
|
||||
nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
|
||||
}
|
||||
}
|
||||
|
||||
// Since we expect XML data, set the type hint accordingly
|
||||
|
@ -2672,22 +2188,16 @@ nsXMLHttpRequest::Send(nsIVariant *aBody)
|
|||
// ignoring return value, as this is not critical
|
||||
mChannel->SetContentType(NS_LITERAL_CSTRING("application/xml"));
|
||||
|
||||
// If we're doing a cross-site non-GET request we need to first do
|
||||
// a GET request to the same URI. Set that up if needed
|
||||
if (mACGetChannel) {
|
||||
nsCOMPtr<nsIStreamListener> acProxyListener =
|
||||
new nsACProxyListener(mChannel, listener, nsnull, mPrincipal, method,
|
||||
withCredentials);
|
||||
NS_ENSURE_TRUE(acProxyListener, NS_ERROR_OUT_OF_MEMORY);
|
||||
// Set up the preflight if needed
|
||||
if (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT) {
|
||||
// Check to see if this initial OPTIONS request has already been cached
|
||||
// in our special Access Control Cache.
|
||||
|
||||
acProxyListener =
|
||||
new nsCrossSiteListenerProxy(acProxyListener, mPrincipal, mACGetChannel,
|
||||
withCredentials, method, mACUnsafeHeaders,
|
||||
&rv);
|
||||
NS_ENSURE_TRUE(acProxyListener, NS_ERROR_OUT_OF_MEMORY);
|
||||
rv = NS_StartCORSPreflight(mChannel, listener,
|
||||
mPrincipal, withCredentials,
|
||||
mCORSUnsafeHeaders,
|
||||
getter_AddRefs(mCORSPreflightChannel));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mACGetChannel->AsyncOpen(acProxyListener, nsnull);
|
||||
}
|
||||
else {
|
||||
// Start reading from the channel
|
||||
|
@ -2697,7 +2207,7 @@ nsXMLHttpRequest::Send(nsIVariant *aBody)
|
|||
if (NS_FAILED(rv)) {
|
||||
// Drop our ref to the channel to avoid cycles
|
||||
mChannel = nsnull;
|
||||
mACGetChannel = nsnull;
|
||||
mCORSPreflightChannel = nsnull;
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -2774,17 +2284,17 @@ nsXMLHttpRequest::SetRequestHeader(const nsACString& header,
|
|||
{
|
||||
nsresult rv;
|
||||
|
||||
// Make sure we don't store an invalid header name in mACUnsafeHeaders
|
||||
// Make sure we don't store an invalid header name in mCORSUnsafeHeaders
|
||||
if (!IsValidHTTPToken(header)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Check that we haven't already opened the channel. We can't rely on
|
||||
// the channel throwing from mChannel->SetRequestHeader since we might
|
||||
// still be waiting for mACGetChannel to actually open mChannel
|
||||
if (mACGetChannel) {
|
||||
// still be waiting for mCORSPreflightChannel to actually open mChannel
|
||||
if (mCORSPreflightChannel) {
|
||||
PRBool pending;
|
||||
rv = mACGetChannel->IsPending(&pending);
|
||||
rv = mCORSPreflightChannel->IsPending(&pending);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (pending) {
|
||||
|
@ -2849,7 +2359,7 @@ nsXMLHttpRequest::SetRequestHeader(const nsACString& header,
|
|||
}
|
||||
|
||||
if (!safeHeader) {
|
||||
mACUnsafeHeaders.AppendElement(header);
|
||||
mCORSUnsafeHeaders.AppendElement(header);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -60,10 +60,6 @@
|
|||
#include "nsTArray.h"
|
||||
#include "nsIJSNativeInitializer.h"
|
||||
#include "nsIDOMLSProgressEvent.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "prclist.h"
|
||||
#include "prtime.h"
|
||||
#include "nsIDOMNSEvent.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsIPrivateDOMEvent.h"
|
||||
|
@ -74,72 +70,6 @@
|
|||
class nsILoadGroup;
|
||||
class AsyncVerifyRedirectCallbackForwarder;
|
||||
|
||||
class nsAccessControlLRUCache
|
||||
{
|
||||
public:
|
||||
struct TokenTime
|
||||
{
|
||||
nsCString token;
|
||||
PRTime expirationTime;
|
||||
};
|
||||
|
||||
struct CacheEntry : public PRCList
|
||||
{
|
||||
CacheEntry(nsCString& aKey)
|
||||
: mKey(aKey)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsAccessControlLRUCache::CacheEntry);
|
||||
}
|
||||
|
||||
~CacheEntry()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsAccessControlLRUCache::CacheEntry);
|
||||
}
|
||||
|
||||
void PurgeExpired(PRTime now);
|
||||
PRBool CheckRequest(const nsCString& aMethod,
|
||||
const nsTArray<nsCString>& aCustomHeaders);
|
||||
|
||||
nsCString mKey;
|
||||
nsTArray<TokenTime> mMethods;
|
||||
nsTArray<TokenTime> mHeaders;
|
||||
};
|
||||
|
||||
nsAccessControlLRUCache()
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsAccessControlLRUCache);
|
||||
PR_INIT_CLIST(&mList);
|
||||
}
|
||||
|
||||
~nsAccessControlLRUCache()
|
||||
{
|
||||
Clear();
|
||||
MOZ_COUNT_DTOR(nsAccessControlLRUCache);
|
||||
}
|
||||
|
||||
PRBool Initialize()
|
||||
{
|
||||
return mTable.Init();
|
||||
}
|
||||
|
||||
CacheEntry* GetEntry(nsIURI* aURI, nsIPrincipal* aPrincipal,
|
||||
PRBool aWithCredentials, PRBool aCreate);
|
||||
void RemoveEntries(nsIURI* aURI, nsIPrincipal* aPrincipal);
|
||||
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
static PLDHashOperator
|
||||
RemoveExpiredEntries(const nsACString& aKey, nsAutoPtr<CacheEntry>& aValue,
|
||||
void* aUserData);
|
||||
|
||||
static PRBool GetCacheKey(nsIURI* aURI, nsIPrincipal* aPrincipal,
|
||||
PRBool aWithCredentials, nsACString& _retval);
|
||||
|
||||
nsClassHashtable<nsCStringHashKey, CacheEntry> mTable;
|
||||
PRCList mList;
|
||||
};
|
||||
|
||||
class nsXHREventTarget : public nsDOMEventTargetWrapperCache,
|
||||
public nsIXMLHttpRequestEventTarget
|
||||
{
|
||||
|
@ -281,32 +211,8 @@ public:
|
|||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXMLHttpRequest,
|
||||
nsXHREventTarget)
|
||||
|
||||
static PRBool EnsureACCache()
|
||||
{
|
||||
if (sAccessControlCache)
|
||||
return PR_TRUE;
|
||||
|
||||
nsAutoPtr<nsAccessControlLRUCache> newCache(new nsAccessControlLRUCache());
|
||||
NS_ENSURE_TRUE(newCache, PR_FALSE);
|
||||
|
||||
if (newCache->Initialize()) {
|
||||
sAccessControlCache = newCache.forget();
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
static void ShutdownACCache()
|
||||
{
|
||||
delete sAccessControlCache;
|
||||
sAccessControlCache = nsnull;
|
||||
}
|
||||
|
||||
PRBool AllowUploadProgress();
|
||||
|
||||
static nsAccessControlLRUCache* sAccessControlCache;
|
||||
|
||||
protected:
|
||||
friend class nsMultipartProxyListener;
|
||||
|
||||
|
@ -357,8 +263,8 @@ protected:
|
|||
// mReadRequest is different from mChannel for multipart requests
|
||||
nsCOMPtr<nsIRequest> mReadRequest;
|
||||
nsCOMPtr<nsIDOMDocument> mResponseXML;
|
||||
nsCOMPtr<nsIChannel> mACGetChannel;
|
||||
nsTArray<nsCString> mACUnsafeHeaders;
|
||||
nsCOMPtr<nsIChannel> mCORSPreflightChannel;
|
||||
nsTArray<nsCString> mCORSUnsafeHeaders;
|
||||
|
||||
nsRefPtr<nsDOMEventListenerWrapper> mOnUploadProgressListener;
|
||||
nsRefPtr<nsDOMEventListenerWrapper> mOnReadystatechangeListener;
|
||||
|
|
|
@ -280,6 +280,7 @@ _TEST_FILES1 = test_bug5141.html \
|
|||
file_XHRDocURI.xml^headers^ \
|
||||
file_XHRDocURI.text \
|
||||
file_XHRDocURI.text^headers^ \
|
||||
test_DOMException.html \
|
||||
$(NULL)
|
||||
|
||||
_TEST_FILES2 = \
|
||||
|
@ -431,6 +432,7 @@ _TEST_FILES2 = \
|
|||
test_bug567350.html \
|
||||
test_bug574596.html \
|
||||
test_bug578096.html \
|
||||
test_bug585978.html \
|
||||
test_bug592366.html \
|
||||
test_bug597345.html \
|
||||
script-1_bug597345.sjs \
|
||||
|
|
|
@ -76,7 +76,7 @@ nsresult TestGetURL(const nsCString& aURL)
|
|||
rv = xhr->Init(systemPrincipal, nsnull, nsnull, nsnull);
|
||||
TEST_ENSURE_SUCCESS(rv, "Couldn't initialize the XHR!");
|
||||
|
||||
rv = xhr->OpenRequest(getString, aURL, PR_FALSE, empty, empty);
|
||||
rv = xhr->Open(getString, aURL, PR_FALSE, empty, empty);
|
||||
TEST_ENSURE_SUCCESS(rv, "OpenRequest failed!");
|
||||
|
||||
rv = xhr->Send(nsnull);
|
||||
|
|
|
@ -77,9 +77,9 @@ nsresult TestNativeXMLHttpRequest()
|
|||
const nsAString& empty = EmptyString();
|
||||
|
||||
printf("*** About to see an expected warning about mPrincipal:\n");
|
||||
rv = xhr->OpenRequest(getString, testURL, PR_FALSE, empty, empty);
|
||||
rv = xhr->Open(getString, testURL, PR_FALSE, empty, empty);
|
||||
printf("*** End of expected warning output.\n");
|
||||
TEST_ENSURE_FAILED(rv, "OpenRequest should have failed!");
|
||||
TEST_ENSURE_FAILED(rv, "Open should have failed!");
|
||||
|
||||
nsCOMPtr<nsIScriptSecurityManager> secman =
|
||||
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
|
||||
|
@ -92,8 +92,8 @@ nsresult TestNativeXMLHttpRequest()
|
|||
rv = xhr->Init(systemPrincipal, nsnull, nsnull, nsnull);
|
||||
TEST_ENSURE_SUCCESS(rv, "Couldn't initialize the XHR!");
|
||||
|
||||
rv = xhr->OpenRequest(getString, testURL, PR_FALSE, empty, empty);
|
||||
TEST_ENSURE_SUCCESS(rv, "OpenRequest failed!");
|
||||
rv = xhr->Open(getString, testURL, PR_FALSE, empty, empty);
|
||||
TEST_ENSURE_SUCCESS(rv, "Open failed!");
|
||||
|
||||
rv = xhr->Send(nsnull);
|
||||
TEST_ENSURE_SUCCESS(rv, "Send failed!");
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче