mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge with m-c
This commit is contained in:
commit
4956b23fb3
@ -740,27 +740,49 @@ PRUint32 nsARIAMap::gWAIUnivAttrMapLength = NS_ARRAY_LENGTH(nsARIAMap::gWAIUnivA
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsStateMapEntry
|
||||
|
||||
nsStateMapEntry:: nsStateMapEntry(nsIAtom **aAttrName, eStateValueType aType,
|
||||
PRUint32 aPermanentState,
|
||||
PRUint32 aTrueState, PRUint32 aTrueExtraState,
|
||||
PRUint32 aFalseState, PRUint32 aFalseExtraState,
|
||||
PRBool aDefinedIfAbsent) :
|
||||
attributeName(aAttrName), isToken(PR_TRUE), permanentState(aPermanentState)
|
||||
nsStateMapEntry::nsStateMapEntry() :
|
||||
mAttributeName(nsnull),
|
||||
mIsToken(PR_FALSE),
|
||||
mPermanentState(0),
|
||||
mValue1(nsnull),
|
||||
mState1(0),
|
||||
mExtraState1(0),
|
||||
mValue2(nsnull),
|
||||
mState2(0),
|
||||
mExtraState2(0),
|
||||
mValue3(nsnull),
|
||||
mState3(0),
|
||||
mExtraState3(0),
|
||||
mDefaultState(0),
|
||||
mDefaultExtraState(0),
|
||||
mDefinedIfAbsent(PR_FALSE)
|
||||
{}
|
||||
|
||||
nsStateMapEntry::nsStateMapEntry(nsIAtom **aAttrName, eStateValueType aType,
|
||||
PRUint32 aPermanentState,
|
||||
PRUint32 aTrueState, PRUint32 aTrueExtraState,
|
||||
PRUint32 aFalseState, PRUint32 aFalseExtraState,
|
||||
PRBool aDefinedIfAbsent) :
|
||||
mAttributeName(aAttrName),
|
||||
mIsToken(PR_TRUE),
|
||||
mPermanentState(aPermanentState),
|
||||
mValue1("false"),
|
||||
mState1(aFalseState),
|
||||
mExtraState1(aFalseExtraState),
|
||||
mValue2(nsnull),
|
||||
mState2(0),
|
||||
mExtraState2(0),
|
||||
mValue3(nsnull),
|
||||
mState3(0),
|
||||
mExtraState3(0),
|
||||
mDefaultState(aTrueState),
|
||||
mDefaultExtraState(aTrueExtraState),
|
||||
mDefinedIfAbsent(aDefinedIfAbsent)
|
||||
{
|
||||
value1 = "false";
|
||||
state1 = aFalseState;
|
||||
extraState1 = aFalseExtraState;
|
||||
|
||||
if (aType == kMixedType) {
|
||||
value2 = "mixed";
|
||||
state2 = nsIAccessibleStates::STATE_MIXED;
|
||||
extraState2 = 0;
|
||||
mValue2 = "mixed";
|
||||
mState2 = nsIAccessibleStates::STATE_MIXED;
|
||||
}
|
||||
|
||||
defaultState = aTrueState;
|
||||
defaultExtraState = aTrueExtraState;
|
||||
|
||||
definedIfAbsent = aDefinedIfAbsent;
|
||||
}
|
||||
|
||||
nsStateMapEntry::nsStateMapEntry(nsIAtom **aAttrName,
|
||||
@ -770,11 +792,11 @@ nsStateMapEntry::nsStateMapEntry(nsIAtom **aAttrName,
|
||||
PRUint32 aState2, PRUint32 aExtraState2,
|
||||
const char *aValue3,
|
||||
PRUint32 aState3, PRUint32 aExtraState3) :
|
||||
attributeName(aAttrName), isToken(PR_FALSE), permanentState(0),
|
||||
value1(aValue1), state1(aState1), extraState1(aExtraState1),
|
||||
value2(aValue2), state2(aState2), extraState2(aExtraState2),
|
||||
value3(aValue3), state3(aState3), extraState3(aExtraState3),
|
||||
defaultState(0), defaultExtraState(0), definedIfAbsent(PR_FALSE)
|
||||
mAttributeName(aAttrName), mIsToken(PR_FALSE), mPermanentState(0),
|
||||
mValue1(aValue1), mState1(aState1), mExtraState1(aExtraState1),
|
||||
mValue2(aValue2), mState2(aState2), mExtraState2(aExtraState2),
|
||||
mValue3(aValue3), mState3(aState3), mExtraState3(aExtraState3),
|
||||
mDefaultState(0), mDefaultExtraState(0), mDefinedIfAbsent(PR_FALSE)
|
||||
{
|
||||
}
|
||||
|
||||
@ -786,15 +808,15 @@ nsStateMapEntry::nsStateMapEntry(nsIAtom **aAttrName,
|
||||
PRUint32 aState2, PRUint32 aExtraState2,
|
||||
const char *aValue3,
|
||||
PRUint32 aState3, PRUint32 aExtraState3) :
|
||||
attributeName(aAttrName), isToken(PR_TRUE), permanentState(0),
|
||||
value1(aValue1), state1(aState1), extraState1(aExtraState1),
|
||||
value2(aValue2), state2(aState2), extraState2(aExtraState2),
|
||||
value3(aValue3), state3(aState3), extraState3(aExtraState3),
|
||||
defaultState(0), defaultExtraState(0), definedIfAbsent(PR_TRUE)
|
||||
mAttributeName(aAttrName), mIsToken(PR_TRUE), mPermanentState(0),
|
||||
mValue1(aValue1), mState1(aState1), mExtraState1(aExtraState1),
|
||||
mValue2(aValue2), mState2(aState2), mExtraState2(aExtraState2),
|
||||
mValue3(aValue3), mState3(aState3), mExtraState3(aExtraState3),
|
||||
mDefaultState(0), mDefaultExtraState(0), mDefinedIfAbsent(PR_TRUE)
|
||||
{
|
||||
if (aDefaultStateRule == eUseFirstState) {
|
||||
defaultState = aState1;
|
||||
defaultExtraState = aExtraState1;
|
||||
mDefaultState = aState1;
|
||||
mDefaultExtraState = aExtraState1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -809,17 +831,17 @@ nsStateMapEntry::MapToStates(nsIContent *aContent,
|
||||
|
||||
const nsStateMapEntry& entry = nsARIAMap::gWAIStateMap[aStateMapEntryID];
|
||||
|
||||
if (entry.isToken) {
|
||||
if (entry.mIsToken) {
|
||||
// If attribute is considered as defined when it's absent then let's act
|
||||
// attribute value is "false" supposedly.
|
||||
PRBool hasAttr = aContent->HasAttr(kNameSpaceID_None, *entry.attributeName);
|
||||
if (entry.definedIfAbsent && !hasAttr) {
|
||||
if (entry.permanentState)
|
||||
*aState |= entry.permanentState;
|
||||
if (entry.state1)
|
||||
*aState |= entry.state1;
|
||||
if (aExtraState && entry.extraState1)
|
||||
*aExtraState |= entry.extraState1;
|
||||
PRBool hasAttr = aContent->HasAttr(kNameSpaceID_None, *entry.mAttributeName);
|
||||
if (entry.mDefinedIfAbsent && !hasAttr) {
|
||||
if (entry.mPermanentState)
|
||||
*aState |= entry.mPermanentState;
|
||||
if (entry.mState1)
|
||||
*aState |= entry.mState1;
|
||||
if (aExtraState && entry.mExtraState1)
|
||||
*aExtraState |= entry.mExtraState1;
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
@ -832,66 +854,66 @@ nsStateMapEntry::MapToStates(nsIContent *aContent,
|
||||
// attribute, for example: aria-label="" or aria-label="undefined", we will
|
||||
// bail out and not explore a state mapping, which is safe.
|
||||
if (!hasAttr ||
|
||||
aContent->AttrValueIs(kNameSpaceID_None, *entry.attributeName,
|
||||
aContent->AttrValueIs(kNameSpaceID_None, *entry.mAttributeName,
|
||||
nsAccessibilityAtoms::_empty, eCaseMatters) ||
|
||||
aContent->AttrValueIs(kNameSpaceID_None, *entry.attributeName,
|
||||
aContent->AttrValueIs(kNameSpaceID_None, *entry.mAttributeName,
|
||||
nsAccessibilityAtoms::_undefined, eCaseMatters)) {
|
||||
|
||||
if (entry.permanentState)
|
||||
*aState &= ~entry.permanentState;
|
||||
if (entry.mPermanentState)
|
||||
*aState &= ~entry.mPermanentState;
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
if (entry.permanentState)
|
||||
*aState |= entry.permanentState;
|
||||
if (entry.mPermanentState)
|
||||
*aState |= entry.mPermanentState;
|
||||
}
|
||||
|
||||
nsAutoString attrValue;
|
||||
if (!aContent->GetAttr(kNameSpaceID_None, *entry.attributeName, attrValue))
|
||||
if (!aContent->GetAttr(kNameSpaceID_None, *entry.mAttributeName, attrValue))
|
||||
return PR_TRUE;
|
||||
|
||||
// Apply states for matched value. If no values was matched then apply default
|
||||
// states.
|
||||
PRBool applyDefaultStates = PR_TRUE;
|
||||
if (entry.value1) {
|
||||
if (attrValue.EqualsASCII(entry.value1)) {
|
||||
if (entry.mValue1) {
|
||||
if (attrValue.EqualsASCII(entry.mValue1)) {
|
||||
applyDefaultStates = PR_FALSE;
|
||||
|
||||
if (entry.state1)
|
||||
*aState |= entry.state1;
|
||||
if (entry.mState1)
|
||||
*aState |= entry.mState1;
|
||||
|
||||
if (aExtraState && entry.extraState1)
|
||||
*aExtraState |= entry.extraState1;
|
||||
if (aExtraState && entry.mExtraState1)
|
||||
*aExtraState |= entry.mExtraState1;
|
||||
|
||||
} else if (entry.value2) {
|
||||
if (attrValue.EqualsASCII(entry.value2)) {
|
||||
} else if (entry.mValue2) {
|
||||
if (attrValue.EqualsASCII(entry.mValue2)) {
|
||||
applyDefaultStates = PR_FALSE;
|
||||
|
||||
if (entry.state2)
|
||||
*aState |= entry.state2;
|
||||
if (entry.mState2)
|
||||
*aState |= entry.mState2;
|
||||
|
||||
if (aExtraState && entry.extraState2)
|
||||
*aExtraState |= entry.extraState2;
|
||||
if (aExtraState && entry.mExtraState2)
|
||||
*aExtraState |= entry.mExtraState2;
|
||||
|
||||
} else if (entry.value3) {
|
||||
if (attrValue.EqualsASCII(entry.value3)) {
|
||||
} else if (entry.mValue3) {
|
||||
if (attrValue.EqualsASCII(entry.mValue3)) {
|
||||
applyDefaultStates = PR_FALSE;
|
||||
|
||||
if (entry.state3)
|
||||
*aState |= entry.state3;
|
||||
if (entry.mState3)
|
||||
*aState |= entry.mState3;
|
||||
|
||||
if (aExtraState && entry.extraState3)
|
||||
*aExtraState |= entry.extraState3;
|
||||
if (aExtraState && entry.mExtraState3)
|
||||
*aExtraState |= entry.mExtraState3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (applyDefaultStates) {
|
||||
if (entry.defaultState)
|
||||
*aState |= entry.defaultState;
|
||||
if (entry.defaultExtraState && aExtraState)
|
||||
*aExtraState |= entry.defaultExtraState;
|
||||
if (entry.mDefaultState)
|
||||
*aState |= entry.mDefaultState;
|
||||
if (entry.mDefaultExtraState && aExtraState)
|
||||
*aExtraState |= entry.mDefaultExtraState;
|
||||
}
|
||||
|
||||
return PR_TRUE;
|
||||
|
@ -194,7 +194,7 @@ public:
|
||||
/**
|
||||
* Used to create stub.
|
||||
*/
|
||||
nsStateMapEntry() {}
|
||||
nsStateMapEntry();
|
||||
|
||||
/**
|
||||
* Used for ARIA attributes having boolean or mixed values.
|
||||
@ -240,33 +240,33 @@ public:
|
||||
|
||||
private:
|
||||
// ARIA attribute name
|
||||
nsIAtom** attributeName;
|
||||
nsIAtom** mAttributeName;
|
||||
|
||||
// Indicates if attribute is token (can be undefined)
|
||||
PRBool isToken;
|
||||
PRBool mIsToken;
|
||||
|
||||
// State applied always if attribute is defined
|
||||
PRUint32 permanentState;
|
||||
PRUint32 mPermanentState;
|
||||
|
||||
// States applied if attribute value is matched to the stored value
|
||||
const char* value1;
|
||||
PRUint32 state1;
|
||||
PRUint32 extraState1;
|
||||
const char* mValue1;
|
||||
PRUint32 mState1;
|
||||
PRUint32 mExtraState1;
|
||||
|
||||
const char* value2;
|
||||
PRUint32 state2;
|
||||
PRUint32 extraState2;
|
||||
const char* mValue2;
|
||||
PRUint32 mState2;
|
||||
PRUint32 mExtraState2;
|
||||
|
||||
const char* value3;
|
||||
PRUint32 state3;
|
||||
PRUint32 extraState3;
|
||||
const char* mValue3;
|
||||
PRUint32 mState3;
|
||||
PRUint32 mExtraState3;
|
||||
|
||||
// States applied if no stored values above are matched
|
||||
PRUint32 defaultState;
|
||||
PRUint32 defaultExtraState;
|
||||
PRUint32 mDefaultState;
|
||||
PRUint32 mDefaultExtraState;
|
||||
|
||||
// Permanent and false states are applied if attribute is absent
|
||||
PRBool definedIfAbsent;
|
||||
PRBool mDefinedIfAbsent;
|
||||
};
|
||||
|
||||
|
||||
|
@ -315,7 +315,8 @@ NS_IMPL_ISUPPORTS_INHERITED1(nsXFormsSelectableAccessible,
|
||||
|
||||
nsXFormsSelectableAccessible::
|
||||
nsXFormsSelectableAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell) :
|
||||
nsXFormsEditableAccessible(aNode, aShell)
|
||||
nsXFormsEditableAccessible(aNode, aShell),
|
||||
mIsSelect1Element(nsnull)
|
||||
{
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
|
||||
if (!content)
|
||||
|
@ -1214,13 +1214,14 @@ nsXULTreeItemAccessible::Shutdown()
|
||||
nsresult
|
||||
nsXULTreeItemAccessible::GetRoleInternal(PRUint32 *aRole)
|
||||
{
|
||||
nsCOMPtr<nsITreeColumn> column =
|
||||
nsCoreUtils::GetFirstSensibleColumn(mTree);
|
||||
nsCOMPtr<nsITreeColumns> columns;
|
||||
mTree->GetColumns(getter_AddRefs(columns));
|
||||
NS_ENSURE_STATE(columns);
|
||||
|
||||
PRBool isPrimary = PR_FALSE;
|
||||
column->GetPrimary(&isPrimary);
|
||||
nsCOMPtr<nsITreeColumn> primaryColumn;
|
||||
columns->GetPrimaryColumn(getter_AddRefs(primaryColumn));
|
||||
|
||||
*aRole = isPrimary ?
|
||||
*aRole = primaryColumn ?
|
||||
nsIAccessibleRole::ROLE_OUTLINEITEM :
|
||||
nsIAccessibleRole::ROLE_LISTITEM;
|
||||
|
||||
|
@ -1341,13 +1341,6 @@ function delayedStartup(isLoadingBlank, mustLoadSidebar) {
|
||||
|
||||
// Initialize the downloads monitor panel listener
|
||||
DownloadMonitorPanel.init();
|
||||
|
||||
if (Win7Features) {
|
||||
let tempScope = {};
|
||||
Cu.import("resource://gre/modules/DownloadTaskbarProgress.jsm",
|
||||
tempScope);
|
||||
tempScope.DownloadTaskbarProgress.onBrowserWindowLoad(window);
|
||||
}
|
||||
}, 10000);
|
||||
|
||||
// Delayed initialization of PlacesDBUtils.
|
||||
|
@ -1360,7 +1360,7 @@ protected:
|
||||
|
||||
#ifdef MOZ_SMIL
|
||||
// SMIL Animation Controller, lazily-initialized in GetAnimationController
|
||||
nsAutoPtr<nsSMILAnimationController> mAnimationController;
|
||||
nsRefPtr<nsSMILAnimationController> mAnimationController;
|
||||
#endif // MOZ_SMIL
|
||||
|
||||
// Table of element properties for this document.
|
||||
|
@ -54,12 +54,7 @@ INPUT(popupText1): blur \n\
|
||||
setPrefAndDoTest(eventLogForNewWindow,'Body',2); // 2 = open new window as window
|
||||
}
|
||||
|
||||
if (navigator.platform.indexOf("Lin") == -1 && navigator.platform.indexOf("SunOS") == -1) {
|
||||
SimpleTest.waitForExplicitFinish(); // the finish() call is in bug299673.js
|
||||
addLoadEvent(doTest);
|
||||
} else {
|
||||
todo(false, "Please write a better test for bug 299673 that also works on Linux");
|
||||
}
|
||||
todo(false, "Please write a test for bug 299673 that actually works");
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
@ -53,12 +53,7 @@ INPUT(popupText1): blur \n\
|
||||
|
||||
}
|
||||
|
||||
if (navigator.platform.indexOf("Lin") == -1) {
|
||||
SimpleTest.waitForExplicitFinish(); // the finish() call is in bug299673.js
|
||||
addLoadEvent(doTest);
|
||||
} else {
|
||||
todo(false, "Please write a better test for bug 299673 that also works on Linux");
|
||||
}
|
||||
todo(false, "Please write a test for bug 299673 that actually works");
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
@ -1537,27 +1537,35 @@ nsHTMLInputElement::Click()
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsIPresShell *shell = doc->GetPrimaryShell();
|
||||
|
||||
nsCOMPtr<nsIPresShell> shell = doc->GetPrimaryShell();
|
||||
nsCOMPtr<nsPresContext> context = nsnull;
|
||||
if (shell) {
|
||||
nsCOMPtr<nsPresContext> context = shell->GetPresContext();
|
||||
context = shell->GetPresContext();
|
||||
}
|
||||
|
||||
if (context) {
|
||||
// Click() is never called from native code, but it may be
|
||||
// called from chrome JS. Mark this event trusted if Click()
|
||||
// is called from chrome code.
|
||||
nsMouseEvent event(nsContentUtils::IsCallerChrome(),
|
||||
NS_MOUSE_CLICK, nsnull, nsMouseEvent::eReal);
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
|
||||
SET_BOOLBIT(mBitField, BF_HANDLING_CLICK, PR_TRUE);
|
||||
|
||||
nsEventDispatcher::Dispatch(static_cast<nsIContent*>(this), context,
|
||||
&event, nsnull, &status);
|
||||
|
||||
SET_BOOLBIT(mBitField, BF_HANDLING_CLICK, PR_FALSE);
|
||||
if (!context) {
|
||||
doc->FlushPendingNotifications(Flush_Frames);
|
||||
shell = doc->GetPrimaryShell();
|
||||
if (shell) {
|
||||
context = shell->GetPresContext();
|
||||
}
|
||||
}
|
||||
|
||||
if (context) {
|
||||
// Click() is never called from native code, but it may be
|
||||
// called from chrome JS. Mark this event trusted if Click()
|
||||
// is called from chrome code.
|
||||
nsMouseEvent event(nsContentUtils::IsCallerChrome(),
|
||||
NS_MOUSE_CLICK, nsnull, nsMouseEvent::eReal);
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
|
||||
SET_BOOLBIT(mBitField, BF_HANDLING_CLICK, PR_TRUE);
|
||||
|
||||
nsEventDispatcher::Dispatch(static_cast<nsIContent*>(this), context,
|
||||
&event, nsnull, &status);
|
||||
|
||||
SET_BOOLBIT(mBitField, BF_HANDLING_CLICK, PR_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -62,6 +62,20 @@
|
||||
//
|
||||
const PRUint32 nsSMILAnimationController::kTimerInterval = 22;
|
||||
|
||||
// Helper method
|
||||
static nsRefreshDriver*
|
||||
GetRefreshDriverForDoc(nsIDocument* aDoc)
|
||||
{
|
||||
nsIPresShell* shell = aDoc->GetPrimaryShell();
|
||||
if (!shell) {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
nsPresContext* context = shell->GetPresContext();
|
||||
return context ? context->RefreshDriver() : nsnull;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// ctors, dtors, factory methods
|
||||
|
||||
@ -75,10 +89,8 @@ nsSMILAnimationController::nsSMILAnimationController()
|
||||
|
||||
nsSMILAnimationController::~nsSMILAnimationController()
|
||||
{
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
mTimer = nsnull;
|
||||
}
|
||||
StopSampling(GetRefreshDriverForDoc(mDocument));
|
||||
mTimer = nsnull;
|
||||
|
||||
NS_ASSERTION(mAnimationElementTable.Count() == 0,
|
||||
"Animation controller shouldn't be tracking any animation"
|
||||
@ -125,7 +137,7 @@ nsSMILAnimationController::Pause(PRUint32 aType)
|
||||
nsSMILTimeContainer::Pause(aType);
|
||||
|
||||
if (mPauseState) {
|
||||
StopTimer();
|
||||
StopSampling(GetRefreshDriverForDoc(mDocument));
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,7 +149,8 @@ nsSMILAnimationController::Resume(PRUint32 aType)
|
||||
nsSMILTimeContainer::Resume(aType);
|
||||
|
||||
if (wasPaused && !mPauseState && mChildContainerTable.Count()) {
|
||||
StartTimer();
|
||||
Sample(); // Run the first sample manually
|
||||
StartSampling(GetRefreshDriverForDoc(mDocument));
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,6 +161,22 @@ nsSMILAnimationController::GetParentTime() const
|
||||
return PR_Now() / PR_USEC_PER_MSEC;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsARefreshObserver methods:
|
||||
NS_IMPL_ADDREF(nsSMILAnimationController)
|
||||
NS_IMPL_RELEASE(nsSMILAnimationController)
|
||||
|
||||
// nsRefreshDriver Callback function
|
||||
// XXXdholbert NOTE: This function isn't used yet
|
||||
void
|
||||
nsSMILAnimationController::WillRefresh(mozilla::TimeStamp aTime)
|
||||
{
|
||||
// XXXdholbert Eventually we should be sampling based on aTime. For now,
|
||||
// though, we keep track of the time on our own, and we just use
|
||||
// nsRefreshDriver for scheduling samples.
|
||||
Sample();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Animation element registration methods:
|
||||
|
||||
@ -226,14 +255,11 @@ nsSMILAnimationController::Notify(nsITimer* timer, void* aClosure)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSMILAnimationController::StartTimer()
|
||||
nsSMILAnimationController::StartSampling(nsRefreshDriver* aRefreshDriver)
|
||||
{
|
||||
NS_ENSURE_TRUE(mTimer, NS_ERROR_FAILURE);
|
||||
NS_ASSERTION(mPauseState == 0, "Starting timer but controller is paused");
|
||||
|
||||
// Run the first sample manually
|
||||
Sample();
|
||||
|
||||
//
|
||||
// XXX Make this self-tuning. Sounds like control theory to me and not
|
||||
// something I'm familiar with.
|
||||
@ -245,7 +271,7 @@ nsSMILAnimationController::StartTimer()
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSMILAnimationController::StopTimer()
|
||||
nsSMILAnimationController::StopSampling(nsRefreshDriver* aRefreshDriver)
|
||||
{
|
||||
NS_ENSURE_TRUE(mTimer, NS_ERROR_FAILURE);
|
||||
|
||||
@ -668,7 +694,8 @@ nsSMILAnimationController::AddChild(nsSMILTimeContainer& aChild)
|
||||
NS_ENSURE_TRUE(key,NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
if (!mPauseState && mChildContainerTable.Count() == 1) {
|
||||
StartTimer();
|
||||
Sample(); // Run the first sample manually
|
||||
StartSampling(GetRefreshDriverForDoc(mDocument));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -680,6 +707,6 @@ nsSMILAnimationController::RemoveChild(nsSMILTimeContainer& aChild)
|
||||
mChildContainerTable.RemoveEntry(&aChild);
|
||||
|
||||
if (!mPauseState && mChildContainerTable.Count() == 0) {
|
||||
StopTimer();
|
||||
StopSampling(GetRefreshDriverForDoc(mDocument));
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include "nsSMILTimeContainer.h"
|
||||
#include "nsSMILCompositorTable.h"
|
||||
#include "nsSMILMilestone.h"
|
||||
#include "nsRefreshDriver.h"
|
||||
|
||||
struct nsSMILTargetIdentifier;
|
||||
class nsISMILAnimationElement;
|
||||
@ -66,7 +67,8 @@ class nsIDocument;
|
||||
// a compound document. These time containers can be paused individually or
|
||||
// here, at the document level.
|
||||
//
|
||||
class nsSMILAnimationController : public nsSMILTimeContainer
|
||||
class nsSMILAnimationController : public nsSMILTimeContainer,
|
||||
public nsARefreshObserver
|
||||
{
|
||||
public:
|
||||
nsSMILAnimationController();
|
||||
@ -77,6 +79,12 @@ public:
|
||||
virtual void Resume(PRUint32 aType);
|
||||
virtual nsSMILTime GetParentTime() const;
|
||||
|
||||
// nsARefreshObserver
|
||||
NS_IMETHOD_(nsrefcnt) AddRef();
|
||||
NS_IMETHOD_(nsrefcnt) Release();
|
||||
|
||||
virtual void WillRefresh(mozilla::TimeStamp aTime);
|
||||
|
||||
// Methods for registering and enumerating animation elements
|
||||
void RegisterAnimationElement(nsISMILAnimationElement* aAnimationElement);
|
||||
void UnregisterAnimationElement(nsISMILAnimationElement* aAnimationElement);
|
||||
@ -102,6 +110,10 @@ public:
|
||||
void Traverse(nsCycleCollectionTraversalCallback* aCallback);
|
||||
void Unlink();
|
||||
|
||||
// Methods for controlling whether we're sampling
|
||||
nsresult StartSampling(nsRefreshDriver* aRefreshDriver);
|
||||
nsresult StopSampling(nsRefreshDriver* aRefreshDriver);
|
||||
|
||||
protected:
|
||||
// Typedefs
|
||||
typedef nsPtrHashKey<nsSMILTimeContainer> TimeContainerPtrKey;
|
||||
@ -138,8 +150,6 @@ protected:
|
||||
|
||||
// Timer-related implementation helpers
|
||||
static void Notify(nsITimer* aTimer, void* aClosure);
|
||||
nsresult StartTimer();
|
||||
nsresult StopTimer();
|
||||
|
||||
// Sample-related callbacks and implementation helpers
|
||||
virtual void DoSample();
|
||||
@ -165,6 +175,9 @@ protected:
|
||||
virtual void RemoveChild(nsSMILTimeContainer& aChild);
|
||||
|
||||
// Members
|
||||
nsAutoRefCnt mRefCnt;
|
||||
NS_DECL_OWNINGTHREAD
|
||||
|
||||
static const PRUint32 kTimerInterval;
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
AnimationElementHashtable mAnimationElementTable;
|
||||
|
@ -259,7 +259,7 @@ PluginModuleParent::ActorDestroy(ActorDestroyReason why)
|
||||
switch (why) {
|
||||
case AbnormalShutdown: {
|
||||
nsCOMPtr<nsIFile> dump;
|
||||
if (GetMinidump(getter_AddRefs(dump))) {
|
||||
if (TakeMinidump(getter_AddRefs(dump))) {
|
||||
WriteExtraDataForMinidump(dump);
|
||||
if (NS_SUCCEEDED(dump->GetLeafName(mDumpID))) {
|
||||
mDumpID.Replace(mDumpID.Length() - 4, 4,
|
||||
@ -295,6 +295,15 @@ PluginModuleParent::NotifyPluginCrashed()
|
||||
// MessageLoop owns this
|
||||
mPluginCrashedTask = NULL;
|
||||
|
||||
if (!OkToCleanup()) {
|
||||
// there's still plugin code on the C++ stack. try again
|
||||
mPluginCrashedTask = NewRunnableMethod(
|
||||
this, &PluginModuleParent::NotifyPluginCrashed);
|
||||
MessageLoop::current()->PostDelayedTask(
|
||||
FROM_HERE, mPluginCrashedTask, 10);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mPlugin)
|
||||
mPlugin->PluginCrashed(mDumpID);
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ typedef struct {
|
||||
|
||||
cairo_bool_t supports_porter_duff;
|
||||
|
||||
#if defined(Q_WS_X11) && defined(CAIRO_HAS_XLIB_XRENDER_SURFACE)
|
||||
#if defined(CAIRO_HAS_XLIB_XRENDER_SURFACE)
|
||||
/* temporary, so that we can share the xlib surface's glyphs code */
|
||||
bool xlib_has_clipping;
|
||||
cairo_surface_t *xlib_equiv;
|
||||
@ -438,7 +438,7 @@ _cairo_qpainter_surface_finish (void *abstract_surface)
|
||||
if (qs->image_equiv)
|
||||
cairo_surface_destroy ((cairo_surface_t*) qs->image_equiv);
|
||||
|
||||
#if defined(Q_WS_X11) && defined(CAIRO_HAS_XLIB_XRENDER_SURFACE)
|
||||
#if defined(CAIRO_HAS_XLIB_XRENDER_SURFACE)
|
||||
if (qs->xlib_equiv)
|
||||
cairo_surface_destroy (qs->xlib_equiv);
|
||||
#endif
|
||||
@ -1365,7 +1365,7 @@ _cairo_qpainter_surface_show_glyphs (void *abstract_surface,
|
||||
Q_UNUSED(extends);
|
||||
cairo_qpainter_surface_t *qs = (cairo_qpainter_surface_t *) abstract_surface;
|
||||
|
||||
#if defined(Q_WS_X11) && defined(CAIRO_HAS_XLIB_XRENDER_SURFACE)
|
||||
#if defined(CAIRO_HAS_XLIB_XRENDER_SURFACE)
|
||||
/* If we have an equivalent X surface, let the xlib surface handle this
|
||||
* until we figure out how to do this natively with Qt.
|
||||
*/
|
||||
@ -1587,7 +1587,7 @@ static const cairo_surface_backend_t cairo_qpainter_surface_backend = {
|
||||
NULL, /* show_text_glyphs */
|
||||
};
|
||||
|
||||
#if defined(Q_WS_X11) && defined(CAIRO_HAS_XLIB_XRENDER_SURFACE)
|
||||
#if defined(CAIRO_HAS_XLIB_XRENDER_SURFACE)
|
||||
void
|
||||
_cairo_qpainter_create_xlib_surface (cairo_qpainter_surface_t *qs)
|
||||
{
|
||||
@ -1661,7 +1661,7 @@ cairo_qpainter_surface_create (QPainter *painter)
|
||||
|
||||
qs->window = painter->window();
|
||||
|
||||
#if defined(Q_WS_X11) && defined(CAIRO_HAS_XLIB_XRENDER_SURFACE)
|
||||
#if defined(CAIRO_HAS_XLIB_XRENDER_SURFACE)
|
||||
_cairo_qpainter_create_xlib_surface (qs);
|
||||
#endif
|
||||
|
||||
@ -1748,7 +1748,7 @@ cairo_qpainter_surface_create_with_qpixmap (cairo_content_t content,
|
||||
|
||||
qs->window = QRect(0, 0, width, height);
|
||||
|
||||
#if defined(Q_WS_X11) && defined(CAIRO_HAS_XLIB_XRENDER_SURFACE)
|
||||
#if defined(CAIRO_HAS_XLIB_XRENDER_SURFACE)
|
||||
_cairo_qpainter_create_xlib_surface (qs);
|
||||
#endif
|
||||
|
||||
|
@ -27,7 +27,12 @@ Message::Message()
|
||||
InitLoggingVariables();
|
||||
}
|
||||
|
||||
#if !defined(CHROMIUM_MOZILLA_BUILD)
|
||||
Message::Message(int32 routing_id, uint16 type, PriorityValue priority)
|
||||
#else
|
||||
Message::Message(int32 routing_id, uint16 type, PriorityValue priority,
|
||||
const char* const name)
|
||||
#endif
|
||||
: Pickle(sizeof(Header)) {
|
||||
header()->routing = routing_id;
|
||||
header()->type = type;
|
||||
@ -39,8 +44,10 @@ Message::Message(int32 routing_id, uint16 type, PriorityValue priority)
|
||||
header()->rpc_remote_stack_depth_guess = static_cast<size_t>(-1);
|
||||
header()->rpc_local_stack_depth = static_cast<size_t>(-1);
|
||||
header()->seqno = 0;
|
||||
#endif
|
||||
InitLoggingVariables(name);
|
||||
#else
|
||||
InitLoggingVariables();
|
||||
#endif
|
||||
}
|
||||
|
||||
Message::Message(const char* data, int data_len) : Pickle(data, data_len) {
|
||||
@ -48,13 +55,22 @@ Message::Message(const char* data, int data_len) : Pickle(data, data_len) {
|
||||
}
|
||||
|
||||
Message::Message(const Message& other) : Pickle(other) {
|
||||
#if !defined(CHROMIUM_MOZILLA_BUILD)
|
||||
InitLoggingVariables();
|
||||
#else
|
||||
InitLoggingVariables(other.name_);
|
||||
#endif
|
||||
#if defined(OS_POSIX)
|
||||
file_descriptor_set_ = other.file_descriptor_set_;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !defined(CHROMIUM_MOZILLA_BUILD)
|
||||
void Message::InitLoggingVariables() {
|
||||
#else
|
||||
void Message::InitLoggingVariables(const char* const name) {
|
||||
name_ = name;
|
||||
#endif
|
||||
#ifdef IPC_MESSAGE_LOG_ENABLED
|
||||
received_time_ = 0;
|
||||
dont_log_ = false;
|
||||
@ -64,6 +80,9 @@ void Message::InitLoggingVariables() {
|
||||
|
||||
Message& Message::operator=(const Message& other) {
|
||||
*static_cast<Pickle*>(this) = other;
|
||||
#if defined(CHROMIUM_MOZILLA_BUILD)
|
||||
InitLoggingVariables(other.name_);
|
||||
#endif
|
||||
#if defined(OS_POSIX)
|
||||
file_descriptor_set_ = other.file_descriptor_set_;
|
||||
#endif
|
||||
|
@ -62,7 +62,12 @@ class Message : public Pickle {
|
||||
|
||||
// Initialize a message with a user-defined type, priority value, and
|
||||
// destination WebView ID.
|
||||
#if !defined(CHROMIUM_MOZILLA_BUILD)
|
||||
Message(int32 routing_id, uint16 type, PriorityValue priority);
|
||||
#else
|
||||
Message(int32 routing_id, uint16 type, PriorityValue priority,
|
||||
const char* const name="???");
|
||||
#endif
|
||||
|
||||
// Initializes a message from a const block of data. The data is not copied;
|
||||
// instead the data is merely referenced by this message. Only const methods
|
||||
@ -166,6 +171,14 @@ class Message : public Pickle {
|
||||
void set_seqno(int32 seqno) {
|
||||
header()->seqno = seqno;
|
||||
}
|
||||
|
||||
const char* const name() const {
|
||||
return name_;
|
||||
}
|
||||
|
||||
void set_name(const char* const name) {
|
||||
name_ = name;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<class T>
|
||||
@ -296,7 +309,11 @@ class Message : public Pickle {
|
||||
return headerT<Header>();
|
||||
}
|
||||
|
||||
#if !defined(CHROMIUM_MOZILLA_BUILD)
|
||||
void InitLoggingVariables();
|
||||
#else
|
||||
void InitLoggingVariables(const char* const name="???");
|
||||
#endif
|
||||
|
||||
#if defined(OS_POSIX)
|
||||
// The set of file descriptors associated with this message.
|
||||
@ -314,6 +331,10 @@ class Message : public Pickle {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CHROMIUM_MOZILLA_BUILD)
|
||||
const char* name_;
|
||||
#endif
|
||||
|
||||
#ifdef IPC_MESSAGE_LOG_ENABLED
|
||||
// Used for logging.
|
||||
mutable int64 received_time_;
|
||||
|
@ -444,11 +444,18 @@ AsyncChannel::OnChannelError()
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
// NB: this can race with the `Goodbye' event being processed by
|
||||
// the worker thread
|
||||
if (ChannelClosing != mChannelState)
|
||||
mChannelState = ChannelError;
|
||||
|
||||
PostErrorNotifyTask();
|
||||
}
|
||||
|
||||
void
|
||||
AsyncChannel::PostErrorNotifyTask()
|
||||
{
|
||||
AssertIOThread();
|
||||
mMutex.AssertCurrentThreadOwns();
|
||||
|
||||
NS_ASSERTION(!mChannelErrorTask, "OnChannelError called twice?");
|
||||
|
||||
// This must be the last code that runs on this thread!
|
||||
|
@ -175,6 +175,7 @@ protected:
|
||||
void OnChannelOpened();
|
||||
void OnSend(Message* aMsg);
|
||||
void OnCloseChannel();
|
||||
void PostErrorNotifyTask();
|
||||
|
||||
// Return true if |msg| is a special message targeted at the IO
|
||||
// thread, in which case it shouldn't be delivered to the worker.
|
||||
|
@ -96,8 +96,7 @@ RPCChannel::RPCChannel(RPCListener* aListener)
|
||||
mOutOfTurnReplies(),
|
||||
mDeferred(),
|
||||
mRemoteStackDepthGuess(0),
|
||||
mBlockedOnParent(false),
|
||||
mCxxStackFrames(0)
|
||||
mBlockedOnParent(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(RPCChannel);
|
||||
|
||||
@ -109,7 +108,7 @@ RPCChannel::RPCChannel(RPCListener* aListener)
|
||||
RPCChannel::~RPCChannel()
|
||||
{
|
||||
MOZ_COUNT_DTOR(RPCChannel);
|
||||
RPC_ASSERT(0 == mCxxStackFrames, "mismatched CxxStackFrame ctor/dtors");
|
||||
RPC_ASSERT(mCxxStackFrames.empty(), "mismatched CxxStackFrame ctor/dtors");
|
||||
}
|
||||
|
||||
void
|
||||
@ -142,14 +141,16 @@ RPCChannel::EventOccurred()
|
||||
bool
|
||||
RPCChannel::Send(Message* msg)
|
||||
{
|
||||
CxxStackFrame f(*this);
|
||||
Message copy = *msg;
|
||||
CxxStackFrame f(*this, OUT_MESSAGE, ©);
|
||||
return AsyncChannel::Send(msg);
|
||||
}
|
||||
|
||||
bool
|
||||
RPCChannel::Send(Message* msg, Message* reply)
|
||||
{
|
||||
CxxStackFrame f(*this);
|
||||
Message copy = *msg;
|
||||
CxxStackFrame f(*this, OUT_MESSAGE, ©);
|
||||
return SyncChannel::Send(msg, reply);
|
||||
}
|
||||
|
||||
@ -162,7 +163,8 @@ RPCChannel::Call(Message* msg, Message* reply)
|
||||
"violation of sync handler invariant");
|
||||
RPC_ASSERT(msg->is_rpc(), "can only Call() RPC messages here");
|
||||
|
||||
CxxStackFrame f(*this);
|
||||
Message copy = *msg;
|
||||
CxxStackFrame f(*this, OUT_MESSAGE, ©);
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
@ -227,7 +229,10 @@ RPCChannel::Call(Message* msg, Message* reply)
|
||||
|
||||
if (!recvd.is_sync() && !recvd.is_rpc()) {
|
||||
MutexAutoUnlock unlock(mMutex);
|
||||
|
||||
CxxStackFrame f(*this, IN_MESSAGE, &recvd);
|
||||
AsyncChannel::OnDispatchMessage(recvd);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -235,7 +240,10 @@ RPCChannel::Call(Message* msg, Message* reply)
|
||||
RPC_ASSERT(mPending.empty(),
|
||||
"other side should have been blocked");
|
||||
MutexAutoUnlock unlock(mMutex);
|
||||
|
||||
CxxStackFrame f(*this, IN_MESSAGE, &recvd);
|
||||
SyncChannel::OnDispatchMessage(recvd);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -271,14 +279,6 @@ RPCChannel::Call(Message* msg, Message* reply)
|
||||
}
|
||||
|
||||
if (0 == StackDepth()) {
|
||||
// we may have received new messages while waiting for
|
||||
// our reply. because we were awaiting a reply,
|
||||
// StackDepth > 0, and the IO thread didn't enqueue
|
||||
// OnMaybeDequeueOne() events for us. so to avoid
|
||||
// "losing" the new messages, we do that now.
|
||||
EnqueuePendingMessages();
|
||||
|
||||
|
||||
RPC_ASSERT(
|
||||
mOutOfTurnReplies.empty(),
|
||||
"still have pending replies with no pending out-calls",
|
||||
@ -296,6 +296,7 @@ RPCChannel::Call(Message* msg, Message* reply)
|
||||
{
|
||||
MutexAutoUnlock unlock(mMutex);
|
||||
// someone called in to us from the other side. handle the call
|
||||
CxxStackFrame f(*this, IN_MESSAGE, &recvd);
|
||||
Incall(recvd, stackDepth);
|
||||
// FIXME/cjones: error handling
|
||||
}
|
||||
@ -332,6 +333,8 @@ RPCChannel::MaybeProcessDeferredIncall()
|
||||
|
||||
MutexAutoUnlock unlock(mMutex);
|
||||
fprintf(stderr, " (processing deferred in-call)\n");
|
||||
|
||||
CxxStackFrame f(*this, IN_MESSAGE, &call);
|
||||
Incall(call, stackDepth);
|
||||
}
|
||||
|
||||
@ -364,6 +367,22 @@ RPCChannel::OnMaybeDequeueOne()
|
||||
AssertWorkerThread();
|
||||
mMutex.AssertNotCurrentThreadOwns();
|
||||
|
||||
if (IsOnCxxStack())
|
||||
// We're running in a nested event loop, and there's
|
||||
// RPCChannel code below us on the stack. We don't want to
|
||||
// dispatch this new message here because the assumptions made
|
||||
// by the code below us on the stack have changed. Just
|
||||
// bailing here isn't enough, however, because we also have to
|
||||
// ensure that the messages received in this nested loop
|
||||
// aren't "lost". We might be running in this nested context
|
||||
// above a non-interruptable handler; these are async
|
||||
// in/out-msg, sync in/out-msg, and rpc in-call. So, we still
|
||||
// bail here, but ensure that at each exit point, we fix up
|
||||
// the IO thread's invariant. Luckily, since we already track
|
||||
// the C++ stack, we know when these exit points are hit:
|
||||
// ExitedCxxStack().
|
||||
return;
|
||||
|
||||
Message recvd;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
@ -383,7 +402,8 @@ RPCChannel::OnMaybeDequeueOne()
|
||||
mPending.pop();
|
||||
}
|
||||
|
||||
CxxStackFrame f(*this);
|
||||
RPC_ASSERT(!IsOnCxxStack(), "RPCChannel code not on C++ stack");
|
||||
CxxStackFrame f(*this, IN_MESSAGE, &recvd);
|
||||
|
||||
if (recvd.is_rpc())
|
||||
return Incall(recvd, 0);
|
||||
@ -554,6 +574,8 @@ RPCChannel::BlockOnParent()
|
||||
mPending.pop();
|
||||
|
||||
MutexAutoUnlock unlock(mMutex);
|
||||
|
||||
CxxStackFrame f(*this, IN_MESSAGE, &recvd);
|
||||
if (recvd.is_rpc()) {
|
||||
// stack depth must be 0 here
|
||||
Incall(recvd, 0);
|
||||
@ -581,6 +603,17 @@ RPCChannel::UnblockFromParent()
|
||||
mBlockedOnParent = false;
|
||||
}
|
||||
|
||||
void
|
||||
RPCChannel::ExitedCxxStack()
|
||||
{
|
||||
Listener()->OnExitedCxxStack();
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
// see long comment in OnMaybeDequeueOne()
|
||||
EnqueuePendingMessages();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RPCChannel::DebugAbort(const char* file, int line, const char* cond,
|
||||
const char* why,
|
||||
@ -594,8 +627,7 @@ RPCChannel::DebugAbort(const char* file, int line, const char* cond,
|
||||
why,
|
||||
type, reply ? "reply" : "");
|
||||
// technically we need the mutex for this, but we're dying anyway
|
||||
fprintf(stderr, " local RPC stack size: %lu\n",
|
||||
mStack.size());
|
||||
DumpRPCStack(stderr, " ");
|
||||
fprintf(stderr, " remote RPC stack guess: %lu\n",
|
||||
mRemoteStackDepthGuess);
|
||||
fprintf(stderr, " deferred stack size: %lu\n",
|
||||
@ -615,6 +647,28 @@ RPCChannel::DebugAbort(const char* file, int line, const char* cond,
|
||||
NS_RUNTIMEABORT(why);
|
||||
}
|
||||
|
||||
void
|
||||
RPCChannel::DumpRPCStack(FILE* outfile, const char* const pfx)
|
||||
{
|
||||
NS_WARN_IF_FALSE(MessageLoop::current() != mWorkerLoop,
|
||||
"The worker thread had better be paused in a debugger!");
|
||||
|
||||
if (!outfile)
|
||||
outfile = stdout;
|
||||
|
||||
fprintf(outfile, "%sRPCChannel 'backtrace':\n", pfx);
|
||||
|
||||
// print a python-style backtrace, first frame to last
|
||||
for (PRUint32 i = 0; i < mCxxStackFrames.size(); ++i) {
|
||||
int32 id;
|
||||
const char* dir, *sems, *name;
|
||||
mCxxStackFrames[i].Describe(&id, &dir, &sems, &name);
|
||||
|
||||
fprintf(outfile, "%s[(%u) %s %s %s(actor=%d) ]\n", pfx,
|
||||
i, dir, sems, name, id);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// The methods below run in the context of the IO thread, and can proxy
|
||||
// back to the methods above
|
||||
@ -654,20 +708,16 @@ RPCChannel::OnChannelError()
|
||||
{
|
||||
AssertIOThread();
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
// NB: this can race with the `Goodbye' event being processed by
|
||||
// the worker thread
|
||||
if (ChannelClosing != mChannelState)
|
||||
mChannelState = ChannelError;
|
||||
if (ChannelClosing != mChannelState)
|
||||
mChannelState = ChannelError;
|
||||
|
||||
// skip SyncChannel::OnError(); we subsume its duties
|
||||
if (AwaitingSyncReply() || 0 < StackDepth())
|
||||
NotifyWorkerThread();
|
||||
}
|
||||
// skip SyncChannel::OnError(); we subsume its duties
|
||||
if (AwaitingSyncReply() || 0 < StackDepth())
|
||||
NotifyWorkerThread();
|
||||
|
||||
AsyncChannel::OnChannelError();
|
||||
PostErrorNotifyTask();
|
||||
}
|
||||
|
||||
} // namespace ipc
|
||||
|
@ -39,9 +39,12 @@
|
||||
#ifndef ipc_glue_RPCChannel_h
|
||||
#define ipc_glue_RPCChannel_h 1
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// FIXME/cjones probably shouldn't depend on STL
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
|
||||
@ -140,7 +143,7 @@ public:
|
||||
|
||||
// Return true iff this has code on the C++ stack.
|
||||
bool IsOnCxxStack() const {
|
||||
return 0 < mCxxStackFrames;
|
||||
return !mCxxStackFrames.empty();
|
||||
}
|
||||
|
||||
NS_OVERRIDE
|
||||
@ -183,7 +186,7 @@ protected:
|
||||
|
||||
NS_OVERRIDE
|
||||
virtual bool ShouldDeferNotifyMaybeError() {
|
||||
return 0 < mCxxStackFrames;
|
||||
return IsOnCxxStack();
|
||||
}
|
||||
|
||||
bool EventOccurred();
|
||||
@ -207,25 +210,44 @@ protected:
|
||||
Listener()->OnEnteredCxxStack();
|
||||
}
|
||||
|
||||
void ExitedCxxStack()
|
||||
{
|
||||
Listener()->OnExitedCxxStack();
|
||||
}
|
||||
void ExitedCxxStack();
|
||||
|
||||
enum Direction { IN_MESSAGE, OUT_MESSAGE };
|
||||
struct RPCFrame {
|
||||
RPCFrame(Direction direction, const Message* msg) :
|
||||
mDirection(direction), mMsg(msg)
|
||||
{ }
|
||||
|
||||
void Describe(int32* id, const char** dir, const char** sems,
|
||||
const char** name)
|
||||
const
|
||||
{
|
||||
*id = mMsg->routing_id();
|
||||
*dir = (IN_MESSAGE == mDirection) ? "in" : "out";
|
||||
*sems = mMsg->is_rpc() ? "rpc" : mMsg->is_sync() ? "sync" : "async";
|
||||
*name = mMsg->name();
|
||||
}
|
||||
|
||||
Direction mDirection;
|
||||
const Message* mMsg;
|
||||
};
|
||||
|
||||
class NS_STACK_CLASS CxxStackFrame
|
||||
{
|
||||
public:
|
||||
CxxStackFrame(RPCChannel& that) : mThat(that) {
|
||||
NS_ABORT_IF_FALSE(0 <= mThat.mCxxStackFrames,
|
||||
"mismatched CxxStackFrame ctor/dtor");
|
||||
|
||||
CxxStackFrame(RPCChannel& that, Direction direction,
|
||||
const Message* msg) : mThat(that) {
|
||||
mThat.AssertWorkerThread();
|
||||
|
||||
if (0 == mThat.mCxxStackFrames++)
|
||||
if (mThat.mCxxStackFrames.empty())
|
||||
mThat.EnteredCxxStack();
|
||||
mThat.mCxxStackFrames.push_back(RPCFrame(direction, msg));
|
||||
}
|
||||
|
||||
~CxxStackFrame() {
|
||||
bool exitingStack = (0 == --mThat.mCxxStackFrames);
|
||||
mThat.mCxxStackFrames.pop_back();
|
||||
bool exitingStack = mThat.mCxxStackFrames.empty();
|
||||
|
||||
// mListener could have gone away if Close() was called while
|
||||
// RPCChannel code was still on the stack
|
||||
@ -255,6 +277,10 @@ protected:
|
||||
const char* why,
|
||||
const char* type="rpc", bool reply=false);
|
||||
|
||||
// This method is only safe to call on the worker thread, or in a
|
||||
// debugger with all threads paused. |outfile| defaults to stdout.
|
||||
void DumpRPCStack(FILE* outfile=NULL, const char* const pfx="");
|
||||
|
||||
//
|
||||
// Queue of all incoming messages, except for replies to sync
|
||||
// messages, which are delivered directly to the SyncChannel
|
||||
@ -350,15 +376,15 @@ protected:
|
||||
// True iff the parent has put us in a |BlockChild()| state.
|
||||
bool mBlockedOnParent;
|
||||
|
||||
// Approximation of number of Sync/RPCChannel-code frames on the
|
||||
// C++ stack. It can only be interpreted as the implication
|
||||
// Approximation of Sync/RPCChannel-code frames on the C++ stack.
|
||||
// It can only be interpreted as the implication
|
||||
//
|
||||
// mCxxStackDepth > 0 => RPCChannel code on C++ stack
|
||||
// !mCxxStackFrames.empty() => RPCChannel code on C++ stack
|
||||
//
|
||||
// This member is only accessed on the worker thread, and so is
|
||||
// not protected by mMutex. It is managed exclusively by the
|
||||
// helper |class CxxStackFrame|.
|
||||
int mCxxStackFrames;
|
||||
std::vector<RPCFrame> mCxxStackFrames;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -213,19 +213,15 @@ SyncChannel::OnChannelError()
|
||||
{
|
||||
AssertIOThread();
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
// NB: this can race with the `Goodbye' event being processed by
|
||||
// the worker thread
|
||||
if (ChannelClosing != mChannelState)
|
||||
mChannelState = ChannelError;
|
||||
if (ChannelClosing != mChannelState)
|
||||
mChannelState = ChannelError;
|
||||
|
||||
if (AwaitingSyncReply())
|
||||
NotifyWorkerThread();
|
||||
}
|
||||
if (AwaitingSyncReply())
|
||||
NotifyWorkerThread();
|
||||
|
||||
AsyncChannel::OnChannelError();
|
||||
PostErrorNotifyTask();
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -617,6 +617,7 @@ class ExprCall(Node):
|
||||
def __init__(self, func, args=[ ]):
|
||||
assert hasattr(func, 'accept')
|
||||
assert isinstance(args, list)
|
||||
for arg in args: assert not isinstance(arg, str)
|
||||
|
||||
Node.__init__(self)
|
||||
self.func = func
|
||||
|
@ -1160,12 +1160,17 @@ class MessageDecl(ipdl.ast.MessageDecl):
|
||||
def msgClass(self):
|
||||
return 'Msg_%s'% (self.decl.progname)
|
||||
|
||||
def prettyMsgName(self, pfx=''):
|
||||
return pfx + self.msgClass()
|
||||
|
||||
def pqMsgClass(self):
|
||||
return '%s::%s'% (self.namespace, self.msgClass())
|
||||
|
||||
def msgCast(self, msgexpr):
|
||||
return ExprCast(msgexpr, Type(self.pqMsgClass(), const=1, ptr=1),
|
||||
static=1)
|
||||
return ExprCast(msgexpr, self.msgCxxType(const=1, ptr=1), static=1)
|
||||
|
||||
def msgCxxType(self, const=0, ref=0, ptr=0):
|
||||
return Type(self.pqMsgClass(), const=const, ref=ref, ptr=ptr)
|
||||
|
||||
def msgId(self): return self.msgClass()+ '__ID'
|
||||
def pqMsgId(self):
|
||||
@ -1185,6 +1190,9 @@ class MessageDecl(ipdl.ast.MessageDecl):
|
||||
def pqReplyId(self):
|
||||
return '%s::%s'% (self.namespace, self.replyId())
|
||||
|
||||
def prettyReplyName(self, pfx=''):
|
||||
return pfx + self.replyClass()
|
||||
|
||||
def actorDecl(self):
|
||||
return self.params[0]
|
||||
|
||||
@ -1645,7 +1653,8 @@ child actors.'''
|
||||
pipetypes=1)
|
||||
ns.addstmts([
|
||||
_generateMessageClass(md.msgClass(), md.msgId(),
|
||||
paramsIn, paramsOut, typedefs),
|
||||
paramsIn, paramsOut, typedefs,
|
||||
md.prettyMsgName(p.name+'::')),
|
||||
Whitespace.NL ])
|
||||
if md.hasReply():
|
||||
returnsIn = md.makeCxxParams(paramsems=None, returnsems='in',
|
||||
@ -1655,14 +1664,15 @@ child actors.'''
|
||||
ns.addstmts([
|
||||
_generateMessageClass(
|
||||
md.replyClass(), md.replyId(), returnsIn, returnsOut,
|
||||
typedefs),
|
||||
typedefs, md.prettyReplyName(p.name+'::')),
|
||||
Whitespace.NL ])
|
||||
|
||||
ns.addstmts([ Whitespace.NL, Whitespace.NL ])
|
||||
|
||||
##--------------------------------------------------
|
||||
|
||||
def _generateMessageClass(clsname, msgid, inparams, outparams, typedefs):
|
||||
def _generateMessageClass(clsname, msgid, inparams, outparams, typedefs,
|
||||
prettyName):
|
||||
cls = Class(name=clsname, inherits=[ Inherit(Type('IPC::Message')) ])
|
||||
cls.addstmt(Label.PRIVATE)
|
||||
cls.addstmts(typedefs)
|
||||
@ -1681,7 +1691,8 @@ def _generateMessageClass(clsname, msgid, inparams, outparams, typedefs):
|
||||
memberinits=[ExprMemberInit(ExprVar('IPC::Message'),
|
||||
[ ExprVar('MSG_ROUTING_NONE'),
|
||||
ExprVar('ID'),
|
||||
ExprVar('PRIORITY_NORMAL') ]) ])
|
||||
ExprVar('PRIORITY_NORMAL'),
|
||||
ExprLiteral.String(prettyName) ]) ])
|
||||
ctor.addstmts([
|
||||
StmtExpr(ExprCall(ExprVar('IPC::WriteParam'),
|
||||
args=[ ExprVar.THIS, ExprVar(p.name) ]))
|
||||
@ -2950,14 +2961,14 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
|
||||
dumpvar = ExprVar('aDump')
|
||||
getdump = MethodDefn(MethodDecl(
|
||||
'GetMinidump',
|
||||
'TakeMinidump',
|
||||
params=[ Decl(Type('nsIFile', ptrptr=1), dumpvar.name) ],
|
||||
ret=Type.BOOL,
|
||||
const=1))
|
||||
getdump.addstmts([
|
||||
CppDirective('ifdef', 'MOZ_CRASHREPORTER'),
|
||||
StmtReturn(ExprCall(
|
||||
ExprVar('XRE_GetMinidumpForChild'),
|
||||
ExprVar('XRE_TakeMinidumpForChild'),
|
||||
args=[ ExprCall(otherpidvar), dumpvar ])),
|
||||
CppDirective('else'),
|
||||
StmtReturn(ExprLiteral.FALSE),
|
||||
@ -3854,6 +3865,15 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
vars = [ ]
|
||||
readvars = [ ]
|
||||
stmts = [
|
||||
# this is kind of naughty, but the only two other options
|
||||
# are serializing the message name (yuk) or making the
|
||||
# IPDL|*Channel abstraction leak more
|
||||
StmtExpr(ExprCall(
|
||||
ExprSelect(
|
||||
ExprCast(msgvar, Type('Message', ref=1), const=1),
|
||||
'.', 'set_name'),
|
||||
args=[ ExprLiteral.String(md.prettyMsgName(self.protocol.name
|
||||
+'::')) ])),
|
||||
self.logMessage(md, md.msgCast(ExprAddrOf(msgvar)),
|
||||
'Received '),
|
||||
Whitespace.NL
|
||||
|
@ -58,23 +58,24 @@ FORCE_STATIC_LIB = 1
|
||||
EXPORT_LIBRARY = 1
|
||||
|
||||
IPDLTESTS = \
|
||||
TestSanity \
|
||||
TestRPCErrorCleanup \
|
||||
TestCrashCleanup \
|
||||
TestStackHooks \
|
||||
TestSyncWakeup \
|
||||
TestLatency \
|
||||
TestRPCRaces \
|
||||
TestRacyRPCReplies \
|
||||
TestRPCShutdownRace \
|
||||
TestHangs \
|
||||
TestArrays \
|
||||
TestBlockChild \
|
||||
TestManyChildAllocs \
|
||||
TestCrashCleanup \
|
||||
TestDesc \
|
||||
TestHangs \
|
||||
TestLatency \
|
||||
TestManyChildAllocs \
|
||||
TestMultiMgrs \
|
||||
TestNestedLoops \
|
||||
TestRPCErrorCleanup \
|
||||
TestRPCRaces \
|
||||
TestRPCShutdownRace \
|
||||
TestRacyRPCReplies \
|
||||
TestSanity \
|
||||
TestShmem \
|
||||
TestShutdown \
|
||||
TestArrays \
|
||||
TestStackHooks \
|
||||
TestSyncWakeup \
|
||||
$(NULL)
|
||||
|
||||
IPDLTESTSRCS = $(addsuffix .cpp,$(IPDLTESTS))
|
||||
|
34
ipc/ipdl/test/cxx/PTestNestedLoops.ipdl
Normal file
34
ipc/ipdl/test/cxx/PTestNestedLoops.ipdl
Normal file
@ -0,0 +1,34 @@
|
||||
|
||||
namespace mozilla {
|
||||
namespace _ipdltest {
|
||||
|
||||
|
||||
rpc protocol PTestNestedLoops {
|
||||
|
||||
child:
|
||||
async Start();
|
||||
rpc R();
|
||||
__delete__();
|
||||
|
||||
parent:
|
||||
async Nonce();
|
||||
|
||||
|
||||
state START:
|
||||
send Start goto RACE;
|
||||
|
||||
state RACE:
|
||||
recv Nonce goto RACE1;
|
||||
call R goto RACE2;
|
||||
state RACE1:
|
||||
call R goto DEAD;
|
||||
state RACE2:
|
||||
recv Nonce goto DEAD;
|
||||
|
||||
state DEAD:
|
||||
send __delete__;
|
||||
};
|
||||
|
||||
|
||||
} // namespace mozilla
|
||||
} // namespace _ipdltest
|
106
ipc/ipdl/test/cxx/TestNestedLoops.cpp
Normal file
106
ipc/ipdl/test/cxx/TestNestedLoops.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
#include "base/basictypes.h"
|
||||
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "TestNestedLoops.h"
|
||||
|
||||
#include "IPDLUnitTests.h" // fail etc.
|
||||
|
||||
template<>
|
||||
struct RunnableMethodTraits<mozilla::_ipdltest::TestNestedLoopsParent>
|
||||
{
|
||||
static void RetainCallee(mozilla::_ipdltest::TestNestedLoopsParent* obj) { }
|
||||
static void ReleaseCallee(mozilla::_ipdltest::TestNestedLoopsParent* obj) { }
|
||||
};
|
||||
|
||||
namespace mozilla {
|
||||
namespace _ipdltest {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// parent
|
||||
|
||||
TestNestedLoopsParent::TestNestedLoopsParent() : mBreakNestedLoop(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(TestNestedLoopsParent);
|
||||
}
|
||||
|
||||
TestNestedLoopsParent::~TestNestedLoopsParent()
|
||||
{
|
||||
MOZ_COUNT_DTOR(TestNestedLoopsParent);
|
||||
}
|
||||
|
||||
void
|
||||
TestNestedLoopsParent::Main()
|
||||
{
|
||||
if (!SendStart())
|
||||
fail("sending Start");
|
||||
|
||||
// sigh ... spin for a while to let Nonce arrive
|
||||
puts(" (sleeping to wait for nonce ... sorry)");
|
||||
PR_Sleep(5000);
|
||||
|
||||
// while waiting for the reply to R, we'll receive Nonce
|
||||
if (!CallR())
|
||||
fail("calling R");
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
bool
|
||||
TestNestedLoopsParent::RecvNonce()
|
||||
{
|
||||
// if we have an OnMaybeDequeueOne waiting for us (we may not, due
|
||||
// to the inherent race condition in this test, then this event
|
||||
// must be ordered after it in the queue
|
||||
MessageLoop::current()->PostTask(
|
||||
FROM_HERE,
|
||||
NewRunnableMethod(this, &TestNestedLoopsParent::BreakNestedLoop));
|
||||
|
||||
// sigh ... spin for a while to let the reply to R arrive
|
||||
puts(" (sleeping to wait for reply to R ... sorry)");
|
||||
PR_Sleep(5000);
|
||||
|
||||
// sigh ... we have no idea when code might do this
|
||||
do {
|
||||
if (!NS_ProcessNextEvent(nsnull, PR_FALSE))
|
||||
fail("expected at least one pending event");
|
||||
} while (!mBreakNestedLoop);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
TestNestedLoopsParent::BreakNestedLoop()
|
||||
{
|
||||
mBreakNestedLoop = true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// child
|
||||
|
||||
TestNestedLoopsChild::TestNestedLoopsChild()
|
||||
{
|
||||
MOZ_COUNT_CTOR(TestNestedLoopsChild);
|
||||
}
|
||||
|
||||
TestNestedLoopsChild::~TestNestedLoopsChild()
|
||||
{
|
||||
MOZ_COUNT_DTOR(TestNestedLoopsChild);
|
||||
}
|
||||
|
||||
bool
|
||||
TestNestedLoopsChild::RecvStart()
|
||||
{
|
||||
if (!SendNonce())
|
||||
fail("sending Nonce");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TestNestedLoopsChild::AnswerR()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace _ipdltest
|
||||
} // namespace mozilla
|
69
ipc/ipdl/test/cxx/TestNestedLoops.h
Normal file
69
ipc/ipdl/test/cxx/TestNestedLoops.h
Normal file
@ -0,0 +1,69 @@
|
||||
#ifndef mozilla__ipdltest_TestNestedLoops_h
|
||||
#define mozilla__ipdltest_TestNestedLoops_h 1
|
||||
|
||||
#include "mozilla/_ipdltest/IPDLUnitTests.h"
|
||||
|
||||
#include "mozilla/_ipdltest/PTestNestedLoopsParent.h"
|
||||
#include "mozilla/_ipdltest/PTestNestedLoopsChild.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace _ipdltest {
|
||||
|
||||
|
||||
class TestNestedLoopsParent :
|
||||
public PTestNestedLoopsParent
|
||||
{
|
||||
public:
|
||||
TestNestedLoopsParent();
|
||||
virtual ~TestNestedLoopsParent();
|
||||
|
||||
void Main();
|
||||
|
||||
protected:
|
||||
NS_OVERRIDE
|
||||
virtual bool RecvNonce();
|
||||
|
||||
void BreakNestedLoop();
|
||||
|
||||
NS_OVERRIDE
|
||||
virtual void ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
if (NormalShutdown != why)
|
||||
fail("unexpected destruction!");
|
||||
passed("ok");
|
||||
QuitParent();
|
||||
}
|
||||
|
||||
bool mBreakNestedLoop;
|
||||
};
|
||||
|
||||
|
||||
class TestNestedLoopsChild :
|
||||
public PTestNestedLoopsChild
|
||||
{
|
||||
public:
|
||||
TestNestedLoopsChild();
|
||||
virtual ~TestNestedLoopsChild();
|
||||
|
||||
protected:
|
||||
NS_OVERRIDE
|
||||
virtual bool RecvStart();
|
||||
|
||||
NS_OVERRIDE
|
||||
virtual bool AnswerR();
|
||||
|
||||
NS_OVERRIDE
|
||||
virtual void ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
if (NormalShutdown != why)
|
||||
fail("unexpected destruction!");
|
||||
QuitChild();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace _ipdltest
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
#endif // ifndef mozilla__ipdltest_TestNestedLoops_h
|
@ -14,6 +14,7 @@ IPDLSRCS = \
|
||||
PTestMultiMgrsLeft.ipdl \
|
||||
PTestMultiMgrsRight.ipdl \
|
||||
PTestMultiMgrsBottom.ipdl \
|
||||
PTestNestedLoops.ipdl \
|
||||
PTestRacyRPCReplies.ipdl \
|
||||
PTestRPCErrorCleanup.ipdl \
|
||||
PTestRPCRaces.ipdl \
|
||||
|
@ -48,7 +48,8 @@
|
||||
*/
|
||||
|
||||
const TOPIC_EXPIRATION_FINISHED = "places-expiration-finished";
|
||||
const MAX_WAIT_SECONDS = 3;
|
||||
const MAX_WAIT_SECONDS = 4;
|
||||
const INTERVAL_CUSHION = 2;
|
||||
|
||||
let os = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
@ -107,7 +108,8 @@ function run_next_test() {
|
||||
};
|
||||
os.addObserver(gCurrentTest.observer, TOPIC_EXPIRATION_FINISHED, false);
|
||||
setInterval(gCurrentTest.interval);
|
||||
let waitSeconds = Math.min(MAX_WAIT_SECONDS, gCurrentTest.interval + 1);
|
||||
let waitSeconds = Math.min(MAX_WAIT_SECONDS,
|
||||
gCurrentTest.interval + INTERVAL_CUSHION);
|
||||
do_timeout(waitSeconds * 1000, check_result);
|
||||
}
|
||||
else {
|
||||
|
@ -1418,13 +1418,22 @@ SetRemoteExceptionHandler()
|
||||
|
||||
|
||||
bool
|
||||
GetMinidumpForChild(PRUint32 childPid, nsIFile** dump)
|
||||
TakeMinidumpForChild(PRUint32 childPid, nsIFile** dump)
|
||||
{
|
||||
if (!GetEnabled())
|
||||
return false;
|
||||
|
||||
MutexAutoLock lock(*dumpMapLock);
|
||||
return pidToMinidump->Get(childPid, dump);
|
||||
|
||||
nsCOMPtr<nsIFile> d;
|
||||
bool found = pidToMinidump->Get(childPid, getter_AddRefs(d));
|
||||
if (found)
|
||||
pidToMinidump->Remove(childPid);
|
||||
|
||||
*dump = NULL;
|
||||
d.swap(*dump);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -77,8 +77,9 @@ nsresult SetSubmitReports(PRBool aSubmitReport);
|
||||
// Out-of-process crash reporter API.
|
||||
|
||||
// Return true iff a dump was found for |childPid|, and return the
|
||||
// path in |dump|.
|
||||
bool GetMinidumpForChild(PRUint32 childPid, nsIFile** dump NS_OUTPARAM);
|
||||
// path in |dump|. The caller owns the last reference to |dump| if it
|
||||
// is non-NULL.
|
||||
bool TakeMinidumpForChild(PRUint32 childPid, nsIFile** dump NS_OUTPARAM);
|
||||
|
||||
# if defined(XP_WIN32)
|
||||
// Parent-side API for children
|
||||
|
@ -1,376 +0,0 @@
|
||||
/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 sts=2 et filetype=javascript
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Windows Download Taskbar Progress.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Siddharth Agarwal <sid.bugzilla@gmail.com>
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
let EXPORTED_SYMBOLS = [
|
||||
"DownloadTaskbarProgress",
|
||||
];
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Constants
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
const kTaskbarID = "@mozilla.org/windows-taskbar;1";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// DownloadTaskbarProgress Object
|
||||
|
||||
const DownloadTaskbarProgress =
|
||||
{
|
||||
/**
|
||||
* Called when a browser window appears. This has an effect only when we
|
||||
* don't already have an active window.
|
||||
*
|
||||
* @param aWindow
|
||||
* The browser window that we'll potentially use to display the
|
||||
* progress.
|
||||
*/
|
||||
onBrowserWindowLoad: function DTP_onBrowserWindowLoad(aWindow)
|
||||
{
|
||||
if (!DownloadTaskbarProgressUpdater) {
|
||||
return;
|
||||
}
|
||||
if (!DownloadTaskbarProgressUpdater._activeTaskbarProgress) {
|
||||
DownloadTaskbarProgressUpdater._setActiveWindow(aWindow, false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the download window appears. The download window will take
|
||||
* over as the active window.
|
||||
*/
|
||||
onDownloadWindowLoad: function DTP_onDownloadWindowLoad(aWindow)
|
||||
{
|
||||
if (!DownloadTaskbarProgressUpdater) {
|
||||
return;
|
||||
}
|
||||
DownloadTaskbarProgressUpdater._setActiveWindow(aWindow, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Getters for internal DownloadTaskbarProgressUpdater values
|
||||
*/
|
||||
|
||||
get activeTaskbarProgress() {
|
||||
if (!DownloadTaskbarProgressUpdater) {
|
||||
return null;
|
||||
}
|
||||
return DownloadTaskbarProgressUpdater._activeTaskbarProgress;
|
||||
},
|
||||
|
||||
get activeWindowIsDownloadWindow() {
|
||||
if (!DownloadTaskbarProgressUpdater) {
|
||||
return null;
|
||||
}
|
||||
return DownloadTaskbarProgressUpdater._activeWindowIsDownloadWindow;
|
||||
},
|
||||
|
||||
get taskbarState() {
|
||||
if (!DownloadTaskbarProgressUpdater) {
|
||||
return null;
|
||||
}
|
||||
return DownloadTaskbarProgressUpdater._taskbarState;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// DownloadTaskbarProgressUpdater Object
|
||||
|
||||
var DownloadTaskbarProgressUpdater =
|
||||
{
|
||||
/// Reference to the taskbar.
|
||||
_taskbar: null,
|
||||
|
||||
/// Reference to the download manager.
|
||||
_dm: null,
|
||||
|
||||
/**
|
||||
* Initialize and register ourselves as a download progress listener.
|
||||
*/
|
||||
_init: function DTPU_init()
|
||||
{
|
||||
if (!(kTaskbarID in Cc)) {
|
||||
// This means that the component isn't available
|
||||
DownloadTaskbarProgressUpdater = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this._taskbar = Cc[kTaskbarID].getService(Ci.nsIWinTaskbar);
|
||||
if (!this._taskbar.available) {
|
||||
// The Windows version is probably too old
|
||||
DownloadTaskbarProgressUpdater = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this._dm = Cc["@mozilla.org/download-manager;1"].
|
||||
getService(Ci.nsIDownloadManager);
|
||||
this._dm.addListener(this);
|
||||
|
||||
this._updateStatus();
|
||||
// onBrowserWindowLoad/onDownloadWindowLoad are going to set the active
|
||||
// window, so don't do it here.
|
||||
},
|
||||
|
||||
/**
|
||||
* This holds a reference to the taskbar progress for the window we're
|
||||
* working with. This window would preferably be download window, but can be
|
||||
* another window if it isn't open.
|
||||
*/
|
||||
_activeTaskbarProgress: null,
|
||||
|
||||
/// Whether the active window is the download window
|
||||
_activeWindowIsDownloadWindow: false,
|
||||
|
||||
/**
|
||||
* Sets the active window, and whether it's the download window. This takes
|
||||
* care of clearing out the previous active window's taskbar item, updating
|
||||
* the taskbar, and setting an onunload listener.
|
||||
*
|
||||
* @param aWindow
|
||||
* The window to set as active.
|
||||
* @param aIsDownloadWindow
|
||||
* Whether this window is a download window.
|
||||
*/
|
||||
_setActiveWindow: function DTPU_setActiveWindow(aWindow, aIsDownloadWindow)
|
||||
{
|
||||
// Clear out the taskbar for the old active window. (If there was no active
|
||||
// window, this is a no-op.)
|
||||
this._clearTaskbar();
|
||||
|
||||
this._activeWindowIsDownloadWindow = aIsDownloadWindow;
|
||||
if (aWindow) {
|
||||
// Get the taskbar progress for this window
|
||||
let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIWebNavigation).
|
||||
QueryInterface(Ci.nsIDocShellTreeItem).treeOwner.
|
||||
QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIXULWindow).docShell;
|
||||
let taskbarProgress = this._taskbar.getTaskbarProgress(docShell);
|
||||
this._activeTaskbarProgress = taskbarProgress;
|
||||
|
||||
this._updateTaskbar();
|
||||
// _onActiveWindowUnload is idempotent, so we don't need to check whether
|
||||
// we've already set this before or not.
|
||||
aWindow.addEventListener("unload", function () {
|
||||
DownloadTaskbarProgressUpdater._onActiveWindowUnload(taskbarProgress);
|
||||
}, false);
|
||||
}
|
||||
else {
|
||||
this._activeTaskbarProgress = null;
|
||||
}
|
||||
},
|
||||
|
||||
/// Current state displayed on the active window's taskbar item
|
||||
_taskbarState: Ci.nsITaskbarProgress.STATE_NO_PROGRESS,
|
||||
_totalSize: 0,
|
||||
_totalTransferred: 0,
|
||||
|
||||
/**
|
||||
* Update the active window's taskbar indicator with the current state. There
|
||||
* are two cases here:
|
||||
* 1. If the active window is the download window, then we always update
|
||||
* the taskbar indicator.
|
||||
* 2. If the active window isn't the download window, then we update only if
|
||||
* the status is Normal, i.e. one or more downloads are currently
|
||||
* progressing. If we aren't, then we clear the indicator.
|
||||
*/
|
||||
_updateTaskbar: function DTPU_updateTaskbar()
|
||||
{
|
||||
if (!this._activeTaskbarProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the active window is not the download manager window, set the state
|
||||
// only if it is Normal
|
||||
if (this._activeWindowIsDownloadWindow ||
|
||||
(this._taskbarState == Ci.nsITaskbarProgress.STATE_NORMAL)) {
|
||||
this._activeTaskbarProgress.setProgressState(this._taskbarState,
|
||||
this._totalTransferred,
|
||||
this._totalSize);
|
||||
}
|
||||
// Clear any state otherwise
|
||||
else {
|
||||
this._clearTaskbar();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear taskbar state. This is needed:
|
||||
* - to transfer the indicator off a window before transferring it onto
|
||||
* another one
|
||||
* - whenever we don't want to show it for a non-download window.
|
||||
*/
|
||||
_clearTaskbar: function DTPU_clearTaskbar()
|
||||
{
|
||||
if (this._activeTaskbarProgress) {
|
||||
this._activeTaskbarProgress.setProgressState(
|
||||
Ci.nsITaskbarProgress.STATE_NO_PROGRESS
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Update this._taskbarState, this._totalSize and this._totalTransferred.
|
||||
* This is called when the download manager is initialized or when the
|
||||
* progress or state of a download changes.
|
||||
* We compute the number of active and paused downloads, and the total size
|
||||
* and total amount already transferred across whichever downloads we have
|
||||
* the data for.
|
||||
* - If there are no active downloads, then we don't want to show any
|
||||
* progress.
|
||||
* - If the number of active downloads is equal to the number of paused
|
||||
* downloads, then we show a paused indicator if we know the size of at
|
||||
* least one download, and no indicator if we don't.
|
||||
* - If the number of active downloads is more than the number of paused
|
||||
* downloads, then we show a "normal" indicator if we know the size of at
|
||||
* least one download, and an indeterminate indicator if we don't.
|
||||
*/
|
||||
_updateStatus: function DTPU_updateStatus()
|
||||
{
|
||||
let numActive = this._dm.activeDownloadCount;
|
||||
let totalSize = 0, totalTransferred = 0;
|
||||
|
||||
if (numActive == 0) {
|
||||
this._taskbarState = Ci.nsITaskbarProgress.STATE_NO_PROGRESS;
|
||||
}
|
||||
else {
|
||||
let numPaused = 0;
|
||||
|
||||
// Enumerate all active downloads
|
||||
let downloads = this._dm.activeDownloads;
|
||||
while (downloads.hasMoreElements()) {
|
||||
let download = downloads.getNext().QueryInterface(Ci.nsIDownload);
|
||||
// Only set values if we actually know the download size
|
||||
if (download.percentComplete < 100 && download.size > 0) {
|
||||
totalSize += download.size;
|
||||
totalTransferred += download.amountTransferred;
|
||||
}
|
||||
// We might need to display a paused state, so track this
|
||||
if (download.state == this._dm.DOWNLOAD_PAUSED) {
|
||||
numPaused++;
|
||||
}
|
||||
}
|
||||
|
||||
// If all downloads are paused, show the progress as paused, unless we
|
||||
// don't have any information about sizes, in which case we don't
|
||||
// display anything
|
||||
if (numActive == numPaused) {
|
||||
if (totalSize == 0) {
|
||||
this._taskbarState = Ci.nsITaskbarProgress.STATE_NO_PROGRESS;
|
||||
totalTransferred = 0;
|
||||
}
|
||||
else {
|
||||
this._taskbarState = Ci.nsITaskbarProgress.STATE_PAUSED;
|
||||
}
|
||||
}
|
||||
// If at least one download is not paused, and we don't have any
|
||||
// information about download sizes, display an indeterminate indicator
|
||||
else if (totalSize == 0) {
|
||||
this._taskbarState = Ci.nsITaskbarProgress.STATE_INDETERMINATE;
|
||||
totalTransferred = 0;
|
||||
}
|
||||
// Otherwise display a normal progress bar
|
||||
else {
|
||||
this._taskbarState = Ci.nsITaskbarProgress.STATE_NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
this._totalSize = totalSize;
|
||||
this._totalTransferred = totalTransferred;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a window that at one point has been an active window is
|
||||
* closed. If this window is currently the active window, we need to look for
|
||||
* another window and make that our active window.
|
||||
*
|
||||
* This function is idempotent, so multiple calls for the same window are not
|
||||
* a problem.
|
||||
*
|
||||
* @param aTaskbarProgress
|
||||
* The taskbar progress for the window that is being unloaded.
|
||||
*/
|
||||
_onActiveWindowUnload: function DTPU_onActiveWindowUnload(aTaskbarProgress)
|
||||
{
|
||||
if (this._activeTaskbarProgress == aTaskbarProgress) {
|
||||
let windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].
|
||||
getService(Ci.nsIWindowMediator);
|
||||
let windows = windowMediator.getEnumerator(null);
|
||||
let newActiveWindow = null;
|
||||
if (windows.hasMoreElements()) {
|
||||
newActiveWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
|
||||
}
|
||||
|
||||
// We aren't ever going to reach this point while the download manager is
|
||||
// open, so it's safe to assume false for the second operand
|
||||
this._setActiveWindow(newActiveWindow, false);
|
||||
}
|
||||
},
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// nsIDownloadProgressListener
|
||||
|
||||
/**
|
||||
* Update status if a download's progress has changed.
|
||||
*/
|
||||
onProgressChange: function DTPU_onProgressChange()
|
||||
{
|
||||
this._updateStatus();
|
||||
this._updateTaskbar();
|
||||
},
|
||||
|
||||
/**
|
||||
* Update status if a download's state has changed.
|
||||
*/
|
||||
onDownloadStateChange: function DTPU_onDownloadStateChange()
|
||||
{
|
||||
this._updateStatus();
|
||||
this._updateTaskbar();
|
||||
},
|
||||
|
||||
onSecurityChange: function() { },
|
||||
|
||||
onStateChange: function() { },
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Initialization
|
||||
|
||||
DownloadTaskbarProgressUpdater._init();
|
@ -51,12 +51,6 @@ EXTRA_JS_MODULES = \
|
||||
DownloadUtils.jsm \
|
||||
$(NULL)
|
||||
|
||||
ifeq ($(OS_ARCH),WINNT)
|
||||
EXTRA_JS_MODULES += \
|
||||
DownloadTaskbarProgress.jsm \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifdef ENABLE_TESTS
|
||||
DIRS += tests
|
||||
endif
|
||||
|
@ -484,14 +484,6 @@ function Startup()
|
||||
e.preventDefault();
|
||||
}
|
||||
}, false);
|
||||
|
||||
#ifdef XP_WIN
|
||||
#ifndef WINCE
|
||||
let tempScope = {};
|
||||
Cu.import("resource://gre/modules/DownloadTaskbarProgress.jsm", tempScope);
|
||||
tempScope.DownloadTaskbarProgress.onDownloadWindowLoad(window);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
function Shutdown()
|
||||
|
@ -82,18 +82,5 @@ _CHROME_FILES += \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifeq ($(OS_ARCH),WINNT)
|
||||
_CHROME_FILES += \
|
||||
test_taskbarprogress_downloadstates.xul \
|
||||
$(NULL)
|
||||
|
||||
_BROWSER_FILES = \
|
||||
browser_taskbarprogress_service.js \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_BROWSER_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
|
||||
endif
|
||||
|
||||
libs:: $(_CHROME_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
|
||||
|
@ -1,147 +0,0 @@
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
const kTaskbarID = "@mozilla.org/windows-taskbar;1";
|
||||
const DOWNLOAD_MANAGER_URL = "chrome://mozapps/content/downloads/downloads.xul";
|
||||
const DLMGR_UI_DONE = "download-manager-ui-done";
|
||||
|
||||
let DownloadTaskbarProgress, TaskbarService, observerService, wwatch;
|
||||
let gGen = null;
|
||||
|
||||
function test() {
|
||||
gGen = doTest();
|
||||
gGen.next();
|
||||
}
|
||||
|
||||
function continueTest() {
|
||||
gGen.next();
|
||||
}
|
||||
|
||||
function doTest() {
|
||||
let isWin7OrHigher = false;
|
||||
try {
|
||||
let version = Cc["@mozilla.org/system-info;1"]
|
||||
.getService(Ci.nsIPropertyBag2)
|
||||
.getProperty("version");
|
||||
isWin7OrHigher = (parseFloat(version) >= 6.1);
|
||||
} catch (ex) { }
|
||||
|
||||
is(!!Win7Features, isWin7OrHigher, "Win7Features available when it should be");
|
||||
if (!isWin7OrHigher) {
|
||||
return;
|
||||
}
|
||||
waitForExplicitFinish();
|
||||
|
||||
let tempScope = {};
|
||||
Cu.import("resource://gre/modules/DownloadTaskbarProgress.jsm", tempScope);
|
||||
|
||||
DownloadTaskbarProgress = tempScope.DownloadTaskbarProgress;
|
||||
TaskbarService = Cc[kTaskbarID].getService(Ci.nsIWinTaskbar);
|
||||
|
||||
observerService = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
|
||||
wwatch = Cc["@mozilla.org/embedcomp/window-watcher;1"].
|
||||
getService(Ci.nsIWindowWatcher);
|
||||
|
||||
|
||||
isnot(DownloadTaskbarProgress, null, "Download taskbar progress service exists");
|
||||
is(TaskbarService.available, true, "Taskbar Service is available");
|
||||
|
||||
//Manually call onBrowserWindowLoad because this is delayed in 10sec
|
||||
DownloadTaskbarProgress.onBrowserWindowLoad(window);
|
||||
|
||||
|
||||
is(DownloadTaskbarProgress.activeWindowIsDownloadWindow, false,
|
||||
"DownloadTaskbarProgress window is not the Download window");
|
||||
|
||||
checkActiveTaskbar(false, window);
|
||||
|
||||
openDownloadManager(continueTest);
|
||||
yield;
|
||||
|
||||
let DMWindow = Cc["@mozilla.org/appshell/window-mediator;1"].
|
||||
getService(Ci.nsIWindowMediator).
|
||||
getMostRecentWindow("Download:Manager");
|
||||
|
||||
ok(DMWindow, "Download Manager window was opened");
|
||||
checkActiveTaskbar(true, DMWindow);
|
||||
|
||||
DMWindow.close();
|
||||
setTimeout(continueTest, 100);
|
||||
yield;
|
||||
|
||||
checkActiveTaskbar(false, window);
|
||||
|
||||
let browserWindow = openBrowserWindow(continueTest);
|
||||
yield;
|
||||
|
||||
ok(browserWindow, "Browser window was opened");
|
||||
DownloadTaskbarProgress.onBrowserWindowLoad(browserWindow);
|
||||
|
||||
// The owner window should not have changed, since our
|
||||
// original window still exists
|
||||
checkActiveTaskbar(false, window);
|
||||
|
||||
browserWindow.close();
|
||||
finish();
|
||||
}
|
||||
|
||||
function checkActiveTaskbar(isDownloadManager, ownerWindow) {
|
||||
|
||||
isnot(DownloadTaskbarProgress.activeTaskbarProgress, null, "DownloadTaskbarProgress has an active taskbar");
|
||||
|
||||
is(DownloadTaskbarProgress.activeWindowIsDownloadWindow, isDownloadManager,
|
||||
"The active taskbar progress " + (isDownloadManager ? "is" : "is not") + " the Download Manager");
|
||||
|
||||
if (ownerWindow) {
|
||||
let ownerWindowDocShell = ownerWindow.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIWebNavigation).
|
||||
QueryInterface(Ci.nsIDocShellTreeItem).treeOwner.
|
||||
QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIXULWindow).docShell;
|
||||
|
||||
let windowTaskbarProgress = TaskbarService.getTaskbarProgress(ownerWindowDocShell);
|
||||
|
||||
is(DownloadTaskbarProgress.activeTaskbarProgress, windowTaskbarProgress,
|
||||
"DownloadTaskbarProgress has the expected taskbar as active");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function openBrowserWindow(callback) {
|
||||
let blank = Cc["@mozilla.org/supports-string;1"].
|
||||
createInstance(Ci.nsISupportsString);
|
||||
blank.data = "about:blank";
|
||||
|
||||
browserWindow = wwatch.openWindow(null, "chrome://browser/content/",
|
||||
"_blank", "chrome,dialog=no", blank);
|
||||
|
||||
let helperFunc = function() {
|
||||
callback();
|
||||
browserWindow.removeEventListener("load", helperFunc, false);
|
||||
}
|
||||
|
||||
browserWindow.addEventListener("load", helperFunc, false);
|
||||
return browserWindow;
|
||||
}
|
||||
|
||||
function openDownloadManager(callback) {
|
||||
|
||||
let testObs = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic != DLMGR_UI_DONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback();
|
||||
observerService.removeObserver(testObs, DLMGR_UI_DONE);
|
||||
}
|
||||
};
|
||||
|
||||
observerService.addObserver(testObs, DLMGR_UI_DONE, false);
|
||||
|
||||
Cc["@mozilla.org/download-manager-ui;1"].
|
||||
getService(Ci.nsIDownloadManagerUI).show();
|
||||
|
||||
}
|
@ -1,276 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Windows Download Taskbar Progress.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Felipe Gomes <felipc@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/**
|
||||
* This tests that the Windows 7 Taskbar Progress is correctly updated when
|
||||
* the download state changes.
|
||||
*/
|
||||
-->
|
||||
|
||||
<window title="Win7 Taskbar Progress"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="test();">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/MochiKit/packed.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/chrome/toolkit/mozapps/downloads/tests/chrome/utils.js"/>
|
||||
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
const kTaskbarID = "@mozilla.org/windows-taskbar;1";
|
||||
const DOWNLOAD_MANAGER_URL = "chrome://mozapps/content/downloads/downloads.xul";
|
||||
const DLMGR_UI_DONE = "download-manager-ui-done";
|
||||
|
||||
const Cu = Components.utils;
|
||||
const nsITP = Ci.nsITaskbarProgress;
|
||||
|
||||
let DownloadTaskbarProgress, dm;
|
||||
let downloadA, downloadB, gGen;
|
||||
|
||||
let testState = {
|
||||
activeDownloadCount: 0,
|
||||
pausedDownloadCount: 0,
|
||||
|
||||
regularStateTested: false,
|
||||
pausedStateTested: false,
|
||||
noDownloadsStateTested: false
|
||||
}
|
||||
|
||||
function continueTest() {
|
||||
setTimeout(function() {
|
||||
gGen.next();
|
||||
}, 10);
|
||||
}
|
||||
|
||||
function downloadListener()
|
||||
{
|
||||
this.wasPaused = false;
|
||||
}
|
||||
|
||||
downloadListener.prototype = {
|
||||
|
||||
onStateChange: function(a, b, c, d, e) {
|
||||
checkCorrectState();
|
||||
},
|
||||
|
||||
onDownloadStateChange: function(aState, aDownload)
|
||||
{
|
||||
|
||||
if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_PAUSED) {
|
||||
testState.pausedDownloadCount++;
|
||||
testState.activeDownloadCount--;
|
||||
|
||||
if (aDownload == downloadB) {
|
||||
continueTest();
|
||||
}
|
||||
}
|
||||
|
||||
if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_FINISHED) {
|
||||
testState.activeDownloadCount--;
|
||||
aDownload.targetFile.remove(false);
|
||||
|
||||
setTimeout(function(){
|
||||
//Check when download finishes too
|
||||
checkCorrectState();
|
||||
if(testState.activeDownloadCount == 0) {
|
||||
finishTest();
|
||||
}
|
||||
},10);
|
||||
}
|
||||
|
||||
if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING) {
|
||||
//when DownloadB is added, begin pausing phase
|
||||
if (aDownload == downloadB && !this.wasPaused) {
|
||||
this.wasPaused = true;
|
||||
continueTest();
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
onProgressChange: function(a, b, c, d, e, f, g) { },
|
||||
onSecurityChange: function(a, b, c, d) { }
|
||||
};
|
||||
|
||||
function testSteps() {
|
||||
|
||||
// Step 1 - Add downloads
|
||||
testState.activeDownloadCount++;
|
||||
downloadA = addDownload();
|
||||
testState.activeDownloadCount++;
|
||||
downloadB = addDownload();
|
||||
yield;
|
||||
|
||||
// Step 2 - Pause downloads
|
||||
dm.pauseDownload(downloadA.id);
|
||||
continueTest();
|
||||
yield;
|
||||
dm.pauseDownload(downloadB.id);
|
||||
yield;
|
||||
|
||||
// Step 3 - Resume downloads
|
||||
testState.activeDownloadCount++;
|
||||
testState.pausedDownloadCount--;
|
||||
dm.resumeDownload(downloadA.id);
|
||||
|
||||
testState.activeDownloadCount++;
|
||||
testState.pausedDownloadCount--;
|
||||
dm.resumeDownload(downloadB.id);
|
||||
yield;
|
||||
}
|
||||
|
||||
function finishTest() {
|
||||
ok(testState.regularStateTested, "Tests went through regular download state");
|
||||
ok(testState.pausedStateTested, "Tests went through paused download state");
|
||||
ok(testState.noDownloadsStateTested, "Tests went through finished downloads state");
|
||||
dm.removeListener(downloadListener);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function checkCorrectState() {
|
||||
|
||||
if(testState.activeDownloadCount < 0 || testState.pausedDownloadCount < 0) {
|
||||
ok(false, "There shouldn't be negative download counts");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
let taskbarState = DownloadTaskbarProgress.taskbarState;
|
||||
|
||||
if (testState.activeDownloadCount) {
|
||||
//There's at least one active download
|
||||
ok(taskbarState == nsITP.STATE_NORMAL || taskbarState == nsITP.STATE_INDETERMINATE, "Correct downloading state");
|
||||
testState.regularStateTested = true;
|
||||
} else if (testState.pausedDownloadCount) {
|
||||
//There are no active downloads but there are paused ones
|
||||
ok(taskbarState == nsITP.STATE_PAUSED, "Correct paused state");
|
||||
testState.pausedStateTested = true;
|
||||
} else {
|
||||
//No more downloads
|
||||
ok(taskbarState == nsITP.STATE_NO_PROGRESS, "Correct finished downloads state");
|
||||
testState.noDownloadsStateTested = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function test() {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
testSetup();
|
||||
}
|
||||
|
||||
function testSetup()
|
||||
{
|
||||
//Test setup
|
||||
let dmui = getDMUI();
|
||||
if (!dmui) {
|
||||
todo(false, "skip test for toolkit download manager UI");
|
||||
return;
|
||||
}
|
||||
|
||||
let isWin7OrHigher = false;
|
||||
try {
|
||||
let version = Cc["@mozilla.org/system-info;1"]
|
||||
.getService(Ci.nsIPropertyBag2)
|
||||
.getProperty("version");
|
||||
isWin7OrHigher = (parseFloat(version) >= 6.1);
|
||||
} catch (ex) { }
|
||||
|
||||
if (!isWin7OrHigher) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tempScope = {};
|
||||
Cu.import("resource://gre/modules/DownloadTaskbarProgress.jsm", tempScope);
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
DownloadTaskbarProgress = tempScope.DownloadTaskbarProgress;
|
||||
let TaskbarService = Cc[kTaskbarID].getService(Ci.nsIWinTaskbar);
|
||||
|
||||
isnot(DownloadTaskbarProgress, null, "Download taskbar progress service exists");
|
||||
is(TaskbarService.available, true, "Taskbar Service is available");
|
||||
|
||||
dm = Cc["@mozilla.org/download-manager;1"].
|
||||
getService(Ci.nsIDownloadManager);
|
||||
|
||||
// First, we clear out the database
|
||||
dm.DBConnection.executeSimpleSQL("DELETE FROM moz_downloads");
|
||||
|
||||
// See if the DM is already open, and if it is, close it!
|
||||
let win = Services.wm.getMostRecentWindow("Download:Manager");
|
||||
if (win) {
|
||||
win.close();
|
||||
}
|
||||
let os = Services.obs;
|
||||
const DLMGR_UI_DONE = "download-manager-ui-done";
|
||||
|
||||
gGen = testSteps();
|
||||
dm.addListener(new downloadListener());
|
||||
|
||||
let testObs = {
|
||||
observe: function(aSubject, aTopic, aData)
|
||||
{
|
||||
if (aTopic != DLMGR_UI_DONE) {
|
||||
return;
|
||||
}
|
||||
os.removeObserver(testObs, DLMGR_UI_DONE);
|
||||
continueTest();
|
||||
}
|
||||
};
|
||||
|
||||
// Register with the observer service
|
||||
os.addObserver(testObs, DLMGR_UI_DONE, false);
|
||||
|
||||
// Show the Download Manager UI
|
||||
dmui.show();
|
||||
}
|
||||
|
||||
]]>
|
||||
</script>
|
||||
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display:none;"></div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
</window>
|
@ -266,9 +266,9 @@ static MessageLoop* sIOMessageLoop;
|
||||
// IPDL wants access to this crashreporter interface, and
|
||||
// crashreporter is built in such a way to make that awkward
|
||||
PRBool
|
||||
XRE_GetMinidumpForChild(PRUint32 aChildPid, nsIFile** aDump)
|
||||
XRE_TakeMinidumpForChild(PRUint32 aChildPid, nsIFile** aDump)
|
||||
{
|
||||
return CrashReporter::GetMinidumpForChild(aChildPid, aDump);
|
||||
return CrashReporter::TakeMinidumpForChild(aChildPid, aDump);
|
||||
}
|
||||
|
||||
PRBool
|
||||
|
@ -79,6 +79,7 @@ CPPSRCS = \
|
||||
nsImageToPixbuf.cpp \
|
||||
nsAccessibilityHelper.cpp \
|
||||
nsAccelerometerUnix.cpp \
|
||||
nsGtkIMModule.cpp \
|
||||
$(NULL)
|
||||
|
||||
ifdef MOZ_X11
|
||||
@ -152,7 +153,6 @@ CXXFLAGS += $(MOZ_GCONF_CFLAGS)
|
||||
endif
|
||||
endif
|
||||
|
||||
DEFINES += -DUSE_XIM
|
||||
DEFINES += -DCAIRO_GFX
|
||||
|
||||
ifdef MOZ_ENABLE_POSTSCRIPT
|
||||
|
@ -51,7 +51,6 @@
|
||||
#ifdef PR_LOGGING
|
||||
PRLogModuleInfo *gWidgetLog = nsnull;
|
||||
PRLogModuleInfo *gWidgetFocusLog = nsnull;
|
||||
PRLogModuleInfo *gWidgetIMLog = nsnull;
|
||||
PRLogModuleInfo *gWidgetDragLog = nsnull;
|
||||
PRLogModuleInfo *gWidgetDrawLog = nsnull;
|
||||
#endif
|
||||
@ -89,8 +88,6 @@ nsAppShell::Init()
|
||||
gWidgetLog = PR_NewLogModule("Widget");
|
||||
if (!gWidgetFocusLog)
|
||||
gWidgetFocusLog = PR_NewLogModule("WidgetFocus");
|
||||
if (!gWidgetIMLog)
|
||||
gWidgetIMLog = PR_NewLogModule("WidgetIM");
|
||||
if (!gWidgetDragLog)
|
||||
gWidgetDragLog = PR_NewLogModule("WidgetDrag");
|
||||
if (!gWidgetDrawLog)
|
||||
|
1276
widget/src/gtk2/nsGtkIMModule.cpp
Normal file
1276
widget/src/gtk2/nsGtkIMModule.cpp
Normal file
File diff suppressed because it is too large
Load Diff
282
widget/src/gtk2/nsGtkIMModule.h
Normal file
282
widget/src/gtk2/nsGtkIMModule.h
Normal file
@ -0,0 +1,282 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim:expandtab:shiftwidth=4:tabstop=4:
|
||||
*/
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Christopher Blizzard
|
||||
* <blizzard@mozilla.org>. Portions created by the Initial Developer
|
||||
* are Copyright (C) 2001 the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Masayuki Nakano <masayuki@d-toybox.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef __nsGtkIMModule_h__
|
||||
#define __nsGtkIMModule_h__
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsGUIEvent.h"
|
||||
|
||||
// If software keyboard is needed in password field and uses GTK2 IM module
|
||||
// for inputting characters, we need to enable IME in password field too.
|
||||
#ifdef MOZ_PLATFORM_MAEMO
|
||||
#define NS_IME_ENABLED_ON_PASSWORD_FIELD 1
|
||||
#endif
|
||||
|
||||
class nsWindow;
|
||||
|
||||
class nsGtkIMModule
|
||||
{
|
||||
public:
|
||||
nsrefcnt AddRef()
|
||||
{
|
||||
NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "mRefCnt is negative");
|
||||
++mRefCnt;
|
||||
NS_LOG_ADDREF(this, mRefCnt, "nsGtkIMModule", sizeof(*this));
|
||||
return mRefCnt;
|
||||
}
|
||||
nsrefcnt Release()
|
||||
{
|
||||
NS_PRECONDITION(mRefCnt != 0, "mRefCnt is alrady zero");
|
||||
--mRefCnt;
|
||||
NS_LOG_RELEASE(this, mRefCnt, "nsGtkIMModule");
|
||||
if (mRefCnt == 0) {
|
||||
mRefCnt = 1; /* stabilize */
|
||||
NS_DELETEXPCOM(this);
|
||||
return 0;
|
||||
}
|
||||
return mRefCnt;
|
||||
}
|
||||
|
||||
protected:
|
||||
nsAutoRefCnt mRefCnt;
|
||||
|
||||
public:
|
||||
// aOwnerWindow is a pointer of the owner window. When aOwnerWindow is
|
||||
// destroyed, the related IME contexts are released (i.e., IME cannot be
|
||||
// used with the instance after that).
|
||||
nsGtkIMModule(nsWindow* aOwnerWindow);
|
||||
~nsGtkIMModule();
|
||||
|
||||
// OnFocusWindow is a notification that aWindow is going to be focused.
|
||||
void OnFocusWindow(nsWindow* aWindow);
|
||||
// OnBlurWindow is a notification that aWindow is going to be unfocused.
|
||||
void OnBlurWindow(nsWindow* aWindow);
|
||||
// OnDestroyWindow is a notification that aWindow is going to be destroyed.
|
||||
void OnDestroyWindow(nsWindow* aWindow);
|
||||
// OnFocusChangeInGecko is a notification that an editor gets focus.
|
||||
void OnFocusChangeInGecko(PRBool aFocus);
|
||||
|
||||
// OnKeyEvent is called when aWindow gets a native key press event or a
|
||||
// native key release event. If this returns TRUE, the key event was
|
||||
// filtered by IME. Otherwise, this returns FALSE.
|
||||
// NOTE: When the keypress event starts composition, this returns TRUE but
|
||||
// this dispatches keydown event before compositionstart event.
|
||||
PRBool OnKeyEvent(nsWindow* aWindow, GdkEventKey* aEvent);
|
||||
|
||||
// IME related nsIWidget methods.
|
||||
nsresult ResetInputState(nsWindow* aCaller);
|
||||
nsresult SetIMEEnabled(nsWindow* aCaller, PRUint32 aState);
|
||||
nsresult GetIMEEnabled(PRUint32* aState);
|
||||
nsresult CancelIMEComposition(nsWindow* aCaller);
|
||||
|
||||
// If a software keyboard has been opened, this returns TRUE.
|
||||
// Otherwise, FALSE.
|
||||
static PRBool IsVirtualKeyboardOpened();
|
||||
|
||||
protected:
|
||||
// Owner of an instance of this class. This should be top level window.
|
||||
// The owner window must release the contexts when it's destroyed because
|
||||
// the IME contexts need the native window. If OnDestroyWindow() is called
|
||||
// with the owner window, it'll release IME contexts. Otherwise, it'll
|
||||
// just clean up any existing composition if it's related to the destroying
|
||||
// child window.
|
||||
nsWindow* mOwnerWindow;
|
||||
|
||||
// A last focused window in this class's context.
|
||||
nsWindow* mLastFocusedWindow;
|
||||
|
||||
// Actual context. This is used for handling the user's input.
|
||||
GtkIMContext *mContext;
|
||||
|
||||
#ifndef NS_IME_ENABLED_ON_PASSWORD_FIELD
|
||||
// mSimpleContext is used for the password field and
|
||||
// the |ime-mode: disabled;| editors. These editors disable IME.
|
||||
// But dead keys should work. Fortunately, the simple IM context of
|
||||
// GTK2 support only them.
|
||||
GtkIMContext *mSimpleContext;
|
||||
#endif // NS_IME_ENABLED_ON_PASSWORD_FIELD
|
||||
|
||||
// mDummyContext is a dummy context and will be used in Focus()
|
||||
// when the state of mEnabled means disabled. This context's IME state is
|
||||
// always "closed", so it closes IME forcedly.
|
||||
GtkIMContext *mDummyContext;
|
||||
|
||||
// IME enabled state in this window. The values is nsIWidget::IME_STATUS_*.
|
||||
// Use following helper methods if you don't need the detail of the status.
|
||||
PRUint32 mEnabled;
|
||||
|
||||
// mCompositionStart is the start offset of the composition string in the
|
||||
// current content. When <textarea> or <input> have focus, it means offset
|
||||
// from the first character of them. When a HTML editor has focus, it
|
||||
// means offset from the first character of the root element of the editor.
|
||||
PRUint32 mCompositionStart;
|
||||
|
||||
// mCompositionString is the current composing string. Even if this is
|
||||
// empty, we can be composing. See mIsComposing.
|
||||
nsString mCompositionString;
|
||||
|
||||
// OnKeyEvent() temporarily sets mProcessingKeyEvent to the given native
|
||||
// event.
|
||||
GdkEventKey* mProcessingKeyEvent;
|
||||
|
||||
|
||||
// mIsComposing is set to TRUE when we dispatch the composition start
|
||||
// event. And it's set to FALSE when we dispatches the composition end
|
||||
// event. Note that mCompositionString can be empty string even if this is
|
||||
// TRUE.
|
||||
PRPackedBool mIsComposing;
|
||||
// mIsIMFocused is set to TRUE when we call gtk_im_context_focus_in(). And
|
||||
// it's set to FALSE when we call gtk_im_context_focus_out().
|
||||
PRPackedBool mIsIMFocused;
|
||||
// mFilterKeyEvent is used by OnKeyEvent(). If the commit event should
|
||||
// be processed as simple key event, this is set to TRUE by the commit
|
||||
// handler.
|
||||
PRPackedBool mFilterKeyEvent;
|
||||
// When mIgnoreNativeCompositionEvent is TRUE, all native composition
|
||||
// should be ignored except that the compositon should be restarted in
|
||||
// another content (nsIContent). Don't refer this value directly, use
|
||||
// ShouldIgnoreNativeCompositionEvent().
|
||||
PRPackedBool mIgnoreNativeCompositionEvent;
|
||||
|
||||
// sLastFocusedModule is a pointer to the last focused instance of this
|
||||
// class. When a instance is destroyed and sLastFocusedModule refers it,
|
||||
// this is cleared. So, this refers valid pointer always.
|
||||
static nsGtkIMModule* sLastFocusedModule;
|
||||
|
||||
// Callback methods for native IME events. These methods should call
|
||||
// the related instance methods simply.
|
||||
static void OnCommitCompositionCallback(GtkIMContext *aContext,
|
||||
const gchar *aString,
|
||||
nsGtkIMModule* aModule);
|
||||
static void OnChangeCompositionCallback(GtkIMContext *aContext,
|
||||
nsGtkIMModule* aModule);
|
||||
static void OnStartCompositionCallback(GtkIMContext *aContext,
|
||||
nsGtkIMModule* aModule);
|
||||
static void OnEndCompositionCallback(GtkIMContext *aContext,
|
||||
nsGtkIMModule* aModule);
|
||||
|
||||
// The instance methods for the native IME events.
|
||||
void OnCommitCompositionNative(GtkIMContext *aContext,
|
||||
const gchar *aString);
|
||||
void OnChangeCompositionNative(GtkIMContext *aContext);
|
||||
void OnStartCompositionNative(GtkIMContext *aContext);
|
||||
void OnEndCompositionNative(GtkIMContext *aContext);
|
||||
|
||||
|
||||
// GetContext() returns current IM context which is chosen by the enabled
|
||||
// state. So, this means *current* IM context.
|
||||
GtkIMContext* GetContext();
|
||||
|
||||
// "Enabled" means the users can use all IMEs.
|
||||
// I.e., the focus is in the normal editors.
|
||||
PRBool IsEnabled();
|
||||
|
||||
// "Editable" means the users can input characters. They may be not able to
|
||||
// use IMEs but they can use dead keys.
|
||||
// I.e., the focus is in the normal editors or the password editors or
|
||||
// the |ime-mode: disabled;| editors.
|
||||
PRBool IsEditable();
|
||||
|
||||
// If the owner window and IM context have been destroyed, returns TRUE.
|
||||
PRBool IsDestroyed() { return !mOwnerWindow; }
|
||||
|
||||
// Sets focus to the instance of this class.
|
||||
void Focus();
|
||||
|
||||
// Steals focus from the instance of this class.
|
||||
void Blur();
|
||||
|
||||
// Initializes the instance.
|
||||
void Init();
|
||||
|
||||
// Reset the current composition of IME. All native composition events
|
||||
// during this processing are ignored.
|
||||
void ResetIME();
|
||||
|
||||
// Gets the current composition string by the native APIs.
|
||||
void GetCompositionString(nsAString &aCompositionString);
|
||||
|
||||
// Generates our text range list from current composition string.
|
||||
void SetTextRangeList(nsTArray<nsTextRange> &aTextRangeList);
|
||||
|
||||
// Sets the offset's cursor position to IME.
|
||||
void SetCursorPosition(PRUint32 aTargetOffset);
|
||||
|
||||
// Queries the current selection offset of the window.
|
||||
PRUint32 GetSelectionOffset(nsWindow* aWindow);
|
||||
|
||||
// Initializes the GUI event.
|
||||
void InitEvent(nsGUIEvent& aEvent);
|
||||
|
||||
// Called before destroying the context to work around some platform bugs.
|
||||
void PrepareToDestroyContext(GtkIMContext *aContext);
|
||||
|
||||
PRBool ShouldIgnoreNativeCompositionEvent();
|
||||
|
||||
/**
|
||||
* WARNING:
|
||||
* Following methods dispatch gecko events. Then, the focused widget
|
||||
* can be destroyed, and also it can be stolen focus. If they returns
|
||||
* FALSE, callers cannot continue the composition.
|
||||
* - CommitCompositionBy
|
||||
* - DispatchCompositionStart
|
||||
* - DispatchCompositionEnd
|
||||
* - DispatchTextEvent
|
||||
*/
|
||||
|
||||
// Commits the current composition by the aString.
|
||||
PRBool CommitCompositionBy(const nsAString& aString);
|
||||
|
||||
// Dispatches a composition start event or a composition end event.
|
||||
PRBool DispatchCompositionStart();
|
||||
PRBool DispatchCompositionEnd();
|
||||
|
||||
// Dispatches a text event. If aCheckAttr is TRUE, dispatches a committed
|
||||
// text event. Otherwise, dispatches a composing text event.
|
||||
PRBool DispatchTextEvent(PRBool aCheckAttr);
|
||||
|
||||
};
|
||||
|
||||
#endif // __nsGtkIMModule_h__
|
File diff suppressed because it is too large
Load Diff
@ -81,15 +81,15 @@
|
||||
#include "prlog.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
#include "nsGtkIMModule.h"
|
||||
|
||||
extern PRLogModuleInfo *gWidgetLog;
|
||||
extern PRLogModuleInfo *gWidgetFocusLog;
|
||||
extern PRLogModuleInfo *gWidgetIMLog;
|
||||
extern PRLogModuleInfo *gWidgetDragLog;
|
||||
extern PRLogModuleInfo *gWidgetDrawLog;
|
||||
|
||||
#define LOG(args) PR_LOG(gWidgetLog, 4, args)
|
||||
#define LOGFOCUS(args) PR_LOG(gWidgetFocusLog, 4, args)
|
||||
#define LOGIM(args) PR_LOG(gWidgetIMLog, 4, args)
|
||||
#define LOGDRAG(args) PR_LOG(gWidgetDragLog, 4, args)
|
||||
#define LOGDRAW(args) PR_LOG(gWidgetDrawLog, 4, args)
|
||||
|
||||
@ -97,7 +97,6 @@ extern PRLogModuleInfo *gWidgetDrawLog;
|
||||
|
||||
#define LOG(args)
|
||||
#define LOGFOCUS(args)
|
||||
#define LOGIM(args)
|
||||
#define LOGDRAG(args)
|
||||
#define LOGDRAW(args)
|
||||
|
||||
@ -310,91 +309,22 @@ public:
|
||||
|
||||
NS_IMETHOD BeginResizeDrag (nsGUIEvent* aEvent, PRInt32 aHorizontal, PRInt32 aVertical);
|
||||
|
||||
#ifdef USE_XIM
|
||||
void IMEInitData (void);
|
||||
void IMEReleaseData (void);
|
||||
void IMEDestroyContext (void);
|
||||
void IMESetFocus (void);
|
||||
void IMELoseFocus (void);
|
||||
void IMEComposeStart (void);
|
||||
void IMEComposeText (const PRUnichar *aText,
|
||||
const PRInt32 aLen,
|
||||
const gchar *aPreeditString,
|
||||
const gint aCursorPos,
|
||||
const PangoAttrList *aFeedback);
|
||||
void IMEComposeEnd (void);
|
||||
GtkIMContext* IMEGetContext (void);
|
||||
// "Enabled" means the users can use all IMEs.
|
||||
// I.e., the focus is in the normal editors.
|
||||
PRBool IMEIsEnabledState (void);
|
||||
// "Editable" means the users can input characters. They may be not able to
|
||||
// use IMEs but they can use dead keys.
|
||||
// I.e., the forcus is in the normal editors or the password editors or
|
||||
// the |ime-mode: disabled;| editors.
|
||||
PRBool IMEIsEditableState(void);
|
||||
nsWindow* IMEComposingWindow(void);
|
||||
void IMECreateContext (void);
|
||||
PRBool IMEFilterEvent (GdkEventKey *aEvent);
|
||||
void IMESetCursorPosition(const nsTextEventReply& aReply);
|
||||
MozContainer* GetMozContainer() { return mContainer; }
|
||||
GdkWindow* GetGdkWindow() { return mGdkWindow; }
|
||||
PRBool IsDestroyed() { return mIsDestroyed; }
|
||||
|
||||
/*
|
||||
* |mIMEData| has all IME data for the window and its children widgets.
|
||||
* Only stand-alone windows and child windows embedded in non-Mozilla GTK
|
||||
* containers own IME contexts.
|
||||
* But this is referred from all children after the widget gets focus.
|
||||
* The children refers to its owning window's object.
|
||||
*/
|
||||
struct nsIMEData {
|
||||
// Actual context. This is used for handling the user's input.
|
||||
GtkIMContext *mContext;
|
||||
// mSimpleContext is used for the password field and
|
||||
// the |ime-mode: disabled;| editors. These editors disable IME.
|
||||
// But dead keys should work. Fortunately, the simple IM context of
|
||||
// GTK2 support only them.
|
||||
GtkIMContext *mSimpleContext;
|
||||
// mDummyContext is a dummy context and will be used in IMESetFocus()
|
||||
// when mEnabled is false. This mDummyContext IM state is always
|
||||
// "off", so it works to switch conversion mode to OFF on IM status
|
||||
// window.
|
||||
GtkIMContext *mDummyContext;
|
||||
// This mComposingWindow is set in IMEComposeStart(), when user starts
|
||||
// composition, then unset in IMEComposeEnd() when user ends the
|
||||
// composition. We will keep the widget where the actual composition is
|
||||
// started. During the composition, we may get some events like
|
||||
// ResetInputStateInternal() and CancelIMECompositionInternal() by
|
||||
// changing input focus, we will use the original widget of
|
||||
// mComposingWindow to commit or reset the composition.
|
||||
nsWindow *mComposingWindow;
|
||||
// Owner of this struct.
|
||||
// The owner window must release the contexts at destroying.
|
||||
nsWindow *mOwner;
|
||||
// The reference counter. When this will be zero by the decrement,
|
||||
// the decrementer must free the instance.
|
||||
PRUint32 mRefCount;
|
||||
// IME enabled state in this window.
|
||||
PRUint32 mEnabled;
|
||||
nsIMEData(nsWindow* aOwner) {
|
||||
mContext = nsnull;
|
||||
mSimpleContext = nsnull;
|
||||
mDummyContext = nsnull;
|
||||
mComposingWindow = nsnull;
|
||||
mOwner = aOwner;
|
||||
mRefCount = 1;
|
||||
mEnabled = nsIWidget::IME_STATUS_ENABLED;
|
||||
}
|
||||
};
|
||||
nsIMEData *mIMEData;
|
||||
// If this dispatched the keydown event actually, this returns TRUE,
|
||||
// otherwise, FALSE.
|
||||
PRBool DispatchKeyDownEvent(GdkEventKey *aEvent,
|
||||
PRBool *aIsCancelled);
|
||||
|
||||
NS_IMETHOD ResetInputState();
|
||||
NS_IMETHOD SetIMEOpenState(PRBool aState);
|
||||
NS_IMETHOD GetIMEOpenState(PRBool* aState);
|
||||
NS_IMETHOD SetIMEEnabled(PRUint32 aState);
|
||||
NS_IMETHOD GetIMEEnabled(PRUint32* aState);
|
||||
NS_IMETHOD CancelIMEComposition();
|
||||
NS_IMETHOD OnIMEFocusChange(PRBool aFocus);
|
||||
NS_IMETHOD GetToggledKeyState(PRUint32 aKeyCode, PRBool* aLEDState);
|
||||
|
||||
#endif
|
||||
|
||||
void ResizeTransparencyBitmap(PRInt32 aNewWidth, PRInt32 aNewHeight);
|
||||
void ApplyTransparencyBitmap();
|
||||
virtual void SetTransparencyMode(nsTransparencyMode aMode);
|
||||
@ -568,6 +498,20 @@ private:
|
||||
*flag &= ~mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* |mIMModule| takes all IME related stuff.
|
||||
*
|
||||
* This is owned by the top-level nsWindow or the topmost child
|
||||
* nsWindow embedded in a non-Gecko widget.
|
||||
*
|
||||
* The instance is created when the top level widget is created. And when
|
||||
* the widget is destroyed, it's released. All child windows refer its
|
||||
* ancestor widget's instance. So, one set of IM contexts is created for
|
||||
* all windows in a hierarchy. If the children are released after the top
|
||||
* level window is released, the children still have a valid pointer,
|
||||
* however, IME doesn't work at that time.
|
||||
*/
|
||||
nsRefPtr<nsGtkIMModule> mIMModule;
|
||||
};
|
||||
|
||||
class nsChildWindow : public nsWindow {
|
||||
|
@ -65,7 +65,7 @@ ifdef MOZ_ENABLE_GTK2
|
||||
CFLAGS += $(MOZ_GTK2_CFLAGS)
|
||||
endif
|
||||
|
||||
DEFINES += -D_IMPL_GTKXTBIN_API -DUSE_XIM
|
||||
DEFINES += -D_IMPL_GTKXTBIN_API
|
||||
|
||||
ifeq ($(OS_ARCH), OpenVMS)
|
||||
DEFINES += -DGENERIC_MOTIF_REDEFINES
|
||||
|
@ -37,15 +37,13 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include <QX11Info>
|
||||
|
||||
#include "nsIdleServiceQt.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsDebug.h"
|
||||
#include "prlink.h"
|
||||
|
||||
|
||||
#ifdef Q_WS_X11
|
||||
#include <QX11Info>
|
||||
|
||||
typedef PRBool (*_XScreenSaverQueryExtension_fn)(Display* dpy, int* event_base,
|
||||
int* error_base);
|
||||
|
||||
@ -134,27 +132,3 @@ nsIdleServiceQt::GetIdleTime(PRUint32 *aTimeDiff)
|
||||
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsIdleServiceQt, nsIIdleService)
|
||||
|
||||
nsIdleServiceQt::nsIdleServiceQt()
|
||||
{
|
||||
}
|
||||
|
||||
static void Initialize()
|
||||
{
|
||||
}
|
||||
|
||||
nsIdleServiceQt::~nsIdleServiceQt()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsIdleServiceQt::GetIdleTime(PRUint32 *aTimeDiff)
|
||||
{
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -160,12 +160,10 @@ static const nsModuleComponentInfo components[] =
|
||||
NS_BIDIKEYBOARD_CID,
|
||||
"@mozilla.org/widget/bidikeyboard;1",
|
||||
nsBidiKeyboardConstructor },
|
||||
#ifdef Q_WS_X11
|
||||
{ "Qt Idle Service",
|
||||
NS_IDLE_SERVICE_CID,
|
||||
"@mozilla.org/widget/idleservice;1",
|
||||
nsIdleServiceQtConstructor },
|
||||
#endif
|
||||
{ "Qt Sound",
|
||||
NS_SOUND_CID,
|
||||
"@mozilla.org/sound;1",
|
||||
|
@ -550,6 +550,10 @@ nsWindow::SetSizeMode(PRInt32 aMode)
|
||||
widget->showMinimized();
|
||||
break;
|
||||
case nsSizeMode_Fullscreen:
|
||||
// Some versions of Qt (4.6.x) crash in XSetInputFocus due to
|
||||
// unsynchronized window activation. Sync here to avoid such
|
||||
// cases.
|
||||
XSync(QX11Info().display(), False);
|
||||
widget->showFullScreen();
|
||||
break;
|
||||
|
||||
@ -767,14 +771,12 @@ nsWindow::GetNativeData(PRUint32 aDataType)
|
||||
return SetupPluginPort();
|
||||
break;
|
||||
|
||||
#ifdef Q_WS_X11
|
||||
case NS_NATIVE_DISPLAY:
|
||||
{
|
||||
QWidget *widget = GetViewWidget();
|
||||
return widget ? widget->x11Info().display() : nsnull;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case NS_NATIVE_GRAPHIC: {
|
||||
NS_ASSERTION(nsnull != mToolkit, "NULL toolkit, unable to get a GC");
|
||||
@ -1772,7 +1774,6 @@ nsWindow::SetWindowClass(const nsAString &xulWinType)
|
||||
nsXPIDLString brandName;
|
||||
GetBrandName(brandName);
|
||||
|
||||
#ifdef Q_WS_X11
|
||||
XClassHint *class_hint = XAllocClassHint();
|
||||
if (!class_hint)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
@ -1814,7 +1815,6 @@ nsWindow::SetWindowClass(const nsAString &xulWinType)
|
||||
nsMemory::Free(class_hint->res_class);
|
||||
nsMemory::Free(class_hint->res_name);
|
||||
XFree(class_hint);
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -1931,12 +1931,10 @@ nsWindow::MakeFullScreen(PRBool aFullScreen)
|
||||
mLastSizeMode = mSizeMode;
|
||||
|
||||
mSizeMode = nsSizeMode_Fullscreen;
|
||||
#ifdef Q_WS_X11
|
||||
// Some versions of Qt (4.6.x) crash in XSetInputFocus due to
|
||||
// unsynchronized window activation. Sync here to avoid such
|
||||
// cases.
|
||||
XSync(QX11Info().display(), False);
|
||||
#endif
|
||||
widget->showFullScreen();
|
||||
}
|
||||
else {
|
||||
@ -1992,11 +1990,9 @@ nsWindow::HideWindowChrome(PRBool aShouldHide)
|
||||
// and flush the queue here so that we don't end up with a BadWindow
|
||||
// error later when this happens (when the persistence timer fires
|
||||
// and GetWindowPos is called)
|
||||
#ifdef Q_WS_X11
|
||||
QWidget *widget = GetViewWidget();
|
||||
NS_ENSURE_TRUE(widget, NS_ERROR_FAILURE);
|
||||
XSync(widget->x11Info().display(), False);
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -452,7 +452,7 @@ XRE_API(GeckoProcessType,
|
||||
#if defined(MOZ_CRASHREPORTER)
|
||||
// Used in the "master" parent process hosting the crash server
|
||||
XRE_API(PRBool,
|
||||
XRE_GetMinidumpForChild, (PRUint32 aChildPid, nsIFile** aDump))
|
||||
XRE_TakeMinidumpForChild, (PRUint32 aChildPid, nsIFile** aDump))
|
||||
|
||||
// Used in child processes.
|
||||
XRE_API(PRBool,
|
||||
|
Loading…
Reference in New Issue
Block a user