Coverage Report

Created: 2018-09-25 14:53

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