diff options
author | wolfbeast <mcwerewolf@gmail.com> | 2018-02-08 12:06:30 +0100 |
---|---|---|
committer | wolfbeast <mcwerewolf@gmail.com> | 2018-02-08 12:06:30 +0100 |
commit | 44cd9f2a915a4879371c5e0b059acc3e5a2378b0 (patch) | |
tree | 8cb1a4758b16d9caae55f525b73f5fca3824b4f7 | |
parent | f8d1830b530cd553d788b3579d41725d35c4da7f (diff) | |
parent | b62fce0dc0c77a5788c331db32b3996e4020e2a5 (diff) | |
download | UXP-44cd9f2a915a4879371c5e0b059acc3e5a2378b0.tar UXP-44cd9f2a915a4879371c5e0b059acc3e5a2378b0.tar.gz UXP-44cd9f2a915a4879371c5e0b059acc3e5a2378b0.tar.lz UXP-44cd9f2a915a4879371c5e0b059acc3e5a2378b0.tar.xz UXP-44cd9f2a915a4879371c5e0b059acc3e5a2378b0.zip |
Merge branch 'ported-upstream'
34 files changed, 352 insertions, 218 deletions
diff --git a/browser/components/downloads/DownloadsCommon.jsm b/browser/components/downloads/DownloadsCommon.jsm index b6684817d..90f14f2d8 100644 --- a/browser/components/downloads/DownloadsCommon.jsm +++ b/browser/components/downloads/DownloadsCommon.jsm @@ -42,6 +42,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PluralForm", "resource://gre/modules/PluralForm.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "AppConstants", + "resource://gre/modules/AppConstants.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Downloads", "resource://gre/modules/Downloads.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "DownloadUIHelper", @@ -460,8 +462,12 @@ this.DownloadsCommon = { throw new Error("aOwnerWindow must be a dom-window object"); } + let isWindowsExe = AppConstants.platform == "win" && + aFile.leafName.toLowerCase().endsWith(".exe"); + let promiseShouldLaunch; - if (aFile.isExecutable()) { + // Don't prompt on Windows for .exe since there will be a native prompt. + if (aFile.isExecutable() && !isWindowsExe) { // We get a prompter for the provided window here, even though anchoring // to the most recently active window should work as well. promiseShouldLaunch = diff --git a/browser/components/extensions/ext-browserAction.js b/browser/components/extensions/ext-browserAction.js index 407366e2c..2c82ac701 100644 --- a/browser/components/extensions/ext-browserAction.js +++ b/browser/components/extensions/ext-browserAction.js @@ -497,6 +497,9 @@ extensions.registerSchemaAPI("browserAction", "addon_parent", context => { // For internal consistency, we currently resolve both relative to the // calling context. let url = details.popup && context.uri.resolve(details.popup); + if (url && !context.checkLoadURL(url)) { + return Promise.reject({message: `Access denied for URL ${url}`}); + } BrowserAction.for(extension).setProperty(tab, "popup", url); }, diff --git a/browser/components/extensions/ext-pageAction.js b/browser/components/extensions/ext-pageAction.js index 153f05d7a..5bf3a9c70 100644 --- a/browser/components/extensions/ext-pageAction.js +++ b/browser/components/extensions/ext-pageAction.js @@ -273,6 +273,9 @@ extensions.registerSchemaAPI("pageAction", "addon_parent", context => { // For internal consistency, we currently resolve both relative to the // calling context. let url = details.popup && context.uri.resolve(details.popup); + if (url && !context.checkLoadURL(url)) { + return Promise.reject({message: `Access denied for URL ${url}`}); + } PageAction.for(extension).setProperty(tab, "popup", url); }, diff --git a/browser/components/extensions/schemas/page_action.json b/browser/components/extensions/schemas/page_action.json index f4f9ee8db..126378ca5 100644 --- a/browser/components/extensions/schemas/page_action.json +++ b/browser/components/extensions/schemas/page_action.json @@ -173,6 +173,7 @@ { "name": "setPopup", "type": "function", + "async": true, "description": "Sets the html document to be opened as a popup when the user clicks on the page action's icon.", "parameters": [ { diff --git a/editor/libeditor/CompositionTransaction.cpp b/editor/libeditor/CompositionTransaction.cpp index 25938fa60..bde53d2e5 100644 --- a/editor/libeditor/CompositionTransaction.cpp +++ b/editor/libeditor/CompositionTransaction.cpp @@ -33,7 +33,7 @@ CompositionTransaction::CompositionTransaction( , mReplaceLength(aReplaceLength) , mRanges(aTextRangeArray) , mStringToInsert(aStringToInsert) - , mEditorBase(aEditorBase) + , mEditorBase(&aEditorBase) , mRangeUpdater(aRangeUpdater) , mFixed(false) { @@ -45,6 +45,7 @@ CompositionTransaction::~CompositionTransaction() } NS_IMPL_CYCLE_COLLECTION_INHERITED(CompositionTransaction, EditTransactionBase, + mEditorBase, mTextNode) // mRangeList can't lead to cycles @@ -60,9 +61,13 @@ NS_IMPL_RELEASE_INHERITED(CompositionTransaction, EditTransactionBase) NS_IMETHODIMP CompositionTransaction::DoTransaction() { + if (NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } + // Fail before making any changes if there's no selection controller nsCOMPtr<nsISelectionController> selCon; - mEditorBase.GetSelectionController(getter_AddRefs(selCon)); + mEditorBase->GetSelectionController(getter_AddRefs(selCon)); NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED); // Advance caret: This requires the presentation shell to get the selection. @@ -108,9 +113,13 @@ CompositionTransaction::DoTransaction() NS_IMETHODIMP CompositionTransaction::UndoTransaction() { + if (NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } + // Get the selection first so we'll fail before making any changes if we // can't get it - RefPtr<Selection> selection = mEditorBase.GetSelection(); + RefPtr<Selection> selection = mEditorBase->GetSelection(); NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED); nsresult rv = mTextNode->DeleteData(mOffset, mStringToInsert.Length()); @@ -171,7 +180,10 @@ CompositionTransaction::GetTxnDescription(nsAString& aString) nsresult CompositionTransaction::SetSelectionForRanges() { - return SetIMESelection(mEditorBase, mTextNode, mOffset, + if (NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } + return SetIMESelection(*mEditorBase, mTextNode, mOffset, mStringToInsert.Length(), mRanges); } diff --git a/editor/libeditor/CompositionTransaction.h b/editor/libeditor/CompositionTransaction.h index acb3d8beb..c2134bedd 100644 --- a/editor/libeditor/CompositionTransaction.h +++ b/editor/libeditor/CompositionTransaction.h @@ -90,7 +90,7 @@ private: nsString mStringToInsert; // The editor, which is used to get the selection controller. - EditorBase& mEditorBase; + RefPtr<EditorBase> mEditorBase; RangeUpdater* mRangeUpdater; diff --git a/editor/libeditor/CreateElementTransaction.cpp b/editor/libeditor/CreateElementTransaction.cpp index 5e4bd961c..5e2b9e1ad 100644 --- a/editor/libeditor/CreateElementTransaction.cpp +++ b/editor/libeditor/CreateElementTransaction.cpp @@ -50,6 +50,7 @@ CreateElementTransaction::~CreateElementTransaction() NS_IMPL_CYCLE_COLLECTION_INHERITED(CreateElementTransaction, EditTransactionBase, + mEditorBase, mParent, mNewNode, mRefNode) @@ -63,7 +64,9 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase) NS_IMETHODIMP CreateElementTransaction::DoTransaction() { - MOZ_ASSERT(mEditorBase && mTag && mParent); + if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mTag) || NS_WARN_IF(!mParent)) { + return NS_ERROR_NOT_INITIALIZED; + } mNewNode = mEditorBase->CreateHTMLContent(mTag); NS_ENSURE_STATE(mNewNode); @@ -106,7 +109,9 @@ CreateElementTransaction::DoTransaction() NS_IMETHODIMP CreateElementTransaction::UndoTransaction() { - MOZ_ASSERT(mEditorBase && mParent); + if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mParent)) { + return NS_ERROR_NOT_INITIALIZED; + } ErrorResult rv; mParent->RemoveChild(*mNewNode, rv); @@ -117,7 +122,9 @@ CreateElementTransaction::UndoTransaction() NS_IMETHODIMP CreateElementTransaction::RedoTransaction() { - MOZ_ASSERT(mEditorBase && mParent); + if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mParent)) { + return NS_ERROR_NOT_INITIALIZED; + } // First, reset mNewNode so it has no attributes or content // XXX We never actually did this, we only cleared mNewNode's contents if it diff --git a/editor/libeditor/CreateElementTransaction.h b/editor/libeditor/CreateElementTransaction.h index 70fecceae..fd5abb8a8 100644 --- a/editor/libeditor/CreateElementTransaction.h +++ b/editor/libeditor/CreateElementTransaction.h @@ -57,7 +57,7 @@ protected: virtual ~CreateElementTransaction(); // The document into which the new node will be inserted. - EditorBase* mEditorBase; + RefPtr<EditorBase> mEditorBase; // The tag (mapping to object type) for the new element. nsCOMPtr<nsIAtom> mTag; diff --git a/editor/libeditor/DeleteNodeTransaction.cpp b/editor/libeditor/DeleteNodeTransaction.cpp index 7f485b066..ae9951e28 100644 --- a/editor/libeditor/DeleteNodeTransaction.cpp +++ b/editor/libeditor/DeleteNodeTransaction.cpp @@ -23,6 +23,7 @@ DeleteNodeTransaction::~DeleteNodeTransaction() } NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteNodeTransaction, EditTransactionBase, + mEditorBase, mNode, mParent, mRefNode) diff --git a/editor/libeditor/DeleteNodeTransaction.h b/editor/libeditor/DeleteNodeTransaction.h index d0bc0dd46..369bbdf56 100644 --- a/editor/libeditor/DeleteNodeTransaction.h +++ b/editor/libeditor/DeleteNodeTransaction.h @@ -55,7 +55,7 @@ protected: nsCOMPtr<nsIContent> mRefNode; // The editor for this transaction. - EditorBase* mEditorBase; + RefPtr<EditorBase> mEditorBase; // Range updater object. RangeUpdater* mRangeUpdater; diff --git a/editor/libeditor/DeleteRangeTransaction.cpp b/editor/libeditor/DeleteRangeTransaction.cpp index 977de4873..16d2344ba 100644 --- a/editor/libeditor/DeleteRangeTransaction.cpp +++ b/editor/libeditor/DeleteRangeTransaction.cpp @@ -33,6 +33,7 @@ DeleteRangeTransaction::DeleteRangeTransaction() NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteRangeTransaction, EditAggregateTransaction, + mEditorBase, mRange) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteRangeTransaction) @@ -62,7 +63,9 @@ DeleteRangeTransaction::Init(EditorBase* aEditorBase, NS_IMETHODIMP DeleteRangeTransaction::DoTransaction() { - MOZ_ASSERT(mRange && mEditorBase); + if (NS_WARN_IF(!mRange) || NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } // build the child transactions nsCOMPtr<nsINode> startParent = mRange->GetStartParent(); @@ -111,16 +114,18 @@ DeleteRangeTransaction::DoTransaction() NS_IMETHODIMP DeleteRangeTransaction::UndoTransaction() { - MOZ_ASSERT(mRange && mEditorBase); - + if (NS_WARN_IF(!mRange) || NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } return EditAggregateTransaction::UndoTransaction(); } NS_IMETHODIMP DeleteRangeTransaction::RedoTransaction() { - MOZ_ASSERT(mRange && mEditorBase); - + if (NS_WARN_IF(!mRange) || NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } return EditAggregateTransaction::RedoTransaction(); } @@ -136,6 +141,10 @@ DeleteRangeTransaction::CreateTxnsToDeleteBetween(nsINode* aNode, int32_t aStartOffset, int32_t aEndOffset) { + if (NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } + // see what kind of node we have if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) { // if the node is a chardata node, then delete chardata content @@ -185,6 +194,10 @@ DeleteRangeTransaction::CreateTxnsToDeleteContent(nsINode* aNode, int32_t aOffset, nsIEditor::EDirection aAction) { + if (NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } + // see what kind of node we have if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) { // if the node is a chardata node, then delete chardata content @@ -217,6 +230,10 @@ DeleteRangeTransaction::CreateTxnsToDeleteContent(nsINode* aNode, nsresult DeleteRangeTransaction::CreateTxnsToDeleteNodesBetween() { + if (NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } + nsCOMPtr<nsIContentIterator> iter = NS_NewContentSubtreeIterator(); nsresult rv = iter->Init(mRange); diff --git a/editor/libeditor/DeleteRangeTransaction.h b/editor/libeditor/DeleteRangeTransaction.h index 9b60a5ba2..9bb4f520a 100644 --- a/editor/libeditor/DeleteRangeTransaction.h +++ b/editor/libeditor/DeleteRangeTransaction.h @@ -67,7 +67,7 @@ protected: RefPtr<nsRange> mRange; // The editor for this transaction. - EditorBase* mEditorBase; + RefPtr<EditorBase> mEditorBase; // Range updater object. RangeUpdater* mRangeUpdater; diff --git a/editor/libeditor/DeleteTextTransaction.cpp b/editor/libeditor/DeleteTextTransaction.cpp index 6de3181da..624aeaa3c 100644 --- a/editor/libeditor/DeleteTextTransaction.cpp +++ b/editor/libeditor/DeleteTextTransaction.cpp @@ -25,7 +25,7 @@ DeleteTextTransaction::DeleteTextTransaction( uint32_t aOffset, uint32_t aNumCharsToDelete, RangeUpdater* aRangeUpdater) - : mEditorBase(aEditorBase) + : mEditorBase(&aEditorBase) , mCharData(&aCharData) , mOffset(aOffset) , mNumCharsToDelete(aNumCharsToDelete) @@ -36,6 +36,7 @@ DeleteTextTransaction::DeleteTextTransaction( } NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteTextTransaction, EditTransactionBase, + mEditorBase, mCharData) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteTextTransaction) @@ -45,7 +46,7 @@ nsresult DeleteTextTransaction::Init() { // Do nothing if the node is read-only - if (!mEditorBase.IsModifiableNode(mCharData)) { + if (NS_WARN_IF(!mEditorBase) || !mEditorBase->IsModifiableNode(mCharData)) { return NS_ERROR_FAILURE; } @@ -55,7 +56,9 @@ DeleteTextTransaction::Init() NS_IMETHODIMP DeleteTextTransaction::DoTransaction() { - MOZ_ASSERT(mCharData); + if (NS_WARN_IF(!mCharData) || NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } // Get the text that we're about to delete nsresult rv = mCharData->SubstringData(mOffset, mNumCharsToDelete, @@ -69,8 +72,8 @@ DeleteTextTransaction::DoTransaction() } // Only set selection to deletion point if editor gives permission - if (mEditorBase.GetShouldTxnSetSelection()) { - RefPtr<Selection> selection = mEditorBase.GetSelection(); + if (mEditorBase->GetShouldTxnSetSelection()) { + RefPtr<Selection> selection = mEditorBase->GetSelection(); NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); rv = selection->Collapse(mCharData, mOffset); NS_ASSERTION(NS_SUCCEEDED(rv), @@ -86,8 +89,9 @@ DeleteTextTransaction::DoTransaction() NS_IMETHODIMP DeleteTextTransaction::UndoTransaction() { - MOZ_ASSERT(mCharData); - + if (NS_WARN_IF(!mCharData)) { + return NS_ERROR_NOT_INITIALIZED; + } return mCharData->InsertData(mOffset, mDeletedText); } diff --git a/editor/libeditor/DeleteTextTransaction.h b/editor/libeditor/DeleteTextTransaction.h index 855d14349..0c8dafbc3 100644 --- a/editor/libeditor/DeleteTextTransaction.h +++ b/editor/libeditor/DeleteTextTransaction.h @@ -53,7 +53,7 @@ public: protected: // The provider of basic editing operations. - EditorBase& mEditorBase; + RefPtr<EditorBase> mEditorBase; // The CharacterData node to operate upon. RefPtr<nsGenericDOMDataNode> mCharData; diff --git a/editor/libeditor/EditorBase.cpp b/editor/libeditor/EditorBase.cpp index 13505b2d3..b793f39b8 100644 --- a/editor/libeditor/EditorBase.cpp +++ b/editor/libeditor/EditorBase.cpp @@ -466,6 +466,13 @@ EditorBase::PreDestroy(bool aDestroyingFrames) mSpellcheckCheckboxState = eTriUnset; mRootElement = nullptr; + // Transaction may grab this instance. Therefore, they should be released + // here for stopping the circular reference with this instance. + if (mTxnMgr) { + mTxnMgr->Clear(); + mTxnMgr = nullptr; + } + mDidPreDestroy = true; return NS_OK; } diff --git a/editor/libeditor/InsertNodeTransaction.cpp b/editor/libeditor/InsertNodeTransaction.cpp index 6af33b74f..6317b8c15 100644 --- a/editor/libeditor/InsertNodeTransaction.cpp +++ b/editor/libeditor/InsertNodeTransaction.cpp @@ -28,7 +28,7 @@ InsertNodeTransaction::InsertNodeTransaction(nsIContent& aNode, : mNode(&aNode) , mParent(&aParent) , mOffset(aOffset) - , mEditorBase(aEditorBase) + , mEditorBase(&aEditorBase) { } @@ -37,6 +37,7 @@ InsertNodeTransaction::~InsertNodeTransaction() } NS_IMPL_CYCLE_COLLECTION_INHERITED(InsertNodeTransaction, EditTransactionBase, + mEditorBase, mNode, mParent) @@ -48,7 +49,9 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase) NS_IMETHODIMP InsertNodeTransaction::DoTransaction() { - MOZ_ASSERT(mNode && mParent); + if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mNode) || NS_WARN_IF(!mParent)) { + return NS_ERROR_NOT_INITIALIZED; + } uint32_t count = mParent->GetChildCount(); if (mOffset > static_cast<int32_t>(count) || mOffset == -1) { @@ -59,15 +62,15 @@ InsertNodeTransaction::DoTransaction() // Note, it's ok for ref to be null. That means append. nsCOMPtr<nsIContent> ref = mParent->GetChildAt(mOffset); - mEditorBase.MarkNodeDirty(GetAsDOMNode(mNode)); + mEditorBase->MarkNodeDirty(GetAsDOMNode(mNode)); ErrorResult rv; mParent->InsertBefore(*mNode, ref, rv); NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult()); // Only set selection to insertion point if editor gives permission - if (mEditorBase.GetShouldTxnSetSelection()) { - RefPtr<Selection> selection = mEditorBase.GetSelection(); + if (mEditorBase->GetShouldTxnSetSelection()) { + RefPtr<Selection> selection = mEditorBase->GetSelection(); NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); // Place the selection just after the inserted element selection->Collapse(mParent, mOffset + 1); @@ -80,8 +83,9 @@ InsertNodeTransaction::DoTransaction() NS_IMETHODIMP InsertNodeTransaction::UndoTransaction() { - MOZ_ASSERT(mNode && mParent); - + if (NS_WARN_IF(!mNode) || NS_WARN_IF(!mParent)) { + return NS_ERROR_NOT_INITIALIZED; + } ErrorResult rv; mParent->RemoveChild(*mNode, rv); return rv.StealNSResult(); diff --git a/editor/libeditor/InsertNodeTransaction.h b/editor/libeditor/InsertNodeTransaction.h index 5af7b8aff..1b9739d6c 100644 --- a/editor/libeditor/InsertNodeTransaction.h +++ b/editor/libeditor/InsertNodeTransaction.h @@ -50,7 +50,7 @@ protected: int32_t mOffset; // The editor for this transaction. - EditorBase& mEditorBase; + RefPtr<EditorBase> mEditorBase; }; } // namespace mozilla diff --git a/editor/libeditor/InsertTextTransaction.cpp b/editor/libeditor/InsertTextTransaction.cpp index 009b2d023..0434b2dd5 100644 --- a/editor/libeditor/InsertTextTransaction.cpp +++ b/editor/libeditor/InsertTextTransaction.cpp @@ -26,7 +26,7 @@ InsertTextTransaction::InsertTextTransaction(Text& aTextNode, : mTextNode(&aTextNode) , mOffset(aOffset) , mStringToInsert(aStringToInsert) - , mEditorBase(aEditorBase) + , mEditorBase(&aEditorBase) , mRangeUpdater(aRangeUpdater) { } @@ -36,6 +36,7 @@ InsertTextTransaction::~InsertTextTransaction() } NS_IMPL_CYCLE_COLLECTION_INHERITED(InsertTextTransaction, EditTransactionBase, + mEditorBase, mTextNode) NS_IMPL_ADDREF_INHERITED(InsertTextTransaction, EditTransactionBase) @@ -50,12 +51,16 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase) NS_IMETHODIMP InsertTextTransaction::DoTransaction() { + if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mTextNode)) { + return NS_ERROR_NOT_AVAILABLE; + } + nsresult rv = mTextNode->InsertData(mOffset, mStringToInsert); NS_ENSURE_SUCCESS(rv, rv); // Only set selection to insertion point if editor gives permission - if (mEditorBase.GetShouldTxnSetSelection()) { - RefPtr<Selection> selection = mEditorBase.GetSelection(); + if (mEditorBase->GetShouldTxnSetSelection()) { + RefPtr<Selection> selection = mEditorBase->GetSelection(); NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); DebugOnly<nsresult> rv = selection->Collapse(mTextNode, mOffset + mStringToInsert.Length()); diff --git a/editor/libeditor/InsertTextTransaction.h b/editor/libeditor/InsertTextTransaction.h index f97f20e37..b6867ad76 100644 --- a/editor/libeditor/InsertTextTransaction.h +++ b/editor/libeditor/InsertTextTransaction.h @@ -76,7 +76,7 @@ private: nsString mStringToInsert; // The editor, which we'll need to get the selection. - EditorBase& mEditorBase; + RefPtr<EditorBase> mEditorBase; RangeUpdater* mRangeUpdater; }; diff --git a/editor/libeditor/JoinNodeTransaction.cpp b/editor/libeditor/JoinNodeTransaction.cpp index 228d1f4d6..50727cce1 100644 --- a/editor/libeditor/JoinNodeTransaction.cpp +++ b/editor/libeditor/JoinNodeTransaction.cpp @@ -21,7 +21,7 @@ using namespace dom; JoinNodeTransaction::JoinNodeTransaction(EditorBase& aEditorBase, nsINode& aLeftNode, nsINode& aRightNode) - : mEditorBase(aEditorBase) + : mEditorBase(&aEditorBase) , mLeftNode(&aLeftNode) , mRightNode(&aRightNode) , mOffset(0) @@ -29,6 +29,7 @@ JoinNodeTransaction::JoinNodeTransaction(EditorBase& aEditorBase, } NS_IMPL_CYCLE_COLLECTION_INHERITED(JoinNodeTransaction, EditTransactionBase, + mEditorBase, mLeftNode, mRightNode, mParent) @@ -39,7 +40,8 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase) nsresult JoinNodeTransaction::CheckValidity() { - if (!mEditorBase.IsModifiableNode(mLeftNode->GetParentNode())) { + if (NS_WARN_IF(!mEditorBase) || + !mEditorBase->IsModifiableNode(mLeftNode->GetParentNode())) { return NS_ERROR_FAILURE; } return NS_OK; @@ -50,6 +52,12 @@ JoinNodeTransaction::CheckValidity() NS_IMETHODIMP JoinNodeTransaction::DoTransaction() { + if (NS_WARN_IF(!mEditorBase) || + NS_WARN_IF(!mLeftNode) || + NS_WARN_IF(!mRightNode)) { + return NS_ERROR_NOT_INITIALIZED; + } + // Get the parent node nsCOMPtr<nsINode> leftParent = mLeftNode->GetParentNode(); NS_ENSURE_TRUE(leftParent, NS_ERROR_NULL_POINTER); @@ -65,7 +73,7 @@ JoinNodeTransaction::DoTransaction() mParent = leftParent; mOffset = mLeftNode->Length(); - return mEditorBase.JoinNodesImpl(mRightNode, mLeftNode, mParent); + return mEditorBase->JoinNodesImpl(mRightNode, mLeftNode, mParent); } //XXX: What if instead of split, we just deleted the unneeded children of @@ -73,7 +81,11 @@ JoinNodeTransaction::DoTransaction() NS_IMETHODIMP JoinNodeTransaction::UndoTransaction() { - MOZ_ASSERT(mParent); + if (NS_WARN_IF(!mParent) || + NS_WARN_IF(!mRightNode) || + NS_WARN_IF(!mLeftNode)) { + return NS_ERROR_NOT_INITIALIZED; + } // First, massage the existing node so it is in its post-split state ErrorResult rv; diff --git a/editor/libeditor/JoinNodeTransaction.h b/editor/libeditor/JoinNodeTransaction.h index 84208cb45..827d9dfaf 100644 --- a/editor/libeditor/JoinNodeTransaction.h +++ b/editor/libeditor/JoinNodeTransaction.h @@ -47,7 +47,7 @@ public: NS_DECL_EDITTRANSACTIONBASE protected: - EditorBase& mEditorBase; + RefPtr<EditorBase> mEditorBase; // The nodes to operate upon. After the merge, mRightNode remains and // mLeftNode is removed from the content tree. diff --git a/editor/libeditor/PlaceholderTransaction.cpp b/editor/libeditor/PlaceholderTransaction.cpp index 1031b45ab..fa808afad 100644 --- a/editor/libeditor/PlaceholderTransaction.cpp +++ b/editor/libeditor/PlaceholderTransaction.cpp @@ -35,6 +35,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PlaceholderTransaction, if (tmp->mStartSel) { ImplCycleCollectionUnlink(*tmp->mStartSel); } + NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorBase); NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndSel); NS_IMPL_CYCLE_COLLECTION_UNLINK_END @@ -43,6 +44,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PlaceholderTransaction, if (tmp->mStartSel) { ImplCycleCollectionTraverse(cb, *tmp->mStartSel, "mStartSel", 0); } + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorBase); NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndSel); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END @@ -76,6 +78,10 @@ PlaceholderTransaction::DoTransaction() NS_IMETHODIMP PlaceholderTransaction::UndoTransaction() { + if (NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } + // Undo transactions. nsresult rv = EditAggregateTransaction::UndoTransaction(); NS_ENSURE_SUCCESS(rv, rv); @@ -91,6 +97,10 @@ PlaceholderTransaction::UndoTransaction() NS_IMETHODIMP PlaceholderTransaction::RedoTransaction() { + if (NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } + // Redo transactions. nsresult rv = EditAggregateTransaction::RedoTransaction(); NS_ENSURE_SUCCESS(rv, rv); @@ -261,6 +271,10 @@ PlaceholderTransaction::Commit() nsresult PlaceholderTransaction::RememberEndingSelection() { + if (NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } + RefPtr<Selection> selection = mEditorBase->GetSelection(); NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); mEndSel.SaveSelection(selection); diff --git a/editor/libeditor/PlaceholderTransaction.h b/editor/libeditor/PlaceholderTransaction.h index 867a82ce3..8193239be 100644 --- a/editor/libeditor/PlaceholderTransaction.h +++ b/editor/libeditor/PlaceholderTransaction.h @@ -84,7 +84,7 @@ protected: SelectionState mEndSel; // The editor for this transaction. - EditorBase* mEditorBase; + RefPtr<EditorBase> mEditorBase; }; } // namespace mozilla diff --git a/editor/libeditor/SplitNodeTransaction.cpp b/editor/libeditor/SplitNodeTransaction.cpp index 113ff7a61..8965b5399 100644 --- a/editor/libeditor/SplitNodeTransaction.cpp +++ b/editor/libeditor/SplitNodeTransaction.cpp @@ -19,7 +19,7 @@ using namespace dom; SplitNodeTransaction::SplitNodeTransaction(EditorBase& aEditorBase, nsIContent& aNode, int32_t aOffset) - : mEditorBase(aEditorBase) + : mEditorBase(&aEditorBase) , mExistingRightNode(&aNode) , mOffset(aOffset) { @@ -30,6 +30,7 @@ SplitNodeTransaction::~SplitNodeTransaction() } NS_IMPL_CYCLE_COLLECTION_INHERITED(SplitNodeTransaction, EditTransactionBase, + mEditorBase, mParent, mNewLeftNode) @@ -41,6 +42,10 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase) NS_IMETHODIMP SplitNodeTransaction::DoTransaction() { + if (NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } + // Create a new node ErrorResult rv; // Don't use .downcast directly because AsContent has an assertion we want @@ -48,16 +53,16 @@ SplitNodeTransaction::DoTransaction() NS_ASSERTION(!rv.Failed() && clone, "Could not create clone"); NS_ENSURE_TRUE(!rv.Failed() && clone, rv.StealNSResult()); mNewLeftNode = dont_AddRef(clone.forget().take()->AsContent()); - mEditorBase.MarkNodeDirty(mExistingRightNode->AsDOMNode()); + mEditorBase->MarkNodeDirty(mExistingRightNode->AsDOMNode()); // Get the parent node mParent = mExistingRightNode->GetParentNode(); NS_ENSURE_TRUE(mParent, NS_ERROR_NULL_POINTER); // Insert the new node - rv = mEditorBase.SplitNodeImpl(*mExistingRightNode, mOffset, *mNewLeftNode); - if (mEditorBase.GetShouldTxnSetSelection()) { - RefPtr<Selection> selection = mEditorBase.GetSelection(); + rv = mEditorBase->SplitNodeImpl(*mExistingRightNode, mOffset, *mNewLeftNode); + if (mEditorBase->GetShouldTxnSetSelection()) { + RefPtr<Selection> selection = mEditorBase->GetSelection(); NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); rv = selection->Collapse(mNewLeftNode, mOffset); } @@ -67,10 +72,14 @@ SplitNodeTransaction::DoTransaction() NS_IMETHODIMP SplitNodeTransaction::UndoTransaction() { - MOZ_ASSERT(mNewLeftNode && mParent); + if (NS_WARN_IF(!mEditorBase) || + NS_WARN_IF(!mNewLeftNode) || + NS_WARN_IF(!mParent)) { + return NS_ERROR_NOT_INITIALIZED; + } // This assumes Do inserted the new node in front of the prior existing node - return mEditorBase.JoinNodesImpl(mExistingRightNode, mNewLeftNode, mParent); + return mEditorBase->JoinNodesImpl(mExistingRightNode, mNewLeftNode, mParent); } /* Redo cannot simply resplit the right node, because subsequent transactions @@ -80,7 +89,10 @@ SplitNodeTransaction::UndoTransaction() NS_IMETHODIMP SplitNodeTransaction::RedoTransaction() { - MOZ_ASSERT(mNewLeftNode && mParent); + if (NS_WARN_IF(!mNewLeftNode) || + NS_WARN_IF(!mParent)) { + return NS_ERROR_NOT_INITIALIZED; + } ErrorResult rv; // First, massage the existing node so it is in its post-split state diff --git a/editor/libeditor/SplitNodeTransaction.h b/editor/libeditor/SplitNodeTransaction.h index 36119518b..4c50143ec 100644 --- a/editor/libeditor/SplitNodeTransaction.h +++ b/editor/libeditor/SplitNodeTransaction.h @@ -49,7 +49,7 @@ public: protected: virtual ~SplitNodeTransaction(); - EditorBase& mEditorBase; + RefPtr<EditorBase> mEditorBase; // The node to operate upon. nsCOMPtr<nsIContent> mExistingRightNode; diff --git a/editor/libeditor/StyleSheetTransactions.cpp b/editor/libeditor/StyleSheetTransactions.cpp index 6a31a16e2..cf32898a9 100644 --- a/editor/libeditor/StyleSheetTransactions.cpp +++ b/editor/libeditor/StyleSheetTransactions.cpp @@ -57,6 +57,7 @@ AddStyleSheetTransaction::AddStyleSheetTransaction() NS_IMPL_CYCLE_COLLECTION_INHERITED(AddStyleSheetTransaction, EditTransactionBase, + mEditor, mSheet) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AddStyleSheetTransaction) @@ -111,6 +112,7 @@ RemoveStyleSheetTransaction::RemoveStyleSheetTransaction() NS_IMPL_CYCLE_COLLECTION_INHERITED(RemoveStyleSheetTransaction, EditTransactionBase, + mEditor, mSheet) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RemoveStyleSheetTransaction) diff --git a/editor/libeditor/StyleSheetTransactions.h b/editor/libeditor/StyleSheetTransactions.h index bf615b263..d6855981b 100644 --- a/editor/libeditor/StyleSheetTransactions.h +++ b/editor/libeditor/StyleSheetTransactions.h @@ -36,7 +36,7 @@ public: protected: // The editor that created this transaction. - nsIEditor* mEditor; + nsCOMPtr<nsIEditor> mEditor; // The style sheet to add. RefPtr<mozilla::StyleSheet> mSheet; }; @@ -62,7 +62,7 @@ public: protected: // The editor that created this transaction. - nsIEditor* mEditor; + nsCOMPtr<nsIEditor> mEditor; // The style sheet to remove. RefPtr<StyleSheet> mSheet; diff --git a/js/src/jit-test/tests/debug/bug1353356.js b/js/src/jit-test/tests/debug/bug1353356.js new file mode 100644 index 000000000..389bb7860 --- /dev/null +++ b/js/src/jit-test/tests/debug/bug1353356.js @@ -0,0 +1,65 @@ +// |jit-test| allow-oom; --fuzzing-safe + +var lfLogBuffer = ` +//corefuzz-dcd-endofdata +//corefuzz-dcd-endofdata +//corefuzz-dcd-endofdata + setJitCompilerOption("ion.warmup.trigger", 4); + var g = newGlobal(); + g.debuggeeGlobal = this; + g.eval("(" + function () { + dbg = new Debugger(debuggeeGlobal); + dbg.onExceptionUnwind = function (frame, exc) { + var s = '!'; + for (var f = frame; f; f = f.older) + debuggeeGlobal.log += s; + }; + } + ")();"); + j('Number.prototype.toSource.call([])'); +//corefuzz-dcd-endofdata +//corefuzz-dcd-endofdata +//corefuzz-dcd-endofdata +//corefuzz-dcd-selectmode 4 +//corefuzz-dcd-endofdata +} +//corefuzz-dcd-endofdata +//corefuzz-dcd-selectmode 5 +//corefuzz-dcd-endofdata +oomTest(() => i({ + new : (true ), + thisprops: true +})); +`; +lfLogBuffer = lfLogBuffer.split('\n'); +var lfRunTypeId = -1; +var lfCodeBuffer = ""; +while (true) { + var line = lfLogBuffer.shift(); + if (line == null) { + break; + } else if (line == "//corefuzz-dcd-endofdata") { + loadFile(lfCodeBuffer); + lfCodeBuffer = ""; + loadFile(line); + } else { + lfCodeBuffer += line + "\n"; + } +} +if (lfCodeBuffer) loadFile(lfCodeBuffer); +function loadFile(lfVarx) { + try { + if (lfVarx.indexOf("//corefuzz-dcd-selectmode ") === 0) { + lfRunTypeId = parseInt(lfVarx.split(" ")[1]) % 6; + } else { + switch (lfRunTypeId) { + case 4: + oomTest(function() { + let m = parseModule(lfVarx); + }); + break; + default: + evaluate(lfVarx); + } + } + } catch (lfVare) {} +} diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 7978d8dbc..439bb1ed4 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -1517,11 +1517,7 @@ jit::JitActivation::getRematerializedFrame(JSContext* cx, const JitFrameIterator uint8_t* top = iter.fp(); RematerializedFrameTable::AddPtr p = rematerializedFrames_->lookupForAdd(top); if (!p) { - RematerializedFrameVector empty(cx); - if (!rematerializedFrames_->add(p, top, Move(empty))) { - ReportOutOfMemory(cx); - return nullptr; - } + RematerializedFrameVector frames(cx); // The unit of rematerialization is an uninlined frame and its inlined // frames. Since inlined frames do not exist outside of snapshots, it @@ -1536,9 +1532,11 @@ jit::JitActivation::getRematerializedFrame(JSContext* cx, const JitFrameIterator // be in the activation's compartment. AutoCompartment ac(cx, compartment_); - if (!RematerializedFrame::RematerializeInlineFrames(cx, top, inlineIter, recover, - p->value())) - { + if (!RematerializedFrame::RematerializeInlineFrames(cx, top, inlineIter, recover, frames)) + return nullptr; + + if (!rematerializedFrames_->add(p, top, Move(frames))) { + ReportOutOfMemory(cx); return nullptr; } diff --git a/js/src/wasm/WasmModule.cpp b/js/src/wasm/WasmModule.cpp index be7ddba8f..b24e01a40 100644 --- a/js/src/wasm/WasmModule.cpp +++ b/js/src/wasm/WasmModule.cpp @@ -1007,12 +1007,16 @@ Module::instantiate(JSContext* cx, maybeBytecode = bytecode_.get(); auto codeSegment = CodeSegment::create(cx, code_, linkData_, *metadata_, memory); - if (!codeSegment) + if (!codeSegment) { + ReportOutOfMemory(cx); return false; + } auto code = cx->make_unique<Code>(Move(codeSegment), *metadata_, maybeBytecode); - if (!code) + if (!code) { + ReportOutOfMemory(cx); return false; + } instance.set(WasmInstanceObject::create(cx, Move(code), diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index a1105ae52..782ac332c 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -544,6 +544,12 @@ nsDocumentViewer::~nsDocumentViewer() mDocument->Destroy(); } + if (mPrintEngine) { + mPrintEngine->Destroy(); + mPrintEngine = nullptr; + } + + MOZ_RELEASE_ASSERT(mDestroyRefCount == 0); NS_ASSERTION(!mPresShell && !mPresContext, "User did not call nsIContentViewer::Destroy"); if (mPresShell || mPresContext) { diff --git a/storage/mozStorageAsyncStatementExecution.cpp b/storage/mozStorageAsyncStatementExecution.cpp index add32131a..e1d344aca 100644 --- a/storage/mozStorageAsyncStatementExecution.cpp +++ b/storage/mozStorageAsyncStatementExecution.cpp @@ -40,122 +40,6 @@ namespace storage { #define MAX_ROWS_PER_RESULT 15 //////////////////////////////////////////////////////////////////////////////// -//// Local Classes - -namespace { - -typedef AsyncExecuteStatements::ExecutionState ExecutionState; -typedef AsyncExecuteStatements::StatementDataArray StatementDataArray; - -/** - * Notifies a callback with a result set. - */ -class CallbackResultNotifier : public Runnable -{ -public: - CallbackResultNotifier(mozIStorageStatementCallback *aCallback, - mozIStorageResultSet *aResults, - AsyncExecuteStatements *aEventStatus) : - mCallback(aCallback) - , mResults(aResults) - , mEventStatus(aEventStatus) - { - } - - NS_IMETHOD Run() override - { - NS_ASSERTION(mCallback, "Trying to notify about results without a callback!"); - - if (mEventStatus->shouldNotify()) { - // Hold a strong reference to the callback while notifying it, so that if - // it spins the event loop, the callback won't be released and freed out - // from under us. - nsCOMPtr<mozIStorageStatementCallback> callback = mCallback; - - (void)callback->HandleResult(mResults); - } - - return NS_OK; - } - -private: - mozIStorageStatementCallback *mCallback; - nsCOMPtr<mozIStorageResultSet> mResults; - RefPtr<AsyncExecuteStatements> mEventStatus; -}; - -/** - * Notifies the calling thread that an error has occurred. - */ -class ErrorNotifier : public Runnable -{ -public: - ErrorNotifier(mozIStorageStatementCallback *aCallback, - mozIStorageError *aErrorObj, - AsyncExecuteStatements *aEventStatus) : - mCallback(aCallback) - , mErrorObj(aErrorObj) - , mEventStatus(aEventStatus) - { - } - - NS_IMETHOD Run() override - { - if (mEventStatus->shouldNotify() && mCallback) { - // Hold a strong reference to the callback while notifying it, so that if - // it spins the event loop, the callback won't be released and freed out - // from under us. - nsCOMPtr<mozIStorageStatementCallback> callback = mCallback; - - (void)callback->HandleError(mErrorObj); - } - - return NS_OK; - } - -private: - mozIStorageStatementCallback *mCallback; - nsCOMPtr<mozIStorageError> mErrorObj; - RefPtr<AsyncExecuteStatements> mEventStatus; -}; - -/** - * Notifies the calling thread that the statement has finished executing. Takes - * ownership of the StatementData so it is released on the proper thread. - */ -class CompletionNotifier : public Runnable -{ -public: - /** - * This takes ownership of the callback and the StatementData. They are - * released on the thread this is dispatched to (which should always be the - * calling thread). - */ - CompletionNotifier(mozIStorageStatementCallback *aCallback, - ExecutionState aReason) - : mCallback(aCallback) - , mReason(aReason) - { - } - - NS_IMETHOD Run() override - { - if (mCallback) { - (void)mCallback->HandleCompletion(mReason); - NS_RELEASE(mCallback); - } - - return NS_OK; - } - -private: - mozIStorageStatementCallback *mCallback; - ExecutionState mReason; -}; - -} // namespace - -//////////////////////////////////////////////////////////////////////////////// //// AsyncExecuteStatements /* static */ @@ -208,16 +92,19 @@ AsyncExecuteStatements::AsyncExecuteStatements(StatementDataArray &aStatements, , mCancelRequested(false) , mMutex(aConnection->sharedAsyncExecutionMutex) , mDBMutex(aConnection->sharedDBMutex) - , mRequestStartDate(TimeStamp::Now()) +, mRequestStartDate(TimeStamp::Now()) { (void)mStatements.SwapElements(aStatements); NS_ASSERTION(mStatements.Length(), "We weren't given any statements!"); - NS_IF_ADDREF(mCallback); } AsyncExecuteStatements::~AsyncExecuteStatements() { + MOZ_ASSERT(!mCallback, "Never called the Completion callback!"); MOZ_ASSERT(!mHasTransaction, "There should be no transaction at this point"); + if (mCallback) { + NS_ProxyRelease(mCallingThread, mCallback.forget()); + } } bool @@ -255,7 +142,7 @@ AsyncExecuteStatements::bindExecuteAndProcessStatement(StatementData &aData, BindingParamsArray::iterator end = paramsArray->end(); while (itr != end && continueProcessing) { // Bind the data to our statement. - nsCOMPtr<IStorageBindingParamsInternal> bindingInternal = + nsCOMPtr<IStorageBindingParamsInternal> bindingInternal = do_QueryInterface(*itr); nsCOMPtr<mozIStorageError> error = bindingInternal->bind(aStatement); if (error) { @@ -462,16 +349,30 @@ AsyncExecuteStatements::notifyComplete() mHasTransaction = false; } - // Always generate a completion notification; it is what guarantees that our - // destruction does not happen here on the async thread. - RefPtr<CompletionNotifier> completionEvent = - new CompletionNotifier(mCallback, mState); - - // We no longer own mCallback (the CompletionNotifier takes ownership). - mCallback = nullptr; + // This will take ownership of mCallback and make sure its destruction will + // happen on the owner thread. + Unused << mCallingThread->Dispatch( + NewRunnableMethod(this, &AsyncExecuteStatements::notifyCompleteOnCallingThread), + NS_DISPATCH_NORMAL); - (void)mCallingThread->Dispatch(completionEvent, NS_DISPATCH_NORMAL); + return NS_OK; +} +nsresult +AsyncExecuteStatements::notifyCompleteOnCallingThread() { +#ifdef DEBUG + bool onCallingThread = false; + (void)mCallingThread->IsOnCurrentThread(&onCallingThread); + MOZ_ASSERT(onCallingThread); +#endif + // Take ownership of mCallback and responsibility for freeing it when we + // release it. Any notifyResultsOnCallingThread and notifyErrorOnCallingThread + // calls on the stack spinning the event loop have guaranteed their safety by + // creating their own strong reference before invoking the callback. + nsCOMPtr<mozIStorageStatementCallback> callback = mCallback.forget(); + if (callback) { + Unused << callback->HandleCompletion(mState); + } return NS_OK; } @@ -500,27 +401,63 @@ AsyncExecuteStatements::notifyError(mozIStorageError *aError) if (!mCallback) return NS_OK; - RefPtr<ErrorNotifier> notifier = - new ErrorNotifier(mCallback, aError, this); - NS_ENSURE_TRUE(notifier, NS_ERROR_OUT_OF_MEMORY); + Unused << mCallingThread->Dispatch( + NewRunnableMethod<nsCOMPtr<mozIStorageError>>(this, &AsyncExecuteStatements::notifyErrorOnCallingThread, aError), + NS_DISPATCH_NORMAL); - return mCallingThread->Dispatch(notifier, NS_DISPATCH_NORMAL); + return NS_OK; +} + +nsresult +AsyncExecuteStatements::notifyErrorOnCallingThread(mozIStorageError *aError) { +#ifdef DEBUG + bool onCallingThread = false; + (void)mCallingThread->IsOnCurrentThread(&onCallingThread); + MOZ_ASSERT(onCallingThread); +#endif + // Acquire our own strong reference so that if the callback spins a nested + // event loop and notifyCompleteOnCallingThread is executed, forgetting + // mCallback, we still have a valid/strong reference that won't be freed until + // we exit. + nsCOMPtr<mozIStorageStatementCallback> callback = mCallback; + if (shouldNotify() && callback) { + Unused << callback->HandleError(aError); + } + return NS_OK; } nsresult AsyncExecuteStatements::notifyResults() { mMutex.AssertNotCurrentThreadOwns(); - NS_ASSERTION(mCallback, "notifyResults called without a callback!"); + MOZ_ASSERT(mCallback, "notifyResults called without a callback!"); - RefPtr<CallbackResultNotifier> notifier = - new CallbackResultNotifier(mCallback, mResultSet, this); - NS_ENSURE_TRUE(notifier, NS_ERROR_OUT_OF_MEMORY); + // This takes ownership of mResultSet, a new one will be generated in + // buildAndNotifyResults() when further results will arrive. + Unused << mCallingThread->Dispatch( + NewRunnableMethod<RefPtr<ResultSet>>(this, &AsyncExecuteStatements::notifyResultsOnCallingThread, mResultSet.forget()), + NS_DISPATCH_NORMAL); - nsresult rv = mCallingThread->Dispatch(notifier, NS_DISPATCH_NORMAL); - if (NS_SUCCEEDED(rv)) - mResultSet = nullptr; // we no longer own it on success - return rv; + return NS_OK; +} + +nsresult +AsyncExecuteStatements::notifyResultsOnCallingThread(ResultSet *aResultSet) +{ +#ifdef DEBUG + bool onCallingThread = false; + (void)mCallingThread->IsOnCurrentThread(&onCallingThread); + MOZ_ASSERT(onCallingThread); +#endif + // Acquire our own strong reference so that if the callback spins a nested + // event loop and notifyCompleteOnCallingThread is executed, forgetting + // mCallback, we still have a valid/strong reference that won't be freed until + // we exit. + nsCOMPtr<mozIStorageStatementCallback> callback = mCallback; + if (shouldNotify() && callback) { + Unused << callback->HandleResult(aResultSet); + } + return NS_OK; } NS_IMPL_ISUPPORTS( diff --git a/storage/mozStorageAsyncStatementExecution.h b/storage/mozStorageAsyncStatementExecution.h index c8493fd77..14ea49c2d 100644 --- a/storage/mozStorageAsyncStatementExecution.h +++ b/storage/mozStorageAsyncStatementExecution.h @@ -82,6 +82,14 @@ public: */ bool shouldNotify(); + /** + * Used by notifyComplete(), notifyError() and notifyResults() to notify on + * the calling thread. + */ + nsresult notifyCompleteOnCallingThread(); + nsresult notifyErrorOnCallingThread(mozIStorageError *aError); + nsresult notifyResultsOnCallingThread(ResultSet *aResultSet); + private: AsyncExecuteStatements(StatementDataArray &aStatements, Connection *aConnection, @@ -186,7 +194,10 @@ private: RefPtr<Connection> mConnection; sqlite3 *mNativeConnection; bool mHasTransaction; - mozIStorageStatementCallback *mCallback; + // Note, this may not be a threadsafe object - never addref/release off + // the calling thread. We take a reference when this is created, and + // release it in the CompletionNotifier::Run() call back to this thread. + nsCOMPtr<mozIStorageStatementCallback> mCallback; nsCOMPtr<nsIThread> mCallingThread; RefPtr<ResultSet> mResultSet; diff --git a/toolkit/components/jsdownloads/src/DownloadIntegration.jsm b/toolkit/components/jsdownloads/src/DownloadIntegration.jsm index 5fed9212a..1d289c24d 100644 --- a/toolkit/components/jsdownloads/src/DownloadIntegration.jsm +++ b/toolkit/components/jsdownloads/src/DownloadIntegration.jsm @@ -28,6 +28,8 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown", "resource://gre/modules/AsyncShutdown.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "AppConstants", + "resource://gre/modules/AppConstants.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask", "resource://gre/modules/DeferredTask.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Downloads", @@ -675,20 +677,6 @@ this.DownloadIntegration = { launchDownload: Task.async(function* (aDownload) { let file = new FileUtils.File(aDownload.target.path); -#ifndef XP_WIN - // Ask for confirmation if the file is executable, except on Windows where - // the operating system will show the prompt based on the security zone. - // We do this here, instead of letting the caller handle the prompt - // separately in the user interface layer, for two reasons. The first is - // because of its security nature, so that add-ons cannot forget to do - // this check. The second is that the system-level security prompt would - // be displayed at launch time in any case. - if (file.isExecutable() && - !(yield this.confirmLaunchExecutable(file.path))) { - return; - } -#endif - // In case of a double extension, like ".tar.gz", we only // consider the last one, because the MIME service cannot // handle multiple extensions. @@ -698,6 +686,21 @@ this.DownloadIntegration = { fileExtension = match[1]; } + let isWindowsExe = AppConstants.platform == "win" && + fileExtension.toLowerCase() == "exe"; + + // Ask for confirmation if the file is executable, except for .exe on + // Windows where the operating system will show the prompt based on the + // security zone. We do this here, instead of letting the caller handle + // the prompt separately in the user interface layer, for two reasons. The + // first is because of its security nature, so that add-ons cannot forget + // to do this check. The second is that the system-level security prompt + // would be displayed at launch time in any case. + if (file.isExecutable() && !isWindowsExe && + !(await this.confirmLaunchExecutable(file.path))) { + return; + } + try { // The MIME service might throw if contentType == "" and it can't find // a MIME type for the given extension, so we'll treat this case as |