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