|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "SetDocTitleTxn.h" |
|
7 #include "mozilla/dom/Element.h" // for Element |
|
8 #include "nsAString.h" |
|
9 #include "nsCOMPtr.h" // for nsCOMPtr, getter_AddRefs, etc |
|
10 #include "nsDebug.h" // for NS_ENSURE_SUCCESS, etc |
|
11 #include "nsError.h" // for NS_OK, NS_ERROR_FAILURE, etc |
|
12 #include "nsIDOMCharacterData.h" // for nsIDOMCharacterData |
|
13 #include "nsIDOMDocument.h" // for nsIDOMDocument |
|
14 #include "nsIDOMElement.h" // for nsIDOMElement |
|
15 #include "nsIDOMNode.h" // for nsIDOMNode |
|
16 #include "nsIDOMNodeList.h" // for nsIDOMNodeList |
|
17 #include "nsIDOMText.h" // for nsIDOMText |
|
18 #include "nsIDocument.h" // for nsIDocument |
|
19 #include "nsIEditor.h" // for nsIEditor |
|
20 #include "nsIHTMLEditor.h" // for nsIHTMLEditor |
|
21 #include "nsLiteralString.h" // for NS_LITERAL_STRING |
|
22 |
|
23 using namespace mozilla; |
|
24 |
|
25 // note that aEditor is not refcounted |
|
26 SetDocTitleTxn::SetDocTitleTxn() |
|
27 : EditTxn() |
|
28 , mIsTransient(false) |
|
29 { |
|
30 } |
|
31 |
|
32 NS_IMETHODIMP SetDocTitleTxn::Init(nsIHTMLEditor *aEditor, |
|
33 const nsAString *aValue) |
|
34 |
|
35 { |
|
36 NS_ASSERTION(aEditor && aValue, "null args"); |
|
37 if (!aEditor || !aValue) { return NS_ERROR_NULL_POINTER; } |
|
38 |
|
39 mEditor = aEditor; |
|
40 mValue = *aValue; |
|
41 |
|
42 return NS_OK; |
|
43 } |
|
44 |
|
45 NS_IMETHODIMP SetDocTitleTxn::DoTransaction(void) |
|
46 { |
|
47 return SetDomTitle(mValue); |
|
48 } |
|
49 |
|
50 NS_IMETHODIMP SetDocTitleTxn::UndoTransaction(void) |
|
51 { |
|
52 // No extra work required; the DOM changes alone are enough |
|
53 return NS_OK; |
|
54 } |
|
55 |
|
56 NS_IMETHODIMP SetDocTitleTxn::RedoTransaction(void) |
|
57 { |
|
58 // No extra work required; the DOM changes alone are enough |
|
59 return NS_OK; |
|
60 } |
|
61 |
|
62 nsresult SetDocTitleTxn::SetDomTitle(const nsAString& aTitle) |
|
63 { |
|
64 nsCOMPtr<nsIEditor> editor = do_QueryInterface(mEditor); |
|
65 NS_ENSURE_TRUE(editor, NS_ERROR_FAILURE); |
|
66 nsCOMPtr<nsIDOMDocument> domDoc; |
|
67 nsresult res = editor->GetDocument(getter_AddRefs(domDoc)); |
|
68 NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE); |
|
69 |
|
70 nsCOMPtr<nsIDOMNodeList> titleList; |
|
71 res = domDoc->GetElementsByTagName(NS_LITERAL_STRING("title"), getter_AddRefs(titleList)); |
|
72 NS_ENSURE_SUCCESS(res, res); |
|
73 |
|
74 // First assume we will NOT really do anything |
|
75 // (transaction will not be pushed on stack) |
|
76 mIsTransient = true; |
|
77 |
|
78 nsCOMPtr<nsIDOMNode>titleNode; |
|
79 if(titleList) |
|
80 { |
|
81 res = titleList->Item(0, getter_AddRefs(titleNode)); |
|
82 NS_ENSURE_SUCCESS(res, res); |
|
83 if (titleNode) |
|
84 { |
|
85 // Delete existing child textnode of title node |
|
86 // (Note: all contents under a TITLE node are always in a single text node) |
|
87 nsCOMPtr<nsIDOMNode> child; |
|
88 res = titleNode->GetFirstChild(getter_AddRefs(child)); |
|
89 if(NS_FAILED(res)) return res; |
|
90 if(child) |
|
91 { |
|
92 // Save current text as the undo value |
|
93 nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(child); |
|
94 if(textNode) |
|
95 { |
|
96 textNode->GetData(mUndoValue); |
|
97 |
|
98 // If title text is identical to what already exists, |
|
99 // quit now (mIsTransient is now TRUE) |
|
100 if (mUndoValue == aTitle) |
|
101 return NS_OK; |
|
102 } |
|
103 res = editor->DeleteNode(child); |
|
104 if(NS_FAILED(res)) return res; |
|
105 } |
|
106 } |
|
107 } |
|
108 |
|
109 // We didn't return above, thus we really will be changing the title |
|
110 mIsTransient = false; |
|
111 |
|
112 // Get the <HEAD> node, create a <TITLE> and insert it under the HEAD |
|
113 nsCOMPtr<nsIDocument> document = do_QueryInterface(domDoc); |
|
114 NS_ENSURE_STATE(document); |
|
115 |
|
116 dom::Element* head = document->GetHeadElement(); |
|
117 NS_ENSURE_STATE(head); |
|
118 |
|
119 bool newTitleNode = false; |
|
120 uint32_t newTitleIndex = 0; |
|
121 |
|
122 if (!titleNode) |
|
123 { |
|
124 // Didn't find one above: Create a new one |
|
125 nsCOMPtr<nsIDOMElement>titleElement; |
|
126 res = domDoc->CreateElement(NS_LITERAL_STRING("title"), getter_AddRefs(titleElement)); |
|
127 NS_ENSURE_SUCCESS(res, res); |
|
128 NS_ENSURE_TRUE(titleElement, NS_ERROR_FAILURE); |
|
129 |
|
130 titleNode = do_QueryInterface(titleElement); |
|
131 newTitleNode = true; |
|
132 |
|
133 // Get index so we append new title node after all existing HEAD children. |
|
134 newTitleIndex = head->GetChildCount(); |
|
135 } |
|
136 |
|
137 // Append a text node under the TITLE |
|
138 // only if the title text isn't empty |
|
139 if (titleNode && !aTitle.IsEmpty()) |
|
140 { |
|
141 nsCOMPtr<nsIDOMText> textNode; |
|
142 res = domDoc->CreateTextNode(aTitle, getter_AddRefs(textNode)); |
|
143 NS_ENSURE_SUCCESS(res, res); |
|
144 nsCOMPtr<nsIDOMNode> newNode = do_QueryInterface(textNode); |
|
145 NS_ENSURE_TRUE(newNode, NS_ERROR_FAILURE); |
|
146 |
|
147 if (newTitleNode) |
|
148 { |
|
149 // Not undoable: We will insert newTitleNode below |
|
150 nsCOMPtr<nsIDOMNode> resultNode; |
|
151 res = titleNode->AppendChild(newNode, getter_AddRefs(resultNode)); |
|
152 } |
|
153 else |
|
154 { |
|
155 // This is an undoable transaction |
|
156 res = editor->InsertNode(newNode, titleNode, 0); |
|
157 } |
|
158 NS_ENSURE_SUCCESS(res, res); |
|
159 } |
|
160 |
|
161 if (newTitleNode) |
|
162 { |
|
163 // Undoable transaction to insert title+text together |
|
164 res = editor->InsertNode(titleNode, head->AsDOMNode(), newTitleIndex); |
|
165 } |
|
166 return res; |
|
167 } |
|
168 |
|
169 NS_IMETHODIMP SetDocTitleTxn::GetTxnDescription(nsAString& aString) |
|
170 { |
|
171 aString.AssignLiteral("SetDocTitleTxn: "); |
|
172 aString += mValue; |
|
173 return NS_OK; |
|
174 } |
|
175 |
|
176 NS_IMETHODIMP SetDocTitleTxn::GetIsTransient(bool *aIsTransient) |
|
177 { |
|
178 NS_ENSURE_TRUE(aIsTransient, NS_ERROR_NULL_POINTER); |
|
179 *aIsTransient = mIsTransient; |
|
180 return NS_OK; |
|
181 } |
|
182 |