summaryrefslogtreecommitdiffstats
path: root/editor/libeditor/SetDocumentTitleTransaction.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'editor/libeditor/SetDocumentTitleTransaction.cpp')
-rw-r--r--editor/libeditor/SetDocumentTitleTransaction.cpp229
1 files changed, 229 insertions, 0 deletions
diff --git a/editor/libeditor/SetDocumentTitleTransaction.cpp b/editor/libeditor/SetDocumentTitleTransaction.cpp
new file mode 100644
index 000000000..88403c0ad
--- /dev/null
+++ b/editor/libeditor/SetDocumentTitleTransaction.cpp
@@ -0,0 +1,229 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SetDocumentTitleTransaction.h"
+#include "mozilla/dom/Element.h" // for Element
+#include "nsAString.h"
+#include "nsCOMPtr.h" // for nsCOMPtr, getter_AddRefs, etc.
+#include "nsDebug.h" // for NS_ENSURE_SUCCESS, etc.
+#include "nsError.h" // for NS_OK, NS_ERROR_FAILURE, etc.
+#include "nsIDOMCharacterData.h" // for nsIDOMCharacterData
+#include "nsIDOMDocument.h" // for nsIDOMDocument
+#include "nsIDOMElement.h" // for nsIDOMElement
+#include "nsIDOMNode.h" // for nsIDOMNode
+#include "nsIDOMNodeList.h" // for nsIDOMNodeList
+#include "nsIDOMText.h" // for nsIDOMText
+#include "nsIDocument.h" // for nsIDocument
+#include "nsIEditor.h" // for nsIEditor
+#include "nsIHTMLEditor.h" // for nsIHTMLEditor
+#include "nsLiteralString.h" // for NS_LITERAL_STRING
+
+namespace mozilla {
+
+// Note that aEditor is not refcounted.
+SetDocumentTitleTransaction::SetDocumentTitleTransaction()
+ : mEditor(nullptr)
+ , mIsTransient(false)
+{
+}
+
+NS_IMETHODIMP
+SetDocumentTitleTransaction::Init(nsIHTMLEditor* aEditor,
+ const nsAString* aValue)
+
+{
+ NS_ASSERTION(aEditor && aValue, "null args");
+ if (!aEditor || !aValue) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ mEditor = aEditor;
+ mValue = *aValue;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SetDocumentTitleTransaction::DoTransaction()
+{
+ return SetDomTitle(mValue);
+}
+
+NS_IMETHODIMP
+SetDocumentTitleTransaction::UndoTransaction()
+{
+ // No extra work required; the DOM changes alone are enough
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SetDocumentTitleTransaction::RedoTransaction()
+{
+ // No extra work required; the DOM changes alone are enough
+ return NS_OK;
+}
+
+nsresult
+SetDocumentTitleTransaction::SetDomTitle(const nsAString& aTitle)
+{
+ nsCOMPtr<nsIEditor> editor = do_QueryInterface(mEditor);
+ if (NS_WARN_IF(!editor)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIDOMDocument> domDoc;
+ nsresult rv = editor->GetDocument(getter_AddRefs(domDoc));
+ if (NS_WARN_IF(!domDoc)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIDOMNodeList> titleList;
+ rv = domDoc->GetElementsByTagName(NS_LITERAL_STRING("title"),
+ getter_AddRefs(titleList));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // First assume we will NOT really do anything
+ // (transaction will not be pushed on stack)
+ mIsTransient = true;
+
+ nsCOMPtr<nsIDOMNode> titleNode;
+ if(titleList) {
+ rv = titleList->Item(0, getter_AddRefs(titleNode));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (titleNode) {
+ // Delete existing child textnode of title node
+ // (Note: all contents under a TITLE node are always in a single text node)
+ nsCOMPtr<nsIDOMNode> child;
+ rv = titleNode->GetFirstChild(getter_AddRefs(child));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if(child) {
+ // Save current text as the undo value
+ nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(child);
+ if(textNode) {
+ textNode->GetData(mUndoValue);
+
+ // If title text is identical to what already exists,
+ // quit now (mIsTransient is now TRUE)
+ if (mUndoValue == aTitle) {
+ return NS_OK;
+ }
+ }
+ rv = editor->DeleteNode(child);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+ }
+ }
+
+ // We didn't return above, thus we really will be changing the title
+ mIsTransient = false;
+
+ // Get the <HEAD> node, create a <TITLE> and insert it under the HEAD
+ nsCOMPtr<nsIDocument> document = do_QueryInterface(domDoc);
+ if (NS_WARN_IF(!document)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ RefPtr<dom::Element> headElement = document->GetHeadElement();
+ if (NS_WARN_IF(!headElement)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ bool newTitleNode = false;
+ uint32_t newTitleIndex = 0;
+
+ if (!titleNode) {
+ // Didn't find one above: Create a new one
+ nsCOMPtr<nsIDOMElement>titleElement;
+ rv = domDoc->CreateElement(NS_LITERAL_STRING("title"),
+ getter_AddRefs(titleElement));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ if (NS_WARN_IF(!titleElement)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ titleNode = do_QueryInterface(titleElement);
+ newTitleNode = true;
+
+ // Get index so we append new title node after all existing HEAD children.
+ newTitleIndex = headElement->GetChildCount();
+ }
+
+ // Append a text node under the TITLE only if the title text isn't empty.
+ if (titleNode && !aTitle.IsEmpty()) {
+ nsCOMPtr<nsIDOMText> textNode;
+ rv = domDoc->CreateTextNode(aTitle, getter_AddRefs(textNode));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCOMPtr<nsIDOMNode> newNode = do_QueryInterface(textNode);
+ if (NS_WARN_IF(!newNode)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (newTitleNode) {
+ // Not undoable: We will insert newTitleNode below
+ nsCOMPtr<nsIDOMNode> resultNode;
+ rv = titleNode->AppendChild(newNode, getter_AddRefs(resultNode));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ } else {
+ // This is an undoable transaction
+ rv = editor->InsertNode(newNode, titleNode, 0);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+ // Calling AppendChild() or InsertNode() could cause removing the head
+ // element. So, let's mark it dirty.
+ headElement = nullptr;
+ }
+
+ if (newTitleNode) {
+ if (!headElement) {
+ headElement = document->GetHeadElement();
+ if (NS_WARN_IF(!headElement)) {
+ // XXX Can we return NS_OK when there is no head element?
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+ // Undoable transaction to insert title+text together
+ rv = editor->InsertNode(titleNode, headElement->AsDOMNode(), newTitleIndex);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+SetDocumentTitleTransaction::GetTxnDescription(nsAString& aString)
+{
+ aString.AssignLiteral("SetDocumentTitleTransaction: ");
+ aString += mValue;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SetDocumentTitleTransaction::GetIsTransient(bool* aIsTransient)
+{
+ if (NS_WARN_IF(!aIsTransient)) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ *aIsTransient = mIsTransient;
+ return NS_OK;
+}
+
+} // namespace mozilla