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