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),
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
);
119 mDocShell
= do_GetWeakReference(docShell
);
121 mInteractive
= aInteractive
;
122 mMakeWholeDocumentEditable
= aMakeWholeDocumentEditable
;
126 rv
= DisableJSAndPlugins(window
->GetCurrentInnerWindow());
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(nsPIDOMWindowInner
* aWindow
) {
179 WindowContext
* wc
= aWindow
->GetWindowContext();
180 BrowsingContext
* bc
= wc
->GetBrowsingContext();
182 mScriptsEnabled
= wc
->GetAllowJavascript();
184 MOZ_TRY(wc
->SetAllowJavascript(false));
186 // Disable plugins in this document:
187 mPluginsEnabled
= bc
->GetAllowPlugins();
189 MOZ_TRY(bc
->SetAllowPlugins(false));
191 mDisabledJSAndPlugins
= true;
196 nsresult
nsEditingSession::RestoreJSAndPlugins(nsPIDOMWindowInner
* aWindow
) {
197 if (!mDisabledJSAndPlugins
) {
201 mDisabledJSAndPlugins
= false;
203 if (NS_WARN_IF(!aWindow
)) {
204 // DetachFromWindow may call this method with nullptr.
205 return NS_ERROR_FAILURE
;
208 WindowContext
* wc
= aWindow
->GetWindowContext();
209 BrowsingContext
* bc
= wc
->GetBrowsingContext();
211 MOZ_TRY(wc
->SetAllowJavascript(mScriptsEnabled
));
213 // Disable plugins in this document:
215 return bc
->SetAllowPlugins(mPluginsEnabled
);
218 /*---------------------------------------------------------------------------
222 boolean windowIsEditable (in nsIDOMWindow aWindow);
223 ----------------------------------------------------------------------------*/
225 nsEditingSession::WindowIsEditable(mozIDOMWindowProxy
* aWindow
,
226 bool* outIsEditable
) {
227 NS_ENSURE_STATE(aWindow
);
228 nsCOMPtr
<nsIDocShell
> docShell
=
229 nsPIDOMWindowOuter::From(aWindow
)->GetDocShell();
230 NS_ENSURE_STATE(docShell
);
232 return docShell
->GetEditable(outIsEditable
);
235 bool IsSupportedTextType(const nsAString
& aMIMEType
) {
236 // These are MIME types that are automatically parsed as "text/plain"
237 // and thus we can edit them as plaintext
238 // Note: in older versions, we attempted to convert the mimetype of
239 // the network channel for these and "text/xml" to "text/plain",
240 // but further investigation reveals that strategy doesn't work
241 static constexpr nsLiteralString sSupportedTextTypes
[] = {
246 u
"text/javascript"_ns
, // obsolete type
247 u
"text/ecmascript"_ns
, // obsolete type
248 u
"application/javascript"_ns
,
249 u
"application/ecmascript"_ns
,
250 u
"application/x-javascript"_ns
, // obsolete type
251 u
"text/xul"_ns
// obsolete type
254 for (const nsLiteralString
& supportedTextType
: sSupportedTextTypes
) {
255 if (aMIMEType
.Equals(supportedTextType
)) {
263 nsresult
nsEditingSession::SetupEditorOnWindow(nsPIDOMWindowOuter
& aWindow
) {
267 // must get the content type
268 // Note: the doc gets this from the network channel during StartPageLoad,
269 // so we don't have to get it from there ourselves
270 nsAutoString mimeType
;
272 // then lets check the mime type
273 if (RefPtr
<Document
> doc
= aWindow
.GetDoc()) {
274 doc
->GetContentType(mimeType
);
276 if (IsSupportedTextType(mimeType
)) {
277 mEditorType
.AssignLiteral("text");
278 mimeType
.AssignLiteral("text/plain");
279 } else if (!doc
->IsHTMLOrXHTML()) {
280 // Neither an acceptable text or html type.
281 mEditorStatus
= eEditorErrorCantEditMimeType
;
283 // Turn editor into HTML -- we will load blank page later
284 mEditorType
.AssignLiteral("html");
285 mimeType
.AssignLiteral("text/html");
288 // Flush out frame construction to make sure that the subframe's
289 // presshell is set up if it needs to be.
290 doc
->FlushPendingNotifications(mozilla::FlushType::Frames
);
291 if (mMakeWholeDocumentEditable
) {
292 doc
->SetEditableFlag(true);
293 // Enable usage of the execCommand API
294 doc
->SetEditingState(Document::EditingState::eDesignMode
);
297 bool needHTMLController
= false;
299 if (mEditorType
.EqualsLiteral("textmail")) {
300 mEditorFlags
= nsIEditor::eEditorPlaintextMask
|
301 nsIEditor::eEditorEnableWrapHackMask
|
302 nsIEditor::eEditorMailMask
;
303 } else if (mEditorType
.EqualsLiteral("text")) {
305 nsIEditor::eEditorPlaintextMask
| nsIEditor::eEditorEnableWrapHackMask
;
306 } else if (mEditorType
.EqualsLiteral("htmlmail")) {
307 if (mimeType
.EqualsLiteral("text/html")) {
308 needHTMLController
= true;
309 mEditorFlags
= nsIEditor::eEditorMailMask
;
311 // Set the flags back to textplain.
312 mEditorFlags
= nsIEditor::eEditorPlaintextMask
|
313 nsIEditor::eEditorEnableWrapHackMask
;
317 needHTMLController
= true;
321 mEditorFlags
|= nsIEditor::eEditorAllowInteraction
;
324 // make the UI state maintainer
325 RefPtr
<ComposerCommandsUpdater
> commandsUpdater
=
326 new ComposerCommandsUpdater();
327 mComposerCommandsUpdater
= commandsUpdater
;
329 // now init the state maintainer
330 // This allows notification of error state
331 // even if we don't create an editor
332 commandsUpdater
->Init(aWindow
);
334 if (mEditorStatus
!= eEditorCreationInProgress
) {
335 commandsUpdater
->OnHTMLEditorCreated();
337 // At this point we have made a final decision that we don't support
338 // editing the current document. This is an internal failure state, but
339 // we return NS_OK to avoid throwing an exception from the designMode
340 // setter for web compatibility. The document editing APIs will tell the
341 // developer if editing has been disabled because we're in a document type
342 // that doesn't support editing.
346 // Create editor and do other things
347 // only if we haven't found some error above,
348 const RefPtr
<nsDocShell
> docShell
= nsDocShell::Cast(aWindow
.GetDocShell());
349 if (NS_WARN_IF(!docShell
)) {
350 return NS_ERROR_FAILURE
;
352 const RefPtr
<PresShell
> presShell
= docShell
->GetPresShell();
353 if (NS_WARN_IF(!presShell
)) {
354 return NS_ERROR_FAILURE
;
358 // Disable animation of images in this document:
359 nsPresContext
* presContext
= presShell
->GetPresContext();
360 NS_ENSURE_TRUE(presContext
, NS_ERROR_FAILURE
);
362 mImageAnimationMode
= presContext
->ImageAnimationMode();
363 presContext
->SetImageAnimationMode(imgIContainer::kDontAnimMode
);
366 // Hide selection changes during initialization, in order to hide this
368 RefPtr
<nsFrameSelection
> fs
= presShell
->FrameSelection();
369 NS_ENSURE_TRUE(fs
, NS_ERROR_FAILURE
);
370 AutoHideSelectionChanges
hideSelectionChanges(fs
);
372 nsCOMPtr
<nsIDocumentViewer
> viewer
;
373 nsresult rv
= docShell
->GetDocViewer(getter_AddRefs(viewer
));
374 if (NS_FAILED(rv
) || NS_WARN_IF(!viewer
)) {
375 NS_WARNING("nsDocShell::GetDocViewer() failed");
379 const RefPtr
<Document
> doc
= viewer
->GetDocument();
380 if (NS_WARN_IF(!doc
)) {
381 return NS_ERROR_FAILURE
;
384 // create and set editor
385 // Try to reuse an existing editor
386 nsCOMPtr
<nsIEditor
> editor
= do_QueryReferent(mExistingEditor
);
387 RefPtr
<HTMLEditor
> htmlEditor
= HTMLEditor::GetFrom(editor
);
388 MOZ_ASSERT_IF(editor
, htmlEditor
);
390 htmlEditor
->PreDestroy();
392 htmlEditor
= new HTMLEditor(*doc
);
394 do_GetWeakReference(static_cast<nsIEditor
*>(htmlEditor
.get()));
396 // set the editor on the docShell. The docShell now owns it.
397 rv
= docShell
->SetHTMLEditor(htmlEditor
);
398 NS_ENSURE_SUCCESS(rv
, rv
);
400 // setup the HTML editor command controller
401 if (needHTMLController
) {
402 // The third controller takes an nsIEditor as the context
403 rv
= SetupEditorCommandController(
404 nsBaseCommandController::CreateHTMLEditorController
, &aWindow
,
405 static_cast<nsIEditor
*>(htmlEditor
), &mHTMLCommandControllerId
);
406 NS_ENSURE_SUCCESS(rv
, rv
);
409 // Set mimetype on editor
410 rv
= htmlEditor
->SetContentsMIMEType(mimeType
);
411 NS_ENSURE_SUCCESS(rv
, rv
);
413 MOZ_ASSERT(docShell
->HasDocumentViewer());
414 MOZ_ASSERT(viewer
->GetDocument());
416 MOZ_DIAGNOSTIC_ASSERT(commandsUpdater
== mComposerCommandsUpdater
);
417 if (MOZ_UNLIKELY(commandsUpdater
!= mComposerCommandsUpdater
)) {
418 commandsUpdater
= mComposerCommandsUpdater
;
420 rv
= htmlEditor
->Init(*doc
, *commandsUpdater
, mEditorFlags
);
421 NS_ENSURE_SUCCESS(rv
, rv
);
423 RefPtr
<Selection
> selection
= htmlEditor
->GetSelection();
424 if (NS_WARN_IF(!selection
)) {
425 return NS_ERROR_FAILURE
;
428 // Set context on all controllers to be the editor
429 rv
= SetEditorOnControllers(aWindow
, htmlEditor
);
430 NS_ENSURE_SUCCESS(rv
, rv
);
432 // Everything went fine!
433 mEditorStatus
= eEditorOK
;
435 // This will trigger documentCreation notification
436 return htmlEditor
->PostCreate();
439 // Removes all listeners and controllers from aWindow and aEditor.
440 void nsEditingSession::RemoveListenersAndControllers(
441 nsPIDOMWindowOuter
* aWindow
, HTMLEditor
* aHTMLEditor
) {
442 if (!mComposerCommandsUpdater
|| !aHTMLEditor
) {
446 // Remove all the listeners
447 RefPtr
<ComposerCommandsUpdater
> composertCommandsUpdater
=
448 std::move(mComposerCommandsUpdater
);
449 MOZ_ASSERT(!mComposerCommandsUpdater
);
450 aHTMLEditor
->Detach(*composertCommandsUpdater
);
452 // Remove editor controllers from the window now that we're not
453 // editing in that window any more.
454 RemoveEditorControllers(aWindow
);
457 /*---------------------------------------------------------------------------
459 TearDownEditorOnWindow
461 void tearDownEditorOnWindow (in nsIDOMWindow aWindow);
462 ----------------------------------------------------------------------------*/
464 nsEditingSession::TearDownEditorOnWindow(mozIDOMWindowProxy
* aWindow
) {
469 NS_ENSURE_TRUE(aWindow
, NS_ERROR_NULL_POINTER
);
471 // Kill any existing reload timer
472 if (mLoadBlankDocTimer
) {
473 mLoadBlankDocTimer
->Cancel();
474 mLoadBlankDocTimer
= nullptr;
479 // Check if we're turning off editing (from contentEditable or designMode).
480 auto* window
= nsPIDOMWindowOuter::From(aWindow
);
482 RefPtr
<Document
> doc
= window
->GetDoc();
483 bool stopEditing
= doc
&& doc
->IsEditingOn();
485 RemoveWebProgressListener(window
);
488 nsCOMPtr
<nsIDocShell
> docShell
= window
->GetDocShell();
489 NS_ENSURE_STATE(docShell
);
491 RefPtr
<HTMLEditor
> htmlEditor
= docShell
->GetHTMLEditor();
493 doc
->TearingDownEditor();
496 if (mComposerCommandsUpdater
&& htmlEditor
) {
497 // Null out the editor on the controllers first to prevent their weak
498 // references from pointing to a destroyed editor.
499 SetEditorOnControllers(*window
, nullptr);
502 // Null out the editor on the docShell to trigger PreDestroy which
503 // needs to happen before document state listeners are removed below.
504 docShell
->SetEditor(nullptr);
506 RemoveListenersAndControllers(window
, htmlEditor
);
509 // Make things the way they were before we started editing.
510 RestoreJSAndPlugins(window
->GetCurrentInnerWindow());
511 RestoreAnimationMode(window
);
513 if (mMakeWholeDocumentEditable
) {
514 doc
->SetEditableFlag(false);
515 doc
->SetEditingState(Document::EditingState::eOff
);
522 /*---------------------------------------------------------------------------
526 nsIEditor getEditorForFrame (in nsIDOMWindow aWindow);
527 ----------------------------------------------------------------------------*/
529 nsEditingSession::GetEditorForWindow(mozIDOMWindowProxy
* aWindow
,
530 nsIEditor
** outEditor
) {
531 if (NS_WARN_IF(!aWindow
)) {
532 return NS_ERROR_INVALID_ARG
;
534 nsCOMPtr
<nsIEditor
> editor
= GetHTMLEditorForWindow(aWindow
);
535 editor
.forget(outEditor
);
539 /*---------------------------------------------------------------------------
543 ----------------------------------------------------------------------------*/
545 nsEditingSession::OnStateChange(nsIWebProgress
* aWebProgress
,
546 nsIRequest
* aRequest
, uint32_t aStateFlags
,
548 #ifdef NOISY_DOC_LOADING
549 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(aRequest
));
551 nsAutoCString contentType
;
552 channel
->GetContentType(contentType
);
553 if (!contentType
.IsEmpty()) {
554 printf(" ++++++ MIMETYPE = %s\n", contentType
.get());
560 // A Request has started...
562 if (aStateFlags
& nsIWebProgressListener::STATE_START
) {
563 #ifdef NOISY_DOC_LOADING
565 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(aRequest
));
567 nsCOMPtr
<nsIURI
> uri
;
568 channel
->GetURI(getter_AddRefs(uri
));
572 printf(" **** STATE_START: CHANNEL URI=%s, flags=%x\n", spec
.get(),
576 printf(" STATE_START: NO CHANNEL flags=%x\n", aStateFlags
);
580 // Page level notification...
581 if (aStateFlags
& nsIWebProgressListener::STATE_IS_NETWORK
) {
582 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(aRequest
));
583 StartPageLoad(channel
);
584 #ifdef NOISY_DOC_LOADING
585 printf("STATE_START & STATE_IS_NETWORK flags=%x\n", aStateFlags
);
589 // Document level notification...
590 if (aStateFlags
& nsIWebProgressListener::STATE_IS_DOCUMENT
&&
591 !(aStateFlags
& nsIWebProgressListener::STATE_RESTORING
)) {
592 #ifdef NOISY_DOC_LOADING
593 printf("STATE_START & STATE_IS_DOCUMENT flags=%x\n", aStateFlags
);
596 bool progressIsForTargetDocument
=
597 IsProgressForTargetDocument(aWebProgress
);
599 if (progressIsForTargetDocument
) {
600 nsCOMPtr
<mozIDOMWindowProxy
> window
;
601 aWebProgress
->GetDOMWindow(getter_AddRefs(window
));
603 auto* piWindow
= nsPIDOMWindowOuter::From(window
);
604 RefPtr
<Document
> doc
= piWindow
->GetDoc();
605 nsHTMLDocument
* htmlDoc
=
606 doc
&& doc
->IsHTMLOrXHTML() ? doc
->AsHTMLDocument() : nullptr;
607 if (htmlDoc
&& doc
->IsWriting()) {
608 nsAutoString designMode
;
609 htmlDoc
->GetDesignMode(designMode
);
611 if (designMode
.EqualsLiteral("on")) {
612 // This notification is for data coming in through
613 // document.open/write/close(), ignore it.
619 mCanCreateEditor
= true;
620 StartDocumentLoad(aWebProgress
, progressIsForTargetDocument
);
625 // A Request is being processed
627 else if (aStateFlags
& nsIWebProgressListener::STATE_TRANSFERRING
) {
628 if (aStateFlags
& nsIWebProgressListener::STATE_IS_DOCUMENT
) {
629 // document transfer started
635 else if (aStateFlags
& nsIWebProgressListener::STATE_REDIRECTING
) {
636 if (aStateFlags
& nsIWebProgressListener::STATE_IS_DOCUMENT
) {
641 // A network or document Request has finished...
643 else if (aStateFlags
& nsIWebProgressListener::STATE_STOP
) {
644 #ifdef NOISY_DOC_LOADING
646 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(aRequest
));
648 nsCOMPtr
<nsIURI
> uri
;
649 channel
->GetURI(getter_AddRefs(uri
));
653 printf(" **** STATE_STOP: CHANNEL URI=%s, flags=%x\n", spec
.get(),
657 printf(" STATE_STOP: NO CHANNEL flags=%x\n", aStateFlags
);
662 // Document level notification...
663 if (aStateFlags
& nsIWebProgressListener::STATE_IS_DOCUMENT
) {
664 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
665 EndDocumentLoad(aWebProgress
, channel
, aStatus
,
666 IsProgressForTargetDocument(aWebProgress
));
667 #ifdef NOISY_DOC_LOADING
668 printf("STATE_STOP & STATE_IS_DOCUMENT flags=%x\n", aStateFlags
);
672 // Page level notification...
673 if (aStateFlags
& nsIWebProgressListener::STATE_IS_NETWORK
) {
674 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
675 (void)EndPageLoad(aWebProgress
, channel
, aStatus
);
676 #ifdef NOISY_DOC_LOADING
677 printf("STATE_STOP & STATE_IS_NETWORK flags=%x\n", aStateFlags
);
685 /*---------------------------------------------------------------------------
689 ----------------------------------------------------------------------------*/
691 nsEditingSession::OnProgressChange(nsIWebProgress
* aWebProgress
,
692 nsIRequest
* aRequest
,
693 int32_t aCurSelfProgress
,
694 int32_t aMaxSelfProgress
,
695 int32_t aCurTotalProgress
,
696 int32_t aMaxTotalProgress
) {
697 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
701 /*---------------------------------------------------------------------------
705 ----------------------------------------------------------------------------*/
707 nsEditingSession::OnLocationChange(nsIWebProgress
* aWebProgress
,
708 nsIRequest
* aRequest
, nsIURI
* aURI
,
710 nsCOMPtr
<mozIDOMWindowProxy
> domWindow
;
711 nsresult rv
= aWebProgress
->GetDOMWindow(getter_AddRefs(domWindow
));
712 NS_ENSURE_SUCCESS(rv
, rv
);
714 auto* piWindow
= nsPIDOMWindowOuter::From(domWindow
);
716 RefPtr
<Document
> doc
= piWindow
->GetDoc();
717 NS_ENSURE_TRUE(doc
, NS_ERROR_FAILURE
);
719 doc
->SetDocumentURI(aURI
);
721 // Notify the location-changed observer that
722 // the document URL has changed
723 nsIDocShell
* docShell
= piWindow
->GetDocShell();
724 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
);
726 RefPtr
<nsCommandManager
> commandManager
= docShell
->GetCommandManager();
727 commandManager
->CommandStatusChanged("obs_documentLocationChanged");
731 /*---------------------------------------------------------------------------
735 ----------------------------------------------------------------------------*/
737 nsEditingSession::OnStatusChange(nsIWebProgress
* aWebProgress
,
738 nsIRequest
* aRequest
, nsresult aStatus
,
739 const char16_t
* aMessage
) {
740 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
744 /*---------------------------------------------------------------------------
748 ----------------------------------------------------------------------------*/
750 nsEditingSession::OnSecurityChange(nsIWebProgress
* aWebProgress
,
751 nsIRequest
* aRequest
, uint32_t aState
) {
752 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
756 /*---------------------------------------------------------------------------
758 OnContentBlockingEvent
760 ----------------------------------------------------------------------------*/
762 nsEditingSession::OnContentBlockingEvent(nsIWebProgress
* aWebProgress
,
763 nsIRequest
* aRequest
,
765 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
769 /*---------------------------------------------------------------------------
771 IsProgressForTargetDocument
773 Check that this notification is for our document.
774 ----------------------------------------------------------------------------*/
776 bool nsEditingSession::IsProgressForTargetDocument(
777 nsIWebProgress
* aWebProgress
) {
778 nsCOMPtr
<nsIWebProgress
> editedWebProgress
= do_QueryReferent(mDocShell
);
779 return editedWebProgress
== aWebProgress
;
782 /*---------------------------------------------------------------------------
786 Called during GetCommandStateParams("obs_documentCreated"...)
787 to determine if editor was created and document
788 was loaded successfully
789 ----------------------------------------------------------------------------*/
791 nsEditingSession::GetEditorStatus(uint32_t* aStatus
) {
792 NS_ENSURE_ARG_POINTER(aStatus
);
793 *aStatus
= mEditorStatus
;
797 /*---------------------------------------------------------------------------
801 Called on start of load in a single frame
802 ----------------------------------------------------------------------------*/
803 nsresult
nsEditingSession::StartDocumentLoad(nsIWebProgress
* aWebProgress
,
804 bool aIsToBeMadeEditable
) {
805 #ifdef NOISY_DOC_LOADING
806 printf("======= StartDocumentLoad ========\n");
809 NS_ENSURE_ARG_POINTER(aWebProgress
);
811 if (aIsToBeMadeEditable
) {
812 mEditorStatus
= eEditorCreationInProgress
;
818 /*---------------------------------------------------------------------------
822 Called on end of load in a single frame
823 ----------------------------------------------------------------------------*/
824 nsresult
nsEditingSession::EndDocumentLoad(nsIWebProgress
* aWebProgress
,
825 nsIChannel
* aChannel
,
827 bool aIsToBeMadeEditable
) {
828 NS_ENSURE_ARG_POINTER(aWebProgress
);
830 #ifdef NOISY_DOC_LOADING
831 printf("======= EndDocumentLoad ========\n");
832 printf("with status %d, ", aStatus
);
833 nsCOMPtr
<nsIURI
> uri
;
835 if (NS_SUCCEEDED(aChannel
->GetURI(getter_AddRefs(uri
)))) {
837 printf(" uri %s\n", spec
.get());
841 // We want to call the base class EndDocumentLoad,
842 // but avoid some of the stuff
843 // that nsDocShell does (need to refactor).
845 // OK, time to make an editor on this document
846 nsCOMPtr
<mozIDOMWindowProxy
> domWindow
;
847 aWebProgress
->GetDOMWindow(getter_AddRefs(domWindow
));
848 NS_ENSURE_TRUE(domWindow
, NS_ERROR_FAILURE
);
850 // Set the error state -- we will create an editor
851 // anyway and load empty doc later
852 if (aIsToBeMadeEditable
&& aStatus
== NS_ERROR_FILE_NOT_FOUND
) {
853 mEditorStatus
= eEditorErrorFileNotFound
;
856 auto* window
= nsPIDOMWindowOuter::From(domWindow
);
857 nsIDocShell
* docShell
= window
->GetDocShell();
858 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
); // better error handling?
860 // cancel refresh from meta tags
861 // we need to make sure that all pages in editor (whether editable or not)
862 // can't refresh contents being edited
863 nsCOMPtr
<nsIRefreshURI
> refreshURI
= do_QueryInterface(docShell
);
865 refreshURI
->CancelRefreshURITimers();
870 // did someone set the flag to make this shell editable?
871 if (aIsToBeMadeEditable
&& mCanCreateEditor
) {
873 docShell
->GetEditable(&makeEditable
);
876 // To keep pre Gecko 1.9 behavior, setup editor always when
877 // mMakeWholeDocumentEditable.
878 bool needsSetup
= false;
879 if (mMakeWholeDocumentEditable
) {
882 // do we already have an editor here?
883 needsSetup
= !docShell
->GetHTMLEditor();
887 mCanCreateEditor
= false;
888 rv
= SetupEditorOnWindow(MOZ_KnownLive(*window
));
890 // If we had an error, setup timer to load a blank page later
891 if (mLoadBlankDocTimer
) {
892 // Must cancel previous timer?
893 mLoadBlankDocTimer
->Cancel();
894 mLoadBlankDocTimer
= nullptr;
897 rv
= NS_NewTimerWithFuncCallback(getter_AddRefs(mLoadBlankDocTimer
),
898 nsEditingSession::TimerCallback
,
899 static_cast<void*>(mDocShell
.get()),
900 10, nsITimer::TYPE_ONE_SHOT
,
901 "nsEditingSession::EndDocumentLoad");
902 NS_ENSURE_SUCCESS(rv
, rv
);
904 mEditorStatus
= eEditorCreationInProgress
;
912 void nsEditingSession::TimerCallback(nsITimer
* aTimer
, void* aClosure
) {
913 nsCOMPtr
<nsIDocShell
> docShell
=
914 do_QueryReferent(static_cast<nsIWeakReference
*>(aClosure
));
916 nsCOMPtr
<nsIWebNavigation
> webNav(do_QueryInterface(docShell
));
918 LoadURIOptions loadURIOptions
;
919 loadURIOptions
.mTriggeringPrincipal
=
920 nsContentUtils::GetSystemPrincipal();
921 nsCOMPtr
<nsIURI
> uri
;
922 MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri
), "about:blank"_ns
));
923 webNav
->LoadURI(uri
, loadURIOptions
);
928 /*---------------------------------------------------------------------------
932 Called on start load of the entire page (incl. subframes)
933 ----------------------------------------------------------------------------*/
934 nsresult
nsEditingSession::StartPageLoad(nsIChannel
* aChannel
) {
935 #ifdef NOISY_DOC_LOADING
936 printf("======= StartPageLoad ========\n");
941 /*---------------------------------------------------------------------------
945 Called on end load of the entire page (incl. subframes)
946 ----------------------------------------------------------------------------*/
947 nsresult
nsEditingSession::EndPageLoad(nsIWebProgress
* aWebProgress
,
948 nsIChannel
* aChannel
, nsresult aStatus
) {
949 #ifdef NOISY_DOC_LOADING
950 printf("======= EndPageLoad ========\n");
951 printf(" with status %d, ", aStatus
);
952 nsCOMPtr
<nsIURI
> uri
;
954 if (NS_SUCCEEDED(aChannel
->GetURI(getter_AddRefs(uri
)))) {
956 printf("uri %s\n", spec
.get());
959 nsAutoCString contentType
;
960 aChannel
->GetContentType(contentType
);
961 if (!contentType
.IsEmpty()) {
962 printf(" flags = %d, status = %d, MIMETYPE = %s\n", mEditorFlags
,
963 mEditorStatus
, contentType
.get());
967 // Set the error state -- we will create an editor anyway
968 // and load empty doc later
969 if (aStatus
== NS_ERROR_FILE_NOT_FOUND
) {
970 mEditorStatus
= eEditorErrorFileNotFound
;
973 nsCOMPtr
<mozIDOMWindowProxy
> domWindow
;
974 aWebProgress
->GetDOMWindow(getter_AddRefs(domWindow
));
976 nsIDocShell
* docShell
=
977 domWindow
? nsPIDOMWindowOuter::From(domWindow
)->GetDocShell() : nullptr;
978 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
);
980 // cancel refresh from meta tags
981 // we need to make sure that all pages in editor (whether editable or not)
982 // can't refresh contents being edited
983 nsCOMPtr
<nsIRefreshURI
> refreshURI
= do_QueryInterface(docShell
);
985 refreshURI
->CancelRefreshURITimers();
989 // Shouldn't we do this when we want to edit sub-frames?
990 return MakeWindowEditable(domWindow
, "html", false, mInteractive
);
996 /*---------------------------------------------------------------------------
1000 Set up this editing session for one or more editors
1001 ----------------------------------------------------------------------------*/
1002 nsresult
nsEditingSession::PrepareForEditing(nsPIDOMWindowOuter
* aWindow
) {
1003 if (mProgressListenerRegistered
) {
1007 nsIDocShell
* docShell
= aWindow
? aWindow
->GetDocShell() : nullptr;
1009 // register callback
1010 nsCOMPtr
<nsIWebProgress
> webProgress
= do_GetInterface(docShell
);
1011 NS_ENSURE_TRUE(webProgress
, NS_ERROR_FAILURE
);
1013 nsresult rv
= webProgress
->AddProgressListener(
1014 this, (nsIWebProgress::NOTIFY_STATE_NETWORK
|
1015 nsIWebProgress::NOTIFY_STATE_DOCUMENT
|
1016 nsIWebProgress::NOTIFY_LOCATION
));
1018 mProgressListenerRegistered
= NS_SUCCEEDED(rv
);
1023 /*---------------------------------------------------------------------------
1025 SetupEditorCommandController
1027 Create a command controller, append to controllers,
1028 get and return the controller ID, and set the context
1029 ----------------------------------------------------------------------------*/
1030 nsresult
nsEditingSession::SetupEditorCommandController(
1031 nsEditingSession::ControllerCreatorFn aControllerCreatorFn
,
1032 mozIDOMWindowProxy
* aWindow
, nsISupports
* aContext
,
1033 uint32_t* aControllerId
) {
1034 NS_ENSURE_ARG_POINTER(aControllerCreatorFn
);
1035 NS_ENSURE_ARG_POINTER(aWindow
);
1036 NS_ENSURE_ARG_POINTER(aContext
);
1037 NS_ENSURE_ARG_POINTER(aControllerId
);
1039 auto* piWindow
= nsPIDOMWindowOuter::From(aWindow
);
1040 MOZ_ASSERT(piWindow
);
1042 nsCOMPtr
<nsIControllers
> controllers
;
1043 nsresult rv
= piWindow
->GetControllers(getter_AddRefs(controllers
));
1044 NS_ENSURE_SUCCESS(rv
, rv
);
1046 // We only have to create each singleton controller once
1047 // We know this has happened once we have a controllerId value
1048 if (!*aControllerId
) {
1049 RefPtr
<nsBaseCommandController
> commandController
= aControllerCreatorFn();
1050 NS_ENSURE_TRUE(commandController
, NS_ERROR_FAILURE
);
1052 // We must insert at head of the list to be sure our
1053 // controller is found before other implementations
1054 // (e.g., not-implemented versions by browser)
1055 rv
= controllers
->InsertControllerAt(0, commandController
);
1056 NS_ENSURE_SUCCESS(rv
, rv
);
1058 // Remember the ID for the controller
1059 rv
= controllers
->GetControllerId(commandController
, aControllerId
);
1060 NS_ENSURE_SUCCESS(rv
, rv
);
1064 return SetContextOnControllerById(controllers
, aContext
, *aControllerId
);
1067 nsresult
nsEditingSession::SetEditorOnControllers(nsPIDOMWindowOuter
& aWindow
,
1068 HTMLEditor
* aEditor
) {
1069 nsCOMPtr
<nsIControllers
> controllers
;
1070 nsresult rv
= aWindow
.GetControllers(getter_AddRefs(controllers
));
1071 NS_ENSURE_SUCCESS(rv
, rv
);
1073 nsCOMPtr
<nsISupports
> editorAsISupports
= static_cast<nsIEditor
*>(aEditor
);
1074 if (mBaseCommandControllerId
) {
1075 rv
= SetContextOnControllerById(controllers
, editorAsISupports
,
1076 mBaseCommandControllerId
);
1077 NS_ENSURE_SUCCESS(rv
, rv
);
1080 if (mDocStateControllerId
) {
1081 rv
= SetContextOnControllerById(controllers
, editorAsISupports
,
1082 mDocStateControllerId
);
1083 NS_ENSURE_SUCCESS(rv
, rv
);
1086 if (mHTMLCommandControllerId
) {
1087 rv
= SetContextOnControllerById(controllers
, editorAsISupports
,
1088 mHTMLCommandControllerId
);
1094 nsresult
nsEditingSession::SetContextOnControllerById(
1095 nsIControllers
* aControllers
, nsISupports
* aContext
, uint32_t aID
) {
1096 NS_ENSURE_ARG_POINTER(aControllers
);
1098 // aContext can be null (when destroying editor)
1099 nsCOMPtr
<nsIController
> controller
;
1100 aControllers
->GetControllerById(aID
, getter_AddRefs(controller
));
1102 // ok with nil controller
1103 nsCOMPtr
<nsIControllerContext
> editorController
=
1104 do_QueryInterface(controller
);
1105 NS_ENSURE_TRUE(editorController
, NS_ERROR_FAILURE
);
1107 return editorController
->SetCommandContext(aContext
);
1110 void nsEditingSession::RemoveEditorControllers(nsPIDOMWindowOuter
* aWindow
) {
1111 // Remove editor controllers from the aWindow, call when we're
1112 // tearing down/detaching editor.
1114 nsCOMPtr
<nsIControllers
> controllers
;
1116 aWindow
->GetControllers(getter_AddRefs(controllers
));
1120 nsCOMPtr
<nsIController
> controller
;
1121 if (mBaseCommandControllerId
) {
1122 controllers
->GetControllerById(mBaseCommandControllerId
,
1123 getter_AddRefs(controller
));
1125 controllers
->RemoveController(controller
);
1129 if (mDocStateControllerId
) {
1130 controllers
->GetControllerById(mDocStateControllerId
,
1131 getter_AddRefs(controller
));
1133 controllers
->RemoveController(controller
);
1137 if (mHTMLCommandControllerId
) {
1138 controllers
->GetControllerById(mHTMLCommandControllerId
,
1139 getter_AddRefs(controller
));
1141 controllers
->RemoveController(controller
);
1146 // Clear IDs to trigger creation of new controllers.
1147 mBaseCommandControllerId
= 0;
1148 mDocStateControllerId
= 0;
1149 mHTMLCommandControllerId
= 0;
1152 void nsEditingSession::RemoveWebProgressListener(nsPIDOMWindowOuter
* aWindow
) {
1153 nsIDocShell
* docShell
= aWindow
? aWindow
->GetDocShell() : nullptr;
1154 nsCOMPtr
<nsIWebProgress
> webProgress
= do_GetInterface(docShell
);
1156 webProgress
->RemoveProgressListener(this);
1157 mProgressListenerRegistered
= false;
1161 void nsEditingSession::RestoreAnimationMode(nsPIDOMWindowOuter
* aWindow
) {
1166 nsCOMPtr
<nsIDocShell
> docShell
= aWindow
? aWindow
->GetDocShell() : nullptr;
1167 NS_ENSURE_TRUE_VOID(docShell
);
1168 RefPtr
<PresShell
> presShell
= docShell
->GetPresShell();
1169 if (NS_WARN_IF(!presShell
)) {
1172 nsPresContext
* presContext
= presShell
->GetPresContext();
1173 NS_ENSURE_TRUE_VOID(presContext
);
1175 presContext
->SetImageAnimationMode(mImageAnimationMode
);
1178 nsresult
nsEditingSession::DetachFromWindow(nsPIDOMWindowOuter
* aWindow
) {
1179 NS_ENSURE_TRUE(mDoneSetup
, NS_OK
);
1181 NS_ASSERTION(mComposerCommandsUpdater
,
1182 "mComposerCommandsUpdater should exist.");
1184 // Kill any existing reload timer
1185 if (mLoadBlankDocTimer
) {
1186 mLoadBlankDocTimer
->Cancel();
1187 mLoadBlankDocTimer
= nullptr;
1190 // Remove controllers, webprogress listener, and otherwise
1191 // make things the way they were before we started editing.
1192 RemoveEditorControllers(aWindow
);
1193 RemoveWebProgressListener(aWindow
);
1194 RestoreJSAndPlugins(aWindow
->GetCurrentInnerWindow());
1195 RestoreAnimationMode(aWindow
);
1197 // Kill our weak reference to our original window, in case
1198 // it changes on restore, or otherwise dies.
1199 mDocShell
= nullptr;
1204 nsresult
nsEditingSession::ReattachToWindow(nsPIDOMWindowOuter
* aWindow
) {
1205 NS_ENSURE_TRUE(mDoneSetup
, NS_OK
);
1206 NS_ENSURE_TRUE(aWindow
, NS_ERROR_FAILURE
);
1208 NS_ASSERTION(mComposerCommandsUpdater
,
1209 "mComposerCommandsUpdater should exist.");
1211 // Imitate nsEditorDocShell::MakeEditable() to reattach the
1212 // old editor to the window.
1215 nsIDocShell
* docShell
= aWindow
->GetDocShell();
1216 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
);
1217 mDocShell
= do_GetWeakReference(docShell
);
1220 if (!mInteractive
) {
1221 rv
= DisableJSAndPlugins(aWindow
->GetCurrentInnerWindow());
1222 NS_ENSURE_SUCCESS(rv
, rv
);
1225 // Tells embedder that startup is in progress.
1226 mEditorStatus
= eEditorCreationInProgress
;
1228 // Adds back web progress listener.
1229 rv
= PrepareForEditing(aWindow
);
1230 NS_ENSURE_SUCCESS(rv
, rv
);
1232 // Setup the command controllers again.
1233 rv
= SetupEditorCommandController(
1234 nsBaseCommandController::CreateEditingController
, aWindow
,
1235 static_cast<nsIEditingSession
*>(this), &mBaseCommandControllerId
);
1236 NS_ENSURE_SUCCESS(rv
, rv
);
1238 rv
= SetupEditorCommandController(
1239 nsBaseCommandController::CreateHTMLEditorDocStateController
, aWindow
,
1240 static_cast<nsIEditingSession
*>(this), &mDocStateControllerId
);
1241 NS_ENSURE_SUCCESS(rv
, rv
);
1243 if (mComposerCommandsUpdater
) {
1244 mComposerCommandsUpdater
->Init(*aWindow
);
1248 RefPtr
<HTMLEditor
> htmlEditor
= GetHTMLEditorForWindow(aWindow
);
1249 if (NS_WARN_IF(!htmlEditor
)) {
1250 return NS_ERROR_FAILURE
;
1253 if (!mInteractive
) {
1254 // Disable animation of images in this document:
1255 RefPtr
<PresShell
> presShell
= docShell
->GetPresShell();
1256 if (NS_WARN_IF(!presShell
)) {
1257 return NS_ERROR_FAILURE
;
1259 nsPresContext
* presContext
= presShell
->GetPresContext();
1260 NS_ENSURE_TRUE(presContext
, NS_ERROR_FAILURE
);
1262 mImageAnimationMode
= presContext
->ImageAnimationMode();
1263 presContext
->SetImageAnimationMode(imgIContainer::kDontAnimMode
);
1266 // The third controller takes an nsIEditor as the context
1267 rv
= SetupEditorCommandController(
1268 nsBaseCommandController::CreateHTMLEditorController
, aWindow
,
1269 static_cast<nsIEditor
*>(htmlEditor
.get()), &mHTMLCommandControllerId
);
1270 NS_ENSURE_SUCCESS(rv
, rv
);
1272 // Set context on all controllers to be the editor
1273 rv
= SetEditorOnControllers(*aWindow
, htmlEditor
);
1274 NS_ENSURE_SUCCESS(rv
, rv
);
1279 rv
= WindowIsEditable(aWindow
, &isEditable
);
1280 NS_ENSURE_SUCCESS(rv
, rv
);
1281 NS_ASSERTION(isEditable
,
1282 "Window is not editable after reattaching editor.");
1289 HTMLEditor
* nsIEditingSession::GetHTMLEditorForWindow(
1290 mozIDOMWindowProxy
* aWindow
) {
1291 if (NS_WARN_IF(!aWindow
)) {
1295 nsCOMPtr
<nsIDocShell
> docShell
=
1296 nsPIDOMWindowOuter::From(aWindow
)->GetDocShell();
1297 if (NS_WARN_IF(!docShell
)) {
1301 return docShell
->GetHTMLEditor();