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