forked from mirrors/gecko-dev
Backed out changeset 8781a0d1254d (bug 1810141) Backed out changeset 131037295784 (bug 1810141) Backed out changeset 3852fbe290f4 (bug 1810141) Backed out changeset 118f131a524a (bug 1810141) Backed out changeset ab5d76846e10 (bug 1810141) Backed out changeset dce3aa683445 (bug 1810141) Backed out changeset 4dc41d90dbb3 (bug 1810141) Backed out changeset 50b57ba1a061 (bug 1810141) Backed out changeset 569de94781e4 (bug 1810141)
1299 lines
43 KiB
C++
1299 lines
43 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=2 sw=2 et tw=78: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include <string.h> // for nullptr, strcmp
|
|
|
|
#include "imgIContainer.h" // for imgIContainer, etc
|
|
#include "mozilla/ComposerCommandsUpdater.h" // for ComposerCommandsUpdater
|
|
#include "mozilla/FlushType.h" // for FlushType::Frames
|
|
#include "mozilla/HTMLEditor.h" // for HTMLEditor
|
|
#include "mozilla/mozalloc.h" // for operator new
|
|
#include "mozilla/PresShell.h" // for PresShell
|
|
#include "nsAString.h"
|
|
#include "nsBaseCommandController.h" // for nsBaseCommandController
|
|
#include "nsCommandManager.h" // for nsCommandManager
|
|
#include "nsComponentManagerUtils.h" // for do_CreateInstance
|
|
#include "nsContentUtils.h"
|
|
#include "nsDebug.h" // for NS_ENSURE_SUCCESS, etc
|
|
#include "nsDocShell.h" // for nsDocShell
|
|
#include "nsEditingSession.h"
|
|
#include "nsError.h" // for NS_ERROR_FAILURE, NS_OK, etc
|
|
#include "nsIChannel.h" // for nsIChannel
|
|
#include "nsIContentViewer.h" // for nsIContentViewer
|
|
#include "nsIControllers.h" // for nsIControllers
|
|
#include "nsID.h" // for NS_GET_IID, etc
|
|
#include "nsHTMLDocument.h" // for nsHTMLDocument
|
|
#include "nsIDocShell.h" // for nsIDocShell
|
|
#include "mozilla/dom/Document.h" // for Document
|
|
#include "nsIEditor.h" // for nsIEditor
|
|
#include "nsIInterfaceRequestorUtils.h" // for do_GetInterface
|
|
#include "nsIRefreshURI.h" // for nsIRefreshURI
|
|
#include "nsIRequest.h" // for nsIRequest
|
|
#include "nsITimer.h" // for nsITimer, etc
|
|
#include "nsIWeakReference.h" // for nsISupportsWeakReference, etc
|
|
#include "nsIWebNavigation.h" // for nsIWebNavigation
|
|
#include "nsIWebProgress.h" // for nsIWebProgress, etc
|
|
#include "nsLiteralString.h" // for NS_LITERAL_STRING
|
|
#include "nsPIDOMWindow.h" // for nsPIDOMWindow
|
|
#include "nsPresContext.h" // for nsPresContext
|
|
#include "nsReadableUtils.h" // for AppendUTF16toUTF8
|
|
#include "nsStringFwd.h" // for nsString
|
|
#include "mozilla/dom/BrowsingContext.h" // for BrowsingContext
|
|
#include "mozilla/dom/Selection.h" // for AutoHideSelectionChanges, etc
|
|
#include "mozilla/dom/WindowContext.h" // for WindowContext
|
|
#include "nsFrameSelection.h" // for nsFrameSelection
|
|
#include "nsBaseCommandController.h" // for nsBaseCommandController
|
|
#include "mozilla/dom/LoadURIOptionsBinding.h"
|
|
|
|
class nsISupports;
|
|
class nsIURI;
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::dom;
|
|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
nsEditingSession
|
|
|
|
----------------------------------------------------------------------------*/
|
|
nsEditingSession::nsEditingSession()
|
|
: mDoneSetup(false),
|
|
mCanCreateEditor(false),
|
|
mInteractive(false),
|
|
mMakeWholeDocumentEditable(true),
|
|
mDisabledJSAndPlugins(false),
|
|
mScriptsEnabled(true),
|
|
mPluginsEnabled(true),
|
|
mProgressListenerRegistered(false),
|
|
mImageAnimationMode(0),
|
|
mEditorFlags(0),
|
|
mEditorStatus(eEditorOK),
|
|
mBaseCommandControllerId(0),
|
|
mDocStateControllerId(0),
|
|
mHTMLCommandControllerId(0) {}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
~nsEditingSession
|
|
|
|
----------------------------------------------------------------------------*/
|
|
nsEditingSession::~nsEditingSession() {
|
|
// Must cancel previous timer?
|
|
if (mLoadBlankDocTimer) mLoadBlankDocTimer->Cancel();
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS(nsEditingSession, nsIEditingSession, nsIWebProgressListener,
|
|
nsISupportsWeakReference)
|
|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
MakeWindowEditable
|
|
|
|
aEditorType string, "html" "htmlsimple" "text" "textsimple"
|
|
void makeWindowEditable(in nsIDOMWindow aWindow, in string aEditorType,
|
|
in boolean aDoAfterUriLoad,
|
|
in boolean aMakeWholeDocumentEditable,
|
|
in boolean aInteractive);
|
|
----------------------------------------------------------------------------*/
|
|
#define DEFAULT_EDITOR_TYPE "html"
|
|
|
|
NS_IMETHODIMP
|
|
nsEditingSession::MakeWindowEditable(mozIDOMWindowProxy* aWindow,
|
|
const char* aEditorType,
|
|
bool aDoAfterUriLoad,
|
|
bool aMakeWholeDocumentEditable,
|
|
bool aInteractive) {
|
|
mEditorType.Truncate();
|
|
mEditorFlags = 0;
|
|
|
|
NS_ENSURE_TRUE(aWindow, NS_ERROR_FAILURE);
|
|
auto* window = nsPIDOMWindowOuter::From(aWindow);
|
|
|
|
// disable plugins
|
|
nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
|
|
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
|
|
mDocShell = do_GetWeakReference(docShell);
|
|
|
|
mInteractive = aInteractive;
|
|
mMakeWholeDocumentEditable = aMakeWholeDocumentEditable;
|
|
|
|
nsresult rv;
|
|
if (!mInteractive) {
|
|
rv = DisableJSAndPlugins(window->GetCurrentInnerWindow());
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
// Always remove existing editor
|
|
TearDownEditorOnWindow(aWindow);
|
|
|
|
// Tells embedder that startup is in progress
|
|
mEditorStatus = eEditorCreationInProgress;
|
|
|
|
// temporary to set editor type here. we will need different classes soon.
|
|
if (!aEditorType) aEditorType = DEFAULT_EDITOR_TYPE;
|
|
mEditorType = aEditorType;
|
|
|
|
// if all this does is setup listeners and I don't need listeners,
|
|
// can't this step be ignored?? (based on aDoAfterURILoad)
|
|
rv = PrepareForEditing(window);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// set the flag on the docShell to say that it's editable
|
|
rv = docShell->MakeEditable(aDoAfterUriLoad);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Setup commands common to plaintext and html editors,
|
|
// including the document creation observers
|
|
// the first is an editing controller
|
|
rv = SetupEditorCommandController(
|
|
nsBaseCommandController::CreateEditingController, aWindow,
|
|
static_cast<nsIEditingSession*>(this), &mBaseCommandControllerId);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// The second is a controller to monitor doc state,
|
|
// such as creation and "dirty flag"
|
|
rv = SetupEditorCommandController(
|
|
nsBaseCommandController::CreateHTMLEditorDocStateController, aWindow,
|
|
static_cast<nsIEditingSession*>(this), &mDocStateControllerId);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// aDoAfterUriLoad can be false only when making an existing window editable
|
|
if (!aDoAfterUriLoad) {
|
|
rv = SetupEditorOnWindow(MOZ_KnownLive(*window));
|
|
|
|
// mEditorStatus is set to the error reason
|
|
// Since this is used only when editing an existing page,
|
|
// it IS ok to destroy current editor
|
|
if (NS_FAILED(rv)) {
|
|
TearDownEditorOnWindow(aWindow);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
nsresult nsEditingSession::DisableJSAndPlugins(nsPIDOMWindowInner* aWindow) {
|
|
WindowContext* wc = aWindow->GetWindowContext();
|
|
BrowsingContext* bc = wc->GetBrowsingContext();
|
|
|
|
mScriptsEnabled = wc->GetAllowJavascript();
|
|
|
|
MOZ_TRY(wc->SetAllowJavascript(false));
|
|
|
|
// Disable plugins in this document:
|
|
mPluginsEnabled = bc->GetAllowPlugins();
|
|
|
|
MOZ_TRY(bc->SetAllowPlugins(false));
|
|
|
|
mDisabledJSAndPlugins = true;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsEditingSession::RestoreJSAndPlugins(nsPIDOMWindowInner* aWindow) {
|
|
if (!mDisabledJSAndPlugins) {
|
|
return NS_OK;
|
|
}
|
|
|
|
mDisabledJSAndPlugins = false;
|
|
|
|
if (NS_WARN_IF(!aWindow)) {
|
|
// DetachFromWindow may call this method with nullptr.
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
WindowContext* wc = aWindow->GetWindowContext();
|
|
BrowsingContext* bc = wc->GetBrowsingContext();
|
|
|
|
MOZ_TRY(wc->SetAllowJavascript(mScriptsEnabled));
|
|
|
|
// Disable plugins in this document:
|
|
|
|
return bc->SetAllowPlugins(mPluginsEnabled);
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
WindowIsEditable
|
|
|
|
boolean windowIsEditable (in nsIDOMWindow aWindow);
|
|
----------------------------------------------------------------------------*/
|
|
NS_IMETHODIMP
|
|
nsEditingSession::WindowIsEditable(mozIDOMWindowProxy* aWindow,
|
|
bool* outIsEditable) {
|
|
NS_ENSURE_STATE(aWindow);
|
|
nsCOMPtr<nsIDocShell> docShell =
|
|
nsPIDOMWindowOuter::From(aWindow)->GetDocShell();
|
|
NS_ENSURE_STATE(docShell);
|
|
|
|
return docShell->GetEditable(outIsEditable);
|
|
}
|
|
|
|
bool IsSupportedTextType(const nsAString& aMIMEType) {
|
|
// These are MIME types that are automatically parsed as "text/plain"
|
|
// and thus we can edit them as plaintext
|
|
// Note: in older versions, we attempted to convert the mimetype of
|
|
// the network channel for these and "text/xml" to "text/plain",
|
|
// but further investigation reveals that strategy doesn't work
|
|
static constexpr nsLiteralString sSupportedTextTypes[] = {
|
|
u"text/plain"_ns,
|
|
u"text/css"_ns,
|
|
u"text/rdf"_ns,
|
|
u"text/xsl"_ns,
|
|
u"text/javascript"_ns, // obsolete type
|
|
u"text/ecmascript"_ns, // obsolete type
|
|
u"application/javascript"_ns,
|
|
u"application/ecmascript"_ns,
|
|
u"application/x-javascript"_ns, // obsolete type
|
|
u"text/xul"_ns // obsolete type
|
|
};
|
|
|
|
for (const nsLiteralString& supportedTextType : sSupportedTextTypes) {
|
|
if (aMIMEType.Equals(supportedTextType)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
nsresult nsEditingSession::SetupEditorOnWindow(nsPIDOMWindowOuter& aWindow) {
|
|
mDoneSetup = true;
|
|
|
|
// MIME CHECKING
|
|
// must get the content type
|
|
// Note: the doc gets this from the network channel during StartPageLoad,
|
|
// so we don't have to get it from there ourselves
|
|
nsAutoString mimeType;
|
|
|
|
// then lets check the mime type
|
|
if (RefPtr<Document> doc = aWindow.GetDoc()) {
|
|
doc->GetContentType(mimeType);
|
|
|
|
if (IsSupportedTextType(mimeType)) {
|
|
mEditorType.AssignLiteral("text");
|
|
mimeType.AssignLiteral("text/plain");
|
|
} else if (!doc->IsHTMLOrXHTML()) {
|
|
// Neither an acceptable text or html type.
|
|
mEditorStatus = eEditorErrorCantEditMimeType;
|
|
|
|
// Turn editor into HTML -- we will load blank page later
|
|
mEditorType.AssignLiteral("html");
|
|
mimeType.AssignLiteral("text/html");
|
|
}
|
|
|
|
// Flush out frame construction to make sure that the subframe's
|
|
// presshell is set up if it needs to be.
|
|
doc->FlushPendingNotifications(mozilla::FlushType::Frames);
|
|
if (mMakeWholeDocumentEditable) {
|
|
doc->SetEditableFlag(true);
|
|
// Enable usage of the execCommand API
|
|
doc->SetEditingState(Document::EditingState::eDesignMode);
|
|
}
|
|
}
|
|
bool needHTMLController = false;
|
|
|
|
if (mEditorType.EqualsLiteral("textmail")) {
|
|
mEditorFlags = nsIEditor::eEditorPlaintextMask |
|
|
nsIEditor::eEditorEnableWrapHackMask |
|
|
nsIEditor::eEditorMailMask;
|
|
} else if (mEditorType.EqualsLiteral("text")) {
|
|
mEditorFlags =
|
|
nsIEditor::eEditorPlaintextMask | nsIEditor::eEditorEnableWrapHackMask;
|
|
} else if (mEditorType.EqualsLiteral("htmlmail")) {
|
|
if (mimeType.EqualsLiteral("text/html")) {
|
|
needHTMLController = true;
|
|
mEditorFlags = nsIEditor::eEditorMailMask;
|
|
} else {
|
|
// Set the flags back to textplain.
|
|
mEditorFlags = nsIEditor::eEditorPlaintextMask |
|
|
nsIEditor::eEditorEnableWrapHackMask;
|
|
}
|
|
} else {
|
|
// Defaulted to html
|
|
needHTMLController = true;
|
|
}
|
|
|
|
if (mInteractive) {
|
|
mEditorFlags |= nsIEditor::eEditorAllowInteraction;
|
|
}
|
|
|
|
// make the UI state maintainer
|
|
RefPtr<ComposerCommandsUpdater> commandsUpdater =
|
|
new ComposerCommandsUpdater();
|
|
mComposerCommandsUpdater = commandsUpdater;
|
|
|
|
// now init the state maintainer
|
|
// This allows notification of error state
|
|
// even if we don't create an editor
|
|
commandsUpdater->Init(aWindow);
|
|
|
|
if (mEditorStatus != eEditorCreationInProgress) {
|
|
commandsUpdater->OnHTMLEditorCreated();
|
|
|
|
// At this point we have made a final decision that we don't support
|
|
// editing the current document. This is an internal failure state, but
|
|
// we return NS_OK to avoid throwing an exception from the designMode
|
|
// setter for web compatibility. The document editing APIs will tell the
|
|
// developer if editing has been disabled because we're in a document type
|
|
// that doesn't support editing.
|
|
return NS_OK;
|
|
}
|
|
|
|
// Create editor and do other things
|
|
// only if we haven't found some error above,
|
|
const RefPtr<nsDocShell> docShell = nsDocShell::Cast(aWindow.GetDocShell());
|
|
if (NS_WARN_IF(!docShell)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
const RefPtr<PresShell> presShell = docShell->GetPresShell();
|
|
if (NS_WARN_IF(!presShell)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (!mInteractive) {
|
|
// Disable animation of images in this document:
|
|
nsPresContext* presContext = presShell->GetPresContext();
|
|
NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
|
|
|
|
mImageAnimationMode = presContext->ImageAnimationMode();
|
|
presContext->SetImageAnimationMode(imgIContainer::kDontAnimMode);
|
|
}
|
|
|
|
// Hide selection changes during initialization, in order to hide this
|
|
// from web pages.
|
|
RefPtr<nsFrameSelection> fs = presShell->FrameSelection();
|
|
NS_ENSURE_TRUE(fs, NS_ERROR_FAILURE);
|
|
AutoHideSelectionChanges hideSelectionChanges(fs);
|
|
|
|
nsCOMPtr<nsIContentViewer> contentViewer;
|
|
nsresult rv = docShell->GetContentViewer(getter_AddRefs(contentViewer));
|
|
if (NS_FAILED(rv) || NS_WARN_IF(!contentViewer)) {
|
|
NS_WARNING("nsDocShell::GetContentViewer() failed");
|
|
return rv;
|
|
}
|
|
|
|
const RefPtr<Document> doc = contentViewer->GetDocument();
|
|
if (NS_WARN_IF(!doc)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// create and set editor
|
|
// Try to reuse an existing editor
|
|
nsCOMPtr<nsIEditor> editor = do_QueryReferent(mExistingEditor);
|
|
RefPtr<HTMLEditor> htmlEditor = HTMLEditor::GetFrom(editor);
|
|
MOZ_ASSERT_IF(editor, htmlEditor);
|
|
if (htmlEditor) {
|
|
htmlEditor->PreDestroy();
|
|
} else {
|
|
htmlEditor = new HTMLEditor(*doc);
|
|
mExistingEditor =
|
|
do_GetWeakReference(static_cast<nsIEditor*>(htmlEditor.get()));
|
|
}
|
|
// set the editor on the docShell. The docShell now owns it.
|
|
rv = docShell->SetHTMLEditor(htmlEditor);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// setup the HTML editor command controller
|
|
if (needHTMLController) {
|
|
// The third controller takes an nsIEditor as the context
|
|
rv = SetupEditorCommandController(
|
|
nsBaseCommandController::CreateHTMLEditorController, &aWindow,
|
|
static_cast<nsIEditor*>(htmlEditor), &mHTMLCommandControllerId);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
// Set mimetype on editor
|
|
rv = htmlEditor->SetContentsMIMEType(mimeType);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
MOZ_ASSERT(docShell->HasContentViewer());
|
|
MOZ_ASSERT(contentViewer->GetDocument());
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(commandsUpdater == mComposerCommandsUpdater);
|
|
if (MOZ_UNLIKELY(commandsUpdater != mComposerCommandsUpdater)) {
|
|
commandsUpdater = mComposerCommandsUpdater;
|
|
}
|
|
rv = htmlEditor->Init(*doc, *commandsUpdater, mEditorFlags);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
RefPtr<Selection> selection = htmlEditor->GetSelection();
|
|
if (NS_WARN_IF(!selection)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// Set context on all controllers to be the editor
|
|
rv = SetEditorOnControllers(aWindow, htmlEditor);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Everything went fine!
|
|
mEditorStatus = eEditorOK;
|
|
|
|
// This will trigger documentCreation notification
|
|
return htmlEditor->PostCreate();
|
|
}
|
|
|
|
// Removes all listeners and controllers from aWindow and aEditor.
|
|
void nsEditingSession::RemoveListenersAndControllers(
|
|
nsPIDOMWindowOuter* aWindow, HTMLEditor* aHTMLEditor) {
|
|
if (!mComposerCommandsUpdater || !aHTMLEditor) {
|
|
return;
|
|
}
|
|
|
|
// Remove all the listeners
|
|
RefPtr<ComposerCommandsUpdater> composertCommandsUpdater =
|
|
std::move(mComposerCommandsUpdater);
|
|
MOZ_ASSERT(!mComposerCommandsUpdater);
|
|
aHTMLEditor->Detach(*composertCommandsUpdater);
|
|
|
|
// Remove editor controllers from the window now that we're not
|
|
// editing in that window any more.
|
|
RemoveEditorControllers(aWindow);
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
TearDownEditorOnWindow
|
|
|
|
void tearDownEditorOnWindow (in nsIDOMWindow aWindow);
|
|
----------------------------------------------------------------------------*/
|
|
NS_IMETHODIMP
|
|
nsEditingSession::TearDownEditorOnWindow(mozIDOMWindowProxy* aWindow) {
|
|
if (!mDoneSetup) {
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_ENSURE_TRUE(aWindow, NS_ERROR_NULL_POINTER);
|
|
|
|
// Kill any existing reload timer
|
|
if (mLoadBlankDocTimer) {
|
|
mLoadBlankDocTimer->Cancel();
|
|
mLoadBlankDocTimer = nullptr;
|
|
}
|
|
|
|
mDoneSetup = false;
|
|
|
|
// Check if we're turning off editing (from contentEditable or designMode).
|
|
auto* window = nsPIDOMWindowOuter::From(aWindow);
|
|
|
|
RefPtr<Document> doc = window->GetDoc();
|
|
bool stopEditing = doc && doc->IsEditingOn();
|
|
if (stopEditing) {
|
|
RemoveWebProgressListener(window);
|
|
}
|
|
|
|
nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
|
|
NS_ENSURE_STATE(docShell);
|
|
|
|
RefPtr<HTMLEditor> htmlEditor = docShell->GetHTMLEditor();
|
|
if (stopEditing) {
|
|
doc->TearingDownEditor();
|
|
}
|
|
|
|
if (mComposerCommandsUpdater && htmlEditor) {
|
|
// Null out the editor on the controllers first to prevent their weak
|
|
// references from pointing to a destroyed editor.
|
|
SetEditorOnControllers(*window, nullptr);
|
|
}
|
|
|
|
// Null out the editor on the docShell to trigger PreDestroy which
|
|
// needs to happen before document state listeners are removed below.
|
|
docShell->SetEditor(nullptr);
|
|
|
|
RemoveListenersAndControllers(window, htmlEditor);
|
|
|
|
if (stopEditing) {
|
|
// Make things the way they were before we started editing.
|
|
RestoreJSAndPlugins(window->GetCurrentInnerWindow());
|
|
RestoreAnimationMode(window);
|
|
|
|
if (mMakeWholeDocumentEditable) {
|
|
doc->SetEditableFlag(false);
|
|
doc->SetEditingState(Document::EditingState::eOff);
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
GetEditorForFrame
|
|
|
|
nsIEditor getEditorForFrame (in nsIDOMWindow aWindow);
|
|
----------------------------------------------------------------------------*/
|
|
NS_IMETHODIMP
|
|
nsEditingSession::GetEditorForWindow(mozIDOMWindowProxy* aWindow,
|
|
nsIEditor** outEditor) {
|
|
if (NS_WARN_IF(!aWindow)) {
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
nsCOMPtr<nsIEditor> editor = GetHTMLEditorForWindow(aWindow);
|
|
editor.forget(outEditor);
|
|
return NS_OK;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
OnStateChange
|
|
|
|
----------------------------------------------------------------------------*/
|
|
NS_IMETHODIMP
|
|
nsEditingSession::OnStateChange(nsIWebProgress* aWebProgress,
|
|
nsIRequest* aRequest, uint32_t aStateFlags,
|
|
nsresult aStatus) {
|
|
#ifdef NOISY_DOC_LOADING
|
|
nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
|
|
if (channel) {
|
|
nsAutoCString contentType;
|
|
channel->GetContentType(contentType);
|
|
if (!contentType.IsEmpty()) {
|
|
printf(" ++++++ MIMETYPE = %s\n", contentType.get());
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// A Request has started...
|
|
//
|
|
if (aStateFlags & nsIWebProgressListener::STATE_START) {
|
|
#ifdef NOISY_DOC_LOADING
|
|
{
|
|
nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
|
|
if (channel) {
|
|
nsCOMPtr<nsIURI> uri;
|
|
channel->GetURI(getter_AddRefs(uri));
|
|
if (uri) {
|
|
nsCString spec;
|
|
uri->GetSpec(spec);
|
|
printf(" **** STATE_START: CHANNEL URI=%s, flags=%x\n", spec.get(),
|
|
aStateFlags);
|
|
}
|
|
} else {
|
|
printf(" STATE_START: NO CHANNEL flags=%x\n", aStateFlags);
|
|
}
|
|
}
|
|
#endif
|
|
// Page level notification...
|
|
if (aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK) {
|
|
nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
|
|
StartPageLoad(channel);
|
|
#ifdef NOISY_DOC_LOADING
|
|
printf("STATE_START & STATE_IS_NETWORK flags=%x\n", aStateFlags);
|
|
#endif
|
|
}
|
|
|
|
// Document level notification...
|
|
if (aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT &&
|
|
!(aStateFlags & nsIWebProgressListener::STATE_RESTORING)) {
|
|
#ifdef NOISY_DOC_LOADING
|
|
printf("STATE_START & STATE_IS_DOCUMENT flags=%x\n", aStateFlags);
|
|
#endif
|
|
|
|
bool progressIsForTargetDocument =
|
|
IsProgressForTargetDocument(aWebProgress);
|
|
|
|
if (progressIsForTargetDocument) {
|
|
nsCOMPtr<mozIDOMWindowProxy> window;
|
|
aWebProgress->GetDOMWindow(getter_AddRefs(window));
|
|
|
|
auto* piWindow = nsPIDOMWindowOuter::From(window);
|
|
RefPtr<Document> doc = piWindow->GetDoc();
|
|
nsHTMLDocument* htmlDoc =
|
|
doc && doc->IsHTMLOrXHTML() ? doc->AsHTMLDocument() : nullptr;
|
|
if (htmlDoc && doc->IsWriting()) {
|
|
nsAutoString designMode;
|
|
htmlDoc->GetDesignMode(designMode);
|
|
|
|
if (designMode.EqualsLiteral("on")) {
|
|
// This notification is for data coming in through
|
|
// document.open/write/close(), ignore it.
|
|
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
mCanCreateEditor = true;
|
|
StartDocumentLoad(aWebProgress, progressIsForTargetDocument);
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// A Request is being processed
|
|
//
|
|
else if (aStateFlags & nsIWebProgressListener::STATE_TRANSFERRING) {
|
|
if (aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT) {
|
|
// document transfer started
|
|
}
|
|
}
|
|
//
|
|
// Got a redirection
|
|
//
|
|
else if (aStateFlags & nsIWebProgressListener::STATE_REDIRECTING) {
|
|
if (aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT) {
|
|
// got a redirect
|
|
}
|
|
}
|
|
//
|
|
// A network or document Request has finished...
|
|
//
|
|
else if (aStateFlags & nsIWebProgressListener::STATE_STOP) {
|
|
#ifdef NOISY_DOC_LOADING
|
|
{
|
|
nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
|
|
if (channel) {
|
|
nsCOMPtr<nsIURI> uri;
|
|
channel->GetURI(getter_AddRefs(uri));
|
|
if (uri) {
|
|
nsCString spec;
|
|
uri->GetSpec(spec);
|
|
printf(" **** STATE_STOP: CHANNEL URI=%s, flags=%x\n", spec.get(),
|
|
aStateFlags);
|
|
}
|
|
} else {
|
|
printf(" STATE_STOP: NO CHANNEL flags=%x\n", aStateFlags);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Document level notification...
|
|
if (aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT) {
|
|
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
|
|
EndDocumentLoad(aWebProgress, channel, aStatus,
|
|
IsProgressForTargetDocument(aWebProgress));
|
|
#ifdef NOISY_DOC_LOADING
|
|
printf("STATE_STOP & STATE_IS_DOCUMENT flags=%x\n", aStateFlags);
|
|
#endif
|
|
}
|
|
|
|
// Page level notification...
|
|
if (aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK) {
|
|
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
|
|
(void)EndPageLoad(aWebProgress, channel, aStatus);
|
|
#ifdef NOISY_DOC_LOADING
|
|
printf("STATE_STOP & STATE_IS_NETWORK flags=%x\n", aStateFlags);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
OnProgressChange
|
|
|
|
----------------------------------------------------------------------------*/
|
|
NS_IMETHODIMP
|
|
nsEditingSession::OnProgressChange(nsIWebProgress* aWebProgress,
|
|
nsIRequest* aRequest,
|
|
int32_t aCurSelfProgress,
|
|
int32_t aMaxSelfProgress,
|
|
int32_t aCurTotalProgress,
|
|
int32_t aMaxTotalProgress) {
|
|
MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
|
|
return NS_OK;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
OnLocationChange
|
|
|
|
----------------------------------------------------------------------------*/
|
|
NS_IMETHODIMP
|
|
nsEditingSession::OnLocationChange(nsIWebProgress* aWebProgress,
|
|
nsIRequest* aRequest, nsIURI* aURI,
|
|
uint32_t aFlags) {
|
|
nsCOMPtr<mozIDOMWindowProxy> domWindow;
|
|
nsresult rv = aWebProgress->GetDOMWindow(getter_AddRefs(domWindow));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
auto* piWindow = nsPIDOMWindowOuter::From(domWindow);
|
|
|
|
RefPtr<Document> doc = piWindow->GetDoc();
|
|
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
|
|
|
|
doc->SetDocumentURI(aURI);
|
|
|
|
// Notify the location-changed observer that
|
|
// the document URL has changed
|
|
nsIDocShell* docShell = piWindow->GetDocShell();
|
|
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
|
|
|
|
RefPtr<nsCommandManager> commandManager = docShell->GetCommandManager();
|
|
commandManager->CommandStatusChanged("obs_documentLocationChanged");
|
|
return NS_OK;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
OnStatusChange
|
|
|
|
----------------------------------------------------------------------------*/
|
|
NS_IMETHODIMP
|
|
nsEditingSession::OnStatusChange(nsIWebProgress* aWebProgress,
|
|
nsIRequest* aRequest, nsresult aStatus,
|
|
const char16_t* aMessage) {
|
|
MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
|
|
return NS_OK;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
OnSecurityChange
|
|
|
|
----------------------------------------------------------------------------*/
|
|
NS_IMETHODIMP
|
|
nsEditingSession::OnSecurityChange(nsIWebProgress* aWebProgress,
|
|
nsIRequest* aRequest, uint32_t aState) {
|
|
MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
|
|
return NS_OK;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
OnContentBlockingEvent
|
|
|
|
----------------------------------------------------------------------------*/
|
|
NS_IMETHODIMP
|
|
nsEditingSession::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
|
|
nsIRequest* aRequest,
|
|
uint32_t aEvent) {
|
|
MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
|
|
return NS_OK;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
IsProgressForTargetDocument
|
|
|
|
Check that this notification is for our document.
|
|
----------------------------------------------------------------------------*/
|
|
|
|
bool nsEditingSession::IsProgressForTargetDocument(
|
|
nsIWebProgress* aWebProgress) {
|
|
nsCOMPtr<nsIWebProgress> editedWebProgress = do_QueryReferent(mDocShell);
|
|
return editedWebProgress == aWebProgress;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
GetEditorStatus
|
|
|
|
Called during GetCommandStateParams("obs_documentCreated"...)
|
|
to determine if editor was created and document
|
|
was loaded successfully
|
|
----------------------------------------------------------------------------*/
|
|
NS_IMETHODIMP
|
|
nsEditingSession::GetEditorStatus(uint32_t* aStatus) {
|
|
NS_ENSURE_ARG_POINTER(aStatus);
|
|
*aStatus = mEditorStatus;
|
|
return NS_OK;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
StartDocumentLoad
|
|
|
|
Called on start of load in a single frame
|
|
----------------------------------------------------------------------------*/
|
|
nsresult nsEditingSession::StartDocumentLoad(nsIWebProgress* aWebProgress,
|
|
bool aIsToBeMadeEditable) {
|
|
#ifdef NOISY_DOC_LOADING
|
|
printf("======= StartDocumentLoad ========\n");
|
|
#endif
|
|
|
|
NS_ENSURE_ARG_POINTER(aWebProgress);
|
|
|
|
if (aIsToBeMadeEditable) {
|
|
mEditorStatus = eEditorCreationInProgress;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
EndDocumentLoad
|
|
|
|
Called on end of load in a single frame
|
|
----------------------------------------------------------------------------*/
|
|
nsresult nsEditingSession::EndDocumentLoad(nsIWebProgress* aWebProgress,
|
|
nsIChannel* aChannel,
|
|
nsresult aStatus,
|
|
bool aIsToBeMadeEditable) {
|
|
NS_ENSURE_ARG_POINTER(aWebProgress);
|
|
|
|
#ifdef NOISY_DOC_LOADING
|
|
printf("======= EndDocumentLoad ========\n");
|
|
printf("with status %d, ", aStatus);
|
|
nsCOMPtr<nsIURI> uri;
|
|
nsCString spec;
|
|
if (NS_SUCCEEDED(aChannel->GetURI(getter_AddRefs(uri)))) {
|
|
uri->GetSpec(spec);
|
|
printf(" uri %s\n", spec.get());
|
|
}
|
|
#endif
|
|
|
|
// We want to call the base class EndDocumentLoad,
|
|
// but avoid some of the stuff
|
|
// that nsDocShell does (need to refactor).
|
|
|
|
// OK, time to make an editor on this document
|
|
nsCOMPtr<mozIDOMWindowProxy> domWindow;
|
|
aWebProgress->GetDOMWindow(getter_AddRefs(domWindow));
|
|
NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
|
|
|
|
// Set the error state -- we will create an editor
|
|
// anyway and load empty doc later
|
|
if (aIsToBeMadeEditable && aStatus == NS_ERROR_FILE_NOT_FOUND) {
|
|
mEditorStatus = eEditorErrorFileNotFound;
|
|
}
|
|
|
|
auto* window = nsPIDOMWindowOuter::From(domWindow);
|
|
nsIDocShell* docShell = window->GetDocShell();
|
|
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); // better error handling?
|
|
|
|
// cancel refresh from meta tags
|
|
// we need to make sure that all pages in editor (whether editable or not)
|
|
// can't refresh contents being edited
|
|
nsCOMPtr<nsIRefreshURI> refreshURI = do_QueryInterface(docShell);
|
|
if (refreshURI) {
|
|
refreshURI->CancelRefreshURITimers();
|
|
}
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
// did someone set the flag to make this shell editable?
|
|
if (aIsToBeMadeEditable && mCanCreateEditor) {
|
|
bool makeEditable;
|
|
docShell->GetEditable(&makeEditable);
|
|
|
|
if (makeEditable) {
|
|
// To keep pre Gecko 1.9 behavior, setup editor always when
|
|
// mMakeWholeDocumentEditable.
|
|
bool needsSetup = false;
|
|
if (mMakeWholeDocumentEditable) {
|
|
needsSetup = true;
|
|
} else {
|
|
// do we already have an editor here?
|
|
needsSetup = !docShell->GetHTMLEditor();
|
|
}
|
|
|
|
if (needsSetup) {
|
|
mCanCreateEditor = false;
|
|
rv = SetupEditorOnWindow(MOZ_KnownLive(*window));
|
|
if (NS_FAILED(rv)) {
|
|
// If we had an error, setup timer to load a blank page later
|
|
if (mLoadBlankDocTimer) {
|
|
// Must cancel previous timer?
|
|
mLoadBlankDocTimer->Cancel();
|
|
mLoadBlankDocTimer = nullptr;
|
|
}
|
|
|
|
rv = NS_NewTimerWithFuncCallback(getter_AddRefs(mLoadBlankDocTimer),
|
|
nsEditingSession::TimerCallback,
|
|
static_cast<void*>(mDocShell.get()),
|
|
10, nsITimer::TYPE_ONE_SHOT,
|
|
"nsEditingSession::EndDocumentLoad");
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
mEditorStatus = eEditorCreationInProgress;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
void nsEditingSession::TimerCallback(nsITimer* aTimer, void* aClosure) {
|
|
nsCOMPtr<nsIDocShell> docShell =
|
|
do_QueryReferent(static_cast<nsIWeakReference*>(aClosure));
|
|
if (docShell) {
|
|
nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(docShell));
|
|
if (webNav) {
|
|
LoadURIOptions loadURIOptions;
|
|
loadURIOptions.mTriggeringPrincipal =
|
|
nsContentUtils::GetSystemPrincipal();
|
|
webNav->LoadURI(u"about:blank"_ns, loadURIOptions);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
StartPageLoad
|
|
|
|
Called on start load of the entire page (incl. subframes)
|
|
----------------------------------------------------------------------------*/
|
|
nsresult nsEditingSession::StartPageLoad(nsIChannel* aChannel) {
|
|
#ifdef NOISY_DOC_LOADING
|
|
printf("======= StartPageLoad ========\n");
|
|
#endif
|
|
return NS_OK;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
EndPageLoad
|
|
|
|
Called on end load of the entire page (incl. subframes)
|
|
----------------------------------------------------------------------------*/
|
|
nsresult nsEditingSession::EndPageLoad(nsIWebProgress* aWebProgress,
|
|
nsIChannel* aChannel, nsresult aStatus) {
|
|
#ifdef NOISY_DOC_LOADING
|
|
printf("======= EndPageLoad ========\n");
|
|
printf(" with status %d, ", aStatus);
|
|
nsCOMPtr<nsIURI> uri;
|
|
nsCString spec;
|
|
if (NS_SUCCEEDED(aChannel->GetURI(getter_AddRefs(uri)))) {
|
|
uri->GetSpec(spec);
|
|
printf("uri %s\n", spec.get());
|
|
}
|
|
|
|
nsAutoCString contentType;
|
|
aChannel->GetContentType(contentType);
|
|
if (!contentType.IsEmpty()) {
|
|
printf(" flags = %d, status = %d, MIMETYPE = %s\n", mEditorFlags,
|
|
mEditorStatus, contentType.get());
|
|
}
|
|
#endif
|
|
|
|
// Set the error state -- we will create an editor anyway
|
|
// and load empty doc later
|
|
if (aStatus == NS_ERROR_FILE_NOT_FOUND) {
|
|
mEditorStatus = eEditorErrorFileNotFound;
|
|
}
|
|
|
|
nsCOMPtr<mozIDOMWindowProxy> domWindow;
|
|
aWebProgress->GetDOMWindow(getter_AddRefs(domWindow));
|
|
|
|
nsIDocShell* docShell =
|
|
domWindow ? nsPIDOMWindowOuter::From(domWindow)->GetDocShell() : nullptr;
|
|
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
|
|
|
|
// cancel refresh from meta tags
|
|
// we need to make sure that all pages in editor (whether editable or not)
|
|
// can't refresh contents being edited
|
|
nsCOMPtr<nsIRefreshURI> refreshURI = do_QueryInterface(docShell);
|
|
if (refreshURI) {
|
|
refreshURI->CancelRefreshURITimers();
|
|
}
|
|
|
|
#if 0
|
|
// Shouldn't we do this when we want to edit sub-frames?
|
|
return MakeWindowEditable(domWindow, "html", false, mInteractive);
|
|
#else
|
|
return NS_OK;
|
|
#endif
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
PrepareForEditing
|
|
|
|
Set up this editing session for one or more editors
|
|
----------------------------------------------------------------------------*/
|
|
nsresult nsEditingSession::PrepareForEditing(nsPIDOMWindowOuter* aWindow) {
|
|
if (mProgressListenerRegistered) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsIDocShell* docShell = aWindow ? aWindow->GetDocShell() : nullptr;
|
|
|
|
// register callback
|
|
nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
|
|
NS_ENSURE_TRUE(webProgress, NS_ERROR_FAILURE);
|
|
|
|
nsresult rv = webProgress->AddProgressListener(
|
|
this, (nsIWebProgress::NOTIFY_STATE_NETWORK |
|
|
nsIWebProgress::NOTIFY_STATE_DOCUMENT |
|
|
nsIWebProgress::NOTIFY_LOCATION));
|
|
|
|
mProgressListenerRegistered = NS_SUCCEEDED(rv);
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
SetupEditorCommandController
|
|
|
|
Create a command controller, append to controllers,
|
|
get and return the controller ID, and set the context
|
|
----------------------------------------------------------------------------*/
|
|
nsresult nsEditingSession::SetupEditorCommandController(
|
|
nsEditingSession::ControllerCreatorFn aControllerCreatorFn,
|
|
mozIDOMWindowProxy* aWindow, nsISupports* aContext,
|
|
uint32_t* aControllerId) {
|
|
NS_ENSURE_ARG_POINTER(aControllerCreatorFn);
|
|
NS_ENSURE_ARG_POINTER(aWindow);
|
|
NS_ENSURE_ARG_POINTER(aContext);
|
|
NS_ENSURE_ARG_POINTER(aControllerId);
|
|
|
|
auto* piWindow = nsPIDOMWindowOuter::From(aWindow);
|
|
MOZ_ASSERT(piWindow);
|
|
|
|
nsCOMPtr<nsIControllers> controllers;
|
|
nsresult rv = piWindow->GetControllers(getter_AddRefs(controllers));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// We only have to create each singleton controller once
|
|
// We know this has happened once we have a controllerId value
|
|
if (!*aControllerId) {
|
|
RefPtr<nsBaseCommandController> commandController = aControllerCreatorFn();
|
|
NS_ENSURE_TRUE(commandController, NS_ERROR_FAILURE);
|
|
|
|
// We must insert at head of the list to be sure our
|
|
// controller is found before other implementations
|
|
// (e.g., not-implemented versions by browser)
|
|
rv = controllers->InsertControllerAt(0, commandController);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Remember the ID for the controller
|
|
rv = controllers->GetControllerId(commandController, aControllerId);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
// Set the context
|
|
return SetContextOnControllerById(controllers, aContext, *aControllerId);
|
|
}
|
|
|
|
nsresult nsEditingSession::SetEditorOnControllers(nsPIDOMWindowOuter& aWindow,
|
|
HTMLEditor* aEditor) {
|
|
nsCOMPtr<nsIControllers> controllers;
|
|
nsresult rv = aWindow.GetControllers(getter_AddRefs(controllers));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsISupports> editorAsISupports = static_cast<nsIEditor*>(aEditor);
|
|
if (mBaseCommandControllerId) {
|
|
rv = SetContextOnControllerById(controllers, editorAsISupports,
|
|
mBaseCommandControllerId);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
if (mDocStateControllerId) {
|
|
rv = SetContextOnControllerById(controllers, editorAsISupports,
|
|
mDocStateControllerId);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
if (mHTMLCommandControllerId) {
|
|
rv = SetContextOnControllerById(controllers, editorAsISupports,
|
|
mHTMLCommandControllerId);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult nsEditingSession::SetContextOnControllerById(
|
|
nsIControllers* aControllers, nsISupports* aContext, uint32_t aID) {
|
|
NS_ENSURE_ARG_POINTER(aControllers);
|
|
|
|
// aContext can be null (when destroying editor)
|
|
nsCOMPtr<nsIController> controller;
|
|
aControllers->GetControllerById(aID, getter_AddRefs(controller));
|
|
|
|
// ok with nil controller
|
|
nsCOMPtr<nsIControllerContext> editorController =
|
|
do_QueryInterface(controller);
|
|
NS_ENSURE_TRUE(editorController, NS_ERROR_FAILURE);
|
|
|
|
return editorController->SetCommandContext(aContext);
|
|
}
|
|
|
|
void nsEditingSession::RemoveEditorControllers(nsPIDOMWindowOuter* aWindow) {
|
|
// Remove editor controllers from the aWindow, call when we're
|
|
// tearing down/detaching editor.
|
|
|
|
nsCOMPtr<nsIControllers> controllers;
|
|
if (aWindow) {
|
|
aWindow->GetControllers(getter_AddRefs(controllers));
|
|
}
|
|
|
|
if (controllers) {
|
|
nsCOMPtr<nsIController> controller;
|
|
if (mBaseCommandControllerId) {
|
|
controllers->GetControllerById(mBaseCommandControllerId,
|
|
getter_AddRefs(controller));
|
|
if (controller) {
|
|
controllers->RemoveController(controller);
|
|
}
|
|
}
|
|
|
|
if (mDocStateControllerId) {
|
|
controllers->GetControllerById(mDocStateControllerId,
|
|
getter_AddRefs(controller));
|
|
if (controller) {
|
|
controllers->RemoveController(controller);
|
|
}
|
|
}
|
|
|
|
if (mHTMLCommandControllerId) {
|
|
controllers->GetControllerById(mHTMLCommandControllerId,
|
|
getter_AddRefs(controller));
|
|
if (controller) {
|
|
controllers->RemoveController(controller);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clear IDs to trigger creation of new controllers.
|
|
mBaseCommandControllerId = 0;
|
|
mDocStateControllerId = 0;
|
|
mHTMLCommandControllerId = 0;
|
|
}
|
|
|
|
void nsEditingSession::RemoveWebProgressListener(nsPIDOMWindowOuter* aWindow) {
|
|
nsIDocShell* docShell = aWindow ? aWindow->GetDocShell() : nullptr;
|
|
nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
|
|
if (webProgress) {
|
|
webProgress->RemoveProgressListener(this);
|
|
mProgressListenerRegistered = false;
|
|
}
|
|
}
|
|
|
|
void nsEditingSession::RestoreAnimationMode(nsPIDOMWindowOuter* aWindow) {
|
|
if (mInteractive) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIDocShell> docShell = aWindow ? aWindow->GetDocShell() : nullptr;
|
|
NS_ENSURE_TRUE_VOID(docShell);
|
|
RefPtr<PresShell> presShell = docShell->GetPresShell();
|
|
if (NS_WARN_IF(!presShell)) {
|
|
return;
|
|
}
|
|
nsPresContext* presContext = presShell->GetPresContext();
|
|
NS_ENSURE_TRUE_VOID(presContext);
|
|
|
|
presContext->SetImageAnimationMode(mImageAnimationMode);
|
|
}
|
|
|
|
nsresult nsEditingSession::DetachFromWindow(nsPIDOMWindowOuter* aWindow) {
|
|
NS_ENSURE_TRUE(mDoneSetup, NS_OK);
|
|
|
|
NS_ASSERTION(mComposerCommandsUpdater,
|
|
"mComposerCommandsUpdater should exist.");
|
|
|
|
// Kill any existing reload timer
|
|
if (mLoadBlankDocTimer) {
|
|
mLoadBlankDocTimer->Cancel();
|
|
mLoadBlankDocTimer = nullptr;
|
|
}
|
|
|
|
// Remove controllers, webprogress listener, and otherwise
|
|
// make things the way they were before we started editing.
|
|
RemoveEditorControllers(aWindow);
|
|
RemoveWebProgressListener(aWindow);
|
|
RestoreJSAndPlugins(aWindow->GetCurrentInnerWindow());
|
|
RestoreAnimationMode(aWindow);
|
|
|
|
// Kill our weak reference to our original window, in case
|
|
// it changes on restore, or otherwise dies.
|
|
mDocShell = nullptr;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsEditingSession::ReattachToWindow(nsPIDOMWindowOuter* aWindow) {
|
|
NS_ENSURE_TRUE(mDoneSetup, NS_OK);
|
|
NS_ENSURE_TRUE(aWindow, NS_ERROR_FAILURE);
|
|
|
|
NS_ASSERTION(mComposerCommandsUpdater,
|
|
"mComposerCommandsUpdater should exist.");
|
|
|
|
// Imitate nsEditorDocShell::MakeEditable() to reattach the
|
|
// old editor to the window.
|
|
nsresult rv;
|
|
|
|
nsIDocShell* docShell = aWindow->GetDocShell();
|
|
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
|
|
mDocShell = do_GetWeakReference(docShell);
|
|
|
|
// Disable plugins.
|
|
if (!mInteractive) {
|
|
rv = DisableJSAndPlugins(aWindow->GetCurrentInnerWindow());
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
// Tells embedder that startup is in progress.
|
|
mEditorStatus = eEditorCreationInProgress;
|
|
|
|
// Adds back web progress listener.
|
|
rv = PrepareForEditing(aWindow);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Setup the command controllers again.
|
|
rv = SetupEditorCommandController(
|
|
nsBaseCommandController::CreateEditingController, aWindow,
|
|
static_cast<nsIEditingSession*>(this), &mBaseCommandControllerId);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = SetupEditorCommandController(
|
|
nsBaseCommandController::CreateHTMLEditorDocStateController, aWindow,
|
|
static_cast<nsIEditingSession*>(this), &mDocStateControllerId);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (mComposerCommandsUpdater) {
|
|
mComposerCommandsUpdater->Init(*aWindow);
|
|
}
|
|
|
|
// Get editor
|
|
RefPtr<HTMLEditor> htmlEditor = GetHTMLEditorForWindow(aWindow);
|
|
if (NS_WARN_IF(!htmlEditor)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (!mInteractive) {
|
|
// Disable animation of images in this document:
|
|
RefPtr<PresShell> presShell = docShell->GetPresShell();
|
|
if (NS_WARN_IF(!presShell)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
nsPresContext* presContext = presShell->GetPresContext();
|
|
NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
|
|
|
|
mImageAnimationMode = presContext->ImageAnimationMode();
|
|
presContext->SetImageAnimationMode(imgIContainer::kDontAnimMode);
|
|
}
|
|
|
|
// The third controller takes an nsIEditor as the context
|
|
rv = SetupEditorCommandController(
|
|
nsBaseCommandController::CreateHTMLEditorController, aWindow,
|
|
static_cast<nsIEditor*>(htmlEditor.get()), &mHTMLCommandControllerId);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Set context on all controllers to be the editor
|
|
rv = SetEditorOnControllers(*aWindow, htmlEditor);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
#ifdef DEBUG
|
|
{
|
|
bool isEditable;
|
|
rv = WindowIsEditable(aWindow, &isEditable);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
NS_ASSERTION(isEditable,
|
|
"Window is not editable after reattaching editor.");
|
|
}
|
|
#endif // DEBUG
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
HTMLEditor* nsIEditingSession::GetHTMLEditorForWindow(
|
|
mozIDOMWindowProxy* aWindow) {
|
|
if (NS_WARN_IF(!aWindow)) {
|
|
return nullptr;
|
|
}
|
|
|
|
nsCOMPtr<nsIDocShell> docShell =
|
|
nsPIDOMWindowOuter::From(aWindow)->GetDocShell();
|
|
if (NS_WARN_IF(!docShell)) {
|
|
return nullptr;
|
|
}
|
|
|
|
return docShell->GetHTMLEditor();
|
|
}
|