/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(¤tIndex); |
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, ¤tBorder, ¤tPadding); |
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 | } |