/src/mozilla-central/editor/composer/nsEditingSession.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=2 sw=2 et tw=78: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include <string.h> // for nullptr, strcmp |
8 | | |
9 | | #include "imgIContainer.h" // for imgIContainer, etc |
10 | | #include "mozilla/ComposerCommandsUpdater.h" // for ComposerCommandsUpdater |
11 | | #include "mozilla/FlushType.h" // for FlushType::Frames |
12 | | #include "mozilla/HTMLEditor.h" // for HTMLEditor |
13 | | #include "mozilla/mozalloc.h" // for operator new |
14 | | #include "nsAString.h" |
15 | | #include "nsComponentManagerUtils.h" // for do_CreateInstance |
16 | | #include "nsContentUtils.h" |
17 | | #include "nsDebug.h" // for NS_ENSURE_SUCCESS, etc |
18 | | #include "nsEditingSession.h" |
19 | | #include "nsError.h" // for NS_ERROR_FAILURE, NS_OK, etc |
20 | | #include "nsIChannel.h" // for nsIChannel |
21 | | #include "nsICommandManager.h" // for nsICommandManager |
22 | | #include "nsIContentViewer.h" // for nsIContentViewer |
23 | | #include "nsIController.h" // for nsIController |
24 | | #include "nsIControllerContext.h" // for nsIControllerContext |
25 | | #include "nsIControllers.h" // for nsIControllers |
26 | | #include "nsID.h" // for NS_GET_IID, etc |
27 | | #include "nsHTMLDocument.h" // for nsHTMLDocument |
28 | | #include "nsIDOMWindow.h" // for nsIDOMWindow |
29 | | #include "nsIDocShell.h" // for nsIDocShell |
30 | | #include "nsIDocument.h" // for nsIDocument |
31 | | #include "nsIDocumentStateListener.h" |
32 | | #include "nsIEditor.h" // for nsIEditor |
33 | | #include "nsIHTMLDocument.h" // for nsIHTMLDocument, etc |
34 | | #include "nsIInterfaceRequestorUtils.h" // for do_GetInterface |
35 | | #include "nsIPlaintextEditor.h" // for nsIPlaintextEditor, etc |
36 | | #include "nsIPresShell.h" // for nsIPresShell |
37 | | #include "nsIRefreshURI.h" // for nsIRefreshURI |
38 | | #include "nsIRequest.h" // for nsIRequest |
39 | | #include "nsITimer.h" // for nsITimer, etc |
40 | | #include "nsITransactionManager.h" // for nsITransactionManager |
41 | | #include "nsIWeakReference.h" // for nsISupportsWeakReference, etc |
42 | | #include "nsIWebNavigation.h" // for nsIWebNavigation |
43 | | #include "nsIWebProgress.h" // for nsIWebProgress, etc |
44 | | #include "nsLiteralString.h" // for NS_LITERAL_STRING |
45 | | #include "nsPICommandUpdater.h" // for nsPICommandUpdater |
46 | | #include "nsPIDOMWindow.h" // for nsPIDOMWindow |
47 | | #include "nsPresContext.h" // for nsPresContext |
48 | | #include "nsReadableUtils.h" // for AppendUTF16toUTF8 |
49 | | #include "nsStringFwd.h" // for nsString |
50 | | #include "mozilla/dom/Selection.h" // for AutoHideSelectionChanges, etc |
51 | | #include "nsFrameSelection.h" // for nsFrameSelection |
52 | | #include "nsBaseCommandController.h" // for nsBaseCommandController |
53 | | |
54 | | class nsISupports; |
55 | | class nsIURI; |
56 | | |
57 | | using namespace mozilla; |
58 | | using namespace mozilla::dom; |
59 | | |
60 | | /*--------------------------------------------------------------------------- |
61 | | |
62 | | nsEditingSession |
63 | | |
64 | | ----------------------------------------------------------------------------*/ |
65 | | nsEditingSession::nsEditingSession() |
66 | | : mDoneSetup(false) |
67 | | , mCanCreateEditor(false) |
68 | | , mInteractive(false) |
69 | | , mMakeWholeDocumentEditable(true) |
70 | | , mDisabledJSAndPlugins(false) |
71 | | , mScriptsEnabled(true) |
72 | | , mPluginsEnabled(true) |
73 | | , mProgressListenerRegistered(false) |
74 | | , mImageAnimationMode(0) |
75 | | , mEditorFlags(0) |
76 | | , mEditorStatus(eEditorOK) |
77 | | , mBaseCommandControllerId(0) |
78 | | , mDocStateControllerId(0) |
79 | | , mHTMLCommandControllerId(0) |
80 | 0 | { |
81 | 0 | } |
82 | | |
83 | | /*--------------------------------------------------------------------------- |
84 | | |
85 | | ~nsEditingSession |
86 | | |
87 | | ----------------------------------------------------------------------------*/ |
88 | | nsEditingSession::~nsEditingSession() |
89 | 0 | { |
90 | 0 | // Must cancel previous timer? |
91 | 0 | if (mLoadBlankDocTimer) |
92 | 0 | mLoadBlankDocTimer->Cancel(); |
93 | 0 | } |
94 | | |
95 | | NS_IMPL_ISUPPORTS(nsEditingSession, nsIEditingSession, nsIWebProgressListener, |
96 | | nsISupportsWeakReference) |
97 | | |
98 | | /*--------------------------------------------------------------------------- |
99 | | |
100 | | MakeWindowEditable |
101 | | |
102 | | aEditorType string, "html" "htmlsimple" "text" "textsimple" |
103 | | void makeWindowEditable(in nsIDOMWindow aWindow, in string aEditorType, |
104 | | in boolean aDoAfterUriLoad, |
105 | | in boolean aMakeWholeDocumentEditable, |
106 | | in boolean aInteractive); |
107 | | ----------------------------------------------------------------------------*/ |
108 | 0 | #define DEFAULT_EDITOR_TYPE "html" |
109 | | |
110 | | NS_IMETHODIMP |
111 | | nsEditingSession::MakeWindowEditable(mozIDOMWindowProxy* aWindow, |
112 | | const char *aEditorType, |
113 | | bool aDoAfterUriLoad, |
114 | | bool aMakeWholeDocumentEditable, |
115 | | bool aInteractive) |
116 | 0 | { |
117 | 0 | mEditorType.Truncate(); |
118 | 0 | mEditorFlags = 0; |
119 | 0 |
|
120 | 0 | NS_ENSURE_TRUE(aWindow, NS_ERROR_FAILURE); |
121 | 0 | auto* window = nsPIDOMWindowOuter::From(aWindow); |
122 | 0 |
|
123 | 0 | // disable plugins |
124 | 0 | nsCOMPtr<nsIDocShell> docShell = window->GetDocShell(); |
125 | 0 | NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); |
126 | 0 |
|
127 | 0 | mDocShell = do_GetWeakReference(docShell); |
128 | 0 | mInteractive = aInteractive; |
129 | 0 | mMakeWholeDocumentEditable = aMakeWholeDocumentEditable; |
130 | 0 |
|
131 | 0 | nsresult rv; |
132 | 0 | if (!mInteractive) { |
133 | 0 | rv = DisableJSAndPlugins(aWindow); |
134 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
135 | 0 | } |
136 | 0 |
|
137 | 0 | // Always remove existing editor |
138 | 0 | TearDownEditorOnWindow(aWindow); |
139 | 0 |
|
140 | 0 | // Tells embedder that startup is in progress |
141 | 0 | mEditorStatus = eEditorCreationInProgress; |
142 | 0 |
|
143 | 0 | //temporary to set editor type here. we will need different classes soon. |
144 | 0 | if (!aEditorType) |
145 | 0 | aEditorType = DEFAULT_EDITOR_TYPE; |
146 | 0 | mEditorType = aEditorType; |
147 | 0 |
|
148 | 0 | // if all this does is setup listeners and I don't need listeners, |
149 | 0 | // can't this step be ignored?? (based on aDoAfterURILoad) |
150 | 0 | rv = PrepareForEditing(window); |
151 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
152 | 0 |
|
153 | 0 | // set the flag on the docShell to say that it's editable |
154 | 0 | rv = docShell->MakeEditable(aDoAfterUriLoad); |
155 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
156 | 0 |
|
157 | 0 | // Setup commands common to plaintext and html editors, |
158 | 0 | // including the document creation observers |
159 | 0 | // the first is an editing controller |
160 | 0 | rv = SetupEditorCommandController(nsBaseCommandController::CreateEditingController, |
161 | 0 | aWindow, |
162 | 0 | static_cast<nsIEditingSession*>(this), |
163 | 0 | &mBaseCommandControllerId); |
164 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
165 | 0 |
|
166 | 0 | // The second is a controller to monitor doc state, |
167 | 0 | // such as creation and "dirty flag" |
168 | 0 | rv = SetupEditorCommandController(nsBaseCommandController::CreateHTMLEditorDocStateController, |
169 | 0 | aWindow, |
170 | 0 | static_cast<nsIEditingSession*>(this), |
171 | 0 | &mDocStateControllerId); |
172 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
173 | 0 |
|
174 | 0 | // aDoAfterUriLoad can be false only when making an existing window editable |
175 | 0 | if (!aDoAfterUriLoad) { |
176 | 0 | rv = SetupEditorOnWindow(aWindow); |
177 | 0 |
|
178 | 0 | // mEditorStatus is set to the error reason |
179 | 0 | // Since this is used only when editing an existing page, |
180 | 0 | // it IS ok to destroy current editor |
181 | 0 | if (NS_FAILED(rv)) { |
182 | 0 | TearDownEditorOnWindow(aWindow); |
183 | 0 | } |
184 | 0 | } |
185 | 0 | return rv; |
186 | 0 | } |
187 | | |
188 | | NS_IMETHODIMP |
189 | | nsEditingSession::DisableJSAndPlugins(mozIDOMWindowProxy* aWindow) |
190 | 0 | { |
191 | 0 | NS_ENSURE_TRUE(aWindow, NS_ERROR_FAILURE); |
192 | 0 | nsIDocShell *docShell = nsPIDOMWindowOuter::From(aWindow)->GetDocShell(); |
193 | 0 | NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); |
194 | 0 |
|
195 | 0 | bool tmp; |
196 | 0 | nsresult rv = docShell->GetAllowJavascript(&tmp); |
197 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
198 | 0 |
|
199 | 0 | mScriptsEnabled = tmp; |
200 | 0 |
|
201 | 0 | rv = docShell->SetAllowJavascript(false); |
202 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
203 | 0 |
|
204 | 0 | // Disable plugins in this document: |
205 | 0 | mPluginsEnabled = docShell->PluginsAllowedInCurrentDoc(); |
206 | 0 |
|
207 | 0 | rv = docShell->SetAllowPlugins(false); |
208 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
209 | 0 |
|
210 | 0 | mDisabledJSAndPlugins = true; |
211 | 0 |
|
212 | 0 | return NS_OK; |
213 | 0 | } |
214 | | |
215 | | NS_IMETHODIMP |
216 | | nsEditingSession::RestoreJSAndPlugins(mozIDOMWindowProxy* aWindow) |
217 | 0 | { |
218 | 0 | if (!mDisabledJSAndPlugins) { |
219 | 0 | return NS_OK; |
220 | 0 | } |
221 | 0 | |
222 | 0 | mDisabledJSAndPlugins = false; |
223 | 0 |
|
224 | 0 | NS_ENSURE_TRUE(aWindow, NS_ERROR_FAILURE); |
225 | 0 | nsIDocShell *docShell = nsPIDOMWindowOuter::From(aWindow)->GetDocShell(); |
226 | 0 | NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); |
227 | 0 |
|
228 | 0 | nsresult rv = docShell->SetAllowJavascript(mScriptsEnabled); |
229 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
230 | 0 |
|
231 | 0 | // Disable plugins in this document: |
232 | 0 | return docShell->SetAllowPlugins(mPluginsEnabled); |
233 | 0 | } |
234 | | |
235 | | NS_IMETHODIMP |
236 | | nsEditingSession::GetJsAndPluginsDisabled(bool *aResult) |
237 | 0 | { |
238 | 0 | NS_ENSURE_ARG_POINTER(aResult); |
239 | 0 | *aResult = mDisabledJSAndPlugins; |
240 | 0 | return NS_OK; |
241 | 0 | } |
242 | | |
243 | | /*--------------------------------------------------------------------------- |
244 | | |
245 | | WindowIsEditable |
246 | | |
247 | | boolean windowIsEditable (in nsIDOMWindow aWindow); |
248 | | ----------------------------------------------------------------------------*/ |
249 | | NS_IMETHODIMP |
250 | | nsEditingSession::WindowIsEditable(mozIDOMWindowProxy* aWindow, |
251 | | bool *outIsEditable) |
252 | 0 | { |
253 | 0 | NS_ENSURE_STATE(aWindow); |
254 | 0 | nsCOMPtr<nsIDocShell> docShell = nsPIDOMWindowOuter::From(aWindow)->GetDocShell(); |
255 | 0 | NS_ENSURE_STATE(docShell); |
256 | 0 |
|
257 | 0 | return docShell->GetEditable(outIsEditable); |
258 | 0 | } |
259 | | |
260 | | |
261 | | // These are MIME types that are automatically parsed as "text/plain" |
262 | | // and thus we can edit them as plaintext |
263 | | // Note: in older versions, we attempted to convert the mimetype of |
264 | | // the network channel for these and "text/xml" to "text/plain", |
265 | | // but further investigation reveals that strategy doesn't work |
266 | | const char* const gSupportedTextTypes[] = { |
267 | | "text/plain", |
268 | | "text/css", |
269 | | "text/rdf", |
270 | | "text/xsl", |
271 | | "text/javascript", // obsolete type |
272 | | "text/ecmascript", // obsolete type |
273 | | "application/javascript", |
274 | | "application/ecmascript", |
275 | | "application/x-javascript", // obsolete type |
276 | | "text/xul", // obsolete type |
277 | | "application/vnd.mozilla.xul+xml", |
278 | | nullptr // IMPORTANT! Null must be at end |
279 | | }; |
280 | | |
281 | | bool |
282 | | IsSupportedTextType(const char* aMIMEType) |
283 | 0 | { |
284 | 0 | NS_ENSURE_TRUE(aMIMEType, false); |
285 | 0 |
|
286 | 0 | for (size_t i = 0; gSupportedTextTypes[i]; ++i) { |
287 | 0 | if (!strcmp(gSupportedTextTypes[i], aMIMEType)) { |
288 | 0 | return true; |
289 | 0 | } |
290 | 0 | } |
291 | 0 |
|
292 | 0 | return false; |
293 | 0 | } |
294 | | |
295 | | /*--------------------------------------------------------------------------- |
296 | | |
297 | | SetupEditorOnWindow |
298 | | |
299 | | nsIEditor setupEditorOnWindow (in nsIDOMWindow aWindow); |
300 | | ----------------------------------------------------------------------------*/ |
301 | | NS_IMETHODIMP |
302 | | nsEditingSession::SetupEditorOnWindow(mozIDOMWindowProxy* aWindow) |
303 | 0 | { |
304 | 0 | mDoneSetup = true; |
305 | 0 |
|
306 | 0 | NS_ENSURE_TRUE(aWindow, NS_ERROR_FAILURE); |
307 | 0 | auto* window = nsPIDOMWindowOuter::From(aWindow); |
308 | 0 |
|
309 | 0 | nsresult rv; |
310 | 0 |
|
311 | 0 | //MIME CHECKING |
312 | 0 | //must get the content type |
313 | 0 | // Note: the doc gets this from the network channel during StartPageLoad, |
314 | 0 | // so we don't have to get it from there ourselves |
315 | 0 | nsAutoCString mimeCType; |
316 | 0 |
|
317 | 0 | //then lets check the mime type |
318 | 0 | if (nsCOMPtr<nsIDocument> doc = window->GetDoc()) { |
319 | 0 | nsAutoString mimeType; |
320 | 0 | doc->GetContentType(mimeType); |
321 | 0 | AppendUTF16toUTF8(mimeType, mimeCType); |
322 | 0 |
|
323 | 0 | if (IsSupportedTextType(mimeCType.get())) { |
324 | 0 | mEditorType.AssignLiteral("text"); |
325 | 0 | mimeCType = "text/plain"; |
326 | 0 | } else if (!mimeCType.EqualsLiteral("text/html") && |
327 | 0 | !mimeCType.EqualsLiteral("application/xhtml+xml")) { |
328 | 0 | // Neither an acceptable text or html type. |
329 | 0 | mEditorStatus = eEditorErrorCantEditMimeType; |
330 | 0 |
|
331 | 0 | // Turn editor into HTML -- we will load blank page later |
332 | 0 | mEditorType.AssignLiteral("html"); |
333 | 0 | mimeCType.AssignLiteral("text/html"); |
334 | 0 | } |
335 | 0 |
|
336 | 0 | // Flush out frame construction to make sure that the subframe's |
337 | 0 | // presshell is set up if it needs to be. |
338 | 0 | doc->FlushPendingNotifications(mozilla::FlushType::Frames); |
339 | 0 | if (mMakeWholeDocumentEditable) { |
340 | 0 | doc->SetEditableFlag(true); |
341 | 0 | nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(doc); |
342 | 0 | if (htmlDocument) { |
343 | 0 | // Enable usage of the execCommand API |
344 | 0 | htmlDocument->SetEditingState(nsIHTMLDocument::eDesignMode); |
345 | 0 | } |
346 | 0 | } |
347 | 0 | } |
348 | 0 | bool needHTMLController = false; |
349 | 0 |
|
350 | 0 | if (mEditorType.EqualsLiteral("textmail")) { |
351 | 0 | mEditorFlags = nsIPlaintextEditor::eEditorPlaintextMask | |
352 | 0 | nsIPlaintextEditor::eEditorEnableWrapHackMask | |
353 | 0 | nsIPlaintextEditor::eEditorMailMask; |
354 | 0 | } else if (mEditorType.EqualsLiteral("text")) { |
355 | 0 | mEditorFlags = nsIPlaintextEditor::eEditorPlaintextMask | |
356 | 0 | nsIPlaintextEditor::eEditorEnableWrapHackMask; |
357 | 0 | } else if (mEditorType.EqualsLiteral("htmlmail")) { |
358 | 0 | if (mimeCType.EqualsLiteral("text/html")) { |
359 | 0 | needHTMLController = true; |
360 | 0 | mEditorFlags = nsIPlaintextEditor::eEditorMailMask; |
361 | 0 | } else { |
362 | 0 | // Set the flags back to textplain. |
363 | 0 | mEditorFlags = nsIPlaintextEditor::eEditorPlaintextMask | |
364 | 0 | nsIPlaintextEditor::eEditorEnableWrapHackMask; |
365 | 0 | } |
366 | 0 | } else { |
367 | 0 | // Defaulted to html |
368 | 0 | needHTMLController = true; |
369 | 0 | } |
370 | 0 |
|
371 | 0 | if (mInteractive) { |
372 | 0 | mEditorFlags |= nsIPlaintextEditor::eEditorAllowInteraction; |
373 | 0 | } |
374 | 0 |
|
375 | 0 | // make the UI state maintainer |
376 | 0 | mComposerCommandsUpdater = new ComposerCommandsUpdater(); |
377 | 0 |
|
378 | 0 | // now init the state maintainer |
379 | 0 | // This allows notification of error state |
380 | 0 | // even if we don't create an editor |
381 | 0 | rv = mComposerCommandsUpdater->Init(window); |
382 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
383 | 0 |
|
384 | 0 | if (mEditorStatus != eEditorCreationInProgress) { |
385 | 0 | mComposerCommandsUpdater->NotifyDocumentCreated(); |
386 | 0 | return NS_ERROR_FAILURE; |
387 | 0 | } |
388 | 0 | |
389 | 0 | // Create editor and do other things |
390 | 0 | // only if we haven't found some error above, |
391 | 0 | nsCOMPtr<nsIDocShell> docShell = window->GetDocShell(); |
392 | 0 | NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); |
393 | 0 | nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell(); |
394 | 0 | NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); |
395 | 0 |
|
396 | 0 | if (!mInteractive) { |
397 | 0 | // Disable animation of images in this document: |
398 | 0 | nsPresContext* presContext = presShell->GetPresContext(); |
399 | 0 | NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE); |
400 | 0 |
|
401 | 0 | mImageAnimationMode = presContext->ImageAnimationMode(); |
402 | 0 | presContext->SetImageAnimationMode(imgIContainer::kDontAnimMode); |
403 | 0 | } |
404 | 0 |
|
405 | 0 | // Hide selection changes during initialization, in order to hide this |
406 | 0 | // from web pages. |
407 | 0 | RefPtr<nsFrameSelection> fs = presShell->FrameSelection(); |
408 | 0 | NS_ENSURE_TRUE(fs, NS_ERROR_FAILURE); |
409 | 0 | AutoHideSelectionChanges hideSelectionChanges(fs); |
410 | 0 |
|
411 | 0 | // create and set editor |
412 | 0 | // Try to reuse an existing editor |
413 | 0 | nsCOMPtr<nsIEditor> editor = do_QueryReferent(mExistingEditor); |
414 | 0 | RefPtr<HTMLEditor> htmlEditor = editor ? editor->AsHTMLEditor() : nullptr; |
415 | 0 | MOZ_ASSERT(!editor || htmlEditor); |
416 | 0 | if (htmlEditor) { |
417 | 0 | htmlEditor->PreDestroy(false); |
418 | 0 | } else { |
419 | 0 | htmlEditor = new HTMLEditor(); |
420 | 0 | mExistingEditor = |
421 | 0 | do_GetWeakReference(static_cast<nsIEditor*>(htmlEditor.get())); |
422 | 0 | } |
423 | 0 | // set the editor on the docShell. The docShell now owns it. |
424 | 0 | rv = docShell->SetHTMLEditor(htmlEditor); |
425 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
426 | 0 |
|
427 | 0 | // setup the HTML editor command controller |
428 | 0 | if (needHTMLController) { |
429 | 0 | // The third controller takes an nsIEditor as the context |
430 | 0 | rv = SetupEditorCommandController(nsBaseCommandController::CreateHTMLEditorController, |
431 | 0 | aWindow, |
432 | 0 | static_cast<nsIEditor*>(htmlEditor), |
433 | 0 | &mHTMLCommandControllerId); |
434 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
435 | 0 | } |
436 | 0 |
|
437 | 0 | // Set mimetype on editor |
438 | 0 | rv = htmlEditor->SetContentsMIMEType(mimeCType.get()); |
439 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
440 | 0 |
|
441 | 0 | nsCOMPtr<nsIContentViewer> contentViewer; |
442 | 0 | rv = docShell->GetContentViewer(getter_AddRefs(contentViewer)); |
443 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
444 | 0 | NS_ENSURE_TRUE(contentViewer, NS_ERROR_FAILURE); |
445 | 0 |
|
446 | 0 | nsCOMPtr<nsIDocument> doc = contentViewer->GetDocument(); |
447 | 0 | if (NS_WARN_IF(!doc)) { |
448 | 0 | return NS_ERROR_FAILURE; |
449 | 0 | } |
450 | 0 | |
451 | 0 | // Set up as a doc state listener |
452 | 0 | // Important! We must have this to broadcast the "obs_documentCreated" message |
453 | 0 | rv = htmlEditor->AddDocumentStateListener(mComposerCommandsUpdater); |
454 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
455 | 0 |
|
456 | 0 | rv = htmlEditor->Init(*doc, nullptr /* root content */, |
457 | 0 | nullptr, mEditorFlags, EmptyString()); |
458 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
459 | 0 |
|
460 | 0 | RefPtr<Selection> selection = htmlEditor->GetSelection(); |
461 | 0 | if (NS_WARN_IF(!selection)) { |
462 | 0 | return NS_ERROR_FAILURE; |
463 | 0 | } |
464 | 0 | |
465 | 0 | htmlEditor->SetComposerCommandsUpdater(mComposerCommandsUpdater); |
466 | 0 |
|
467 | 0 | // and as a transaction listener |
468 | 0 | MOZ_ASSERT(mComposerCommandsUpdater); |
469 | 0 | DebugOnly<bool> addedTransactionListener = |
470 | 0 | htmlEditor->AddTransactionListener(*mComposerCommandsUpdater); |
471 | 0 | NS_WARNING_ASSERTION(addedTransactionListener, |
472 | 0 | "Failed to add transaction listener to the editor"); |
473 | 0 |
|
474 | 0 | // Set context on all controllers to be the editor |
475 | 0 | rv = SetEditorOnControllers(aWindow, htmlEditor); |
476 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
477 | 0 |
|
478 | 0 | // Everything went fine! |
479 | 0 | mEditorStatus = eEditorOK; |
480 | 0 |
|
481 | 0 | // This will trigger documentCreation notification |
482 | 0 | return htmlEditor->PostCreate(); |
483 | 0 | } |
484 | | |
485 | | // Removes all listeners and controllers from aWindow and aEditor. |
486 | | void |
487 | | nsEditingSession::RemoveListenersAndControllers(nsPIDOMWindowOuter* aWindow, |
488 | | HTMLEditor* aHTMLEditor) |
489 | 0 | { |
490 | 0 | if (!mComposerCommandsUpdater || !aHTMLEditor) { |
491 | 0 | return; |
492 | 0 | } |
493 | 0 | |
494 | 0 | // Remove all the listeners |
495 | 0 | aHTMLEditor->SetComposerCommandsUpdater(nullptr); |
496 | 0 | aHTMLEditor->RemoveDocumentStateListener(mComposerCommandsUpdater); |
497 | 0 | DebugOnly<bool> removedTransactionListener = |
498 | 0 | aHTMLEditor->RemoveTransactionListener(*mComposerCommandsUpdater); |
499 | 0 | NS_WARNING_ASSERTION(removedTransactionListener, |
500 | 0 | "Failed to remove transaction listener from the editor"); |
501 | 0 |
|
502 | 0 | // Remove editor controllers from the window now that we're not |
503 | 0 | // editing in that window any more. |
504 | 0 | RemoveEditorControllers(aWindow); |
505 | 0 | } |
506 | | |
507 | | /*--------------------------------------------------------------------------- |
508 | | |
509 | | TearDownEditorOnWindow |
510 | | |
511 | | void tearDownEditorOnWindow (in nsIDOMWindow aWindow); |
512 | | ----------------------------------------------------------------------------*/ |
513 | | NS_IMETHODIMP |
514 | | nsEditingSession::TearDownEditorOnWindow(mozIDOMWindowProxy *aWindow) |
515 | 0 | { |
516 | 0 | if (!mDoneSetup) { |
517 | 0 | return NS_OK; |
518 | 0 | } |
519 | 0 | |
520 | 0 | NS_ENSURE_TRUE(aWindow, NS_ERROR_NULL_POINTER); |
521 | 0 |
|
522 | 0 | // Kill any existing reload timer |
523 | 0 | if (mLoadBlankDocTimer) { |
524 | 0 | mLoadBlankDocTimer->Cancel(); |
525 | 0 | mLoadBlankDocTimer = nullptr; |
526 | 0 | } |
527 | 0 |
|
528 | 0 | mDoneSetup = false; |
529 | 0 |
|
530 | 0 | // Check if we're turning off editing (from contentEditable or designMode). |
531 | 0 | auto* window = nsPIDOMWindowOuter::From(aWindow); |
532 | 0 |
|
533 | 0 | nsCOMPtr<nsIDocument> doc = window->GetDoc(); |
534 | 0 | nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(doc); |
535 | 0 | bool stopEditing = htmlDoc && htmlDoc->IsEditingOn(); |
536 | 0 | if (stopEditing) { |
537 | 0 | RemoveWebProgressListener(window); |
538 | 0 | } |
539 | 0 |
|
540 | 0 | nsCOMPtr<nsIDocShell> docShell = window->GetDocShell(); |
541 | 0 | NS_ENSURE_STATE(docShell); |
542 | 0 |
|
543 | 0 | RefPtr<HTMLEditor> htmlEditor = docShell->GetHTMLEditor(); |
544 | 0 | if (stopEditing) { |
545 | 0 | htmlDoc->TearingDownEditor(); |
546 | 0 | } |
547 | 0 |
|
548 | 0 | if (mComposerCommandsUpdater && htmlEditor) { |
549 | 0 | // Null out the editor on the controllers first to prevent their weak |
550 | 0 | // references from pointing to a destroyed editor. |
551 | 0 | SetEditorOnControllers(aWindow, nullptr); |
552 | 0 | } |
553 | 0 |
|
554 | 0 | // Null out the editor on the docShell to trigger PreDestroy which |
555 | 0 | // needs to happen before document state listeners are removed below. |
556 | 0 | docShell->SetEditor(nullptr); |
557 | 0 |
|
558 | 0 | RemoveListenersAndControllers(window, htmlEditor); |
559 | 0 |
|
560 | 0 | if (stopEditing) { |
561 | 0 | // Make things the way they were before we started editing. |
562 | 0 | RestoreJSAndPlugins(aWindow); |
563 | 0 | RestoreAnimationMode(window); |
564 | 0 |
|
565 | 0 | if (mMakeWholeDocumentEditable) { |
566 | 0 | doc->SetEditableFlag(false); |
567 | 0 | nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(doc); |
568 | 0 | if (htmlDocument) { |
569 | 0 | htmlDocument->SetEditingState(nsIHTMLDocument::eOff); |
570 | 0 | } |
571 | 0 | } |
572 | 0 | } |
573 | 0 |
|
574 | 0 | return NS_OK; |
575 | 0 | } |
576 | | |
577 | | /*--------------------------------------------------------------------------- |
578 | | |
579 | | GetEditorForFrame |
580 | | |
581 | | nsIEditor getEditorForFrame (in nsIDOMWindow aWindow); |
582 | | ----------------------------------------------------------------------------*/ |
583 | | NS_IMETHODIMP |
584 | | nsEditingSession::GetEditorForWindow(mozIDOMWindowProxy* aWindow, |
585 | | nsIEditor **outEditor) |
586 | 0 | { |
587 | 0 | if (NS_WARN_IF(!aWindow)) { |
588 | 0 | return NS_ERROR_INVALID_ARG; |
589 | 0 | } |
590 | 0 | nsCOMPtr<nsIEditor> editor = GetHTMLEditorForWindow(aWindow); |
591 | 0 | editor.forget(outEditor); |
592 | 0 | return NS_OK; |
593 | 0 | } |
594 | | |
595 | | /*--------------------------------------------------------------------------- |
596 | | |
597 | | OnStateChange |
598 | | |
599 | | ----------------------------------------------------------------------------*/ |
600 | | NS_IMETHODIMP |
601 | | nsEditingSession::OnStateChange(nsIWebProgress *aWebProgress, |
602 | | nsIRequest *aRequest, |
603 | | uint32_t aStateFlags, nsresult aStatus) |
604 | 0 | { |
605 | 0 |
|
606 | | #ifdef NOISY_DOC_LOADING |
607 | | nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest)); |
608 | | if (channel) { |
609 | | nsAutoCString contentType; |
610 | | channel->GetContentType(contentType); |
611 | | if (!contentType.IsEmpty()) { |
612 | | printf(" ++++++ MIMETYPE = %s\n", contentType.get()); |
613 | | } |
614 | | } |
615 | | #endif |
616 | |
|
617 | 0 | // |
618 | 0 | // A Request has started... |
619 | 0 | // |
620 | 0 | if (aStateFlags & nsIWebProgressListener::STATE_START) { |
621 | | #ifdef NOISY_DOC_LOADING |
622 | | { |
623 | | nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest)); |
624 | | if (channel) { |
625 | | nsCOMPtr<nsIURI> uri; |
626 | | channel->GetURI(getter_AddRefs(uri)); |
627 | | if (uri) { |
628 | | nsCString spec; |
629 | | uri->GetSpec(spec); |
630 | | printf(" **** STATE_START: CHANNEL URI=%s, flags=%x\n", |
631 | | spec.get(), aStateFlags); |
632 | | } |
633 | | } else { |
634 | | printf(" STATE_START: NO CHANNEL flags=%x\n", aStateFlags); |
635 | | } |
636 | | } |
637 | | #endif |
638 | | // Page level notification... |
639 | 0 | if (aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK) { |
640 | 0 | nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest)); |
641 | 0 | StartPageLoad(channel); |
642 | | #ifdef NOISY_DOC_LOADING |
643 | | printf("STATE_START & STATE_IS_NETWORK flags=%x\n", aStateFlags); |
644 | | #endif |
645 | | } |
646 | 0 |
|
647 | 0 | // Document level notification... |
648 | 0 | if (aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT && |
649 | 0 | !(aStateFlags & nsIWebProgressListener::STATE_RESTORING)) { |
650 | | #ifdef NOISY_DOC_LOADING |
651 | | printf("STATE_START & STATE_IS_DOCUMENT flags=%x\n", aStateFlags); |
652 | | #endif |
653 | |
|
654 | 0 | bool progressIsForTargetDocument = |
655 | 0 | IsProgressForTargetDocument(aWebProgress); |
656 | 0 |
|
657 | 0 | if (progressIsForTargetDocument) { |
658 | 0 | nsCOMPtr<mozIDOMWindowProxy> window; |
659 | 0 | aWebProgress->GetDOMWindow(getter_AddRefs(window)); |
660 | 0 |
|
661 | 0 | auto* piWindow = nsPIDOMWindowOuter::From(window); |
662 | 0 | nsCOMPtr<nsIDocument> doc = piWindow->GetDoc(); |
663 | 0 | nsHTMLDocument* htmlDoc = doc && doc->IsHTMLOrXHTML() |
664 | 0 | ? doc->AsHTMLDocument() : nullptr; |
665 | 0 | if (htmlDoc && htmlDoc->IsWriting()) { |
666 | 0 | nsAutoString designMode; |
667 | 0 | htmlDoc->GetDesignMode(designMode); |
668 | 0 |
|
669 | 0 | if (designMode.EqualsLiteral("on")) { |
670 | 0 | // This notification is for data coming in through |
671 | 0 | // document.open/write/close(), ignore it. |
672 | 0 |
|
673 | 0 | return NS_OK; |
674 | 0 | } |
675 | 0 | } |
676 | 0 | |
677 | 0 | mCanCreateEditor = true; |
678 | 0 | StartDocumentLoad(aWebProgress, progressIsForTargetDocument); |
679 | 0 | } |
680 | 0 | } |
681 | 0 | } |
682 | 0 | // |
683 | 0 | // A Request is being processed |
684 | 0 | // |
685 | 0 | else if (aStateFlags & nsIWebProgressListener::STATE_TRANSFERRING) { |
686 | 0 | if (aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT) { |
687 | 0 | // document transfer started |
688 | 0 | } |
689 | 0 | } |
690 | 0 | // |
691 | 0 | // Got a redirection |
692 | 0 | // |
693 | 0 | else if (aStateFlags & nsIWebProgressListener::STATE_REDIRECTING) { |
694 | 0 | if (aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT) { |
695 | 0 | // got a redirect |
696 | 0 | } |
697 | 0 | } |
698 | 0 | // |
699 | 0 | // A network or document Request has finished... |
700 | 0 | // |
701 | 0 | else if (aStateFlags & nsIWebProgressListener::STATE_STOP) { |
702 | | #ifdef NOISY_DOC_LOADING |
703 | | { |
704 | | nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest)); |
705 | | if (channel) { |
706 | | nsCOMPtr<nsIURI> uri; |
707 | | channel->GetURI(getter_AddRefs(uri)); |
708 | | if (uri) { |
709 | | nsCString spec; |
710 | | uri->GetSpec(spec); |
711 | | printf(" **** STATE_STOP: CHANNEL URI=%s, flags=%x\n", |
712 | | spec.get(), aStateFlags); |
713 | | } |
714 | | } else { |
715 | | printf(" STATE_STOP: NO CHANNEL flags=%x\n", aStateFlags); |
716 | | } |
717 | | } |
718 | | #endif |
719 | |
|
720 | 0 | // Document level notification... |
721 | 0 | if (aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT) { |
722 | 0 | nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest); |
723 | 0 | EndDocumentLoad(aWebProgress, channel, aStatus, |
724 | 0 | IsProgressForTargetDocument(aWebProgress)); |
725 | | #ifdef NOISY_DOC_LOADING |
726 | | printf("STATE_STOP & STATE_IS_DOCUMENT flags=%x\n", aStateFlags); |
727 | | #endif |
728 | | } |
729 | 0 |
|
730 | 0 | // Page level notification... |
731 | 0 | if (aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK) { |
732 | 0 | nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest); |
733 | 0 | (void)EndPageLoad(aWebProgress, channel, aStatus); |
734 | | #ifdef NOISY_DOC_LOADING |
735 | | printf("STATE_STOP & STATE_IS_NETWORK flags=%x\n", aStateFlags); |
736 | | #endif |
737 | | } |
738 | 0 | } |
739 | 0 |
|
740 | 0 | return NS_OK; |
741 | 0 | } |
742 | | |
743 | | /*--------------------------------------------------------------------------- |
744 | | |
745 | | OnProgressChange |
746 | | |
747 | | ----------------------------------------------------------------------------*/ |
748 | | NS_IMETHODIMP |
749 | | nsEditingSession::OnProgressChange(nsIWebProgress *aWebProgress, |
750 | | nsIRequest *aRequest, |
751 | | int32_t aCurSelfProgress, |
752 | | int32_t aMaxSelfProgress, |
753 | | int32_t aCurTotalProgress, |
754 | | int32_t aMaxTotalProgress) |
755 | 0 | { |
756 | 0 | MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)"); |
757 | 0 | return NS_OK; |
758 | 0 | } |
759 | | |
760 | | /*--------------------------------------------------------------------------- |
761 | | |
762 | | OnLocationChange |
763 | | |
764 | | ----------------------------------------------------------------------------*/ |
765 | | NS_IMETHODIMP |
766 | | nsEditingSession::OnLocationChange(nsIWebProgress *aWebProgress, |
767 | | nsIRequest *aRequest, nsIURI *aURI, |
768 | | uint32_t aFlags) |
769 | 0 | { |
770 | 0 | nsCOMPtr<mozIDOMWindowProxy> domWindow; |
771 | 0 | nsresult rv = aWebProgress->GetDOMWindow(getter_AddRefs(domWindow)); |
772 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
773 | 0 |
|
774 | 0 | auto* piWindow = nsPIDOMWindowOuter::From(domWindow); |
775 | 0 |
|
776 | 0 | nsCOMPtr<nsIDocument> doc = piWindow->GetDoc(); |
777 | 0 | NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); |
778 | 0 |
|
779 | 0 | doc->SetDocumentURI(aURI); |
780 | 0 |
|
781 | 0 | // Notify the location-changed observer that |
782 | 0 | // the document URL has changed |
783 | 0 | nsIDocShell *docShell = piWindow->GetDocShell(); |
784 | 0 | NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); |
785 | 0 |
|
786 | 0 | nsCOMPtr<nsICommandManager> commandManager = docShell->GetCommandManager(); |
787 | 0 | nsCOMPtr<nsPICommandUpdater> commandUpdater = |
788 | 0 | do_QueryInterface(commandManager); |
789 | 0 | NS_ENSURE_TRUE(commandUpdater, NS_ERROR_FAILURE); |
790 | 0 |
|
791 | 0 | return commandUpdater->CommandStatusChanged("obs_documentLocationChanged"); |
792 | 0 | } |
793 | | |
794 | | /*--------------------------------------------------------------------------- |
795 | | |
796 | | OnStatusChange |
797 | | |
798 | | ----------------------------------------------------------------------------*/ |
799 | | NS_IMETHODIMP |
800 | | nsEditingSession::OnStatusChange(nsIWebProgress *aWebProgress, |
801 | | nsIRequest *aRequest, |
802 | | nsresult aStatus, |
803 | | const char16_t *aMessage) |
804 | 0 | { |
805 | 0 | MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)"); |
806 | 0 | return NS_OK; |
807 | 0 | } |
808 | | |
809 | | /*--------------------------------------------------------------------------- |
810 | | |
811 | | OnSecurityChange |
812 | | |
813 | | ----------------------------------------------------------------------------*/ |
814 | | NS_IMETHODIMP |
815 | | nsEditingSession::OnSecurityChange(nsIWebProgress *aWebProgress, |
816 | | nsIRequest *aRequest, uint32_t state) |
817 | 0 | { |
818 | 0 | MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)"); |
819 | 0 | return NS_OK; |
820 | 0 | } |
821 | | |
822 | | |
823 | | /*--------------------------------------------------------------------------- |
824 | | |
825 | | IsProgressForTargetDocument |
826 | | |
827 | | Check that this notification is for our document. |
828 | | ----------------------------------------------------------------------------*/ |
829 | | |
830 | | bool |
831 | | nsEditingSession::IsProgressForTargetDocument(nsIWebProgress *aWebProgress) |
832 | 0 | { |
833 | 0 | nsCOMPtr<nsIWebProgress> editedWebProgress = do_QueryReferent(mDocShell); |
834 | 0 | return editedWebProgress == aWebProgress; |
835 | 0 | } |
836 | | |
837 | | |
838 | | /*--------------------------------------------------------------------------- |
839 | | |
840 | | GetEditorStatus |
841 | | |
842 | | Called during GetCommandStateParams("obs_documentCreated"...) |
843 | | to determine if editor was created and document |
844 | | was loaded successfully |
845 | | ----------------------------------------------------------------------------*/ |
846 | | NS_IMETHODIMP |
847 | | nsEditingSession::GetEditorStatus(uint32_t *aStatus) |
848 | 0 | { |
849 | 0 | NS_ENSURE_ARG_POINTER(aStatus); |
850 | 0 | *aStatus = mEditorStatus; |
851 | 0 | return NS_OK; |
852 | 0 | } |
853 | | |
854 | | /*--------------------------------------------------------------------------- |
855 | | |
856 | | StartDocumentLoad |
857 | | |
858 | | Called on start of load in a single frame |
859 | | ----------------------------------------------------------------------------*/ |
860 | | nsresult |
861 | | nsEditingSession::StartDocumentLoad(nsIWebProgress *aWebProgress, |
862 | | bool aIsToBeMadeEditable) |
863 | 0 | { |
864 | | #ifdef NOISY_DOC_LOADING |
865 | | printf("======= StartDocumentLoad ========\n"); |
866 | | #endif |
867 | |
|
868 | 0 | NS_ENSURE_ARG_POINTER(aWebProgress); |
869 | 0 |
|
870 | 0 | if (aIsToBeMadeEditable) { |
871 | 0 | mEditorStatus = eEditorCreationInProgress; |
872 | 0 | } |
873 | 0 |
|
874 | 0 | return NS_OK; |
875 | 0 | } |
876 | | |
877 | | /*--------------------------------------------------------------------------- |
878 | | |
879 | | EndDocumentLoad |
880 | | |
881 | | Called on end of load in a single frame |
882 | | ----------------------------------------------------------------------------*/ |
883 | | nsresult |
884 | | nsEditingSession::EndDocumentLoad(nsIWebProgress *aWebProgress, |
885 | | nsIChannel* aChannel, nsresult aStatus, |
886 | | bool aIsToBeMadeEditable) |
887 | 0 | { |
888 | 0 | NS_ENSURE_ARG_POINTER(aWebProgress); |
889 | 0 |
|
890 | | #ifdef NOISY_DOC_LOADING |
891 | | printf("======= EndDocumentLoad ========\n"); |
892 | | printf("with status %d, ", aStatus); |
893 | | nsCOMPtr<nsIURI> uri; |
894 | | nsCString spec; |
895 | | if (NS_SUCCEEDED(aChannel->GetURI(getter_AddRefs(uri)))) { |
896 | | uri->GetSpec(spec); |
897 | | printf(" uri %s\n", spec.get()); |
898 | | } |
899 | | #endif |
900 | |
|
901 | 0 | // We want to call the base class EndDocumentLoad, |
902 | 0 | // but avoid some of the stuff |
903 | 0 | // that nsDocShell does (need to refactor). |
904 | 0 |
|
905 | 0 | // OK, time to make an editor on this document |
906 | 0 | nsCOMPtr<mozIDOMWindowProxy> domWindow; |
907 | 0 | aWebProgress->GetDOMWindow(getter_AddRefs(domWindow)); |
908 | 0 | NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE); |
909 | 0 |
|
910 | 0 | // Set the error state -- we will create an editor |
911 | 0 | // anyway and load empty doc later |
912 | 0 | if (aIsToBeMadeEditable && aStatus == NS_ERROR_FILE_NOT_FOUND) { |
913 | 0 | mEditorStatus = eEditorErrorFileNotFound; |
914 | 0 | } |
915 | 0 |
|
916 | 0 | nsIDocShell *docShell = nsPIDOMWindowOuter::From(domWindow)->GetDocShell(); |
917 | 0 | NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); // better error handling? |
918 | 0 |
|
919 | 0 | // cancel refresh from meta tags |
920 | 0 | // we need to make sure that all pages in editor (whether editable or not) |
921 | 0 | // can't refresh contents being edited |
922 | 0 | nsCOMPtr<nsIRefreshURI> refreshURI = do_QueryInterface(docShell); |
923 | 0 | if (refreshURI) { |
924 | 0 | refreshURI->CancelRefreshURITimers(); |
925 | 0 | } |
926 | 0 |
|
927 | 0 | nsresult rv = NS_OK; |
928 | 0 |
|
929 | 0 | // did someone set the flag to make this shell editable? |
930 | 0 | if (aIsToBeMadeEditable && mCanCreateEditor) { |
931 | 0 | bool makeEditable; |
932 | 0 | docShell->GetEditable(&makeEditable); |
933 | 0 |
|
934 | 0 | if (makeEditable) { |
935 | 0 | // To keep pre Gecko 1.9 behavior, setup editor always when |
936 | 0 | // mMakeWholeDocumentEditable. |
937 | 0 | bool needsSetup = false; |
938 | 0 | if (mMakeWholeDocumentEditable) { |
939 | 0 | needsSetup = true; |
940 | 0 | } else { |
941 | 0 | // do we already have an editor here? |
942 | 0 | needsSetup = !docShell->GetHTMLEditor(); |
943 | 0 | } |
944 | 0 |
|
945 | 0 | if (needsSetup) { |
946 | 0 | mCanCreateEditor = false; |
947 | 0 | rv = SetupEditorOnWindow(domWindow); |
948 | 0 | if (NS_FAILED(rv)) { |
949 | 0 | // If we had an error, setup timer to load a blank page later |
950 | 0 | if (mLoadBlankDocTimer) { |
951 | 0 | // Must cancel previous timer? |
952 | 0 | mLoadBlankDocTimer->Cancel(); |
953 | 0 | mLoadBlankDocTimer = nullptr; |
954 | 0 | } |
955 | 0 |
|
956 | 0 | rv = NS_NewTimerWithFuncCallback( |
957 | 0 | getter_AddRefs(mLoadBlankDocTimer), |
958 | 0 | nsEditingSession::TimerCallback, |
959 | 0 | static_cast<void*>(mDocShell.get()), |
960 | 0 | 10, |
961 | 0 | nsITimer::TYPE_ONE_SHOT, |
962 | 0 | "nsEditingSession::EndDocumentLoad"); |
963 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
964 | 0 |
|
965 | 0 | mEditorStatus = eEditorCreationInProgress; |
966 | 0 | } |
967 | 0 | } |
968 | 0 | } |
969 | 0 | } |
970 | 0 | return rv; |
971 | 0 | } |
972 | | |
973 | | |
974 | | void |
975 | | nsEditingSession::TimerCallback(nsITimer* aTimer, void* aClosure) |
976 | 0 | { |
977 | 0 | nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(static_cast<nsIWeakReference*> (aClosure)); |
978 | 0 | if (docShell) { |
979 | 0 | nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(docShell)); |
980 | 0 | if (webNav) { |
981 | 0 | webNav->LoadURI(NS_LITERAL_STRING("about:blank"), 0, nullptr, nullptr, |
982 | 0 | nullptr, nsContentUtils::GetSystemPrincipal()); |
983 | 0 | } |
984 | 0 | } |
985 | 0 | } |
986 | | |
987 | | /*--------------------------------------------------------------------------- |
988 | | |
989 | | StartPageLoad |
990 | | |
991 | | Called on start load of the entire page (incl. subframes) |
992 | | ----------------------------------------------------------------------------*/ |
993 | | nsresult |
994 | | nsEditingSession::StartPageLoad(nsIChannel *aChannel) |
995 | 0 | { |
996 | | #ifdef NOISY_DOC_LOADING |
997 | | printf("======= StartPageLoad ========\n"); |
998 | | #endif |
999 | | return NS_OK; |
1000 | 0 | } |
1001 | | |
1002 | | /*--------------------------------------------------------------------------- |
1003 | | |
1004 | | EndPageLoad |
1005 | | |
1006 | | Called on end load of the entire page (incl. subframes) |
1007 | | ----------------------------------------------------------------------------*/ |
1008 | | nsresult |
1009 | | nsEditingSession::EndPageLoad(nsIWebProgress *aWebProgress, |
1010 | | nsIChannel* aChannel, nsresult aStatus) |
1011 | 0 | { |
1012 | | #ifdef NOISY_DOC_LOADING |
1013 | | printf("======= EndPageLoad ========\n"); |
1014 | | printf(" with status %d, ", aStatus); |
1015 | | nsCOMPtr<nsIURI> uri; |
1016 | | nsCString spec; |
1017 | | if (NS_SUCCEEDED(aChannel->GetURI(getter_AddRefs(uri)))) { |
1018 | | uri->GetSpec(spec); |
1019 | | printf("uri %s\n", spec.get()); |
1020 | | } |
1021 | | |
1022 | | nsAutoCString contentType; |
1023 | | aChannel->GetContentType(contentType); |
1024 | | if (!contentType.IsEmpty()) { |
1025 | | printf(" flags = %d, status = %d, MIMETYPE = %s\n", |
1026 | | mEditorFlags, mEditorStatus, contentType.get()); |
1027 | | } |
1028 | | #endif |
1029 | |
|
1030 | 0 | // Set the error state -- we will create an editor anyway |
1031 | 0 | // and load empty doc later |
1032 | 0 | if (aStatus == NS_ERROR_FILE_NOT_FOUND) { |
1033 | 0 | mEditorStatus = eEditorErrorFileNotFound; |
1034 | 0 | } |
1035 | 0 |
|
1036 | 0 | nsCOMPtr<mozIDOMWindowProxy> domWindow; |
1037 | 0 | aWebProgress->GetDOMWindow(getter_AddRefs(domWindow)); |
1038 | 0 |
|
1039 | 0 | nsIDocShell *docShell = |
1040 | 0 | domWindow ? nsPIDOMWindowOuter::From(domWindow)->GetDocShell() : nullptr; |
1041 | 0 | NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); |
1042 | 0 |
|
1043 | 0 | // cancel refresh from meta tags |
1044 | 0 | // we need to make sure that all pages in editor (whether editable or not) |
1045 | 0 | // can't refresh contents being edited |
1046 | 0 | nsCOMPtr<nsIRefreshURI> refreshURI = do_QueryInterface(docShell); |
1047 | 0 | if (refreshURI) { |
1048 | 0 | refreshURI->CancelRefreshURITimers(); |
1049 | 0 | } |
1050 | 0 |
|
1051 | | #if 0 |
1052 | | // Shouldn't we do this when we want to edit sub-frames? |
1053 | | return MakeWindowEditable(domWindow, "html", false, mInteractive); |
1054 | | #else |
1055 | | return NS_OK; |
1056 | 0 | #endif |
1057 | 0 | } |
1058 | | |
1059 | | /*--------------------------------------------------------------------------- |
1060 | | |
1061 | | PrepareForEditing |
1062 | | |
1063 | | Set up this editing session for one or more editors |
1064 | | ----------------------------------------------------------------------------*/ |
1065 | | nsresult |
1066 | | nsEditingSession::PrepareForEditing(nsPIDOMWindowOuter* aWindow) |
1067 | 0 | { |
1068 | 0 | if (mProgressListenerRegistered) { |
1069 | 0 | return NS_OK; |
1070 | 0 | } |
1071 | 0 | |
1072 | 0 | nsIDocShell *docShell = aWindow ? aWindow->GetDocShell() : nullptr; |
1073 | 0 |
|
1074 | 0 | // register callback |
1075 | 0 | nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell); |
1076 | 0 | NS_ENSURE_TRUE(webProgress, NS_ERROR_FAILURE); |
1077 | 0 |
|
1078 | 0 | nsresult rv = |
1079 | 0 | webProgress->AddProgressListener(this, |
1080 | 0 | (nsIWebProgress::NOTIFY_STATE_NETWORK | |
1081 | 0 | nsIWebProgress::NOTIFY_STATE_DOCUMENT | |
1082 | 0 | nsIWebProgress::NOTIFY_LOCATION)); |
1083 | 0 |
|
1084 | 0 | mProgressListenerRegistered = NS_SUCCEEDED(rv); |
1085 | 0 |
|
1086 | 0 | return rv; |
1087 | 0 | } |
1088 | | |
1089 | | /*--------------------------------------------------------------------------- |
1090 | | |
1091 | | SetupEditorCommandController |
1092 | | |
1093 | | Create a command controller, append to controllers, |
1094 | | get and return the controller ID, and set the context |
1095 | | ----------------------------------------------------------------------------*/ |
1096 | | nsresult |
1097 | | nsEditingSession::SetupEditorCommandController( |
1098 | | nsEditingSession::ControllerCreatorFn aControllerCreatorFn, |
1099 | | mozIDOMWindowProxy* aWindow, |
1100 | | nsISupports* aContext, |
1101 | | uint32_t* aControllerId) |
1102 | 0 | { |
1103 | 0 | NS_ENSURE_ARG_POINTER(aControllerCreatorFn); |
1104 | 0 | NS_ENSURE_ARG_POINTER(aWindow); |
1105 | 0 | NS_ENSURE_ARG_POINTER(aContext); |
1106 | 0 | NS_ENSURE_ARG_POINTER(aControllerId); |
1107 | 0 |
|
1108 | 0 | auto* piWindow = nsPIDOMWindowOuter::From(aWindow); |
1109 | 0 | MOZ_ASSERT(piWindow); |
1110 | 0 |
|
1111 | 0 | nsCOMPtr<nsIControllers> controllers; |
1112 | 0 | nsresult rv = piWindow->GetControllers(getter_AddRefs(controllers)); |
1113 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1114 | 0 |
|
1115 | 0 | // We only have to create each singleton controller once |
1116 | 0 | // We know this has happened once we have a controllerId value |
1117 | 0 | if (!*aControllerId) { |
1118 | 0 | nsCOMPtr<nsIController> controller = aControllerCreatorFn(); |
1119 | 0 | NS_ENSURE_TRUE(controller, NS_ERROR_FAILURE); |
1120 | 0 |
|
1121 | 0 | // We must insert at head of the list to be sure our |
1122 | 0 | // controller is found before other implementations |
1123 | 0 | // (e.g., not-implemented versions by browser) |
1124 | 0 | rv = controllers->InsertControllerAt(0, controller); |
1125 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1126 | 0 |
|
1127 | 0 | // Remember the ID for the controller |
1128 | 0 | rv = controllers->GetControllerId(controller, aControllerId); |
1129 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1130 | 0 | } |
1131 | 0 |
|
1132 | 0 | // Set the context |
1133 | 0 | return SetContextOnControllerById(controllers, aContext, *aControllerId); |
1134 | 0 | } |
1135 | | |
1136 | | /*--------------------------------------------------------------------------- |
1137 | | |
1138 | | SetEditorOnControllers |
1139 | | |
1140 | | Set the editor on the controller(s) for this window |
1141 | | ----------------------------------------------------------------------------*/ |
1142 | | NS_IMETHODIMP |
1143 | | nsEditingSession::SetEditorOnControllers(mozIDOMWindowProxy* aWindow, |
1144 | | nsIEditor* aEditor) |
1145 | 0 | { |
1146 | 0 | NS_ENSURE_TRUE(aWindow, NS_ERROR_NULL_POINTER); |
1147 | 0 |
|
1148 | 0 | auto* piWindow = nsPIDOMWindowOuter::From(aWindow); |
1149 | 0 |
|
1150 | 0 | nsCOMPtr<nsIControllers> controllers; |
1151 | 0 | nsresult rv = piWindow->GetControllers(getter_AddRefs(controllers)); |
1152 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1153 | 0 |
|
1154 | 0 | nsCOMPtr<nsISupports> editorAsISupports = static_cast<nsISupports*>(aEditor); |
1155 | 0 | if (mBaseCommandControllerId) { |
1156 | 0 | rv = SetContextOnControllerById(controllers, editorAsISupports, |
1157 | 0 | mBaseCommandControllerId); |
1158 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1159 | 0 | } |
1160 | 0 |
|
1161 | 0 | if (mDocStateControllerId) { |
1162 | 0 | rv = SetContextOnControllerById(controllers, editorAsISupports, |
1163 | 0 | mDocStateControllerId); |
1164 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1165 | 0 | } |
1166 | 0 |
|
1167 | 0 | if (mHTMLCommandControllerId) { |
1168 | 0 | rv = SetContextOnControllerById(controllers, editorAsISupports, |
1169 | 0 | mHTMLCommandControllerId); |
1170 | 0 | } |
1171 | 0 |
|
1172 | 0 | return rv; |
1173 | 0 | } |
1174 | | |
1175 | | nsresult |
1176 | | nsEditingSession::SetContextOnControllerById(nsIControllers* aControllers, |
1177 | | nsISupports* aContext, |
1178 | | uint32_t aID) |
1179 | 0 | { |
1180 | 0 | NS_ENSURE_ARG_POINTER(aControllers); |
1181 | 0 |
|
1182 | 0 | // aContext can be null (when destroying editor) |
1183 | 0 | nsCOMPtr<nsIController> controller; |
1184 | 0 | aControllers->GetControllerById(aID, getter_AddRefs(controller)); |
1185 | 0 |
|
1186 | 0 | // ok with nil controller |
1187 | 0 | nsCOMPtr<nsIControllerContext> editorController = |
1188 | 0 | do_QueryInterface(controller); |
1189 | 0 | NS_ENSURE_TRUE(editorController, NS_ERROR_FAILURE); |
1190 | 0 |
|
1191 | 0 | return editorController->SetCommandContext(aContext); |
1192 | 0 | } |
1193 | | |
1194 | | void |
1195 | | nsEditingSession::RemoveEditorControllers(nsPIDOMWindowOuter* aWindow) |
1196 | 0 | { |
1197 | 0 | // Remove editor controllers from the aWindow, call when we're |
1198 | 0 | // tearing down/detaching editor. |
1199 | 0 |
|
1200 | 0 | nsCOMPtr<nsIControllers> controllers; |
1201 | 0 | if (aWindow) { |
1202 | 0 | aWindow->GetControllers(getter_AddRefs(controllers)); |
1203 | 0 | } |
1204 | 0 |
|
1205 | 0 | if (controllers) { |
1206 | 0 | nsCOMPtr<nsIController> controller; |
1207 | 0 | if (mBaseCommandControllerId) { |
1208 | 0 | controllers->GetControllerById(mBaseCommandControllerId, |
1209 | 0 | getter_AddRefs(controller)); |
1210 | 0 | if (controller) { |
1211 | 0 | controllers->RemoveController(controller); |
1212 | 0 | } |
1213 | 0 | } |
1214 | 0 |
|
1215 | 0 | if (mDocStateControllerId) { |
1216 | 0 | controllers->GetControllerById(mDocStateControllerId, |
1217 | 0 | getter_AddRefs(controller)); |
1218 | 0 | if (controller) { |
1219 | 0 | controllers->RemoveController(controller); |
1220 | 0 | } |
1221 | 0 | } |
1222 | 0 |
|
1223 | 0 | if (mHTMLCommandControllerId) { |
1224 | 0 | controllers->GetControllerById(mHTMLCommandControllerId, |
1225 | 0 | getter_AddRefs(controller)); |
1226 | 0 | if (controller) { |
1227 | 0 | controllers->RemoveController(controller); |
1228 | 0 | } |
1229 | 0 | } |
1230 | 0 | } |
1231 | 0 |
|
1232 | 0 | // Clear IDs to trigger creation of new controllers. |
1233 | 0 | mBaseCommandControllerId = 0; |
1234 | 0 | mDocStateControllerId = 0; |
1235 | 0 | mHTMLCommandControllerId = 0; |
1236 | 0 | } |
1237 | | |
1238 | | void |
1239 | | nsEditingSession::RemoveWebProgressListener(nsPIDOMWindowOuter* aWindow) |
1240 | 0 | { |
1241 | 0 | nsIDocShell *docShell = aWindow ? aWindow->GetDocShell() : nullptr; |
1242 | 0 | nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell); |
1243 | 0 | if (webProgress) { |
1244 | 0 | webProgress->RemoveProgressListener(this); |
1245 | 0 | mProgressListenerRegistered = false; |
1246 | 0 | } |
1247 | 0 | } |
1248 | | |
1249 | | void |
1250 | | nsEditingSession::RestoreAnimationMode(nsPIDOMWindowOuter* aWindow) |
1251 | 0 | { |
1252 | 0 | if (mInteractive) { |
1253 | 0 | return; |
1254 | 0 | } |
1255 | 0 | |
1256 | 0 | nsCOMPtr<nsIDocShell> docShell = aWindow ? aWindow->GetDocShell() : nullptr; |
1257 | 0 | NS_ENSURE_TRUE_VOID(docShell); |
1258 | 0 | nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell(); |
1259 | 0 | NS_ENSURE_TRUE_VOID(presShell); |
1260 | 0 | nsPresContext* presContext = presShell->GetPresContext(); |
1261 | 0 | NS_ENSURE_TRUE_VOID(presContext); |
1262 | 0 |
|
1263 | 0 | presContext->SetImageAnimationMode(mImageAnimationMode); |
1264 | 0 | } |
1265 | | |
1266 | | nsresult |
1267 | | nsEditingSession::DetachFromWindow(mozIDOMWindowProxy* aWindow) |
1268 | 0 | { |
1269 | 0 | NS_ENSURE_TRUE(mDoneSetup, NS_OK); |
1270 | 0 |
|
1271 | 0 | NS_ASSERTION(mComposerCommandsUpdater, |
1272 | 0 | "mComposerCommandsUpdater should exist."); |
1273 | 0 |
|
1274 | 0 | // Kill any existing reload timer |
1275 | 0 | if (mLoadBlankDocTimer) { |
1276 | 0 | mLoadBlankDocTimer->Cancel(); |
1277 | 0 | mLoadBlankDocTimer = nullptr; |
1278 | 0 | } |
1279 | 0 |
|
1280 | 0 | auto* window = nsPIDOMWindowOuter::From(aWindow); |
1281 | 0 |
|
1282 | 0 | // Remove controllers, webprogress listener, and otherwise |
1283 | 0 | // make things the way they were before we started editing. |
1284 | 0 | RemoveEditorControllers(window); |
1285 | 0 | RemoveWebProgressListener(window); |
1286 | 0 | RestoreJSAndPlugins(aWindow); |
1287 | 0 | RestoreAnimationMode(window); |
1288 | 0 |
|
1289 | 0 | // Kill our weak reference to our original window, in case |
1290 | 0 | // it changes on restore, or otherwise dies. |
1291 | 0 | mDocShell = nullptr; |
1292 | 0 |
|
1293 | 0 | return NS_OK; |
1294 | 0 | } |
1295 | | |
1296 | | nsresult |
1297 | | nsEditingSession::ReattachToWindow(mozIDOMWindowProxy* aWindow) |
1298 | 0 | { |
1299 | 0 | NS_ENSURE_TRUE(mDoneSetup, NS_OK); |
1300 | 0 | NS_ENSURE_TRUE(aWindow, NS_ERROR_FAILURE); |
1301 | 0 |
|
1302 | 0 | NS_ASSERTION(mComposerCommandsUpdater, |
1303 | 0 | "mComposerCommandsUpdater should exist."); |
1304 | 0 |
|
1305 | 0 | // Imitate nsEditorDocShell::MakeEditable() to reattach the |
1306 | 0 | // old editor ot the window. |
1307 | 0 | nsresult rv; |
1308 | 0 |
|
1309 | 0 | auto* window = nsPIDOMWindowOuter::From(aWindow); |
1310 | 0 | nsIDocShell *docShell = window->GetDocShell(); |
1311 | 0 | NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); |
1312 | 0 | mDocShell = do_GetWeakReference(docShell); |
1313 | 0 |
|
1314 | 0 | // Disable plugins. |
1315 | 0 | if (!mInteractive) { |
1316 | 0 | rv = DisableJSAndPlugins(aWindow); |
1317 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1318 | 0 | } |
1319 | 0 |
|
1320 | 0 | // Tells embedder that startup is in progress. |
1321 | 0 | mEditorStatus = eEditorCreationInProgress; |
1322 | 0 |
|
1323 | 0 | // Adds back web progress listener. |
1324 | 0 | rv = PrepareForEditing(window); |
1325 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1326 | 0 |
|
1327 | 0 | // Setup the command controllers again. |
1328 | 0 | rv = SetupEditorCommandController(nsBaseCommandController::CreateEditingController, |
1329 | 0 | aWindow, |
1330 | 0 | static_cast<nsIEditingSession*>(this), |
1331 | 0 | &mBaseCommandControllerId); |
1332 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1333 | 0 |
|
1334 | 0 | rv = SetupEditorCommandController(nsBaseCommandController::CreateHTMLEditorDocStateController, |
1335 | 0 | aWindow, |
1336 | 0 | static_cast<nsIEditingSession*>(this), |
1337 | 0 | &mDocStateControllerId); |
1338 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1339 | 0 |
|
1340 | 0 | if (mComposerCommandsUpdater) { |
1341 | 0 | mComposerCommandsUpdater->Init(window); |
1342 | 0 | } |
1343 | 0 |
|
1344 | 0 | // Get editor |
1345 | 0 | RefPtr<HTMLEditor> htmlEditor = GetHTMLEditorForWindow(aWindow); |
1346 | 0 | if (NS_WARN_IF(!htmlEditor)) { |
1347 | 0 | return NS_ERROR_FAILURE; |
1348 | 0 | } |
1349 | 0 | |
1350 | 0 | if (!mInteractive) { |
1351 | 0 | // Disable animation of images in this document: |
1352 | 0 | nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell(); |
1353 | 0 | NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); |
1354 | 0 | nsPresContext* presContext = presShell->GetPresContext(); |
1355 | 0 | NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE); |
1356 | 0 |
|
1357 | 0 | mImageAnimationMode = presContext->ImageAnimationMode(); |
1358 | 0 | presContext->SetImageAnimationMode(imgIContainer::kDontAnimMode); |
1359 | 0 | } |
1360 | 0 |
|
1361 | 0 | // The third controller takes an nsIEditor as the context |
1362 | 0 | rv = SetupEditorCommandController(nsBaseCommandController::CreateHTMLEditorController, |
1363 | 0 | aWindow, |
1364 | 0 | static_cast<nsIEditor*>(htmlEditor.get()), |
1365 | 0 | &mHTMLCommandControllerId); |
1366 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1367 | 0 |
|
1368 | 0 | // Set context on all controllers to be the editor |
1369 | 0 | rv = SetEditorOnControllers(aWindow, htmlEditor); |
1370 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1371 | 0 |
|
1372 | | #ifdef DEBUG |
1373 | | { |
1374 | | bool isEditable; |
1375 | | rv = WindowIsEditable(aWindow, &isEditable); |
1376 | | NS_ENSURE_SUCCESS(rv, rv); |
1377 | | NS_ASSERTION(isEditable, "Window is not editable after reattaching editor."); |
1378 | | } |
1379 | | #endif // DEBUG |
1380 | |
|
1381 | 0 | return NS_OK; |
1382 | 0 | } |
1383 | | |
1384 | | HTMLEditor* |
1385 | | nsIEditingSession::GetHTMLEditorForWindow(mozIDOMWindowProxy* aWindow) |
1386 | 0 | { |
1387 | 0 | if (NS_WARN_IF(!aWindow)) { |
1388 | 0 | return nullptr; |
1389 | 0 | } |
1390 | 0 | |
1391 | 0 | nsCOMPtr<nsIDocShell> docShell = |
1392 | 0 | nsPIDOMWindowOuter::From(aWindow)->GetDocShell(); |
1393 | 0 | if (NS_WARN_IF(!docShell)) { |
1394 | 0 | return nullptr; |
1395 | 0 | } |
1396 | 0 | |
1397 | 0 | return docShell->GetHTMLEditor(); |
1398 | 0 | } |