Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/base/PresShell.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
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
/* a presentation of a document, part 2 */
8
9
#include "mozilla/PresShell.h"
10
11
#include "mozilla/dom/FontFaceSet.h"
12
#include "mozilla/ArrayUtils.h"
13
#include "mozilla/Attributes.h"
14
#include "mozilla/AutoRestore.h"
15
#include "mozilla/StyleSheetInlines.h"
16
#include "mozilla/EventDispatcher.h"
17
#include "mozilla/EventStateManager.h"
18
#include "mozilla/EventStates.h"
19
#include "mozilla/IMEStateManager.h"
20
#include "mozilla/MemoryReporting.h"
21
#include "mozilla/dom/TabChild.h"
22
#include "mozilla/Likely.h"
23
#include "mozilla/Logging.h"
24
#include "mozilla/MouseEvents.h"
25
#include "mozilla/Sprintf.h"
26
#include "mozilla/StaticPrefs.h"
27
#include "mozilla/TextEvents.h"
28
#include "mozilla/TimeStamp.h"
29
#include "mozilla/TouchEvents.h"
30
#include "mozilla/UniquePtr.h"
31
#include "mozilla/Unused.h"
32
#include <algorithm>
33
34
#ifdef XP_WIN
35
#include "winuser.h"
36
#endif
37
38
#include "gfxContext.h"
39
#include "gfxPrefs.h"
40
#include "gfxUserFontSet.h"
41
#include "nsContentList.h"
42
#include "nsPresContext.h"
43
#include "nsIContent.h"
44
#include "nsIContentIterator.h"
45
#include "nsIPresShellInlines.h"
46
#include "mozilla/dom/Element.h"
47
#include "mozilla/dom/Event.h" // for Event::GetEventPopupControlState()
48
#include "mozilla/dom/PointerEventHandler.h"
49
#include "nsIDocument.h"
50
#include "nsAnimationManager.h"
51
#include "nsNameSpaceManager.h"  // for Pref-related rule management (bugs 22963,20760,31816)
52
#include "nsFrame.h"
53
#include "FrameLayerBuilder.h"
54
#include "nsViewManager.h"
55
#include "nsView.h"
56
#include "nsCRTGlue.h"
57
#include "prinrval.h"
58
#include "nsTArray.h"
59
#include "nsCOMArray.h"
60
#include "nsContainerFrame.h"
61
#include "mozilla/dom/Selection.h"
62
#include "nsGkAtoms.h"
63
#include "nsRange.h"
64
#include "nsWindowSizes.h"
65
#include "nsCOMPtr.h"
66
#include "nsAutoPtr.h"
67
#include "nsReadableUtils.h"
68
#include "nsIPageSequenceFrame.h"
69
#include "nsIPermissionManager.h"
70
#include "nsIMozBrowserFrame.h"
71
#include "nsCaret.h"
72
#include "mozilla/AccessibleCaretEventHub.h"
73
#include "nsFrameManager.h"
74
#include "nsXPCOM.h"
75
#include "nsILayoutHistoryState.h"
76
#include "nsILineIterator.h" // for ScrollContentIntoView
77
#include "PLDHashTable.h"
78
#include "mozilla/dom/Touch.h"
79
#include "mozilla/dom/TouchEvent.h"
80
#include "mozilla/dom/PointerEventBinding.h"
81
#include "nsIObserverService.h"
82
#include "nsDocShell.h"        // for reflow observation
83
#include "nsIBaseWindow.h"
84
#include "nsError.h"
85
#include "nsLayoutUtils.h"
86
#include "nsViewportInfo.h"
87
#include "nsCSSRendering.h"
88
  // for |#ifdef DEBUG| code
89
#include "prenv.h"
90
#include "nsDisplayList.h"
91
#include "nsRegion.h"
92
#include "nsAutoLayoutPhase.h"
93
#ifdef MOZ_GECKO_PROFILER
94
#include "AutoProfilerStyleMarker.h"
95
#endif
96
#ifdef MOZ_REFLOW_PERF
97
#include "nsFontMetrics.h"
98
#endif
99
#include "PositionedEventTargeting.h"
100
101
#include "nsIReflowCallback.h"
102
103
#include "nsPIDOMWindow.h"
104
#include "nsFocusManager.h"
105
#include "nsIObjectFrame.h"
106
#include "nsIObjectLoadingContent.h"
107
#include "nsNetUtil.h"
108
#include "nsThreadUtils.h"
109
#include "nsStyleSheetService.h"
110
#include "gfxUtils.h"
111
#include "nsSMILAnimationController.h"
112
#include "SVGContentUtils.h"
113
#include "SVGObserverUtils.h"
114
#include "SVGFragmentIdentifier.h"
115
#include "nsFrameSelection.h"
116
117
#include "mozilla/dom/Performance.h"
118
#include "nsRefreshDriver.h"
119
#include "nsDOMNavigationTiming.h"
120
121
// Drag & Drop, Clipboard
122
#include "nsIDocShellTreeItem.h"
123
#include "nsIURI.h"
124
#include "nsIScrollableFrame.h"
125
#include "nsITimer.h"
126
#ifdef ACCESSIBILITY
127
#include "nsAccessibilityService.h"
128
#include "mozilla/a11y/DocAccessible.h"
129
#ifdef DEBUG
130
#include "mozilla/a11y/Logging.h"
131
#endif
132
#endif
133
134
// For style data reconstruction
135
#include "nsStyleChangeList.h"
136
#include "nsCSSFrameConstructor.h"
137
#ifdef MOZ_XUL
138
#include "nsMenuFrame.h"
139
#include "nsTreeBodyFrame.h"
140
#include "nsIBoxObject.h"
141
#include "nsITreeBoxObject.h"
142
#include "nsMenuPopupFrame.h"
143
#include "nsTreeColumns.h"
144
#include "nsIDOMXULMultSelectCntrlEl.h"
145
#include "nsIDOMXULSelectCntrlItemEl.h"
146
#include "nsIDOMXULMenuListElement.h"
147
#include "nsXULElement.h"
148
#include "mozilla/dom/BoxObject.h"
149
#endif // MOZ_XUL
150
151
#include "mozilla/layers/CompositorBridgeChild.h"
152
#include "ClientLayerManager.h"
153
#include "GeckoProfiler.h"
154
#include "gfxPlatform.h"
155
#include "Layers.h"
156
#include "LayerTreeInvalidation.h"
157
#include "mozilla/css/ImageLoader.h"
158
#include "mozilla/dom/DocumentTimeline.h"
159
#include "mozilla/dom/ScriptSettings.h"
160
#include "mozilla/ErrorResult.h"
161
#include "mozilla/Preferences.h"
162
#include "mozilla/Telemetry.h"
163
#include "nsCanvasFrame.h"
164
#include "nsIImageLoadingContent.h"
165
#include "nsImageFrame.h"
166
#include "nsIScreen.h"
167
#include "nsIScreenManager.h"
168
#include "nsPlaceholderFrame.h"
169
#include "nsTransitionManager.h"
170
#include "ChildIterator.h"
171
#include "mozilla/RestyleManager.h"
172
#include "nsIDragSession.h"
173
#include "nsIFrameInlines.h"
174
#include "mozilla/gfx/2D.h"
175
#include "nsSubDocumentFrame.h"
176
#include "nsQueryObject.h"
177
#include "nsLayoutStylesheetCache.h"
178
#include "mozilla/layers/InputAPZContext.h"
179
#include "mozilla/layers/FocusTarget.h"
180
#include "mozilla/layers/WebRenderLayerManager.h"
181
#include "mozilla/layers/WebRenderUserData.h"
182
#include "mozilla/ServoBindings.h"
183
#include "mozilla/ServoStyleSet.h"
184
#include "mozilla/StyleSheet.h"
185
#include "mozilla/StyleSheetInlines.h"
186
#include "mozilla/dom/ImageTracker.h"
187
#include "nsIDocShellTreeOwner.h"
188
#include "nsBindingManager.h"
189
#include "nsClassHashtable.h"
190
#include "nsHashKeys.h"
191
192
#ifdef MOZ_TASK_TRACER
193
#include "GeckoTaskTracer.h"
194
using namespace mozilla::tasktracer;
195
#endif
196
197
#define ANCHOR_SCROLL_FLAGS \
198
0
  (nsIPresShell::SCROLL_OVERFLOW_HIDDEN | nsIPresShell::SCROLL_NO_PARENT_FRAMES)
199
200
  // define the scalfactor of drag and drop images
201
  // relative to the max screen height/width
202
0
#define RELATIVE_SCALEFACTOR 0.0925f
203
204
using namespace mozilla;
205
using namespace mozilla::css;
206
using namespace mozilla::dom;
207
using namespace mozilla::gfx;
208
using namespace mozilla::layers;
209
using namespace mozilla::gfx;
210
using namespace mozilla::layout;
211
using PaintFrameFlags = nsLayoutUtils::PaintFrameFlags;
212
typedef FrameMetrics::ViewID ViewID;
213
214
CapturingContentInfo nsIPresShell::gCaptureInfo =
215
  { false /* mAllowed */, false /* mPointerLock */, false /* mRetargetToElement */,
216
    false /* mPreventDrag */ };
217
nsIContent* nsIPresShell::gKeyDownTarget;
218
219
// RangePaintInfo is used to paint ranges to offscreen buffers
220
struct RangePaintInfo {
221
  RefPtr<nsRange> mRange;
222
  nsDisplayListBuilder mBuilder;
223
  nsDisplayList mList;
224
225
  // offset of builder's reference frame to the root frame
226
  nsPoint mRootOffset;
227
228
  RangePaintInfo(nsRange* aRange, nsIFrame* aFrame)
229
    : mRange(aRange)
230
    , mBuilder(aFrame, nsDisplayListBuilderMode::PAINTING, false)
231
0
  {
232
0
    MOZ_COUNT_CTOR(RangePaintInfo);
233
0
    mBuilder.BeginFrame();
234
0
  }
235
236
  ~RangePaintInfo()
237
0
  {
238
0
    mList.DeleteAll(&mBuilder);
239
0
    mBuilder.EndFrame();
240
0
    MOZ_COUNT_DTOR(RangePaintInfo);
241
0
  }
242
};
243
244
#undef NOISY
245
246
// ----------------------------------------------------------------------
247
248
#ifdef DEBUG
249
// Set the environment variable GECKO_VERIFY_REFLOW_FLAGS to one or
250
// more of the following flags (comma separated) for handy debug
251
// output.
252
static uint32_t gVerifyReflowFlags;
253
254
struct VerifyReflowFlags {
255
  const char*    name;
256
  uint32_t bit;
257
};
258
259
static const VerifyReflowFlags gFlags[] = {
260
  { "verify",                VERIFY_REFLOW_ON },
261
  { "reflow",                VERIFY_REFLOW_NOISY },
262
  { "all",                   VERIFY_REFLOW_ALL },
263
  { "list-commands",         VERIFY_REFLOW_DUMP_COMMANDS },
264
  { "noisy-commands",        VERIFY_REFLOW_NOISY_RC },
265
  { "really-noisy-commands", VERIFY_REFLOW_REALLY_NOISY_RC },
266
  { "resize",                VERIFY_REFLOW_DURING_RESIZE_REFLOW },
267
};
268
269
#define NUM_VERIFY_REFLOW_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
270
271
static void
272
ShowVerifyReflowFlags()
273
{
274
  printf("Here are the available GECKO_VERIFY_REFLOW_FLAGS:\n");
275
  const VerifyReflowFlags* flag = gFlags;
276
  const VerifyReflowFlags* limit = gFlags + NUM_VERIFY_REFLOW_FLAGS;
277
  while (flag < limit) {
278
    printf("  %s\n", flag->name);
279
    ++flag;
280
  }
281
  printf("Note: GECKO_VERIFY_REFLOW_FLAGS is a comma separated list of flag\n");
282
  printf("names (no whitespace)\n");
283
}
284
#endif
285
286
//========================================================================
287
//========================================================================
288
//========================================================================
289
#ifdef MOZ_REFLOW_PERF
290
class ReflowCountMgr;
291
292
static const char kGrandTotalsStr[] = "Grand Totals";
293
294
// Counting Class
295
class ReflowCounter {
296
public:
297
  explicit ReflowCounter(ReflowCountMgr * aMgr = nullptr);
298
  ~ReflowCounter();
299
300
  void ClearTotals();
301
  void DisplayTotals(const char * aStr);
302
  void DisplayDiffTotals(const char * aStr);
303
  void DisplayHTMLTotals(const char * aStr);
304
305
  void Add()                { mTotal++;         }
306
  void Add(uint32_t aTotal) { mTotal += aTotal; }
307
308
  void CalcDiffInTotals();
309
  void SetTotalsCache();
310
311
  void SetMgr(ReflowCountMgr * aMgr) { mMgr = aMgr; }
312
313
  uint32_t GetTotal() { return mTotal; }
314
315
protected:
316
  void DisplayTotals(uint32_t aTotal, const char * aTitle);
317
  void DisplayHTMLTotals(uint32_t aTotal, const char * aTitle);
318
319
  uint32_t mTotal;
320
  uint32_t mCacheTotal;
321
322
  ReflowCountMgr * mMgr; // weak reference (don't delete)
323
};
324
325
// Counting Class
326
class IndiReflowCounter {
327
public:
328
  explicit IndiReflowCounter(ReflowCountMgr * aMgr = nullptr)
329
    : mFrame(nullptr),
330
      mCount(0),
331
      mMgr(aMgr),
332
      mCounter(aMgr),
333
      mHasBeenOutput(false)
334
    {}
335
  virtual ~IndiReflowCounter() {}
336
337
  nsAutoString mName;
338
  nsIFrame *   mFrame;   // weak reference (don't delete)
339
  int32_t      mCount;
340
341
  ReflowCountMgr * mMgr; // weak reference (don't delete)
342
343
  ReflowCounter mCounter;
344
  bool          mHasBeenOutput;
345
346
};
347
348
//--------------------
349
// Manager Class
350
//--------------------
351
class ReflowCountMgr {
352
public:
353
  ReflowCountMgr();
354
  virtual ~ReflowCountMgr();
355
356
  void ClearTotals();
357
  void ClearGrandTotals();
358
  void DisplayTotals(const char * aStr);
359
  void DisplayHTMLTotals(const char * aStr);
360
  void DisplayDiffsInTotals();
361
362
  void Add(const char * aName, nsIFrame * aFrame);
363
  ReflowCounter * LookUp(const char * aName);
364
365
  void PaintCount(const char *aName, gfxContext* aRenderingContext,
366
                  nsPresContext *aPresContext, nsIFrame *aFrame,
367
                  const nsPoint &aOffset, uint32_t aColor);
368
369
  FILE * GetOutFile() { return mFD; }
370
371
  void SetPresContext(nsPresContext * aPresContext) { mPresContext = aPresContext; } // weak reference
372
  void SetPresShell(nsIPresShell* aPresShell) { mPresShell= aPresShell; } // weak reference
373
374
  void SetDumpFrameCounts(bool aVal)         { mDumpFrameCounts = aVal; }
375
  void SetDumpFrameByFrameCounts(bool aVal)  { mDumpFrameByFrameCounts = aVal; }
376
  void SetPaintFrameCounts(bool aVal)        { mPaintFrameByFrameCounts = aVal; }
377
378
  bool IsPaintingFrameCounts() { return mPaintFrameByFrameCounts; }
379
380
protected:
381
  void DisplayTotals(uint32_t aTotal, uint32_t * aDupArray, char * aTitle);
382
  void DisplayHTMLTotals(uint32_t aTotal, uint32_t * aDupArray, char * aTitle);
383
384
  void DoGrandTotals();
385
  void DoIndiTotalsTree();
386
387
  // HTML Output Methods
388
  void DoGrandHTMLTotals();
389
390
  nsClassHashtable<nsCharPtrHashKey, ReflowCounter> mCounts;
391
  nsClassHashtable<nsCharPtrHashKey, IndiReflowCounter> mIndiFrameCounts;
392
  FILE * mFD;
393
394
  bool mDumpFrameCounts;
395
  bool mDumpFrameByFrameCounts;
396
  bool mPaintFrameByFrameCounts;
397
398
  bool mCycledOnce;
399
400
  // Root Frame for Individual Tracking
401
  nsPresContext * mPresContext;
402
  nsIPresShell*    mPresShell;
403
404
  // ReflowCountMgr gReflowCountMgr;
405
};
406
#endif
407
//========================================================================
408
409
// comment out to hide caret
410
#define SHOW_CARET
411
412
// The upper bound on the amount of time to spend reflowing, in
413
// microseconds.  When this bound is exceeded and reflow commands are
414
// still queued up, a reflow event is posted.  The idea is for reflow
415
// to not hog the processor beyond the time specifed in
416
// gMaxRCProcessingTime.  This data member is initialized from the
417
// layout.reflow.timeslice pref.
418
0
#define NS_MAX_REFLOW_TIME    1000000
419
static int32_t gMaxRCProcessingTime = -1;
420
421
struct nsCallbackEventRequest
422
{
423
  nsIReflowCallback* callback;
424
  nsCallbackEventRequest* next;
425
};
426
427
// ----------------------------------------------------------------------------
428
//
429
// NOTE(emilio): It'd be nice for this to assert that our document isn't in the
430
// bfcache, but font pref changes don't care about that, and maybe / probably
431
// shouldn't.
432
#ifdef DEBUG
433
#define ASSERT_REFLOW_SCHEDULED_STATE()                                       \
434
{                                                                             \
435
  if (ObservingLayoutFlushes()) {                                             \
436
    MOZ_ASSERT(mDocument->GetBFCacheEntry() ||                                \
437
               mPresContext->RefreshDriver()->IsLayoutFlushObserver(this),    \
438
               "Unexpected state");                                           \
439
  } else {                                                                    \
440
    MOZ_ASSERT(!mPresContext->RefreshDriver()->IsLayoutFlushObserver(this),   \
441
               "Unexpected state");                                           \
442
  }                                                                           \
443
}
444
#else
445
#define ASSERT_REFLOW_SCHEDULED_STATE() /* nothing */
446
#endif
447
448
class nsAutoCauseReflowNotifier
449
{
450
public:
451
  explicit nsAutoCauseReflowNotifier(PresShell* aShell)
452
    : mShell(aShell)
453
0
  {
454
0
    mShell->WillCauseReflow();
455
0
  }
456
  ~nsAutoCauseReflowNotifier()
457
0
  {
458
0
    // This check should not be needed. Currently the only place that seem
459
0
    // to need it is the code that deals with bug 337586.
460
0
    if (!mShell->mHaveShutDown) {
461
0
      mShell->DidCauseReflow();
462
0
    }
463
0
    else {
464
0
      nsContentUtils::RemoveScriptBlocker();
465
0
    }
466
0
  }
467
468
  PresShell* mShell;
469
};
470
471
class MOZ_STACK_CLASS nsPresShellEventCB : public EventDispatchingCallback
472
{
473
public:
474
0
  explicit nsPresShellEventCB(PresShell* aPresShell) : mPresShell(aPresShell) {}
475
476
  virtual void HandleEvent(EventChainPostVisitor& aVisitor) override
477
0
  {
478
0
    if (aVisitor.mPresContext && aVisitor.mEvent->mClass != eBasicEventClass) {
479
0
      if (aVisitor.mEvent->mMessage == eMouseDown ||
480
0
          aVisitor.mEvent->mMessage == eMouseUp) {
481
0
        // Mouse-up and mouse-down events call nsFrame::HandlePress/Release
482
0
        // which call GetContentOffsetsFromPoint which requires up-to-date layout.
483
0
        // Bring layout up-to-date now so that GetCurrentEventFrame() below
484
0
        // will return a real frame and we don't have to worry about
485
0
        // destroying it by flushing later.
486
0
        mPresShell->FlushPendingNotifications(FlushType::Layout);
487
0
      } else if (aVisitor.mEvent->mMessage == eWheel &&
488
0
                 aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault) {
489
0
        nsIFrame* frame = mPresShell->GetCurrentEventFrame();
490
0
        if (frame) {
491
0
          // chrome (including addons) should be able to know if content
492
0
          // handles both D3E "wheel" event and legacy mouse scroll events.
493
0
          // We should dispatch legacy mouse events before dispatching the
494
0
          // "wheel" event into system group.
495
0
          RefPtr<EventStateManager> esm =
496
0
            aVisitor.mPresContext->EventStateManager();
497
0
          esm->DispatchLegacyMouseScrollEvents(frame,
498
0
                                               aVisitor.mEvent->AsWheelEvent(),
499
0
                                               &aVisitor.mEventStatus);
500
0
        }
501
0
      }
502
0
      nsIFrame* frame = mPresShell->GetCurrentEventFrame();
503
0
      if (!frame &&
504
0
          (aVisitor.mEvent->mMessage == eMouseUp ||
505
0
           aVisitor.mEvent->mMessage == eTouchEnd)) {
506
0
        // Redirect BUTTON_UP and TOUCH_END events to the root frame to ensure
507
0
        // that capturing is released.
508
0
        frame = mPresShell->GetRootFrame();
509
0
      }
510
0
      if (frame) {
511
0
        frame->HandleEvent(aVisitor.mPresContext,
512
0
                           aVisitor.mEvent->AsGUIEvent(),
513
0
                           &aVisitor.mEventStatus);
514
0
      }
515
0
    }
516
0
  }
517
518
  RefPtr<PresShell> mPresShell;
519
};
520
521
class nsBeforeFirstPaintDispatcher : public Runnable
522
{
523
public:
524
  explicit nsBeforeFirstPaintDispatcher(nsIDocument* aDocument)
525
    : mozilla::Runnable("nsBeforeFirstPaintDispatcher")
526
    , mDocument(aDocument)
527
0
  {
528
0
  }
529
530
  // Fires the "before-first-paint" event so that interested parties (right now, the
531
  // mobile browser) are aware of it.
532
  NS_IMETHOD Run() override
533
0
  {
534
0
    nsCOMPtr<nsIObserverService> observerService =
535
0
      mozilla::services::GetObserverService();
536
0
    if (observerService) {
537
0
      observerService->NotifyObservers(mDocument, "before-first-paint",
538
0
                                       nullptr);
539
0
    }
540
0
    return NS_OK;
541
0
  }
542
543
private:
544
  nsCOMPtr<nsIDocument> mDocument;
545
};
546
547
// This is a helper class to track whether the targeted frame is destroyed after
548
// dispatching pointer events. In that case, we need the original targeted
549
// content so that we can dispatch the mouse events to it.
550
class MOZ_STACK_CLASS AutoPointerEventTargetUpdater final
551
{
552
public:
553
  AutoPointerEventTargetUpdater(PresShell* aShell,
554
                                WidgetEvent* aEvent,
555
                                nsIFrame* aFrame,
556
                                nsIContent** aTargetContent)
557
0
  {
558
0
    MOZ_ASSERT(aEvent);
559
0
    if (!aTargetContent || aEvent->mClass != ePointerEventClass) {
560
0
      // Make the destructor happy.
561
0
      mTargetContent = nullptr;
562
0
      return;
563
0
    }
564
0
    MOZ_ASSERT(aShell);
565
0
    MOZ_ASSERT(aFrame);
566
0
    MOZ_ASSERT(!aFrame->GetContent() ||
567
0
               aShell->GetDocument() == aFrame->GetContent()->OwnerDoc());
568
0
569
0
    MOZ_ASSERT(PointerEventHandler::IsPointerEventEnabled());
570
0
    mShell = aShell;
571
0
    mWeakFrame = aFrame;
572
0
    mTargetContent = aTargetContent;
573
0
    aShell->mPointerEventTarget = aFrame->GetContent();
574
0
  }
575
576
  ~AutoPointerEventTargetUpdater()
577
0
  {
578
0
    if (!mTargetContent || !mShell || mWeakFrame.IsAlive()) {
579
0
      return;
580
0
    }
581
0
    mShell->mPointerEventTarget.swap(*mTargetContent);
582
0
  }
583
584
private:
585
  RefPtr<PresShell> mShell;
586
  AutoWeakFrame mWeakFrame;
587
  nsIContent** mTargetContent;
588
};
589
590
bool PresShell::sDisableNonTestMouseEvents = false;
591
592
mozilla::LazyLogModule PresShell::gLog("PresShell");
593
594
mozilla::TimeStamp PresShell::sLastInputCreated;
595
mozilla::TimeStamp PresShell::sLastInputProcessed;
596
597
bool PresShell::sProcessInteractable = false;
598
599
static bool gVerifyReflowEnabled;
600
601
bool
602
nsIPresShell::GetVerifyReflowEnable()
603
0
{
604
#ifdef DEBUG
605
  static bool firstTime = true;
606
  if (firstTime) {
607
    firstTime = false;
608
    char* flags = PR_GetEnv("GECKO_VERIFY_REFLOW_FLAGS");
609
    if (flags) {
610
      bool error = false;
611
612
      for (;;) {
613
        char* comma = PL_strchr(flags, ',');
614
        if (comma)
615
          *comma = '\0';
616
617
        bool found = false;
618
        const VerifyReflowFlags* flag = gFlags;
619
        const VerifyReflowFlags* limit = gFlags + NUM_VERIFY_REFLOW_FLAGS;
620
        while (flag < limit) {
621
          if (PL_strcasecmp(flag->name, flags) == 0) {
622
            gVerifyReflowFlags |= flag->bit;
623
            found = true;
624
            break;
625
          }
626
          ++flag;
627
        }
628
629
        if (! found)
630
          error = true;
631
632
        if (! comma)
633
          break;
634
635
        *comma = ',';
636
        flags = comma + 1;
637
      }
638
639
      if (error)
640
        ShowVerifyReflowFlags();
641
    }
642
643
    if (VERIFY_REFLOW_ON & gVerifyReflowFlags) {
644
      gVerifyReflowEnabled = true;
645
646
      printf("Note: verifyreflow is enabled");
647
      if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
648
        printf(" (noisy)");
649
      }
650
      if (VERIFY_REFLOW_ALL & gVerifyReflowFlags) {
651
        printf(" (all)");
652
      }
653
      if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) {
654
        printf(" (show reflow commands)");
655
      }
656
      if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) {
657
        printf(" (noisy reflow commands)");
658
        if (VERIFY_REFLOW_REALLY_NOISY_RC & gVerifyReflowFlags) {
659
          printf(" (REALLY noisy reflow commands)");
660
        }
661
      }
662
      printf("\n");
663
    }
664
  }
665
#endif
666
  return gVerifyReflowEnabled;
667
0
}
668
669
void
670
nsIPresShell::SetVerifyReflowEnable(bool aEnabled)
671
0
{
672
0
  gVerifyReflowEnabled = aEnabled;
673
0
}
674
675
void
676
nsIPresShell::AddAutoWeakFrame(AutoWeakFrame* aWeakFrame)
677
0
{
678
0
  if (aWeakFrame->GetFrame()) {
679
0
    aWeakFrame->GetFrame()->AddStateBits(NS_FRAME_EXTERNAL_REFERENCE);
680
0
  }
681
0
  aWeakFrame->SetPreviousWeakFrame(mAutoWeakFrames);
682
0
  mAutoWeakFrames = aWeakFrame;
683
0
}
684
685
void
686
nsIPresShell::AddWeakFrame(WeakFrame* aWeakFrame)
687
0
{
688
0
  if (aWeakFrame->GetFrame()) {
689
0
    aWeakFrame->GetFrame()->AddStateBits(NS_FRAME_EXTERNAL_REFERENCE);
690
0
  }
691
0
  MOZ_ASSERT(!mWeakFrames.GetEntry(aWeakFrame));
692
0
  mWeakFrames.PutEntry(aWeakFrame);
693
0
}
694
695
void
696
nsIPresShell::RemoveAutoWeakFrame(AutoWeakFrame* aWeakFrame)
697
0
{
698
0
  if (mAutoWeakFrames == aWeakFrame) {
699
0
    mAutoWeakFrames = aWeakFrame->GetPreviousWeakFrame();
700
0
    return;
701
0
  }
702
0
  AutoWeakFrame* nextWeak = mAutoWeakFrames;
703
0
  while (nextWeak && nextWeak->GetPreviousWeakFrame() != aWeakFrame) {
704
0
    nextWeak = nextWeak->GetPreviousWeakFrame();
705
0
  }
706
0
  if (nextWeak) {
707
0
    nextWeak->SetPreviousWeakFrame(aWeakFrame->GetPreviousWeakFrame());
708
0
  }
709
0
}
710
711
void
712
nsIPresShell::RemoveWeakFrame(WeakFrame* aWeakFrame)
713
0
{
714
0
  MOZ_ASSERT(mWeakFrames.GetEntry(aWeakFrame));
715
0
  mWeakFrames.RemoveEntry(aWeakFrame);
716
0
}
717
718
already_AddRefed<nsFrameSelection>
719
nsIPresShell::FrameSelection()
720
0
{
721
0
  RefPtr<nsFrameSelection> ret = mSelection;
722
0
  return ret.forget();
723
0
}
724
725
//----------------------------------------------------------------------
726
727
static bool sSynthMouseMove = true;
728
static uint32_t sNextPresShellId;
729
730
/* static */ bool
731
PresShell::AccessibleCaretEnabled(nsIDocShell* aDocShell)
732
0
{
733
0
  // If the pref forces it on, then enable it.
734
0
  if (StaticPrefs::layout_accessiblecaret_enabled()) {
735
0
    return true;
736
0
  }
737
0
  // If the touch pref is on, and touch events are enabled (this depends
738
0
  // on the specific device running), then enable it.
739
0
  if (StaticPrefs::layout_accessiblecaret_enabled_on_touch() &&
740
0
      dom::TouchEvent::PrefEnabled(aDocShell)) {
741
0
    return true;
742
0
  }
743
0
  // Otherwise, disabled.
744
0
  return false;
745
0
}
746
747
nsIPresShell::nsIPresShell()
748
    : mFrameConstructor(nullptr)
749
    , mViewManager(nullptr)
750
    , mFrameManager(nullptr)
751
#ifdef ACCESSIBILITY
752
    , mDocAccessible(nullptr)
753
#endif
754
#ifdef DEBUG
755
    , mDrawEventTargetFrame(nullptr)
756
#endif
757
    , mPaintCount(0)
758
    , mAutoWeakFrames(nullptr)
759
    , mCanvasBackgroundColor(NS_RGBA(0,0,0,0))
760
    , mSelectionFlags(0)
761
    , mChangeNestCount(0)
762
    , mRenderFlags(0)
763
    , mDidInitialize(false)
764
    , mIsDestroying(false)
765
    , mIsReflowing(false)
766
    , mIsObservingDocument(false)
767
    , mIsDocumentGone(false)
768
    , mPaintingSuppressed(false)
769
    , mIsActive(false)
770
    , mFrozen(false)
771
    , mIsFirstPaint(false)
772
    , mObservesMutationsForPrint(false)
773
    , mWasLastReflowInterrupted(false)
774
    , mVisualViewportSizeSet(false)
775
    , mNeedLayoutFlush(true)
776
    , mNeedStyleFlush(true)
777
    , mObservingStyleFlushes(false)
778
    , mObservingLayoutFlushes(false)
779
    , mResizeEventPending(false)
780
    , mNeedThrottledAnimationFlush(true)
781
    , mPresShellId(0)
782
    , mFontSizeInflationEmPerLine(0)
783
    , mFontSizeInflationMinTwips(0)
784
    , mFontSizeInflationLineThreshold(0)
785
    , mFontSizeInflationForceEnabled(false)
786
    , mFontSizeInflationDisabledInMasterProcess(false)
787
    , mFontSizeInflationEnabled(false)
788
    , mPaintingIsFrozen(false)
789
    , mIsNeverPainting(false)
790
    , mInFlush(false)
791
0
  {}
792
793
PresShell::PresShell()
794
  : mCaretEnabled(false)
795
#ifdef DEBUG
796
  , mInVerifyReflow(false)
797
  , mCurrentReflowRoot(nullptr)
798
#endif
799
#ifdef MOZ_REFLOW_PERF
800
  , mReflowCountMgr(nullptr)
801
#endif
802
  , mMouseLocation(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)
803
  , mCurrentEventFrame(nullptr)
804
  , mFirstCallbackEventRequest(nullptr)
805
  , mLastCallbackEventRequest(nullptr)
806
  , mLastReflowStart(0.0)
807
  , mLastAnchorScrollPositionY(0)
808
  , mActiveSuppressDisplayport(0)
809
  , mAPZFocusSequenceNumber(0)
810
  , mDocumentLoading(false)
811
  , mIgnoreFrameDestruction(false)
812
  , mHaveShutDown(false)
813
  , mLastRootReflowHadUnconstrainedBSize(false)
814
  , mNoDelayedMouseEvents(false)
815
  , mNoDelayedKeyEvents(false)
816
  , mShouldUnsuppressPainting(false)
817
  , mApproximateFrameVisibilityVisited(false)
818
  , mNextPaintCompressed(false)
819
  , mHasCSSBackgroundColor(false)
820
  , mScaleToResolution(false)
821
  , mIsLastChromeOnlyEscapeKeyConsumed(false)
822
  , mHasReceivedPaintMessage(false)
823
  , mIsLastKeyDownCanceled(false)
824
  , mHasHandledUserInput(false)
825
#ifdef NIGHTLY_BUILD
826
  , mForceDispatchKeyPressEventsForNonPrintableKeys(false)
827
  , mInitializedForceDispatchKeyPressEventsForNonPrintableKeys(false)
828
#endif // #ifdef NIGHTLY_BUILD
829
0
{
830
0
  MOZ_LOG(gLog, LogLevel::Debug, ("PresShell::PresShell this=%p", this));
831
0
832
#ifdef MOZ_REFLOW_PERF
833
  mReflowCountMgr = new ReflowCountMgr();
834
  mReflowCountMgr->SetPresContext(mPresContext);
835
  mReflowCountMgr->SetPresShell(this);
836
#endif
837
  mLastOSWake = mLoadBegin = TimeStamp::Now();
838
0
839
0
  mSelectionFlags = nsISelectionDisplay::DISPLAY_TEXT | nsISelectionDisplay::DISPLAY_IMAGES;
840
0
  mIsActive = true;
841
0
  // FIXME/bug 735029: find a better solution to this problem
842
0
  mIsFirstPaint = true;
843
0
  mPresShellId = sNextPresShellId++;
844
0
  mFrozen = false;
845
0
  mRenderFlags = 0;
846
0
847
0
  mVisualViewportSizeSet = false;
848
0
849
0
  static bool addedSynthMouseMove = false;
850
0
  if (!addedSynthMouseMove) {
851
0
    Preferences::AddBoolVarCache(&sSynthMouseMove,
852
0
                                 "layout.reflow.synthMouseMove", true);
853
0
    addedSynthMouseMove = true;
854
0
  }
855
0
  PointerEventHandler::Initialize();
856
0
  mPaintingIsFrozen = false;
857
0
  mHasCSSBackgroundColor = true;
858
0
  mIsLastChromeOnlyEscapeKeyConsumed = false;
859
0
  mHasReceivedPaintMessage = false;
860
0
}
861
862
NS_IMPL_ISUPPORTS(PresShell, nsIPresShell, nsIDocumentObserver,
863
                  nsISelectionController,
864
                  nsISelectionDisplay, nsIObserver, nsISupportsWeakReference,
865
                  nsIMutationObserver)
866
867
PresShell::~PresShell()
868
0
{
869
0
  MOZ_LOG(gLog, LogLevel::Debug, ("PresShell::~PresShell this=%p", this));
870
0
871
0
  if (!mHaveShutDown) {
872
0
    MOZ_ASSERT_UNREACHABLE("Someone did not call nsIPresShell::destroy");
873
0
    Destroy();
874
0
  }
875
0
876
0
  NS_ASSERTION(mCurrentEventContentStack.Count() == 0,
877
0
               "Huh, event content left on the stack in pres shell dtor!");
878
0
  NS_ASSERTION(mFirstCallbackEventRequest == nullptr &&
879
0
               mLastCallbackEventRequest == nullptr,
880
0
               "post-reflow queues not empty.  This means we're leaking");
881
0
882
0
  // Verify that if painting was frozen, but we're being removed from the tree,
883
0
  // that we now re-enable painting on our refresh driver, since it may need to
884
0
  // be re-used by another presentation.
885
0
  if (mPaintingIsFrozen) {
886
0
    mPresContext->RefreshDriver()->Thaw();
887
0
  }
888
0
889
0
  MOZ_ASSERT(mAllocatedPointers.IsEmpty(), "Some pres arena objects were not freed");
890
0
891
0
  mStyleSet = nullptr;
892
0
  delete mFrameConstructor;
893
0
894
0
  mCurrentEventContent = nullptr;
895
0
}
896
897
/**
898
 * Initialize the presentation shell. Create view manager and style
899
 * manager.
900
 * Note this can't be merged into our constructor because caret initialization
901
 * calls AddRef() on us.
902
 */
903
void
904
PresShell::Init(nsIDocument* aDocument,
905
                nsPresContext* aPresContext,
906
                nsViewManager* aViewManager,
907
                UniquePtr<ServoStyleSet> aStyleSet)
908
0
{
909
0
  MOZ_ASSERT(aDocument, "null ptr");
910
0
  MOZ_ASSERT(aPresContext, "null ptr");
911
0
  MOZ_ASSERT(aViewManager, "null ptr");
912
0
  MOZ_ASSERT(!mDocument, "already initialized");
913
0
914
0
  if (!aDocument || !aPresContext || !aViewManager || mDocument) {
915
0
    return;
916
0
  }
917
0
918
0
  mDocument = aDocument;
919
0
  mViewManager = aViewManager;
920
0
921
0
  // mDocument is now set.  It might have a display document whose "need layout/
922
0
  // style" flush flags are not set, but ours will be set.  To keep these
923
0
  // consistent, call the flag setting functions to propagate those flags up
924
0
  // to the display document.
925
0
  SetNeedLayoutFlush();
926
0
  SetNeedStyleFlush();
927
0
928
0
  // Create our frame constructor.
929
0
  mFrameConstructor = new nsCSSFrameConstructor(mDocument, this);
930
0
931
0
  mFrameManager = mFrameConstructor;
932
0
933
0
  // The document viewer owns both view manager and pres shell.
934
0
  mViewManager->SetPresShell(this);
935
0
936
0
  // Bind the context to the presentation shell.
937
0
  mPresContext = aPresContext;
938
0
  mPresContext->AttachShell(this);
939
0
940
0
  // Now we can initialize the style set. Make sure to set the member before
941
0
  // calling Init, since various subroutines need to find the style set off
942
0
  // the PresContext during initialization.
943
0
  mStyleSet = std::move(aStyleSet);
944
0
  mStyleSet->Init(aPresContext);
945
0
946
0
  // Notify our prescontext that it now has a compatibility mode.  Note that
947
0
  // this MUST happen after we set up our style set but before we create any
948
0
  // frames.
949
0
  mPresContext->CompatibilityModeChanged();
950
0
951
0
  // Add the preference style sheet.
952
0
  UpdatePreferenceStyles();
953
0
954
0
  bool accessibleCaretEnabled = AccessibleCaretEnabled(mDocument->GetDocShell());
955
0
  if (accessibleCaretEnabled) {
956
0
    // Need to happen before nsFrameSelection has been set up.
957
0
    mAccessibleCaretEventHub = new AccessibleCaretEventHub(this);
958
0
  }
959
0
960
0
  mSelection = new nsFrameSelection();
961
0
962
0
  RefPtr<nsFrameSelection> frameSelection = mSelection;
963
0
  frameSelection->Init(this, nullptr, accessibleCaretEnabled);
964
0
965
0
  // Important: this has to happen after the selection has been set up
966
0
#ifdef SHOW_CARET
967
0
  // make the caret
968
0
  mCaret = new nsCaret();
969
0
  mCaret->Init(this);
970
0
  mOriginalCaret = mCaret;
971
0
972
0
  //SetCaretEnabled(true);       // make it show in browser windows
973
0
#endif
974
0
  //set up selection to be displayed in document
975
0
  // Don't enable selection for print media
976
0
  nsPresContext::nsPresContextType type = aPresContext->Type();
977
0
  if (type != nsPresContext::eContext_PrintPreview &&
978
0
      type != nsPresContext::eContext_Print)
979
0
    SetDisplaySelection(nsISelectionController::SELECTION_DISABLED);
980
0
981
0
  if (gMaxRCProcessingTime == -1) {
982
0
    gMaxRCProcessingTime =
983
0
      Preferences::GetInt("layout.reflow.timeslice", NS_MAX_REFLOW_TIME);
984
0
  }
985
0
986
0
  if (nsStyleSheetService* ss = nsStyleSheetService::GetInstance()) {
987
0
    ss->RegisterPresShell(this);
988
0
  }
989
0
990
0
  {
991
0
    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
992
0
    if (os) {
993
0
#ifdef MOZ_XUL
994
0
      os->AddObserver(this, "chrome-flush-skin-caches", false);
995
0
#endif
996
0
      os->AddObserver(this, "memory-pressure", false);
997
0
      os->AddObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC, false);
998
0
      if (XRE_IsParentProcess() && !sProcessInteractable) {
999
0
        os->AddObserver(this, "sessionstore-one-or-no-tab-restored", false);
1000
0
      }
1001
0
      os->AddObserver(this, "font-info-updated", false);
1002
0
    }
1003
0
  }
1004
0
1005
#ifdef MOZ_REFLOW_PERF
1006
    if (mReflowCountMgr) {
1007
      bool paintFrameCounts =
1008
        Preferences::GetBool("layout.reflow.showframecounts");
1009
1010
      bool dumpFrameCounts =
1011
        Preferences::GetBool("layout.reflow.dumpframecounts");
1012
1013
      bool dumpFrameByFrameCounts =
1014
        Preferences::GetBool("layout.reflow.dumpframebyframecounts");
1015
1016
      mReflowCountMgr->SetDumpFrameCounts(dumpFrameCounts);
1017
      mReflowCountMgr->SetDumpFrameByFrameCounts(dumpFrameByFrameCounts);
1018
      mReflowCountMgr->SetPaintFrameCounts(paintFrameCounts);
1019
    }
1020
#endif
1021
1022
0
  if (mDocument->HasAnimationController()) {
1023
0
    nsSMILAnimationController* animCtrl = mDocument->GetAnimationController();
1024
0
    animCtrl->NotifyRefreshDriverCreated(GetPresContext()->RefreshDriver());
1025
0
  }
1026
0
1027
0
  for (DocumentTimeline* timeline : mDocument->Timelines()) {
1028
0
    timeline->NotifyRefreshDriverCreated(GetPresContext()->RefreshDriver());
1029
0
  }
1030
0
1031
0
  // Get our activeness from the docShell.
1032
0
  QueryIsActive();
1033
0
1034
0
  // Setup our font inflation preferences.
1035
0
  mFontSizeInflationEmPerLine = nsLayoutUtils::FontSizeInflationEmPerLine();
1036
0
  mFontSizeInflationMinTwips = nsLayoutUtils::FontSizeInflationMinTwips();
1037
0
  mFontSizeInflationLineThreshold = nsLayoutUtils::FontSizeInflationLineThreshold();
1038
0
  mFontSizeInflationForceEnabled = nsLayoutUtils::FontSizeInflationForceEnabled();
1039
0
  mFontSizeInflationDisabledInMasterProcess = nsLayoutUtils::FontSizeInflationDisabledInMasterProcess();
1040
0
  // We'll compute the font size inflation state in Initialize(), when we know
1041
0
  // the document type.
1042
0
1043
0
  mTouchManager.Init(this, mDocument);
1044
0
1045
0
  if (mPresContext->IsRootContentDocument()) {
1046
0
    mZoomConstraintsClient = new ZoomConstraintsClient();
1047
0
    mZoomConstraintsClient->Init(this, mDocument);
1048
0
    if (gfxPrefs::MetaViewportEnabled() || gfxPrefs::APZAllowZooming()) {
1049
0
      mMobileViewportManager = new MobileViewportManager(this, mDocument);
1050
0
    }
1051
0
  }
1052
0
}
1053
1054
enum TextPerfLogType {
1055
  eLog_reflow,
1056
  eLog_loaddone,
1057
  eLog_totals
1058
};
1059
1060
static void
1061
LogTextPerfStats(gfxTextPerfMetrics* aTextPerf,
1062
                 PresShell* aPresShell,
1063
                 const gfxTextPerfMetrics::TextCounts& aCounts,
1064
                 float aTime, TextPerfLogType aLogType, const char* aURL)
1065
0
{
1066
0
  LogModule* tpLog = gfxPlatform::GetLog(eGfxLog_textperf);
1067
0
1068
0
  // ignore XUL contexts unless at debug level
1069
0
  mozilla::LogLevel logLevel = LogLevel::Warning;
1070
0
  if (aCounts.numContentTextRuns == 0) {
1071
0
    logLevel = LogLevel::Debug;
1072
0
  }
1073
0
1074
0
  if (!MOZ_LOG_TEST(tpLog, logLevel)) {
1075
0
    return;
1076
0
  }
1077
0
1078
0
  char prefix[256];
1079
0
1080
0
  switch (aLogType) {
1081
0
    case eLog_reflow:
1082
0
      SprintfLiteral(prefix, "(textperf-reflow) %p time-ms: %7.0f", aPresShell, aTime);
1083
0
      break;
1084
0
    case eLog_loaddone:
1085
0
      SprintfLiteral(prefix, "(textperf-loaddone) %p time-ms: %7.0f", aPresShell, aTime);
1086
0
      break;
1087
0
    default:
1088
0
      MOZ_ASSERT(aLogType == eLog_totals, "unknown textperf log type");
1089
0
      SprintfLiteral(prefix, "(textperf-totals) %p", aPresShell);
1090
0
  }
1091
0
1092
0
  double hitRatio = 0.0;
1093
0
  uint32_t lookups = aCounts.wordCacheHit + aCounts.wordCacheMiss;
1094
0
  if (lookups) {
1095
0
    hitRatio = double(aCounts.wordCacheHit) / double(lookups);
1096
0
  }
1097
0
1098
0
  if (aLogType == eLog_loaddone) {
1099
0
    MOZ_LOG(tpLog, logLevel,
1100
0
           ("%s reflow: %d chars: %d "
1101
0
            "[%s] "
1102
0
            "content-textruns: %d chrome-textruns: %d "
1103
0
            "max-textrun-len: %d "
1104
0
            "word-cache-lookups: %d word-cache-hit-ratio: %4.3f "
1105
0
            "word-cache-space: %d word-cache-long: %d "
1106
0
            "pref-fallbacks: %d system-fallbacks: %d "
1107
0
            "textruns-const: %d textruns-destr: %d "
1108
0
            "generic-lookups: %d "
1109
0
            "cumulative-textruns-destr: %d\n",
1110
0
            prefix, aTextPerf->reflowCount, aCounts.numChars,
1111
0
            (aURL ? aURL : ""),
1112
0
            aCounts.numContentTextRuns, aCounts.numChromeTextRuns,
1113
0
            aCounts.maxTextRunLen,
1114
0
            lookups, hitRatio,
1115
0
            aCounts.wordCacheSpaceRules, aCounts.wordCacheLong,
1116
0
            aCounts.fallbackPrefs, aCounts.fallbackSystem,
1117
0
            aCounts.textrunConst, aCounts.textrunDestr,
1118
0
            aCounts.genericLookups,
1119
0
            aTextPerf->cumulative.textrunDestr));
1120
0
  } else {
1121
0
    MOZ_LOG(tpLog, logLevel,
1122
0
           ("%s reflow: %d chars: %d "
1123
0
            "content-textruns: %d chrome-textruns: %d "
1124
0
            "max-textrun-len: %d "
1125
0
            "word-cache-lookups: %d word-cache-hit-ratio: %4.3f "
1126
0
            "word-cache-space: %d word-cache-long: %d "
1127
0
            "pref-fallbacks: %d system-fallbacks: %d "
1128
0
            "textruns-const: %d textruns-destr: %d "
1129
0
            "generic-lookups: %d "
1130
0
            "cumulative-textruns-destr: %d\n",
1131
0
            prefix, aTextPerf->reflowCount, aCounts.numChars,
1132
0
            aCounts.numContentTextRuns, aCounts.numChromeTextRuns,
1133
0
            aCounts.maxTextRunLen,
1134
0
            lookups, hitRatio,
1135
0
            aCounts.wordCacheSpaceRules, aCounts.wordCacheLong,
1136
0
            aCounts.fallbackPrefs, aCounts.fallbackSystem,
1137
0
            aCounts.textrunConst, aCounts.textrunDestr,
1138
0
            aCounts.genericLookups,
1139
0
            aTextPerf->cumulative.textrunDestr));
1140
0
  }
1141
0
}
1142
1143
void
1144
PresShell::Destroy()
1145
0
{
1146
0
  // Do not add code before this line please!
1147
0
  if (mHaveShutDown) {
1148
0
    return;
1149
0
  }
1150
0
1151
0
  NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
1152
0
    "destroy called on presshell while scripts not blocked");
1153
0
1154
0
  // dump out cumulative text perf metrics
1155
0
  gfxTextPerfMetrics* tp;
1156
0
  if (mPresContext && (tp = mPresContext->GetTextPerfMetrics())) {
1157
0
    tp->Accumulate();
1158
0
    if (tp->cumulative.numChars > 0) {
1159
0
      LogTextPerfStats(tp, this, tp->cumulative, 0.0, eLog_totals, nullptr);
1160
0
    }
1161
0
  }
1162
0
  if (mPresContext) {
1163
0
    const bool mayFlushUserFontSet = false;
1164
0
    gfxUserFontSet* fs = mPresContext->GetUserFontSet(mayFlushUserFontSet);
1165
0
    if (fs) {
1166
0
      uint32_t fontCount;
1167
0
      uint64_t fontSize;
1168
0
      fs->GetLoadStatistics(fontCount, fontSize);
1169
0
      Telemetry::Accumulate(Telemetry::WEBFONT_PER_PAGE, fontCount);
1170
0
      Telemetry::Accumulate(Telemetry::WEBFONT_SIZE_PER_PAGE,
1171
0
                            uint32_t(fontSize/1024));
1172
0
    } else {
1173
0
      Telemetry::Accumulate(Telemetry::WEBFONT_PER_PAGE, 0);
1174
0
      Telemetry::Accumulate(Telemetry::WEBFONT_SIZE_PER_PAGE, 0);
1175
0
    }
1176
0
  }
1177
0
1178
#ifdef MOZ_REFLOW_PERF
1179
  DumpReflows();
1180
  if (mReflowCountMgr) {
1181
    delete mReflowCountMgr;
1182
    mReflowCountMgr = nullptr;
1183
  }
1184
#endif
1185
1186
0
  if (mZoomConstraintsClient) {
1187
0
    mZoomConstraintsClient->Destroy();
1188
0
    mZoomConstraintsClient = nullptr;
1189
0
  }
1190
0
  if (mMobileViewportManager) {
1191
0
    mMobileViewportManager->Destroy();
1192
0
    mMobileViewportManager = nullptr;
1193
0
  }
1194
0
1195
0
#ifdef ACCESSIBILITY
1196
0
  if (mDocAccessible) {
1197
#ifdef DEBUG
1198
    if (a11y::logging::IsEnabled(a11y::logging::eDocDestroy))
1199
      a11y::logging::DocDestroy("presshell destroyed", mDocument);
1200
#endif
1201
1202
0
    mDocAccessible->Shutdown();
1203
0
    mDocAccessible = nullptr;
1204
0
  }
1205
0
#endif // ACCESSIBILITY
1206
0
1207
0
  MaybeReleaseCapturingContent();
1208
0
1209
0
  if (gKeyDownTarget && gKeyDownTarget->OwnerDoc() == mDocument) {
1210
0
    NS_RELEASE(gKeyDownTarget);
1211
0
  }
1212
0
1213
0
  if (mContentToScrollTo) {
1214
0
    mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
1215
0
    mContentToScrollTo = nullptr;
1216
0
  }
1217
0
1218
0
  if (mPresContext) {
1219
0
    // We need to notify the destroying the nsPresContext to ESM for
1220
0
    // suppressing to use from ESM.
1221
0
    mPresContext->EventStateManager()->NotifyDestroyPresContext(mPresContext);
1222
0
  }
1223
0
1224
0
  if (nsStyleSheetService* ss = nsStyleSheetService::GetInstance()) {
1225
0
    ss->UnregisterPresShell(this);
1226
0
  }
1227
0
1228
0
  {
1229
0
    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1230
0
    if (os) {
1231
0
#ifdef MOZ_XUL
1232
0
      os->RemoveObserver(this, "chrome-flush-skin-caches");
1233
0
#endif
1234
0
      os->RemoveObserver(this, "memory-pressure");
1235
0
      os->RemoveObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC);
1236
0
      if (XRE_IsParentProcess()) {
1237
0
        os->RemoveObserver(this, "sessionstore-one-or-no-tab-restored");
1238
0
      }
1239
0
      os->RemoveObserver(this, "font-info-updated");
1240
0
    }
1241
0
  }
1242
0
1243
0
  // If our paint suppression timer is still active, kill it.
1244
0
  if (mPaintSuppressionTimer) {
1245
0
    mPaintSuppressionTimer->Cancel();
1246
0
    mPaintSuppressionTimer = nullptr;
1247
0
  }
1248
0
1249
0
  // Same for our reflow continuation timer
1250
0
  if (mReflowContinueTimer) {
1251
0
    mReflowContinueTimer->Cancel();
1252
0
    mReflowContinueTimer = nullptr;
1253
0
  }
1254
0
1255
0
  if (mDelayedPaintTimer) {
1256
0
    mDelayedPaintTimer->Cancel();
1257
0
    mDelayedPaintTimer = nullptr;
1258
0
  }
1259
0
1260
0
  mSynthMouseMoveEvent.Revoke();
1261
0
1262
0
  mUpdateApproximateFrameVisibilityEvent.Revoke();
1263
0
1264
0
  ClearApproximatelyVisibleFramesList(Some(OnNonvisible::DISCARD_IMAGES));
1265
0
1266
0
  if (mCaret) {
1267
0
    mCaret->Terminate();
1268
0
    mCaret = nullptr;
1269
0
  }
1270
0
1271
0
  if (mSelection) {
1272
0
    RefPtr<nsFrameSelection> frameSelection = mSelection;
1273
0
    frameSelection->DisconnectFromPresShell();
1274
0
  }
1275
0
1276
0
  if (mAccessibleCaretEventHub) {
1277
0
    mAccessibleCaretEventHub->Terminate();
1278
0
    mAccessibleCaretEventHub = nullptr;
1279
0
  }
1280
0
1281
0
  // release our pref style sheet, if we have one still
1282
0
  //
1283
0
  // FIXME(emilio): Why do we need to do this? The stylist is getting nixed with
1284
0
  // us anyway.
1285
0
  RemovePreferenceStyles();
1286
0
1287
0
  mIsDestroying = true;
1288
0
1289
0
  // We can't release all the event content in
1290
0
  // mCurrentEventContentStack here since there might be code on the
1291
0
  // stack that will release the event content too. Double release
1292
0
  // bad!
1293
0
1294
0
  // The frames will be torn down, so remove them from the current
1295
0
  // event frame stack (since they'd be dangling references if we'd
1296
0
  // leave them in) and null out the mCurrentEventFrame pointer as
1297
0
  // well.
1298
0
1299
0
  mCurrentEventFrame = nullptr;
1300
0
1301
0
  int32_t i, count = mCurrentEventFrameStack.Length();
1302
0
  for (i = 0; i < count; i++) {
1303
0
    mCurrentEventFrameStack[i] = nullptr;
1304
0
  }
1305
0
1306
0
  mFramesToDirty.Clear();
1307
0
1308
0
  if (mViewManager) {
1309
0
    // Clear the view manager's weak pointer back to |this| in case it
1310
0
    // was leaked.
1311
0
    mViewManager->SetPresShell(nullptr);
1312
0
    mViewManager = nullptr;
1313
0
  }
1314
0
1315
0
  // mFrameArena will be destroyed soon.  Clear out any ArenaRefPtrs
1316
0
  // pointing to objects in the arena now.  This is done:
1317
0
  //
1318
0
  //   (a) before mFrameArena's destructor runs so that our
1319
0
  //       mAllocatedPointers becomes empty and doesn't trip the assertion
1320
0
  //       in ~PresShell,
1321
0
  //   (b) before the mPresContext->DetachShell() below, so
1322
0
  //       that when we clear the ArenaRefPtrs they'll still be able to
1323
0
  //       get back to this PresShell to deregister themselves (e.g. note
1324
0
  //       how ComputedStyle::Arena returns the PresShell got from its
1325
0
  //       rule node's nsPresContext, which would return null if we'd already
1326
0
  //       called mPresContext->DetachShell()), and
1327
0
  //   (c) before the mStyleSet->BeginShutdown() call just below, so that
1328
0
  //       the ComputedStyles don't complain they're being destroyed later
1329
0
  //       than the rule tree is.
1330
0
  mFrameArena.ClearArenaRefPtrs();
1331
0
1332
0
  mStyleSet->BeginShutdown();
1333
0
  nsRefreshDriver* rd = GetPresContext()->RefreshDriver();
1334
0
1335
0
  // This shell must be removed from the document before the frame
1336
0
  // hierarchy is torn down to avoid finding deleted frames through
1337
0
  // this presshell while the frames are being torn down
1338
0
  if (mDocument) {
1339
0
    NS_ASSERTION(mDocument->GetShell() == this, "Wrong shell?");
1340
0
    mDocument->ClearServoRestyleRoot();
1341
0
    mDocument->DeleteShell();
1342
0
1343
0
    if (mDocument->HasAnimationController()) {
1344
0
      mDocument->GetAnimationController()->NotifyRefreshDriverDestroying(rd);
1345
0
    }
1346
0
    for (DocumentTimeline* timeline : mDocument->Timelines()) {
1347
0
      timeline->NotifyRefreshDriverDestroying(rd);
1348
0
    }
1349
0
  }
1350
0
1351
0
  if (mPresContext) {
1352
0
    rd->CancelPendingAnimationEvents(mPresContext->AnimationEventDispatcher());
1353
0
  }
1354
0
1355
0
  // Revoke any pending events.  We need to do this and cancel pending reflows
1356
0
  // before we destroy the frame manager, since apparently frame destruction
1357
0
  // sometimes spins the event queue when plug-ins are involved(!).
1358
0
  StopObservingRefreshDriver();
1359
0
1360
0
  if (rd->GetPresContext() == GetPresContext()) {
1361
0
    rd->RevokeViewManagerFlush();
1362
0
  }
1363
0
1364
0
  CancelAllPendingReflows();
1365
0
  CancelPostedReflowCallbacks();
1366
0
1367
0
  // Destroy the frame manager. This will destroy the frame hierarchy
1368
0
  mFrameConstructor->WillDestroyFrameTree();
1369
0
1370
0
  NS_WARNING_ASSERTION(!mAutoWeakFrames && mWeakFrames.IsEmpty(),
1371
0
                       "Weak frames alive after destroying FrameManager");
1372
0
  while (mAutoWeakFrames) {
1373
0
    mAutoWeakFrames->Clear(this);
1374
0
  }
1375
0
  nsTArray<WeakFrame*> toRemove(mWeakFrames.Count());
1376
0
  for (auto iter = mWeakFrames.Iter(); !iter.Done(); iter.Next()) {
1377
0
    toRemove.AppendElement(iter.Get()->GetKey());
1378
0
  }
1379
0
  for (WeakFrame* weakFrame : toRemove) {
1380
0
    weakFrame->Clear(this);
1381
0
  }
1382
0
1383
0
  // Let the style set do its cleanup.
1384
0
  mStyleSet->Shutdown();
1385
0
1386
0
  if (mPresContext) {
1387
0
    // We hold a reference to the pres context, and it holds a weak link back
1388
0
    // to us. To avoid the pres context having a dangling reference, set its
1389
0
    // pres shell to nullptr
1390
0
    mPresContext->DetachShell();
1391
0
1392
0
    // Clear the link handler (weak reference) as well
1393
0
    mPresContext->SetLinkHandler(nullptr);
1394
0
  }
1395
0
1396
0
  mHaveShutDown = true;
1397
0
1398
0
  mTouchManager.Destroy();
1399
0
}
1400
1401
void
1402
nsIPresShell::StopObservingRefreshDriver()
1403
0
{
1404
0
  nsRefreshDriver* rd = mPresContext->RefreshDriver();
1405
0
  if (mResizeEventPending) {
1406
0
    rd->RemoveResizeEventFlushObserver(this);
1407
0
  }
1408
0
  if (mObservingLayoutFlushes) {
1409
0
    rd->RemoveLayoutFlushObserver(this);
1410
0
  }
1411
0
  if (mObservingStyleFlushes) {
1412
0
    rd->RemoveStyleFlushObserver(this);
1413
0
  }
1414
0
}
1415
1416
void
1417
nsIPresShell::StartObservingRefreshDriver()
1418
0
{
1419
0
  nsRefreshDriver* rd = mPresContext->RefreshDriver();
1420
0
  if (mResizeEventPending) {
1421
0
    rd->AddResizeEventFlushObserver(this);
1422
0
  }
1423
0
  if (mObservingLayoutFlushes) {
1424
0
    rd->AddLayoutFlushObserver(this);
1425
0
  }
1426
0
  if (mObservingStyleFlushes) {
1427
0
    rd->AddStyleFlushObserver(this);
1428
0
  }
1429
0
}
1430
1431
nsRefreshDriver*
1432
nsIPresShell::GetRefreshDriver() const
1433
0
{
1434
0
  return mPresContext ? mPresContext->RefreshDriver() : nullptr;
1435
0
}
1436
1437
void
1438
nsIPresShell::SetAuthorStyleDisabled(bool aStyleDisabled)
1439
0
{
1440
0
  if (aStyleDisabled != mStyleSet->GetAuthorStyleDisabled()) {
1441
0
    mStyleSet->SetAuthorStyleDisabled(aStyleDisabled);
1442
0
    ApplicableStylesChanged();
1443
0
1444
0
    nsCOMPtr<nsIObserverService> observerService =
1445
0
      mozilla::services::GetObserverService();
1446
0
    if (observerService) {
1447
0
      observerService->NotifyObservers(mDocument,
1448
0
                                       "author-style-disabled-changed",
1449
0
                                       nullptr);
1450
0
    }
1451
0
  }
1452
0
}
1453
1454
bool
1455
nsIPresShell::GetAuthorStyleDisabled() const
1456
0
{
1457
0
  return mStyleSet->GetAuthorStyleDisabled();
1458
0
}
1459
1460
void
1461
PresShell::UpdatePreferenceStyles()
1462
0
{
1463
0
  if (!mDocument) {
1464
0
    return;
1465
0
  }
1466
0
1467
0
  // If the document doesn't have a window there's no need to notify
1468
0
  // its presshell about changes to preferences since the document is
1469
0
  // in a state where it doesn't matter any more (see
1470
0
  // nsDocumentViewer::Close()).
1471
0
  if (!mDocument->GetWindow()) {
1472
0
    return;
1473
0
  }
1474
0
1475
0
  // Documents in chrome shells do not have any preference style rules applied.
1476
0
  if (nsContentUtils::IsInChromeDocshell(mDocument)) {
1477
0
    return;
1478
0
  }
1479
0
1480
0
  // We need to pass in mPresContext so that if the nsLayoutStylesheetCache
1481
0
  // needs to recreate the pref style sheet, it has somewhere to get the
1482
0
  // pref styling information from.  All pres contexts for
1483
0
  // IsChromeOriginImage() == false will have the same pref styling information,
1484
0
  // and similarly for IsChromeOriginImage() == true, so it doesn't really
1485
0
  // matter which pres context we pass in when it does need to be recreated.
1486
0
  // (See nsPresContext::GetDocumentColorPreferences for how whether we
1487
0
  // are a chrome origin image affects some pref styling information.)
1488
0
  auto cache = nsLayoutStylesheetCache::Singleton();
1489
0
  RefPtr<StyleSheet> newPrefSheet =
1490
0
    mPresContext->IsChromeOriginImage() ?
1491
0
      cache->ChromePreferenceSheet(mPresContext) :
1492
0
      cache->ContentPreferenceSheet(mPresContext);
1493
0
1494
0
  if (mPrefStyleSheet == newPrefSheet) {
1495
0
    return;
1496
0
  }
1497
0
1498
0
  RemovePreferenceStyles();
1499
0
1500
0
  // NOTE(emilio): This sheet is added as an agent sheet, because we don't want
1501
0
  // it to be modifiable from devtools and similar, see bugs 1239336 and
1502
0
  // 1436782. I think it conceptually should be a user sheet, and could be
1503
0
  // without too much trouble I'd think.
1504
0
  mStyleSet->AppendStyleSheet(SheetType::Agent, newPrefSheet);
1505
0
  mPrefStyleSheet = newPrefSheet;
1506
0
}
1507
1508
void
1509
PresShell::RemovePreferenceStyles()
1510
0
{
1511
0
  if (mPrefStyleSheet) {
1512
0
    mStyleSet->RemoveStyleSheet(SheetType::Agent, mPrefStyleSheet);
1513
0
    mPrefStyleSheet = nullptr;
1514
0
  }
1515
0
}
1516
1517
void
1518
PresShell::AddUserSheet(StyleSheet* aSheet)
1519
0
{
1520
0
  // Make sure this does what nsDocumentViewer::CreateStyleSet does wrt
1521
0
  // ordering. We want this new sheet to come after all the existing stylesheet
1522
0
  // service sheets, but before other user sheets; see nsIStyleSheetService.idl
1523
0
  // for the ordering.  Just remove and readd all the nsStyleSheetService
1524
0
  // sheets.
1525
0
  nsCOMPtr<nsIStyleSheetService> dummy =
1526
0
    do_GetService(NS_STYLESHEETSERVICE_CONTRACTID);
1527
0
1528
0
  nsStyleSheetService* sheetService = nsStyleSheetService::gInstance;
1529
0
  nsTArray<RefPtr<StyleSheet>>& userSheets = *sheetService->UserStyleSheets();
1530
0
  // Iterate forwards when removing so the searches for RemoveStyleSheet are as
1531
0
  // short as possible.
1532
0
  for (StyleSheet* sheet : userSheets) {
1533
0
    mStyleSet->RemoveStyleSheet(SheetType::User, sheet);
1534
0
  }
1535
0
1536
0
  // Now iterate backwards, so that the order of userSheets will be the same as
1537
0
  // the order of sheets from it in the style set.
1538
0
  for (StyleSheet* sheet : Reversed(userSheets)) {
1539
0
    mStyleSet->PrependStyleSheet(SheetType::User, sheet);
1540
0
  }
1541
0
1542
0
  ApplicableStylesChanged();
1543
0
}
1544
1545
void
1546
PresShell::AddAgentSheet(StyleSheet* aSheet)
1547
0
{
1548
0
  // Make sure this does what nsDocumentViewer::CreateStyleSet does
1549
0
  // wrt ordering.
1550
0
  mStyleSet->AppendStyleSheet(SheetType::Agent, aSheet);
1551
0
  ApplicableStylesChanged();
1552
0
}
1553
1554
void
1555
PresShell::AddAuthorSheet(StyleSheet* aSheet)
1556
0
{
1557
0
  // Document specific "additional" Author sheets should be stronger than the
1558
0
  // ones added with the StyleSheetService.
1559
0
  StyleSheet* firstAuthorSheet =
1560
0
    mDocument->GetFirstAdditionalAuthorSheet();
1561
0
  if (firstAuthorSheet) {
1562
0
    mStyleSet->InsertStyleSheetBefore(SheetType::Doc, aSheet,
1563
0
                                      firstAuthorSheet);
1564
0
  } else {
1565
0
    mStyleSet->AppendStyleSheet(SheetType::Doc, aSheet);
1566
0
  }
1567
0
1568
0
  ApplicableStylesChanged();
1569
0
}
1570
1571
void
1572
PresShell::RemoveSheet(SheetType aType, StyleSheet* aSheet)
1573
0
{
1574
0
  mStyleSet->RemoveStyleSheet(aType, aSheet);
1575
0
  ApplicableStylesChanged();
1576
0
}
1577
1578
NS_IMETHODIMP
1579
PresShell::SetDisplaySelection(int16_t aToggle)
1580
0
{
1581
0
  RefPtr<nsFrameSelection> frameSelection = mSelection;
1582
0
  frameSelection->SetDisplaySelection(aToggle);
1583
0
  return NS_OK;
1584
0
}
1585
1586
NS_IMETHODIMP
1587
PresShell::GetDisplaySelection(int16_t *aToggle)
1588
0
{
1589
0
  RefPtr<nsFrameSelection> frameSelection = mSelection;
1590
0
  *aToggle = frameSelection->GetDisplaySelection();
1591
0
  return NS_OK;
1592
0
}
1593
1594
NS_IMETHODIMP
1595
PresShell::GetSelectionFromScript(RawSelectionType aRawSelectionType,
1596
                                  Selection **aSelection)
1597
0
{
1598
0
  if (!aSelection || !mSelection)
1599
0
    return NS_ERROR_NULL_POINTER;
1600
0
1601
0
  RefPtr<nsFrameSelection> frameSelection = mSelection;
1602
0
  RefPtr<Selection> selection =
1603
0
    frameSelection->GetSelection(ToSelectionType(aRawSelectionType));
1604
0
1605
0
  if (!selection) {
1606
0
    return NS_ERROR_INVALID_ARG;
1607
0
  }
1608
0
1609
0
  selection.forget(aSelection);
1610
0
  return NS_OK;
1611
0
}
1612
1613
Selection*
1614
PresShell::GetSelection(RawSelectionType aRawSelectionType)
1615
0
{
1616
0
  if (!mSelection) {
1617
0
    return nullptr;
1618
0
  }
1619
0
1620
0
  RefPtr<nsFrameSelection> frameSelection = mSelection;
1621
0
  return frameSelection->GetSelection(ToSelectionType(aRawSelectionType));
1622
0
}
1623
1624
Selection*
1625
PresShell::GetCurrentSelection(SelectionType aSelectionType)
1626
0
{
1627
0
  if (!mSelection)
1628
0
    return nullptr;
1629
0
1630
0
  RefPtr<nsFrameSelection> frameSelection = mSelection;
1631
0
  return frameSelection->GetSelection(aSelectionType);
1632
0
}
1633
1634
already_AddRefed<nsISelectionController>
1635
PresShell::GetSelectionControllerForFocusedContent(nsIContent** aFocusedContent)
1636
0
{
1637
0
  if (aFocusedContent) {
1638
0
    *aFocusedContent = nullptr;
1639
0
  }
1640
0
1641
0
  if (mDocument) {
1642
0
    nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
1643
0
    nsCOMPtr<nsIContent> focusedContent =
1644
0
      nsFocusManager::GetFocusedDescendant(mDocument->GetWindow(),
1645
0
                                           nsFocusManager::eOnlyCurrentWindow,
1646
0
                                           getter_AddRefs(focusedWindow));
1647
0
    if (focusedContent) {
1648
0
      nsIFrame* frame = focusedContent->GetPrimaryFrame();
1649
0
      if (frame) {
1650
0
        nsCOMPtr<nsISelectionController> selectionController;
1651
0
        frame->GetSelectionController(mPresContext,
1652
0
                                      getter_AddRefs(selectionController));
1653
0
        if (selectionController) {
1654
0
          if (aFocusedContent) {
1655
0
            focusedContent.forget(aFocusedContent);
1656
0
          }
1657
0
          return selectionController.forget();
1658
0
        }
1659
0
      }
1660
0
    }
1661
0
  }
1662
0
  nsCOMPtr<nsISelectionController> self(this);
1663
0
  return self.forget();
1664
0
}
1665
1666
NS_IMETHODIMP
1667
PresShell::ScrollSelectionIntoView(RawSelectionType aRawSelectionType,
1668
                                   SelectionRegion aRegion,
1669
                                   int16_t aFlags)
1670
0
{
1671
0
  if (!mSelection)
1672
0
    return NS_ERROR_NULL_POINTER;
1673
0
1674
0
  RefPtr<nsFrameSelection> frameSelection = mSelection;
1675
0
  return frameSelection->ScrollSelectionIntoView(
1676
0
                           ToSelectionType(aRawSelectionType), aRegion, aFlags);
1677
0
}
1678
1679
NS_IMETHODIMP
1680
PresShell::RepaintSelection(RawSelectionType aRawSelectionType)
1681
0
{
1682
0
  if (!mSelection) {
1683
0
    return NS_ERROR_NULL_POINTER;
1684
0
  }
1685
0
1686
0
  if (MOZ_UNLIKELY(mIsDestroying)) {
1687
0
    return NS_OK;
1688
0
  }
1689
0
1690
0
  RefPtr<nsFrameSelection> frameSelection = mSelection;
1691
0
  return frameSelection->RepaintSelection(ToSelectionType(aRawSelectionType));
1692
0
}
1693
1694
// Make shell be a document observer
1695
void
1696
nsIPresShell::BeginObservingDocument()
1697
0
{
1698
0
  if (mDocument && !mIsDestroying) {
1699
0
    mIsObservingDocument = true;
1700
0
    if (mIsDocumentGone) {
1701
0
      NS_WARNING("Adding a presshell that was disconnected from the document "
1702
0
                 "as a document observer?  Sounds wrong...");
1703
0
      mIsDocumentGone = false;
1704
0
    }
1705
0
  }
1706
0
}
1707
1708
// Make shell stop being a document observer
1709
void
1710
nsIPresShell::EndObservingDocument()
1711
0
{
1712
0
  // XXXbz do we need to tell the frame constructor that the document
1713
0
  // is gone, perhaps?  Except for printing it's NOT gone, sometimes.
1714
0
  mIsDocumentGone = true;
1715
0
  mIsObservingDocument = false;
1716
0
}
1717
1718
#ifdef DEBUG_kipp
1719
char* nsPresShell_ReflowStackPointerTop;
1720
#endif
1721
1722
class XBLConstructorRunner : public Runnable
1723
{
1724
public:
1725
  explicit XBLConstructorRunner(nsIDocument* aDocument)
1726
    : Runnable("XBLConstructorRunner")
1727
    , mDocument(aDocument)
1728
0
  {
1729
0
  }
1730
1731
  NS_IMETHOD Run() override
1732
0
  {
1733
0
    mDocument->BindingManager()->ProcessAttachedQueue();
1734
0
    return NS_OK;
1735
0
  }
1736
1737
private:
1738
  nsCOMPtr<nsIDocument> mDocument;
1739
};
1740
1741
nsresult
1742
PresShell::Initialize()
1743
0
{
1744
0
  if (mIsDestroying) {
1745
0
    return NS_OK;
1746
0
  }
1747
0
1748
0
  if (!mDocument) {
1749
0
    // Nothing to do
1750
0
    return NS_OK;
1751
0
  }
1752
0
1753
0
  MOZ_LOG(gLog, LogLevel::Debug, ("PresShell::Initialize this=%p", this));
1754
0
1755
0
  NS_ASSERTION(!mDidInitialize, "Why are we being called?");
1756
0
1757
0
  nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
1758
0
1759
0
  RecomputeFontSizeInflationEnabled();
1760
0
  MOZ_DIAGNOSTIC_ASSERT(!mIsDestroying);
1761
0
1762
0
  // Ensure the pres context doesn't think it has changed, since we haven't even
1763
0
  // started layout. This avoids spurious restyles / reflows afterwards.
1764
0
  //
1765
0
  // Note that this is very intentionally before setting mDidInitialize so it
1766
0
  // doesn't notify the document, or run media query change events.
1767
0
  mPresContext->FlushPendingMediaFeatureValuesChanged();
1768
0
  MOZ_DIAGNOSTIC_ASSERT(!mIsDestroying);
1769
0
1770
0
  mDidInitialize = true;
1771
0
1772
#ifdef DEBUG
1773
  if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) {
1774
    if (mDocument) {
1775
      nsIURI *uri = mDocument->GetDocumentURI();
1776
      if (uri) {
1777
        printf("*** PresShell::Initialize (this=%p, url='%s')\n",
1778
               (void*)this, uri->GetSpecOrDefault().get());
1779
      }
1780
    }
1781
  }
1782
#endif
1783
1784
0
  // Get the root frame from the frame manager
1785
0
  // XXXbz it would be nice to move this somewhere else... like frame manager
1786
0
  // Init(), say.  But we need to make sure our views are all set up by the
1787
0
  // time we do this!
1788
0
  nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
1789
0
  NS_ASSERTION(!rootFrame, "How did that happen, exactly?");
1790
0
1791
0
  if (!rootFrame) {
1792
0
    nsAutoScriptBlocker scriptBlocker;
1793
0
    rootFrame = mFrameConstructor->ConstructRootFrame();
1794
0
    mFrameConstructor->SetRootFrame(rootFrame);
1795
0
  }
1796
0
1797
0
  NS_ENSURE_STATE(!mHaveShutDown);
1798
0
1799
0
  if (!rootFrame) {
1800
0
    return NS_ERROR_OUT_OF_MEMORY;
1801
0
  }
1802
0
1803
0
  if (Element* root = mDocument->GetRootElement()) {
1804
0
    {
1805
0
      nsAutoCauseReflowNotifier reflowNotifier(this);
1806
0
      // Have the style sheet processor construct frame for the root
1807
0
      // content object down
1808
0
      mFrameConstructor->ContentInserted(
1809
0
          root, nullptr, nsCSSFrameConstructor::InsertionKind::Sync);
1810
0
1811
0
      // Something in mFrameConstructor->ContentInserted may have caused
1812
0
      // Destroy() to get called, bug 337586.
1813
0
      NS_ENSURE_STATE(!mHaveShutDown);
1814
0
    }
1815
0
1816
0
    // nsAutoCauseReflowNotifier (which sets up a script blocker) going out of
1817
0
    // scope may have killed us too
1818
0
    NS_ENSURE_STATE(!mHaveShutDown);
1819
0
1820
0
    // Run the XBL binding constructors for any new frames we've constructed.
1821
0
    // (Do this in a script runner, since our caller might have a script
1822
0
    // blocker on the stack.)
1823
0
    nsContentUtils::AddScriptRunner(new XBLConstructorRunner(mDocument));
1824
0
1825
0
    // XBLConstructorRunner might destroy us.
1826
0
    NS_ENSURE_STATE(!mHaveShutDown);
1827
0
  }
1828
0
1829
0
  mDocument->TriggerAutoFocus();
1830
0
1831
0
  NS_ASSERTION(rootFrame, "How did that happen?");
1832
0
1833
0
  // Note: when the frame was created above it had the NS_FRAME_IS_DIRTY bit
1834
0
  // set, but XBL processing could have caused a reflow which clears it.
1835
0
  if (MOZ_LIKELY(rootFrame->GetStateBits() & NS_FRAME_IS_DIRTY)) {
1836
0
    // Unset the DIRTY bits so that FrameNeedsReflow() will work right.
1837
0
    rootFrame->RemoveStateBits(NS_FRAME_IS_DIRTY |
1838
0
                               NS_FRAME_HAS_DIRTY_CHILDREN);
1839
0
    NS_ASSERTION(!mDirtyRoots.Contains(rootFrame),
1840
0
                 "Why is the root in mDirtyRoots already?");
1841
0
    FrameNeedsReflow(rootFrame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
1842
0
    NS_ASSERTION(mDirtyRoots.Contains(rootFrame),
1843
0
                 "Should be in mDirtyRoots now");
1844
0
    NS_ASSERTION(mObservingLayoutFlushes, "Why no reflow scheduled?");
1845
0
  }
1846
0
1847
0
  // Restore our root scroll position now if we're getting here after EndLoad
1848
0
  // got called, since this is our one chance to do it.  Note that we need not
1849
0
  // have reflowed for this to work; when the scrollframe is finally reflowed
1850
0
  // it'll pick up the position we store in it here.
1851
0
  if (!mDocumentLoading) {
1852
0
    RestoreRootScrollPosition();
1853
0
  }
1854
0
1855
0
  // For printing, we just immediately unsuppress.
1856
0
  if (!mPresContext->IsPaginated()) {
1857
0
    // Kick off a one-shot timer based off our pref value.  When this timer
1858
0
    // fires, if painting is still locked down, then we will go ahead and
1859
0
    // trigger a full invalidate and allow painting to proceed normally.
1860
0
    mPaintingSuppressed = true;
1861
0
    // Don't suppress painting if the document isn't loading.
1862
0
    nsIDocument::ReadyState readyState = mDocument->GetReadyStateEnum();
1863
0
    if (readyState != nsIDocument::READYSTATE_COMPLETE) {
1864
0
      mPaintSuppressionTimer = NS_NewTimer();
1865
0
    }
1866
0
    if (!mPaintSuppressionTimer) {
1867
0
      mPaintingSuppressed = false;
1868
0
    } else {
1869
0
      // Initialize the timer.
1870
0
1871
0
      // Default to PAINTLOCK_EVENT_DELAY if we can't get the pref value.
1872
0
      int32_t delay =
1873
0
        Preferences::GetInt("nglayout.initialpaint.delay",
1874
0
                            PAINTLOCK_EVENT_DELAY);
1875
0
1876
0
      mPaintSuppressionTimer->SetTarget(
1877
0
          mDocument->EventTargetFor(TaskCategory::Other));
1878
0
      mPaintSuppressionTimer->InitWithNamedFuncCallback(
1879
0
        sPaintSuppressionCallback, this, delay, nsITimer::TYPE_ONE_SHOT,
1880
0
        "PresShell::sPaintSuppressionCallback");
1881
0
    }
1882
0
  }
1883
0
1884
0
  // If we get here and painting is not suppressed, then we can paint anytime
1885
0
  // and we should fire the before-first-paint notification
1886
0
  if (!mPaintingSuppressed) {
1887
0
    ScheduleBeforeFirstPaint();
1888
0
  }
1889
0
1890
0
  return NS_OK; //XXX this needs to be real. MMP
1891
0
}
1892
1893
void
1894
PresShell::sPaintSuppressionCallback(nsITimer *aTimer, void* aPresShell)
1895
0
{
1896
0
  RefPtr<PresShell> self = static_cast<PresShell*>(aPresShell);
1897
0
  if (self)
1898
0
    self->UnsuppressPainting();
1899
0
}
1900
1901
nsresult
1902
PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight, nscoord aOldWidth,
1903
                        nscoord aOldHeight, ResizeReflowOptions aOptions)
1904
0
{
1905
0
  if (mZoomConstraintsClient) {
1906
0
    // If we have a ZoomConstraintsClient and the available screen area
1907
0
    // changed, then we might need to disable double-tap-to-zoom, so notify
1908
0
    // the ZCC to update itself.
1909
0
    mZoomConstraintsClient->ScreenSizeChanged();
1910
0
  }
1911
0
  if (mMobileViewportManager) {
1912
0
    // If we have a mobile viewport manager, request a reflow from it. It can
1913
0
    // recompute the final CSS viewport and trigger a call to
1914
0
    // ResizeReflowIgnoreOverride if it changed.
1915
0
    mMobileViewportManager->RequestReflow();
1916
0
    return NS_OK;
1917
0
  }
1918
0
1919
0
  return ResizeReflowIgnoreOverride(aWidth, aHeight, aOldWidth,
1920
0
                                    aOldHeight, aOptions);
1921
0
}
1922
1923
nsresult
1924
PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight,
1925
                                      nscoord aOldWidth, nscoord aOldHeight,
1926
                                      ResizeReflowOptions aOptions)
1927
0
{
1928
0
  MOZ_ASSERT(!mIsReflowing, "Shouldn't be in reflow here!");
1929
0
1930
0
  nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
1931
0
  if (!rootFrame) {
1932
0
    // If we don't have a root frame yet, that means we haven't had our initial
1933
0
    // reflow... If that's the case, and aWidth or aHeight is unconstrained,
1934
0
    // ignore them altogether.
1935
0
    if (aHeight == NS_UNCONSTRAINEDSIZE || aWidth == NS_UNCONSTRAINEDSIZE) {
1936
0
      // We can't do the work needed for SizeToContent without a root
1937
0
      // frame, and we want to return before setting the visible area.
1938
0
      return NS_ERROR_NOT_AVAILABLE;
1939
0
    }
1940
0
1941
0
    mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
1942
0
    // There isn't anything useful we can do if the initial reflow hasn't
1943
0
    // happened.
1944
0
    return NS_OK;
1945
0
  }
1946
0
1947
0
  WritingMode wm = rootFrame->GetWritingMode();
1948
0
  const bool shrinkToFit = aOptions == ResizeReflowOptions::eBSizeLimit;
1949
0
  MOZ_ASSERT(shrinkToFit ||
1950
0
             (wm.IsVertical() ? aWidth : aHeight) != NS_UNCONSTRAINEDSIZE,
1951
0
             "unconstrained bsize only usable with eBSizeLimit");
1952
0
  MOZ_ASSERT((wm.IsVertical() ? aHeight : aWidth) != NS_UNCONSTRAINEDSIZE,
1953
0
             "unconstrained isize not allowed");
1954
0
  bool isBSizeChanging = wm.IsVertical() ? aOldWidth != aWidth
1955
0
                                         : aOldHeight != aHeight;
1956
0
  nscoord targetWidth = aWidth;
1957
0
  nscoord targetHeight = aHeight;
1958
0
1959
0
  if (shrinkToFit) {
1960
0
    if (wm.IsVertical()) {
1961
0
      targetWidth = NS_UNCONSTRAINEDSIZE;
1962
0
    } else {
1963
0
      targetHeight = NS_UNCONSTRAINEDSIZE;
1964
0
    }
1965
0
    isBSizeChanging = true;
1966
0
  }
1967
0
1968
0
  const bool suppressingResizeReflow =
1969
0
    GetPresContext()->SuppressingResizeReflow();
1970
0
1971
0
  RefPtr<nsViewManager> viewManager = mViewManager;
1972
0
  nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
1973
0
1974
0
  if (!suppressingResizeReflow && shrinkToFit) {
1975
0
    // Make sure that style is flushed before setting the pres context
1976
0
    // VisibleArea if we're shrinking to fit.
1977
0
    //
1978
0
    // Otherwise we may end up with bogus viewport units resolved against the
1979
0
    // unconstrained bsize, or restyling the whole document resolving viewport
1980
0
    // units against targetWidth, which may end up doing wasteful work.
1981
0
    mDocument->FlushPendingNotifications(FlushType::Frames);
1982
0
  }
1983
0
1984
0
  if (!mIsDestroying) {
1985
0
    mPresContext->SetVisibleArea(nsRect(0, 0, targetWidth, targetHeight));
1986
0
  }
1987
0
1988
0
  if (!mIsDestroying && !suppressingResizeReflow) {
1989
0
    if (!shrinkToFit) {
1990
0
      // Flush styles _now_ (with the correct visible area) if not computing the
1991
0
      // shrink-to-fit size.
1992
0
      //
1993
0
      // We've asserted above that sizes are not unconstrained, so this is going
1994
0
      // to be the final size, which means that we'll get the (correct) final
1995
0
      // styles now, and avoid a further potentially-wasteful full recascade on
1996
0
      // the next flush.
1997
0
      mDocument->FlushPendingNotifications(FlushType::Frames);
1998
0
    }
1999
0
2000
0
    rootFrame = mFrameConstructor->GetRootFrame();
2001
0
    if (!mIsDestroying && rootFrame) {
2002
0
      // XXX Do a full invalidate at the beginning so that invalidates along
2003
0
      // the way don't have region accumulation issues?
2004
0
2005
0
      if (isBSizeChanging) {
2006
0
        // For BSize changes driven by style, RestyleManager handles this.
2007
0
        // For height:auto BSizes (i.e. layout-controlled), descendant
2008
0
        // intrinsic sizes can't depend on them. So the only other case is
2009
0
        // viewport-controlled BSizes which we handle here.
2010
0
        nsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(rootFrame);
2011
0
      }
2012
0
2013
0
      {
2014
0
        nsAutoCauseReflowNotifier crNotifier(this);
2015
0
        WillDoReflow();
2016
0
2017
0
        // Kick off a top-down reflow
2018
0
        AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
2019
0
        nsViewManager::AutoDisableRefresh refreshBlocker(viewManager);
2020
0
2021
0
        mDirtyRoots.RemoveElement(rootFrame);
2022
0
        DoReflow(rootFrame, true);
2023
0
2024
0
        if (shrinkToFit) {
2025
0
          const bool reflowAgain = wm.IsVertical() ?
2026
0
                                mPresContext->GetVisibleArea().width > aWidth :
2027
0
                                mPresContext->GetVisibleArea().height > aHeight;
2028
0
2029
0
          if (reflowAgain) {
2030
0
            mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
2031
0
            DoReflow(rootFrame, true);
2032
0
          }
2033
0
        }
2034
0
      }
2035
0
2036
0
      // the first DoReflow above should've set our bsize if it was
2037
0
      // NS_UNCONSTRAINEDSIZE, and the isize shouldn't be NS_UNCONSTRAINEDSIZE
2038
0
      // anyway
2039
0
      NS_ASSERTION(
2040
0
        mPresContext->GetVisibleArea().width != NS_UNCONSTRAINEDSIZE,
2041
0
        "width should not be NS_UNCONSTRAINEDSIZE after reflow");
2042
0
      NS_ASSERTION(
2043
0
        mPresContext->GetVisibleArea().height != NS_UNCONSTRAINEDSIZE,
2044
0
        "height should not be NS_UNCONSTRAINEDSIZE after reflow");
2045
0
2046
0
      DidDoReflow(true);
2047
0
    }
2048
0
  }
2049
0
2050
0
  rootFrame = mFrameConstructor->GetRootFrame();
2051
0
  if (rootFrame) {
2052
0
    wm = rootFrame->GetWritingMode();
2053
0
    // reflow did not happen; if the reflow happened, our bsize should not be
2054
0
    // NS_UNCONSTRAINEDSIZE because DoReflow will fix it up to the same values
2055
0
    // as below
2056
0
    if (wm.IsVertical()) {
2057
0
      if (mPresContext->GetVisibleArea().width == NS_UNCONSTRAINEDSIZE) {
2058
0
        mPresContext->SetVisibleArea(
2059
0
          nsRect(0, 0, rootFrame->GetRect().width, aHeight));
2060
0
      }
2061
0
    } else {
2062
0
      if (mPresContext->GetVisibleArea().height == NS_UNCONSTRAINEDSIZE) {
2063
0
        mPresContext->SetVisibleArea(
2064
0
          nsRect(0, 0, aWidth, rootFrame->GetRect().height));
2065
0
      }
2066
0
    }
2067
0
  }
2068
0
2069
0
  if (!mIsDestroying && !mResizeEventPending) {
2070
0
    mResizeEventPending = true;
2071
0
    if (MOZ_LIKELY(!mDocument->GetBFCacheEntry())) {
2072
0
      mPresContext->RefreshDriver()->AddResizeEventFlushObserver(this);
2073
0
    }
2074
0
  }
2075
0
2076
0
  return NS_OK; //XXX this needs to be real. MMP
2077
0
}
2078
2079
void
2080
PresShell::FireResizeEvent()
2081
0
{
2082
0
  if (mIsDocumentGone) {
2083
0
    return;
2084
0
  }
2085
0
2086
0
  mResizeEventPending = false;
2087
0
2088
0
  //Send resize event from here.
2089
0
  WidgetEvent event(true, mozilla::eResize);
2090
0
  nsEventStatus status = nsEventStatus_eIgnore;
2091
0
2092
0
  if (nsPIDOMWindowOuter* window = mDocument->GetWindow()) {
2093
0
    EventDispatcher::Dispatch(window, mPresContext, &event, nullptr, &status);
2094
0
  }
2095
0
}
2096
2097
void
2098
PresShell::SetIgnoreFrameDestruction(bool aIgnore)
2099
0
{
2100
0
  if (mDocument) {
2101
0
    // We need to tell the ImageLoader to drop all its references to frames
2102
0
    // because they're about to go away and it won't get notifications of that.
2103
0
    mDocument->StyleImageLoader()->ClearFrames(mPresContext);
2104
0
  }
2105
0
  mIgnoreFrameDestruction = aIgnore;
2106
0
}
2107
2108
void
2109
PresShell::NotifyDestroyingFrame(nsIFrame* aFrame)
2110
0
{
2111
0
  // We must remove these from FrameLayerBuilder::DisplayItemData::mFrameList here,
2112
0
  // otherwise the DisplayItemData destructor will use the destroyed frame when it
2113
0
  // tries to remove it from the (array) value of this property.
2114
0
  aFrame->RemoveDisplayItemDataForDeletion();
2115
0
2116
0
  if (!mIgnoreFrameDestruction) {
2117
0
    if (aFrame->HasImageRequest()) {
2118
0
      mDocument->StyleImageLoader()->DropRequestsForFrame(aFrame);
2119
0
    }
2120
0
2121
0
    mFrameConstructor->NotifyDestroyingFrame(aFrame);
2122
0
2123
0
    for (int32_t idx = mDirtyRoots.Length(); idx; ) {
2124
0
      --idx;
2125
0
      if (mDirtyRoots[idx] == aFrame) {
2126
0
        mDirtyRoots.RemoveElementAt(idx);
2127
0
      }
2128
0
    }
2129
0
2130
0
    // Remove frame properties
2131
0
    aFrame->DeleteAllProperties();
2132
0
2133
0
    if (aFrame == mCurrentEventFrame) {
2134
0
      mCurrentEventContent = aFrame->GetContent();
2135
0
      mCurrentEventFrame = nullptr;
2136
0
    }
2137
0
2138
  #ifdef DEBUG
2139
    if (aFrame == mDrawEventTargetFrame) {
2140
      mDrawEventTargetFrame = nullptr;
2141
    }
2142
  #endif
2143
2144
0
    for (unsigned int i=0; i < mCurrentEventFrameStack.Length(); i++) {
2145
0
      if (aFrame == mCurrentEventFrameStack.ElementAt(i)) {
2146
0
        //One of our stack frames was deleted.  Get its content so that when we
2147
0
        //pop it we can still get its new frame from its content
2148
0
        nsIContent *currentEventContent = aFrame->GetContent();
2149
0
        mCurrentEventContentStack.ReplaceObjectAt(currentEventContent, i);
2150
0
        mCurrentEventFrameStack[i] = nullptr;
2151
0
      }
2152
0
    }
2153
0
2154
0
    mFramesToDirty.RemoveEntry(aFrame);
2155
0
  }
2156
0
}
2157
2158
already_AddRefed<nsCaret> PresShell::GetCaret() const
2159
0
{
2160
0
  RefPtr<nsCaret> caret = mCaret;
2161
0
  return caret.forget();
2162
0
}
2163
2164
already_AddRefed<AccessibleCaretEventHub> PresShell::GetAccessibleCaretEventHub() const
2165
0
{
2166
0
  RefPtr<AccessibleCaretEventHub> eventHub = mAccessibleCaretEventHub;
2167
0
  return eventHub.forget();
2168
0
}
2169
2170
void PresShell::SetCaret(nsCaret *aNewCaret)
2171
0
{
2172
0
  mCaret = aNewCaret;
2173
0
}
2174
2175
void PresShell::RestoreCaret()
2176
0
{
2177
0
  mCaret = mOriginalCaret;
2178
0
}
2179
2180
NS_IMETHODIMP PresShell::SetCaretEnabled(bool aInEnable)
2181
0
{
2182
0
  bool oldEnabled = mCaretEnabled;
2183
0
2184
0
  mCaretEnabled = aInEnable;
2185
0
2186
0
  if (mCaretEnabled != oldEnabled)
2187
0
  {
2188
0
    MOZ_ASSERT(mCaret);
2189
0
    if (mCaret) {
2190
0
      mCaret->SetVisible(mCaretEnabled);
2191
0
    }
2192
0
  }
2193
0
2194
0
  return NS_OK;
2195
0
}
2196
2197
NS_IMETHODIMP PresShell::SetCaretReadOnly(bool aReadOnly)
2198
0
{
2199
0
  if (mCaret)
2200
0
    mCaret->SetCaretReadOnly(aReadOnly);
2201
0
  return NS_OK;
2202
0
}
2203
2204
NS_IMETHODIMP PresShell::GetCaretEnabled(bool *aOutEnabled)
2205
0
{
2206
0
  NS_ENSURE_ARG_POINTER(aOutEnabled);
2207
0
  *aOutEnabled = mCaretEnabled;
2208
0
  return NS_OK;
2209
0
}
2210
2211
NS_IMETHODIMP PresShell::SetCaretVisibilityDuringSelection(bool aVisibility)
2212
0
{
2213
0
  if (mCaret)
2214
0
    mCaret->SetVisibilityDuringSelection(aVisibility);
2215
0
  return NS_OK;
2216
0
}
2217
2218
NS_IMETHODIMP PresShell::GetCaretVisible(bool *aOutIsVisible)
2219
0
{
2220
0
  *aOutIsVisible = false;
2221
0
  if (mCaret) {
2222
0
    *aOutIsVisible = mCaret->IsVisible();
2223
0
  }
2224
0
  return NS_OK;
2225
0
}
2226
2227
NS_IMETHODIMP PresShell::SetSelectionFlags(int16_t aInEnable)
2228
0
{
2229
0
  mSelectionFlags = aInEnable;
2230
0
  return NS_OK;
2231
0
}
2232
2233
NS_IMETHODIMP PresShell::GetSelectionFlags(int16_t *aOutEnable)
2234
0
{
2235
0
  if (!aOutEnable)
2236
0
    return NS_ERROR_INVALID_ARG;
2237
0
  *aOutEnable = mSelectionFlags;
2238
0
  return NS_OK;
2239
0
}
2240
2241
//implementation of nsISelectionController
2242
2243
NS_IMETHODIMP
2244
PresShell::PhysicalMove(int16_t aDirection, int16_t aAmount, bool aExtend)
2245
0
{
2246
0
  RefPtr<nsFrameSelection> frameSelection = mSelection;
2247
0
  return frameSelection->PhysicalMove(aDirection, aAmount, aExtend);
2248
0
}
2249
2250
NS_IMETHODIMP
2251
PresShell::CharacterMove(bool aForward, bool aExtend)
2252
0
{
2253
0
  RefPtr<nsFrameSelection> frameSelection = mSelection;
2254
0
  return frameSelection->CharacterMove(aForward, aExtend);
2255
0
}
2256
2257
NS_IMETHODIMP
2258
PresShell::CharacterExtendForDelete()
2259
0
{
2260
0
  RefPtr<nsFrameSelection> frameSelection = mSelection;
2261
0
  return frameSelection->CharacterExtendForDelete();
2262
0
}
2263
2264
NS_IMETHODIMP
2265
PresShell::CharacterExtendForBackspace()
2266
0
{
2267
0
  RefPtr<nsFrameSelection> frameSelection = mSelection;
2268
0
  return frameSelection->CharacterExtendForBackspace();
2269
0
}
2270
2271
NS_IMETHODIMP
2272
PresShell::WordMove(bool aForward, bool aExtend)
2273
0
{
2274
0
  RefPtr<nsFrameSelection> frameSelection = mSelection;
2275
0
  nsresult result = frameSelection->WordMove(aForward, aExtend);
2276
0
// if we can't go down/up any more we must then move caret completely to
2277
0
// end/beginning respectively.
2278
0
  if (NS_FAILED(result))
2279
0
    result = CompleteMove(aForward, aExtend);
2280
0
  return result;
2281
0
}
2282
2283
NS_IMETHODIMP
2284
PresShell::WordExtendForDelete(bool aForward)
2285
0
{
2286
0
  RefPtr<nsFrameSelection> frameSelection = mSelection;
2287
0
  return frameSelection->WordExtendForDelete(aForward);
2288
0
}
2289
2290
NS_IMETHODIMP
2291
PresShell::LineMove(bool aForward, bool aExtend)
2292
0
{
2293
0
  RefPtr<nsFrameSelection> frameSelection = mSelection;
2294
0
  nsresult result = frameSelection->LineMove(aForward, aExtend);
2295
0
// if we can't go down/up any more we must then move caret completely to
2296
0
// end/beginning respectively.
2297
0
  if (NS_FAILED(result))
2298
0
    result = CompleteMove(aForward,aExtend);
2299
0
  return result;
2300
0
}
2301
2302
NS_IMETHODIMP
2303
PresShell::IntraLineMove(bool aForward, bool aExtend)
2304
0
{
2305
0
  RefPtr<nsFrameSelection> frameSelection = mSelection;
2306
0
  return frameSelection->IntraLineMove(aForward, aExtend);
2307
0
}
2308
2309
2310
2311
NS_IMETHODIMP
2312
PresShell::PageMove(bool aForward, bool aExtend)
2313
0
{
2314
0
  nsIScrollableFrame *scrollableFrame =
2315
0
    GetScrollableFrameToScroll(nsIPresShell::eVertical);
2316
0
  if (!scrollableFrame)
2317
0
    return NS_OK;
2318
0
2319
0
  RefPtr<nsFrameSelection> frameSelection = mSelection;
2320
0
  frameSelection->CommonPageMove(aForward, aExtend, scrollableFrame);
2321
0
  // After ScrollSelectionIntoView(), the pending notifications might be
2322
0
  // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
2323
0
  return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
2324
0
                                 nsISelectionController::SELECTION_FOCUS_REGION,
2325
0
                                 nsISelectionController::SCROLL_SYNCHRONOUS |
2326
0
                                 nsISelectionController::SCROLL_FOR_CARET_MOVE);
2327
0
}
2328
2329
2330
2331
NS_IMETHODIMP
2332
PresShell::ScrollPage(bool aForward)
2333
0
{
2334
0
  nsIScrollableFrame* scrollFrame =
2335
0
    GetScrollableFrameToScroll(nsIPresShell::eVertical);
2336
0
  if (scrollFrame) {
2337
0
    scrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
2338
0
                          nsIScrollableFrame::PAGES,
2339
0
                          nsIScrollableFrame::SMOOTH,
2340
0
                          nullptr, nullptr,
2341
0
                          nsIScrollableFrame::NOT_MOMENTUM,
2342
0
                          nsIScrollableFrame::ENABLE_SNAP);
2343
0
  }
2344
0
  return NS_OK;
2345
0
}
2346
2347
NS_IMETHODIMP
2348
PresShell::ScrollLine(bool aForward)
2349
0
{
2350
0
  nsIScrollableFrame* scrollFrame =
2351
0
    GetScrollableFrameToScroll(nsIPresShell::eVertical);
2352
0
  if (scrollFrame) {
2353
0
    int32_t lineCount = Preferences::GetInt("toolkit.scrollbox.verticalScrollDistance",
2354
0
                                            NS_DEFAULT_VERTICAL_SCROLL_DISTANCE);
2355
0
    scrollFrame->ScrollBy(nsIntPoint(0, aForward ? lineCount : -lineCount),
2356
0
                          nsIScrollableFrame::LINES,
2357
0
                          nsIScrollableFrame::SMOOTH,
2358
0
                          nullptr, nullptr,
2359
0
                          nsIScrollableFrame::NOT_MOMENTUM,
2360
0
                          nsIScrollableFrame::ENABLE_SNAP);
2361
0
  }
2362
0
  return NS_OK;
2363
0
}
2364
2365
NS_IMETHODIMP
2366
PresShell::ScrollCharacter(bool aRight)
2367
0
{
2368
0
  nsIScrollableFrame* scrollFrame =
2369
0
    GetScrollableFrameToScroll(nsIPresShell::eHorizontal);
2370
0
  if (scrollFrame) {
2371
0
    int32_t h = Preferences::GetInt("toolkit.scrollbox.horizontalScrollDistance",
2372
0
                                    NS_DEFAULT_HORIZONTAL_SCROLL_DISTANCE);
2373
0
    scrollFrame->ScrollBy(nsIntPoint(aRight ? h : -h, 0),
2374
0
                          nsIScrollableFrame::LINES,
2375
0
                          nsIScrollableFrame::SMOOTH,
2376
0
                          nullptr, nullptr,
2377
0
                          nsIScrollableFrame::NOT_MOMENTUM,
2378
0
                          nsIScrollableFrame::ENABLE_SNAP);
2379
0
  }
2380
0
  return NS_OK;
2381
0
}
2382
2383
NS_IMETHODIMP
2384
PresShell::CompleteScroll(bool aForward)
2385
0
{
2386
0
  nsIScrollableFrame* scrollFrame =
2387
0
    GetScrollableFrameToScroll(nsIPresShell::eVertical);
2388
0
  if (scrollFrame) {
2389
0
    scrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
2390
0
                          nsIScrollableFrame::WHOLE,
2391
0
                          nsIScrollableFrame::SMOOTH,
2392
0
                          nullptr, nullptr,
2393
0
                          nsIScrollableFrame::NOT_MOMENTUM,
2394
0
                          nsIScrollableFrame::ENABLE_SNAP);
2395
0
  }
2396
0
  return NS_OK;
2397
0
}
2398
2399
NS_IMETHODIMP
2400
PresShell::CompleteMove(bool aForward, bool aExtend)
2401
0
{
2402
0
  // Beware! This may flush notifications via synchronous
2403
0
  // ScrollSelectionIntoView.
2404
0
  RefPtr<nsFrameSelection> frameSelection = mSelection;
2405
0
  nsIContent* limiter = frameSelection->GetAncestorLimiter();
2406
0
  nsIFrame* frame = limiter ? limiter->GetPrimaryFrame()
2407
0
                            : FrameConstructor()->GetRootElementFrame();
2408
0
  if (!frame)
2409
0
    return NS_ERROR_FAILURE;
2410
0
  nsIFrame::CaretPosition pos =
2411
0
    frame->GetExtremeCaretPosition(!aForward);
2412
0
  frameSelection->HandleClick(pos.mResultContent, pos.mContentOffset,
2413
0
                              pos.mContentOffset, aExtend, false,
2414
0
                              aForward ? CARET_ASSOCIATE_AFTER :
2415
0
                                         CARET_ASSOCIATE_BEFORE);
2416
0
  if (limiter) {
2417
0
    // HandleClick resets ancestorLimiter, so set it again.
2418
0
    frameSelection->SetAncestorLimiter(limiter);
2419
0
  }
2420
0
2421
0
  // After ScrollSelectionIntoView(), the pending notifications might be
2422
0
  // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
2423
0
  return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
2424
0
                                 nsISelectionController::SELECTION_FOCUS_REGION,
2425
0
                                 nsISelectionController::SCROLL_SYNCHRONOUS |
2426
0
                                 nsISelectionController::SCROLL_FOR_CARET_MOVE);
2427
0
}
2428
2429
NS_IMETHODIMP
2430
PresShell::SelectAll()
2431
0
{
2432
0
  RefPtr<nsFrameSelection> frameSelection = mSelection;
2433
0
  return frameSelection->SelectAll();
2434
0
}
2435
2436
static void
2437
DoCheckVisibility(nsPresContext* aPresContext,
2438
                  nsIContent* aNode,
2439
                  int16_t aStartOffset,
2440
                  int16_t aEndOffset,
2441
                  bool* aRetval)
2442
0
{
2443
0
  nsIFrame* frame = aNode->GetPrimaryFrame();
2444
0
  if (!frame) {
2445
0
    // No frame to look at so it must not be visible.
2446
0
    return;
2447
0
  }
2448
0
2449
0
  // Start process now to go through all frames to find startOffset. Then check
2450
0
  // chars after that to see if anything until EndOffset is visible.
2451
0
  bool finished = false;
2452
0
  frame->CheckVisibility(aPresContext, aStartOffset, aEndOffset, true,
2453
0
                         &finished, aRetval);
2454
0
  // Don't worry about other return value.
2455
0
}
2456
2457
NS_IMETHODIMP
2458
PresShell::CheckVisibility(nsINode *node, int16_t startOffset, int16_t EndOffset, bool *_retval)
2459
0
{
2460
0
  if (!node || startOffset>EndOffset || !_retval || startOffset<0 || EndOffset<0)
2461
0
    return NS_ERROR_INVALID_ARG;
2462
0
  *_retval = false; //initialize return parameter
2463
0
  nsCOMPtr<nsIContent> content(do_QueryInterface(node));
2464
0
  if (!content)
2465
0
    return NS_ERROR_FAILURE;
2466
0
2467
0
  DoCheckVisibility(mPresContext, content, startOffset, EndOffset, _retval);
2468
0
  return NS_OK;
2469
0
}
2470
2471
nsresult
2472
PresShell::CheckVisibilityContent(nsIContent* aNode, int16_t aStartOffset,
2473
                                  int16_t aEndOffset, bool* aRetval)
2474
0
{
2475
0
  if (!aNode || aStartOffset > aEndOffset || !aRetval ||
2476
0
      aStartOffset < 0 || aEndOffset < 0) {
2477
0
    return NS_ERROR_INVALID_ARG;
2478
0
  }
2479
0
2480
0
  *aRetval = false;
2481
0
  DoCheckVisibility(mPresContext, aNode, aStartOffset, aEndOffset, aRetval);
2482
0
  return NS_OK;
2483
0
}
2484
2485
//end implementations nsISelectionController
2486
2487
nsIFrame*
2488
nsIPresShell::GetRootScrollFrame() const
2489
0
{
2490
0
  nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
2491
0
  // Ensure root frame is a viewport frame
2492
0
  if (!rootFrame || !rootFrame->IsViewportFrame())
2493
0
    return nullptr;
2494
0
  nsIFrame* theFrame = rootFrame->PrincipalChildList().FirstChild();
2495
0
  if (!theFrame || !theFrame->IsScrollFrame())
2496
0
    return nullptr;
2497
0
  return theFrame;
2498
0
}
2499
2500
nsIScrollableFrame*
2501
nsIPresShell::GetRootScrollFrameAsScrollable() const
2502
0
{
2503
0
  nsIFrame* frame = GetRootScrollFrame();
2504
0
  if (!frame)
2505
0
    return nullptr;
2506
0
  nsIScrollableFrame* scrollableFrame = do_QueryFrame(frame);
2507
0
  NS_ASSERTION(scrollableFrame,
2508
0
               "All scroll frames must implement nsIScrollableFrame");
2509
0
  return scrollableFrame;
2510
0
}
2511
2512
nsIPageSequenceFrame*
2513
PresShell::GetPageSequenceFrame() const
2514
0
{
2515
0
  nsIFrame* frame = mFrameConstructor->GetPageSequenceFrame();
2516
0
  return do_QueryFrame(frame);
2517
0
}
2518
2519
nsCanvasFrame*
2520
PresShell::GetCanvasFrame() const
2521
0
{
2522
0
  nsIFrame* frame = mFrameConstructor->GetDocElementContainingBlock();
2523
0
  return do_QueryFrame(frame);
2524
0
}
2525
2526
void
2527
PresShell::RestoreRootScrollPosition()
2528
0
{
2529
0
  nsIScrollableFrame* scrollableFrame = GetRootScrollFrameAsScrollable();
2530
0
  if (scrollableFrame) {
2531
0
    scrollableFrame->ScrollToRestoredPosition();
2532
0
  }
2533
0
}
2534
2535
void
2536
PresShell::MaybeReleaseCapturingContent()
2537
0
{
2538
0
  RefPtr<nsFrameSelection> frameSelection = FrameSelection();
2539
0
  if (frameSelection) {
2540
0
    frameSelection->SetDragState(false);
2541
0
  }
2542
0
  if (gCaptureInfo.mContent &&
2543
0
      gCaptureInfo.mContent->OwnerDoc() == mDocument) {
2544
0
    SetCapturingContent(nullptr, 0);
2545
0
  }
2546
0
}
2547
2548
void
2549
PresShell::BeginLoad(nsIDocument *aDocument)
2550
0
{
2551
0
  mDocumentLoading = true;
2552
0
2553
0
  gfxTextPerfMetrics *tp = nullptr;
2554
0
  if (mPresContext) {
2555
0
    tp = mPresContext->GetTextPerfMetrics();
2556
0
  }
2557
0
2558
0
  bool shouldLog = MOZ_LOG_TEST(gLog, LogLevel::Debug);
2559
0
  if (shouldLog || tp) {
2560
0
    mLoadBegin = TimeStamp::Now();
2561
0
  }
2562
0
2563
0
  if (shouldLog) {
2564
0
    nsIURI* uri = mDocument->GetDocumentURI();
2565
0
    MOZ_LOG(gLog, LogLevel::Debug,
2566
0
           ("(presshell) %p load begin [%s]\n",
2567
0
            this, uri ? uri->GetSpecOrDefault().get() : ""));
2568
0
  }
2569
0
}
2570
2571
void
2572
PresShell::EndLoad(nsIDocument *aDocument)
2573
0
{
2574
0
  MOZ_ASSERT(aDocument == mDocument, "Wrong document");
2575
0
2576
0
  RestoreRootScrollPosition();
2577
0
2578
0
  mDocumentLoading = false;
2579
0
}
2580
2581
void
2582
PresShell::LoadComplete()
2583
0
{
2584
0
  gfxTextPerfMetrics *tp = nullptr;
2585
0
  if (mPresContext) {
2586
0
    tp = mPresContext->GetTextPerfMetrics();
2587
0
  }
2588
0
2589
0
  // log load
2590
0
  bool shouldLog = MOZ_LOG_TEST(gLog, LogLevel::Debug);
2591
0
  if (shouldLog || tp) {
2592
0
    TimeDuration loadTime = TimeStamp::Now() - mLoadBegin;
2593
0
    nsIURI* uri = mDocument->GetDocumentURI();
2594
0
    nsAutoCString spec;
2595
0
    if (uri) {
2596
0
      spec = uri->GetSpecOrDefault();
2597
0
    }
2598
0
    if (shouldLog) {
2599
0
      MOZ_LOG(gLog, LogLevel::Debug,
2600
0
             ("(presshell) %p load done time-ms: %9.2f [%s]\n",
2601
0
              this, loadTime.ToMilliseconds(), spec.get()));
2602
0
    }
2603
0
    if (tp) {
2604
0
      tp->Accumulate();
2605
0
      if (tp->cumulative.numChars > 0) {
2606
0
        LogTextPerfStats(tp, this, tp->cumulative, loadTime.ToMilliseconds(),
2607
0
                         eLog_loaddone, spec.get());
2608
0
      }
2609
0
    }
2610
0
  }
2611
0
}
2612
2613
#ifdef DEBUG
2614
void
2615
PresShell::VerifyHasDirtyRootAncestor(nsIFrame* aFrame)
2616
{
2617
  // XXXbz due to bug 372769, can't actually assert anything here...
2618
  return;
2619
2620
  // XXXbz shouldn't need this part; remove it once FrameNeedsReflow
2621
  // handles the root frame correctly.
2622
  if (!aFrame->GetParent()) {
2623
    return;
2624
  }
2625
2626
  // Make sure that there is a reflow root ancestor of |aFrame| that's
2627
  // in mDirtyRoots already.
2628
  while (aFrame && (aFrame->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN)) {
2629
    if (((aFrame->GetStateBits() & NS_FRAME_REFLOW_ROOT) ||
2630
         !aFrame->GetParent()) &&
2631
        mDirtyRoots.Contains(aFrame)) {
2632
      return;
2633
    }
2634
2635
    aFrame = aFrame->GetParent();
2636
  }
2637
2638
  MOZ_ASSERT_UNREACHABLE("Frame has dirty bits set but isn't scheduled to be "
2639
                         "reflowed?");
2640
}
2641
#endif
2642
2643
void
2644
PresShell::FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
2645
                            nsFrameState aBitToAdd,
2646
                            ReflowRootHandling aRootHandling)
2647
0
{
2648
0
  MOZ_ASSERT(aBitToAdd == NS_FRAME_IS_DIRTY ||
2649
0
             aBitToAdd == NS_FRAME_HAS_DIRTY_CHILDREN ||
2650
0
             !aBitToAdd,
2651
0
             "Unexpected bits being added");
2652
0
2653
0
  // FIXME bug 478135
2654
0
  NS_ASSERTION(!(aIntrinsicDirty == eStyleChange &&
2655
0
                 aBitToAdd == NS_FRAME_HAS_DIRTY_CHILDREN),
2656
0
               "bits don't correspond to style change reason");
2657
0
2658
0
  // FIXME bug 457400
2659
0
  NS_ASSERTION(!mIsReflowing, "can't mark frame dirty during reflow");
2660
0
2661
0
  // If we've not yet done the initial reflow, then don't bother
2662
0
  // enqueuing a reflow command yet.
2663
0
  if (! mDidInitialize)
2664
0
    return;
2665
0
2666
0
  // If we're already destroying, don't bother with this either.
2667
0
  if (mIsDestroying)
2668
0
    return;
2669
0
2670
#ifdef DEBUG
2671
  //printf("gShellCounter: %d\n", gShellCounter++);
2672
  if (mInVerifyReflow)
2673
    return;
2674
2675
  if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) {
2676
    printf("\nPresShell@%p: frame %p needs reflow\n", (void*)this, (void*)aFrame);
2677
    if (VERIFY_REFLOW_REALLY_NOISY_RC & gVerifyReflowFlags) {
2678
      printf("Current content model:\n");
2679
      Element *rootElement = mDocument->GetRootElement();
2680
      if (rootElement) {
2681
        rootElement->List(stdout, 0);
2682
      }
2683
    }
2684
  }
2685
#endif
2686
2687
0
  AutoTArray<nsIFrame*, 4> subtrees;
2688
0
  subtrees.AppendElement(aFrame);
2689
0
2690
0
  do {
2691
0
    nsIFrame *subtreeRoot = subtrees.PopLastElement();
2692
0
2693
0
    // Grab |wasDirty| now so we can go ahead and update the bits on
2694
0
    // subtreeRoot.
2695
0
    bool wasDirty = NS_SUBTREE_DIRTY(subtreeRoot);
2696
0
    subtreeRoot->AddStateBits(aBitToAdd);
2697
0
2698
0
    // Determine whether we need to keep looking for the next ancestor
2699
0
    // reflow root if subtreeRoot itself is a reflow root.
2700
0
    bool targetNeedsReflowFromParent;
2701
0
    switch (aRootHandling) {
2702
0
      case ePositionOrSizeChange:
2703
0
        targetNeedsReflowFromParent = true;
2704
0
        break;
2705
0
      case eNoPositionOrSizeChange:
2706
0
        targetNeedsReflowFromParent = false;
2707
0
        break;
2708
0
      case eInferFromBitToAdd:
2709
0
        targetNeedsReflowFromParent = (aBitToAdd == NS_FRAME_IS_DIRTY);
2710
0
        break;
2711
0
    }
2712
0
2713
0
#define FRAME_IS_REFLOW_ROOT(_f)                   \
2714
0
  ((_f->GetStateBits() & NS_FRAME_REFLOW_ROOT) &&  \
2715
0
   (_f != subtreeRoot || !targetNeedsReflowFromParent))
2716
0
2717
0
2718
0
    // Mark the intrinsic widths as dirty on the frame, all of its ancestors,
2719
0
    // and all of its descendants, if needed:
2720
0
2721
0
    if (aIntrinsicDirty != nsIPresShell::eResize) {
2722
0
      // Mark argument and all ancestors dirty. (Unless we hit a reflow
2723
0
      // root that should contain the reflow.  That root could be
2724
0
      // subtreeRoot itself if it's not dirty, or it could be some
2725
0
      // ancestor of subtreeRoot.)
2726
0
      for (nsIFrame *a = subtreeRoot;
2727
0
           a && !FRAME_IS_REFLOW_ROOT(a);
2728
0
           a = a->GetParent())
2729
0
        a->MarkIntrinsicISizesDirty();
2730
0
    }
2731
0
2732
0
    if (aIntrinsicDirty == eStyleChange) {
2733
0
      // Mark all descendants dirty (using an nsTArray stack rather than
2734
0
      // recursion).
2735
0
      // Note that ReflowInput::InitResizeFlags has some similar
2736
0
      // code; see comments there for how and why it differs.
2737
0
      AutoTArray<nsIFrame*, 32> stack;
2738
0
      stack.AppendElement(subtreeRoot);
2739
0
2740
0
      do {
2741
0
        nsIFrame *f = stack.PopLastElement();
2742
0
2743
0
        if (f->IsPlaceholderFrame()) {
2744
0
          nsIFrame *oof = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
2745
0
          if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot, oof)) {
2746
0
            // We have another distinct subtree we need to mark.
2747
0
            subtrees.AppendElement(oof);
2748
0
          }
2749
0
        }
2750
0
2751
0
        nsIFrame::ChildListIterator lists(f);
2752
0
        for (; !lists.IsDone(); lists.Next()) {
2753
0
          for (nsIFrame* kid : lists.CurrentList()) {
2754
0
            kid->MarkIntrinsicISizesDirty();
2755
0
            stack.AppendElement(kid);
2756
0
          }
2757
0
        }
2758
0
      } while (stack.Length() != 0);
2759
0
    }
2760
0
2761
0
    // Skip setting dirty bits up the tree if we weren't given a bit to add.
2762
0
    if (!aBitToAdd) {
2763
0
      continue;
2764
0
    }
2765
0
2766
0
    // Set NS_FRAME_HAS_DIRTY_CHILDREN bits (via nsIFrame::ChildIsDirty)
2767
0
    // up the tree until we reach either a frame that's already dirty or
2768
0
    // a reflow root.
2769
0
    nsIFrame *f = subtreeRoot;
2770
0
    for (;;) {
2771
0
      if (FRAME_IS_REFLOW_ROOT(f) || !f->GetParent()) {
2772
0
        // we've hit a reflow root or the root frame
2773
0
        if (!wasDirty) {
2774
0
          mDirtyRoots.AppendElement(f);
2775
0
          SetNeedLayoutFlush();
2776
0
        }
2777
#ifdef DEBUG
2778
        else {
2779
          VerifyHasDirtyRootAncestor(f);
2780
        }
2781
#endif
2782
2783
0
        break;
2784
0
      }
2785
0
2786
0
      nsIFrame *child = f;
2787
0
      f = f->GetParent();
2788
0
      wasDirty = NS_SUBTREE_DIRTY(f);
2789
0
      f->ChildIsDirty(child);
2790
0
      NS_ASSERTION(f->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN,
2791
0
                   "ChildIsDirty didn't do its job");
2792
0
      if (wasDirty) {
2793
0
        // This frame was already marked dirty.
2794
#ifdef DEBUG
2795
        VerifyHasDirtyRootAncestor(f);
2796
#endif
2797
        break;
2798
0
      }
2799
0
    }
2800
0
  } while (subtrees.Length() != 0);
2801
0
2802
0
  MaybeScheduleReflow();
2803
0
}
2804
2805
void
2806
PresShell::FrameNeedsToContinueReflow(nsIFrame *aFrame)
2807
0
{
2808
0
  NS_ASSERTION(mIsReflowing, "Must be in reflow when marking path dirty.");
2809
0
  MOZ_ASSERT(mCurrentReflowRoot, "Must have a current reflow root here");
2810
0
  NS_ASSERTION(aFrame == mCurrentReflowRoot ||
2811
0
               nsLayoutUtils::IsProperAncestorFrame(mCurrentReflowRoot, aFrame),
2812
0
               "Frame passed in is not the descendant of mCurrentReflowRoot");
2813
0
  NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW,
2814
0
               "Frame passed in not in reflow?");
2815
0
2816
0
  mFramesToDirty.PutEntry(aFrame);
2817
0
}
2818
2819
already_AddRefed<nsIContent>
2820
nsIPresShell::GetContentForScrolling() const
2821
0
{
2822
0
  if (nsCOMPtr<nsIContent> focused = GetFocusedContentInOurWindow()) {
2823
0
    return focused.forget();
2824
0
  }
2825
0
  return GetSelectedContentForScrolling();
2826
0
}
2827
2828
already_AddRefed<nsIContent>
2829
nsIPresShell::GetSelectedContentForScrolling() const
2830
0
{
2831
0
  nsCOMPtr<nsIContent> selectedContent;
2832
0
  if (mSelection) {
2833
0
    Selection* domSelection =
2834
0
      mSelection->GetSelection(SelectionType::eNormal);
2835
0
    if (domSelection) {
2836
0
      selectedContent = nsIContent::FromNodeOrNull(domSelection->GetFocusNode());
2837
0
    }
2838
0
  }
2839
0
  return selectedContent.forget();
2840
0
}
2841
2842
nsIScrollableFrame*
2843
nsIPresShell::GetNearestScrollableFrame(
2844
                nsIFrame* aFrame,
2845
                nsIPresShell::ScrollDirection aDirection)
2846
0
{
2847
0
  if (aDirection == nsIPresShell::eEither) {
2848
0
    return nsLayoutUtils::GetNearestScrollableFrame(aFrame);
2849
0
  }
2850
0
2851
0
  return nsLayoutUtils::GetNearestScrollableFrameForDirection(aFrame,
2852
0
           aDirection == eVertical ? nsLayoutUtils::eVertical :
2853
0
                                     nsLayoutUtils::eHorizontal);
2854
0
}
2855
2856
nsIScrollableFrame*
2857
nsIPresShell::GetScrollableFrameToScrollForContent(
2858
                nsIContent* aContent,
2859
                nsIPresShell::ScrollDirection aDirection)
2860
0
{
2861
0
  nsIScrollableFrame* scrollFrame = nullptr;
2862
0
  if (aContent) {
2863
0
    nsIFrame* startFrame = aContent->GetPrimaryFrame();
2864
0
    if (startFrame) {
2865
0
      scrollFrame = startFrame->GetScrollTargetFrame();
2866
0
      if (scrollFrame) {
2867
0
        startFrame = scrollFrame->GetScrolledFrame();
2868
0
      }
2869
0
      scrollFrame = GetNearestScrollableFrame(startFrame, aDirection);
2870
0
    }
2871
0
  }
2872
0
  if (!scrollFrame) {
2873
0
    scrollFrame = GetRootScrollFrameAsScrollable();
2874
0
    if (!scrollFrame || !scrollFrame->GetScrolledFrame()) {
2875
0
      return nullptr;
2876
0
    }
2877
0
    scrollFrame = GetNearestScrollableFrame(scrollFrame->GetScrolledFrame(),
2878
0
                                            aDirection);
2879
0
  }
2880
0
  return scrollFrame;
2881
0
}
2882
2883
nsIScrollableFrame*
2884
nsIPresShell::GetScrollableFrameToScroll(nsIPresShell::ScrollDirection aDirection)
2885
0
{
2886
0
  nsCOMPtr<nsIContent> content = GetContentForScrolling();
2887
0
  return GetScrollableFrameToScrollForContent(content.get(), aDirection);
2888
0
}
2889
2890
void
2891
PresShell::CancelAllPendingReflows()
2892
0
{
2893
0
  mDirtyRoots.Clear();
2894
0
2895
0
  if (mObservingLayoutFlushes) {
2896
0
    GetPresContext()->RefreshDriver()->RemoveLayoutFlushObserver(this);
2897
0
    mObservingLayoutFlushes = false;
2898
0
  }
2899
0
2900
0
  ASSERT_REFLOW_SCHEDULED_STATE();
2901
0
}
2902
2903
static bool
2904
DestroyFramesAndStyleDataFor(Element* aElement,
2905
                             nsPresContext& aPresContext,
2906
                             RestyleManager::IncludeRoot aIncludeRoot)
2907
0
{
2908
0
  bool didReconstruct =
2909
0
    aPresContext.FrameConstructor()->DestroyFramesFor(aElement);
2910
0
  RestyleManager::ClearServoDataFromSubtree(aElement, aIncludeRoot);
2911
0
  return didReconstruct;
2912
0
}
2913
2914
void
2915
nsIPresShell::SlotAssignmentWillChange(Element& aElement,
2916
                                       HTMLSlotElement* aOldSlot,
2917
                                       HTMLSlotElement* aNewSlot)
2918
0
{
2919
0
  MOZ_ASSERT(aOldSlot != aNewSlot);
2920
0
2921
0
  if (MOZ_UNLIKELY(!mDidInitialize)) {
2922
0
    return;
2923
0
  }
2924
0
2925
0
  // If the old slot is about to become empty, let layout know that it needs to
2926
0
  // do work.
2927
0
  if (aOldSlot && aOldSlot->AssignedNodes().Length() == 1) {
2928
0
    DestroyFramesForAndRestyle(aOldSlot);
2929
0
  }
2930
0
2931
0
  // Ensure the new element starts off clean.
2932
0
  DestroyFramesAndStyleDataFor(&aElement,
2933
0
                               *mPresContext,
2934
0
                               RestyleManager::IncludeRoot::Yes);
2935
0
2936
0
  if (aNewSlot) {
2937
0
    // If the new slot will stop showing fallback content, we need to reframe it
2938
0
    // altogether.
2939
0
    if (aNewSlot->AssignedNodes().IsEmpty()) {
2940
0
      DestroyFramesForAndRestyle(aNewSlot);
2941
0
    // Otherwise we just care about the element, but we need to ensure that
2942
0
    // something takes care of traversing to the relevant slot, if needed.
2943
0
    } else if (aNewSlot->HasServoData() &&
2944
0
               !Servo_Element_IsDisplayNone(aNewSlot)) {
2945
0
      // Set the reframe bits...
2946
0
      aNewSlot->NoteDescendantsNeedFramesForServo();
2947
0
      aElement.SetFlags(NODE_NEEDS_FRAME);
2948
0
      // Now the style dirty bits. Note that we can't just do
2949
0
      // aElement.NoteDirtyForServo(), because the new slot is not setup yet.
2950
0
      aNewSlot->SetHasDirtyDescendantsForServo();
2951
0
      aNewSlot->NoteDirtySubtreeForServo();
2952
0
    }
2953
0
  }
2954
0
}
2955
2956
#ifdef DEBUG
2957
static void
2958
AssertNoFramesInSubtree(nsIContent* aContent)
2959
{
2960
  for (nsIContent* c = aContent; c; c = c->GetNextNode(aContent)) {
2961
    MOZ_ASSERT(!c->GetPrimaryFrame());
2962
    if (auto* shadowRoot = c->GetShadowRoot()) {
2963
      AssertNoFramesInSubtree(shadowRoot);
2964
    }
2965
    if (auto* binding = c->GetXBLBinding()) {
2966
      if (auto* bindingWithContent = binding->GetBindingWithContent()) {
2967
        nsIContent* anonContent = bindingWithContent->GetAnonymousContent();
2968
        MOZ_ASSERT(!anonContent->GetPrimaryFrame());
2969
2970
        // Need to do this instead of just AssertNoFramesInSubtree(anonContent),
2971
        // because the parent of the children of the <content> element isn't the
2972
        // <content> element, but the bound element, and that confuses
2973
        // GetNextNode a lot.
2974
        for (nsIContent* child = anonContent->GetFirstChild();
2975
             child;
2976
             child = child->GetNextSibling()) {
2977
          AssertNoFramesInSubtree(child);
2978
        }
2979
      }
2980
    }
2981
  }
2982
}
2983
#endif
2984
2985
void
2986
nsIPresShell::DestroyFramesForAndRestyle(Element* aElement)
2987
0
{
2988
#ifdef DEBUG
2989
  auto postCondition = mozilla::MakeScopeExit([&]() {
2990
    AssertNoFramesInSubtree(aElement);
2991
  });
2992
#endif
2993
2994
0
  MOZ_ASSERT(aElement);
2995
0
  if (MOZ_UNLIKELY(!mDidInitialize)) {
2996
0
    return;
2997
0
  }
2998
0
2999
0
  if (!aElement->GetFlattenedTreeParentNode()) {
3000
0
    // Nothing to do here, the element already is out of the frame tree.
3001
0
    return;
3002
0
  }
3003
0
3004
0
  nsAutoScriptBlocker scriptBlocker;
3005
0
3006
0
  // Mark ourselves as not safe to flush while we're doing frame destruction.
3007
0
  ++mChangeNestCount;
3008
0
3009
0
  const bool didReconstruct = FrameConstructor()->DestroyFramesFor(aElement);
3010
0
3011
0
  // Clear the style data from all the flattened tree descendants, but _not_
3012
0
  // from us, since otherwise we wouldn't see the reframe.
3013
0
  RestyleManager::ClearServoDataFromSubtree(
3014
0
      aElement, RestyleManager::IncludeRoot::No);
3015
0
3016
0
  auto changeHint = didReconstruct
3017
0
    ? nsChangeHint(0)
3018
0
    : nsChangeHint_ReconstructFrame;
3019
0
3020
0
  // NOTE(emilio): eRestyle_Subtree is needed to force also a full subtree
3021
0
  // restyle for the content (in Stylo, where the existence of frames != the
3022
0
  // existence of styles).
3023
0
  mPresContext->RestyleManager()->PostRestyleEvent(
3024
0
    aElement, eRestyle_Subtree, changeHint);
3025
0
3026
0
  --mChangeNestCount;
3027
0
}
3028
3029
void
3030
nsIPresShell::PostRecreateFramesFor(Element* aElement)
3031
0
{
3032
0
  if (MOZ_UNLIKELY(!mDidInitialize)) {
3033
0
    // Nothing to do here. In fact, if we proceed and aElement is the root, we
3034
0
    // will crash.
3035
0
    return;
3036
0
  }
3037
0
3038
0
  mPresContext->RestyleManager()->PostRestyleEvent(aElement, nsRestyleHint(0),
3039
0
                                                   nsChangeHint_ReconstructFrame);
3040
0
}
3041
3042
void
3043
nsIPresShell::RestyleForAnimation(Element* aElement, nsRestyleHint aHint)
3044
0
{
3045
0
  // Now that we no longer have separate non-animation and animation
3046
0
  // restyles, this method having a distinct identity is less important,
3047
0
  // but it still seems useful to offer as a "more public" API and as a
3048
0
  // chokepoint for these restyles to go through.
3049
0
  mPresContext->RestyleManager()->PostRestyleEvent(aElement, aHint,
3050
0
                                                   nsChangeHint(0));
3051
0
}
3052
3053
void
3054
nsIPresShell::SetForwardingContainer(const WeakPtr<nsDocShell> &aContainer)
3055
0
{
3056
0
  mForwardingContainer = aContainer;
3057
0
}
3058
3059
void
3060
PresShell::ClearFrameRefs(nsIFrame* aFrame)
3061
0
{
3062
0
  mPresContext->EventStateManager()->ClearFrameRefs(aFrame);
3063
0
3064
0
  AutoWeakFrame* weakFrame = mAutoWeakFrames;
3065
0
  while (weakFrame) {
3066
0
    AutoWeakFrame* prev = weakFrame->GetPreviousWeakFrame();
3067
0
    if (weakFrame->GetFrame() == aFrame) {
3068
0
      // This removes weakFrame from mAutoWeakFrames.
3069
0
      weakFrame->Clear(this);
3070
0
    }
3071
0
    weakFrame = prev;
3072
0
  }
3073
0
3074
0
  AutoTArray<WeakFrame*, 4> toRemove;
3075
0
  for (auto iter = mWeakFrames.Iter(); !iter.Done(); iter.Next()) {
3076
0
    WeakFrame* weakFrame = iter.Get()->GetKey();
3077
0
    if (weakFrame->GetFrame() == aFrame) {
3078
0
      toRemove.AppendElement(weakFrame);
3079
0
    }
3080
0
  }
3081
0
  for (WeakFrame* weakFrame : toRemove) {
3082
0
    weakFrame->Clear(this);
3083
0
  }
3084
0
}
3085
3086
already_AddRefed<gfxContext>
3087
PresShell::CreateReferenceRenderingContext()
3088
0
{
3089
0
  nsDeviceContext* devCtx = mPresContext->DeviceContext();
3090
0
  RefPtr<gfxContext> rc;
3091
0
  if (mPresContext->IsScreen()) {
3092
0
    rc = gfxContext::CreateOrNull(
3093
0
      gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget().get());
3094
0
  } else {
3095
0
    // We assume the devCtx has positive width and height for this call.
3096
0
    // However, width and height, may be outside of the reasonable range
3097
0
    // so rc may still be null.
3098
0
    rc = devCtx->CreateReferenceRenderingContext();
3099
0
  }
3100
0
3101
0
  return rc ? rc.forget() : nullptr;
3102
0
}
3103
3104
nsresult
3105
PresShell::GoToAnchor(const nsAString& aAnchorName, bool aScroll,
3106
                      uint32_t aAdditionalScrollFlags)
3107
0
{
3108
0
  if (!mDocument) {
3109
0
    return NS_ERROR_FAILURE;
3110
0
  }
3111
0
3112
0
  const Element *root = mDocument->GetRootElement();
3113
0
  if (root && root->IsSVGElement(nsGkAtoms::svg)) {
3114
0
    // We need to execute this even if there is an empty anchor name
3115
0
    // so that any existing SVG fragment identifier effect is removed
3116
0
    if (SVGFragmentIdentifier::ProcessFragmentIdentifier(mDocument, aAnchorName)) {
3117
0
      return NS_OK;
3118
0
    }
3119
0
  }
3120
0
3121
0
  // Hold a reference to the ESM in case event dispatch tears us down.
3122
0
  RefPtr<EventStateManager> esm = mPresContext->EventStateManager();
3123
0
3124
0
  if (aAnchorName.IsEmpty()) {
3125
0
    NS_ASSERTION(!aScroll, "can't scroll to empty anchor name");
3126
0
    esm->SetContentState(nullptr, NS_EVENT_STATE_URLTARGET);
3127
0
    return NS_OK;
3128
0
  }
3129
0
3130
0
  nsresult rv = NS_OK;
3131
0
  nsCOMPtr<nsIContent> content;
3132
0
3133
0
  // Search for an element with a matching "id" attribute
3134
0
  if (mDocument) {
3135
0
    content = mDocument->GetElementById(aAnchorName);
3136
0
  }
3137
0
3138
0
  // Search for an anchor element with a matching "name" attribute
3139
0
  if (!content && mDocument->IsHTMLDocument()) {
3140
0
    // Find a matching list of named nodes
3141
0
    nsCOMPtr<nsINodeList> list = mDocument->GetElementsByName(aAnchorName);
3142
0
    if (list) {
3143
0
      // Loop through the named nodes looking for the first anchor
3144
0
      uint32_t length = list->Length();
3145
0
      for (uint32_t i = 0; i < length; i++) {
3146
0
        nsIContent* node = list->Item(i);
3147
0
        if (node->IsHTMLElement(nsGkAtoms::a)) {
3148
0
          content = node;
3149
0
          break;
3150
0
        }
3151
0
      }
3152
0
    }
3153
0
  }
3154
0
3155
0
  // Search for anchor in the HTML namespace with a matching name
3156
0
  if (!content && !mDocument->IsHTMLDocument())
3157
0
  {
3158
0
    NS_NAMED_LITERAL_STRING(nameSpace, "http://www.w3.org/1999/xhtml");
3159
0
    // Get the list of anchor elements
3160
0
    nsCOMPtr<nsINodeList> list =
3161
0
      mDocument->GetElementsByTagNameNS(nameSpace, NS_LITERAL_STRING("a"));
3162
0
    // Loop through the anchors looking for the first one with the given name.
3163
0
    for (uint32_t i = 0; true; i++) {
3164
0
      nsIContent* node = list->Item(i);
3165
0
      if (!node) { // End of list
3166
0
        break;
3167
0
      }
3168
0
3169
0
      // Compare the name attribute
3170
0
      if (node->IsElement() &&
3171
0
          node->AsElement()->AttrValueIs(kNameSpaceID_None,
3172
0
                                         nsGkAtoms::name,
3173
0
                                         aAnchorName,
3174
0
                                         eCaseMatters)) {
3175
0
        content = node;
3176
0
        break;
3177
0
      }
3178
0
    }
3179
0
  }
3180
0
3181
0
  esm->SetContentState(content, NS_EVENT_STATE_URLTARGET);
3182
0
3183
0
#ifdef ACCESSIBILITY
3184
0
  nsIContent *anchorTarget = content;
3185
0
#endif
3186
0
3187
0
  nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
3188
0
  if (rootScroll && rootScroll->DidHistoryRestore()) {
3189
0
    // Scroll position restored from history trumps scrolling to anchor.
3190
0
    aScroll = false;
3191
0
    rootScroll->ClearDidHistoryRestore();
3192
0
  }
3193
0
3194
0
  if (content) {
3195
0
    if (aScroll) {
3196
0
      rv = ScrollContentIntoView(content,
3197
0
                                 ScrollAxis(SCROLL_TOP, SCROLL_ALWAYS),
3198
0
                                 ScrollAxis(),
3199
0
                                 ANCHOR_SCROLL_FLAGS | aAdditionalScrollFlags);
3200
0
      NS_ENSURE_SUCCESS(rv, rv);
3201
0
3202
0
      nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
3203
0
      if (rootScroll) {
3204
0
        mLastAnchorScrolledTo = content;
3205
0
        mLastAnchorScrollPositionY = rootScroll->GetScrollPosition().y;
3206
0
      }
3207
0
    }
3208
0
3209
0
    // Should we select the target? This action is controlled by a
3210
0
    // preference: the default is to not select.
3211
0
    bool selectAnchor = Preferences::GetBool("layout.selectanchor");
3212
0
3213
0
    // Even if select anchor pref is false, we must still move the
3214
0
    // caret there. That way tabbing will start from the new
3215
0
    // location
3216
0
    RefPtr<nsRange> jumpToRange = new nsRange(mDocument);
3217
0
    while (content && content->GetFirstChild()) {
3218
0
      content = content->GetFirstChild();
3219
0
    }
3220
0
    jumpToRange->SelectNodeContents(*content, IgnoreErrors());
3221
0
    // Select the anchor
3222
0
    RefPtr<Selection> sel = mSelection->GetSelection(SelectionType::eNormal);
3223
0
    if (sel) {
3224
0
      sel->RemoveAllRanges(IgnoreErrors());
3225
0
      sel->AddRange(*jumpToRange, IgnoreErrors());
3226
0
      if (!selectAnchor) {
3227
0
        // Use a caret (collapsed selection) at the start of the anchor
3228
0
        sel->CollapseToStart(IgnoreErrors());
3229
0
      }
3230
0
    }
3231
0
    // Selection is at anchor.
3232
0
    // Now focus the document itself if focus is on an element within it.
3233
0
    nsPIDOMWindowOuter *win = mDocument->GetWindow();
3234
0
3235
0
    nsIFocusManager* fm = nsFocusManager::GetFocusManager();
3236
0
    if (fm && win) {
3237
0
      nsCOMPtr<mozIDOMWindowProxy> focusedWindow;
3238
0
      fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
3239
0
      if (SameCOMIdentity(win, focusedWindow)) {
3240
0
        fm->ClearFocus(focusedWindow);
3241
0
      }
3242
0
    }
3243
0
3244
0
    // If the target is an animation element, activate the animation
3245
0
    if (content->IsNodeOfType(nsINode::eANIMATION)) {
3246
0
      SVGContentUtils::ActivateByHyperlink(content.get());
3247
0
    }
3248
0
  } else {
3249
0
    rv = NS_ERROR_FAILURE;
3250
0
    NS_NAMED_LITERAL_STRING(top, "top");
3251
0
    if (nsContentUtils::EqualsIgnoreASCIICase(aAnchorName, top)) {
3252
0
      // Scroll to the top/left if aAnchorName is "top" and there is no element
3253
0
      // with such a name or id.
3254
0
      rv = NS_OK;
3255
0
      nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable();
3256
0
      // Check |aScroll| after setting |rv| so we set |rv| to the same
3257
0
      // thing whether or not |aScroll| is true.
3258
0
      if (aScroll && sf) {
3259
0
        // Scroll to the top of the page
3260
0
        sf->ScrollTo(nsPoint(0, 0), nsIScrollableFrame::INSTANT);
3261
0
      }
3262
0
    }
3263
0
  }
3264
0
3265
0
#ifdef ACCESSIBILITY
3266
0
  if (anchorTarget) {
3267
0
    nsAccessibilityService* accService = AccService();
3268
0
    if (accService)
3269
0
      accService->NotifyOfAnchorJumpTo(anchorTarget);
3270
0
  }
3271
0
#endif
3272
0
3273
0
  return rv;
3274
0
}
3275
3276
nsresult
3277
PresShell::ScrollToAnchor()
3278
0
{
3279
0
  if (!mLastAnchorScrolledTo) {
3280
0
    return NS_OK;
3281
0
  }
3282
0
  NS_ASSERTION(mDidInitialize, "should have done initial reflow by now");
3283
0
3284
0
  nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
3285
0
  if (!rootScroll ||
3286
0
      mLastAnchorScrollPositionY != rootScroll->GetScrollPosition().y) {
3287
0
    return NS_OK;
3288
0
  }
3289
0
  nsresult rv = ScrollContentIntoView(mLastAnchorScrolledTo,
3290
0
                                      ScrollAxis(SCROLL_TOP, SCROLL_ALWAYS),
3291
0
                                      ScrollAxis(),
3292
0
                                      ANCHOR_SCROLL_FLAGS);
3293
0
  mLastAnchorScrolledTo = nullptr;
3294
0
  return rv;
3295
0
}
3296
3297
/*
3298
 * Helper (per-continuation) for ScrollContentIntoView.
3299
 *
3300
 * @param aContainerFrame [in] the frame which aRect is relative to
3301
 * @param aFrame [in] Frame whose bounds should be unioned
3302
 * @param aUseWholeLineHeightForInlines [in] if true, then for inline frames
3303
 * we should include the top of the line in the added rectangle
3304
 * @param aRect [inout] rect into which its bounds should be unioned
3305
 * @param aHaveRect [inout] whether aRect contains data yet
3306
 * @param aPrevBlock [inout] the block aLines is a line iterator for
3307
 * @param aLines [inout] the line iterator we're using
3308
 * @param aCurLine [inout] the line to start looking from in this iterator
3309
 */
3310
static void
3311
AccumulateFrameBounds(nsIFrame* aContainerFrame,
3312
                      nsIFrame* aFrame,
3313
                      bool aUseWholeLineHeightForInlines,
3314
                      nsRect& aRect,
3315
                      bool& aHaveRect,
3316
                      nsIFrame*& aPrevBlock,
3317
                      nsAutoLineIterator& aLines,
3318
                      int32_t& aCurLine)
3319
0
{
3320
0
  nsIFrame* frame = aFrame;
3321
0
  nsRect frameBounds = nsRect(nsPoint(0, 0), aFrame->GetSize());
3322
0
3323
0
  // If this is an inline frame and either the bounds height is 0 (quirks
3324
0
  // layout model) or aUseWholeLineHeightForInlines is set, we need to
3325
0
  // change the top of the bounds to include the whole line.
3326
0
  if (frameBounds.height == 0 || aUseWholeLineHeightForInlines) {
3327
0
    nsIFrame *prevFrame = aFrame;
3328
0
    nsIFrame *f = aFrame;
3329
0
3330
0
    while (f && f->IsFrameOfType(nsIFrame::eLineParticipant) &&
3331
0
           !f->IsTransformed() && !f->IsAbsPosContainingBlock()) {
3332
0
      prevFrame = f;
3333
0
      f = prevFrame->GetParent();
3334
0
    }
3335
0
3336
0
    if (f != aFrame && f && f->IsBlockFrame()) {
3337
0
      // find the line containing aFrame and increase the top of |offset|.
3338
0
      if (f != aPrevBlock) {
3339
0
        aLines = f->GetLineIterator();
3340
0
        aPrevBlock = f;
3341
0
        aCurLine = 0;
3342
0
      }
3343
0
      if (aLines) {
3344
0
        int32_t index = aLines->FindLineContaining(prevFrame, aCurLine);
3345
0
        if (index >= 0) {
3346
0
          aCurLine = index;
3347
0
          nsIFrame *trash1;
3348
0
          int32_t trash2;
3349
0
          nsRect lineBounds;
3350
0
3351
0
          if (NS_SUCCEEDED(aLines->GetLine(index, &trash1, &trash2,
3352
0
                                           lineBounds))) {
3353
0
            frameBounds += frame->GetOffsetTo(f);
3354
0
            frame = f;
3355
0
            if (lineBounds.y < frameBounds.y) {
3356
0
              frameBounds.height = frameBounds.YMost() - lineBounds.y;
3357
0
              frameBounds.y = lineBounds.y;
3358
0
            }
3359
0
          }
3360
0
        }
3361
0
      }
3362
0
    }
3363
0
  }
3364
0
3365
0
  nsRect transformedBounds = nsLayoutUtils::TransformFrameRectToAncestor(frame,
3366
0
    frameBounds, aContainerFrame);
3367
0
3368
0
  if (aHaveRect) {
3369
0
    // We can't use nsRect::UnionRect since it drops empty rects on
3370
0
    // the floor, and we need to include them.  (Thus we need
3371
0
    // aHaveRect to know when to drop the initial value on the floor.)
3372
0
    aRect.UnionRectEdges(aRect, transformedBounds);
3373
0
  } else {
3374
0
    aHaveRect = true;
3375
0
    aRect = transformedBounds;
3376
0
  }
3377
0
}
3378
3379
static bool
3380
ComputeNeedToScroll(nsIPresShell::WhenToScroll aWhenToScroll,
3381
                    nscoord                    aLineSize,
3382
                    nscoord                    aRectMin,
3383
                    nscoord                    aRectMax,
3384
                    nscoord                    aViewMin,
3385
0
                    nscoord                    aViewMax) {
3386
0
  // See how the rect should be positioned vertically
3387
0
  if (nsIPresShell::SCROLL_ALWAYS == aWhenToScroll) {
3388
0
    // The caller wants the frame as visible as possible
3389
0
    return true;
3390
0
  } else if (nsIPresShell::SCROLL_IF_NOT_VISIBLE == aWhenToScroll) {
3391
0
    // Scroll only if no part of the frame is visible in this view
3392
0
    return aRectMax - aLineSize <= aViewMin ||
3393
0
           aRectMin + aLineSize >= aViewMax;
3394
0
  } else if (nsIPresShell::SCROLL_IF_NOT_FULLY_VISIBLE == aWhenToScroll) {
3395
0
    // Scroll only if part of the frame is hidden and more can fit in view
3396
0
    return !(aRectMin >= aViewMin && aRectMax <= aViewMax) &&
3397
0
      std::min(aViewMax, aRectMax) - std::max(aRectMin, aViewMin) < aViewMax - aViewMin;
3398
0
  }
3399
0
  return false;
3400
0
}
3401
3402
static nscoord
3403
ComputeWhereToScroll(int16_t aWhereToScroll,
3404
                     nscoord aOriginalCoord,
3405
                     nscoord aRectMin,
3406
                     nscoord aRectMax,
3407
                     nscoord aViewMin,
3408
                     nscoord aViewMax,
3409
                     nscoord* aRangeMin,
3410
0
                     nscoord* aRangeMax) {
3411
0
  nscoord resultCoord = aOriginalCoord;
3412
0
  nscoord scrollPortLength = aViewMax - aViewMin;
3413
0
  if (nsIPresShell::SCROLL_MINIMUM == aWhereToScroll) {
3414
0
    // Scroll the minimum amount necessary to show as much as possible of the frame.
3415
0
    // If the frame is too large, don't hide any initially visible part of it.
3416
0
    nscoord min = std::min(aRectMin, aRectMax - scrollPortLength);
3417
0
    nscoord max = std::max(aRectMin, aRectMax - scrollPortLength);
3418
0
    resultCoord = std::min(std::max(aOriginalCoord, min), max);
3419
0
  } else {
3420
0
    nscoord frameAlignCoord =
3421
0
      NSToCoordRound(aRectMin + (aRectMax - aRectMin) * (aWhereToScroll / 100.0f));
3422
0
    resultCoord =  NSToCoordRound(frameAlignCoord - scrollPortLength * (
3423
0
                                  aWhereToScroll / 100.0f));
3424
0
  }
3425
0
  // Force the scroll range to extend to include resultCoord.
3426
0
  *aRangeMin = std::min(resultCoord, aRectMax - scrollPortLength);
3427
0
  *aRangeMax = std::max(resultCoord, aRectMin);
3428
0
  return resultCoord;
3429
0
}
3430
3431
/**
3432
 * This function takes a scrollable frame, a rect in the coordinate system
3433
 * of the scrolled frame, and a desired percentage-based scroll
3434
 * position and attempts to scroll the rect to that position in the
3435
 * scrollport.
3436
 *
3437
 * This needs to work even if aRect has a width or height of zero.
3438
 */
3439
static void ScrollToShowRect(nsIScrollableFrame*      aFrameAsScrollable,
3440
                             const nsRect&            aRect,
3441
                             nsIPresShell::ScrollAxis aVertical,
3442
                             nsIPresShell::ScrollAxis aHorizontal,
3443
                             uint32_t                 aFlags)
3444
0
{
3445
0
  nsPoint scrollPt = aFrameAsScrollable->GetScrollPosition();
3446
0
  nsRect visibleRect(scrollPt,
3447
0
                     aFrameAsScrollable->GetVisualViewportSize());
3448
0
3449
0
  nsSize lineSize;
3450
0
  // Don't call GetLineScrollAmount unless we actually need it. Not only
3451
0
  // does this save time, but it's not safe to call GetLineScrollAmount
3452
0
  // during reflow (because it depends on font size inflation and doesn't
3453
0
  // use the in-reflow-safe font-size inflation path). If we did call it,
3454
0
  // it would assert and possible give the wrong result.
3455
0
  if (aVertical.mWhenToScroll == nsIPresShell::SCROLL_IF_NOT_VISIBLE ||
3456
0
      aHorizontal.mWhenToScroll == nsIPresShell::SCROLL_IF_NOT_VISIBLE) {
3457
0
    lineSize = aFrameAsScrollable->GetLineScrollAmount();
3458
0
  }
3459
0
  ScrollStyles ss = aFrameAsScrollable->GetScrollStyles();
3460
0
  nsRect allowedRange(scrollPt, nsSize(0, 0));
3461
0
  bool needToScroll = false;
3462
0
  uint32_t directions = aFrameAsScrollable->GetPerceivedScrollingDirections();
3463
0
3464
0
  if (((aFlags & nsIPresShell::SCROLL_OVERFLOW_HIDDEN) ||
3465
0
       ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN) &&
3466
0
      (!aVertical.mOnlyIfPerceivedScrollableDirection ||
3467
0
       (directions & nsIScrollableFrame::VERTICAL))) {
3468
0
3469
0
    if (ComputeNeedToScroll(aVertical.mWhenToScroll,
3470
0
                            lineSize.height,
3471
0
                            aRect.y,
3472
0
                            aRect.YMost(),
3473
0
                            visibleRect.y,
3474
0
                            visibleRect.YMost())) {
3475
0
      nscoord maxHeight;
3476
0
      scrollPt.y = ComputeWhereToScroll(aVertical.mWhereToScroll,
3477
0
                                        scrollPt.y,
3478
0
                                        aRect.y,
3479
0
                                        aRect.YMost(),
3480
0
                                        visibleRect.y,
3481
0
                                        visibleRect.YMost(),
3482
0
                                        &allowedRange.y, &maxHeight);
3483
0
      allowedRange.height = maxHeight - allowedRange.y;
3484
0
      needToScroll = true;
3485
0
    }
3486
0
  }
3487
0
3488
0
  if (((aFlags & nsIPresShell::SCROLL_OVERFLOW_HIDDEN) ||
3489
0
       ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN) &&
3490
0
      (!aHorizontal.mOnlyIfPerceivedScrollableDirection ||
3491
0
       (directions & nsIScrollableFrame::HORIZONTAL))) {
3492
0
3493
0
    if (ComputeNeedToScroll(aHorizontal.mWhenToScroll,
3494
0
                            lineSize.width,
3495
0
                            aRect.x,
3496
0
                            aRect.XMost(),
3497
0
                            visibleRect.x,
3498
0
                            visibleRect.XMost())) {
3499
0
      nscoord maxWidth;
3500
0
      scrollPt.x = ComputeWhereToScroll(aHorizontal.mWhereToScroll,
3501
0
                                        scrollPt.x,
3502
0
                                        aRect.x,
3503
0
                                        aRect.XMost(),
3504
0
                                        visibleRect.x,
3505
0
                                        visibleRect.XMost(),
3506
0
                                        &allowedRange.x, &maxWidth);
3507
0
      allowedRange.width = maxWidth - allowedRange.x;
3508
0
      needToScroll = true;
3509
0
    }
3510
0
  }
3511
0
3512
0
  // If we don't need to scroll, then don't try since it might cancel
3513
0
  // a current smooth scroll operation.
3514
0
  if (needToScroll) {
3515
0
    nsIScrollableFrame::ScrollMode scrollMode = nsIScrollableFrame::INSTANT;
3516
0
    bool autoBehaviorIsSmooth = (aFrameAsScrollable->GetScrollStyles().mScrollBehavior
3517
0
                                  == NS_STYLE_SCROLL_BEHAVIOR_SMOOTH);
3518
0
    bool smoothScroll = (aFlags & nsIPresShell::SCROLL_SMOOTH) ||
3519
0
                          ((aFlags & nsIPresShell::SCROLL_SMOOTH_AUTO) && autoBehaviorIsSmooth);
3520
0
    if (gfxPrefs::ScrollBehaviorEnabled() && smoothScroll) {
3521
0
      scrollMode = nsIScrollableFrame::SMOOTH_MSD;
3522
0
    }
3523
0
    aFrameAsScrollable->ScrollTo(scrollPt, scrollMode, &allowedRange);
3524
0
  }
3525
0
}
3526
3527
nsresult
3528
PresShell::ScrollContentIntoView(nsIContent*              aContent,
3529
                                 nsIPresShell::ScrollAxis aVertical,
3530
                                 nsIPresShell::ScrollAxis aHorizontal,
3531
                                 uint32_t                 aFlags)
3532
0
{
3533
0
  NS_ENSURE_TRUE(aContent, NS_ERROR_NULL_POINTER);
3534
0
  nsCOMPtr<nsIDocument> composedDoc = aContent->GetComposedDoc();
3535
0
  NS_ENSURE_STATE(composedDoc);
3536
0
3537
0
  NS_ASSERTION(mDidInitialize, "should have done initial reflow by now");
3538
0
3539
0
  if (mContentToScrollTo) {
3540
0
    mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
3541
0
  }
3542
0
  mContentToScrollTo = aContent;
3543
0
  ScrollIntoViewData* data = new ScrollIntoViewData();
3544
0
  data->mContentScrollVAxis = aVertical;
3545
0
  data->mContentScrollHAxis = aHorizontal;
3546
0
  data->mContentToScrollToFlags = aFlags;
3547
0
  if (NS_FAILED(mContentToScrollTo->SetProperty(nsGkAtoms::scrolling, data,
3548
0
                                                nsINode::DeleteProperty<PresShell::ScrollIntoViewData>))) {
3549
0
    mContentToScrollTo = nullptr;
3550
0
  }
3551
0
3552
0
  // Flush layout and attempt to scroll in the process.
3553
0
  if (nsIPresShell* shell = composedDoc->GetShell()) {
3554
0
    shell->SetNeedLayoutFlush();
3555
0
  }
3556
0
  composedDoc->FlushPendingNotifications(FlushType::InterruptibleLayout);
3557
0
3558
0
  // If mContentToScrollTo is non-null, that means we interrupted the reflow
3559
0
  // (or suppressed it altogether because we're suppressing interruptible
3560
0
  // flushes right now) and won't necessarily get the position correct, but do
3561
0
  // a best-effort scroll here.  The other option would be to do this inside
3562
0
  // FlushPendingNotifications, but I'm not sure the repeated scrolling that
3563
0
  // could trigger if reflows keep getting interrupted would be more desirable
3564
0
  // than a single best-effort scroll followed by one final scroll on the first
3565
0
  // completed reflow.
3566
0
  if (mContentToScrollTo) {
3567
0
    DoScrollContentIntoView();
3568
0
  }
3569
0
  return NS_OK;
3570
0
}
3571
3572
void
3573
PresShell::DoScrollContentIntoView()
3574
0
{
3575
0
  NS_ASSERTION(mDidInitialize, "should have done initial reflow by now");
3576
0
3577
0
  nsIFrame* frame = mContentToScrollTo->GetPrimaryFrame();
3578
0
  if (!frame) {
3579
0
    mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
3580
0
    mContentToScrollTo = nullptr;
3581
0
    return;
3582
0
  }
3583
0
3584
0
  if (frame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
3585
0
    // The reflow flush before this scroll got interrupted, and this frame's
3586
0
    // coords and size are all zero, and it has no content showing anyway.
3587
0
    // Don't bother scrolling to it.  We'll try again when we finish up layout.
3588
0
    return;
3589
0
  }
3590
0
3591
0
  // Make sure we skip 'frame' ... if it's scrollable, we should use its
3592
0
  // scrollable ancestor as the container.
3593
0
  nsIFrame* container = nsLayoutUtils::GetClosestFrameOfType(
3594
0
    frame->GetParent(), LayoutFrameType::Scroll);
3595
0
  if (!container) {
3596
0
    // nothing can be scrolled
3597
0
    return;
3598
0
  }
3599
0
3600
0
  ScrollIntoViewData* data = static_cast<ScrollIntoViewData*>(
3601
0
    mContentToScrollTo->GetProperty(nsGkAtoms::scrolling));
3602
0
  if (MOZ_UNLIKELY(!data)) {
3603
0
    mContentToScrollTo = nullptr;
3604
0
    return;
3605
0
  }
3606
0
3607
0
  // This is a two-step process.
3608
0
  // Step 1: Find the bounds of the rect we want to scroll into view.  For
3609
0
  //         example, for an inline frame we may want to scroll in the whole
3610
0
  //         line, or we may want to scroll multiple lines into view.
3611
0
  // Step 2: Walk container frame and its ancestors and scroll them
3612
0
  //         appropriately.
3613
0
  // frameBounds is relative to container. We're assuming
3614
0
  // that scrollframes don't split so every continuation of frame will
3615
0
  // be a descendant of container. (Things would still mostly work
3616
0
  // even if that assumption was false.)
3617
0
  nsRect frameBounds;
3618
0
  bool haveRect = false;
3619
0
  bool useWholeLineHeightForInlines =
3620
0
    data->mContentScrollVAxis.mWhenToScroll != nsIPresShell::SCROLL_IF_NOT_FULLY_VISIBLE;
3621
0
  // Reuse the same line iterator across calls to AccumulateFrameBounds.  We set
3622
0
  // it every time we detect a new block (stored in prevBlock).
3623
0
  nsIFrame* prevBlock = nullptr;
3624
0
  nsAutoLineIterator lines;
3625
0
  // The last line we found a continuation on in |lines|.  We assume that later
3626
0
  // continuations cannot come on earlier lines.
3627
0
  int32_t curLine = 0;
3628
0
  do {
3629
0
    AccumulateFrameBounds(container, frame, useWholeLineHeightForInlines,
3630
0
                          frameBounds, haveRect, prevBlock, lines, curLine);
3631
0
  } while ((frame = frame->GetNextContinuation()));
3632
0
3633
0
  ScrollFrameRectIntoView(container, frameBounds, data->mContentScrollVAxis,
3634
0
                          data->mContentScrollHAxis,
3635
0
                          data->mContentToScrollToFlags);
3636
0
}
3637
3638
bool
3639
PresShell::ScrollFrameRectIntoView(nsIFrame*                aFrame,
3640
                                   const nsRect&            aRect,
3641
                                   nsIPresShell::ScrollAxis aVertical,
3642
                                   nsIPresShell::ScrollAxis aHorizontal,
3643
                                   uint32_t                 aFlags)
3644
0
{
3645
0
  bool didScroll = false;
3646
0
  // This function needs to work even if rect has a width or height of 0.
3647
0
  nsRect rect = aRect;
3648
0
  nsIFrame* container = aFrame;
3649
0
  // Walk up the frame hierarchy scrolling the rect into view and
3650
0
  // keeping rect relative to container
3651
0
  do {
3652
0
    nsIScrollableFrame* sf = do_QueryFrame(container);
3653
0
    if (sf) {
3654
0
      nsPoint oldPosition = sf->GetScrollPosition();
3655
0
      nsRect targetRect = rect;
3656
0
      // Inflate the scrolled rect by the container's padding in each dimension,
3657
0
      // unless we have 'overflow-clip-box-*: content-box' in that dimension.
3658
0
      auto* disp = container->StyleDisplay();
3659
0
      if (disp->mOverflowClipBoxBlock ==
3660
0
            NS_STYLE_OVERFLOW_CLIP_BOX_CONTENT_BOX ||
3661
0
          disp->mOverflowClipBoxInline ==
3662
0
            NS_STYLE_OVERFLOW_CLIP_BOX_CONTENT_BOX) {
3663
0
        WritingMode wm = container->GetWritingMode();
3664
0
        bool cbH = (wm.IsVertical() ? disp->mOverflowClipBoxBlock
3665
0
                                    : disp->mOverflowClipBoxInline) ==
3666
0
                   NS_STYLE_OVERFLOW_CLIP_BOX_CONTENT_BOX;
3667
0
        bool cbV = (wm.IsVertical() ? disp->mOverflowClipBoxInline
3668
0
                                    : disp->mOverflowClipBoxBlock) ==
3669
0
                   NS_STYLE_OVERFLOW_CLIP_BOX_CONTENT_BOX;
3670
0
        nsMargin padding = container->GetUsedPadding();
3671
0
        if (!cbH) {
3672
0
          padding.left = padding.right = nscoord(0);
3673
0
        }
3674
0
        if (!cbV) {
3675
0
          padding.top = padding.bottom = nscoord(0);
3676
0
        }
3677
0
        targetRect.Inflate(padding);
3678
0
      }
3679
0
      ScrollToShowRect(sf, targetRect - sf->GetScrolledFrame()->GetPosition(),
3680
0
                       aVertical, aHorizontal, aFlags);
3681
0
      nsPoint newPosition = sf->LastScrollDestination();
3682
0
      // If the scroll position increased, that means our content moved up,
3683
0
      // so our rect's offset should decrease
3684
0
      rect += oldPosition - newPosition;
3685
0
3686
0
      if (oldPosition != newPosition) {
3687
0
        didScroll = true;
3688
0
      }
3689
0
3690
0
      // only scroll one container when this flag is set
3691
0
      if (aFlags & nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY) {
3692
0
        break;
3693
0
      }
3694
0
    }
3695
0
    nsIFrame* parent;
3696
0
    if (container->IsTransformed()) {
3697
0
      container->GetTransformMatrix(nullptr, &parent);
3698
0
      rect = nsLayoutUtils::TransformFrameRectToAncestor(container, rect, parent);
3699
0
    } else {
3700
0
      rect += container->GetPosition();
3701
0
      parent = container->GetParent();
3702
0
    }
3703
0
    if (!parent && !(aFlags & nsIPresShell::SCROLL_NO_PARENT_FRAMES)) {
3704
0
      nsPoint extraOffset(0,0);
3705
0
      parent = nsLayoutUtils::GetCrossDocParentFrame(container, &extraOffset);
3706
0
      if (parent) {
3707
0
        int32_t APD = container->PresContext()->AppUnitsPerDevPixel();
3708
0
        int32_t parentAPD = parent->PresContext()->AppUnitsPerDevPixel();
3709
0
        rect = rect.ScaleToOtherAppUnitsRoundOut(APD, parentAPD);
3710
0
        rect += extraOffset;
3711
0
      }
3712
0
    }
3713
0
    container = parent;
3714
0
  } while (container);
3715
0
3716
0
  return didScroll;
3717
0
}
3718
3719
nsRectVisibility
3720
PresShell::GetRectVisibility(nsIFrame* aFrame,
3721
                             const nsRect &aRect,
3722
                             nscoord aMinTwips) const
3723
0
{
3724
0
  NS_ASSERTION(aFrame->PresContext() == GetPresContext(),
3725
0
               "prescontext mismatch?");
3726
0
  nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
3727
0
  NS_ASSERTION(rootFrame,
3728
0
               "How can someone have a frame for this presshell when there's no root?");
3729
0
  nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable();
3730
0
  nsRect scrollPortRect;
3731
0
  if (sf) {
3732
0
    scrollPortRect = sf->GetScrollPortRect();
3733
0
    nsIFrame* f = do_QueryFrame(sf);
3734
0
    scrollPortRect += f->GetOffsetTo(rootFrame);
3735
0
  } else {
3736
0
    scrollPortRect = nsRect(nsPoint(0,0), rootFrame->GetSize());
3737
0
  }
3738
0
3739
0
  // scrollPortRect has the viewport visible area relative to rootFrame.
3740
0
  nsRect visibleAreaRect(scrollPortRect);
3741
0
  // Find the intersection of this and the frame's ancestor scrollable
3742
0
  // frames. We walk the whole ancestor chain to find all the scrollable
3743
0
  // frames.
3744
0
  nsIScrollableFrame* scrollAncestorFrame =
3745
0
    nsLayoutUtils::GetNearestScrollableFrame(aFrame,
3746
0
      nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
3747
0
  while (scrollAncestorFrame) {
3748
0
    nsRect scrollAncestorRect = scrollAncestorFrame->GetScrollPortRect();
3749
0
    nsIFrame* f = do_QueryFrame(scrollAncestorFrame);
3750
0
    scrollAncestorRect += f->GetOffsetTo(rootFrame);
3751
0
3752
0
    visibleAreaRect = visibleAreaRect.Intersect(scrollAncestorRect);
3753
0
3754
0
    // Continue up the chain.
3755
0
    scrollAncestorFrame =
3756
0
      nsLayoutUtils::GetNearestScrollableFrame(f->GetParent(),
3757
0
        nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
3758
0
  }
3759
0
3760
0
  // aRect is in the aFrame coordinate space, so bring it into rootFrame
3761
0
  // coordinate space.
3762
0
  nsRect r = aRect + aFrame->GetOffsetTo(rootFrame);
3763
0
  // If aRect is entirely visible then we don't need to ensure that
3764
0
  // at least aMinTwips of it is visible
3765
0
  if (visibleAreaRect.Contains(r)) {
3766
0
    return nsRectVisibility_kVisible;
3767
0
  }
3768
0
3769
0
  nsRect insetRect = visibleAreaRect;
3770
0
  insetRect.Deflate(aMinTwips, aMinTwips);
3771
0
  if (r.YMost() <= insetRect.y)
3772
0
    return nsRectVisibility_kAboveViewport;
3773
0
  if (r.y >= insetRect.YMost())
3774
0
    return nsRectVisibility_kBelowViewport;
3775
0
  if (r.XMost() <= insetRect.x)
3776
0
    return nsRectVisibility_kLeftOfViewport;
3777
0
  if (r.x >= insetRect.XMost())
3778
0
    return nsRectVisibility_kRightOfViewport;
3779
0
3780
0
  return nsRectVisibility_kVisible;
3781
0
}
3782
3783
void
3784
PresShell::ScheduleViewManagerFlush(PaintType aType)
3785
0
{
3786
0
  if (MOZ_UNLIKELY(mIsDestroying)) {
3787
0
    return;
3788
0
  }
3789
0
3790
0
  if (aType == PAINT_DELAYED_COMPRESS) {
3791
0
    // Delay paint for 1 second.
3792
0
    static const uint32_t kPaintDelayPeriod = 1000;
3793
0
    if (!mDelayedPaintTimer) {
3794
0
      nsTimerCallbackFunc
3795
0
        PaintTimerCallBack = [](nsITimer* aTimer, void* aClosure) {
3796
0
          // The passed-in PresShell is always alive here. Because if PresShell
3797
0
          // died, mDelayedPaintTimer->Cancel() would be called during the
3798
0
          // destruction and this callback would never be invoked.
3799
0
          auto self = static_cast<PresShell*>(aClosure);
3800
0
          self->SetNextPaintCompressed();
3801
0
          self->ScheduleViewManagerFlush();
3802
0
      };
3803
0
3804
0
      NS_NewTimerWithFuncCallback(getter_AddRefs(mDelayedPaintTimer),
3805
0
                                  PaintTimerCallBack,
3806
0
                                  this,
3807
0
                                  kPaintDelayPeriod,
3808
0
                                  nsITimer::TYPE_ONE_SHOT,
3809
0
                                  "PaintTimerCallBack",
3810
0
                                  mDocument->EventTargetFor(TaskCategory::Other));
3811
0
    }
3812
0
    return;
3813
0
  }
3814
0
3815
0
  nsPresContext* presContext = GetPresContext();
3816
0
  if (presContext) {
3817
0
    presContext->RefreshDriver()->ScheduleViewManagerFlush();
3818
0
  }
3819
0
  SetNeedLayoutFlush();
3820
0
}
3821
3822
void
3823
nsIPresShell::DispatchSynthMouseMove(WidgetGUIEvent* aEvent)
3824
0
{
3825
0
  AUTO_PROFILER_TRACING("Paint", "DispatchSynthMouseMove");
3826
0
  nsEventStatus status = nsEventStatus_eIgnore;
3827
0
  nsView* targetView = nsView::GetViewFor(aEvent->mWidget);
3828
0
  if (!targetView)
3829
0
    return;
3830
0
  targetView->GetViewManager()->DispatchEvent(aEvent, targetView, &status);
3831
0
}
3832
3833
void
3834
PresShell::ClearMouseCaptureOnView(nsView* aView)
3835
0
{
3836
0
  if (gCaptureInfo.mContent) {
3837
0
    if (aView) {
3838
0
      // if a view was specified, ensure that the captured content is within
3839
0
      // this view.
3840
0
      nsIFrame* frame = gCaptureInfo.mContent->GetPrimaryFrame();
3841
0
      if (frame) {
3842
0
        nsView* view = frame->GetClosestView();
3843
0
        // if there is no view, capturing won't be handled any more, so
3844
0
        // just release the capture.
3845
0
        if (view) {
3846
0
          do {
3847
0
            if (view == aView) {
3848
0
              gCaptureInfo.mContent = nullptr;
3849
0
              // the view containing the captured content likely disappeared so
3850
0
              // disable capture for now.
3851
0
              gCaptureInfo.mAllowed = false;
3852
0
              break;
3853
0
            }
3854
0
3855
0
            view = view->GetParent();
3856
0
          } while (view);
3857
0
          // return if the view wasn't found
3858
0
          return;
3859
0
        }
3860
0
      }
3861
0
    }
3862
0
3863
0
    gCaptureInfo.mContent = nullptr;
3864
0
  }
3865
0
3866
0
  // disable mouse capture until the next mousedown as a dialog has opened
3867
0
  // or a drag has started. Otherwise, someone could start capture during
3868
0
  // the modal dialog or drag.
3869
0
  gCaptureInfo.mAllowed = false;
3870
0
}
3871
3872
void
3873
nsIPresShell::ClearMouseCapture(nsIFrame* aFrame)
3874
0
{
3875
0
  if (!gCaptureInfo.mContent) {
3876
0
    gCaptureInfo.mAllowed = false;
3877
0
    return;
3878
0
  }
3879
0
3880
0
  // null frame argument means clear the capture
3881
0
  if (!aFrame) {
3882
0
    gCaptureInfo.mContent = nullptr;
3883
0
    gCaptureInfo.mAllowed = false;
3884
0
    return;
3885
0
  }
3886
0
3887
0
  nsIFrame* capturingFrame = gCaptureInfo.mContent->GetPrimaryFrame();
3888
0
  if (!capturingFrame) {
3889
0
    gCaptureInfo.mContent = nullptr;
3890
0
    gCaptureInfo.mAllowed = false;
3891
0
    return;
3892
0
  }
3893
0
3894
0
  if (nsLayoutUtils::IsAncestorFrameCrossDoc(aFrame, capturingFrame)) {
3895
0
    gCaptureInfo.mContent = nullptr;
3896
0
    gCaptureInfo.mAllowed = false;
3897
0
  }
3898
0
}
3899
3900
nsresult
3901
PresShell::CaptureHistoryState(nsILayoutHistoryState** aState)
3902
0
{
3903
0
  MOZ_ASSERT(nullptr != aState, "null state pointer");
3904
0
3905
0
  // We actually have to mess with the docshell here, since we want to
3906
0
  // store the state back in it.
3907
0
  // XXXbz this isn't really right, since this is being called in the
3908
0
  // content viewer's Hide() method...  by that point the docshell's
3909
0
  // state could be wrong.  We should sort out a better ownership
3910
0
  // model for the layout history state.
3911
0
  nsCOMPtr<nsIDocShell> docShell(mPresContext->GetDocShell());
3912
0
  if (!docShell)
3913
0
    return NS_ERROR_FAILURE;
3914
0
3915
0
  nsCOMPtr<nsILayoutHistoryState> historyState;
3916
0
  docShell->GetLayoutHistoryState(getter_AddRefs(historyState));
3917
0
  if (!historyState) {
3918
0
    // Create the document state object
3919
0
    historyState = NS_NewLayoutHistoryState();
3920
0
    docShell->SetLayoutHistoryState(historyState);
3921
0
  }
3922
0
3923
0
  *aState = historyState;
3924
0
  NS_IF_ADDREF(*aState);
3925
0
3926
0
  // Capture frame state for the entire frame hierarchy
3927
0
  nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
3928
0
  if (!rootFrame) return NS_OK;
3929
0
3930
0
  mFrameConstructor->CaptureFrameState(rootFrame, historyState);
3931
0
3932
0
  return NS_OK;
3933
0
}
3934
3935
void
3936
PresShell::ScheduleBeforeFirstPaint()
3937
0
{
3938
0
  if (!mDocument->IsResourceDoc()) {
3939
0
    // Notify observers that a new page is about to be drawn. Execute this
3940
0
    // as soon as it is safe to run JS, which is guaranteed to be before we
3941
0
    // go back to the event loop and actually draw the page.
3942
0
    MOZ_LOG(gLog, LogLevel::Debug,
3943
0
           ("PresShell::ScheduleBeforeFirstPaint this=%p", this));
3944
0
3945
0
    nsContentUtils::AddScriptRunner(new nsBeforeFirstPaintDispatcher(mDocument));
3946
0
  }
3947
0
}
3948
3949
void
3950
PresShell::UnsuppressAndInvalidate()
3951
0
{
3952
0
  // Note: We ignore the EnsureVisible check for resource documents, because
3953
0
  // they won't have a docshell, so they'll always fail EnsureVisible.
3954
0
  if ((!mDocument->IsResourceDoc() && !mPresContext->EnsureVisible()) ||
3955
0
      mHaveShutDown) {
3956
0
    // No point; we're about to be torn down anyway.
3957
0
    return;
3958
0
  }
3959
0
3960
0
  ScheduleBeforeFirstPaint();
3961
0
3962
0
  mPaintingSuppressed = false;
3963
0
  nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
3964
0
  if (rootFrame) {
3965
0
    // let's assume that outline on a root frame is not supported
3966
0
    rootFrame->InvalidateFrame();
3967
0
  }
3968
0
3969
0
  // now that painting is unsuppressed, focus may be set on the document
3970
0
  if (nsPIDOMWindowOuter* win = mDocument->GetWindow())
3971
0
    win->SetReadyForFocus();
3972
0
3973
0
  if (!mHaveShutDown) {
3974
0
    SynthesizeMouseMove(false);
3975
0
    ScheduleApproximateFrameVisibilityUpdateNow();
3976
0
  }
3977
0
}
3978
3979
void
3980
PresShell::UnsuppressPainting()
3981
0
{
3982
0
  if (mPaintSuppressionTimer) {
3983
0
    mPaintSuppressionTimer->Cancel();
3984
0
    mPaintSuppressionTimer = nullptr;
3985
0
  }
3986
0
3987
0
  if (mIsDocumentGone || !mPaintingSuppressed)
3988
0
    return;
3989
0
3990
0
  // If we have reflows pending, just wait until we process
3991
0
  // the reflows and get all the frames where we want them
3992
0
  // before actually unlocking the painting.  Otherwise
3993
0
  // go ahead and unlock now.
3994
0
  if (!mDirtyRoots.IsEmpty())
3995
0
    mShouldUnsuppressPainting = true;
3996
0
  else
3997
0
    UnsuppressAndInvalidate();
3998
0
}
3999
4000
// Post a request to handle an arbitrary callback after reflow has finished.
4001
nsresult
4002
PresShell::PostReflowCallback(nsIReflowCallback* aCallback)
4003
0
{
4004
0
  void* result = AllocateByObjectID(eArenaObjectID_nsCallbackEventRequest,
4005
0
                                    sizeof(nsCallbackEventRequest));
4006
0
  nsCallbackEventRequest* request = (nsCallbackEventRequest*)result;
4007
0
4008
0
  request->callback = aCallback;
4009
0
  request->next = nullptr;
4010
0
4011
0
  if (mLastCallbackEventRequest) {
4012
0
    mLastCallbackEventRequest = mLastCallbackEventRequest->next = request;
4013
0
  } else {
4014
0
    mFirstCallbackEventRequest = request;
4015
0
    mLastCallbackEventRequest = request;
4016
0
  }
4017
0
4018
0
  return NS_OK;
4019
0
}
4020
4021
void
4022
PresShell::CancelReflowCallback(nsIReflowCallback* aCallback)
4023
0
{
4024
0
   nsCallbackEventRequest* before = nullptr;
4025
0
   nsCallbackEventRequest* node = mFirstCallbackEventRequest;
4026
0
   while(node)
4027
0
   {
4028
0
      nsIReflowCallback* callback = node->callback;
4029
0
4030
0
      if (callback == aCallback)
4031
0
      {
4032
0
        nsCallbackEventRequest* toFree = node;
4033
0
        if (node == mFirstCallbackEventRequest) {
4034
0
          node = node->next;
4035
0
          mFirstCallbackEventRequest = node;
4036
0
          NS_ASSERTION(before == nullptr, "impossible");
4037
0
        } else {
4038
0
          node = node->next;
4039
0
          before->next = node;
4040
0
        }
4041
0
4042
0
        if (toFree == mLastCallbackEventRequest) {
4043
0
          mLastCallbackEventRequest = before;
4044
0
        }
4045
0
4046
0
        FreeByObjectID(eArenaObjectID_nsCallbackEventRequest, toFree);
4047
0
      } else {
4048
0
        before = node;
4049
0
        node = node->next;
4050
0
      }
4051
0
   }
4052
0
}
4053
4054
void
4055
PresShell::CancelPostedReflowCallbacks()
4056
0
{
4057
0
  while (mFirstCallbackEventRequest) {
4058
0
    nsCallbackEventRequest* node = mFirstCallbackEventRequest;
4059
0
    mFirstCallbackEventRequest = node->next;
4060
0
    if (!mFirstCallbackEventRequest) {
4061
0
      mLastCallbackEventRequest = nullptr;
4062
0
    }
4063
0
    nsIReflowCallback* callback = node->callback;
4064
0
    FreeByObjectID(eArenaObjectID_nsCallbackEventRequest, node);
4065
0
    if (callback) {
4066
0
      callback->ReflowCallbackCanceled();
4067
0
    }
4068
0
  }
4069
0
}
4070
4071
void
4072
PresShell::HandlePostedReflowCallbacks(bool aInterruptible)
4073
0
{
4074
0
   bool shouldFlush = false;
4075
0
4076
0
   while (mFirstCallbackEventRequest) {
4077
0
     nsCallbackEventRequest* node = mFirstCallbackEventRequest;
4078
0
     mFirstCallbackEventRequest = node->next;
4079
0
     if (!mFirstCallbackEventRequest) {
4080
0
       mLastCallbackEventRequest = nullptr;
4081
0
     }
4082
0
     nsIReflowCallback* callback = node->callback;
4083
0
     FreeByObjectID(eArenaObjectID_nsCallbackEventRequest, node);
4084
0
     if (callback) {
4085
0
       if (callback->ReflowFinished()) {
4086
0
         shouldFlush = true;
4087
0
       }
4088
0
     }
4089
0
   }
4090
0
4091
0
   FlushType flushType =
4092
0
     aInterruptible ? FlushType::InterruptibleLayout : FlushType::Layout;
4093
0
   if (shouldFlush && !mIsDestroying) {
4094
0
     FlushPendingNotifications(flushType);
4095
0
   }
4096
0
}
4097
4098
bool
4099
nsIPresShell::IsSafeToFlush() const
4100
0
{
4101
0
  // Not safe if we are getting torn down, reflowing, or in the middle of frame
4102
0
  // construction.
4103
0
  if (mIsReflowing || mChangeNestCount || mIsDestroying) {
4104
0
    return false;
4105
0
  }
4106
0
4107
0
    // Not safe if we are painting
4108
0
  if (nsViewManager* viewManager = GetViewManager()) {
4109
0
    bool isPainting = false;
4110
0
    viewManager->IsPainting(isPainting);
4111
0
    if (isPainting) {
4112
0
      return false;
4113
0
    }
4114
0
  }
4115
0
4116
0
  return true;
4117
0
}
4118
4119
void
4120
nsIPresShell::NotifyFontFaceSetOnRefresh()
4121
0
{
4122
0
  if (FontFaceSet* set = mDocument->GetFonts()) {
4123
0
    set->DidRefresh();
4124
0
  }
4125
0
}
4126
4127
void
4128
PresShell::DoFlushPendingNotifications(FlushType aType)
4129
0
{
4130
0
  // by default, flush animations if aType >= FlushType::Style
4131
0
  mozilla::ChangesToFlush flush(aType, aType >= FlushType::Style);
4132
0
  FlushPendingNotifications(flush);
4133
0
}
4134
4135
#ifdef DEBUG
4136
static void
4137
AssertFrameSubtreeIsSane(const nsIFrame& aRoot)
4138
{
4139
  if (const nsIContent* content = aRoot.GetContent()) {
4140
    MOZ_ASSERT(content->GetFlattenedTreeParentNodeForStyle(),
4141
               "Node not in the flattened tree still has a frame?");
4142
  }
4143
4144
  nsIFrame::ChildListIterator childLists(&aRoot);
4145
  for (; !childLists.IsDone(); childLists.Next()) {
4146
    for (const nsIFrame* child : childLists.CurrentList()) {
4147
      AssertFrameSubtreeIsSane(*child);
4148
    }
4149
  }
4150
}
4151
#endif
4152
4153
static inline void
4154
AssertFrameTreeIsSane(const nsIPresShell& aShell)
4155
0
{
4156
#ifdef DEBUG
4157
  if (const nsIFrame* root = aShell.GetRootFrame()) {
4158
    AssertFrameSubtreeIsSane(*root);
4159
  }
4160
#endif
4161
}
4162
4163
void
4164
PresShell::DoFlushPendingNotifications(mozilla::ChangesToFlush aFlush)
4165
0
{
4166
0
  // Per our API contract, hold a strong ref to ourselves until we return.
4167
0
  nsCOMPtr<nsIPresShell> kungFuDeathGrip = this;
4168
0
4169
0
  /**
4170
0
   * VERY IMPORTANT: If you add some sort of new flushing to this
4171
0
   * method, make sure to add the relevant SetNeedLayoutFlush or
4172
0
   * SetNeedStyleFlush calls on the shell.
4173
0
   */
4174
0
  FlushType flushType = aFlush.mFlushType;
4175
0
4176
0
  MOZ_ASSERT(NeedFlush(flushType), "Why did we get called?");
4177
0
4178
0
#ifdef MOZ_GECKO_PROFILER
4179
0
  static const EnumeratedArray<FlushType,
4180
0
                               FlushType::Count,
4181
0
                               const char*> flushTypeNames = {
4182
0
    "",
4183
0
    "Event",
4184
0
    "Content",
4185
0
    "ContentAndNotify",
4186
0
    // As far as the profiler is concerned, EnsurePresShellInitAndFrames and
4187
0
    // Frames are the same
4188
0
    "Style",
4189
0
    "Style",
4190
0
    "InterruptibleLayout",
4191
0
    "Layout",
4192
0
    "Display"
4193
0
  };
4194
0
  AUTO_PROFILER_LABEL_DYNAMIC_CSTR("PresShell::DoFlushPendingNotifications",
4195
0
                                   LAYOUT, flushTypeNames[flushType]);
4196
0
#endif
4197
0
4198
0
4199
0
#ifdef ACCESSIBILITY
4200
#ifdef DEBUG
4201
  if (nsAccessibilityService* accService = GetAccService()) {
4202
    NS_ASSERTION(!accService->IsProcessingRefreshDriverNotification(),
4203
                 "Flush during accessible tree update!");
4204
  }
4205
#endif
4206
#endif
4207
0
4208
0
  NS_ASSERTION(flushType >= FlushType::Frames, "Why did we get called?");
4209
0
4210
0
  mNeedStyleFlush = false;
4211
0
  mNeedThrottledAnimationFlush =
4212
0
    mNeedThrottledAnimationFlush && !aFlush.mFlushAnimations;
4213
0
  mNeedLayoutFlush =
4214
0
    mNeedLayoutFlush && (flushType < FlushType::InterruptibleLayout);
4215
0
4216
0
  bool isSafeToFlush = IsSafeToFlush();
4217
0
4218
0
  // If layout could possibly trigger scripts, then it's only safe to flush if
4219
0
  // it's safe to run script.
4220
0
  bool hasHadScriptObject;
4221
0
  if (mDocument->GetScriptHandlingObject(hasHadScriptObject) ||
4222
0
      hasHadScriptObject) {
4223
0
    isSafeToFlush = isSafeToFlush && nsContentUtils::IsSafeToRunScript();
4224
0
  }
4225
0
4226
0
  MOZ_DIAGNOSTIC_ASSERT(!mIsDestroying || !isSafeToFlush);
4227
0
  MOZ_DIAGNOSTIC_ASSERT(mIsDestroying || mViewManager);
4228
0
  MOZ_DIAGNOSTIC_ASSERT(mIsDestroying || mDocument->HasShellOrBFCacheEntry());
4229
0
  MOZ_DIAGNOSTIC_ASSERT(mIsDestroying || mDocument->GetShell() == this);
4230
0
4231
0
  // Make sure the view manager stays alive.
4232
0
  RefPtr<nsViewManager> viewManager = mViewManager;
4233
0
  bool didStyleFlush = false;
4234
0
  bool didLayoutFlush = false;
4235
0
  if (isSafeToFlush) {
4236
0
    // Record that we are in a flush, so that our optimization in
4237
0
    // nsDocument::FlushPendingNotifications doesn't skip any re-entrant
4238
0
    // calls to us.  Otherwise, we might miss some needed flushes, since
4239
0
    // we clear mNeedStyleFlush / mNeedLayoutFlush here at the top of
4240
0
    // the function but we might not have done the work yet.
4241
0
    AutoRestore<bool> guard(mInFlush);
4242
0
    mInFlush = true;
4243
0
4244
0
    // We need to make sure external resource documents are flushed too (for
4245
0
    // example, svg filters that reference a filter in an external document
4246
0
    // need the frames in the external document to be constructed for the
4247
0
    // filter to work). We only need external resources to be flushed when the
4248
0
    // main document is flushing >= FlushType::Frames, so we flush external
4249
0
    // resources here instead of nsDocument::FlushPendingNotifications.
4250
0
    mDocument->FlushExternalResources(flushType);
4251
0
4252
0
    // Force flushing of any pending content notifications that might have
4253
0
    // queued up while our event was pending.  That will ensure that we don't
4254
0
    // construct frames for content right now that's still waiting to be
4255
0
    // notified on,
4256
0
    mDocument->FlushPendingNotifications(FlushType::ContentAndNotify);
4257
0
4258
0
    mDocument->UpdateSVGUseElementShadowTrees();
4259
0
4260
0
    // Process pending restyles, since any flush of the presshell wants
4261
0
    // up-to-date style data.
4262
0
    if (MOZ_LIKELY(!mIsDestroying)) {
4263
0
      viewManager->FlushDelayedResize(false);
4264
0
      mPresContext->FlushPendingMediaFeatureValuesChanged();
4265
0
    }
4266
0
4267
0
    if (MOZ_LIKELY(!mIsDestroying)) {
4268
0
      // Now that we have flushed media queries, update the rules before looking
4269
0
      // up @font-face / @counter-style / @font-feature-values rules.
4270
0
      mStyleSet->UpdateStylistIfNeeded();
4271
0
4272
0
      // Flush any pending update of the user font set, since that could
4273
0
      // cause style changes (for updating ex/ch units, and to cause a
4274
0
      // reflow).
4275
0
      mDocument->FlushUserFontSet();
4276
0
4277
0
      mPresContext->FlushCounterStyles();
4278
0
4279
0
      mPresContext->FlushFontFeatureValues();
4280
0
4281
0
      // Flush any requested SMIL samples.
4282
0
      if (mDocument->HasAnimationController()) {
4283
0
        mDocument->GetAnimationController()->FlushResampleRequests();
4284
0
      }
4285
0
4286
0
      if (aFlush.mFlushAnimations && mPresContext->EffectCompositor()) {
4287
0
        mPresContext->EffectCompositor()->PostRestyleForThrottledAnimations();
4288
0
      }
4289
0
    }
4290
0
4291
0
    // The FlushResampleRequests() above flushed style changes.
4292
0
    if (MOZ_LIKELY(!mIsDestroying)) {
4293
0
      nsAutoScriptBlocker scriptBlocker;
4294
0
#ifdef MOZ_GECKO_PROFILER
4295
0
      AutoProfilerStyleMarker tracingStyleFlush(std::move(mStyleCause));
4296
0
#endif
4297
0
4298
0
      mPresContext->RestyleManager()->ProcessPendingRestyles();
4299
0
    }
4300
0
4301
0
    // Process whatever XBL constructors those restyles queued up.  This
4302
0
    // ensures that onload doesn't fire too early and that we won't do extra
4303
0
    // reflows after those constructors run.
4304
0
    if (MOZ_LIKELY(!mIsDestroying)) {
4305
0
      mDocument->BindingManager()->ProcessAttachedQueue();
4306
0
    }
4307
0
4308
0
    // Now those constructors or events might have posted restyle
4309
0
    // events.  At the same time, we still need up-to-date style data.
4310
0
    // In particular, reflow depends on style being completely up to
4311
0
    // date.  If it's not, then style reparenting, which can
4312
0
    // happen during reflow, might suddenly pick up the new rules and
4313
0
    // we'll end up with frames whose style doesn't match the frame
4314
0
    // type.
4315
0
    if (MOZ_LIKELY(!mIsDestroying)) {
4316
0
      nsAutoScriptBlocker scriptBlocker;
4317
0
#ifdef MOZ_GECKO_PROFILER
4318
0
      AutoProfilerStyleMarker tracingStyleFlush(std::move(mStyleCause));
4319
0
#endif
4320
0
4321
0
      mPresContext->RestyleManager()->ProcessPendingRestyles();
4322
0
      // Clear mNeedStyleFlush here agagin to make this flag work properly for
4323
0
      // optimization since the flag might have set in ProcessPendingRestyles().
4324
0
      mNeedStyleFlush = false;
4325
0
    }
4326
0
4327
0
    AssertFrameTreeIsSane(*this);
4328
0
4329
0
    didStyleFlush = true;
4330
0
4331
0
    // There might be more pending constructors now, but we're not going to
4332
0
    // worry about them.  They can't be triggered during reflow, so we should
4333
0
    // be good.
4334
0
4335
0
    if (flushType >= (SuppressInterruptibleReflows()
4336
0
                        ? FlushType::Layout
4337
0
                        : FlushType::InterruptibleLayout) &&
4338
0
        !mIsDestroying) {
4339
0
      didLayoutFlush = true;
4340
0
      mFrameConstructor->RecalcQuotesAndCounters();
4341
0
      viewManager->FlushDelayedResize(true);
4342
0
      if (ProcessReflowCommands(flushType < FlushType::Layout) &&
4343
0
          mContentToScrollTo) {
4344
0
        // We didn't get interrupted.  Go ahead and scroll to our content
4345
0
        DoScrollContentIntoView();
4346
0
        if (mContentToScrollTo) {
4347
0
          mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
4348
0
          mContentToScrollTo = nullptr;
4349
0
        }
4350
0
      }
4351
0
    }
4352
0
4353
0
    if (flushType >= FlushType::Layout) {
4354
0
      if (!mIsDestroying) {
4355
0
        viewManager->UpdateWidgetGeometry();
4356
0
      }
4357
0
    }
4358
0
  }
4359
0
4360
0
  if (!didStyleFlush && flushType >= FlushType::Style && !mIsDestroying) {
4361
0
    SetNeedStyleFlush();
4362
0
    if (aFlush.mFlushAnimations) {
4363
0
      SetNeedThrottledAnimationFlush();
4364
0
    }
4365
0
  }
4366
0
4367
0
  if (!didLayoutFlush && flushType >= FlushType::InterruptibleLayout &&
4368
0
      !mIsDestroying) {
4369
0
    // We suppressed this flush either due to it not being safe to flush,
4370
0
    // or due to SuppressInterruptibleReflows().  Either way, the
4371
0
    // mNeedLayoutFlush flag needs to be re-set.
4372
0
    SetNeedLayoutFlush();
4373
0
  }
4374
0
}
4375
4376
void
4377
PresShell::CharacterDataChanged(nsIContent* aContent,
4378
                                const CharacterDataChangeInfo& aInfo)
4379
0
{
4380
0
  MOZ_ASSERT(!mIsDocumentGone, "Unexpected CharacterDataChanged");
4381
0
  MOZ_ASSERT(aContent->OwnerDoc() == mDocument, "Unexpected document");
4382
0
4383
0
  nsAutoCauseReflowNotifier crNotifier(this);
4384
0
4385
0
  mPresContext->RestyleManager()->CharacterDataChanged(aContent, aInfo);
4386
0
  mFrameConstructor->CharacterDataChanged(aContent, aInfo);
4387
0
}
4388
4389
void
4390
PresShell::ContentStateChanged(nsIDocument* aDocument,
4391
                               nsIContent* aContent,
4392
                               EventStates aStateMask)
4393
0
{
4394
0
  MOZ_ASSERT(!mIsDocumentGone, "Unexpected ContentStateChanged");
4395
0
  MOZ_ASSERT(aDocument == mDocument, "Unexpected aDocument");
4396
0
4397
0
  if (mDidInitialize) {
4398
0
    nsAutoCauseReflowNotifier crNotifier(this);
4399
0
    mPresContext->RestyleManager()->ContentStateChanged(aContent, aStateMask);
4400
0
  }
4401
0
}
4402
4403
void
4404
PresShell::DocumentStatesChanged(nsIDocument* aDocument, EventStates aStateMask)
4405
0
{
4406
0
  MOZ_ASSERT(!mIsDocumentGone, "Unexpected DocumentStatesChanged");
4407
0
  MOZ_ASSERT(aDocument == mDocument, "Unexpected aDocument");
4408
0
  MOZ_ASSERT(!aStateMask.IsEmpty());
4409
0
4410
0
  if (mDidInitialize) {
4411
0
    mStyleSet->InvalidateStyleForDocumentStateChanges(aStateMask);
4412
0
  }
4413
0
4414
0
  if (aStateMask.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
4415
0
    if (nsIFrame* root = mFrameConstructor->GetRootFrame()) {
4416
0
      root->SchedulePaint();
4417
0
    }
4418
0
  }
4419
0
}
4420
4421
void
4422
PresShell::AttributeWillChange(Element* aElement,
4423
                               int32_t aNameSpaceID,
4424
                               nsAtom* aAttribute,
4425
                               int32_t aModType,
4426
                               const nsAttrValue* aNewValue)
4427
0
{
4428
0
  MOZ_ASSERT(!mIsDocumentGone, "Unexpected AttributeWillChange");
4429
0
  MOZ_ASSERT(aElement->OwnerDoc() == mDocument, "Unexpected document");
4430
0
4431
0
  // XXXwaterson it might be more elegant to wait until after the
4432
0
  // initial reflow to begin observing the document. That would
4433
0
  // squelch any other inappropriate notifications as well.
4434
0
  if (mDidInitialize) {
4435
0
    nsAutoCauseReflowNotifier crNotifier(this);
4436
0
    mPresContext->RestyleManager()->AttributeWillChange(aElement, aNameSpaceID,
4437
0
                                                        aAttribute, aModType,
4438
0
                                                        aNewValue);
4439
0
  }
4440
0
}
4441
4442
void
4443
PresShell::AttributeChanged(Element* aElement,
4444
                            int32_t aNameSpaceID,
4445
                            nsAtom* aAttribute,
4446
                            int32_t aModType,
4447
                            const nsAttrValue* aOldValue)
4448
0
{
4449
0
  MOZ_ASSERT(!mIsDocumentGone, "Unexpected AttributeChanged");
4450
0
  MOZ_ASSERT(aElement->OwnerDoc() == mDocument, "Unexpected document");
4451
0
4452
0
  // XXXwaterson it might be more elegant to wait until after the
4453
0
  // initial reflow to begin observing the document. That would
4454
0
  // squelch any other inappropriate notifications as well.
4455
0
  if (mDidInitialize) {
4456
0
    nsAutoCauseReflowNotifier crNotifier(this);
4457
0
    mPresContext->RestyleManager()->AttributeChanged(aElement, aNameSpaceID,
4458
0
                                                     aAttribute, aModType,
4459
0
                                                     aOldValue);
4460
0
  }
4461
0
}
4462
4463
void
4464
PresShell::ContentAppended(nsIContent* aFirstNewContent)
4465
0
{
4466
0
  MOZ_ASSERT(!mIsDocumentGone, "Unexpected ContentAppended");
4467
0
  MOZ_ASSERT(aFirstNewContent->OwnerDoc() == mDocument,
4468
0
                  "Unexpected document");
4469
0
4470
0
  // We never call ContentAppended with a document as the container, so we can
4471
0
  // assert that we have an nsIContent parent.
4472
0
  MOZ_ASSERT(aFirstNewContent->GetParent());
4473
0
  MOZ_ASSERT(aFirstNewContent->GetParent()->IsElement() ||
4474
0
             aFirstNewContent->GetParent()->IsShadowRoot());
4475
0
4476
0
  if (!mDidInitialize) {
4477
0
    return;
4478
0
  }
4479
0
4480
0
  nsAutoCauseReflowNotifier crNotifier(this);
4481
0
4482
0
  // Call this here so it only happens for real content mutations and
4483
0
  // not cases when the frame constructor calls its own methods to force
4484
0
  // frame reconstruction.
4485
0
  mPresContext->RestyleManager()->ContentAppended(aFirstNewContent);
4486
0
4487
0
  mFrameConstructor->ContentAppended(
4488
0
      aFirstNewContent,
4489
0
      nsCSSFrameConstructor::InsertionKind::Async);
4490
0
}
4491
4492
void
4493
PresShell::ContentInserted(nsIContent* aChild)
4494
0
{
4495
0
  MOZ_ASSERT(!mIsDocumentGone, "Unexpected ContentInserted");
4496
0
  MOZ_ASSERT(aChild->OwnerDoc() == mDocument, "Unexpected document");
4497
0
4498
0
  if (!mDidInitialize) {
4499
0
    return;
4500
0
  }
4501
0
4502
0
  nsAutoCauseReflowNotifier crNotifier(this);
4503
0
4504
0
  // Call this here so it only happens for real content mutations and
4505
0
  // not cases when the frame constructor calls its own methods to force
4506
0
  // frame reconstruction.
4507
0
  mPresContext->RestyleManager()->ContentInserted(aChild);
4508
0
4509
0
  mFrameConstructor->ContentInserted(
4510
0
      aChild,
4511
0
      nullptr,
4512
0
      nsCSSFrameConstructor::InsertionKind::Async);
4513
0
}
4514
4515
void
4516
PresShell::ContentRemoved(nsIContent* aChild, nsIContent* aPreviousSibling)
4517
0
{
4518
0
  MOZ_ASSERT(!mIsDocumentGone, "Unexpected ContentRemoved");
4519
0
  MOZ_ASSERT(aChild->OwnerDoc() == mDocument, "Unexpected document");
4520
0
  nsINode* container = aChild->GetParentNode();
4521
0
4522
0
  // Notify the ESM that the content has been removed, so that
4523
0
  // it can clean up any state related to the content.
4524
0
4525
0
  mPresContext->EventStateManager()->ContentRemoved(mDocument, aChild);
4526
0
4527
0
  nsAutoCauseReflowNotifier crNotifier(this);
4528
0
4529
0
  // Call this here so it only happens for real content mutations and
4530
0
  // not cases when the frame constructor calls its own methods to force
4531
0
  // frame reconstruction.
4532
0
  nsIContent* oldNextSibling = nullptr;
4533
0
4534
0
  // Editor calls into here with NAC via HTMLEditor::DeleteRefToAnonymousNode.
4535
0
  // This could be asserted if that caller is fixed.
4536
0
  if (MOZ_LIKELY(!aChild->IsRootOfAnonymousSubtree())) {
4537
0
    oldNextSibling = aPreviousSibling
4538
0
      ? aPreviousSibling->GetNextSibling()
4539
0
      : container->GetFirstChild();
4540
0
  }
4541
0
4542
0
  // After removing aChild from tree we should save information about live ancestor
4543
0
  if (mPointerEventTarget &&
4544
0
      nsContentUtils::ContentIsDescendantOf(mPointerEventTarget, aChild)) {
4545
0
    mPointerEventTarget = aChild->GetParent();
4546
0
  }
4547
0
4548
0
  mFrameConstructor->ContentRemoved(aChild,
4549
0
                                    oldNextSibling,
4550
0
                                    nsCSSFrameConstructor::REMOVE_CONTENT);
4551
0
4552
0
  // NOTE(emilio): It's important that this goes after the frame constructor
4553
0
  // stuff, otherwise the frame constructor can't see elements which are
4554
0
  // display: contents / display: none, because we'd have cleared all the style
4555
0
  // data from there.
4556
0
  mPresContext->RestyleManager()->ContentRemoved(aChild, oldNextSibling);
4557
0
}
4558
4559
void
4560
PresShell::NotifyCounterStylesAreDirty()
4561
0
{
4562
0
  nsAutoCauseReflowNotifier reflowNotifier(this);
4563
0
  mFrameConstructor->NotifyCounterStylesAreDirty();
4564
0
}
4565
4566
bool
4567
nsIPresShell::FrameIsAncestorOfDirtyRoot(nsIFrame* aFrame) const
4568
0
{
4569
0
  MOZ_ASSERT(aFrame);
4570
0
4571
0
  // Look for a path from any dirty roots to aFrame, following GetParent().
4572
0
  // This check mirrors what FrameNeedsReflow() would have done if the reflow
4573
0
  // root didn't get in the way.
4574
0
  for (nsIFrame* dirtyFrame : mDirtyRoots) {
4575
0
    while (dirtyFrame) {
4576
0
      if (dirtyFrame == aFrame) {
4577
0
        return true;
4578
0
      }
4579
0
4580
0
      dirtyFrame = dirtyFrame->GetParent();
4581
0
    }
4582
0
  }
4583
0
4584
0
  return false;
4585
0
}
4586
4587
void
4588
PresShell::ReconstructFrames()
4589
0
{
4590
0
  MOZ_ASSERT(!mFrameConstructor->GetRootFrame() || mDidInitialize,
4591
0
             "Must not have root frame before initial reflow");
4592
0
  if (!mDidInitialize || mIsDestroying) {
4593
0
    // Nothing to do here
4594
0
    return;
4595
0
  }
4596
0
4597
0
  nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
4598
0
4599
0
  // Have to make sure that the content notifications are flushed before we
4600
0
  // start messing with the frame model; otherwise we can get content doubling.
4601
0
  //
4602
0
  // Also make sure that styles are flushed before calling into the frame
4603
0
  // constructor, since that's what it expects.
4604
0
  mDocument->FlushPendingNotifications(FlushType::Style);
4605
0
4606
0
  if (mIsDestroying) {
4607
0
    return;
4608
0
  }
4609
0
4610
0
  nsAutoCauseReflowNotifier crNotifier(this);
4611
0
  mFrameConstructor->ReconstructDocElementHierarchy(nsCSSFrameConstructor::InsertionKind::Sync);
4612
0
}
4613
4614
void
4615
nsIPresShell::ApplicableStylesChanged()
4616
0
{
4617
0
  if (mIsDestroying) {
4618
0
    // We don't want to mess with restyles at this point
4619
0
    return;
4620
0
  }
4621
0
4622
0
  EnsureStyleFlush();
4623
0
  mDocument->MarkUserFontSetDirty();
4624
0
4625
0
  if (mPresContext) {
4626
0
    mPresContext->MarkCounterStylesDirty();
4627
0
    mPresContext->MarkFontFeatureValuesDirty();
4628
0
    mPresContext->RestyleManager()->NextRestyleIsForCSSRuleChanges();
4629
0
  }
4630
0
}
4631
4632
nsresult
4633
PresShell::RenderDocument(const nsRect& aRect, uint32_t aFlags,
4634
                          nscolor aBackgroundColor,
4635
                          gfxContext* aThebesContext)
4636
0
{
4637
0
  NS_ENSURE_TRUE(!(aFlags & RENDER_IS_UNTRUSTED), NS_ERROR_NOT_IMPLEMENTED);
4638
0
4639
0
  nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
4640
0
  if (rootPresContext) {
4641
0
    rootPresContext->FlushWillPaintObservers();
4642
0
    if (mIsDestroying)
4643
0
      return NS_OK;
4644
0
  }
4645
0
4646
0
  nsAutoScriptBlocker blockScripts;
4647
0
4648
0
  // Set up the rectangle as the path in aThebesContext
4649
0
  gfxRect r(0, 0,
4650
0
            nsPresContext::AppUnitsToFloatCSSPixels(aRect.width),
4651
0
            nsPresContext::AppUnitsToFloatCSSPixels(aRect.height));
4652
0
  aThebesContext->NewPath();
4653
#ifdef MOZ_GFX_OPTIMIZE_MOBILE
4654
  aThebesContext->Rectangle(r, true);
4655
#else
4656
  aThebesContext->Rectangle(r);
4657
0
#endif
4658
0
4659
0
  nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
4660
0
  if (!rootFrame) {
4661
0
    // Nothing to paint, just fill the rect
4662
0
    aThebesContext->SetColor(Color::FromABGR(aBackgroundColor));
4663
0
    aThebesContext->Fill();
4664
0
    return NS_OK;
4665
0
  }
4666
0
4667
0
  gfxContextAutoSaveRestore save(aThebesContext);
4668
0
4669
0
  MOZ_ASSERT(aThebesContext->CurrentOp() == CompositionOp::OP_OVER);
4670
0
4671
0
  aThebesContext->Clip();
4672
0
4673
0
  nsDeviceContext* devCtx = mPresContext->DeviceContext();
4674
0
4675
0
  gfxPoint offset(-nsPresContext::AppUnitsToFloatCSSPixels(aRect.x),
4676
0
                  -nsPresContext::AppUnitsToFloatCSSPixels(aRect.y));
4677
0
  gfxFloat scale = gfxFloat(devCtx->AppUnitsPerDevPixel())/AppUnitsPerCSSPixel();
4678
0
4679
0
  // Since canvas APIs use floats to set up their matrices, we may have some
4680
0
  // slight rounding errors here.  We use NudgeToIntegers() here to adjust
4681
0
  // matrix components that are integers up to the accuracy of floats to be
4682
0
  // those integers.
4683
0
  gfxMatrix newTM = aThebesContext->CurrentMatrixDouble().PreTranslate(offset).
4684
0
                                                          PreScale(scale, scale).
4685
0
                                                          NudgeToIntegers();
4686
0
  aThebesContext->SetMatrixDouble(newTM);
4687
0
4688
0
  AutoSaveRestoreRenderingState _(this);
4689
0
4690
0
  bool wouldFlushRetainedLayers = false;
4691
0
  PaintFrameFlags flags = PaintFrameFlags::PAINT_IGNORE_SUPPRESSION;
4692
0
  if (aThebesContext->CurrentMatrix().HasNonIntegerTranslation()) {
4693
0
    flags |= PaintFrameFlags::PAINT_IN_TRANSFORM;
4694
0
  }
4695
0
  if (!(aFlags & RENDER_ASYNC_DECODE_IMAGES)) {
4696
0
    flags |= PaintFrameFlags::PAINT_SYNC_DECODE_IMAGES;
4697
0
  }
4698
0
  if (aFlags & RENDER_USE_WIDGET_LAYERS) {
4699
0
    // We only support using widget layers on display root's with widgets.
4700
0
    nsView* view = rootFrame->GetView();
4701
0
    if (view && view->GetWidget() &&
4702
0
        nsLayoutUtils::GetDisplayRootFrame(rootFrame) == rootFrame) {
4703
0
      LayerManager* layerManager = view->GetWidget()->GetLayerManager();
4704
0
      // ClientLayerManagers or WebRenderLayerManagers in content processes
4705
0
      // don't support taking snapshots.
4706
0
      if (layerManager &&
4707
0
          (!layerManager->AsKnowsCompositor() ||
4708
0
           XRE_IsParentProcess())) {
4709
0
        flags |= PaintFrameFlags::PAINT_WIDGET_LAYERS;
4710
0
      }
4711
0
    }
4712
0
  }
4713
0
  if (!(aFlags & RENDER_CARET)) {
4714
0
    wouldFlushRetainedLayers = true;
4715
0
    flags |= PaintFrameFlags::PAINT_HIDE_CARET;
4716
0
  }
4717
0
  if (aFlags & RENDER_IGNORE_VIEWPORT_SCROLLING) {
4718
0
    wouldFlushRetainedLayers = !IgnoringViewportScrolling();
4719
0
    mRenderFlags = ChangeFlag(mRenderFlags, true, STATE_IGNORING_VIEWPORT_SCROLLING);
4720
0
  }
4721
0
  if (aFlags & RENDER_DRAWWINDOW_NOT_FLUSHING) {
4722
0
    mRenderFlags = ChangeFlag(mRenderFlags, true, STATE_DRAWWINDOW_NOT_FLUSHING);
4723
0
  }
4724
0
  if (aFlags & RENDER_DOCUMENT_RELATIVE) {
4725
0
    // XXX be smarter about this ... drawWindow might want a rect
4726
0
    // that's "pretty close" to what our retained layer tree covers.
4727
0
    // In that case, it wouldn't disturb normal rendering too much,
4728
0
    // and we should allow it.
4729
0
    wouldFlushRetainedLayers = true;
4730
0
    flags |= PaintFrameFlags::PAINT_DOCUMENT_RELATIVE;
4731
0
  }
4732
0
4733
0
  // Don't let drawWindow blow away our retained layer tree
4734
0
  if ((flags & PaintFrameFlags::PAINT_WIDGET_LAYERS) && wouldFlushRetainedLayers) {
4735
0
    flags &= ~PaintFrameFlags::PAINT_WIDGET_LAYERS;
4736
0
  }
4737
0
4738
0
  nsLayoutUtils::PaintFrame(aThebesContext, rootFrame, nsRegion(aRect),
4739
0
                            aBackgroundColor,
4740
0
                            nsDisplayListBuilderMode::PAINTING,
4741
0
                            flags);
4742
0
4743
0
  return NS_OK;
4744
0
}
4745
4746
/*
4747
 * Clip the display list aList to a range. Returns the clipped
4748
 * rectangle surrounding the range.
4749
 */
4750
nsRect
4751
PresShell::ClipListToRange(nsDisplayListBuilder *aBuilder,
4752
                           nsDisplayList* aList,
4753
                           nsRange* aRange)
4754
0
{
4755
0
  // iterate though the display items and add up the bounding boxes of each.
4756
0
  // This will allow the total area of the frames within the range to be
4757
0
  // determined. To do this, remove an item from the bottom of the list, check
4758
0
  // whether it should be part of the range, and if so, append it to the top
4759
0
  // of the temporary list tmpList. If the item is a text frame at the end of
4760
0
  // the selection range, clip it to the portion of the text frame that is
4761
0
  // part of the selection. Then, append the wrapper to the top of the list.
4762
0
  // Otherwise, just delete the item and don't append it.
4763
0
  nsRect surfaceRect;
4764
0
  nsDisplayList tmpList;
4765
0
4766
0
  nsDisplayItem* i;
4767
0
  while ((i = aList->RemoveBottom())) {
4768
0
    // itemToInsert indiciates the item that should be inserted into the
4769
0
    // temporary list. If null, no item should be inserted.
4770
0
    nsDisplayItem* itemToInsert = nullptr;
4771
0
    nsIFrame* frame = i->Frame();
4772
0
    nsIContent* content = frame->GetContent();
4773
0
    if (content) {
4774
0
      bool atStart = (content == aRange->GetStartContainer());
4775
0
      bool atEnd = (content == aRange->GetEndContainer());
4776
0
      if ((atStart || atEnd) && frame->IsTextFrame()) {
4777
0
        int32_t frameStartOffset, frameEndOffset;
4778
0
        frame->GetOffsets(frameStartOffset, frameEndOffset);
4779
0
4780
0
        int32_t hilightStart =
4781
0
          atStart ? std::max(static_cast<int32_t>(aRange->StartOffset()),
4782
0
                             frameStartOffset) : frameStartOffset;
4783
0
        int32_t hilightEnd =
4784
0
          atEnd ? std::min(static_cast<int32_t>(aRange->EndOffset()),
4785
0
                           frameEndOffset) : frameEndOffset;
4786
0
        if (hilightStart < hilightEnd) {
4787
0
          // determine the location of the start and end edges of the range.
4788
0
          nsPoint startPoint, endPoint;
4789
0
          frame->GetPointFromOffset(hilightStart, &startPoint);
4790
0
          frame->GetPointFromOffset(hilightEnd, &endPoint);
4791
0
4792
0
          // The clip rectangle is determined by taking the the start and
4793
0
          // end points of the range, offset from the reference frame.
4794
0
          // Because of rtl, the end point may be to the left of (or above,
4795
0
          // in vertical mode) the start point, so x (or y) is set to the
4796
0
          // lower of the values.
4797
0
          nsRect textRect(aBuilder->ToReferenceFrame(frame), frame->GetSize());
4798
0
          if (frame->GetWritingMode().IsVertical()) {
4799
0
            nscoord y = std::min(startPoint.y, endPoint.y);
4800
0
            textRect.y += y;
4801
0
            textRect.height = std::max(startPoint.y, endPoint.y) - y;
4802
0
          } else {
4803
0
            nscoord x = std::min(startPoint.x, endPoint.x);
4804
0
            textRect.x += x;
4805
0
            textRect.width = std::max(startPoint.x, endPoint.x) - x;
4806
0
          }
4807
0
          surfaceRect.UnionRect(surfaceRect, textRect);
4808
0
4809
0
          const ActiveScrolledRoot* asr = i->GetActiveScrolledRoot();
4810
0
4811
0
          DisplayItemClip newClip;
4812
0
          newClip.SetTo(textRect);
4813
0
4814
0
          const DisplayItemClipChain* newClipChain =
4815
0
            aBuilder->AllocateDisplayItemClipChain(newClip, asr, nullptr);
4816
0
4817
0
          i->IntersectClip(aBuilder, newClipChain, true);
4818
0
          itemToInsert = i;
4819
0
        }
4820
0
      }
4821
0
      // Don't try to descend into subdocuments.
4822
0
      // If this ever changes we'd need to add handling for subdocuments with
4823
0
      // different zoom levels.
4824
0
      else if (content->GetUncomposedDoc() ==
4825
0
                 aRange->GetStartContainer()->GetUncomposedDoc()) {
4826
0
        // if the node is within the range, append it to the temporary list
4827
0
        bool before, after;
4828
0
        nsresult rv =
4829
0
          nsRange::CompareNodeToRange(content, aRange, &before, &after);
4830
0
        if (NS_SUCCEEDED(rv) && !before && !after) {
4831
0
          itemToInsert = i;
4832
0
          bool snap;
4833
0
          surfaceRect.UnionRect(surfaceRect, i->GetBounds(aBuilder, &snap));
4834
0
        }
4835
0
      }
4836
0
    }
4837
0
4838
0
    // insert the item into the list if necessary. If the item has a child
4839
0
    // list, insert that as well
4840
0
    nsDisplayList* sublist = i->GetSameCoordinateSystemChildren();
4841
0
    if (itemToInsert || sublist) {
4842
0
      tmpList.AppendToTop(itemToInsert ? itemToInsert : i);
4843
0
      // if the item is a list, iterate over it as well
4844
0
      if (sublist)
4845
0
        surfaceRect.UnionRect(surfaceRect,
4846
0
          ClipListToRange(aBuilder, sublist, aRange));
4847
0
    }
4848
0
    else {
4849
0
      // otherwise, just delete the item and don't readd it to the list
4850
0
      i->Destroy(aBuilder);
4851
0
    }
4852
0
  }
4853
0
4854
0
  // now add all the items back onto the original list again
4855
0
  aList->AppendToTop(&tmpList);
4856
0
4857
0
  return surfaceRect;
4858
0
}
4859
4860
#ifdef DEBUG
4861
#include <stdio.h>
4862
4863
static bool gDumpRangePaintList = false;
4864
#endif
4865
4866
UniquePtr<RangePaintInfo>
4867
PresShell::CreateRangePaintInfo(nsRange* aRange,
4868
                                nsRect& aSurfaceRect,
4869
                                bool aForPrimarySelection)
4870
0
{
4871
0
  nsIFrame* ancestorFrame;
4872
0
  nsIFrame* rootFrame = GetRootFrame();
4873
0
4874
0
  // If the start or end of the range is the document, just use the root
4875
0
  // frame, otherwise get the common ancestor of the two endpoints of the
4876
0
  // range.
4877
0
  nsINode* startContainer = aRange->GetStartContainer();
4878
0
  nsINode* endContainer = aRange->GetEndContainer();
4879
0
  nsIDocument* doc = startContainer->GetComposedDoc();
4880
0
  if (startContainer == doc || endContainer == doc) {
4881
0
    ancestorFrame = rootFrame;
4882
0
  } else {
4883
0
    nsINode* ancestor =
4884
0
      nsContentUtils::GetCommonAncestor(startContainer, endContainer);
4885
0
    NS_ASSERTION(!ancestor || ancestor->IsContent(),
4886
0
                 "common ancestor is not content");
4887
0
    if (!ancestor || !ancestor->IsContent())
4888
0
      return nullptr;
4889
0
4890
0
    ancestorFrame = ancestor->AsContent()->GetPrimaryFrame();
4891
0
4892
0
    // XXX deal with ancestorFrame being null due to display:contents
4893
0
4894
0
    // use the nearest ancestor frame that includes all continuations as the
4895
0
    // root for building the display list
4896
0
    while (ancestorFrame &&
4897
0
           nsLayoutUtils::GetNextContinuationOrIBSplitSibling(ancestorFrame))
4898
0
      ancestorFrame = ancestorFrame->GetParent();
4899
0
  }
4900
0
4901
0
  if (!ancestorFrame) {
4902
0
    return nullptr;
4903
0
  }
4904
0
4905
0
  // get a display list containing the range
4906
0
  auto info = MakeUnique<RangePaintInfo>(aRange, ancestorFrame);
4907
0
  info->mBuilder.SetIncludeAllOutOfFlows();
4908
0
  if (aForPrimarySelection) {
4909
0
    info->mBuilder.SetSelectedFramesOnly();
4910
0
  }
4911
0
  info->mBuilder.EnterPresShell(ancestorFrame);
4912
0
4913
0
  nsCOMPtr<nsIContentIterator> iter = NS_NewContentSubtreeIterator();
4914
0
  nsresult rv = iter->Init(aRange);
4915
0
  if (NS_FAILED(rv)) {
4916
0
    return nullptr;
4917
0
  }
4918
0
4919
0
  auto BuildDisplayListForNode = [&] (nsINode* aNode) {
4920
0
    if (MOZ_UNLIKELY(!aNode->IsContent())) {
4921
0
      return;
4922
0
    }
4923
0
    nsIFrame* frame = aNode->AsContent()->GetPrimaryFrame();
4924
0
    // XXX deal with frame being null due to display:contents
4925
0
    for (; frame; frame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame)) {
4926
0
      info->mBuilder.SetVisibleRect(frame->GetVisualOverflowRect());
4927
0
      info->mBuilder.SetDirtyRect(frame->GetVisualOverflowRect());
4928
0
      frame->BuildDisplayListForStackingContext(&info->mBuilder, &info->mList);
4929
0
    }
4930
0
  };
4931
0
  if (startContainer->NodeType() == nsINode::TEXT_NODE) {
4932
0
    BuildDisplayListForNode(startContainer);
4933
0
  }
4934
0
  for (; !iter->IsDone(); iter->Next()) {
4935
0
    nsCOMPtr<nsINode> node = iter->GetCurrentNode();
4936
0
    BuildDisplayListForNode(node);
4937
0
  }
4938
0
  if (endContainer != startContainer &&
4939
0
      endContainer->NodeType() == nsINode::TEXT_NODE) {
4940
0
    BuildDisplayListForNode(endContainer);
4941
0
  }
4942
0
4943
#ifdef DEBUG
4944
  if (gDumpRangePaintList) {
4945
    fprintf(stderr, "CreateRangePaintInfo --- before ClipListToRange:\n");
4946
    nsFrame::PrintDisplayList(&(info->mBuilder), info->mList);
4947
  }
4948
#endif
4949
4950
0
  nsRect rangeRect = ClipListToRange(&info->mBuilder, &info->mList, aRange);
4951
0
4952
0
  info->mBuilder.LeavePresShell(ancestorFrame, &info->mList);
4953
0
4954
#ifdef DEBUG
4955
  if (gDumpRangePaintList) {
4956
    fprintf(stderr, "CreateRangePaintInfo --- after ClipListToRange:\n");
4957
    nsFrame::PrintDisplayList(&(info->mBuilder), info->mList);
4958
  }
4959
#endif
4960
4961
0
  // determine the offset of the reference frame for the display list
4962
0
  // to the root frame. This will allow the coordinates used when painting
4963
0
  // to all be offset from the same point
4964
0
  info->mRootOffset = ancestorFrame->GetOffsetTo(rootFrame);
4965
0
  rangeRect.MoveBy(info->mRootOffset);
4966
0
  aSurfaceRect.UnionRect(aSurfaceRect, rangeRect);
4967
0
4968
0
  return info;
4969
0
}
4970
4971
already_AddRefed<SourceSurface>
4972
PresShell::PaintRangePaintInfo(const nsTArray<UniquePtr<RangePaintInfo>>& aItems,
4973
                               Selection* aSelection,
4974
                               const Maybe<CSSIntRegion>& aRegion,
4975
                               nsRect aArea,
4976
                               const LayoutDeviceIntPoint aPoint,
4977
                               LayoutDeviceIntRect* aScreenRect,
4978
                               uint32_t aFlags)
4979
0
{
4980
0
  nsPresContext* pc = GetPresContext();
4981
0
  if (!pc || aArea.width == 0 || aArea.height == 0)
4982
0
    return nullptr;
4983
0
4984
0
  // use the rectangle to create the surface
4985
0
  nsIntRect pixelArea = aArea.ToOutsidePixels(pc->AppUnitsPerDevPixel());
4986
0
4987
0
  // if the image should not be resized, scale must be 1
4988
0
  float scale = 1.0;
4989
0
  nsIntRect rootScreenRect =
4990
0
    GetRootFrame()->GetScreenRectInAppUnits().ToNearestPixels(
4991
0
      pc->AppUnitsPerDevPixel());
4992
0
4993
0
  nsRect maxSize;
4994
0
  pc->DeviceContext()->GetClientRect(maxSize);
4995
0
4996
0
  // check if the image should be resized
4997
0
  bool resize = aFlags & RENDER_AUTO_SCALE;
4998
0
4999
0
  if (resize) {
5000
0
    // check if image-resizing-algorithm should be used
5001
0
    if (aFlags & RENDER_IS_IMAGE) {
5002
0
      // get max screensize
5003
0
      nscoord maxWidth = pc->AppUnitsToDevPixels(maxSize.width);
5004
0
      nscoord maxHeight = pc->AppUnitsToDevPixels(maxSize.height);
5005
0
      // resize image relative to the screensize
5006
0
      // get best height/width relative to screensize
5007
0
      float bestHeight = float(maxHeight)*RELATIVE_SCALEFACTOR;
5008
0
      float bestWidth = float(maxWidth)*RELATIVE_SCALEFACTOR;
5009
0
      // calculate scale for bestWidth
5010
0
      float adjustedScale = bestWidth / float(pixelArea.width);
5011
0
      // get the worst height (height when width is perfect)
5012
0
      float worstHeight = float(pixelArea.height)*adjustedScale;
5013
0
      // get the difference of best and worst height
5014
0
      float difference = bestHeight - worstHeight;
5015
0
      // halve the difference and add it to worstHeight to get
5016
0
      // the best compromise between bestHeight and bestWidth,
5017
0
      // then calculate the corresponding scale factor
5018
0
      adjustedScale = (worstHeight + difference / 2) / float(pixelArea.height);
5019
0
      // prevent upscaling
5020
0
      scale = std::min(scale, adjustedScale);
5021
0
    } else {
5022
0
      // get half of max screensize
5023
0
      nscoord maxWidth = pc->AppUnitsToDevPixels(maxSize.width >> 1);
5024
0
      nscoord maxHeight = pc->AppUnitsToDevPixels(maxSize.height >> 1);
5025
0
      if (pixelArea.width > maxWidth || pixelArea.height > maxHeight) {
5026
0
        // divide the maximum size by the image size in both directions. Whichever
5027
0
        // direction produces the smallest result determines how much should be
5028
0
        // scaled.
5029
0
        if (pixelArea.width > maxWidth)
5030
0
          scale = std::min(scale, float(maxWidth) / pixelArea.width);
5031
0
        if (pixelArea.height > maxHeight)
5032
0
          scale = std::min(scale, float(maxHeight) / pixelArea.height);
5033
0
      }
5034
0
    }
5035
0
5036
0
5037
0
    pixelArea.width = NSToIntFloor(float(pixelArea.width) * scale);
5038
0
    pixelArea.height = NSToIntFloor(float(pixelArea.height) * scale);
5039
0
    if (!pixelArea.width || !pixelArea.height)
5040
0
      return nullptr;
5041
0
5042
0
    // adjust the screen position based on the rescaled size
5043
0
    nscoord left = rootScreenRect.x + pixelArea.x;
5044
0
    nscoord top = rootScreenRect.y + pixelArea.y;
5045
0
    aScreenRect->x = NSToIntFloor(aPoint.x - float(aPoint.x - left) * scale);
5046
0
    aScreenRect->y = NSToIntFloor(aPoint.y - float(aPoint.y - top) * scale);
5047
0
  }
5048
0
  else {
5049
0
    // move aScreenRect to the position of the surface in screen coordinates
5050
0
    aScreenRect->MoveTo(rootScreenRect.x + pixelArea.x, rootScreenRect.y + pixelArea.y);
5051
0
  }
5052
0
  aScreenRect->width = pixelArea.width;
5053
0
  aScreenRect->height = pixelArea.height;
5054
0
5055
0
  RefPtr<DrawTarget> dt =
5056
0
   gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
5057
0
                                 IntSize(pixelArea.width, pixelArea.height),
5058
0
                                 SurfaceFormat::B8G8R8A8);
5059
0
  if (!dt || !dt->IsValid()) {
5060
0
    return nullptr;
5061
0
  }
5062
0
5063
0
  RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
5064
0
  MOZ_ASSERT(ctx); // already checked the draw target above
5065
0
5066
0
  if (aRegion) {
5067
0
    RefPtr<PathBuilder> builder = dt->CreatePathBuilder(FillRule::FILL_WINDING);
5068
0
5069
0
    // Convert aRegion from CSS pixels to dev pixels
5070
0
    nsIntRegion region =
5071
0
        aRegion->ToAppUnits(AppUnitsPerCSSPixel())
5072
0
          .ToOutsidePixels(pc->AppUnitsPerDevPixel());
5073
0
    for (auto iter = region.RectIter(); !iter.Done(); iter.Next()) {
5074
0
      const IntRect& rect = iter.Get();
5075
0
5076
0
      builder->MoveTo(rect.TopLeft());
5077
0
      builder->LineTo(rect.TopRight());
5078
0
      builder->LineTo(rect.BottomRight());
5079
0
      builder->LineTo(rect.BottomLeft());
5080
0
      builder->LineTo(rect.TopLeft());
5081
0
    }
5082
0
5083
0
    RefPtr<Path> path = builder->Finish();
5084
0
    ctx->Clip(path);
5085
0
  }
5086
0
5087
0
  gfxMatrix initialTM = ctx->CurrentMatrixDouble();
5088
0
5089
0
  if (resize)
5090
0
    initialTM.PreScale(scale, scale);
5091
0
5092
0
  // translate so that points are relative to the surface area
5093
0
  gfxPoint surfaceOffset =
5094
0
    nsLayoutUtils::PointToGfxPoint(-aArea.TopLeft(), pc->AppUnitsPerDevPixel());
5095
0
  initialTM.PreTranslate(surfaceOffset);
5096
0
5097
0
  // temporarily hide the selection so that text is drawn normally. If a
5098
0
  // selection is being rendered, use that, otherwise use the presshell's
5099
0
  // selection.
5100
0
  RefPtr<nsFrameSelection> frameSelection;
5101
0
  if (aSelection) {
5102
0
    frameSelection = aSelection->GetFrameSelection();
5103
0
  }
5104
0
  else {
5105
0
    frameSelection = FrameSelection();
5106
0
  }
5107
0
  int16_t oldDisplaySelection = frameSelection->GetDisplaySelection();
5108
0
  frameSelection->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
5109
0
5110
0
  // next, paint each range in the selection
5111
0
  for (const UniquePtr<RangePaintInfo>& rangeInfo : aItems) {
5112
0
    // the display lists paint relative to the offset from the reference
5113
0
    // frame, so account for that translation too:
5114
0
    gfxPoint rootOffset =
5115
0
      nsLayoutUtils::PointToGfxPoint(rangeInfo->mRootOffset,
5116
0
                                     pc->AppUnitsPerDevPixel());
5117
0
    ctx->SetMatrixDouble(initialTM.PreTranslate(rootOffset));
5118
0
    aArea.MoveBy(-rangeInfo->mRootOffset.x, -rangeInfo->mRootOffset.y);
5119
0
    nsRegion visible(aArea);
5120
0
    RefPtr<LayerManager> layerManager =
5121
0
        rangeInfo->mList.PaintRoot(&rangeInfo->mBuilder, ctx,
5122
0
                                   nsDisplayList::PAINT_DEFAULT);
5123
0
    aArea.MoveBy(rangeInfo->mRootOffset.x, rangeInfo->mRootOffset.y);
5124
0
  }
5125
0
5126
0
  // restore the old selection display state
5127
0
  frameSelection->SetDisplaySelection(oldDisplaySelection);
5128
0
5129
0
  return dt->Snapshot();
5130
0
}
5131
5132
already_AddRefed<SourceSurface>
5133
PresShell::RenderNode(nsINode* aNode,
5134
                      const Maybe<CSSIntRegion>& aRegion,
5135
                      const LayoutDeviceIntPoint aPoint,
5136
                      LayoutDeviceIntRect* aScreenRect,
5137
                      uint32_t aFlags)
5138
0
{
5139
0
  // area will hold the size of the surface needed to draw the node, measured
5140
0
  // from the root frame.
5141
0
  nsRect area;
5142
0
  nsTArray<UniquePtr<RangePaintInfo>> rangeItems;
5143
0
5144
0
  // nothing to draw if the node isn't in a document
5145
0
  if (!aNode->IsInComposedDoc()) {
5146
0
    return nullptr;
5147
0
  }
5148
0
5149
0
  RefPtr<nsRange> range = new nsRange(aNode);
5150
0
  IgnoredErrorResult rv;
5151
0
  range->SelectNode(*aNode, rv);
5152
0
  if (rv.Failed()) {
5153
0
    return nullptr;
5154
0
  }
5155
0
5156
0
  UniquePtr<RangePaintInfo> info = CreateRangePaintInfo(range, area, false);
5157
0
  if (info && !rangeItems.AppendElement(std::move(info))) {
5158
0
    return nullptr;
5159
0
  }
5160
0
5161
0
  Maybe<CSSIntRegion> region = aRegion;
5162
0
  if (region) {
5163
0
    // combine the area with the supplied region
5164
0
    CSSIntRect rrectPixels = region->GetBounds();
5165
0
5166
0
    nsRect rrect = ToAppUnits(rrectPixels, AppUnitsPerCSSPixel());
5167
0
    area.IntersectRect(area, rrect);
5168
0
5169
0
    nsPresContext* pc = GetPresContext();
5170
0
    if (!pc)
5171
0
      return nullptr;
5172
0
5173
0
    // move the region so that it is offset from the topleft corner of the surface
5174
0
    region->MoveBy(-nsPresContext::AppUnitsToIntCSSPixels(area.x),
5175
0
                   -nsPresContext::AppUnitsToIntCSSPixels(area.y));
5176
0
  }
5177
0
5178
0
  return PaintRangePaintInfo(rangeItems, nullptr, region, area, aPoint,
5179
0
                             aScreenRect, aFlags);
5180
0
}
5181
5182
already_AddRefed<SourceSurface>
5183
PresShell::RenderSelection(Selection* aSelection,
5184
                           const LayoutDeviceIntPoint aPoint,
5185
                           LayoutDeviceIntRect* aScreenRect,
5186
                           uint32_t aFlags)
5187
0
{
5188
0
  // area will hold the size of the surface needed to draw the selection,
5189
0
  // measured from the root frame.
5190
0
  nsRect area;
5191
0
  nsTArray<UniquePtr<RangePaintInfo>> rangeItems;
5192
0
5193
0
  // iterate over each range and collect them into the rangeItems array.
5194
0
  // This is done so that the size of selection can be determined so as
5195
0
  // to allocate a surface area
5196
0
  uint32_t numRanges = aSelection->RangeCount();
5197
0
  NS_ASSERTION(numRanges > 0, "RenderSelection called with no selection");
5198
0
5199
0
  for (uint32_t r = 0; r < numRanges; r++)
5200
0
  {
5201
0
    RefPtr<nsRange> range = aSelection->GetRangeAt(r);
5202
0
5203
0
    UniquePtr<RangePaintInfo> info = CreateRangePaintInfo(range, area, true);
5204
0
    if (info && !rangeItems.AppendElement(std::move(info))) {
5205
0
      return nullptr;
5206
0
    }
5207
0
  }
5208
0
5209
0
  return PaintRangePaintInfo(rangeItems, aSelection, Nothing(), area, aPoint,
5210
0
                             aScreenRect, aFlags);
5211
0
}
5212
5213
void
5214
PresShell::AddPrintPreviewBackgroundItem(nsDisplayListBuilder& aBuilder,
5215
                                         nsDisplayList&        aList,
5216
                                         nsIFrame*             aFrame,
5217
                                         const nsRect&         aBounds)
5218
0
{
5219
0
  aList.AppendToBottom(
5220
0
    MakeDisplayItem<nsDisplaySolidColor>(&aBuilder, aFrame, aBounds, NS_RGB(115, 115, 115)));
5221
0
}
5222
5223
static bool
5224
AddCanvasBackgroundColor(const nsDisplayList& aList, nsIFrame* aCanvasFrame,
5225
                         nscolor aColor, bool aCSSBackgroundColor)
5226
0
{
5227
0
  for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) {
5228
0
    const DisplayItemType type = i->GetType();
5229
0
5230
0
    if (i->Frame() == aCanvasFrame &&
5231
0
        type == DisplayItemType::TYPE_CANVAS_BACKGROUND_COLOR) {
5232
0
      nsDisplayCanvasBackgroundColor* bg = static_cast<nsDisplayCanvasBackgroundColor*>(i);
5233
0
      bg->SetExtraBackgroundColor(aColor);
5234
0
      return true;
5235
0
    }
5236
0
5237
0
    const bool isBlendContainer =
5238
0
      type == DisplayItemType::TYPE_BLEND_CONTAINER ||
5239
0
      type == DisplayItemType::TYPE_TABLE_BLEND_CONTAINER;
5240
0
5241
0
    nsDisplayList* sublist = i->GetSameCoordinateSystemChildren();
5242
0
    if (sublist && !(isBlendContainer && !aCSSBackgroundColor) &&
5243
0
        AddCanvasBackgroundColor(*sublist, aCanvasFrame, aColor, aCSSBackgroundColor))
5244
0
      return true;
5245
0
  }
5246
0
  return false;
5247
0
}
5248
5249
void
5250
PresShell::AddCanvasBackgroundColorItem(nsDisplayListBuilder& aBuilder,
5251
                                        nsDisplayList&        aList,
5252
                                        nsIFrame*             aFrame,
5253
                                        const nsRect&         aBounds,
5254
                                        nscolor               aBackstopColor,
5255
                                        uint32_t              aFlags)
5256
0
{
5257
0
  if (aBounds.IsEmpty()) {
5258
0
    return;
5259
0
  }
5260
0
  // We don't want to add an item for the canvas background color if the frame
5261
0
  // (sub)tree we are painting doesn't include any canvas frames. There isn't
5262
0
  // an easy way to check this directly, but if we check if the root of the
5263
0
  // (sub)tree we are painting is a canvas frame that should cover us in all
5264
0
  // cases (it will usually be a viewport frame when we have a canvas frame in
5265
0
  // the (sub)tree).
5266
0
  if (!(aFlags & nsIPresShell::FORCE_DRAW) &&
5267
0
      !nsCSSRendering::IsCanvasFrame(aFrame)) {
5268
0
    return;
5269
0
  }
5270
0
5271
0
  nscolor bgcolor = NS_ComposeColors(aBackstopColor, mCanvasBackgroundColor);
5272
0
  if (NS_GET_A(bgcolor) == 0)
5273
0
    return;
5274
0
5275
0
  // To make layers work better, we want to avoid having a big non-scrolled
5276
0
  // color background behind a scrolled transparent background. Instead,
5277
0
  // we'll try to move the color background into the scrolled content
5278
0
  // by making nsDisplayCanvasBackground paint it.
5279
0
  // If we're only adding an unscrolled item, then pretend that we've
5280
0
  // already done it.
5281
0
  bool addedScrollingBackgroundColor = (aFlags & APPEND_UNSCROLLED_ONLY);
5282
0
  if (!aFrame->GetParent() && !addedScrollingBackgroundColor) {
5283
0
    nsIScrollableFrame* sf =
5284
0
      aFrame->PresShell()->GetRootScrollFrameAsScrollable();
5285
0
    if (sf) {
5286
0
      nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame());
5287
0
      if (canvasFrame && canvasFrame->IsVisibleForPainting(&aBuilder)) {
5288
0
        addedScrollingBackgroundColor =
5289
0
          AddCanvasBackgroundColor(aList, canvasFrame, bgcolor, mHasCSSBackgroundColor);
5290
0
      }
5291
0
    }
5292
0
  }
5293
0
5294
0
  // With async scrolling, we'd like to have two instances of the background
5295
0
  // color: one that scrolls with the content (for the reasons stated above),
5296
0
  // and one underneath which does not scroll with the content, but which can
5297
0
  // be shown during checkerboarding and overscroll.
5298
0
  // We can only do that if the color is opaque.
5299
0
  bool forceUnscrolledItem = nsLayoutUtils::UsesAsyncScrolling(aFrame) &&
5300
0
                             NS_GET_A(bgcolor) == 255;
5301
0
  if ((aFlags & ADD_FOR_SUBDOC) && gfxPrefs::LayoutUseContainersForRootFrames()) {
5302
0
    // If we're using ContainerLayers for a subdoc, then any items we add here will
5303
0
    // still be scrolled (since we're inside the container at this point), so don't
5304
0
    // bother and we will do it manually later.
5305
0
    forceUnscrolledItem = false;
5306
0
  }
5307
0
5308
0
  if (!addedScrollingBackgroundColor || forceUnscrolledItem) {
5309
0
    aList.AppendToBottom(
5310
0
      MakeDisplayItem<nsDisplaySolidColor>(&aBuilder, aFrame, aBounds, bgcolor));
5311
0
  }
5312
0
}
5313
5314
static bool IsTransparentContainerElement(nsPresContext* aPresContext)
5315
0
{
5316
0
  nsCOMPtr<nsIDocShell> docShell = aPresContext->GetDocShell();
5317
0
  if (!docShell) {
5318
0
    return false;
5319
0
  }
5320
0
5321
0
  nsCOMPtr<nsPIDOMWindowOuter> pwin = docShell->GetWindow();
5322
0
  if (!pwin)
5323
0
    return false;
5324
0
  nsCOMPtr<Element> containerElement = pwin->GetFrameElementInternal();
5325
0
5326
0
  TabChild* tab = TabChild::GetFrom(docShell);
5327
0
  if (tab) {
5328
0
    // Check if presShell is the top PresShell. Only the top can
5329
0
    // influence the canvas background color.
5330
0
    nsCOMPtr<nsIPresShell> presShell = aPresContext->GetPresShell();
5331
0
    nsCOMPtr<nsIPresShell> topPresShell = tab->GetPresShell();
5332
0
    if (presShell != topPresShell) {
5333
0
      tab = nullptr;
5334
0
    }
5335
0
  }
5336
0
5337
0
  return (containerElement &&
5338
0
          containerElement->HasAttr(kNameSpaceID_None, nsGkAtoms::transparent))
5339
0
    || (tab && tab->IsTransparent());
5340
0
}
5341
5342
nscolor PresShell::GetDefaultBackgroundColorToDraw()
5343
0
{
5344
0
  if (!mPresContext || !mPresContext->GetBackgroundColorDraw()) {
5345
0
    return NS_RGB(255,255,255);
5346
0
  }
5347
0
  return mPresContext->DefaultBackgroundColor();
5348
0
}
5349
5350
void PresShell::UpdateCanvasBackground()
5351
0
{
5352
0
  // If we have a frame tree and it has style information that
5353
0
  // specifies the background color of the canvas, update our local
5354
0
  // cache of that color.
5355
0
  nsIFrame* rootStyleFrame = FrameConstructor()->GetRootElementStyleFrame();
5356
0
  if (rootStyleFrame) {
5357
0
    ComputedStyle* bgStyle =
5358
0
      nsCSSRendering::FindRootFrameBackground(rootStyleFrame);
5359
0
    // XXX We should really be passing the canvasframe, not the root element
5360
0
    // style frame but we don't have access to the canvasframe here. It isn't
5361
0
    // a problem because only a few frames can return something other than true
5362
0
    // and none of them would be a canvas frame or root element style frame.
5363
0
    bool drawBackgroundImage;
5364
0
    bool drawBackgroundColor;
5365
0
    mCanvasBackgroundColor =
5366
0
      nsCSSRendering::DetermineBackgroundColor(mPresContext, bgStyle,
5367
0
                                               rootStyleFrame,
5368
0
                                               drawBackgroundImage,
5369
0
                                               drawBackgroundColor);
5370
0
    mHasCSSBackgroundColor = drawBackgroundColor;
5371
0
    if (mPresContext->IsRootContentDocument() &&
5372
0
        !IsTransparentContainerElement(mPresContext)) {
5373
0
      mCanvasBackgroundColor =
5374
0
        NS_ComposeColors(GetDefaultBackgroundColorToDraw(), mCanvasBackgroundColor);
5375
0
    }
5376
0
  }
5377
0
5378
0
  // If the root element of the document (ie html) has style 'display: none'
5379
0
  // then the document's background color does not get drawn; cache the
5380
0
  // color we actually draw.
5381
0
  if (!FrameConstructor()->GetRootElementFrame()) {
5382
0
    mCanvasBackgroundColor = GetDefaultBackgroundColorToDraw();
5383
0
  }
5384
0
}
5385
5386
nscolor PresShell::ComputeBackstopColor(nsView* aDisplayRoot)
5387
0
{
5388
0
  nsIWidget* widget = aDisplayRoot->GetWidget();
5389
0
  if (widget && (widget->GetTransparencyMode() != eTransparencyOpaque ||
5390
0
                 widget->WidgetPaintsBackground())) {
5391
0
    // Within a transparent widget, so the backstop color must be
5392
0
    // totally transparent.
5393
0
    return NS_RGBA(0,0,0,0);
5394
0
  }
5395
0
  // Within an opaque widget (or no widget at all), so the backstop
5396
0
  // color must be totally opaque. The user's default background
5397
0
  // as reported by the prescontext is guaranteed to be opaque.
5398
0
  return GetDefaultBackgroundColorToDraw();
5399
0
}
5400
5401
struct PaintParams {
5402
  nscolor mBackgroundColor;
5403
};
5404
5405
LayerManager* PresShell::GetLayerManager()
5406
0
{
5407
0
  NS_ASSERTION(mViewManager, "Should have view manager");
5408
0
5409
0
  nsView* rootView = mViewManager->GetRootView();
5410
0
  if (rootView) {
5411
0
    if (nsIWidget* widget = rootView->GetWidget()) {
5412
0
      return widget->GetLayerManager();
5413
0
    }
5414
0
  }
5415
0
  return nullptr;
5416
0
}
5417
5418
bool PresShell::AsyncPanZoomEnabled()
5419
0
{
5420
0
  NS_ASSERTION(mViewManager, "Should have view manager");
5421
0
  nsView* rootView = mViewManager->GetRootView();
5422
0
  if (rootView) {
5423
0
    if (nsIWidget* widget = rootView->GetWidget()) {
5424
0
      return widget->AsyncPanZoomEnabled();
5425
0
    }
5426
0
  }
5427
0
  return gfxPlatform::AsyncPanZoomEnabled();
5428
0
}
5429
5430
void PresShell::SetIgnoreViewportScrolling(bool aIgnore)
5431
0
{
5432
0
  if (IgnoringViewportScrolling() == aIgnore) {
5433
0
    return;
5434
0
  }
5435
0
  RenderingState state(this);
5436
0
  state.mRenderFlags = ChangeFlag(state.mRenderFlags, aIgnore,
5437
0
                                  STATE_IGNORING_VIEWPORT_SCROLLING);
5438
0
  SetRenderingState(state);
5439
0
}
5440
5441
nsresult PresShell::SetResolutionImpl(float aResolution, bool aScaleToResolution)
5442
0
{
5443
0
  if (!(aResolution > 0.0)) {
5444
0
    return NS_ERROR_ILLEGAL_VALUE;
5445
0
  }
5446
0
  if (aResolution == mResolution.valueOr(0.0)) {
5447
0
    MOZ_ASSERT(mResolution.isSome());
5448
0
    return NS_OK;
5449
0
  }
5450
0
  RenderingState state(this);
5451
0
  state.mResolution = Some(aResolution);
5452
0
  SetRenderingState(state);
5453
0
  mScaleToResolution = aScaleToResolution;
5454
0
  if (mMobileViewportManager) {
5455
0
    mMobileViewportManager->ResolutionUpdated();
5456
0
  }
5457
0
5458
0
  return NS_OK;
5459
0
}
5460
5461
bool PresShell::ScaleToResolution() const
5462
0
{
5463
0
  return mScaleToResolution;
5464
0
}
5465
5466
float PresShell::GetCumulativeResolution()
5467
0
{
5468
0
  float resolution = GetResolution();
5469
0
  nsPresContext* parentCtx = GetPresContext()->GetParentPresContext();
5470
0
  if (parentCtx) {
5471
0
    resolution *= parentCtx->PresShell()->GetCumulativeResolution();
5472
0
  }
5473
0
  return resolution;
5474
0
}
5475
5476
float PresShell::GetCumulativeNonRootScaleResolution()
5477
0
{
5478
0
  float resolution = 1.0;
5479
0
  nsIPresShell* currentShell = this;
5480
0
  while (currentShell) {
5481
0
    nsPresContext* currentCtx = currentShell->GetPresContext();
5482
0
    if (currentCtx != currentCtx->GetRootPresContext()) {
5483
0
      resolution *=  currentShell->ScaleToResolution() ? currentShell->GetResolution() : 1.0f;
5484
0
    }
5485
0
    nsPresContext* parentCtx = currentCtx->GetParentPresContext();
5486
0
    if (parentCtx) {
5487
0
      currentShell = parentCtx->PresShell();
5488
0
    } else {
5489
0
      currentShell = nullptr;
5490
0
    }
5491
0
  }
5492
0
  return resolution;
5493
0
}
5494
5495
void PresShell::SetRestoreResolution(float aResolution,
5496
                                     LayoutDeviceIntSize aDisplaySize)
5497
0
{
5498
0
  if (mMobileViewportManager) {
5499
0
    mMobileViewportManager->SetRestoreResolution(aResolution, aDisplaySize);
5500
0
  }
5501
0
}
5502
5503
void PresShell::SetRenderingState(const RenderingState& aState)
5504
0
{
5505
0
  if (mRenderFlags != aState.mRenderFlags) {
5506
0
    // Rendering state changed in a way that forces us to flush any
5507
0
    // retained layers we might already have.
5508
0
    LayerManager* manager = GetLayerManager();
5509
0
    if (manager) {
5510
0
      FrameLayerBuilder::InvalidateAllLayers(manager);
5511
0
    }
5512
0
  }
5513
0
5514
0
  mRenderFlags = aState.mRenderFlags;
5515
0
  mResolution = aState.mResolution;
5516
0
}
5517
5518
void PresShell::SynthesizeMouseMove(bool aFromScroll)
5519
0
{
5520
0
  if (!sSynthMouseMove)
5521
0
    return;
5522
0
5523
0
  if (mPaintingSuppressed || !mIsActive || !mPresContext) {
5524
0
    return;
5525
0
  }
5526
0
5527
0
  if (!mPresContext->IsRoot()) {
5528
0
    nsIPresShell* rootPresShell = GetRootPresShell();
5529
0
    if (rootPresShell) {
5530
0
      rootPresShell->SynthesizeMouseMove(aFromScroll);
5531
0
    }
5532
0
    return;
5533
0
  }
5534
0
5535
0
  if (mMouseLocation == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE))
5536
0
    return;
5537
0
5538
0
  if (!mSynthMouseMoveEvent.IsPending()) {
5539
0
    RefPtr<nsSynthMouseMoveEvent> ev =
5540
0
        new nsSynthMouseMoveEvent(this, aFromScroll);
5541
0
5542
0
    if (!GetPresContext()->RefreshDriver()
5543
0
                         ->AddRefreshObserver(ev, FlushType::Display)) {
5544
0
      NS_WARNING("failed to dispatch nsSynthMouseMoveEvent");
5545
0
      return;
5546
0
    }
5547
0
5548
0
    mSynthMouseMoveEvent = std::move(ev);
5549
0
  }
5550
0
}
5551
5552
/**
5553
 * Find the first floating view with a widget in a postorder traversal of the
5554
 * view tree that contains the point. Thus more deeply nested floating views
5555
 * are preferred over their ancestors, and floating views earlier in the
5556
 * view hierarchy (i.e., added later) are preferred over their siblings.
5557
 * This is adequate for finding the "topmost" floating view under a point,
5558
 * given that floating views don't supporting having a specific z-index.
5559
 *
5560
 * We cannot exit early when aPt is outside the view bounds, because floating
5561
 * views aren't necessarily included in their parent's bounds, so this could
5562
 * traverse the entire view hierarchy --- use carefully.
5563
 */
5564
static nsView* FindFloatingViewContaining(nsView* aView, nsPoint aPt)
5565
0
{
5566
0
  if (aView->GetVisibility() == nsViewVisibility_kHide)
5567
0
    // No need to look into descendants.
5568
0
    return nullptr;
5569
0
5570
0
  nsIFrame* frame = aView->GetFrame();
5571
0
  if (frame) {
5572
0
    if (!frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) ||
5573
0
        !frame->PresShell()->IsActive()) {
5574
0
      return nullptr;
5575
0
    }
5576
0
  }
5577
0
5578
0
  for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
5579
0
    nsView* r = FindFloatingViewContaining(v, v->ConvertFromParentCoords(aPt));
5580
0
    if (r)
5581
0
      return r;
5582
0
  }
5583
0
5584
0
  if (aView->GetFloating() && aView->HasWidget() &&
5585
0
      aView->GetDimensions().Contains(aPt))
5586
0
    return aView;
5587
0
5588
0
  return nullptr;
5589
0
}
5590
5591
/*
5592
 * This finds the first view containing the given point in a postorder
5593
 * traversal of the view tree that contains the point, assuming that the
5594
 * point is not in a floating view.  It assumes that only floating views
5595
 * extend outside the bounds of their parents.
5596
 *
5597
 * This methods should only be called if FindFloatingViewContaining
5598
 * returns null.
5599
 */
5600
static nsView* FindViewContaining(nsView* aView, nsPoint aPt)
5601
0
{
5602
0
  if (!aView->GetDimensions().Contains(aPt) ||
5603
0
      aView->GetVisibility() == nsViewVisibility_kHide) {
5604
0
    return nullptr;
5605
0
  }
5606
0
5607
0
  nsIFrame* frame = aView->GetFrame();
5608
0
  if (frame) {
5609
0
    if (!frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) ||
5610
0
        !frame->PresShell()->IsActive()) {
5611
0
      return nullptr;
5612
0
    }
5613
0
  }
5614
0
5615
0
  for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
5616
0
    nsView* r = FindViewContaining(v, v->ConvertFromParentCoords(aPt));
5617
0
    if (r)
5618
0
      return r;
5619
0
  }
5620
0
5621
0
  return aView;
5622
0
}
5623
5624
void
5625
PresShell::ProcessSynthMouseMoveEvent(bool aFromScroll)
5626
0
{
5627
0
  // If drag session has started, we shouldn't synthesize mousemove event.
5628
0
  nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
5629
0
  if (dragSession) {
5630
0
    mSynthMouseMoveEvent.Forget();
5631
0
    return;
5632
0
  }
5633
0
5634
0
  // allow new event to be posted while handling this one only if the
5635
0
  // source of the event is a scroll (to prevent infinite reflow loops)
5636
0
  if (aFromScroll) {
5637
0
    mSynthMouseMoveEvent.Forget();
5638
0
  }
5639
0
5640
0
  nsView* rootView = mViewManager ? mViewManager->GetRootView() : nullptr;
5641
0
  if (mMouseLocation == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) ||
5642
0
      !rootView || !rootView->HasWidget() || !mPresContext) {
5643
0
    mSynthMouseMoveEvent.Forget();
5644
0
    return;
5645
0
  }
5646
0
5647
0
  NS_ASSERTION(mPresContext->IsRoot(), "Only a root pres shell should be here");
5648
0
5649
0
  // Hold a ref to ourselves so DispatchEvent won't destroy us (since
5650
0
  // we need to access members after we call DispatchEvent).
5651
0
  nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
5652
0
5653
#ifdef DEBUG_MOUSE_LOCATION
5654
  printf("[ps=%p]synthesizing mouse move to (%d,%d)\n",
5655
         this, mMouseLocation.x, mMouseLocation.y);
5656
#endif
5657
5658
0
  int32_t APD = mPresContext->AppUnitsPerDevPixel();
5659
0
5660
0
  // We need a widget to put in the event we are going to dispatch so we look
5661
0
  // for a view that has a widget and the mouse location is over. We first look
5662
0
  // for floating views, if there isn't one we use the root view. |view| holds
5663
0
  // that view.
5664
0
  nsView* view = nullptr;
5665
0
5666
0
  // The appunits per devpixel ratio of |view|.
5667
0
  int32_t viewAPD;
5668
0
5669
0
  // mRefPoint will be mMouseLocation relative to the widget of |view|, the
5670
0
  // widget we will put in the event we dispatch, in viewAPD appunits
5671
0
  nsPoint refpoint(0, 0);
5672
0
5673
0
  // We always dispatch the event to the pres shell that contains the view that
5674
0
  // the mouse is over. pointVM is the VM of that pres shell.
5675
0
  nsViewManager *pointVM = nullptr;
5676
0
5677
0
  // This could be a bit slow (traverses entire view hierarchy)
5678
0
  // but it's OK to do it once per synthetic mouse event
5679
0
  view = FindFloatingViewContaining(rootView, mMouseLocation);
5680
0
  if (!view) {
5681
0
    view = rootView;
5682
0
    nsView *pointView = FindViewContaining(rootView, mMouseLocation);
5683
0
    // pointView can be null in situations related to mouse capture
5684
0
    pointVM = (pointView ? pointView : view)->GetViewManager();
5685
0
    refpoint = mMouseLocation + rootView->ViewToWidgetOffset();
5686
0
    viewAPD = APD;
5687
0
  } else {
5688
0
    pointVM = view->GetViewManager();
5689
0
    nsIFrame* frame = view->GetFrame();
5690
0
    NS_ASSERTION(frame, "floating views can't be anonymous");
5691
0
    viewAPD = frame->PresContext()->AppUnitsPerDevPixel();
5692
0
    refpoint = mMouseLocation.ScaleToOtherAppUnits(APD, viewAPD);
5693
0
    refpoint -= view->GetOffsetTo(rootView);
5694
0
    refpoint += view->ViewToWidgetOffset();
5695
0
  }
5696
0
  NS_ASSERTION(view->GetWidget(), "view should have a widget here");
5697
0
  WidgetMouseEvent event(true, eMouseMove, view->GetWidget(),
5698
0
                         WidgetMouseEvent::eSynthesized);
5699
0
  event.mRefPoint =
5700
0
    LayoutDeviceIntPoint::FromAppUnitsToNearest(refpoint, viewAPD);
5701
0
  event.mTime = PR_IntervalNow();
5702
0
  // XXX set event.mModifiers ?
5703
0
  // XXX mnakano I think that we should get the latest information from widget.
5704
0
5705
0
  nsCOMPtr<nsIPresShell> shell = pointVM->GetPresShell();
5706
0
  if (shell) {
5707
0
    // Since this gets run in a refresh tick there isn't an InputAPZContext on
5708
0
    // the stack from the nsBaseWidget. We need to simulate one with at least
5709
0
    // the correct target guid, so that the correct callback transform gets
5710
0
    // applied if this event goes to a child process. The input block id is set
5711
0
    // to 0 because this is a synthetic event which doesn't really belong to any
5712
0
    // input block. Same for the APZ response field.
5713
0
    InputAPZContext apzContext(mMouseEventTargetGuid, 0, nsEventStatus_eIgnore);
5714
0
    shell->DispatchSynthMouseMove(&event);
5715
0
  }
5716
0
5717
0
  if (!aFromScroll) {
5718
0
    mSynthMouseMoveEvent.Forget();
5719
0
  }
5720
0
}
5721
5722
/* static */ void
5723
PresShell::MarkFramesInListApproximatelyVisible(const nsDisplayList& aList)
5724
0
{
5725
0
  for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) {
5726
0
    nsDisplayList* sublist = item->GetChildren();
5727
0
    if (sublist) {
5728
0
      MarkFramesInListApproximatelyVisible(*sublist);
5729
0
      continue;
5730
0
    }
5731
0
5732
0
    nsIFrame* frame = item->Frame();
5733
0
    MOZ_ASSERT(frame);
5734
0
5735
0
    if (!frame->TrackingVisibility()) {
5736
0
      continue;
5737
0
    }
5738
0
5739
0
    // Use the presshell containing the frame.
5740
0
    auto* presShell = static_cast<PresShell*>(frame->PresShell());
5741
0
    MOZ_ASSERT(!presShell->AssumeAllFramesVisible());
5742
0
    if (presShell->mApproximatelyVisibleFrames.EnsureInserted(frame)) {
5743
0
      // The frame was added to mApproximatelyVisibleFrames, so increment its visible count.
5744
0
      frame->IncApproximateVisibleCount();
5745
0
    }
5746
0
  }
5747
0
}
5748
5749
/* static */ void
5750
PresShell::DecApproximateVisibleCount(VisibleFrames& aFrames,
5751
                                      const Maybe<OnNonvisible>& aNonvisibleAction
5752
                                        /* = Nothing() */)
5753
0
{
5754
0
  for (auto iter = aFrames.Iter(); !iter.Done(); iter.Next()) {
5755
0
    nsIFrame* frame = iter.Get()->GetKey();
5756
0
    // Decrement the frame's visible count if we're still tracking its
5757
0
    // visibility. (We may not be, if the frame disabled visibility tracking
5758
0
    // after we added it to the visible frames list.)
5759
0
    if (frame->TrackingVisibility()) {
5760
0
      frame->DecApproximateVisibleCount(aNonvisibleAction);
5761
0
    }
5762
0
  }
5763
0
}
5764
5765
void
5766
PresShell::RebuildApproximateFrameVisibilityDisplayList(const nsDisplayList& aList)
5767
0
{
5768
0
  MOZ_ASSERT(!mApproximateFrameVisibilityVisited, "already visited?");
5769
0
  mApproximateFrameVisibilityVisited = true;
5770
0
5771
0
  // Remove the entries of the mApproximatelyVisibleFrames hashtable and put
5772
0
  // them in oldApproxVisibleFrames.
5773
0
  VisibleFrames oldApproximatelyVisibleFrames;
5774
0
  mApproximatelyVisibleFrames.SwapElements(oldApproximatelyVisibleFrames);
5775
0
5776
0
  MarkFramesInListApproximatelyVisible(aList);
5777
0
5778
0
  DecApproximateVisibleCount(oldApproximatelyVisibleFrames);
5779
0
}
5780
5781
/* static */ void
5782
PresShell::ClearApproximateFrameVisibilityVisited(nsView* aView, bool aClear)
5783
0
{
5784
0
  nsViewManager* vm = aView->GetViewManager();
5785
0
  if (aClear) {
5786
0
    PresShell* presShell = static_cast<PresShell*>(vm->GetPresShell());
5787
0
    if (!presShell->mApproximateFrameVisibilityVisited) {
5788
0
      presShell->ClearApproximatelyVisibleFramesList();
5789
0
    }
5790
0
    presShell->mApproximateFrameVisibilityVisited = false;
5791
0
  }
5792
0
  for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
5793
0
    ClearApproximateFrameVisibilityVisited(v, v->GetViewManager() != vm);
5794
0
  }
5795
0
}
5796
5797
void
5798
PresShell::ClearApproximatelyVisibleFramesList(const Maybe<OnNonvisible>& aNonvisibleAction
5799
                                                 /* = Nothing() */)
5800
0
{
5801
0
  DecApproximateVisibleCount(mApproximatelyVisibleFrames, aNonvisibleAction);
5802
0
  mApproximatelyVisibleFrames.Clear();
5803
0
}
5804
5805
void
5806
PresShell::MarkFramesInSubtreeApproximatelyVisible(nsIFrame* aFrame,
5807
                                                   const nsRect& aRect,
5808
                                                   bool aRemoveOnly /* = false */)
5809
0
{
5810
0
  MOZ_ASSERT(aFrame->PresShell() == this, "wrong presshell");
5811
0
5812
0
  if (aFrame->TrackingVisibility() &&
5813
0
      aFrame->StyleVisibility()->IsVisible() &&
5814
0
      (!aRemoveOnly || aFrame->GetVisibility() == Visibility::APPROXIMATELY_VISIBLE)) {
5815
0
    MOZ_ASSERT(!AssumeAllFramesVisible());
5816
0
    if (mApproximatelyVisibleFrames.EnsureInserted(aFrame)) {
5817
0
      // The frame was added to mApproximatelyVisibleFrames, so increment its visible count.
5818
0
      aFrame->IncApproximateVisibleCount();
5819
0
    }
5820
0
  }
5821
0
5822
0
  nsSubDocumentFrame* subdocFrame = do_QueryFrame(aFrame);
5823
0
  if (subdocFrame) {
5824
0
    nsIPresShell* presShell = subdocFrame->GetSubdocumentPresShellForPainting(
5825
0
      nsSubDocumentFrame::IGNORE_PAINT_SUPPRESSION);
5826
0
    if (presShell && !presShell->AssumeAllFramesVisible()) {
5827
0
      nsRect rect = aRect;
5828
0
      nsIFrame* root = presShell->GetRootFrame();
5829
0
      if (root) {
5830
0
        rect.MoveBy(aFrame->GetOffsetToCrossDoc(root));
5831
0
      } else {
5832
0
        rect.MoveBy(-aFrame->GetContentRectRelativeToSelf().TopLeft());
5833
0
      }
5834
0
      rect = rect.ScaleToOtherAppUnitsRoundOut(
5835
0
        aFrame->PresContext()->AppUnitsPerDevPixel(),
5836
0
        presShell->GetPresContext()->AppUnitsPerDevPixel());
5837
0
5838
0
      presShell->RebuildApproximateFrameVisibility(&rect);
5839
0
    }
5840
0
    return;
5841
0
  }
5842
0
5843
0
  nsRect rect = aRect;
5844
0
5845
0
  nsIScrollableFrame* scrollFrame = do_QueryFrame(aFrame);
5846
0
  if (scrollFrame) {
5847
0
    bool ignoreDisplayPort = false;
5848
0
    if (nsLayoutUtils::IsMissingDisplayPortBaseRect(aFrame->GetContent())) {
5849
0
      // We can properly set the base rect for root scroll frames on top level
5850
0
      // and root content documents. Otherwise the base rect we compute might
5851
0
      // be way too big without the limiting that
5852
0
      // ScrollFrameHelper::DecideScrollableLayer does, so we just ignore the
5853
0
      // displayport in that case.
5854
0
      nsPresContext* pc = aFrame->PresContext();
5855
0
      if (scrollFrame->IsRootScrollFrameOfDocument() &&
5856
0
          (pc->IsRootContentDocument() || !pc->GetParentPresContext())) {
5857
0
        nsRect baseRect =
5858
0
          nsRect(nsPoint(0, 0), nsLayoutUtils::CalculateCompositionSizeForFrame(aFrame));
5859
0
        nsLayoutUtils::SetDisplayPortBase(aFrame->GetContent(), baseRect);
5860
0
      } else {
5861
0
        ignoreDisplayPort = true;
5862
0
      }
5863
0
    }
5864
0
5865
0
    nsRect displayPort;
5866
0
    bool usingDisplayport = !ignoreDisplayPort &&
5867
0
      nsLayoutUtils::GetDisplayPortForVisibilityTesting(
5868
0
        aFrame->GetContent(), &displayPort, RelativeTo::ScrollFrame);
5869
0
5870
0
    scrollFrame->NotifyApproximateFrameVisibilityUpdate(!usingDisplayport);
5871
0
5872
0
    if (usingDisplayport) {
5873
0
      rect = displayPort;
5874
0
    } else {
5875
0
      rect = rect.Intersect(scrollFrame->GetScrollPortRect());
5876
0
    }
5877
0
    rect = scrollFrame->ExpandRectToNearlyVisible(rect);
5878
0
  }
5879
0
5880
0
  bool preserves3DChildren = aFrame->Extend3DContext();
5881
0
5882
0
  // We assume all frames in popups are visible, so we skip them here.
5883
0
  const nsIFrame::ChildListIDs skip(nsIFrame::kPopupList |
5884
0
                                    nsIFrame::kSelectPopupList);
5885
0
  for (nsIFrame::ChildListIterator childLists(aFrame);
5886
0
       !childLists.IsDone(); childLists.Next()) {
5887
0
    if (skip.Contains(childLists.CurrentID())) {
5888
0
      continue;
5889
0
    }
5890
0
5891
0
    for (nsIFrame* child : childLists.CurrentList()) {
5892
0
      nsRect r = rect - child->GetPosition();
5893
0
      if (!r.IntersectRect(r, child->GetVisualOverflowRect())) {
5894
0
        continue;
5895
0
      }
5896
0
      if (child->IsTransformed()) {
5897
0
        // for children of a preserve3d element we just pass down the same dirty rect
5898
0
        if (!preserves3DChildren || !child->Combines3DTransformWithAncestors()) {
5899
0
          const nsRect overflow = child->GetVisualOverflowRectRelativeToSelf();
5900
0
          nsRect out;
5901
0
          if (nsDisplayTransform::UntransformRect(r, overflow, child, &out)) {
5902
0
            r = out;
5903
0
          } else {
5904
0
            r.SetEmpty();
5905
0
          }
5906
0
        }
5907
0
      }
5908
0
      MarkFramesInSubtreeApproximatelyVisible(child, r);
5909
0
    }
5910
0
  }
5911
0
}
5912
5913
void
5914
PresShell::RebuildApproximateFrameVisibility(nsRect* aRect,
5915
                                             bool aRemoveOnly /* = false */)
5916
0
{
5917
0
  MOZ_ASSERT(!mApproximateFrameVisibilityVisited, "already visited?");
5918
0
  mApproximateFrameVisibilityVisited = true;
5919
0
5920
0
  nsIFrame* rootFrame = GetRootFrame();
5921
0
  if (!rootFrame) {
5922
0
    return;
5923
0
  }
5924
0
5925
0
  // Remove the entries of the mApproximatelyVisibleFrames hashtable and put
5926
0
  // them in oldApproximatelyVisibleFrames.
5927
0
  VisibleFrames oldApproximatelyVisibleFrames;
5928
0
  mApproximatelyVisibleFrames.SwapElements(oldApproximatelyVisibleFrames);
5929
0
5930
0
  nsRect vis(nsPoint(0, 0), rootFrame->GetSize());
5931
0
  if (aRect) {
5932
0
    vis = *aRect;
5933
0
  }
5934
0
5935
0
  MarkFramesInSubtreeApproximatelyVisible(rootFrame, vis, aRemoveOnly);
5936
0
5937
0
  DecApproximateVisibleCount(oldApproximatelyVisibleFrames);
5938
0
}
5939
5940
void
5941
PresShell::UpdateApproximateFrameVisibility()
5942
0
{
5943
0
  DoUpdateApproximateFrameVisibility(/* aRemoveOnly = */ false);
5944
0
}
5945
5946
void
5947
PresShell::DoUpdateApproximateFrameVisibility(bool aRemoveOnly)
5948
0
{
5949
0
  MOZ_ASSERT(!mPresContext || mPresContext->IsRootContentDocument(),
5950
0
             "Updating approximate frame visibility on a non-root content document?");
5951
0
5952
0
  mUpdateApproximateFrameVisibilityEvent.Revoke();
5953
0
5954
0
  if (mHaveShutDown || mIsDestroying) {
5955
0
    return;
5956
0
  }
5957
0
5958
0
  // call update on that frame
5959
0
  nsIFrame* rootFrame = GetRootFrame();
5960
0
  if (!rootFrame) {
5961
0
    ClearApproximatelyVisibleFramesList(Some(OnNonvisible::DISCARD_IMAGES));
5962
0
    return;
5963
0
  }
5964
0
5965
0
  RebuildApproximateFrameVisibility(/* aRect = */ nullptr, aRemoveOnly);
5966
0
  ClearApproximateFrameVisibilityVisited(rootFrame->GetView(), true);
5967
0
5968
#ifdef DEBUG_FRAME_VISIBILITY_DISPLAY_LIST
5969
  // This can be used to debug the frame walker by comparing beforeFrameList
5970
  // and mApproximatelyVisibleFrames in RebuildFrameVisibilityDisplayList to see if
5971
  // they produce the same results (mApproximatelyVisibleFrames holds the frames the
5972
  // display list thinks are visible, beforeFrameList holds the frames the
5973
  // frame walker thinks are visible).
5974
  nsDisplayListBuilder builder(rootFrame, nsDisplayListBuilderMode::FRAME_VISIBILITY, false);
5975
  nsRect updateRect(nsPoint(0, 0), rootFrame->GetSize());
5976
  nsIFrame* rootScroll = GetRootScrollFrame();
5977
  if (rootScroll) {
5978
    nsIContent* content = rootScroll->GetContent();
5979
    if (content) {
5980
      Unused << nsLayoutUtils::GetDisplayPortForVisibilityTesting(content, &updateRect,
5981
        RelativeTo::ScrollFrame);
5982
    }
5983
5984
    if (IgnoringViewportScrolling()) {
5985
      builder.SetIgnoreScrollFrame(rootScroll);
5986
    }
5987
  }
5988
  builder.IgnorePaintSuppression();
5989
  builder.EnterPresShell(rootFrame);
5990
  nsDisplayList list;
5991
  rootFrame->BuildDisplayListForStackingContext(&builder, updateRect, &list);
5992
  builder.LeavePresShell(rootFrame, &list);
5993
5994
  RebuildApproximateFrameVisibilityDisplayList(list);
5995
5996
  ClearApproximateFrameVisibilityVisited(rootFrame->GetView(), true);
5997
5998
  list.DeleteAll(&builder);
5999
#endif
6000
}
6001
6002
bool
6003
PresShell::AssumeAllFramesVisible()
6004
0
{
6005
0
  static bool sFrameVisibilityEnabled = true;
6006
0
  static bool sFrameVisibilityPrefCached = false;
6007
0
6008
0
  if (!sFrameVisibilityPrefCached) {
6009
0
    Preferences::AddBoolVarCache(&sFrameVisibilityEnabled,
6010
0
      "layout.framevisibility.enabled", true);
6011
0
    sFrameVisibilityPrefCached = true;
6012
0
  }
6013
0
6014
0
  if (!sFrameVisibilityEnabled || !mPresContext || !mDocument) {
6015
0
    return true;
6016
0
  }
6017
0
6018
0
  // We assume all frames are visible in print, print preview, chrome, and
6019
0
  // resource docs and don't keep track of them.
6020
0
  if (mPresContext->Type() == nsPresContext::eContext_PrintPreview ||
6021
0
      mPresContext->Type() == nsPresContext::eContext_Print ||
6022
0
      mPresContext->IsChrome() ||
6023
0
      mDocument->IsResourceDoc()) {
6024
0
    return true;
6025
0
  }
6026
0
6027
0
  // If we're assuming all frames are visible in the top level content
6028
0
  // document, we need to in subdocuments as well. Otherwise we can get in a
6029
0
  // situation where things like animations won't work in subdocuments because
6030
0
  // their frames appear not to be visible, since we won't schedule an image
6031
0
  // visibility update if the top level content document is assuming all
6032
0
  // frames are visible.
6033
0
  //
6034
0
  // Note that it's not safe to call IsRootContentDocument() if we're
6035
0
  // currently being destroyed, so we have to check that first.
6036
0
  if (!mHaveShutDown && !mIsDestroying &&
6037
0
      !mPresContext->IsRootContentDocument()) {
6038
0
    nsPresContext* presContext =
6039
0
      mPresContext->GetToplevelContentDocumentPresContext();
6040
0
    if (presContext && presContext->PresShell()->AssumeAllFramesVisible()) {
6041
0
      return true;
6042
0
    }
6043
0
  }
6044
0
6045
0
  return false;
6046
0
}
6047
6048
void
6049
PresShell::ScheduleApproximateFrameVisibilityUpdateSoon()
6050
0
{
6051
0
  if (AssumeAllFramesVisible()) {
6052
0
    return;
6053
0
  }
6054
0
6055
0
  if (!mPresContext) {
6056
0
    return;
6057
0
  }
6058
0
6059
0
  nsRefreshDriver* refreshDriver = mPresContext->RefreshDriver();
6060
0
  if (!refreshDriver) {
6061
0
    return;
6062
0
  }
6063
0
6064
0
  // Ask the refresh driver to update frame visibility soon.
6065
0
  refreshDriver->ScheduleFrameVisibilityUpdate();
6066
0
}
6067
6068
void
6069
PresShell::ScheduleApproximateFrameVisibilityUpdateNow()
6070
0
{
6071
0
  if (AssumeAllFramesVisible()) {
6072
0
    return;
6073
0
  }
6074
0
6075
0
  if (!mPresContext->IsRootContentDocument()) {
6076
0
    nsPresContext* presContext = mPresContext->GetToplevelContentDocumentPresContext();
6077
0
    if (!presContext)
6078
0
      return;
6079
0
    MOZ_ASSERT(presContext->IsRootContentDocument(),
6080
0
      "Didn't get a root prescontext from GetToplevelContentDocumentPresContext?");
6081
0
    presContext->PresShell()->ScheduleApproximateFrameVisibilityUpdateNow();
6082
0
    return;
6083
0
  }
6084
0
6085
0
  if (mHaveShutDown || mIsDestroying) {
6086
0
    return;
6087
0
  }
6088
0
6089
0
  if (mUpdateApproximateFrameVisibilityEvent.IsPending()) {
6090
0
    return;
6091
0
  }
6092
0
6093
0
  RefPtr<nsRunnableMethod<PresShell>> event =
6094
0
    NewRunnableMethod("PresShell::UpdateApproximateFrameVisibility",
6095
0
                      this,
6096
0
                      &PresShell::UpdateApproximateFrameVisibility);
6097
0
  nsresult rv =
6098
0
    mDocument->Dispatch(TaskCategory::Other, do_AddRef(event));
6099
0
6100
0
  if (NS_SUCCEEDED(rv)) {
6101
0
    mUpdateApproximateFrameVisibilityEvent = std::move(event);
6102
0
  }
6103
0
}
6104
6105
void
6106
PresShell::EnsureFrameInApproximatelyVisibleList(nsIFrame* aFrame)
6107
0
{
6108
0
  if (!aFrame->TrackingVisibility()) {
6109
0
    return;
6110
0
  }
6111
0
6112
0
  if (AssumeAllFramesVisible()) {
6113
0
    aFrame->IncApproximateVisibleCount();
6114
0
    return;
6115
0
  }
6116
0
6117
#ifdef DEBUG
6118
  // Make sure it's in this pres shell.
6119
  nsCOMPtr<nsIContent> content = aFrame->GetContent();
6120
  if (content) {
6121
    PresShell* shell = static_cast<PresShell*>(content->OwnerDoc()->GetShell());
6122
    MOZ_ASSERT(!shell || shell == this, "wrong shell");
6123
  }
6124
#endif
6125
6126
0
  if (mApproximatelyVisibleFrames.EnsureInserted(aFrame)) {
6127
0
    // We inserted a new entry.
6128
0
    aFrame->IncApproximateVisibleCount();
6129
0
  }
6130
0
}
6131
6132
void
6133
PresShell::RemoveFrameFromApproximatelyVisibleList(nsIFrame* aFrame)
6134
0
{
6135
#ifdef DEBUG
6136
  // Make sure it's in this pres shell.
6137
  nsCOMPtr<nsIContent> content = aFrame->GetContent();
6138
  if (content) {
6139
    PresShell* shell = static_cast<PresShell*>(content->OwnerDoc()->GetShell());
6140
    MOZ_ASSERT(!shell || shell == this, "wrong shell");
6141
  }
6142
#endif
6143
6144
0
  if (AssumeAllFramesVisible()) {
6145
0
    MOZ_ASSERT(mApproximatelyVisibleFrames.Count() == 0,
6146
0
               "Shouldn't have any frames in the table");
6147
0
    return;
6148
0
  }
6149
0
6150
0
  if (mApproximatelyVisibleFrames.EnsureRemoved(aFrame) &&
6151
0
      aFrame->TrackingVisibility()) {
6152
0
    // aFrame was in the hashtable, and we're still tracking its visibility,
6153
0
    // so we need to decrement its visible count.
6154
0
    aFrame->DecApproximateVisibleCount();
6155
0
  }
6156
0
}
6157
6158
class nsAutoNotifyDidPaint
6159
{
6160
public:
6161
  nsAutoNotifyDidPaint(PresShell* aShell, uint32_t aFlags)
6162
    : mShell(aShell), mFlags(aFlags)
6163
0
  {
6164
0
  }
6165
  ~nsAutoNotifyDidPaint()
6166
0
  {
6167
0
    if (mFlags & nsIPresShell::PAINT_COMPOSITE) {
6168
0
      mShell->GetPresContext()->NotifyDidPaintForSubtree();
6169
0
    }
6170
0
  }
6171
6172
private:
6173
  PresShell* mShell;
6174
  uint32_t mFlags;
6175
};
6176
6177
void
6178
nsIPresShell::RecordShadowStyleChange(ShadowRoot& aShadowRoot)
6179
0
{
6180
0
  mStyleSet->RecordShadowStyleChange(aShadowRoot);
6181
0
  ApplicableStylesChanged();
6182
0
}
6183
6184
void
6185
PresShell::Paint(nsView*         aViewToPaint,
6186
                 const nsRegion& aDirtyRegion,
6187
                 uint32_t        aFlags)
6188
0
{
6189
0
#ifdef MOZ_GECKO_PROFILER
6190
0
  nsIURI* uri = mDocument->GetDocumentURI();
6191
0
  nsIDocument* contentRoot = GetPrimaryContentDocument();
6192
0
  if (contentRoot) {
6193
0
    uri = contentRoot->GetDocumentURI();
6194
0
  }
6195
0
  AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(
6196
0
    "PresShell::Paint", GRAPHICS,
6197
0
    uri ? uri->GetSpecOrDefault() : NS_LITERAL_CSTRING("N/A"));
6198
0
#endif
6199
0
6200
0
  Maybe<js::AutoAssertNoContentJS> nojs;
6201
0
6202
0
  // On Android, Flash can call into content JS during painting, so we can't
6203
0
  // assert there. However, we don't rely on this assertion on Android because
6204
0
  // we don't paint while JS is running.
6205
0
#if !defined(MOZ_WIDGET_ANDROID)
6206
0
  if (!(aFlags & nsIPresShell::PAINT_COMPOSITE)) {
6207
0
    // We need to allow content JS when the flag is set since we may trigger
6208
0
    // MozAfterPaint events in content in those cases.
6209
0
    nojs.emplace(dom::danger::GetJSContext());
6210
0
  }
6211
0
#endif
6212
0
6213
0
  NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell");
6214
0
  NS_ASSERTION(aViewToPaint, "null view");
6215
0
6216
0
  MOZ_ASSERT(!mApproximateFrameVisibilityVisited, "Should have been cleared");
6217
0
6218
0
  if (!mIsActive) {
6219
0
    return;
6220
0
  }
6221
0
6222
0
  if (gfxPrefs::APZKeyboardEnabled()) {
6223
0
    // Update the focus target for async keyboard scrolling. This will be forwarded
6224
0
    // to APZ by nsDisplayList::PaintRoot. We need to to do this before we enter
6225
0
    // the paint phase because dispatching eVoid events can cause layout to happen.
6226
0
    mAPZFocusTarget = FocusTarget(this, mAPZFocusSequenceNumber);
6227
0
  }
6228
0
6229
0
  nsPresContext* presContext = GetPresContext();
6230
0
  AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext, Paint);
6231
0
6232
0
  nsIFrame* frame = aViewToPaint->GetFrame();
6233
0
6234
0
  LayerManager* layerManager =
6235
0
    aViewToPaint->GetWidget()->GetLayerManager();
6236
0
  NS_ASSERTION(layerManager, "Must be in paint event");
6237
0
  bool shouldInvalidate = layerManager->NeedsWidgetInvalidation();
6238
0
6239
0
  nsAutoNotifyDidPaint notifyDidPaint(this, aFlags);
6240
0
6241
0
  // Whether or not we should set first paint when painting is suppressed
6242
0
  // is debatable. For now we'll do it because B2G relied on first paint
6243
0
  // to configure the viewport and we only want to do that when we have
6244
0
  // real content to paint. See Bug 798245
6245
0
  if (mIsFirstPaint && !mPaintingSuppressed) {
6246
0
    layerManager->SetIsFirstPaint();
6247
0
    mIsFirstPaint = false;
6248
0
  }
6249
0
6250
0
  if (!layerManager->BeginTransaction()) {
6251
0
    return;
6252
0
  }
6253
0
6254
0
  // Send an updated focus target with this transaction. Be sure to do this
6255
0
  // before we paint in the case this is an empty transaction.
6256
0
  layerManager->SetFocusTarget(mAPZFocusTarget);
6257
0
6258
0
  if (frame) {
6259
0
    // Try to do an empty transaction, if the frame tree does not
6260
0
    // need to be updated. Do not try to do an empty transaction on
6261
0
    // a non-retained layer manager (like the BasicLayerManager that
6262
0
    // draws the window title bar on Mac), because a) it won't work
6263
0
    // and b) below we don't want to clear NS_FRAME_UPDATE_LAYER_TREE,
6264
0
    // that will cause us to forget to update the real layer manager!
6265
0
6266
0
    if (!(aFlags & PAINT_LAYERS)) {
6267
0
      if (layerManager->EndEmptyTransaction()) {
6268
0
        return;
6269
0
      }
6270
0
      NS_WARNING("Must complete empty transaction when compositing!");
6271
0
    }
6272
0
6273
0
    if (!(aFlags & PAINT_SYNC_DECODE_IMAGES) &&
6274
0
        !(frame->GetStateBits() & NS_FRAME_UPDATE_LAYER_TREE) &&
6275
0
        !mNextPaintCompressed) {
6276
0
      NotifySubDocInvalidationFunc computeInvalidFunc =
6277
0
        presContext->MayHavePaintEventListenerInSubDocument() ? nsPresContext::NotifySubDocInvalidation : 0;
6278
0
      bool computeInvalidRect = computeInvalidFunc ||
6279
0
                                (layerManager->GetBackendType() == LayersBackend::LAYERS_BASIC);
6280
0
6281
0
      UniquePtr<LayerProperties> props;
6282
0
      // For WR, the layermanager has no root layer. We want to avoid
6283
0
      // calling ComputeDifferences in that case because it assumes non-null
6284
0
      // and crashes.
6285
0
      if (computeInvalidRect && layerManager->GetRoot()) {
6286
0
        props = LayerProperties::CloneFrom(layerManager->GetRoot());
6287
0
      }
6288
0
6289
0
      MaybeSetupTransactionIdAllocator(layerManager, presContext);
6290
0
6291
0
      if (layerManager->EndEmptyTransaction((aFlags & PAINT_COMPOSITE) ?
6292
0
            LayerManager::END_DEFAULT : LayerManager::END_NO_COMPOSITE)) {
6293
0
        nsIntRegion invalid;
6294
0
        bool areaOverflowed = false;
6295
0
        if (props) {
6296
0
          if (!props->ComputeDifferences(layerManager->GetRoot(), invalid, computeInvalidFunc)) {
6297
0
            areaOverflowed = true;
6298
0
          }
6299
0
        } else {
6300
0
          LayerProperties::ClearInvalidations(layerManager->GetRoot());
6301
0
        }
6302
0
        if (props && !areaOverflowed) {
6303
0
          if (!invalid.IsEmpty()) {
6304
0
            nsIntRect bounds = invalid.GetBounds();
6305
0
            nsRect rect(presContext->DevPixelsToAppUnits(bounds.x),
6306
0
                        presContext->DevPixelsToAppUnits(bounds.y),
6307
0
                        presContext->DevPixelsToAppUnits(bounds.width),
6308
0
                        presContext->DevPixelsToAppUnits(bounds.height));
6309
0
            if (shouldInvalidate) {
6310
0
              aViewToPaint->GetViewManager()->InvalidateViewNoSuppression(aViewToPaint, rect);
6311
0
            }
6312
0
            presContext->NotifyInvalidation(layerManager->GetLastTransactionId(), bounds);
6313
0
          }
6314
0
        } else if (shouldInvalidate) {
6315
0
          aViewToPaint->GetViewManager()->InvalidateView(aViewToPaint);
6316
0
        }
6317
0
6318
0
        frame->UpdatePaintCountForPaintedPresShells();
6319
0
        return;
6320
0
      }
6321
0
    }
6322
0
    frame->RemoveStateBits(NS_FRAME_UPDATE_LAYER_TREE);
6323
0
  }
6324
0
  if (frame) {
6325
0
    frame->ClearPresShellsFromLastPaint();
6326
0
  }
6327
0
6328
0
  nscolor bgcolor = ComputeBackstopColor(aViewToPaint);
6329
0
  PaintFrameFlags flags = PaintFrameFlags::PAINT_WIDGET_LAYERS |
6330
0
                          PaintFrameFlags::PAINT_EXISTING_TRANSACTION;
6331
0
  if (!(aFlags & PAINT_COMPOSITE)) {
6332
0
    flags |= PaintFrameFlags::PAINT_NO_COMPOSITE;
6333
0
  }
6334
0
  if (aFlags & PAINT_SYNC_DECODE_IMAGES) {
6335
0
    flags |= PaintFrameFlags::PAINT_SYNC_DECODE_IMAGES;
6336
0
  }
6337
0
  if (mNextPaintCompressed) {
6338
0
    flags |= PaintFrameFlags::PAINT_COMPRESSED;
6339
0
    mNextPaintCompressed = false;
6340
0
  }
6341
0
6342
0
  if (frame) {
6343
0
    // We can paint directly into the widget using its layer manager.
6344
0
    nsLayoutUtils::PaintFrame(nullptr, frame, aDirtyRegion, bgcolor,
6345
0
                              nsDisplayListBuilderMode::PAINTING, flags);
6346
0
    return;
6347
0
  }
6348
0
6349
0
  if (layerManager->GetBackendType() == layers::LayersBackend::LAYERS_WR) {
6350
0
    nsPresContext* pc = GetPresContext();
6351
0
    LayoutDeviceRect bounds =
6352
0
      LayoutDeviceRect::FromAppUnits(pc->GetVisibleArea(), pc->AppUnitsPerDevPixel());
6353
0
    bgcolor = NS_ComposeColors(bgcolor, mCanvasBackgroundColor);
6354
0
    WebRenderBackgroundData data(wr::ToLayoutRect(bounds), wr::ToColorF(ToDeviceColor(bgcolor)));
6355
0
    nsTArray<wr::WrFilterOp> wrFilters;
6356
0
6357
0
    MaybeSetupTransactionIdAllocator(layerManager, presContext);
6358
0
    layerManager->AsWebRenderLayerManager()->EndTransactionWithoutLayer(nullptr, nullptr, wrFilters, &data);
6359
0
    return;
6360
0
  }
6361
0
6362
0
  RefPtr<ColorLayer> root = layerManager->CreateColorLayer();
6363
0
  if (root) {
6364
0
    nsPresContext* pc = GetPresContext();
6365
0
    nsIntRect bounds =
6366
0
      pc->GetVisibleArea().ToOutsidePixels(pc->AppUnitsPerDevPixel());
6367
0
    bgcolor = NS_ComposeColors(bgcolor, mCanvasBackgroundColor);
6368
0
    root->SetColor(Color::FromABGR(bgcolor));
6369
0
    root->SetVisibleRegion(LayerIntRegion::FromUnknownRegion(bounds));
6370
0
    layerManager->SetRoot(root);
6371
0
  }
6372
0
  MaybeSetupTransactionIdAllocator(layerManager, presContext);
6373
0
  layerManager->EndTransaction(nullptr, nullptr, (aFlags & PAINT_COMPOSITE) ?
6374
0
    LayerManager::END_DEFAULT : LayerManager::END_NO_COMPOSITE);
6375
0
}
6376
6377
// static
6378
void
6379
nsIPresShell::SetCapturingContent(nsIContent* aContent, uint8_t aFlags)
6380
0
{
6381
0
  // If capture was set for pointer lock, don't unlock unless we are coming
6382
0
  // out of pointer lock explicitly.
6383
0
  if (!aContent && gCaptureInfo.mPointerLock &&
6384
0
      !(aFlags & CAPTURE_POINTERLOCK)) {
6385
0
    return;
6386
0
  }
6387
0
6388
0
  gCaptureInfo.mContent = nullptr;
6389
0
6390
0
  // only set capturing content if allowed or the CAPTURE_IGNOREALLOWED or
6391
0
  // CAPTURE_POINTERLOCK flags are used.
6392
0
  if ((aFlags & CAPTURE_IGNOREALLOWED) || gCaptureInfo.mAllowed ||
6393
0
      (aFlags & CAPTURE_POINTERLOCK)) {
6394
0
    if (aContent) {
6395
0
      gCaptureInfo.mContent = aContent;
6396
0
    }
6397
0
    // CAPTURE_POINTERLOCK is the same as CAPTURE_RETARGETTOELEMENT & CAPTURE_IGNOREALLOWED
6398
0
    gCaptureInfo.mRetargetToElement = ((aFlags & CAPTURE_RETARGETTOELEMENT) != 0) ||
6399
0
                                      ((aFlags & CAPTURE_POINTERLOCK) != 0);
6400
0
    gCaptureInfo.mPreventDrag = (aFlags & CAPTURE_PREVENTDRAG) != 0;
6401
0
    gCaptureInfo.mPointerLock = (aFlags & CAPTURE_POINTERLOCK) != 0;
6402
0
  }
6403
0
}
6404
6405
nsIContent*
6406
PresShell::GetCurrentEventContent()
6407
0
{
6408
0
  if (mCurrentEventContent &&
6409
0
      mCurrentEventContent->GetComposedDoc() != mDocument) {
6410
0
    mCurrentEventContent = nullptr;
6411
0
    mCurrentEventFrame = nullptr;
6412
0
  }
6413
0
  return mCurrentEventContent;
6414
0
}
6415
6416
nsIFrame*
6417
PresShell::GetCurrentEventFrame()
6418
0
{
6419
0
  if (MOZ_UNLIKELY(mIsDestroying)) {
6420
0
    return nullptr;
6421
0
  }
6422
0
6423
0
  // GetCurrentEventContent() makes sure the content is still in the
6424
0
  // same document that this pres shell belongs to. If not, then the
6425
0
  // frame shouldn't get an event, nor should we even assume its safe
6426
0
  // to try and find the frame.
6427
0
  nsIContent* content = GetCurrentEventContent();
6428
0
  if (!mCurrentEventFrame && content) {
6429
0
    mCurrentEventFrame = content->GetPrimaryFrame();
6430
0
    MOZ_ASSERT(!mCurrentEventFrame ||
6431
0
               mCurrentEventFrame->PresContext()->GetPresShell() == this);
6432
0
  }
6433
0
  return mCurrentEventFrame;
6434
0
}
6435
6436
already_AddRefed<nsIContent>
6437
PresShell::GetEventTargetContent(WidgetEvent* aEvent)
6438
0
{
6439
0
  nsCOMPtr<nsIContent> content = GetCurrentEventContent();
6440
0
  if (!content) {
6441
0
    nsIFrame* currentEventFrame = GetCurrentEventFrame();
6442
0
    if (currentEventFrame) {
6443
0
      currentEventFrame->GetContentForEvent(aEvent, getter_AddRefs(content));
6444
0
      NS_ASSERTION(!content || content->GetComposedDoc() == mDocument,
6445
0
                   "handing out content from a different doc");
6446
0
    }
6447
0
  }
6448
0
  return content.forget();
6449
0
}
6450
6451
void
6452
PresShell::PushCurrentEventInfo(nsIFrame* aFrame, nsIContent* aContent)
6453
0
{
6454
0
  if (mCurrentEventFrame || mCurrentEventContent) {
6455
0
    mCurrentEventFrameStack.InsertElementAt(0, mCurrentEventFrame);
6456
0
    mCurrentEventContentStack.InsertObjectAt(mCurrentEventContent, 0);
6457
0
  }
6458
0
  mCurrentEventFrame = aFrame;
6459
0
  mCurrentEventContent = aContent;
6460
0
}
6461
6462
void
6463
PresShell::PopCurrentEventInfo()
6464
0
{
6465
0
  mCurrentEventFrame = nullptr;
6466
0
  mCurrentEventContent = nullptr;
6467
0
6468
0
  if (0 != mCurrentEventFrameStack.Length()) {
6469
0
    mCurrentEventFrame = mCurrentEventFrameStack.ElementAt(0);
6470
0
    mCurrentEventFrameStack.RemoveElementAt(0);
6471
0
    mCurrentEventContent = mCurrentEventContentStack.ObjectAt(0);
6472
0
    mCurrentEventContentStack.RemoveObjectAt(0);
6473
0
6474
0
    // Don't use it if it has moved to a different document.
6475
0
    if (mCurrentEventContent &&
6476
0
        mCurrentEventContent->GetComposedDoc() != mDocument) {
6477
0
      mCurrentEventContent = nullptr;
6478
0
      mCurrentEventFrame = nullptr;
6479
0
    }
6480
0
  }
6481
0
}
6482
6483
bool PresShell::InZombieDocument(nsIContent *aContent)
6484
0
{
6485
0
  // If a content node points to a null document, or the document is not
6486
0
  // attached to a window, then it is possibly in a zombie document,
6487
0
  // about to be replaced by a newly loading document.
6488
0
  // Such documents cannot handle DOM events.
6489
0
  // It might actually be in a node not attached to any document,
6490
0
  // in which case there is not parent presshell to retarget it to.
6491
0
  nsIDocument* doc = aContent->GetComposedDoc();
6492
0
  return !doc || !doc->GetWindow();
6493
0
}
6494
6495
already_AddRefed<nsPIDOMWindowOuter>
6496
PresShell::GetRootWindow()
6497
0
{
6498
0
  nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow();
6499
0
  if (window) {
6500
0
    nsCOMPtr<nsPIDOMWindowOuter> rootWindow = window->GetPrivateRoot();
6501
0
    NS_ASSERTION(rootWindow, "nsPIDOMWindow::GetPrivateRoot() returns NULL");
6502
0
    return rootWindow.forget();
6503
0
  }
6504
0
6505
0
  // If we don't have DOM window, we're zombie, we should find the root window
6506
0
  // with our parent shell.
6507
0
  nsCOMPtr<nsIPresShell> parent = GetParentPresShellForEventHandling();
6508
0
  NS_ENSURE_TRUE(parent, nullptr);
6509
0
  return parent->GetRootWindow();
6510
0
}
6511
6512
already_AddRefed<nsPIDOMWindowOuter>
6513
PresShell::GetFocusedDOMWindowInOurWindow()
6514
0
{
6515
0
  nsCOMPtr<nsPIDOMWindowOuter> rootWindow = GetRootWindow();
6516
0
  NS_ENSURE_TRUE(rootWindow, nullptr);
6517
0
  nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
6518
0
  nsFocusManager::GetFocusedDescendant(rootWindow,
6519
0
                                       nsFocusManager::eIncludeAllDescendants,
6520
0
                                       getter_AddRefs(focusedWindow));
6521
0
  return focusedWindow.forget();
6522
0
}
6523
6524
already_AddRefed<nsIContent>
6525
nsIPresShell::GetFocusedContentInOurWindow() const
6526
0
{
6527
0
  nsIFocusManager* fm = nsFocusManager::GetFocusManager();
6528
0
  if (fm && mDocument) {
6529
0
    RefPtr<Element> focusedElement;
6530
0
    fm->GetFocusedElementForWindow(mDocument->GetWindow(), false, nullptr,
6531
0
                                   getter_AddRefs(focusedElement));
6532
0
    return focusedElement.forget();
6533
0
  }
6534
0
  return nullptr;
6535
0
}
6536
6537
already_AddRefed<nsIPresShell>
6538
PresShell::GetParentPresShellForEventHandling()
6539
0
{
6540
0
  NS_ENSURE_TRUE(mPresContext, nullptr);
6541
0
6542
0
  // Now, find the parent pres shell and send the event there
6543
0
  nsCOMPtr<nsIDocShellTreeItem> treeItem = mPresContext->GetDocShell();
6544
0
  if (!treeItem) {
6545
0
    treeItem = mForwardingContainer.get();
6546
0
  }
6547
0
6548
0
  // Might have gone away, or never been around to start with
6549
0
  NS_ENSURE_TRUE(treeItem, nullptr);
6550
0
6551
0
  nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
6552
0
  treeItem->GetParent(getter_AddRefs(parentTreeItem));
6553
0
  nsCOMPtr<nsIDocShell> parentDocShell = do_QueryInterface(parentTreeItem);
6554
0
  NS_ENSURE_TRUE(parentDocShell && treeItem != parentTreeItem, nullptr);
6555
0
6556
0
  nsCOMPtr<nsIPresShell> parentPresShell = parentDocShell->GetPresShell();
6557
0
  return parentPresShell.forget();
6558
0
}
6559
6560
nsresult
6561
PresShell::RetargetEventToParent(WidgetGUIEvent* aEvent,
6562
                                 nsEventStatus* aEventStatus)
6563
0
{
6564
0
  // Send this events straight up to the parent pres shell.
6565
0
  // We do this for keystroke events in zombie documents or if either a frame
6566
0
  // or a root content is not present.
6567
0
  // That way at least the UI key bindings can work.
6568
0
6569
0
  nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
6570
0
  nsCOMPtr<nsIPresShell> parentPresShell = GetParentPresShellForEventHandling();
6571
0
  NS_ENSURE_TRUE(parentPresShell, NS_ERROR_FAILURE);
6572
0
6573
0
  // Fake the event as though it's from the parent pres shell's root frame.
6574
0
  return parentPresShell->HandleEvent(parentPresShell->GetRootFrame(), aEvent, true, aEventStatus);
6575
0
}
6576
6577
void
6578
PresShell::DisableNonTestMouseEvents(bool aDisable)
6579
0
{
6580
0
  sDisableNonTestMouseEvents = aDisable;
6581
0
}
6582
6583
void
6584
PresShell::RecordMouseLocation(WidgetGUIEvent* aEvent)
6585
0
{
6586
0
  if (!mPresContext)
6587
0
    return;
6588
0
6589
0
  if (!mPresContext->IsRoot()) {
6590
0
    PresShell* rootPresShell = GetRootPresShell();
6591
0
    if (rootPresShell) {
6592
0
      rootPresShell->RecordMouseLocation(aEvent);
6593
0
    }
6594
0
    return;
6595
0
  }
6596
0
6597
0
  if ((aEvent->mMessage == eMouseMove &&
6598
0
       aEvent->AsMouseEvent()->mReason == WidgetMouseEvent::eReal) ||
6599
0
      aEvent->mMessage == eMouseEnterIntoWidget ||
6600
0
      aEvent->mMessage == eMouseDown ||
6601
0
      aEvent->mMessage == eMouseUp) {
6602
0
    nsIFrame* rootFrame = GetRootFrame();
6603
0
    if (!rootFrame) {
6604
0
      nsView* rootView = mViewManager->GetRootView();
6605
0
      mMouseLocation = nsLayoutUtils::TranslateWidgetToView(mPresContext,
6606
0
        aEvent->mWidget, aEvent->mRefPoint, rootView);
6607
0
      mMouseEventTargetGuid = InputAPZContext::GetTargetLayerGuid();
6608
0
    } else {
6609
0
      mMouseLocation =
6610
0
        nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, rootFrame);
6611
0
      mMouseEventTargetGuid = InputAPZContext::GetTargetLayerGuid();
6612
0
    }
6613
#ifdef DEBUG_MOUSE_LOCATION
6614
    if (aEvent->mMessage == eMouseEnterIntoWidget) {
6615
      printf("[ps=%p]got mouse enter for %p\n",
6616
             this, aEvent->mWidget);
6617
    }
6618
    printf("[ps=%p]setting mouse location to (%d,%d)\n",
6619
           this, mMouseLocation.x, mMouseLocation.y);
6620
#endif
6621
0
    if (aEvent->mMessage == eMouseEnterIntoWidget) {
6622
0
      SynthesizeMouseMove(false);
6623
0
    }
6624
0
  } else if (aEvent->mMessage == eMouseExitFromWidget) {
6625
0
    // Although we only care about the mouse moving into an area for which this
6626
0
    // pres shell doesn't receive mouse move events, we don't check which widget
6627
0
    // the mouse exit was for since this seems to vary by platform.  Hopefully
6628
0
    // this won't matter at all since we'll get the mouse move or enter after
6629
0
    // the mouse exit when the mouse moves from one of our widgets into another.
6630
0
    mMouseLocation = nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
6631
0
    mMouseEventTargetGuid = InputAPZContext::GetTargetLayerGuid();
6632
#ifdef DEBUG_MOUSE_LOCATION
6633
    printf("[ps=%p]got mouse exit for %p\n",
6634
           this, aEvent->mWidget);
6635
    printf("[ps=%p]clearing mouse location\n",
6636
           this);
6637
#endif
6638
  }
6639
0
}
6640
6641
static nsIFrame*
6642
GetNearestFrameContainingPresShell(nsIPresShell* aPresShell)
6643
0
{
6644
0
  nsView* view = aPresShell->GetViewManager()->GetRootView();
6645
0
  while (view && !view->GetFrame()) {
6646
0
    view = view->GetParent();
6647
0
  }
6648
0
6649
0
  nsIFrame* frame = nullptr;
6650
0
  if (view) {
6651
0
    frame = view->GetFrame();
6652
0
  }
6653
0
6654
0
  return frame;
6655
0
}
6656
6657
static bool
6658
FlushThrottledStyles(nsIDocument* aDocument, void *aData)
6659
0
{
6660
0
  nsIPresShell* shell = aDocument->GetShell();
6661
0
  if (shell && shell->IsVisible()) {
6662
0
    nsPresContext* presContext = shell->GetPresContext();
6663
0
    if (presContext) {
6664
0
      presContext->RestyleManager()->UpdateOnlyAnimationStyles();
6665
0
    }
6666
0
  }
6667
0
6668
0
  aDocument->EnumerateSubDocuments(FlushThrottledStyles, nullptr);
6669
0
  return true;
6670
0
}
6671
6672
bool
6673
PresShell::CanDispatchEvent(const WidgetGUIEvent* aEvent) const
6674
0
{
6675
0
  bool rv =
6676
0
    mPresContext && !mHaveShutDown && nsContentUtils::IsSafeToRunScript();
6677
0
  if (aEvent) {
6678
0
    rv &= (aEvent && aEvent->mWidget && !aEvent->mWidget->Destroyed());
6679
0
  }
6680
0
  return rv;
6681
0
}
6682
6683
/* static */ PresShell*
6684
PresShell::GetShellForEventTarget(nsIFrame* aFrame, nsIContent* aContent)
6685
0
{
6686
0
  if (aFrame) {
6687
0
    return static_cast<PresShell*>(aFrame->PresShell());
6688
0
  }
6689
0
  if (aContent) {
6690
0
    nsIDocument* doc = aContent->GetComposedDoc();
6691
0
    if (!doc) {
6692
0
      return nullptr;
6693
0
    }
6694
0
    return static_cast<PresShell*>(doc->GetShell());
6695
0
  }
6696
0
  return nullptr;
6697
0
}
6698
6699
/* static */ PresShell*
6700
PresShell::GetShellForTouchEvent(WidgetGUIEvent* aEvent)
6701
0
{
6702
0
  PresShell* shell = nullptr;
6703
0
  switch (aEvent->mMessage) {
6704
0
  case eTouchMove:
6705
0
  case eTouchCancel:
6706
0
  case eTouchEnd: {
6707
0
    // get the correct shell to dispatch to
6708
0
    WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
6709
0
    for (dom::Touch* touch : touchEvent->mTouches) {
6710
0
      if (!touch) {
6711
0
        break;
6712
0
      }
6713
0
6714
0
      RefPtr<dom::Touch> oldTouch =
6715
0
        TouchManager::GetCapturedTouch(touch->Identifier());
6716
0
      if (!oldTouch) {
6717
0
        break;
6718
0
      }
6719
0
6720
0
      nsCOMPtr<nsIContent> content = do_QueryInterface(oldTouch->GetTarget());
6721
0
      if (!content) {
6722
0
        break;
6723
0
      }
6724
0
6725
0
      nsIFrame* contentFrame = content->GetPrimaryFrame();
6726
0
      if (!contentFrame) {
6727
0
        break;
6728
0
      }
6729
0
6730
0
      shell = static_cast<PresShell*>(contentFrame->PresContext()->PresShell());
6731
0
      if (shell) {
6732
0
        break;
6733
0
      }
6734
0
    }
6735
0
    break;
6736
0
  }
6737
0
  default:
6738
0
    break;
6739
0
  }
6740
0
  return shell;
6741
0
}
6742
6743
nsresult
6744
PresShell::HandleEvent(nsIFrame* aFrame,
6745
                       WidgetGUIEvent* aEvent,
6746
                       bool aDontRetargetEvents,
6747
                       nsEventStatus* aEventStatus)
6748
0
{
6749
#ifdef MOZ_TASK_TRACER
6750
  Maybe<AutoSourceEvent> taskTracerEvent;
6751
  if (MOZ_UNLIKELY(IsStartLogging())) {
6752
    // Make touch events, mouse events and hardware key events to be
6753
    // the source events of TaskTracer, and originate the rest
6754
    // correlation tasks from here.
6755
    SourceEventType type = SourceEventType::Unknown;
6756
    if (aEvent->AsTouchEvent()) {
6757
      type = SourceEventType::Touch;
6758
    } else if (aEvent->AsMouseEvent()) {
6759
      type = SourceEventType::Mouse;
6760
    } else if (aEvent->AsKeyboardEvent()) {
6761
      type = SourceEventType::Key;
6762
    }
6763
    taskTracerEvent.emplace(type);
6764
  }
6765
#endif
6766
6767
0
  NS_ASSERTION(aFrame, "aFrame should be not null");
6768
0
6769
0
  // Update the latest focus sequence number with this new sequence number;
6770
0
  // the next transasction that gets sent to the compositor will carry this over
6771
0
  if (mAPZFocusSequenceNumber < aEvent->mFocusSequenceNumber) {
6772
0
    mAPZFocusSequenceNumber = aEvent->mFocusSequenceNumber;
6773
0
  }
6774
0
6775
0
  if (mIsDestroying ||
6776
0
      (sDisableNonTestMouseEvents && !aEvent->mFlags.mIsSynthesizedForTests &&
6777
0
       aEvent->HasMouseEventMessage())) {
6778
0
    return NS_OK;
6779
0
  }
6780
0
6781
0
  RecordMouseLocation(aEvent);
6782
0
6783
0
  if (AccessibleCaretEnabled(mDocument->GetDocShell())) {
6784
0
    // We have to target the focus window because regardless of where the
6785
0
    // touch goes, we want to access the copy paste manager.
6786
0
    nsCOMPtr<nsPIDOMWindowOuter> window = GetFocusedDOMWindowInOurWindow();
6787
0
    nsCOMPtr<nsIDocument> retargetEventDoc =
6788
0
      window ? window->GetExtantDoc() : nullptr;
6789
0
    nsCOMPtr<nsIPresShell> presShell =
6790
0
      retargetEventDoc ? retargetEventDoc->GetShell() : nullptr;
6791
0
6792
0
    RefPtr<AccessibleCaretEventHub> eventHub =
6793
0
      presShell ? presShell->GetAccessibleCaretEventHub() : nullptr;
6794
0
    if (eventHub && *aEventStatus != nsEventStatus_eConsumeNoDefault) {
6795
0
      // Don't dispatch event to AccessibleCaretEventHub when the event status
6796
0
      // is nsEventStatus_eConsumeNoDefault. This might be happened when content
6797
0
      // preventDefault on the pointer events. In such case, we also call
6798
0
      // preventDefault on mouse events to stop default behaviors.
6799
0
      *aEventStatus = eventHub->HandleEvent(aEvent);
6800
0
      if (*aEventStatus == nsEventStatus_eConsumeNoDefault) {
6801
0
        // If the event is consumed, cancel APZC panning by setting
6802
0
        // mMultipleActionsPrevented.
6803
0
        aEvent->mFlags.mMultipleActionsPrevented = true;
6804
0
        return NS_OK;
6805
0
      }
6806
0
    }
6807
0
  }
6808
0
6809
0
  if (!nsContentUtils::IsSafeToRunScript() &&
6810
0
      aEvent->IsAllowedToDispatchDOMEvent()) {
6811
0
    if (aEvent->mClass == eCompositionEventClass) {
6812
0
      IMEStateManager::OnCompositionEventDiscarded(
6813
0
        aEvent->AsCompositionEvent());
6814
0
    }
6815
#ifdef DEBUG
6816
    if (aEvent->IsIMERelatedEvent()) {
6817
      nsPrintfCString warning("%d event is discarded", aEvent->mMessage);
6818
      NS_WARNING(warning.get());
6819
    }
6820
#endif
6821
    nsContentUtils::WarnScriptWasIgnored(GetDocument());
6822
0
    return NS_OK;
6823
0
  }
6824
0
6825
0
  nsIContent* capturingContent = ((aEvent->mClass == ePointerEventClass ||
6826
0
                                   aEvent->mClass == eWheelEventClass ||
6827
0
                                   aEvent->HasMouseEventMessage())
6828
0
                                 ? GetCapturingContent()
6829
0
                                 : nullptr);
6830
0
6831
0
  nsCOMPtr<nsIDocument> retargetEventDoc;
6832
0
  if (!aDontRetargetEvents) {
6833
0
    // key and IME related events should not cross top level window boundary.
6834
0
    // Basically, such input events should be fired only on focused widget.
6835
0
    // However, some IMEs might need to clean up composition after focused
6836
0
    // window is deactivated.  And also some tests on MozMill want to test key
6837
0
    // handling on deactivated window because MozMill window can be activated
6838
0
    // during tests.  So, there is no merit the events should be redirected to
6839
0
    // active window.  So, the events should be handled on the last focused
6840
0
    // content in the last focused DOM window in same top level window.
6841
0
    // Note, if no DOM window has been focused yet, we can discard the events.
6842
0
    if (aEvent->IsTargetedAtFocusedWindow()) {
6843
0
      nsCOMPtr<nsPIDOMWindowOuter> window = GetFocusedDOMWindowInOurWindow();
6844
0
      // No DOM window in same top level window has not been focused yet,
6845
0
      // discard the events.
6846
0
      if (!window) {
6847
0
        return NS_OK;
6848
0
      }
6849
0
6850
0
      retargetEventDoc = window->GetExtantDoc();
6851
0
      if (!retargetEventDoc)
6852
0
        return NS_OK;
6853
0
    } else if (capturingContent) {
6854
0
      // if the mouse is being captured then retarget the mouse event at the
6855
0
      // document that is being captured.
6856
0
      retargetEventDoc = capturingContent->GetComposedDoc();
6857
#ifdef ANDROID
6858
    } else if ((aEvent->mClass == eTouchEventClass) ||
6859
               (aEvent->mClass == eMouseEventClass) ||
6860
               (aEvent->mClass == eWheelEventClass)) {
6861
      retargetEventDoc = GetPrimaryContentDocument();
6862
#endif
6863
    }
6864
0
6865
0
    if (retargetEventDoc) {
6866
0
      nsCOMPtr<nsIPresShell> presShell = retargetEventDoc->GetShell();
6867
0
      // Even if the document doesn't have PresShell, i.e., it's invisible, we
6868
0
      // need to dispatch only KeyboardEvent in its nearest visible document
6869
0
      // because key focus shouldn't be caught by invisible document.
6870
0
      if (!presShell) {
6871
0
        if (!aEvent->HasKeyEventMessage()) {
6872
0
          return NS_OK;
6873
0
        }
6874
0
        while (!presShell) {
6875
0
          retargetEventDoc = retargetEventDoc->GetParentDocument();
6876
0
          if (!retargetEventDoc) {
6877
0
            return NS_OK;
6878
0
          }
6879
0
          presShell = retargetEventDoc->GetShell();
6880
0
        }
6881
0
      }
6882
0
6883
0
      if (presShell != this) {
6884
0
        nsIFrame* frame = presShell->GetRootFrame();
6885
0
        if (!frame) {
6886
0
          if (aEvent->mMessage == eQueryTextContent ||
6887
0
              aEvent->IsContentCommandEvent()) {
6888
0
            return NS_OK;
6889
0
          }
6890
0
6891
0
          frame = GetNearestFrameContainingPresShell(presShell);
6892
0
        }
6893
0
6894
0
        if (!frame)
6895
0
          return NS_OK;
6896
0
6897
0
        nsCOMPtr<nsIPresShell> shell = frame->PresContext()->GetPresShell();
6898
0
        return shell->HandleEvent(frame, aEvent, true, aEventStatus);
6899
0
      }
6900
0
    }
6901
0
  }
6902
0
6903
0
  if (aEvent->mClass == eKeyboardEventClass &&
6904
0
      mDocument && mDocument->EventHandlingSuppressed()) {
6905
0
    if (aEvent->mMessage == eKeyDown) {
6906
0
      mNoDelayedKeyEvents = true;
6907
0
    } else if (!mNoDelayedKeyEvents) {
6908
0
      DelayedEvent* event = new DelayedKeyEvent(aEvent->AsKeyboardEvent());
6909
0
      if (!mDelayedEvents.AppendElement(event)) {
6910
0
        delete event;
6911
0
      }
6912
0
    }
6913
0
    aEvent->mFlags.mIsSuppressedOrDelayed = true;
6914
0
    return NS_OK;
6915
0
  }
6916
0
6917
0
  nsIFrame* frame = aFrame;
6918
0
6919
0
  if (aEvent->IsUsingCoordinates()) {
6920
0
    if (mDocument) {
6921
0
      if (aEvent->mClass == eTouchEventClass) {
6922
0
        nsIDocument::UnlockPointer();
6923
0
      }
6924
0
6925
0
      AutoWeakFrame weakFrame(frame);
6926
0
      {  // scope for scriptBlocker.
6927
0
        nsAutoScriptBlocker scriptBlocker;
6928
0
        FlushThrottledStyles(GetRootPresShell()->GetDocument(), nullptr);
6929
0
      }
6930
0
6931
0
6932
0
      if (!weakFrame.IsAlive()) {
6933
0
        frame = GetNearestFrameContainingPresShell(this);
6934
0
      }
6935
0
    }
6936
0
6937
0
    if (!frame) {
6938
0
      NS_WARNING("Nothing to handle this event!");
6939
0
      return NS_OK;
6940
0
    }
6941
0
6942
0
    nsPresContext* framePresContext = frame->PresContext();
6943
0
    nsPresContext* rootPresContext = framePresContext->GetRootPresContext();
6944
0
    NS_ASSERTION(rootPresContext == mPresContext->GetRootPresContext(),
6945
0
                 "How did we end up outside the connected prescontext/viewmanager hierarchy?");
6946
0
    nsIFrame* popupFrame =
6947
0
      nsLayoutUtils::GetPopupFrameForEventCoordinates(rootPresContext, aEvent);
6948
0
    // If a remote browser is currently capturing input break out if we
6949
0
    // detect a chrome generated popup.
6950
0
    if (popupFrame && capturingContent &&
6951
0
        EventStateManager::IsRemoteTarget(capturingContent)) {
6952
0
      capturingContent = nullptr;
6953
0
    }
6954
0
    // If the popupFrame is an ancestor of the 'frame', the frame should
6955
0
    // handle the event, otherwise, the popup should handle it.
6956
0
    if (popupFrame &&
6957
0
        !nsContentUtils::ContentIsCrossDocDescendantOf(
6958
0
           framePresContext->GetPresShell()->GetDocument(),
6959
0
           popupFrame->GetContent())) {
6960
0
6961
0
      // If we aren't starting our event dispatch from the root frame of the
6962
0
      // root prescontext, then someone must be capturing the mouse. In that
6963
0
      // case we only want to use the popup list if the capture is
6964
0
      // inside the popup.
6965
0
      if (framePresContext == rootPresContext &&
6966
0
          frame == mFrameConstructor->GetRootFrame()) {
6967
0
        frame = popupFrame;
6968
0
      } else if (capturingContent &&
6969
0
                 nsContentUtils::ContentIsDescendantOf(
6970
0
                   capturingContent, popupFrame->GetContent())) {
6971
0
        frame = popupFrame;
6972
0
      }
6973
0
    }
6974
0
6975
0
    bool captureRetarget = false;
6976
0
    if (capturingContent) {
6977
0
      // If a capture is active, determine if the docshell is visible. If not,
6978
0
      // clear the capture and target the mouse event normally instead. This
6979
0
      // would occur if the mouse button is held down while a tab change occurs.
6980
0
      // If the docshell is visible, look for a scrolling container.
6981
0
      bool vis;
6982
0
      nsCOMPtr<nsIBaseWindow> baseWin =
6983
0
        do_QueryInterface(mPresContext->GetContainerWeak());
6984
0
      if (baseWin && NS_SUCCEEDED(baseWin->GetVisibility(&vis)) && vis) {
6985
0
        captureRetarget = gCaptureInfo.mRetargetToElement;
6986
0
        if (!captureRetarget) {
6987
0
          // A check was already done above to ensure that capturingContent is
6988
0
          // in this presshell.
6989
0
          NS_ASSERTION(capturingContent->GetComposedDoc() == GetDocument(),
6990
0
                       "Unexpected document");
6991
0
          nsIFrame* captureFrame = capturingContent->GetPrimaryFrame();
6992
0
          if (captureFrame) {
6993
0
            if (capturingContent->IsHTMLElement(nsGkAtoms::select)) {
6994
0
              // a dropdown <select> has a child in its selectPopupList and we should
6995
0
              // capture on that instead.
6996
0
              nsIFrame* childFrame = captureFrame->GetChildList(nsIFrame::kSelectPopupList).FirstChild();
6997
0
              if (childFrame) {
6998
0
                captureFrame = childFrame;
6999
0
              }
7000
0
            }
7001
0
7002
0
            // scrollable frames should use the scrolling container as
7003
0
            // the root instead of the document
7004
0
            nsIScrollableFrame* scrollFrame = do_QueryFrame(captureFrame);
7005
0
            if (scrollFrame) {
7006
0
              frame = scrollFrame->GetScrolledFrame();
7007
0
            }
7008
0
          }
7009
0
        }
7010
0
      }
7011
0
      else {
7012
0
        ClearMouseCapture(nullptr);
7013
0
        capturingContent = nullptr;
7014
0
      }
7015
0
    }
7016
0
7017
0
    // The order to generate pointer event is
7018
0
    // 1. check pending pointer capture.
7019
0
    // 2. check if there is a capturing content.
7020
0
    // 3. hit test
7021
0
    // 4. dispatch pointer events
7022
0
    // 5. check whether the targets of all Touch instances are in the same
7023
0
    //    document and suppress invalid instances.
7024
0
    // 6. dispatch mouse or touch events.
7025
0
7026
0
    // Try to keep frame for following check, because frame can be damaged
7027
0
    // during MaybeProcessPointerCapture.
7028
0
    {
7029
0
      AutoWeakFrame frameKeeper(frame);
7030
0
      PointerEventHandler::MaybeProcessPointerCapture(aEvent);
7031
0
      // Prevent application crashes, in case damaged frame.
7032
0
      if (!frameKeeper.IsAlive()) {
7033
0
        NS_WARNING("Nothing to handle this event!");
7034
0
        return NS_OK;
7035
0
      }
7036
0
    }
7037
0
7038
0
    // Only capture mouse events and pointer events.
7039
0
    nsCOMPtr<nsIContent> pointerCapturingContent =
7040
0
      PointerEventHandler::GetPointerCapturingContent(aEvent);
7041
0
7042
0
    if (pointerCapturingContent) {
7043
0
      frame = pointerCapturingContent->GetPrimaryFrame();
7044
0
7045
0
      if (!frame) {
7046
0
        RefPtr<PresShell> shell =
7047
0
          GetShellForEventTarget(nullptr, pointerCapturingContent);
7048
0
        if (!shell) {
7049
0
          // If we can't process event for the capturing content, release
7050
0
          // the capture.
7051
0
          PointerEventHandler::ReleaseIfCaptureByDescendant(
7052
0
            pointerCapturingContent);
7053
0
          return NS_OK;
7054
0
        }
7055
0
7056
0
        nsCOMPtr<nsIContent> overrideClickTarget =
7057
0
          GetOverrideClickTarget(aEvent, aFrame);
7058
0
7059
0
        // Dispatch events to the capturing content even it's frame is
7060
0
        // destroyed.
7061
0
        PointerEventHandler::DispatchPointerFromMouseOrTouch(
7062
0
          shell, nullptr, pointerCapturingContent, aEvent, false,
7063
0
          aEventStatus, nullptr);
7064
0
7065
0
        return shell->HandleEventWithTarget(aEvent, nullptr,
7066
0
                                            pointerCapturingContent,
7067
0
                                            aEventStatus, true, nullptr,
7068
0
                                            overrideClickTarget);
7069
0
      }
7070
0
    }
7071
0
7072
0
    WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
7073
0
    bool isWindowLevelMouseExit = (aEvent->mMessage == eMouseExitFromWidget) &&
7074
0
      (mouseEvent && mouseEvent->mExitFrom == WidgetMouseEvent::eTopLevel);
7075
0
7076
0
    // Get the frame at the event point. However, don't do this if we're
7077
0
    // capturing and retargeting the event because the captured frame will
7078
0
    // be used instead below. Also keep using the root frame if we're dealing
7079
0
    // with a window-level mouse exit event since we want to start sending
7080
0
    // mouse out events at the root EventStateManager.
7081
0
    if (!captureRetarget && !isWindowLevelMouseExit &&
7082
0
        !pointerCapturingContent) {
7083
0
      if (aEvent->mClass == eTouchEventClass) {
7084
0
        frame = TouchManager::SetupTarget(aEvent->AsTouchEvent(), frame);
7085
0
      } else {
7086
0
        uint32_t flags = 0;
7087
0
        nsPoint eventPoint =
7088
0
          nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame);
7089
0
7090
0
        if (mouseEvent && mouseEvent->mClass == eMouseEventClass &&
7091
0
            mouseEvent->mIgnoreRootScrollFrame) {
7092
0
          flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
7093
0
        }
7094
0
        nsIFrame* target =
7095
0
          FindFrameTargetedByInputEvent(aEvent, frame, eventPoint, flags);
7096
0
        if (target) {
7097
0
          frame = target;
7098
0
        }
7099
0
      }
7100
0
    }
7101
0
7102
0
    // if a node is capturing the mouse, check if the event needs to be
7103
0
    // retargeted at the capturing content instead. This will be the case when
7104
0
    // capture retargeting is being used, no frame was found or the frame's
7105
0
    // content is not a descendant of the capturing content.
7106
0
    if (capturingContent && !pointerCapturingContent &&
7107
0
        (gCaptureInfo.mRetargetToElement || !frame->GetContent() ||
7108
0
         !nsContentUtils::ContentIsCrossDocDescendantOf(frame->GetContent(),
7109
0
                                                        capturingContent))) {
7110
0
      // A check was already done above to ensure that capturingContent is
7111
0
      // in this presshell.
7112
0
      NS_ASSERTION(capturingContent->GetComposedDoc() == GetDocument(),
7113
0
                   "Unexpected document");
7114
0
      nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();
7115
0
      if (capturingFrame) {
7116
0
        frame = capturingFrame;
7117
0
      }
7118
0
    }
7119
0
7120
0
    // Suppress mouse event if it's being targeted at an element inside
7121
0
    // a document which needs events suppressed
7122
0
    if (aEvent->mClass == eMouseEventClass &&
7123
0
        frame->PresContext()->Document()->EventHandlingSuppressed()) {
7124
0
      if (aEvent->mMessage == eMouseDown) {
7125
0
        mNoDelayedMouseEvents = true;
7126
0
      } else if (!mNoDelayedMouseEvents && (aEvent->mMessage == eMouseUp ||
7127
0
        // contextmenu is triggered after right mouseup on Windows and right
7128
0
        // mousedown on other platforms.
7129
0
        aEvent->mMessage == eContextMenu)) {
7130
0
        DelayedEvent* event = new DelayedMouseEvent(aEvent->AsMouseEvent());
7131
0
        if (!mDelayedEvents.AppendElement(event)) {
7132
0
          delete event;
7133
0
        }
7134
0
      }
7135
0
      return NS_OK;
7136
0
    }
7137
0
7138
0
    if (!frame) {
7139
0
      NS_WARNING("Nothing to handle this event!");
7140
0
      return NS_OK;
7141
0
    }
7142
0
7143
0
    RefPtr<PresShell> shell = static_cast<PresShell*>(frame->PresShell());
7144
0
    // Check if we have an active EventStateManager which isn't the
7145
0
    // EventStateManager of the current PresContext.
7146
0
    // If that is the case, and mouse is over some ancestor document,
7147
0
    // forward event handling to the active document.
7148
0
    // This way content can get mouse events even when
7149
0
    // mouse is over the chrome or outside the window.
7150
0
    //
7151
0
    // Note, currently for backwards compatibility we don't forward mouse events
7152
0
    // to the active document when mouse is over some subdocument.
7153
0
    if (EventStateManager* activeESM = EventStateManager::GetActiveEventStateManager()) {
7154
0
      if (aEvent->mClass == ePointerEventClass || aEvent->HasMouseEventMessage()) {
7155
0
        if (activeESM != shell->GetPresContext()->EventStateManager()) {
7156
0
          if (nsPresContext* activeContext = activeESM->GetPresContext()) {
7157
0
            if (nsIPresShell* activeShell = activeContext->GetPresShell()) {
7158
0
              if (nsContentUtils::ContentIsCrossDocDescendantOf(activeShell->GetDocument(),
7159
0
                                                                shell->GetDocument())) {
7160
0
                shell = static_cast<PresShell*>(activeShell);
7161
0
                frame = shell->GetRootFrame();
7162
0
              }
7163
0
            }
7164
0
          }
7165
0
        }
7166
0
      }
7167
0
    }
7168
0
7169
0
    if (!frame) {
7170
0
      NS_WARNING("Nothing to handle this event!");
7171
0
      return NS_OK;
7172
0
    }
7173
0
7174
0
    nsCOMPtr<nsIContent> targetElement;
7175
0
    frame->GetContentForEvent(aEvent, getter_AddRefs(targetElement));
7176
0
7177
0
    // If there is no content for this frame, target it anyway.  Some
7178
0
    // frames can be targeted but do not have content, particularly
7179
0
    // windows with scrolling off.
7180
0
    if (targetElement) {
7181
0
      // Bug 103055, bug 185889: mouse events apply to *elements*, not all
7182
0
      // nodes.  Thus we get the nearest element parent here.
7183
0
      // XXX we leave the frame the same even if we find an element
7184
0
      // parent, so that the text frame will receive the event (selection
7185
0
      // and friends are the ones who care about that anyway)
7186
0
      //
7187
0
      // We use weak pointers because during this tight loop, the node
7188
0
      // will *not* go away.  And this happens on every mousemove.
7189
0
      while (targetElement && !targetElement->IsElement()) {
7190
0
        targetElement = targetElement->GetFlattenedTreeParent();
7191
0
      }
7192
0
7193
0
      // If we found an element, target it.  Otherwise, target *nothing*.
7194
0
      if (!targetElement) {
7195
0
        return NS_OK;
7196
0
      }
7197
0
    }
7198
0
7199
0
    nsCOMPtr<nsIContent> overrideClickTarget;
7200
0
    if (PointerEventHandler::IsPointerEventEnabled()) {
7201
0
      // Dispatch pointer events from the mouse or touch events. Regarding
7202
0
      // pointer events from mouse, we should dispatch those pointer events to
7203
0
      // the same target as the source mouse events. We pass the frame found
7204
0
      // in hit test to PointerEventHandler and dispatch pointer events to it.
7205
0
      //
7206
0
      // Regarding pointer events from touch, the behavior is different. Touch
7207
0
      // events are dispatched to the same target as the target of touchstart.
7208
0
      // Multiple touch points must be dispatched to the same document. Pointer
7209
0
      // events from touch can be dispatched to different documents. We Pass the
7210
0
      // original frame to PointerEventHandler, reentry PresShell::HandleEvent,
7211
0
      // and do hit test for each point.
7212
0
      nsIFrame* targetFrame =
7213
0
        aEvent->mClass == eTouchEventClass ? aFrame : frame;
7214
0
7215
0
      if (pointerCapturingContent) {
7216
0
        overrideClickTarget = GetOverrideClickTarget(aEvent, aFrame);
7217
0
        shell = GetShellForEventTarget(nullptr, pointerCapturingContent);
7218
0
        if (!shell) {
7219
0
          // If we can't process event for the capturing content, release
7220
0
          // the capture.
7221
0
          PointerEventHandler::ReleaseIfCaptureByDescendant(
7222
0
            pointerCapturingContent);
7223
0
          return NS_OK;
7224
0
        }
7225
0
7226
0
        targetFrame = pointerCapturingContent->GetPrimaryFrame();
7227
0
        frame = targetFrame;
7228
0
      }
7229
0
7230
0
      AutoWeakFrame weakTargetFrame(targetFrame);
7231
0
      AutoWeakFrame weakFrame(frame);
7232
0
      nsCOMPtr<nsIContent> targetContent;
7233
0
      PointerEventHandler::DispatchPointerFromMouseOrTouch(
7234
0
                             shell, targetFrame, targetElement, aEvent,
7235
0
                             aDontRetargetEvents, aEventStatus,
7236
0
                             getter_AddRefs(targetContent));
7237
0
7238
0
      if (!weakTargetFrame.IsAlive() && aEvent->mClass == eMouseEventClass) {
7239
0
        // Spec only defines that mouse events must be dispatched to the same
7240
0
        // target as the pointer event. If the target is no longer participating
7241
0
        // in its ownerDocument's tree, fire the event at the original target's
7242
0
        // nearest ancestor node
7243
0
        if (!targetContent) {
7244
0
          return NS_OK;
7245
0
        }
7246
0
        frame = targetContent->GetPrimaryFrame();
7247
0
        shell = GetShellForEventTarget(frame, targetContent);
7248
0
        if (!shell) {
7249
0
          return NS_OK;
7250
0
        }
7251
0
      } else if (!weakFrame.IsAlive()) {
7252
0
        return NS_OK;
7253
0
      }
7254
0
    }
7255
0
7256
0
    // frame could be null after dispatching pointer events.
7257
0
    if (aEvent->mClass == eTouchEventClass) {
7258
0
      if (aEvent->mMessage == eTouchStart) {
7259
0
        WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
7260
0
        if (nsIFrame* newFrame =
7261
0
              TouchManager::SuppressInvalidPointsAndGetTargetedFrame(
7262
0
                touchEvent)) {
7263
0
          frame = newFrame;
7264
0
          frame->GetContentForEvent(aEvent, getter_AddRefs(targetElement));
7265
0
          shell = static_cast<PresShell*>(frame->PresShell());
7266
0
        }
7267
0
      } else if (PresShell* newShell = GetShellForTouchEvent(aEvent)) {
7268
0
        // Touch events (except touchstart) are dispatching to the captured
7269
0
        // element. Get correct shell from it.
7270
0
        shell = newShell;
7271
0
      }
7272
0
    }
7273
0
7274
0
    nsresult rv;
7275
0
7276
0
    // Handle the event in the correct shell.
7277
0
    // We pass the subshell's root frame as the frame to start from. This is
7278
0
    // the only correct alternative; if the event was captured then it
7279
0
    // must have been captured by us or some ancestor shell and we
7280
0
    // now ask the subshell to dispatch it normally.
7281
0
    shell->PushCurrentEventInfo(frame, targetElement);
7282
0
    rv = shell->HandleEventInternal(aEvent, aEventStatus, true,
7283
0
                                    overrideClickTarget);
7284
#ifdef DEBUG
7285
    shell->ShowEventTargetDebug();
7286
#endif
7287
    shell->PopCurrentEventInfo();
7288
0
    return rv;
7289
0
  }
7290
0
7291
0
  nsresult rv = NS_OK;
7292
0
7293
0
  if (frame) {
7294
0
    PushCurrentEventInfo(nullptr, nullptr);
7295
0
7296
0
    // key and IME related events go to the focused frame in this DOM window.
7297
0
    if (aEvent->IsTargetedAtFocusedContent()) {
7298
0
      mCurrentEventContent = nullptr;
7299
0
7300
0
      nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow();
7301
0
      nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
7302
0
      nsCOMPtr<nsIContent> eventTarget =
7303
0
        nsFocusManager::GetFocusedDescendant(window,
7304
0
                                             nsFocusManager::eOnlyCurrentWindow,
7305
0
                                             getter_AddRefs(focusedWindow));
7306
0
7307
0
      // otherwise, if there is no focused content or the focused content has
7308
0
      // no frame, just use the root content. This ensures that key events
7309
0
      // still get sent to the window properly if nothing is focused or if a
7310
0
      // frame goes away while it is focused.
7311
0
      if (!eventTarget || !eventTarget->GetPrimaryFrame()) {
7312
0
        eventTarget = mDocument->GetUnfocusedKeyEventTarget();
7313
0
      }
7314
0
7315
0
      if (aEvent->mMessage == eKeyDown) {
7316
0
        NS_IF_RELEASE(gKeyDownTarget);
7317
0
        NS_IF_ADDREF(gKeyDownTarget = eventTarget);
7318
0
      }
7319
0
      else if ((aEvent->mMessage == eKeyPress ||
7320
0
                aEvent->mMessage == eKeyUp) &&
7321
0
               gKeyDownTarget) {
7322
0
        // If a different element is now focused for the keypress/keyup event
7323
0
        // than what was focused during the keydown event, check if the new
7324
0
        // focused element is not in a chrome document any more, and if so,
7325
0
        // retarget the event back at the keydown target. This prevents a
7326
0
        // content area from grabbing the focus from chrome in-between key
7327
0
        // events.
7328
0
        if (eventTarget) {
7329
0
          bool keyDownIsChrome = nsContentUtils::IsChromeDoc(gKeyDownTarget->GetComposedDoc());
7330
0
          if (keyDownIsChrome != nsContentUtils::IsChromeDoc(eventTarget->GetComposedDoc()) ||
7331
0
              (keyDownIsChrome && TabParent::GetFrom(eventTarget))) {
7332
0
            eventTarget = gKeyDownTarget;
7333
0
          }
7334
0
        }
7335
0
7336
0
        if (aEvent->mMessage == eKeyUp) {
7337
0
          NS_RELEASE(gKeyDownTarget);
7338
0
        }
7339
0
      }
7340
0
7341
0
      mCurrentEventFrame = nullptr;
7342
0
      nsIDocument* targetDoc = eventTarget ? eventTarget->OwnerDoc() : nullptr;
7343
0
      if (targetDoc && targetDoc != mDocument) {
7344
0
        PopCurrentEventInfo();
7345
0
        nsCOMPtr<nsIPresShell> shell = targetDoc->GetShell();
7346
0
        if (shell) {
7347
0
          rv = static_cast<PresShell*>(shell.get())->
7348
0
            HandleRetargetedEvent(aEvent, aEventStatus, eventTarget);
7349
0
        }
7350
0
        return rv;
7351
0
      } else {
7352
0
        mCurrentEventContent = eventTarget;
7353
0
      }
7354
0
7355
0
      if (!GetCurrentEventContent() || !GetCurrentEventFrame() ||
7356
0
          InZombieDocument(mCurrentEventContent)) {
7357
0
        rv = RetargetEventToParent(aEvent, aEventStatus);
7358
0
        PopCurrentEventInfo();
7359
0
        return rv;
7360
0
      }
7361
0
    } else {
7362
0
      mCurrentEventFrame = frame;
7363
0
    }
7364
0
    if (GetCurrentEventFrame()) {
7365
0
      rv = HandleEventInternal(aEvent, aEventStatus, true);
7366
0
    }
7367
0
7368
#ifdef DEBUG
7369
    ShowEventTargetDebug();
7370
#endif
7371
    PopCurrentEventInfo();
7372
0
  } else {
7373
0
    // Activation events need to be dispatched even if no frame was found, since
7374
0
    // we don't want the focus to be out of sync.
7375
0
7376
0
    if (!NS_EVENT_NEEDS_FRAME(aEvent)) {
7377
0
      mCurrentEventFrame = nullptr;
7378
0
      return HandleEventInternal(aEvent, aEventStatus, true);
7379
0
    }
7380
0
    else if (aEvent->HasKeyEventMessage()) {
7381
0
      // Keypress events in new blank tabs should not be completely thrown away.
7382
0
      // Retarget them -- the parent chrome shell might make use of them.
7383
0
      return RetargetEventToParent(aEvent, aEventStatus);
7384
0
    }
7385
0
  }
7386
0
7387
0
  return rv;
7388
0
}
7389
7390
nsIDocument*
7391
PresShell::GetPrimaryContentDocument()
7392
0
{
7393
0
  nsPresContext* context = GetPresContext();
7394
0
  if (!context || !context->IsRoot()) {
7395
0
    return nullptr;
7396
0
  }
7397
0
7398
0
  nsCOMPtr<nsIDocShellTreeItem> shellAsTreeItem = context->GetDocShell();
7399
0
  if (!shellAsTreeItem) {
7400
0
    return nullptr;
7401
0
  }
7402
0
7403
0
  nsCOMPtr<nsIDocShellTreeOwner> owner;
7404
0
  shellAsTreeItem->GetTreeOwner(getter_AddRefs(owner));
7405
0
  if (!owner) {
7406
0
    return nullptr;
7407
0
  }
7408
0
7409
0
  // now get the primary content shell (active tab)
7410
0
  nsCOMPtr<nsIDocShellTreeItem> item;
7411
0
  owner->GetPrimaryContentShell(getter_AddRefs(item));
7412
0
  nsCOMPtr<nsIDocShell> childDocShell = do_QueryInterface(item);
7413
0
  if (!childDocShell) {
7414
0
    return nullptr;
7415
0
  }
7416
0
7417
0
  return childDocShell->GetDocument();
7418
0
}
7419
7420
#ifdef DEBUG
7421
void
7422
PresShell::ShowEventTargetDebug()
7423
{
7424
  if (nsFrame::GetShowEventTargetFrameBorder() &&
7425
      GetCurrentEventFrame()) {
7426
    if (mDrawEventTargetFrame) {
7427
      mDrawEventTargetFrame->InvalidateFrame();
7428
    }
7429
7430
    mDrawEventTargetFrame = mCurrentEventFrame;
7431
    mDrawEventTargetFrame->InvalidateFrame();
7432
  }
7433
}
7434
#endif
7435
7436
nsresult
7437
PresShell::HandleEventWithTarget(WidgetEvent* aEvent, nsIFrame* aFrame,
7438
                                 nsIContent* aContent, nsEventStatus* aStatus,
7439
                                 bool aIsHandlingNativeEvent,
7440
                                 nsIContent** aTargetContent,
7441
                                 nsIContent* aOverrideClickTarget)
7442
0
{
7443
#if DEBUG
7444
  MOZ_ASSERT(!aFrame || aFrame->PresContext()->GetPresShell() == this,
7445
             "wrong shell");
7446
  if (aContent) {
7447
    nsIDocument* doc = aContent->GetComposedDoc();
7448
    NS_ASSERTION(doc, "event for content that isn't in a document");
7449
    // NOTE: We don't require that the document still have a PresShell.
7450
    // See bug 1375940.
7451
  }
7452
#endif
7453
0
  NS_ENSURE_STATE(!aContent || aContent->GetComposedDoc() == mDocument);
7454
0
  AutoPointerEventTargetUpdater updater(this, aEvent, aFrame, aTargetContent);
7455
0
  PushCurrentEventInfo(aFrame, aContent);
7456
0
  nsresult rv =
7457
0
    HandleEventInternal(aEvent, aStatus, false, aOverrideClickTarget);
7458
0
  PopCurrentEventInfo();
7459
0
  return rv;
7460
0
}
7461
7462
nsresult
7463
PresShell::HandleEventInternal(WidgetEvent* aEvent,
7464
                               nsEventStatus* aStatus,
7465
                               bool aIsHandlingNativeEvent,
7466
                               nsIContent* aOverrideClickTarget)
7467
0
{
7468
0
  RefPtr<EventStateManager> manager = mPresContext->EventStateManager();
7469
0
  nsresult rv = NS_OK;
7470
0
7471
0
  if (!NS_EVENT_NEEDS_FRAME(aEvent) || GetCurrentEventFrame() || GetCurrentEventContent()) {
7472
0
    bool touchIsNew = false;
7473
0
    bool isHandlingUserInput = false;
7474
0
7475
0
    if (mCurrentEventContent && aEvent->IsTargetedAtFocusedWindow()) {
7476
0
      nsFocusManager* fm = nsFocusManager::GetFocusManager();
7477
0
      if (fm) {
7478
0
         fm->FlushBeforeEventHandlingIfNeeded(mCurrentEventContent);
7479
0
      }
7480
0
    }
7481
0
7482
0
    // XXX How about IME events and input events for plugins?
7483
0
    if (aEvent->IsTrusted()) {
7484
0
      if (aEvent->IsUserAction()) {
7485
0
        mHasHandledUserInput = true;
7486
0
      }
7487
0
7488
0
      switch (aEvent->mMessage) {
7489
0
      case eKeyPress:
7490
0
      case eKeyDown:
7491
0
      case eKeyUp: {
7492
0
        nsIDocument* doc = GetCurrentEventContent() ?
7493
0
                           mCurrentEventContent->OwnerDoc() : nullptr;
7494
0
        auto keyCode = aEvent->AsKeyboardEvent()->mKeyCode;
7495
0
        if (keyCode == NS_VK_ESCAPE) {
7496
0
          nsIDocument* root = nsContentUtils::GetRootDocument(doc);
7497
0
          if (root && root->GetFullscreenElement()) {
7498
0
            // Prevent default action on ESC key press when exiting
7499
0
            // DOM fullscreen mode. This prevents the browser ESC key
7500
0
            // handler from stopping all loads in the document, which
7501
0
            // would cause <video> loads to stop.
7502
0
            // XXX We need to claim the Escape key event which will be
7503
0
            //     dispatched only into chrome is already consumed by
7504
0
            //     content because we need to prevent its default here
7505
0
            //     for some reasons (not sure) but we need to detect
7506
0
            //     if a chrome event handler will call PreventDefault()
7507
0
            //     again and check it later.
7508
0
            aEvent->PreventDefaultBeforeDispatch(CrossProcessForwarding::eStop);
7509
0
            aEvent->mFlags.mOnlyChromeDispatch = true;
7510
0
7511
0
            // The event listeners in chrome can prevent this ESC behavior by
7512
0
            // calling prevent default on the preceding keydown/press events.
7513
0
            if (!mIsLastChromeOnlyEscapeKeyConsumed &&
7514
0
                aEvent->mMessage == eKeyUp) {
7515
0
              // ESC key released while in DOM fullscreen mode.
7516
0
              // Fully exit all browser windows and documents from
7517
0
              // fullscreen mode.
7518
0
              nsIDocument::AsyncExitFullscreen(nullptr);
7519
0
            }
7520
0
          }
7521
0
          nsCOMPtr<nsIDocument> pointerLockedDoc =
7522
0
            do_QueryReferent(EventStateManager::sPointerLockedDoc);
7523
0
          if (!mIsLastChromeOnlyEscapeKeyConsumed && pointerLockedDoc) {
7524
0
            // XXX See above comment to understand the reason why this needs
7525
0
            //     to claim that the Escape key event is consumed by content
7526
0
            //     even though it will be dispatched only into chrome.
7527
0
            aEvent->PreventDefaultBeforeDispatch(CrossProcessForwarding::eStop);
7528
0
            aEvent->mFlags.mOnlyChromeDispatch = true;
7529
0
            if (aEvent->mMessage == eKeyUp) {
7530
0
              nsIDocument::UnlockPointer();
7531
0
            }
7532
0
          }
7533
0
        }
7534
0
        if (keyCode != NS_VK_ESCAPE && keyCode != NS_VK_SHIFT &&
7535
0
            keyCode != NS_VK_CONTROL && keyCode != NS_VK_ALT &&
7536
0
            keyCode != NS_VK_WIN && keyCode != NS_VK_META) {
7537
0
          // Allow keys other than ESC and modifiers be marked as a
7538
0
          // valid user input for triggering popup, fullscreen, and
7539
0
          // pointer lock.
7540
0
          isHandlingUserInput = true;
7541
0
          mPresContext->RecordInteractionTime(
7542
0
            nsPresContext::InteractionType::eKeyInteraction,
7543
0
            aEvent->mTimeStamp);
7544
0
        }
7545
0
7546
0
        Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_QUEUED_KEYBOARD_MS, aEvent->mTimeStamp);
7547
0
        break;
7548
0
      }
7549
0
      case eMouseDown:
7550
0
      case eMouseUp:
7551
0
        Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_QUEUED_CLICK_MS, aEvent->mTimeStamp);
7552
0
        MOZ_FALLTHROUGH;
7553
0
      case ePointerDown:
7554
0
      case ePointerUp:
7555
0
        isHandlingUserInput = true;
7556
0
        mPresContext->RecordInteractionTime(
7557
0
          nsPresContext::InteractionType::eClickInteraction,
7558
0
          aEvent->mTimeStamp);
7559
0
        break;
7560
0
7561
0
      case eMouseMove:
7562
0
        if (aEvent->mFlags.mHandledByAPZ) {
7563
0
          Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_QUEUED_APZ_MOUSE_MOVE_MS, aEvent->mTimeStamp);
7564
0
        }
7565
0
        break;
7566
0
7567
0
      case eDrop: {
7568
0
        nsCOMPtr<nsIDragSession> session = nsContentUtils::GetDragSession();
7569
0
        if (session) {
7570
0
          bool onlyChromeDrop = false;
7571
0
          session->GetOnlyChromeDrop(&onlyChromeDrop);
7572
0
          if (onlyChromeDrop) {
7573
0
            aEvent->mFlags.mOnlyChromeDispatch = true;
7574
0
          }
7575
0
        }
7576
0
        break;
7577
0
      }
7578
0
7579
0
      case eWheel:
7580
0
        if (aEvent->mFlags.mHandledByAPZ) {
7581
0
          Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_QUEUED_APZ_WHEEL_MS, aEvent->mTimeStamp);
7582
0
        }
7583
0
        break;
7584
0
7585
0
      case eTouchMove:
7586
0
        if (aEvent->mFlags.mHandledByAPZ) {
7587
0
          Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_QUEUED_APZ_TOUCH_MOVE_MS, aEvent->mTimeStamp);
7588
0
        }
7589
0
        break;
7590
0
7591
0
      default:
7592
0
        break;
7593
0
      }
7594
0
7595
0
      if (!mTouchManager.PreHandleEvent(aEvent, aStatus,
7596
0
                                        touchIsNew, isHandlingUserInput,
7597
0
                                        mCurrentEventContent)) {
7598
0
        return NS_OK;
7599
0
      }
7600
0
    }
7601
0
7602
0
    if (aEvent->mMessage == eContextMenu) {
7603
0
      WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
7604
0
      if (mouseEvent->IsContextMenuKeyEvent() &&
7605
0
          !AdjustContextMenuKeyEvent(mouseEvent)) {
7606
0
        return NS_OK;
7607
0
      }
7608
0
      if (mouseEvent->IsShift()) {
7609
0
        aEvent->mFlags.mOnlyChromeDispatch = true;
7610
0
        aEvent->mFlags.mRetargetToNonNativeAnonymous = true;
7611
0
      }
7612
0
    }
7613
0
7614
0
    AutoHandlingUserInputStatePusher userInpStatePusher(isHandlingUserInput,
7615
0
                                                        aEvent, mDocument);
7616
0
7617
0
    if (aEvent->IsTrusted() && aEvent->mMessage == eMouseMove) {
7618
0
      nsIPresShell::AllowMouseCapture(
7619
0
        EventStateManager::GetActiveEventStateManager() == manager);
7620
0
7621
0
      mPresContext->RecordInteractionTime(
7622
0
        nsPresContext::InteractionType::eMouseMoveInteraction,
7623
0
        aEvent->mTimeStamp);
7624
0
    }
7625
0
7626
0
    nsAutoPopupStatePusher popupStatePusher(
7627
0
                             Event::GetEventPopupControlState(aEvent));
7628
0
7629
0
    // FIXME. If the event was reused, we need to clear the old target,
7630
0
    // bug 329430
7631
0
    aEvent->mTarget = nullptr;
7632
0
7633
0
    TimeStamp handlerStartTime = TimeStamp::Now();
7634
0
7635
0
    // 1. Give event to event manager for pre event state changes and
7636
0
    //    generation of synthetic events.
7637
0
    rv = manager->PreHandleEvent(mPresContext, aEvent, mCurrentEventFrame,
7638
0
                                 mCurrentEventContent, aStatus,
7639
0
                                 aOverrideClickTarget);
7640
0
7641
0
    // 2. Give event to the DOM for third party and JS use.
7642
0
    if (NS_SUCCEEDED(rv)) {
7643
0
      bool wasHandlingKeyBoardEvent =
7644
0
        nsContentUtils::IsHandlingKeyBoardEvent();
7645
0
      if (aEvent->mClass == eKeyboardEventClass) {
7646
0
        nsContentUtils::SetIsHandlingKeyBoardEvent(true);
7647
0
      }
7648
0
      // If EventStateManager or something wants reply from remote process and
7649
0
      // needs to win any other event listeners in chrome, the event is both
7650
0
      // stopped its propagation and marked as "waiting reply from remote
7651
0
      // process".  In this case, PresShell shouldn't dispatch the event into
7652
0
      // the DOM tree because they don't have a chance to stop propagation in
7653
0
      // the system event group.  On the other hand, if its propagation is not
7654
0
      // stopped, that means that the event may be reserved by chrome.  If it's
7655
0
      // reserved by chrome, the event shouldn't be sent to any remote
7656
0
      // processes.  In this case, PresShell needs to dispatch the event to
7657
0
      // the DOM tree for checking if it's reserved.
7658
0
      if (aEvent->IsAllowedToDispatchDOMEvent() &&
7659
0
          !(aEvent->PropagationStopped() &&
7660
0
            aEvent->IsWaitingReplyFromRemoteProcess())) {
7661
0
        MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(),
7662
0
          "Somebody changed aEvent to cause a DOM event!");
7663
0
        nsPresShellEventCB eventCB(this);
7664
0
        if (nsIFrame* target = GetCurrentEventFrame()) {
7665
0
          if (target->OnlySystemGroupDispatch(aEvent->mMessage)) {
7666
0
              aEvent->StopPropagation();
7667
0
          }
7668
0
        }
7669
0
        if (aEvent->mClass == eTouchEventClass) {
7670
0
          DispatchTouchEventToDOM(aEvent, aStatus, &eventCB, touchIsNew);
7671
0
        } else {
7672
0
          DispatchEventToDOM(aEvent, aStatus, &eventCB);
7673
0
        }
7674
0
      }
7675
0
7676
0
      nsContentUtils::SetIsHandlingKeyBoardEvent(wasHandlingKeyBoardEvent);
7677
0
7678
0
      if (aEvent->mMessage == ePointerUp ||
7679
0
          aEvent->mMessage == ePointerCancel) {
7680
0
        // Implicitly releasing capture for given pointer.
7681
0
        // ePointerLostCapture should be send after ePointerUp or
7682
0
        // ePointerCancel.
7683
0
        WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
7684
0
        MOZ_ASSERT(pointerEvent);
7685
0
        PointerEventHandler::ReleasePointerCaptureById(pointerEvent->pointerId);
7686
0
        PointerEventHandler::CheckPointerCaptureState(pointerEvent);
7687
0
      }
7688
0
7689
0
      // 3. Give event to event manager for post event state changes and
7690
0
      //    generation of synthetic events.
7691
0
      if (!mIsDestroying && NS_SUCCEEDED(rv)) {
7692
0
        rv = manager->PostHandleEvent(mPresContext, aEvent,
7693
0
                                      GetCurrentEventFrame(), aStatus,
7694
0
                                      aOverrideClickTarget);
7695
0
      }
7696
0
    }
7697
0
7698
0
    if (!mIsDestroying && aIsHandlingNativeEvent) {
7699
0
      // Ensure that notifications to IME should be sent before getting next
7700
0
      // native event from the event queue.
7701
0
      // XXX Should we check the event message or event class instead of
7702
0
      //     using aIsHandlingNativeEvent?
7703
0
      manager->TryToFlushPendingNotificationsToIME();
7704
0
    }
7705
0
7706
0
    switch (aEvent->mMessage) {
7707
0
    case eKeyPress:
7708
0
    case eKeyDown:
7709
0
    case eKeyUp: {
7710
0
      if (aEvent->AsKeyboardEvent()->mKeyCode == NS_VK_ESCAPE) {
7711
0
        if (aEvent->mMessage == eKeyUp) {
7712
0
          // Reset this flag after key up is handled.
7713
0
          mIsLastChromeOnlyEscapeKeyConsumed = false;
7714
0
        } else {
7715
0
          if (aEvent->mFlags.mOnlyChromeDispatch &&
7716
0
              aEvent->mFlags.mDefaultPreventedByChrome) {
7717
0
            mIsLastChromeOnlyEscapeKeyConsumed = true;
7718
0
          }
7719
0
        }
7720
0
      }
7721
0
      if (aEvent->mMessage == eKeyDown) {
7722
0
        mIsLastKeyDownCanceled = aEvent->mFlags.mDefaultPrevented;
7723
0
      }
7724
0
      break;
7725
0
    }
7726
0
    case eMouseUp:
7727
0
      // reset the capturing content now that the mouse button is up
7728
0
      SetCapturingContent(nullptr, 0);
7729
0
      break;
7730
0
    case eMouseMove:
7731
0
      nsIPresShell::AllowMouseCapture(false);
7732
0
      break;
7733
0
    case eDrag:
7734
0
    case eDragEnd:
7735
0
    case eDragEnter:
7736
0
    case eDragExit:
7737
0
    case eDragLeave:
7738
0
    case eDragOver:
7739
0
    case eDrop: {
7740
0
      // After any drag event other than dragstart (which is handled separately,
7741
0
      // as we need to collect the data first), the DataTransfer needs to be
7742
0
      // made protected, and then disconnected.
7743
0
      DataTransfer* dataTransfer = aEvent->AsDragEvent()->mDataTransfer;
7744
0
      if (dataTransfer) {
7745
0
        dataTransfer->Disconnect();
7746
0
      }
7747
0
      break;
7748
0
    }
7749
0
    default:
7750
0
      break;
7751
0
    }
7752
0
7753
0
    if (aEvent->IsTrusted() && aEvent->mTimeStamp > mLastOSWake) {
7754
0
      switch (aEvent->mMessage) {
7755
0
        case eKeyPress:
7756
0
        case eKeyDown:
7757
0
        case eKeyUp:
7758
0
          Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_HANDLED_KEYBOARD_MS, handlerStartTime);
7759
0
          break;
7760
0
        case eMouseDown:
7761
0
          Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_HANDLED_MOUSE_DOWN_MS, handlerStartTime);
7762
0
          break;
7763
0
        case eMouseUp:
7764
0
          Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_HANDLED_MOUSE_UP_MS, handlerStartTime);
7765
0
          break;
7766
0
        case eMouseMove:
7767
0
          if (aEvent->mFlags.mHandledByAPZ) {
7768
0
            Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_HANDLED_APZ_MOUSE_MOVE_MS, handlerStartTime);
7769
0
          }
7770
0
          break;
7771
0
        case eWheel:
7772
0
          if (aEvent->mFlags.mHandledByAPZ) {
7773
0
            Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_HANDLED_APZ_WHEEL_MS, handlerStartTime);
7774
0
          }
7775
0
          break;
7776
0
        case eTouchMove:
7777
0
          if (aEvent->mFlags.mHandledByAPZ) {
7778
0
            Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_HANDLED_APZ_TOUCH_MOVE_MS, handlerStartTime);
7779
0
          }
7780
0
          break;
7781
0
        default:
7782
0
          break;
7783
0
      }
7784
0
    }
7785
0
  }
7786
0
7787
0
  if (Telemetry::CanRecordBase() &&
7788
0
      !aEvent->mTimeStamp.IsNull() &&
7789
0
      aEvent->mTimeStamp > mLastOSWake &&
7790
0
      aEvent->AsInputEvent()) {
7791
0
    TimeStamp now = TimeStamp::Now();
7792
0
    double millis = (now - aEvent->mTimeStamp).ToMilliseconds();
7793
0
    Telemetry::Accumulate(Telemetry::INPUT_EVENT_RESPONSE_MS, millis);
7794
0
    if (mDocument && mDocument->GetReadyStateEnum() != nsIDocument::READYSTATE_COMPLETE) {
7795
0
      Telemetry::Accumulate(Telemetry::LOAD_INPUT_EVENT_RESPONSE_MS, millis);
7796
0
    }
7797
0
7798
0
    if (!sLastInputProcessed || sLastInputProcessed < aEvent->mTimeStamp) {
7799
0
      if (sLastInputProcessed) {
7800
0
        // This input event was created after we handled the last one.
7801
0
        // Accumulate the previous events' coalesced duration.
7802
0
        double lastMillis = (sLastInputProcessed - sLastInputCreated).ToMilliseconds();
7803
0
        Telemetry::Accumulate(Telemetry::INPUT_EVENT_RESPONSE_COALESCED_MS,
7804
0
                              lastMillis);
7805
0
7806
0
        if (MOZ_UNLIKELY(!sProcessInteractable)) {
7807
0
          // For content process, we use the ready state of
7808
0
          // top-level-content-document to know if the process has finished the
7809
0
          // start-up.
7810
0
          // For parent process, see the topic
7811
0
          // 'sessionstore-one-or-no-tab-restored' in PresShell::Observe.
7812
0
          if (XRE_IsContentProcess() &&
7813
0
              mDocument && mDocument->IsTopLevelContentDocument()) {
7814
0
            switch (mDocument->GetReadyStateEnum()) {
7815
0
              case nsIDocument::READYSTATE_INTERACTIVE:
7816
0
              case nsIDocument::READYSTATE_COMPLETE:
7817
0
                sProcessInteractable = true;
7818
0
                break;
7819
0
              default:
7820
0
                break;
7821
0
            }
7822
0
          }
7823
0
        }
7824
0
        if (MOZ_LIKELY(sProcessInteractable)) {
7825
0
          Telemetry::Accumulate(Telemetry::INPUT_EVENT_RESPONSE_POST_STARTUP_MS,
7826
0
                                lastMillis);
7827
0
        } else {
7828
0
          Telemetry::Accumulate(Telemetry::INPUT_EVENT_RESPONSE_STARTUP_MS,
7829
0
                                lastMillis);
7830
0
        }
7831
0
      }
7832
0
      sLastInputCreated = aEvent->mTimeStamp;
7833
0
    } else if (aEvent->mTimeStamp < sLastInputCreated) {
7834
0
      // This event was created before the last input. May be processing out
7835
0
      // of order, so coalesce backwards, too.
7836
0
      sLastInputCreated = aEvent->mTimeStamp;
7837
0
    }
7838
0
    sLastInputProcessed = now;
7839
0
  }
7840
0
7841
0
  return rv;
7842
0
}
7843
7844
#ifdef NIGHTLY_BUILD
7845
static already_AddRefed<nsIURI>
7846
GetDocumentURIToCompareWithBlacklist(PresShell& aPresShell)
7847
0
{
7848
0
  nsPresContext* presContext = aPresShell.GetPresContext();
7849
0
  if (NS_WARN_IF(!presContext)) {
7850
0
    return nullptr;
7851
0
  }
7852
0
  // If the document is sandboxed document or data: document, we should
7853
0
  // get URI of the parent document.
7854
0
  for (nsIDocument* document = presContext->Document();
7855
0
       document && document->IsContentDocument();
7856
0
       document = document->GetParentDocument()) {
7857
0
    // The document URI may be about:blank even if it comes from actual web
7858
0
    // site.  Therefore, we need to check the URI of its principal.
7859
0
    nsIPrincipal* principal = document->NodePrincipal();
7860
0
    if (principal->GetIsNullPrincipal()) {
7861
0
      continue;
7862
0
    }
7863
0
    nsCOMPtr<nsIURI> uri;
7864
0
    principal->GetURI(getter_AddRefs(uri));
7865
0
    return uri.forget();
7866
0
  }
7867
0
  return nullptr;
7868
0
}
7869
7870
static bool
7871
DispatchKeyPressEventsEvenForNonPrintableKeys(nsIURI* aURI)
7872
0
{
7873
0
  if (!aURI) {
7874
0
    return false;
7875
0
  }
7876
0
7877
0
  nsAutoCString scheme;
7878
0
  aURI->GetScheme(scheme);
7879
0
  if (!scheme.EqualsLiteral("http") &&
7880
0
      !scheme.EqualsLiteral("https")) {
7881
0
    return false;
7882
0
  }
7883
0
7884
0
  nsAutoCString host;
7885
0
  aURI->GetHost(host);
7886
0
  if (host.IsEmpty()) {
7887
0
    return false;
7888
0
  }
7889
0
7890
0
  // The black list is comma separated domain list.  Each item may start with
7891
0
  // "*.".  If starts with "*.", it matches any sub-domains.
7892
0
  static const char* kPrefNameOfBlackList =
7893
0
    "dom.keyboardevent.keypress.hack.dispatch_non_printable_keys";
7894
0
7895
0
  nsAutoCString blackList;
7896
0
  Preferences::GetCString(kPrefNameOfBlackList, blackList);
7897
0
  if (blackList.IsEmpty()) {
7898
0
    return false;
7899
0
  }
7900
0
7901
0
  for (;;) {
7902
0
    int32_t index = blackList.Find(host, false);
7903
0
    if (index >= 0 &&
7904
0
        static_cast<uint32_t>(index) + host.Length() <= blackList.Length() &&
7905
0
        // If start of the black list or next to ","?
7906
0
        (!index || blackList[index - 1] == ',')) {
7907
0
      // If end of the black list or immediately before ","?
7908
0
      size_t indexAfterHost = index + host.Length();
7909
0
      if (indexAfterHost == blackList.Length() ||
7910
0
          blackList[indexAfterHost] == ',') {
7911
0
        return true;
7912
0
      }
7913
0
      // If next character is '/', we need to check the path too.
7914
0
      // We assume the path in blacklist means "/foo" + "*".
7915
0
      if (blackList[indexAfterHost] == '/') {
7916
0
        int32_t endOfPath = blackList.Find(",", false, indexAfterHost);
7917
0
        nsDependentCSubstring::size_type length =
7918
0
          endOfPath < 0 ? static_cast<nsDependentCSubstring::size_type>(-1) :
7919
0
                          endOfPath - indexAfterHost;
7920
0
        nsDependentCSubstring pathInBlackList(blackList,
7921
0
                                              indexAfterHost, length);
7922
0
        nsAutoCString filePath;
7923
0
        aURI->GetFilePath(filePath);
7924
0
        if (StringBeginsWith(filePath, pathInBlackList)) {
7925
0
          return true;
7926
0
        }
7927
0
      }
7928
0
    }
7929
0
    int32_t startIndexOfCurrentLevel = host[0] == '*' ? 1 : 0;
7930
0
    int32_t startIndexOfNextLevel =
7931
0
      host.Find(".", false, startIndexOfCurrentLevel + 1);
7932
0
    if (startIndexOfNextLevel <= 0) {
7933
0
      return false;
7934
0
    }
7935
0
    host = NS_LITERAL_CSTRING("*") +
7936
0
             nsDependentCSubstring(host, startIndexOfNextLevel);
7937
0
  }
7938
0
}
7939
#endif // #ifdef NIGHTLY_BUILD
7940
7941
nsresult
7942
PresShell::DispatchEventToDOM(WidgetEvent* aEvent,
7943
                              nsEventStatus* aStatus,
7944
                              nsPresShellEventCB* aEventCB)
7945
0
{
7946
0
  nsresult rv = NS_OK;
7947
0
  nsCOMPtr<nsINode> eventTarget = mCurrentEventContent.get();
7948
0
  nsPresShellEventCB* eventCBPtr = aEventCB;
7949
0
  if (!eventTarget) {
7950
0
    nsCOMPtr<nsIContent> targetContent;
7951
0
    if (mCurrentEventFrame) {
7952
0
      rv = mCurrentEventFrame->
7953
0
             GetContentForEvent(aEvent, getter_AddRefs(targetContent));
7954
0
    }
7955
0
    if (NS_SUCCEEDED(rv) && targetContent) {
7956
0
      eventTarget = do_QueryInterface(targetContent);
7957
0
    } else if (mDocument) {
7958
0
      eventTarget = do_QueryInterface(mDocument);
7959
0
      // If we don't have any content, the callback wouldn't probably
7960
0
      // do nothing.
7961
0
      eventCBPtr = nullptr;
7962
0
    }
7963
0
  }
7964
0
  if (eventTarget) {
7965
0
    if (aEvent->IsBlockedForFingerprintingResistance()) {
7966
0
      aEvent->mFlags.mOnlySystemGroupDispatchInContent = true;
7967
0
#ifdef NIGHTLY_BUILD
7968
0
    } else if (aEvent->mMessage == eKeyPress &&
7969
0
               aEvent->mFlags.mOnlySystemGroupDispatchInContent) {
7970
0
      // If eKeyPress event is marked as not dispatched in the default event
7971
0
      // group in web content, it's caused by non-printable key or key
7972
0
      // combination.  In this case, UI Events declares that browsers
7973
0
      // shouldn't dispatch keypress event.  However, some web apps may be
7974
0
      // broken with this strict behavior due to historical issue.
7975
0
      // Therefore, we need to keep dispatching keypress event for such keys
7976
0
      // even with breaking the standard.
7977
0
      if (!mInitializedForceDispatchKeyPressEventsForNonPrintableKeys) {
7978
0
        mInitializedForceDispatchKeyPressEventsForNonPrintableKeys = true;
7979
0
        nsCOMPtr<nsIURI> uri = GetDocumentURIToCompareWithBlacklist(*this);
7980
0
        mForceDispatchKeyPressEventsForNonPrintableKeys =
7981
0
          DispatchKeyPressEventsEvenForNonPrintableKeys(uri);
7982
0
      }
7983
0
      if (mForceDispatchKeyPressEventsForNonPrintableKeys) {
7984
0
        aEvent->mFlags.mOnlySystemGroupDispatchInContent = false;
7985
0
      }
7986
0
#endif // #ifdef NIGHTLY_BUILD
7987
0
    }
7988
0
7989
0
    if (aEvent->mClass == eCompositionEventClass) {
7990
0
      IMEStateManager::DispatchCompositionEvent(eventTarget, mPresContext,
7991
0
                                                aEvent->AsCompositionEvent(),
7992
0
                                                aStatus, eventCBPtr);
7993
0
    } else {
7994
0
      EventDispatcher::Dispatch(eventTarget, mPresContext,
7995
0
                                aEvent, nullptr, aStatus, eventCBPtr);
7996
0
    }
7997
0
  }
7998
0
  return rv;
7999
0
}
8000
8001
void
8002
PresShell::DispatchTouchEventToDOM(WidgetEvent* aEvent,
8003
                                   nsEventStatus* aStatus,
8004
                                   nsPresShellEventCB* aEventCB,
8005
                                   bool aTouchIsNew)
8006
0
{
8007
0
  // calling preventDefault on touchstart or the first touchmove for a
8008
0
  // point prevents mouse events. calling it on the touchend should
8009
0
  // prevent click dispatching.
8010
0
  bool canPrevent = (aEvent->mMessage == eTouchStart) ||
8011
0
                    (aEvent->mMessage == eTouchMove && aTouchIsNew) ||
8012
0
                    (aEvent->mMessage == eTouchEnd);
8013
0
  bool preventDefault = false;
8014
0
  nsEventStatus tmpStatus = nsEventStatus_eIgnore;
8015
0
  WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
8016
0
8017
0
  // loop over all touches and dispatch events on any that have changed
8018
0
  for (dom::Touch* touch : touchEvent->mTouches) {
8019
0
    // We should remove all suppressed touch instances in
8020
0
    // TouchManager::PreHandleEvent.
8021
0
    MOZ_ASSERT(!touch->mIsTouchEventSuppressed);
8022
0
8023
0
    if (!touch || !touch->mChanged) {
8024
0
      continue;
8025
0
    }
8026
0
8027
0
    nsCOMPtr<EventTarget> targetPtr = touch->mTarget;
8028
0
    nsCOMPtr<nsIContent> content = do_QueryInterface(targetPtr);
8029
0
    if (!content) {
8030
0
      continue;
8031
0
    }
8032
0
8033
0
    nsIDocument* doc = content->OwnerDoc();
8034
0
    nsIContent* capturingContent = GetCapturingContent();
8035
0
    if (capturingContent) {
8036
0
      if (capturingContent->OwnerDoc() != doc) {
8037
0
        // Wrong document, don't dispatch anything.
8038
0
        continue;
8039
0
      }
8040
0
      content = capturingContent;
8041
0
    }
8042
0
    // copy the event
8043
0
    WidgetTouchEvent newEvent(touchEvent->IsTrusted(),
8044
0
                              touchEvent->mMessage, touchEvent->mWidget);
8045
0
    newEvent.AssignTouchEventData(*touchEvent, false);
8046
0
    newEvent.mTarget = targetPtr;
8047
0
    newEvent.mFlags.mHandledByAPZ = touchEvent->mFlags.mHandledByAPZ;
8048
0
8049
0
    RefPtr<PresShell> contentPresShell;
8050
0
    if (doc == mDocument) {
8051
0
      contentPresShell = static_cast<PresShell*>(doc->GetShell());
8052
0
      if (contentPresShell) {
8053
0
        //XXXsmaug huge hack. Pushing possibly capturing content,
8054
0
        //         even though event target is something else.
8055
0
        contentPresShell->PushCurrentEventInfo(
8056
0
            content->GetPrimaryFrame(), content);
8057
0
      }
8058
0
    }
8059
0
8060
0
    nsPresContext *context = doc->GetPresContext();
8061
0
    if (!context) {
8062
0
      continue;
8063
0
    }
8064
0
8065
0
    tmpStatus = nsEventStatus_eIgnore;
8066
0
    EventDispatcher::Dispatch(targetPtr, context,
8067
0
                              &newEvent, nullptr, &tmpStatus, aEventCB);
8068
0
    if (nsEventStatus_eConsumeNoDefault == tmpStatus ||
8069
0
        newEvent.mFlags.mMultipleActionsPrevented) {
8070
0
      preventDefault = true;
8071
0
    }
8072
0
8073
0
    if (newEvent.mFlags.mMultipleActionsPrevented) {
8074
0
      touchEvent->mFlags.mMultipleActionsPrevented = true;
8075
0
    }
8076
0
8077
0
    if (contentPresShell) {
8078
0
      contentPresShell->PopCurrentEventInfo();
8079
0
    }
8080
0
  }
8081
0
8082
0
  if (preventDefault && canPrevent) {
8083
0
    *aStatus = nsEventStatus_eConsumeNoDefault;
8084
0
  } else {
8085
0
    *aStatus = nsEventStatus_eIgnore;
8086
0
  }
8087
0
}
8088
8089
// Dispatch event to content only (NOT full processing)
8090
// See also HandleEventWithTarget which does full event processing.
8091
nsresult
8092
PresShell::HandleDOMEventWithTarget(nsIContent* aTargetContent,
8093
                                    WidgetEvent* aEvent,
8094
                                    nsEventStatus* aStatus)
8095
0
{
8096
0
  nsresult rv = NS_OK;
8097
0
8098
0
  PushCurrentEventInfo(nullptr, aTargetContent);
8099
0
8100
0
  // Bug 41013: Check if the event should be dispatched to content.
8101
0
  // It's possible that we are in the middle of destroying the window
8102
0
  // and the js context is out of date. This check detects the case
8103
0
  // that caused a crash in bug 41013, but there may be a better way
8104
0
  // to handle this situation!
8105
0
  nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak();
8106
0
  if (container) {
8107
0
8108
0
    // Dispatch event to content
8109
0
    rv = EventDispatcher::Dispatch(aTargetContent, mPresContext, aEvent,
8110
0
                                   nullptr, aStatus);
8111
0
  }
8112
0
8113
0
  PopCurrentEventInfo();
8114
0
  return rv;
8115
0
}
8116
8117
// See the method above.
8118
nsresult
8119
PresShell::HandleDOMEventWithTarget(nsIContent* aTargetContent,
8120
                                    Event* aEvent,
8121
                                    nsEventStatus* aStatus)
8122
0
{
8123
0
  nsresult rv = NS_OK;
8124
0
8125
0
  PushCurrentEventInfo(nullptr, aTargetContent);
8126
0
  nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak();
8127
0
  if (container) {
8128
0
    rv = EventDispatcher::DispatchDOMEvent(aTargetContent, nullptr, aEvent,
8129
0
                                           mPresContext, aStatus);
8130
0
  }
8131
0
8132
0
  PopCurrentEventInfo();
8133
0
  return rv;
8134
0
}
8135
8136
bool
8137
PresShell::AdjustContextMenuKeyEvent(WidgetMouseEvent* aEvent)
8138
0
{
8139
0
#ifdef MOZ_XUL
8140
0
  // if a menu is open, open the context menu relative to the active item on the menu.
8141
0
  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
8142
0
  if (pm) {
8143
0
    nsIFrame* popupFrame = pm->GetTopPopup(ePopupTypeMenu);
8144
0
    if (popupFrame) {
8145
0
      nsIFrame* itemFrame =
8146
0
        (static_cast<nsMenuPopupFrame *>(popupFrame))->GetCurrentMenuItem();
8147
0
      if (!itemFrame)
8148
0
        itemFrame = popupFrame;
8149
0
8150
0
      nsCOMPtr<nsIWidget> widget = popupFrame->GetNearestWidget();
8151
0
      aEvent->mWidget = widget;
8152
0
      LayoutDeviceIntPoint widgetPoint = widget->WidgetToScreenOffset();
8153
0
      aEvent->mRefPoint = LayoutDeviceIntPoint::FromAppUnitsToNearest(
8154
0
        itemFrame->GetScreenRectInAppUnits().BottomLeft(),
8155
0
        itemFrame->PresContext()->AppUnitsPerDevPixel()) - widgetPoint;
8156
0
8157
0
      mCurrentEventContent = itemFrame->GetContent();
8158
0
      mCurrentEventFrame = itemFrame;
8159
0
8160
0
      return true;
8161
0
    }
8162
0
  }
8163
0
#endif
8164
0
8165
0
  // If we're here because of the key-equiv for showing context menus, we
8166
0
  // have to twiddle with the NS event to make sure the context menu comes
8167
0
  // up in the upper left of the relevant content area before we create
8168
0
  // the DOM event. Since we never call InitMouseEvent() on the event,
8169
0
  // the client X/Y will be 0,0. We can make use of that if the widget is null.
8170
0
  // Use the root view manager's widget since it's most likely to have one,
8171
0
  // and the coordinates returned by GetCurrentItemAndPositionForElement
8172
0
  // are relative to the widget of the root of the root view manager.
8173
0
  nsRootPresContext* rootPC = mPresContext->GetRootPresContext();
8174
0
  aEvent->mRefPoint = LayoutDeviceIntPoint(0, 0);
8175
0
  if (rootPC) {
8176
0
    rootPC->PresShell()->GetViewManager()->
8177
0
      GetRootWidget(getter_AddRefs(aEvent->mWidget));
8178
0
8179
0
    if (aEvent->mWidget) {
8180
0
      // default the refpoint to the topleft of our document
8181
0
      nsPoint offset(0, 0);
8182
0
      nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
8183
0
      if (rootFrame) {
8184
0
        nsView* view = rootFrame->GetClosestView(&offset);
8185
0
        offset += view->GetOffsetToWidget(aEvent->mWidget);
8186
0
        aEvent->mRefPoint =
8187
0
          LayoutDeviceIntPoint::FromAppUnitsToNearest(offset, mPresContext->AppUnitsPerDevPixel());
8188
0
      }
8189
0
    }
8190
0
  } else {
8191
0
    aEvent->mWidget = nullptr;
8192
0
  }
8193
0
8194
0
  // see if we should use the caret position for the popup
8195
0
  LayoutDeviceIntPoint caretPoint;
8196
0
  // Beware! This may flush notifications via synchronous
8197
0
  // ScrollSelectionIntoView.
8198
0
  if (PrepareToUseCaretPosition(aEvent->mWidget, caretPoint)) {
8199
0
    // caret position is good
8200
0
    aEvent->mRefPoint = caretPoint;
8201
0
    return true;
8202
0
  }
8203
0
8204
0
  // If we're here because of the key-equiv for showing context menus, we
8205
0
  // have to reset the event target to the currently focused element. Get it
8206
0
  // from the focus controller.
8207
0
  RefPtr<Element> currentFocus;
8208
0
  nsFocusManager* fm = nsFocusManager::GetFocusManager();
8209
0
  if (fm) {
8210
0
    currentFocus = fm->GetFocusedElement();
8211
0
  }
8212
0
8213
0
  // Reset event coordinates relative to focused frame in view
8214
0
  if (currentFocus) {
8215
0
    nsCOMPtr<nsIContent> currentPointElement;
8216
0
    GetCurrentItemAndPositionForElement(currentFocus,
8217
0
                                        getter_AddRefs(currentPointElement),
8218
0
                                        aEvent->mRefPoint,
8219
0
                                        aEvent->mWidget);
8220
0
    if (currentPointElement) {
8221
0
      mCurrentEventContent = currentPointElement;
8222
0
      mCurrentEventFrame = nullptr;
8223
0
      GetCurrentEventFrame();
8224
0
    }
8225
0
  }
8226
0
8227
0
  return true;
8228
0
}
8229
8230
// PresShell::PrepareToUseCaretPosition
8231
//
8232
//    This checks to see if we should use the caret position for popup context
8233
//    menus. Returns true if the caret position should be used, and the
8234
//    coordinates of that position is returned in aTargetPt. This function
8235
//    will also scroll the window as needed to make the caret visible.
8236
//
8237
//    The event widget should be the widget that generated the event, and
8238
//    whose coordinate system the resulting event's mRefPoint should be
8239
//    relative to.  The returned point is in device pixels realtive to the
8240
//    widget passed in.
8241
bool
8242
PresShell::PrepareToUseCaretPosition(nsIWidget* aEventWidget,
8243
                                     LayoutDeviceIntPoint& aTargetPt)
8244
0
{
8245
0
  nsresult rv;
8246
0
8247
0
  // check caret visibility
8248
0
  RefPtr<nsCaret> caret = GetCaret();
8249
0
  NS_ENSURE_TRUE(caret, false);
8250
0
8251
0
  bool caretVisible = caret->IsVisible();
8252
0
  if (!caretVisible)
8253
0
    return false;
8254
0
8255
0
  // caret selection, this is a temporary weak reference, so no refcounting is
8256
0
  // needed
8257
0
  Selection* domSelection = caret->GetSelection();
8258
0
  NS_ENSURE_TRUE(domSelection, false);
8259
0
8260
0
  // since the match could be an anonymous textnode inside a
8261
0
  // <textarea> or text <input>, we need to get the outer frame
8262
0
  // note: frames are not refcounted
8263
0
  nsIFrame* frame = nullptr; // may be nullptr
8264
0
  nsINode* node = domSelection->GetFocusNode();
8265
0
  NS_ENSURE_TRUE(node, false);
8266
0
  nsCOMPtr<nsIContent> content = nsIContent::FromNode(node);
8267
0
  if (content) {
8268
0
    nsIContent* nonNative = content->FindFirstNonChromeOnlyAccessContent();
8269
0
    content = nonNative;
8270
0
  }
8271
0
8272
0
  if (content) {
8273
0
    // It seems like ScrollSelectionIntoView should be enough, but it's
8274
0
    // not. The problem is that scrolling the selection into view when it is
8275
0
    // below the current viewport will align the top line of the frame exactly
8276
0
    // with the bottom of the window. This is fine, BUT, the popup event causes
8277
0
    // the control to be re-focused which does this exact call to
8278
0
    // ScrollContentIntoView, which has a one-pixel disagreement of whether the
8279
0
    // frame is actually in view. The result is that the frame is aligned with
8280
0
    // the top of the window, but the menu is still at the bottom.
8281
0
    //
8282
0
    // Doing this call first forces the frame to be in view, eliminating the
8283
0
    // problem. The only difference in the result is that if your cursor is in
8284
0
    // an edit box below the current view, you'll get the edit box aligned with
8285
0
    // the top of the window. This is arguably better behavior anyway.
8286
0
    rv = ScrollContentIntoView(content,
8287
0
                               nsIPresShell::ScrollAxis(
8288
0
                                 nsIPresShell::SCROLL_MINIMUM,
8289
0
                                 nsIPresShell::SCROLL_IF_NOT_VISIBLE),
8290
0
                               nsIPresShell::ScrollAxis(
8291
0
                                 nsIPresShell::SCROLL_MINIMUM,
8292
0
                                 nsIPresShell::SCROLL_IF_NOT_VISIBLE),
8293
0
                               nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
8294
0
    NS_ENSURE_SUCCESS(rv, false);
8295
0
    frame = content->GetPrimaryFrame();
8296
0
    NS_WARNING_ASSERTION(frame, "No frame for focused content?");
8297
0
  }
8298
0
8299
0
  // Actually scroll the selection (ie caret) into view. Note that this must
8300
0
  // be synchronous since we will be checking the caret position on the screen.
8301
0
  //
8302
0
  // Be easy about errors, and just don't scroll in those cases. Better to have
8303
0
  // the correct menu at a weird place than the wrong menu.
8304
0
  // After ScrollSelectionIntoView(), the pending notifications might be
8305
0
  // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
8306
0
  nsCOMPtr<nsISelectionController> selCon;
8307
0
  if (frame)
8308
0
    frame->GetSelectionController(GetPresContext(), getter_AddRefs(selCon));
8309
0
  else
8310
0
    selCon = static_cast<nsISelectionController *>(this);
8311
0
  if (selCon) {
8312
0
    rv = selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
8313
0
                                         nsISelectionController::SELECTION_FOCUS_REGION,
8314
0
                                         nsISelectionController::SCROLL_SYNCHRONOUS);
8315
0
    NS_ENSURE_SUCCESS(rv, false);
8316
0
  }
8317
0
8318
0
  nsPresContext* presContext = GetPresContext();
8319
0
8320
0
  // get caret position relative to the closest view
8321
0
  nsRect caretCoords;
8322
0
  nsIFrame* caretFrame = caret->GetGeometry(&caretCoords);
8323
0
  if (!caretFrame)
8324
0
    return false;
8325
0
  nsPoint viewOffset;
8326
0
  nsView* view = caretFrame->GetClosestView(&viewOffset);
8327
0
  if (!view)
8328
0
    return false;
8329
0
  // and then get the caret coords relative to the event widget
8330
0
  if (aEventWidget) {
8331
0
    viewOffset += view->GetOffsetToWidget(aEventWidget);
8332
0
  }
8333
0
  caretCoords.MoveBy(viewOffset);
8334
0
8335
0
  // caret coordinates are in app units, convert to pixels
8336
0
  aTargetPt.x =
8337
0
    presContext->AppUnitsToDevPixels(caretCoords.x + caretCoords.width);
8338
0
  aTargetPt.y =
8339
0
    presContext->AppUnitsToDevPixels(caretCoords.y + caretCoords.height);
8340
0
8341
0
  // make sure rounding doesn't return a pixel which is outside the caret
8342
0
  // (e.g. one line lower)
8343
0
  aTargetPt.y -= 1;
8344
0
8345
0
  return true;
8346
0
}
8347
8348
void
8349
PresShell::GetCurrentItemAndPositionForElement(Element* aFocusedElement,
8350
                                               nsIContent** aTargetToUse,
8351
                                               LayoutDeviceIntPoint& aTargetPt,
8352
                                               nsIWidget *aRootWidget)
8353
0
{
8354
0
  nsCOMPtr<nsIContent> focusedContent = aFocusedElement;
8355
0
  ScrollContentIntoView(focusedContent,
8356
0
                        ScrollAxis(),
8357
0
                        ScrollAxis(),
8358
0
                        nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
8359
0
8360
0
  nsPresContext* presContext = GetPresContext();
8361
0
8362
0
  bool istree = false, checkLineHeight = true;
8363
0
  nscoord extraTreeY = 0;
8364
0
8365
0
#ifdef MOZ_XUL
8366
0
  // Set the position to just underneath the current item for multi-select
8367
0
  // lists or just underneath the selected item for single-select lists. If
8368
0
  // the element is not a list, or there is no selection, leave the position
8369
0
  // as is.
8370
0
  nsCOMPtr<nsIDOMXULSelectControlItemElement> item;
8371
0
  nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
8372
0
    do_QueryInterface(aFocusedElement);
8373
0
  if (multiSelect) {
8374
0
    checkLineHeight = false;
8375
0
8376
0
    int32_t currentIndex;
8377
0
    multiSelect->GetCurrentIndex(&currentIndex);
8378
0
    if (currentIndex >= 0) {
8379
0
      RefPtr<nsXULElement> xulElement = nsXULElement::FromNode(focusedContent);
8380
0
      if (xulElement) {
8381
0
        nsCOMPtr<nsIBoxObject> box = xulElement->GetBoxObject(IgnoreErrors());
8382
0
        nsCOMPtr<nsITreeBoxObject> treeBox(do_QueryInterface(box));
8383
0
        // Tree view special case (tree items have no frames)
8384
0
        // Get the focused row and add its coordinates, which are already in pixels
8385
0
        // XXX Boris, should we create a new interface so that this doesn't
8386
0
        // need to know about trees? Something like nsINodelessChildCreator which
8387
0
        // could provide the current focus coordinates?
8388
0
        if (treeBox) {
8389
0
          treeBox->EnsureRowIsVisible(currentIndex);
8390
0
          int32_t firstVisibleRow, rowHeight;
8391
0
          treeBox->GetFirstVisibleRow(&firstVisibleRow);
8392
0
          treeBox->GetRowHeight(&rowHeight);
8393
0
8394
0
          extraTreeY += nsPresContext::CSSPixelsToAppUnits(
8395
0
                          (currentIndex - firstVisibleRow + 1) * rowHeight);
8396
0
          istree = true;
8397
0
8398
0
          RefPtr<nsTreeColumns> cols;
8399
0
          treeBox->GetColumns(getter_AddRefs(cols));
8400
0
          if (cols) {
8401
0
            nsTreeColumn* col = cols->GetFirstColumn();
8402
0
            if (col) {
8403
0
              RefPtr<Element> colElement = col->Element();
8404
0
              nsIFrame* frame = colElement->GetPrimaryFrame();
8405
0
              if (frame) {
8406
0
                extraTreeY += frame->GetSize().height;
8407
0
              }
8408
0
            }
8409
0
          }
8410
0
        }
8411
0
        else {
8412
0
          multiSelect->GetCurrentItem(getter_AddRefs(item));
8413
0
        }
8414
0
      }
8415
0
    }
8416
0
  }
8417
0
  else {
8418
0
    // don't check menulists as the selected item will be inside a popup.
8419
0
    nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(aFocusedElement);
8420
0
    if (!menulist) {
8421
0
      nsCOMPtr<nsIDOMXULSelectControlElement> select =
8422
0
        do_QueryInterface(aFocusedElement);
8423
0
      if (select) {
8424
0
        checkLineHeight = false;
8425
0
        select->GetSelectedItem(getter_AddRefs(item));
8426
0
      }
8427
0
    }
8428
0
  }
8429
0
8430
0
  if (item)
8431
0
    focusedContent = do_QueryInterface(item);
8432
0
#endif
8433
0
8434
0
  nsIFrame *frame = focusedContent->GetPrimaryFrame();
8435
0
  if (frame) {
8436
0
    NS_ASSERTION(frame->PresContext() == GetPresContext(),
8437
0
      "handling event for focused content that is not in our document?");
8438
0
8439
0
    nsPoint frameOrigin(0, 0);
8440
0
8441
0
    // Get the frame's origin within its view
8442
0
    nsView *view = frame->GetClosestView(&frameOrigin);
8443
0
    NS_ASSERTION(view, "No view for frame");
8444
0
8445
0
    // View's origin relative the widget
8446
0
    if (aRootWidget) {
8447
0
      frameOrigin += view->GetOffsetToWidget(aRootWidget);
8448
0
    }
8449
0
8450
0
    // Start context menu down and to the right from top left of frame
8451
0
    // use the lineheight. This is a good distance to move the context
8452
0
    // menu away from the top left corner of the frame. If we always
8453
0
    // used the frame height, the context menu could end up far away,
8454
0
    // for example when we're focused on linked images.
8455
0
    // On the other hand, we want to use the frame height if it's less
8456
0
    // than the current line height, so that the context menu appears
8457
0
    // associated with the correct frame.
8458
0
    nscoord extra = 0;
8459
0
    if (!istree) {
8460
0
      extra = frame->GetSize().height;
8461
0
      if (checkLineHeight) {
8462
0
        nsIScrollableFrame *scrollFrame =
8463
0
          nsLayoutUtils::GetNearestScrollableFrame(frame);
8464
0
        if (scrollFrame) {
8465
0
          nsSize scrollAmount = scrollFrame->GetLineScrollAmount();
8466
0
          nsIFrame* f = do_QueryFrame(scrollFrame);
8467
0
          int32_t APD = presContext->AppUnitsPerDevPixel();
8468
0
          int32_t scrollAPD = f->PresContext()->AppUnitsPerDevPixel();
8469
0
          scrollAmount = scrollAmount.ScaleToOtherAppUnits(scrollAPD, APD);
8470
0
          if (extra > scrollAmount.height) {
8471
0
            extra = scrollAmount.height;
8472
0
          }
8473
0
        }
8474
0
      }
8475
0
    }
8476
0
8477
0
    aTargetPt.x = presContext->AppUnitsToDevPixels(frameOrigin.x);
8478
0
    aTargetPt.y = presContext->AppUnitsToDevPixels(
8479
0
                    frameOrigin.y + extra + extraTreeY);
8480
0
  }
8481
0
8482
0
  NS_IF_ADDREF(*aTargetToUse = focusedContent);
8483
0
}
8484
8485
bool
8486
PresShell::ShouldIgnoreInvalidation()
8487
0
{
8488
0
  return mPaintingSuppressed || !mIsActive || mIsNeverPainting;
8489
0
}
8490
8491
void
8492
PresShell::WillPaint()
8493
0
{
8494
0
  // Check the simplest things first.  In particular, it's important to
8495
0
  // check mIsActive before making any of the more expensive calls such
8496
0
  // as GetRootPresContext, for the case of a browser with a large
8497
0
  // number of tabs.
8498
0
  // Don't bother doing anything if some viewmanager in our tree is painting
8499
0
  // while we still have painting suppressed or we are not active.
8500
0
  if (!mIsActive || mPaintingSuppressed || !IsVisible()) {
8501
0
    return;
8502
0
  }
8503
0
8504
0
  nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
8505
0
  if (!rootPresContext) {
8506
0
    // In some edge cases, such as when we don't have a root frame yet,
8507
0
    // we can't find the root prescontext. There's nothing to do in that
8508
0
    // case.
8509
0
    return;
8510
0
  }
8511
0
8512
0
  rootPresContext->FlushWillPaintObservers();
8513
0
  if (mIsDestroying)
8514
0
    return;
8515
0
8516
0
  // Process reflows, if we have them, to reduce flicker due to invalidates and
8517
0
  // reflow being interspersed.  Note that we _do_ allow this to be
8518
0
  // interruptible; if we can't do all the reflows it's better to flicker a bit
8519
0
  // than to freeze up.
8520
0
  FlushPendingNotifications(ChangesToFlush(FlushType::InterruptibleLayout, false));
8521
0
}
8522
8523
void
8524
PresShell::WillPaintWindow()
8525
0
{
8526
0
  nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
8527
0
  if (rootPresContext != mPresContext) {
8528
0
    // This could be a popup's presshell. We don't allow plugins in popups
8529
0
    // so there's nothing to do here.
8530
0
    return;
8531
0
  }
8532
0
8533
0
#ifndef XP_MACOSX
8534
0
  rootPresContext->ApplyPluginGeometryUpdates();
8535
0
#endif
8536
0
}
8537
8538
void
8539
PresShell::DidPaintWindow()
8540
0
{
8541
0
  nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
8542
0
  if (rootPresContext != mPresContext) {
8543
0
    // This could be a popup's presshell. No point in notifying XPConnect
8544
0
    // about compositing of popups.
8545
0
    return;
8546
0
  }
8547
0
8548
0
  if (!mHasReceivedPaintMessage) {
8549
0
    mHasReceivedPaintMessage = true;
8550
0
8551
0
    nsCOMPtr<nsIObserverService> obsvc = services::GetObserverService();
8552
0
    if (obsvc && mDocument) {
8553
0
      nsPIDOMWindowOuter* window = mDocument->GetWindow();
8554
0
      nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(window));
8555
0
      if (chromeWin) {
8556
0
        obsvc->NotifyObservers(chromeWin, "widget-first-paint", nullptr);
8557
0
      }
8558
0
    }
8559
0
  }
8560
0
}
8561
8562
bool
8563
PresShell::IsVisible()
8564
0
{
8565
0
  if (!mIsActive || !mViewManager)
8566
0
    return false;
8567
0
8568
0
  nsView* view = mViewManager->GetRootView();
8569
0
  if (!view)
8570
0
    return true;
8571
0
8572
0
  // inner view of subdoc frame
8573
0
  view = view->GetParent();
8574
0
  if (!view)
8575
0
    return true;
8576
0
8577
0
  // subdoc view
8578
0
  view = view->GetParent();
8579
0
  if (!view)
8580
0
    return true;
8581
0
8582
0
  nsIFrame* frame = view->GetFrame();
8583
0
  if (!frame)
8584
0
    return true;
8585
0
8586
0
  return frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY);
8587
0
}
8588
8589
void
8590
PresShell::SuppressDisplayport(bool aEnabled)
8591
0
{
8592
0
  if (aEnabled) {
8593
0
    mActiveSuppressDisplayport++;
8594
0
  } else if (mActiveSuppressDisplayport > 0) {
8595
0
    bool isSuppressed = IsDisplayportSuppressed();
8596
0
    mActiveSuppressDisplayport--;
8597
0
    if (isSuppressed && !IsDisplayportSuppressed()) {
8598
0
      // We unsuppressed the displayport, trigger a paint
8599
0
      if (nsIFrame* rootFrame = mFrameConstructor->GetRootFrame()) {
8600
0
        rootFrame->SchedulePaint();
8601
0
      }
8602
0
    }
8603
0
  }
8604
0
}
8605
8606
static bool sDisplayPortSuppressionRespected = true;
8607
8608
void
8609
PresShell::RespectDisplayportSuppression(bool aEnabled)
8610
0
{
8611
0
  bool isSuppressed = IsDisplayportSuppressed();
8612
0
  sDisplayPortSuppressionRespected = aEnabled;
8613
0
  if (isSuppressed && !IsDisplayportSuppressed()) {
8614
0
    // We unsuppressed the displayport, trigger a paint
8615
0
    if (nsIFrame* rootFrame = mFrameConstructor->GetRootFrame()) {
8616
0
      rootFrame->SchedulePaint();
8617
0
    }
8618
0
  }
8619
0
}
8620
8621
bool
8622
PresShell::IsDisplayportSuppressed()
8623
0
{
8624
0
  return sDisplayPortSuppressionRespected &&
8625
0
         mActiveSuppressDisplayport > 0;
8626
0
}
8627
8628
nsresult
8629
PresShell::GetAgentStyleSheets(nsTArray<RefPtr<StyleSheet>>& aSheets)
8630
0
{
8631
0
  aSheets.Clear();
8632
0
  int32_t sheetCount = mStyleSet->SheetCount(SheetType::Agent);
8633
0
8634
0
  if (!aSheets.SetCapacity(sheetCount, fallible)) {
8635
0
    return NS_ERROR_OUT_OF_MEMORY;
8636
0
  }
8637
0
8638
0
  for (int32_t i = 0; i < sheetCount; ++i) {
8639
0
    StyleSheet* sheet = mStyleSet->StyleSheetAt(SheetType::Agent, i);
8640
0
    aSheets.AppendElement(sheet);
8641
0
  }
8642
0
8643
0
  return NS_OK;
8644
0
}
8645
8646
nsresult
8647
PresShell::SetAgentStyleSheets(const nsTArray<RefPtr<StyleSheet>>& aSheets)
8648
0
{
8649
0
  return mStyleSet->ReplaceSheets(SheetType::Agent, aSheets);
8650
0
}
8651
8652
nsresult
8653
PresShell::AddOverrideStyleSheet(StyleSheet* aSheet)
8654
0
{
8655
0
  return mStyleSet->PrependStyleSheet(SheetType::Override, aSheet);
8656
0
}
8657
8658
nsresult
8659
PresShell::RemoveOverrideStyleSheet(StyleSheet* aSheet)
8660
0
{
8661
0
  return mStyleSet->RemoveStyleSheet(SheetType::Override, aSheet);
8662
0
}
8663
8664
static void
8665
FreezeElement(nsISupports *aSupports, void * /* unused */)
8666
0
{
8667
0
  nsCOMPtr<nsIObjectLoadingContent> olc(do_QueryInterface(aSupports));
8668
0
  if (olc) {
8669
0
    olc->StopPluginInstance();
8670
0
  }
8671
0
}
8672
8673
static bool
8674
FreezeSubDocument(nsIDocument *aDocument, void *aData)
8675
0
{
8676
0
  nsIPresShell *shell = aDocument->GetShell();
8677
0
  if (shell)
8678
0
    shell->Freeze();
8679
0
8680
0
  return true;
8681
0
}
8682
8683
void
8684
PresShell::Freeze()
8685
0
{
8686
0
  mUpdateApproximateFrameVisibilityEvent.Revoke();
8687
0
8688
0
  MaybeReleaseCapturingContent();
8689
0
8690
0
  mDocument->EnumerateActivityObservers(FreezeElement, nullptr);
8691
0
8692
0
  if (mCaret) {
8693
0
    SetCaretEnabled(false);
8694
0
  }
8695
0
8696
0
  mPaintingSuppressed = true;
8697
0
8698
0
  if (mDocument) {
8699
0
    mDocument->EnumerateSubDocuments(FreezeSubDocument, nullptr);
8700
0
  }
8701
0
8702
0
  nsPresContext* presContext = GetPresContext();
8703
0
  if (presContext) {
8704
0
    presContext->DisableInteractionTimeRecording();
8705
0
    if (presContext->RefreshDriver()->GetPresContext() == presContext) {
8706
0
      presContext->RefreshDriver()->Freeze();
8707
0
    }
8708
0
  }
8709
0
8710
0
  mFrozen = true;
8711
0
  if (mDocument) {
8712
0
    UpdateImageLockingState();
8713
0
  }
8714
0
}
8715
8716
void
8717
PresShell::FireOrClearDelayedEvents(bool aFireEvents)
8718
0
{
8719
0
  mNoDelayedMouseEvents = false;
8720
0
  mNoDelayedKeyEvents = false;
8721
0
  if (!aFireEvents) {
8722
0
    mDelayedEvents.Clear();
8723
0
    return;
8724
0
  }
8725
0
8726
0
  if (mDocument) {
8727
0
    nsCOMPtr<nsIDocument> doc = mDocument;
8728
0
    while (!mIsDestroying && mDelayedEvents.Length() &&
8729
0
           !doc->EventHandlingSuppressed()) {
8730
0
      nsAutoPtr<DelayedEvent> ev(mDelayedEvents[0].forget());
8731
0
      mDelayedEvents.RemoveElementAt(0);
8732
0
      if (ev->IsKeyPressEvent() && mIsLastKeyDownCanceled) {
8733
0
        continue;
8734
0
      }
8735
0
      ev->Dispatch();
8736
0
    }
8737
0
    if (!doc->EventHandlingSuppressed()) {
8738
0
      mDelayedEvents.Clear();
8739
0
    }
8740
0
  }
8741
0
}
8742
8743
static void
8744
ThawElement(nsISupports *aSupports, void *aShell)
8745
0
{
8746
0
  nsCOMPtr<nsIObjectLoadingContent> olc(do_QueryInterface(aSupports));
8747
0
  if (olc) {
8748
0
    olc->AsyncStartPluginInstance();
8749
0
  }
8750
0
}
8751
8752
static bool
8753
ThawSubDocument(nsIDocument *aDocument, void *aData)
8754
0
{
8755
0
  nsIPresShell *shell = aDocument->GetShell();
8756
0
  if (shell)
8757
0
    shell->Thaw();
8758
0
8759
0
  return true;
8760
0
}
8761
8762
void
8763
PresShell::Thaw()
8764
0
{
8765
0
  nsPresContext* presContext = GetPresContext();
8766
0
  if (presContext &&
8767
0
      presContext->RefreshDriver()->GetPresContext() == presContext) {
8768
0
    presContext->RefreshDriver()->Thaw();
8769
0
  }
8770
0
8771
0
  mDocument->EnumerateActivityObservers(ThawElement, this);
8772
0
8773
0
  if (mDocument)
8774
0
    mDocument->EnumerateSubDocuments(ThawSubDocument, nullptr);
8775
0
8776
0
  // Get the activeness of our presshell, as this might have changed
8777
0
  // while we were in the bfcache
8778
0
  QueryIsActive();
8779
0
8780
0
  // We're now unfrozen
8781
0
  mFrozen = false;
8782
0
  UpdateImageLockingState();
8783
0
8784
0
  UnsuppressPainting();
8785
0
}
8786
8787
//--------------------------------------------------------
8788
// Start of protected and private methods on the PresShell
8789
//--------------------------------------------------------
8790
8791
void
8792
PresShell::MaybeScheduleReflow()
8793
0
{
8794
0
  ASSERT_REFLOW_SCHEDULED_STATE();
8795
0
  if (mObservingLayoutFlushes || mIsDestroying || mIsReflowing ||
8796
0
      mDirtyRoots.IsEmpty())
8797
0
    return;
8798
0
8799
0
  if (!mPresContext->HasPendingInterrupt() || !ScheduleReflowOffTimer()) {
8800
0
    ScheduleReflow();
8801
0
  }
8802
0
8803
0
  ASSERT_REFLOW_SCHEDULED_STATE();
8804
0
}
8805
8806
void
8807
PresShell::ScheduleReflow()
8808
0
{
8809
0
  ASSERT_REFLOW_SCHEDULED_STATE();
8810
0
  DoObserveLayoutFlushes();
8811
0
  ASSERT_REFLOW_SCHEDULED_STATE();
8812
0
}
8813
8814
nsresult
8815
PresShell::DidCauseReflow()
8816
0
{
8817
0
  NS_ASSERTION(mChangeNestCount != 0, "Unexpected call to DidCauseReflow()");
8818
0
  --mChangeNestCount;
8819
0
  nsContentUtils::RemoveScriptBlocker();
8820
0
8821
0
  return NS_OK;
8822
0
}
8823
8824
void
8825
PresShell::WillDoReflow()
8826
0
{
8827
0
  mDocument->FlushUserFontSet();
8828
0
8829
0
  mPresContext->FlushCounterStyles();
8830
0
8831
0
  mPresContext->FlushFontFeatureValues();
8832
0
8833
0
  mLastReflowStart = GetPerformanceNowUnclamped();
8834
0
}
8835
8836
void
8837
PresShell::DidDoReflow(bool aInterruptible)
8838
0
{
8839
0
  HandlePostedReflowCallbacks(aInterruptible);
8840
0
8841
0
  nsCOMPtr<nsIDocShell> docShell = mPresContext->GetDocShell();
8842
0
  if (docShell) {
8843
0
    DOMHighResTimeStamp now = GetPerformanceNowUnclamped();
8844
0
    docShell->NotifyReflowObservers(aInterruptible, mLastReflowStart, now);
8845
0
  }
8846
0
8847
0
  if (sSynthMouseMove) {
8848
0
    SynthesizeMouseMove(false);
8849
0
  }
8850
0
8851
0
  mPresContext->NotifyMissingFonts();
8852
0
}
8853
8854
DOMHighResTimeStamp
8855
PresShell::GetPerformanceNowUnclamped()
8856
0
{
8857
0
  DOMHighResTimeStamp now = 0;
8858
0
8859
0
  if (nsPIDOMWindowInner* window = mDocument->GetInnerWindow()) {
8860
0
    Performance* perf = window->GetPerformance();
8861
0
8862
0
    if (perf) {
8863
0
      now = perf->NowUnclamped();
8864
0
    }
8865
0
  }
8866
0
8867
0
  return now;
8868
0
}
8869
8870
void
8871
PresShell::sReflowContinueCallback(nsITimer* aTimer, void* aPresShell)
8872
0
{
8873
0
  RefPtr<PresShell> self = static_cast<PresShell*>(aPresShell);
8874
0
8875
0
  MOZ_ASSERT(aTimer == self->mReflowContinueTimer, "Unexpected timer");
8876
0
  self->mReflowContinueTimer = nullptr;
8877
0
  self->ScheduleReflow();
8878
0
}
8879
8880
bool
8881
PresShell::ScheduleReflowOffTimer()
8882
0
{
8883
0
  MOZ_ASSERT(!mObservingLayoutFlushes, "Shouldn't get here");
8884
0
  ASSERT_REFLOW_SCHEDULED_STATE();
8885
0
8886
0
  if (!mReflowContinueTimer) {
8887
0
    nsresult rv = NS_NewTimerWithFuncCallback(
8888
0
        getter_AddRefs(mReflowContinueTimer),
8889
0
        sReflowContinueCallback, this, 30,
8890
0
        nsITimer::TYPE_ONE_SHOT,
8891
0
        "sReflowContinueCallback",
8892
0
        mDocument->EventTargetFor(TaskCategory::Other));
8893
0
    return NS_SUCCEEDED(rv);
8894
0
  }
8895
0
  return true;
8896
0
}
8897
8898
bool
8899
PresShell::DoReflow(nsIFrame* target, bool aInterruptible)
8900
0
{
8901
0
#ifdef MOZ_GECKO_PROFILER
8902
0
  nsIURI* uri = mDocument->GetDocumentURI();
8903
0
  AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(
8904
0
    "PresShell::DoReflow", LAYOUT,
8905
0
    uri ? uri->GetSpecOrDefault() : NS_LITERAL_CSTRING("N/A"));
8906
0
#endif
8907
0
8908
0
  gfxTextPerfMetrics* tp = mPresContext->GetTextPerfMetrics();
8909
0
  TimeStamp timeStart;
8910
0
  if (tp) {
8911
0
    tp->Accumulate();
8912
0
    tp->reflowCount++;
8913
0
    timeStart = TimeStamp::Now();
8914
0
  }
8915
0
8916
0
  // Schedule a paint, but don't actually mark this frame as changed for
8917
0
  // retained DL building purposes. If any child frames get moved, then
8918
0
  // they will schedule paint again. We could probaby skip this, and just
8919
0
  // schedule a similar paint when a frame is deleted.
8920
0
  target->SchedulePaint(nsIFrame::PAINT_DEFAULT, false);
8921
0
8922
0
  nsDocShell* docShell = static_cast<nsDocShell*>(GetPresContext()->GetDocShell());
8923
0
  RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
8924
0
  bool isTimelineRecording = timelines && timelines->HasConsumer(docShell);
8925
0
8926
0
  if (isTimelineRecording) {
8927
0
    timelines->AddMarkerForDocShell(docShell, "Reflow", MarkerTracingType::START);
8928
0
  }
8929
0
8930
0
#ifdef MOZ_GECKO_PROFILER
8931
0
  AutoProfilerTracing tracingLayoutFlush("Paint", "Reflow",
8932
0
                                          std::move(mReflowCause));
8933
0
  mReflowCause = nullptr;
8934
0
#endif
8935
0
8936
0
  if (mReflowContinueTimer) {
8937
0
    mReflowContinueTimer->Cancel();
8938
0
    mReflowContinueTimer = nullptr;
8939
0
  }
8940
0
8941
0
  nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
8942
0
8943
0
  // CreateReferenceRenderingContext can return nullptr
8944
0
  RefPtr<gfxContext> rcx(CreateReferenceRenderingContext());
8945
0
8946
#ifdef DEBUG
8947
  mCurrentReflowRoot = target;
8948
#endif
8949
8950
0
  // If the target frame is the root of the frame hierarchy, then
8951
0
  // use all the available space. If it's simply a `reflow root',
8952
0
  // then use the target frame's size as the available space.
8953
0
  WritingMode wm = target->GetWritingMode();
8954
0
  LogicalSize size(wm);
8955
0
  if (target == rootFrame) {
8956
0
    size = LogicalSize(wm, mPresContext->GetVisibleArea().Size());
8957
0
  } else {
8958
0
    size = target->GetLogicalSize();
8959
0
  }
8960
0
8961
0
  NS_ASSERTION(!target->GetNextInFlow() && !target->GetPrevInFlow(),
8962
0
               "reflow roots should never split");
8963
0
8964
0
  // Don't pass size directly to the reflow state, since a
8965
0
  // constrained height implies page/column breaking.
8966
0
  LogicalSize reflowSize(wm, size.ISize(wm), NS_UNCONSTRAINEDSIZE);
8967
0
  ReflowInput reflowInput(mPresContext, target, rcx, reflowSize,
8968
0
                                ReflowInput::CALLER_WILL_INIT);
8969
0
  reflowInput.mOrthogonalLimit = size.BSize(wm);
8970
0
8971
0
  if (rootFrame == target) {
8972
0
    reflowInput.Init(mPresContext);
8973
0
8974
0
    // When the root frame is being reflowed with unconstrained block-size
8975
0
    // (which happens when we're called from
8976
0
    // nsDocumentViewer::SizeToContent), we're effectively doing a
8977
0
    // resize in the block direction, since it changes the meaning of
8978
0
    // percentage block-sizes even if no block-sizes actually changed.
8979
0
    // The same applies when we reflow again after that computation. This is
8980
0
    // an unusual case, and isn't caught by ReflowInput::InitResizeFlags.
8981
0
    bool hasUnconstrainedBSize = size.BSize(wm) == NS_UNCONSTRAINEDSIZE;
8982
0
8983
0
    if (hasUnconstrainedBSize || mLastRootReflowHadUnconstrainedBSize) {
8984
0
      reflowInput.SetBResize(true);
8985
0
    }
8986
0
8987
0
    mLastRootReflowHadUnconstrainedBSize = hasUnconstrainedBSize;
8988
0
  } else {
8989
0
    // Initialize reflow state with current used border and padding,
8990
0
    // in case this was set specially by the parent frame when the reflow root
8991
0
    // was reflowed by its parent.
8992
0
    nsMargin currentBorder = target->GetUsedBorder();
8993
0
    nsMargin currentPadding = target->GetUsedPadding();
8994
0
    reflowInput.Init(mPresContext, nullptr, &currentBorder, &currentPadding);
8995
0
  }
8996
0
8997
0
  // fix the computed height
8998
0
  NS_ASSERTION(reflowInput.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0),
8999
0
               "reflow state should not set margin for reflow roots");
9000
0
  if (size.BSize(wm) != NS_UNCONSTRAINEDSIZE) {
9001
0
    nscoord computedBSize =
9002
0
      size.BSize(wm) - reflowInput.ComputedLogicalBorderPadding().BStartEnd(wm);
9003
0
    computedBSize = std::max(computedBSize, 0);
9004
0
    reflowInput.SetComputedBSize(computedBSize);
9005
0
  }
9006
0
  NS_ASSERTION(reflowInput.ComputedISize() ==
9007
0
               size.ISize(wm) -
9008
0
                   reflowInput.ComputedLogicalBorderPadding().IStartEnd(wm),
9009
0
               "reflow state computed incorrect inline size");
9010
0
9011
0
  mPresContext->ReflowStarted(aInterruptible);
9012
0
  mIsReflowing = true;
9013
0
9014
0
  nsReflowStatus status;
9015
0
  ReflowOutput desiredSize(reflowInput);
9016
0
  target->Reflow(mPresContext, desiredSize, reflowInput, status);
9017
0
9018
0
  // If an incremental reflow is initiated at a frame other than the
9019
0
  // root frame, then its desired size had better not change!  If it's
9020
0
  // initiated at the root, then the size better not change unless its
9021
0
  // height was unconstrained to start with.
9022
0
  nsRect boundsRelativeToTarget = nsRect(0, 0, desiredSize.Width(), desiredSize.Height());
9023
0
  NS_ASSERTION((target == rootFrame &&
9024
0
                size.BSize(wm) == NS_UNCONSTRAINEDSIZE) ||
9025
0
               (desiredSize.ISize(wm) == size.ISize(wm) &&
9026
0
                desiredSize.BSize(wm) == size.BSize(wm)),
9027
0
               "non-root frame's desired size changed during an "
9028
0
               "incremental reflow");
9029
0
  NS_ASSERTION(target == rootFrame ||
9030
0
               desiredSize.VisualOverflow().IsEqualInterior(boundsRelativeToTarget),
9031
0
               "non-root reflow roots must not have visible overflow");
9032
0
  NS_ASSERTION(target == rootFrame ||
9033
0
               desiredSize.ScrollableOverflow().IsEqualEdges(boundsRelativeToTarget),
9034
0
               "non-root reflow roots must not have scrollable overflow");
9035
0
  NS_ASSERTION(status.IsEmpty(),
9036
0
               "reflow roots should never split");
9037
0
9038
0
  target->SetSize(boundsRelativeToTarget.Size());
9039
0
9040
0
  // Always use boundsRelativeToTarget here, not desiredSize.GetVisualOverflowArea(),
9041
0
  // because for root frames (where they could be different, since root frames
9042
0
  // are allowed to have overflow) the root view bounds need to match the
9043
0
  // viewport bounds; the view manager "window dimensions" code depends on it.
9044
0
  nsContainerFrame::SyncFrameViewAfterReflow(mPresContext, target,
9045
0
                                             target->GetView(),
9046
0
                                             boundsRelativeToTarget);
9047
0
  nsContainerFrame::SyncWindowProperties(mPresContext, target,
9048
0
                                         target->GetView(), rcx,
9049
0
                                         nsContainerFrame::SET_ASYNC);
9050
0
9051
0
  target->DidReflow(mPresContext, nullptr);
9052
0
  if (target == rootFrame && size.BSize(wm) == NS_UNCONSTRAINEDSIZE) {
9053
0
    mPresContext->SetVisibleArea(boundsRelativeToTarget);
9054
0
  }
9055
0
9056
#ifdef DEBUG
9057
  mCurrentReflowRoot = nullptr;
9058
#endif
9059
9060
0
  NS_ASSERTION(mPresContext->HasPendingInterrupt() ||
9061
0
               mFramesToDirty.Count() == 0,
9062
0
               "Why do we need to dirty anything if not interrupted?");
9063
0
9064
0
  mIsReflowing = false;
9065
0
  bool interrupted = mPresContext->HasPendingInterrupt();
9066
0
  if (interrupted) {
9067
0
    // Make sure target gets reflowed again.
9068
0
    for (auto iter = mFramesToDirty.Iter(); !iter.Done(); iter.Next()) {
9069
0
      // Mark frames dirty until target frame.
9070
0
      nsPtrHashKey<nsIFrame>* p = iter.Get();
9071
0
      for (nsIFrame* f = p->GetKey();
9072
0
           f && !NS_SUBTREE_DIRTY(f);
9073
0
           f = f->GetParent()) {
9074
0
        f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
9075
0
9076
0
        if (f == target) {
9077
0
          break;
9078
0
        }
9079
0
      }
9080
0
    }
9081
0
9082
0
    NS_ASSERTION(NS_SUBTREE_DIRTY(target), "Why is the target not dirty?");
9083
0
    mDirtyRoots.AppendElement(target);
9084
0
    SetNeedLayoutFlush();
9085
0
9086
0
    // Clear mFramesToDirty after we've done the NS_SUBTREE_DIRTY(target)
9087
0
    // assertion so that if it fails it's easier to see what's going on.
9088
#ifdef NOISY_INTERRUPTIBLE_REFLOW
9089
    printf("mFramesToDirty.Count() == %u\n", mFramesToDirty.Count());
9090
#endif /* NOISY_INTERRUPTIBLE_REFLOW */
9091
    mFramesToDirty.Clear();
9092
0
9093
0
    // Any FlushPendingNotifications with interruptible reflows
9094
0
    // should be suppressed now. We don't want to do extra reflow work
9095
0
    // before our reflow event happens.
9096
0
    mWasLastReflowInterrupted = true;
9097
0
    MaybeScheduleReflow();
9098
0
  }
9099
0
9100
0
  // dump text perf metrics for reflows with significant text processing
9101
0
  if (tp) {
9102
0
    if (tp->current.numChars > 100) {
9103
0
      TimeDuration reflowTime = TimeStamp::Now() - timeStart;
9104
0
      LogTextPerfStats(tp, this, tp->current,
9105
0
                       reflowTime.ToMilliseconds(), eLog_reflow, nullptr);
9106
0
    }
9107
0
    tp->Accumulate();
9108
0
  }
9109
0
9110
0
  if (isTimelineRecording) {
9111
0
    timelines->AddMarkerForDocShell(docShell, "Reflow", MarkerTracingType::END);
9112
0
  }
9113
0
9114
0
  return !interrupted;
9115
0
}
9116
9117
#ifdef DEBUG
9118
void
9119
PresShell::DoVerifyReflow()
9120
{
9121
  if (GetVerifyReflowEnable()) {
9122
    // First synchronously render what we have so far so that we can
9123
    // see it.
9124
    nsView* rootView = mViewManager->GetRootView();
9125
    mViewManager->InvalidateView(rootView);
9126
9127
    FlushPendingNotifications(FlushType::Layout);
9128
    mInVerifyReflow = true;
9129
    bool ok = VerifyIncrementalReflow();
9130
    mInVerifyReflow = false;
9131
    if (VERIFY_REFLOW_ALL & gVerifyReflowFlags) {
9132
      printf("ProcessReflowCommands: finished (%s)\n",
9133
             ok ? "ok" : "failed");
9134
    }
9135
9136
    if (!mDirtyRoots.IsEmpty()) {
9137
      printf("XXX yikes! reflow commands queued during verify-reflow\n");
9138
    }
9139
  }
9140
}
9141
#endif
9142
9143
// used with Telemetry metrics
9144
0
#define NS_LONG_REFLOW_TIME_MS    5000
9145
9146
bool
9147
PresShell::ProcessReflowCommands(bool aInterruptible)
9148
0
{
9149
0
  if (mDirtyRoots.IsEmpty() && !mShouldUnsuppressPainting) {
9150
0
    // Nothing to do; bail out
9151
0
    return true;
9152
0
  }
9153
0
9154
0
  mozilla::TimeStamp timerStart = mozilla::TimeStamp::Now();
9155
0
  bool interrupted = false;
9156
0
  if (!mDirtyRoots.IsEmpty()) {
9157
0
9158
#ifdef DEBUG
9159
    if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) {
9160
      printf("ProcessReflowCommands: begin incremental reflow\n");
9161
    }
9162
#endif
9163
9164
0
    // If reflow is interruptible, then make a note of our deadline.
9165
0
    const PRIntervalTime deadline = aInterruptible
9166
0
        ? PR_IntervalNow() + PR_MicrosecondsToInterval(gMaxRCProcessingTime)
9167
0
        : (PRIntervalTime)0;
9168
0
9169
0
    // Scope for the reflow entry point
9170
0
    {
9171
0
      nsAutoScriptBlocker scriptBlocker;
9172
0
      WillDoReflow();
9173
0
      AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
9174
0
      nsViewManager::AutoDisableRefresh refreshBlocker(mViewManager);
9175
0
9176
0
      do {
9177
0
        // Send an incremental reflow notification to the target frame.
9178
0
        int32_t idx = mDirtyRoots.Length() - 1;
9179
0
        nsIFrame *target = mDirtyRoots[idx];
9180
0
        mDirtyRoots.RemoveElementAt(idx);
9181
0
9182
0
        if (!NS_SUBTREE_DIRTY(target)) {
9183
0
          // It's not dirty anymore, which probably means the notification
9184
0
          // was posted in the middle of a reflow (perhaps with a reflow
9185
0
          // root in the middle).  Don't do anything.
9186
0
          continue;
9187
0
        }
9188
0
9189
0
        interrupted = !DoReflow(target, aInterruptible);
9190
0
9191
0
        // Keep going until we're out of reflow commands, or we've run
9192
0
        // past our deadline, or we're interrupted.
9193
0
      } while (!interrupted && !mDirtyRoots.IsEmpty() &&
9194
0
               (!aInterruptible || PR_IntervalNow() < deadline));
9195
0
9196
0
      interrupted = !mDirtyRoots.IsEmpty();
9197
0
    }
9198
0
9199
0
    // Exiting the scriptblocker might have killed us
9200
0
    if (!mIsDestroying) {
9201
0
      DidDoReflow(aInterruptible);
9202
0
    }
9203
0
9204
0
    // DidDoReflow might have killed us
9205
0
    if (!mIsDestroying) {
9206
#ifdef DEBUG
9207
      if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) {
9208
        printf("\nPresShell::ProcessReflowCommands() finished: this=%p\n",
9209
               (void*)this);
9210
      }
9211
      DoVerifyReflow();
9212
#endif
9213
9214
0
      // If any new reflow commands were enqueued during the reflow, schedule
9215
0
      // another reflow event to process them.  Note that we want to do this
9216
0
      // after DidDoReflow(), since that method can change whether there are
9217
0
      // dirty roots around by flushing, and there's no point in posting a
9218
0
      // reflow event just to have the flush revoke it.
9219
0
      if (!mDirtyRoots.IsEmpty()) {
9220
0
        MaybeScheduleReflow();
9221
0
        // And record that we might need flushing
9222
0
        SetNeedLayoutFlush();
9223
0
      }
9224
0
    }
9225
0
  }
9226
0
9227
0
  if (!mIsDestroying && mShouldUnsuppressPainting &&
9228
0
      mDirtyRoots.IsEmpty()) {
9229
0
    // We only unlock if we're out of reflows.  It's pointless
9230
0
    // to unlock if reflows are still pending, since reflows
9231
0
    // are just going to thrash the frames around some more.  By
9232
0
    // waiting we avoid an overeager "jitter" effect.
9233
0
    mShouldUnsuppressPainting = false;
9234
0
    UnsuppressAndInvalidate();
9235
0
  }
9236
0
9237
0
  if (mDocument->GetRootElement()) {
9238
0
    TimeDuration elapsed = TimeStamp::Now() - timerStart;
9239
0
    int32_t intElapsed = int32_t(elapsed.ToMilliseconds());
9240
0
9241
0
    if (intElapsed > NS_LONG_REFLOW_TIME_MS) {
9242
0
      Telemetry::Accumulate(Telemetry::LONG_REFLOW_INTERRUPTIBLE,
9243
0
                            aInterruptible ? 1 : 0);
9244
0
    }
9245
0
  }
9246
0
9247
0
  return !interrupted;
9248
0
}
9249
9250
void
9251
PresShell::WindowSizeMoveDone()
9252
0
{
9253
0
  if (mPresContext) {
9254
0
    EventStateManager::ClearGlobalActiveContent(nullptr);
9255
0
    ClearMouseCapture(nullptr);
9256
0
  }
9257
0
}
9258
9259
#ifdef MOZ_XUL
9260
/*
9261
 * It's better to add stuff to the |DidSetComputedStyle| method of the
9262
 * relevant frames than adding it here.  These methods should (ideally,
9263
 * anyway) go away.
9264
 */
9265
9266
// Return value says whether to walk children.
9267
typedef bool (*frameWalkerFn)(nsIFrame* aFrame);
9268
9269
static bool
9270
ReResolveMenusAndTrees(nsIFrame* aFrame)
9271
0
{
9272
0
  // Trees have a special style cache that needs to be flushed when
9273
0
  // the theme changes.
9274
0
  nsTreeBodyFrame* treeBody = do_QueryFrame(aFrame);
9275
0
  if (treeBody)
9276
0
    treeBody->ClearStyleAndImageCaches();
9277
0
9278
0
  // We deliberately don't re-resolve style on a menu's popup
9279
0
  // sub-content, since doing so slows menus to a crawl.  That means we
9280
0
  // have to special-case them on a skin switch, and ensure that the
9281
0
  // popup frames just get destroyed completely.
9282
0
  nsMenuFrame* menu = do_QueryFrame(aFrame);
9283
0
  if (menu)
9284
0
    menu->CloseMenu(true);
9285
0
  return true;
9286
0
}
9287
9288
static bool
9289
ReframeImageBoxes(nsIFrame* aFrame)
9290
0
{
9291
0
  if (aFrame->IsImageBoxFrame()) {
9292
0
    aFrame->PresContext()->RestyleManager()->PostRestyleEvent(
9293
0
        aFrame->GetContent()->AsElement(),
9294
0
        nsRestyleHint(0),
9295
0
        nsChangeHint_ReconstructFrame);
9296
0
    return false; // don't walk descendants
9297
0
  }
9298
0
  return true; // walk descendants
9299
0
}
9300
9301
static void
9302
WalkFramesThroughPlaceholders(nsPresContext* aPresContext,
9303
                              nsIFrame* aFrame,
9304
                              frameWalkerFn aFunc)
9305
0
{
9306
0
  bool walkChildren = (*aFunc)(aFrame);
9307
0
  if (!walkChildren)
9308
0
    return;
9309
0
9310
0
  nsIFrame::ChildListIterator lists(aFrame);
9311
0
  for (; !lists.IsDone(); lists.Next()) {
9312
0
    nsFrameList::Enumerator childFrames(lists.CurrentList());
9313
0
    for (; !childFrames.AtEnd(); childFrames.Next()) {
9314
0
      nsIFrame* child = childFrames.get();
9315
0
      if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
9316
0
        // only do frames that are in flow, and recur through the
9317
0
        // out-of-flows of placeholders.
9318
0
        WalkFramesThroughPlaceholders(aPresContext,
9319
0
                                      nsPlaceholderFrame::GetRealFrameFor(child),
9320
0
                                      aFunc);
9321
0
      }
9322
0
    }
9323
0
  }
9324
0
}
9325
#endif
9326
9327
NS_IMETHODIMP
9328
PresShell::Observe(nsISupports* aSubject,
9329
                   const char* aTopic,
9330
                   const char16_t* aData)
9331
0
{
9332
0
  if (mIsDestroying) {
9333
0
    NS_WARNING("our observers should have been unregistered by now");
9334
0
    return NS_OK;
9335
0
  }
9336
0
9337
0
#ifdef MOZ_XUL
9338
0
  if (!nsCRT::strcmp(aTopic, "chrome-flush-skin-caches")) {
9339
0
    // Need to null-check because "chrome-flush-skin-caches" can happen
9340
0
    // at interesting times during startup.
9341
0
    if (nsIFrame* rootFrame = mFrameConstructor->GetRootFrame()) {
9342
0
      NS_ASSERTION(mViewManager, "View manager must exist");
9343
0
9344
0
      WalkFramesThroughPlaceholders(
9345
0
          mPresContext, rootFrame, ReResolveMenusAndTrees);
9346
0
9347
0
      // Because "chrome:" URL equality is messy, reframe image box
9348
0
      // frames (hack!).
9349
0
      WalkFramesThroughPlaceholders(
9350
0
          mPresContext, rootFrame, ReframeImageBoxes);
9351
0
    }
9352
0
    return NS_OK;
9353
0
  }
9354
0
#endif
9355
0
9356
0
  if (!nsCRT::strcmp(aTopic, "memory-pressure")) {
9357
0
    if (!AssumeAllFramesVisible() && mPresContext->IsRootContentDocument()) {
9358
0
      DoUpdateApproximateFrameVisibility(/* aRemoveOnly = */ true);
9359
0
    }
9360
0
    return NS_OK;
9361
0
  }
9362
0
9363
0
  if (!nsCRT::strcmp(aTopic, NS_WIDGET_WAKE_OBSERVER_TOPIC)) {
9364
0
    mLastOSWake = TimeStamp::Now();
9365
0
    return NS_OK;
9366
0
  }
9367
0
9368
0
  // For parent process, user may expect the UI is interactable after a
9369
0
  // tab (previously opened page or home page) has restored.
9370
0
  if (!nsCRT::strcmp(aTopic, "sessionstore-one-or-no-tab-restored")) {
9371
0
    MOZ_ASSERT(XRE_IsParentProcess());
9372
0
    sProcessInteractable = true;
9373
0
9374
0
    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
9375
0
    if (os) {
9376
0
      os->RemoveObserver(this, "sessionstore-one-or-no-tab-restored");
9377
0
    }
9378
0
    return NS_OK;
9379
0
  }
9380
0
9381
0
  if (!nsCRT::strcmp(aTopic, "font-info-updated")) {
9382
0
    mPresContext->ForceReflowForFontInfoUpdate();
9383
0
    return NS_OK;
9384
0
  }
9385
0
9386
0
  NS_WARNING("unrecognized topic in PresShell::Observe");
9387
0
  return NS_ERROR_FAILURE;
9388
0
}
9389
9390
bool
9391
nsIPresShell::AddRefreshObserver(nsARefreshObserver* aObserver,
9392
                                 FlushType aFlushType)
9393
0
{
9394
0
  nsPresContext* presContext = GetPresContext();
9395
0
  return presContext &&
9396
0
      presContext->RefreshDriver()->AddRefreshObserver(aObserver, aFlushType);
9397
0
}
9398
9399
bool
9400
nsIPresShell::RemoveRefreshObserver(nsARefreshObserver* aObserver,
9401
                                    FlushType aFlushType)
9402
0
{
9403
0
  nsPresContext* presContext = GetPresContext();
9404
0
  return presContext &&
9405
0
      presContext->RefreshDriver()->RemoveRefreshObserver(aObserver, aFlushType);
9406
0
}
9407
9408
/* virtual */ bool
9409
nsIPresShell::AddPostRefreshObserver(nsAPostRefreshObserver* aObserver)
9410
0
{
9411
0
  nsPresContext* presContext = GetPresContext();
9412
0
  if (!presContext) {
9413
0
    return false;
9414
0
  }
9415
0
  presContext->RefreshDriver()->AddPostRefreshObserver(aObserver);
9416
0
  return true;
9417
0
}
9418
9419
/* virtual */ bool
9420
nsIPresShell::RemovePostRefreshObserver(nsAPostRefreshObserver* aObserver)
9421
0
{
9422
0
  nsPresContext* presContext = GetPresContext();
9423
0
  if (!presContext) {
9424
0
    return false;
9425
0
  }
9426
0
  presContext->RefreshDriver()->RemovePostRefreshObserver(aObserver);
9427
0
  return true;
9428
0
}
9429
9430
void
9431
nsIPresShell::DoObserveStyleFlushes()
9432
0
{
9433
0
  MOZ_ASSERT(!ObservingStyleFlushes());
9434
0
  mObservingStyleFlushes = true;
9435
0
9436
0
  if (MOZ_LIKELY(!mDocument->GetBFCacheEntry())) {
9437
0
    mPresContext->RefreshDriver()->AddStyleFlushObserver(this);
9438
0
  }
9439
0
}
9440
9441
void
9442
nsIPresShell::DoObserveLayoutFlushes()
9443
0
{
9444
0
  MOZ_ASSERT(!ObservingLayoutFlushes());
9445
0
  mObservingLayoutFlushes = true;
9446
0
9447
0
  if (MOZ_LIKELY(!mDocument->GetBFCacheEntry())) {
9448
0
    mPresContext->RefreshDriver()->AddLayoutFlushObserver(this);
9449
0
  }
9450
0
}
9451
9452
//------------------------------------------------------
9453
// End of protected and private methods on the PresShell
9454
//------------------------------------------------------
9455
9456
//------------------------------------------------------------------
9457
//-- Delayed event Classes Impls
9458
//------------------------------------------------------------------
9459
9460
PresShell::DelayedInputEvent::DelayedInputEvent() :
9461
  DelayedEvent(),
9462
  mEvent(nullptr)
9463
0
{
9464
0
}
9465
9466
PresShell::DelayedInputEvent::~DelayedInputEvent()
9467
0
{
9468
0
  delete mEvent;
9469
0
}
9470
9471
void
9472
PresShell::DelayedInputEvent::Dispatch()
9473
0
{
9474
0
  if (!mEvent || !mEvent->mWidget) {
9475
0
    return;
9476
0
  }
9477
0
  nsCOMPtr<nsIWidget> widget = mEvent->mWidget;
9478
0
  nsEventStatus status;
9479
0
  widget->DispatchEvent(mEvent, status);
9480
0
}
9481
9482
PresShell::DelayedMouseEvent::DelayedMouseEvent(WidgetMouseEvent* aEvent) :
9483
  DelayedInputEvent()
9484
0
{
9485
0
  WidgetMouseEvent* mouseEvent =
9486
0
    new WidgetMouseEvent(aEvent->IsTrusted(),
9487
0
                         aEvent->mMessage,
9488
0
                         aEvent->mWidget,
9489
0
                         aEvent->mReason,
9490
0
                         aEvent->mContextMenuTrigger);
9491
0
  mouseEvent->AssignMouseEventData(*aEvent, false);
9492
0
  mEvent = mouseEvent;
9493
0
}
9494
9495
PresShell::DelayedKeyEvent::DelayedKeyEvent(WidgetKeyboardEvent* aEvent) :
9496
  DelayedInputEvent()
9497
0
{
9498
0
  WidgetKeyboardEvent* keyEvent =
9499
0
    new WidgetKeyboardEvent(aEvent->IsTrusted(),
9500
0
                            aEvent->mMessage,
9501
0
                            aEvent->mWidget);
9502
0
  keyEvent->AssignKeyEventData(*aEvent, false);
9503
0
  keyEvent->mFlags.mIsSynthesizedForTests = aEvent->mFlags.mIsSynthesizedForTests;
9504
0
  keyEvent->mFlags.mIsSuppressedOrDelayed = true;
9505
0
  mEvent = keyEvent;
9506
0
}
9507
9508
bool
9509
PresShell::DelayedKeyEvent::IsKeyPressEvent()
9510
0
{
9511
0
  return mEvent->mMessage == eKeyPress;
9512
0
}
9513
9514
// Start of DEBUG only code
9515
9516
#ifdef DEBUG
9517
9518
static void
9519
LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg)
9520
{
9521
  nsAutoString n1, n2;
9522
  if (k1) {
9523
    k1->GetFrameName(n1);
9524
  } else {
9525
    n1.AssignLiteral(u"(null)");
9526
  }
9527
9528
  if (k2) {
9529
    k2->GetFrameName(n2);
9530
  } else {
9531
    n2.AssignLiteral(u"(null)");
9532
  }
9533
9534
  printf("verifyreflow: %s %p != %s %p  %s\n",
9535
         NS_LossyConvertUTF16toASCII(n1).get(), (void*)k1,
9536
         NS_LossyConvertUTF16toASCII(n2).get(), (void*)k2, aMsg);
9537
}
9538
9539
static void
9540
LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg,
9541
                 const nsRect& r1, const nsRect& r2)
9542
{
9543
  printf("VerifyReflow Error:\n");
9544
  nsAutoString name;
9545
9546
  if (k1) {
9547
    k1->GetFrameName(name);
9548
    printf("  %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k1);
9549
  }
9550
  printf("{%d, %d, %d, %d} != \n", r1.x, r1.y, r1.width, r1.height);
9551
9552
  if (k2) {
9553
    k2->GetFrameName(name);
9554
    printf("  %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k2);
9555
  }
9556
  printf("{%d, %d, %d, %d}\n  %s\n",
9557
         r2.x, r2.y, r2.width, r2.height, aMsg);
9558
}
9559
9560
static void
9561
LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg,
9562
                 const nsIntRect& r1, const nsIntRect& r2)
9563
{
9564
  printf("VerifyReflow Error:\n");
9565
  nsAutoString name;
9566
9567
  if (k1) {
9568
    k1->GetFrameName(name);
9569
    printf("  %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k1);
9570
  }
9571
  printf("{%d, %d, %d, %d} != \n", r1.x, r1.y, r1.width, r1.height);
9572
9573
  if (k2) {
9574
    k2->GetFrameName(name);
9575
    printf("  %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k2);
9576
  }
9577
  printf("{%d, %d, %d, %d}\n  %s\n",
9578
         r2.x, r2.y, r2.width, r2.height, aMsg);
9579
}
9580
9581
static bool
9582
CompareTrees(nsPresContext* aFirstPresContext, nsIFrame* aFirstFrame,
9583
             nsPresContext* aSecondPresContext, nsIFrame* aSecondFrame)
9584
{
9585
  if (!aFirstPresContext || !aFirstFrame || !aSecondPresContext || !aSecondFrame)
9586
    return true;
9587
  // XXX Evil hack to reduce false positives; I can't seem to figure
9588
  // out how to flush scrollbar changes correctly
9589
  //if (aFirstFrame->IsScrollbarFrame())
9590
  //  return true;
9591
  bool ok = true;
9592
  nsIFrame::ChildListIterator lists1(aFirstFrame);
9593
  nsIFrame::ChildListIterator lists2(aSecondFrame);
9594
  do {
9595
    const nsFrameList& kids1 = !lists1.IsDone() ? lists1.CurrentList() : nsFrameList();
9596
    const nsFrameList& kids2 = !lists2.IsDone() ? lists2.CurrentList() : nsFrameList();
9597
    int32_t l1 = kids1.GetLength();
9598
    int32_t l2 = kids2.GetLength();
9599
    if (l1 != l2) {
9600
      ok = false;
9601
      LogVerifyMessage(kids1.FirstChild(), kids2.FirstChild(),
9602
                       "child counts don't match: ");
9603
      printf("%d != %d\n", l1, l2);
9604
      if (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags)) {
9605
        break;
9606
      }
9607
    }
9608
9609
    LayoutDeviceIntRect r1, r2;
9610
    nsView* v1;
9611
    nsView* v2;
9612
    for (nsFrameList::Enumerator e1(kids1), e2(kids2);
9613
         ;
9614
         e1.Next(), e2.Next()) {
9615
      nsIFrame* k1 = e1.get();
9616
      nsIFrame* k2 = e2.get();
9617
      if (((nullptr == k1) && (nullptr != k2)) ||
9618
          ((nullptr != k1) && (nullptr == k2))) {
9619
        ok = false;
9620
        LogVerifyMessage(k1, k2, "child lists are different\n");
9621
        break;
9622
      }
9623
      else if (nullptr != k1) {
9624
        // Verify that the frames are the same size
9625
        if (!k1->GetRect().IsEqualInterior(k2->GetRect())) {
9626
          ok = false;
9627
          LogVerifyMessage(k1, k2, "(frame rects)", k1->GetRect(), k2->GetRect());
9628
        }
9629
9630
        // Make sure either both have views or neither have views; if they
9631
        // do have views, make sure the views are the same size. If the
9632
        // views have widgets, make sure they both do or neither does. If
9633
        // they do, make sure the widgets are the same size.
9634
        v1 = k1->GetView();
9635
        v2 = k2->GetView();
9636
        if (((nullptr == v1) && (nullptr != v2)) ||
9637
            ((nullptr != v1) && (nullptr == v2))) {
9638
          ok = false;
9639
          LogVerifyMessage(k1, k2, "child views are not matched\n");
9640
        }
9641
        else if (nullptr != v1) {
9642
          if (!v1->GetBounds().IsEqualInterior(v2->GetBounds())) {
9643
            LogVerifyMessage(k1, k2, "(view rects)", v1->GetBounds(), v2->GetBounds());
9644
          }
9645
9646
          nsIWidget* w1 = v1->GetWidget();
9647
          nsIWidget* w2 = v2->GetWidget();
9648
          if (((nullptr == w1) && (nullptr != w2)) ||
9649
              ((nullptr != w1) && (nullptr == w2))) {
9650
            ok = false;
9651
            LogVerifyMessage(k1, k2, "child widgets are not matched\n");
9652
          }
9653
          else if (nullptr != w1) {
9654
            r1 = w1->GetBounds();
9655
            r2 = w2->GetBounds();
9656
            if (!r1.IsEqualEdges(r2)) {
9657
              LogVerifyMessage(k1, k2, "(widget rects)",
9658
                               r1.ToUnknownRect(), r2.ToUnknownRect());
9659
            }
9660
          }
9661
        }
9662
        if (!ok && (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags))) {
9663
          break;
9664
        }
9665
9666
        // XXX Should perhaps compare their float managers.
9667
9668
        // Compare the sub-trees too
9669
        if (!CompareTrees(aFirstPresContext, k1, aSecondPresContext, k2)) {
9670
          ok = false;
9671
          if (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags)) {
9672
            break;
9673
          }
9674
        }
9675
      }
9676
      else {
9677
        break;
9678
      }
9679
    }
9680
    if (!ok && (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags))) {
9681
      break;
9682
    }
9683
9684
    lists1.Next();
9685
    lists2.Next();
9686
    if (lists1.IsDone() != lists2.IsDone() ||
9687
        (!lists1.IsDone() && lists1.CurrentID() != lists2.CurrentID())) {
9688
      if (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags)) {
9689
        ok = false;
9690
      }
9691
      LogVerifyMessage(kids1.FirstChild(), kids2.FirstChild(),
9692
                       "child list names are not matched: ");
9693
      fprintf(stdout, "%s != %s\n",
9694
              !lists1.IsDone() ? mozilla::layout::ChildListName(lists1.CurrentID()) : "(null)",
9695
              !lists2.IsDone() ? mozilla::layout::ChildListName(lists2.CurrentID()) : "(null)");
9696
      break;
9697
    }
9698
  } while (ok && !lists1.IsDone());
9699
9700
  return ok;
9701
}
9702
#endif
9703
9704
#if 0
9705
static nsIFrame*
9706
FindTopFrame(nsIFrame* aRoot)
9707
{
9708
  if (aRoot) {
9709
    nsIContent* content = aRoot->GetContent();
9710
    if (content) {
9711
      nsAtom* tag;
9712
      content->GetTag(tag);
9713
      if (nullptr != tag) {
9714
        NS_RELEASE(tag);
9715
        return aRoot;
9716
      }
9717
    }
9718
9719
    // Try one of the children
9720
    for (nsIFrame* kid : aRoot->PrincipalChildList()) {
9721
      nsIFrame* result = FindTopFrame(kid);
9722
      if (nullptr != result) {
9723
        return result;
9724
      }
9725
    }
9726
  }
9727
  return nullptr;
9728
}
9729
#endif
9730
9731
9732
#ifdef DEBUG
9733
9734
static void
9735
CopySheetsIntoClone(ServoStyleSet* aSet, ServoStyleSet* aClone)
9736
{
9737
  int32_t i, n = aSet->SheetCount(SheetType::Override);
9738
  for (i = 0; i < n; i++) {
9739
    StyleSheet* ss = aSet->StyleSheetAt(SheetType::Override, i);
9740
    if (ss)
9741
      aClone->AppendStyleSheet(SheetType::Override, ss);
9742
  }
9743
9744
  // The document expects to insert document stylesheets itself
9745
#if 0
9746
  n = aSet->SheetCount(SheetType::Doc);
9747
  for (i = 0; i < n; i++) {
9748
    StyleSheet* ss = aSet->StyleSheetAt(SheetType::Doc, i);
9749
    if (ss)
9750
      aClone->AddDocStyleSheet(ss, mDocument);
9751
  }
9752
#endif
9753
9754
  n = aSet->SheetCount(SheetType::User);
9755
  for (i = 0; i < n; i++) {
9756
    StyleSheet* ss = aSet->StyleSheetAt(SheetType::User, i);
9757
    if (ss)
9758
      aClone->AppendStyleSheet(SheetType::User, ss);
9759
  }
9760
9761
  n = aSet->SheetCount(SheetType::Agent);
9762
  for (i = 0; i < n; i++) {
9763
    StyleSheet* ss = aSet->StyleSheetAt(SheetType::Agent, i);
9764
    if (ss)
9765
      aClone->AppendStyleSheet(SheetType::Agent, ss);
9766
  }
9767
}
9768
9769
9770
UniquePtr<ServoStyleSet>
9771
PresShell::CloneStyleSet(ServoStyleSet* aSet)
9772
{
9773
  auto clone = MakeUnique<ServoStyleSet>();
9774
  CopySheetsIntoClone(aSet, clone.get());
9775
  return clone;
9776
}
9777
9778
// After an incremental reflow, we verify the correctness by doing a
9779
// full reflow into a fresh frame tree.
9780
bool
9781
PresShell::VerifyIncrementalReflow()
9782
{
9783
   if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
9784
     printf("Building Verification Tree...\n");
9785
   }
9786
9787
  // Create a presentation context to view the new frame tree
9788
  RefPtr<nsPresContext> cx =
9789
       new nsRootPresContext(mDocument, mPresContext->IsPaginated() ?
9790
                                        nsPresContext::eContext_PrintPreview :
9791
                                        nsPresContext::eContext_Galley);
9792
  NS_ENSURE_TRUE(cx, false);
9793
9794
  nsDeviceContext *dc = mPresContext->DeviceContext();
9795
  nsresult rv = cx->Init(dc);
9796
  NS_ENSURE_SUCCESS(rv, false);
9797
9798
  // Get our scrolling preference
9799
  nsView* rootView = mViewManager->GetRootView();
9800
  NS_ENSURE_TRUE(rootView->HasWidget(), false);
9801
  nsIWidget* parentWidget = rootView->GetWidget();
9802
9803
  // Create a new view manager.
9804
  RefPtr<nsViewManager> vm = new nsViewManager();
9805
  NS_ENSURE_TRUE(vm, false);
9806
  rv = vm->Init(dc);
9807
  NS_ENSURE_SUCCESS(rv, false);
9808
9809
  // Create a child window of the parent that is our "root view/window"
9810
  // Create a view
9811
  nsRect tbounds = mPresContext->GetVisibleArea();
9812
  nsView* view = vm->CreateView(tbounds, nullptr);
9813
  NS_ENSURE_TRUE(view, false);
9814
9815
  //now create the widget for the view
9816
  rv = view->CreateWidgetForParent(parentWidget, nullptr, true);
9817
  NS_ENSURE_SUCCESS(rv, false);
9818
9819
  // Setup hierarchical relationship in view manager
9820
  vm->SetRootView(view);
9821
9822
  // Make the new presentation context the same size as our
9823
  // presentation context.
9824
  cx->SetVisibleArea(mPresContext->GetVisibleArea());
9825
9826
  // Create a new presentation shell to view the document. Use the
9827
  // exact same style information that this document has.
9828
  UniquePtr<ServoStyleSet> newSet = CloneStyleSet(StyleSet());
9829
9830
  nsCOMPtr<nsIPresShell> sh = mDocument->CreateShell(cx, vm, std::move(newSet));
9831
  NS_ENSURE_TRUE(sh, false);
9832
  // Note that after we create the shell, we must make sure to destroy it
9833
  sh->SetVerifyReflowEnable(false); // turn off verify reflow while we're reflowing the test frame tree
9834
  vm->SetPresShell(sh);
9835
  {
9836
    nsAutoCauseReflowNotifier crNotifier(this);
9837
    sh->Initialize();
9838
  }
9839
  mDocument->BindingManager()->ProcessAttachedQueue();
9840
  sh->FlushPendingNotifications(FlushType::Layout);
9841
  sh->SetVerifyReflowEnable(true);  // turn on verify reflow again now that we're done reflowing the test frame tree
9842
  // Force the non-primary presshell to unsuppress; it doesn't want to normally
9843
  // because it thinks it's hidden
9844
  ((PresShell*)sh.get())->mPaintingSuppressed = false;
9845
  if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
9846
     printf("Verification Tree built, comparing...\n");
9847
  }
9848
9849
  // Now that the document has been reflowed, use its frame tree to
9850
  // compare against our frame tree.
9851
  nsIFrame* root1 = mFrameConstructor->GetRootFrame();
9852
  nsIFrame* root2 = sh->GetRootFrame();
9853
  bool ok = CompareTrees(mPresContext, root1, cx, root2);
9854
  if (!ok && (VERIFY_REFLOW_NOISY & gVerifyReflowFlags)) {
9855
    printf("Verify reflow failed, primary tree:\n");
9856
    root1->List(stdout, 0);
9857
    printf("Verification tree:\n");
9858
    root2->List(stdout, 0);
9859
  }
9860
9861
#if 0
9862
  // Sample code for dumping page to png
9863
  // XXX Needs to be made more flexible
9864
  if (!ok) {
9865
    nsString stra;
9866
    static int num = 0;
9867
    stra.AppendLiteral("C:\\mozilla\\mozilla\\debug\\filea");
9868
    stra.AppendInt(num);
9869
    stra.AppendLiteral(".png");
9870
    gfxUtils::WriteAsPNG(sh, stra);
9871
    nsString strb;
9872
    strb.AppendLiteral("C:\\mozilla\\mozilla\\debug\\fileb");
9873
    strb.AppendInt(num);
9874
    strb.AppendLiteral(".png");
9875
    gfxUtils::WriteAsPNG(sh, strb);
9876
    ++num;
9877
  }
9878
#endif
9879
9880
  sh->EndObservingDocument();
9881
  sh->Destroy();
9882
  if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
9883
    printf("Finished Verifying Reflow...\n");
9884
  }
9885
9886
  return ok;
9887
}
9888
9889
// Layout debugging hooks
9890
void
9891
PresShell::ListComputedStyles(FILE *out, int32_t aIndent)
9892
{
9893
  nsIFrame* rootFrame = GetRootFrame();
9894
  if (rootFrame) {
9895
    rootFrame->Style()->List(out, aIndent);
9896
  }
9897
9898
  // The root element's frame's ComputedStyle is the root of a separate tree.
9899
  Element* rootElement = mDocument->GetRootElement();
9900
  if (rootElement) {
9901
    nsIFrame* rootElementFrame = rootElement->GetPrimaryFrame();
9902
    if (rootElementFrame) {
9903
      rootElementFrame->Style()->List(out, aIndent);
9904
    }
9905
  }
9906
}
9907
9908
void
9909
PresShell::ListStyleSheets(FILE *out, int32_t aIndent)
9910
{
9911
  int32_t sheetCount = mStyleSet->SheetCount(SheetType::Doc);
9912
  for (int32_t i = 0; i < sheetCount; ++i) {
9913
    mStyleSet->StyleSheetAt(SheetType::Doc, i)->List(out, aIndent);
9914
    fputs("\n", out);
9915
  }
9916
}
9917
#endif
9918
9919
//=============================================================
9920
//=============================================================
9921
//-- Debug Reflow Counts
9922
//=============================================================
9923
//=============================================================
9924
#ifdef MOZ_REFLOW_PERF
9925
//-------------------------------------------------------------
9926
void
9927
PresShell::DumpReflows()
9928
{
9929
  if (mReflowCountMgr) {
9930
    nsAutoCString uriStr;
9931
    if (mDocument) {
9932
      nsIURI *uri = mDocument->GetDocumentURI();
9933
      if (uri) {
9934
        uri->GetPathQueryRef(uriStr);
9935
      }
9936
    }
9937
    mReflowCountMgr->DisplayTotals(uriStr.get());
9938
    mReflowCountMgr->DisplayHTMLTotals(uriStr.get());
9939
    mReflowCountMgr->DisplayDiffsInTotals();
9940
  }
9941
}
9942
9943
//-------------------------------------------------------------
9944
void
9945
PresShell::CountReflows(const char * aName, nsIFrame * aFrame)
9946
{
9947
  if (mReflowCountMgr) {
9948
    mReflowCountMgr->Add(aName, aFrame);
9949
  }
9950
}
9951
9952
//-------------------------------------------------------------
9953
void
9954
PresShell::PaintCount(const char * aName,
9955
                      gfxContext* aRenderingContext,
9956
                      nsPresContext* aPresContext,
9957
                      nsIFrame * aFrame,
9958
                      const nsPoint& aOffset,
9959
                      uint32_t aColor)
9960
{
9961
  if (mReflowCountMgr) {
9962
    mReflowCountMgr->PaintCount(aName, aRenderingContext, aPresContext,
9963
                                aFrame, aOffset, aColor);
9964
  }
9965
}
9966
9967
//-------------------------------------------------------------
9968
void
9969
PresShell::SetPaintFrameCount(bool aPaintFrameCounts)
9970
{
9971
  if (mReflowCountMgr) {
9972
    mReflowCountMgr->SetPaintFrameCounts(aPaintFrameCounts);
9973
  }
9974
}
9975
9976
bool
9977
PresShell::IsPaintingFrameCounts()
9978
{
9979
  if (mReflowCountMgr)
9980
    return mReflowCountMgr->IsPaintingFrameCounts();
9981
  return false;
9982
}
9983
9984
//------------------------------------------------------------------
9985
//-- Reflow Counter Classes Impls
9986
//------------------------------------------------------------------
9987
9988
//------------------------------------------------------------------
9989
ReflowCounter::ReflowCounter(ReflowCountMgr * aMgr) :
9990
  mMgr(aMgr)
9991
{
9992
  ClearTotals();
9993
  SetTotalsCache();
9994
}
9995
9996
//------------------------------------------------------------------
9997
ReflowCounter::~ReflowCounter()
9998
{
9999
10000
}
10001
10002
//------------------------------------------------------------------
10003
void ReflowCounter::ClearTotals()
10004
{
10005
  mTotal = 0;
10006
}
10007
10008
//------------------------------------------------------------------
10009
void ReflowCounter::SetTotalsCache()
10010
{
10011
  mCacheTotal = mTotal;
10012
}
10013
10014
//------------------------------------------------------------------
10015
void ReflowCounter::CalcDiffInTotals()
10016
{
10017
  mCacheTotal = mTotal - mCacheTotal;
10018
}
10019
10020
//------------------------------------------------------------------
10021
void ReflowCounter::DisplayTotals(const char * aStr)
10022
{
10023
  DisplayTotals(mTotal, aStr?aStr:"Totals");
10024
}
10025
10026
//------------------------------------------------------------------
10027
void ReflowCounter::DisplayDiffTotals(const char * aStr)
10028
{
10029
  DisplayTotals(mCacheTotal, aStr?aStr:"Diff Totals");
10030
}
10031
10032
//------------------------------------------------------------------
10033
void ReflowCounter::DisplayHTMLTotals(const char * aStr)
10034
{
10035
  DisplayHTMLTotals(mTotal, aStr?aStr:"Totals");
10036
}
10037
10038
//------------------------------------------------------------------
10039
void ReflowCounter::DisplayTotals(uint32_t aTotal, const char * aTitle)
10040
{
10041
  // figure total
10042
  if (aTotal == 0) {
10043
    return;
10044
  }
10045
  ReflowCounter * gTots = (ReflowCounter *)mMgr->LookUp(kGrandTotalsStr);
10046
10047
  printf("%25s\t", aTitle);
10048
  printf("%d\t", aTotal);
10049
  if (gTots != this && aTotal > 0) {
10050
    gTots->Add(aTotal);
10051
  }
10052
}
10053
10054
//------------------------------------------------------------------
10055
void ReflowCounter::DisplayHTMLTotals(uint32_t aTotal, const char * aTitle)
10056
{
10057
  if (aTotal == 0) {
10058
    return;
10059
  }
10060
10061
  ReflowCounter * gTots = (ReflowCounter *)mMgr->LookUp(kGrandTotalsStr);
10062
  FILE * fd = mMgr->GetOutFile();
10063
  if (!fd) {
10064
    return;
10065
  }
10066
10067
  fprintf(fd, "<tr><td><center>%s</center></td>", aTitle);
10068
  fprintf(fd, "<td><center>%d</center></td></tr>\n", aTotal);
10069
10070
  if (gTots != this && aTotal > 0) {
10071
    gTots->Add(aTotal);
10072
  }
10073
}
10074
10075
//------------------------------------------------------------------
10076
//-- ReflowCountMgr
10077
//------------------------------------------------------------------
10078
10079
#define KEY_BUF_SIZE_FOR_PTR  24 // adequate char[] buffer to sprintf a pointer
10080
10081
ReflowCountMgr::ReflowCountMgr()
10082
  : mCounts(10)
10083
  , mIndiFrameCounts(10)
10084
{
10085
  mCycledOnce              = false;
10086
  mDumpFrameCounts         = false;
10087
  mDumpFrameByFrameCounts  = false;
10088
  mPaintFrameByFrameCounts = false;
10089
}
10090
10091
//------------------------------------------------------------------
10092
ReflowCountMgr::~ReflowCountMgr()
10093
{
10094
}
10095
10096
//------------------------------------------------------------------
10097
ReflowCounter * ReflowCountMgr::LookUp(const char * aName)
10098
{
10099
  return mCounts.Get(aName);
10100
}
10101
10102
//------------------------------------------------------------------
10103
void ReflowCountMgr::Add(const char * aName, nsIFrame * aFrame)
10104
{
10105
  NS_ASSERTION(aName != nullptr, "Name shouldn't be null!");
10106
10107
  if (mDumpFrameCounts) {
10108
    ReflowCounter * counter = mCounts.LookupForAdd(aName).OrInsert([this]() {
10109
      return new ReflowCounter(this);
10110
    });
10111
    counter->Add();
10112
  }
10113
10114
  if ((mDumpFrameByFrameCounts || mPaintFrameByFrameCounts) &&
10115
      aFrame != nullptr) {
10116
    char key[KEY_BUF_SIZE_FOR_PTR];
10117
    SprintfLiteral(key, "%p", (void*)aFrame);
10118
    IndiReflowCounter * counter =
10119
      mIndiFrameCounts.LookupForAdd(key).OrInsert([&aName, &aFrame, this]() {
10120
        auto counter = new IndiReflowCounter(this);
10121
        counter->mFrame = aFrame;
10122
        counter->mName.AssignASCII(aName);
10123
        return counter;
10124
      });
10125
    // this eliminates extra counts from super classes
10126
    if (counter != nullptr && counter->mName.EqualsASCII(aName)) {
10127
      counter->mCount++;
10128
      counter->mCounter.Add(1);
10129
    }
10130
  }
10131
}
10132
10133
//------------------------------------------------------------------
10134
void ReflowCountMgr::PaintCount(const char*     aName,
10135
                                gfxContext*     aRenderingContext,
10136
                                nsPresContext*  aPresContext,
10137
                                nsIFrame*       aFrame,
10138
                                const nsPoint&  aOffset,
10139
                                uint32_t        aColor)
10140
{
10141
  if (mPaintFrameByFrameCounts &&
10142
      aFrame != nullptr) {
10143
    char key[KEY_BUF_SIZE_FOR_PTR];
10144
    SprintfLiteral(key, "%p", (void*)aFrame);
10145
    IndiReflowCounter * counter = mIndiFrameCounts.Get(key);
10146
    if (counter != nullptr && counter->mName.EqualsASCII(aName)) {
10147
      DrawTarget* drawTarget = aRenderingContext->GetDrawTarget();
10148
      int32_t appUnitsPerDevPixel = aPresContext->AppUnitsPerDevPixel();
10149
10150
      aRenderingContext->Save();
10151
      gfxPoint devPixelOffset =
10152
        nsLayoutUtils::PointToGfxPoint(aOffset, appUnitsPerDevPixel);
10153
      aRenderingContext->SetMatrixDouble(
10154
        aRenderingContext->CurrentMatrixDouble().PreTranslate(devPixelOffset));
10155
10156
      // We don't care about the document language or user fonts here;
10157
      // just get a default Latin font.
10158
      nsFont font(eFamily_serif, nsPresContext::CSSPixelsToAppUnits(11));
10159
      nsFontMetrics::Params params;
10160
      params.language = nsGkAtoms::x_western;
10161
      params.textPerf = aPresContext->GetTextPerfMetrics();
10162
      RefPtr<nsFontMetrics> fm =
10163
        aPresContext->DeviceContext()->GetMetricsFor(font, params);
10164
10165
      char buf[16];
10166
      int len = SprintfLiteral(buf, "%d", counter->mCount);
10167
      nscoord x = 0, y = fm->MaxAscent();
10168
      nscoord width, height = fm->MaxHeight();
10169
      fm->SetTextRunRTL(false);
10170
      width = fm->GetWidth(buf, len, drawTarget);
10171
10172
      Color color;
10173
      Color color2;
10174
      if (aColor != 0) {
10175
        color  = Color::FromABGR(aColor);
10176
        color2 = Color(0.f, 0.f, 0.f);
10177
      } else {
10178
        gfx::Float rc = 0.f, gc = 0.f, bc = 0.f;
10179
        if (counter->mCount < 5) {
10180
          rc = 1.f;
10181
          gc = 1.f;
10182
        } else if (counter->mCount < 11) {
10183
          gc = 1.f;
10184
        } else {
10185
          rc = 1.f;
10186
        }
10187
        color  = Color(rc, gc, bc);
10188
        color2 = Color(rc/2, gc/2, bc/2);
10189
      }
10190
10191
      nsRect rect(0,0, width+15, height+15);
10192
      Rect devPxRect =
10193
        NSRectToSnappedRect(rect, appUnitsPerDevPixel, *drawTarget);
10194
      ColorPattern black(ToDeviceColor(Color(0.f, 0.f, 0.f, 1.f)));
10195
      drawTarget->FillRect(devPxRect, black);
10196
10197
      aRenderingContext->SetColor(color2);
10198
      fm->DrawString(buf, len, x+15, y+15, aRenderingContext);
10199
      aRenderingContext->SetColor(color);
10200
      fm->DrawString(buf, len, x, y, aRenderingContext);
10201
10202
      aRenderingContext->Restore();
10203
    }
10204
  }
10205
}
10206
10207
//------------------------------------------------------------------
10208
void ReflowCountMgr::DoGrandTotals()
10209
{
10210
  auto entry = mCounts.LookupForAdd(kGrandTotalsStr);
10211
  if (!entry) {
10212
    entry.OrInsert([this]() { return new ReflowCounter(this); });
10213
  } else {
10214
    entry.Data()->ClearTotals();
10215
  }
10216
10217
  printf("\t\t\t\tTotal\n");
10218
  for (uint32_t i=0;i<78;i++) {
10219
    printf("-");
10220
  }
10221
  printf("\n");
10222
  for (auto iter = mCounts.Iter(); !iter.Done(); iter.Next()) {
10223
    iter.Data()->DisplayTotals(iter.Key());
10224
  }
10225
}
10226
10227
static void RecurseIndiTotals(nsPresContext* aPresContext,
10228
                              nsClassHashtable<nsCharPtrHashKey,
10229
                                               IndiReflowCounter>& aHT,
10230
                              nsIFrame *      aParentFrame,
10231
                              int32_t         aLevel)
10232
{
10233
  if (aParentFrame == nullptr) {
10234
    return;
10235
  }
10236
10237
  char key[KEY_BUF_SIZE_FOR_PTR];
10238
  SprintfLiteral(key, "%p", (void*)aParentFrame);
10239
  IndiReflowCounter * counter = aHT.Get(key);
10240
  if (counter) {
10241
    counter->mHasBeenOutput = true;
10242
    char * name = ToNewCString(counter->mName);
10243
    for (int32_t i=0;i<aLevel;i++) printf(" ");
10244
    printf("%s - %p   [%d][", name, (void*)aParentFrame, counter->mCount);
10245
    printf("%d", counter->mCounter.GetTotal());
10246
    printf("]\n");
10247
    free(name);
10248
  }
10249
10250
  for (nsIFrame* child : aParentFrame->PrincipalChildList()) {
10251
    RecurseIndiTotals(aPresContext, aHT, child, aLevel+1);
10252
  }
10253
10254
}
10255
10256
//------------------------------------------------------------------
10257
void ReflowCountMgr::DoIndiTotalsTree()
10258
{
10259
  printf("\n------------------------------------------------\n");
10260
  printf("-- Individual Frame Counts\n");
10261
  printf("------------------------------------------------\n");
10262
10263
  if (mPresShell) {
10264
    nsIFrame* rootFrame = mPresShell->GetRootFrame();
10265
    RecurseIndiTotals(mPresContext, mIndiFrameCounts, rootFrame, 0);
10266
    printf("------------------------------------------------\n");
10267
    printf("-- Individual Counts of Frames not in Root Tree\n");
10268
    printf("------------------------------------------------\n");
10269
    for (auto iter = mIndiFrameCounts.Iter(); !iter.Done(); iter.Next()) {
10270
      IndiReflowCounter* counter = iter.Data();
10271
      if (!counter->mHasBeenOutput) {
10272
        char * name = ToNewCString(counter->mName);
10273
        printf("%s - %p   [%d][", name, (void*)counter->mFrame, counter->mCount);
10274
        printf("%d", counter->mCounter.GetTotal());
10275
        printf("]\n");
10276
        free(name);
10277
      }
10278
    }
10279
  }
10280
}
10281
10282
//------------------------------------------------------------------
10283
void ReflowCountMgr::DoGrandHTMLTotals()
10284
{
10285
  auto entry = mCounts.LookupForAdd(kGrandTotalsStr);
10286
  if (!entry) {
10287
    entry.OrInsert([this]() { return new ReflowCounter(this); });
10288
  } else {
10289
    entry.Data()->ClearTotals();
10290
  }
10291
10292
  static const char * title[] = {"Class", "Reflows"};
10293
  fprintf(mFD, "<tr>");
10294
  for (uint32_t i=0; i < ArrayLength(title); i++) {
10295
    fprintf(mFD, "<td><center><b>%s<b></center></td>", title[i]);
10296
  }
10297
  fprintf(mFD, "</tr>\n");
10298
10299
  for (auto iter = mCounts.Iter(); !iter.Done(); iter.Next()) {
10300
    iter.Data()->DisplayHTMLTotals(iter.Key());
10301
  }
10302
}
10303
10304
//------------------------------------
10305
void ReflowCountMgr::DisplayTotals(const char * aStr)
10306
{
10307
#ifdef DEBUG_rods
10308
  printf("%s\n", aStr?aStr:"No name");
10309
#endif
10310
  if (mDumpFrameCounts) {
10311
    DoGrandTotals();
10312
  }
10313
  if (mDumpFrameByFrameCounts) {
10314
    DoIndiTotalsTree();
10315
  }
10316
10317
}
10318
//------------------------------------
10319
void ReflowCountMgr::DisplayHTMLTotals(const char * aStr)
10320
{
10321
#ifdef WIN32x // XXX NOT XP!
10322
  char name[1024];
10323
10324
  char * sptr = strrchr(aStr, '/');
10325
  if (sptr) {
10326
    sptr++;
10327
    strcpy(name, sptr);
10328
    char * eptr = strrchr(name, '.');
10329
    if (eptr) {
10330
      *eptr = 0;
10331
    }
10332
    strcat(name, "_stats.html");
10333
  }
10334
  mFD = fopen(name, "w");
10335
  if (mFD) {
10336
    fprintf(mFD, "<html><head><title>Reflow Stats</title></head><body>\n");
10337
    const char * title = aStr?aStr:"No name";
10338
    fprintf(mFD, "<center><b>%s</b><br><table border=1 style=\"background-color:#e0e0e0\">", title);
10339
    DoGrandHTMLTotals();
10340
    fprintf(mFD, "</center></table>\n");
10341
    fprintf(mFD, "</body></html>\n");
10342
    fclose(mFD);
10343
    mFD = nullptr;
10344
  }
10345
#endif // not XP!
10346
}
10347
10348
//------------------------------------------------------------------
10349
void ReflowCountMgr::ClearTotals()
10350
{
10351
  for (auto iter = mCounts.Iter(); !iter.Done(); iter.Next()) {
10352
    iter.Data()->ClearTotals();
10353
  }
10354
}
10355
10356
//------------------------------------------------------------------
10357
void ReflowCountMgr::ClearGrandTotals()
10358
{
10359
  auto entry = mCounts.LookupForAdd(kGrandTotalsStr);
10360
  if (!entry) {
10361
    entry.OrInsert([this]() { return new ReflowCounter(this); });
10362
  } else {
10363
    entry.Data()->ClearTotals();
10364
    entry.Data()->SetTotalsCache();
10365
  }
10366
}
10367
10368
//------------------------------------------------------------------
10369
void ReflowCountMgr::DisplayDiffsInTotals()
10370
{
10371
  if (mCycledOnce) {
10372
    printf("Differences\n");
10373
    for (int32_t i=0;i<78;i++) {
10374
      printf("-");
10375
    }
10376
    printf("\n");
10377
    ClearGrandTotals();
10378
  }
10379
10380
  for (auto iter = mCounts.Iter(); !iter.Done(); iter.Next()) {
10381
    if (mCycledOnce) {
10382
      iter.Data()->CalcDiffInTotals();
10383
      iter.Data()->DisplayDiffTotals(iter.Key());
10384
    }
10385
    iter.Data()->SetTotalsCache();
10386
  }
10387
10388
  mCycledOnce = true;
10389
}
10390
10391
#endif // MOZ_REFLOW_PERF
10392
10393
nsIFrame* nsIPresShell::GetAbsoluteContainingBlock(nsIFrame *aFrame)
10394
0
{
10395
0
  return FrameConstructor()->GetAbsoluteContainingBlock(aFrame,
10396
0
      nsCSSFrameConstructor::ABS_POS);
10397
0
}
10398
10399
#ifdef ACCESSIBILITY
10400
bool
10401
nsIPresShell::IsAccessibilityActive()
10402
0
{
10403
0
  return GetAccService() != nullptr;
10404
0
}
10405
10406
nsAccessibilityService*
10407
nsIPresShell::AccService()
10408
0
{
10409
0
  return GetAccService();
10410
0
}
10411
#endif
10412
10413
// Asks our docshell whether we're active.
10414
void PresShell::QueryIsActive()
10415
0
{
10416
0
  nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak();
10417
0
  if (mDocument) {
10418
0
    nsIDocument* displayDoc = mDocument->GetDisplayDocument();
10419
0
    if (displayDoc) {
10420
0
      // Ok, we're an external resource document -- we need to use our display
10421
0
      // document's docshell to determine "IsActive" status, since we lack
10422
0
      // a container.
10423
0
      MOZ_ASSERT(!container,
10424
0
                 "external resource doc shouldn't have its own container");
10425
0
10426
0
      nsPresContext* displayPresContext = displayDoc->GetPresContext();
10427
0
      if (displayPresContext) {
10428
0
        container = displayPresContext->GetContainerWeak();
10429
0
      }
10430
0
    }
10431
0
  }
10432
0
10433
0
  nsCOMPtr<nsIDocShell> docshell(do_QueryInterface(container));
10434
0
  if (docshell) {
10435
0
    bool isActive;
10436
0
    nsresult rv = docshell->GetIsActive(&isActive);
10437
0
    // Even though in theory the docshell here could be "Inactive and
10438
0
    // Foreground", thus implying aIsHidden=false for SetIsActive(),
10439
0
    // this is a newly created PresShell so we'd like to invalidate anyway
10440
0
    // upon being made active to ensure that the contents get painted.
10441
0
    if (NS_SUCCEEDED(rv))
10442
0
      SetIsActive(isActive);
10443
0
  }
10444
0
}
10445
10446
// Helper for propagating mIsActive changes to external resources
10447
static bool
10448
SetExternalResourceIsActive(nsIDocument* aDocument, void* aClosure)
10449
0
{
10450
0
  nsIPresShell* shell = aDocument->GetShell();
10451
0
  if (shell) {
10452
0
    shell->SetIsActive(*static_cast<bool*>(aClosure));
10453
0
  }
10454
0
  return true;
10455
0
}
10456
10457
static void
10458
SetPluginIsActive(nsISupports* aSupports, void* aClosure)
10459
0
{
10460
0
  nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
10461
0
  if (!content) {
10462
0
    return;
10463
0
  }
10464
0
10465
0
  nsIFrame *frame = content->GetPrimaryFrame();
10466
0
  nsIObjectFrame *objectFrame = do_QueryFrame(frame);
10467
0
  if (objectFrame) {
10468
0
    objectFrame->SetIsDocumentActive(*static_cast<bool*>(aClosure));
10469
0
  }
10470
0
}
10471
10472
nsresult
10473
PresShell::SetIsActive(bool aIsActive)
10474
0
{
10475
0
  MOZ_ASSERT(mDocument, "should only be called with a document");
10476
0
10477
0
  mIsActive = aIsActive;
10478
0
10479
0
  nsPresContext* presContext = GetPresContext();
10480
0
  if (presContext &&
10481
0
      presContext->RefreshDriver()->GetPresContext() == presContext) {
10482
0
    presContext->RefreshDriver()->SetThrottled(!mIsActive);
10483
0
  }
10484
0
10485
0
  // Propagate state-change to my resource documents' PresShells
10486
0
  mDocument->EnumerateExternalResources(SetExternalResourceIsActive,
10487
0
                                        &aIsActive);
10488
0
  mDocument->EnumerateActivityObservers(SetPluginIsActive,
10489
0
                                        &aIsActive);
10490
0
  nsresult rv = UpdateImageLockingState();
10491
0
#ifdef ACCESSIBILITY
10492
0
  if (aIsActive) {
10493
0
    nsAccessibilityService* accService = AccService();
10494
0
    if (accService) {
10495
0
      accService->PresShellActivated(this);
10496
0
    }
10497
0
  }
10498
0
#endif
10499
0
  return rv;
10500
0
}
10501
10502
/*
10503
 * Determines the current image locking state. Called when one of the
10504
 * dependent factors changes.
10505
 */
10506
nsresult
10507
PresShell::UpdateImageLockingState()
10508
0
{
10509
0
  // We're locked if we're both thawed and active.
10510
0
  bool locked = !mFrozen && mIsActive;
10511
0
10512
0
  nsresult rv = mDocument->ImageTracker()->SetLockingState(locked);
10513
0
10514
0
  if (locked) {
10515
0
    // Request decodes for visible image frames; we want to start decoding as
10516
0
    // quickly as possible when we get foregrounded to minimize flashing.
10517
0
    for (auto iter = mApproximatelyVisibleFrames.Iter(); !iter.Done(); iter.Next()) {
10518
0
      nsImageFrame* imageFrame = do_QueryFrame(iter.Get()->GetKey());
10519
0
      if (imageFrame) {
10520
0
        imageFrame->MaybeDecodeForPredictedSize();
10521
0
      }
10522
0
    }
10523
0
  }
10524
0
10525
0
  return rv;
10526
0
}
10527
10528
PresShell*
10529
PresShell::GetRootPresShell()
10530
0
{
10531
0
  if (mPresContext) {
10532
0
    nsPresContext* rootPresContext = mPresContext->GetRootPresContext();
10533
0
    if (rootPresContext) {
10534
0
      return static_cast<PresShell*>(rootPresContext->PresShell());
10535
0
    }
10536
0
  }
10537
0
  return nullptr;
10538
0
}
10539
10540
void
10541
PresShell::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const
10542
0
{
10543
0
  MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
10544
0
  mFrameArena.AddSizeOfExcludingThis(aSizes);
10545
0
  aSizes.mLayoutPresShellSize += mallocSizeOf(this);
10546
0
  if (mCaret) {
10547
0
    aSizes.mLayoutPresShellSize += mCaret->SizeOfIncludingThis(mallocSizeOf);
10548
0
  }
10549
0
  aSizes.mLayoutPresShellSize +=
10550
0
    mApproximatelyVisibleFrames.ShallowSizeOfExcludingThis(mallocSizeOf) +
10551
0
    mFramesToDirty.ShallowSizeOfExcludingThis(mallocSizeOf);
10552
0
10553
0
  StyleSet()->AddSizeOfIncludingThis(aSizes);
10554
0
10555
0
  aSizes.mLayoutTextRunsSize += SizeOfTextRuns(mallocSizeOf);
10556
0
10557
0
  aSizes.mLayoutPresContextSize +=
10558
0
    mPresContext->SizeOfIncludingThis(mallocSizeOf);
10559
0
10560
0
  mFrameConstructor->AddSizeOfIncludingThis(aSizes);
10561
0
}
10562
10563
size_t
10564
PresShell::SizeOfTextRuns(MallocSizeOf aMallocSizeOf) const
10565
0
{
10566
0
  nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
10567
0
  if (!rootFrame) {
10568
0
    return 0;
10569
0
  }
10570
0
10571
0
  // clear the TEXT_RUN_MEMORY_ACCOUNTED flags
10572
0
  nsLayoutUtils::SizeOfTextRunsForFrames(rootFrame, nullptr,
10573
0
                                         /* clear = */true);
10574
0
10575
0
  // collect the total memory in use for textruns
10576
0
  return nsLayoutUtils::SizeOfTextRunsForFrames(rootFrame, aMallocSizeOf,
10577
0
                                                /* clear = */false);
10578
0
}
10579
10580
void
10581
nsIPresShell::MarkFixedFramesForReflow(IntrinsicDirty aIntrinsicDirty)
10582
0
{
10583
0
  nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
10584
0
  if (rootFrame) {
10585
0
    const nsFrameList& childList = rootFrame->GetChildList(nsIFrame::kFixedList);
10586
0
    for (nsIFrame* childFrame : childList) {
10587
0
      FrameNeedsReflow(childFrame, aIntrinsicDirty, NS_FRAME_IS_DIRTY);
10588
0
    }
10589
0
  }
10590
0
}
10591
10592
void
10593
nsIPresShell::SetVisualViewportSize(nscoord aWidth, nscoord aHeight)
10594
0
{
10595
0
  if (!mVisualViewportSizeSet ||
10596
0
      mVisualViewportSize.width != aWidth ||
10597
0
      mVisualViewportSize.height != aHeight) {
10598
0
    mVisualViewportSizeSet = true;
10599
0
    mVisualViewportSize.width = aWidth;
10600
0
    mVisualViewportSize.height = aHeight;
10601
0
10602
0
    if (nsIScrollableFrame* rootScrollFrame = GetRootScrollFrameAsScrollable()) {
10603
0
      rootScrollFrame->MarkScrollbarsDirtyForReflow();
10604
0
    }
10605
0
    MarkFixedFramesForReflow(nsIPresShell::eResize);
10606
0
  }
10607
0
}
10608
10609
void
10610
nsIPresShell::RecomputeFontSizeInflationEnabled()
10611
0
{
10612
0
  mFontSizeInflationEnabled = DetermineFontSizeInflationState();
10613
0
10614
0
  float fontScale = nsLayoutUtils::SystemFontScale();
10615
0
  if (fontScale == 0.0f) {
10616
0
    return;
10617
0
  }
10618
0
10619
0
  MOZ_ASSERT(mDocument);
10620
0
  MOZ_ASSERT(mPresContext);
10621
0
  if (mFontSizeInflationEnabled || mDocument->IsSyntheticDocument()) {
10622
0
    mPresContext->SetSystemFontScale(1.0f);
10623
0
  } else {
10624
0
    mPresContext->SetSystemFontScale(fontScale);
10625
0
  }
10626
0
}
10627
10628
bool
10629
nsIPresShell::DetermineFontSizeInflationState()
10630
0
{
10631
0
  MOZ_ASSERT(mPresContext, "our pres context should not be null");
10632
0
  if (mPresContext->IsChrome()) {
10633
0
    return false;
10634
0
  }
10635
0
10636
0
  if (FontSizeInflationEmPerLine() == 0 && FontSizeInflationMinTwips() == 0) {
10637
0
    return false;
10638
0
  }
10639
0
10640
0
  // Force-enabling font inflation always trumps the heuristics here.
10641
0
  if (!FontSizeInflationForceEnabled()) {
10642
0
    if (TabChild* tab = TabChild::GetFrom(this)) {
10643
0
      // We're in a child process.  Cancel inflation if we're not
10644
0
      // async-pan zoomed.
10645
0
      if (!tab->AsyncPanZoomEnabled()) {
10646
0
        return false;
10647
0
      }
10648
0
    } else if (XRE_IsParentProcess()) {
10649
0
      // We're in the master process.  Cancel inflation if it's been
10650
0
      // explicitly disabled.
10651
0
      if (FontSizeInflationDisabledInMasterProcess()) {
10652
0
        return false;
10653
0
      }
10654
0
    }
10655
0
  }
10656
0
10657
0
  // XXXjwir3:
10658
0
  // See bug 706918, comment 23 for more information on this particular section
10659
0
  // of the code. We're using "screen size" in place of the size of the content
10660
0
  // area, because on mobile, these are close or equal. This will work for our
10661
0
  // purposes (bug 706198), but it will need to be changed in the future to be
10662
0
  // more correct when we bring the rest of the viewport code into platform.
10663
0
  // We actually want the size of the content area, in the event that we don't
10664
0
  // have any metadata about the width and/or height. On mobile, the screen size
10665
0
  // and the size of the content area are very close, or the same value.
10666
0
  // In XUL fennec, the content area is the size of the <browser> widget, but
10667
0
  // in native fennec, the content area is the size of the Gecko LayerView
10668
0
  // object.
10669
0
10670
0
  // TODO:
10671
0
  // Once bug 716575 has been resolved, this code should be changed so that it
10672
0
  // does the right thing on all platforms.
10673
0
  nsresult rv;
10674
0
  nsCOMPtr<nsIScreenManager> screenMgr =
10675
0
    do_GetService("@mozilla.org/gfx/screenmanager;1", &rv);
10676
0
  if (!NS_SUCCEEDED(rv)) {
10677
0
    return false;
10678
0
  }
10679
0
10680
0
  nsCOMPtr<nsIScreen> screen;
10681
0
  screenMgr->GetPrimaryScreen(getter_AddRefs(screen));
10682
0
  if (screen) {
10683
0
    int32_t screenLeft, screenTop, screenWidth, screenHeight;
10684
0
    screen->GetRect(&screenLeft, &screenTop, &screenWidth, &screenHeight);
10685
0
10686
0
    nsViewportInfo vInf =
10687
0
      GetDocument()->GetViewportInfo(ScreenIntSize(screenWidth, screenHeight));
10688
0
10689
0
    if (vInf.GetDefaultZoom() >= CSSToScreenScale(1.0f) || vInf.IsAutoSizeEnabled()) {
10690
0
      return false;
10691
0
    }
10692
0
  }
10693
0
10694
0
  return true;
10695
0
}
10696
10697
void
10698
PresShell::PausePainting()
10699
0
{
10700
0
  if (GetPresContext()->RefreshDriver()->GetPresContext() != GetPresContext())
10701
0
    return;
10702
0
10703
0
  mPaintingIsFrozen = true;
10704
0
  GetPresContext()->RefreshDriver()->Freeze();
10705
0
}
10706
10707
void
10708
PresShell::ResumePainting()
10709
0
{
10710
0
  if (GetPresContext()->RefreshDriver()->GetPresContext() != GetPresContext())
10711
0
    return;
10712
0
10713
0
  mPaintingIsFrozen = false;
10714
0
  GetPresContext()->RefreshDriver()->Thaw();
10715
0
}
10716
10717
void
10718
nsIPresShell::SyncWindowProperties(nsView* aView)
10719
0
{
10720
0
  nsIFrame* frame = aView->GetFrame();
10721
0
  if (frame && mPresContext) {
10722
0
    // CreateReferenceRenderingContext can return nullptr
10723
0
    RefPtr<gfxContext> rcx(CreateReferenceRenderingContext());
10724
0
    nsContainerFrame::SyncWindowProperties(mPresContext, frame, aView, rcx, 0);
10725
0
  }
10726
0
}
10727
10728
static SheetType
10729
ToSheetType(uint32_t aServiceSheetType)
10730
0
{
10731
0
  switch (aServiceSheetType) {
10732
0
    case nsIStyleSheetService::AGENT_SHEET:
10733
0
      return SheetType::Agent;
10734
0
      break;
10735
0
    case nsIStyleSheetService::USER_SHEET:
10736
0
      return SheetType::User;
10737
0
      break;
10738
0
    default:
10739
0
      MOZ_FALLTHROUGH_ASSERT("unexpected aSheetType value");
10740
0
    case nsIStyleSheetService::AUTHOR_SHEET:
10741
0
      return SheetType::Doc;
10742
0
  }
10743
0
}
10744
10745
nsresult
10746
nsIPresShell::HasRuleProcessorUsedByMultipleStyleSets(uint32_t aSheetType,
10747
                                                      bool* aRetVal)
10748
0
{
10749
0
  *aRetVal = false;
10750
0
  return NS_OK;
10751
0
}
10752
10753
void
10754
PresShell::NotifyStyleSheetServiceSheetAdded(StyleSheet* aSheet,
10755
                                             uint32_t aSheetType)
10756
0
{
10757
0
  if (!mStyleSet) {
10758
0
    return;
10759
0
  }
10760
0
10761
0
  switch (aSheetType) {
10762
0
    case nsIStyleSheetService::AGENT_SHEET:
10763
0
      AddAgentSheet(aSheet);
10764
0
      break;
10765
0
    case nsIStyleSheetService::USER_SHEET:
10766
0
      AddUserSheet(aSheet);
10767
0
      break;
10768
0
    case nsIStyleSheetService::AUTHOR_SHEET:
10769
0
      AddAuthorSheet(aSheet);
10770
0
      break;
10771
0
    default:
10772
0
      MOZ_ASSERT_UNREACHABLE("unexpected aSheetType value");
10773
0
      break;
10774
0
  }
10775
0
}
10776
10777
void
10778
PresShell::NotifyStyleSheetServiceSheetRemoved(StyleSheet* aSheet,
10779
                                               uint32_t aSheetType)
10780
0
{
10781
0
  if (!mStyleSet) {
10782
0
    return;
10783
0
  }
10784
0
10785
0
  RemoveSheet(ToSheetType(aSheetType), aSheet);
10786
0
}
10787
10788
nsIContent*
10789
PresShell::GetOverrideClickTarget(WidgetGUIEvent* aEvent,
10790
                                  nsIFrame* aFrame)
10791
0
{
10792
0
  if (aEvent->mMessage != eMouseUp) {
10793
0
    return nullptr;
10794
0
  }
10795
0
10796
0
  MOZ_ASSERT(aEvent->mClass == eMouseEventClass);
10797
0
  WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
10798
0
10799
0
  uint32_t flags = 0;
10800
0
  nsPoint eventPoint =
10801
0
    nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, aFrame);
10802
0
  if (mouseEvent->mIgnoreRootScrollFrame) {
10803
0
    flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
10804
0
  }
10805
0
10806
0
  nsIFrame* target =
10807
0
    FindFrameTargetedByInputEvent(aEvent, aFrame, eventPoint, flags);
10808
0
  if (!target) {
10809
0
    return nullptr;
10810
0
  }
10811
0
10812
0
  nsIContent* overrideClickTarget = target->GetContent();
10813
0
  while (overrideClickTarget && !overrideClickTarget->IsElement()) {
10814
0
    overrideClickTarget = overrideClickTarget->GetFlattenedTreeParent();
10815
0
  }
10816
0
  return overrideClickTarget;
10817
0
}