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 "mozilla/Try.h" // for MOZ_TRY
16 #include "nsAString.h"
17 #include "nsBaseCommandController.h" // for nsBaseCommandController
18 #include "nsCommandManager.h" // for nsCommandManager
19 #include "nsComponentManagerUtils.h" // for do_CreateInstance
20 #include "nsContentUtils.h"
21 #include "nsDebug.h" // for NS_ENSURE_SUCCESS, etc
22 #include "nsDocShell.h" // for nsDocShell
23 #include "nsEditingSession.h"
24 #include "nsError.h" // for NS_ERROR_FAILURE, NS_OK, etc
25 #include "nsIChannel.h" // for nsIChannel
26 #include "nsIDocumentViewer.h" // for nsIDocumentViewer
27 #include "nsIControllers.h" // for nsIControllers
28 #include "nsID.h" // for NS_GET_IID, etc
29 #include "nsHTMLDocument.h" // for nsHTMLDocument
30 #include "nsIDocShell.h" // for nsIDocShell
31 #include "mozilla/dom/Document.h" // for Document
32 #include "nsIEditor.h" // for nsIEditor
33 #include "nsIInterfaceRequestorUtils.h" // for do_GetInterface
34 #include "nsIRefreshURI.h" // for nsIRefreshURI
35 #include "nsIRequest.h" // for nsIRequest
36 #include "nsITimer.h" // for nsITimer, etc
37 #include "nsIWeakReference.h" // for nsISupportsWeakReference, etc
38 #include "nsIWebNavigation.h" // for nsIWebNavigation
39 #include "nsIWebProgress.h" // for nsIWebProgress, etc
40 #include "nsLiteralString.h" // for NS_LITERAL_STRING
41 #include "nsPIDOMWindow.h" // for nsPIDOMWindow
42 #include "nsPresContext.h" // for nsPresContext
43 #include "nsReadableUtils.h" // for AppendUTF16toUTF8
44 #include "nsStringFwd.h" // for nsString
45 #include "mozilla/dom/BrowsingContext.h" // for BrowsingContext
46 #include "mozilla/dom/Selection.h" // for AutoHideSelectionChanges, etc
47 #include "mozilla/dom/WindowContext.h" // for WindowContext
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),
69 mScriptsEnabled(true),
70 mProgressListenerRegistered(false),
71 mImageAnimationMode(0),
73 mEditorStatus(eEditorOK
),
74 mBaseCommandControllerId(0),
75 mDocStateControllerId(0),
76 mHTMLCommandControllerId(0) {}
78 /*---------------------------------------------------------------------------
82 ----------------------------------------------------------------------------*/
83 nsEditingSession::~nsEditingSession() {
84 // Must cancel previous timer?
85 if (mLoadBlankDocTimer
) mLoadBlankDocTimer
->Cancel();
88 NS_IMPL_ISUPPORTS(nsEditingSession
, nsIEditingSession
, nsIWebProgressListener
,
89 nsISupportsWeakReference
)
91 /*---------------------------------------------------------------------------
95 aEditorType string, "html" "htmlsimple" "text" "textsimple"
96 void makeWindowEditable(in nsIDOMWindow aWindow, in string aEditorType,
97 in boolean aDoAfterUriLoad,
98 in boolean aMakeWholeDocumentEditable,
99 in boolean aInteractive);
100 ----------------------------------------------------------------------------*/
101 #define DEFAULT_EDITOR_TYPE "html"
104 nsEditingSession::MakeWindowEditable(mozIDOMWindowProxy
* aWindow
,
105 const char* aEditorType
,
106 bool aDoAfterUriLoad
,
107 bool aMakeWholeDocumentEditable
,
109 mEditorType
.Truncate();
112 NS_ENSURE_TRUE(aWindow
, NS_ERROR_FAILURE
);
113 auto* window
= nsPIDOMWindowOuter::From(aWindow
);
116 nsCOMPtr
<nsIDocShell
> docShell
= window
->GetDocShell();
117 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
);
118 mDocShell
= do_GetWeakReference(docShell
);
120 mInteractive
= aInteractive
;
121 mMakeWholeDocumentEditable
= aMakeWholeDocumentEditable
;
125 rv
= DisableJS(window
->GetCurrentInnerWindow());
126 NS_ENSURE_SUCCESS(rv
, rv
);
129 // Always remove existing editor
130 TearDownEditorOnWindow(aWindow
);
132 // Tells embedder that startup is in progress
133 mEditorStatus
= eEditorCreationInProgress
;
135 // temporary to set editor type here. we will need different classes soon.
136 if (!aEditorType
) aEditorType
= DEFAULT_EDITOR_TYPE
;
137 mEditorType
= aEditorType
;
139 // if all this does is setup listeners and I don't need listeners,
140 // can't this step be ignored?? (based on aDoAfterURILoad)
141 rv
= PrepareForEditing(window
);
142 NS_ENSURE_SUCCESS(rv
, rv
);
144 // set the flag on the docShell to say that it's editable
145 rv
= docShell
->MakeEditable(aDoAfterUriLoad
);
146 NS_ENSURE_SUCCESS(rv
, rv
);
148 // Setup commands common to plaintext and html editors,
149 // including the document creation observers
150 // the first is an editing controller
151 rv
= SetupEditorCommandController(
152 nsBaseCommandController::CreateEditingController
, aWindow
,
153 static_cast<nsIEditingSession
*>(this), &mBaseCommandControllerId
);
154 NS_ENSURE_SUCCESS(rv
, rv
);
156 // The second is a controller to monitor doc state,
157 // such as creation and "dirty flag"
158 rv
= SetupEditorCommandController(
159 nsBaseCommandController::CreateHTMLEditorDocStateController
, aWindow
,
160 static_cast<nsIEditingSession
*>(this), &mDocStateControllerId
);
161 NS_ENSURE_SUCCESS(rv
, rv
);
163 // aDoAfterUriLoad can be false only when making an existing window editable
164 if (!aDoAfterUriLoad
) {
165 rv
= SetupEditorOnWindow(MOZ_KnownLive(*window
));
167 // mEditorStatus is set to the error reason
168 // Since this is used only when editing an existing page,
169 // it IS ok to destroy current editor
171 TearDownEditorOnWindow(aWindow
);
177 nsresult
nsEditingSession::DisableJS(nsPIDOMWindowInner
* aWindow
) {
178 WindowContext
* wc
= aWindow
->GetWindowContext();
180 mScriptsEnabled
= wc
->GetAllowJavascript();
181 MOZ_TRY(wc
->SetAllowJavascript(false));
186 nsresult
nsEditingSession::RestoreJS(nsPIDOMWindowInner
* aWindow
) {
193 if (NS_WARN_IF(!aWindow
)) {
194 // DetachFromWindow may call this method with nullptr.
195 return NS_ERROR_FAILURE
;
198 WindowContext
* wc
= aWindow
->GetWindowContext();
199 return wc
->SetAllowJavascript(mScriptsEnabled
);
202 /*---------------------------------------------------------------------------
206 boolean windowIsEditable (in nsIDOMWindow aWindow);
207 ----------------------------------------------------------------------------*/
209 nsEditingSession::WindowIsEditable(mozIDOMWindowProxy
* aWindow
,
210 bool* outIsEditable
) {
211 NS_ENSURE_STATE(aWindow
);
212 nsCOMPtr
<nsIDocShell
> docShell
=
213 nsPIDOMWindowOuter::From(aWindow
)->GetDocShell();
214 NS_ENSURE_STATE(docShell
);
216 return docShell
->GetEditable(outIsEditable
);
219 bool IsSupportedTextType(const nsAString
& aMIMEType
) {
220 // These are MIME types that are automatically parsed as "text/plain"
221 // and thus we can edit them as plaintext
222 // Note: in older versions, we attempted to convert the mimetype of
223 // the network channel for these and "text/xml" to "text/plain",
224 // but further investigation reveals that strategy doesn't work
225 static constexpr nsLiteralString sSupportedTextTypes
[] = {
230 u
"text/javascript"_ns
, // obsolete type
231 u
"text/ecmascript"_ns
, // obsolete type
232 u
"application/javascript"_ns
,
233 u
"application/ecmascript"_ns
,
234 u
"application/x-javascript"_ns
, // obsolete type
235 u
"text/xul"_ns
// obsolete type
238 for (const nsLiteralString
& supportedTextType
: sSupportedTextTypes
) {
239 if (aMIMEType
.Equals(supportedTextType
)) {
247 nsresult
nsEditingSession::SetupEditorOnWindow(nsPIDOMWindowOuter
& aWindow
) {
251 // must get the content type
252 // Note: the doc gets this from the network channel during StartPageLoad,
253 // so we don't have to get it from there ourselves
254 nsAutoString mimeType
;
256 // then lets check the mime type
257 if (RefPtr
<Document
> doc
= aWindow
.GetDoc()) {
258 doc
->GetContentType(mimeType
);
260 if (IsSupportedTextType(mimeType
)) {
261 mEditorType
.AssignLiteral("text");
262 mimeType
.AssignLiteral("text/plain");
263 } else if (!doc
->IsHTMLOrXHTML()) {
264 // Neither an acceptable text or html type.
265 mEditorStatus
= eEditorErrorCantEditMimeType
;
267 // Turn editor into HTML -- we will load blank page later
268 mEditorType
.AssignLiteral("html");
269 mimeType
.AssignLiteral("text/html");
272 // Flush out frame construction to make sure that the subframe's
273 // presshell is set up if it needs to be.
274 doc
->FlushPendingNotifications(mozilla::FlushType::Frames
);
275 if (mMakeWholeDocumentEditable
) {
276 doc
->SetEditableFlag(true);
277 // Enable usage of the execCommand API
278 doc
->SetEditingState(Document::EditingState::eDesignMode
);
281 bool needHTMLController
= false;
283 if (mEditorType
.EqualsLiteral("textmail")) {
284 mEditorFlags
= nsIEditor::eEditorPlaintextMask
|
285 nsIEditor::eEditorEnableWrapHackMask
|
286 nsIEditor::eEditorMailMask
;
287 } else if (mEditorType
.EqualsLiteral("text")) {
289 nsIEditor::eEditorPlaintextMask
| nsIEditor::eEditorEnableWrapHackMask
;
290 } else if (mEditorType
.EqualsLiteral("htmlmail")) {
291 if (mimeType
.EqualsLiteral("text/html")) {
292 needHTMLController
= true;
293 mEditorFlags
= nsIEditor::eEditorMailMask
;
295 // Set the flags back to textplain.
296 mEditorFlags
= nsIEditor::eEditorPlaintextMask
|
297 nsIEditor::eEditorEnableWrapHackMask
;
301 needHTMLController
= true;
305 mEditorFlags
|= nsIEditor::eEditorAllowInteraction
;
308 // make the UI state maintainer
309 RefPtr
<ComposerCommandsUpdater
> commandsUpdater
=
310 new ComposerCommandsUpdater();
311 mComposerCommandsUpdater
= commandsUpdater
;
313 // now init the state maintainer
314 // This allows notification of error state
315 // even if we don't create an editor
316 commandsUpdater
->Init(aWindow
);
318 if (mEditorStatus
!= eEditorCreationInProgress
) {
319 commandsUpdater
->OnHTMLEditorCreated();
321 // At this point we have made a final decision that we don't support
322 // editing the current document. This is an internal failure state, but
323 // we return NS_OK to avoid throwing an exception from the designMode
324 // setter for web compatibility. The document editing APIs will tell the
325 // developer if editing has been disabled because we're in a document type
326 // that doesn't support editing.
330 // Create editor and do other things
331 // only if we haven't found some error above,
332 const RefPtr
<nsDocShell
> docShell
= nsDocShell::Cast(aWindow
.GetDocShell());
333 if (NS_WARN_IF(!docShell
)) {
334 return NS_ERROR_FAILURE
;
336 const RefPtr
<PresShell
> presShell
= docShell
->GetPresShell();
337 if (NS_WARN_IF(!presShell
)) {
338 return NS_ERROR_FAILURE
;
342 // Disable animation of images in this document:
343 nsPresContext
* presContext
= presShell
->GetPresContext();
344 NS_ENSURE_TRUE(presContext
, NS_ERROR_FAILURE
);
346 mImageAnimationMode
= presContext
->ImageAnimationMode();
347 presContext
->SetImageAnimationMode(imgIContainer::kDontAnimMode
);
350 // Hide selection changes during initialization, in order to hide this
352 RefPtr
<nsFrameSelection
> fs
= presShell
->FrameSelection();
353 NS_ENSURE_TRUE(fs
, NS_ERROR_FAILURE
);
354 AutoHideSelectionChanges
hideSelectionChanges(fs
);
356 nsCOMPtr
<nsIDocumentViewer
> viewer
;
357 nsresult rv
= docShell
->GetDocViewer(getter_AddRefs(viewer
));
358 if (NS_FAILED(rv
) || NS_WARN_IF(!viewer
)) {
359 NS_WARNING("nsDocShell::GetDocViewer() failed");
363 const RefPtr
<Document
> doc
= viewer
->GetDocument();
364 if (NS_WARN_IF(!doc
)) {
365 return NS_ERROR_FAILURE
;
368 // create and set editor
369 // Try to reuse an existing editor
370 nsCOMPtr
<nsIEditor
> editor
= do_QueryReferent(mExistingEditor
);
371 RefPtr
<HTMLEditor
> htmlEditor
= HTMLEditor::GetFrom(editor
);
372 MOZ_ASSERT_IF(editor
, htmlEditor
);
374 htmlEditor
->PreDestroy();
376 htmlEditor
= new HTMLEditor(*doc
);
378 do_GetWeakReference(static_cast<nsIEditor
*>(htmlEditor
.get()));
380 // set the editor on the docShell. The docShell now owns it.
381 rv
= docShell
->SetHTMLEditor(htmlEditor
);
382 NS_ENSURE_SUCCESS(rv
, rv
);
384 // setup the HTML editor command controller
385 if (needHTMLController
) {
386 // The third controller takes an nsIEditor as the context
387 rv
= SetupEditorCommandController(
388 nsBaseCommandController::CreateHTMLEditorController
, &aWindow
,
389 static_cast<nsIEditor
*>(htmlEditor
), &mHTMLCommandControllerId
);
390 NS_ENSURE_SUCCESS(rv
, rv
);
393 // Set mimetype on editor
394 rv
= htmlEditor
->SetContentsMIMEType(mimeType
);
395 NS_ENSURE_SUCCESS(rv
, rv
);
397 MOZ_ASSERT(docShell
->HasDocumentViewer());
398 MOZ_ASSERT(viewer
->GetDocument());
400 MOZ_DIAGNOSTIC_ASSERT(commandsUpdater
== mComposerCommandsUpdater
);
401 if (MOZ_UNLIKELY(commandsUpdater
!= mComposerCommandsUpdater
)) {
402 commandsUpdater
= mComposerCommandsUpdater
;
404 rv
= htmlEditor
->Init(*doc
, *commandsUpdater
, mEditorFlags
);
405 NS_ENSURE_SUCCESS(rv
, rv
);
407 RefPtr
<Selection
> selection
= htmlEditor
->GetSelection();
408 if (NS_WARN_IF(!selection
)) {
409 return NS_ERROR_FAILURE
;
412 // Set context on all controllers to be the editor
413 rv
= SetEditorOnControllers(aWindow
, htmlEditor
);
414 NS_ENSURE_SUCCESS(rv
, rv
);
416 // Everything went fine!
417 mEditorStatus
= eEditorOK
;
419 // This will trigger documentCreation notification
420 return htmlEditor
->PostCreate();
423 // Removes all listeners and controllers from aWindow and aEditor.
424 void nsEditingSession::RemoveListenersAndControllers(
425 nsPIDOMWindowOuter
* aWindow
, HTMLEditor
* aHTMLEditor
) {
426 if (!mComposerCommandsUpdater
|| !aHTMLEditor
) {
430 // Remove all the listeners
431 RefPtr
<ComposerCommandsUpdater
> composertCommandsUpdater
=
432 std::move(mComposerCommandsUpdater
);
433 MOZ_ASSERT(!mComposerCommandsUpdater
);
434 aHTMLEditor
->Detach(*composertCommandsUpdater
);
436 // Remove editor controllers from the window now that we're not
437 // editing in that window any more.
438 RemoveEditorControllers(aWindow
);
441 /*---------------------------------------------------------------------------
443 TearDownEditorOnWindow
445 void tearDownEditorOnWindow (in nsIDOMWindow aWindow);
446 ----------------------------------------------------------------------------*/
448 nsEditingSession::TearDownEditorOnWindow(mozIDOMWindowProxy
* aWindow
) {
453 NS_ENSURE_TRUE(aWindow
, NS_ERROR_NULL_POINTER
);
455 // Kill any existing reload timer
456 if (mLoadBlankDocTimer
) {
457 mLoadBlankDocTimer
->Cancel();
458 mLoadBlankDocTimer
= nullptr;
463 // Check if we're turning off editing (from contentEditable or designMode).
464 auto* window
= nsPIDOMWindowOuter::From(aWindow
);
466 RefPtr
<Document
> doc
= window
->GetDoc();
467 bool stopEditing
= doc
&& doc
->IsEditingOn();
469 RemoveWebProgressListener(window
);
472 nsCOMPtr
<nsIDocShell
> docShell
= window
->GetDocShell();
473 NS_ENSURE_STATE(docShell
);
475 RefPtr
<HTMLEditor
> htmlEditor
= docShell
->GetHTMLEditor();
477 doc
->TearingDownEditor();
480 if (mComposerCommandsUpdater
&& htmlEditor
) {
481 // Null out the editor on the controllers first to prevent their weak
482 // references from pointing to a destroyed editor.
483 SetEditorOnControllers(*window
, nullptr);
486 // Null out the editor on the docShell to trigger PreDestroy which
487 // needs to happen before document state listeners are removed below.
488 docShell
->SetEditor(nullptr);
490 RemoveListenersAndControllers(window
, htmlEditor
);
493 // Make things the way they were before we started editing.
494 RestoreJS(window
->GetCurrentInnerWindow());
495 RestoreAnimationMode(window
);
497 if (mMakeWholeDocumentEditable
) {
498 doc
->SetEditableFlag(false);
499 doc
->SetEditingState(Document::EditingState::eOff
);
506 /*---------------------------------------------------------------------------
510 nsIEditor getEditorForFrame (in nsIDOMWindow aWindow);
511 ----------------------------------------------------------------------------*/
513 nsEditingSession::GetEditorForWindow(mozIDOMWindowProxy
* aWindow
,
514 nsIEditor
** outEditor
) {
515 if (NS_WARN_IF(!aWindow
)) {
516 return NS_ERROR_INVALID_ARG
;
518 nsCOMPtr
<nsIEditor
> editor
= GetHTMLEditorForWindow(aWindow
);
519 editor
.forget(outEditor
);
523 /*---------------------------------------------------------------------------
527 ----------------------------------------------------------------------------*/
529 nsEditingSession::OnStateChange(nsIWebProgress
* aWebProgress
,
530 nsIRequest
* aRequest
, uint32_t aStateFlags
,
532 #ifdef NOISY_DOC_LOADING
533 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(aRequest
));
535 nsAutoCString contentType
;
536 channel
->GetContentType(contentType
);
537 if (!contentType
.IsEmpty()) {
538 printf(" ++++++ MIMETYPE = %s\n", contentType
.get());
544 // A Request has started...
546 if (aStateFlags
& nsIWebProgressListener::STATE_START
) {
547 #ifdef NOISY_DOC_LOADING
549 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(aRequest
));
551 nsCOMPtr
<nsIURI
> uri
;
552 channel
->GetURI(getter_AddRefs(uri
));
556 printf(" **** STATE_START: CHANNEL URI=%s, flags=%x\n", spec
.get(),
560 printf(" STATE_START: NO CHANNEL flags=%x\n", aStateFlags
);
564 // Page level notification...
565 if (aStateFlags
& nsIWebProgressListener::STATE_IS_NETWORK
) {
566 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(aRequest
));
567 StartPageLoad(channel
);
568 #ifdef NOISY_DOC_LOADING
569 printf("STATE_START & STATE_IS_NETWORK flags=%x\n", aStateFlags
);
573 // Document level notification...
574 if (aStateFlags
& nsIWebProgressListener::STATE_IS_DOCUMENT
&&
575 !(aStateFlags
& nsIWebProgressListener::STATE_RESTORING
)) {
576 #ifdef NOISY_DOC_LOADING
577 printf("STATE_START & STATE_IS_DOCUMENT flags=%x\n", aStateFlags
);
580 bool progressIsForTargetDocument
=
581 IsProgressForTargetDocument(aWebProgress
);
583 if (progressIsForTargetDocument
) {
584 nsCOMPtr
<mozIDOMWindowProxy
> window
;
585 aWebProgress
->GetDOMWindow(getter_AddRefs(window
));
587 auto* piWindow
= nsPIDOMWindowOuter::From(window
);
588 RefPtr
<Document
> doc
= piWindow
->GetDoc();
589 nsHTMLDocument
* htmlDoc
=
590 doc
&& doc
->IsHTMLOrXHTML() ? doc
->AsHTMLDocument() : nullptr;
591 if (htmlDoc
&& doc
->IsWriting()) {
592 nsAutoString designMode
;
593 htmlDoc
->GetDesignMode(designMode
);
595 if (designMode
.EqualsLiteral("on")) {
596 // This notification is for data coming in through
597 // document.open/write/close(), ignore it.
603 mCanCreateEditor
= true;
604 StartDocumentLoad(aWebProgress
, progressIsForTargetDocument
);
609 // A Request is being processed
611 else if (aStateFlags
& nsIWebProgressListener::STATE_TRANSFERRING
) {
612 if (aStateFlags
& nsIWebProgressListener::STATE_IS_DOCUMENT
) {
613 // document transfer started
619 else if (aStateFlags
& nsIWebProgressListener::STATE_REDIRECTING
) {
620 if (aStateFlags
& nsIWebProgressListener::STATE_IS_DOCUMENT
) {
625 // A network or document Request has finished...
627 else if (aStateFlags
& nsIWebProgressListener::STATE_STOP
) {
628 #ifdef NOISY_DOC_LOADING
630 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(aRequest
));
632 nsCOMPtr
<nsIURI
> uri
;
633 channel
->GetURI(getter_AddRefs(uri
));
637 printf(" **** STATE_STOP: CHANNEL URI=%s, flags=%x\n", spec
.get(),
641 printf(" STATE_STOP: NO CHANNEL flags=%x\n", aStateFlags
);
646 // Document level notification...
647 if (aStateFlags
& nsIWebProgressListener::STATE_IS_DOCUMENT
) {
648 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
649 EndDocumentLoad(aWebProgress
, channel
, aStatus
,
650 IsProgressForTargetDocument(aWebProgress
));
651 #ifdef NOISY_DOC_LOADING
652 printf("STATE_STOP & STATE_IS_DOCUMENT flags=%x\n", aStateFlags
);
656 // Page level notification...
657 if (aStateFlags
& nsIWebProgressListener::STATE_IS_NETWORK
) {
658 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
659 (void)EndPageLoad(aWebProgress
, channel
, aStatus
);
660 #ifdef NOISY_DOC_LOADING
661 printf("STATE_STOP & STATE_IS_NETWORK flags=%x\n", aStateFlags
);
669 /*---------------------------------------------------------------------------
673 ----------------------------------------------------------------------------*/
675 nsEditingSession::OnProgressChange(nsIWebProgress
* aWebProgress
,
676 nsIRequest
* aRequest
,
677 int32_t aCurSelfProgress
,
678 int32_t aMaxSelfProgress
,
679 int32_t aCurTotalProgress
,
680 int32_t aMaxTotalProgress
) {
681 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
685 /*---------------------------------------------------------------------------
689 ----------------------------------------------------------------------------*/
691 nsEditingSession::OnLocationChange(nsIWebProgress
* aWebProgress
,
692 nsIRequest
* aRequest
, nsIURI
* aURI
,
694 nsCOMPtr
<mozIDOMWindowProxy
> domWindow
;
695 nsresult rv
= aWebProgress
->GetDOMWindow(getter_AddRefs(domWindow
));
696 NS_ENSURE_SUCCESS(rv
, rv
);
698 auto* piWindow
= nsPIDOMWindowOuter::From(domWindow
);
700 RefPtr
<Document
> doc
= piWindow
->GetDoc();
701 NS_ENSURE_TRUE(doc
, NS_ERROR_FAILURE
);
703 doc
->SetDocumentURI(aURI
);
705 // Notify the location-changed observer that
706 // the document URL has changed
707 nsIDocShell
* docShell
= piWindow
->GetDocShell();
708 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
);
710 RefPtr
<nsCommandManager
> commandManager
= docShell
->GetCommandManager();
711 commandManager
->CommandStatusChanged("obs_documentLocationChanged");
715 /*---------------------------------------------------------------------------
719 ----------------------------------------------------------------------------*/
721 nsEditingSession::OnStatusChange(nsIWebProgress
* aWebProgress
,
722 nsIRequest
* aRequest
, nsresult aStatus
,
723 const char16_t
* aMessage
) {
724 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
728 /*---------------------------------------------------------------------------
732 ----------------------------------------------------------------------------*/
734 nsEditingSession::OnSecurityChange(nsIWebProgress
* aWebProgress
,
735 nsIRequest
* aRequest
, uint32_t aState
) {
736 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
740 /*---------------------------------------------------------------------------
742 OnContentBlockingEvent
744 ----------------------------------------------------------------------------*/
746 nsEditingSession::OnContentBlockingEvent(nsIWebProgress
* aWebProgress
,
747 nsIRequest
* aRequest
,
749 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
753 /*---------------------------------------------------------------------------
755 IsProgressForTargetDocument
757 Check that this notification is for our document.
758 ----------------------------------------------------------------------------*/
760 bool nsEditingSession::IsProgressForTargetDocument(
761 nsIWebProgress
* aWebProgress
) {
762 nsCOMPtr
<nsIWebProgress
> editedWebProgress
= do_QueryReferent(mDocShell
);
763 return editedWebProgress
== aWebProgress
;
766 /*---------------------------------------------------------------------------
770 Called during GetCommandStateParams("obs_documentCreated"...)
771 to determine if editor was created and document
772 was loaded successfully
773 ----------------------------------------------------------------------------*/
775 nsEditingSession::GetEditorStatus(uint32_t* aStatus
) {
776 NS_ENSURE_ARG_POINTER(aStatus
);
777 *aStatus
= mEditorStatus
;
781 /*---------------------------------------------------------------------------
785 Called on start of load in a single frame
786 ----------------------------------------------------------------------------*/
787 nsresult
nsEditingSession::StartDocumentLoad(nsIWebProgress
* aWebProgress
,
788 bool aIsToBeMadeEditable
) {
789 #ifdef NOISY_DOC_LOADING
790 printf("======= StartDocumentLoad ========\n");
793 NS_ENSURE_ARG_POINTER(aWebProgress
);
795 if (aIsToBeMadeEditable
) {
796 mEditorStatus
= eEditorCreationInProgress
;
802 /*---------------------------------------------------------------------------
806 Called on end of load in a single frame
807 ----------------------------------------------------------------------------*/
808 nsresult
nsEditingSession::EndDocumentLoad(nsIWebProgress
* aWebProgress
,
809 nsIChannel
* aChannel
,
811 bool aIsToBeMadeEditable
) {
812 NS_ENSURE_ARG_POINTER(aWebProgress
);
814 #ifdef NOISY_DOC_LOADING
815 printf("======= EndDocumentLoad ========\n");
816 printf("with status %d, ", aStatus
);
817 nsCOMPtr
<nsIURI
> uri
;
819 if (NS_SUCCEEDED(aChannel
->GetURI(getter_AddRefs(uri
)))) {
821 printf(" uri %s\n", spec
.get());
825 // We want to call the base class EndDocumentLoad,
826 // but avoid some of the stuff
827 // that nsDocShell does (need to refactor).
829 // OK, time to make an editor on this document
830 nsCOMPtr
<mozIDOMWindowProxy
> domWindow
;
831 aWebProgress
->GetDOMWindow(getter_AddRefs(domWindow
));
832 NS_ENSURE_TRUE(domWindow
, NS_ERROR_FAILURE
);
834 // Set the error state -- we will create an editor
835 // anyway and load empty doc later
836 if (aIsToBeMadeEditable
&& aStatus
== NS_ERROR_FILE_NOT_FOUND
) {
837 mEditorStatus
= eEditorErrorFileNotFound
;
840 auto* window
= nsPIDOMWindowOuter::From(domWindow
);
841 nsIDocShell
* docShell
= window
->GetDocShell();
842 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
); // better error handling?
844 // cancel refresh from meta tags
845 // we need to make sure that all pages in editor (whether editable or not)
846 // can't refresh contents being edited
847 nsCOMPtr
<nsIRefreshURI
> refreshURI
= do_QueryInterface(docShell
);
849 refreshURI
->CancelRefreshURITimers();
854 // did someone set the flag to make this shell editable?
855 if (aIsToBeMadeEditable
&& mCanCreateEditor
) {
857 docShell
->GetEditable(&makeEditable
);
860 // To keep pre Gecko 1.9 behavior, setup editor always when
861 // mMakeWholeDocumentEditable.
862 bool needsSetup
= false;
863 if (mMakeWholeDocumentEditable
) {
866 // do we already have an editor here?
867 needsSetup
= !docShell
->GetHTMLEditor();
871 mCanCreateEditor
= false;
872 rv
= SetupEditorOnWindow(MOZ_KnownLive(*window
));
874 // If we had an error, setup timer to load a blank page later
875 if (mLoadBlankDocTimer
) {
876 // Must cancel previous timer?
877 mLoadBlankDocTimer
->Cancel();
878 mLoadBlankDocTimer
= nullptr;
881 rv
= NS_NewTimerWithFuncCallback(getter_AddRefs(mLoadBlankDocTimer
),
882 nsEditingSession::TimerCallback
,
883 static_cast<void*>(mDocShell
.get()),
884 10, nsITimer::TYPE_ONE_SHOT
,
885 "nsEditingSession::EndDocumentLoad");
886 NS_ENSURE_SUCCESS(rv
, rv
);
888 mEditorStatus
= eEditorCreationInProgress
;
896 void nsEditingSession::TimerCallback(nsITimer
* aTimer
, void* aClosure
) {
897 nsCOMPtr
<nsIDocShell
> docShell
=
898 do_QueryReferent(static_cast<nsIWeakReference
*>(aClosure
));
900 nsCOMPtr
<nsIWebNavigation
> webNav(do_QueryInterface(docShell
));
902 LoadURIOptions loadURIOptions
;
903 loadURIOptions
.mTriggeringPrincipal
=
904 nsContentUtils::GetSystemPrincipal();
905 nsCOMPtr
<nsIURI
> uri
;
906 MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri
), "about:blank"_ns
));
907 webNav
->LoadURI(uri
, loadURIOptions
);
912 /*---------------------------------------------------------------------------
916 Called on start load of the entire page (incl. subframes)
917 ----------------------------------------------------------------------------*/
918 nsresult
nsEditingSession::StartPageLoad(nsIChannel
* aChannel
) {
919 #ifdef NOISY_DOC_LOADING
920 printf("======= StartPageLoad ========\n");
925 /*---------------------------------------------------------------------------
929 Called on end load of the entire page (incl. subframes)
930 ----------------------------------------------------------------------------*/
931 nsresult
nsEditingSession::EndPageLoad(nsIWebProgress
* aWebProgress
,
932 nsIChannel
* aChannel
, nsresult aStatus
) {
933 #ifdef NOISY_DOC_LOADING
934 printf("======= EndPageLoad ========\n");
935 printf(" with status %d, ", aStatus
);
936 nsCOMPtr
<nsIURI
> uri
;
938 if (NS_SUCCEEDED(aChannel
->GetURI(getter_AddRefs(uri
)))) {
940 printf("uri %s\n", spec
.get());
943 nsAutoCString contentType
;
944 aChannel
->GetContentType(contentType
);
945 if (!contentType
.IsEmpty()) {
946 printf(" flags = %d, status = %d, MIMETYPE = %s\n", mEditorFlags
,
947 mEditorStatus
, contentType
.get());
951 // Set the error state -- we will create an editor anyway
952 // and load empty doc later
953 if (aStatus
== NS_ERROR_FILE_NOT_FOUND
) {
954 mEditorStatus
= eEditorErrorFileNotFound
;
957 nsCOMPtr
<mozIDOMWindowProxy
> domWindow
;
958 aWebProgress
->GetDOMWindow(getter_AddRefs(domWindow
));
960 nsIDocShell
* docShell
=
961 domWindow
? nsPIDOMWindowOuter::From(domWindow
)->GetDocShell() : nullptr;
962 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
);
964 // cancel refresh from meta tags
965 // we need to make sure that all pages in editor (whether editable or not)
966 // can't refresh contents being edited
967 nsCOMPtr
<nsIRefreshURI
> refreshURI
= do_QueryInterface(docShell
);
969 refreshURI
->CancelRefreshURITimers();
973 // Shouldn't we do this when we want to edit sub-frames?
974 return MakeWindowEditable(domWindow
, "html", false, mInteractive
);
980 /*---------------------------------------------------------------------------
984 Set up this editing session for one or more editors
985 ----------------------------------------------------------------------------*/
986 nsresult
nsEditingSession::PrepareForEditing(nsPIDOMWindowOuter
* aWindow
) {
987 if (mProgressListenerRegistered
) {
991 nsIDocShell
* docShell
= aWindow
? aWindow
->GetDocShell() : nullptr;
994 nsCOMPtr
<nsIWebProgress
> webProgress
= do_GetInterface(docShell
);
995 NS_ENSURE_TRUE(webProgress
, NS_ERROR_FAILURE
);
997 nsresult rv
= webProgress
->AddProgressListener(
998 this, (nsIWebProgress::NOTIFY_STATE_NETWORK
|
999 nsIWebProgress::NOTIFY_STATE_DOCUMENT
|
1000 nsIWebProgress::NOTIFY_LOCATION
));
1002 mProgressListenerRegistered
= NS_SUCCEEDED(rv
);
1007 /*---------------------------------------------------------------------------
1009 SetupEditorCommandController
1011 Create a command controller, append to controllers,
1012 get and return the controller ID, and set the context
1013 ----------------------------------------------------------------------------*/
1014 nsresult
nsEditingSession::SetupEditorCommandController(
1015 nsEditingSession::ControllerCreatorFn aControllerCreatorFn
,
1016 mozIDOMWindowProxy
* aWindow
, nsISupports
* aContext
,
1017 uint32_t* aControllerId
) {
1018 NS_ENSURE_ARG_POINTER(aControllerCreatorFn
);
1019 NS_ENSURE_ARG_POINTER(aWindow
);
1020 NS_ENSURE_ARG_POINTER(aContext
);
1021 NS_ENSURE_ARG_POINTER(aControllerId
);
1023 auto* piWindow
= nsPIDOMWindowOuter::From(aWindow
);
1024 MOZ_ASSERT(piWindow
);
1026 nsCOMPtr
<nsIControllers
> controllers
;
1027 nsresult rv
= piWindow
->GetControllers(getter_AddRefs(controllers
));
1028 NS_ENSURE_SUCCESS(rv
, rv
);
1030 // We only have to create each singleton controller once
1031 // We know this has happened once we have a controllerId value
1032 if (!*aControllerId
) {
1033 RefPtr
<nsBaseCommandController
> commandController
= aControllerCreatorFn();
1034 NS_ENSURE_TRUE(commandController
, NS_ERROR_FAILURE
);
1036 // We must insert at head of the list to be sure our
1037 // controller is found before other implementations
1038 // (e.g., not-implemented versions by browser)
1039 rv
= controllers
->InsertControllerAt(0, commandController
);
1040 NS_ENSURE_SUCCESS(rv
, rv
);
1042 // Remember the ID for the controller
1043 rv
= controllers
->GetControllerId(commandController
, aControllerId
);
1044 NS_ENSURE_SUCCESS(rv
, rv
);
1048 return SetContextOnControllerById(controllers
, aContext
, *aControllerId
);
1051 nsresult
nsEditingSession::SetEditorOnControllers(nsPIDOMWindowOuter
& aWindow
,
1052 HTMLEditor
* aEditor
) {
1053 nsCOMPtr
<nsIControllers
> controllers
;
1054 nsresult rv
= aWindow
.GetControllers(getter_AddRefs(controllers
));
1055 NS_ENSURE_SUCCESS(rv
, rv
);
1057 nsCOMPtr
<nsISupports
> editorAsISupports
= static_cast<nsIEditor
*>(aEditor
);
1058 if (mBaseCommandControllerId
) {
1059 rv
= SetContextOnControllerById(controllers
, editorAsISupports
,
1060 mBaseCommandControllerId
);
1061 NS_ENSURE_SUCCESS(rv
, rv
);
1064 if (mDocStateControllerId
) {
1065 rv
= SetContextOnControllerById(controllers
, editorAsISupports
,
1066 mDocStateControllerId
);
1067 NS_ENSURE_SUCCESS(rv
, rv
);
1070 if (mHTMLCommandControllerId
) {
1071 rv
= SetContextOnControllerById(controllers
, editorAsISupports
,
1072 mHTMLCommandControllerId
);
1078 nsresult
nsEditingSession::SetContextOnControllerById(
1079 nsIControllers
* aControllers
, nsISupports
* aContext
, uint32_t aID
) {
1080 NS_ENSURE_ARG_POINTER(aControllers
);
1082 // aContext can be null (when destroying editor)
1083 nsCOMPtr
<nsIController
> controller
;
1084 aControllers
->GetControllerById(aID
, getter_AddRefs(controller
));
1086 // ok with nil controller
1087 nsCOMPtr
<nsIControllerContext
> editorController
=
1088 do_QueryInterface(controller
);
1089 NS_ENSURE_TRUE(editorController
, NS_ERROR_FAILURE
);
1091 return editorController
->SetCommandContext(aContext
);
1094 void nsEditingSession::RemoveEditorControllers(nsPIDOMWindowOuter
* aWindow
) {
1095 // Remove editor controllers from the aWindow, call when we're
1096 // tearing down/detaching editor.
1098 nsCOMPtr
<nsIControllers
> controllers
;
1100 aWindow
->GetControllers(getter_AddRefs(controllers
));
1104 nsCOMPtr
<nsIController
> controller
;
1105 if (mBaseCommandControllerId
) {
1106 controllers
->GetControllerById(mBaseCommandControllerId
,
1107 getter_AddRefs(controller
));
1109 controllers
->RemoveController(controller
);
1113 if (mDocStateControllerId
) {
1114 controllers
->GetControllerById(mDocStateControllerId
,
1115 getter_AddRefs(controller
));
1117 controllers
->RemoveController(controller
);
1121 if (mHTMLCommandControllerId
) {
1122 controllers
->GetControllerById(mHTMLCommandControllerId
,
1123 getter_AddRefs(controller
));
1125 controllers
->RemoveController(controller
);
1130 // Clear IDs to trigger creation of new controllers.
1131 mBaseCommandControllerId
= 0;
1132 mDocStateControllerId
= 0;
1133 mHTMLCommandControllerId
= 0;
1136 void nsEditingSession::RemoveWebProgressListener(nsPIDOMWindowOuter
* aWindow
) {
1137 nsIDocShell
* docShell
= aWindow
? aWindow
->GetDocShell() : nullptr;
1138 nsCOMPtr
<nsIWebProgress
> webProgress
= do_GetInterface(docShell
);
1140 webProgress
->RemoveProgressListener(this);
1141 mProgressListenerRegistered
= false;
1145 void nsEditingSession::RestoreAnimationMode(nsPIDOMWindowOuter
* aWindow
) {
1150 nsCOMPtr
<nsIDocShell
> docShell
= aWindow
? aWindow
->GetDocShell() : nullptr;
1151 NS_ENSURE_TRUE_VOID(docShell
);
1152 RefPtr
<PresShell
> presShell
= docShell
->GetPresShell();
1153 if (NS_WARN_IF(!presShell
)) {
1156 nsPresContext
* presContext
= presShell
->GetPresContext();
1157 NS_ENSURE_TRUE_VOID(presContext
);
1159 presContext
->SetImageAnimationMode(mImageAnimationMode
);
1162 nsresult
nsEditingSession::DetachFromWindow(nsPIDOMWindowOuter
* aWindow
) {
1163 NS_ENSURE_TRUE(mDoneSetup
, NS_OK
);
1165 NS_ASSERTION(mComposerCommandsUpdater
,
1166 "mComposerCommandsUpdater should exist.");
1168 // Kill any existing reload timer
1169 if (mLoadBlankDocTimer
) {
1170 mLoadBlankDocTimer
->Cancel();
1171 mLoadBlankDocTimer
= nullptr;
1174 // Remove controllers, webprogress listener, and otherwise
1175 // make things the way they were before we started editing.
1176 RemoveEditorControllers(aWindow
);
1177 RemoveWebProgressListener(aWindow
);
1178 RestoreJS(aWindow
->GetCurrentInnerWindow());
1179 RestoreAnimationMode(aWindow
);
1181 // Kill our weak reference to our original window, in case
1182 // it changes on restore, or otherwise dies.
1183 mDocShell
= nullptr;
1188 nsresult
nsEditingSession::ReattachToWindow(nsPIDOMWindowOuter
* aWindow
) {
1189 NS_ENSURE_TRUE(mDoneSetup
, NS_OK
);
1190 NS_ENSURE_TRUE(aWindow
, NS_ERROR_FAILURE
);
1192 NS_ASSERTION(mComposerCommandsUpdater
,
1193 "mComposerCommandsUpdater should exist.");
1195 // Imitate nsEditorDocShell::MakeEditable() to reattach the
1196 // old editor to the window.
1199 nsIDocShell
* docShell
= aWindow
->GetDocShell();
1200 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
);
1201 mDocShell
= do_GetWeakReference(docShell
);
1204 if (!mInteractive
) {
1205 rv
= DisableJS(aWindow
->GetCurrentInnerWindow());
1206 NS_ENSURE_SUCCESS(rv
, rv
);
1209 // Tells embedder that startup is in progress.
1210 mEditorStatus
= eEditorCreationInProgress
;
1212 // Adds back web progress listener.
1213 rv
= PrepareForEditing(aWindow
);
1214 NS_ENSURE_SUCCESS(rv
, rv
);
1216 // Setup the command controllers again.
1217 rv
= SetupEditorCommandController(
1218 nsBaseCommandController::CreateEditingController
, aWindow
,
1219 static_cast<nsIEditingSession
*>(this), &mBaseCommandControllerId
);
1220 NS_ENSURE_SUCCESS(rv
, rv
);
1222 rv
= SetupEditorCommandController(
1223 nsBaseCommandController::CreateHTMLEditorDocStateController
, aWindow
,
1224 static_cast<nsIEditingSession
*>(this), &mDocStateControllerId
);
1225 NS_ENSURE_SUCCESS(rv
, rv
);
1227 if (mComposerCommandsUpdater
) {
1228 mComposerCommandsUpdater
->Init(*aWindow
);
1232 RefPtr
<HTMLEditor
> htmlEditor
= GetHTMLEditorForWindow(aWindow
);
1233 if (NS_WARN_IF(!htmlEditor
)) {
1234 return NS_ERROR_FAILURE
;
1237 if (!mInteractive
) {
1238 // Disable animation of images in this document:
1239 RefPtr
<PresShell
> presShell
= docShell
->GetPresShell();
1240 if (NS_WARN_IF(!presShell
)) {
1241 return NS_ERROR_FAILURE
;
1243 nsPresContext
* presContext
= presShell
->GetPresContext();
1244 NS_ENSURE_TRUE(presContext
, NS_ERROR_FAILURE
);
1246 mImageAnimationMode
= presContext
->ImageAnimationMode();
1247 presContext
->SetImageAnimationMode(imgIContainer::kDontAnimMode
);
1250 // The third controller takes an nsIEditor as the context
1251 rv
= SetupEditorCommandController(
1252 nsBaseCommandController::CreateHTMLEditorController
, aWindow
,
1253 static_cast<nsIEditor
*>(htmlEditor
.get()), &mHTMLCommandControllerId
);
1254 NS_ENSURE_SUCCESS(rv
, rv
);
1256 // Set context on all controllers to be the editor
1257 rv
= SetEditorOnControllers(*aWindow
, htmlEditor
);
1258 NS_ENSURE_SUCCESS(rv
, rv
);
1263 rv
= WindowIsEditable(aWindow
, &isEditable
);
1264 NS_ENSURE_SUCCESS(rv
, rv
);
1265 NS_ASSERTION(isEditable
,
1266 "Window is not editable after reattaching editor.");
1273 HTMLEditor
* nsIEditingSession::GetHTMLEditorForWindow(
1274 mozIDOMWindowProxy
* aWindow
) {
1275 if (NS_WARN_IF(!aWindow
)) {
1279 nsCOMPtr
<nsIDocShell
> docShell
=
1280 nsPIDOMWindowOuter::From(aWindow
)->GetDocShell();
1281 if (NS_WARN_IF(!docShell
)) {
1285 return docShell
->GetHTMLEditor();