editor/composer/src/nsEditingSession.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 sw=2 et tw=78: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include <string.h> // for nullptr, strcmp
michael@0 8
michael@0 9 #include "imgIContainer.h" // for imgIContainer, etc
michael@0 10 #include "mozFlushType.h" // for mozFlushType::Flush_Frames
michael@0 11 #include "mozilla/mozalloc.h" // for operator new
michael@0 12 #include "nsAString.h"
michael@0 13 #include "nsComponentManagerUtils.h" // for do_CreateInstance
michael@0 14 #include "nsComposerCommandsUpdater.h" // for nsComposerCommandsUpdater
michael@0 15 #include "nsDebug.h" // for NS_ENSURE_SUCCESS, etc
michael@0 16 #include "nsEditingSession.h"
michael@0 17 #include "nsError.h" // for NS_ERROR_FAILURE, NS_OK, etc
michael@0 18 #include "nsIChannel.h" // for nsIChannel
michael@0 19 #include "nsICommandManager.h" // for nsICommandManager
michael@0 20 #include "nsIContentViewer.h" // for nsIContentViewer
michael@0 21 #include "nsIController.h" // for nsIController
michael@0 22 #include "nsIControllerContext.h" // for nsIControllerContext
michael@0 23 #include "nsIControllers.h" // for nsIControllers
michael@0 24 #include "nsID.h" // for NS_GET_IID, etc
michael@0 25 #include "nsIDOMDocument.h" // for nsIDOMDocument
michael@0 26 #include "nsIDOMHTMLDocument.h" // for nsIDOMHTMLDocument
michael@0 27 #include "nsIDOMWindow.h" // for nsIDOMWindow
michael@0 28 #include "nsIDOMWindowUtils.h" // for nsIDOMWindowUtils
michael@0 29 #include "nsIDocShell.h" // for nsIDocShell
michael@0 30 #include "nsIDocument.h" // for nsIDocument
michael@0 31 #include "nsIDocumentStateListener.h"
michael@0 32 #include "nsIEditor.h" // for nsIEditor
michael@0 33 #include "nsIHTMLDocument.h" // for nsIHTMLDocument, etc
michael@0 34 #include "nsIInterfaceRequestorUtils.h" // for do_GetInterface
michael@0 35 #include "nsIPlaintextEditor.h" // for nsIPlaintextEditor, etc
michael@0 36 #include "nsIRefreshURI.h" // for nsIRefreshURI
michael@0 37 #include "nsIRequest.h" // for nsIRequest
michael@0 38 #include "nsISelection.h" // for nsISelection
michael@0 39 #include "nsISelectionPrivate.h" // for nsISelectionPrivate
michael@0 40 #include "nsITimer.h" // for nsITimer, etc
michael@0 41 #include "nsITransactionManager.h" // for nsITransactionManager
michael@0 42 #include "nsIWeakReference.h" // for nsISupportsWeakReference, etc
michael@0 43 #include "nsIWebNavigation.h" // for nsIWebNavigation
michael@0 44 #include "nsIWebProgress.h" // for nsIWebProgress, etc
michael@0 45 #include "nsLiteralString.h" // for NS_LITERAL_STRING
michael@0 46 #include "nsPICommandUpdater.h" // for nsPICommandUpdater
michael@0 47 #include "nsPIDOMWindow.h" // for nsPIDOMWindow
michael@0 48 #include "nsReadableUtils.h" // for AppendUTF16toUTF8
michael@0 49 #include "nsStringFwd.h" // for nsAFlatString
michael@0 50
michael@0 51 class nsISupports;
michael@0 52 class nsIURI;
michael@0 53
michael@0 54 /*---------------------------------------------------------------------------
michael@0 55
michael@0 56 nsEditingSession
michael@0 57
michael@0 58 ----------------------------------------------------------------------------*/
michael@0 59 nsEditingSession::nsEditingSession()
michael@0 60 : mDoneSetup(false)
michael@0 61 , mCanCreateEditor(false)
michael@0 62 , mInteractive(false)
michael@0 63 , mMakeWholeDocumentEditable(true)
michael@0 64 , mDisabledJSAndPlugins(false)
michael@0 65 , mScriptsEnabled(true)
michael@0 66 , mPluginsEnabled(true)
michael@0 67 , mProgressListenerRegistered(false)
michael@0 68 , mImageAnimationMode(0)
michael@0 69 , mEditorFlags(0)
michael@0 70 , mEditorStatus(eEditorOK)
michael@0 71 , mBaseCommandControllerId(0)
michael@0 72 , mDocStateControllerId(0)
michael@0 73 , mHTMLCommandControllerId(0)
michael@0 74 {
michael@0 75 }
michael@0 76
michael@0 77 /*---------------------------------------------------------------------------
michael@0 78
michael@0 79 ~nsEditingSession
michael@0 80
michael@0 81 ----------------------------------------------------------------------------*/
michael@0 82 nsEditingSession::~nsEditingSession()
michael@0 83 {
michael@0 84 // Must cancel previous timer?
michael@0 85 if (mLoadBlankDocTimer)
michael@0 86 mLoadBlankDocTimer->Cancel();
michael@0 87 }
michael@0 88
michael@0 89 NS_IMPL_ISUPPORTS(nsEditingSession, nsIEditingSession, nsIWebProgressListener,
michael@0 90 nsISupportsWeakReference)
michael@0 91
michael@0 92 /*---------------------------------------------------------------------------
michael@0 93
michael@0 94 MakeWindowEditable
michael@0 95
michael@0 96 aEditorType string, "html" "htmlsimple" "text" "textsimple"
michael@0 97 void makeWindowEditable(in nsIDOMWindow aWindow, in string aEditorType,
michael@0 98 in boolean aDoAfterUriLoad,
michael@0 99 in boolean aMakeWholeDocumentEditable,
michael@0 100 in boolean aInteractive);
michael@0 101 ----------------------------------------------------------------------------*/
michael@0 102 #define DEFAULT_EDITOR_TYPE "html"
michael@0 103
michael@0 104 NS_IMETHODIMP
michael@0 105 nsEditingSession::MakeWindowEditable(nsIDOMWindow *aWindow,
michael@0 106 const char *aEditorType,
michael@0 107 bool aDoAfterUriLoad,
michael@0 108 bool aMakeWholeDocumentEditable,
michael@0 109 bool aInteractive)
michael@0 110 {
michael@0 111 mEditorType.Truncate();
michael@0 112 mEditorFlags = 0;
michael@0 113
michael@0 114 // disable plugins
michael@0 115 nsCOMPtr<nsIDocShell> docShell = GetDocShellFromWindow(aWindow);
michael@0 116 NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
michael@0 117
michael@0 118 mDocShell = do_GetWeakReference(docShell);
michael@0 119 mInteractive = aInteractive;
michael@0 120 mMakeWholeDocumentEditable = aMakeWholeDocumentEditable;
michael@0 121
michael@0 122 nsresult rv;
michael@0 123 if (!mInteractive) {
michael@0 124 rv = DisableJSAndPlugins(aWindow);
michael@0 125 NS_ENSURE_SUCCESS(rv, rv);
michael@0 126 }
michael@0 127
michael@0 128 // Always remove existing editor
michael@0 129 TearDownEditorOnWindow(aWindow);
michael@0 130
michael@0 131 // Tells embedder that startup is in progress
michael@0 132 mEditorStatus = eEditorCreationInProgress;
michael@0 133
michael@0 134 //temporary to set editor type here. we will need different classes soon.
michael@0 135 if (!aEditorType)
michael@0 136 aEditorType = DEFAULT_EDITOR_TYPE;
michael@0 137 mEditorType = aEditorType;
michael@0 138
michael@0 139 // if all this does is setup listeners and I don't need listeners,
michael@0 140 // can't this step be ignored?? (based on aDoAfterURILoad)
michael@0 141 rv = PrepareForEditing(aWindow);
michael@0 142 NS_ENSURE_SUCCESS(rv, rv);
michael@0 143
michael@0 144 // set the flag on the docShell to say that it's editable
michael@0 145 rv = docShell->MakeEditable(aDoAfterUriLoad);
michael@0 146 NS_ENSURE_SUCCESS(rv, rv);
michael@0 147
michael@0 148 // Setup commands common to plaintext and html editors,
michael@0 149 // including the document creation observers
michael@0 150 // the first is an editing controller
michael@0 151 rv = SetupEditorCommandController("@mozilla.org/editor/editingcontroller;1",
michael@0 152 aWindow,
michael@0 153 static_cast<nsIEditingSession*>(this),
michael@0 154 &mBaseCommandControllerId);
michael@0 155 NS_ENSURE_SUCCESS(rv, rv);
michael@0 156
michael@0 157 // The second is a controller to monitor doc state,
michael@0 158 // such as creation and "dirty flag"
michael@0 159 rv = SetupEditorCommandController("@mozilla.org/editor/editordocstatecontroller;1",
michael@0 160 aWindow,
michael@0 161 static_cast<nsIEditingSession*>(this),
michael@0 162 &mDocStateControllerId);
michael@0 163 NS_ENSURE_SUCCESS(rv, rv);
michael@0 164
michael@0 165 // aDoAfterUriLoad can be false only when making an existing window editable
michael@0 166 if (!aDoAfterUriLoad)
michael@0 167 {
michael@0 168 rv = SetupEditorOnWindow(aWindow);
michael@0 169
michael@0 170 // mEditorStatus is set to the error reason
michael@0 171 // Since this is used only when editing an existing page,
michael@0 172 // it IS ok to destroy current editor
michael@0 173 if (NS_FAILED(rv))
michael@0 174 TearDownEditorOnWindow(aWindow);
michael@0 175 }
michael@0 176 return rv;
michael@0 177 }
michael@0 178
michael@0 179 NS_IMETHODIMP
michael@0 180 nsEditingSession::DisableJSAndPlugins(nsIDOMWindow *aWindow)
michael@0 181 {
michael@0 182 nsIDocShell *docShell = GetDocShellFromWindow(aWindow);
michael@0 183 NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
michael@0 184
michael@0 185 bool tmp;
michael@0 186 nsresult rv = docShell->GetAllowJavascript(&tmp);
michael@0 187 NS_ENSURE_SUCCESS(rv, rv);
michael@0 188
michael@0 189 mScriptsEnabled = tmp;
michael@0 190
michael@0 191 rv = docShell->SetAllowJavascript(false);
michael@0 192 NS_ENSURE_SUCCESS(rv, rv);
michael@0 193
michael@0 194 // Disable plugins in this document:
michael@0 195 mPluginsEnabled = docShell->PluginsAllowedInCurrentDoc();
michael@0 196
michael@0 197 rv = docShell->SetAllowPlugins(false);
michael@0 198 NS_ENSURE_SUCCESS(rv, rv);
michael@0 199
michael@0 200 mDisabledJSAndPlugins = true;
michael@0 201
michael@0 202 return NS_OK;
michael@0 203 }
michael@0 204
michael@0 205 NS_IMETHODIMP
michael@0 206 nsEditingSession::RestoreJSAndPlugins(nsIDOMWindow *aWindow)
michael@0 207 {
michael@0 208 NS_ENSURE_TRUE(mDisabledJSAndPlugins, NS_OK);
michael@0 209
michael@0 210 mDisabledJSAndPlugins = false;
michael@0 211
michael@0 212 nsIDocShell *docShell = GetDocShellFromWindow(aWindow);
michael@0 213 NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
michael@0 214
michael@0 215 nsresult rv = docShell->SetAllowJavascript(mScriptsEnabled);
michael@0 216 NS_ENSURE_SUCCESS(rv, rv);
michael@0 217
michael@0 218 // Disable plugins in this document:
michael@0 219 return docShell->SetAllowPlugins(mPluginsEnabled);
michael@0 220 }
michael@0 221
michael@0 222 NS_IMETHODIMP
michael@0 223 nsEditingSession::GetJsAndPluginsDisabled(bool *aResult)
michael@0 224 {
michael@0 225 NS_ENSURE_ARG_POINTER(aResult);
michael@0 226 *aResult = mDisabledJSAndPlugins;
michael@0 227 return NS_OK;
michael@0 228 }
michael@0 229
michael@0 230 /*---------------------------------------------------------------------------
michael@0 231
michael@0 232 WindowIsEditable
michael@0 233
michael@0 234 boolean windowIsEditable (in nsIDOMWindow aWindow);
michael@0 235 ----------------------------------------------------------------------------*/
michael@0 236 NS_IMETHODIMP
michael@0 237 nsEditingSession::WindowIsEditable(nsIDOMWindow *aWindow, bool *outIsEditable)
michael@0 238 {
michael@0 239 nsCOMPtr<nsIDocShell> docShell = GetDocShellFromWindow(aWindow);
michael@0 240 NS_ENSURE_STATE(docShell);
michael@0 241
michael@0 242 return docShell->GetEditable(outIsEditable);
michael@0 243 }
michael@0 244
michael@0 245
michael@0 246 // These are MIME types that are automatically parsed as "text/plain"
michael@0 247 // and thus we can edit them as plaintext
michael@0 248 // Note: in older versions, we attempted to convert the mimetype of
michael@0 249 // the network channel for these and "text/xml" to "text/plain",
michael@0 250 // but further investigation reveals that strategy doesn't work
michael@0 251 const char* const gSupportedTextTypes[] = {
michael@0 252 "text/plain",
michael@0 253 "text/css",
michael@0 254 "text/rdf",
michael@0 255 "text/xsl",
michael@0 256 "text/javascript", // obsolete type
michael@0 257 "text/ecmascript", // obsolete type
michael@0 258 "application/javascript",
michael@0 259 "application/ecmascript",
michael@0 260 "application/x-javascript", // obsolete type
michael@0 261 "text/xul", // obsolete type
michael@0 262 "application/vnd.mozilla.xul+xml",
michael@0 263 nullptr // IMPORTANT! Null must be at end
michael@0 264 };
michael@0 265
michael@0 266 bool
michael@0 267 IsSupportedTextType(const char* aMIMEType)
michael@0 268 {
michael@0 269 NS_ENSURE_TRUE(aMIMEType, false);
michael@0 270
michael@0 271 int32_t i = 0;
michael@0 272 while (gSupportedTextTypes[i])
michael@0 273 {
michael@0 274 if (strcmp(gSupportedTextTypes[i], aMIMEType) == 0)
michael@0 275 {
michael@0 276 return true;
michael@0 277 }
michael@0 278
michael@0 279 i ++;
michael@0 280 }
michael@0 281
michael@0 282 return false;
michael@0 283 }
michael@0 284
michael@0 285 /*---------------------------------------------------------------------------
michael@0 286
michael@0 287 SetupEditorOnWindow
michael@0 288
michael@0 289 nsIEditor setupEditorOnWindow (in nsIDOMWindow aWindow);
michael@0 290 ----------------------------------------------------------------------------*/
michael@0 291 NS_IMETHODIMP
michael@0 292 nsEditingSession::SetupEditorOnWindow(nsIDOMWindow *aWindow)
michael@0 293 {
michael@0 294 mDoneSetup = true;
michael@0 295
michael@0 296 nsresult rv;
michael@0 297
michael@0 298 //MIME CHECKING
michael@0 299 //must get the content type
michael@0 300 // Note: the doc gets this from the network channel during StartPageLoad,
michael@0 301 // so we don't have to get it from there ourselves
michael@0 302 nsCOMPtr<nsIDOMDocument> doc;
michael@0 303 nsAutoCString mimeCType;
michael@0 304
michael@0 305 //then lets check the mime type
michael@0 306 if (NS_SUCCEEDED(aWindow->GetDocument(getter_AddRefs(doc))) && doc)
michael@0 307 {
michael@0 308 nsAutoString mimeType;
michael@0 309 if (NS_SUCCEEDED(doc->GetContentType(mimeType)))
michael@0 310 AppendUTF16toUTF8(mimeType, mimeCType);
michael@0 311
michael@0 312 if (IsSupportedTextType(mimeCType.get()))
michael@0 313 {
michael@0 314 mEditorType.AssignLiteral("text");
michael@0 315 mimeCType = "text/plain";
michael@0 316 }
michael@0 317 else if (!mimeCType.EqualsLiteral("text/html") &&
michael@0 318 !mimeCType.EqualsLiteral("application/xhtml+xml"))
michael@0 319 {
michael@0 320 // Neither an acceptable text or html type.
michael@0 321 mEditorStatus = eEditorErrorCantEditMimeType;
michael@0 322
michael@0 323 // Turn editor into HTML -- we will load blank page later
michael@0 324 mEditorType.AssignLiteral("html");
michael@0 325 mimeCType.AssignLiteral("text/html");
michael@0 326 }
michael@0 327
michael@0 328 // Flush out frame construction to make sure that the subframe's
michael@0 329 // presshell is set up if it needs to be.
michael@0 330 nsCOMPtr<nsIDocument> document = do_QueryInterface(doc);
michael@0 331 if (document) {
michael@0 332 document->FlushPendingNotifications(Flush_Frames);
michael@0 333 if (mMakeWholeDocumentEditable) {
michael@0 334 document->SetEditableFlag(true);
michael@0 335 nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(document);
michael@0 336 if (htmlDocument) {
michael@0 337 // Enable usage of the execCommand API
michael@0 338 htmlDocument->SetEditingState(nsIHTMLDocument::eDesignMode);
michael@0 339 }
michael@0 340 }
michael@0 341 }
michael@0 342 }
michael@0 343 bool needHTMLController = false;
michael@0 344
michael@0 345 const char *classString = "@mozilla.org/editor/htmleditor;1";
michael@0 346 if (mEditorType.EqualsLiteral("textmail"))
michael@0 347 {
michael@0 348 mEditorFlags = nsIPlaintextEditor::eEditorPlaintextMask |
michael@0 349 nsIPlaintextEditor::eEditorEnableWrapHackMask |
michael@0 350 nsIPlaintextEditor::eEditorMailMask;
michael@0 351 }
michael@0 352 else if (mEditorType.EqualsLiteral("text"))
michael@0 353 {
michael@0 354 mEditorFlags = nsIPlaintextEditor::eEditorPlaintextMask |
michael@0 355 nsIPlaintextEditor::eEditorEnableWrapHackMask;
michael@0 356 }
michael@0 357 else if (mEditorType.EqualsLiteral("htmlmail"))
michael@0 358 {
michael@0 359 if (mimeCType.EqualsLiteral("text/html"))
michael@0 360 {
michael@0 361 needHTMLController = true;
michael@0 362 mEditorFlags = nsIPlaintextEditor::eEditorMailMask;
michael@0 363 }
michael@0 364 else //set the flags back to textplain.
michael@0 365 mEditorFlags = nsIPlaintextEditor::eEditorPlaintextMask |
michael@0 366 nsIPlaintextEditor::eEditorEnableWrapHackMask;
michael@0 367 }
michael@0 368 else // Defaulted to html
michael@0 369 {
michael@0 370 needHTMLController = true;
michael@0 371 }
michael@0 372
michael@0 373 if (mInteractive) {
michael@0 374 mEditorFlags |= nsIPlaintextEditor::eEditorAllowInteraction;
michael@0 375 }
michael@0 376
michael@0 377 // make the UI state maintainer
michael@0 378 mStateMaintainer = new nsComposerCommandsUpdater();
michael@0 379
michael@0 380 // now init the state maintainer
michael@0 381 // This allows notification of error state
michael@0 382 // even if we don't create an editor
michael@0 383 rv = mStateMaintainer->Init(aWindow);
michael@0 384 NS_ENSURE_SUCCESS(rv, rv);
michael@0 385
michael@0 386 if (mEditorStatus != eEditorCreationInProgress)
michael@0 387 {
michael@0 388 mStateMaintainer->NotifyDocumentCreated();
michael@0 389 return NS_ERROR_FAILURE;
michael@0 390 }
michael@0 391
michael@0 392 // Create editor and do other things
michael@0 393 // only if we haven't found some error above,
michael@0 394 nsCOMPtr<nsIDocShell> docShell = GetDocShellFromWindow(aWindow);
michael@0 395 NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
michael@0 396
michael@0 397 if (!mInteractive) {
michael@0 398 // Disable animation of images in this document:
michael@0 399 nsCOMPtr<nsIDOMWindowUtils> utils(do_GetInterface(aWindow));
michael@0 400 NS_ENSURE_TRUE(utils, NS_ERROR_FAILURE);
michael@0 401
michael@0 402 rv = utils->GetImageAnimationMode(&mImageAnimationMode);
michael@0 403 NS_ENSURE_SUCCESS(rv, rv);
michael@0 404 utils->SetImageAnimationMode(imgIContainer::kDontAnimMode);
michael@0 405 }
michael@0 406
michael@0 407 // create and set editor
michael@0 408 // Try to reuse an existing editor
michael@0 409 nsCOMPtr<nsIEditor> editor = do_QueryReferent(mExistingEditor);
michael@0 410 if (editor) {
michael@0 411 editor->PreDestroy(false);
michael@0 412 } else {
michael@0 413 editor = do_CreateInstance(classString, &rv);
michael@0 414 NS_ENSURE_SUCCESS(rv, rv);
michael@0 415 mExistingEditor = do_GetWeakReference(editor);
michael@0 416 }
michael@0 417 // set the editor on the docShell. The docShell now owns it.
michael@0 418 rv = docShell->SetEditor(editor);
michael@0 419 NS_ENSURE_SUCCESS(rv, rv);
michael@0 420
michael@0 421 // setup the HTML editor command controller
michael@0 422 if (needHTMLController)
michael@0 423 {
michael@0 424 // The third controller takes an nsIEditor as the context
michael@0 425 rv = SetupEditorCommandController("@mozilla.org/editor/htmleditorcontroller;1",
michael@0 426 aWindow, editor,
michael@0 427 &mHTMLCommandControllerId);
michael@0 428 NS_ENSURE_SUCCESS(rv, rv);
michael@0 429 }
michael@0 430
michael@0 431 // Set mimetype on editor
michael@0 432 rv = editor->SetContentsMIMEType(mimeCType.get());
michael@0 433 NS_ENSURE_SUCCESS(rv, rv);
michael@0 434
michael@0 435 nsCOMPtr<nsIContentViewer> contentViewer;
michael@0 436 rv = docShell->GetContentViewer(getter_AddRefs(contentViewer));
michael@0 437 NS_ENSURE_SUCCESS(rv, rv);
michael@0 438 NS_ENSURE_TRUE(contentViewer, NS_ERROR_FAILURE);
michael@0 439
michael@0 440 nsCOMPtr<nsIDOMDocument> domDoc;
michael@0 441 rv = contentViewer->GetDOMDocument(getter_AddRefs(domDoc));
michael@0 442 NS_ENSURE_SUCCESS(rv, rv);
michael@0 443 NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE);
michael@0 444
michael@0 445 // Set up as a doc state listener
michael@0 446 // Important! We must have this to broadcast the "obs_documentCreated" message
michael@0 447 rv = editor->AddDocumentStateListener(mStateMaintainer);
michael@0 448 NS_ENSURE_SUCCESS(rv, rv);
michael@0 449
michael@0 450 rv = editor->Init(domDoc, nullptr /* root content */,
michael@0 451 nullptr, mEditorFlags, EmptyString());
michael@0 452 NS_ENSURE_SUCCESS(rv, rv);
michael@0 453
michael@0 454 nsCOMPtr<nsISelection> selection;
michael@0 455 editor->GetSelection(getter_AddRefs(selection));
michael@0 456 nsCOMPtr<nsISelectionPrivate> selPriv = do_QueryInterface(selection);
michael@0 457 NS_ENSURE_TRUE(selPriv, NS_ERROR_FAILURE);
michael@0 458
michael@0 459 rv = selPriv->AddSelectionListener(mStateMaintainer);
michael@0 460 NS_ENSURE_SUCCESS(rv, rv);
michael@0 461
michael@0 462 // and as a transaction listener
michael@0 463 nsCOMPtr<nsITransactionManager> txnMgr;
michael@0 464 editor->GetTransactionManager(getter_AddRefs(txnMgr));
michael@0 465 if (txnMgr)
michael@0 466 txnMgr->AddListener(mStateMaintainer);
michael@0 467
michael@0 468 // Set context on all controllers to be the editor
michael@0 469 rv = SetEditorOnControllers(aWindow, editor);
michael@0 470 NS_ENSURE_SUCCESS(rv, rv);
michael@0 471
michael@0 472 // Everything went fine!
michael@0 473 mEditorStatus = eEditorOK;
michael@0 474
michael@0 475 // This will trigger documentCreation notification
michael@0 476 return editor->PostCreate();
michael@0 477 }
michael@0 478
michael@0 479 // Removes all listeners and controllers from aWindow and aEditor.
michael@0 480 void
michael@0 481 nsEditingSession::RemoveListenersAndControllers(nsIDOMWindow *aWindow,
michael@0 482 nsIEditor *aEditor)
michael@0 483 {
michael@0 484 if (!mStateMaintainer || !aEditor)
michael@0 485 return;
michael@0 486
michael@0 487 // Remove all the listeners
michael@0 488 nsCOMPtr<nsISelection> selection;
michael@0 489 aEditor->GetSelection(getter_AddRefs(selection));
michael@0 490 nsCOMPtr<nsISelectionPrivate> selPriv = do_QueryInterface(selection);
michael@0 491 if (selPriv)
michael@0 492 selPriv->RemoveSelectionListener(mStateMaintainer);
michael@0 493
michael@0 494 aEditor->RemoveDocumentStateListener(mStateMaintainer);
michael@0 495
michael@0 496 nsCOMPtr<nsITransactionManager> txnMgr;
michael@0 497 aEditor->GetTransactionManager(getter_AddRefs(txnMgr));
michael@0 498 if (txnMgr)
michael@0 499 txnMgr->RemoveListener(mStateMaintainer);
michael@0 500
michael@0 501 // Remove editor controllers from the window now that we're not
michael@0 502 // editing in that window any more.
michael@0 503 RemoveEditorControllers(aWindow);
michael@0 504 }
michael@0 505
michael@0 506 /*---------------------------------------------------------------------------
michael@0 507
michael@0 508 TearDownEditorOnWindow
michael@0 509
michael@0 510 void tearDownEditorOnWindow (in nsIDOMWindow aWindow);
michael@0 511 ----------------------------------------------------------------------------*/
michael@0 512 NS_IMETHODIMP
michael@0 513 nsEditingSession::TearDownEditorOnWindow(nsIDOMWindow *aWindow)
michael@0 514 {
michael@0 515 if (!mDoneSetup) {
michael@0 516 return NS_OK;
michael@0 517 }
michael@0 518
michael@0 519 NS_ENSURE_TRUE(aWindow, NS_ERROR_NULL_POINTER);
michael@0 520
michael@0 521 nsresult rv;
michael@0 522
michael@0 523 // Kill any existing reload timer
michael@0 524 if (mLoadBlankDocTimer)
michael@0 525 {
michael@0 526 mLoadBlankDocTimer->Cancel();
michael@0 527 mLoadBlankDocTimer = nullptr;
michael@0 528 }
michael@0 529
michael@0 530 mDoneSetup = false;
michael@0 531
michael@0 532 // Check if we're turning off editing (from contentEditable or designMode).
michael@0 533 nsCOMPtr<nsIDOMDocument> domDoc;
michael@0 534 aWindow->GetDocument(getter_AddRefs(domDoc));
michael@0 535 nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(domDoc);
michael@0 536 bool stopEditing = htmlDoc && htmlDoc->IsEditingOn();
michael@0 537 if (stopEditing)
michael@0 538 RemoveWebProgressListener(aWindow);
michael@0 539
michael@0 540 nsCOMPtr<nsIDocShell> docShell = GetDocShellFromWindow(aWindow);
michael@0 541 NS_ENSURE_STATE(docShell);
michael@0 542
michael@0 543 nsCOMPtr<nsIEditor> editor;
michael@0 544 rv = docShell->GetEditor(getter_AddRefs(editor));
michael@0 545 NS_ENSURE_SUCCESS(rv, rv);
michael@0 546
michael@0 547 if (stopEditing)
michael@0 548 htmlDoc->TearingDownEditor(editor);
michael@0 549
michael@0 550 if (mStateMaintainer && editor)
michael@0 551 {
michael@0 552 // Null out the editor on the controllers first to prevent their weak
michael@0 553 // references from pointing to a destroyed editor.
michael@0 554 SetEditorOnControllers(aWindow, nullptr);
michael@0 555 }
michael@0 556
michael@0 557 // Null out the editor on the docShell to trigger PreDestroy which
michael@0 558 // needs to happen before document state listeners are removed below.
michael@0 559 docShell->SetEditor(nullptr);
michael@0 560
michael@0 561 RemoveListenersAndControllers(aWindow, editor);
michael@0 562
michael@0 563 if (stopEditing)
michael@0 564 {
michael@0 565 // Make things the way they were before we started editing.
michael@0 566 RestoreJSAndPlugins(aWindow);
michael@0 567 RestoreAnimationMode(aWindow);
michael@0 568
michael@0 569 if (mMakeWholeDocumentEditable)
michael@0 570 {
michael@0 571 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc, &rv);
michael@0 572 NS_ENSURE_SUCCESS(rv, rv);
michael@0 573
michael@0 574 doc->SetEditableFlag(false);
michael@0 575 nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(doc);
michael@0 576 if (htmlDocument) {
michael@0 577 htmlDocument->SetEditingState(nsIHTMLDocument::eOff);
michael@0 578 }
michael@0 579 }
michael@0 580 }
michael@0 581
michael@0 582 return rv;
michael@0 583 }
michael@0 584
michael@0 585 /*---------------------------------------------------------------------------
michael@0 586
michael@0 587 GetEditorForFrame
michael@0 588
michael@0 589 nsIEditor getEditorForFrame (in nsIDOMWindow aWindow);
michael@0 590 ----------------------------------------------------------------------------*/
michael@0 591 NS_IMETHODIMP
michael@0 592 nsEditingSession::GetEditorForWindow(nsIDOMWindow *aWindow,
michael@0 593 nsIEditor **outEditor)
michael@0 594 {
michael@0 595 nsCOMPtr<nsIDocShell> docShell = GetDocShellFromWindow(aWindow);
michael@0 596 NS_ENSURE_STATE(aWindow);
michael@0 597
michael@0 598 return docShell->GetEditor(outEditor);
michael@0 599 }
michael@0 600
michael@0 601 /*---------------------------------------------------------------------------
michael@0 602
michael@0 603 OnStateChange
michael@0 604
michael@0 605 ----------------------------------------------------------------------------*/
michael@0 606 NS_IMETHODIMP
michael@0 607 nsEditingSession::OnStateChange(nsIWebProgress *aWebProgress,
michael@0 608 nsIRequest *aRequest,
michael@0 609 uint32_t aStateFlags, nsresult aStatus)
michael@0 610 {
michael@0 611
michael@0 612 #ifdef NOISY_DOC_LOADING
michael@0 613 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
michael@0 614 if (channel)
michael@0 615 {
michael@0 616 nsAutoCString contentType;
michael@0 617 channel->GetContentType(contentType);
michael@0 618 if (!contentType.IsEmpty())
michael@0 619 printf(" ++++++ MIMETYPE = %s\n", contentType.get());
michael@0 620 }
michael@0 621 #endif
michael@0 622
michael@0 623 //
michael@0 624 // A Request has started...
michael@0 625 //
michael@0 626 if (aStateFlags & nsIWebProgressListener::STATE_START)
michael@0 627 {
michael@0 628 #ifdef NOISY_DOC_LOADING
michael@0 629 {
michael@0 630 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
michael@0 631 if (channel)
michael@0 632 {
michael@0 633 nsCOMPtr<nsIURI> uri;
michael@0 634 channel->GetURI(getter_AddRefs(uri));
michael@0 635 if (uri)
michael@0 636 {
michael@0 637 nsXPIDLCString spec;
michael@0 638 uri->GetSpec(spec);
michael@0 639 printf(" **** STATE_START: CHANNEL URI=%s, flags=%x\n",
michael@0 640 spec.get(), aStateFlags);
michael@0 641 }
michael@0 642 }
michael@0 643 else
michael@0 644 printf(" STATE_START: NO CHANNEL flags=%x\n", aStateFlags);
michael@0 645 }
michael@0 646 #endif
michael@0 647 // Page level notification...
michael@0 648 if (aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK)
michael@0 649 {
michael@0 650 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
michael@0 651 StartPageLoad(channel);
michael@0 652 #ifdef NOISY_DOC_LOADING
michael@0 653 printf("STATE_START & STATE_IS_NETWORK flags=%x\n", aStateFlags);
michael@0 654 #endif
michael@0 655 }
michael@0 656
michael@0 657 // Document level notification...
michael@0 658 if (aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT &&
michael@0 659 !(aStateFlags & nsIWebProgressListener::STATE_RESTORING)) {
michael@0 660 #ifdef NOISY_DOC_LOADING
michael@0 661 printf("STATE_START & STATE_IS_DOCUMENT flags=%x\n", aStateFlags);
michael@0 662 #endif
michael@0 663
michael@0 664 bool progressIsForTargetDocument =
michael@0 665 IsProgressForTargetDocument(aWebProgress);
michael@0 666
michael@0 667 if (progressIsForTargetDocument)
michael@0 668 {
michael@0 669 nsCOMPtr<nsIDOMWindow> window;
michael@0 670 aWebProgress->GetDOMWindow(getter_AddRefs(window));
michael@0 671
michael@0 672 nsCOMPtr<nsIDOMDocument> doc;
michael@0 673 window->GetDocument(getter_AddRefs(doc));
michael@0 674
michael@0 675 nsCOMPtr<nsIHTMLDocument> htmlDoc(do_QueryInterface(doc));
michael@0 676
michael@0 677 if (htmlDoc && htmlDoc->IsWriting())
michael@0 678 {
michael@0 679 nsCOMPtr<nsIDOMHTMLDocument> htmlDomDoc = do_QueryInterface(doc);
michael@0 680 nsAutoString designMode;
michael@0 681 htmlDomDoc->GetDesignMode(designMode);
michael@0 682
michael@0 683 if (designMode.EqualsLiteral("on"))
michael@0 684 {
michael@0 685 // This notification is for data coming in through
michael@0 686 // document.open/write/close(), ignore it.
michael@0 687
michael@0 688 return NS_OK;
michael@0 689 }
michael@0 690 }
michael@0 691
michael@0 692 mCanCreateEditor = true;
michael@0 693 StartDocumentLoad(aWebProgress, progressIsForTargetDocument);
michael@0 694 }
michael@0 695 }
michael@0 696 }
michael@0 697 //
michael@0 698 // A Request is being processed
michael@0 699 //
michael@0 700 else if (aStateFlags & nsIWebProgressListener::STATE_TRANSFERRING)
michael@0 701 {
michael@0 702 if (aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT)
michael@0 703 {
michael@0 704 // document transfer started
michael@0 705 }
michael@0 706 }
michael@0 707 //
michael@0 708 // Got a redirection
michael@0 709 //
michael@0 710 else if (aStateFlags & nsIWebProgressListener::STATE_REDIRECTING)
michael@0 711 {
michael@0 712 if (aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT)
michael@0 713 {
michael@0 714 // got a redirect
michael@0 715 }
michael@0 716 }
michael@0 717 //
michael@0 718 // A network or document Request has finished...
michael@0 719 //
michael@0 720 else if (aStateFlags & nsIWebProgressListener::STATE_STOP)
michael@0 721 {
michael@0 722
michael@0 723 #ifdef NOISY_DOC_LOADING
michael@0 724 {
michael@0 725 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
michael@0 726 if (channel)
michael@0 727 {
michael@0 728 nsCOMPtr<nsIURI> uri;
michael@0 729 channel->GetURI(getter_AddRefs(uri));
michael@0 730 if (uri)
michael@0 731 {
michael@0 732 nsXPIDLCString spec;
michael@0 733 uri->GetSpec(spec);
michael@0 734 printf(" **** STATE_STOP: CHANNEL URI=%s, flags=%x\n",
michael@0 735 spec.get(), aStateFlags);
michael@0 736 }
michael@0 737 }
michael@0 738 else
michael@0 739 printf(" STATE_STOP: NO CHANNEL flags=%x\n", aStateFlags);
michael@0 740 }
michael@0 741 #endif
michael@0 742
michael@0 743 // Document level notification...
michael@0 744 if (aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT)
michael@0 745 {
michael@0 746 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
michael@0 747 EndDocumentLoad(aWebProgress, channel, aStatus,
michael@0 748 IsProgressForTargetDocument(aWebProgress));
michael@0 749 #ifdef NOISY_DOC_LOADING
michael@0 750 printf("STATE_STOP & STATE_IS_DOCUMENT flags=%x\n", aStateFlags);
michael@0 751 #endif
michael@0 752 }
michael@0 753
michael@0 754 // Page level notification...
michael@0 755 if (aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK)
michael@0 756 {
michael@0 757 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
michael@0 758 (void)EndPageLoad(aWebProgress, channel, aStatus);
michael@0 759 #ifdef NOISY_DOC_LOADING
michael@0 760 printf("STATE_STOP & STATE_IS_NETWORK flags=%x\n", aStateFlags);
michael@0 761 #endif
michael@0 762 }
michael@0 763 }
michael@0 764
michael@0 765 return NS_OK;
michael@0 766 }
michael@0 767
michael@0 768 /*---------------------------------------------------------------------------
michael@0 769
michael@0 770 OnProgressChange
michael@0 771
michael@0 772 ----------------------------------------------------------------------------*/
michael@0 773 NS_IMETHODIMP
michael@0 774 nsEditingSession::OnProgressChange(nsIWebProgress *aWebProgress,
michael@0 775 nsIRequest *aRequest,
michael@0 776 int32_t aCurSelfProgress,
michael@0 777 int32_t aMaxSelfProgress,
michael@0 778 int32_t aCurTotalProgress,
michael@0 779 int32_t aMaxTotalProgress)
michael@0 780 {
michael@0 781 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
michael@0 782 return NS_OK;
michael@0 783 }
michael@0 784
michael@0 785 /*---------------------------------------------------------------------------
michael@0 786
michael@0 787 OnLocationChange
michael@0 788
michael@0 789 ----------------------------------------------------------------------------*/
michael@0 790 NS_IMETHODIMP
michael@0 791 nsEditingSession::OnLocationChange(nsIWebProgress *aWebProgress,
michael@0 792 nsIRequest *aRequest, nsIURI *aURI,
michael@0 793 uint32_t aFlags)
michael@0 794 {
michael@0 795 nsCOMPtr<nsIDOMWindow> domWindow;
michael@0 796 nsresult rv = aWebProgress->GetDOMWindow(getter_AddRefs(domWindow));
michael@0 797 NS_ENSURE_SUCCESS(rv, rv);
michael@0 798
michael@0 799 nsCOMPtr<nsIDOMDocument> domDoc;
michael@0 800 rv = domWindow->GetDocument(getter_AddRefs(domDoc));
michael@0 801 NS_ENSURE_SUCCESS(rv, rv);
michael@0 802
michael@0 803 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
michael@0 804 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
michael@0 805
michael@0 806 doc->SetDocumentURI(aURI);
michael@0 807
michael@0 808 // Notify the location-changed observer that
michael@0 809 // the document URL has changed
michael@0 810 nsIDocShell *docShell = GetDocShellFromWindow(domWindow);
michael@0 811 NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
michael@0 812
michael@0 813 nsCOMPtr<nsICommandManager> commandManager = do_GetInterface(docShell);
michael@0 814 nsCOMPtr<nsPICommandUpdater> commandUpdater =
michael@0 815 do_QueryInterface(commandManager);
michael@0 816 NS_ENSURE_TRUE(commandUpdater, NS_ERROR_FAILURE);
michael@0 817
michael@0 818 return commandUpdater->CommandStatusChanged("obs_documentLocationChanged");
michael@0 819 }
michael@0 820
michael@0 821 /*---------------------------------------------------------------------------
michael@0 822
michael@0 823 OnStatusChange
michael@0 824
michael@0 825 ----------------------------------------------------------------------------*/
michael@0 826 NS_IMETHODIMP
michael@0 827 nsEditingSession::OnStatusChange(nsIWebProgress *aWebProgress,
michael@0 828 nsIRequest *aRequest,
michael@0 829 nsresult aStatus,
michael@0 830 const char16_t *aMessage)
michael@0 831 {
michael@0 832 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
michael@0 833 return NS_OK;
michael@0 834 }
michael@0 835
michael@0 836 /*---------------------------------------------------------------------------
michael@0 837
michael@0 838 OnSecurityChange
michael@0 839
michael@0 840 ----------------------------------------------------------------------------*/
michael@0 841 NS_IMETHODIMP
michael@0 842 nsEditingSession::OnSecurityChange(nsIWebProgress *aWebProgress,
michael@0 843 nsIRequest *aRequest, uint32_t state)
michael@0 844 {
michael@0 845 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
michael@0 846 return NS_OK;
michael@0 847 }
michael@0 848
michael@0 849
michael@0 850 /*---------------------------------------------------------------------------
michael@0 851
michael@0 852 IsProgressForTargetDocument
michael@0 853
michael@0 854 Check that this notification is for our document.
michael@0 855 ----------------------------------------------------------------------------*/
michael@0 856
michael@0 857 bool
michael@0 858 nsEditingSession::IsProgressForTargetDocument(nsIWebProgress *aWebProgress)
michael@0 859 {
michael@0 860 nsCOMPtr<nsIWebProgress> editedWebProgress = do_QueryReferent(mDocShell);
michael@0 861 return editedWebProgress == aWebProgress;
michael@0 862 }
michael@0 863
michael@0 864
michael@0 865 /*---------------------------------------------------------------------------
michael@0 866
michael@0 867 GetEditorStatus
michael@0 868
michael@0 869 Called during GetCommandStateParams("obs_documentCreated"...)
michael@0 870 to determine if editor was created and document
michael@0 871 was loaded successfully
michael@0 872 ----------------------------------------------------------------------------*/
michael@0 873 NS_IMETHODIMP
michael@0 874 nsEditingSession::GetEditorStatus(uint32_t *aStatus)
michael@0 875 {
michael@0 876 NS_ENSURE_ARG_POINTER(aStatus);
michael@0 877 *aStatus = mEditorStatus;
michael@0 878 return NS_OK;
michael@0 879 }
michael@0 880
michael@0 881 /*---------------------------------------------------------------------------
michael@0 882
michael@0 883 StartDocumentLoad
michael@0 884
michael@0 885 Called on start of load in a single frame
michael@0 886 ----------------------------------------------------------------------------*/
michael@0 887 nsresult
michael@0 888 nsEditingSession::StartDocumentLoad(nsIWebProgress *aWebProgress,
michael@0 889 bool aIsToBeMadeEditable)
michael@0 890 {
michael@0 891 #ifdef NOISY_DOC_LOADING
michael@0 892 printf("======= StartDocumentLoad ========\n");
michael@0 893 #endif
michael@0 894
michael@0 895 NS_ENSURE_ARG_POINTER(aWebProgress);
michael@0 896
michael@0 897 // If we have an editor here, then we got a reload after making the editor.
michael@0 898 // We need to blow it away and make a new one at the end of the load.
michael@0 899 nsCOMPtr<nsIDOMWindow> domWindow;
michael@0 900 aWebProgress->GetDOMWindow(getter_AddRefs(domWindow));
michael@0 901 if (domWindow)
michael@0 902 {
michael@0 903 nsIDocShell *docShell = GetDocShellFromWindow(domWindow);
michael@0 904 NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
michael@0 905 docShell->DetachEditorFromWindow();
michael@0 906 }
michael@0 907
michael@0 908 if (aIsToBeMadeEditable)
michael@0 909 mEditorStatus = eEditorCreationInProgress;
michael@0 910
michael@0 911 return NS_OK;
michael@0 912 }
michael@0 913
michael@0 914 /*---------------------------------------------------------------------------
michael@0 915
michael@0 916 EndDocumentLoad
michael@0 917
michael@0 918 Called on end of load in a single frame
michael@0 919 ----------------------------------------------------------------------------*/
michael@0 920 nsresult
michael@0 921 nsEditingSession::EndDocumentLoad(nsIWebProgress *aWebProgress,
michael@0 922 nsIChannel* aChannel, nsresult aStatus,
michael@0 923 bool aIsToBeMadeEditable)
michael@0 924 {
michael@0 925 NS_ENSURE_ARG_POINTER(aWebProgress);
michael@0 926
michael@0 927 #ifdef NOISY_DOC_LOADING
michael@0 928 printf("======= EndDocumentLoad ========\n");
michael@0 929 printf("with status %d, ", aStatus);
michael@0 930 nsCOMPtr<nsIURI> uri;
michael@0 931 nsXPIDLCString spec;
michael@0 932 if (NS_SUCCEEDED(aChannel->GetURI(getter_AddRefs(uri)))) {
michael@0 933 uri->GetSpec(spec);
michael@0 934 printf(" uri %s\n", spec.get());
michael@0 935 }
michael@0 936 #endif
michael@0 937
michael@0 938 // We want to call the base class EndDocumentLoad,
michael@0 939 // but avoid some of the stuff
michael@0 940 // that nsDocShell does (need to refactor).
michael@0 941
michael@0 942 // OK, time to make an editor on this document
michael@0 943 nsCOMPtr<nsIDOMWindow> domWindow;
michael@0 944 aWebProgress->GetDOMWindow(getter_AddRefs(domWindow));
michael@0 945
michael@0 946 // Set the error state -- we will create an editor
michael@0 947 // anyway and load empty doc later
michael@0 948 if (aIsToBeMadeEditable) {
michael@0 949 if (aStatus == NS_ERROR_FILE_NOT_FOUND)
michael@0 950 mEditorStatus = eEditorErrorFileNotFound;
michael@0 951 }
michael@0 952
michael@0 953 nsIDocShell *docShell = GetDocShellFromWindow(domWindow);
michael@0 954 NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); // better error handling?
michael@0 955
michael@0 956 // cancel refresh from meta tags
michael@0 957 // we need to make sure that all pages in editor (whether editable or not)
michael@0 958 // can't refresh contents being edited
michael@0 959 nsCOMPtr<nsIRefreshURI> refreshURI = do_QueryInterface(docShell);
michael@0 960 if (refreshURI)
michael@0 961 refreshURI->CancelRefreshURITimers();
michael@0 962
michael@0 963 nsresult rv = NS_OK;
michael@0 964
michael@0 965 // did someone set the flag to make this shell editable?
michael@0 966 if (aIsToBeMadeEditable && mCanCreateEditor)
michael@0 967 {
michael@0 968 bool makeEditable;
michael@0 969 docShell->GetEditable(&makeEditable);
michael@0 970
michael@0 971 if (makeEditable)
michael@0 972 {
michael@0 973 // To keep pre Gecko 1.9 behavior, setup editor always when
michael@0 974 // mMakeWholeDocumentEditable.
michael@0 975 bool needsSetup = false;
michael@0 976 if (mMakeWholeDocumentEditable) {
michael@0 977 needsSetup = true;
michael@0 978 } else {
michael@0 979 // do we already have an editor here?
michael@0 980 nsCOMPtr<nsIEditor> editor;
michael@0 981 rv = docShell->GetEditor(getter_AddRefs(editor));
michael@0 982 NS_ENSURE_SUCCESS(rv, rv);
michael@0 983
michael@0 984 needsSetup = !editor;
michael@0 985 }
michael@0 986
michael@0 987 if (needsSetup)
michael@0 988 {
michael@0 989 mCanCreateEditor = false;
michael@0 990 rv = SetupEditorOnWindow(domWindow);
michael@0 991 if (NS_FAILED(rv))
michael@0 992 {
michael@0 993 // If we had an error, setup timer to load a blank page later
michael@0 994 if (mLoadBlankDocTimer)
michael@0 995 {
michael@0 996 // Must cancel previous timer?
michael@0 997 mLoadBlankDocTimer->Cancel();
michael@0 998 mLoadBlankDocTimer = nullptr;
michael@0 999 }
michael@0 1000
michael@0 1001 mLoadBlankDocTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
michael@0 1002 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1003
michael@0 1004 mEditorStatus = eEditorCreationInProgress;
michael@0 1005 mLoadBlankDocTimer->InitWithFuncCallback(
michael@0 1006 nsEditingSession::TimerCallback,
michael@0 1007 static_cast<void*> (mDocShell.get()),
michael@0 1008 10, nsITimer::TYPE_ONE_SHOT);
michael@0 1009 }
michael@0 1010 }
michael@0 1011 }
michael@0 1012 }
michael@0 1013 return rv;
michael@0 1014 }
michael@0 1015
michael@0 1016
michael@0 1017 void
michael@0 1018 nsEditingSession::TimerCallback(nsITimer* aTimer, void* aClosure)
michael@0 1019 {
michael@0 1020 nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(static_cast<nsIWeakReference*> (aClosure));
michael@0 1021 if (docShell)
michael@0 1022 {
michael@0 1023 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(docShell));
michael@0 1024 if (webNav)
michael@0 1025 webNav->LoadURI(MOZ_UTF16("about:blank"),
michael@0 1026 0, nullptr, nullptr, nullptr);
michael@0 1027 }
michael@0 1028 }
michael@0 1029
michael@0 1030 /*---------------------------------------------------------------------------
michael@0 1031
michael@0 1032 StartPageLoad
michael@0 1033
michael@0 1034 Called on start load of the entire page (incl. subframes)
michael@0 1035 ----------------------------------------------------------------------------*/
michael@0 1036 nsresult
michael@0 1037 nsEditingSession::StartPageLoad(nsIChannel *aChannel)
michael@0 1038 {
michael@0 1039 #ifdef NOISY_DOC_LOADING
michael@0 1040 printf("======= StartPageLoad ========\n");
michael@0 1041 #endif
michael@0 1042 return NS_OK;
michael@0 1043 }
michael@0 1044
michael@0 1045 /*---------------------------------------------------------------------------
michael@0 1046
michael@0 1047 EndPageLoad
michael@0 1048
michael@0 1049 Called on end load of the entire page (incl. subframes)
michael@0 1050 ----------------------------------------------------------------------------*/
michael@0 1051 nsresult
michael@0 1052 nsEditingSession::EndPageLoad(nsIWebProgress *aWebProgress,
michael@0 1053 nsIChannel* aChannel, nsresult aStatus)
michael@0 1054 {
michael@0 1055 #ifdef NOISY_DOC_LOADING
michael@0 1056 printf("======= EndPageLoad ========\n");
michael@0 1057 printf(" with status %d, ", aStatus);
michael@0 1058 nsCOMPtr<nsIURI> uri;
michael@0 1059 nsXPIDLCString spec;
michael@0 1060 if (NS_SUCCEEDED(aChannel->GetURI(getter_AddRefs(uri)))) {
michael@0 1061 uri->GetSpec(spec);
michael@0 1062 printf("uri %s\n", spec.get());
michael@0 1063 }
michael@0 1064
michael@0 1065 nsAutoCString contentType;
michael@0 1066 aChannel->GetContentType(contentType);
michael@0 1067 if (!contentType.IsEmpty())
michael@0 1068 printf(" flags = %d, status = %d, MIMETYPE = %s\n",
michael@0 1069 mEditorFlags, mEditorStatus, contentType.get());
michael@0 1070 #endif
michael@0 1071
michael@0 1072 // Set the error state -- we will create an editor anyway
michael@0 1073 // and load empty doc later
michael@0 1074 if (aStatus == NS_ERROR_FILE_NOT_FOUND)
michael@0 1075 mEditorStatus = eEditorErrorFileNotFound;
michael@0 1076
michael@0 1077 nsCOMPtr<nsIDOMWindow> domWindow;
michael@0 1078 aWebProgress->GetDOMWindow(getter_AddRefs(domWindow));
michael@0 1079
michael@0 1080 nsIDocShell *docShell = GetDocShellFromWindow(domWindow);
michael@0 1081 NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
michael@0 1082
michael@0 1083 // cancel refresh from meta tags
michael@0 1084 // we need to make sure that all pages in editor (whether editable or not)
michael@0 1085 // can't refresh contents being edited
michael@0 1086 nsCOMPtr<nsIRefreshURI> refreshURI = do_QueryInterface(docShell);
michael@0 1087 if (refreshURI)
michael@0 1088 refreshURI->CancelRefreshURITimers();
michael@0 1089
michael@0 1090 #if 0
michael@0 1091 // Shouldn't we do this when we want to edit sub-frames?
michael@0 1092 return MakeWindowEditable(domWindow, "html", false, mInteractive);
michael@0 1093 #else
michael@0 1094 return NS_OK;
michael@0 1095 #endif
michael@0 1096 }
michael@0 1097
michael@0 1098 /*---------------------------------------------------------------------------
michael@0 1099
michael@0 1100 GetDocShellFromWindow
michael@0 1101
michael@0 1102 Utility method. This will always return nullptr if no docShell is found.
michael@0 1103 ----------------------------------------------------------------------------*/
michael@0 1104 nsIDocShell *
michael@0 1105 nsEditingSession::GetDocShellFromWindow(nsIDOMWindow *aWindow)
michael@0 1106 {
michael@0 1107 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
michael@0 1108 NS_ENSURE_TRUE(window, nullptr);
michael@0 1109
michael@0 1110 return window->GetDocShell();
michael@0 1111 }
michael@0 1112
michael@0 1113 /*---------------------------------------------------------------------------
michael@0 1114
michael@0 1115 PrepareForEditing
michael@0 1116
michael@0 1117 Set up this editing session for one or more editors
michael@0 1118 ----------------------------------------------------------------------------*/
michael@0 1119 nsresult
michael@0 1120 nsEditingSession::PrepareForEditing(nsIDOMWindow *aWindow)
michael@0 1121 {
michael@0 1122 if (mProgressListenerRegistered)
michael@0 1123 return NS_OK;
michael@0 1124
michael@0 1125 nsIDocShell *docShell = GetDocShellFromWindow(aWindow);
michael@0 1126
michael@0 1127 // register callback
michael@0 1128 nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
michael@0 1129 NS_ENSURE_TRUE(webProgress, NS_ERROR_FAILURE);
michael@0 1130
michael@0 1131 nsresult rv =
michael@0 1132 webProgress->AddProgressListener(this,
michael@0 1133 (nsIWebProgress::NOTIFY_STATE_NETWORK |
michael@0 1134 nsIWebProgress::NOTIFY_STATE_DOCUMENT |
michael@0 1135 nsIWebProgress::NOTIFY_LOCATION));
michael@0 1136
michael@0 1137 mProgressListenerRegistered = NS_SUCCEEDED(rv);
michael@0 1138
michael@0 1139 return rv;
michael@0 1140 }
michael@0 1141
michael@0 1142 /*---------------------------------------------------------------------------
michael@0 1143
michael@0 1144 SetupEditorCommandController
michael@0 1145
michael@0 1146 Create a command controller, append to controllers,
michael@0 1147 get and return the controller ID, and set the context
michael@0 1148 ----------------------------------------------------------------------------*/
michael@0 1149 nsresult
michael@0 1150 nsEditingSession::SetupEditorCommandController(
michael@0 1151 const char *aControllerClassName,
michael@0 1152 nsIDOMWindow *aWindow,
michael@0 1153 nsISupports *aContext,
michael@0 1154 uint32_t *aControllerId)
michael@0 1155 {
michael@0 1156 NS_ENSURE_ARG_POINTER(aControllerClassName);
michael@0 1157 NS_ENSURE_ARG_POINTER(aWindow);
michael@0 1158 NS_ENSURE_ARG_POINTER(aContext);
michael@0 1159 NS_ENSURE_ARG_POINTER(aControllerId);
michael@0 1160
michael@0 1161 nsCOMPtr<nsIControllers> controllers;
michael@0 1162 nsresult rv = aWindow->GetControllers(getter_AddRefs(controllers));
michael@0 1163 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1164
michael@0 1165 // We only have to create each singleton controller once
michael@0 1166 // We know this has happened once we have a controllerId value
michael@0 1167 if (!*aControllerId)
michael@0 1168 {
michael@0 1169 nsCOMPtr<nsIController> controller;
michael@0 1170 controller = do_CreateInstance(aControllerClassName, &rv);
michael@0 1171 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1172
michael@0 1173 // We must insert at head of the list to be sure our
michael@0 1174 // controller is found before other implementations
michael@0 1175 // (e.g., not-implemented versions by browser)
michael@0 1176 rv = controllers->InsertControllerAt(0, controller);
michael@0 1177 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1178
michael@0 1179 // Remember the ID for the controller
michael@0 1180 rv = controllers->GetControllerId(controller, aControllerId);
michael@0 1181 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1182 }
michael@0 1183
michael@0 1184 // Set the context
michael@0 1185 return SetContextOnControllerById(controllers, aContext, *aControllerId);
michael@0 1186 }
michael@0 1187
michael@0 1188 /*---------------------------------------------------------------------------
michael@0 1189
michael@0 1190 SetEditorOnControllers
michael@0 1191
michael@0 1192 Set the editor on the controller(s) for this window
michael@0 1193 ----------------------------------------------------------------------------*/
michael@0 1194 NS_IMETHODIMP
michael@0 1195 nsEditingSession::SetEditorOnControllers(nsIDOMWindow *aWindow,
michael@0 1196 nsIEditor* aEditor)
michael@0 1197 {
michael@0 1198 NS_ENSURE_TRUE(aWindow, NS_ERROR_NULL_POINTER);
michael@0 1199
michael@0 1200 nsCOMPtr<nsIControllers> controllers;
michael@0 1201 nsresult rv = aWindow->GetControllers(getter_AddRefs(controllers));
michael@0 1202 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1203
michael@0 1204 nsCOMPtr<nsISupports> editorAsISupports = do_QueryInterface(aEditor);
michael@0 1205 if (mBaseCommandControllerId)
michael@0 1206 {
michael@0 1207 rv = SetContextOnControllerById(controllers, editorAsISupports,
michael@0 1208 mBaseCommandControllerId);
michael@0 1209 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1210 }
michael@0 1211
michael@0 1212 if (mDocStateControllerId)
michael@0 1213 {
michael@0 1214 rv = SetContextOnControllerById(controllers, editorAsISupports,
michael@0 1215 mDocStateControllerId);
michael@0 1216 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1217 }
michael@0 1218
michael@0 1219 if (mHTMLCommandControllerId)
michael@0 1220 rv = SetContextOnControllerById(controllers, editorAsISupports,
michael@0 1221 mHTMLCommandControllerId);
michael@0 1222
michael@0 1223 return rv;
michael@0 1224 }
michael@0 1225
michael@0 1226 nsresult
michael@0 1227 nsEditingSession::SetContextOnControllerById(nsIControllers* aControllers,
michael@0 1228 nsISupports* aContext,
michael@0 1229 uint32_t aID)
michael@0 1230 {
michael@0 1231 NS_ENSURE_ARG_POINTER(aControllers);
michael@0 1232
michael@0 1233 // aContext can be null (when destroying editor)
michael@0 1234 nsCOMPtr<nsIController> controller;
michael@0 1235 aControllers->GetControllerById(aID, getter_AddRefs(controller));
michael@0 1236
michael@0 1237 // ok with nil controller
michael@0 1238 nsCOMPtr<nsIControllerContext> editorController =
michael@0 1239 do_QueryInterface(controller);
michael@0 1240 NS_ENSURE_TRUE(editorController, NS_ERROR_FAILURE);
michael@0 1241
michael@0 1242 return editorController->SetCommandContext(aContext);
michael@0 1243 }
michael@0 1244
michael@0 1245 void
michael@0 1246 nsEditingSession::RemoveEditorControllers(nsIDOMWindow *aWindow)
michael@0 1247 {
michael@0 1248 // Remove editor controllers from the aWindow, call when we're
michael@0 1249 // tearing down/detaching editor.
michael@0 1250
michael@0 1251 nsCOMPtr<nsIControllers> controllers;
michael@0 1252 if (aWindow)
michael@0 1253 aWindow->GetControllers(getter_AddRefs(controllers));
michael@0 1254
michael@0 1255 if (controllers)
michael@0 1256 {
michael@0 1257 nsCOMPtr<nsIController> controller;
michael@0 1258 if (mBaseCommandControllerId)
michael@0 1259 {
michael@0 1260 controllers->GetControllerById(mBaseCommandControllerId,
michael@0 1261 getter_AddRefs(controller));
michael@0 1262 if (controller)
michael@0 1263 controllers->RemoveController(controller);
michael@0 1264 }
michael@0 1265
michael@0 1266 if (mDocStateControllerId)
michael@0 1267 {
michael@0 1268 controllers->GetControllerById(mDocStateControllerId,
michael@0 1269 getter_AddRefs(controller));
michael@0 1270 if (controller)
michael@0 1271 controllers->RemoveController(controller);
michael@0 1272 }
michael@0 1273
michael@0 1274 if (mHTMLCommandControllerId)
michael@0 1275 {
michael@0 1276 controllers->GetControllerById(mHTMLCommandControllerId,
michael@0 1277 getter_AddRefs(controller));
michael@0 1278 if (controller)
michael@0 1279 controllers->RemoveController(controller);
michael@0 1280 }
michael@0 1281 }
michael@0 1282
michael@0 1283 // Clear IDs to trigger creation of new controllers.
michael@0 1284 mBaseCommandControllerId = 0;
michael@0 1285 mDocStateControllerId = 0;
michael@0 1286 mHTMLCommandControllerId = 0;
michael@0 1287 }
michael@0 1288
michael@0 1289 void
michael@0 1290 nsEditingSession::RemoveWebProgressListener(nsIDOMWindow *aWindow)
michael@0 1291 {
michael@0 1292 nsIDocShell *docShell = GetDocShellFromWindow(aWindow);
michael@0 1293 nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
michael@0 1294 if (webProgress)
michael@0 1295 {
michael@0 1296 webProgress->RemoveProgressListener(this);
michael@0 1297 mProgressListenerRegistered = false;
michael@0 1298 }
michael@0 1299 }
michael@0 1300
michael@0 1301 void
michael@0 1302 nsEditingSession::RestoreAnimationMode(nsIDOMWindow *aWindow)
michael@0 1303 {
michael@0 1304 if (!mInteractive)
michael@0 1305 {
michael@0 1306 nsCOMPtr<nsIDOMWindowUtils> utils(do_GetInterface(aWindow));
michael@0 1307 if (utils)
michael@0 1308 utils->SetImageAnimationMode(mImageAnimationMode);
michael@0 1309 }
michael@0 1310 }
michael@0 1311
michael@0 1312 nsresult
michael@0 1313 nsEditingSession::DetachFromWindow(nsIDOMWindow* aWindow)
michael@0 1314 {
michael@0 1315 NS_ENSURE_TRUE(mDoneSetup, NS_OK);
michael@0 1316
michael@0 1317 NS_ASSERTION(mStateMaintainer, "mStateMaintainer should exist.");
michael@0 1318
michael@0 1319 // Kill any existing reload timer
michael@0 1320 if (mLoadBlankDocTimer)
michael@0 1321 {
michael@0 1322 mLoadBlankDocTimer->Cancel();
michael@0 1323 mLoadBlankDocTimer = nullptr;
michael@0 1324 }
michael@0 1325
michael@0 1326 // Remove controllers, webprogress listener, and otherwise
michael@0 1327 // make things the way they were before we started editing.
michael@0 1328 RemoveEditorControllers(aWindow);
michael@0 1329 RemoveWebProgressListener(aWindow);
michael@0 1330 RestoreJSAndPlugins(aWindow);
michael@0 1331 RestoreAnimationMode(aWindow);
michael@0 1332
michael@0 1333 // Kill our weak reference to our original window, in case
michael@0 1334 // it changes on restore, or otherwise dies.
michael@0 1335 mDocShell = nullptr;
michael@0 1336
michael@0 1337 return NS_OK;
michael@0 1338 }
michael@0 1339
michael@0 1340 nsresult
michael@0 1341 nsEditingSession::ReattachToWindow(nsIDOMWindow* aWindow)
michael@0 1342 {
michael@0 1343 NS_ENSURE_TRUE(mDoneSetup, NS_OK);
michael@0 1344
michael@0 1345 NS_ASSERTION(mStateMaintainer, "mStateMaintainer should exist.");
michael@0 1346
michael@0 1347 // Imitate nsEditorDocShell::MakeEditable() to reattach the
michael@0 1348 // old editor ot the window.
michael@0 1349 nsresult rv;
michael@0 1350
michael@0 1351 nsIDocShell *docShell = GetDocShellFromWindow(aWindow);
michael@0 1352 NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
michael@0 1353 mDocShell = do_GetWeakReference(docShell);
michael@0 1354
michael@0 1355 // Disable plugins.
michael@0 1356 if (!mInteractive)
michael@0 1357 {
michael@0 1358 rv = DisableJSAndPlugins(aWindow);
michael@0 1359 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1360 }
michael@0 1361
michael@0 1362 // Tells embedder that startup is in progress.
michael@0 1363 mEditorStatus = eEditorCreationInProgress;
michael@0 1364
michael@0 1365 // Adds back web progress listener.
michael@0 1366 rv = PrepareForEditing(aWindow);
michael@0 1367 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1368
michael@0 1369 // Setup the command controllers again.
michael@0 1370 rv = SetupEditorCommandController("@mozilla.org/editor/editingcontroller;1",
michael@0 1371 aWindow,
michael@0 1372 static_cast<nsIEditingSession*>(this),
michael@0 1373 &mBaseCommandControllerId);
michael@0 1374 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1375
michael@0 1376 rv = SetupEditorCommandController("@mozilla.org/editor/editordocstatecontroller;1",
michael@0 1377 aWindow,
michael@0 1378 static_cast<nsIEditingSession*>(this),
michael@0 1379 &mDocStateControllerId);
michael@0 1380 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1381
michael@0 1382 if (mStateMaintainer)
michael@0 1383 mStateMaintainer->Init(aWindow);
michael@0 1384
michael@0 1385 // Get editor
michael@0 1386 nsCOMPtr<nsIEditor> editor;
michael@0 1387 rv = GetEditorForWindow(aWindow, getter_AddRefs(editor));
michael@0 1388 NS_ENSURE_TRUE(editor, NS_ERROR_FAILURE);
michael@0 1389
michael@0 1390 if (!mInteractive)
michael@0 1391 {
michael@0 1392 // Disable animation of images in this document:
michael@0 1393 nsCOMPtr<nsIDOMWindowUtils> utils(do_GetInterface(aWindow));
michael@0 1394 NS_ENSURE_TRUE(utils, NS_ERROR_FAILURE);
michael@0 1395
michael@0 1396 rv = utils->GetImageAnimationMode(&mImageAnimationMode);
michael@0 1397 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1398 utils->SetImageAnimationMode(imgIContainer::kDontAnimMode);
michael@0 1399 }
michael@0 1400
michael@0 1401 // The third controller takes an nsIEditor as the context
michael@0 1402 rv = SetupEditorCommandController("@mozilla.org/editor/htmleditorcontroller;1",
michael@0 1403 aWindow, editor,
michael@0 1404 &mHTMLCommandControllerId);
michael@0 1405 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1406
michael@0 1407 // Set context on all controllers to be the editor
michael@0 1408 rv = SetEditorOnControllers(aWindow, editor);
michael@0 1409 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1410
michael@0 1411 #ifdef DEBUG
michael@0 1412 {
michael@0 1413 bool isEditable;
michael@0 1414 rv = WindowIsEditable(aWindow, &isEditable);
michael@0 1415 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1416 NS_ASSERTION(isEditable, "Window is not editable after reattaching editor.");
michael@0 1417 }
michael@0 1418 #endif // DEBUG
michael@0 1419
michael@0 1420 return NS_OK;
michael@0 1421 }

mercurial