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/. */
7 #include <string.h> // for nullptr, strcmp
9 #include "imgIContainer.h" // for imgIContainer, etc
10 #include "mozilla/ComposerCommandsUpdater.h" // for ComposerCommandsUpdater
11 #include "mozilla/FlushType.h" // for FlushType::Frames
12 #include "mozilla/HTMLEditor.h" // for HTMLEditor
13 #include "mozilla/mozalloc.h" // for operator new
14 #include "mozilla/PresShell.h" // for PresShell
15 #include "nsAString.h"
16 #include "nsBaseCommandController.h" // for nsBaseCommandController
17 #include "nsCommandManager.h" // for nsCommandManager
18 #include "nsComponentManagerUtils.h" // for do_CreateInstance
19 #include "nsContentUtils.h"
20 #include "nsDebug.h" // for NS_ENSURE_SUCCESS, etc
21 #include "nsEditingSession.h"
22 #include "nsError.h" // for NS_ERROR_FAILURE, NS_OK, etc
23 #include "nsIChannel.h" // for nsIChannel
24 #include "nsIContentViewer.h" // for nsIContentViewer
25 #include "nsIControllers.h" // for nsIControllers
26 #include "nsID.h" // for NS_GET_IID, etc
27 #include "nsHTMLDocument.h" // for nsHTMLDocument
28 #include "nsIDOMWindow.h" // for nsIDOMWindow
29 #include "nsIDocShell.h" // for nsIDocShell
30 #include "mozilla/dom/Document.h" // for Document
31 #include "nsIDocumentStateListener.h"
32 #include "nsIEditor.h" // for nsIEditor
33 #include "nsIInterfaceRequestorUtils.h" // for do_GetInterface
34 #include "nsIPlaintextEditor.h" // for nsIPlaintextEditor, etc
35 #include "nsIRefreshURI.h" // for nsIRefreshURI
36 #include "nsIRequest.h" // for nsIRequest
37 #include "nsITimer.h" // for nsITimer, etc
38 #include "nsITransactionManager.h" // for nsITransactionManager
39 #include "nsIWeakReference.h" // for nsISupportsWeakReference, etc
40 #include "nsIWebNavigation.h" // for nsIWebNavigation
41 #include "nsIWebProgress.h" // for nsIWebProgress, etc
42 #include "nsLiteralString.h" // for NS_LITERAL_STRING
43 #include "nsPIDOMWindow.h" // for nsPIDOMWindow
44 #include "nsPresContext.h" // for nsPresContext
45 #include "nsReadableUtils.h" // for AppendUTF16toUTF8
46 #include "nsStringFwd.h" // for nsString
47 #include "mozilla/dom/Selection.h" // for AutoHideSelectionChanges, etc
48 #include "nsFrameSelection.h" // for nsFrameSelection
49 #include "nsBaseCommandController.h" // for nsBaseCommandController
50 #include "mozilla/dom/LoadURIOptionsBinding.h"
55 using namespace mozilla
;
56 using namespace mozilla::dom
;
58 /*---------------------------------------------------------------------------
62 ----------------------------------------------------------------------------*/
63 nsEditingSession::nsEditingSession()
65 mCanCreateEditor(false),
67 mMakeWholeDocumentEditable(true),
68 mDisabledJSAndPlugins(false),
69 mScriptsEnabled(true),
70 mPluginsEnabled(true),
71 mProgressListenerRegistered(false),
72 mImageAnimationMode(0),
74 mEditorStatus(eEditorOK
),
75 mBaseCommandControllerId(0),
76 mDocStateControllerId(0),
77 mHTMLCommandControllerId(0) {}
79 /*---------------------------------------------------------------------------
83 ----------------------------------------------------------------------------*/
84 nsEditingSession::~nsEditingSession() {
85 // Must cancel previous timer?
86 if (mLoadBlankDocTimer
) mLoadBlankDocTimer
->Cancel();
89 NS_IMPL_ISUPPORTS(nsEditingSession
, nsIEditingSession
, nsIWebProgressListener
,
90 nsISupportsWeakReference
)
92 /*---------------------------------------------------------------------------
96 aEditorType string, "html" "htmlsimple" "text" "textsimple"
97 void makeWindowEditable(in nsIDOMWindow aWindow, in string aEditorType,
98 in boolean aDoAfterUriLoad,
99 in boolean aMakeWholeDocumentEditable,
100 in boolean aInteractive);
101 ----------------------------------------------------------------------------*/
102 #define DEFAULT_EDITOR_TYPE "html"
105 nsEditingSession::MakeWindowEditable(mozIDOMWindowProxy
* aWindow
,
106 const char* aEditorType
,
107 bool aDoAfterUriLoad
,
108 bool aMakeWholeDocumentEditable
,
110 mEditorType
.Truncate();
113 NS_ENSURE_TRUE(aWindow
, NS_ERROR_FAILURE
);
114 auto* window
= nsPIDOMWindowOuter::From(aWindow
);
117 nsCOMPtr
<nsIDocShell
> docShell
= window
->GetDocShell();
118 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
);
120 mDocShell
= do_GetWeakReference(docShell
);
121 mInteractive
= aInteractive
;
122 mMakeWholeDocumentEditable
= aMakeWholeDocumentEditable
;
126 rv
= DisableJSAndPlugins(*docShell
);
127 NS_ENSURE_SUCCESS(rv
, rv
);
130 // Always remove existing editor
131 TearDownEditorOnWindow(aWindow
);
133 // Tells embedder that startup is in progress
134 mEditorStatus
= eEditorCreationInProgress
;
136 // temporary to set editor type here. we will need different classes soon.
137 if (!aEditorType
) aEditorType
= DEFAULT_EDITOR_TYPE
;
138 mEditorType
= aEditorType
;
140 // if all this does is setup listeners and I don't need listeners,
141 // can't this step be ignored?? (based on aDoAfterURILoad)
142 rv
= PrepareForEditing(window
);
143 NS_ENSURE_SUCCESS(rv
, rv
);
145 // set the flag on the docShell to say that it's editable
146 rv
= docShell
->MakeEditable(aDoAfterUriLoad
);
147 NS_ENSURE_SUCCESS(rv
, rv
);
149 // Setup commands common to plaintext and html editors,
150 // including the document creation observers
151 // the first is an editing controller
152 rv
= SetupEditorCommandController(
153 nsBaseCommandController::CreateEditingController
, aWindow
,
154 static_cast<nsIEditingSession
*>(this), &mBaseCommandControllerId
);
155 NS_ENSURE_SUCCESS(rv
, rv
);
157 // The second is a controller to monitor doc state,
158 // such as creation and "dirty flag"
159 rv
= SetupEditorCommandController(
160 nsBaseCommandController::CreateHTMLEditorDocStateController
, aWindow
,
161 static_cast<nsIEditingSession
*>(this), &mDocStateControllerId
);
162 NS_ENSURE_SUCCESS(rv
, rv
);
164 // aDoAfterUriLoad can be false only when making an existing window editable
165 if (!aDoAfterUriLoad
) {
166 rv
= SetupEditorOnWindow(MOZ_KnownLive(*window
));
168 // mEditorStatus is set to the error reason
169 // Since this is used only when editing an existing page,
170 // it IS ok to destroy current editor
172 TearDownEditorOnWindow(aWindow
);
178 nsresult
nsEditingSession::DisableJSAndPlugins(nsIDocShell
& aDocShell
) {
180 nsresult rv
= aDocShell
.GetAllowJavascript(&tmp
);
181 NS_ENSURE_SUCCESS(rv
, rv
);
183 mScriptsEnabled
= tmp
;
185 rv
= aDocShell
.SetAllowJavascript(false);
186 NS_ENSURE_SUCCESS(rv
, rv
);
188 // Disable plugins in this document:
189 mPluginsEnabled
= aDocShell
.PluginsAllowedInCurrentDoc();
191 rv
= aDocShell
.SetAllowPlugins(false);
192 NS_ENSURE_SUCCESS(rv
, rv
);
194 mDisabledJSAndPlugins
= true;
199 nsresult
nsEditingSession::RestoreJSAndPlugins(nsPIDOMWindowOuter
* aWindow
) {
200 if (!mDisabledJSAndPlugins
) {
204 mDisabledJSAndPlugins
= false;
206 if (NS_WARN_IF(!aWindow
)) {
207 // DetachFromWindow may call this method with nullptr.
208 return NS_ERROR_FAILURE
;
210 nsIDocShell
* docShell
= aWindow
->GetDocShell();
211 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
);
213 nsresult rv
= docShell
->SetAllowJavascript(mScriptsEnabled
);
214 NS_ENSURE_SUCCESS(rv
, rv
);
216 // Disable plugins in this document:
217 return docShell
->SetAllowPlugins(mPluginsEnabled
);
220 /*---------------------------------------------------------------------------
224 boolean windowIsEditable (in nsIDOMWindow aWindow);
225 ----------------------------------------------------------------------------*/
227 nsEditingSession::WindowIsEditable(mozIDOMWindowProxy
* aWindow
,
228 bool* outIsEditable
) {
229 NS_ENSURE_STATE(aWindow
);
230 nsCOMPtr
<nsIDocShell
> docShell
=
231 nsPIDOMWindowOuter::From(aWindow
)->GetDocShell();
232 NS_ENSURE_STATE(docShell
);
234 return docShell
->GetEditable(outIsEditable
);
237 // These are MIME types that are automatically parsed as "text/plain"
238 // and thus we can edit them as plaintext
239 // Note: in older versions, we attempted to convert the mimetype of
240 // the network channel for these and "text/xml" to "text/plain",
241 // but further investigation reveals that strategy doesn't work
242 const char* const gSupportedTextTypes
[] = {
247 "text/javascript", // obsolete type
248 "text/ecmascript", // obsolete type
249 "application/javascript",
250 "application/ecmascript",
251 "application/x-javascript", // obsolete type
252 "text/xul", // obsolete type
253 "application/vnd.mozilla.xul+xml",
254 nullptr // IMPORTANT! Null must be at end
257 bool IsSupportedTextType(const char* aMIMEType
) {
258 NS_ENSURE_TRUE(aMIMEType
, false);
260 for (size_t i
= 0; gSupportedTextTypes
[i
]; ++i
) {
261 if (!strcmp(gSupportedTextTypes
[i
], aMIMEType
)) {
269 nsresult
nsEditingSession::SetupEditorOnWindow(nsPIDOMWindowOuter
& aWindow
) {
273 // must get the content type
274 // Note: the doc gets this from the network channel during StartPageLoad,
275 // so we don't have to get it from there ourselves
276 nsAutoCString mimeCType
;
278 // then lets check the mime type
279 if (RefPtr
<Document
> doc
= aWindow
.GetDoc()) {
280 nsAutoString mimeType
;
281 doc
->GetContentType(mimeType
);
282 AppendUTF16toUTF8(mimeType
, mimeCType
);
284 if (IsSupportedTextType(mimeCType
.get())) {
285 mEditorType
.AssignLiteral("text");
286 mimeCType
= "text/plain";
287 } else if (!mimeCType
.EqualsLiteral("text/html") &&
288 !mimeCType
.EqualsLiteral("application/xhtml+xml")) {
289 // Neither an acceptable text or html type.
290 mEditorStatus
= eEditorErrorCantEditMimeType
;
292 // Turn editor into HTML -- we will load blank page later
293 mEditorType
.AssignLiteral("html");
294 mimeCType
.AssignLiteral("text/html");
297 // Flush out frame construction to make sure that the subframe's
298 // presshell is set up if it needs to be.
299 doc
->FlushPendingNotifications(mozilla::FlushType::Frames
);
300 if (mMakeWholeDocumentEditable
) {
301 doc
->SetEditableFlag(true);
302 // Enable usage of the execCommand API
303 doc
->SetEditingState(Document::EditingState::eDesignMode
);
306 bool needHTMLController
= false;
308 if (mEditorType
.EqualsLiteral("textmail")) {
309 mEditorFlags
= nsIPlaintextEditor::eEditorPlaintextMask
|
310 nsIPlaintextEditor::eEditorEnableWrapHackMask
|
311 nsIPlaintextEditor::eEditorMailMask
;
312 } else if (mEditorType
.EqualsLiteral("text")) {
313 mEditorFlags
= nsIPlaintextEditor::eEditorPlaintextMask
|
314 nsIPlaintextEditor::eEditorEnableWrapHackMask
;
315 } else if (mEditorType
.EqualsLiteral("htmlmail")) {
316 if (mimeCType
.EqualsLiteral("text/html")) {
317 needHTMLController
= true;
318 mEditorFlags
= nsIPlaintextEditor::eEditorMailMask
;
320 // Set the flags back to textplain.
321 mEditorFlags
= nsIPlaintextEditor::eEditorPlaintextMask
|
322 nsIPlaintextEditor::eEditorEnableWrapHackMask
;
326 needHTMLController
= true;
330 mEditorFlags
|= nsIPlaintextEditor::eEditorAllowInteraction
;
333 // make the UI state maintainer
334 mComposerCommandsUpdater
= new ComposerCommandsUpdater();
336 // now init the state maintainer
337 // This allows notification of error state
338 // even if we don't create an editor
339 mComposerCommandsUpdater
->Init(aWindow
);
341 if (mEditorStatus
!= eEditorCreationInProgress
) {
342 RefPtr
<ComposerCommandsUpdater
> updater
= mComposerCommandsUpdater
;
343 updater
->NotifyDocumentCreated();
345 // At this point we have made a final decision that we don't support
346 // editing the current document. This is an internal failure state, but
347 // we return NS_OK to avoid throwing an exception from the designMode
348 // setter for web compatibility. The document editing APIs will tell the
349 // developer if editing has been disabled because we're in a document type
350 // that doesn't support editing.
354 // Create editor and do other things
355 // only if we haven't found some error above,
356 nsCOMPtr
<nsIDocShell
> docShell
= aWindow
.GetDocShell();
357 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
);
358 RefPtr
<PresShell
> presShell
= docShell
->GetPresShell();
359 if (NS_WARN_IF(!presShell
)) {
360 return NS_ERROR_FAILURE
;
364 // Disable animation of images in this document:
365 nsPresContext
* presContext
= presShell
->GetPresContext();
366 NS_ENSURE_TRUE(presContext
, NS_ERROR_FAILURE
);
368 mImageAnimationMode
= presContext
->ImageAnimationMode();
369 presContext
->SetImageAnimationMode(imgIContainer::kDontAnimMode
);
372 // Hide selection changes during initialization, in order to hide this
374 RefPtr
<nsFrameSelection
> fs
= presShell
->FrameSelection();
375 NS_ENSURE_TRUE(fs
, NS_ERROR_FAILURE
);
376 AutoHideSelectionChanges
hideSelectionChanges(fs
);
378 // create and set editor
379 // Try to reuse an existing editor
380 nsCOMPtr
<nsIEditor
> editor
= do_QueryReferent(mExistingEditor
);
381 RefPtr
<HTMLEditor
> htmlEditor
= editor
? editor
->AsHTMLEditor() : nullptr;
382 MOZ_ASSERT(!editor
|| htmlEditor
);
384 htmlEditor
->PreDestroy(false);
386 htmlEditor
= new HTMLEditor();
388 do_GetWeakReference(static_cast<nsIEditor
*>(htmlEditor
.get()));
390 // set the editor on the docShell. The docShell now owns it.
391 nsresult rv
= docShell
->SetHTMLEditor(htmlEditor
);
392 NS_ENSURE_SUCCESS(rv
, rv
);
394 // setup the HTML editor command controller
395 if (needHTMLController
) {
396 // The third controller takes an nsIEditor as the context
397 rv
= SetupEditorCommandController(
398 nsBaseCommandController::CreateHTMLEditorController
, &aWindow
,
399 static_cast<nsIEditor
*>(htmlEditor
), &mHTMLCommandControllerId
);
400 NS_ENSURE_SUCCESS(rv
, rv
);
403 // Set mimetype on editor
404 rv
= htmlEditor
->SetContentsMIMEType(mimeCType
.get());
405 NS_ENSURE_SUCCESS(rv
, rv
);
407 nsCOMPtr
<nsIContentViewer
> contentViewer
;
408 rv
= docShell
->GetContentViewer(getter_AddRefs(contentViewer
));
409 NS_ENSURE_SUCCESS(rv
, rv
);
410 NS_ENSURE_TRUE(contentViewer
, NS_ERROR_FAILURE
);
412 RefPtr
<Document
> doc
= contentViewer
->GetDocument();
413 if (NS_WARN_IF(!doc
)) {
414 return NS_ERROR_FAILURE
;
417 // Set up as a doc state listener
418 // Important! We must have this to broadcast the "obs_documentCreated" message
419 rv
= htmlEditor
->AddDocumentStateListener(mComposerCommandsUpdater
);
420 NS_ENSURE_SUCCESS(rv
, rv
);
422 rv
= htmlEditor
->Init(*doc
, nullptr /* root content */, nullptr, mEditorFlags
,
424 NS_ENSURE_SUCCESS(rv
, rv
);
426 RefPtr
<Selection
> selection
= htmlEditor
->GetSelection();
427 if (NS_WARN_IF(!selection
)) {
428 return NS_ERROR_FAILURE
;
431 htmlEditor
->SetComposerCommandsUpdater(mComposerCommandsUpdater
);
433 // and as a transaction listener
434 MOZ_ASSERT(mComposerCommandsUpdater
);
435 DebugOnly
<bool> addedTransactionListener
=
436 htmlEditor
->AddTransactionListener(*mComposerCommandsUpdater
);
437 NS_WARNING_ASSERTION(addedTransactionListener
,
438 "Failed to add transaction listener to the editor");
440 // Set context on all controllers to be the editor
441 rv
= SetEditorOnControllers(aWindow
, htmlEditor
);
442 NS_ENSURE_SUCCESS(rv
, rv
);
444 // Everything went fine!
445 mEditorStatus
= eEditorOK
;
447 // This will trigger documentCreation notification
448 return htmlEditor
->PostCreate();
451 // Removes all listeners and controllers from aWindow and aEditor.
452 void nsEditingSession::RemoveListenersAndControllers(
453 nsPIDOMWindowOuter
* aWindow
, HTMLEditor
* aHTMLEditor
) {
454 if (!mComposerCommandsUpdater
|| !aHTMLEditor
) {
458 // Remove all the listeners
459 aHTMLEditor
->SetComposerCommandsUpdater(nullptr);
460 aHTMLEditor
->RemoveDocumentStateListener(mComposerCommandsUpdater
);
461 DebugOnly
<bool> removedTransactionListener
=
462 aHTMLEditor
->RemoveTransactionListener(*mComposerCommandsUpdater
);
463 NS_WARNING_ASSERTION(removedTransactionListener
,
464 "Failed to remove transaction listener from the editor");
466 // Remove editor controllers from the window now that we're not
467 // editing in that window any more.
468 RemoveEditorControllers(aWindow
);
471 /*---------------------------------------------------------------------------
473 TearDownEditorOnWindow
475 void tearDownEditorOnWindow (in nsIDOMWindow aWindow);
476 ----------------------------------------------------------------------------*/
478 nsEditingSession::TearDownEditorOnWindow(mozIDOMWindowProxy
* aWindow
) {
483 NS_ENSURE_TRUE(aWindow
, NS_ERROR_NULL_POINTER
);
485 // Kill any existing reload timer
486 if (mLoadBlankDocTimer
) {
487 mLoadBlankDocTimer
->Cancel();
488 mLoadBlankDocTimer
= nullptr;
493 // Check if we're turning off editing (from contentEditable or designMode).
494 auto* window
= nsPIDOMWindowOuter::From(aWindow
);
496 RefPtr
<Document
> doc
= window
->GetDoc();
497 bool stopEditing
= doc
&& doc
->IsEditingOn();
499 RemoveWebProgressListener(window
);
502 nsCOMPtr
<nsIDocShell
> docShell
= window
->GetDocShell();
503 NS_ENSURE_STATE(docShell
);
505 RefPtr
<HTMLEditor
> htmlEditor
= docShell
->GetHTMLEditor();
507 doc
->TearingDownEditor();
510 if (mComposerCommandsUpdater
&& htmlEditor
) {
511 // Null out the editor on the controllers first to prevent their weak
512 // references from pointing to a destroyed editor.
513 SetEditorOnControllers(*window
, nullptr);
516 // Null out the editor on the docShell to trigger PreDestroy which
517 // needs to happen before document state listeners are removed below.
518 docShell
->SetEditor(nullptr);
520 RemoveListenersAndControllers(window
, htmlEditor
);
523 // Make things the way they were before we started editing.
524 RestoreJSAndPlugins(window
);
525 RestoreAnimationMode(window
);
527 if (mMakeWholeDocumentEditable
) {
528 doc
->SetEditableFlag(false);
529 doc
->SetEditingState(Document::EditingState::eOff
);
536 /*---------------------------------------------------------------------------
540 nsIEditor getEditorForFrame (in nsIDOMWindow aWindow);
541 ----------------------------------------------------------------------------*/
543 nsEditingSession::GetEditorForWindow(mozIDOMWindowProxy
* aWindow
,
544 nsIEditor
** outEditor
) {
545 if (NS_WARN_IF(!aWindow
)) {
546 return NS_ERROR_INVALID_ARG
;
548 nsCOMPtr
<nsIEditor
> editor
= GetHTMLEditorForWindow(aWindow
);
549 editor
.forget(outEditor
);
553 /*---------------------------------------------------------------------------
557 ----------------------------------------------------------------------------*/
559 nsEditingSession::OnStateChange(nsIWebProgress
* aWebProgress
,
560 nsIRequest
* aRequest
, uint32_t aStateFlags
,
562 #ifdef NOISY_DOC_LOADING
563 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(aRequest
));
565 nsAutoCString contentType
;
566 channel
->GetContentType(contentType
);
567 if (!contentType
.IsEmpty()) {
568 printf(" ++++++ MIMETYPE = %s\n", contentType
.get());
574 // A Request has started...
576 if (aStateFlags
& nsIWebProgressListener::STATE_START
) {
577 #ifdef NOISY_DOC_LOADING
579 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(aRequest
));
581 nsCOMPtr
<nsIURI
> uri
;
582 channel
->GetURI(getter_AddRefs(uri
));
586 printf(" **** STATE_START: CHANNEL URI=%s, flags=%x\n", spec
.get(),
590 printf(" STATE_START: NO CHANNEL flags=%x\n", aStateFlags
);
594 // Page level notification...
595 if (aStateFlags
& nsIWebProgressListener::STATE_IS_NETWORK
) {
596 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(aRequest
));
597 StartPageLoad(channel
);
598 #ifdef NOISY_DOC_LOADING
599 printf("STATE_START & STATE_IS_NETWORK flags=%x\n", aStateFlags
);
603 // Document level notification...
604 if (aStateFlags
& nsIWebProgressListener::STATE_IS_DOCUMENT
&&
605 !(aStateFlags
& nsIWebProgressListener::STATE_RESTORING
)) {
606 #ifdef NOISY_DOC_LOADING
607 printf("STATE_START & STATE_IS_DOCUMENT flags=%x\n", aStateFlags
);
610 bool progressIsForTargetDocument
=
611 IsProgressForTargetDocument(aWebProgress
);
613 if (progressIsForTargetDocument
) {
614 nsCOMPtr
<mozIDOMWindowProxy
> window
;
615 aWebProgress
->GetDOMWindow(getter_AddRefs(window
));
617 auto* piWindow
= nsPIDOMWindowOuter::From(window
);
618 RefPtr
<Document
> doc
= piWindow
->GetDoc();
619 nsHTMLDocument
* htmlDoc
=
620 doc
&& doc
->IsHTMLOrXHTML() ? doc
->AsHTMLDocument() : nullptr;
621 if (htmlDoc
&& doc
->IsWriting()) {
622 nsAutoString designMode
;
623 htmlDoc
->GetDesignMode(designMode
);
625 if (designMode
.EqualsLiteral("on")) {
626 // This notification is for data coming in through
627 // document.open/write/close(), ignore it.
633 mCanCreateEditor
= true;
634 StartDocumentLoad(aWebProgress
, progressIsForTargetDocument
);
639 // A Request is being processed
641 else if (aStateFlags
& nsIWebProgressListener::STATE_TRANSFERRING
) {
642 if (aStateFlags
& nsIWebProgressListener::STATE_IS_DOCUMENT
) {
643 // document transfer started
649 else if (aStateFlags
& nsIWebProgressListener::STATE_REDIRECTING
) {
650 if (aStateFlags
& nsIWebProgressListener::STATE_IS_DOCUMENT
) {
655 // A network or document Request has finished...
657 else if (aStateFlags
& nsIWebProgressListener::STATE_STOP
) {
658 #ifdef NOISY_DOC_LOADING
660 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(aRequest
));
662 nsCOMPtr
<nsIURI
> uri
;
663 channel
->GetURI(getter_AddRefs(uri
));
667 printf(" **** STATE_STOP: CHANNEL URI=%s, flags=%x\n", spec
.get(),
671 printf(" STATE_STOP: NO CHANNEL flags=%x\n", aStateFlags
);
676 // Document level notification...
677 if (aStateFlags
& nsIWebProgressListener::STATE_IS_DOCUMENT
) {
678 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
679 EndDocumentLoad(aWebProgress
, channel
, aStatus
,
680 IsProgressForTargetDocument(aWebProgress
));
681 #ifdef NOISY_DOC_LOADING
682 printf("STATE_STOP & STATE_IS_DOCUMENT flags=%x\n", aStateFlags
);
686 // Page level notification...
687 if (aStateFlags
& nsIWebProgressListener::STATE_IS_NETWORK
) {
688 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
689 (void)EndPageLoad(aWebProgress
, channel
, aStatus
);
690 #ifdef NOISY_DOC_LOADING
691 printf("STATE_STOP & STATE_IS_NETWORK flags=%x\n", aStateFlags
);
699 /*---------------------------------------------------------------------------
703 ----------------------------------------------------------------------------*/
705 nsEditingSession::OnProgressChange(nsIWebProgress
* aWebProgress
,
706 nsIRequest
* aRequest
,
707 int32_t aCurSelfProgress
,
708 int32_t aMaxSelfProgress
,
709 int32_t aCurTotalProgress
,
710 int32_t aMaxTotalProgress
) {
711 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
715 /*---------------------------------------------------------------------------
719 ----------------------------------------------------------------------------*/
721 nsEditingSession::OnLocationChange(nsIWebProgress
* aWebProgress
,
722 nsIRequest
* aRequest
, nsIURI
* aURI
,
724 nsCOMPtr
<mozIDOMWindowProxy
> domWindow
;
725 nsresult rv
= aWebProgress
->GetDOMWindow(getter_AddRefs(domWindow
));
726 NS_ENSURE_SUCCESS(rv
, rv
);
728 auto* piWindow
= nsPIDOMWindowOuter::From(domWindow
);
730 RefPtr
<Document
> doc
= piWindow
->GetDoc();
731 NS_ENSURE_TRUE(doc
, NS_ERROR_FAILURE
);
733 doc
->SetDocumentURI(aURI
);
735 // Notify the location-changed observer that
736 // the document URL has changed
737 nsIDocShell
* docShell
= piWindow
->GetDocShell();
738 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
);
740 RefPtr
<nsCommandManager
> commandManager
= docShell
->GetCommandManager();
741 return commandManager
->CommandStatusChanged("obs_documentLocationChanged");
744 /*---------------------------------------------------------------------------
748 ----------------------------------------------------------------------------*/
750 nsEditingSession::OnStatusChange(nsIWebProgress
* aWebProgress
,
751 nsIRequest
* aRequest
, nsresult aStatus
,
752 const char16_t
* aMessage
) {
753 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
757 /*---------------------------------------------------------------------------
761 ----------------------------------------------------------------------------*/
763 nsEditingSession::OnSecurityChange(nsIWebProgress
* aWebProgress
,
764 nsIRequest
* aRequest
, uint32_t aState
) {
765 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
769 /*---------------------------------------------------------------------------
771 OnContentBlockingEvent
773 ----------------------------------------------------------------------------*/
775 nsEditingSession::OnContentBlockingEvent(nsIWebProgress
* aWebProgress
,
776 nsIRequest
* aRequest
,
778 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
782 /*---------------------------------------------------------------------------
784 IsProgressForTargetDocument
786 Check that this notification is for our document.
787 ----------------------------------------------------------------------------*/
789 bool nsEditingSession::IsProgressForTargetDocument(
790 nsIWebProgress
* aWebProgress
) {
791 nsCOMPtr
<nsIWebProgress
> editedWebProgress
= do_QueryReferent(mDocShell
);
792 return editedWebProgress
== aWebProgress
;
795 /*---------------------------------------------------------------------------
799 Called during GetCommandStateParams("obs_documentCreated"...)
800 to determine if editor was created and document
801 was loaded successfully
802 ----------------------------------------------------------------------------*/
804 nsEditingSession::GetEditorStatus(uint32_t* aStatus
) {
805 NS_ENSURE_ARG_POINTER(aStatus
);
806 *aStatus
= mEditorStatus
;
810 /*---------------------------------------------------------------------------
814 Called on start of load in a single frame
815 ----------------------------------------------------------------------------*/
816 nsresult
nsEditingSession::StartDocumentLoad(nsIWebProgress
* aWebProgress
,
817 bool aIsToBeMadeEditable
) {
818 #ifdef NOISY_DOC_LOADING
819 printf("======= StartDocumentLoad ========\n");
822 NS_ENSURE_ARG_POINTER(aWebProgress
);
824 if (aIsToBeMadeEditable
) {
825 mEditorStatus
= eEditorCreationInProgress
;
831 /*---------------------------------------------------------------------------
835 Called on end of load in a single frame
836 ----------------------------------------------------------------------------*/
837 nsresult
nsEditingSession::EndDocumentLoad(nsIWebProgress
* aWebProgress
,
838 nsIChannel
* aChannel
,
840 bool aIsToBeMadeEditable
) {
841 NS_ENSURE_ARG_POINTER(aWebProgress
);
843 #ifdef NOISY_DOC_LOADING
844 printf("======= EndDocumentLoad ========\n");
845 printf("with status %d, ", aStatus
);
846 nsCOMPtr
<nsIURI
> uri
;
848 if (NS_SUCCEEDED(aChannel
->GetURI(getter_AddRefs(uri
)))) {
850 printf(" uri %s\n", spec
.get());
854 // We want to call the base class EndDocumentLoad,
855 // but avoid some of the stuff
856 // that nsDocShell does (need to refactor).
858 // OK, time to make an editor on this document
859 nsCOMPtr
<mozIDOMWindowProxy
> domWindow
;
860 aWebProgress
->GetDOMWindow(getter_AddRefs(domWindow
));
861 NS_ENSURE_TRUE(domWindow
, NS_ERROR_FAILURE
);
863 // Set the error state -- we will create an editor
864 // anyway and load empty doc later
865 if (aIsToBeMadeEditable
&& aStatus
== NS_ERROR_FILE_NOT_FOUND
) {
866 mEditorStatus
= eEditorErrorFileNotFound
;
869 auto* window
= nsPIDOMWindowOuter::From(domWindow
);
870 nsIDocShell
* docShell
= window
->GetDocShell();
871 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
); // better error handling?
873 // cancel refresh from meta tags
874 // we need to make sure that all pages in editor (whether editable or not)
875 // can't refresh contents being edited
876 nsCOMPtr
<nsIRefreshURI
> refreshURI
= do_QueryInterface(docShell
);
878 refreshURI
->CancelRefreshURITimers();
883 // did someone set the flag to make this shell editable?
884 if (aIsToBeMadeEditable
&& mCanCreateEditor
) {
886 docShell
->GetEditable(&makeEditable
);
889 // To keep pre Gecko 1.9 behavior, setup editor always when
890 // mMakeWholeDocumentEditable.
891 bool needsSetup
= false;
892 if (mMakeWholeDocumentEditable
) {
895 // do we already have an editor here?
896 needsSetup
= !docShell
->GetHTMLEditor();
900 mCanCreateEditor
= false;
901 rv
= SetupEditorOnWindow(MOZ_KnownLive(*window
));
903 // If we had an error, setup timer to load a blank page later
904 if (mLoadBlankDocTimer
) {
905 // Must cancel previous timer?
906 mLoadBlankDocTimer
->Cancel();
907 mLoadBlankDocTimer
= nullptr;
910 rv
= NS_NewTimerWithFuncCallback(getter_AddRefs(mLoadBlankDocTimer
),
911 nsEditingSession::TimerCallback
,
912 static_cast<void*>(mDocShell
.get()),
913 10, nsITimer::TYPE_ONE_SHOT
,
914 "nsEditingSession::EndDocumentLoad");
915 NS_ENSURE_SUCCESS(rv
, rv
);
917 mEditorStatus
= eEditorCreationInProgress
;
925 void nsEditingSession::TimerCallback(nsITimer
* aTimer
, void* aClosure
) {
926 nsCOMPtr
<nsIDocShell
> docShell
=
927 do_QueryReferent(static_cast<nsIWeakReference
*>(aClosure
));
929 nsCOMPtr
<nsIWebNavigation
> webNav(do_QueryInterface(docShell
));
931 LoadURIOptions loadURIOptions
;
932 loadURIOptions
.mTriggeringPrincipal
=
933 nsContentUtils::GetSystemPrincipal();
934 webNav
->LoadURI(NS_LITERAL_STRING("about:blank"), loadURIOptions
);
939 /*---------------------------------------------------------------------------
943 Called on start load of the entire page (incl. subframes)
944 ----------------------------------------------------------------------------*/
945 nsresult
nsEditingSession::StartPageLoad(nsIChannel
* aChannel
) {
946 #ifdef NOISY_DOC_LOADING
947 printf("======= StartPageLoad ========\n");
952 /*---------------------------------------------------------------------------
956 Called on end load of the entire page (incl. subframes)
957 ----------------------------------------------------------------------------*/
958 nsresult
nsEditingSession::EndPageLoad(nsIWebProgress
* aWebProgress
,
959 nsIChannel
* aChannel
, nsresult aStatus
) {
960 #ifdef NOISY_DOC_LOADING
961 printf("======= EndPageLoad ========\n");
962 printf(" with status %d, ", aStatus
);
963 nsCOMPtr
<nsIURI
> uri
;
965 if (NS_SUCCEEDED(aChannel
->GetURI(getter_AddRefs(uri
)))) {
967 printf("uri %s\n", spec
.get());
970 nsAutoCString contentType
;
971 aChannel
->GetContentType(contentType
);
972 if (!contentType
.IsEmpty()) {
973 printf(" flags = %d, status = %d, MIMETYPE = %s\n", mEditorFlags
,
974 mEditorStatus
, contentType
.get());
978 // Set the error state -- we will create an editor anyway
979 // and load empty doc later
980 if (aStatus
== NS_ERROR_FILE_NOT_FOUND
) {
981 mEditorStatus
= eEditorErrorFileNotFound
;
984 nsCOMPtr
<mozIDOMWindowProxy
> domWindow
;
985 aWebProgress
->GetDOMWindow(getter_AddRefs(domWindow
));
987 nsIDocShell
* docShell
=
988 domWindow
? nsPIDOMWindowOuter::From(domWindow
)->GetDocShell() : nullptr;
989 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
);
991 // cancel refresh from meta tags
992 // we need to make sure that all pages in editor (whether editable or not)
993 // can't refresh contents being edited
994 nsCOMPtr
<nsIRefreshURI
> refreshURI
= do_QueryInterface(docShell
);
996 refreshURI
->CancelRefreshURITimers();
1000 // Shouldn't we do this when we want to edit sub-frames?
1001 return MakeWindowEditable(domWindow
, "html", false, mInteractive
);
1007 /*---------------------------------------------------------------------------
1011 Set up this editing session for one or more editors
1012 ----------------------------------------------------------------------------*/
1013 nsresult
nsEditingSession::PrepareForEditing(nsPIDOMWindowOuter
* aWindow
) {
1014 if (mProgressListenerRegistered
) {
1018 nsIDocShell
* docShell
= aWindow
? aWindow
->GetDocShell() : nullptr;
1020 // register callback
1021 nsCOMPtr
<nsIWebProgress
> webProgress
= do_GetInterface(docShell
);
1022 NS_ENSURE_TRUE(webProgress
, NS_ERROR_FAILURE
);
1024 nsresult rv
= webProgress
->AddProgressListener(
1025 this, (nsIWebProgress::NOTIFY_STATE_NETWORK
|
1026 nsIWebProgress::NOTIFY_STATE_DOCUMENT
|
1027 nsIWebProgress::NOTIFY_LOCATION
));
1029 mProgressListenerRegistered
= NS_SUCCEEDED(rv
);
1034 /*---------------------------------------------------------------------------
1036 SetupEditorCommandController
1038 Create a command controller, append to controllers,
1039 get and return the controller ID, and set the context
1040 ----------------------------------------------------------------------------*/
1041 nsresult
nsEditingSession::SetupEditorCommandController(
1042 nsEditingSession::ControllerCreatorFn aControllerCreatorFn
,
1043 mozIDOMWindowProxy
* aWindow
, nsISupports
* aContext
,
1044 uint32_t* aControllerId
) {
1045 NS_ENSURE_ARG_POINTER(aControllerCreatorFn
);
1046 NS_ENSURE_ARG_POINTER(aWindow
);
1047 NS_ENSURE_ARG_POINTER(aContext
);
1048 NS_ENSURE_ARG_POINTER(aControllerId
);
1050 auto* piWindow
= nsPIDOMWindowOuter::From(aWindow
);
1051 MOZ_ASSERT(piWindow
);
1053 nsCOMPtr
<nsIControllers
> controllers
;
1054 nsresult rv
= piWindow
->GetControllers(getter_AddRefs(controllers
));
1055 NS_ENSURE_SUCCESS(rv
, rv
);
1057 // We only have to create each singleton controller once
1058 // We know this has happened once we have a controllerId value
1059 if (!*aControllerId
) {
1060 RefPtr
<nsBaseCommandController
> commandController
= aControllerCreatorFn();
1061 NS_ENSURE_TRUE(commandController
, NS_ERROR_FAILURE
);
1063 // We must insert at head of the list to be sure our
1064 // controller is found before other implementations
1065 // (e.g., not-implemented versions by browser)
1066 rv
= controllers
->InsertControllerAt(0, commandController
);
1067 NS_ENSURE_SUCCESS(rv
, rv
);
1069 // Remember the ID for the controller
1070 rv
= controllers
->GetControllerId(commandController
, aControllerId
);
1071 NS_ENSURE_SUCCESS(rv
, rv
);
1075 return SetContextOnControllerById(controllers
, aContext
, *aControllerId
);
1078 nsresult
nsEditingSession::SetEditorOnControllers(nsPIDOMWindowOuter
& aWindow
,
1079 HTMLEditor
* aEditor
) {
1080 nsCOMPtr
<nsIControllers
> controllers
;
1081 nsresult rv
= aWindow
.GetControllers(getter_AddRefs(controllers
));
1082 NS_ENSURE_SUCCESS(rv
, rv
);
1084 nsCOMPtr
<nsISupports
> editorAsISupports
= static_cast<nsIEditor
*>(aEditor
);
1085 if (mBaseCommandControllerId
) {
1086 rv
= SetContextOnControllerById(controllers
, editorAsISupports
,
1087 mBaseCommandControllerId
);
1088 NS_ENSURE_SUCCESS(rv
, rv
);
1091 if (mDocStateControllerId
) {
1092 rv
= SetContextOnControllerById(controllers
, editorAsISupports
,
1093 mDocStateControllerId
);
1094 NS_ENSURE_SUCCESS(rv
, rv
);
1097 if (mHTMLCommandControllerId
) {
1098 rv
= SetContextOnControllerById(controllers
, editorAsISupports
,
1099 mHTMLCommandControllerId
);
1105 nsresult
nsEditingSession::SetContextOnControllerById(
1106 nsIControllers
* aControllers
, nsISupports
* aContext
, uint32_t aID
) {
1107 NS_ENSURE_ARG_POINTER(aControllers
);
1109 // aContext can be null (when destroying editor)
1110 nsCOMPtr
<nsIController
> controller
;
1111 aControllers
->GetControllerById(aID
, getter_AddRefs(controller
));
1113 // ok with nil controller
1114 nsCOMPtr
<nsIControllerContext
> editorController
=
1115 do_QueryInterface(controller
);
1116 NS_ENSURE_TRUE(editorController
, NS_ERROR_FAILURE
);
1118 return editorController
->SetCommandContext(aContext
);
1121 void nsEditingSession::RemoveEditorControllers(nsPIDOMWindowOuter
* aWindow
) {
1122 // Remove editor controllers from the aWindow, call when we're
1123 // tearing down/detaching editor.
1125 nsCOMPtr
<nsIControllers
> controllers
;
1127 aWindow
->GetControllers(getter_AddRefs(controllers
));
1131 nsCOMPtr
<nsIController
> controller
;
1132 if (mBaseCommandControllerId
) {
1133 controllers
->GetControllerById(mBaseCommandControllerId
,
1134 getter_AddRefs(controller
));
1136 controllers
->RemoveController(controller
);
1140 if (mDocStateControllerId
) {
1141 controllers
->GetControllerById(mDocStateControllerId
,
1142 getter_AddRefs(controller
));
1144 controllers
->RemoveController(controller
);
1148 if (mHTMLCommandControllerId
) {
1149 controllers
->GetControllerById(mHTMLCommandControllerId
,
1150 getter_AddRefs(controller
));
1152 controllers
->RemoveController(controller
);
1157 // Clear IDs to trigger creation of new controllers.
1158 mBaseCommandControllerId
= 0;
1159 mDocStateControllerId
= 0;
1160 mHTMLCommandControllerId
= 0;
1163 void nsEditingSession::RemoveWebProgressListener(nsPIDOMWindowOuter
* aWindow
) {
1164 nsIDocShell
* docShell
= aWindow
? aWindow
->GetDocShell() : nullptr;
1165 nsCOMPtr
<nsIWebProgress
> webProgress
= do_GetInterface(docShell
);
1167 webProgress
->RemoveProgressListener(this);
1168 mProgressListenerRegistered
= false;
1172 void nsEditingSession::RestoreAnimationMode(nsPIDOMWindowOuter
* aWindow
) {
1177 nsCOMPtr
<nsIDocShell
> docShell
= aWindow
? aWindow
->GetDocShell() : nullptr;
1178 NS_ENSURE_TRUE_VOID(docShell
);
1179 RefPtr
<PresShell
> presShell
= docShell
->GetPresShell();
1180 if (NS_WARN_IF(!presShell
)) {
1183 nsPresContext
* presContext
= presShell
->GetPresContext();
1184 NS_ENSURE_TRUE_VOID(presContext
);
1186 presContext
->SetImageAnimationMode(mImageAnimationMode
);
1189 nsresult
nsEditingSession::DetachFromWindow(nsPIDOMWindowOuter
* aWindow
) {
1190 NS_ENSURE_TRUE(mDoneSetup
, NS_OK
);
1192 NS_ASSERTION(mComposerCommandsUpdater
,
1193 "mComposerCommandsUpdater should exist.");
1195 // Kill any existing reload timer
1196 if (mLoadBlankDocTimer
) {
1197 mLoadBlankDocTimer
->Cancel();
1198 mLoadBlankDocTimer
= nullptr;
1201 // Remove controllers, webprogress listener, and otherwise
1202 // make things the way they were before we started editing.
1203 RemoveEditorControllers(aWindow
);
1204 RemoveWebProgressListener(aWindow
);
1205 RestoreJSAndPlugins(aWindow
);
1206 RestoreAnimationMode(aWindow
);
1208 // Kill our weak reference to our original window, in case
1209 // it changes on restore, or otherwise dies.
1210 mDocShell
= nullptr;
1215 nsresult
nsEditingSession::ReattachToWindow(nsPIDOMWindowOuter
* aWindow
) {
1216 NS_ENSURE_TRUE(mDoneSetup
, NS_OK
);
1217 NS_ENSURE_TRUE(aWindow
, NS_ERROR_FAILURE
);
1219 NS_ASSERTION(mComposerCommandsUpdater
,
1220 "mComposerCommandsUpdater should exist.");
1222 // Imitate nsEditorDocShell::MakeEditable() to reattach the
1223 // old editor ot the window.
1226 nsIDocShell
* docShell
= aWindow
->GetDocShell();
1227 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
);
1228 mDocShell
= do_GetWeakReference(docShell
);
1231 if (!mInteractive
) {
1232 rv
= DisableJSAndPlugins(*docShell
);
1233 NS_ENSURE_SUCCESS(rv
, rv
);
1236 // Tells embedder that startup is in progress.
1237 mEditorStatus
= eEditorCreationInProgress
;
1239 // Adds back web progress listener.
1240 rv
= PrepareForEditing(aWindow
);
1241 NS_ENSURE_SUCCESS(rv
, rv
);
1243 // Setup the command controllers again.
1244 rv
= SetupEditorCommandController(
1245 nsBaseCommandController::CreateEditingController
, aWindow
,
1246 static_cast<nsIEditingSession
*>(this), &mBaseCommandControllerId
);
1247 NS_ENSURE_SUCCESS(rv
, rv
);
1249 rv
= SetupEditorCommandController(
1250 nsBaseCommandController::CreateHTMLEditorDocStateController
, aWindow
,
1251 static_cast<nsIEditingSession
*>(this), &mDocStateControllerId
);
1252 NS_ENSURE_SUCCESS(rv
, rv
);
1254 if (mComposerCommandsUpdater
) {
1255 mComposerCommandsUpdater
->Init(*aWindow
);
1259 RefPtr
<HTMLEditor
> htmlEditor
= GetHTMLEditorForWindow(aWindow
);
1260 if (NS_WARN_IF(!htmlEditor
)) {
1261 return NS_ERROR_FAILURE
;
1264 if (!mInteractive
) {
1265 // Disable animation of images in this document:
1266 RefPtr
<PresShell
> presShell
= docShell
->GetPresShell();
1267 if (NS_WARN_IF(!presShell
)) {
1268 return NS_ERROR_FAILURE
;
1270 nsPresContext
* presContext
= presShell
->GetPresContext();
1271 NS_ENSURE_TRUE(presContext
, NS_ERROR_FAILURE
);
1273 mImageAnimationMode
= presContext
->ImageAnimationMode();
1274 presContext
->SetImageAnimationMode(imgIContainer::kDontAnimMode
);
1277 // The third controller takes an nsIEditor as the context
1278 rv
= SetupEditorCommandController(
1279 nsBaseCommandController::CreateHTMLEditorController
, aWindow
,
1280 static_cast<nsIEditor
*>(htmlEditor
.get()), &mHTMLCommandControllerId
);
1281 NS_ENSURE_SUCCESS(rv
, rv
);
1283 // Set context on all controllers to be the editor
1284 rv
= SetEditorOnControllers(*aWindow
, htmlEditor
);
1285 NS_ENSURE_SUCCESS(rv
, rv
);
1290 rv
= WindowIsEditable(aWindow
, &isEditable
);
1291 NS_ENSURE_SUCCESS(rv
, rv
);
1292 NS_ASSERTION(isEditable
,
1293 "Window is not editable after reattaching editor.");
1300 HTMLEditor
* nsIEditingSession::GetHTMLEditorForWindow(
1301 mozIDOMWindowProxy
* aWindow
) {
1302 if (NS_WARN_IF(!aWindow
)) {
1306 nsCOMPtr
<nsIDocShell
> docShell
=
1307 nsPIDOMWindowOuter::From(aWindow
)->GetDocShell();
1308 if (NS_WARN_IF(!docShell
)) {
1312 return docShell
->GetHTMLEditor();