michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "SetDocTitleTxn.h" michael@0: #include "mozilla/dom/Element.h" // for Element michael@0: #include "nsAString.h" michael@0: #include "nsCOMPtr.h" // for nsCOMPtr, getter_AddRefs, etc michael@0: #include "nsDebug.h" // for NS_ENSURE_SUCCESS, etc michael@0: #include "nsError.h" // for NS_OK, NS_ERROR_FAILURE, etc michael@0: #include "nsIDOMCharacterData.h" // for nsIDOMCharacterData michael@0: #include "nsIDOMDocument.h" // for nsIDOMDocument michael@0: #include "nsIDOMElement.h" // for nsIDOMElement michael@0: #include "nsIDOMNode.h" // for nsIDOMNode michael@0: #include "nsIDOMNodeList.h" // for nsIDOMNodeList michael@0: #include "nsIDOMText.h" // for nsIDOMText michael@0: #include "nsIDocument.h" // for nsIDocument michael@0: #include "nsIEditor.h" // for nsIEditor michael@0: #include "nsIHTMLEditor.h" // for nsIHTMLEditor michael@0: #include "nsLiteralString.h" // for NS_LITERAL_STRING michael@0: michael@0: using namespace mozilla; michael@0: michael@0: // note that aEditor is not refcounted michael@0: SetDocTitleTxn::SetDocTitleTxn() michael@0: : EditTxn() michael@0: , mIsTransient(false) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP SetDocTitleTxn::Init(nsIHTMLEditor *aEditor, michael@0: const nsAString *aValue) michael@0: michael@0: { michael@0: NS_ASSERTION(aEditor && aValue, "null args"); michael@0: if (!aEditor || !aValue) { return NS_ERROR_NULL_POINTER; } michael@0: michael@0: mEditor = aEditor; michael@0: mValue = *aValue; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP SetDocTitleTxn::DoTransaction(void) michael@0: { michael@0: return SetDomTitle(mValue); michael@0: } michael@0: michael@0: NS_IMETHODIMP SetDocTitleTxn::UndoTransaction(void) michael@0: { michael@0: // No extra work required; the DOM changes alone are enough michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP SetDocTitleTxn::RedoTransaction(void) michael@0: { michael@0: // No extra work required; the DOM changes alone are enough michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult SetDocTitleTxn::SetDomTitle(const nsAString& aTitle) michael@0: { michael@0: nsCOMPtr editor = do_QueryInterface(mEditor); michael@0: NS_ENSURE_TRUE(editor, NS_ERROR_FAILURE); michael@0: nsCOMPtr domDoc; michael@0: nsresult res = editor->GetDocument(getter_AddRefs(domDoc)); michael@0: NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE); michael@0: michael@0: nsCOMPtr titleList; michael@0: res = domDoc->GetElementsByTagName(NS_LITERAL_STRING("title"), getter_AddRefs(titleList)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // First assume we will NOT really do anything michael@0: // (transaction will not be pushed on stack) michael@0: mIsTransient = true; michael@0: michael@0: nsCOMPtrtitleNode; michael@0: if(titleList) michael@0: { michael@0: res = titleList->Item(0, getter_AddRefs(titleNode)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: if (titleNode) michael@0: { michael@0: // Delete existing child textnode of title node michael@0: // (Note: all contents under a TITLE node are always in a single text node) michael@0: nsCOMPtr child; michael@0: res = titleNode->GetFirstChild(getter_AddRefs(child)); michael@0: if(NS_FAILED(res)) return res; michael@0: if(child) michael@0: { michael@0: // Save current text as the undo value michael@0: nsCOMPtr textNode = do_QueryInterface(child); michael@0: if(textNode) michael@0: { michael@0: textNode->GetData(mUndoValue); michael@0: michael@0: // If title text is identical to what already exists, michael@0: // quit now (mIsTransient is now TRUE) michael@0: if (mUndoValue == aTitle) michael@0: return NS_OK; michael@0: } michael@0: res = editor->DeleteNode(child); michael@0: if(NS_FAILED(res)) return res; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // We didn't return above, thus we really will be changing the title michael@0: mIsTransient = false; michael@0: michael@0: // Get the node, create a and insert it under the HEAD michael@0: nsCOMPtr<nsIDocument> document = do_QueryInterface(domDoc); michael@0: NS_ENSURE_STATE(document); michael@0: michael@0: dom::Element* head = document->GetHeadElement(); michael@0: NS_ENSURE_STATE(head); michael@0: michael@0: bool newTitleNode = false; michael@0: uint32_t newTitleIndex = 0; michael@0: michael@0: if (!titleNode) michael@0: { michael@0: // Didn't find one above: Create a new one michael@0: nsCOMPtr<nsIDOMElement>titleElement; michael@0: res = domDoc->CreateElement(NS_LITERAL_STRING("title"), getter_AddRefs(titleElement)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(titleElement, NS_ERROR_FAILURE); michael@0: michael@0: titleNode = do_QueryInterface(titleElement); michael@0: newTitleNode = true; michael@0: michael@0: // Get index so we append new title node after all existing HEAD children. michael@0: newTitleIndex = head->GetChildCount(); michael@0: } michael@0: michael@0: // Append a text node under the TITLE michael@0: // only if the title text isn't empty michael@0: if (titleNode && !aTitle.IsEmpty()) michael@0: { michael@0: nsCOMPtr<nsIDOMText> textNode; michael@0: res = domDoc->CreateTextNode(aTitle, getter_AddRefs(textNode)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: nsCOMPtr<nsIDOMNode> newNode = do_QueryInterface(textNode); michael@0: NS_ENSURE_TRUE(newNode, NS_ERROR_FAILURE); michael@0: michael@0: if (newTitleNode) michael@0: { michael@0: // Not undoable: We will insert newTitleNode below michael@0: nsCOMPtr<nsIDOMNode> resultNode; michael@0: res = titleNode->AppendChild(newNode, getter_AddRefs(resultNode)); michael@0: } michael@0: else michael@0: { michael@0: // This is an undoable transaction michael@0: res = editor->InsertNode(newNode, titleNode, 0); michael@0: } michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: } michael@0: michael@0: if (newTitleNode) michael@0: { michael@0: // Undoable transaction to insert title+text together michael@0: res = editor->InsertNode(titleNode, head->AsDOMNode(), newTitleIndex); michael@0: } michael@0: return res; michael@0: } michael@0: michael@0: NS_IMETHODIMP SetDocTitleTxn::GetTxnDescription(nsAString& aString) michael@0: { michael@0: aString.AssignLiteral("SetDocTitleTxn: "); michael@0: aString += mValue; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP SetDocTitleTxn::GetIsTransient(bool *aIsTransient) michael@0: { michael@0: NS_ENSURE_TRUE(aIsTransient, NS_ERROR_NULL_POINTER); michael@0: *aIsTransient = mIsTransient; michael@0: return NS_OK; michael@0: } michael@0: