Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/editor/composer/ComposerCommandsUpdater.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
 *
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 "mozilla/ComposerCommandsUpdater.h"
8
9
#include "mozilla/mozalloc.h"           // for operator new
10
#include "mozilla/TransactionManager.h" // for TransactionManager
11
#include "mozilla/dom/Selection.h"
12
#include "nsAString.h"
13
#include "nsComponentManagerUtils.h"    // for do_CreateInstance
14
#include "nsDebug.h"                    // for NS_ENSURE_TRUE, etc
15
#include "nsError.h"                    // for NS_OK, NS_ERROR_FAILURE, etc
16
#include "nsICommandManager.h"          // for nsICommandManager
17
#include "nsID.h"                       // for NS_GET_IID, etc
18
#include "nsIDOMWindow.h"               // for nsIDOMWindow
19
#include "nsIDocShell.h"                // for nsIDocShell
20
#include "nsIInterfaceRequestorUtils.h"  // for do_GetInterface
21
#include "nsITransactionManager.h"      // for nsITransactionManager
22
#include "nsLiteralString.h"            // for NS_LITERAL_STRING
23
#include "nsPICommandUpdater.h"         // for nsPICommandUpdater
24
#include "nsPIDOMWindow.h"              // for nsPIDOMWindow
25
26
class nsITransaction;
27
28
namespace mozilla {
29
30
ComposerCommandsUpdater::ComposerCommandsUpdater()
31
  : mDirtyState(eStateUninitialized)
32
  , mSelectionCollapsed(eStateUninitialized)
33
  , mFirstDoOfFirstUndo(true)
34
0
{
35
0
}
36
37
ComposerCommandsUpdater::~ComposerCommandsUpdater()
38
0
{
39
0
  // cancel any outstanding update timer
40
0
  if (mUpdateTimer) {
41
0
    mUpdateTimer->Cancel();
42
0
  }
43
0
}
44
45
NS_IMPL_CYCLE_COLLECTING_ADDREF(ComposerCommandsUpdater)
46
NS_IMPL_CYCLE_COLLECTING_RELEASE(ComposerCommandsUpdater)
47
48
0
NS_INTERFACE_MAP_BEGIN(ComposerCommandsUpdater)
49
0
  NS_INTERFACE_MAP_ENTRY(nsIDocumentStateListener)
50
0
  NS_INTERFACE_MAP_ENTRY(nsITransactionListener)
51
0
  NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
52
0
  NS_INTERFACE_MAP_ENTRY(nsINamed)
53
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocumentStateListener)
54
0
  NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(ComposerCommandsUpdater)
55
0
NS_INTERFACE_MAP_END
56
57
NS_IMPL_CYCLE_COLLECTION(ComposerCommandsUpdater,
58
                         mUpdateTimer,
59
                         mDOMWindow,
60
                         mDocShell)
61
62
#if 0
63
#pragma mark -
64
#endif
65
66
NS_IMETHODIMP
67
ComposerCommandsUpdater::NotifyDocumentCreated()
68
0
{
69
0
  // Trigger an nsIObserve notification that the document has been created
70
0
  UpdateOneCommand("obs_documentCreated");
71
0
  return NS_OK;
72
0
}
73
74
NS_IMETHODIMP
75
ComposerCommandsUpdater::NotifyDocumentWillBeDestroyed()
76
0
{
77
0
  // cancel any outstanding update timer
78
0
  if (mUpdateTimer) {
79
0
    mUpdateTimer->Cancel();
80
0
    mUpdateTimer = nullptr;
81
0
  }
82
0
83
0
  // We can't call this right now; it is too late in some cases and the window
84
0
  // is already partially destructed (e.g. JS objects may be gone).
85
#if 0
86
  // Trigger an nsIObserve notification that the document will be destroyed
87
  UpdateOneCommand("obs_documentWillBeDestroyed");
88
#endif
89
  return NS_OK;
90
0
}
91
92
93
NS_IMETHODIMP
94
ComposerCommandsUpdater::NotifyDocumentStateChanged(bool aNowDirty)
95
0
{
96
0
  // update document modified. We should have some other notifications for this too.
97
0
  return UpdateDirtyState(aNowDirty);
98
0
}
99
100
#if 0
101
#pragma mark -
102
#endif
103
104
NS_IMETHODIMP
105
ComposerCommandsUpdater::WillDo(nsITransactionManager* aManager,
106
                                nsITransaction* aTransaction,
107
                                bool* aInterrupt)
108
0
{
109
0
  *aInterrupt = false;
110
0
  return NS_OK;
111
0
}
112
113
NS_IMETHODIMP
114
ComposerCommandsUpdater::DidDo(nsITransactionManager* aManager,
115
                               nsITransaction* aTransaction,
116
                               nsresult aDoResult)
117
0
{
118
0
  // only need to update if the status of the Undo menu item changes.
119
0
  size_t undoCount = aManager->AsTransactionManager()->NumberOfUndoItems();
120
0
  if (undoCount == 1) {
121
0
    if (mFirstDoOfFirstUndo) {
122
0
      UpdateCommandGroup(NS_LITERAL_STRING("undo"));
123
0
    }
124
0
    mFirstDoOfFirstUndo = false;
125
0
  }
126
0
127
0
  return NS_OK;
128
0
}
129
130
NS_IMETHODIMP
131
ComposerCommandsUpdater::WillUndo(nsITransactionManager* aManager,
132
                                  nsITransaction* aTransaction,
133
                                  bool* aInterrupt)
134
0
{
135
0
  *aInterrupt = false;
136
0
  return NS_OK;
137
0
}
138
139
NS_IMETHODIMP
140
ComposerCommandsUpdater::DidUndo(nsITransactionManager* aManager,
141
                                 nsITransaction* aTransaction,
142
                                 nsresult aUndoResult)
143
0
{
144
0
  size_t undoCount = aManager->AsTransactionManager()->NumberOfUndoItems();
145
0
  if (!undoCount) {
146
0
    mFirstDoOfFirstUndo = true;    // reset the state for the next do
147
0
  }
148
0
  UpdateCommandGroup(NS_LITERAL_STRING("undo"));
149
0
  return NS_OK;
150
0
}
151
152
NS_IMETHODIMP
153
ComposerCommandsUpdater::WillRedo(nsITransactionManager* aManager,
154
                                  nsITransaction* aTransaction,
155
                                  bool* aInterrupt)
156
0
{
157
0
  *aInterrupt = false;
158
0
  return NS_OK;
159
0
}
160
161
NS_IMETHODIMP
162
ComposerCommandsUpdater::DidRedo(nsITransactionManager* aManager,
163
                                 nsITransaction* aTransaction,
164
                                 nsresult aRedoResult)
165
0
{
166
0
  UpdateCommandGroup(NS_LITERAL_STRING("undo"));
167
0
  return NS_OK;
168
0
}
169
170
NS_IMETHODIMP
171
ComposerCommandsUpdater::WillBeginBatch(nsITransactionManager* aManager,
172
                                        bool* aInterrupt)
173
0
{
174
0
  *aInterrupt = false;
175
0
  return NS_OK;
176
0
}
177
178
NS_IMETHODIMP
179
ComposerCommandsUpdater::DidBeginBatch(nsITransactionManager* aManager,
180
                                       nsresult aResult)
181
0
{
182
0
  return NS_OK;
183
0
}
184
185
NS_IMETHODIMP
186
ComposerCommandsUpdater::WillEndBatch(nsITransactionManager* aManager,
187
                                      bool* aInterrupt)
188
0
{
189
0
  *aInterrupt = false;
190
0
  return NS_OK;
191
0
}
192
193
NS_IMETHODIMP
194
ComposerCommandsUpdater::DidEndBatch(nsITransactionManager* aManager,
195
                                     nsresult aResult)
196
0
{
197
0
  return NS_OK;
198
0
}
199
200
NS_IMETHODIMP
201
ComposerCommandsUpdater::WillMerge(nsITransactionManager* aManager,
202
                                   nsITransaction* aTopTransaction,
203
                                   nsITransaction* aTransactionToMerge,
204
                                   bool* aInterrupt)
205
0
{
206
0
  *aInterrupt = false;
207
0
  return NS_OK;
208
0
}
209
210
NS_IMETHODIMP
211
ComposerCommandsUpdater::DidMerge(nsITransactionManager* aManager,
212
                                  nsITransaction* aTopTransaction,
213
                                  nsITransaction* aTransactionToMerge,
214
                                  bool aDidMerge,
215
                                  nsresult aMergeResult)
216
0
{
217
0
  return NS_OK;
218
0
}
219
220
#if 0
221
#pragma mark -
222
#endif
223
224
nsresult
225
ComposerCommandsUpdater::Init(nsPIDOMWindowOuter* aDOMWindow)
226
0
{
227
0
  if (NS_WARN_IF(!aDOMWindow)) {
228
0
    return NS_ERROR_INVALID_ARG;
229
0
  }
230
0
  mDOMWindow = aDOMWindow;
231
0
  mDocShell = aDOMWindow->GetDocShell();
232
0
  return NS_OK;
233
0
}
234
235
nsresult
236
ComposerCommandsUpdater::PrimeUpdateTimer()
237
0
{
238
0
  if (!mUpdateTimer) {
239
0
    mUpdateTimer = NS_NewTimer();;
240
0
    NS_ENSURE_TRUE(mUpdateTimer, NS_ERROR_OUT_OF_MEMORY);
241
0
  }
242
0
243
0
  const uint32_t kUpdateTimerDelay = 150;
244
0
  return mUpdateTimer->InitWithCallback(static_cast<nsITimerCallback*>(this),
245
0
                                        kUpdateTimerDelay,
246
0
                                        nsITimer::TYPE_ONE_SHOT);
247
0
}
248
249
250
void
251
ComposerCommandsUpdater::TimerCallback()
252
0
{
253
0
  // if the selection state has changed, update stuff
254
0
  bool isCollapsed = SelectionIsCollapsed();
255
0
  if (static_cast<int8_t>(isCollapsed) != mSelectionCollapsed) {
256
0
    UpdateCommandGroup(NS_LITERAL_STRING("select"));
257
0
    mSelectionCollapsed = isCollapsed;
258
0
  }
259
0
260
0
  // isn't this redundant with the UpdateCommandGroup above?
261
0
  // can we just nuke the above call? or create a meta command group?
262
0
  UpdateCommandGroup(NS_LITERAL_STRING("style"));
263
0
}
264
265
nsresult
266
ComposerCommandsUpdater::UpdateDirtyState(bool aNowDirty)
267
0
{
268
0
  if (mDirtyState != static_cast<int8_t>(aNowDirty)) {
269
0
    UpdateCommandGroup(NS_LITERAL_STRING("save"));
270
0
    UpdateCommandGroup(NS_LITERAL_STRING("undo"));
271
0
    mDirtyState = aNowDirty;
272
0
  }
273
0
274
0
  return NS_OK;
275
0
}
276
277
nsresult
278
ComposerCommandsUpdater::UpdateCommandGroup(const nsAString& aCommandGroup)
279
0
{
280
0
  nsCOMPtr<nsPICommandUpdater> commandUpdater = GetCommandUpdater();
281
0
  NS_ENSURE_TRUE(commandUpdater, NS_ERROR_FAILURE);
282
0
283
0
284
0
  if (aCommandGroup.EqualsLiteral("undo")) {
285
0
    commandUpdater->CommandStatusChanged("cmd_undo");
286
0
    commandUpdater->CommandStatusChanged("cmd_redo");
287
0
    return NS_OK;
288
0
  }
289
0
290
0
  if (aCommandGroup.EqualsLiteral("select") ||
291
0
      aCommandGroup.EqualsLiteral("style")) {
292
0
    commandUpdater->CommandStatusChanged("cmd_bold");
293
0
    commandUpdater->CommandStatusChanged("cmd_italic");
294
0
    commandUpdater->CommandStatusChanged("cmd_underline");
295
0
    commandUpdater->CommandStatusChanged("cmd_tt");
296
0
297
0
    commandUpdater->CommandStatusChanged("cmd_strikethrough");
298
0
    commandUpdater->CommandStatusChanged("cmd_superscript");
299
0
    commandUpdater->CommandStatusChanged("cmd_subscript");
300
0
    commandUpdater->CommandStatusChanged("cmd_nobreak");
301
0
302
0
    commandUpdater->CommandStatusChanged("cmd_em");
303
0
    commandUpdater->CommandStatusChanged("cmd_strong");
304
0
    commandUpdater->CommandStatusChanged("cmd_cite");
305
0
    commandUpdater->CommandStatusChanged("cmd_abbr");
306
0
    commandUpdater->CommandStatusChanged("cmd_acronym");
307
0
    commandUpdater->CommandStatusChanged("cmd_code");
308
0
    commandUpdater->CommandStatusChanged("cmd_samp");
309
0
    commandUpdater->CommandStatusChanged("cmd_var");
310
0
311
0
    commandUpdater->CommandStatusChanged("cmd_increaseFont");
312
0
    commandUpdater->CommandStatusChanged("cmd_decreaseFont");
313
0
314
0
    commandUpdater->CommandStatusChanged("cmd_paragraphState");
315
0
    commandUpdater->CommandStatusChanged("cmd_fontFace");
316
0
    commandUpdater->CommandStatusChanged("cmd_fontColor");
317
0
    commandUpdater->CommandStatusChanged("cmd_backgroundColor");
318
0
    commandUpdater->CommandStatusChanged("cmd_highlight");
319
0
    return NS_OK;
320
0
  }
321
0
322
0
  if (aCommandGroup.EqualsLiteral("save")) {
323
0
    // save commands (most are not in C++)
324
0
    commandUpdater->CommandStatusChanged("cmd_setDocumentModified");
325
0
    commandUpdater->CommandStatusChanged("cmd_save");
326
0
    return NS_OK;
327
0
  }
328
0
329
0
  return NS_OK;
330
0
}
331
332
nsresult
333
ComposerCommandsUpdater::UpdateOneCommand(const char* aCommand)
334
0
{
335
0
  nsCOMPtr<nsPICommandUpdater> commandUpdater = GetCommandUpdater();
336
0
  NS_ENSURE_TRUE(commandUpdater, NS_ERROR_FAILURE);
337
0
338
0
  commandUpdater->CommandStatusChanged(aCommand);
339
0
340
0
  return NS_OK;
341
0
}
342
343
bool
344
ComposerCommandsUpdater::SelectionIsCollapsed()
345
0
{
346
0
  if (NS_WARN_IF(!mDOMWindow)) {
347
0
    return true;
348
0
  }
349
0
350
0
  RefPtr<dom::Selection> domSelection = mDOMWindow->GetSelection();
351
0
  if (NS_WARN_IF(!domSelection)) {
352
0
    return false;
353
0
  }
354
0
355
0
  return domSelection->IsCollapsed();
356
0
}
357
358
already_AddRefed<nsPICommandUpdater>
359
ComposerCommandsUpdater::GetCommandUpdater()
360
0
{
361
0
  if (NS_WARN_IF(!mDocShell)) {
362
0
    return nullptr;
363
0
  }
364
0
365
0
  nsCOMPtr<nsICommandManager> manager = mDocShell->GetCommandManager();
366
0
  nsCOMPtr<nsPICommandUpdater> updater = do_QueryInterface(manager);
367
0
  return updater.forget();
368
0
}
369
370
NS_IMETHODIMP
371
ComposerCommandsUpdater::GetName(nsACString& aName)
372
0
{
373
0
  aName.AssignLiteral("ComposerCommandsUpdater");
374
0
  return NS_OK;
375
0
}
376
377
#if 0
378
#pragma mark -
379
#endif
380
381
nsresult
382
ComposerCommandsUpdater::Notify(nsITimer* aTimer)
383
0
{
384
0
  NS_ASSERTION(aTimer == mUpdateTimer.get(), "Hey, this ain't my timer!");
385
0
  TimerCallback();
386
0
  return NS_OK;
387
0
}
388
389
#if 0
390
#pragma mark -
391
#endif
392
393
} // namespace mozilla