Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
michael@0 | 2 | * vim: set ts=2 sw=2 et tw=78: |
michael@0 | 3 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 6 | * |
michael@0 | 7 | * This Original Code has been modified by IBM Corporation. |
michael@0 | 8 | * Modifications made by IBM described herein are |
michael@0 | 9 | * Copyright (c) International Business Machines |
michael@0 | 10 | * Corporation, 2000 |
michael@0 | 11 | * |
michael@0 | 12 | * Modifications to Mozilla code or documentation |
michael@0 | 13 | * identified per MPL Section 3.3 |
michael@0 | 14 | * |
michael@0 | 15 | * Date Modified by Description of modification |
michael@0 | 16 | * 05/03/2000 IBM Corp. Observer events for reflow states |
michael@0 | 17 | */ |
michael@0 | 18 | |
michael@0 | 19 | /* a presentation of a document, part 2 */ |
michael@0 | 20 | |
michael@0 | 21 | #ifdef MOZ_LOGGING |
michael@0 | 22 | #define FORCE_PR_LOG /* Allow logging in the release build */ |
michael@0 | 23 | #endif |
michael@0 | 24 | #include "prlog.h" |
michael@0 | 25 | |
michael@0 | 26 | #include "mozilla/ArrayUtils.h" |
michael@0 | 27 | #include "mozilla/EventDispatcher.h" |
michael@0 | 28 | #include "mozilla/EventStateManager.h" |
michael@0 | 29 | #include "mozilla/EventStates.h" |
michael@0 | 30 | #include "mozilla/IMEStateManager.h" |
michael@0 | 31 | #include "mozilla/MemoryReporting.h" |
michael@0 | 32 | #include "mozilla/dom/TabChild.h" |
michael@0 | 33 | #include "mozilla/Likely.h" |
michael@0 | 34 | #include "mozilla/MouseEvents.h" |
michael@0 | 35 | #include "mozilla/TextEvents.h" |
michael@0 | 36 | #include "mozilla/TouchEvents.h" |
michael@0 | 37 | #include <algorithm> |
michael@0 | 38 | |
michael@0 | 39 | #ifdef XP_WIN |
michael@0 | 40 | #include "winuser.h" |
michael@0 | 41 | #endif |
michael@0 | 42 | |
michael@0 | 43 | #include "nsPresShell.h" |
michael@0 | 44 | #include "nsPresContext.h" |
michael@0 | 45 | #include "nsIContent.h" |
michael@0 | 46 | #include "mozilla/dom/Element.h" |
michael@0 | 47 | #include "mozilla/dom/Event.h" // for Event::GetEventPopupControlState() |
michael@0 | 48 | #include "mozilla/dom/ShadowRoot.h" |
michael@0 | 49 | #include "mozilla/dom/PointerEvent.h" |
michael@0 | 50 | #include "nsIDocument.h" |
michael@0 | 51 | #include "nsCSSStyleSheet.h" |
michael@0 | 52 | #include "nsAnimationManager.h" |
michael@0 | 53 | #include "nsNameSpaceManager.h" // for Pref-related rule management (bugs 22963,20760,31816) |
michael@0 | 54 | #include "nsFrame.h" |
michael@0 | 55 | #include "FrameLayerBuilder.h" |
michael@0 | 56 | #include "nsViewManager.h" |
michael@0 | 57 | #include "nsView.h" |
michael@0 | 58 | #include "nsCRTGlue.h" |
michael@0 | 59 | #include "prprf.h" |
michael@0 | 60 | #include "prinrval.h" |
michael@0 | 61 | #include "nsTArray.h" |
michael@0 | 62 | #include "nsCOMArray.h" |
michael@0 | 63 | #include "nsContainerFrame.h" |
michael@0 | 64 | #include "nsISelection.h" |
michael@0 | 65 | #include "mozilla/dom/Selection.h" |
michael@0 | 66 | #include "nsGkAtoms.h" |
michael@0 | 67 | #include "nsIDOMRange.h" |
michael@0 | 68 | #include "nsIDOMDocument.h" |
michael@0 | 69 | #include "nsIDOMNode.h" |
michael@0 | 70 | #include "nsIDOMNodeList.h" |
michael@0 | 71 | #include "nsIDOMElement.h" |
michael@0 | 72 | #include "nsRange.h" |
michael@0 | 73 | #include "nsCOMPtr.h" |
michael@0 | 74 | #include "nsAutoPtr.h" |
michael@0 | 75 | #include "nsReadableUtils.h" |
michael@0 | 76 | #include "nsIPageSequenceFrame.h" |
michael@0 | 77 | #include "nsCaret.h" |
michael@0 | 78 | #include "nsIDOMHTMLDocument.h" |
michael@0 | 79 | #include "nsFrameManager.h" |
michael@0 | 80 | #include "nsXPCOM.h" |
michael@0 | 81 | #include "nsILayoutHistoryState.h" |
michael@0 | 82 | #include "nsILineIterator.h" // for ScrollContentIntoView |
michael@0 | 83 | #include "pldhash.h" |
michael@0 | 84 | #include "mozilla/dom/Touch.h" |
michael@0 | 85 | #include "mozilla/dom/PointerEventBinding.h" |
michael@0 | 86 | #include "nsIObserverService.h" |
michael@0 | 87 | #include "nsDocShell.h" // for reflow observation |
michael@0 | 88 | #include "nsIBaseWindow.h" |
michael@0 | 89 | #include "nsError.h" |
michael@0 | 90 | #include "nsLayoutUtils.h" |
michael@0 | 91 | #include "nsViewportInfo.h" |
michael@0 | 92 | #include "nsCSSRendering.h" |
michael@0 | 93 | // for |#ifdef DEBUG| code |
michael@0 | 94 | #include "prenv.h" |
michael@0 | 95 | #include "nsDisplayList.h" |
michael@0 | 96 | #include "nsRegion.h" |
michael@0 | 97 | #include "nsRenderingContext.h" |
michael@0 | 98 | #include "nsAutoLayoutPhase.h" |
michael@0 | 99 | #ifdef MOZ_REFLOW_PERF |
michael@0 | 100 | #include "nsFontMetrics.h" |
michael@0 | 101 | #endif |
michael@0 | 102 | #include "PositionedEventTargeting.h" |
michael@0 | 103 | |
michael@0 | 104 | #include "nsIReflowCallback.h" |
michael@0 | 105 | |
michael@0 | 106 | #include "nsPIDOMWindow.h" |
michael@0 | 107 | #include "nsFocusManager.h" |
michael@0 | 108 | #include "nsIObjectFrame.h" |
michael@0 | 109 | #include "nsIObjectLoadingContent.h" |
michael@0 | 110 | #include "nsNetUtil.h" |
michael@0 | 111 | #include "nsThreadUtils.h" |
michael@0 | 112 | #include "nsStyleSheetService.h" |
michael@0 | 113 | #include "gfxImageSurface.h" |
michael@0 | 114 | #include "gfxContext.h" |
michael@0 | 115 | #include "nsSMILAnimationController.h" |
michael@0 | 116 | #include "SVGContentUtils.h" |
michael@0 | 117 | #include "nsSVGEffects.h" |
michael@0 | 118 | #include "SVGFragmentIdentifier.h" |
michael@0 | 119 | #include "nsArenaMemoryStats.h" |
michael@0 | 120 | |
michael@0 | 121 | #include "nsPerformance.h" |
michael@0 | 122 | #include "nsRefreshDriver.h" |
michael@0 | 123 | #include "nsDOMNavigationTiming.h" |
michael@0 | 124 | |
michael@0 | 125 | // Drag & Drop, Clipboard |
michael@0 | 126 | #include "nsIDocShellTreeItem.h" |
michael@0 | 127 | #include "nsIURI.h" |
michael@0 | 128 | #include "nsIScrollableFrame.h" |
michael@0 | 129 | #include "nsITimer.h" |
michael@0 | 130 | #ifdef ACCESSIBILITY |
michael@0 | 131 | #include "nsAccessibilityService.h" |
michael@0 | 132 | #include "mozilla/a11y/DocAccessible.h" |
michael@0 | 133 | #ifdef DEBUG |
michael@0 | 134 | #include "mozilla/a11y/Logging.h" |
michael@0 | 135 | #endif |
michael@0 | 136 | #endif |
michael@0 | 137 | |
michael@0 | 138 | // For style data reconstruction |
michael@0 | 139 | #include "nsStyleChangeList.h" |
michael@0 | 140 | #include "nsCSSFrameConstructor.h" |
michael@0 | 141 | #ifdef MOZ_XUL |
michael@0 | 142 | #include "nsMenuFrame.h" |
michael@0 | 143 | #include "nsTreeBodyFrame.h" |
michael@0 | 144 | #include "nsIBoxObject.h" |
michael@0 | 145 | #include "nsITreeBoxObject.h" |
michael@0 | 146 | #include "nsMenuPopupFrame.h" |
michael@0 | 147 | #include "nsITreeColumns.h" |
michael@0 | 148 | #include "nsIDOMXULMultSelectCntrlEl.h" |
michael@0 | 149 | #include "nsIDOMXULSelectCntrlItemEl.h" |
michael@0 | 150 | #include "nsIDOMXULMenuListElement.h" |
michael@0 | 151 | |
michael@0 | 152 | #endif |
michael@0 | 153 | |
michael@0 | 154 | #include "GeckoProfiler.h" |
michael@0 | 155 | #include "gfxPlatform.h" |
michael@0 | 156 | #include "Layers.h" |
michael@0 | 157 | #include "LayerTreeInvalidation.h" |
michael@0 | 158 | #include "mozilla/css/ImageLoader.h" |
michael@0 | 159 | #include "mozilla/Preferences.h" |
michael@0 | 160 | #include "mozilla/Telemetry.h" |
michael@0 | 161 | #include "nsCanvasFrame.h" |
michael@0 | 162 | #include "nsIImageLoadingContent.h" |
michael@0 | 163 | #include "nsIScreen.h" |
michael@0 | 164 | #include "nsIScreenManager.h" |
michael@0 | 165 | #include "nsPlaceholderFrame.h" |
michael@0 | 166 | #include "nsTransitionManager.h" |
michael@0 | 167 | #include "ChildIterator.h" |
michael@0 | 168 | #include "RestyleManager.h" |
michael@0 | 169 | #include "nsIDOMHTMLElement.h" |
michael@0 | 170 | #include "nsIDragSession.h" |
michael@0 | 171 | #include "nsIFrameInlines.h" |
michael@0 | 172 | #include "mozilla/gfx/2D.h" |
michael@0 | 173 | |
michael@0 | 174 | #ifdef ANDROID |
michael@0 | 175 | #include "nsIDocShellTreeOwner.h" |
michael@0 | 176 | #endif |
michael@0 | 177 | |
michael@0 | 178 | #ifdef MOZ_TASK_TRACER |
michael@0 | 179 | #include "GeckoTaskTracer.h" |
michael@0 | 180 | using namespace mozilla::tasktracer; |
michael@0 | 181 | #endif |
michael@0 | 182 | |
michael@0 | 183 | #define ANCHOR_SCROLL_FLAGS \ |
michael@0 | 184 | (nsIPresShell::SCROLL_OVERFLOW_HIDDEN | nsIPresShell::SCROLL_NO_PARENT_FRAMES) |
michael@0 | 185 | |
michael@0 | 186 | using namespace mozilla; |
michael@0 | 187 | using namespace mozilla::css; |
michael@0 | 188 | using namespace mozilla::dom; |
michael@0 | 189 | using namespace mozilla::gfx; |
michael@0 | 190 | using namespace mozilla::layers; |
michael@0 | 191 | using namespace mozilla::gfx; |
michael@0 | 192 | |
michael@0 | 193 | CapturingContentInfo nsIPresShell::gCaptureInfo = |
michael@0 | 194 | { false /* mAllowed */, false /* mPointerLock */, false /* mRetargetToElement */, |
michael@0 | 195 | false /* mPreventDrag */, nullptr /* mContent */ }; |
michael@0 | 196 | nsIContent* nsIPresShell::gKeyDownTarget; |
michael@0 | 197 | nsRefPtrHashtable<nsUint32HashKey, dom::Touch>* nsIPresShell::gCaptureTouchList; |
michael@0 | 198 | nsRefPtrHashtable<nsUint32HashKey, nsIContent>* nsIPresShell::gPointerCaptureList; |
michael@0 | 199 | nsClassHashtable<nsUint32HashKey, nsIPresShell::PointerInfo>* nsIPresShell::gActivePointersIds; |
michael@0 | 200 | bool nsIPresShell::gPreventMouseEvents = false; |
michael@0 | 201 | |
michael@0 | 202 | // convert a color value to a string, in the CSS format #RRGGBB |
michael@0 | 203 | // * - initially created for bugs 31816, 20760, 22963 |
michael@0 | 204 | static void ColorToString(nscolor aColor, nsAutoString &aString); |
michael@0 | 205 | |
michael@0 | 206 | // RangePaintInfo is used to paint ranges to offscreen buffers |
michael@0 | 207 | struct RangePaintInfo { |
michael@0 | 208 | nsRefPtr<nsRange> mRange; |
michael@0 | 209 | nsDisplayListBuilder mBuilder; |
michael@0 | 210 | nsDisplayList mList; |
michael@0 | 211 | |
michael@0 | 212 | // offset of builder's reference frame to the root frame |
michael@0 | 213 | nsPoint mRootOffset; |
michael@0 | 214 | |
michael@0 | 215 | RangePaintInfo(nsRange* aRange, nsIFrame* aFrame) |
michael@0 | 216 | : mRange(aRange), mBuilder(aFrame, nsDisplayListBuilder::PAINTING, false) |
michael@0 | 217 | { |
michael@0 | 218 | MOZ_COUNT_CTOR(RangePaintInfo); |
michael@0 | 219 | } |
michael@0 | 220 | |
michael@0 | 221 | ~RangePaintInfo() |
michael@0 | 222 | { |
michael@0 | 223 | mList.DeleteAll(); |
michael@0 | 224 | MOZ_COUNT_DTOR(RangePaintInfo); |
michael@0 | 225 | } |
michael@0 | 226 | }; |
michael@0 | 227 | |
michael@0 | 228 | #undef NOISY |
michael@0 | 229 | |
michael@0 | 230 | // ---------------------------------------------------------------------- |
michael@0 | 231 | |
michael@0 | 232 | #ifdef DEBUG |
michael@0 | 233 | // Set the environment variable GECKO_VERIFY_REFLOW_FLAGS to one or |
michael@0 | 234 | // more of the following flags (comma separated) for handy debug |
michael@0 | 235 | // output. |
michael@0 | 236 | static uint32_t gVerifyReflowFlags; |
michael@0 | 237 | |
michael@0 | 238 | struct VerifyReflowFlags { |
michael@0 | 239 | const char* name; |
michael@0 | 240 | uint32_t bit; |
michael@0 | 241 | }; |
michael@0 | 242 | |
michael@0 | 243 | static const VerifyReflowFlags gFlags[] = { |
michael@0 | 244 | { "verify", VERIFY_REFLOW_ON }, |
michael@0 | 245 | { "reflow", VERIFY_REFLOW_NOISY }, |
michael@0 | 246 | { "all", VERIFY_REFLOW_ALL }, |
michael@0 | 247 | { "list-commands", VERIFY_REFLOW_DUMP_COMMANDS }, |
michael@0 | 248 | { "noisy-commands", VERIFY_REFLOW_NOISY_RC }, |
michael@0 | 249 | { "really-noisy-commands", VERIFY_REFLOW_REALLY_NOISY_RC }, |
michael@0 | 250 | { "resize", VERIFY_REFLOW_DURING_RESIZE_REFLOW }, |
michael@0 | 251 | }; |
michael@0 | 252 | |
michael@0 | 253 | #define NUM_VERIFY_REFLOW_FLAGS (sizeof(gFlags) / sizeof(gFlags[0])) |
michael@0 | 254 | |
michael@0 | 255 | static void |
michael@0 | 256 | ShowVerifyReflowFlags() |
michael@0 | 257 | { |
michael@0 | 258 | printf("Here are the available GECKO_VERIFY_REFLOW_FLAGS:\n"); |
michael@0 | 259 | const VerifyReflowFlags* flag = gFlags; |
michael@0 | 260 | const VerifyReflowFlags* limit = gFlags + NUM_VERIFY_REFLOW_FLAGS; |
michael@0 | 261 | while (flag < limit) { |
michael@0 | 262 | printf(" %s\n", flag->name); |
michael@0 | 263 | ++flag; |
michael@0 | 264 | } |
michael@0 | 265 | printf("Note: GECKO_VERIFY_REFLOW_FLAGS is a comma separated list of flag\n"); |
michael@0 | 266 | printf("names (no whitespace)\n"); |
michael@0 | 267 | } |
michael@0 | 268 | #endif |
michael@0 | 269 | |
michael@0 | 270 | //======================================================================== |
michael@0 | 271 | //======================================================================== |
michael@0 | 272 | //======================================================================== |
michael@0 | 273 | #ifdef MOZ_REFLOW_PERF |
michael@0 | 274 | class ReflowCountMgr; |
michael@0 | 275 | |
michael@0 | 276 | static const char kGrandTotalsStr[] = "Grand Totals"; |
michael@0 | 277 | |
michael@0 | 278 | // Counting Class |
michael@0 | 279 | class ReflowCounter { |
michael@0 | 280 | public: |
michael@0 | 281 | ReflowCounter(ReflowCountMgr * aMgr = nullptr); |
michael@0 | 282 | ~ReflowCounter(); |
michael@0 | 283 | |
michael@0 | 284 | void ClearTotals(); |
michael@0 | 285 | void DisplayTotals(const char * aStr); |
michael@0 | 286 | void DisplayDiffTotals(const char * aStr); |
michael@0 | 287 | void DisplayHTMLTotals(const char * aStr); |
michael@0 | 288 | |
michael@0 | 289 | void Add() { mTotal++; } |
michael@0 | 290 | void Add(uint32_t aTotal) { mTotal += aTotal; } |
michael@0 | 291 | |
michael@0 | 292 | void CalcDiffInTotals(); |
michael@0 | 293 | void SetTotalsCache(); |
michael@0 | 294 | |
michael@0 | 295 | void SetMgr(ReflowCountMgr * aMgr) { mMgr = aMgr; } |
michael@0 | 296 | |
michael@0 | 297 | uint32_t GetTotal() { return mTotal; } |
michael@0 | 298 | |
michael@0 | 299 | protected: |
michael@0 | 300 | void DisplayTotals(uint32_t aTotal, const char * aTitle); |
michael@0 | 301 | void DisplayHTMLTotals(uint32_t aTotal, const char * aTitle); |
michael@0 | 302 | |
michael@0 | 303 | uint32_t mTotal; |
michael@0 | 304 | uint32_t mCacheTotal; |
michael@0 | 305 | |
michael@0 | 306 | ReflowCountMgr * mMgr; // weak reference (don't delete) |
michael@0 | 307 | }; |
michael@0 | 308 | |
michael@0 | 309 | // Counting Class |
michael@0 | 310 | class IndiReflowCounter { |
michael@0 | 311 | public: |
michael@0 | 312 | IndiReflowCounter(ReflowCountMgr * aMgr = nullptr) |
michael@0 | 313 | : mFrame(nullptr), |
michael@0 | 314 | mCount(0), |
michael@0 | 315 | mMgr(aMgr), |
michael@0 | 316 | mCounter(aMgr), |
michael@0 | 317 | mHasBeenOutput(false) |
michael@0 | 318 | {} |
michael@0 | 319 | virtual ~IndiReflowCounter() {} |
michael@0 | 320 | |
michael@0 | 321 | nsAutoString mName; |
michael@0 | 322 | nsIFrame * mFrame; // weak reference (don't delete) |
michael@0 | 323 | int32_t mCount; |
michael@0 | 324 | |
michael@0 | 325 | ReflowCountMgr * mMgr; // weak reference (don't delete) |
michael@0 | 326 | |
michael@0 | 327 | ReflowCounter mCounter; |
michael@0 | 328 | bool mHasBeenOutput; |
michael@0 | 329 | |
michael@0 | 330 | }; |
michael@0 | 331 | |
michael@0 | 332 | //-------------------- |
michael@0 | 333 | // Manager Class |
michael@0 | 334 | //-------------------- |
michael@0 | 335 | class ReflowCountMgr { |
michael@0 | 336 | public: |
michael@0 | 337 | ReflowCountMgr(); |
michael@0 | 338 | virtual ~ReflowCountMgr(); |
michael@0 | 339 | |
michael@0 | 340 | void ClearTotals(); |
michael@0 | 341 | void ClearGrandTotals(); |
michael@0 | 342 | void DisplayTotals(const char * aStr); |
michael@0 | 343 | void DisplayHTMLTotals(const char * aStr); |
michael@0 | 344 | void DisplayDiffsInTotals(const char * aStr); |
michael@0 | 345 | |
michael@0 | 346 | void Add(const char * aName, nsIFrame * aFrame); |
michael@0 | 347 | ReflowCounter * LookUp(const char * aName); |
michael@0 | 348 | |
michael@0 | 349 | void PaintCount(const char *aName, nsRenderingContext* aRenderingContext, |
michael@0 | 350 | nsPresContext *aPresContext, nsIFrame *aFrame, |
michael@0 | 351 | const nsPoint &aOffset, uint32_t aColor); |
michael@0 | 352 | |
michael@0 | 353 | FILE * GetOutFile() { return mFD; } |
michael@0 | 354 | |
michael@0 | 355 | PLHashTable * GetIndiFrameHT() { return mIndiFrameCounts; } |
michael@0 | 356 | |
michael@0 | 357 | void SetPresContext(nsPresContext * aPresContext) { mPresContext = aPresContext; } // weak reference |
michael@0 | 358 | void SetPresShell(nsIPresShell* aPresShell) { mPresShell= aPresShell; } // weak reference |
michael@0 | 359 | |
michael@0 | 360 | void SetDumpFrameCounts(bool aVal) { mDumpFrameCounts = aVal; } |
michael@0 | 361 | void SetDumpFrameByFrameCounts(bool aVal) { mDumpFrameByFrameCounts = aVal; } |
michael@0 | 362 | void SetPaintFrameCounts(bool aVal) { mPaintFrameByFrameCounts = aVal; } |
michael@0 | 363 | |
michael@0 | 364 | bool IsPaintingFrameCounts() { return mPaintFrameByFrameCounts; } |
michael@0 | 365 | |
michael@0 | 366 | protected: |
michael@0 | 367 | void DisplayTotals(uint32_t aTotal, uint32_t * aDupArray, char * aTitle); |
michael@0 | 368 | void DisplayHTMLTotals(uint32_t aTotal, uint32_t * aDupArray, char * aTitle); |
michael@0 | 369 | |
michael@0 | 370 | static int RemoveItems(PLHashEntry *he, int i, void *arg); |
michael@0 | 371 | static int RemoveIndiItems(PLHashEntry *he, int i, void *arg); |
michael@0 | 372 | void CleanUp(); |
michael@0 | 373 | |
michael@0 | 374 | // stdout Output Methods |
michael@0 | 375 | static int DoSingleTotal(PLHashEntry *he, int i, void *arg); |
michael@0 | 376 | static int DoSingleIndi(PLHashEntry *he, int i, void *arg); |
michael@0 | 377 | |
michael@0 | 378 | void DoGrandTotals(); |
michael@0 | 379 | void DoIndiTotalsTree(); |
michael@0 | 380 | |
michael@0 | 381 | // HTML Output Methods |
michael@0 | 382 | static int DoSingleHTMLTotal(PLHashEntry *he, int i, void *arg); |
michael@0 | 383 | void DoGrandHTMLTotals(); |
michael@0 | 384 | |
michael@0 | 385 | // Zero Out the Totals |
michael@0 | 386 | static int DoClearTotals(PLHashEntry *he, int i, void *arg); |
michael@0 | 387 | |
michael@0 | 388 | // Displays the Diff Totals |
michael@0 | 389 | static int DoDisplayDiffTotals(PLHashEntry *he, int i, void *arg); |
michael@0 | 390 | |
michael@0 | 391 | PLHashTable * mCounts; |
michael@0 | 392 | PLHashTable * mIndiFrameCounts; |
michael@0 | 393 | FILE * mFD; |
michael@0 | 394 | |
michael@0 | 395 | bool mDumpFrameCounts; |
michael@0 | 396 | bool mDumpFrameByFrameCounts; |
michael@0 | 397 | bool mPaintFrameByFrameCounts; |
michael@0 | 398 | |
michael@0 | 399 | bool mCycledOnce; |
michael@0 | 400 | |
michael@0 | 401 | // Root Frame for Individual Tracking |
michael@0 | 402 | nsPresContext * mPresContext; |
michael@0 | 403 | nsIPresShell* mPresShell; |
michael@0 | 404 | |
michael@0 | 405 | // ReflowCountMgr gReflowCountMgr; |
michael@0 | 406 | }; |
michael@0 | 407 | #endif |
michael@0 | 408 | //======================================================================== |
michael@0 | 409 | |
michael@0 | 410 | // comment out to hide caret |
michael@0 | 411 | #define SHOW_CARET |
michael@0 | 412 | |
michael@0 | 413 | // The upper bound on the amount of time to spend reflowing, in |
michael@0 | 414 | // microseconds. When this bound is exceeded and reflow commands are |
michael@0 | 415 | // still queued up, a reflow event is posted. The idea is for reflow |
michael@0 | 416 | // to not hog the processor beyond the time specifed in |
michael@0 | 417 | // gMaxRCProcessingTime. This data member is initialized from the |
michael@0 | 418 | // layout.reflow.timeslice pref. |
michael@0 | 419 | #define NS_MAX_REFLOW_TIME 1000000 |
michael@0 | 420 | static int32_t gMaxRCProcessingTime = -1; |
michael@0 | 421 | |
michael@0 | 422 | struct nsCallbackEventRequest |
michael@0 | 423 | { |
michael@0 | 424 | nsIReflowCallback* callback; |
michael@0 | 425 | nsCallbackEventRequest* next; |
michael@0 | 426 | }; |
michael@0 | 427 | |
michael@0 | 428 | // ---------------------------------------------------------------------------- |
michael@0 | 429 | #define ASSERT_REFLOW_SCHEDULED_STATE() \ |
michael@0 | 430 | NS_ASSERTION(mReflowScheduled == \ |
michael@0 | 431 | GetPresContext()->RefreshDriver()-> \ |
michael@0 | 432 | IsLayoutFlushObserver(this), "Unexpected state") |
michael@0 | 433 | |
michael@0 | 434 | class nsAutoCauseReflowNotifier |
michael@0 | 435 | { |
michael@0 | 436 | public: |
michael@0 | 437 | nsAutoCauseReflowNotifier(PresShell* aShell) |
michael@0 | 438 | : mShell(aShell) |
michael@0 | 439 | { |
michael@0 | 440 | mShell->WillCauseReflow(); |
michael@0 | 441 | } |
michael@0 | 442 | ~nsAutoCauseReflowNotifier() |
michael@0 | 443 | { |
michael@0 | 444 | // This check should not be needed. Currently the only place that seem |
michael@0 | 445 | // to need it is the code that deals with bug 337586. |
michael@0 | 446 | if (!mShell->mHaveShutDown) { |
michael@0 | 447 | mShell->DidCauseReflow(); |
michael@0 | 448 | } |
michael@0 | 449 | else { |
michael@0 | 450 | nsContentUtils::RemoveScriptBlocker(); |
michael@0 | 451 | } |
michael@0 | 452 | } |
michael@0 | 453 | |
michael@0 | 454 | PresShell* mShell; |
michael@0 | 455 | }; |
michael@0 | 456 | |
michael@0 | 457 | class MOZ_STACK_CLASS nsPresShellEventCB : public EventDispatchingCallback |
michael@0 | 458 | { |
michael@0 | 459 | public: |
michael@0 | 460 | nsPresShellEventCB(PresShell* aPresShell) : mPresShell(aPresShell) {} |
michael@0 | 461 | |
michael@0 | 462 | virtual void HandleEvent(EventChainPostVisitor& aVisitor) MOZ_OVERRIDE |
michael@0 | 463 | { |
michael@0 | 464 | if (aVisitor.mPresContext && aVisitor.mEvent->eventStructType != NS_EVENT) { |
michael@0 | 465 | if (aVisitor.mEvent->message == NS_MOUSE_BUTTON_DOWN || |
michael@0 | 466 | aVisitor.mEvent->message == NS_MOUSE_BUTTON_UP) { |
michael@0 | 467 | // Mouse-up and mouse-down events call nsFrame::HandlePress/Release |
michael@0 | 468 | // which call GetContentOffsetsFromPoint which requires up-to-date layout. |
michael@0 | 469 | // Bring layout up-to-date now so that GetCurrentEventFrame() below |
michael@0 | 470 | // will return a real frame and we don't have to worry about |
michael@0 | 471 | // destroying it by flushing later. |
michael@0 | 472 | mPresShell->FlushPendingNotifications(Flush_Layout); |
michael@0 | 473 | } else if (aVisitor.mEvent->message == NS_WHEEL_WHEEL && |
michael@0 | 474 | aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault) { |
michael@0 | 475 | nsIFrame* frame = mPresShell->GetCurrentEventFrame(); |
michael@0 | 476 | if (frame) { |
michael@0 | 477 | // chrome (including addons) should be able to know if content |
michael@0 | 478 | // handles both D3E "wheel" event and legacy mouse scroll events. |
michael@0 | 479 | // We should dispatch legacy mouse events before dispatching the |
michael@0 | 480 | // "wheel" event into system group. |
michael@0 | 481 | nsRefPtr<EventStateManager> esm = |
michael@0 | 482 | aVisitor.mPresContext->EventStateManager(); |
michael@0 | 483 | esm->DispatchLegacyMouseScrollEvents(frame, |
michael@0 | 484 | aVisitor.mEvent->AsWheelEvent(), |
michael@0 | 485 | &aVisitor.mEventStatus); |
michael@0 | 486 | } |
michael@0 | 487 | } |
michael@0 | 488 | nsIFrame* frame = mPresShell->GetCurrentEventFrame(); |
michael@0 | 489 | if (!frame && |
michael@0 | 490 | (aVisitor.mEvent->message == NS_MOUSE_BUTTON_UP || |
michael@0 | 491 | aVisitor.mEvent->message == NS_TOUCH_END)) { |
michael@0 | 492 | // Redirect BUTTON_UP and TOUCH_END events to the root frame to ensure |
michael@0 | 493 | // that capturing is released. |
michael@0 | 494 | frame = mPresShell->GetRootFrame(); |
michael@0 | 495 | } |
michael@0 | 496 | if (frame) { |
michael@0 | 497 | frame->HandleEvent(aVisitor.mPresContext, |
michael@0 | 498 | aVisitor.mEvent->AsGUIEvent(), |
michael@0 | 499 | &aVisitor.mEventStatus); |
michael@0 | 500 | } |
michael@0 | 501 | } |
michael@0 | 502 | } |
michael@0 | 503 | |
michael@0 | 504 | nsRefPtr<PresShell> mPresShell; |
michael@0 | 505 | }; |
michael@0 | 506 | |
michael@0 | 507 | class nsBeforeFirstPaintDispatcher : public nsRunnable |
michael@0 | 508 | { |
michael@0 | 509 | public: |
michael@0 | 510 | nsBeforeFirstPaintDispatcher(nsIDocument* aDocument) |
michael@0 | 511 | : mDocument(aDocument) {} |
michael@0 | 512 | |
michael@0 | 513 | // Fires the "before-first-paint" event so that interested parties (right now, the |
michael@0 | 514 | // mobile browser) are aware of it. |
michael@0 | 515 | NS_IMETHOD Run() MOZ_OVERRIDE |
michael@0 | 516 | { |
michael@0 | 517 | nsCOMPtr<nsIObserverService> observerService = |
michael@0 | 518 | mozilla::services::GetObserverService(); |
michael@0 | 519 | if (observerService) { |
michael@0 | 520 | observerService->NotifyObservers(mDocument, "before-first-paint", |
michael@0 | 521 | nullptr); |
michael@0 | 522 | } |
michael@0 | 523 | return NS_OK; |
michael@0 | 524 | } |
michael@0 | 525 | |
michael@0 | 526 | private: |
michael@0 | 527 | nsCOMPtr<nsIDocument> mDocument; |
michael@0 | 528 | }; |
michael@0 | 529 | |
michael@0 | 530 | bool PresShell::sDisableNonTestMouseEvents = false; |
michael@0 | 531 | |
michael@0 | 532 | #ifdef PR_LOGGING |
michael@0 | 533 | PRLogModuleInfo* PresShell::gLog; |
michael@0 | 534 | #endif |
michael@0 | 535 | |
michael@0 | 536 | #ifdef DEBUG |
michael@0 | 537 | static void |
michael@0 | 538 | VerifyStyleTree(nsPresContext* aPresContext, nsFrameManager* aFrameManager) |
michael@0 | 539 | { |
michael@0 | 540 | if (nsFrame::GetVerifyStyleTreeEnable()) { |
michael@0 | 541 | nsIFrame* rootFrame = aFrameManager->GetRootFrame(); |
michael@0 | 542 | aPresContext->RestyleManager()->DebugVerifyStyleTree(rootFrame); |
michael@0 | 543 | } |
michael@0 | 544 | } |
michael@0 | 545 | #define VERIFY_STYLE_TREE ::VerifyStyleTree(mPresContext, mFrameConstructor) |
michael@0 | 546 | #else |
michael@0 | 547 | #define VERIFY_STYLE_TREE |
michael@0 | 548 | #endif |
michael@0 | 549 | |
michael@0 | 550 | static bool gVerifyReflowEnabled; |
michael@0 | 551 | |
michael@0 | 552 | bool |
michael@0 | 553 | nsIPresShell::GetVerifyReflowEnable() |
michael@0 | 554 | { |
michael@0 | 555 | #ifdef DEBUG |
michael@0 | 556 | static bool firstTime = true; |
michael@0 | 557 | if (firstTime) { |
michael@0 | 558 | firstTime = false; |
michael@0 | 559 | char* flags = PR_GetEnv("GECKO_VERIFY_REFLOW_FLAGS"); |
michael@0 | 560 | if (flags) { |
michael@0 | 561 | bool error = false; |
michael@0 | 562 | |
michael@0 | 563 | for (;;) { |
michael@0 | 564 | char* comma = PL_strchr(flags, ','); |
michael@0 | 565 | if (comma) |
michael@0 | 566 | *comma = '\0'; |
michael@0 | 567 | |
michael@0 | 568 | bool found = false; |
michael@0 | 569 | const VerifyReflowFlags* flag = gFlags; |
michael@0 | 570 | const VerifyReflowFlags* limit = gFlags + NUM_VERIFY_REFLOW_FLAGS; |
michael@0 | 571 | while (flag < limit) { |
michael@0 | 572 | if (PL_strcasecmp(flag->name, flags) == 0) { |
michael@0 | 573 | gVerifyReflowFlags |= flag->bit; |
michael@0 | 574 | found = true; |
michael@0 | 575 | break; |
michael@0 | 576 | } |
michael@0 | 577 | ++flag; |
michael@0 | 578 | } |
michael@0 | 579 | |
michael@0 | 580 | if (! found) |
michael@0 | 581 | error = true; |
michael@0 | 582 | |
michael@0 | 583 | if (! comma) |
michael@0 | 584 | break; |
michael@0 | 585 | |
michael@0 | 586 | *comma = ','; |
michael@0 | 587 | flags = comma + 1; |
michael@0 | 588 | } |
michael@0 | 589 | |
michael@0 | 590 | if (error) |
michael@0 | 591 | ShowVerifyReflowFlags(); |
michael@0 | 592 | } |
michael@0 | 593 | |
michael@0 | 594 | if (VERIFY_REFLOW_ON & gVerifyReflowFlags) { |
michael@0 | 595 | gVerifyReflowEnabled = true; |
michael@0 | 596 | |
michael@0 | 597 | printf("Note: verifyreflow is enabled"); |
michael@0 | 598 | if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) { |
michael@0 | 599 | printf(" (noisy)"); |
michael@0 | 600 | } |
michael@0 | 601 | if (VERIFY_REFLOW_ALL & gVerifyReflowFlags) { |
michael@0 | 602 | printf(" (all)"); |
michael@0 | 603 | } |
michael@0 | 604 | if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) { |
michael@0 | 605 | printf(" (show reflow commands)"); |
michael@0 | 606 | } |
michael@0 | 607 | if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) { |
michael@0 | 608 | printf(" (noisy reflow commands)"); |
michael@0 | 609 | if (VERIFY_REFLOW_REALLY_NOISY_RC & gVerifyReflowFlags) { |
michael@0 | 610 | printf(" (REALLY noisy reflow commands)"); |
michael@0 | 611 | } |
michael@0 | 612 | } |
michael@0 | 613 | printf("\n"); |
michael@0 | 614 | } |
michael@0 | 615 | } |
michael@0 | 616 | #endif |
michael@0 | 617 | return gVerifyReflowEnabled; |
michael@0 | 618 | } |
michael@0 | 619 | |
michael@0 | 620 | void |
michael@0 | 621 | PresShell::AddInvalidateHiddenPresShellObserver(nsRefreshDriver *aDriver) |
michael@0 | 622 | { |
michael@0 | 623 | if (!mHiddenInvalidationObserverRefreshDriver && !mIsDestroying && !mHaveShutDown) { |
michael@0 | 624 | aDriver->AddPresShellToInvalidateIfHidden(this); |
michael@0 | 625 | mHiddenInvalidationObserverRefreshDriver = aDriver; |
michael@0 | 626 | } |
michael@0 | 627 | } |
michael@0 | 628 | |
michael@0 | 629 | void |
michael@0 | 630 | nsIPresShell::InvalidatePresShellIfHidden() |
michael@0 | 631 | { |
michael@0 | 632 | if (!IsVisible() && mPresContext) { |
michael@0 | 633 | mPresContext->NotifyInvalidation(0); |
michael@0 | 634 | } |
michael@0 | 635 | mHiddenInvalidationObserverRefreshDriver = nullptr; |
michael@0 | 636 | } |
michael@0 | 637 | |
michael@0 | 638 | void |
michael@0 | 639 | nsIPresShell::CancelInvalidatePresShellIfHidden() |
michael@0 | 640 | { |
michael@0 | 641 | if (mHiddenInvalidationObserverRefreshDriver) { |
michael@0 | 642 | mHiddenInvalidationObserverRefreshDriver->RemovePresShellToInvalidateIfHidden(this); |
michael@0 | 643 | mHiddenInvalidationObserverRefreshDriver = nullptr; |
michael@0 | 644 | } |
michael@0 | 645 | } |
michael@0 | 646 | |
michael@0 | 647 | void |
michael@0 | 648 | nsIPresShell::SetVerifyReflowEnable(bool aEnabled) |
michael@0 | 649 | { |
michael@0 | 650 | gVerifyReflowEnabled = aEnabled; |
michael@0 | 651 | } |
michael@0 | 652 | |
michael@0 | 653 | /* virtual */ void |
michael@0 | 654 | nsIPresShell::AddWeakFrameExternal(nsWeakFrame* aWeakFrame) |
michael@0 | 655 | { |
michael@0 | 656 | AddWeakFrameInternal(aWeakFrame); |
michael@0 | 657 | } |
michael@0 | 658 | |
michael@0 | 659 | void |
michael@0 | 660 | nsIPresShell::AddWeakFrameInternal(nsWeakFrame* aWeakFrame) |
michael@0 | 661 | { |
michael@0 | 662 | if (aWeakFrame->GetFrame()) { |
michael@0 | 663 | aWeakFrame->GetFrame()->AddStateBits(NS_FRAME_EXTERNAL_REFERENCE); |
michael@0 | 664 | } |
michael@0 | 665 | aWeakFrame->SetPreviousWeakFrame(mWeakFrames); |
michael@0 | 666 | mWeakFrames = aWeakFrame; |
michael@0 | 667 | } |
michael@0 | 668 | |
michael@0 | 669 | /* virtual */ void |
michael@0 | 670 | nsIPresShell::RemoveWeakFrameExternal(nsWeakFrame* aWeakFrame) |
michael@0 | 671 | { |
michael@0 | 672 | RemoveWeakFrameInternal(aWeakFrame); |
michael@0 | 673 | } |
michael@0 | 674 | |
michael@0 | 675 | void |
michael@0 | 676 | nsIPresShell::RemoveWeakFrameInternal(nsWeakFrame* aWeakFrame) |
michael@0 | 677 | { |
michael@0 | 678 | if (mWeakFrames == aWeakFrame) { |
michael@0 | 679 | mWeakFrames = aWeakFrame->GetPreviousWeakFrame(); |
michael@0 | 680 | return; |
michael@0 | 681 | } |
michael@0 | 682 | nsWeakFrame* nextWeak = mWeakFrames; |
michael@0 | 683 | while (nextWeak && nextWeak->GetPreviousWeakFrame() != aWeakFrame) { |
michael@0 | 684 | nextWeak = nextWeak->GetPreviousWeakFrame(); |
michael@0 | 685 | } |
michael@0 | 686 | if (nextWeak) { |
michael@0 | 687 | nextWeak->SetPreviousWeakFrame(aWeakFrame->GetPreviousWeakFrame()); |
michael@0 | 688 | } |
michael@0 | 689 | } |
michael@0 | 690 | |
michael@0 | 691 | already_AddRefed<nsFrameSelection> |
michael@0 | 692 | nsIPresShell::FrameSelection() |
michael@0 | 693 | { |
michael@0 | 694 | nsRefPtr<nsFrameSelection> ret = mSelection; |
michael@0 | 695 | return ret.forget(); |
michael@0 | 696 | } |
michael@0 | 697 | |
michael@0 | 698 | //---------------------------------------------------------------------- |
michael@0 | 699 | |
michael@0 | 700 | static bool sSynthMouseMove = true; |
michael@0 | 701 | static uint32_t sNextPresShellId; |
michael@0 | 702 | static bool sPointerEventEnabled = true; |
michael@0 | 703 | |
michael@0 | 704 | PresShell::PresShell() |
michael@0 | 705 | : mMouseLocation(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) |
michael@0 | 706 | { |
michael@0 | 707 | mSelection = nullptr; |
michael@0 | 708 | #ifdef MOZ_REFLOW_PERF |
michael@0 | 709 | mReflowCountMgr = new ReflowCountMgr(); |
michael@0 | 710 | mReflowCountMgr->SetPresContext(mPresContext); |
michael@0 | 711 | mReflowCountMgr->SetPresShell(this); |
michael@0 | 712 | #endif |
michael@0 | 713 | #ifdef PR_LOGGING |
michael@0 | 714 | mLoadBegin = TimeStamp::Now(); |
michael@0 | 715 | if (!gLog) { |
michael@0 | 716 | gLog = PR_NewLogModule("PresShell"); |
michael@0 | 717 | } |
michael@0 | 718 | #endif |
michael@0 | 719 | mSelectionFlags = nsISelectionDisplay::DISPLAY_TEXT | nsISelectionDisplay::DISPLAY_IMAGES; |
michael@0 | 720 | mIsThemeSupportDisabled = false; |
michael@0 | 721 | mIsActive = true; |
michael@0 | 722 | // FIXME/bug 735029: find a better solution to this problem |
michael@0 | 723 | #ifdef MOZ_ANDROID_OMTC |
michael@0 | 724 | // The java pan/zoom code uses this to mean approximately "request a |
michael@0 | 725 | // reset of pan/zoom state" which doesn't necessarily correspond |
michael@0 | 726 | // with the first paint of content. |
michael@0 | 727 | mIsFirstPaint = false; |
michael@0 | 728 | #else |
michael@0 | 729 | mIsFirstPaint = true; |
michael@0 | 730 | #endif |
michael@0 | 731 | mPresShellId = sNextPresShellId++; |
michael@0 | 732 | mFrozen = false; |
michael@0 | 733 | #ifdef DEBUG |
michael@0 | 734 | mPresArenaAllocCount = 0; |
michael@0 | 735 | #endif |
michael@0 | 736 | mRenderFlags = 0; |
michael@0 | 737 | mXResolution = 1.0; |
michael@0 | 738 | mYResolution = 1.0; |
michael@0 | 739 | mViewportOverridden = false; |
michael@0 | 740 | |
michael@0 | 741 | mScrollPositionClampingScrollPortSizeSet = false; |
michael@0 | 742 | |
michael@0 | 743 | mMaxLineBoxWidth = 0; |
michael@0 | 744 | |
michael@0 | 745 | static bool addedSynthMouseMove = false; |
michael@0 | 746 | if (!addedSynthMouseMove) { |
michael@0 | 747 | Preferences::AddBoolVarCache(&sSynthMouseMove, |
michael@0 | 748 | "layout.reflow.synthMouseMove", true); |
michael@0 | 749 | addedSynthMouseMove = true; |
michael@0 | 750 | } |
michael@0 | 751 | static bool addedPointerEventEnabled = false; |
michael@0 | 752 | if (!addedPointerEventEnabled) { |
michael@0 | 753 | Preferences::AddBoolVarCache(&sPointerEventEnabled, |
michael@0 | 754 | "dom.w3c_pointer_events.enabled", true); |
michael@0 | 755 | addedPointerEventEnabled = true; |
michael@0 | 756 | } |
michael@0 | 757 | |
michael@0 | 758 | mPaintingIsFrozen = false; |
michael@0 | 759 | } |
michael@0 | 760 | |
michael@0 | 761 | NS_IMPL_ISUPPORTS(PresShell, nsIPresShell, nsIDocumentObserver, |
michael@0 | 762 | nsISelectionController, |
michael@0 | 763 | nsISelectionDisplay, nsIObserver, nsISupportsWeakReference, |
michael@0 | 764 | nsIMutationObserver) |
michael@0 | 765 | |
michael@0 | 766 | PresShell::~PresShell() |
michael@0 | 767 | { |
michael@0 | 768 | if (!mHaveShutDown) { |
michael@0 | 769 | NS_NOTREACHED("Someone did not call nsIPresShell::destroy"); |
michael@0 | 770 | Destroy(); |
michael@0 | 771 | } |
michael@0 | 772 | |
michael@0 | 773 | NS_ASSERTION(mCurrentEventContentStack.Count() == 0, |
michael@0 | 774 | "Huh, event content left on the stack in pres shell dtor!"); |
michael@0 | 775 | NS_ASSERTION(mFirstCallbackEventRequest == nullptr && |
michael@0 | 776 | mLastCallbackEventRequest == nullptr, |
michael@0 | 777 | "post-reflow queues not empty. This means we're leaking"); |
michael@0 | 778 | |
michael@0 | 779 | // Verify that if painting was frozen, but we're being removed from the tree, |
michael@0 | 780 | // that we now re-enable painting on our refresh driver, since it may need to |
michael@0 | 781 | // be re-used by another presentation. |
michael@0 | 782 | if (mPaintingIsFrozen) { |
michael@0 | 783 | mPresContext->RefreshDriver()->Thaw(); |
michael@0 | 784 | } |
michael@0 | 785 | |
michael@0 | 786 | #ifdef DEBUG |
michael@0 | 787 | MOZ_ASSERT(mPresArenaAllocCount == 0, |
michael@0 | 788 | "Some pres arena objects were not freed"); |
michael@0 | 789 | #endif |
michael@0 | 790 | |
michael@0 | 791 | delete mStyleSet; |
michael@0 | 792 | delete mFrameConstructor; |
michael@0 | 793 | |
michael@0 | 794 | mCurrentEventContent = nullptr; |
michael@0 | 795 | |
michael@0 | 796 | NS_IF_RELEASE(mPresContext); |
michael@0 | 797 | NS_IF_RELEASE(mDocument); |
michael@0 | 798 | NS_IF_RELEASE(mSelection); |
michael@0 | 799 | } |
michael@0 | 800 | |
michael@0 | 801 | /** |
michael@0 | 802 | * Initialize the presentation shell. Create view manager and style |
michael@0 | 803 | * manager. |
michael@0 | 804 | * Note this can't be merged into our constructor because caret initialization |
michael@0 | 805 | * calls AddRef() on us. |
michael@0 | 806 | */ |
michael@0 | 807 | void |
michael@0 | 808 | PresShell::Init(nsIDocument* aDocument, |
michael@0 | 809 | nsPresContext* aPresContext, |
michael@0 | 810 | nsViewManager* aViewManager, |
michael@0 | 811 | nsStyleSet* aStyleSet, |
michael@0 | 812 | nsCompatibility aCompatMode) |
michael@0 | 813 | { |
michael@0 | 814 | NS_PRECONDITION(aDocument, "null ptr"); |
michael@0 | 815 | NS_PRECONDITION(aPresContext, "null ptr"); |
michael@0 | 816 | NS_PRECONDITION(aViewManager, "null ptr"); |
michael@0 | 817 | NS_PRECONDITION(!mDocument, "already initialized"); |
michael@0 | 818 | |
michael@0 | 819 | if (!aDocument || !aPresContext || !aViewManager || mDocument) { |
michael@0 | 820 | return; |
michael@0 | 821 | } |
michael@0 | 822 | |
michael@0 | 823 | mDocument = aDocument; |
michael@0 | 824 | NS_ADDREF(mDocument); |
michael@0 | 825 | mViewManager = aViewManager; |
michael@0 | 826 | |
michael@0 | 827 | // Create our frame constructor. |
michael@0 | 828 | mFrameConstructor = new nsCSSFrameConstructor(mDocument, this, aStyleSet); |
michael@0 | 829 | |
michael@0 | 830 | mFrameManager = mFrameConstructor; |
michael@0 | 831 | |
michael@0 | 832 | // The document viewer owns both view manager and pres shell. |
michael@0 | 833 | mViewManager->SetPresShell(this); |
michael@0 | 834 | |
michael@0 | 835 | // Bind the context to the presentation shell. |
michael@0 | 836 | mPresContext = aPresContext; |
michael@0 | 837 | NS_ADDREF(mPresContext); |
michael@0 | 838 | aPresContext->SetShell(this); |
michael@0 | 839 | |
michael@0 | 840 | // Now we can initialize the style set. |
michael@0 | 841 | aStyleSet->Init(aPresContext); |
michael@0 | 842 | mStyleSet = aStyleSet; |
michael@0 | 843 | |
michael@0 | 844 | // Notify our prescontext that it now has a compatibility mode. Note that |
michael@0 | 845 | // this MUST happen after we set up our style set but before we create any |
michael@0 | 846 | // frames. |
michael@0 | 847 | mPresContext->CompatibilityModeChanged(); |
michael@0 | 848 | |
michael@0 | 849 | // setup the preference style rules (no forced reflow), and do it |
michael@0 | 850 | // before creating any frames. |
michael@0 | 851 | SetPreferenceStyleRules(false); |
michael@0 | 852 | |
michael@0 | 853 | NS_ADDREF(mSelection = new nsFrameSelection()); |
michael@0 | 854 | |
michael@0 | 855 | mSelection->Init(this, nullptr); |
michael@0 | 856 | |
michael@0 | 857 | // Important: this has to happen after the selection has been set up |
michael@0 | 858 | #ifdef SHOW_CARET |
michael@0 | 859 | // make the caret |
michael@0 | 860 | mCaret = new nsCaret(); |
michael@0 | 861 | mCaret->Init(this); |
michael@0 | 862 | mOriginalCaret = mCaret; |
michael@0 | 863 | |
michael@0 | 864 | //SetCaretEnabled(true); // make it show in browser windows |
michael@0 | 865 | #endif |
michael@0 | 866 | //set up selection to be displayed in document |
michael@0 | 867 | // Don't enable selection for print media |
michael@0 | 868 | nsPresContext::nsPresContextType type = aPresContext->Type(); |
michael@0 | 869 | if (type != nsPresContext::eContext_PrintPreview && |
michael@0 | 870 | type != nsPresContext::eContext_Print) |
michael@0 | 871 | SetDisplaySelection(nsISelectionController::SELECTION_DISABLED); |
michael@0 | 872 | |
michael@0 | 873 | if (gMaxRCProcessingTime == -1) { |
michael@0 | 874 | gMaxRCProcessingTime = |
michael@0 | 875 | Preferences::GetInt("layout.reflow.timeslice", NS_MAX_REFLOW_TIME); |
michael@0 | 876 | } |
michael@0 | 877 | |
michael@0 | 878 | { |
michael@0 | 879 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
michael@0 | 880 | if (os) { |
michael@0 | 881 | os->AddObserver(this, "agent-sheet-added", false); |
michael@0 | 882 | os->AddObserver(this, "user-sheet-added", false); |
michael@0 | 883 | os->AddObserver(this, "author-sheet-added", false); |
michael@0 | 884 | os->AddObserver(this, "agent-sheet-removed", false); |
michael@0 | 885 | os->AddObserver(this, "user-sheet-removed", false); |
michael@0 | 886 | os->AddObserver(this, "author-sheet-removed", false); |
michael@0 | 887 | #ifdef MOZ_XUL |
michael@0 | 888 | os->AddObserver(this, "chrome-flush-skin-caches", false); |
michael@0 | 889 | #endif |
michael@0 | 890 | } |
michael@0 | 891 | } |
michael@0 | 892 | |
michael@0 | 893 | #ifdef MOZ_REFLOW_PERF |
michael@0 | 894 | if (mReflowCountMgr) { |
michael@0 | 895 | bool paintFrameCounts = |
michael@0 | 896 | Preferences::GetBool("layout.reflow.showframecounts"); |
michael@0 | 897 | |
michael@0 | 898 | bool dumpFrameCounts = |
michael@0 | 899 | Preferences::GetBool("layout.reflow.dumpframecounts"); |
michael@0 | 900 | |
michael@0 | 901 | bool dumpFrameByFrameCounts = |
michael@0 | 902 | Preferences::GetBool("layout.reflow.dumpframebyframecounts"); |
michael@0 | 903 | |
michael@0 | 904 | mReflowCountMgr->SetDumpFrameCounts(dumpFrameCounts); |
michael@0 | 905 | mReflowCountMgr->SetDumpFrameByFrameCounts(dumpFrameByFrameCounts); |
michael@0 | 906 | mReflowCountMgr->SetPaintFrameCounts(paintFrameCounts); |
michael@0 | 907 | } |
michael@0 | 908 | #endif |
michael@0 | 909 | |
michael@0 | 910 | if (mDocument->HasAnimationController()) { |
michael@0 | 911 | nsSMILAnimationController* animCtrl = mDocument->GetAnimationController(); |
michael@0 | 912 | animCtrl->NotifyRefreshDriverCreated(GetPresContext()->RefreshDriver()); |
michael@0 | 913 | } |
michael@0 | 914 | |
michael@0 | 915 | // Get our activeness from the docShell. |
michael@0 | 916 | QueryIsActive(); |
michael@0 | 917 | |
michael@0 | 918 | // Setup our font inflation preferences. |
michael@0 | 919 | SetupFontInflation(); |
michael@0 | 920 | } |
michael@0 | 921 | |
michael@0 | 922 | #ifdef PR_LOGGING |
michael@0 | 923 | enum TextPerfLogType { |
michael@0 | 924 | eLog_reflow, |
michael@0 | 925 | eLog_loaddone, |
michael@0 | 926 | eLog_totals |
michael@0 | 927 | }; |
michael@0 | 928 | |
michael@0 | 929 | static void |
michael@0 | 930 | LogTextPerfStats(gfxTextPerfMetrics* aTextPerf, |
michael@0 | 931 | PresShell* aPresShell, |
michael@0 | 932 | const gfxTextPerfMetrics::TextCounts& aCounts, |
michael@0 | 933 | float aTime, TextPerfLogType aLogType, const char* aURL) |
michael@0 | 934 | { |
michael@0 | 935 | char prefix[256]; |
michael@0 | 936 | |
michael@0 | 937 | switch (aLogType) { |
michael@0 | 938 | case eLog_reflow: |
michael@0 | 939 | sprintf(prefix, "(textperf-reflow) %p time-ms: %7.0f", aPresShell, aTime); |
michael@0 | 940 | break; |
michael@0 | 941 | case eLog_loaddone: |
michael@0 | 942 | sprintf(prefix, "(textperf-loaddone) %p time-ms: %7.0f", aPresShell, aTime); |
michael@0 | 943 | break; |
michael@0 | 944 | default: |
michael@0 | 945 | MOZ_ASSERT(aLogType == eLog_totals, "unknown textperf log type"); |
michael@0 | 946 | sprintf(prefix, "(textperf-totals) %p", aPresShell); |
michael@0 | 947 | } |
michael@0 | 948 | |
michael@0 | 949 | PRLogModuleInfo* tpLog = gfxPlatform::GetLog(eGfxLog_textperf); |
michael@0 | 950 | |
michael@0 | 951 | // ignore XUL contexts unless at debug level |
michael@0 | 952 | PRLogModuleLevel logLevel = PR_LOG_WARNING; |
michael@0 | 953 | if (aCounts.numContentTextRuns == 0) { |
michael@0 | 954 | logLevel = PR_LOG_DEBUG; |
michael@0 | 955 | } |
michael@0 | 956 | |
michael@0 | 957 | double hitRatio = 0.0; |
michael@0 | 958 | uint32_t lookups = aCounts.wordCacheHit + aCounts.wordCacheMiss; |
michael@0 | 959 | if (lookups) { |
michael@0 | 960 | hitRatio = double(aCounts.wordCacheHit) / double(lookups); |
michael@0 | 961 | } |
michael@0 | 962 | |
michael@0 | 963 | if (aLogType == eLog_loaddone) { |
michael@0 | 964 | PR_LOG(tpLog, logLevel, |
michael@0 | 965 | ("%s reflow: %d chars: %d " |
michael@0 | 966 | "[%s] " |
michael@0 | 967 | "content-textruns: %d chrome-textruns: %d " |
michael@0 | 968 | "max-textrun-len: %d " |
michael@0 | 969 | "word-cache-lookups: %d word-cache-hit-ratio: %4.3f " |
michael@0 | 970 | "word-cache-space: %d word-cache-long: %d " |
michael@0 | 971 | "pref-fallbacks: %d system-fallbacks: %d " |
michael@0 | 972 | "textruns-const: %d textruns-destr: %d " |
michael@0 | 973 | "cumulative-textruns-destr: %d\n", |
michael@0 | 974 | prefix, aTextPerf->reflowCount, aCounts.numChars, |
michael@0 | 975 | (aURL ? aURL : ""), |
michael@0 | 976 | aCounts.numContentTextRuns, aCounts.numChromeTextRuns, |
michael@0 | 977 | aCounts.maxTextRunLen, |
michael@0 | 978 | lookups, hitRatio, |
michael@0 | 979 | aCounts.wordCacheSpaceRules, aCounts.wordCacheLong, |
michael@0 | 980 | aCounts.fallbackPrefs, aCounts.fallbackSystem, |
michael@0 | 981 | aCounts.textrunConst, aCounts.textrunDestr, |
michael@0 | 982 | aTextPerf->cumulative.textrunDestr)); |
michael@0 | 983 | } else { |
michael@0 | 984 | PR_LOG(tpLog, logLevel, |
michael@0 | 985 | ("%s reflow: %d chars: %d " |
michael@0 | 986 | "content-textruns: %d chrome-textruns: %d " |
michael@0 | 987 | "max-textrun-len: %d " |
michael@0 | 988 | "word-cache-lookups: %d word-cache-hit-ratio: %4.3f " |
michael@0 | 989 | "word-cache-space: %d word-cache-long: %d " |
michael@0 | 990 | "pref-fallbacks: %d system-fallbacks: %d " |
michael@0 | 991 | "textruns-const: %d textruns-destr: %d " |
michael@0 | 992 | "cumulative-textruns-destr: %d\n", |
michael@0 | 993 | prefix, aTextPerf->reflowCount, aCounts.numChars, |
michael@0 | 994 | aCounts.numContentTextRuns, aCounts.numChromeTextRuns, |
michael@0 | 995 | aCounts.maxTextRunLen, |
michael@0 | 996 | lookups, hitRatio, |
michael@0 | 997 | aCounts.wordCacheSpaceRules, aCounts.wordCacheLong, |
michael@0 | 998 | aCounts.fallbackPrefs, aCounts.fallbackSystem, |
michael@0 | 999 | aCounts.textrunConst, aCounts.textrunDestr, |
michael@0 | 1000 | aTextPerf->cumulative.textrunDestr)); |
michael@0 | 1001 | } |
michael@0 | 1002 | } |
michael@0 | 1003 | #endif |
michael@0 | 1004 | |
michael@0 | 1005 | void |
michael@0 | 1006 | PresShell::Destroy() |
michael@0 | 1007 | { |
michael@0 | 1008 | NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), |
michael@0 | 1009 | "destroy called on presshell while scripts not blocked"); |
michael@0 | 1010 | |
michael@0 | 1011 | // dump out cumulative text perf metrics |
michael@0 | 1012 | #ifdef PR_LOGGING |
michael@0 | 1013 | gfxTextPerfMetrics* tp; |
michael@0 | 1014 | if (mPresContext && (tp = mPresContext->GetTextPerfMetrics())) { |
michael@0 | 1015 | tp->Accumulate(); |
michael@0 | 1016 | if (tp->cumulative.numChars > 0) { |
michael@0 | 1017 | LogTextPerfStats(tp, this, tp->cumulative, 0.0, eLog_totals, nullptr); |
michael@0 | 1018 | } |
michael@0 | 1019 | } |
michael@0 | 1020 | #endif |
michael@0 | 1021 | |
michael@0 | 1022 | #ifdef MOZ_REFLOW_PERF |
michael@0 | 1023 | DumpReflows(); |
michael@0 | 1024 | if (mReflowCountMgr) { |
michael@0 | 1025 | delete mReflowCountMgr; |
michael@0 | 1026 | mReflowCountMgr = nullptr; |
michael@0 | 1027 | } |
michael@0 | 1028 | #endif |
michael@0 | 1029 | |
michael@0 | 1030 | if (mHaveShutDown) |
michael@0 | 1031 | return; |
michael@0 | 1032 | |
michael@0 | 1033 | #ifdef ACCESSIBILITY |
michael@0 | 1034 | if (mDocAccessible) { |
michael@0 | 1035 | #ifdef DEBUG |
michael@0 | 1036 | if (a11y::logging::IsEnabled(a11y::logging::eDocDestroy)) |
michael@0 | 1037 | a11y::logging::DocDestroy("presshell destroyed", mDocument); |
michael@0 | 1038 | #endif |
michael@0 | 1039 | |
michael@0 | 1040 | mDocAccessible->Shutdown(); |
michael@0 | 1041 | mDocAccessible = nullptr; |
michael@0 | 1042 | } |
michael@0 | 1043 | #endif // ACCESSIBILITY |
michael@0 | 1044 | |
michael@0 | 1045 | MaybeReleaseCapturingContent(); |
michael@0 | 1046 | |
michael@0 | 1047 | if (gKeyDownTarget && gKeyDownTarget->OwnerDoc() == mDocument) { |
michael@0 | 1048 | NS_RELEASE(gKeyDownTarget); |
michael@0 | 1049 | } |
michael@0 | 1050 | |
michael@0 | 1051 | if (mContentToScrollTo) { |
michael@0 | 1052 | mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling); |
michael@0 | 1053 | mContentToScrollTo = nullptr; |
michael@0 | 1054 | } |
michael@0 | 1055 | |
michael@0 | 1056 | if (mPresContext) { |
michael@0 | 1057 | // We need to notify the destroying the nsPresContext to ESM for |
michael@0 | 1058 | // suppressing to use from ESM. |
michael@0 | 1059 | mPresContext->EventStateManager()->NotifyDestroyPresContext(mPresContext); |
michael@0 | 1060 | } |
michael@0 | 1061 | |
michael@0 | 1062 | { |
michael@0 | 1063 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
michael@0 | 1064 | if (os) { |
michael@0 | 1065 | os->RemoveObserver(this, "agent-sheet-added"); |
michael@0 | 1066 | os->RemoveObserver(this, "user-sheet-added"); |
michael@0 | 1067 | os->RemoveObserver(this, "author-sheet-added"); |
michael@0 | 1068 | os->RemoveObserver(this, "agent-sheet-removed"); |
michael@0 | 1069 | os->RemoveObserver(this, "user-sheet-removed"); |
michael@0 | 1070 | os->RemoveObserver(this, "author-sheet-removed"); |
michael@0 | 1071 | #ifdef MOZ_XUL |
michael@0 | 1072 | os->RemoveObserver(this, "chrome-flush-skin-caches"); |
michael@0 | 1073 | #endif |
michael@0 | 1074 | } |
michael@0 | 1075 | } |
michael@0 | 1076 | |
michael@0 | 1077 | // If our paint suppression timer is still active, kill it. |
michael@0 | 1078 | if (mPaintSuppressionTimer) { |
michael@0 | 1079 | mPaintSuppressionTimer->Cancel(); |
michael@0 | 1080 | mPaintSuppressionTimer = nullptr; |
michael@0 | 1081 | } |
michael@0 | 1082 | |
michael@0 | 1083 | // Same for our reflow continuation timer |
michael@0 | 1084 | if (mReflowContinueTimer) { |
michael@0 | 1085 | mReflowContinueTimer->Cancel(); |
michael@0 | 1086 | mReflowContinueTimer = nullptr; |
michael@0 | 1087 | } |
michael@0 | 1088 | |
michael@0 | 1089 | if (mDelayedPaintTimer) { |
michael@0 | 1090 | mDelayedPaintTimer->Cancel(); |
michael@0 | 1091 | mDelayedPaintTimer = nullptr; |
michael@0 | 1092 | } |
michael@0 | 1093 | |
michael@0 | 1094 | mSynthMouseMoveEvent.Revoke(); |
michael@0 | 1095 | |
michael@0 | 1096 | mUpdateImageVisibilityEvent.Revoke(); |
michael@0 | 1097 | |
michael@0 | 1098 | ClearVisibleImagesList(); |
michael@0 | 1099 | |
michael@0 | 1100 | if (mCaret) { |
michael@0 | 1101 | mCaret->Terminate(); |
michael@0 | 1102 | mCaret = nullptr; |
michael@0 | 1103 | } |
michael@0 | 1104 | |
michael@0 | 1105 | if (mSelection) { |
michael@0 | 1106 | mSelection->DisconnectFromPresShell(); |
michael@0 | 1107 | } |
michael@0 | 1108 | |
michael@0 | 1109 | // release our pref style sheet, if we have one still |
michael@0 | 1110 | ClearPreferenceStyleRules(); |
michael@0 | 1111 | |
michael@0 | 1112 | mIsDestroying = true; |
michael@0 | 1113 | |
michael@0 | 1114 | // We can't release all the event content in |
michael@0 | 1115 | // mCurrentEventContentStack here since there might be code on the |
michael@0 | 1116 | // stack that will release the event content too. Double release |
michael@0 | 1117 | // bad! |
michael@0 | 1118 | |
michael@0 | 1119 | // The frames will be torn down, so remove them from the current |
michael@0 | 1120 | // event frame stack (since they'd be dangling references if we'd |
michael@0 | 1121 | // leave them in) and null out the mCurrentEventFrame pointer as |
michael@0 | 1122 | // well. |
michael@0 | 1123 | |
michael@0 | 1124 | mCurrentEventFrame = nullptr; |
michael@0 | 1125 | |
michael@0 | 1126 | int32_t i, count = mCurrentEventFrameStack.Length(); |
michael@0 | 1127 | for (i = 0; i < count; i++) { |
michael@0 | 1128 | mCurrentEventFrameStack[i] = nullptr; |
michael@0 | 1129 | } |
michael@0 | 1130 | |
michael@0 | 1131 | mFramesToDirty.Clear(); |
michael@0 | 1132 | |
michael@0 | 1133 | if (mViewManager) { |
michael@0 | 1134 | // Clear the view manager's weak pointer back to |this| in case it |
michael@0 | 1135 | // was leaked. |
michael@0 | 1136 | mViewManager->SetPresShell(nullptr); |
michael@0 | 1137 | mViewManager = nullptr; |
michael@0 | 1138 | } |
michael@0 | 1139 | |
michael@0 | 1140 | mStyleSet->BeginShutdown(mPresContext); |
michael@0 | 1141 | nsRefreshDriver* rd = GetPresContext()->RefreshDriver(); |
michael@0 | 1142 | |
michael@0 | 1143 | // This shell must be removed from the document before the frame |
michael@0 | 1144 | // hierarchy is torn down to avoid finding deleted frames through |
michael@0 | 1145 | // this presshell while the frames are being torn down |
michael@0 | 1146 | if (mDocument) { |
michael@0 | 1147 | NS_ASSERTION(mDocument->GetShell() == this, "Wrong shell?"); |
michael@0 | 1148 | mDocument->DeleteShell(); |
michael@0 | 1149 | |
michael@0 | 1150 | if (mDocument->HasAnimationController()) { |
michael@0 | 1151 | mDocument->GetAnimationController()->NotifyRefreshDriverDestroying(rd); |
michael@0 | 1152 | } |
michael@0 | 1153 | } |
michael@0 | 1154 | |
michael@0 | 1155 | // Revoke any pending events. We need to do this and cancel pending reflows |
michael@0 | 1156 | // before we destroy the frame manager, since apparently frame destruction |
michael@0 | 1157 | // sometimes spins the event queue when plug-ins are involved(!). |
michael@0 | 1158 | rd->RemoveLayoutFlushObserver(this); |
michael@0 | 1159 | if (mHiddenInvalidationObserverRefreshDriver) { |
michael@0 | 1160 | mHiddenInvalidationObserverRefreshDriver->RemovePresShellToInvalidateIfHidden(this); |
michael@0 | 1161 | } |
michael@0 | 1162 | |
michael@0 | 1163 | if (rd->PresContext() == GetPresContext()) { |
michael@0 | 1164 | rd->RevokeViewManagerFlush(); |
michael@0 | 1165 | } |
michael@0 | 1166 | |
michael@0 | 1167 | mResizeEvent.Revoke(); |
michael@0 | 1168 | if (mAsyncResizeTimerIsActive) { |
michael@0 | 1169 | mAsyncResizeEventTimer->Cancel(); |
michael@0 | 1170 | mAsyncResizeTimerIsActive = false; |
michael@0 | 1171 | } |
michael@0 | 1172 | |
michael@0 | 1173 | CancelAllPendingReflows(); |
michael@0 | 1174 | CancelPostedReflowCallbacks(); |
michael@0 | 1175 | |
michael@0 | 1176 | // Destroy the frame manager. This will destroy the frame hierarchy |
michael@0 | 1177 | mFrameConstructor->WillDestroyFrameTree(); |
michael@0 | 1178 | |
michael@0 | 1179 | // Destroy all frame properties (whose destruction was suppressed |
michael@0 | 1180 | // while destroying the frame tree, but which might contain more |
michael@0 | 1181 | // frames within the properties. |
michael@0 | 1182 | if (mPresContext) { |
michael@0 | 1183 | // Clear out the prescontext's property table -- since our frame tree is |
michael@0 | 1184 | // now dead, we shouldn't be looking up any more properties in that table. |
michael@0 | 1185 | // We want to do this before we call SetShell() on the prescontext, so |
michael@0 | 1186 | // property destructors can usefully call GetPresShell() on the |
michael@0 | 1187 | // prescontext. |
michael@0 | 1188 | mPresContext->PropertyTable()->DeleteAll(); |
michael@0 | 1189 | } |
michael@0 | 1190 | |
michael@0 | 1191 | |
michael@0 | 1192 | NS_WARN_IF_FALSE(!mWeakFrames, "Weak frames alive after destroying FrameManager"); |
michael@0 | 1193 | while (mWeakFrames) { |
michael@0 | 1194 | mWeakFrames->Clear(this); |
michael@0 | 1195 | } |
michael@0 | 1196 | |
michael@0 | 1197 | // Let the style set do its cleanup. |
michael@0 | 1198 | mStyleSet->Shutdown(mPresContext); |
michael@0 | 1199 | |
michael@0 | 1200 | if (mPresContext) { |
michael@0 | 1201 | // We hold a reference to the pres context, and it holds a weak link back |
michael@0 | 1202 | // to us. To avoid the pres context having a dangling reference, set its |
michael@0 | 1203 | // pres shell to nullptr |
michael@0 | 1204 | mPresContext->SetShell(nullptr); |
michael@0 | 1205 | |
michael@0 | 1206 | // Clear the link handler (weak reference) as well |
michael@0 | 1207 | mPresContext->SetLinkHandler(nullptr); |
michael@0 | 1208 | } |
michael@0 | 1209 | |
michael@0 | 1210 | mHaveShutDown = true; |
michael@0 | 1211 | |
michael@0 | 1212 | EvictTouches(); |
michael@0 | 1213 | } |
michael@0 | 1214 | |
michael@0 | 1215 | void |
michael@0 | 1216 | PresShell::MakeZombie() |
michael@0 | 1217 | { |
michael@0 | 1218 | mIsZombie = true; |
michael@0 | 1219 | CancelAllPendingReflows(); |
michael@0 | 1220 | } |
michael@0 | 1221 | |
michael@0 | 1222 | void |
michael@0 | 1223 | nsIPresShell::SetAuthorStyleDisabled(bool aStyleDisabled) |
michael@0 | 1224 | { |
michael@0 | 1225 | if (aStyleDisabled != mStyleSet->GetAuthorStyleDisabled()) { |
michael@0 | 1226 | mStyleSet->SetAuthorStyleDisabled(aStyleDisabled); |
michael@0 | 1227 | ReconstructStyleData(); |
michael@0 | 1228 | |
michael@0 | 1229 | nsCOMPtr<nsIObserverService> observerService = |
michael@0 | 1230 | mozilla::services::GetObserverService(); |
michael@0 | 1231 | if (observerService) { |
michael@0 | 1232 | observerService->NotifyObservers(mDocument, |
michael@0 | 1233 | "author-style-disabled-changed", |
michael@0 | 1234 | nullptr); |
michael@0 | 1235 | } |
michael@0 | 1236 | } |
michael@0 | 1237 | } |
michael@0 | 1238 | |
michael@0 | 1239 | bool |
michael@0 | 1240 | nsIPresShell::GetAuthorStyleDisabled() const |
michael@0 | 1241 | { |
michael@0 | 1242 | return mStyleSet->GetAuthorStyleDisabled(); |
michael@0 | 1243 | } |
michael@0 | 1244 | |
michael@0 | 1245 | nsresult |
michael@0 | 1246 | PresShell::SetPreferenceStyleRules(bool aForceReflow) |
michael@0 | 1247 | { |
michael@0 | 1248 | if (!mDocument) { |
michael@0 | 1249 | return NS_ERROR_NULL_POINTER; |
michael@0 | 1250 | } |
michael@0 | 1251 | |
michael@0 | 1252 | nsPIDOMWindow *window = mDocument->GetWindow(); |
michael@0 | 1253 | |
michael@0 | 1254 | // If the document doesn't have a window there's no need to notify |
michael@0 | 1255 | // its presshell about changes to preferences since the document is |
michael@0 | 1256 | // in a state where it doesn't matter any more (see |
michael@0 | 1257 | // nsDocumentViewer::Close()). |
michael@0 | 1258 | |
michael@0 | 1259 | if (!window) { |
michael@0 | 1260 | return NS_ERROR_NULL_POINTER; |
michael@0 | 1261 | } |
michael@0 | 1262 | |
michael@0 | 1263 | NS_PRECONDITION(mPresContext, "presContext cannot be null"); |
michael@0 | 1264 | if (mPresContext) { |
michael@0 | 1265 | // first, make sure this is not a chrome shell |
michael@0 | 1266 | if (nsContentUtils::IsInChromeDocshell(mDocument)) { |
michael@0 | 1267 | return NS_OK; |
michael@0 | 1268 | } |
michael@0 | 1269 | |
michael@0 | 1270 | #ifdef DEBUG_attinasi |
michael@0 | 1271 | printf("Setting Preference Style Rules:\n"); |
michael@0 | 1272 | #endif |
michael@0 | 1273 | // if here, we need to create rules for the prefs |
michael@0 | 1274 | // - this includes the background-color, the text-color, |
michael@0 | 1275 | // the link color, the visited link color and the link-underlining |
michael@0 | 1276 | |
michael@0 | 1277 | // first clear any exising rules |
michael@0 | 1278 | nsresult result = ClearPreferenceStyleRules(); |
michael@0 | 1279 | |
michael@0 | 1280 | // now the link rules (must come after the color rules, or links will not be correct color!) |
michael@0 | 1281 | // XXX - when there is both an override and agent pref stylesheet this won't matter, |
michael@0 | 1282 | // as the color rules will be overrides and the links rules will be agent |
michael@0 | 1283 | if (NS_SUCCEEDED(result)) { |
michael@0 | 1284 | result = SetPrefLinkRules(); |
michael@0 | 1285 | } |
michael@0 | 1286 | if (NS_SUCCEEDED(result)) { |
michael@0 | 1287 | result = SetPrefFocusRules(); |
michael@0 | 1288 | } |
michael@0 | 1289 | if (NS_SUCCEEDED(result)) { |
michael@0 | 1290 | result = SetPrefNoScriptRule(); |
michael@0 | 1291 | } |
michael@0 | 1292 | if (NS_SUCCEEDED(result)) { |
michael@0 | 1293 | result = SetPrefNoFramesRule(); |
michael@0 | 1294 | } |
michael@0 | 1295 | #ifdef DEBUG_attinasi |
michael@0 | 1296 | printf( "Preference Style Rules set: error=%ld\n", (long)result); |
michael@0 | 1297 | #endif |
michael@0 | 1298 | |
michael@0 | 1299 | // Note that this method never needs to force any calculation; the caller |
michael@0 | 1300 | // will recalculate style if needed |
michael@0 | 1301 | |
michael@0 | 1302 | return result; |
michael@0 | 1303 | } |
michael@0 | 1304 | |
michael@0 | 1305 | return NS_ERROR_NULL_POINTER; |
michael@0 | 1306 | } |
michael@0 | 1307 | |
michael@0 | 1308 | nsresult PresShell::ClearPreferenceStyleRules(void) |
michael@0 | 1309 | { |
michael@0 | 1310 | nsresult result = NS_OK; |
michael@0 | 1311 | if (mPrefStyleSheet) { |
michael@0 | 1312 | NS_ASSERTION(mStyleSet, "null styleset entirely unexpected!"); |
michael@0 | 1313 | if (mStyleSet) { |
michael@0 | 1314 | // remove the sheet from the styleset: |
michael@0 | 1315 | // - note that we have to check for success by comparing the count before and after... |
michael@0 | 1316 | #ifdef DEBUG |
michael@0 | 1317 | int32_t numBefore = mStyleSet->SheetCount(nsStyleSet::eUserSheet); |
michael@0 | 1318 | NS_ASSERTION(numBefore > 0, "no user stylesheets in styleset, but we have one!"); |
michael@0 | 1319 | #endif |
michael@0 | 1320 | mStyleSet->RemoveStyleSheet(nsStyleSet::eUserSheet, mPrefStyleSheet); |
michael@0 | 1321 | |
michael@0 | 1322 | #ifdef DEBUG_attinasi |
michael@0 | 1323 | NS_ASSERTION((numBefore - 1) == mStyleSet->GetNumberOfUserStyleSheets(), |
michael@0 | 1324 | "Pref stylesheet was not removed"); |
michael@0 | 1325 | printf("PrefStyleSheet removed\n"); |
michael@0 | 1326 | #endif |
michael@0 | 1327 | // clear the sheet pointer: it is strictly historical now |
michael@0 | 1328 | mPrefStyleSheet = nullptr; |
michael@0 | 1329 | } |
michael@0 | 1330 | } |
michael@0 | 1331 | return result; |
michael@0 | 1332 | } |
michael@0 | 1333 | |
michael@0 | 1334 | nsresult |
michael@0 | 1335 | PresShell::CreatePreferenceStyleSheet() |
michael@0 | 1336 | { |
michael@0 | 1337 | NS_ASSERTION(!mPrefStyleSheet, "prefStyleSheet already exists"); |
michael@0 | 1338 | mPrefStyleSheet = new nsCSSStyleSheet(CORS_NONE); |
michael@0 | 1339 | nsCOMPtr<nsIURI> uri; |
michael@0 | 1340 | nsresult rv = NS_NewURI(getter_AddRefs(uri), "about:PreferenceStyleSheet", nullptr); |
michael@0 | 1341 | if (NS_FAILED(rv)) { |
michael@0 | 1342 | mPrefStyleSheet = nullptr; |
michael@0 | 1343 | return rv; |
michael@0 | 1344 | } |
michael@0 | 1345 | NS_ASSERTION(uri, "null but no error"); |
michael@0 | 1346 | mPrefStyleSheet->SetURIs(uri, uri, uri); |
michael@0 | 1347 | mPrefStyleSheet->SetComplete(); |
michael@0 | 1348 | uint32_t index; |
michael@0 | 1349 | rv = |
michael@0 | 1350 | mPrefStyleSheet->InsertRuleInternal(NS_LITERAL_STRING("@namespace svg url(http://www.w3.org/2000/svg);"), |
michael@0 | 1351 | 0, &index); |
michael@0 | 1352 | if (NS_FAILED(rv)) { |
michael@0 | 1353 | mPrefStyleSheet = nullptr; |
michael@0 | 1354 | return rv; |
michael@0 | 1355 | } |
michael@0 | 1356 | rv = |
michael@0 | 1357 | mPrefStyleSheet->InsertRuleInternal(NS_LITERAL_STRING("@namespace url(http://www.w3.org/1999/xhtml);"), |
michael@0 | 1358 | 0, &index); |
michael@0 | 1359 | if (NS_FAILED(rv)) { |
michael@0 | 1360 | mPrefStyleSheet = nullptr; |
michael@0 | 1361 | return rv; |
michael@0 | 1362 | } |
michael@0 | 1363 | |
michael@0 | 1364 | mStyleSet->AppendStyleSheet(nsStyleSet::eUserSheet, mPrefStyleSheet); |
michael@0 | 1365 | return NS_OK; |
michael@0 | 1366 | } |
michael@0 | 1367 | |
michael@0 | 1368 | // XXX We want these after the @namespace rules. Does order matter |
michael@0 | 1369 | // for these rules, or can we call StyleRule::StyleRuleCount() |
michael@0 | 1370 | // and just "append"? |
michael@0 | 1371 | static uint32_t sInsertPrefSheetRulesAt = 2; |
michael@0 | 1372 | |
michael@0 | 1373 | nsresult |
michael@0 | 1374 | PresShell::SetPrefNoScriptRule() |
michael@0 | 1375 | { |
michael@0 | 1376 | nsresult rv = NS_OK; |
michael@0 | 1377 | |
michael@0 | 1378 | // also handle the case where print is done from print preview |
michael@0 | 1379 | // see bug #342439 for more details |
michael@0 | 1380 | nsIDocument* doc = mDocument; |
michael@0 | 1381 | if (doc->IsStaticDocument()) { |
michael@0 | 1382 | doc = doc->GetOriginalDocument(); |
michael@0 | 1383 | } |
michael@0 | 1384 | |
michael@0 | 1385 | bool scriptEnabled = doc->IsScriptEnabled(); |
michael@0 | 1386 | if (scriptEnabled) { |
michael@0 | 1387 | if (!mPrefStyleSheet) { |
michael@0 | 1388 | rv = CreatePreferenceStyleSheet(); |
michael@0 | 1389 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1390 | } |
michael@0 | 1391 | |
michael@0 | 1392 | uint32_t index = 0; |
michael@0 | 1393 | mPrefStyleSheet-> |
michael@0 | 1394 | InsertRuleInternal(NS_LITERAL_STRING("noscript{display:none!important}"), |
michael@0 | 1395 | sInsertPrefSheetRulesAt, &index); |
michael@0 | 1396 | } |
michael@0 | 1397 | |
michael@0 | 1398 | return rv; |
michael@0 | 1399 | } |
michael@0 | 1400 | |
michael@0 | 1401 | nsresult PresShell::SetPrefNoFramesRule(void) |
michael@0 | 1402 | { |
michael@0 | 1403 | NS_ASSERTION(mPresContext,"null prescontext not allowed"); |
michael@0 | 1404 | if (!mPresContext) { |
michael@0 | 1405 | return NS_ERROR_FAILURE; |
michael@0 | 1406 | } |
michael@0 | 1407 | |
michael@0 | 1408 | nsresult rv = NS_OK; |
michael@0 | 1409 | |
michael@0 | 1410 | if (!mPrefStyleSheet) { |
michael@0 | 1411 | rv = CreatePreferenceStyleSheet(); |
michael@0 | 1412 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1413 | } |
michael@0 | 1414 | |
michael@0 | 1415 | NS_ASSERTION(mPrefStyleSheet, "prefstylesheet should not be null"); |
michael@0 | 1416 | |
michael@0 | 1417 | bool allowSubframes = true; |
michael@0 | 1418 | nsCOMPtr<nsIDocShell> docShell(mPresContext->GetDocShell()); |
michael@0 | 1419 | if (docShell) { |
michael@0 | 1420 | docShell->GetAllowSubframes(&allowSubframes); |
michael@0 | 1421 | } |
michael@0 | 1422 | if (!allowSubframes) { |
michael@0 | 1423 | uint32_t index = 0; |
michael@0 | 1424 | rv = mPrefStyleSheet-> |
michael@0 | 1425 | InsertRuleInternal(NS_LITERAL_STRING("noframes{display:block}"), |
michael@0 | 1426 | sInsertPrefSheetRulesAt, &index); |
michael@0 | 1427 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1428 | rv = mPrefStyleSheet-> |
michael@0 | 1429 | InsertRuleInternal(NS_LITERAL_STRING("frame, frameset, iframe {display:none!important}"), |
michael@0 | 1430 | sInsertPrefSheetRulesAt, &index); |
michael@0 | 1431 | } |
michael@0 | 1432 | return rv; |
michael@0 | 1433 | } |
michael@0 | 1434 | |
michael@0 | 1435 | nsresult PresShell::SetPrefLinkRules(void) |
michael@0 | 1436 | { |
michael@0 | 1437 | NS_ASSERTION(mPresContext,"null prescontext not allowed"); |
michael@0 | 1438 | if (!mPresContext) { |
michael@0 | 1439 | return NS_ERROR_FAILURE; |
michael@0 | 1440 | } |
michael@0 | 1441 | |
michael@0 | 1442 | nsresult rv = NS_OK; |
michael@0 | 1443 | |
michael@0 | 1444 | if (!mPrefStyleSheet) { |
michael@0 | 1445 | rv = CreatePreferenceStyleSheet(); |
michael@0 | 1446 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1447 | } |
michael@0 | 1448 | |
michael@0 | 1449 | NS_ASSERTION(mPrefStyleSheet, "prefstylesheet should not be null"); |
michael@0 | 1450 | |
michael@0 | 1451 | // support default link colors: |
michael@0 | 1452 | // this means the link colors need to be overridable, |
michael@0 | 1453 | // which they are if we put them in the agent stylesheet, |
michael@0 | 1454 | // though if using an override sheet this will cause authors grief still |
michael@0 | 1455 | // In the agent stylesheet, they are !important when we are ignoring document colors |
michael@0 | 1456 | |
michael@0 | 1457 | nscolor linkColor(mPresContext->DefaultLinkColor()); |
michael@0 | 1458 | nscolor activeColor(mPresContext->DefaultActiveLinkColor()); |
michael@0 | 1459 | nscolor visitedColor(mPresContext->DefaultVisitedLinkColor()); |
michael@0 | 1460 | |
michael@0 | 1461 | NS_NAMED_LITERAL_STRING(ruleClose, "}"); |
michael@0 | 1462 | uint32_t index = 0; |
michael@0 | 1463 | nsAutoString strColor; |
michael@0 | 1464 | |
michael@0 | 1465 | // insert a rule to color links: '*|*:link {color: #RRGGBB [!important];}' |
michael@0 | 1466 | ColorToString(linkColor, strColor); |
michael@0 | 1467 | rv = mPrefStyleSheet-> |
michael@0 | 1468 | InsertRuleInternal(NS_LITERAL_STRING("*|*:link{color:") + |
michael@0 | 1469 | strColor + ruleClose, |
michael@0 | 1470 | sInsertPrefSheetRulesAt, &index); |
michael@0 | 1471 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1472 | |
michael@0 | 1473 | // - visited links: '*|*:visited {color: #RRGGBB [!important];}' |
michael@0 | 1474 | ColorToString(visitedColor, strColor); |
michael@0 | 1475 | rv = mPrefStyleSheet-> |
michael@0 | 1476 | InsertRuleInternal(NS_LITERAL_STRING("*|*:visited{color:") + |
michael@0 | 1477 | strColor + ruleClose, |
michael@0 | 1478 | sInsertPrefSheetRulesAt, &index); |
michael@0 | 1479 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1480 | |
michael@0 | 1481 | // - active links: '*|*:-moz-any-link:active {color: #RRGGBB [!important];}' |
michael@0 | 1482 | ColorToString(activeColor, strColor); |
michael@0 | 1483 | rv = mPrefStyleSheet-> |
michael@0 | 1484 | InsertRuleInternal(NS_LITERAL_STRING("*|*:-moz-any-link:active{color:") + |
michael@0 | 1485 | strColor + ruleClose, |
michael@0 | 1486 | sInsertPrefSheetRulesAt, &index); |
michael@0 | 1487 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1488 | |
michael@0 | 1489 | bool underlineLinks = |
michael@0 | 1490 | mPresContext->GetCachedBoolPref(kPresContext_UnderlineLinks); |
michael@0 | 1491 | |
michael@0 | 1492 | if (underlineLinks) { |
michael@0 | 1493 | // create a rule to make underlining happen |
michael@0 | 1494 | // '*|*:-moz-any-link {text-decoration:[underline|none];}' |
michael@0 | 1495 | // no need for important, we want these to be overridable |
michael@0 | 1496 | // NOTE: these must go in the agent stylesheet or they cannot be |
michael@0 | 1497 | // overridden by authors |
michael@0 | 1498 | rv = mPrefStyleSheet-> |
michael@0 | 1499 | InsertRuleInternal(NS_LITERAL_STRING("*|*:-moz-any-link:not(svg|a){text-decoration:underline}"), |
michael@0 | 1500 | sInsertPrefSheetRulesAt, &index); |
michael@0 | 1501 | } else { |
michael@0 | 1502 | rv = mPrefStyleSheet-> |
michael@0 | 1503 | InsertRuleInternal(NS_LITERAL_STRING("*|*:-moz-any-link{text-decoration:none}"), |
michael@0 | 1504 | sInsertPrefSheetRulesAt, &index); |
michael@0 | 1505 | } |
michael@0 | 1506 | |
michael@0 | 1507 | return rv; |
michael@0 | 1508 | } |
michael@0 | 1509 | |
michael@0 | 1510 | nsresult PresShell::SetPrefFocusRules(void) |
michael@0 | 1511 | { |
michael@0 | 1512 | NS_ASSERTION(mPresContext,"null prescontext not allowed"); |
michael@0 | 1513 | nsresult result = NS_OK; |
michael@0 | 1514 | |
michael@0 | 1515 | if (!mPresContext) |
michael@0 | 1516 | result = NS_ERROR_FAILURE; |
michael@0 | 1517 | |
michael@0 | 1518 | if (NS_SUCCEEDED(result) && !mPrefStyleSheet) |
michael@0 | 1519 | result = CreatePreferenceStyleSheet(); |
michael@0 | 1520 | |
michael@0 | 1521 | if (NS_SUCCEEDED(result)) { |
michael@0 | 1522 | NS_ASSERTION(mPrefStyleSheet, "prefstylesheet should not be null"); |
michael@0 | 1523 | |
michael@0 | 1524 | if (mPresContext->GetUseFocusColors()) { |
michael@0 | 1525 | nscolor focusBackground(mPresContext->FocusBackgroundColor()); |
michael@0 | 1526 | nscolor focusText(mPresContext->FocusTextColor()); |
michael@0 | 1527 | |
michael@0 | 1528 | // insert a rule to make focus the preferred color |
michael@0 | 1529 | uint32_t index = 0; |
michael@0 | 1530 | nsAutoString strRule, strColor; |
michael@0 | 1531 | |
michael@0 | 1532 | /////////////////////////////////////////////////////////////// |
michael@0 | 1533 | // - focus: '*:focus |
michael@0 | 1534 | ColorToString(focusText,strColor); |
michael@0 | 1535 | strRule.AppendLiteral("*:focus,*:focus>font {color: "); |
michael@0 | 1536 | strRule.Append(strColor); |
michael@0 | 1537 | strRule.AppendLiteral(" !important; background-color: "); |
michael@0 | 1538 | ColorToString(focusBackground,strColor); |
michael@0 | 1539 | strRule.Append(strColor); |
michael@0 | 1540 | strRule.AppendLiteral(" !important; } "); |
michael@0 | 1541 | // insert the rules |
michael@0 | 1542 | result = mPrefStyleSheet-> |
michael@0 | 1543 | InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index); |
michael@0 | 1544 | } |
michael@0 | 1545 | uint8_t focusRingWidth = mPresContext->FocusRingWidth(); |
michael@0 | 1546 | bool focusRingOnAnything = mPresContext->GetFocusRingOnAnything(); |
michael@0 | 1547 | uint8_t focusRingStyle = mPresContext->GetFocusRingStyle(); |
michael@0 | 1548 | |
michael@0 | 1549 | if ((NS_SUCCEEDED(result) && focusRingWidth != 1 && focusRingWidth <= 4 ) || focusRingOnAnything) { |
michael@0 | 1550 | uint32_t index = 0; |
michael@0 | 1551 | nsAutoString strRule; |
michael@0 | 1552 | if (!focusRingOnAnything) |
michael@0 | 1553 | strRule.AppendLiteral("*|*:link:focus, *|*:visited"); // If we only want focus rings on the normal things like links |
michael@0 | 1554 | strRule.AppendLiteral(":focus {outline: "); // For example 3px dotted WindowText (maximum 4) |
michael@0 | 1555 | strRule.AppendInt(focusRingWidth); |
michael@0 | 1556 | if (focusRingStyle == 0) // solid |
michael@0 | 1557 | strRule.AppendLiteral("px solid -moz-mac-focusring !important; -moz-outline-radius: 3px; outline-offset: 1px; } "); |
michael@0 | 1558 | else // dotted |
michael@0 | 1559 | strRule.AppendLiteral("px dotted WindowText !important; } "); |
michael@0 | 1560 | // insert the rules |
michael@0 | 1561 | result = mPrefStyleSheet-> |
michael@0 | 1562 | InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index); |
michael@0 | 1563 | NS_ENSURE_SUCCESS(result, result); |
michael@0 | 1564 | if (focusRingWidth != 1) { |
michael@0 | 1565 | // If the focus ring width is different from the default, fix buttons with rings |
michael@0 | 1566 | strRule.AssignLiteral("button::-moz-focus-inner, input[type=\"reset\"]::-moz-focus-inner,"); |
michael@0 | 1567 | strRule.AppendLiteral("input[type=\"button\"]::-moz-focus-inner, "); |
michael@0 | 1568 | strRule.AppendLiteral("input[type=\"submit\"]::-moz-focus-inner { padding: 1px 2px 1px 2px; border: "); |
michael@0 | 1569 | strRule.AppendInt(focusRingWidth); |
michael@0 | 1570 | if (focusRingStyle == 0) // solid |
michael@0 | 1571 | strRule.AppendLiteral("px solid transparent !important; } "); |
michael@0 | 1572 | else |
michael@0 | 1573 | strRule.AppendLiteral("px dotted transparent !important; } "); |
michael@0 | 1574 | result = mPrefStyleSheet-> |
michael@0 | 1575 | InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index); |
michael@0 | 1576 | NS_ENSURE_SUCCESS(result, result); |
michael@0 | 1577 | |
michael@0 | 1578 | strRule.AssignLiteral("button:focus::-moz-focus-inner, input[type=\"reset\"]:focus::-moz-focus-inner,"); |
michael@0 | 1579 | strRule.AppendLiteral("input[type=\"button\"]:focus::-moz-focus-inner, input[type=\"submit\"]:focus::-moz-focus-inner {"); |
michael@0 | 1580 | strRule.AppendLiteral("border-color: ButtonText !important; }"); |
michael@0 | 1581 | result = mPrefStyleSheet-> |
michael@0 | 1582 | InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index); |
michael@0 | 1583 | } |
michael@0 | 1584 | } |
michael@0 | 1585 | } |
michael@0 | 1586 | return result; |
michael@0 | 1587 | } |
michael@0 | 1588 | |
michael@0 | 1589 | void |
michael@0 | 1590 | PresShell::AddUserSheet(nsISupports* aSheet) |
michael@0 | 1591 | { |
michael@0 | 1592 | // Make sure this does what nsDocumentViewer::CreateStyleSet does wrt |
michael@0 | 1593 | // ordering. We want this new sheet to come after all the existing stylesheet |
michael@0 | 1594 | // service sheets, but before other user sheets; see nsIStyleSheetService.idl |
michael@0 | 1595 | // for the ordering. Just remove and readd all the nsStyleSheetService |
michael@0 | 1596 | // sheets. |
michael@0 | 1597 | nsCOMPtr<nsIStyleSheetService> dummy = |
michael@0 | 1598 | do_GetService(NS_STYLESHEETSERVICE_CONTRACTID); |
michael@0 | 1599 | |
michael@0 | 1600 | mStyleSet->BeginUpdate(); |
michael@0 | 1601 | |
michael@0 | 1602 | nsStyleSheetService *sheetService = nsStyleSheetService::gInstance; |
michael@0 | 1603 | nsCOMArray<nsIStyleSheet> & userSheets = *sheetService->UserStyleSheets(); |
michael@0 | 1604 | int32_t i; |
michael@0 | 1605 | // Iterate forwards when removing so the searches for RemoveStyleSheet are as |
michael@0 | 1606 | // short as possible. |
michael@0 | 1607 | for (i = 0; i < userSheets.Count(); ++i) { |
michael@0 | 1608 | mStyleSet->RemoveStyleSheet(nsStyleSet::eUserSheet, userSheets[i]); |
michael@0 | 1609 | } |
michael@0 | 1610 | |
michael@0 | 1611 | // Now iterate backwards, so that the order of userSheets will be the same as |
michael@0 | 1612 | // the order of sheets from it in the style set. |
michael@0 | 1613 | for (i = userSheets.Count() - 1; i >= 0; --i) { |
michael@0 | 1614 | mStyleSet->PrependStyleSheet(nsStyleSet::eUserSheet, userSheets[i]); |
michael@0 | 1615 | } |
michael@0 | 1616 | |
michael@0 | 1617 | mStyleSet->EndUpdate(); |
michael@0 | 1618 | |
michael@0 | 1619 | ReconstructStyleData(); |
michael@0 | 1620 | } |
michael@0 | 1621 | |
michael@0 | 1622 | void |
michael@0 | 1623 | PresShell::AddAgentSheet(nsISupports* aSheet) |
michael@0 | 1624 | { |
michael@0 | 1625 | // Make sure this does what nsDocumentViewer::CreateStyleSet does |
michael@0 | 1626 | // wrt ordering. |
michael@0 | 1627 | nsCOMPtr<nsIStyleSheet> sheet = do_QueryInterface(aSheet); |
michael@0 | 1628 | if (!sheet) { |
michael@0 | 1629 | return; |
michael@0 | 1630 | } |
michael@0 | 1631 | |
michael@0 | 1632 | mStyleSet->AppendStyleSheet(nsStyleSet::eAgentSheet, sheet); |
michael@0 | 1633 | ReconstructStyleData(); |
michael@0 | 1634 | } |
michael@0 | 1635 | |
michael@0 | 1636 | void |
michael@0 | 1637 | PresShell::AddAuthorSheet(nsISupports* aSheet) |
michael@0 | 1638 | { |
michael@0 | 1639 | nsCOMPtr<nsIStyleSheet> sheet = do_QueryInterface(aSheet); |
michael@0 | 1640 | if (!sheet) { |
michael@0 | 1641 | return; |
michael@0 | 1642 | } |
michael@0 | 1643 | |
michael@0 | 1644 | // Document specific "additional" Author sheets should be stronger than the ones |
michael@0 | 1645 | // added with the StyleSheetService. |
michael@0 | 1646 | nsIStyleSheet* firstAuthorSheet = mDocument->FirstAdditionalAuthorSheet(); |
michael@0 | 1647 | if (firstAuthorSheet) { |
michael@0 | 1648 | mStyleSet->InsertStyleSheetBefore(nsStyleSet::eDocSheet, sheet, firstAuthorSheet); |
michael@0 | 1649 | } else { |
michael@0 | 1650 | mStyleSet->AppendStyleSheet(nsStyleSet::eDocSheet, sheet); |
michael@0 | 1651 | } |
michael@0 | 1652 | |
michael@0 | 1653 | ReconstructStyleData(); |
michael@0 | 1654 | } |
michael@0 | 1655 | |
michael@0 | 1656 | void |
michael@0 | 1657 | PresShell::RemoveSheet(nsStyleSet::sheetType aType, nsISupports* aSheet) |
michael@0 | 1658 | { |
michael@0 | 1659 | nsCOMPtr<nsIStyleSheet> sheet = do_QueryInterface(aSheet); |
michael@0 | 1660 | if (!sheet) { |
michael@0 | 1661 | return; |
michael@0 | 1662 | } |
michael@0 | 1663 | |
michael@0 | 1664 | mStyleSet->RemoveStyleSheet(aType, sheet); |
michael@0 | 1665 | ReconstructStyleData(); |
michael@0 | 1666 | } |
michael@0 | 1667 | |
michael@0 | 1668 | NS_IMETHODIMP |
michael@0 | 1669 | PresShell::SetDisplaySelection(int16_t aToggle) |
michael@0 | 1670 | { |
michael@0 | 1671 | mSelection->SetDisplaySelection(aToggle); |
michael@0 | 1672 | return NS_OK; |
michael@0 | 1673 | } |
michael@0 | 1674 | |
michael@0 | 1675 | NS_IMETHODIMP |
michael@0 | 1676 | PresShell::GetDisplaySelection(int16_t *aToggle) |
michael@0 | 1677 | { |
michael@0 | 1678 | *aToggle = mSelection->GetDisplaySelection(); |
michael@0 | 1679 | return NS_OK; |
michael@0 | 1680 | } |
michael@0 | 1681 | |
michael@0 | 1682 | NS_IMETHODIMP |
michael@0 | 1683 | PresShell::GetSelection(SelectionType aType, nsISelection **aSelection) |
michael@0 | 1684 | { |
michael@0 | 1685 | if (!aSelection || !mSelection) |
michael@0 | 1686 | return NS_ERROR_NULL_POINTER; |
michael@0 | 1687 | |
michael@0 | 1688 | *aSelection = mSelection->GetSelection(aType); |
michael@0 | 1689 | |
michael@0 | 1690 | if (!(*aSelection)) |
michael@0 | 1691 | return NS_ERROR_INVALID_ARG; |
michael@0 | 1692 | |
michael@0 | 1693 | NS_ADDREF(*aSelection); |
michael@0 | 1694 | |
michael@0 | 1695 | return NS_OK; |
michael@0 | 1696 | } |
michael@0 | 1697 | |
michael@0 | 1698 | Selection* |
michael@0 | 1699 | PresShell::GetCurrentSelection(SelectionType aType) |
michael@0 | 1700 | { |
michael@0 | 1701 | if (!mSelection) |
michael@0 | 1702 | return nullptr; |
michael@0 | 1703 | |
michael@0 | 1704 | return mSelection->GetSelection(aType); |
michael@0 | 1705 | } |
michael@0 | 1706 | |
michael@0 | 1707 | NS_IMETHODIMP |
michael@0 | 1708 | PresShell::ScrollSelectionIntoView(SelectionType aType, SelectionRegion aRegion, |
michael@0 | 1709 | int16_t aFlags) |
michael@0 | 1710 | { |
michael@0 | 1711 | if (!mSelection) |
michael@0 | 1712 | return NS_ERROR_NULL_POINTER; |
michael@0 | 1713 | |
michael@0 | 1714 | return mSelection->ScrollSelectionIntoView(aType, aRegion, aFlags); |
michael@0 | 1715 | } |
michael@0 | 1716 | |
michael@0 | 1717 | NS_IMETHODIMP |
michael@0 | 1718 | PresShell::RepaintSelection(SelectionType aType) |
michael@0 | 1719 | { |
michael@0 | 1720 | if (!mSelection) |
michael@0 | 1721 | return NS_ERROR_NULL_POINTER; |
michael@0 | 1722 | |
michael@0 | 1723 | return mSelection->RepaintSelection(aType); |
michael@0 | 1724 | } |
michael@0 | 1725 | |
michael@0 | 1726 | // Make shell be a document observer |
michael@0 | 1727 | void |
michael@0 | 1728 | PresShell::BeginObservingDocument() |
michael@0 | 1729 | { |
michael@0 | 1730 | if (mDocument && !mIsDestroying) { |
michael@0 | 1731 | mDocument->AddObserver(this); |
michael@0 | 1732 | if (mIsDocumentGone) { |
michael@0 | 1733 | NS_WARNING("Adding a presshell that was disconnected from the document " |
michael@0 | 1734 | "as a document observer? Sounds wrong..."); |
michael@0 | 1735 | mIsDocumentGone = false; |
michael@0 | 1736 | } |
michael@0 | 1737 | } |
michael@0 | 1738 | } |
michael@0 | 1739 | |
michael@0 | 1740 | // Make shell stop being a document observer |
michael@0 | 1741 | void |
michael@0 | 1742 | PresShell::EndObservingDocument() |
michael@0 | 1743 | { |
michael@0 | 1744 | // XXXbz do we need to tell the frame constructor that the document |
michael@0 | 1745 | // is gone, perhaps? Except for printing it's NOT gone, sometimes. |
michael@0 | 1746 | mIsDocumentGone = true; |
michael@0 | 1747 | if (mDocument) { |
michael@0 | 1748 | mDocument->RemoveObserver(this); |
michael@0 | 1749 | } |
michael@0 | 1750 | } |
michael@0 | 1751 | |
michael@0 | 1752 | #ifdef DEBUG_kipp |
michael@0 | 1753 | char* nsPresShell_ReflowStackPointerTop; |
michael@0 | 1754 | #endif |
michael@0 | 1755 | |
michael@0 | 1756 | nsresult |
michael@0 | 1757 | PresShell::Initialize(nscoord aWidth, nscoord aHeight) |
michael@0 | 1758 | { |
michael@0 | 1759 | if (mIsDestroying) { |
michael@0 | 1760 | return NS_OK; |
michael@0 | 1761 | } |
michael@0 | 1762 | |
michael@0 | 1763 | if (!mDocument) { |
michael@0 | 1764 | // Nothing to do |
michael@0 | 1765 | return NS_OK; |
michael@0 | 1766 | } |
michael@0 | 1767 | |
michael@0 | 1768 | mozilla::TimeStamp timerStart = mozilla::TimeStamp::Now(); |
michael@0 | 1769 | |
michael@0 | 1770 | NS_ASSERTION(!mDidInitialize, "Why are we being called?"); |
michael@0 | 1771 | |
michael@0 | 1772 | nsCOMPtr<nsIPresShell> kungFuDeathGrip(this); |
michael@0 | 1773 | mDidInitialize = true; |
michael@0 | 1774 | |
michael@0 | 1775 | #ifdef DEBUG |
michael@0 | 1776 | if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) { |
michael@0 | 1777 | if (mDocument) { |
michael@0 | 1778 | nsIURI *uri = mDocument->GetDocumentURI(); |
michael@0 | 1779 | if (uri) { |
michael@0 | 1780 | nsAutoCString url; |
michael@0 | 1781 | uri->GetSpec(url); |
michael@0 | 1782 | printf("*** PresShell::Initialize (this=%p, url='%s')\n", (void*)this, url.get()); |
michael@0 | 1783 | } |
michael@0 | 1784 | } |
michael@0 | 1785 | } |
michael@0 | 1786 | #endif |
michael@0 | 1787 | |
michael@0 | 1788 | if (mCaret) |
michael@0 | 1789 | mCaret->EraseCaret(); |
michael@0 | 1790 | |
michael@0 | 1791 | // XXX Do a full invalidate at the beginning so that invalidates along |
michael@0 | 1792 | // the way don't have region accumulation issues? |
michael@0 | 1793 | |
michael@0 | 1794 | mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight)); |
michael@0 | 1795 | |
michael@0 | 1796 | // Get the root frame from the frame manager |
michael@0 | 1797 | // XXXbz it would be nice to move this somewhere else... like frame manager |
michael@0 | 1798 | // Init(), say. But we need to make sure our views are all set up by the |
michael@0 | 1799 | // time we do this! |
michael@0 | 1800 | nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); |
michael@0 | 1801 | NS_ASSERTION(!rootFrame, "How did that happen, exactly?"); |
michael@0 | 1802 | if (!rootFrame) { |
michael@0 | 1803 | nsAutoScriptBlocker scriptBlocker; |
michael@0 | 1804 | mFrameConstructor->BeginUpdate(); |
michael@0 | 1805 | rootFrame = mFrameConstructor->ConstructRootFrame(); |
michael@0 | 1806 | mFrameConstructor->SetRootFrame(rootFrame); |
michael@0 | 1807 | mFrameConstructor->EndUpdate(); |
michael@0 | 1808 | } |
michael@0 | 1809 | |
michael@0 | 1810 | NS_ENSURE_STATE(!mHaveShutDown); |
michael@0 | 1811 | |
michael@0 | 1812 | if (!rootFrame) { |
michael@0 | 1813 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 1814 | } |
michael@0 | 1815 | |
michael@0 | 1816 | for (nsIFrame* f = rootFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) { |
michael@0 | 1817 | if (f->GetStateBits() & NS_FRAME_NO_COMPONENT_ALPHA) { |
michael@0 | 1818 | f->InvalidateFrameSubtree(); |
michael@0 | 1819 | f->RemoveStateBits(NS_FRAME_NO_COMPONENT_ALPHA); |
michael@0 | 1820 | } |
michael@0 | 1821 | } |
michael@0 | 1822 | |
michael@0 | 1823 | Element *root = mDocument->GetRootElement(); |
michael@0 | 1824 | |
michael@0 | 1825 | if (root) { |
michael@0 | 1826 | { |
michael@0 | 1827 | nsAutoCauseReflowNotifier reflowNotifier(this); |
michael@0 | 1828 | mFrameConstructor->BeginUpdate(); |
michael@0 | 1829 | |
michael@0 | 1830 | // Have the style sheet processor construct frame for the root |
michael@0 | 1831 | // content object down |
michael@0 | 1832 | mFrameConstructor->ContentInserted(nullptr, root, nullptr, false); |
michael@0 | 1833 | VERIFY_STYLE_TREE; |
michael@0 | 1834 | |
michael@0 | 1835 | // Something in mFrameConstructor->ContentInserted may have caused |
michael@0 | 1836 | // Destroy() to get called, bug 337586. |
michael@0 | 1837 | NS_ENSURE_STATE(!mHaveShutDown); |
michael@0 | 1838 | |
michael@0 | 1839 | mFrameConstructor->EndUpdate(); |
michael@0 | 1840 | } |
michael@0 | 1841 | |
michael@0 | 1842 | // nsAutoScriptBlocker going out of scope may have killed us too |
michael@0 | 1843 | NS_ENSURE_STATE(!mHaveShutDown); |
michael@0 | 1844 | |
michael@0 | 1845 | // Run the XBL binding constructors for any new frames we've constructed |
michael@0 | 1846 | mDocument->BindingManager()->ProcessAttachedQueue(); |
michael@0 | 1847 | |
michael@0 | 1848 | // Constructors may have killed us too |
michael@0 | 1849 | NS_ENSURE_STATE(!mHaveShutDown); |
michael@0 | 1850 | |
michael@0 | 1851 | // Now flush out pending restyles before we actually reflow, in |
michael@0 | 1852 | // case XBL constructors changed styles somewhere. |
michael@0 | 1853 | { |
michael@0 | 1854 | nsAutoScriptBlocker scriptBlocker; |
michael@0 | 1855 | mPresContext->RestyleManager()->ProcessPendingRestyles(); |
michael@0 | 1856 | } |
michael@0 | 1857 | |
michael@0 | 1858 | // And that might have run _more_ XBL constructors |
michael@0 | 1859 | NS_ENSURE_STATE(!mHaveShutDown); |
michael@0 | 1860 | } |
michael@0 | 1861 | |
michael@0 | 1862 | NS_ASSERTION(rootFrame, "How did that happen?"); |
michael@0 | 1863 | |
michael@0 | 1864 | // Note: when the frame was created above it had the NS_FRAME_IS_DIRTY bit |
michael@0 | 1865 | // set, but XBL processing could have caused a reflow which clears it. |
michael@0 | 1866 | if (MOZ_LIKELY(rootFrame->GetStateBits() & NS_FRAME_IS_DIRTY)) { |
michael@0 | 1867 | // Unset the DIRTY bits so that FrameNeedsReflow() will work right. |
michael@0 | 1868 | rootFrame->RemoveStateBits(NS_FRAME_IS_DIRTY | |
michael@0 | 1869 | NS_FRAME_HAS_DIRTY_CHILDREN); |
michael@0 | 1870 | NS_ASSERTION(!mDirtyRoots.Contains(rootFrame), |
michael@0 | 1871 | "Why is the root in mDirtyRoots already?"); |
michael@0 | 1872 | FrameNeedsReflow(rootFrame, eResize, NS_FRAME_IS_DIRTY); |
michael@0 | 1873 | NS_ASSERTION(mDirtyRoots.Contains(rootFrame), |
michael@0 | 1874 | "Should be in mDirtyRoots now"); |
michael@0 | 1875 | NS_ASSERTION(mReflowScheduled, "Why no reflow scheduled?"); |
michael@0 | 1876 | } |
michael@0 | 1877 | |
michael@0 | 1878 | // Restore our root scroll position now if we're getting here after EndLoad |
michael@0 | 1879 | // got called, since this is our one chance to do it. Note that we need not |
michael@0 | 1880 | // have reflowed for this to work; when the scrollframe is finally reflowed |
michael@0 | 1881 | // it'll pick up the position we store in it here. |
michael@0 | 1882 | if (!mDocumentLoading) { |
michael@0 | 1883 | RestoreRootScrollPosition(); |
michael@0 | 1884 | } |
michael@0 | 1885 | |
michael@0 | 1886 | // For printing, we just immediately unsuppress. |
michael@0 | 1887 | if (!mPresContext->IsPaginated()) { |
michael@0 | 1888 | // Kick off a one-shot timer based off our pref value. When this timer |
michael@0 | 1889 | // fires, if painting is still locked down, then we will go ahead and |
michael@0 | 1890 | // trigger a full invalidate and allow painting to proceed normally. |
michael@0 | 1891 | mPaintingSuppressed = true; |
michael@0 | 1892 | // Don't suppress painting if the document isn't loading. |
michael@0 | 1893 | nsIDocument::ReadyState readyState = mDocument->GetReadyStateEnum(); |
michael@0 | 1894 | if (readyState != nsIDocument::READYSTATE_COMPLETE) { |
michael@0 | 1895 | mPaintSuppressionTimer = do_CreateInstance("@mozilla.org/timer;1"); |
michael@0 | 1896 | } |
michael@0 | 1897 | if (!mPaintSuppressionTimer) { |
michael@0 | 1898 | mPaintingSuppressed = false; |
michael@0 | 1899 | } else { |
michael@0 | 1900 | // Initialize the timer. |
michael@0 | 1901 | |
michael@0 | 1902 | // Default to PAINTLOCK_EVENT_DELAY if we can't get the pref value. |
michael@0 | 1903 | int32_t delay = |
michael@0 | 1904 | Preferences::GetInt("nglayout.initialpaint.delay", |
michael@0 | 1905 | PAINTLOCK_EVENT_DELAY); |
michael@0 | 1906 | |
michael@0 | 1907 | mPaintSuppressionTimer->InitWithFuncCallback(sPaintSuppressionCallback, |
michael@0 | 1908 | this, delay, |
michael@0 | 1909 | nsITimer::TYPE_ONE_SHOT); |
michael@0 | 1910 | } |
michael@0 | 1911 | } |
michael@0 | 1912 | |
michael@0 | 1913 | if (root && root->IsXUL()) { |
michael@0 | 1914 | mozilla::Telemetry::AccumulateTimeDelta(Telemetry::XUL_INITIAL_FRAME_CONSTRUCTION, |
michael@0 | 1915 | timerStart); |
michael@0 | 1916 | } |
michael@0 | 1917 | |
michael@0 | 1918 | return NS_OK; //XXX this needs to be real. MMP |
michael@0 | 1919 | } |
michael@0 | 1920 | |
michael@0 | 1921 | void |
michael@0 | 1922 | PresShell::sPaintSuppressionCallback(nsITimer *aTimer, void* aPresShell) |
michael@0 | 1923 | { |
michael@0 | 1924 | nsRefPtr<PresShell> self = static_cast<PresShell*>(aPresShell); |
michael@0 | 1925 | if (self) |
michael@0 | 1926 | self->UnsuppressPainting(); |
michael@0 | 1927 | } |
michael@0 | 1928 | |
michael@0 | 1929 | void |
michael@0 | 1930 | PresShell::AsyncResizeEventCallback(nsITimer* aTimer, void* aPresShell) |
michael@0 | 1931 | { |
michael@0 | 1932 | static_cast<PresShell*>(aPresShell)->FireResizeEvent(); |
michael@0 | 1933 | } |
michael@0 | 1934 | |
michael@0 | 1935 | nsresult |
michael@0 | 1936 | PresShell::ResizeReflowOverride(nscoord aWidth, nscoord aHeight) |
michael@0 | 1937 | { |
michael@0 | 1938 | mViewportOverridden = true; |
michael@0 | 1939 | return ResizeReflowIgnoreOverride(aWidth, aHeight); |
michael@0 | 1940 | } |
michael@0 | 1941 | |
michael@0 | 1942 | nsresult |
michael@0 | 1943 | PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight) |
michael@0 | 1944 | { |
michael@0 | 1945 | if (mViewportOverridden) { |
michael@0 | 1946 | // The viewport has been overridden, and this reflow request |
michael@0 | 1947 | // didn't ask to ignore the override. Pretend it didn't happen. |
michael@0 | 1948 | return NS_OK; |
michael@0 | 1949 | } |
michael@0 | 1950 | return ResizeReflowIgnoreOverride(aWidth, aHeight); |
michael@0 | 1951 | } |
michael@0 | 1952 | |
michael@0 | 1953 | nsresult |
michael@0 | 1954 | PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight) |
michael@0 | 1955 | { |
michael@0 | 1956 | NS_PRECONDITION(!mIsReflowing, "Shouldn't be in reflow here!"); |
michael@0 | 1957 | NS_PRECONDITION(aWidth != NS_UNCONSTRAINEDSIZE, |
michael@0 | 1958 | "shouldn't use unconstrained widths anymore"); |
michael@0 | 1959 | |
michael@0 | 1960 | // If we don't have a root frame yet, that means we haven't had our initial |
michael@0 | 1961 | // reflow... If that's the case, and aWidth or aHeight is unconstrained, |
michael@0 | 1962 | // ignore them altogether. |
michael@0 | 1963 | nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); |
michael@0 | 1964 | if (!rootFrame && aHeight == NS_UNCONSTRAINEDSIZE) { |
michael@0 | 1965 | // We can't do the work needed for SizeToContent without a root |
michael@0 | 1966 | // frame, and we want to return before setting the visible area. |
michael@0 | 1967 | return NS_ERROR_NOT_AVAILABLE; |
michael@0 | 1968 | } |
michael@0 | 1969 | |
michael@0 | 1970 | mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight)); |
michael@0 | 1971 | |
michael@0 | 1972 | // There isn't anything useful we can do if the initial reflow hasn't happened. |
michael@0 | 1973 | if (!rootFrame) { |
michael@0 | 1974 | return NS_OK; |
michael@0 | 1975 | } |
michael@0 | 1976 | |
michael@0 | 1977 | nsRefPtr<nsViewManager> viewManagerDeathGrip = mViewManager; |
michael@0 | 1978 | // Take this ref after viewManager so it'll make sure to go away first. |
michael@0 | 1979 | nsCOMPtr<nsIPresShell> kungFuDeathGrip(this); |
michael@0 | 1980 | |
michael@0 | 1981 | if (!GetPresContext()->SupressingResizeReflow()) { |
michael@0 | 1982 | // Have to make sure that the content notifications are flushed before we |
michael@0 | 1983 | // start messing with the frame model; otherwise we can get content doubling. |
michael@0 | 1984 | mDocument->FlushPendingNotifications(Flush_ContentAndNotify); |
michael@0 | 1985 | |
michael@0 | 1986 | // Make sure style is up to date |
michael@0 | 1987 | { |
michael@0 | 1988 | nsAutoScriptBlocker scriptBlocker; |
michael@0 | 1989 | mPresContext->RestyleManager()->ProcessPendingRestyles(); |
michael@0 | 1990 | } |
michael@0 | 1991 | |
michael@0 | 1992 | rootFrame = mFrameConstructor->GetRootFrame(); |
michael@0 | 1993 | if (!mIsDestroying && rootFrame) { |
michael@0 | 1994 | // XXX Do a full invalidate at the beginning so that invalidates along |
michael@0 | 1995 | // the way don't have region accumulation issues? |
michael@0 | 1996 | |
michael@0 | 1997 | { |
michael@0 | 1998 | nsAutoCauseReflowNotifier crNotifier(this); |
michael@0 | 1999 | WillDoReflow(); |
michael@0 | 2000 | |
michael@0 | 2001 | // Kick off a top-down reflow |
michael@0 | 2002 | AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow); |
michael@0 | 2003 | nsViewManager::AutoDisableRefresh refreshBlocker(mViewManager); |
michael@0 | 2004 | |
michael@0 | 2005 | mDirtyRoots.RemoveElement(rootFrame); |
michael@0 | 2006 | DoReflow(rootFrame, true); |
michael@0 | 2007 | } |
michael@0 | 2008 | |
michael@0 | 2009 | DidDoReflow(true, false); |
michael@0 | 2010 | } |
michael@0 | 2011 | } |
michael@0 | 2012 | |
michael@0 | 2013 | rootFrame = mFrameConstructor->GetRootFrame(); |
michael@0 | 2014 | if (aHeight == NS_UNCONSTRAINEDSIZE && rootFrame) { |
michael@0 | 2015 | mPresContext->SetVisibleArea( |
michael@0 | 2016 | nsRect(0, 0, aWidth, rootFrame->GetRect().height)); |
michael@0 | 2017 | } |
michael@0 | 2018 | |
michael@0 | 2019 | if (!mIsDestroying && !mResizeEvent.IsPending() && |
michael@0 | 2020 | !mAsyncResizeTimerIsActive) { |
michael@0 | 2021 | if (mInResize) { |
michael@0 | 2022 | if (!mAsyncResizeEventTimer) { |
michael@0 | 2023 | mAsyncResizeEventTimer = do_CreateInstance("@mozilla.org/timer;1"); |
michael@0 | 2024 | } |
michael@0 | 2025 | if (mAsyncResizeEventTimer) { |
michael@0 | 2026 | mAsyncResizeTimerIsActive = true; |
michael@0 | 2027 | mAsyncResizeEventTimer->InitWithFuncCallback(AsyncResizeEventCallback, |
michael@0 | 2028 | this, 15, |
michael@0 | 2029 | nsITimer::TYPE_ONE_SHOT); |
michael@0 | 2030 | } |
michael@0 | 2031 | } else { |
michael@0 | 2032 | nsRefPtr<nsRunnableMethod<PresShell> > resizeEvent = |
michael@0 | 2033 | NS_NewRunnableMethod(this, &PresShell::FireResizeEvent); |
michael@0 | 2034 | if (NS_SUCCEEDED(NS_DispatchToCurrentThread(resizeEvent))) { |
michael@0 | 2035 | mResizeEvent = resizeEvent; |
michael@0 | 2036 | mDocument->SetNeedStyleFlush(); |
michael@0 | 2037 | } |
michael@0 | 2038 | } |
michael@0 | 2039 | } |
michael@0 | 2040 | |
michael@0 | 2041 | return NS_OK; //XXX this needs to be real. MMP |
michael@0 | 2042 | } |
michael@0 | 2043 | |
michael@0 | 2044 | void |
michael@0 | 2045 | PresShell::FireResizeEvent() |
michael@0 | 2046 | { |
michael@0 | 2047 | if (mAsyncResizeTimerIsActive) { |
michael@0 | 2048 | mAsyncResizeTimerIsActive = false; |
michael@0 | 2049 | mAsyncResizeEventTimer->Cancel(); |
michael@0 | 2050 | } |
michael@0 | 2051 | mResizeEvent.Revoke(); |
michael@0 | 2052 | |
michael@0 | 2053 | if (mIsDocumentGone) |
michael@0 | 2054 | return; |
michael@0 | 2055 | |
michael@0 | 2056 | //Send resize event from here. |
michael@0 | 2057 | WidgetEvent event(true, NS_RESIZE_EVENT); |
michael@0 | 2058 | nsEventStatus status = nsEventStatus_eIgnore; |
michael@0 | 2059 | |
michael@0 | 2060 | nsPIDOMWindow *window = mDocument->GetWindow(); |
michael@0 | 2061 | if (window) { |
michael@0 | 2062 | nsCOMPtr<nsIPresShell> kungFuDeathGrip(this); |
michael@0 | 2063 | mInResize = true; |
michael@0 | 2064 | EventDispatcher::Dispatch(window, mPresContext, &event, nullptr, &status); |
michael@0 | 2065 | mInResize = false; |
michael@0 | 2066 | } |
michael@0 | 2067 | } |
michael@0 | 2068 | |
michael@0 | 2069 | void |
michael@0 | 2070 | PresShell::SetIgnoreFrameDestruction(bool aIgnore) |
michael@0 | 2071 | { |
michael@0 | 2072 | if (mDocument) { |
michael@0 | 2073 | // We need to tell the ImageLoader to drop all its references to frames |
michael@0 | 2074 | // because they're about to go away and it won't get notifications of that. |
michael@0 | 2075 | mDocument->StyleImageLoader()->ClearFrames(); |
michael@0 | 2076 | } |
michael@0 | 2077 | mIgnoreFrameDestruction = aIgnore; |
michael@0 | 2078 | } |
michael@0 | 2079 | |
michael@0 | 2080 | void |
michael@0 | 2081 | PresShell::NotifyDestroyingFrame(nsIFrame* aFrame) |
michael@0 | 2082 | { |
michael@0 | 2083 | if (!mIgnoreFrameDestruction) { |
michael@0 | 2084 | mDocument->StyleImageLoader()->DropRequestsForFrame(aFrame); |
michael@0 | 2085 | |
michael@0 | 2086 | mFrameConstructor->NotifyDestroyingFrame(aFrame); |
michael@0 | 2087 | |
michael@0 | 2088 | for (int32_t idx = mDirtyRoots.Length(); idx; ) { |
michael@0 | 2089 | --idx; |
michael@0 | 2090 | if (mDirtyRoots[idx] == aFrame) { |
michael@0 | 2091 | mDirtyRoots.RemoveElementAt(idx); |
michael@0 | 2092 | } |
michael@0 | 2093 | } |
michael@0 | 2094 | |
michael@0 | 2095 | // Remove frame properties |
michael@0 | 2096 | mPresContext->NotifyDestroyingFrame(aFrame); |
michael@0 | 2097 | |
michael@0 | 2098 | if (aFrame == mCurrentEventFrame) { |
michael@0 | 2099 | mCurrentEventContent = aFrame->GetContent(); |
michael@0 | 2100 | mCurrentEventFrame = nullptr; |
michael@0 | 2101 | } |
michael@0 | 2102 | |
michael@0 | 2103 | #ifdef DEBUG |
michael@0 | 2104 | if (aFrame == mDrawEventTargetFrame) { |
michael@0 | 2105 | mDrawEventTargetFrame = nullptr; |
michael@0 | 2106 | } |
michael@0 | 2107 | #endif |
michael@0 | 2108 | |
michael@0 | 2109 | for (unsigned int i=0; i < mCurrentEventFrameStack.Length(); i++) { |
michael@0 | 2110 | if (aFrame == mCurrentEventFrameStack.ElementAt(i)) { |
michael@0 | 2111 | //One of our stack frames was deleted. Get its content so that when we |
michael@0 | 2112 | //pop it we can still get its new frame from its content |
michael@0 | 2113 | nsIContent *currentEventContent = aFrame->GetContent(); |
michael@0 | 2114 | mCurrentEventContentStack.ReplaceObjectAt(currentEventContent, i); |
michael@0 | 2115 | mCurrentEventFrameStack[i] = nullptr; |
michael@0 | 2116 | } |
michael@0 | 2117 | } |
michael@0 | 2118 | |
michael@0 | 2119 | mFramesToDirty.RemoveEntry(aFrame); |
michael@0 | 2120 | } else { |
michael@0 | 2121 | // We must delete this property in situ so that its destructor removes the |
michael@0 | 2122 | // frame from FrameLayerBuilder::DisplayItemData::mFrameList -- otherwise |
michael@0 | 2123 | // the DisplayItemData destructor will use the destroyed frame when it |
michael@0 | 2124 | // tries to remove it from the (array) value of this property. |
michael@0 | 2125 | mPresContext->PropertyTable()-> |
michael@0 | 2126 | Delete(aFrame, FrameLayerBuilder::LayerManagerDataProperty()); |
michael@0 | 2127 | } |
michael@0 | 2128 | } |
michael@0 | 2129 | |
michael@0 | 2130 | already_AddRefed<nsCaret> PresShell::GetCaret() const |
michael@0 | 2131 | { |
michael@0 | 2132 | nsRefPtr<nsCaret> caret = mCaret; |
michael@0 | 2133 | return caret.forget(); |
michael@0 | 2134 | } |
michael@0 | 2135 | |
michael@0 | 2136 | void PresShell::MaybeInvalidateCaretPosition() |
michael@0 | 2137 | { |
michael@0 | 2138 | if (mCaret) { |
michael@0 | 2139 | mCaret->InvalidateOutsideCaret(); |
michael@0 | 2140 | } |
michael@0 | 2141 | } |
michael@0 | 2142 | |
michael@0 | 2143 | void PresShell::SetCaret(nsCaret *aNewCaret) |
michael@0 | 2144 | { |
michael@0 | 2145 | mCaret = aNewCaret; |
michael@0 | 2146 | } |
michael@0 | 2147 | |
michael@0 | 2148 | void PresShell::RestoreCaret() |
michael@0 | 2149 | { |
michael@0 | 2150 | mCaret = mOriginalCaret; |
michael@0 | 2151 | } |
michael@0 | 2152 | |
michael@0 | 2153 | NS_IMETHODIMP PresShell::SetCaretEnabled(bool aInEnable) |
michael@0 | 2154 | { |
michael@0 | 2155 | bool oldEnabled = mCaretEnabled; |
michael@0 | 2156 | |
michael@0 | 2157 | mCaretEnabled = aInEnable; |
michael@0 | 2158 | |
michael@0 | 2159 | if (mCaret && (mCaretEnabled != oldEnabled)) |
michael@0 | 2160 | { |
michael@0 | 2161 | /* Don't change the caret's selection here! This was an evil side-effect of SetCaretEnabled() |
michael@0 | 2162 | nsCOMPtr<nsIDOMSelection> domSel; |
michael@0 | 2163 | if (NS_SUCCEEDED(GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSel))) && domSel) |
michael@0 | 2164 | mCaret->SetCaretDOMSelection(domSel); |
michael@0 | 2165 | */ |
michael@0 | 2166 | mCaret->SetCaretVisible(mCaretEnabled); |
michael@0 | 2167 | } |
michael@0 | 2168 | |
michael@0 | 2169 | return NS_OK; |
michael@0 | 2170 | } |
michael@0 | 2171 | |
michael@0 | 2172 | NS_IMETHODIMP PresShell::SetCaretReadOnly(bool aReadOnly) |
michael@0 | 2173 | { |
michael@0 | 2174 | if (mCaret) |
michael@0 | 2175 | mCaret->SetCaretReadOnly(aReadOnly); |
michael@0 | 2176 | return NS_OK; |
michael@0 | 2177 | } |
michael@0 | 2178 | |
michael@0 | 2179 | NS_IMETHODIMP PresShell::GetCaretEnabled(bool *aOutEnabled) |
michael@0 | 2180 | { |
michael@0 | 2181 | NS_ENSURE_ARG_POINTER(aOutEnabled); |
michael@0 | 2182 | *aOutEnabled = mCaretEnabled; |
michael@0 | 2183 | return NS_OK; |
michael@0 | 2184 | } |
michael@0 | 2185 | |
michael@0 | 2186 | NS_IMETHODIMP PresShell::SetCaretVisibilityDuringSelection(bool aVisibility) |
michael@0 | 2187 | { |
michael@0 | 2188 | if (mCaret) |
michael@0 | 2189 | mCaret->SetVisibilityDuringSelection(aVisibility); |
michael@0 | 2190 | return NS_OK; |
michael@0 | 2191 | } |
michael@0 | 2192 | |
michael@0 | 2193 | NS_IMETHODIMP PresShell::GetCaretVisible(bool *aOutIsVisible) |
michael@0 | 2194 | { |
michael@0 | 2195 | *aOutIsVisible = false; |
michael@0 | 2196 | if (mCaret) { |
michael@0 | 2197 | nsresult rv = mCaret->GetCaretVisible(aOutIsVisible); |
michael@0 | 2198 | NS_ENSURE_SUCCESS(rv,rv); |
michael@0 | 2199 | } |
michael@0 | 2200 | return NS_OK; |
michael@0 | 2201 | } |
michael@0 | 2202 | |
michael@0 | 2203 | NS_IMETHODIMP PresShell::SetSelectionFlags(int16_t aInEnable) |
michael@0 | 2204 | { |
michael@0 | 2205 | mSelectionFlags = aInEnable; |
michael@0 | 2206 | return NS_OK; |
michael@0 | 2207 | } |
michael@0 | 2208 | |
michael@0 | 2209 | NS_IMETHODIMP PresShell::GetSelectionFlags(int16_t *aOutEnable) |
michael@0 | 2210 | { |
michael@0 | 2211 | if (!aOutEnable) |
michael@0 | 2212 | return NS_ERROR_INVALID_ARG; |
michael@0 | 2213 | *aOutEnable = mSelectionFlags; |
michael@0 | 2214 | return NS_OK; |
michael@0 | 2215 | } |
michael@0 | 2216 | |
michael@0 | 2217 | //implementation of nsISelectionController |
michael@0 | 2218 | |
michael@0 | 2219 | NS_IMETHODIMP |
michael@0 | 2220 | PresShell::CharacterMove(bool aForward, bool aExtend) |
michael@0 | 2221 | { |
michael@0 | 2222 | return mSelection->CharacterMove(aForward, aExtend); |
michael@0 | 2223 | } |
michael@0 | 2224 | |
michael@0 | 2225 | NS_IMETHODIMP |
michael@0 | 2226 | PresShell::CharacterExtendForDelete() |
michael@0 | 2227 | { |
michael@0 | 2228 | return mSelection->CharacterExtendForDelete(); |
michael@0 | 2229 | } |
michael@0 | 2230 | |
michael@0 | 2231 | NS_IMETHODIMP |
michael@0 | 2232 | PresShell::CharacterExtendForBackspace() |
michael@0 | 2233 | { |
michael@0 | 2234 | return mSelection->CharacterExtendForBackspace(); |
michael@0 | 2235 | } |
michael@0 | 2236 | |
michael@0 | 2237 | NS_IMETHODIMP |
michael@0 | 2238 | PresShell::WordMove(bool aForward, bool aExtend) |
michael@0 | 2239 | { |
michael@0 | 2240 | nsresult result = mSelection->WordMove(aForward, aExtend); |
michael@0 | 2241 | // if we can't go down/up any more we must then move caret completely to |
michael@0 | 2242 | // end/beginning respectively. |
michael@0 | 2243 | if (NS_FAILED(result)) |
michael@0 | 2244 | result = CompleteMove(aForward, aExtend); |
michael@0 | 2245 | return result; |
michael@0 | 2246 | } |
michael@0 | 2247 | |
michael@0 | 2248 | NS_IMETHODIMP |
michael@0 | 2249 | PresShell::WordExtendForDelete(bool aForward) |
michael@0 | 2250 | { |
michael@0 | 2251 | return mSelection->WordExtendForDelete(aForward); |
michael@0 | 2252 | } |
michael@0 | 2253 | |
michael@0 | 2254 | NS_IMETHODIMP |
michael@0 | 2255 | PresShell::LineMove(bool aForward, bool aExtend) |
michael@0 | 2256 | { |
michael@0 | 2257 | nsresult result = mSelection->LineMove(aForward, aExtend); |
michael@0 | 2258 | // if we can't go down/up any more we must then move caret completely to |
michael@0 | 2259 | // end/beginning respectively. |
michael@0 | 2260 | if (NS_FAILED(result)) |
michael@0 | 2261 | result = CompleteMove(aForward,aExtend); |
michael@0 | 2262 | return result; |
michael@0 | 2263 | } |
michael@0 | 2264 | |
michael@0 | 2265 | NS_IMETHODIMP |
michael@0 | 2266 | PresShell::IntraLineMove(bool aForward, bool aExtend) |
michael@0 | 2267 | { |
michael@0 | 2268 | return mSelection->IntraLineMove(aForward, aExtend); |
michael@0 | 2269 | } |
michael@0 | 2270 | |
michael@0 | 2271 | |
michael@0 | 2272 | |
michael@0 | 2273 | NS_IMETHODIMP |
michael@0 | 2274 | PresShell::PageMove(bool aForward, bool aExtend) |
michael@0 | 2275 | { |
michael@0 | 2276 | nsIScrollableFrame *scrollableFrame = |
michael@0 | 2277 | GetFrameToScrollAsScrollable(nsIPresShell::eVertical); |
michael@0 | 2278 | if (!scrollableFrame) |
michael@0 | 2279 | return NS_OK; |
michael@0 | 2280 | |
michael@0 | 2281 | mSelection->CommonPageMove(aForward, aExtend, scrollableFrame); |
michael@0 | 2282 | // After ScrollSelectionIntoView(), the pending notifications might be |
michael@0 | 2283 | // flushed and PresShell/PresContext/Frames may be dead. See bug 418470. |
michael@0 | 2284 | return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, |
michael@0 | 2285 | nsISelectionController::SELECTION_FOCUS_REGION, |
michael@0 | 2286 | nsISelectionController::SCROLL_SYNCHRONOUS); |
michael@0 | 2287 | } |
michael@0 | 2288 | |
michael@0 | 2289 | |
michael@0 | 2290 | |
michael@0 | 2291 | NS_IMETHODIMP |
michael@0 | 2292 | PresShell::ScrollPage(bool aForward) |
michael@0 | 2293 | { |
michael@0 | 2294 | nsIScrollableFrame* scrollFrame = |
michael@0 | 2295 | GetFrameToScrollAsScrollable(nsIPresShell::eVertical); |
michael@0 | 2296 | if (scrollFrame) { |
michael@0 | 2297 | scrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1), |
michael@0 | 2298 | nsIScrollableFrame::PAGES, |
michael@0 | 2299 | nsIScrollableFrame::SMOOTH); |
michael@0 | 2300 | } |
michael@0 | 2301 | return NS_OK; |
michael@0 | 2302 | } |
michael@0 | 2303 | |
michael@0 | 2304 | NS_IMETHODIMP |
michael@0 | 2305 | PresShell::ScrollLine(bool aForward) |
michael@0 | 2306 | { |
michael@0 | 2307 | nsIScrollableFrame* scrollFrame = |
michael@0 | 2308 | GetFrameToScrollAsScrollable(nsIPresShell::eVertical); |
michael@0 | 2309 | if (scrollFrame) { |
michael@0 | 2310 | int32_t lineCount = Preferences::GetInt("toolkit.scrollbox.verticalScrollDistance", |
michael@0 | 2311 | NS_DEFAULT_VERTICAL_SCROLL_DISTANCE); |
michael@0 | 2312 | scrollFrame->ScrollBy(nsIntPoint(0, aForward ? lineCount : -lineCount), |
michael@0 | 2313 | nsIScrollableFrame::LINES, |
michael@0 | 2314 | nsIScrollableFrame::SMOOTH); |
michael@0 | 2315 | } |
michael@0 | 2316 | return NS_OK; |
michael@0 | 2317 | } |
michael@0 | 2318 | |
michael@0 | 2319 | NS_IMETHODIMP |
michael@0 | 2320 | PresShell::ScrollCharacter(bool aRight) |
michael@0 | 2321 | { |
michael@0 | 2322 | nsIScrollableFrame* scrollFrame = |
michael@0 | 2323 | GetFrameToScrollAsScrollable(nsIPresShell::eHorizontal); |
michael@0 | 2324 | if (scrollFrame) { |
michael@0 | 2325 | int32_t h = Preferences::GetInt("toolkit.scrollbox.horizontalScrollDistance", |
michael@0 | 2326 | NS_DEFAULT_HORIZONTAL_SCROLL_DISTANCE); |
michael@0 | 2327 | scrollFrame->ScrollBy(nsIntPoint(aRight ? h : -h, 0), |
michael@0 | 2328 | nsIScrollableFrame::LINES, |
michael@0 | 2329 | nsIScrollableFrame::SMOOTH); |
michael@0 | 2330 | } |
michael@0 | 2331 | return NS_OK; |
michael@0 | 2332 | } |
michael@0 | 2333 | |
michael@0 | 2334 | NS_IMETHODIMP |
michael@0 | 2335 | PresShell::CompleteScroll(bool aForward) |
michael@0 | 2336 | { |
michael@0 | 2337 | nsIScrollableFrame* scrollFrame = |
michael@0 | 2338 | GetFrameToScrollAsScrollable(nsIPresShell::eVertical); |
michael@0 | 2339 | if (scrollFrame) { |
michael@0 | 2340 | scrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1), |
michael@0 | 2341 | nsIScrollableFrame::WHOLE, |
michael@0 | 2342 | nsIScrollableFrame::SMOOTH); |
michael@0 | 2343 | } |
michael@0 | 2344 | return NS_OK; |
michael@0 | 2345 | } |
michael@0 | 2346 | |
michael@0 | 2347 | NS_IMETHODIMP |
michael@0 | 2348 | PresShell::CompleteMove(bool aForward, bool aExtend) |
michael@0 | 2349 | { |
michael@0 | 2350 | // Beware! This may flush notifications via synchronous |
michael@0 | 2351 | // ScrollSelectionIntoView. |
michael@0 | 2352 | nsIContent* limiter = mSelection->GetAncestorLimiter(); |
michael@0 | 2353 | nsIFrame* frame = limiter ? limiter->GetPrimaryFrame() |
michael@0 | 2354 | : FrameConstructor()->GetRootElementFrame(); |
michael@0 | 2355 | if (!frame) |
michael@0 | 2356 | return NS_ERROR_FAILURE; |
michael@0 | 2357 | nsIFrame::CaretPosition pos = |
michael@0 | 2358 | frame->GetExtremeCaretPosition(!aForward); |
michael@0 | 2359 | mSelection->HandleClick(pos.mResultContent, pos.mContentOffset, |
michael@0 | 2360 | pos.mContentOffset, aExtend, false, aForward); |
michael@0 | 2361 | if (limiter) { |
michael@0 | 2362 | // HandleClick resets ancestorLimiter, so set it again. |
michael@0 | 2363 | mSelection->SetAncestorLimiter(limiter); |
michael@0 | 2364 | } |
michael@0 | 2365 | |
michael@0 | 2366 | // After ScrollSelectionIntoView(), the pending notifications might be |
michael@0 | 2367 | // flushed and PresShell/PresContext/Frames may be dead. See bug 418470. |
michael@0 | 2368 | return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, |
michael@0 | 2369 | nsISelectionController::SELECTION_FOCUS_REGION, |
michael@0 | 2370 | nsISelectionController::SCROLL_SYNCHRONOUS); |
michael@0 | 2371 | } |
michael@0 | 2372 | |
michael@0 | 2373 | NS_IMETHODIMP |
michael@0 | 2374 | PresShell::SelectAll() |
michael@0 | 2375 | { |
michael@0 | 2376 | return mSelection->SelectAll(); |
michael@0 | 2377 | } |
michael@0 | 2378 | |
michael@0 | 2379 | static void |
michael@0 | 2380 | DoCheckVisibility(nsPresContext* aPresContext, |
michael@0 | 2381 | nsIContent* aNode, |
michael@0 | 2382 | int16_t aStartOffset, |
michael@0 | 2383 | int16_t aEndOffset, |
michael@0 | 2384 | bool* aRetval) |
michael@0 | 2385 | { |
michael@0 | 2386 | nsIFrame* frame = aNode->GetPrimaryFrame(); |
michael@0 | 2387 | if (!frame) { |
michael@0 | 2388 | // No frame to look at so it must not be visible. |
michael@0 | 2389 | return; |
michael@0 | 2390 | } |
michael@0 | 2391 | |
michael@0 | 2392 | // Start process now to go through all frames to find startOffset. Then check |
michael@0 | 2393 | // chars after that to see if anything until EndOffset is visible. |
michael@0 | 2394 | bool finished = false; |
michael@0 | 2395 | frame->CheckVisibility(aPresContext, aStartOffset, aEndOffset, true, |
michael@0 | 2396 | &finished, aRetval); |
michael@0 | 2397 | // Don't worry about other return value. |
michael@0 | 2398 | } |
michael@0 | 2399 | |
michael@0 | 2400 | NS_IMETHODIMP |
michael@0 | 2401 | PresShell::CheckVisibility(nsIDOMNode *node, int16_t startOffset, int16_t EndOffset, bool *_retval) |
michael@0 | 2402 | { |
michael@0 | 2403 | if (!node || startOffset>EndOffset || !_retval || startOffset<0 || EndOffset<0) |
michael@0 | 2404 | return NS_ERROR_INVALID_ARG; |
michael@0 | 2405 | *_retval = false; //initialize return parameter |
michael@0 | 2406 | nsCOMPtr<nsIContent> content(do_QueryInterface(node)); |
michael@0 | 2407 | if (!content) |
michael@0 | 2408 | return NS_ERROR_FAILURE; |
michael@0 | 2409 | |
michael@0 | 2410 | DoCheckVisibility(mPresContext, content, startOffset, EndOffset, _retval); |
michael@0 | 2411 | return NS_OK; |
michael@0 | 2412 | } |
michael@0 | 2413 | |
michael@0 | 2414 | nsresult |
michael@0 | 2415 | PresShell::CheckVisibilityContent(nsIContent* aNode, int16_t aStartOffset, |
michael@0 | 2416 | int16_t aEndOffset, bool* aRetval) |
michael@0 | 2417 | { |
michael@0 | 2418 | if (!aNode || aStartOffset > aEndOffset || !aRetval || |
michael@0 | 2419 | aStartOffset < 0 || aEndOffset < 0) { |
michael@0 | 2420 | return NS_ERROR_INVALID_ARG; |
michael@0 | 2421 | } |
michael@0 | 2422 | |
michael@0 | 2423 | *aRetval = false; |
michael@0 | 2424 | DoCheckVisibility(mPresContext, aNode, aStartOffset, aEndOffset, aRetval); |
michael@0 | 2425 | return NS_OK; |
michael@0 | 2426 | } |
michael@0 | 2427 | |
michael@0 | 2428 | //end implementations nsISelectionController |
michael@0 | 2429 | |
michael@0 | 2430 | nsIFrame* |
michael@0 | 2431 | nsIPresShell::GetRootFrameExternal() const |
michael@0 | 2432 | { |
michael@0 | 2433 | return mFrameConstructor->GetRootFrame(); |
michael@0 | 2434 | } |
michael@0 | 2435 | |
michael@0 | 2436 | nsIFrame* |
michael@0 | 2437 | nsIPresShell::GetRootScrollFrame() const |
michael@0 | 2438 | { |
michael@0 | 2439 | nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); |
michael@0 | 2440 | // Ensure root frame is a viewport frame |
michael@0 | 2441 | if (!rootFrame || nsGkAtoms::viewportFrame != rootFrame->GetType()) |
michael@0 | 2442 | return nullptr; |
michael@0 | 2443 | nsIFrame* theFrame = rootFrame->GetFirstPrincipalChild(); |
michael@0 | 2444 | if (!theFrame || nsGkAtoms::scrollFrame != theFrame->GetType()) |
michael@0 | 2445 | return nullptr; |
michael@0 | 2446 | return theFrame; |
michael@0 | 2447 | } |
michael@0 | 2448 | |
michael@0 | 2449 | nsIScrollableFrame* |
michael@0 | 2450 | nsIPresShell::GetRootScrollFrameAsScrollable() const |
michael@0 | 2451 | { |
michael@0 | 2452 | nsIFrame* frame = GetRootScrollFrame(); |
michael@0 | 2453 | if (!frame) |
michael@0 | 2454 | return nullptr; |
michael@0 | 2455 | nsIScrollableFrame* scrollableFrame = do_QueryFrame(frame); |
michael@0 | 2456 | NS_ASSERTION(scrollableFrame, |
michael@0 | 2457 | "All scroll frames must implement nsIScrollableFrame"); |
michael@0 | 2458 | return scrollableFrame; |
michael@0 | 2459 | } |
michael@0 | 2460 | |
michael@0 | 2461 | nsIScrollableFrame* |
michael@0 | 2462 | nsIPresShell::GetRootScrollFrameAsScrollableExternal() const |
michael@0 | 2463 | { |
michael@0 | 2464 | return GetRootScrollFrameAsScrollable(); |
michael@0 | 2465 | } |
michael@0 | 2466 | |
michael@0 | 2467 | nsIPageSequenceFrame* |
michael@0 | 2468 | PresShell::GetPageSequenceFrame() const |
michael@0 | 2469 | { |
michael@0 | 2470 | nsIFrame* frame = mFrameConstructor->GetPageSequenceFrame(); |
michael@0 | 2471 | return do_QueryFrame(frame); |
michael@0 | 2472 | } |
michael@0 | 2473 | |
michael@0 | 2474 | void |
michael@0 | 2475 | PresShell::BeginUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType) |
michael@0 | 2476 | { |
michael@0 | 2477 | #ifdef DEBUG |
michael@0 | 2478 | mUpdateCount++; |
michael@0 | 2479 | #endif |
michael@0 | 2480 | mFrameConstructor->BeginUpdate(); |
michael@0 | 2481 | |
michael@0 | 2482 | if (aUpdateType & UPDATE_STYLE) |
michael@0 | 2483 | mStyleSet->BeginUpdate(); |
michael@0 | 2484 | } |
michael@0 | 2485 | |
michael@0 | 2486 | void |
michael@0 | 2487 | PresShell::EndUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType) |
michael@0 | 2488 | { |
michael@0 | 2489 | #ifdef DEBUG |
michael@0 | 2490 | NS_PRECONDITION(0 != mUpdateCount, "too many EndUpdate's"); |
michael@0 | 2491 | --mUpdateCount; |
michael@0 | 2492 | #endif |
michael@0 | 2493 | |
michael@0 | 2494 | if (aUpdateType & UPDATE_STYLE) { |
michael@0 | 2495 | mStyleSet->EndUpdate(); |
michael@0 | 2496 | if (mStylesHaveChanged || !mChangedScopeStyleRoots.IsEmpty()) |
michael@0 | 2497 | ReconstructStyleData(); |
michael@0 | 2498 | } |
michael@0 | 2499 | |
michael@0 | 2500 | mFrameConstructor->EndUpdate(); |
michael@0 | 2501 | } |
michael@0 | 2502 | |
michael@0 | 2503 | void |
michael@0 | 2504 | PresShell::RestoreRootScrollPosition() |
michael@0 | 2505 | { |
michael@0 | 2506 | nsIScrollableFrame* scrollableFrame = GetRootScrollFrameAsScrollable(); |
michael@0 | 2507 | if (scrollableFrame) { |
michael@0 | 2508 | scrollableFrame->ScrollToRestoredPosition(); |
michael@0 | 2509 | } |
michael@0 | 2510 | } |
michael@0 | 2511 | |
michael@0 | 2512 | void |
michael@0 | 2513 | PresShell::BeginLoad(nsIDocument *aDocument) |
michael@0 | 2514 | { |
michael@0 | 2515 | mDocumentLoading = true; |
michael@0 | 2516 | |
michael@0 | 2517 | #ifdef PR_LOGGING |
michael@0 | 2518 | gfxTextPerfMetrics *tp = nullptr; |
michael@0 | 2519 | if (mPresContext) { |
michael@0 | 2520 | tp = mPresContext->GetTextPerfMetrics(); |
michael@0 | 2521 | } |
michael@0 | 2522 | |
michael@0 | 2523 | bool shouldLog = gLog && PR_LOG_TEST(gLog, PR_LOG_DEBUG); |
michael@0 | 2524 | if (shouldLog || tp) { |
michael@0 | 2525 | mLoadBegin = TimeStamp::Now(); |
michael@0 | 2526 | } |
michael@0 | 2527 | |
michael@0 | 2528 | if (shouldLog) { |
michael@0 | 2529 | nsIURI* uri = mDocument->GetDocumentURI(); |
michael@0 | 2530 | nsAutoCString spec; |
michael@0 | 2531 | if (uri) { |
michael@0 | 2532 | uri->GetSpec(spec); |
michael@0 | 2533 | } |
michael@0 | 2534 | PR_LOG(gLog, PR_LOG_DEBUG, |
michael@0 | 2535 | ("(presshell) %p load begin [%s]\n", |
michael@0 | 2536 | this, spec.get())); |
michael@0 | 2537 | } |
michael@0 | 2538 | #endif |
michael@0 | 2539 | } |
michael@0 | 2540 | |
michael@0 | 2541 | void |
michael@0 | 2542 | PresShell::EndLoad(nsIDocument *aDocument) |
michael@0 | 2543 | { |
michael@0 | 2544 | NS_PRECONDITION(aDocument == mDocument, "Wrong document"); |
michael@0 | 2545 | |
michael@0 | 2546 | RestoreRootScrollPosition(); |
michael@0 | 2547 | |
michael@0 | 2548 | mDocumentLoading = false; |
michael@0 | 2549 | } |
michael@0 | 2550 | |
michael@0 | 2551 | void |
michael@0 | 2552 | PresShell::LoadComplete() |
michael@0 | 2553 | { |
michael@0 | 2554 | #ifdef PR_LOGGING |
michael@0 | 2555 | gfxTextPerfMetrics *tp = nullptr; |
michael@0 | 2556 | if (mPresContext) { |
michael@0 | 2557 | tp = mPresContext->GetTextPerfMetrics(); |
michael@0 | 2558 | } |
michael@0 | 2559 | |
michael@0 | 2560 | // log load |
michael@0 | 2561 | bool shouldLog = gLog && PR_LOG_TEST(gLog, PR_LOG_DEBUG); |
michael@0 | 2562 | if (shouldLog || tp) { |
michael@0 | 2563 | TimeDuration loadTime = TimeStamp::Now() - mLoadBegin; |
michael@0 | 2564 | nsIURI* uri = mDocument->GetDocumentURI(); |
michael@0 | 2565 | nsAutoCString spec; |
michael@0 | 2566 | if (uri) { |
michael@0 | 2567 | uri->GetSpec(spec); |
michael@0 | 2568 | } |
michael@0 | 2569 | if (shouldLog) { |
michael@0 | 2570 | PR_LOG(gLog, PR_LOG_DEBUG, |
michael@0 | 2571 | ("(presshell) %p load done time-ms: %9.2f [%s]\n", |
michael@0 | 2572 | this, loadTime.ToMilliseconds(), spec.get())); |
michael@0 | 2573 | } |
michael@0 | 2574 | if (tp) { |
michael@0 | 2575 | tp->Accumulate(); |
michael@0 | 2576 | if (tp->cumulative.numChars > 0) { |
michael@0 | 2577 | LogTextPerfStats(tp, this, tp->cumulative, loadTime.ToMilliseconds(), |
michael@0 | 2578 | eLog_loaddone, spec.get()); |
michael@0 | 2579 | } |
michael@0 | 2580 | } |
michael@0 | 2581 | } |
michael@0 | 2582 | #endif |
michael@0 | 2583 | } |
michael@0 | 2584 | |
michael@0 | 2585 | #ifdef DEBUG |
michael@0 | 2586 | void |
michael@0 | 2587 | PresShell::VerifyHasDirtyRootAncestor(nsIFrame* aFrame) |
michael@0 | 2588 | { |
michael@0 | 2589 | // XXXbz due to bug 372769, can't actually assert anything here... |
michael@0 | 2590 | return; |
michael@0 | 2591 | |
michael@0 | 2592 | // XXXbz shouldn't need this part; remove it once FrameNeedsReflow |
michael@0 | 2593 | // handles the root frame correctly. |
michael@0 | 2594 | if (!aFrame->GetParent()) { |
michael@0 | 2595 | return; |
michael@0 | 2596 | } |
michael@0 | 2597 | |
michael@0 | 2598 | // Make sure that there is a reflow root ancestor of |aFrame| that's |
michael@0 | 2599 | // in mDirtyRoots already. |
michael@0 | 2600 | while (aFrame && (aFrame->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN)) { |
michael@0 | 2601 | if (((aFrame->GetStateBits() & NS_FRAME_REFLOW_ROOT) || |
michael@0 | 2602 | !aFrame->GetParent()) && |
michael@0 | 2603 | mDirtyRoots.Contains(aFrame)) { |
michael@0 | 2604 | return; |
michael@0 | 2605 | } |
michael@0 | 2606 | |
michael@0 | 2607 | aFrame = aFrame->GetParent(); |
michael@0 | 2608 | } |
michael@0 | 2609 | NS_NOTREACHED("Frame has dirty bits set but isn't scheduled to be " |
michael@0 | 2610 | "reflowed?"); |
michael@0 | 2611 | } |
michael@0 | 2612 | #endif |
michael@0 | 2613 | |
michael@0 | 2614 | void |
michael@0 | 2615 | PresShell::FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty, |
michael@0 | 2616 | nsFrameState aBitToAdd) |
michael@0 | 2617 | { |
michael@0 | 2618 | NS_PRECONDITION(aBitToAdd == NS_FRAME_IS_DIRTY || |
michael@0 | 2619 | aBitToAdd == NS_FRAME_HAS_DIRTY_CHILDREN || |
michael@0 | 2620 | !aBitToAdd, |
michael@0 | 2621 | "Unexpected bits being added"); |
michael@0 | 2622 | NS_PRECONDITION(!(aIntrinsicDirty == eStyleChange && |
michael@0 | 2623 | aBitToAdd == NS_FRAME_HAS_DIRTY_CHILDREN), |
michael@0 | 2624 | "bits don't correspond to style change reason"); |
michael@0 | 2625 | |
michael@0 | 2626 | NS_ASSERTION(!mIsReflowing, "can't mark frame dirty during reflow"); |
michael@0 | 2627 | |
michael@0 | 2628 | // If we've not yet done the initial reflow, then don't bother |
michael@0 | 2629 | // enqueuing a reflow command yet. |
michael@0 | 2630 | if (! mDidInitialize) |
michael@0 | 2631 | return; |
michael@0 | 2632 | |
michael@0 | 2633 | // If we're already destroying, don't bother with this either. |
michael@0 | 2634 | if (mIsDestroying) |
michael@0 | 2635 | return; |
michael@0 | 2636 | |
michael@0 | 2637 | #ifdef DEBUG |
michael@0 | 2638 | //printf("gShellCounter: %d\n", gShellCounter++); |
michael@0 | 2639 | if (mInVerifyReflow) |
michael@0 | 2640 | return; |
michael@0 | 2641 | |
michael@0 | 2642 | if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) { |
michael@0 | 2643 | printf("\nPresShell@%p: frame %p needs reflow\n", (void*)this, (void*)aFrame); |
michael@0 | 2644 | if (VERIFY_REFLOW_REALLY_NOISY_RC & gVerifyReflowFlags) { |
michael@0 | 2645 | printf("Current content model:\n"); |
michael@0 | 2646 | Element *rootElement = mDocument->GetRootElement(); |
michael@0 | 2647 | if (rootElement) { |
michael@0 | 2648 | rootElement->List(stdout, 0); |
michael@0 | 2649 | } |
michael@0 | 2650 | } |
michael@0 | 2651 | } |
michael@0 | 2652 | #endif |
michael@0 | 2653 | |
michael@0 | 2654 | nsAutoTArray<nsIFrame*, 4> subtrees; |
michael@0 | 2655 | subtrees.AppendElement(aFrame); |
michael@0 | 2656 | |
michael@0 | 2657 | do { |
michael@0 | 2658 | nsIFrame *subtreeRoot = subtrees.ElementAt(subtrees.Length() - 1); |
michael@0 | 2659 | subtrees.RemoveElementAt(subtrees.Length() - 1); |
michael@0 | 2660 | |
michael@0 | 2661 | // Grab |wasDirty| now so we can go ahead and update the bits on |
michael@0 | 2662 | // subtreeRoot. |
michael@0 | 2663 | bool wasDirty = NS_SUBTREE_DIRTY(subtreeRoot); |
michael@0 | 2664 | subtreeRoot->AddStateBits(aBitToAdd); |
michael@0 | 2665 | |
michael@0 | 2666 | // Now if subtreeRoot is a reflow root we can cut off this reflow at it if |
michael@0 | 2667 | // the bit being added is NS_FRAME_HAS_DIRTY_CHILDREN. |
michael@0 | 2668 | bool targetFrameDirty = (aBitToAdd == NS_FRAME_IS_DIRTY); |
michael@0 | 2669 | |
michael@0 | 2670 | #define FRAME_IS_REFLOW_ROOT(_f) \ |
michael@0 | 2671 | ((_f->GetStateBits() & NS_FRAME_REFLOW_ROOT) && \ |
michael@0 | 2672 | (_f != subtreeRoot || !targetFrameDirty)) |
michael@0 | 2673 | |
michael@0 | 2674 | |
michael@0 | 2675 | // Mark the intrinsic widths as dirty on the frame, all of its ancestors, |
michael@0 | 2676 | // and all of its descendants, if needed: |
michael@0 | 2677 | |
michael@0 | 2678 | if (aIntrinsicDirty != eResize) { |
michael@0 | 2679 | // Mark argument and all ancestors dirty. (Unless we hit a reflow |
michael@0 | 2680 | // root that should contain the reflow. That root could be |
michael@0 | 2681 | // subtreeRoot itself if it's not dirty, or it could be some |
michael@0 | 2682 | // ancestor of subtreeRoot.) |
michael@0 | 2683 | for (nsIFrame *a = subtreeRoot; |
michael@0 | 2684 | a && !FRAME_IS_REFLOW_ROOT(a); |
michael@0 | 2685 | a = a->GetParent()) |
michael@0 | 2686 | a->MarkIntrinsicWidthsDirty(); |
michael@0 | 2687 | } |
michael@0 | 2688 | |
michael@0 | 2689 | if (aIntrinsicDirty == eStyleChange) { |
michael@0 | 2690 | // Mark all descendants dirty (using an nsTArray stack rather than |
michael@0 | 2691 | // recursion). |
michael@0 | 2692 | // Note that nsHTMLReflowState::InitResizeFlags has some similar |
michael@0 | 2693 | // code; see comments there for how and why it differs. |
michael@0 | 2694 | nsAutoTArray<nsIFrame*, 32> stack; |
michael@0 | 2695 | stack.AppendElement(subtreeRoot); |
michael@0 | 2696 | |
michael@0 | 2697 | do { |
michael@0 | 2698 | nsIFrame *f = stack.ElementAt(stack.Length() - 1); |
michael@0 | 2699 | stack.RemoveElementAt(stack.Length() - 1); |
michael@0 | 2700 | |
michael@0 | 2701 | if (f->GetType() == nsGkAtoms::placeholderFrame) { |
michael@0 | 2702 | nsIFrame *oof = nsPlaceholderFrame::GetRealFrameForPlaceholder(f); |
michael@0 | 2703 | if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot, oof)) { |
michael@0 | 2704 | // We have another distinct subtree we need to mark. |
michael@0 | 2705 | subtrees.AppendElement(oof); |
michael@0 | 2706 | } |
michael@0 | 2707 | } |
michael@0 | 2708 | |
michael@0 | 2709 | nsIFrame::ChildListIterator lists(f); |
michael@0 | 2710 | for (; !lists.IsDone(); lists.Next()) { |
michael@0 | 2711 | nsFrameList::Enumerator childFrames(lists.CurrentList()); |
michael@0 | 2712 | for (; !childFrames.AtEnd(); childFrames.Next()) { |
michael@0 | 2713 | nsIFrame* kid = childFrames.get(); |
michael@0 | 2714 | kid->MarkIntrinsicWidthsDirty(); |
michael@0 | 2715 | stack.AppendElement(kid); |
michael@0 | 2716 | } |
michael@0 | 2717 | } |
michael@0 | 2718 | } while (stack.Length() != 0); |
michael@0 | 2719 | } |
michael@0 | 2720 | |
michael@0 | 2721 | // Skip setting dirty bits up the tree if we weren't given a bit to add. |
michael@0 | 2722 | if (!aBitToAdd) { |
michael@0 | 2723 | continue; |
michael@0 | 2724 | } |
michael@0 | 2725 | |
michael@0 | 2726 | // Set NS_FRAME_HAS_DIRTY_CHILDREN bits (via nsIFrame::ChildIsDirty) |
michael@0 | 2727 | // up the tree until we reach either a frame that's already dirty or |
michael@0 | 2728 | // a reflow root. |
michael@0 | 2729 | nsIFrame *f = subtreeRoot; |
michael@0 | 2730 | for (;;) { |
michael@0 | 2731 | if (FRAME_IS_REFLOW_ROOT(f) || !f->GetParent()) { |
michael@0 | 2732 | // we've hit a reflow root or the root frame |
michael@0 | 2733 | if (!wasDirty) { |
michael@0 | 2734 | mDirtyRoots.AppendElement(f); |
michael@0 | 2735 | mDocument->SetNeedLayoutFlush(); |
michael@0 | 2736 | } |
michael@0 | 2737 | #ifdef DEBUG |
michael@0 | 2738 | else { |
michael@0 | 2739 | VerifyHasDirtyRootAncestor(f); |
michael@0 | 2740 | } |
michael@0 | 2741 | #endif |
michael@0 | 2742 | |
michael@0 | 2743 | break; |
michael@0 | 2744 | } |
michael@0 | 2745 | |
michael@0 | 2746 | nsIFrame *child = f; |
michael@0 | 2747 | f = f->GetParent(); |
michael@0 | 2748 | wasDirty = NS_SUBTREE_DIRTY(f); |
michael@0 | 2749 | f->ChildIsDirty(child); |
michael@0 | 2750 | NS_ASSERTION(f->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN, |
michael@0 | 2751 | "ChildIsDirty didn't do its job"); |
michael@0 | 2752 | if (wasDirty) { |
michael@0 | 2753 | // This frame was already marked dirty. |
michael@0 | 2754 | #ifdef DEBUG |
michael@0 | 2755 | VerifyHasDirtyRootAncestor(f); |
michael@0 | 2756 | #endif |
michael@0 | 2757 | break; |
michael@0 | 2758 | } |
michael@0 | 2759 | } |
michael@0 | 2760 | } while (subtrees.Length() != 0); |
michael@0 | 2761 | |
michael@0 | 2762 | MaybeScheduleReflow(); |
michael@0 | 2763 | } |
michael@0 | 2764 | |
michael@0 | 2765 | void |
michael@0 | 2766 | PresShell::FrameNeedsToContinueReflow(nsIFrame *aFrame) |
michael@0 | 2767 | { |
michael@0 | 2768 | NS_ASSERTION(mIsReflowing, "Must be in reflow when marking path dirty."); |
michael@0 | 2769 | NS_PRECONDITION(mCurrentReflowRoot, "Must have a current reflow root here"); |
michael@0 | 2770 | NS_ASSERTION(aFrame == mCurrentReflowRoot || |
michael@0 | 2771 | nsLayoutUtils::IsProperAncestorFrame(mCurrentReflowRoot, aFrame), |
michael@0 | 2772 | "Frame passed in is not the descendant of mCurrentReflowRoot"); |
michael@0 | 2773 | NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW, |
michael@0 | 2774 | "Frame passed in not in reflow?"); |
michael@0 | 2775 | |
michael@0 | 2776 | mFramesToDirty.PutEntry(aFrame); |
michael@0 | 2777 | } |
michael@0 | 2778 | |
michael@0 | 2779 | nsIScrollableFrame* |
michael@0 | 2780 | nsIPresShell::GetFrameToScrollAsScrollable( |
michael@0 | 2781 | nsIPresShell::ScrollDirection aDirection) |
michael@0 | 2782 | { |
michael@0 | 2783 | nsIScrollableFrame* scrollFrame = nullptr; |
michael@0 | 2784 | |
michael@0 | 2785 | nsCOMPtr<nsIContent> focusedContent; |
michael@0 | 2786 | nsIFocusManager* fm = nsFocusManager::GetFocusManager(); |
michael@0 | 2787 | if (fm && mDocument) { |
michael@0 | 2788 | nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(mDocument->GetWindow()); |
michael@0 | 2789 | |
michael@0 | 2790 | nsCOMPtr<nsIDOMElement> focusedElement; |
michael@0 | 2791 | fm->GetFocusedElementForWindow(window, false, nullptr, getter_AddRefs(focusedElement)); |
michael@0 | 2792 | focusedContent = do_QueryInterface(focusedElement); |
michael@0 | 2793 | } |
michael@0 | 2794 | if (!focusedContent && mSelection) { |
michael@0 | 2795 | nsISelection* domSelection = mSelection-> |
michael@0 | 2796 | GetSelection(nsISelectionController::SELECTION_NORMAL); |
michael@0 | 2797 | if (domSelection) { |
michael@0 | 2798 | nsCOMPtr<nsIDOMNode> focusedNode; |
michael@0 | 2799 | domSelection->GetFocusNode(getter_AddRefs(focusedNode)); |
michael@0 | 2800 | focusedContent = do_QueryInterface(focusedNode); |
michael@0 | 2801 | } |
michael@0 | 2802 | } |
michael@0 | 2803 | if (focusedContent) { |
michael@0 | 2804 | nsIFrame* startFrame = focusedContent->GetPrimaryFrame(); |
michael@0 | 2805 | if (startFrame) { |
michael@0 | 2806 | scrollFrame = startFrame->GetScrollTargetFrame(); |
michael@0 | 2807 | if (scrollFrame) { |
michael@0 | 2808 | startFrame = scrollFrame->GetScrolledFrame(); |
michael@0 | 2809 | } |
michael@0 | 2810 | if (aDirection == nsIPresShell::eEither) { |
michael@0 | 2811 | scrollFrame = |
michael@0 | 2812 | nsLayoutUtils::GetNearestScrollableFrame(startFrame); |
michael@0 | 2813 | } else { |
michael@0 | 2814 | scrollFrame = |
michael@0 | 2815 | nsLayoutUtils::GetNearestScrollableFrameForDirection(startFrame, |
michael@0 | 2816 | aDirection == eVertical ? nsLayoutUtils::eVertical : |
michael@0 | 2817 | nsLayoutUtils::eHorizontal); |
michael@0 | 2818 | } |
michael@0 | 2819 | } |
michael@0 | 2820 | } |
michael@0 | 2821 | if (!scrollFrame) { |
michael@0 | 2822 | scrollFrame = GetRootScrollFrameAsScrollable(); |
michael@0 | 2823 | } |
michael@0 | 2824 | return scrollFrame; |
michael@0 | 2825 | } |
michael@0 | 2826 | |
michael@0 | 2827 | void |
michael@0 | 2828 | PresShell::CancelAllPendingReflows() |
michael@0 | 2829 | { |
michael@0 | 2830 | mDirtyRoots.Clear(); |
michael@0 | 2831 | |
michael@0 | 2832 | if (mReflowScheduled) { |
michael@0 | 2833 | GetPresContext()->RefreshDriver()->RemoveLayoutFlushObserver(this); |
michael@0 | 2834 | mReflowScheduled = false; |
michael@0 | 2835 | } |
michael@0 | 2836 | |
michael@0 | 2837 | ASSERT_REFLOW_SCHEDULED_STATE(); |
michael@0 | 2838 | } |
michael@0 | 2839 | |
michael@0 | 2840 | nsresult |
michael@0 | 2841 | PresShell::RecreateFramesFor(nsIContent* aContent) |
michael@0 | 2842 | { |
michael@0 | 2843 | NS_ENSURE_TRUE(mPresContext, NS_ERROR_FAILURE); |
michael@0 | 2844 | if (!mDidInitialize) { |
michael@0 | 2845 | // Nothing to do here. In fact, if we proceed and aContent is the |
michael@0 | 2846 | // root we will crash. |
michael@0 | 2847 | return NS_OK; |
michael@0 | 2848 | } |
michael@0 | 2849 | |
michael@0 | 2850 | // Don't call RecreateFramesForContent since that is not exported and we want |
michael@0 | 2851 | // to keep the number of entrypoints down. |
michael@0 | 2852 | |
michael@0 | 2853 | NS_ASSERTION(mViewManager, "Should have view manager"); |
michael@0 | 2854 | |
michael@0 | 2855 | // Have to make sure that the content notifications are flushed before we |
michael@0 | 2856 | // start messing with the frame model; otherwise we can get content doubling. |
michael@0 | 2857 | mDocument->FlushPendingNotifications(Flush_ContentAndNotify); |
michael@0 | 2858 | |
michael@0 | 2859 | nsAutoScriptBlocker scriptBlocker; |
michael@0 | 2860 | |
michael@0 | 2861 | nsStyleChangeList changeList; |
michael@0 | 2862 | changeList.AppendChange(nullptr, aContent, nsChangeHint_ReconstructFrame); |
michael@0 | 2863 | |
michael@0 | 2864 | // Mark ourselves as not safe to flush while we're doing frame construction. |
michael@0 | 2865 | ++mChangeNestCount; |
michael@0 | 2866 | RestyleManager* restyleManager = mPresContext->RestyleManager(); |
michael@0 | 2867 | nsresult rv = restyleManager->ProcessRestyledFrames(changeList); |
michael@0 | 2868 | restyleManager->FlushOverflowChangedTracker(); |
michael@0 | 2869 | --mChangeNestCount; |
michael@0 | 2870 | |
michael@0 | 2871 | return rv; |
michael@0 | 2872 | } |
michael@0 | 2873 | |
michael@0 | 2874 | void |
michael@0 | 2875 | nsIPresShell::PostRecreateFramesFor(Element* aElement) |
michael@0 | 2876 | { |
michael@0 | 2877 | mPresContext->RestyleManager()->PostRestyleEvent(aElement, nsRestyleHint(0), |
michael@0 | 2878 | nsChangeHint_ReconstructFrame); |
michael@0 | 2879 | } |
michael@0 | 2880 | |
michael@0 | 2881 | void |
michael@0 | 2882 | nsIPresShell::RestyleForAnimation(Element* aElement, nsRestyleHint aHint) |
michael@0 | 2883 | { |
michael@0 | 2884 | mPresContext->RestyleManager()->PostAnimationRestyleEvent(aElement, aHint, |
michael@0 | 2885 | NS_STYLE_HINT_NONE); |
michael@0 | 2886 | } |
michael@0 | 2887 | |
michael@0 | 2888 | void |
michael@0 | 2889 | nsIPresShell::SetForwardingContainer(const WeakPtr<nsDocShell> &aContainer) |
michael@0 | 2890 | { |
michael@0 | 2891 | mForwardingContainer = aContainer; |
michael@0 | 2892 | } |
michael@0 | 2893 | |
michael@0 | 2894 | void |
michael@0 | 2895 | PresShell::ClearFrameRefs(nsIFrame* aFrame) |
michael@0 | 2896 | { |
michael@0 | 2897 | mPresContext->EventStateManager()->ClearFrameRefs(aFrame); |
michael@0 | 2898 | |
michael@0 | 2899 | nsWeakFrame* weakFrame = mWeakFrames; |
michael@0 | 2900 | while (weakFrame) { |
michael@0 | 2901 | nsWeakFrame* prev = weakFrame->GetPreviousWeakFrame(); |
michael@0 | 2902 | if (weakFrame->GetFrame() == aFrame) { |
michael@0 | 2903 | // This removes weakFrame from mWeakFrames. |
michael@0 | 2904 | weakFrame->Clear(this); |
michael@0 | 2905 | } |
michael@0 | 2906 | weakFrame = prev; |
michael@0 | 2907 | } |
michael@0 | 2908 | } |
michael@0 | 2909 | |
michael@0 | 2910 | already_AddRefed<nsRenderingContext> |
michael@0 | 2911 | PresShell::CreateReferenceRenderingContext() |
michael@0 | 2912 | { |
michael@0 | 2913 | nsDeviceContext* devCtx = mPresContext->DeviceContext(); |
michael@0 | 2914 | nsRefPtr<nsRenderingContext> rc; |
michael@0 | 2915 | if (mPresContext->IsScreen()) { |
michael@0 | 2916 | rc = new nsRenderingContext(); |
michael@0 | 2917 | rc->Init(devCtx, gfxPlatform::GetPlatform()->ScreenReferenceSurface()); |
michael@0 | 2918 | } else { |
michael@0 | 2919 | rc = devCtx->CreateRenderingContext(); |
michael@0 | 2920 | } |
michael@0 | 2921 | |
michael@0 | 2922 | MOZ_ASSERT(rc, "shouldn't break promise to return non-null"); |
michael@0 | 2923 | return rc.forget(); |
michael@0 | 2924 | } |
michael@0 | 2925 | |
michael@0 | 2926 | nsresult |
michael@0 | 2927 | PresShell::GoToAnchor(const nsAString& aAnchorName, bool aScroll) |
michael@0 | 2928 | { |
michael@0 | 2929 | if (!mDocument) { |
michael@0 | 2930 | return NS_ERROR_FAILURE; |
michael@0 | 2931 | } |
michael@0 | 2932 | |
michael@0 | 2933 | const Element *root = mDocument->GetRootElement(); |
michael@0 | 2934 | if (root && root->IsSVG(nsGkAtoms::svg)) { |
michael@0 | 2935 | // We need to execute this even if there is an empty anchor name |
michael@0 | 2936 | // so that any existing SVG fragment identifier effect is removed |
michael@0 | 2937 | if (SVGFragmentIdentifier::ProcessFragmentIdentifier(mDocument, aAnchorName)) { |
michael@0 | 2938 | return NS_OK; |
michael@0 | 2939 | } |
michael@0 | 2940 | } |
michael@0 | 2941 | |
michael@0 | 2942 | // Hold a reference to the ESM in case event dispatch tears us down. |
michael@0 | 2943 | nsRefPtr<EventStateManager> esm = mPresContext->EventStateManager(); |
michael@0 | 2944 | |
michael@0 | 2945 | if (aAnchorName.IsEmpty()) { |
michael@0 | 2946 | NS_ASSERTION(!aScroll, "can't scroll to empty anchor name"); |
michael@0 | 2947 | esm->SetContentState(nullptr, NS_EVENT_STATE_URLTARGET); |
michael@0 | 2948 | return NS_OK; |
michael@0 | 2949 | } |
michael@0 | 2950 | |
michael@0 | 2951 | nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(mDocument); |
michael@0 | 2952 | nsresult rv = NS_OK; |
michael@0 | 2953 | nsCOMPtr<nsIContent> content; |
michael@0 | 2954 | |
michael@0 | 2955 | // Search for an element with a matching "id" attribute |
michael@0 | 2956 | if (mDocument) { |
michael@0 | 2957 | content = mDocument->GetElementById(aAnchorName); |
michael@0 | 2958 | } |
michael@0 | 2959 | |
michael@0 | 2960 | // Search for an anchor element with a matching "name" attribute |
michael@0 | 2961 | if (!content && htmlDoc) { |
michael@0 | 2962 | nsCOMPtr<nsIDOMNodeList> list; |
michael@0 | 2963 | // Find a matching list of named nodes |
michael@0 | 2964 | rv = htmlDoc->GetElementsByName(aAnchorName, getter_AddRefs(list)); |
michael@0 | 2965 | if (NS_SUCCEEDED(rv) && list) { |
michael@0 | 2966 | uint32_t i; |
michael@0 | 2967 | // Loop through the named nodes looking for the first anchor |
michael@0 | 2968 | for (i = 0; true; i++) { |
michael@0 | 2969 | nsCOMPtr<nsIDOMNode> node; |
michael@0 | 2970 | rv = list->Item(i, getter_AddRefs(node)); |
michael@0 | 2971 | if (!node) { // End of list |
michael@0 | 2972 | break; |
michael@0 | 2973 | } |
michael@0 | 2974 | // Ensure it's an anchor element |
michael@0 | 2975 | content = do_QueryInterface(node); |
michael@0 | 2976 | if (content) { |
michael@0 | 2977 | if (content->Tag() == nsGkAtoms::a && content->IsHTML()) { |
michael@0 | 2978 | break; |
michael@0 | 2979 | } |
michael@0 | 2980 | content = nullptr; |
michael@0 | 2981 | } |
michael@0 | 2982 | } |
michael@0 | 2983 | } |
michael@0 | 2984 | } |
michael@0 | 2985 | |
michael@0 | 2986 | // Search for anchor in the HTML namespace with a matching name |
michael@0 | 2987 | if (!content && !htmlDoc) |
michael@0 | 2988 | { |
michael@0 | 2989 | nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(mDocument); |
michael@0 | 2990 | nsCOMPtr<nsIDOMNodeList> list; |
michael@0 | 2991 | NS_NAMED_LITERAL_STRING(nameSpace, "http://www.w3.org/1999/xhtml"); |
michael@0 | 2992 | // Get the list of anchor elements |
michael@0 | 2993 | rv = doc->GetElementsByTagNameNS(nameSpace, NS_LITERAL_STRING("a"), getter_AddRefs(list)); |
michael@0 | 2994 | if (NS_SUCCEEDED(rv) && list) { |
michael@0 | 2995 | uint32_t i; |
michael@0 | 2996 | // Loop through the named nodes looking for the first anchor |
michael@0 | 2997 | for (i = 0; true; i++) { |
michael@0 | 2998 | nsCOMPtr<nsIDOMNode> node; |
michael@0 | 2999 | rv = list->Item(i, getter_AddRefs(node)); |
michael@0 | 3000 | if (!node) { // End of list |
michael@0 | 3001 | break; |
michael@0 | 3002 | } |
michael@0 | 3003 | // Compare the name attribute |
michael@0 | 3004 | nsCOMPtr<nsIDOMElement> element = do_QueryInterface(node); |
michael@0 | 3005 | nsAutoString value; |
michael@0 | 3006 | if (element && NS_SUCCEEDED(element->GetAttribute(NS_LITERAL_STRING("name"), value))) { |
michael@0 | 3007 | if (value.Equals(aAnchorName)) { |
michael@0 | 3008 | content = do_QueryInterface(element); |
michael@0 | 3009 | break; |
michael@0 | 3010 | } |
michael@0 | 3011 | } |
michael@0 | 3012 | } |
michael@0 | 3013 | } |
michael@0 | 3014 | } |
michael@0 | 3015 | |
michael@0 | 3016 | esm->SetContentState(content, NS_EVENT_STATE_URLTARGET); |
michael@0 | 3017 | |
michael@0 | 3018 | #ifdef ACCESSIBILITY |
michael@0 | 3019 | nsIContent *anchorTarget = content; |
michael@0 | 3020 | #endif |
michael@0 | 3021 | |
michael@0 | 3022 | nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable(); |
michael@0 | 3023 | if (rootScroll && rootScroll->DidHistoryRestore()) { |
michael@0 | 3024 | // Scroll position restored from history trumps scrolling to anchor. |
michael@0 | 3025 | aScroll = false; |
michael@0 | 3026 | rootScroll->ClearDidHistoryRestore(); |
michael@0 | 3027 | } |
michael@0 | 3028 | |
michael@0 | 3029 | if (content) { |
michael@0 | 3030 | if (aScroll) { |
michael@0 | 3031 | rv = ScrollContentIntoView(content, |
michael@0 | 3032 | ScrollAxis(SCROLL_TOP, SCROLL_ALWAYS), |
michael@0 | 3033 | ScrollAxis(), |
michael@0 | 3034 | ANCHOR_SCROLL_FLAGS); |
michael@0 | 3035 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 3036 | |
michael@0 | 3037 | nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable(); |
michael@0 | 3038 | if (rootScroll) { |
michael@0 | 3039 | mLastAnchorScrolledTo = content; |
michael@0 | 3040 | mLastAnchorScrollPositionY = rootScroll->GetScrollPosition().y; |
michael@0 | 3041 | } |
michael@0 | 3042 | } |
michael@0 | 3043 | |
michael@0 | 3044 | // Should we select the target? This action is controlled by a |
michael@0 | 3045 | // preference: the default is to not select. |
michael@0 | 3046 | bool selectAnchor = Preferences::GetBool("layout.selectanchor"); |
michael@0 | 3047 | |
michael@0 | 3048 | // Even if select anchor pref is false, we must still move the |
michael@0 | 3049 | // caret there. That way tabbing will start from the new |
michael@0 | 3050 | // location |
michael@0 | 3051 | nsRefPtr<nsIDOMRange> jumpToRange = new nsRange(mDocument); |
michael@0 | 3052 | while (content && content->GetFirstChild()) { |
michael@0 | 3053 | content = content->GetFirstChild(); |
michael@0 | 3054 | } |
michael@0 | 3055 | nsCOMPtr<nsIDOMNode> node(do_QueryInterface(content)); |
michael@0 | 3056 | NS_ASSERTION(node, "No nsIDOMNode for descendant of anchor"); |
michael@0 | 3057 | jumpToRange->SelectNodeContents(node); |
michael@0 | 3058 | // Select the anchor |
michael@0 | 3059 | nsISelection* sel = mSelection-> |
michael@0 | 3060 | GetSelection(nsISelectionController::SELECTION_NORMAL); |
michael@0 | 3061 | if (sel) { |
michael@0 | 3062 | sel->RemoveAllRanges(); |
michael@0 | 3063 | sel->AddRange(jumpToRange); |
michael@0 | 3064 | if (!selectAnchor) { |
michael@0 | 3065 | // Use a caret (collapsed selection) at the start of the anchor |
michael@0 | 3066 | sel->CollapseToStart(); |
michael@0 | 3067 | } |
michael@0 | 3068 | } |
michael@0 | 3069 | // Selection is at anchor. |
michael@0 | 3070 | // Now focus the document itself if focus is on an element within it. |
michael@0 | 3071 | nsPIDOMWindow *win = mDocument->GetWindow(); |
michael@0 | 3072 | |
michael@0 | 3073 | nsIFocusManager* fm = nsFocusManager::GetFocusManager(); |
michael@0 | 3074 | if (fm && win) { |
michael@0 | 3075 | nsCOMPtr<nsIDOMWindow> focusedWindow; |
michael@0 | 3076 | fm->GetFocusedWindow(getter_AddRefs(focusedWindow)); |
michael@0 | 3077 | if (SameCOMIdentity(win, focusedWindow)) { |
michael@0 | 3078 | fm->ClearFocus(focusedWindow); |
michael@0 | 3079 | } |
michael@0 | 3080 | } |
michael@0 | 3081 | |
michael@0 | 3082 | // If the target is an animation element, activate the animation |
michael@0 | 3083 | if (content->IsNodeOfType(nsINode::eANIMATION)) { |
michael@0 | 3084 | SVGContentUtils::ActivateByHyperlink(content.get()); |
michael@0 | 3085 | } |
michael@0 | 3086 | } else { |
michael@0 | 3087 | rv = NS_ERROR_FAILURE; |
michael@0 | 3088 | NS_NAMED_LITERAL_STRING(top, "top"); |
michael@0 | 3089 | if (nsContentUtils::EqualsIgnoreASCIICase(aAnchorName, top)) { |
michael@0 | 3090 | // Scroll to the top/left if aAnchorName is "top" and there is no element |
michael@0 | 3091 | // with such a name or id. |
michael@0 | 3092 | rv = NS_OK; |
michael@0 | 3093 | nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable(); |
michael@0 | 3094 | // Check |aScroll| after setting |rv| so we set |rv| to the same |
michael@0 | 3095 | // thing whether or not |aScroll| is true. |
michael@0 | 3096 | if (aScroll && sf) { |
michael@0 | 3097 | // Scroll to the top of the page |
michael@0 | 3098 | sf->ScrollTo(nsPoint(0, 0), nsIScrollableFrame::INSTANT); |
michael@0 | 3099 | } |
michael@0 | 3100 | } |
michael@0 | 3101 | } |
michael@0 | 3102 | |
michael@0 | 3103 | #ifdef ACCESSIBILITY |
michael@0 | 3104 | if (anchorTarget) { |
michael@0 | 3105 | nsAccessibilityService* accService = AccService(); |
michael@0 | 3106 | if (accService) |
michael@0 | 3107 | accService->NotifyOfAnchorJumpTo(anchorTarget); |
michael@0 | 3108 | } |
michael@0 | 3109 | #endif |
michael@0 | 3110 | |
michael@0 | 3111 | return rv; |
michael@0 | 3112 | } |
michael@0 | 3113 | |
michael@0 | 3114 | nsresult |
michael@0 | 3115 | PresShell::ScrollToAnchor() |
michael@0 | 3116 | { |
michael@0 | 3117 | if (!mLastAnchorScrolledTo) { |
michael@0 | 3118 | return NS_OK; |
michael@0 | 3119 | } |
michael@0 | 3120 | NS_ASSERTION(mDidInitialize, "should have done initial reflow by now"); |
michael@0 | 3121 | |
michael@0 | 3122 | nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable(); |
michael@0 | 3123 | if (!rootScroll || |
michael@0 | 3124 | mLastAnchorScrollPositionY != rootScroll->GetScrollPosition().y) { |
michael@0 | 3125 | return NS_OK; |
michael@0 | 3126 | } |
michael@0 | 3127 | nsresult rv = ScrollContentIntoView(mLastAnchorScrolledTo, |
michael@0 | 3128 | ScrollAxis(SCROLL_TOP, SCROLL_ALWAYS), |
michael@0 | 3129 | ScrollAxis(), |
michael@0 | 3130 | ANCHOR_SCROLL_FLAGS); |
michael@0 | 3131 | mLastAnchorScrolledTo = nullptr; |
michael@0 | 3132 | return rv; |
michael@0 | 3133 | } |
michael@0 | 3134 | |
michael@0 | 3135 | /* |
michael@0 | 3136 | * Helper (per-continuation) for ScrollContentIntoView. |
michael@0 | 3137 | * |
michael@0 | 3138 | * @param aContainerFrame [in] the frame which aRect is relative to |
michael@0 | 3139 | * @param aFrame [in] Frame whose bounds should be unioned |
michael@0 | 3140 | * @param aUseWholeLineHeightForInlines [in] if true, then for inline frames |
michael@0 | 3141 | * we should include the top of the line in the added rectangle |
michael@0 | 3142 | * @param aRect [inout] rect into which its bounds should be unioned |
michael@0 | 3143 | * @param aHaveRect [inout] whether aRect contains data yet |
michael@0 | 3144 | * @param aPrevBlock [inout] the block aLines is a line iterator for |
michael@0 | 3145 | * @param aLines [inout] the line iterator we're using |
michael@0 | 3146 | * @param aCurLine [inout] the line to start looking from in this iterator |
michael@0 | 3147 | */ |
michael@0 | 3148 | static void |
michael@0 | 3149 | AccumulateFrameBounds(nsIFrame* aContainerFrame, |
michael@0 | 3150 | nsIFrame* aFrame, |
michael@0 | 3151 | bool aUseWholeLineHeightForInlines, |
michael@0 | 3152 | nsRect& aRect, |
michael@0 | 3153 | bool& aHaveRect, |
michael@0 | 3154 | nsIFrame*& aPrevBlock, |
michael@0 | 3155 | nsAutoLineIterator& aLines, |
michael@0 | 3156 | int32_t& aCurLine) |
michael@0 | 3157 | { |
michael@0 | 3158 | nsIFrame* frame = aFrame; |
michael@0 | 3159 | nsRect frameBounds = nsRect(nsPoint(0, 0), aFrame->GetSize()); |
michael@0 | 3160 | |
michael@0 | 3161 | // If this is an inline frame and either the bounds height is 0 (quirks |
michael@0 | 3162 | // layout model) or aUseWholeLineHeightForInlines is set, we need to |
michael@0 | 3163 | // change the top of the bounds to include the whole line. |
michael@0 | 3164 | if (frameBounds.height == 0 || aUseWholeLineHeightForInlines) { |
michael@0 | 3165 | nsIFrame *prevFrame = aFrame; |
michael@0 | 3166 | nsIFrame *f = aFrame; |
michael@0 | 3167 | |
michael@0 | 3168 | while (f && f->IsFrameOfType(nsIFrame::eLineParticipant) && |
michael@0 | 3169 | !f->IsTransformed() && !f->IsPositioned()) { |
michael@0 | 3170 | prevFrame = f; |
michael@0 | 3171 | f = prevFrame->GetParent(); |
michael@0 | 3172 | } |
michael@0 | 3173 | |
michael@0 | 3174 | if (f != aFrame && |
michael@0 | 3175 | f && |
michael@0 | 3176 | f->GetType() == nsGkAtoms::blockFrame) { |
michael@0 | 3177 | // find the line containing aFrame and increase the top of |offset|. |
michael@0 | 3178 | if (f != aPrevBlock) { |
michael@0 | 3179 | aLines = f->GetLineIterator(); |
michael@0 | 3180 | aPrevBlock = f; |
michael@0 | 3181 | aCurLine = 0; |
michael@0 | 3182 | } |
michael@0 | 3183 | if (aLines) { |
michael@0 | 3184 | int32_t index = aLines->FindLineContaining(prevFrame, aCurLine); |
michael@0 | 3185 | if (index >= 0) { |
michael@0 | 3186 | aCurLine = index; |
michael@0 | 3187 | nsIFrame *trash1; |
michael@0 | 3188 | int32_t trash2; |
michael@0 | 3189 | nsRect lineBounds; |
michael@0 | 3190 | uint32_t trash3; |
michael@0 | 3191 | |
michael@0 | 3192 | if (NS_SUCCEEDED(aLines->GetLine(index, &trash1, &trash2, |
michael@0 | 3193 | lineBounds, &trash3))) { |
michael@0 | 3194 | frameBounds += frame->GetOffsetTo(f); |
michael@0 | 3195 | frame = f; |
michael@0 | 3196 | if (lineBounds.y < frameBounds.y) { |
michael@0 | 3197 | frameBounds.height = frameBounds.YMost() - lineBounds.y; |
michael@0 | 3198 | frameBounds.y = lineBounds.y; |
michael@0 | 3199 | } |
michael@0 | 3200 | } |
michael@0 | 3201 | } |
michael@0 | 3202 | } |
michael@0 | 3203 | } |
michael@0 | 3204 | } |
michael@0 | 3205 | |
michael@0 | 3206 | nsRect transformedBounds = nsLayoutUtils::TransformFrameRectToAncestor(frame, |
michael@0 | 3207 | frameBounds, aContainerFrame); |
michael@0 | 3208 | |
michael@0 | 3209 | if (aHaveRect) { |
michael@0 | 3210 | // We can't use nsRect::UnionRect since it drops empty rects on |
michael@0 | 3211 | // the floor, and we need to include them. (Thus we need |
michael@0 | 3212 | // aHaveRect to know when to drop the initial value on the floor.) |
michael@0 | 3213 | aRect.UnionRectEdges(aRect, transformedBounds); |
michael@0 | 3214 | } else { |
michael@0 | 3215 | aHaveRect = true; |
michael@0 | 3216 | aRect = transformedBounds; |
michael@0 | 3217 | } |
michael@0 | 3218 | } |
michael@0 | 3219 | |
michael@0 | 3220 | static bool |
michael@0 | 3221 | ComputeNeedToScroll(nsIPresShell::WhenToScroll aWhenToScroll, |
michael@0 | 3222 | nscoord aLineSize, |
michael@0 | 3223 | nscoord aRectMin, |
michael@0 | 3224 | nscoord aRectMax, |
michael@0 | 3225 | nscoord aViewMin, |
michael@0 | 3226 | nscoord aViewMax) { |
michael@0 | 3227 | // See how the rect should be positioned vertically |
michael@0 | 3228 | if (nsIPresShell::SCROLL_ALWAYS == aWhenToScroll) { |
michael@0 | 3229 | // The caller wants the frame as visible as possible |
michael@0 | 3230 | return true; |
michael@0 | 3231 | } else if (nsIPresShell::SCROLL_IF_NOT_VISIBLE == aWhenToScroll) { |
michael@0 | 3232 | // Scroll only if no part of the frame is visible in this view |
michael@0 | 3233 | return aRectMax - aLineSize <= aViewMin || |
michael@0 | 3234 | aRectMin + aLineSize >= aViewMax; |
michael@0 | 3235 | } else if (nsIPresShell::SCROLL_IF_NOT_FULLY_VISIBLE == aWhenToScroll) { |
michael@0 | 3236 | // Scroll only if part of the frame is hidden and more can fit in view |
michael@0 | 3237 | return !(aRectMin >= aViewMin && aRectMax <= aViewMax) && |
michael@0 | 3238 | std::min(aViewMax, aRectMax) - std::max(aRectMin, aViewMin) < aViewMax - aViewMin; |
michael@0 | 3239 | } |
michael@0 | 3240 | return false; |
michael@0 | 3241 | } |
michael@0 | 3242 | |
michael@0 | 3243 | static nscoord |
michael@0 | 3244 | ComputeWhereToScroll(int16_t aWhereToScroll, |
michael@0 | 3245 | nscoord aOriginalCoord, |
michael@0 | 3246 | nscoord aRectMin, |
michael@0 | 3247 | nscoord aRectMax, |
michael@0 | 3248 | nscoord aViewMin, |
michael@0 | 3249 | nscoord aViewMax, |
michael@0 | 3250 | nscoord* aRangeMin, |
michael@0 | 3251 | nscoord* aRangeMax) { |
michael@0 | 3252 | nscoord resultCoord = aOriginalCoord; |
michael@0 | 3253 | // Allow the scroll operation to land anywhere that |
michael@0 | 3254 | // makes the whole rectangle visible. |
michael@0 | 3255 | if (nsIPresShell::SCROLL_MINIMUM == aWhereToScroll) { |
michael@0 | 3256 | if (aRectMin < aViewMin) { |
michael@0 | 3257 | // Scroll up so the frame's top edge is visible |
michael@0 | 3258 | resultCoord = aRectMin; |
michael@0 | 3259 | } else if (aRectMax > aViewMax) { |
michael@0 | 3260 | // Scroll down so the frame's bottom edge is visible. Make sure the |
michael@0 | 3261 | // frame's top edge is still visible |
michael@0 | 3262 | resultCoord = aOriginalCoord + aRectMax - aViewMax; |
michael@0 | 3263 | if (resultCoord > aRectMin) { |
michael@0 | 3264 | resultCoord = aRectMin; |
michael@0 | 3265 | } |
michael@0 | 3266 | } |
michael@0 | 3267 | } else { |
michael@0 | 3268 | nscoord frameAlignCoord = |
michael@0 | 3269 | NSToCoordRound(aRectMin + (aRectMax - aRectMin) * (aWhereToScroll / 100.0f)); |
michael@0 | 3270 | resultCoord = NSToCoordRound(frameAlignCoord - (aViewMax - aViewMin) * ( |
michael@0 | 3271 | aWhereToScroll / 100.0f)); |
michael@0 | 3272 | } |
michael@0 | 3273 | nscoord scrollPortLength = aViewMax - aViewMin; |
michael@0 | 3274 | // Force the scroll range to extend to include resultCoord. |
michael@0 | 3275 | *aRangeMin = std::min(resultCoord, aRectMax - scrollPortLength); |
michael@0 | 3276 | *aRangeMax = std::max(resultCoord, aRectMin); |
michael@0 | 3277 | return resultCoord; |
michael@0 | 3278 | } |
michael@0 | 3279 | |
michael@0 | 3280 | /** |
michael@0 | 3281 | * This function takes a scrollable frame, a rect in the coordinate system |
michael@0 | 3282 | * of the scrolled frame, and a desired percentage-based scroll |
michael@0 | 3283 | * position and attempts to scroll the rect to that position in the |
michael@0 | 3284 | * scrollport. |
michael@0 | 3285 | * |
michael@0 | 3286 | * This needs to work even if aRect has a width or height of zero. |
michael@0 | 3287 | */ |
michael@0 | 3288 | static void ScrollToShowRect(nsIFrame* aFrame, |
michael@0 | 3289 | nsIScrollableFrame* aFrameAsScrollable, |
michael@0 | 3290 | const nsRect& aRect, |
michael@0 | 3291 | nsIPresShell::ScrollAxis aVertical, |
michael@0 | 3292 | nsIPresShell::ScrollAxis aHorizontal, |
michael@0 | 3293 | uint32_t aFlags) |
michael@0 | 3294 | { |
michael@0 | 3295 | nsPoint scrollPt = aFrameAsScrollable->GetScrollPosition(); |
michael@0 | 3296 | nsRect visibleRect(scrollPt, |
michael@0 | 3297 | aFrameAsScrollable->GetScrollPositionClampingScrollPortSize()); |
michael@0 | 3298 | |
michael@0 | 3299 | // If this is the root scroll frame, make sure to take into account the |
michael@0 | 3300 | // content document fixed position margins. When set, these indicate that |
michael@0 | 3301 | // chrome is obscuring the viewport. |
michael@0 | 3302 | nsRect targetRect(aRect); |
michael@0 | 3303 | nsIPresShell *presShell = aFrame->PresContext()->PresShell(); |
michael@0 | 3304 | if (aFrameAsScrollable == presShell->GetRootScrollFrameAsScrollable()) { |
michael@0 | 3305 | targetRect.Inflate(presShell->GetContentDocumentFixedPositionMargins()); |
michael@0 | 3306 | } |
michael@0 | 3307 | |
michael@0 | 3308 | nsSize lineSize; |
michael@0 | 3309 | // Don't call GetLineScrollAmount unless we actually need it. Not only |
michael@0 | 3310 | // does this save time, but it's not safe to call GetLineScrollAmount |
michael@0 | 3311 | // during reflow (because it depends on font size inflation and doesn't |
michael@0 | 3312 | // use the in-reflow-safe font-size inflation path). If we did call it, |
michael@0 | 3313 | // it would assert and possible give the wrong result. |
michael@0 | 3314 | if (aVertical.mWhenToScroll == nsIPresShell::SCROLL_IF_NOT_VISIBLE || |
michael@0 | 3315 | aHorizontal.mWhenToScroll == nsIPresShell::SCROLL_IF_NOT_VISIBLE) { |
michael@0 | 3316 | lineSize = aFrameAsScrollable->GetLineScrollAmount(); |
michael@0 | 3317 | } |
michael@0 | 3318 | ScrollbarStyles ss = aFrameAsScrollable->GetScrollbarStyles(); |
michael@0 | 3319 | nsRect allowedRange(scrollPt, nsSize(0, 0)); |
michael@0 | 3320 | bool needToScroll = false; |
michael@0 | 3321 | uint32_t directions = aFrameAsScrollable->GetPerceivedScrollingDirections(); |
michael@0 | 3322 | |
michael@0 | 3323 | if (((aFlags & nsIPresShell::SCROLL_OVERFLOW_HIDDEN) || |
michael@0 | 3324 | ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN) && |
michael@0 | 3325 | (!aVertical.mOnlyIfPerceivedScrollableDirection || |
michael@0 | 3326 | (directions & nsIScrollableFrame::VERTICAL))) { |
michael@0 | 3327 | |
michael@0 | 3328 | if (ComputeNeedToScroll(aVertical.mWhenToScroll, |
michael@0 | 3329 | lineSize.height, |
michael@0 | 3330 | targetRect.y, |
michael@0 | 3331 | targetRect.YMost(), |
michael@0 | 3332 | visibleRect.y, |
michael@0 | 3333 | visibleRect.YMost())) { |
michael@0 | 3334 | nscoord maxHeight; |
michael@0 | 3335 | scrollPt.y = ComputeWhereToScroll(aVertical.mWhereToScroll, |
michael@0 | 3336 | scrollPt.y, |
michael@0 | 3337 | targetRect.y, |
michael@0 | 3338 | targetRect.YMost(), |
michael@0 | 3339 | visibleRect.y, |
michael@0 | 3340 | visibleRect.YMost(), |
michael@0 | 3341 | &allowedRange.y, &maxHeight); |
michael@0 | 3342 | allowedRange.height = maxHeight - allowedRange.y; |
michael@0 | 3343 | needToScroll = true; |
michael@0 | 3344 | } |
michael@0 | 3345 | } |
michael@0 | 3346 | |
michael@0 | 3347 | if (((aFlags & nsIPresShell::SCROLL_OVERFLOW_HIDDEN) || |
michael@0 | 3348 | ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN) && |
michael@0 | 3349 | (!aHorizontal.mOnlyIfPerceivedScrollableDirection || |
michael@0 | 3350 | (directions & nsIScrollableFrame::HORIZONTAL))) { |
michael@0 | 3351 | |
michael@0 | 3352 | if (ComputeNeedToScroll(aHorizontal.mWhenToScroll, |
michael@0 | 3353 | lineSize.width, |
michael@0 | 3354 | targetRect.x, |
michael@0 | 3355 | targetRect.XMost(), |
michael@0 | 3356 | visibleRect.x, |
michael@0 | 3357 | visibleRect.XMost())) { |
michael@0 | 3358 | nscoord maxWidth; |
michael@0 | 3359 | scrollPt.x = ComputeWhereToScroll(aHorizontal.mWhereToScroll, |
michael@0 | 3360 | scrollPt.x, |
michael@0 | 3361 | targetRect.x, |
michael@0 | 3362 | targetRect.XMost(), |
michael@0 | 3363 | visibleRect.x, |
michael@0 | 3364 | visibleRect.XMost(), |
michael@0 | 3365 | &allowedRange.x, &maxWidth); |
michael@0 | 3366 | allowedRange.width = maxWidth - allowedRange.x; |
michael@0 | 3367 | needToScroll = true; |
michael@0 | 3368 | } |
michael@0 | 3369 | } |
michael@0 | 3370 | |
michael@0 | 3371 | // If we don't need to scroll, then don't try since it might cancel |
michael@0 | 3372 | // a current smooth scroll operation. |
michael@0 | 3373 | if (needToScroll) { |
michael@0 | 3374 | aFrameAsScrollable->ScrollTo(scrollPt, nsIScrollableFrame::INSTANT, &allowedRange); |
michael@0 | 3375 | } |
michael@0 | 3376 | } |
michael@0 | 3377 | |
michael@0 | 3378 | nsresult |
michael@0 | 3379 | PresShell::ScrollContentIntoView(nsIContent* aContent, |
michael@0 | 3380 | nsIPresShell::ScrollAxis aVertical, |
michael@0 | 3381 | nsIPresShell::ScrollAxis aHorizontal, |
michael@0 | 3382 | uint32_t aFlags) |
michael@0 | 3383 | { |
michael@0 | 3384 | NS_ENSURE_TRUE(aContent, NS_ERROR_NULL_POINTER); |
michael@0 | 3385 | nsCOMPtr<nsIDocument> currentDoc = aContent->GetCurrentDoc(); |
michael@0 | 3386 | NS_ENSURE_STATE(currentDoc); |
michael@0 | 3387 | |
michael@0 | 3388 | NS_ASSERTION(mDidInitialize, "should have done initial reflow by now"); |
michael@0 | 3389 | |
michael@0 | 3390 | if (mContentToScrollTo) { |
michael@0 | 3391 | mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling); |
michael@0 | 3392 | } |
michael@0 | 3393 | mContentToScrollTo = aContent; |
michael@0 | 3394 | ScrollIntoViewData* data = new ScrollIntoViewData(); |
michael@0 | 3395 | data->mContentScrollVAxis = aVertical; |
michael@0 | 3396 | data->mContentScrollHAxis = aHorizontal; |
michael@0 | 3397 | data->mContentToScrollToFlags = aFlags; |
michael@0 | 3398 | if (NS_FAILED(mContentToScrollTo->SetProperty(nsGkAtoms::scrolling, data, |
michael@0 | 3399 | nsINode::DeleteProperty<PresShell::ScrollIntoViewData>))) { |
michael@0 | 3400 | mContentToScrollTo = nullptr; |
michael@0 | 3401 | } |
michael@0 | 3402 | |
michael@0 | 3403 | // Flush layout and attempt to scroll in the process. |
michael@0 | 3404 | currentDoc->SetNeedLayoutFlush(); |
michael@0 | 3405 | currentDoc->FlushPendingNotifications(Flush_InterruptibleLayout); |
michael@0 | 3406 | |
michael@0 | 3407 | // If mContentToScrollTo is non-null, that means we interrupted the reflow |
michael@0 | 3408 | // (or suppressed it altogether because we're suppressing interruptible |
michael@0 | 3409 | // flushes right now) and won't necessarily get the position correct, but do |
michael@0 | 3410 | // a best-effort scroll here. The other option would be to do this inside |
michael@0 | 3411 | // FlushPendingNotifications, but I'm not sure the repeated scrolling that |
michael@0 | 3412 | // could trigger if reflows keep getting interrupted would be more desirable |
michael@0 | 3413 | // than a single best-effort scroll followed by one final scroll on the first |
michael@0 | 3414 | // completed reflow. |
michael@0 | 3415 | if (mContentToScrollTo) { |
michael@0 | 3416 | DoScrollContentIntoView(); |
michael@0 | 3417 | } |
michael@0 | 3418 | return NS_OK; |
michael@0 | 3419 | } |
michael@0 | 3420 | |
michael@0 | 3421 | void |
michael@0 | 3422 | PresShell::DoScrollContentIntoView() |
michael@0 | 3423 | { |
michael@0 | 3424 | NS_ASSERTION(mDidInitialize, "should have done initial reflow by now"); |
michael@0 | 3425 | |
michael@0 | 3426 | nsIFrame* frame = mContentToScrollTo->GetPrimaryFrame(); |
michael@0 | 3427 | if (!frame) { |
michael@0 | 3428 | mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling); |
michael@0 | 3429 | mContentToScrollTo = nullptr; |
michael@0 | 3430 | return; |
michael@0 | 3431 | } |
michael@0 | 3432 | |
michael@0 | 3433 | if (frame->GetStateBits() & NS_FRAME_FIRST_REFLOW) { |
michael@0 | 3434 | // The reflow flush before this scroll got interrupted, and this frame's |
michael@0 | 3435 | // coords and size are all zero, and it has no content showing anyway. |
michael@0 | 3436 | // Don't bother scrolling to it. We'll try again when we finish up layout. |
michael@0 | 3437 | return; |
michael@0 | 3438 | } |
michael@0 | 3439 | |
michael@0 | 3440 | // Make sure we skip 'frame' ... if it's scrollable, we should use its |
michael@0 | 3441 | // scrollable ancestor as the container. |
michael@0 | 3442 | nsIFrame* container = |
michael@0 | 3443 | nsLayoutUtils::GetClosestFrameOfType(frame->GetParent(), nsGkAtoms::scrollFrame); |
michael@0 | 3444 | if (!container) { |
michael@0 | 3445 | // nothing can be scrolled |
michael@0 | 3446 | return; |
michael@0 | 3447 | } |
michael@0 | 3448 | |
michael@0 | 3449 | ScrollIntoViewData* data = static_cast<ScrollIntoViewData*>( |
michael@0 | 3450 | mContentToScrollTo->GetProperty(nsGkAtoms::scrolling)); |
michael@0 | 3451 | if (MOZ_UNLIKELY(!data)) { |
michael@0 | 3452 | mContentToScrollTo = nullptr; |
michael@0 | 3453 | return; |
michael@0 | 3454 | } |
michael@0 | 3455 | |
michael@0 | 3456 | // This is a two-step process. |
michael@0 | 3457 | // Step 1: Find the bounds of the rect we want to scroll into view. For |
michael@0 | 3458 | // example, for an inline frame we may want to scroll in the whole |
michael@0 | 3459 | // line, or we may want to scroll multiple lines into view. |
michael@0 | 3460 | // Step 2: Walk container frame and its ancestors and scroll them |
michael@0 | 3461 | // appropriately. |
michael@0 | 3462 | // frameBounds is relative to container. We're assuming |
michael@0 | 3463 | // that scrollframes don't split so every continuation of frame will |
michael@0 | 3464 | // be a descendant of container. (Things would still mostly work |
michael@0 | 3465 | // even if that assumption was false.) |
michael@0 | 3466 | nsRect frameBounds; |
michael@0 | 3467 | bool haveRect = false; |
michael@0 | 3468 | bool useWholeLineHeightForInlines = |
michael@0 | 3469 | data->mContentScrollVAxis.mWhenToScroll != nsIPresShell::SCROLL_IF_NOT_FULLY_VISIBLE; |
michael@0 | 3470 | // Reuse the same line iterator across calls to AccumulateFrameBounds. We set |
michael@0 | 3471 | // it every time we detect a new block (stored in prevBlock). |
michael@0 | 3472 | nsIFrame* prevBlock = nullptr; |
michael@0 | 3473 | nsAutoLineIterator lines; |
michael@0 | 3474 | // The last line we found a continuation on in |lines|. We assume that later |
michael@0 | 3475 | // continuations cannot come on earlier lines. |
michael@0 | 3476 | int32_t curLine = 0; |
michael@0 | 3477 | do { |
michael@0 | 3478 | AccumulateFrameBounds(container, frame, useWholeLineHeightForInlines, |
michael@0 | 3479 | frameBounds, haveRect, prevBlock, lines, curLine); |
michael@0 | 3480 | } while ((frame = frame->GetNextContinuation())); |
michael@0 | 3481 | |
michael@0 | 3482 | ScrollFrameRectIntoView(container, frameBounds, data->mContentScrollVAxis, |
michael@0 | 3483 | data->mContentScrollHAxis, |
michael@0 | 3484 | data->mContentToScrollToFlags); |
michael@0 | 3485 | } |
michael@0 | 3486 | |
michael@0 | 3487 | bool |
michael@0 | 3488 | PresShell::ScrollFrameRectIntoView(nsIFrame* aFrame, |
michael@0 | 3489 | const nsRect& aRect, |
michael@0 | 3490 | nsIPresShell::ScrollAxis aVertical, |
michael@0 | 3491 | nsIPresShell::ScrollAxis aHorizontal, |
michael@0 | 3492 | uint32_t aFlags) |
michael@0 | 3493 | { |
michael@0 | 3494 | bool didScroll = false; |
michael@0 | 3495 | // This function needs to work even if rect has a width or height of 0. |
michael@0 | 3496 | nsRect rect = aRect; |
michael@0 | 3497 | nsIFrame* container = aFrame; |
michael@0 | 3498 | // Walk up the frame hierarchy scrolling the rect into view and |
michael@0 | 3499 | // keeping rect relative to container |
michael@0 | 3500 | do { |
michael@0 | 3501 | nsIScrollableFrame* sf = do_QueryFrame(container); |
michael@0 | 3502 | if (sf) { |
michael@0 | 3503 | nsPoint oldPosition = sf->GetScrollPosition(); |
michael@0 | 3504 | nsRect targetRect = rect; |
michael@0 | 3505 | if (container->StyleDisplay()->mOverflowClipBox == |
michael@0 | 3506 | NS_STYLE_OVERFLOW_CLIP_BOX_CONTENT_BOX) { |
michael@0 | 3507 | nsMargin padding = container->GetUsedPadding(); |
michael@0 | 3508 | targetRect.Inflate(padding); |
michael@0 | 3509 | } |
michael@0 | 3510 | ScrollToShowRect(container, sf, targetRect - sf->GetScrolledFrame()->GetPosition(), |
michael@0 | 3511 | aVertical, aHorizontal, aFlags); |
michael@0 | 3512 | nsPoint newPosition = sf->GetScrollPosition(); |
michael@0 | 3513 | // If the scroll position increased, that means our content moved up, |
michael@0 | 3514 | // so our rect's offset should decrease |
michael@0 | 3515 | rect += oldPosition - newPosition; |
michael@0 | 3516 | |
michael@0 | 3517 | if (oldPosition != newPosition) { |
michael@0 | 3518 | didScroll = true; |
michael@0 | 3519 | } |
michael@0 | 3520 | |
michael@0 | 3521 | // only scroll one container when this flag is set |
michael@0 | 3522 | if (aFlags & nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY) { |
michael@0 | 3523 | break; |
michael@0 | 3524 | } |
michael@0 | 3525 | } |
michael@0 | 3526 | nsIFrame* parent; |
michael@0 | 3527 | if (container->IsTransformed()) { |
michael@0 | 3528 | container->GetTransformMatrix(nullptr, &parent); |
michael@0 | 3529 | rect = nsLayoutUtils::TransformFrameRectToAncestor(container, rect, parent); |
michael@0 | 3530 | } else { |
michael@0 | 3531 | rect += container->GetPosition(); |
michael@0 | 3532 | parent = container->GetParent(); |
michael@0 | 3533 | } |
michael@0 | 3534 | if (!parent && !(aFlags & nsIPresShell::SCROLL_NO_PARENT_FRAMES)) { |
michael@0 | 3535 | nsPoint extraOffset(0,0); |
michael@0 | 3536 | parent = nsLayoutUtils::GetCrossDocParentFrame(container, &extraOffset); |
michael@0 | 3537 | if (parent) { |
michael@0 | 3538 | int32_t APD = container->PresContext()->AppUnitsPerDevPixel(); |
michael@0 | 3539 | int32_t parentAPD = parent->PresContext()->AppUnitsPerDevPixel(); |
michael@0 | 3540 | rect = rect.ConvertAppUnitsRoundOut(APD, parentAPD); |
michael@0 | 3541 | rect += extraOffset; |
michael@0 | 3542 | } |
michael@0 | 3543 | } |
michael@0 | 3544 | container = parent; |
michael@0 | 3545 | } while (container); |
michael@0 | 3546 | |
michael@0 | 3547 | return didScroll; |
michael@0 | 3548 | } |
michael@0 | 3549 | |
michael@0 | 3550 | nsRectVisibility |
michael@0 | 3551 | PresShell::GetRectVisibility(nsIFrame* aFrame, |
michael@0 | 3552 | const nsRect &aRect, |
michael@0 | 3553 | nscoord aMinTwips) const |
michael@0 | 3554 | { |
michael@0 | 3555 | NS_ASSERTION(aFrame->PresContext() == GetPresContext(), |
michael@0 | 3556 | "prescontext mismatch?"); |
michael@0 | 3557 | nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); |
michael@0 | 3558 | NS_ASSERTION(rootFrame, |
michael@0 | 3559 | "How can someone have a frame for this presshell when there's no root?"); |
michael@0 | 3560 | nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable(); |
michael@0 | 3561 | nsRect scrollPortRect; |
michael@0 | 3562 | if (sf) { |
michael@0 | 3563 | scrollPortRect = sf->GetScrollPortRect(); |
michael@0 | 3564 | nsIFrame* f = do_QueryFrame(sf); |
michael@0 | 3565 | scrollPortRect += f->GetOffsetTo(rootFrame); |
michael@0 | 3566 | } else { |
michael@0 | 3567 | scrollPortRect = nsRect(nsPoint(0,0), rootFrame->GetSize()); |
michael@0 | 3568 | } |
michael@0 | 3569 | |
michael@0 | 3570 | nsRect r = aRect + aFrame->GetOffsetTo(rootFrame); |
michael@0 | 3571 | // If aRect is entirely visible then we don't need to ensure that |
michael@0 | 3572 | // at least aMinTwips of it is visible |
michael@0 | 3573 | if (scrollPortRect.Contains(r)) |
michael@0 | 3574 | return nsRectVisibility_kVisible; |
michael@0 | 3575 | |
michael@0 | 3576 | nsRect insetRect = scrollPortRect; |
michael@0 | 3577 | insetRect.Deflate(aMinTwips, aMinTwips); |
michael@0 | 3578 | if (r.YMost() <= insetRect.y) |
michael@0 | 3579 | return nsRectVisibility_kAboveViewport; |
michael@0 | 3580 | if (r.y >= insetRect.YMost()) |
michael@0 | 3581 | return nsRectVisibility_kBelowViewport; |
michael@0 | 3582 | if (r.XMost() <= insetRect.x) |
michael@0 | 3583 | return nsRectVisibility_kLeftOfViewport; |
michael@0 | 3584 | if (r.x >= insetRect.XMost()) |
michael@0 | 3585 | return nsRectVisibility_kRightOfViewport; |
michael@0 | 3586 | |
michael@0 | 3587 | return nsRectVisibility_kVisible; |
michael@0 | 3588 | } |
michael@0 | 3589 | |
michael@0 | 3590 | class PaintTimerCallBack MOZ_FINAL : public nsITimerCallback |
michael@0 | 3591 | { |
michael@0 | 3592 | public: |
michael@0 | 3593 | PaintTimerCallBack(PresShell* aShell) : mShell(aShell) {} |
michael@0 | 3594 | |
michael@0 | 3595 | NS_DECL_ISUPPORTS |
michael@0 | 3596 | |
michael@0 | 3597 | NS_IMETHODIMP Notify(nsITimer* aTimer) MOZ_FINAL |
michael@0 | 3598 | { |
michael@0 | 3599 | mShell->SetNextPaintCompressed(); |
michael@0 | 3600 | mShell->AddInvalidateHiddenPresShellObserver(mShell->GetPresContext()->RefreshDriver()); |
michael@0 | 3601 | mShell->ScheduleViewManagerFlush(); |
michael@0 | 3602 | return NS_OK; |
michael@0 | 3603 | } |
michael@0 | 3604 | |
michael@0 | 3605 | private: |
michael@0 | 3606 | PresShell* mShell; |
michael@0 | 3607 | }; |
michael@0 | 3608 | |
michael@0 | 3609 | NS_IMPL_ISUPPORTS(PaintTimerCallBack, nsITimerCallback) |
michael@0 | 3610 | |
michael@0 | 3611 | void |
michael@0 | 3612 | PresShell::ScheduleViewManagerFlush(PaintType aType) |
michael@0 | 3613 | { |
michael@0 | 3614 | if (aType == PAINT_DELAYED_COMPRESS) { |
michael@0 | 3615 | // Delay paint for 1 second. |
michael@0 | 3616 | static const uint32_t kPaintDelayPeriod = 1000; |
michael@0 | 3617 | if (!mDelayedPaintTimer) { |
michael@0 | 3618 | mDelayedPaintTimer = do_CreateInstance(NS_TIMER_CONTRACTID); |
michael@0 | 3619 | nsRefPtr<PaintTimerCallBack> cb = new PaintTimerCallBack(this); |
michael@0 | 3620 | mDelayedPaintTimer->InitWithCallback(cb, kPaintDelayPeriod, nsITimer::TYPE_ONE_SHOT); |
michael@0 | 3621 | } |
michael@0 | 3622 | return; |
michael@0 | 3623 | } |
michael@0 | 3624 | |
michael@0 | 3625 | nsPresContext* presContext = GetPresContext(); |
michael@0 | 3626 | if (presContext) { |
michael@0 | 3627 | presContext->RefreshDriver()->ScheduleViewManagerFlush(); |
michael@0 | 3628 | } |
michael@0 | 3629 | if (mDocument) { |
michael@0 | 3630 | mDocument->SetNeedLayoutFlush(); |
michael@0 | 3631 | } |
michael@0 | 3632 | } |
michael@0 | 3633 | |
michael@0 | 3634 | void |
michael@0 | 3635 | PresShell::DispatchSynthMouseMove(WidgetGUIEvent* aEvent, |
michael@0 | 3636 | bool aFlushOnHoverChange) |
michael@0 | 3637 | { |
michael@0 | 3638 | RestyleManager* restyleManager = mPresContext->RestyleManager(); |
michael@0 | 3639 | uint32_t hoverGenerationBefore = restyleManager->GetHoverGeneration(); |
michael@0 | 3640 | nsEventStatus status; |
michael@0 | 3641 | nsView* targetView = nsView::GetViewFor(aEvent->widget); |
michael@0 | 3642 | if (!targetView) |
michael@0 | 3643 | return; |
michael@0 | 3644 | targetView->GetViewManager()->DispatchEvent(aEvent, targetView, &status); |
michael@0 | 3645 | if (MOZ_UNLIKELY(mIsDestroying)) { |
michael@0 | 3646 | return; |
michael@0 | 3647 | } |
michael@0 | 3648 | if (aFlushOnHoverChange && |
michael@0 | 3649 | hoverGenerationBefore != restyleManager->GetHoverGeneration()) { |
michael@0 | 3650 | // Flush so that the resulting reflow happens now so that our caller |
michael@0 | 3651 | // can suppress any synthesized mouse moves caused by that reflow. |
michael@0 | 3652 | FlushPendingNotifications(Flush_Layout); |
michael@0 | 3653 | } |
michael@0 | 3654 | } |
michael@0 | 3655 | |
michael@0 | 3656 | void |
michael@0 | 3657 | PresShell::ClearMouseCaptureOnView(nsView* aView) |
michael@0 | 3658 | { |
michael@0 | 3659 | if (gCaptureInfo.mContent) { |
michael@0 | 3660 | if (aView) { |
michael@0 | 3661 | // if a view was specified, ensure that the captured content is within |
michael@0 | 3662 | // this view. |
michael@0 | 3663 | nsIFrame* frame = gCaptureInfo.mContent->GetPrimaryFrame(); |
michael@0 | 3664 | if (frame) { |
michael@0 | 3665 | nsView* view = frame->GetClosestView(); |
michael@0 | 3666 | // if there is no view, capturing won't be handled any more, so |
michael@0 | 3667 | // just release the capture. |
michael@0 | 3668 | if (view) { |
michael@0 | 3669 | do { |
michael@0 | 3670 | if (view == aView) { |
michael@0 | 3671 | NS_RELEASE(gCaptureInfo.mContent); |
michael@0 | 3672 | // the view containing the captured content likely disappeared so |
michael@0 | 3673 | // disable capture for now. |
michael@0 | 3674 | gCaptureInfo.mAllowed = false; |
michael@0 | 3675 | break; |
michael@0 | 3676 | } |
michael@0 | 3677 | |
michael@0 | 3678 | view = view->GetParent(); |
michael@0 | 3679 | } while (view); |
michael@0 | 3680 | // return if the view wasn't found |
michael@0 | 3681 | return; |
michael@0 | 3682 | } |
michael@0 | 3683 | } |
michael@0 | 3684 | } |
michael@0 | 3685 | |
michael@0 | 3686 | NS_RELEASE(gCaptureInfo.mContent); |
michael@0 | 3687 | } |
michael@0 | 3688 | |
michael@0 | 3689 | // disable mouse capture until the next mousedown as a dialog has opened |
michael@0 | 3690 | // or a drag has started. Otherwise, someone could start capture during |
michael@0 | 3691 | // the modal dialog or drag. |
michael@0 | 3692 | gCaptureInfo.mAllowed = false; |
michael@0 | 3693 | } |
michael@0 | 3694 | |
michael@0 | 3695 | void |
michael@0 | 3696 | nsIPresShell::ClearMouseCapture(nsIFrame* aFrame) |
michael@0 | 3697 | { |
michael@0 | 3698 | if (!gCaptureInfo.mContent) { |
michael@0 | 3699 | gCaptureInfo.mAllowed = false; |
michael@0 | 3700 | return; |
michael@0 | 3701 | } |
michael@0 | 3702 | |
michael@0 | 3703 | // null frame argument means clear the capture |
michael@0 | 3704 | if (!aFrame) { |
michael@0 | 3705 | NS_RELEASE(gCaptureInfo.mContent); |
michael@0 | 3706 | gCaptureInfo.mAllowed = false; |
michael@0 | 3707 | return; |
michael@0 | 3708 | } |
michael@0 | 3709 | |
michael@0 | 3710 | nsIFrame* capturingFrame = gCaptureInfo.mContent->GetPrimaryFrame(); |
michael@0 | 3711 | if (!capturingFrame) { |
michael@0 | 3712 | NS_RELEASE(gCaptureInfo.mContent); |
michael@0 | 3713 | gCaptureInfo.mAllowed = false; |
michael@0 | 3714 | return; |
michael@0 | 3715 | } |
michael@0 | 3716 | |
michael@0 | 3717 | if (nsLayoutUtils::IsAncestorFrameCrossDoc(aFrame, capturingFrame)) { |
michael@0 | 3718 | NS_RELEASE(gCaptureInfo.mContent); |
michael@0 | 3719 | gCaptureInfo.mAllowed = false; |
michael@0 | 3720 | } |
michael@0 | 3721 | } |
michael@0 | 3722 | |
michael@0 | 3723 | nsresult |
michael@0 | 3724 | PresShell::CaptureHistoryState(nsILayoutHistoryState** aState) |
michael@0 | 3725 | { |
michael@0 | 3726 | NS_PRECONDITION(nullptr != aState, "null state pointer"); |
michael@0 | 3727 | |
michael@0 | 3728 | // We actually have to mess with the docshell here, since we want to |
michael@0 | 3729 | // store the state back in it. |
michael@0 | 3730 | // XXXbz this isn't really right, since this is being called in the |
michael@0 | 3731 | // content viewer's Hide() method... by that point the docshell's |
michael@0 | 3732 | // state could be wrong. We should sort out a better ownership |
michael@0 | 3733 | // model for the layout history state. |
michael@0 | 3734 | nsCOMPtr<nsIDocShell> docShell(mPresContext->GetDocShell()); |
michael@0 | 3735 | if (!docShell) |
michael@0 | 3736 | return NS_ERROR_FAILURE; |
michael@0 | 3737 | |
michael@0 | 3738 | nsCOMPtr<nsILayoutHistoryState> historyState; |
michael@0 | 3739 | docShell->GetLayoutHistoryState(getter_AddRefs(historyState)); |
michael@0 | 3740 | if (!historyState) { |
michael@0 | 3741 | // Create the document state object |
michael@0 | 3742 | historyState = NS_NewLayoutHistoryState(); |
michael@0 | 3743 | docShell->SetLayoutHistoryState(historyState); |
michael@0 | 3744 | } |
michael@0 | 3745 | |
michael@0 | 3746 | *aState = historyState; |
michael@0 | 3747 | NS_IF_ADDREF(*aState); |
michael@0 | 3748 | |
michael@0 | 3749 | // Capture frame state for the entire frame hierarchy |
michael@0 | 3750 | nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); |
michael@0 | 3751 | if (!rootFrame) return NS_OK; |
michael@0 | 3752 | |
michael@0 | 3753 | mFrameConstructor->CaptureFrameState(rootFrame, historyState); |
michael@0 | 3754 | |
michael@0 | 3755 | return NS_OK; |
michael@0 | 3756 | } |
michael@0 | 3757 | |
michael@0 | 3758 | void |
michael@0 | 3759 | PresShell::UnsuppressAndInvalidate() |
michael@0 | 3760 | { |
michael@0 | 3761 | // Note: We ignore the EnsureVisible check for resource documents, because |
michael@0 | 3762 | // they won't have a docshell, so they'll always fail EnsureVisible. |
michael@0 | 3763 | if ((!mDocument->IsResourceDoc() && !mPresContext->EnsureVisible()) || |
michael@0 | 3764 | mHaveShutDown) { |
michael@0 | 3765 | // No point; we're about to be torn down anyway. |
michael@0 | 3766 | return; |
michael@0 | 3767 | } |
michael@0 | 3768 | |
michael@0 | 3769 | if (!mDocument->IsResourceDoc()) { |
michael@0 | 3770 | // Notify observers that a new page is about to be drawn. Execute this |
michael@0 | 3771 | // as soon as it is safe to run JS, which is guaranteed to be before we |
michael@0 | 3772 | // go back to the event loop and actually draw the page. |
michael@0 | 3773 | nsContentUtils::AddScriptRunner(new nsBeforeFirstPaintDispatcher(mDocument)); |
michael@0 | 3774 | } |
michael@0 | 3775 | |
michael@0 | 3776 | mPaintingSuppressed = false; |
michael@0 | 3777 | nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); |
michael@0 | 3778 | if (rootFrame) { |
michael@0 | 3779 | // let's assume that outline on a root frame is not supported |
michael@0 | 3780 | rootFrame->InvalidateFrame(); |
michael@0 | 3781 | |
michael@0 | 3782 | if (mCaretEnabled && mCaret) { |
michael@0 | 3783 | mCaret->CheckCaretDrawingState(); |
michael@0 | 3784 | } |
michael@0 | 3785 | } |
michael@0 | 3786 | |
michael@0 | 3787 | // now that painting is unsuppressed, focus may be set on the document |
michael@0 | 3788 | nsPIDOMWindow *win = mDocument->GetWindow(); |
michael@0 | 3789 | if (win) |
michael@0 | 3790 | win->SetReadyForFocus(); |
michael@0 | 3791 | |
michael@0 | 3792 | if (!mHaveShutDown) { |
michael@0 | 3793 | SynthesizeMouseMove(false); |
michael@0 | 3794 | ScheduleImageVisibilityUpdate(); |
michael@0 | 3795 | } |
michael@0 | 3796 | } |
michael@0 | 3797 | |
michael@0 | 3798 | void |
michael@0 | 3799 | PresShell::UnsuppressPainting() |
michael@0 | 3800 | { |
michael@0 | 3801 | if (mPaintSuppressionTimer) { |
michael@0 | 3802 | mPaintSuppressionTimer->Cancel(); |
michael@0 | 3803 | mPaintSuppressionTimer = nullptr; |
michael@0 | 3804 | } |
michael@0 | 3805 | |
michael@0 | 3806 | if (mIsDocumentGone || !mPaintingSuppressed) |
michael@0 | 3807 | return; |
michael@0 | 3808 | |
michael@0 | 3809 | // If we have reflows pending, just wait until we process |
michael@0 | 3810 | // the reflows and get all the frames where we want them |
michael@0 | 3811 | // before actually unlocking the painting. Otherwise |
michael@0 | 3812 | // go ahead and unlock now. |
michael@0 | 3813 | if (!mDirtyRoots.IsEmpty()) |
michael@0 | 3814 | mShouldUnsuppressPainting = true; |
michael@0 | 3815 | else |
michael@0 | 3816 | UnsuppressAndInvalidate(); |
michael@0 | 3817 | } |
michael@0 | 3818 | |
michael@0 | 3819 | // Post a request to handle an arbitrary callback after reflow has finished. |
michael@0 | 3820 | nsresult |
michael@0 | 3821 | PresShell::PostReflowCallback(nsIReflowCallback* aCallback) |
michael@0 | 3822 | { |
michael@0 | 3823 | void* result = AllocateMisc(sizeof(nsCallbackEventRequest)); |
michael@0 | 3824 | nsCallbackEventRequest* request = (nsCallbackEventRequest*)result; |
michael@0 | 3825 | |
michael@0 | 3826 | request->callback = aCallback; |
michael@0 | 3827 | request->next = nullptr; |
michael@0 | 3828 | |
michael@0 | 3829 | if (mLastCallbackEventRequest) { |
michael@0 | 3830 | mLastCallbackEventRequest = mLastCallbackEventRequest->next = request; |
michael@0 | 3831 | } else { |
michael@0 | 3832 | mFirstCallbackEventRequest = request; |
michael@0 | 3833 | mLastCallbackEventRequest = request; |
michael@0 | 3834 | } |
michael@0 | 3835 | |
michael@0 | 3836 | return NS_OK; |
michael@0 | 3837 | } |
michael@0 | 3838 | |
michael@0 | 3839 | void |
michael@0 | 3840 | PresShell::CancelReflowCallback(nsIReflowCallback* aCallback) |
michael@0 | 3841 | { |
michael@0 | 3842 | nsCallbackEventRequest* before = nullptr; |
michael@0 | 3843 | nsCallbackEventRequest* node = mFirstCallbackEventRequest; |
michael@0 | 3844 | while(node) |
michael@0 | 3845 | { |
michael@0 | 3846 | nsIReflowCallback* callback = node->callback; |
michael@0 | 3847 | |
michael@0 | 3848 | if (callback == aCallback) |
michael@0 | 3849 | { |
michael@0 | 3850 | nsCallbackEventRequest* toFree = node; |
michael@0 | 3851 | if (node == mFirstCallbackEventRequest) { |
michael@0 | 3852 | node = node->next; |
michael@0 | 3853 | mFirstCallbackEventRequest = node; |
michael@0 | 3854 | NS_ASSERTION(before == nullptr, "impossible"); |
michael@0 | 3855 | } else { |
michael@0 | 3856 | node = node->next; |
michael@0 | 3857 | before->next = node; |
michael@0 | 3858 | } |
michael@0 | 3859 | |
michael@0 | 3860 | if (toFree == mLastCallbackEventRequest) { |
michael@0 | 3861 | mLastCallbackEventRequest = before; |
michael@0 | 3862 | } |
michael@0 | 3863 | |
michael@0 | 3864 | FreeMisc(sizeof(nsCallbackEventRequest), toFree); |
michael@0 | 3865 | } else { |
michael@0 | 3866 | before = node; |
michael@0 | 3867 | node = node->next; |
michael@0 | 3868 | } |
michael@0 | 3869 | } |
michael@0 | 3870 | } |
michael@0 | 3871 | |
michael@0 | 3872 | void |
michael@0 | 3873 | PresShell::CancelPostedReflowCallbacks() |
michael@0 | 3874 | { |
michael@0 | 3875 | while (mFirstCallbackEventRequest) { |
michael@0 | 3876 | nsCallbackEventRequest* node = mFirstCallbackEventRequest; |
michael@0 | 3877 | mFirstCallbackEventRequest = node->next; |
michael@0 | 3878 | if (!mFirstCallbackEventRequest) { |
michael@0 | 3879 | mLastCallbackEventRequest = nullptr; |
michael@0 | 3880 | } |
michael@0 | 3881 | nsIReflowCallback* callback = node->callback; |
michael@0 | 3882 | FreeMisc(sizeof(nsCallbackEventRequest), node); |
michael@0 | 3883 | if (callback) { |
michael@0 | 3884 | callback->ReflowCallbackCanceled(); |
michael@0 | 3885 | } |
michael@0 | 3886 | } |
michael@0 | 3887 | } |
michael@0 | 3888 | |
michael@0 | 3889 | void |
michael@0 | 3890 | PresShell::HandlePostedReflowCallbacks(bool aInterruptible) |
michael@0 | 3891 | { |
michael@0 | 3892 | bool shouldFlush = false; |
michael@0 | 3893 | |
michael@0 | 3894 | while (mFirstCallbackEventRequest) { |
michael@0 | 3895 | nsCallbackEventRequest* node = mFirstCallbackEventRequest; |
michael@0 | 3896 | mFirstCallbackEventRequest = node->next; |
michael@0 | 3897 | if (!mFirstCallbackEventRequest) { |
michael@0 | 3898 | mLastCallbackEventRequest = nullptr; |
michael@0 | 3899 | } |
michael@0 | 3900 | nsIReflowCallback* callback = node->callback; |
michael@0 | 3901 | FreeMisc(sizeof(nsCallbackEventRequest), node); |
michael@0 | 3902 | if (callback) { |
michael@0 | 3903 | if (callback->ReflowFinished()) { |
michael@0 | 3904 | shouldFlush = true; |
michael@0 | 3905 | } |
michael@0 | 3906 | } |
michael@0 | 3907 | } |
michael@0 | 3908 | |
michael@0 | 3909 | mozFlushType flushType = |
michael@0 | 3910 | aInterruptible ? Flush_InterruptibleLayout : Flush_Layout; |
michael@0 | 3911 | if (shouldFlush && !mIsDestroying) { |
michael@0 | 3912 | FlushPendingNotifications(flushType); |
michael@0 | 3913 | } |
michael@0 | 3914 | } |
michael@0 | 3915 | |
michael@0 | 3916 | bool |
michael@0 | 3917 | PresShell::IsSafeToFlush() const |
michael@0 | 3918 | { |
michael@0 | 3919 | // Not safe if we are reflowing or in the middle of frame construction |
michael@0 | 3920 | bool isSafeToFlush = !mIsReflowing && |
michael@0 | 3921 | !mChangeNestCount; |
michael@0 | 3922 | |
michael@0 | 3923 | if (isSafeToFlush) { |
michael@0 | 3924 | // Not safe if we are painting |
michael@0 | 3925 | nsViewManager* viewManager = GetViewManager(); |
michael@0 | 3926 | if (viewManager) { |
michael@0 | 3927 | bool isPainting = false; |
michael@0 | 3928 | viewManager->IsPainting(isPainting); |
michael@0 | 3929 | if (isPainting) { |
michael@0 | 3930 | isSafeToFlush = false; |
michael@0 | 3931 | } |
michael@0 | 3932 | } |
michael@0 | 3933 | } |
michael@0 | 3934 | |
michael@0 | 3935 | return isSafeToFlush; |
michael@0 | 3936 | } |
michael@0 | 3937 | |
michael@0 | 3938 | |
michael@0 | 3939 | void |
michael@0 | 3940 | PresShell::FlushPendingNotifications(mozFlushType aType) |
michael@0 | 3941 | { |
michael@0 | 3942 | // by default, flush animations if aType >= Flush_Style |
michael@0 | 3943 | mozilla::ChangesToFlush flush(aType, aType >= Flush_Style); |
michael@0 | 3944 | FlushPendingNotifications(flush); |
michael@0 | 3945 | } |
michael@0 | 3946 | |
michael@0 | 3947 | void |
michael@0 | 3948 | PresShell::FlushPendingNotifications(mozilla::ChangesToFlush aFlush) |
michael@0 | 3949 | { |
michael@0 | 3950 | if (mIsZombie) { |
michael@0 | 3951 | return; |
michael@0 | 3952 | } |
michael@0 | 3953 | |
michael@0 | 3954 | /** |
michael@0 | 3955 | * VERY IMPORTANT: If you add some sort of new flushing to this |
michael@0 | 3956 | * method, make sure to add the relevant SetNeedLayoutFlush or |
michael@0 | 3957 | * SetNeedStyleFlush calls on the document. |
michael@0 | 3958 | */ |
michael@0 | 3959 | mozFlushType flushType = aFlush.mFlushType; |
michael@0 | 3960 | |
michael@0 | 3961 | #ifdef MOZ_ENABLE_PROFILER_SPS |
michael@0 | 3962 | static const char flushTypeNames[][20] = { |
michael@0 | 3963 | "Content", |
michael@0 | 3964 | "ContentAndNotify", |
michael@0 | 3965 | "Style", |
michael@0 | 3966 | "InterruptibleLayout", |
michael@0 | 3967 | "Layout", |
michael@0 | 3968 | "Display" |
michael@0 | 3969 | }; |
michael@0 | 3970 | |
michael@0 | 3971 | // Make sure that we don't miss things added to mozFlushType! |
michael@0 | 3972 | MOZ_ASSERT(static_cast<uint32_t>(flushType) <= ArrayLength(flushTypeNames)); |
michael@0 | 3973 | |
michael@0 | 3974 | PROFILER_LABEL_PRINTF("layout", "Flush", "(Flush_%s)", |
michael@0 | 3975 | flushTypeNames[flushType - 1]); |
michael@0 | 3976 | #endif |
michael@0 | 3977 | |
michael@0 | 3978 | #ifdef ACCESSIBILITY |
michael@0 | 3979 | #ifdef DEBUG |
michael@0 | 3980 | nsAccessibilityService* accService = GetAccService(); |
michael@0 | 3981 | if (accService) { |
michael@0 | 3982 | NS_ASSERTION(!accService->IsProcessingRefreshDriverNotification(), |
michael@0 | 3983 | "Flush during accessible tree update!"); |
michael@0 | 3984 | } |
michael@0 | 3985 | #endif |
michael@0 | 3986 | #endif |
michael@0 | 3987 | |
michael@0 | 3988 | NS_ASSERTION(flushType >= Flush_Frames, "Why did we get called?"); |
michael@0 | 3989 | |
michael@0 | 3990 | bool isSafeToFlush = IsSafeToFlush(); |
michael@0 | 3991 | |
michael@0 | 3992 | // If layout could possibly trigger scripts, then it's only safe to flush if |
michael@0 | 3993 | // it's safe to run script. |
michael@0 | 3994 | bool hasHadScriptObject; |
michael@0 | 3995 | if (mDocument->GetScriptHandlingObject(hasHadScriptObject) || |
michael@0 | 3996 | hasHadScriptObject) { |
michael@0 | 3997 | isSafeToFlush = isSafeToFlush && nsContentUtils::IsSafeToRunScript(); |
michael@0 | 3998 | } |
michael@0 | 3999 | |
michael@0 | 4000 | NS_ASSERTION(!isSafeToFlush || mViewManager, "Must have view manager"); |
michael@0 | 4001 | // Make sure the view manager stays alive. |
michael@0 | 4002 | nsRefPtr<nsViewManager> viewManagerDeathGrip = mViewManager; |
michael@0 | 4003 | if (isSafeToFlush && mViewManager) { |
michael@0 | 4004 | // Processing pending notifications can kill us, and some callers only |
michael@0 | 4005 | // hold weak refs when calling FlushPendingNotifications(). :( |
michael@0 | 4006 | nsCOMPtr<nsIPresShell> kungFuDeathGrip(this); |
michael@0 | 4007 | |
michael@0 | 4008 | if (mResizeEvent.IsPending()) { |
michael@0 | 4009 | FireResizeEvent(); |
michael@0 | 4010 | if (mIsDestroying) { |
michael@0 | 4011 | return; |
michael@0 | 4012 | } |
michael@0 | 4013 | } |
michael@0 | 4014 | |
michael@0 | 4015 | // We need to make sure external resource documents are flushed too (for |
michael@0 | 4016 | // example, svg filters that reference a filter in an external document |
michael@0 | 4017 | // need the frames in the external document to be constructed for the |
michael@0 | 4018 | // filter to work). We only need external resources to be flushed when the |
michael@0 | 4019 | // main document is flushing >= Flush_Frames, so we flush external |
michael@0 | 4020 | // resources here instead of nsDocument::FlushPendingNotifications. |
michael@0 | 4021 | mDocument->FlushExternalResources(flushType); |
michael@0 | 4022 | |
michael@0 | 4023 | // Force flushing of any pending content notifications that might have |
michael@0 | 4024 | // queued up while our event was pending. That will ensure that we don't |
michael@0 | 4025 | // construct frames for content right now that's still waiting to be |
michael@0 | 4026 | // notified on, |
michael@0 | 4027 | mDocument->FlushPendingNotifications(Flush_ContentAndNotify); |
michael@0 | 4028 | |
michael@0 | 4029 | // Process pending restyles, since any flush of the presshell wants |
michael@0 | 4030 | // up-to-date style data. |
michael@0 | 4031 | if (!mIsDestroying) { |
michael@0 | 4032 | mViewManager->FlushDelayedResize(false); |
michael@0 | 4033 | mPresContext->FlushPendingMediaFeatureValuesChanged(); |
michael@0 | 4034 | |
michael@0 | 4035 | // Flush any pending update of the user font set, since that could |
michael@0 | 4036 | // cause style changes (for updating ex/ch units, and to cause a |
michael@0 | 4037 | // reflow). |
michael@0 | 4038 | mPresContext->FlushUserFontSet(); |
michael@0 | 4039 | |
michael@0 | 4040 | // Flush any requested SMIL samples. |
michael@0 | 4041 | if (mDocument->HasAnimationController()) { |
michael@0 | 4042 | mDocument->GetAnimationController()->FlushResampleRequests(); |
michael@0 | 4043 | } |
michael@0 | 4044 | |
michael@0 | 4045 | if (aFlush.mFlushAnimations && |
michael@0 | 4046 | nsLayoutUtils::AreAsyncAnimationsEnabled() && |
michael@0 | 4047 | !mPresContext->StyleUpdateForAllAnimationsIsUpToDate()) { |
michael@0 | 4048 | mPresContext->AnimationManager()-> |
michael@0 | 4049 | FlushAnimations(CommonAnimationManager::Cannot_Throttle); |
michael@0 | 4050 | mPresContext->TransitionManager()-> |
michael@0 | 4051 | FlushTransitions(CommonAnimationManager::Cannot_Throttle); |
michael@0 | 4052 | mPresContext->TickLastStyleUpdateForAllAnimations(); |
michael@0 | 4053 | } |
michael@0 | 4054 | |
michael@0 | 4055 | // The FlushResampleRequests() above flushed style changes. |
michael@0 | 4056 | if (!mIsDestroying) { |
michael@0 | 4057 | nsAutoScriptBlocker scriptBlocker; |
michael@0 | 4058 | mPresContext->RestyleManager()->ProcessPendingRestyles(); |
michael@0 | 4059 | } |
michael@0 | 4060 | } |
michael@0 | 4061 | |
michael@0 | 4062 | // Dispatch any 'animationstart' events those (or earlier) restyles |
michael@0 | 4063 | // queued up. |
michael@0 | 4064 | if (!mIsDestroying) { |
michael@0 | 4065 | mPresContext->AnimationManager()->DispatchEvents(); |
michael@0 | 4066 | } |
michael@0 | 4067 | |
michael@0 | 4068 | // Process whatever XBL constructors those restyles queued up. This |
michael@0 | 4069 | // ensures that onload doesn't fire too early and that we won't do extra |
michael@0 | 4070 | // reflows after those constructors run. |
michael@0 | 4071 | if (!mIsDestroying) { |
michael@0 | 4072 | mDocument->BindingManager()->ProcessAttachedQueue(); |
michael@0 | 4073 | } |
michael@0 | 4074 | |
michael@0 | 4075 | // Now those constructors or events might have posted restyle |
michael@0 | 4076 | // events. At the same time, we still need up-to-date style data. |
michael@0 | 4077 | // In particular, reflow depends on style being completely up to |
michael@0 | 4078 | // date. If it's not, then style context reparenting, which can |
michael@0 | 4079 | // happen during reflow, might suddenly pick up the new rules and |
michael@0 | 4080 | // we'll end up with frames whose style doesn't match the frame |
michael@0 | 4081 | // type. |
michael@0 | 4082 | if (!mIsDestroying) { |
michael@0 | 4083 | nsAutoScriptBlocker scriptBlocker; |
michael@0 | 4084 | mPresContext->RestyleManager()->ProcessPendingRestyles(); |
michael@0 | 4085 | } |
michael@0 | 4086 | |
michael@0 | 4087 | |
michael@0 | 4088 | // There might be more pending constructors now, but we're not going to |
michael@0 | 4089 | // worry about them. They can't be triggered during reflow, so we should |
michael@0 | 4090 | // be good. |
michael@0 | 4091 | |
michael@0 | 4092 | if (flushType >= (mSuppressInterruptibleReflows ? Flush_Layout : Flush_InterruptibleLayout) && |
michael@0 | 4093 | !mIsDestroying) { |
michael@0 | 4094 | mFrameConstructor->RecalcQuotesAndCounters(); |
michael@0 | 4095 | mViewManager->FlushDelayedResize(true); |
michael@0 | 4096 | if (ProcessReflowCommands(flushType < Flush_Layout) && mContentToScrollTo) { |
michael@0 | 4097 | // We didn't get interrupted. Go ahead and scroll to our content |
michael@0 | 4098 | DoScrollContentIntoView(); |
michael@0 | 4099 | if (mContentToScrollTo) { |
michael@0 | 4100 | mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling); |
michael@0 | 4101 | mContentToScrollTo = nullptr; |
michael@0 | 4102 | } |
michael@0 | 4103 | } |
michael@0 | 4104 | } else if (!mIsDestroying && mSuppressInterruptibleReflows && |
michael@0 | 4105 | flushType == Flush_InterruptibleLayout) { |
michael@0 | 4106 | // We suppressed this flush, but the document thinks it doesn't |
michael@0 | 4107 | // need to flush anymore. Let it know what's really going on. |
michael@0 | 4108 | mDocument->SetNeedLayoutFlush(); |
michael@0 | 4109 | } |
michael@0 | 4110 | |
michael@0 | 4111 | if (flushType >= Flush_Layout) { |
michael@0 | 4112 | if (!mIsDestroying) { |
michael@0 | 4113 | mViewManager->UpdateWidgetGeometry(); |
michael@0 | 4114 | } |
michael@0 | 4115 | } |
michael@0 | 4116 | } |
michael@0 | 4117 | } |
michael@0 | 4118 | |
michael@0 | 4119 | void |
michael@0 | 4120 | PresShell::CharacterDataWillChange(nsIDocument *aDocument, |
michael@0 | 4121 | nsIContent* aContent, |
michael@0 | 4122 | CharacterDataChangeInfo* aInfo) |
michael@0 | 4123 | { |
michael@0 | 4124 | NS_PRECONDITION(!mIsDocumentGone, "Unexpected CharacterDataChanged"); |
michael@0 | 4125 | NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument"); |
michael@0 | 4126 | |
michael@0 | 4127 | if (mCaret) { |
michael@0 | 4128 | // Invalidate the caret's current location before we call into the frame |
michael@0 | 4129 | // constructor. It is important to do this now, and not wait until the |
michael@0 | 4130 | // resulting reflow, because this call causes continuation frames of the |
michael@0 | 4131 | // text frame the caret is in to forget what part of the content they |
michael@0 | 4132 | // refer to, making it hard for them to return the correct continuation |
michael@0 | 4133 | // frame to the caret. |
michael@0 | 4134 | // |
michael@0 | 4135 | // It's also important to do this before the content actually changes, since |
michael@0 | 4136 | // in bidi text the caret needs to look at the content to determine its |
michael@0 | 4137 | // position and shape. |
michael@0 | 4138 | mCaret->InvalidateOutsideCaret(); |
michael@0 | 4139 | } |
michael@0 | 4140 | } |
michael@0 | 4141 | |
michael@0 | 4142 | void |
michael@0 | 4143 | PresShell::CharacterDataChanged(nsIDocument *aDocument, |
michael@0 | 4144 | nsIContent* aContent, |
michael@0 | 4145 | CharacterDataChangeInfo* aInfo) |
michael@0 | 4146 | { |
michael@0 | 4147 | NS_PRECONDITION(!mIsDocumentGone, "Unexpected CharacterDataChanged"); |
michael@0 | 4148 | NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument"); |
michael@0 | 4149 | |
michael@0 | 4150 | nsAutoCauseReflowNotifier crNotifier(this); |
michael@0 | 4151 | |
michael@0 | 4152 | // Call this here so it only happens for real content mutations and |
michael@0 | 4153 | // not cases when the frame constructor calls its own methods to force |
michael@0 | 4154 | // frame reconstruction. |
michael@0 | 4155 | nsIContent *container = aContent->GetParent(); |
michael@0 | 4156 | uint32_t selectorFlags = |
michael@0 | 4157 | container ? (container->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0; |
michael@0 | 4158 | if (selectorFlags != 0 && !aContent->IsRootOfAnonymousSubtree()) { |
michael@0 | 4159 | Element* element = container->AsElement(); |
michael@0 | 4160 | if (aInfo->mAppend && !aContent->GetNextSibling()) |
michael@0 | 4161 | mPresContext->RestyleManager()->RestyleForAppend(element, aContent); |
michael@0 | 4162 | else |
michael@0 | 4163 | mPresContext->RestyleManager()->RestyleForInsertOrChange(element, aContent); |
michael@0 | 4164 | } |
michael@0 | 4165 | |
michael@0 | 4166 | mFrameConstructor->CharacterDataChanged(aContent, aInfo); |
michael@0 | 4167 | VERIFY_STYLE_TREE; |
michael@0 | 4168 | } |
michael@0 | 4169 | |
michael@0 | 4170 | void |
michael@0 | 4171 | PresShell::ContentStateChanged(nsIDocument* aDocument, |
michael@0 | 4172 | nsIContent* aContent, |
michael@0 | 4173 | EventStates aStateMask) |
michael@0 | 4174 | { |
michael@0 | 4175 | NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentStateChanged"); |
michael@0 | 4176 | NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument"); |
michael@0 | 4177 | |
michael@0 | 4178 | if (mDidInitialize) { |
michael@0 | 4179 | nsAutoCauseReflowNotifier crNotifier(this); |
michael@0 | 4180 | mPresContext->RestyleManager()->ContentStateChanged(aContent, aStateMask); |
michael@0 | 4181 | VERIFY_STYLE_TREE; |
michael@0 | 4182 | } |
michael@0 | 4183 | } |
michael@0 | 4184 | |
michael@0 | 4185 | void |
michael@0 | 4186 | PresShell::DocumentStatesChanged(nsIDocument* aDocument, |
michael@0 | 4187 | EventStates aStateMask) |
michael@0 | 4188 | { |
michael@0 | 4189 | NS_PRECONDITION(!mIsDocumentGone, "Unexpected DocumentStatesChanged"); |
michael@0 | 4190 | NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument"); |
michael@0 | 4191 | |
michael@0 | 4192 | if (mDidInitialize && |
michael@0 | 4193 | mStyleSet->HasDocumentStateDependentStyle(mPresContext, |
michael@0 | 4194 | mDocument->GetRootElement(), |
michael@0 | 4195 | aStateMask)) { |
michael@0 | 4196 | mPresContext->RestyleManager()->PostRestyleEvent(mDocument->GetRootElement(), |
michael@0 | 4197 | eRestyle_Subtree, |
michael@0 | 4198 | NS_STYLE_HINT_NONE); |
michael@0 | 4199 | VERIFY_STYLE_TREE; |
michael@0 | 4200 | } |
michael@0 | 4201 | |
michael@0 | 4202 | if (aStateMask.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) { |
michael@0 | 4203 | nsIFrame* root = mFrameConstructor->GetRootFrame(); |
michael@0 | 4204 | if (root) { |
michael@0 | 4205 | root->SchedulePaint(); |
michael@0 | 4206 | } |
michael@0 | 4207 | } |
michael@0 | 4208 | } |
michael@0 | 4209 | |
michael@0 | 4210 | void |
michael@0 | 4211 | PresShell::AttributeWillChange(nsIDocument* aDocument, |
michael@0 | 4212 | Element* aElement, |
michael@0 | 4213 | int32_t aNameSpaceID, |
michael@0 | 4214 | nsIAtom* aAttribute, |
michael@0 | 4215 | int32_t aModType) |
michael@0 | 4216 | { |
michael@0 | 4217 | NS_PRECONDITION(!mIsDocumentGone, "Unexpected AttributeWillChange"); |
michael@0 | 4218 | NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument"); |
michael@0 | 4219 | |
michael@0 | 4220 | // XXXwaterson it might be more elegant to wait until after the |
michael@0 | 4221 | // initial reflow to begin observing the document. That would |
michael@0 | 4222 | // squelch any other inappropriate notifications as well. |
michael@0 | 4223 | if (mDidInitialize) { |
michael@0 | 4224 | nsAutoCauseReflowNotifier crNotifier(this); |
michael@0 | 4225 | mPresContext->RestyleManager()->AttributeWillChange(aElement, aNameSpaceID, |
michael@0 | 4226 | aAttribute, aModType); |
michael@0 | 4227 | VERIFY_STYLE_TREE; |
michael@0 | 4228 | } |
michael@0 | 4229 | } |
michael@0 | 4230 | |
michael@0 | 4231 | void |
michael@0 | 4232 | PresShell::AttributeChanged(nsIDocument* aDocument, |
michael@0 | 4233 | Element* aElement, |
michael@0 | 4234 | int32_t aNameSpaceID, |
michael@0 | 4235 | nsIAtom* aAttribute, |
michael@0 | 4236 | int32_t aModType) |
michael@0 | 4237 | { |
michael@0 | 4238 | NS_PRECONDITION(!mIsDocumentGone, "Unexpected AttributeChanged"); |
michael@0 | 4239 | NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument"); |
michael@0 | 4240 | |
michael@0 | 4241 | // XXXwaterson it might be more elegant to wait until after the |
michael@0 | 4242 | // initial reflow to begin observing the document. That would |
michael@0 | 4243 | // squelch any other inappropriate notifications as well. |
michael@0 | 4244 | if (mDidInitialize) { |
michael@0 | 4245 | nsAutoCauseReflowNotifier crNotifier(this); |
michael@0 | 4246 | mPresContext->RestyleManager()->AttributeChanged(aElement, aNameSpaceID, |
michael@0 | 4247 | aAttribute, aModType); |
michael@0 | 4248 | VERIFY_STYLE_TREE; |
michael@0 | 4249 | } |
michael@0 | 4250 | } |
michael@0 | 4251 | |
michael@0 | 4252 | void |
michael@0 | 4253 | PresShell::ContentAppended(nsIDocument *aDocument, |
michael@0 | 4254 | nsIContent* aContainer, |
michael@0 | 4255 | nsIContent* aFirstNewContent, |
michael@0 | 4256 | int32_t aNewIndexInContainer) |
michael@0 | 4257 | { |
michael@0 | 4258 | NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentAppended"); |
michael@0 | 4259 | NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument"); |
michael@0 | 4260 | NS_PRECONDITION(aContainer, "must have container"); |
michael@0 | 4261 | |
michael@0 | 4262 | if (!mDidInitialize) { |
michael@0 | 4263 | return; |
michael@0 | 4264 | } |
michael@0 | 4265 | |
michael@0 | 4266 | nsAutoCauseReflowNotifier crNotifier(this); |
michael@0 | 4267 | |
michael@0 | 4268 | // Call this here so it only happens for real content mutations and |
michael@0 | 4269 | // not cases when the frame constructor calls its own methods to force |
michael@0 | 4270 | // frame reconstruction. |
michael@0 | 4271 | if (aContainer->IsElement()) { |
michael@0 | 4272 | // Ensure the container is an element before trying to restyle |
michael@0 | 4273 | // because it can be the case that the container is a ShadowRoot |
michael@0 | 4274 | // which is a document fragment. |
michael@0 | 4275 | mPresContext->RestyleManager()-> |
michael@0 | 4276 | RestyleForAppend(aContainer->AsElement(), aFirstNewContent); |
michael@0 | 4277 | } |
michael@0 | 4278 | |
michael@0 | 4279 | mFrameConstructor->ContentAppended(aContainer, aFirstNewContent, true); |
michael@0 | 4280 | |
michael@0 | 4281 | if (static_cast<nsINode*>(aContainer) == static_cast<nsINode*>(aDocument) && |
michael@0 | 4282 | aFirstNewContent->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) { |
michael@0 | 4283 | NotifyFontSizeInflationEnabledIsDirty(); |
michael@0 | 4284 | } |
michael@0 | 4285 | |
michael@0 | 4286 | VERIFY_STYLE_TREE; |
michael@0 | 4287 | } |
michael@0 | 4288 | |
michael@0 | 4289 | void |
michael@0 | 4290 | PresShell::ContentInserted(nsIDocument* aDocument, |
michael@0 | 4291 | nsIContent* aContainer, |
michael@0 | 4292 | nsIContent* aChild, |
michael@0 | 4293 | int32_t aIndexInContainer) |
michael@0 | 4294 | { |
michael@0 | 4295 | NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentInserted"); |
michael@0 | 4296 | NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument"); |
michael@0 | 4297 | |
michael@0 | 4298 | if (!mDidInitialize) { |
michael@0 | 4299 | return; |
michael@0 | 4300 | } |
michael@0 | 4301 | |
michael@0 | 4302 | nsAutoCauseReflowNotifier crNotifier(this); |
michael@0 | 4303 | |
michael@0 | 4304 | // Call this here so it only happens for real content mutations and |
michael@0 | 4305 | // not cases when the frame constructor calls its own methods to force |
michael@0 | 4306 | // frame reconstruction. |
michael@0 | 4307 | if (aContainer && aContainer->IsElement()) { |
michael@0 | 4308 | // Ensure the container is an element before trying to restyle |
michael@0 | 4309 | // because it can be the case that the container is a ShadowRoot |
michael@0 | 4310 | // which is a document fragment. |
michael@0 | 4311 | mPresContext->RestyleManager()-> |
michael@0 | 4312 | RestyleForInsertOrChange(aContainer->AsElement(), aChild); |
michael@0 | 4313 | } |
michael@0 | 4314 | |
michael@0 | 4315 | mFrameConstructor->ContentInserted(aContainer, aChild, nullptr, true); |
michael@0 | 4316 | |
michael@0 | 4317 | if (((!aContainer && aDocument) || |
michael@0 | 4318 | (static_cast<nsINode*>(aContainer) == static_cast<nsINode*>(aDocument))) && |
michael@0 | 4319 | aChild->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) { |
michael@0 | 4320 | NotifyFontSizeInflationEnabledIsDirty(); |
michael@0 | 4321 | } |
michael@0 | 4322 | |
michael@0 | 4323 | VERIFY_STYLE_TREE; |
michael@0 | 4324 | } |
michael@0 | 4325 | |
michael@0 | 4326 | void |
michael@0 | 4327 | PresShell::ContentRemoved(nsIDocument *aDocument, |
michael@0 | 4328 | nsIContent* aContainer, |
michael@0 | 4329 | nsIContent* aChild, |
michael@0 | 4330 | int32_t aIndexInContainer, |
michael@0 | 4331 | nsIContent* aPreviousSibling) |
michael@0 | 4332 | { |
michael@0 | 4333 | NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentRemoved"); |
michael@0 | 4334 | NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument"); |
michael@0 | 4335 | |
michael@0 | 4336 | // Make sure that the caret doesn't leave a turd where the child used to be. |
michael@0 | 4337 | if (mCaret) { |
michael@0 | 4338 | mCaret->InvalidateOutsideCaret(); |
michael@0 | 4339 | } |
michael@0 | 4340 | |
michael@0 | 4341 | // Notify the ESM that the content has been removed, so that |
michael@0 | 4342 | // it can clean up any state related to the content. |
michael@0 | 4343 | |
michael@0 | 4344 | // XXX_jwir3: There is no null check for aDocument necessary, since, even |
michael@0 | 4345 | // though by nsIMutationObserver, aDocument could be null, the |
michael@0 | 4346 | // precondition check that mDocument == aDocument ensures that |
michael@0 | 4347 | // aDocument will not be null (since mDocument can't be null unless |
michael@0 | 4348 | // we're still intializing). |
michael@0 | 4349 | mPresContext->EventStateManager()->ContentRemoved(aDocument, aChild); |
michael@0 | 4350 | |
michael@0 | 4351 | nsAutoCauseReflowNotifier crNotifier(this); |
michael@0 | 4352 | |
michael@0 | 4353 | // Call this here so it only happens for real content mutations and |
michael@0 | 4354 | // not cases when the frame constructor calls its own methods to force |
michael@0 | 4355 | // frame reconstruction. |
michael@0 | 4356 | nsIContent* oldNextSibling; |
michael@0 | 4357 | if (aContainer) { |
michael@0 | 4358 | oldNextSibling = aContainer->GetChildAt(aIndexInContainer); |
michael@0 | 4359 | } else { |
michael@0 | 4360 | oldNextSibling = nullptr; |
michael@0 | 4361 | } |
michael@0 | 4362 | |
michael@0 | 4363 | if (aContainer && aContainer->IsElement()) { |
michael@0 | 4364 | mPresContext->RestyleManager()-> |
michael@0 | 4365 | RestyleForRemove(aContainer->AsElement(), aChild, oldNextSibling); |
michael@0 | 4366 | } |
michael@0 | 4367 | |
michael@0 | 4368 | bool didReconstruct; |
michael@0 | 4369 | mFrameConstructor->ContentRemoved(aContainer, aChild, oldNextSibling, |
michael@0 | 4370 | nsCSSFrameConstructor::REMOVE_CONTENT, |
michael@0 | 4371 | &didReconstruct); |
michael@0 | 4372 | |
michael@0 | 4373 | |
michael@0 | 4374 | if (((aContainer && |
michael@0 | 4375 | static_cast<nsINode*>(aContainer) == static_cast<nsINode*>(aDocument)) || |
michael@0 | 4376 | aDocument) && aChild->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) { |
michael@0 | 4377 | NotifyFontSizeInflationEnabledIsDirty(); |
michael@0 | 4378 | } |
michael@0 | 4379 | |
michael@0 | 4380 | VERIFY_STYLE_TREE; |
michael@0 | 4381 | } |
michael@0 | 4382 | |
michael@0 | 4383 | nsresult |
michael@0 | 4384 | PresShell::ReconstructFrames(void) |
michael@0 | 4385 | { |
michael@0 | 4386 | NS_PRECONDITION(!mFrameConstructor->GetRootFrame() || mDidInitialize, |
michael@0 | 4387 | "Must not have root frame before initial reflow"); |
michael@0 | 4388 | if (!mDidInitialize) { |
michael@0 | 4389 | // Nothing to do here |
michael@0 | 4390 | return NS_OK; |
michael@0 | 4391 | } |
michael@0 | 4392 | |
michael@0 | 4393 | nsCOMPtr<nsIPresShell> kungFuDeathGrip(this); |
michael@0 | 4394 | |
michael@0 | 4395 | // Have to make sure that the content notifications are flushed before we |
michael@0 | 4396 | // start messing with the frame model; otherwise we can get content doubling. |
michael@0 | 4397 | mDocument->FlushPendingNotifications(Flush_ContentAndNotify); |
michael@0 | 4398 | |
michael@0 | 4399 | nsAutoCauseReflowNotifier crNotifier(this); |
michael@0 | 4400 | mFrameConstructor->BeginUpdate(); |
michael@0 | 4401 | nsresult rv = mFrameConstructor->ReconstructDocElementHierarchy(); |
michael@0 | 4402 | VERIFY_STYLE_TREE; |
michael@0 | 4403 | mFrameConstructor->EndUpdate(); |
michael@0 | 4404 | |
michael@0 | 4405 | return rv; |
michael@0 | 4406 | } |
michael@0 | 4407 | |
michael@0 | 4408 | void |
michael@0 | 4409 | nsIPresShell::ReconstructStyleDataInternal() |
michael@0 | 4410 | { |
michael@0 | 4411 | nsAutoTArray<nsRefPtr<mozilla::dom::Element>,1> scopeRoots; |
michael@0 | 4412 | mChangedScopeStyleRoots.SwapElements(scopeRoots); |
michael@0 | 4413 | |
michael@0 | 4414 | if (mStylesHaveChanged) { |
michael@0 | 4415 | // If we need to restyle everything, no need to restyle individual |
michael@0 | 4416 | // scoped style roots. |
michael@0 | 4417 | scopeRoots.Clear(); |
michael@0 | 4418 | } |
michael@0 | 4419 | |
michael@0 | 4420 | mStylesHaveChanged = false; |
michael@0 | 4421 | |
michael@0 | 4422 | if (mIsDestroying) { |
michael@0 | 4423 | // We don't want to mess with restyles at this point |
michael@0 | 4424 | return; |
michael@0 | 4425 | } |
michael@0 | 4426 | |
michael@0 | 4427 | if (mPresContext) { |
michael@0 | 4428 | mPresContext->RebuildUserFontSet(); |
michael@0 | 4429 | } |
michael@0 | 4430 | |
michael@0 | 4431 | Element* root = mDocument->GetRootElement(); |
michael@0 | 4432 | if (!mDidInitialize) { |
michael@0 | 4433 | // Nothing to do here, since we have no frames yet |
michael@0 | 4434 | return; |
michael@0 | 4435 | } |
michael@0 | 4436 | |
michael@0 | 4437 | if (!root) { |
michael@0 | 4438 | // No content to restyle |
michael@0 | 4439 | return; |
michael@0 | 4440 | } |
michael@0 | 4441 | |
michael@0 | 4442 | RestyleManager* restyleManager = mPresContext->RestyleManager(); |
michael@0 | 4443 | if (scopeRoots.IsEmpty()) { |
michael@0 | 4444 | // If scopeRoots is empty, we know that mStylesHaveChanged was true at |
michael@0 | 4445 | // the beginning of this function, and that we need to restyle the whole |
michael@0 | 4446 | // document. |
michael@0 | 4447 | restyleManager->PostRestyleEvent(root, eRestyle_Subtree, |
michael@0 | 4448 | NS_STYLE_HINT_NONE); |
michael@0 | 4449 | } else { |
michael@0 | 4450 | for (uint32_t i = 0; i < scopeRoots.Length(); i++) { |
michael@0 | 4451 | Element* scopeRoot = scopeRoots[i]; |
michael@0 | 4452 | restyleManager->PostRestyleEvent(scopeRoot, eRestyle_Subtree, |
michael@0 | 4453 | NS_STYLE_HINT_NONE); |
michael@0 | 4454 | } |
michael@0 | 4455 | } |
michael@0 | 4456 | } |
michael@0 | 4457 | |
michael@0 | 4458 | void |
michael@0 | 4459 | nsIPresShell::ReconstructStyleDataExternal() |
michael@0 | 4460 | { |
michael@0 | 4461 | ReconstructStyleDataInternal(); |
michael@0 | 4462 | } |
michael@0 | 4463 | |
michael@0 | 4464 | void |
michael@0 | 4465 | PresShell::RecordStyleSheetChange(nsIStyleSheet* aStyleSheet) |
michael@0 | 4466 | { |
michael@0 | 4467 | if (mStylesHaveChanged) |
michael@0 | 4468 | return; |
michael@0 | 4469 | |
michael@0 | 4470 | nsRefPtr<nsCSSStyleSheet> cssStyleSheet = do_QueryObject(aStyleSheet); |
michael@0 | 4471 | if (cssStyleSheet) { |
michael@0 | 4472 | Element* scopeElement = cssStyleSheet->GetScopeElement(); |
michael@0 | 4473 | if (scopeElement) { |
michael@0 | 4474 | mChangedScopeStyleRoots.AppendElement(scopeElement); |
michael@0 | 4475 | return; |
michael@0 | 4476 | } |
michael@0 | 4477 | } |
michael@0 | 4478 | |
michael@0 | 4479 | mStylesHaveChanged = true; |
michael@0 | 4480 | } |
michael@0 | 4481 | |
michael@0 | 4482 | void |
michael@0 | 4483 | PresShell::StyleSheetAdded(nsIDocument *aDocument, |
michael@0 | 4484 | nsIStyleSheet* aStyleSheet, |
michael@0 | 4485 | bool aDocumentSheet) |
michael@0 | 4486 | { |
michael@0 | 4487 | // We only care when enabled sheets are added |
michael@0 | 4488 | NS_PRECONDITION(aStyleSheet, "Must have a style sheet!"); |
michael@0 | 4489 | |
michael@0 | 4490 | if (aStyleSheet->IsApplicable() && aStyleSheet->HasRules()) { |
michael@0 | 4491 | RecordStyleSheetChange(aStyleSheet); |
michael@0 | 4492 | } |
michael@0 | 4493 | } |
michael@0 | 4494 | |
michael@0 | 4495 | void |
michael@0 | 4496 | PresShell::StyleSheetRemoved(nsIDocument *aDocument, |
michael@0 | 4497 | nsIStyleSheet* aStyleSheet, |
michael@0 | 4498 | bool aDocumentSheet) |
michael@0 | 4499 | { |
michael@0 | 4500 | // We only care when enabled sheets are removed |
michael@0 | 4501 | NS_PRECONDITION(aStyleSheet, "Must have a style sheet!"); |
michael@0 | 4502 | |
michael@0 | 4503 | if (aStyleSheet->IsApplicable() && aStyleSheet->HasRules()) { |
michael@0 | 4504 | RecordStyleSheetChange(aStyleSheet); |
michael@0 | 4505 | } |
michael@0 | 4506 | } |
michael@0 | 4507 | |
michael@0 | 4508 | void |
michael@0 | 4509 | PresShell::StyleSheetApplicableStateChanged(nsIDocument *aDocument, |
michael@0 | 4510 | nsIStyleSheet* aStyleSheet, |
michael@0 | 4511 | bool aApplicable) |
michael@0 | 4512 | { |
michael@0 | 4513 | if (aStyleSheet->HasRules()) { |
michael@0 | 4514 | RecordStyleSheetChange(aStyleSheet); |
michael@0 | 4515 | } |
michael@0 | 4516 | } |
michael@0 | 4517 | |
michael@0 | 4518 | void |
michael@0 | 4519 | PresShell::StyleRuleChanged(nsIDocument *aDocument, |
michael@0 | 4520 | nsIStyleSheet* aStyleSheet, |
michael@0 | 4521 | nsIStyleRule* aOldStyleRule, |
michael@0 | 4522 | nsIStyleRule* aNewStyleRule) |
michael@0 | 4523 | { |
michael@0 | 4524 | RecordStyleSheetChange(aStyleSheet); |
michael@0 | 4525 | } |
michael@0 | 4526 | |
michael@0 | 4527 | void |
michael@0 | 4528 | PresShell::StyleRuleAdded(nsIDocument *aDocument, |
michael@0 | 4529 | nsIStyleSheet* aStyleSheet, |
michael@0 | 4530 | nsIStyleRule* aStyleRule) |
michael@0 | 4531 | { |
michael@0 | 4532 | RecordStyleSheetChange(aStyleSheet); |
michael@0 | 4533 | } |
michael@0 | 4534 | |
michael@0 | 4535 | void |
michael@0 | 4536 | PresShell::StyleRuleRemoved(nsIDocument *aDocument, |
michael@0 | 4537 | nsIStyleSheet* aStyleSheet, |
michael@0 | 4538 | nsIStyleRule* aStyleRule) |
michael@0 | 4539 | { |
michael@0 | 4540 | RecordStyleSheetChange(aStyleSheet); |
michael@0 | 4541 | } |
michael@0 | 4542 | |
michael@0 | 4543 | nsIFrame* |
michael@0 | 4544 | PresShell::GetRealPrimaryFrameFor(nsIContent* aContent) const |
michael@0 | 4545 | { |
michael@0 | 4546 | if (aContent->GetDocument() != GetDocument()) { |
michael@0 | 4547 | return nullptr; |
michael@0 | 4548 | } |
michael@0 | 4549 | nsIFrame *primaryFrame = aContent->GetPrimaryFrame(); |
michael@0 | 4550 | if (!primaryFrame) |
michael@0 | 4551 | return nullptr; |
michael@0 | 4552 | return nsPlaceholderFrame::GetRealFrameFor(primaryFrame); |
michael@0 | 4553 | } |
michael@0 | 4554 | |
michael@0 | 4555 | nsIFrame* |
michael@0 | 4556 | PresShell::GetPlaceholderFrameFor(nsIFrame* aFrame) const |
michael@0 | 4557 | { |
michael@0 | 4558 | return mFrameConstructor->GetPlaceholderFrameFor(aFrame); |
michael@0 | 4559 | } |
michael@0 | 4560 | |
michael@0 | 4561 | nsresult |
michael@0 | 4562 | PresShell::RenderDocument(const nsRect& aRect, uint32_t aFlags, |
michael@0 | 4563 | nscolor aBackgroundColor, |
michael@0 | 4564 | gfxContext* aThebesContext) |
michael@0 | 4565 | { |
michael@0 | 4566 | NS_ENSURE_TRUE(!(aFlags & RENDER_IS_UNTRUSTED), NS_ERROR_NOT_IMPLEMENTED); |
michael@0 | 4567 | |
michael@0 | 4568 | nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext(); |
michael@0 | 4569 | if (rootPresContext) { |
michael@0 | 4570 | rootPresContext->FlushWillPaintObservers(); |
michael@0 | 4571 | if (mIsDestroying) |
michael@0 | 4572 | return NS_OK; |
michael@0 | 4573 | } |
michael@0 | 4574 | |
michael@0 | 4575 | nsAutoScriptBlocker blockScripts; |
michael@0 | 4576 | |
michael@0 | 4577 | // Set up the rectangle as the path in aThebesContext |
michael@0 | 4578 | gfxRect r(0, 0, |
michael@0 | 4579 | nsPresContext::AppUnitsToFloatCSSPixels(aRect.width), |
michael@0 | 4580 | nsPresContext::AppUnitsToFloatCSSPixels(aRect.height)); |
michael@0 | 4581 | aThebesContext->NewPath(); |
michael@0 | 4582 | #ifdef MOZ_GFX_OPTIMIZE_MOBILE |
michael@0 | 4583 | aThebesContext->Rectangle(r, true); |
michael@0 | 4584 | #else |
michael@0 | 4585 | aThebesContext->Rectangle(r); |
michael@0 | 4586 | #endif |
michael@0 | 4587 | |
michael@0 | 4588 | nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); |
michael@0 | 4589 | if (!rootFrame) { |
michael@0 | 4590 | // Nothing to paint, just fill the rect |
michael@0 | 4591 | aThebesContext->SetColor(gfxRGBA(aBackgroundColor)); |
michael@0 | 4592 | aThebesContext->Fill(); |
michael@0 | 4593 | return NS_OK; |
michael@0 | 4594 | } |
michael@0 | 4595 | |
michael@0 | 4596 | gfxContextAutoSaveRestore save(aThebesContext); |
michael@0 | 4597 | |
michael@0 | 4598 | gfxContext::GraphicsOperator oldOperator = aThebesContext->CurrentOperator(); |
michael@0 | 4599 | if (oldOperator == gfxContext::OPERATOR_OVER) { |
michael@0 | 4600 | // Clip to the destination rectangle before we push the group, |
michael@0 | 4601 | // to limit the size of the temporary surface |
michael@0 | 4602 | aThebesContext->Clip(); |
michael@0 | 4603 | } |
michael@0 | 4604 | |
michael@0 | 4605 | // we want the window to be composited as a single image using |
michael@0 | 4606 | // whatever operator was set; set OPERATOR_OVER here, which is |
michael@0 | 4607 | // either already the case, or overrides the operator in a group. |
michael@0 | 4608 | // the original operator will be present when we PopGroup. |
michael@0 | 4609 | // we can avoid using a temporary surface if we're using OPERATOR_OVER |
michael@0 | 4610 | bool needsGroup = oldOperator != gfxContext::OPERATOR_OVER; |
michael@0 | 4611 | |
michael@0 | 4612 | if (needsGroup) { |
michael@0 | 4613 | aThebesContext->PushGroup(NS_GET_A(aBackgroundColor) == 0xff ? |
michael@0 | 4614 | gfxContentType::COLOR : |
michael@0 | 4615 | gfxContentType::COLOR_ALPHA); |
michael@0 | 4616 | aThebesContext->Save(); |
michael@0 | 4617 | |
michael@0 | 4618 | if (oldOperator != gfxContext::OPERATOR_OVER) { |
michael@0 | 4619 | // Clip now while we paint to the temporary surface. For |
michael@0 | 4620 | // non-source-bounded operators (e.g., SOURCE), we need to do clip |
michael@0 | 4621 | // here after we've pushed the group, so that eventually popping |
michael@0 | 4622 | // the group and painting it will be able to clear the entire |
michael@0 | 4623 | // destination surface. |
michael@0 | 4624 | aThebesContext->Clip(); |
michael@0 | 4625 | aThebesContext->SetOperator(gfxContext::OPERATOR_OVER); |
michael@0 | 4626 | } |
michael@0 | 4627 | } |
michael@0 | 4628 | |
michael@0 | 4629 | aThebesContext->Translate(gfxPoint(-nsPresContext::AppUnitsToFloatCSSPixels(aRect.x), |
michael@0 | 4630 | -nsPresContext::AppUnitsToFloatCSSPixels(aRect.y))); |
michael@0 | 4631 | |
michael@0 | 4632 | nsDeviceContext* devCtx = mPresContext->DeviceContext(); |
michael@0 | 4633 | gfxFloat scale = gfxFloat(devCtx->AppUnitsPerDevPixel())/nsPresContext::AppUnitsPerCSSPixel(); |
michael@0 | 4634 | aThebesContext->Scale(scale, scale); |
michael@0 | 4635 | |
michael@0 | 4636 | // Since canvas APIs use floats to set up their matrices, we may have |
michael@0 | 4637 | // some slight inaccuracy here. Adjust matrix components that are |
michael@0 | 4638 | // integers up to the accuracy of floats to be those integers. |
michael@0 | 4639 | aThebesContext->NudgeCurrentMatrixToIntegers(); |
michael@0 | 4640 | |
michael@0 | 4641 | AutoSaveRestoreRenderingState _(this); |
michael@0 | 4642 | |
michael@0 | 4643 | nsRefPtr<nsRenderingContext> rc = new nsRenderingContext(); |
michael@0 | 4644 | rc->Init(devCtx, aThebesContext); |
michael@0 | 4645 | |
michael@0 | 4646 | bool wouldFlushRetainedLayers = false; |
michael@0 | 4647 | uint32_t flags = nsLayoutUtils::PAINT_IGNORE_SUPPRESSION; |
michael@0 | 4648 | if (aThebesContext->CurrentMatrix().HasNonIntegerTranslation()) { |
michael@0 | 4649 | flags |= nsLayoutUtils::PAINT_IN_TRANSFORM; |
michael@0 | 4650 | } |
michael@0 | 4651 | if (!(aFlags & RENDER_ASYNC_DECODE_IMAGES)) { |
michael@0 | 4652 | flags |= nsLayoutUtils::PAINT_SYNC_DECODE_IMAGES; |
michael@0 | 4653 | } |
michael@0 | 4654 | if (aFlags & RENDER_USE_WIDGET_LAYERS) { |
michael@0 | 4655 | // We only support using widget layers on display root's with widgets. |
michael@0 | 4656 | nsView* view = rootFrame->GetView(); |
michael@0 | 4657 | if (view && view->GetWidget() && |
michael@0 | 4658 | nsLayoutUtils::GetDisplayRootFrame(rootFrame) == rootFrame) { |
michael@0 | 4659 | flags |= nsLayoutUtils::PAINT_WIDGET_LAYERS; |
michael@0 | 4660 | } |
michael@0 | 4661 | } |
michael@0 | 4662 | if (!(aFlags & RENDER_CARET)) { |
michael@0 | 4663 | wouldFlushRetainedLayers = true; |
michael@0 | 4664 | flags |= nsLayoutUtils::PAINT_HIDE_CARET; |
michael@0 | 4665 | } |
michael@0 | 4666 | if (aFlags & RENDER_IGNORE_VIEWPORT_SCROLLING) { |
michael@0 | 4667 | wouldFlushRetainedLayers = !IgnoringViewportScrolling(); |
michael@0 | 4668 | mRenderFlags = ChangeFlag(mRenderFlags, true, STATE_IGNORING_VIEWPORT_SCROLLING); |
michael@0 | 4669 | } |
michael@0 | 4670 | if (aFlags & RENDER_DRAWWINDOW_NOT_FLUSHING) { |
michael@0 | 4671 | mRenderFlags = ChangeFlag(mRenderFlags, true, STATE_DRAWWINDOW_NOT_FLUSHING); |
michael@0 | 4672 | } |
michael@0 | 4673 | if (aFlags & RENDER_DOCUMENT_RELATIVE) { |
michael@0 | 4674 | // XXX be smarter about this ... drawWindow might want a rect |
michael@0 | 4675 | // that's "pretty close" to what our retained layer tree covers. |
michael@0 | 4676 | // In that case, it wouldn't disturb normal rendering too much, |
michael@0 | 4677 | // and we should allow it. |
michael@0 | 4678 | wouldFlushRetainedLayers = true; |
michael@0 | 4679 | flags |= nsLayoutUtils::PAINT_DOCUMENT_RELATIVE; |
michael@0 | 4680 | } |
michael@0 | 4681 | |
michael@0 | 4682 | // Don't let drawWindow blow away our retained layer tree |
michael@0 | 4683 | if ((flags & nsLayoutUtils::PAINT_WIDGET_LAYERS) && wouldFlushRetainedLayers) { |
michael@0 | 4684 | flags &= ~nsLayoutUtils::PAINT_WIDGET_LAYERS; |
michael@0 | 4685 | } |
michael@0 | 4686 | |
michael@0 | 4687 | nsLayoutUtils::PaintFrame(rc, rootFrame, nsRegion(aRect), |
michael@0 | 4688 | aBackgroundColor, flags); |
michael@0 | 4689 | |
michael@0 | 4690 | // if we had to use a group, paint it to the destination now |
michael@0 | 4691 | if (needsGroup) { |
michael@0 | 4692 | aThebesContext->Restore(); |
michael@0 | 4693 | aThebesContext->PopGroupToSource(); |
michael@0 | 4694 | aThebesContext->Paint(); |
michael@0 | 4695 | } |
michael@0 | 4696 | |
michael@0 | 4697 | return NS_OK; |
michael@0 | 4698 | } |
michael@0 | 4699 | |
michael@0 | 4700 | /* |
michael@0 | 4701 | * Clip the display list aList to a range. Returns the clipped |
michael@0 | 4702 | * rectangle surrounding the range. |
michael@0 | 4703 | */ |
michael@0 | 4704 | nsRect |
michael@0 | 4705 | PresShell::ClipListToRange(nsDisplayListBuilder *aBuilder, |
michael@0 | 4706 | nsDisplayList* aList, |
michael@0 | 4707 | nsRange* aRange) |
michael@0 | 4708 | { |
michael@0 | 4709 | // iterate though the display items and add up the bounding boxes of each. |
michael@0 | 4710 | // This will allow the total area of the frames within the range to be |
michael@0 | 4711 | // determined. To do this, remove an item from the bottom of the list, check |
michael@0 | 4712 | // whether it should be part of the range, and if so, append it to the top |
michael@0 | 4713 | // of the temporary list tmpList. If the item is a text frame at the end of |
michael@0 | 4714 | // the selection range, clip it to the portion of the text frame that is |
michael@0 | 4715 | // part of the selection. Then, append the wrapper to the top of the list. |
michael@0 | 4716 | // Otherwise, just delete the item and don't append it. |
michael@0 | 4717 | nsRect surfaceRect; |
michael@0 | 4718 | nsDisplayList tmpList; |
michael@0 | 4719 | |
michael@0 | 4720 | nsDisplayItem* i; |
michael@0 | 4721 | while ((i = aList->RemoveBottom())) { |
michael@0 | 4722 | // itemToInsert indiciates the item that should be inserted into the |
michael@0 | 4723 | // temporary list. If null, no item should be inserted. |
michael@0 | 4724 | nsDisplayItem* itemToInsert = nullptr; |
michael@0 | 4725 | nsIFrame* frame = i->Frame(); |
michael@0 | 4726 | nsIContent* content = frame->GetContent(); |
michael@0 | 4727 | if (content) { |
michael@0 | 4728 | bool atStart = (content == aRange->GetStartParent()); |
michael@0 | 4729 | bool atEnd = (content == aRange->GetEndParent()); |
michael@0 | 4730 | if ((atStart || atEnd) && frame->GetType() == nsGkAtoms::textFrame) { |
michael@0 | 4731 | int32_t frameStartOffset, frameEndOffset; |
michael@0 | 4732 | frame->GetOffsets(frameStartOffset, frameEndOffset); |
michael@0 | 4733 | |
michael@0 | 4734 | int32_t hilightStart = |
michael@0 | 4735 | atStart ? std::max(aRange->StartOffset(), frameStartOffset) : frameStartOffset; |
michael@0 | 4736 | int32_t hilightEnd = |
michael@0 | 4737 | atEnd ? std::min(aRange->EndOffset(), frameEndOffset) : frameEndOffset; |
michael@0 | 4738 | if (hilightStart < hilightEnd) { |
michael@0 | 4739 | // determine the location of the start and end edges of the range. |
michael@0 | 4740 | nsPoint startPoint, endPoint; |
michael@0 | 4741 | frame->GetPointFromOffset(hilightStart, &startPoint); |
michael@0 | 4742 | frame->GetPointFromOffset(hilightEnd, &endPoint); |
michael@0 | 4743 | |
michael@0 | 4744 | // the clip rectangle is determined by taking the the start and |
michael@0 | 4745 | // end points of the range, offset from the reference frame. |
michael@0 | 4746 | // Because of rtl, the end point may be to the left of the |
michael@0 | 4747 | // start point, so x is set to the lowest value |
michael@0 | 4748 | nsRect textRect(aBuilder->ToReferenceFrame(frame), frame->GetSize()); |
michael@0 | 4749 | nscoord x = std::min(startPoint.x, endPoint.x); |
michael@0 | 4750 | textRect.x += x; |
michael@0 | 4751 | textRect.width = std::max(startPoint.x, endPoint.x) - x; |
michael@0 | 4752 | surfaceRect.UnionRect(surfaceRect, textRect); |
michael@0 | 4753 | |
michael@0 | 4754 | DisplayItemClip newClip; |
michael@0 | 4755 | newClip.SetTo(textRect); |
michael@0 | 4756 | newClip.IntersectWith(i->GetClip()); |
michael@0 | 4757 | i->SetClip(aBuilder, newClip); |
michael@0 | 4758 | itemToInsert = i; |
michael@0 | 4759 | } |
michael@0 | 4760 | } |
michael@0 | 4761 | // Don't try to descend into subdocuments. |
michael@0 | 4762 | // If this ever changes we'd need to add handling for subdocuments with |
michael@0 | 4763 | // different zoom levels. |
michael@0 | 4764 | else if (content->GetCurrentDoc() == |
michael@0 | 4765 | aRange->GetStartParent()->GetCurrentDoc()) { |
michael@0 | 4766 | // if the node is within the range, append it to the temporary list |
michael@0 | 4767 | bool before, after; |
michael@0 | 4768 | nsresult rv = |
michael@0 | 4769 | nsRange::CompareNodeToRange(content, aRange, &before, &after); |
michael@0 | 4770 | if (NS_SUCCEEDED(rv) && !before && !after) { |
michael@0 | 4771 | itemToInsert = i; |
michael@0 | 4772 | bool snap; |
michael@0 | 4773 | surfaceRect.UnionRect(surfaceRect, i->GetBounds(aBuilder, &snap)); |
michael@0 | 4774 | } |
michael@0 | 4775 | } |
michael@0 | 4776 | } |
michael@0 | 4777 | |
michael@0 | 4778 | // insert the item into the list if necessary. If the item has a child |
michael@0 | 4779 | // list, insert that as well |
michael@0 | 4780 | nsDisplayList* sublist = i->GetSameCoordinateSystemChildren(); |
michael@0 | 4781 | if (itemToInsert || sublist) { |
michael@0 | 4782 | tmpList.AppendToTop(itemToInsert ? itemToInsert : i); |
michael@0 | 4783 | // if the item is a list, iterate over it as well |
michael@0 | 4784 | if (sublist) |
michael@0 | 4785 | surfaceRect.UnionRect(surfaceRect, |
michael@0 | 4786 | ClipListToRange(aBuilder, sublist, aRange)); |
michael@0 | 4787 | } |
michael@0 | 4788 | else { |
michael@0 | 4789 | // otherwise, just delete the item and don't readd it to the list |
michael@0 | 4790 | i->~nsDisplayItem(); |
michael@0 | 4791 | } |
michael@0 | 4792 | } |
michael@0 | 4793 | |
michael@0 | 4794 | // now add all the items back onto the original list again |
michael@0 | 4795 | aList->AppendToTop(&tmpList); |
michael@0 | 4796 | |
michael@0 | 4797 | return surfaceRect; |
michael@0 | 4798 | } |
michael@0 | 4799 | |
michael@0 | 4800 | #ifdef DEBUG |
michael@0 | 4801 | #include <stdio.h> |
michael@0 | 4802 | |
michael@0 | 4803 | static bool gDumpRangePaintList = false; |
michael@0 | 4804 | #endif |
michael@0 | 4805 | |
michael@0 | 4806 | RangePaintInfo* |
michael@0 | 4807 | PresShell::CreateRangePaintInfo(nsIDOMRange* aRange, |
michael@0 | 4808 | nsRect& aSurfaceRect, |
michael@0 | 4809 | bool aForPrimarySelection) |
michael@0 | 4810 | { |
michael@0 | 4811 | RangePaintInfo* info = nullptr; |
michael@0 | 4812 | |
michael@0 | 4813 | nsRange* range = static_cast<nsRange*>(aRange); |
michael@0 | 4814 | |
michael@0 | 4815 | nsIFrame* ancestorFrame; |
michael@0 | 4816 | nsIFrame* rootFrame = GetRootFrame(); |
michael@0 | 4817 | |
michael@0 | 4818 | // If the start or end of the range is the document, just use the root |
michael@0 | 4819 | // frame, otherwise get the common ancestor of the two endpoints of the |
michael@0 | 4820 | // range. |
michael@0 | 4821 | nsINode* startParent = range->GetStartParent(); |
michael@0 | 4822 | nsINode* endParent = range->GetEndParent(); |
michael@0 | 4823 | nsIDocument* doc = startParent->GetCurrentDoc(); |
michael@0 | 4824 | if (startParent == doc || endParent == doc) { |
michael@0 | 4825 | ancestorFrame = rootFrame; |
michael@0 | 4826 | } |
michael@0 | 4827 | else { |
michael@0 | 4828 | nsINode* ancestor = nsContentUtils::GetCommonAncestor(startParent, endParent); |
michael@0 | 4829 | NS_ASSERTION(!ancestor || ancestor->IsNodeOfType(nsINode::eCONTENT), |
michael@0 | 4830 | "common ancestor is not content"); |
michael@0 | 4831 | if (!ancestor || !ancestor->IsNodeOfType(nsINode::eCONTENT)) |
michael@0 | 4832 | return nullptr; |
michael@0 | 4833 | |
michael@0 | 4834 | nsIContent* ancestorContent = static_cast<nsIContent*>(ancestor); |
michael@0 | 4835 | ancestorFrame = ancestorContent->GetPrimaryFrame(); |
michael@0 | 4836 | |
michael@0 | 4837 | // use the nearest ancestor frame that includes all continuations as the |
michael@0 | 4838 | // root for building the display list |
michael@0 | 4839 | while (ancestorFrame && |
michael@0 | 4840 | nsLayoutUtils::GetNextContinuationOrIBSplitSibling(ancestorFrame)) |
michael@0 | 4841 | ancestorFrame = ancestorFrame->GetParent(); |
michael@0 | 4842 | } |
michael@0 | 4843 | |
michael@0 | 4844 | if (!ancestorFrame) |
michael@0 | 4845 | return nullptr; |
michael@0 | 4846 | |
michael@0 | 4847 | info = new RangePaintInfo(range, ancestorFrame); |
michael@0 | 4848 | |
michael@0 | 4849 | nsRect ancestorRect = ancestorFrame->GetVisualOverflowRect(); |
michael@0 | 4850 | |
michael@0 | 4851 | // get a display list containing the range |
michael@0 | 4852 | info->mBuilder.SetIncludeAllOutOfFlows(); |
michael@0 | 4853 | if (aForPrimarySelection) { |
michael@0 | 4854 | info->mBuilder.SetSelectedFramesOnly(); |
michael@0 | 4855 | } |
michael@0 | 4856 | info->mBuilder.EnterPresShell(ancestorFrame, ancestorRect); |
michael@0 | 4857 | ancestorFrame->BuildDisplayListForStackingContext(&info->mBuilder, |
michael@0 | 4858 | ancestorRect, &info->mList); |
michael@0 | 4859 | |
michael@0 | 4860 | #ifdef DEBUG |
michael@0 | 4861 | if (gDumpRangePaintList) { |
michael@0 | 4862 | fprintf(stderr, "CreateRangePaintInfo --- before ClipListToRange:\n"); |
michael@0 | 4863 | nsFrame::PrintDisplayList(&(info->mBuilder), info->mList); |
michael@0 | 4864 | } |
michael@0 | 4865 | #endif |
michael@0 | 4866 | |
michael@0 | 4867 | nsRect rangeRect = ClipListToRange(&info->mBuilder, &info->mList, range); |
michael@0 | 4868 | |
michael@0 | 4869 | info->mBuilder.LeavePresShell(ancestorFrame, ancestorRect); |
michael@0 | 4870 | |
michael@0 | 4871 | #ifdef DEBUG |
michael@0 | 4872 | if (gDumpRangePaintList) { |
michael@0 | 4873 | fprintf(stderr, "CreateRangePaintInfo --- after ClipListToRange:\n"); |
michael@0 | 4874 | nsFrame::PrintDisplayList(&(info->mBuilder), info->mList); |
michael@0 | 4875 | } |
michael@0 | 4876 | #endif |
michael@0 | 4877 | |
michael@0 | 4878 | // determine the offset of the reference frame for the display list |
michael@0 | 4879 | // to the root frame. This will allow the coordinates used when painting |
michael@0 | 4880 | // to all be offset from the same point |
michael@0 | 4881 | info->mRootOffset = ancestorFrame->GetOffsetTo(rootFrame); |
michael@0 | 4882 | rangeRect.MoveBy(info->mRootOffset); |
michael@0 | 4883 | aSurfaceRect.UnionRect(aSurfaceRect, rangeRect); |
michael@0 | 4884 | |
michael@0 | 4885 | return info; |
michael@0 | 4886 | } |
michael@0 | 4887 | |
michael@0 | 4888 | TemporaryRef<SourceSurface> |
michael@0 | 4889 | PresShell::PaintRangePaintInfo(nsTArray<nsAutoPtr<RangePaintInfo> >* aItems, |
michael@0 | 4890 | nsISelection* aSelection, |
michael@0 | 4891 | nsIntRegion* aRegion, |
michael@0 | 4892 | nsRect aArea, |
michael@0 | 4893 | nsIntPoint& aPoint, |
michael@0 | 4894 | nsIntRect* aScreenRect) |
michael@0 | 4895 | { |
michael@0 | 4896 | nsPresContext* pc = GetPresContext(); |
michael@0 | 4897 | if (!pc || aArea.width == 0 || aArea.height == 0) |
michael@0 | 4898 | return nullptr; |
michael@0 | 4899 | |
michael@0 | 4900 | nsDeviceContext* deviceContext = pc->DeviceContext(); |
michael@0 | 4901 | |
michael@0 | 4902 | // use the rectangle to create the surface |
michael@0 | 4903 | nsIntRect pixelArea = aArea.ToOutsidePixels(pc->AppUnitsPerDevPixel()); |
michael@0 | 4904 | |
michael@0 | 4905 | // if the area of the image is larger than the maximum area, scale it down |
michael@0 | 4906 | float scale = 0.0; |
michael@0 | 4907 | nsIntRect rootScreenRect = |
michael@0 | 4908 | GetRootFrame()->GetScreenRectInAppUnits().ToNearestPixels( |
michael@0 | 4909 | pc->AppUnitsPerDevPixel()); |
michael@0 | 4910 | |
michael@0 | 4911 | // if the image is larger in one or both directions than half the size of |
michael@0 | 4912 | // the available screen area, scale the image down to that size. |
michael@0 | 4913 | nsRect maxSize; |
michael@0 | 4914 | deviceContext->GetClientRect(maxSize); |
michael@0 | 4915 | nscoord maxWidth = pc->AppUnitsToDevPixels(maxSize.width >> 1); |
michael@0 | 4916 | nscoord maxHeight = pc->AppUnitsToDevPixels(maxSize.height >> 1); |
michael@0 | 4917 | bool resize = (pixelArea.width > maxWidth || pixelArea.height > maxHeight); |
michael@0 | 4918 | if (resize) { |
michael@0 | 4919 | scale = 1.0; |
michael@0 | 4920 | // divide the maximum size by the image size in both directions. Whichever |
michael@0 | 4921 | // direction produces the smallest result determines how much should be |
michael@0 | 4922 | // scaled. |
michael@0 | 4923 | if (pixelArea.width > maxWidth) |
michael@0 | 4924 | scale = std::min(scale, float(maxWidth) / pixelArea.width); |
michael@0 | 4925 | if (pixelArea.height > maxHeight) |
michael@0 | 4926 | scale = std::min(scale, float(maxHeight) / pixelArea.height); |
michael@0 | 4927 | |
michael@0 | 4928 | pixelArea.width = NSToIntFloor(float(pixelArea.width) * scale); |
michael@0 | 4929 | pixelArea.height = NSToIntFloor(float(pixelArea.height) * scale); |
michael@0 | 4930 | |
michael@0 | 4931 | // adjust the screen position based on the rescaled size |
michael@0 | 4932 | nscoord left = rootScreenRect.x + pixelArea.x; |
michael@0 | 4933 | nscoord top = rootScreenRect.y + pixelArea.y; |
michael@0 | 4934 | aScreenRect->x = NSToIntFloor(aPoint.x - float(aPoint.x - left) * scale); |
michael@0 | 4935 | aScreenRect->y = NSToIntFloor(aPoint.y - float(aPoint.y - top) * scale); |
michael@0 | 4936 | } |
michael@0 | 4937 | else { |
michael@0 | 4938 | // move aScreenRect to the position of the surface in screen coordinates |
michael@0 | 4939 | aScreenRect->MoveTo(rootScreenRect.x + pixelArea.x, rootScreenRect.y + pixelArea.y); |
michael@0 | 4940 | } |
michael@0 | 4941 | aScreenRect->width = pixelArea.width; |
michael@0 | 4942 | aScreenRect->height = pixelArea.height; |
michael@0 | 4943 | |
michael@0 | 4944 | RefPtr<DrawTarget> dt = |
michael@0 | 4945 | gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( |
michael@0 | 4946 | IntSize(pixelArea.width, pixelArea.height), |
michael@0 | 4947 | SurfaceFormat::B8G8R8A8); |
michael@0 | 4948 | if (!dt) { |
michael@0 | 4949 | return nullptr; |
michael@0 | 4950 | } |
michael@0 | 4951 | |
michael@0 | 4952 | nsRefPtr<gfxContext> ctx = new gfxContext(dt); |
michael@0 | 4953 | nsRefPtr<nsRenderingContext> rc = new nsRenderingContext(); |
michael@0 | 4954 | rc->Init(deviceContext, ctx); |
michael@0 | 4955 | |
michael@0 | 4956 | if (aRegion) { |
michael@0 | 4957 | // Convert aRegion from CSS pixels to dev pixels |
michael@0 | 4958 | nsIntRegion region = |
michael@0 | 4959 | aRegion->ToAppUnits(nsPresContext::AppUnitsPerCSSPixel()) |
michael@0 | 4960 | .ToOutsidePixels(pc->AppUnitsPerDevPixel()); |
michael@0 | 4961 | rc->SetClip(region); |
michael@0 | 4962 | } |
michael@0 | 4963 | |
michael@0 | 4964 | if (resize) |
michael@0 | 4965 | rc->Scale(scale, scale); |
michael@0 | 4966 | |
michael@0 | 4967 | // translate so that points are relative to the surface area |
michael@0 | 4968 | rc->Translate(-aArea.TopLeft()); |
michael@0 | 4969 | |
michael@0 | 4970 | // temporarily hide the selection so that text is drawn normally. If a |
michael@0 | 4971 | // selection is being rendered, use that, otherwise use the presshell's |
michael@0 | 4972 | // selection. |
michael@0 | 4973 | nsRefPtr<nsFrameSelection> frameSelection; |
michael@0 | 4974 | if (aSelection) { |
michael@0 | 4975 | frameSelection = static_cast<Selection*>(aSelection)->GetFrameSelection(); |
michael@0 | 4976 | } |
michael@0 | 4977 | else { |
michael@0 | 4978 | frameSelection = FrameSelection(); |
michael@0 | 4979 | } |
michael@0 | 4980 | int16_t oldDisplaySelection = frameSelection->GetDisplaySelection(); |
michael@0 | 4981 | frameSelection->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN); |
michael@0 | 4982 | |
michael@0 | 4983 | // next, paint each range in the selection |
michael@0 | 4984 | int32_t count = aItems->Length(); |
michael@0 | 4985 | for (int32_t i = 0; i < count; i++) { |
michael@0 | 4986 | RangePaintInfo* rangeInfo = (*aItems)[i]; |
michael@0 | 4987 | // the display lists paint relative to the offset from the reference |
michael@0 | 4988 | // frame, so translate the rendering context |
michael@0 | 4989 | nsRenderingContext::AutoPushTranslation |
michael@0 | 4990 | translate(rc, rangeInfo->mRootOffset); |
michael@0 | 4991 | |
michael@0 | 4992 | aArea.MoveBy(-rangeInfo->mRootOffset.x, -rangeInfo->mRootOffset.y); |
michael@0 | 4993 | nsRegion visible(aArea); |
michael@0 | 4994 | rangeInfo->mList.ComputeVisibilityForRoot(&rangeInfo->mBuilder, &visible); |
michael@0 | 4995 | rangeInfo->mList.PaintRoot(&rangeInfo->mBuilder, rc, nsDisplayList::PAINT_DEFAULT); |
michael@0 | 4996 | aArea.MoveBy(rangeInfo->mRootOffset.x, rangeInfo->mRootOffset.y); |
michael@0 | 4997 | } |
michael@0 | 4998 | |
michael@0 | 4999 | // restore the old selection display state |
michael@0 | 5000 | frameSelection->SetDisplaySelection(oldDisplaySelection); |
michael@0 | 5001 | |
michael@0 | 5002 | return dt->Snapshot(); |
michael@0 | 5003 | } |
michael@0 | 5004 | |
michael@0 | 5005 | TemporaryRef<SourceSurface> |
michael@0 | 5006 | PresShell::RenderNode(nsIDOMNode* aNode, |
michael@0 | 5007 | nsIntRegion* aRegion, |
michael@0 | 5008 | nsIntPoint& aPoint, |
michael@0 | 5009 | nsIntRect* aScreenRect) |
michael@0 | 5010 | { |
michael@0 | 5011 | // area will hold the size of the surface needed to draw the node, measured |
michael@0 | 5012 | // from the root frame. |
michael@0 | 5013 | nsRect area; |
michael@0 | 5014 | nsTArray<nsAutoPtr<RangePaintInfo> > rangeItems; |
michael@0 | 5015 | |
michael@0 | 5016 | // nothing to draw if the node isn't in a document |
michael@0 | 5017 | nsCOMPtr<nsINode> node = do_QueryInterface(aNode); |
michael@0 | 5018 | if (!node->IsInDoc()) |
michael@0 | 5019 | return nullptr; |
michael@0 | 5020 | |
michael@0 | 5021 | nsRefPtr<nsRange> range = new nsRange(node); |
michael@0 | 5022 | if (NS_FAILED(range->SelectNode(aNode))) |
michael@0 | 5023 | return nullptr; |
michael@0 | 5024 | |
michael@0 | 5025 | RangePaintInfo* info = CreateRangePaintInfo(range, area, false); |
michael@0 | 5026 | if (info && !rangeItems.AppendElement(info)) { |
michael@0 | 5027 | delete info; |
michael@0 | 5028 | return nullptr; |
michael@0 | 5029 | } |
michael@0 | 5030 | |
michael@0 | 5031 | if (aRegion) { |
michael@0 | 5032 | // combine the area with the supplied region |
michael@0 | 5033 | nsIntRect rrectPixels = aRegion->GetBounds(); |
michael@0 | 5034 | |
michael@0 | 5035 | nsRect rrect = rrectPixels.ToAppUnits(nsPresContext::AppUnitsPerCSSPixel()); |
michael@0 | 5036 | area.IntersectRect(area, rrect); |
michael@0 | 5037 | |
michael@0 | 5038 | nsPresContext* pc = GetPresContext(); |
michael@0 | 5039 | if (!pc) |
michael@0 | 5040 | return nullptr; |
michael@0 | 5041 | |
michael@0 | 5042 | // move the region so that it is offset from the topleft corner of the surface |
michael@0 | 5043 | aRegion->MoveBy(-pc->AppUnitsToDevPixels(area.x), |
michael@0 | 5044 | -pc->AppUnitsToDevPixels(area.y)); |
michael@0 | 5045 | } |
michael@0 | 5046 | |
michael@0 | 5047 | return PaintRangePaintInfo(&rangeItems, nullptr, aRegion, area, aPoint, |
michael@0 | 5048 | aScreenRect); |
michael@0 | 5049 | } |
michael@0 | 5050 | |
michael@0 | 5051 | TemporaryRef<SourceSurface> |
michael@0 | 5052 | PresShell::RenderSelection(nsISelection* aSelection, |
michael@0 | 5053 | nsIntPoint& aPoint, |
michael@0 | 5054 | nsIntRect* aScreenRect) |
michael@0 | 5055 | { |
michael@0 | 5056 | // area will hold the size of the surface needed to draw the selection, |
michael@0 | 5057 | // measured from the root frame. |
michael@0 | 5058 | nsRect area; |
michael@0 | 5059 | nsTArray<nsAutoPtr<RangePaintInfo> > rangeItems; |
michael@0 | 5060 | |
michael@0 | 5061 | // iterate over each range and collect them into the rangeItems array. |
michael@0 | 5062 | // This is done so that the size of selection can be determined so as |
michael@0 | 5063 | // to allocate a surface area |
michael@0 | 5064 | int32_t numRanges; |
michael@0 | 5065 | aSelection->GetRangeCount(&numRanges); |
michael@0 | 5066 | NS_ASSERTION(numRanges > 0, "RenderSelection called with no selection"); |
michael@0 | 5067 | |
michael@0 | 5068 | for (int32_t r = 0; r < numRanges; r++) |
michael@0 | 5069 | { |
michael@0 | 5070 | nsCOMPtr<nsIDOMRange> range; |
michael@0 | 5071 | aSelection->GetRangeAt(r, getter_AddRefs(range)); |
michael@0 | 5072 | |
michael@0 | 5073 | RangePaintInfo* info = CreateRangePaintInfo(range, area, true); |
michael@0 | 5074 | if (info && !rangeItems.AppendElement(info)) { |
michael@0 | 5075 | delete info; |
michael@0 | 5076 | return nullptr; |
michael@0 | 5077 | } |
michael@0 | 5078 | } |
michael@0 | 5079 | |
michael@0 | 5080 | return PaintRangePaintInfo(&rangeItems, aSelection, nullptr, area, aPoint, |
michael@0 | 5081 | aScreenRect); |
michael@0 | 5082 | } |
michael@0 | 5083 | |
michael@0 | 5084 | void |
michael@0 | 5085 | PresShell::AddPrintPreviewBackgroundItem(nsDisplayListBuilder& aBuilder, |
michael@0 | 5086 | nsDisplayList& aList, |
michael@0 | 5087 | nsIFrame* aFrame, |
michael@0 | 5088 | const nsRect& aBounds) |
michael@0 | 5089 | { |
michael@0 | 5090 | aList.AppendNewToBottom(new (&aBuilder) |
michael@0 | 5091 | nsDisplaySolidColor(&aBuilder, aFrame, aBounds, NS_RGB(115, 115, 115))); |
michael@0 | 5092 | } |
michael@0 | 5093 | |
michael@0 | 5094 | static bool |
michael@0 | 5095 | AddCanvasBackgroundColor(const nsDisplayList& aList, nsIFrame* aCanvasFrame, |
michael@0 | 5096 | nscolor aColor) |
michael@0 | 5097 | { |
michael@0 | 5098 | for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) { |
michael@0 | 5099 | if (i->Frame() == aCanvasFrame && |
michael@0 | 5100 | i->GetType() == nsDisplayItem::TYPE_CANVAS_BACKGROUND_COLOR) { |
michael@0 | 5101 | nsDisplayCanvasBackgroundColor* bg = static_cast<nsDisplayCanvasBackgroundColor*>(i); |
michael@0 | 5102 | bg->SetExtraBackgroundColor(aColor); |
michael@0 | 5103 | return true; |
michael@0 | 5104 | } |
michael@0 | 5105 | nsDisplayList* sublist = i->GetSameCoordinateSystemChildren(); |
michael@0 | 5106 | if (sublist && AddCanvasBackgroundColor(*sublist, aCanvasFrame, aColor)) |
michael@0 | 5107 | return true; |
michael@0 | 5108 | } |
michael@0 | 5109 | return false; |
michael@0 | 5110 | } |
michael@0 | 5111 | |
michael@0 | 5112 | void |
michael@0 | 5113 | PresShell::AddCanvasBackgroundColorItem(nsDisplayListBuilder& aBuilder, |
michael@0 | 5114 | nsDisplayList& aList, |
michael@0 | 5115 | nsIFrame* aFrame, |
michael@0 | 5116 | const nsRect& aBounds, |
michael@0 | 5117 | nscolor aBackstopColor, |
michael@0 | 5118 | uint32_t aFlags) |
michael@0 | 5119 | { |
michael@0 | 5120 | if (aBounds.IsEmpty()) { |
michael@0 | 5121 | return; |
michael@0 | 5122 | } |
michael@0 | 5123 | // We don't want to add an item for the canvas background color if the frame |
michael@0 | 5124 | // (sub)tree we are painting doesn't include any canvas frames. There isn't |
michael@0 | 5125 | // an easy way to check this directly, but if we check if the root of the |
michael@0 | 5126 | // (sub)tree we are painting is a canvas frame that should cover us in all |
michael@0 | 5127 | // cases (it will usually be a viewport frame when we have a canvas frame in |
michael@0 | 5128 | // the (sub)tree). |
michael@0 | 5129 | if (!(aFlags & nsIPresShell::FORCE_DRAW) && |
michael@0 | 5130 | !nsCSSRendering::IsCanvasFrame(aFrame)) { |
michael@0 | 5131 | return; |
michael@0 | 5132 | } |
michael@0 | 5133 | |
michael@0 | 5134 | nscolor bgcolor = NS_ComposeColors(aBackstopColor, mCanvasBackgroundColor); |
michael@0 | 5135 | if (NS_GET_A(bgcolor) == 0) |
michael@0 | 5136 | return; |
michael@0 | 5137 | |
michael@0 | 5138 | // To make layers work better, we want to avoid having a big non-scrolled |
michael@0 | 5139 | // color background behind a scrolled transparent background. Instead, |
michael@0 | 5140 | // we'll try to move the color background into the scrolled content |
michael@0 | 5141 | // by making nsDisplayCanvasBackground paint it. |
michael@0 | 5142 | if (!aFrame->GetParent()) { |
michael@0 | 5143 | nsIScrollableFrame* sf = |
michael@0 | 5144 | aFrame->PresContext()->PresShell()->GetRootScrollFrameAsScrollable(); |
michael@0 | 5145 | if (sf) { |
michael@0 | 5146 | nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame()); |
michael@0 | 5147 | if (canvasFrame && canvasFrame->IsVisibleForPainting(&aBuilder)) { |
michael@0 | 5148 | if (AddCanvasBackgroundColor(aList, canvasFrame, bgcolor)) |
michael@0 | 5149 | return; |
michael@0 | 5150 | } |
michael@0 | 5151 | } |
michael@0 | 5152 | } |
michael@0 | 5153 | |
michael@0 | 5154 | aList.AppendNewToBottom( |
michael@0 | 5155 | new (&aBuilder) nsDisplaySolidColor(&aBuilder, aFrame, aBounds, bgcolor)); |
michael@0 | 5156 | } |
michael@0 | 5157 | |
michael@0 | 5158 | static bool IsTransparentContainerElement(nsPresContext* aPresContext) |
michael@0 | 5159 | { |
michael@0 | 5160 | nsCOMPtr<nsIDocShellTreeItem> docShellItem = aPresContext->GetDocShell(); |
michael@0 | 5161 | nsCOMPtr<nsPIDOMWindow> pwin(do_GetInterface(docShellItem)); |
michael@0 | 5162 | if (!pwin) |
michael@0 | 5163 | return false; |
michael@0 | 5164 | nsCOMPtr<nsIContent> containerElement = |
michael@0 | 5165 | do_QueryInterface(pwin->GetFrameElementInternal()); |
michael@0 | 5166 | return containerElement && |
michael@0 | 5167 | containerElement->HasAttr(kNameSpaceID_None, nsGkAtoms::transparent); |
michael@0 | 5168 | } |
michael@0 | 5169 | |
michael@0 | 5170 | nscolor PresShell::GetDefaultBackgroundColorToDraw() |
michael@0 | 5171 | { |
michael@0 | 5172 | if (!mPresContext || !mPresContext->GetBackgroundColorDraw()) { |
michael@0 | 5173 | return NS_RGB(255,255,255); |
michael@0 | 5174 | } |
michael@0 | 5175 | return mPresContext->DefaultBackgroundColor(); |
michael@0 | 5176 | } |
michael@0 | 5177 | |
michael@0 | 5178 | void PresShell::UpdateCanvasBackground() |
michael@0 | 5179 | { |
michael@0 | 5180 | // If we have a frame tree and it has style information that |
michael@0 | 5181 | // specifies the background color of the canvas, update our local |
michael@0 | 5182 | // cache of that color. |
michael@0 | 5183 | nsIFrame* rootStyleFrame = FrameConstructor()->GetRootElementStyleFrame(); |
michael@0 | 5184 | if (rootStyleFrame) { |
michael@0 | 5185 | nsStyleContext* bgStyle = |
michael@0 | 5186 | nsCSSRendering::FindRootFrameBackground(rootStyleFrame); |
michael@0 | 5187 | // XXX We should really be passing the canvasframe, not the root element |
michael@0 | 5188 | // style frame but we don't have access to the canvasframe here. It isn't |
michael@0 | 5189 | // a problem because only a few frames can return something other than true |
michael@0 | 5190 | // and none of them would be a canvas frame or root element style frame. |
michael@0 | 5191 | bool drawBackgroundImage; |
michael@0 | 5192 | bool drawBackgroundColor; |
michael@0 | 5193 | |
michael@0 | 5194 | mCanvasBackgroundColor = |
michael@0 | 5195 | nsCSSRendering::DetermineBackgroundColor(mPresContext, bgStyle, |
michael@0 | 5196 | rootStyleFrame, |
michael@0 | 5197 | drawBackgroundImage, |
michael@0 | 5198 | drawBackgroundColor); |
michael@0 | 5199 | if (GetPresContext()->IsCrossProcessRootContentDocument() && |
michael@0 | 5200 | !IsTransparentContainerElement(mPresContext)) { |
michael@0 | 5201 | mCanvasBackgroundColor = |
michael@0 | 5202 | NS_ComposeColors(GetDefaultBackgroundColorToDraw(), mCanvasBackgroundColor); |
michael@0 | 5203 | } |
michael@0 | 5204 | } |
michael@0 | 5205 | |
michael@0 | 5206 | // If the root element of the document (ie html) has style 'display: none' |
michael@0 | 5207 | // then the document's background color does not get drawn; cache the |
michael@0 | 5208 | // color we actually draw. |
michael@0 | 5209 | if (!FrameConstructor()->GetRootElementFrame()) { |
michael@0 | 5210 | mCanvasBackgroundColor = GetDefaultBackgroundColorToDraw(); |
michael@0 | 5211 | } |
michael@0 | 5212 | if (XRE_GetProcessType() == GeckoProcessType_Content) { |
michael@0 | 5213 | if (TabChild* tabChild = TabChild::GetFrom(this)) { |
michael@0 | 5214 | tabChild->SetBackgroundColor(mCanvasBackgroundColor); |
michael@0 | 5215 | } |
michael@0 | 5216 | } |
michael@0 | 5217 | } |
michael@0 | 5218 | |
michael@0 | 5219 | nscolor PresShell::ComputeBackstopColor(nsView* aDisplayRoot) |
michael@0 | 5220 | { |
michael@0 | 5221 | nsIWidget* widget = aDisplayRoot->GetWidget(); |
michael@0 | 5222 | if (widget && (widget->GetTransparencyMode() != eTransparencyOpaque || |
michael@0 | 5223 | widget->WidgetPaintsBackground())) { |
michael@0 | 5224 | // Within a transparent widget, so the backstop color must be |
michael@0 | 5225 | // totally transparent. |
michael@0 | 5226 | return NS_RGBA(0,0,0,0); |
michael@0 | 5227 | } |
michael@0 | 5228 | // Within an opaque widget (or no widget at all), so the backstop |
michael@0 | 5229 | // color must be totally opaque. The user's default background |
michael@0 | 5230 | // as reported by the prescontext is guaranteed to be opaque. |
michael@0 | 5231 | return GetDefaultBackgroundColorToDraw(); |
michael@0 | 5232 | } |
michael@0 | 5233 | |
michael@0 | 5234 | struct PaintParams { |
michael@0 | 5235 | nscolor mBackgroundColor; |
michael@0 | 5236 | }; |
michael@0 | 5237 | |
michael@0 | 5238 | LayerManager* PresShell::GetLayerManager() |
michael@0 | 5239 | { |
michael@0 | 5240 | NS_ASSERTION(mViewManager, "Should have view manager"); |
michael@0 | 5241 | |
michael@0 | 5242 | nsView* rootView = mViewManager->GetRootView(); |
michael@0 | 5243 | if (rootView) { |
michael@0 | 5244 | if (nsIWidget* widget = rootView->GetWidget()) { |
michael@0 | 5245 | return widget->GetLayerManager(); |
michael@0 | 5246 | } |
michael@0 | 5247 | } |
michael@0 | 5248 | return nullptr; |
michael@0 | 5249 | } |
michael@0 | 5250 | |
michael@0 | 5251 | void PresShell::SetIgnoreViewportScrolling(bool aIgnore) |
michael@0 | 5252 | { |
michael@0 | 5253 | if (IgnoringViewportScrolling() == aIgnore) { |
michael@0 | 5254 | return; |
michael@0 | 5255 | } |
michael@0 | 5256 | RenderingState state(this); |
michael@0 | 5257 | state.mRenderFlags = ChangeFlag(state.mRenderFlags, aIgnore, |
michael@0 | 5258 | STATE_IGNORING_VIEWPORT_SCROLLING); |
michael@0 | 5259 | SetRenderingState(state); |
michael@0 | 5260 | } |
michael@0 | 5261 | |
michael@0 | 5262 | nsresult PresShell::SetResolution(float aXResolution, float aYResolution) |
michael@0 | 5263 | { |
michael@0 | 5264 | if (!(aXResolution > 0.0 && aYResolution > 0.0)) { |
michael@0 | 5265 | return NS_ERROR_ILLEGAL_VALUE; |
michael@0 | 5266 | } |
michael@0 | 5267 | if (aXResolution == mXResolution && aYResolution == mYResolution) { |
michael@0 | 5268 | return NS_OK; |
michael@0 | 5269 | } |
michael@0 | 5270 | RenderingState state(this); |
michael@0 | 5271 | state.mXResolution = aXResolution; |
michael@0 | 5272 | state.mYResolution = aYResolution; |
michael@0 | 5273 | SetRenderingState(state); |
michael@0 | 5274 | return NS_OK; |
michael@0 | 5275 | } |
michael@0 | 5276 | |
michael@0 | 5277 | gfxSize PresShell::GetCumulativeResolution() |
michael@0 | 5278 | { |
michael@0 | 5279 | gfxSize resolution = GetResolution(); |
michael@0 | 5280 | nsPresContext* parentCtx = GetPresContext()->GetParentPresContext(); |
michael@0 | 5281 | if (parentCtx) { |
michael@0 | 5282 | resolution = resolution * parentCtx->PresShell()->GetCumulativeResolution(); |
michael@0 | 5283 | } |
michael@0 | 5284 | return resolution; |
michael@0 | 5285 | } |
michael@0 | 5286 | |
michael@0 | 5287 | void PresShell::SetRenderingState(const RenderingState& aState) |
michael@0 | 5288 | { |
michael@0 | 5289 | if (mRenderFlags != aState.mRenderFlags) { |
michael@0 | 5290 | // Rendering state changed in a way that forces us to flush any |
michael@0 | 5291 | // retained layers we might already have. |
michael@0 | 5292 | LayerManager* manager = GetLayerManager(); |
michael@0 | 5293 | if (manager) { |
michael@0 | 5294 | FrameLayerBuilder::InvalidateAllLayers(manager); |
michael@0 | 5295 | } |
michael@0 | 5296 | } |
michael@0 | 5297 | |
michael@0 | 5298 | mRenderFlags = aState.mRenderFlags; |
michael@0 | 5299 | mXResolution = aState.mXResolution; |
michael@0 | 5300 | mYResolution = aState.mYResolution; |
michael@0 | 5301 | } |
michael@0 | 5302 | |
michael@0 | 5303 | void PresShell::SynthesizeMouseMove(bool aFromScroll) |
michael@0 | 5304 | { |
michael@0 | 5305 | if (!sSynthMouseMove) |
michael@0 | 5306 | return; |
michael@0 | 5307 | |
michael@0 | 5308 | if (mPaintingSuppressed || !mIsActive || !mPresContext) { |
michael@0 | 5309 | return; |
michael@0 | 5310 | } |
michael@0 | 5311 | |
michael@0 | 5312 | if (!mPresContext->IsRoot()) { |
michael@0 | 5313 | nsIPresShell* rootPresShell = GetRootPresShell(); |
michael@0 | 5314 | if (rootPresShell) { |
michael@0 | 5315 | rootPresShell->SynthesizeMouseMove(aFromScroll); |
michael@0 | 5316 | } |
michael@0 | 5317 | return; |
michael@0 | 5318 | } |
michael@0 | 5319 | |
michael@0 | 5320 | if (mMouseLocation == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) |
michael@0 | 5321 | return; |
michael@0 | 5322 | |
michael@0 | 5323 | if (!mSynthMouseMoveEvent.IsPending()) { |
michael@0 | 5324 | nsRefPtr<nsSynthMouseMoveEvent> ev = |
michael@0 | 5325 | new nsSynthMouseMoveEvent(this, aFromScroll); |
michael@0 | 5326 | |
michael@0 | 5327 | if (!GetPresContext()->RefreshDriver()->AddRefreshObserver(ev, |
michael@0 | 5328 | Flush_Display)) { |
michael@0 | 5329 | NS_WARNING("failed to dispatch nsSynthMouseMoveEvent"); |
michael@0 | 5330 | return; |
michael@0 | 5331 | } |
michael@0 | 5332 | |
michael@0 | 5333 | mSynthMouseMoveEvent = ev; |
michael@0 | 5334 | } |
michael@0 | 5335 | } |
michael@0 | 5336 | |
michael@0 | 5337 | /** |
michael@0 | 5338 | * Find the first floating view with a widget in a postorder traversal of the |
michael@0 | 5339 | * view tree that contains the point. Thus more deeply nested floating views |
michael@0 | 5340 | * are preferred over their ancestors, and floating views earlier in the |
michael@0 | 5341 | * view hierarchy (i.e., added later) are preferred over their siblings. |
michael@0 | 5342 | * This is adequate for finding the "topmost" floating view under a point, |
michael@0 | 5343 | * given that floating views don't supporting having a specific z-index. |
michael@0 | 5344 | * |
michael@0 | 5345 | * We cannot exit early when aPt is outside the view bounds, because floating |
michael@0 | 5346 | * views aren't necessarily included in their parent's bounds, so this could |
michael@0 | 5347 | * traverse the entire view hierarchy --- use carefully. |
michael@0 | 5348 | */ |
michael@0 | 5349 | static nsView* FindFloatingViewContaining(nsView* aView, nsPoint aPt) |
michael@0 | 5350 | { |
michael@0 | 5351 | if (aView->GetVisibility() == nsViewVisibility_kHide) |
michael@0 | 5352 | // No need to look into descendants. |
michael@0 | 5353 | return nullptr; |
michael@0 | 5354 | |
michael@0 | 5355 | nsIFrame* frame = aView->GetFrame(); |
michael@0 | 5356 | if (frame) { |
michael@0 | 5357 | if (!frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) || |
michael@0 | 5358 | !frame->PresContext()->PresShell()->IsActive()) { |
michael@0 | 5359 | return nullptr; |
michael@0 | 5360 | } |
michael@0 | 5361 | } |
michael@0 | 5362 | |
michael@0 | 5363 | for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) { |
michael@0 | 5364 | nsView* r = FindFloatingViewContaining(v, v->ConvertFromParentCoords(aPt)); |
michael@0 | 5365 | if (r) |
michael@0 | 5366 | return r; |
michael@0 | 5367 | } |
michael@0 | 5368 | |
michael@0 | 5369 | if (aView->GetFloating() && aView->HasWidget() && |
michael@0 | 5370 | aView->GetDimensions().Contains(aPt)) |
michael@0 | 5371 | return aView; |
michael@0 | 5372 | |
michael@0 | 5373 | return nullptr; |
michael@0 | 5374 | } |
michael@0 | 5375 | |
michael@0 | 5376 | /* |
michael@0 | 5377 | * This finds the first view containing the given point in a postorder |
michael@0 | 5378 | * traversal of the view tree that contains the point, assuming that the |
michael@0 | 5379 | * point is not in a floating view. It assumes that only floating views |
michael@0 | 5380 | * extend outside the bounds of their parents. |
michael@0 | 5381 | * |
michael@0 | 5382 | * This methods should only be called if FindFloatingViewContaining |
michael@0 | 5383 | * returns null. |
michael@0 | 5384 | */ |
michael@0 | 5385 | static nsView* FindViewContaining(nsView* aView, nsPoint aPt) |
michael@0 | 5386 | { |
michael@0 | 5387 | if (!aView->GetDimensions().Contains(aPt) || |
michael@0 | 5388 | aView->GetVisibility() == nsViewVisibility_kHide) { |
michael@0 | 5389 | return nullptr; |
michael@0 | 5390 | } |
michael@0 | 5391 | |
michael@0 | 5392 | nsIFrame* frame = aView->GetFrame(); |
michael@0 | 5393 | if (frame) { |
michael@0 | 5394 | if (!frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) || |
michael@0 | 5395 | !frame->PresContext()->PresShell()->IsActive()) { |
michael@0 | 5396 | return nullptr; |
michael@0 | 5397 | } |
michael@0 | 5398 | } |
michael@0 | 5399 | |
michael@0 | 5400 | for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) { |
michael@0 | 5401 | nsView* r = FindViewContaining(v, v->ConvertFromParentCoords(aPt)); |
michael@0 | 5402 | if (r) |
michael@0 | 5403 | return r; |
michael@0 | 5404 | } |
michael@0 | 5405 | |
michael@0 | 5406 | return aView; |
michael@0 | 5407 | } |
michael@0 | 5408 | |
michael@0 | 5409 | void |
michael@0 | 5410 | PresShell::ProcessSynthMouseMoveEvent(bool aFromScroll) |
michael@0 | 5411 | { |
michael@0 | 5412 | // If drag session has started, we shouldn't synthesize mousemove event. |
michael@0 | 5413 | nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession(); |
michael@0 | 5414 | if (dragSession) { |
michael@0 | 5415 | mSynthMouseMoveEvent.Forget(); |
michael@0 | 5416 | return; |
michael@0 | 5417 | } |
michael@0 | 5418 | |
michael@0 | 5419 | // allow new event to be posted while handling this one only if the |
michael@0 | 5420 | // source of the event is a scroll (to prevent infinite reflow loops) |
michael@0 | 5421 | if (aFromScroll) { |
michael@0 | 5422 | mSynthMouseMoveEvent.Forget(); |
michael@0 | 5423 | } |
michael@0 | 5424 | |
michael@0 | 5425 | nsView* rootView = mViewManager ? mViewManager->GetRootView() : nullptr; |
michael@0 | 5426 | if (mMouseLocation == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) || |
michael@0 | 5427 | !rootView || !rootView->HasWidget() || !mPresContext) { |
michael@0 | 5428 | mSynthMouseMoveEvent.Forget(); |
michael@0 | 5429 | return; |
michael@0 | 5430 | } |
michael@0 | 5431 | |
michael@0 | 5432 | NS_ASSERTION(mPresContext->IsRoot(), "Only a root pres shell should be here"); |
michael@0 | 5433 | |
michael@0 | 5434 | // Hold a ref to ourselves so DispatchEvent won't destroy us (since |
michael@0 | 5435 | // we need to access members after we call DispatchEvent). |
michael@0 | 5436 | nsCOMPtr<nsIPresShell> kungFuDeathGrip(this); |
michael@0 | 5437 | |
michael@0 | 5438 | #ifdef DEBUG_MOUSE_LOCATION |
michael@0 | 5439 | printf("[ps=%p]synthesizing mouse move to (%d,%d)\n", |
michael@0 | 5440 | this, mMouseLocation.x, mMouseLocation.y); |
michael@0 | 5441 | #endif |
michael@0 | 5442 | |
michael@0 | 5443 | int32_t APD = mPresContext->AppUnitsPerDevPixel(); |
michael@0 | 5444 | |
michael@0 | 5445 | // We need a widget to put in the event we are going to dispatch so we look |
michael@0 | 5446 | // for a view that has a widget and the mouse location is over. We first look |
michael@0 | 5447 | // for floating views, if there isn't one we use the root view. |view| holds |
michael@0 | 5448 | // that view. |
michael@0 | 5449 | nsView* view = nullptr; |
michael@0 | 5450 | |
michael@0 | 5451 | // The appunits per devpixel ratio of |view|. |
michael@0 | 5452 | int32_t viewAPD; |
michael@0 | 5453 | |
michael@0 | 5454 | // refPoint will be mMouseLocation relative to the widget of |view|, the |
michael@0 | 5455 | // widget we will put in the event we dispatch, in viewAPD appunits |
michael@0 | 5456 | nsPoint refpoint(0, 0); |
michael@0 | 5457 | |
michael@0 | 5458 | // We always dispatch the event to the pres shell that contains the view that |
michael@0 | 5459 | // the mouse is over. pointVM is the VM of that pres shell. |
michael@0 | 5460 | nsViewManager *pointVM = nullptr; |
michael@0 | 5461 | |
michael@0 | 5462 | // This could be a bit slow (traverses entire view hierarchy) |
michael@0 | 5463 | // but it's OK to do it once per synthetic mouse event |
michael@0 | 5464 | view = FindFloatingViewContaining(rootView, mMouseLocation); |
michael@0 | 5465 | if (!view) { |
michael@0 | 5466 | view = rootView; |
michael@0 | 5467 | nsView *pointView = FindViewContaining(rootView, mMouseLocation); |
michael@0 | 5468 | // pointView can be null in situations related to mouse capture |
michael@0 | 5469 | pointVM = (pointView ? pointView : view)->GetViewManager(); |
michael@0 | 5470 | refpoint = mMouseLocation + rootView->ViewToWidgetOffset(); |
michael@0 | 5471 | viewAPD = APD; |
michael@0 | 5472 | } else { |
michael@0 | 5473 | pointVM = view->GetViewManager(); |
michael@0 | 5474 | nsIFrame* frame = view->GetFrame(); |
michael@0 | 5475 | NS_ASSERTION(frame, "floating views can't be anonymous"); |
michael@0 | 5476 | viewAPD = frame->PresContext()->AppUnitsPerDevPixel(); |
michael@0 | 5477 | refpoint = mMouseLocation.ConvertAppUnits(APD, viewAPD); |
michael@0 | 5478 | refpoint -= view->GetOffsetTo(rootView); |
michael@0 | 5479 | refpoint += view->ViewToWidgetOffset(); |
michael@0 | 5480 | } |
michael@0 | 5481 | NS_ASSERTION(view->GetWidget(), "view should have a widget here"); |
michael@0 | 5482 | WidgetMouseEvent event(true, NS_MOUSE_MOVE, view->GetWidget(), |
michael@0 | 5483 | WidgetMouseEvent::eSynthesized); |
michael@0 | 5484 | event.refPoint = LayoutDeviceIntPoint::FromAppUnitsToNearest(refpoint, viewAPD); |
michael@0 | 5485 | event.time = PR_IntervalNow(); |
michael@0 | 5486 | // XXX set event.modifiers ? |
michael@0 | 5487 | // XXX mnakano I think that we should get the latest information from widget. |
michael@0 | 5488 | |
michael@0 | 5489 | nsCOMPtr<nsIPresShell> shell = pointVM->GetPresShell(); |
michael@0 | 5490 | if (shell) { |
michael@0 | 5491 | shell->DispatchSynthMouseMove(&event, !aFromScroll); |
michael@0 | 5492 | } |
michael@0 | 5493 | |
michael@0 | 5494 | if (!aFromScroll) { |
michael@0 | 5495 | mSynthMouseMoveEvent.Forget(); |
michael@0 | 5496 | } |
michael@0 | 5497 | } |
michael@0 | 5498 | |
michael@0 | 5499 | /* static */ void |
michael@0 | 5500 | PresShell::MarkImagesInListVisible(const nsDisplayList& aList) |
michael@0 | 5501 | { |
michael@0 | 5502 | for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) { |
michael@0 | 5503 | nsDisplayList* sublist = item->GetChildren(); |
michael@0 | 5504 | if (sublist) { |
michael@0 | 5505 | MarkImagesInListVisible(*sublist); |
michael@0 | 5506 | continue; |
michael@0 | 5507 | } |
michael@0 | 5508 | nsIFrame* f = item->Frame(); |
michael@0 | 5509 | // We could check the type of the display item, only a handful can hold an |
michael@0 | 5510 | // image loading content. |
michael@0 | 5511 | // dont bother nscomptr here, it is wasteful |
michael@0 | 5512 | nsCOMPtr<nsIImageLoadingContent> content(do_QueryInterface(f->GetContent())); |
michael@0 | 5513 | if (content) { |
michael@0 | 5514 | // use the presshell containing the image |
michael@0 | 5515 | PresShell* presShell = static_cast<PresShell*>(f->PresContext()->PresShell()); |
michael@0 | 5516 | uint32_t count = presShell->mVisibleImages.Count(); |
michael@0 | 5517 | presShell->mVisibleImages.PutEntry(content); |
michael@0 | 5518 | if (presShell->mVisibleImages.Count() > count) { |
michael@0 | 5519 | // content was added to mVisibleImages, so we need to increment its visible count |
michael@0 | 5520 | content->IncrementVisibleCount(); |
michael@0 | 5521 | } |
michael@0 | 5522 | } |
michael@0 | 5523 | } |
michael@0 | 5524 | } |
michael@0 | 5525 | |
michael@0 | 5526 | static PLDHashOperator |
michael@0 | 5527 | RemoveAndStore(nsRefPtrHashKey<nsIImageLoadingContent>* aEntry, void* userArg) |
michael@0 | 5528 | { |
michael@0 | 5529 | nsTArray< nsRefPtr<nsIImageLoadingContent> >* array = |
michael@0 | 5530 | static_cast< nsTArray< nsRefPtr<nsIImageLoadingContent> >* >(userArg); |
michael@0 | 5531 | array->AppendElement(aEntry->GetKey()); |
michael@0 | 5532 | return PL_DHASH_REMOVE; |
michael@0 | 5533 | } |
michael@0 | 5534 | |
michael@0 | 5535 | void |
michael@0 | 5536 | PresShell::RebuildImageVisibility(const nsDisplayList& aList) |
michael@0 | 5537 | { |
michael@0 | 5538 | MOZ_ASSERT(!mImageVisibilityVisited, "already visited?"); |
michael@0 | 5539 | mImageVisibilityVisited = true; |
michael@0 | 5540 | // Remove the entries of the mVisibleImages hashtable and put them in the |
michael@0 | 5541 | // beforeImageList array. |
michael@0 | 5542 | nsTArray< nsRefPtr<nsIImageLoadingContent> > beforeImageList; |
michael@0 | 5543 | beforeImageList.SetCapacity(mVisibleImages.Count()); |
michael@0 | 5544 | mVisibleImages.EnumerateEntries(RemoveAndStore, &beforeImageList); |
michael@0 | 5545 | MarkImagesInListVisible(aList); |
michael@0 | 5546 | for (uint32_t i = 0; i < beforeImageList.Length(); ++i) { |
michael@0 | 5547 | beforeImageList[i]->DecrementVisibleCount(); |
michael@0 | 5548 | } |
michael@0 | 5549 | } |
michael@0 | 5550 | |
michael@0 | 5551 | /* static */ void |
michael@0 | 5552 | PresShell::ClearImageVisibilityVisited(nsView* aView, bool aClear) |
michael@0 | 5553 | { |
michael@0 | 5554 | nsViewManager* vm = aView->GetViewManager(); |
michael@0 | 5555 | if (aClear) { |
michael@0 | 5556 | PresShell* presShell = static_cast<PresShell*>(vm->GetPresShell()); |
michael@0 | 5557 | if (!presShell->mImageVisibilityVisited) { |
michael@0 | 5558 | presShell->ClearVisibleImagesList(); |
michael@0 | 5559 | } |
michael@0 | 5560 | presShell->mImageVisibilityVisited = false; |
michael@0 | 5561 | } |
michael@0 | 5562 | for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) { |
michael@0 | 5563 | ClearImageVisibilityVisited(v, v->GetViewManager() != vm); |
michael@0 | 5564 | } |
michael@0 | 5565 | } |
michael@0 | 5566 | |
michael@0 | 5567 | static PLDHashOperator |
michael@0 | 5568 | DecrementVisibleCount(nsRefPtrHashKey<nsIImageLoadingContent>* aEntry, void* userArg) |
michael@0 | 5569 | { |
michael@0 | 5570 | aEntry->GetKey()->DecrementVisibleCount(); |
michael@0 | 5571 | return PL_DHASH_NEXT; |
michael@0 | 5572 | } |
michael@0 | 5573 | |
michael@0 | 5574 | void |
michael@0 | 5575 | PresShell::ClearVisibleImagesList() |
michael@0 | 5576 | { |
michael@0 | 5577 | mVisibleImages.EnumerateEntries(DecrementVisibleCount, nullptr); |
michael@0 | 5578 | mVisibleImages.Clear(); |
michael@0 | 5579 | } |
michael@0 | 5580 | |
michael@0 | 5581 | void |
michael@0 | 5582 | PresShell::UpdateImageVisibility() |
michael@0 | 5583 | { |
michael@0 | 5584 | MOZ_ASSERT(!mPresContext || mPresContext->IsRootContentDocument(), |
michael@0 | 5585 | "updating image visibility on a non-root content document?"); |
michael@0 | 5586 | |
michael@0 | 5587 | mUpdateImageVisibilityEvent.Revoke(); |
michael@0 | 5588 | |
michael@0 | 5589 | if (mHaveShutDown || mIsDestroying) { |
michael@0 | 5590 | return; |
michael@0 | 5591 | } |
michael@0 | 5592 | |
michael@0 | 5593 | // call update on that frame |
michael@0 | 5594 | nsIFrame* rootFrame = GetRootFrame(); |
michael@0 | 5595 | if (!rootFrame) { |
michael@0 | 5596 | ClearVisibleImagesList(); |
michael@0 | 5597 | return; |
michael@0 | 5598 | } |
michael@0 | 5599 | |
michael@0 | 5600 | // We could walk the frame tree directly and skip creating a display list for |
michael@0 | 5601 | // better perf. |
michael@0 | 5602 | nsRect updateRect(nsPoint(0, 0), rootFrame->GetSize()); |
michael@0 | 5603 | nsDisplayListBuilder builder(rootFrame, nsDisplayListBuilder::IMAGE_VISIBILITY, true); |
michael@0 | 5604 | builder.IgnorePaintSuppression(); |
michael@0 | 5605 | builder.EnterPresShell(rootFrame, updateRect); |
michael@0 | 5606 | nsDisplayList list; |
michael@0 | 5607 | rootFrame->BuildDisplayListForStackingContext(&builder, updateRect, &list); |
michael@0 | 5608 | builder.LeavePresShell(rootFrame, updateRect); |
michael@0 | 5609 | |
michael@0 | 5610 | RebuildImageVisibility(list); |
michael@0 | 5611 | |
michael@0 | 5612 | ClearImageVisibilityVisited(rootFrame->GetView(), true); |
michael@0 | 5613 | |
michael@0 | 5614 | list.DeleteAll(); |
michael@0 | 5615 | } |
michael@0 | 5616 | |
michael@0 | 5617 | bool |
michael@0 | 5618 | PresShell::AssumeAllImagesVisible() |
michael@0 | 5619 | { |
michael@0 | 5620 | static bool sImageVisibilityEnabled = true; |
michael@0 | 5621 | static bool sImageVisibilityEnabledForBrowserElementsOnly = false; |
michael@0 | 5622 | static bool sImageVisibilityPrefCached = false; |
michael@0 | 5623 | |
michael@0 | 5624 | if (!sImageVisibilityPrefCached) { |
michael@0 | 5625 | Preferences::AddBoolVarCache(&sImageVisibilityEnabled, |
michael@0 | 5626 | "layout.imagevisibility.enabled", true); |
michael@0 | 5627 | Preferences::AddBoolVarCache(&sImageVisibilityEnabledForBrowserElementsOnly, |
michael@0 | 5628 | "layout.imagevisibility.enabled_for_browser_elements_only", false); |
michael@0 | 5629 | sImageVisibilityPrefCached = true; |
michael@0 | 5630 | } |
michael@0 | 5631 | |
michael@0 | 5632 | if ((!sImageVisibilityEnabled && |
michael@0 | 5633 | !sImageVisibilityEnabledForBrowserElementsOnly) || |
michael@0 | 5634 | !mPresContext || !mDocument) { |
michael@0 | 5635 | return true; |
michael@0 | 5636 | } |
michael@0 | 5637 | |
michael@0 | 5638 | // We assume all images are visible in print, print preview, chrome, xul, and |
michael@0 | 5639 | // resource docs and don't keep track of them. |
michael@0 | 5640 | if (mPresContext->Type() == nsPresContext::eContext_PrintPreview || |
michael@0 | 5641 | mPresContext->Type() == nsPresContext::eContext_Print || |
michael@0 | 5642 | mPresContext->IsChrome() || |
michael@0 | 5643 | mDocument->IsResourceDoc() || |
michael@0 | 5644 | mDocument->IsXUL()) { |
michael@0 | 5645 | return true; |
michael@0 | 5646 | } |
michael@0 | 5647 | |
michael@0 | 5648 | if (!sImageVisibilityEnabled && |
michael@0 | 5649 | sImageVisibilityEnabledForBrowserElementsOnly) { |
michael@0 | 5650 | nsCOMPtr<nsIDocShell> docshell(mPresContext->GetDocShell()); |
michael@0 | 5651 | if (!docshell || !docshell->GetIsInBrowserElement()) { |
michael@0 | 5652 | return true; |
michael@0 | 5653 | } |
michael@0 | 5654 | } |
michael@0 | 5655 | |
michael@0 | 5656 | return false; |
michael@0 | 5657 | } |
michael@0 | 5658 | |
michael@0 | 5659 | void |
michael@0 | 5660 | PresShell::ScheduleImageVisibilityUpdate() |
michael@0 | 5661 | { |
michael@0 | 5662 | if (AssumeAllImagesVisible()) |
michael@0 | 5663 | return; |
michael@0 | 5664 | |
michael@0 | 5665 | if (!mPresContext->IsRootContentDocument()) { |
michael@0 | 5666 | nsPresContext* presContext = mPresContext->GetToplevelContentDocumentPresContext(); |
michael@0 | 5667 | if (!presContext) |
michael@0 | 5668 | return; |
michael@0 | 5669 | MOZ_ASSERT(presContext->IsRootContentDocument(), |
michael@0 | 5670 | "Didn't get a root prescontext from GetToplevelContentDocumentPresContext?"); |
michael@0 | 5671 | presContext->PresShell()->ScheduleImageVisibilityUpdate(); |
michael@0 | 5672 | return; |
michael@0 | 5673 | } |
michael@0 | 5674 | |
michael@0 | 5675 | if (mHaveShutDown || mIsDestroying) |
michael@0 | 5676 | return; |
michael@0 | 5677 | |
michael@0 | 5678 | if (mUpdateImageVisibilityEvent.IsPending()) |
michael@0 | 5679 | return; |
michael@0 | 5680 | |
michael@0 | 5681 | nsRefPtr<nsRunnableMethod<PresShell> > ev = |
michael@0 | 5682 | NS_NewRunnableMethod(this, &PresShell::UpdateImageVisibility); |
michael@0 | 5683 | if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) { |
michael@0 | 5684 | mUpdateImageVisibilityEvent = ev; |
michael@0 | 5685 | } |
michael@0 | 5686 | } |
michael@0 | 5687 | |
michael@0 | 5688 | void |
michael@0 | 5689 | PresShell::EnsureImageInVisibleList(nsIImageLoadingContent* aImage) |
michael@0 | 5690 | { |
michael@0 | 5691 | if (AssumeAllImagesVisible()) { |
michael@0 | 5692 | aImage->IncrementVisibleCount(); |
michael@0 | 5693 | return; |
michael@0 | 5694 | } |
michael@0 | 5695 | |
michael@0 | 5696 | #ifdef DEBUG |
michael@0 | 5697 | // if it has a frame make sure its in this presshell |
michael@0 | 5698 | nsCOMPtr<nsIContent> content = do_QueryInterface(aImage); |
michael@0 | 5699 | if (content) { |
michael@0 | 5700 | PresShell* shell = static_cast<PresShell*>(content->OwnerDoc()->GetShell()); |
michael@0 | 5701 | MOZ_ASSERT(!shell || shell == this, "wrong shell"); |
michael@0 | 5702 | } |
michael@0 | 5703 | #endif |
michael@0 | 5704 | |
michael@0 | 5705 | if (!mVisibleImages.Contains(aImage)) { |
michael@0 | 5706 | mVisibleImages.PutEntry(aImage); |
michael@0 | 5707 | aImage->IncrementVisibleCount(); |
michael@0 | 5708 | } |
michael@0 | 5709 | } |
michael@0 | 5710 | |
michael@0 | 5711 | void |
michael@0 | 5712 | PresShell::RemoveImageFromVisibleList(nsIImageLoadingContent* aImage) |
michael@0 | 5713 | { |
michael@0 | 5714 | #ifdef DEBUG |
michael@0 | 5715 | // if it has a frame make sure its in this presshell |
michael@0 | 5716 | nsCOMPtr<nsIContent> content = do_QueryInterface(aImage); |
michael@0 | 5717 | if (content) { |
michael@0 | 5718 | PresShell* shell = static_cast<PresShell*>(content->OwnerDoc()->GetShell()); |
michael@0 | 5719 | MOZ_ASSERT(!shell || shell == this, "wrong shell"); |
michael@0 | 5720 | } |
michael@0 | 5721 | #endif |
michael@0 | 5722 | |
michael@0 | 5723 | if (AssumeAllImagesVisible()) { |
michael@0 | 5724 | MOZ_ASSERT(mVisibleImages.Count() == 0, "shouldn't have any images in the table"); |
michael@0 | 5725 | return; |
michael@0 | 5726 | } |
michael@0 | 5727 | |
michael@0 | 5728 | uint32_t count = mVisibleImages.Count(); |
michael@0 | 5729 | mVisibleImages.RemoveEntry(aImage); |
michael@0 | 5730 | if (mVisibleImages.Count() < count) { |
michael@0 | 5731 | // aImage was in the hashtable, so we need to decrement its visible count |
michael@0 | 5732 | aImage->DecrementVisibleCount(); |
michael@0 | 5733 | } |
michael@0 | 5734 | } |
michael@0 | 5735 | |
michael@0 | 5736 | class nsAutoNotifyDidPaint |
michael@0 | 5737 | { |
michael@0 | 5738 | public: |
michael@0 | 5739 | nsAutoNotifyDidPaint(PresShell* aShell, uint32_t aFlags) |
michael@0 | 5740 | : mShell(aShell), mFlags(aFlags) |
michael@0 | 5741 | { |
michael@0 | 5742 | } |
michael@0 | 5743 | ~nsAutoNotifyDidPaint() |
michael@0 | 5744 | { |
michael@0 | 5745 | mShell->GetPresContext()->NotifyDidPaintForSubtree(mFlags); |
michael@0 | 5746 | } |
michael@0 | 5747 | |
michael@0 | 5748 | private: |
michael@0 | 5749 | PresShell* mShell; |
michael@0 | 5750 | uint32_t mFlags; |
michael@0 | 5751 | }; |
michael@0 | 5752 | |
michael@0 | 5753 | class AutoUpdateHitRegion |
michael@0 | 5754 | { |
michael@0 | 5755 | public: |
michael@0 | 5756 | AutoUpdateHitRegion(PresShell* aShell, nsIFrame* aFrame) |
michael@0 | 5757 | : mShell(aShell), mFrame(aFrame) |
michael@0 | 5758 | { |
michael@0 | 5759 | } |
michael@0 | 5760 | ~AutoUpdateHitRegion() |
michael@0 | 5761 | { |
michael@0 | 5762 | if (XRE_GetProcessType() != GeckoProcessType_Content || |
michael@0 | 5763 | !mFrame || !mShell) { |
michael@0 | 5764 | return; |
michael@0 | 5765 | } |
michael@0 | 5766 | TabChild* tabChild = TabChild::GetFrom(mShell); |
michael@0 | 5767 | if (!tabChild || !tabChild->GetUpdateHitRegion()) { |
michael@0 | 5768 | return; |
michael@0 | 5769 | } |
michael@0 | 5770 | nsRegion region; |
michael@0 | 5771 | nsDisplayListBuilder builder(mFrame, |
michael@0 | 5772 | nsDisplayListBuilder::EVENT_DELIVERY, |
michael@0 | 5773 | /* aBuildCert= */ false); |
michael@0 | 5774 | nsDisplayList list; |
michael@0 | 5775 | nsAutoTArray<nsIFrame*, 100> outFrames; |
michael@0 | 5776 | nsDisplayItem::HitTestState hitTestState; |
michael@0 | 5777 | nsRect bounds = mShell->GetPresContext()->GetVisibleArea(); |
michael@0 | 5778 | builder.EnterPresShell(mFrame, bounds); |
michael@0 | 5779 | mFrame->BuildDisplayListForStackingContext(&builder, bounds, &list); |
michael@0 | 5780 | builder.LeavePresShell(mFrame, bounds); |
michael@0 | 5781 | list.HitTest(&builder, bounds, &hitTestState, &outFrames); |
michael@0 | 5782 | list.DeleteAll(); |
michael@0 | 5783 | for (int32_t i = outFrames.Length() - 1; i >= 0; --i) { |
michael@0 | 5784 | region.Or(region, nsLayoutUtils::TransformFrameRectToAncestor( |
michael@0 | 5785 | outFrames[i], nsRect(nsPoint(0, 0), outFrames[i]->GetSize()), mFrame)); |
michael@0 | 5786 | } |
michael@0 | 5787 | tabChild->UpdateHitRegion(region); |
michael@0 | 5788 | } |
michael@0 | 5789 | private: |
michael@0 | 5790 | PresShell* mShell; |
michael@0 | 5791 | nsIFrame* mFrame; |
michael@0 | 5792 | }; |
michael@0 | 5793 | |
michael@0 | 5794 | void |
michael@0 | 5795 | PresShell::RestyleShadowRoot(ShadowRoot* aShadowRoot) |
michael@0 | 5796 | { |
michael@0 | 5797 | // Mark the children of the ShadowRoot as style changed but not |
michael@0 | 5798 | // the ShadowRoot itself because it is a document fragment and does not |
michael@0 | 5799 | // have a frame. |
michael@0 | 5800 | ExplicitChildIterator iterator(aShadowRoot); |
michael@0 | 5801 | for (nsIContent* child = iterator.GetNextChild(); |
michael@0 | 5802 | child; |
michael@0 | 5803 | child = iterator.GetNextChild()) { |
michael@0 | 5804 | if (child->IsElement()) { |
michael@0 | 5805 | mChangedScopeStyleRoots.AppendElement(child->AsElement()); |
michael@0 | 5806 | } |
michael@0 | 5807 | } |
michael@0 | 5808 | } |
michael@0 | 5809 | |
michael@0 | 5810 | void |
michael@0 | 5811 | PresShell::Paint(nsView* aViewToPaint, |
michael@0 | 5812 | const nsRegion& aDirtyRegion, |
michael@0 | 5813 | uint32_t aFlags) |
michael@0 | 5814 | { |
michael@0 | 5815 | PROFILER_LABEL("Paint", "PresShell::Paint"); |
michael@0 | 5816 | NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell"); |
michael@0 | 5817 | NS_ASSERTION(aViewToPaint, "null view"); |
michael@0 | 5818 | |
michael@0 | 5819 | MOZ_ASSERT(!mImageVisibilityVisited, "should have been cleared"); |
michael@0 | 5820 | |
michael@0 | 5821 | if (!mIsActive || mIsZombie) { |
michael@0 | 5822 | return; |
michael@0 | 5823 | } |
michael@0 | 5824 | |
michael@0 | 5825 | nsPresContext* presContext = GetPresContext(); |
michael@0 | 5826 | AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext, Paint); |
michael@0 | 5827 | |
michael@0 | 5828 | nsIFrame* frame = aViewToPaint->GetFrame(); |
michael@0 | 5829 | |
michael@0 | 5830 | bool isRetainingManager; |
michael@0 | 5831 | LayerManager* layerManager = |
michael@0 | 5832 | aViewToPaint->GetWidget()->GetLayerManager(&isRetainingManager); |
michael@0 | 5833 | NS_ASSERTION(layerManager, "Must be in paint event"); |
michael@0 | 5834 | bool shouldInvalidate = layerManager->NeedsWidgetInvalidation(); |
michael@0 | 5835 | |
michael@0 | 5836 | nsAutoNotifyDidPaint notifyDidPaint(this, aFlags); |
michael@0 | 5837 | AutoUpdateHitRegion updateHitRegion(this, frame); |
michael@0 | 5838 | |
michael@0 | 5839 | // Whether or not we should set first paint when painting is |
michael@0 | 5840 | // suppressed is debatable. For now we'll do it because |
michael@0 | 5841 | // B2G relies on first paint to configure the viewport and |
michael@0 | 5842 | // we only want to do that when we have real content to paint. |
michael@0 | 5843 | // See Bug 798245 |
michael@0 | 5844 | if (mIsFirstPaint && !mPaintingSuppressed) { |
michael@0 | 5845 | layerManager->SetIsFirstPaint(); |
michael@0 | 5846 | mIsFirstPaint = false; |
michael@0 | 5847 | } |
michael@0 | 5848 | |
michael@0 | 5849 | layerManager->BeginTransaction(); |
michael@0 | 5850 | |
michael@0 | 5851 | if (frame && isRetainingManager) { |
michael@0 | 5852 | // Try to do an empty transaction, if the frame tree does not |
michael@0 | 5853 | // need to be updated. Do not try to do an empty transaction on |
michael@0 | 5854 | // a non-retained layer manager (like the BasicLayerManager that |
michael@0 | 5855 | // draws the window title bar on Mac), because a) it won't work |
michael@0 | 5856 | // and b) below we don't want to clear NS_FRAME_UPDATE_LAYER_TREE, |
michael@0 | 5857 | // that will cause us to forget to update the real layer manager! |
michael@0 | 5858 | |
michael@0 | 5859 | if (!(aFlags & PAINT_LAYERS)) { |
michael@0 | 5860 | if (layerManager->EndEmptyTransaction()) { |
michael@0 | 5861 | return; |
michael@0 | 5862 | } |
michael@0 | 5863 | NS_WARNING("Must complete empty transaction when compositing!"); |
michael@0 | 5864 | } |
michael@0 | 5865 | |
michael@0 | 5866 | if (!(frame->GetStateBits() & NS_FRAME_UPDATE_LAYER_TREE) && |
michael@0 | 5867 | !mNextPaintCompressed) { |
michael@0 | 5868 | NotifySubDocInvalidationFunc computeInvalidFunc = |
michael@0 | 5869 | presContext->MayHavePaintEventListenerInSubDocument() ? nsPresContext::NotifySubDocInvalidation : 0; |
michael@0 | 5870 | bool computeInvalidRect = computeInvalidFunc || |
michael@0 | 5871 | (layerManager->GetBackendType() == LayersBackend::LAYERS_BASIC); |
michael@0 | 5872 | |
michael@0 | 5873 | nsAutoPtr<LayerProperties> props(computeInvalidRect ? |
michael@0 | 5874 | LayerProperties::CloneFrom(layerManager->GetRoot()) : |
michael@0 | 5875 | nullptr); |
michael@0 | 5876 | |
michael@0 | 5877 | if (layerManager->EndEmptyTransaction((aFlags & PAINT_COMPOSITE) ? |
michael@0 | 5878 | LayerManager::END_DEFAULT : LayerManager::END_NO_COMPOSITE)) { |
michael@0 | 5879 | nsIntRegion invalid; |
michael@0 | 5880 | if (props) { |
michael@0 | 5881 | invalid = props->ComputeDifferences(layerManager->GetRoot(), computeInvalidFunc); |
michael@0 | 5882 | } else { |
michael@0 | 5883 | LayerProperties::ClearInvalidations(layerManager->GetRoot()); |
michael@0 | 5884 | } |
michael@0 | 5885 | if (props) { |
michael@0 | 5886 | if (!invalid.IsEmpty()) { |
michael@0 | 5887 | nsIntRect bounds = invalid.GetBounds(); |
michael@0 | 5888 | nsRect rect(presContext->DevPixelsToAppUnits(bounds.x), |
michael@0 | 5889 | presContext->DevPixelsToAppUnits(bounds.y), |
michael@0 | 5890 | presContext->DevPixelsToAppUnits(bounds.width), |
michael@0 | 5891 | presContext->DevPixelsToAppUnits(bounds.height)); |
michael@0 | 5892 | if (shouldInvalidate) { |
michael@0 | 5893 | aViewToPaint->GetViewManager()->InvalidateViewNoSuppression(aViewToPaint, rect); |
michael@0 | 5894 | } |
michael@0 | 5895 | presContext->NotifyInvalidation(bounds, 0); |
michael@0 | 5896 | } |
michael@0 | 5897 | } else if (shouldInvalidate) { |
michael@0 | 5898 | aViewToPaint->GetViewManager()->InvalidateView(aViewToPaint); |
michael@0 | 5899 | } |
michael@0 | 5900 | |
michael@0 | 5901 | frame->UpdatePaintCountForPaintedPresShells(); |
michael@0 | 5902 | return; |
michael@0 | 5903 | } |
michael@0 | 5904 | } |
michael@0 | 5905 | frame->RemoveStateBits(NS_FRAME_UPDATE_LAYER_TREE); |
michael@0 | 5906 | } |
michael@0 | 5907 | if (frame) { |
michael@0 | 5908 | frame->ClearPresShellsFromLastPaint(); |
michael@0 | 5909 | } |
michael@0 | 5910 | |
michael@0 | 5911 | nscolor bgcolor = ComputeBackstopColor(aViewToPaint); |
michael@0 | 5912 | uint32_t flags = nsLayoutUtils::PAINT_WIDGET_LAYERS | nsLayoutUtils::PAINT_EXISTING_TRANSACTION; |
michael@0 | 5913 | if (!(aFlags & PAINT_COMPOSITE)) { |
michael@0 | 5914 | flags |= nsLayoutUtils::PAINT_NO_COMPOSITE; |
michael@0 | 5915 | } |
michael@0 | 5916 | if (mNextPaintCompressed) { |
michael@0 | 5917 | flags |= nsLayoutUtils::PAINT_COMPRESSED; |
michael@0 | 5918 | mNextPaintCompressed = false; |
michael@0 | 5919 | } |
michael@0 | 5920 | |
michael@0 | 5921 | if (frame) { |
michael@0 | 5922 | // We can paint directly into the widget using its layer manager. |
michael@0 | 5923 | nsLayoutUtils::PaintFrame(nullptr, frame, aDirtyRegion, bgcolor, flags); |
michael@0 | 5924 | return; |
michael@0 | 5925 | } |
michael@0 | 5926 | |
michael@0 | 5927 | nsRefPtr<ColorLayer> root = layerManager->CreateColorLayer(); |
michael@0 | 5928 | if (root) { |
michael@0 | 5929 | nsPresContext* pc = GetPresContext(); |
michael@0 | 5930 | nsIntRect bounds = |
michael@0 | 5931 | pc->GetVisibleArea().ToOutsidePixels(pc->AppUnitsPerDevPixel()); |
michael@0 | 5932 | bgcolor = NS_ComposeColors(bgcolor, mCanvasBackgroundColor); |
michael@0 | 5933 | root->SetColor(bgcolor); |
michael@0 | 5934 | root->SetVisibleRegion(bounds); |
michael@0 | 5935 | layerManager->SetRoot(root); |
michael@0 | 5936 | } |
michael@0 | 5937 | layerManager->EndTransaction(nullptr, nullptr, (aFlags & PAINT_COMPOSITE) ? |
michael@0 | 5938 | LayerManager::END_DEFAULT : LayerManager::END_NO_COMPOSITE); |
michael@0 | 5939 | } |
michael@0 | 5940 | |
michael@0 | 5941 | // static |
michael@0 | 5942 | void |
michael@0 | 5943 | nsIPresShell::SetCapturingContent(nsIContent* aContent, uint8_t aFlags) |
michael@0 | 5944 | { |
michael@0 | 5945 | // If capture was set for pointer lock, don't unlock unless we are coming |
michael@0 | 5946 | // out of pointer lock explicitly. |
michael@0 | 5947 | if (!aContent && gCaptureInfo.mPointerLock && |
michael@0 | 5948 | !(aFlags & CAPTURE_POINTERLOCK)) { |
michael@0 | 5949 | return; |
michael@0 | 5950 | } |
michael@0 | 5951 | |
michael@0 | 5952 | NS_IF_RELEASE(gCaptureInfo.mContent); |
michael@0 | 5953 | |
michael@0 | 5954 | // only set capturing content if allowed or the CAPTURE_IGNOREALLOWED or |
michael@0 | 5955 | // CAPTURE_POINTERLOCK flags are used. |
michael@0 | 5956 | if ((aFlags & CAPTURE_IGNOREALLOWED) || gCaptureInfo.mAllowed || |
michael@0 | 5957 | (aFlags & CAPTURE_POINTERLOCK)) { |
michael@0 | 5958 | if (aContent) { |
michael@0 | 5959 | NS_ADDREF(gCaptureInfo.mContent = aContent); |
michael@0 | 5960 | } |
michael@0 | 5961 | // CAPTURE_POINTERLOCK is the same as CAPTURE_RETARGETTOELEMENT & CAPTURE_IGNOREALLOWED |
michael@0 | 5962 | gCaptureInfo.mRetargetToElement = ((aFlags & CAPTURE_RETARGETTOELEMENT) != 0) || |
michael@0 | 5963 | ((aFlags & CAPTURE_POINTERLOCK) != 0); |
michael@0 | 5964 | gCaptureInfo.mPreventDrag = (aFlags & CAPTURE_PREVENTDRAG) != 0; |
michael@0 | 5965 | gCaptureInfo.mPointerLock = (aFlags & CAPTURE_POINTERLOCK) != 0; |
michael@0 | 5966 | } |
michael@0 | 5967 | } |
michael@0 | 5968 | |
michael@0 | 5969 | /* static */ void |
michael@0 | 5970 | nsIPresShell::SetPointerCapturingContent(uint32_t aPointerId, nsIContent* aContent) |
michael@0 | 5971 | { |
michael@0 | 5972 | nsIContent* content = GetPointerCapturingContent(aPointerId); |
michael@0 | 5973 | |
michael@0 | 5974 | PointerInfo* pointerInfo = nullptr; |
michael@0 | 5975 | if (!content && gActivePointersIds->Get(aPointerId, &pointerInfo) && |
michael@0 | 5976 | pointerInfo && |
michael@0 | 5977 | nsIDOMMouseEvent::MOZ_SOURCE_MOUSE == pointerInfo->mPointerType) { |
michael@0 | 5978 | SetCapturingContent(aContent, CAPTURE_PREVENTDRAG); |
michael@0 | 5979 | } |
michael@0 | 5980 | |
michael@0 | 5981 | if (content) { |
michael@0 | 5982 | // Releasing capture for given pointer. |
michael@0 | 5983 | gPointerCaptureList->Remove(aPointerId); |
michael@0 | 5984 | DispatchGotOrLostPointerCaptureEvent(false, aPointerId, content); |
michael@0 | 5985 | // Need to check the state because a lostpointercapture listener |
michael@0 | 5986 | // may have called SetPointerCapture |
michael@0 | 5987 | if (GetPointerCapturingContent(aPointerId)) { |
michael@0 | 5988 | return; |
michael@0 | 5989 | } |
michael@0 | 5990 | } |
michael@0 | 5991 | |
michael@0 | 5992 | gPointerCaptureList->Put(aPointerId, aContent); |
michael@0 | 5993 | DispatchGotOrLostPointerCaptureEvent(true, aPointerId, aContent); |
michael@0 | 5994 | } |
michael@0 | 5995 | |
michael@0 | 5996 | /* static */ void |
michael@0 | 5997 | nsIPresShell::ReleasePointerCapturingContent(uint32_t aPointerId, nsIContent* aContent) |
michael@0 | 5998 | { |
michael@0 | 5999 | if (gActivePointersIds->Get(aPointerId)) { |
michael@0 | 6000 | SetCapturingContent(nullptr, CAPTURE_PREVENTDRAG); |
michael@0 | 6001 | } |
michael@0 | 6002 | |
michael@0 | 6003 | // Releasing capture for given pointer. |
michael@0 | 6004 | gPointerCaptureList->Remove(aPointerId); |
michael@0 | 6005 | |
michael@0 | 6006 | DispatchGotOrLostPointerCaptureEvent(false, aPointerId, aContent); |
michael@0 | 6007 | } |
michael@0 | 6008 | |
michael@0 | 6009 | /* static */ nsIContent* |
michael@0 | 6010 | nsIPresShell::GetPointerCapturingContent(uint32_t aPointerId) |
michael@0 | 6011 | { |
michael@0 | 6012 | return gPointerCaptureList->GetWeak(aPointerId); |
michael@0 | 6013 | } |
michael@0 | 6014 | |
michael@0 | 6015 | /* static */ bool |
michael@0 | 6016 | nsIPresShell::GetPointerInfo(uint32_t aPointerId, bool& aActiveState) |
michael@0 | 6017 | { |
michael@0 | 6018 | PointerInfo* pointerInfo = nullptr; |
michael@0 | 6019 | if (gActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) { |
michael@0 | 6020 | aActiveState = pointerInfo->mActiveState; |
michael@0 | 6021 | return true; |
michael@0 | 6022 | } |
michael@0 | 6023 | return false; |
michael@0 | 6024 | } |
michael@0 | 6025 | |
michael@0 | 6026 | void |
michael@0 | 6027 | PresShell::UpdateActivePointerState(WidgetGUIEvent* aEvent) |
michael@0 | 6028 | { |
michael@0 | 6029 | switch (aEvent->message) { |
michael@0 | 6030 | case NS_MOUSE_ENTER: |
michael@0 | 6031 | // In this case we have to know information about available mouse pointers |
michael@0 | 6032 | if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) { |
michael@0 | 6033 | gActivePointersIds->Put(mouseEvent->pointerId, new PointerInfo(false, mouseEvent->inputSource)); |
michael@0 | 6034 | } |
michael@0 | 6035 | break; |
michael@0 | 6036 | case NS_POINTER_DOWN: |
michael@0 | 6037 | // In this case we switch pointer to active state |
michael@0 | 6038 | if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) { |
michael@0 | 6039 | gActivePointersIds->Put(pointerEvent->pointerId, new PointerInfo(true, pointerEvent->inputSource)); |
michael@0 | 6040 | } |
michael@0 | 6041 | break; |
michael@0 | 6042 | case NS_POINTER_UP: |
michael@0 | 6043 | // In this case we remove information about pointer or turn off active state |
michael@0 | 6044 | if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) { |
michael@0 | 6045 | if(pointerEvent->inputSource != nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) { |
michael@0 | 6046 | gActivePointersIds->Put(pointerEvent->pointerId, new PointerInfo(false, pointerEvent->inputSource)); |
michael@0 | 6047 | } else { |
michael@0 | 6048 | gActivePointersIds->Remove(pointerEvent->pointerId); |
michael@0 | 6049 | } |
michael@0 | 6050 | } |
michael@0 | 6051 | break; |
michael@0 | 6052 | case NS_MOUSE_EXIT: |
michael@0 | 6053 | // In this case we have to remove information about disappeared mouse pointers |
michael@0 | 6054 | if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) { |
michael@0 | 6055 | gActivePointersIds->Remove(mouseEvent->pointerId); |
michael@0 | 6056 | } |
michael@0 | 6057 | break; |
michael@0 | 6058 | } |
michael@0 | 6059 | } |
michael@0 | 6060 | |
michael@0 | 6061 | nsIContent* |
michael@0 | 6062 | PresShell::GetCurrentEventContent() |
michael@0 | 6063 | { |
michael@0 | 6064 | if (mCurrentEventContent && |
michael@0 | 6065 | mCurrentEventContent->GetCurrentDoc() != mDocument) { |
michael@0 | 6066 | mCurrentEventContent = nullptr; |
michael@0 | 6067 | mCurrentEventFrame = nullptr; |
michael@0 | 6068 | } |
michael@0 | 6069 | return mCurrentEventContent; |
michael@0 | 6070 | } |
michael@0 | 6071 | |
michael@0 | 6072 | nsIFrame* |
michael@0 | 6073 | PresShell::GetCurrentEventFrame() |
michael@0 | 6074 | { |
michael@0 | 6075 | if (MOZ_UNLIKELY(mIsDestroying)) { |
michael@0 | 6076 | return nullptr; |
michael@0 | 6077 | } |
michael@0 | 6078 | |
michael@0 | 6079 | // GetCurrentEventContent() makes sure the content is still in the |
michael@0 | 6080 | // same document that this pres shell belongs to. If not, then the |
michael@0 | 6081 | // frame shouldn't get an event, nor should we even assume its safe |
michael@0 | 6082 | // to try and find the frame. |
michael@0 | 6083 | nsIContent* content = GetCurrentEventContent(); |
michael@0 | 6084 | if (!mCurrentEventFrame && content) { |
michael@0 | 6085 | mCurrentEventFrame = content->GetPrimaryFrame(); |
michael@0 | 6086 | MOZ_ASSERT(!mCurrentEventFrame || |
michael@0 | 6087 | mCurrentEventFrame->PresContext()->GetPresShell() == this); |
michael@0 | 6088 | } |
michael@0 | 6089 | return mCurrentEventFrame; |
michael@0 | 6090 | } |
michael@0 | 6091 | |
michael@0 | 6092 | nsIFrame* |
michael@0 | 6093 | PresShell::GetEventTargetFrame() |
michael@0 | 6094 | { |
michael@0 | 6095 | return GetCurrentEventFrame(); |
michael@0 | 6096 | } |
michael@0 | 6097 | |
michael@0 | 6098 | already_AddRefed<nsIContent> |
michael@0 | 6099 | PresShell::GetEventTargetContent(WidgetEvent* aEvent) |
michael@0 | 6100 | { |
michael@0 | 6101 | nsCOMPtr<nsIContent> content = GetCurrentEventContent(); |
michael@0 | 6102 | if (!content) { |
michael@0 | 6103 | nsIFrame* currentEventFrame = GetCurrentEventFrame(); |
michael@0 | 6104 | if (currentEventFrame) { |
michael@0 | 6105 | currentEventFrame->GetContentForEvent(aEvent, getter_AddRefs(content)); |
michael@0 | 6106 | NS_ASSERTION(!content || content->GetCurrentDoc() == mDocument, |
michael@0 | 6107 | "handing out content from a different doc"); |
michael@0 | 6108 | } |
michael@0 | 6109 | } |
michael@0 | 6110 | return content.forget(); |
michael@0 | 6111 | } |
michael@0 | 6112 | |
michael@0 | 6113 | void |
michael@0 | 6114 | PresShell::PushCurrentEventInfo(nsIFrame* aFrame, nsIContent* aContent) |
michael@0 | 6115 | { |
michael@0 | 6116 | if (mCurrentEventFrame || mCurrentEventContent) { |
michael@0 | 6117 | mCurrentEventFrameStack.InsertElementAt(0, mCurrentEventFrame); |
michael@0 | 6118 | mCurrentEventContentStack.InsertObjectAt(mCurrentEventContent, 0); |
michael@0 | 6119 | } |
michael@0 | 6120 | mCurrentEventFrame = aFrame; |
michael@0 | 6121 | mCurrentEventContent = aContent; |
michael@0 | 6122 | } |
michael@0 | 6123 | |
michael@0 | 6124 | void |
michael@0 | 6125 | PresShell::PopCurrentEventInfo() |
michael@0 | 6126 | { |
michael@0 | 6127 | mCurrentEventFrame = nullptr; |
michael@0 | 6128 | mCurrentEventContent = nullptr; |
michael@0 | 6129 | |
michael@0 | 6130 | if (0 != mCurrentEventFrameStack.Length()) { |
michael@0 | 6131 | mCurrentEventFrame = mCurrentEventFrameStack.ElementAt(0); |
michael@0 | 6132 | mCurrentEventFrameStack.RemoveElementAt(0); |
michael@0 | 6133 | mCurrentEventContent = mCurrentEventContentStack.ObjectAt(0); |
michael@0 | 6134 | mCurrentEventContentStack.RemoveObjectAt(0); |
michael@0 | 6135 | |
michael@0 | 6136 | // Don't use it if it has moved to a different document. |
michael@0 | 6137 | if (mCurrentEventContent && |
michael@0 | 6138 | mCurrentEventContent->GetCurrentDoc() != mDocument) { |
michael@0 | 6139 | mCurrentEventContent = nullptr; |
michael@0 | 6140 | mCurrentEventFrame = nullptr; |
michael@0 | 6141 | } |
michael@0 | 6142 | } |
michael@0 | 6143 | } |
michael@0 | 6144 | |
michael@0 | 6145 | bool PresShell::InZombieDocument(nsIContent *aContent) |
michael@0 | 6146 | { |
michael@0 | 6147 | // If a content node points to a null document, or the document is not |
michael@0 | 6148 | // attached to a window, then it is possibly in a zombie document, |
michael@0 | 6149 | // about to be replaced by a newly loading document. |
michael@0 | 6150 | // Such documents cannot handle DOM events. |
michael@0 | 6151 | // It might actually be in a node not attached to any document, |
michael@0 | 6152 | // in which case there is not parent presshell to retarget it to. |
michael@0 | 6153 | nsIDocument *doc = aContent->GetDocument(); |
michael@0 | 6154 | return !doc || !doc->GetWindow(); |
michael@0 | 6155 | } |
michael@0 | 6156 | |
michael@0 | 6157 | already_AddRefed<nsPIDOMWindow> |
michael@0 | 6158 | PresShell::GetRootWindow() |
michael@0 | 6159 | { |
michael@0 | 6160 | nsCOMPtr<nsPIDOMWindow> window = |
michael@0 | 6161 | do_QueryInterface(mDocument->GetWindow()); |
michael@0 | 6162 | if (window) { |
michael@0 | 6163 | nsCOMPtr<nsPIDOMWindow> rootWindow = window->GetPrivateRoot(); |
michael@0 | 6164 | NS_ASSERTION(rootWindow, "nsPIDOMWindow::GetPrivateRoot() returns NULL"); |
michael@0 | 6165 | return rootWindow.forget(); |
michael@0 | 6166 | } |
michael@0 | 6167 | |
michael@0 | 6168 | // If we don't have DOM window, we're zombie, we should find the root window |
michael@0 | 6169 | // with our parent shell. |
michael@0 | 6170 | nsCOMPtr<nsIPresShell> parent = GetParentPresShellForEventHandling(); |
michael@0 | 6171 | NS_ENSURE_TRUE(parent, nullptr); |
michael@0 | 6172 | return parent->GetRootWindow(); |
michael@0 | 6173 | } |
michael@0 | 6174 | |
michael@0 | 6175 | already_AddRefed<nsIPresShell> |
michael@0 | 6176 | PresShell::GetParentPresShellForEventHandling() |
michael@0 | 6177 | { |
michael@0 | 6178 | NS_ENSURE_TRUE(mPresContext, nullptr); |
michael@0 | 6179 | |
michael@0 | 6180 | // Now, find the parent pres shell and send the event there |
michael@0 | 6181 | nsCOMPtr<nsIDocShellTreeItem> treeItem = mPresContext->GetDocShell(); |
michael@0 | 6182 | if (!treeItem) { |
michael@0 | 6183 | treeItem = mForwardingContainer.get(); |
michael@0 | 6184 | } |
michael@0 | 6185 | |
michael@0 | 6186 | // Might have gone away, or never been around to start with |
michael@0 | 6187 | NS_ENSURE_TRUE(treeItem, nullptr); |
michael@0 | 6188 | |
michael@0 | 6189 | nsCOMPtr<nsIDocShellTreeItem> parentTreeItem; |
michael@0 | 6190 | treeItem->GetParent(getter_AddRefs(parentTreeItem)); |
michael@0 | 6191 | nsCOMPtr<nsIDocShell> parentDocShell = do_QueryInterface(parentTreeItem); |
michael@0 | 6192 | NS_ENSURE_TRUE(parentDocShell && treeItem != parentTreeItem, nullptr); |
michael@0 | 6193 | |
michael@0 | 6194 | nsCOMPtr<nsIPresShell> parentPresShell = parentDocShell->GetPresShell(); |
michael@0 | 6195 | return parentPresShell.forget(); |
michael@0 | 6196 | } |
michael@0 | 6197 | |
michael@0 | 6198 | nsresult |
michael@0 | 6199 | PresShell::RetargetEventToParent(WidgetGUIEvent* aEvent, |
michael@0 | 6200 | nsEventStatus* aEventStatus) |
michael@0 | 6201 | { |
michael@0 | 6202 | // Send this events straight up to the parent pres shell. |
michael@0 | 6203 | // We do this for keystroke events in zombie documents or if either a frame |
michael@0 | 6204 | // or a root content is not present. |
michael@0 | 6205 | // That way at least the UI key bindings can work. |
michael@0 | 6206 | |
michael@0 | 6207 | nsCOMPtr<nsIPresShell> kungFuDeathGrip(this); |
michael@0 | 6208 | nsCOMPtr<nsIPresShell> parentPresShell = GetParentPresShellForEventHandling(); |
michael@0 | 6209 | NS_ENSURE_TRUE(parentPresShell, NS_ERROR_FAILURE); |
michael@0 | 6210 | |
michael@0 | 6211 | // Fake the event as though it's from the parent pres shell's root frame. |
michael@0 | 6212 | return parentPresShell->HandleEvent(parentPresShell->GetRootFrame(), aEvent, true, aEventStatus); |
michael@0 | 6213 | } |
michael@0 | 6214 | |
michael@0 | 6215 | void |
michael@0 | 6216 | PresShell::DisableNonTestMouseEvents(bool aDisable) |
michael@0 | 6217 | { |
michael@0 | 6218 | sDisableNonTestMouseEvents = aDisable; |
michael@0 | 6219 | } |
michael@0 | 6220 | |
michael@0 | 6221 | already_AddRefed<nsPIDOMWindow> |
michael@0 | 6222 | PresShell::GetFocusedDOMWindowInOurWindow() |
michael@0 | 6223 | { |
michael@0 | 6224 | nsCOMPtr<nsPIDOMWindow> rootWindow = GetRootWindow(); |
michael@0 | 6225 | NS_ENSURE_TRUE(rootWindow, nullptr); |
michael@0 | 6226 | nsCOMPtr<nsPIDOMWindow> focusedWindow; |
michael@0 | 6227 | nsFocusManager::GetFocusedDescendant(rootWindow, true, |
michael@0 | 6228 | getter_AddRefs(focusedWindow)); |
michael@0 | 6229 | return focusedWindow.forget(); |
michael@0 | 6230 | } |
michael@0 | 6231 | |
michael@0 | 6232 | void |
michael@0 | 6233 | PresShell::RecordMouseLocation(WidgetGUIEvent* aEvent) |
michael@0 | 6234 | { |
michael@0 | 6235 | if (!mPresContext) |
michael@0 | 6236 | return; |
michael@0 | 6237 | |
michael@0 | 6238 | if (!mPresContext->IsRoot()) { |
michael@0 | 6239 | PresShell* rootPresShell = GetRootPresShell(); |
michael@0 | 6240 | if (rootPresShell) { |
michael@0 | 6241 | rootPresShell->RecordMouseLocation(aEvent); |
michael@0 | 6242 | } |
michael@0 | 6243 | return; |
michael@0 | 6244 | } |
michael@0 | 6245 | |
michael@0 | 6246 | if ((aEvent->message == NS_MOUSE_MOVE && |
michael@0 | 6247 | aEvent->AsMouseEvent()->reason == WidgetMouseEvent::eReal) || |
michael@0 | 6248 | aEvent->message == NS_MOUSE_ENTER || |
michael@0 | 6249 | aEvent->message == NS_MOUSE_BUTTON_DOWN || |
michael@0 | 6250 | aEvent->message == NS_MOUSE_BUTTON_UP) { |
michael@0 | 6251 | nsIFrame* rootFrame = GetRootFrame(); |
michael@0 | 6252 | if (!rootFrame) { |
michael@0 | 6253 | nsView* rootView = mViewManager->GetRootView(); |
michael@0 | 6254 | mMouseLocation = nsLayoutUtils::TranslateWidgetToView(mPresContext, |
michael@0 | 6255 | aEvent->widget, LayoutDeviceIntPoint::ToUntyped(aEvent->refPoint), |
michael@0 | 6256 | rootView); |
michael@0 | 6257 | } else { |
michael@0 | 6258 | mMouseLocation = |
michael@0 | 6259 | nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, rootFrame); |
michael@0 | 6260 | } |
michael@0 | 6261 | #ifdef DEBUG_MOUSE_LOCATION |
michael@0 | 6262 | if (aEvent->message == NS_MOUSE_ENTER) |
michael@0 | 6263 | printf("[ps=%p]got mouse enter for %p\n", |
michael@0 | 6264 | this, aEvent->widget); |
michael@0 | 6265 | printf("[ps=%p]setting mouse location to (%d,%d)\n", |
michael@0 | 6266 | this, mMouseLocation.x, mMouseLocation.y); |
michael@0 | 6267 | #endif |
michael@0 | 6268 | if (aEvent->message == NS_MOUSE_ENTER) |
michael@0 | 6269 | SynthesizeMouseMove(false); |
michael@0 | 6270 | } else if (aEvent->message == NS_MOUSE_EXIT) { |
michael@0 | 6271 | // Although we only care about the mouse moving into an area for which this |
michael@0 | 6272 | // pres shell doesn't receive mouse move events, we don't check which widget |
michael@0 | 6273 | // the mouse exit was for since this seems to vary by platform. Hopefully |
michael@0 | 6274 | // this won't matter at all since we'll get the mouse move or enter after |
michael@0 | 6275 | // the mouse exit when the mouse moves from one of our widgets into another. |
michael@0 | 6276 | mMouseLocation = nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); |
michael@0 | 6277 | #ifdef DEBUG_MOUSE_LOCATION |
michael@0 | 6278 | printf("[ps=%p]got mouse exit for %p\n", |
michael@0 | 6279 | this, aEvent->widget); |
michael@0 | 6280 | printf("[ps=%p]clearing mouse location\n", |
michael@0 | 6281 | this); |
michael@0 | 6282 | #endif |
michael@0 | 6283 | } |
michael@0 | 6284 | } |
michael@0 | 6285 | |
michael@0 | 6286 | static void |
michael@0 | 6287 | EvictTouchPoint(nsRefPtr<dom::Touch>& aTouch, |
michael@0 | 6288 | nsIDocument* aLimitToDocument = nullptr) |
michael@0 | 6289 | { |
michael@0 | 6290 | nsCOMPtr<nsINode> node(do_QueryInterface(aTouch->mTarget)); |
michael@0 | 6291 | if (node) { |
michael@0 | 6292 | nsIDocument* doc = node->GetCurrentDoc(); |
michael@0 | 6293 | if (doc && (!aLimitToDocument || aLimitToDocument == doc)) { |
michael@0 | 6294 | nsIPresShell* presShell = doc->GetShell(); |
michael@0 | 6295 | if (presShell) { |
michael@0 | 6296 | nsIFrame* frame = presShell->GetRootFrame(); |
michael@0 | 6297 | if (frame) { |
michael@0 | 6298 | nsPoint pt(aTouch->mRefPoint.x, aTouch->mRefPoint.y); |
michael@0 | 6299 | nsCOMPtr<nsIWidget> widget = frame->GetView()->GetNearestWidget(&pt); |
michael@0 | 6300 | if (widget) { |
michael@0 | 6301 | WidgetTouchEvent event(true, NS_TOUCH_END, widget); |
michael@0 | 6302 | event.widget = widget; |
michael@0 | 6303 | event.time = PR_IntervalNow(); |
michael@0 | 6304 | event.touches.AppendElement(aTouch); |
michael@0 | 6305 | nsEventStatus status; |
michael@0 | 6306 | widget->DispatchEvent(&event, status); |
michael@0 | 6307 | return; |
michael@0 | 6308 | } |
michael@0 | 6309 | } |
michael@0 | 6310 | } |
michael@0 | 6311 | } |
michael@0 | 6312 | } |
michael@0 | 6313 | if (!node || !aLimitToDocument || node->OwnerDoc() == aLimitToDocument) { |
michael@0 | 6314 | // We couldn't dispatch touchend. Remove the touch from gCaptureTouchList |
michael@0 | 6315 | // explicitly. |
michael@0 | 6316 | nsIPresShell::gCaptureTouchList->Remove(aTouch->Identifier()); |
michael@0 | 6317 | } |
michael@0 | 6318 | } |
michael@0 | 6319 | |
michael@0 | 6320 | static PLDHashOperator |
michael@0 | 6321 | AppendToTouchList(const uint32_t& aKey, nsRefPtr<dom::Touch>& aData, void *aTouchList) |
michael@0 | 6322 | { |
michael@0 | 6323 | nsTArray< nsRefPtr<dom::Touch> >* touches = |
michael@0 | 6324 | static_cast<nsTArray< nsRefPtr<dom::Touch> >*>(aTouchList); |
michael@0 | 6325 | aData->mChanged = false; |
michael@0 | 6326 | touches->AppendElement(aData); |
michael@0 | 6327 | return PL_DHASH_NEXT; |
michael@0 | 6328 | } |
michael@0 | 6329 | |
michael@0 | 6330 | void |
michael@0 | 6331 | PresShell::EvictTouches() |
michael@0 | 6332 | { |
michael@0 | 6333 | nsTArray< nsRefPtr<dom::Touch> > touches; |
michael@0 | 6334 | gCaptureTouchList->Enumerate(&AppendToTouchList, &touches); |
michael@0 | 6335 | for (uint32_t i = 0; i < touches.Length(); ++i) { |
michael@0 | 6336 | EvictTouchPoint(touches[i], mDocument); |
michael@0 | 6337 | } |
michael@0 | 6338 | } |
michael@0 | 6339 | |
michael@0 | 6340 | static PLDHashOperator |
michael@0 | 6341 | FindAnyTarget(const uint32_t& aKey, nsRefPtr<dom::Touch>& aData, |
michael@0 | 6342 | void* aAnyTarget) |
michael@0 | 6343 | { |
michael@0 | 6344 | if (aData) { |
michael@0 | 6345 | dom::EventTarget* target = aData->Target(); |
michael@0 | 6346 | if (target) { |
michael@0 | 6347 | nsCOMPtr<nsIContent>* content = |
michael@0 | 6348 | static_cast<nsCOMPtr<nsIContent>*>(aAnyTarget); |
michael@0 | 6349 | *content = do_QueryInterface(target); |
michael@0 | 6350 | return PL_DHASH_STOP; |
michael@0 | 6351 | } |
michael@0 | 6352 | } |
michael@0 | 6353 | return PL_DHASH_NEXT; |
michael@0 | 6354 | } |
michael@0 | 6355 | |
michael@0 | 6356 | nsIFrame* GetNearestFrameContainingPresShell(nsIPresShell* aPresShell) |
michael@0 | 6357 | { |
michael@0 | 6358 | nsView* view = aPresShell->GetViewManager()->GetRootView(); |
michael@0 | 6359 | while (view && !view->GetFrame()) { |
michael@0 | 6360 | view = view->GetParent(); |
michael@0 | 6361 | } |
michael@0 | 6362 | |
michael@0 | 6363 | nsIFrame* frame = nullptr; |
michael@0 | 6364 | if (view) { |
michael@0 | 6365 | frame = view->GetFrame(); |
michael@0 | 6366 | } |
michael@0 | 6367 | |
michael@0 | 6368 | return frame; |
michael@0 | 6369 | } |
michael@0 | 6370 | |
michael@0 | 6371 | static bool |
michael@0 | 6372 | FlushThrottledStyles(nsIDocument *aDocument, void *aData) |
michael@0 | 6373 | { |
michael@0 | 6374 | nsIPresShell* shell = aDocument->GetShell(); |
michael@0 | 6375 | if (shell && shell->IsVisible()) { |
michael@0 | 6376 | nsPresContext* presContext = shell->GetPresContext(); |
michael@0 | 6377 | if (presContext) { |
michael@0 | 6378 | presContext->TransitionManager()->UpdateAllThrottledStyles(); |
michael@0 | 6379 | presContext->AnimationManager()->UpdateAllThrottledStyles(); |
michael@0 | 6380 | } |
michael@0 | 6381 | } |
michael@0 | 6382 | |
michael@0 | 6383 | return true; |
michael@0 | 6384 | } |
michael@0 | 6385 | |
michael@0 | 6386 | static nsresult |
michael@0 | 6387 | DispatchPointerFromMouseOrTouch(PresShell* aShell, |
michael@0 | 6388 | nsIFrame* aFrame, |
michael@0 | 6389 | WidgetGUIEvent* aEvent, |
michael@0 | 6390 | bool aDontRetargetEvents, |
michael@0 | 6391 | nsEventStatus* aStatus) |
michael@0 | 6392 | { |
michael@0 | 6393 | uint32_t pointerMessage = 0; |
michael@0 | 6394 | if (aEvent->eventStructType == NS_MOUSE_EVENT) { |
michael@0 | 6395 | WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); |
michael@0 | 6396 | // if it is not mouse then it is likely will come as touch event |
michael@0 | 6397 | if (!mouseEvent->convertToPointer) { |
michael@0 | 6398 | return NS_OK; |
michael@0 | 6399 | } |
michael@0 | 6400 | int16_t button = mouseEvent->button; |
michael@0 | 6401 | switch (mouseEvent->message) { |
michael@0 | 6402 | case NS_MOUSE_MOVE: |
michael@0 | 6403 | if (mouseEvent->buttons == 0) { |
michael@0 | 6404 | button = -1; |
michael@0 | 6405 | } |
michael@0 | 6406 | pointerMessage = NS_POINTER_MOVE; |
michael@0 | 6407 | break; |
michael@0 | 6408 | case NS_MOUSE_BUTTON_UP: |
michael@0 | 6409 | pointerMessage = NS_POINTER_UP; |
michael@0 | 6410 | break; |
michael@0 | 6411 | case NS_MOUSE_BUTTON_DOWN: |
michael@0 | 6412 | pointerMessage = NS_POINTER_DOWN; |
michael@0 | 6413 | break; |
michael@0 | 6414 | default: |
michael@0 | 6415 | return NS_OK; |
michael@0 | 6416 | } |
michael@0 | 6417 | |
michael@0 | 6418 | WidgetPointerEvent event(*mouseEvent); |
michael@0 | 6419 | event.message = pointerMessage; |
michael@0 | 6420 | event.button = button; |
michael@0 | 6421 | event.pressure = event.buttons ? |
michael@0 | 6422 | mouseEvent->pressure ? mouseEvent->pressure : 0.5f : |
michael@0 | 6423 | 0.0f; |
michael@0 | 6424 | event.convertToPointer = mouseEvent->convertToPointer = false; |
michael@0 | 6425 | aShell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus); |
michael@0 | 6426 | } else if (aEvent->eventStructType == NS_TOUCH_EVENT) { |
michael@0 | 6427 | WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent(); |
michael@0 | 6428 | // loop over all touches and dispatch pointer events on each touch |
michael@0 | 6429 | // copy the event |
michael@0 | 6430 | switch (touchEvent->message) { |
michael@0 | 6431 | case NS_TOUCH_MOVE: |
michael@0 | 6432 | pointerMessage = NS_POINTER_MOVE; |
michael@0 | 6433 | break; |
michael@0 | 6434 | case NS_TOUCH_END: |
michael@0 | 6435 | pointerMessage = NS_POINTER_UP; |
michael@0 | 6436 | break; |
michael@0 | 6437 | case NS_TOUCH_START: |
michael@0 | 6438 | pointerMessage = NS_POINTER_DOWN; |
michael@0 | 6439 | break; |
michael@0 | 6440 | case NS_TOUCH_CANCEL: |
michael@0 | 6441 | pointerMessage = NS_POINTER_CANCEL; |
michael@0 | 6442 | break; |
michael@0 | 6443 | default: |
michael@0 | 6444 | return NS_OK; |
michael@0 | 6445 | } |
michael@0 | 6446 | |
michael@0 | 6447 | for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) { |
michael@0 | 6448 | mozilla::dom::Touch* touch = touchEvent->touches[i]; |
michael@0 | 6449 | if (!touch || !touch->convertToPointer) { |
michael@0 | 6450 | continue; |
michael@0 | 6451 | } |
michael@0 | 6452 | |
michael@0 | 6453 | WidgetPointerEvent event(touchEvent->mFlags.mIsTrusted, pointerMessage, touchEvent->widget); |
michael@0 | 6454 | event.isPrimary = i == 0; |
michael@0 | 6455 | event.pointerId = touch->Identifier(); |
michael@0 | 6456 | event.refPoint.x = touch->mRefPoint.x; |
michael@0 | 6457 | event.refPoint.y = touch->mRefPoint.y; |
michael@0 | 6458 | event.modifiers = touchEvent->modifiers; |
michael@0 | 6459 | event.width = touch->RadiusX(); |
michael@0 | 6460 | event.height = touch->RadiusY(); |
michael@0 | 6461 | event.tiltX = touch->tiltX; |
michael@0 | 6462 | event.tiltY = touch->tiltY; |
michael@0 | 6463 | event.time = touchEvent->time; |
michael@0 | 6464 | event.mFlags = touchEvent->mFlags; |
michael@0 | 6465 | event.button = WidgetMouseEvent::eLeftButton; |
michael@0 | 6466 | event.buttons = WidgetMouseEvent::eLeftButtonFlag; |
michael@0 | 6467 | event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; |
michael@0 | 6468 | event.convertToPointer = touch->convertToPointer = false; |
michael@0 | 6469 | aShell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus); |
michael@0 | 6470 | } |
michael@0 | 6471 | } |
michael@0 | 6472 | return NS_OK; |
michael@0 | 6473 | } |
michael@0 | 6474 | |
michael@0 | 6475 | class ReleasePointerCaptureCaller |
michael@0 | 6476 | { |
michael@0 | 6477 | public: |
michael@0 | 6478 | ReleasePointerCaptureCaller() : |
michael@0 | 6479 | mPointerId(0), |
michael@0 | 6480 | mContent(nullptr) |
michael@0 | 6481 | { |
michael@0 | 6482 | } |
michael@0 | 6483 | ~ReleasePointerCaptureCaller() |
michael@0 | 6484 | { |
michael@0 | 6485 | if (mContent) { |
michael@0 | 6486 | nsIPresShell::ReleasePointerCapturingContent(mPointerId, mContent); |
michael@0 | 6487 | } |
michael@0 | 6488 | } |
michael@0 | 6489 | void SetTarget(uint32_t aPointerId, nsIContent* aContent) |
michael@0 | 6490 | { |
michael@0 | 6491 | mPointerId = aPointerId; |
michael@0 | 6492 | mContent = aContent; |
michael@0 | 6493 | } |
michael@0 | 6494 | |
michael@0 | 6495 | private: |
michael@0 | 6496 | int32_t mPointerId; |
michael@0 | 6497 | nsCOMPtr<nsIContent> mContent; |
michael@0 | 6498 | }; |
michael@0 | 6499 | |
michael@0 | 6500 | nsresult |
michael@0 | 6501 | PresShell::HandleEvent(nsIFrame* aFrame, |
michael@0 | 6502 | WidgetGUIEvent* aEvent, |
michael@0 | 6503 | bool aDontRetargetEvents, |
michael@0 | 6504 | nsEventStatus* aEventStatus) |
michael@0 | 6505 | { |
michael@0 | 6506 | #ifdef MOZ_TASK_TRACER |
michael@0 | 6507 | // Make touch events, mouse events and hardware key events to be the source |
michael@0 | 6508 | // events of TaskTracer, and originate the rest correlation tasks from here. |
michael@0 | 6509 | SourceEventType type = SourceEventType::UNKNOWN; |
michael@0 | 6510 | if (WidgetTouchEvent* inputEvent = aEvent->AsTouchEvent()) { |
michael@0 | 6511 | type = SourceEventType::TOUCH; |
michael@0 | 6512 | } else if (WidgetMouseEvent* inputEvent = aEvent->AsMouseEvent()) { |
michael@0 | 6513 | type = SourceEventType::MOUSE; |
michael@0 | 6514 | } else if (WidgetKeyboardEvent* inputEvent = aEvent->AsKeyboardEvent()) { |
michael@0 | 6515 | type = SourceEventType::KEY; |
michael@0 | 6516 | } |
michael@0 | 6517 | AutoSourceEvent taskTracerEvent(type); |
michael@0 | 6518 | #endif |
michael@0 | 6519 | |
michael@0 | 6520 | if (sPointerEventEnabled) { |
michael@0 | 6521 | DispatchPointerFromMouseOrTouch(this, aFrame, aEvent, aDontRetargetEvents, aEventStatus); |
michael@0 | 6522 | } |
michael@0 | 6523 | |
michael@0 | 6524 | NS_ASSERTION(aFrame, "null frame"); |
michael@0 | 6525 | |
michael@0 | 6526 | if (mIsDestroying || |
michael@0 | 6527 | (sDisableNonTestMouseEvents && !aEvent->mFlags.mIsSynthesizedForTests && |
michael@0 | 6528 | aEvent->HasMouseEventMessage())) { |
michael@0 | 6529 | return NS_OK; |
michael@0 | 6530 | } |
michael@0 | 6531 | |
michael@0 | 6532 | RecordMouseLocation(aEvent); |
michael@0 | 6533 | if (sPointerEventEnabled) { |
michael@0 | 6534 | UpdateActivePointerState(aEvent); |
michael@0 | 6535 | } |
michael@0 | 6536 | |
michael@0 | 6537 | if (!nsContentUtils::IsSafeToRunScript()) |
michael@0 | 6538 | return NS_OK; |
michael@0 | 6539 | |
michael@0 | 6540 | nsIContent* capturingContent = |
michael@0 | 6541 | (aEvent->HasMouseEventMessage() || |
michael@0 | 6542 | aEvent->eventStructType == NS_WHEEL_EVENT ? GetCapturingContent() : |
michael@0 | 6543 | nullptr); |
michael@0 | 6544 | |
michael@0 | 6545 | nsCOMPtr<nsIDocument> retargetEventDoc; |
michael@0 | 6546 | if (!aDontRetargetEvents) { |
michael@0 | 6547 | // key and IME related events should not cross top level window boundary. |
michael@0 | 6548 | // Basically, such input events should be fired only on focused widget. |
michael@0 | 6549 | // However, some IMEs might need to clean up composition after focused |
michael@0 | 6550 | // window is deactivated. And also some tests on MozMill want to test key |
michael@0 | 6551 | // handling on deactivated window because MozMill window can be activated |
michael@0 | 6552 | // during tests. So, there is no merit the events should be redirected to |
michael@0 | 6553 | // active window. So, the events should be handled on the last focused |
michael@0 | 6554 | // content in the last focused DOM window in same top level window. |
michael@0 | 6555 | // Note, if no DOM window has been focused yet, we can discard the events. |
michael@0 | 6556 | if (aEvent->IsTargetedAtFocusedWindow()) { |
michael@0 | 6557 | nsCOMPtr<nsPIDOMWindow> window = GetFocusedDOMWindowInOurWindow(); |
michael@0 | 6558 | // No DOM window in same top level window has not been focused yet, |
michael@0 | 6559 | // discard the events. |
michael@0 | 6560 | if (!window) { |
michael@0 | 6561 | return NS_OK; |
michael@0 | 6562 | } |
michael@0 | 6563 | |
michael@0 | 6564 | retargetEventDoc = window->GetExtantDoc(); |
michael@0 | 6565 | if (!retargetEventDoc) |
michael@0 | 6566 | return NS_OK; |
michael@0 | 6567 | } else if (capturingContent) { |
michael@0 | 6568 | // if the mouse is being captured then retarget the mouse event at the |
michael@0 | 6569 | // document that is being captured. |
michael@0 | 6570 | retargetEventDoc = capturingContent->GetCurrentDoc(); |
michael@0 | 6571 | #ifdef ANDROID |
michael@0 | 6572 | } else if (aEvent->eventStructType == NS_TOUCH_EVENT) { |
michael@0 | 6573 | retargetEventDoc = GetTouchEventTargetDocument(); |
michael@0 | 6574 | #endif |
michael@0 | 6575 | } |
michael@0 | 6576 | |
michael@0 | 6577 | if (retargetEventDoc) { |
michael@0 | 6578 | nsCOMPtr<nsIPresShell> presShell = retargetEventDoc->GetShell(); |
michael@0 | 6579 | if (!presShell) |
michael@0 | 6580 | return NS_OK; |
michael@0 | 6581 | |
michael@0 | 6582 | if (presShell != this) { |
michael@0 | 6583 | nsIFrame* frame = presShell->GetRootFrame(); |
michael@0 | 6584 | if (!frame) { |
michael@0 | 6585 | if (aEvent->message == NS_QUERY_TEXT_CONTENT || |
michael@0 | 6586 | aEvent->IsContentCommandEvent()) { |
michael@0 | 6587 | return NS_OK; |
michael@0 | 6588 | } |
michael@0 | 6589 | |
michael@0 | 6590 | frame = GetNearestFrameContainingPresShell(presShell); |
michael@0 | 6591 | } |
michael@0 | 6592 | |
michael@0 | 6593 | if (!frame) |
michael@0 | 6594 | return NS_OK; |
michael@0 | 6595 | |
michael@0 | 6596 | nsCOMPtr<nsIPresShell> shell = frame->PresContext()->GetPresShell(); |
michael@0 | 6597 | return shell->HandleEvent(frame, aEvent, true, aEventStatus); |
michael@0 | 6598 | } |
michael@0 | 6599 | } |
michael@0 | 6600 | } |
michael@0 | 6601 | |
michael@0 | 6602 | if (aEvent->eventStructType == NS_KEY_EVENT && |
michael@0 | 6603 | mDocument && mDocument->EventHandlingSuppressed()) { |
michael@0 | 6604 | if (aEvent->message == NS_KEY_DOWN) { |
michael@0 | 6605 | mNoDelayedKeyEvents = true; |
michael@0 | 6606 | } else if (!mNoDelayedKeyEvents) { |
michael@0 | 6607 | DelayedEvent* event = new DelayedKeyEvent(aEvent->AsKeyboardEvent()); |
michael@0 | 6608 | if (!mDelayedEvents.AppendElement(event)) { |
michael@0 | 6609 | delete event; |
michael@0 | 6610 | } |
michael@0 | 6611 | } |
michael@0 | 6612 | return NS_OK; |
michael@0 | 6613 | } |
michael@0 | 6614 | |
michael@0 | 6615 | nsIFrame* frame = aFrame; |
michael@0 | 6616 | |
michael@0 | 6617 | if (aEvent->IsUsingCoordinates()) { |
michael@0 | 6618 | ReleasePointerCaptureCaller releasePointerCaptureCaller; |
michael@0 | 6619 | if (nsLayoutUtils::AreAsyncAnimationsEnabled() && mDocument) { |
michael@0 | 6620 | if (aEvent->eventStructType == NS_TOUCH_EVENT) { |
michael@0 | 6621 | nsIDocument::UnlockPointer(); |
michael@0 | 6622 | } |
michael@0 | 6623 | |
michael@0 | 6624 | { // scope for scriptBlocker. |
michael@0 | 6625 | nsAutoScriptBlocker scriptBlocker; |
michael@0 | 6626 | GetRootPresShell()->GetDocument()-> |
michael@0 | 6627 | EnumerateSubDocuments(FlushThrottledStyles, nullptr); |
michael@0 | 6628 | } |
michael@0 | 6629 | frame = GetNearestFrameContainingPresShell(this); |
michael@0 | 6630 | } |
michael@0 | 6631 | |
michael@0 | 6632 | NS_WARN_IF_FALSE(frame, "Nothing to handle this event!"); |
michael@0 | 6633 | if (!frame) |
michael@0 | 6634 | return NS_OK; |
michael@0 | 6635 | |
michael@0 | 6636 | nsPresContext* framePresContext = frame->PresContext(); |
michael@0 | 6637 | nsPresContext* rootPresContext = framePresContext->GetRootPresContext(); |
michael@0 | 6638 | NS_ASSERTION(rootPresContext == mPresContext->GetRootPresContext(), |
michael@0 | 6639 | "How did we end up outside the connected prescontext/viewmanager hierarchy?"); |
michael@0 | 6640 | // If we aren't starting our event dispatch from the root frame of the root prescontext, |
michael@0 | 6641 | // then someone must be capturing the mouse. In that case we don't want to search the popup |
michael@0 | 6642 | // list. |
michael@0 | 6643 | if (framePresContext == rootPresContext && |
michael@0 | 6644 | frame == mFrameConstructor->GetRootFrame()) { |
michael@0 | 6645 | nsIFrame* popupFrame = |
michael@0 | 6646 | nsLayoutUtils::GetPopupFrameForEventCoordinates(rootPresContext, aEvent); |
michael@0 | 6647 | // If the popupFrame is an ancestor of the 'frame', the frame should |
michael@0 | 6648 | // handle the event, otherwise, the popup should handle it. |
michael@0 | 6649 | if (popupFrame && |
michael@0 | 6650 | !nsContentUtils::ContentIsCrossDocDescendantOf( |
michael@0 | 6651 | framePresContext->GetPresShell()->GetDocument(), |
michael@0 | 6652 | popupFrame->GetContent())) { |
michael@0 | 6653 | frame = popupFrame; |
michael@0 | 6654 | } |
michael@0 | 6655 | } |
michael@0 | 6656 | |
michael@0 | 6657 | bool captureRetarget = false; |
michael@0 | 6658 | if (capturingContent) { |
michael@0 | 6659 | // If a capture is active, determine if the docshell is visible. If not, |
michael@0 | 6660 | // clear the capture and target the mouse event normally instead. This |
michael@0 | 6661 | // would occur if the mouse button is held down while a tab change occurs. |
michael@0 | 6662 | // If the docshell is visible, look for a scrolling container. |
michael@0 | 6663 | bool vis; |
michael@0 | 6664 | nsCOMPtr<nsIBaseWindow> baseWin = |
michael@0 | 6665 | do_QueryInterface(mPresContext->GetContainerWeak()); |
michael@0 | 6666 | if (baseWin && NS_SUCCEEDED(baseWin->GetVisibility(&vis)) && vis) { |
michael@0 | 6667 | captureRetarget = gCaptureInfo.mRetargetToElement; |
michael@0 | 6668 | if (!captureRetarget) { |
michael@0 | 6669 | // A check was already done above to ensure that capturingContent is |
michael@0 | 6670 | // in this presshell. |
michael@0 | 6671 | NS_ASSERTION(capturingContent->GetCurrentDoc() == GetDocument(), |
michael@0 | 6672 | "Unexpected document"); |
michael@0 | 6673 | nsIFrame* captureFrame = capturingContent->GetPrimaryFrame(); |
michael@0 | 6674 | if (captureFrame) { |
michael@0 | 6675 | if (capturingContent->Tag() == nsGkAtoms::select && |
michael@0 | 6676 | capturingContent->IsHTML()) { |
michael@0 | 6677 | // a dropdown <select> has a child in its selectPopupList and we should |
michael@0 | 6678 | // capture on that instead. |
michael@0 | 6679 | nsIFrame* childFrame = captureFrame->GetChildList(nsIFrame::kSelectPopupList).FirstChild(); |
michael@0 | 6680 | if (childFrame) { |
michael@0 | 6681 | captureFrame = childFrame; |
michael@0 | 6682 | } |
michael@0 | 6683 | } |
michael@0 | 6684 | |
michael@0 | 6685 | // scrollable frames should use the scrolling container as |
michael@0 | 6686 | // the root instead of the document |
michael@0 | 6687 | nsIScrollableFrame* scrollFrame = do_QueryFrame(captureFrame); |
michael@0 | 6688 | if (scrollFrame) { |
michael@0 | 6689 | frame = scrollFrame->GetScrolledFrame(); |
michael@0 | 6690 | } |
michael@0 | 6691 | } |
michael@0 | 6692 | } |
michael@0 | 6693 | } |
michael@0 | 6694 | else { |
michael@0 | 6695 | ClearMouseCapture(nullptr); |
michael@0 | 6696 | capturingContent = nullptr; |
michael@0 | 6697 | } |
michael@0 | 6698 | } |
michael@0 | 6699 | |
michael@0 | 6700 | // all touch events except for touchstart use a captured target |
michael@0 | 6701 | if (aEvent->eventStructType == NS_TOUCH_EVENT && |
michael@0 | 6702 | aEvent->message != NS_TOUCH_START) { |
michael@0 | 6703 | captureRetarget = true; |
michael@0 | 6704 | } |
michael@0 | 6705 | |
michael@0 | 6706 | WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); |
michael@0 | 6707 | bool isWindowLevelMouseExit = (aEvent->message == NS_MOUSE_EXIT) && |
michael@0 | 6708 | (mouseEvent && mouseEvent->exit == WidgetMouseEvent::eTopLevel); |
michael@0 | 6709 | |
michael@0 | 6710 | // Get the frame at the event point. However, don't do this if we're |
michael@0 | 6711 | // capturing and retargeting the event because the captured frame will |
michael@0 | 6712 | // be used instead below. Also keep using the root frame if we're dealing |
michael@0 | 6713 | // with a window-level mouse exit event since we want to start sending |
michael@0 | 6714 | // mouse out events at the root EventStateManager. |
michael@0 | 6715 | if (!captureRetarget && !isWindowLevelMouseExit) { |
michael@0 | 6716 | nsPoint eventPoint; |
michael@0 | 6717 | uint32_t flags = 0; |
michael@0 | 6718 | if (aEvent->message == NS_TOUCH_START) { |
michael@0 | 6719 | flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME; |
michael@0 | 6720 | WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent(); |
michael@0 | 6721 | // if this is a continuing session, ensure that all these events are |
michael@0 | 6722 | // in the same document by taking the target of the events already in |
michael@0 | 6723 | // the capture list |
michael@0 | 6724 | nsCOMPtr<nsIContent> anyTarget; |
michael@0 | 6725 | if (gCaptureTouchList->Count() > 0 && touchEvent->touches.Length() > 1) { |
michael@0 | 6726 | gCaptureTouchList->Enumerate(&FindAnyTarget, &anyTarget); |
michael@0 | 6727 | } else { |
michael@0 | 6728 | gPreventMouseEvents = false; |
michael@0 | 6729 | } |
michael@0 | 6730 | |
michael@0 | 6731 | for (int32_t i = touchEvent->touches.Length(); i; ) { |
michael@0 | 6732 | --i; |
michael@0 | 6733 | dom::Touch* touch = touchEvent->touches[i]; |
michael@0 | 6734 | |
michael@0 | 6735 | int32_t id = touch->Identifier(); |
michael@0 | 6736 | if (!gCaptureTouchList->Get(id, nullptr)) { |
michael@0 | 6737 | // find the target for this touch |
michael@0 | 6738 | eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, |
michael@0 | 6739 | touch->mRefPoint, |
michael@0 | 6740 | frame); |
michael@0 | 6741 | nsIFrame* target = FindFrameTargetedByInputEvent(aEvent, |
michael@0 | 6742 | frame, |
michael@0 | 6743 | eventPoint, |
michael@0 | 6744 | flags); |
michael@0 | 6745 | if (target && !anyTarget) { |
michael@0 | 6746 | target->GetContentForEvent(aEvent, getter_AddRefs(anyTarget)); |
michael@0 | 6747 | while (anyTarget && !anyTarget->IsElement()) { |
michael@0 | 6748 | anyTarget = anyTarget->GetParent(); |
michael@0 | 6749 | } |
michael@0 | 6750 | touch->SetTarget(anyTarget); |
michael@0 | 6751 | } else { |
michael@0 | 6752 | nsIFrame* newTargetFrame = nullptr; |
michael@0 | 6753 | for (nsIFrame* f = target; f; |
michael@0 | 6754 | f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) { |
michael@0 | 6755 | if (f->PresContext()->Document() == anyTarget->OwnerDoc()) { |
michael@0 | 6756 | newTargetFrame = f; |
michael@0 | 6757 | break; |
michael@0 | 6758 | } |
michael@0 | 6759 | // We must be in a subdocument so jump directly to the root frame. |
michael@0 | 6760 | // GetParentOrPlaceholderForCrossDoc gets called immediately to |
michael@0 | 6761 | // jump up to the containing document. |
michael@0 | 6762 | f = f->PresContext()->GetPresShell()->GetRootFrame(); |
michael@0 | 6763 | } |
michael@0 | 6764 | |
michael@0 | 6765 | // if we couldn't find a target frame in the same document as |
michael@0 | 6766 | // anyTarget, remove the touch from the capture touch list, as |
michael@0 | 6767 | // well as the event->touches array. touchmove events that aren't |
michael@0 | 6768 | // in the captured touch list will be discarded |
michael@0 | 6769 | if (!newTargetFrame) { |
michael@0 | 6770 | touchEvent->touches.RemoveElementAt(i); |
michael@0 | 6771 | } else { |
michael@0 | 6772 | target = newTargetFrame; |
michael@0 | 6773 | nsCOMPtr<nsIContent> targetContent; |
michael@0 | 6774 | target->GetContentForEvent(aEvent, getter_AddRefs(targetContent)); |
michael@0 | 6775 | while (targetContent && !targetContent->IsElement()) { |
michael@0 | 6776 | targetContent = targetContent->GetParent(); |
michael@0 | 6777 | } |
michael@0 | 6778 | touch->SetTarget(targetContent); |
michael@0 | 6779 | } |
michael@0 | 6780 | } |
michael@0 | 6781 | if (target) { |
michael@0 | 6782 | frame = target; |
michael@0 | 6783 | } |
michael@0 | 6784 | } else { |
michael@0 | 6785 | // This touch is an old touch, we need to ensure that is not |
michael@0 | 6786 | // marked as changed and set its target correctly |
michael@0 | 6787 | touch->mChanged = false; |
michael@0 | 6788 | int32_t id = touch->Identifier(); |
michael@0 | 6789 | |
michael@0 | 6790 | nsRefPtr<dom::Touch> oldTouch = gCaptureTouchList->GetWeak(id); |
michael@0 | 6791 | if (oldTouch) { |
michael@0 | 6792 | touch->SetTarget(oldTouch->mTarget); |
michael@0 | 6793 | } |
michael@0 | 6794 | } |
michael@0 | 6795 | } |
michael@0 | 6796 | } else { |
michael@0 | 6797 | eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame); |
michael@0 | 6798 | } |
michael@0 | 6799 | if (mouseEvent && mouseEvent->eventStructType == NS_MOUSE_EVENT && |
michael@0 | 6800 | mouseEvent->ignoreRootScrollFrame) { |
michael@0 | 6801 | flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME; |
michael@0 | 6802 | } |
michael@0 | 6803 | nsIFrame* target = |
michael@0 | 6804 | FindFrameTargetedByInputEvent(aEvent, frame, eventPoint, flags); |
michael@0 | 6805 | if (target) { |
michael@0 | 6806 | frame = target; |
michael@0 | 6807 | } |
michael@0 | 6808 | } |
michael@0 | 6809 | |
michael@0 | 6810 | // if a node is capturing the mouse, check if the event needs to be |
michael@0 | 6811 | // retargeted at the capturing content instead. This will be the case when |
michael@0 | 6812 | // capture retargeting is being used, no frame was found or the frame's |
michael@0 | 6813 | // content is not a descendant of the capturing content. |
michael@0 | 6814 | if (capturingContent && |
michael@0 | 6815 | (gCaptureInfo.mRetargetToElement || !frame->GetContent() || |
michael@0 | 6816 | !nsContentUtils::ContentIsCrossDocDescendantOf(frame->GetContent(), |
michael@0 | 6817 | capturingContent))) { |
michael@0 | 6818 | // A check was already done above to ensure that capturingContent is |
michael@0 | 6819 | // in this presshell. |
michael@0 | 6820 | NS_ASSERTION(capturingContent->GetCurrentDoc() == GetDocument(), |
michael@0 | 6821 | "Unexpected document"); |
michael@0 | 6822 | nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame(); |
michael@0 | 6823 | if (capturingFrame) { |
michael@0 | 6824 | frame = capturingFrame; |
michael@0 | 6825 | } |
michael@0 | 6826 | } |
michael@0 | 6827 | |
michael@0 | 6828 | if (aEvent->eventStructType == NS_POINTER_EVENT && |
michael@0 | 6829 | aEvent->message != NS_POINTER_DOWN) { |
michael@0 | 6830 | if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) { |
michael@0 | 6831 | uint32_t pointerId = pointerEvent->pointerId; |
michael@0 | 6832 | nsIContent* pointerCapturingContent = GetPointerCapturingContent(pointerId); |
michael@0 | 6833 | |
michael@0 | 6834 | if (pointerCapturingContent) { |
michael@0 | 6835 | if (nsIFrame* capturingFrame = pointerCapturingContent->GetPrimaryFrame()) { |
michael@0 | 6836 | frame = capturingFrame; |
michael@0 | 6837 | } |
michael@0 | 6838 | |
michael@0 | 6839 | if (pointerEvent->message == NS_POINTER_UP || |
michael@0 | 6840 | pointerEvent->message == NS_POINTER_CANCEL) { |
michael@0 | 6841 | // Implicitly releasing capture for given pointer. |
michael@0 | 6842 | // LOST_POINTER_CAPTURE should be send after NS_POINTER_UP or NS_POINTER_CANCEL. |
michael@0 | 6843 | releasePointerCaptureCaller.SetTarget(pointerId, pointerCapturingContent); |
michael@0 | 6844 | } |
michael@0 | 6845 | } |
michael@0 | 6846 | } |
michael@0 | 6847 | } |
michael@0 | 6848 | |
michael@0 | 6849 | // Suppress mouse event if it's being targeted at an element inside |
michael@0 | 6850 | // a document which needs events suppressed |
michael@0 | 6851 | if (aEvent->eventStructType == NS_MOUSE_EVENT && |
michael@0 | 6852 | frame->PresContext()->Document()->EventHandlingSuppressed()) { |
michael@0 | 6853 | if (aEvent->message == NS_MOUSE_BUTTON_DOWN) { |
michael@0 | 6854 | mNoDelayedMouseEvents = true; |
michael@0 | 6855 | } else if (!mNoDelayedMouseEvents && aEvent->message == NS_MOUSE_BUTTON_UP) { |
michael@0 | 6856 | DelayedEvent* event = new DelayedMouseEvent(aEvent->AsMouseEvent()); |
michael@0 | 6857 | if (!mDelayedEvents.AppendElement(event)) { |
michael@0 | 6858 | delete event; |
michael@0 | 6859 | } |
michael@0 | 6860 | } |
michael@0 | 6861 | |
michael@0 | 6862 | return NS_OK; |
michael@0 | 6863 | } |
michael@0 | 6864 | |
michael@0 | 6865 | PresShell* shell = |
michael@0 | 6866 | static_cast<PresShell*>(frame->PresContext()->PresShell()); |
michael@0 | 6867 | switch (aEvent->message) { |
michael@0 | 6868 | case NS_TOUCH_MOVE: |
michael@0 | 6869 | case NS_TOUCH_CANCEL: |
michael@0 | 6870 | case NS_TOUCH_END: { |
michael@0 | 6871 | // get the correct shell to dispatch to |
michael@0 | 6872 | WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent(); |
michael@0 | 6873 | nsTArray< nsRefPtr<dom::Touch> >& touches = touchEvent->touches; |
michael@0 | 6874 | for (uint32_t i = 0; i < touches.Length(); ++i) { |
michael@0 | 6875 | dom::Touch* touch = touches[i]; |
michael@0 | 6876 | if (!touch) { |
michael@0 | 6877 | break; |
michael@0 | 6878 | } |
michael@0 | 6879 | |
michael@0 | 6880 | nsRefPtr<dom::Touch> oldTouch = |
michael@0 | 6881 | gCaptureTouchList->GetWeak(touch->Identifier()); |
michael@0 | 6882 | if (!oldTouch) { |
michael@0 | 6883 | break; |
michael@0 | 6884 | } |
michael@0 | 6885 | |
michael@0 | 6886 | nsCOMPtr<nsIContent> content = |
michael@0 | 6887 | do_QueryInterface(oldTouch->Target()); |
michael@0 | 6888 | if (!content) { |
michael@0 | 6889 | break; |
michael@0 | 6890 | } |
michael@0 | 6891 | |
michael@0 | 6892 | nsIFrame* contentFrame = content->GetPrimaryFrame(); |
michael@0 | 6893 | if (!contentFrame) { |
michael@0 | 6894 | break; |
michael@0 | 6895 | } |
michael@0 | 6896 | |
michael@0 | 6897 | shell = static_cast<PresShell*>( |
michael@0 | 6898 | contentFrame->PresContext()->PresShell()); |
michael@0 | 6899 | if (shell) { |
michael@0 | 6900 | break; |
michael@0 | 6901 | } |
michael@0 | 6902 | } |
michael@0 | 6903 | break; |
michael@0 | 6904 | } |
michael@0 | 6905 | } |
michael@0 | 6906 | |
michael@0 | 6907 | // Check if we have an active EventStateManager which isn't the |
michael@0 | 6908 | // EventStateManager of the current PresContext. |
michael@0 | 6909 | // If that is the case, and mouse is over some ancestor document, |
michael@0 | 6910 | // forward event handling to the active document. |
michael@0 | 6911 | // This way content can get mouse events even when |
michael@0 | 6912 | // mouse is over the chrome or outside the window. |
michael@0 | 6913 | // |
michael@0 | 6914 | // Note, currently for backwards compatibility we don't forward mouse events |
michael@0 | 6915 | // to the active document when mouse is over some subdocument. |
michael@0 | 6916 | EventStateManager* activeESM = |
michael@0 | 6917 | EventStateManager::GetActiveEventStateManager(); |
michael@0 | 6918 | if (activeESM && aEvent->HasMouseEventMessage() && |
michael@0 | 6919 | activeESM != shell->GetPresContext()->EventStateManager() && |
michael@0 | 6920 | static_cast<EventStateManager*>(activeESM)->GetPresContext()) { |
michael@0 | 6921 | nsIPresShell* activeShell = |
michael@0 | 6922 | static_cast<EventStateManager*>(activeESM)->GetPresContext()-> |
michael@0 | 6923 | GetPresShell(); |
michael@0 | 6924 | if (activeShell && |
michael@0 | 6925 | nsContentUtils::ContentIsCrossDocDescendantOf(activeShell->GetDocument(), |
michael@0 | 6926 | shell->GetDocument())) { |
michael@0 | 6927 | shell = static_cast<PresShell*>(activeShell); |
michael@0 | 6928 | frame = shell->GetRootFrame(); |
michael@0 | 6929 | } |
michael@0 | 6930 | } |
michael@0 | 6931 | |
michael@0 | 6932 | if (shell != this) { |
michael@0 | 6933 | // Handle the event in the correct shell. |
michael@0 | 6934 | // Prevent deletion until we're done with event handling (bug 336582). |
michael@0 | 6935 | nsCOMPtr<nsIPresShell> kungFuDeathGrip(shell); |
michael@0 | 6936 | // We pass the subshell's root frame as the frame to start from. This is |
michael@0 | 6937 | // the only correct alternative; if the event was captured then it |
michael@0 | 6938 | // must have been captured by us or some ancestor shell and we |
michael@0 | 6939 | // now ask the subshell to dispatch it normally. |
michael@0 | 6940 | return shell->HandlePositionedEvent(frame, aEvent, aEventStatus); |
michael@0 | 6941 | } |
michael@0 | 6942 | |
michael@0 | 6943 | return HandlePositionedEvent(frame, aEvent, aEventStatus); |
michael@0 | 6944 | } |
michael@0 | 6945 | |
michael@0 | 6946 | nsresult rv = NS_OK; |
michael@0 | 6947 | |
michael@0 | 6948 | if (frame) { |
michael@0 | 6949 | PushCurrentEventInfo(nullptr, nullptr); |
michael@0 | 6950 | |
michael@0 | 6951 | // key and IME related events go to the focused frame in this DOM window. |
michael@0 | 6952 | if (aEvent->IsTargetedAtFocusedContent()) { |
michael@0 | 6953 | mCurrentEventContent = nullptr; |
michael@0 | 6954 | |
michael@0 | 6955 | nsCOMPtr<nsPIDOMWindow> window = |
michael@0 | 6956 | do_QueryInterface(mDocument->GetWindow()); |
michael@0 | 6957 | nsCOMPtr<nsPIDOMWindow> focusedWindow; |
michael@0 | 6958 | nsCOMPtr<nsIContent> eventTarget = |
michael@0 | 6959 | nsFocusManager::GetFocusedDescendant(window, false, |
michael@0 | 6960 | getter_AddRefs(focusedWindow)); |
michael@0 | 6961 | |
michael@0 | 6962 | // otherwise, if there is no focused content or the focused content has |
michael@0 | 6963 | // no frame, just use the root content. This ensures that key events |
michael@0 | 6964 | // still get sent to the window properly if nothing is focused or if a |
michael@0 | 6965 | // frame goes away while it is focused. |
michael@0 | 6966 | if (!eventTarget || !eventTarget->GetPrimaryFrame()) { |
michael@0 | 6967 | nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(mDocument); |
michael@0 | 6968 | if (htmlDoc) { |
michael@0 | 6969 | nsCOMPtr<nsIDOMHTMLElement> body; |
michael@0 | 6970 | htmlDoc->GetBody(getter_AddRefs(body)); |
michael@0 | 6971 | eventTarget = do_QueryInterface(body); |
michael@0 | 6972 | if (!eventTarget) { |
michael@0 | 6973 | eventTarget = mDocument->GetRootElement(); |
michael@0 | 6974 | } |
michael@0 | 6975 | } else { |
michael@0 | 6976 | eventTarget = mDocument->GetRootElement(); |
michael@0 | 6977 | } |
michael@0 | 6978 | } |
michael@0 | 6979 | |
michael@0 | 6980 | if (aEvent->message == NS_KEY_DOWN) { |
michael@0 | 6981 | NS_IF_RELEASE(gKeyDownTarget); |
michael@0 | 6982 | NS_IF_ADDREF(gKeyDownTarget = eventTarget); |
michael@0 | 6983 | } |
michael@0 | 6984 | else if ((aEvent->message == NS_KEY_PRESS || aEvent->message == NS_KEY_UP) && |
michael@0 | 6985 | gKeyDownTarget) { |
michael@0 | 6986 | // If a different element is now focused for the keypress/keyup event |
michael@0 | 6987 | // than what was focused during the keydown event, check if the new |
michael@0 | 6988 | // focused element is not in a chrome document any more, and if so, |
michael@0 | 6989 | // retarget the event back at the keydown target. This prevents a |
michael@0 | 6990 | // content area from grabbing the focus from chrome in-between key |
michael@0 | 6991 | // events. |
michael@0 | 6992 | if (eventTarget && |
michael@0 | 6993 | nsContentUtils::IsChromeDoc(gKeyDownTarget->GetCurrentDoc()) != |
michael@0 | 6994 | nsContentUtils::IsChromeDoc(eventTarget->GetCurrentDoc())) { |
michael@0 | 6995 | eventTarget = gKeyDownTarget; |
michael@0 | 6996 | } |
michael@0 | 6997 | |
michael@0 | 6998 | if (aEvent->message == NS_KEY_UP) { |
michael@0 | 6999 | NS_RELEASE(gKeyDownTarget); |
michael@0 | 7000 | } |
michael@0 | 7001 | } |
michael@0 | 7002 | |
michael@0 | 7003 | mCurrentEventFrame = nullptr; |
michael@0 | 7004 | nsIDocument* targetDoc = eventTarget ? eventTarget->OwnerDoc() : nullptr; |
michael@0 | 7005 | if (targetDoc && targetDoc != mDocument) { |
michael@0 | 7006 | PopCurrentEventInfo(); |
michael@0 | 7007 | nsCOMPtr<nsIPresShell> shell = targetDoc->GetShell(); |
michael@0 | 7008 | if (shell) { |
michael@0 | 7009 | rv = static_cast<PresShell*>(shell.get())-> |
michael@0 | 7010 | HandleRetargetedEvent(aEvent, aEventStatus, eventTarget); |
michael@0 | 7011 | } |
michael@0 | 7012 | return rv; |
michael@0 | 7013 | } else { |
michael@0 | 7014 | mCurrentEventContent = eventTarget; |
michael@0 | 7015 | } |
michael@0 | 7016 | |
michael@0 | 7017 | if (!GetCurrentEventContent() || !GetCurrentEventFrame() || |
michael@0 | 7018 | InZombieDocument(mCurrentEventContent)) { |
michael@0 | 7019 | rv = RetargetEventToParent(aEvent, aEventStatus); |
michael@0 | 7020 | PopCurrentEventInfo(); |
michael@0 | 7021 | return rv; |
michael@0 | 7022 | } |
michael@0 | 7023 | } else { |
michael@0 | 7024 | mCurrentEventFrame = frame; |
michael@0 | 7025 | } |
michael@0 | 7026 | if (GetCurrentEventFrame()) { |
michael@0 | 7027 | rv = HandleEventInternal(aEvent, aEventStatus); |
michael@0 | 7028 | } |
michael@0 | 7029 | |
michael@0 | 7030 | #ifdef DEBUG |
michael@0 | 7031 | ShowEventTargetDebug(); |
michael@0 | 7032 | #endif |
michael@0 | 7033 | PopCurrentEventInfo(); |
michael@0 | 7034 | } else { |
michael@0 | 7035 | // Activation events need to be dispatched even if no frame was found, since |
michael@0 | 7036 | // we don't want the focus to be out of sync. |
michael@0 | 7037 | |
michael@0 | 7038 | if (!NS_EVENT_NEEDS_FRAME(aEvent)) { |
michael@0 | 7039 | mCurrentEventFrame = nullptr; |
michael@0 | 7040 | return HandleEventInternal(aEvent, aEventStatus); |
michael@0 | 7041 | } |
michael@0 | 7042 | else if (aEvent->HasKeyEventMessage()) { |
michael@0 | 7043 | // Keypress events in new blank tabs should not be completely thrown away. |
michael@0 | 7044 | // Retarget them -- the parent chrome shell might make use of them. |
michael@0 | 7045 | return RetargetEventToParent(aEvent, aEventStatus); |
michael@0 | 7046 | } |
michael@0 | 7047 | } |
michael@0 | 7048 | |
michael@0 | 7049 | return rv; |
michael@0 | 7050 | } |
michael@0 | 7051 | |
michael@0 | 7052 | #ifdef ANDROID |
michael@0 | 7053 | nsIDocument* |
michael@0 | 7054 | PresShell::GetTouchEventTargetDocument() |
michael@0 | 7055 | { |
michael@0 | 7056 | nsPresContext* context = GetPresContext(); |
michael@0 | 7057 | if (!context || !context->IsRoot()) { |
michael@0 | 7058 | return nullptr; |
michael@0 | 7059 | } |
michael@0 | 7060 | |
michael@0 | 7061 | nsCOMPtr<nsIDocShellTreeItem> shellAsTreeItem = context->GetDocShell(); |
michael@0 | 7062 | if (!shellAsTreeItem) { |
michael@0 | 7063 | return nullptr; |
michael@0 | 7064 | } |
michael@0 | 7065 | |
michael@0 | 7066 | nsCOMPtr<nsIDocShellTreeOwner> owner; |
michael@0 | 7067 | shellAsTreeItem->GetTreeOwner(getter_AddRefs(owner)); |
michael@0 | 7068 | if (!owner) { |
michael@0 | 7069 | return nullptr; |
michael@0 | 7070 | } |
michael@0 | 7071 | |
michael@0 | 7072 | // now get the primary content shell (active tab) |
michael@0 | 7073 | nsCOMPtr<nsIDocShellTreeItem> item; |
michael@0 | 7074 | owner->GetPrimaryContentShell(getter_AddRefs(item)); |
michael@0 | 7075 | nsCOMPtr<nsIDocShell> childDocShell = do_QueryInterface(item); |
michael@0 | 7076 | nsCOMPtr<nsIDocument> result = do_GetInterface(childDocShell); |
michael@0 | 7077 | return result; |
michael@0 | 7078 | } |
michael@0 | 7079 | #endif |
michael@0 | 7080 | |
michael@0 | 7081 | #ifdef DEBUG |
michael@0 | 7082 | void |
michael@0 | 7083 | PresShell::ShowEventTargetDebug() |
michael@0 | 7084 | { |
michael@0 | 7085 | if (nsFrame::GetShowEventTargetFrameBorder() && |
michael@0 | 7086 | GetCurrentEventFrame()) { |
michael@0 | 7087 | if (mDrawEventTargetFrame) { |
michael@0 | 7088 | mDrawEventTargetFrame->InvalidateFrame(); |
michael@0 | 7089 | } |
michael@0 | 7090 | |
michael@0 | 7091 | mDrawEventTargetFrame = mCurrentEventFrame; |
michael@0 | 7092 | mDrawEventTargetFrame->InvalidateFrame(); |
michael@0 | 7093 | } |
michael@0 | 7094 | } |
michael@0 | 7095 | #endif |
michael@0 | 7096 | |
michael@0 | 7097 | nsresult |
michael@0 | 7098 | PresShell::HandlePositionedEvent(nsIFrame* aTargetFrame, |
michael@0 | 7099 | WidgetGUIEvent* aEvent, |
michael@0 | 7100 | nsEventStatus* aEventStatus) |
michael@0 | 7101 | { |
michael@0 | 7102 | nsresult rv = NS_OK; |
michael@0 | 7103 | |
michael@0 | 7104 | PushCurrentEventInfo(nullptr, nullptr); |
michael@0 | 7105 | |
michael@0 | 7106 | mCurrentEventFrame = aTargetFrame; |
michael@0 | 7107 | |
michael@0 | 7108 | if (mCurrentEventFrame) { |
michael@0 | 7109 | nsCOMPtr<nsIContent> targetElement; |
michael@0 | 7110 | mCurrentEventFrame->GetContentForEvent(aEvent, |
michael@0 | 7111 | getter_AddRefs(targetElement)); |
michael@0 | 7112 | |
michael@0 | 7113 | // If there is no content for this frame, target it anyway. Some |
michael@0 | 7114 | // frames can be targeted but do not have content, particularly |
michael@0 | 7115 | // windows with scrolling off. |
michael@0 | 7116 | if (targetElement) { |
michael@0 | 7117 | // Bug 103055, bug 185889: mouse events apply to *elements*, not all |
michael@0 | 7118 | // nodes. Thus we get the nearest element parent here. |
michael@0 | 7119 | // XXX we leave the frame the same even if we find an element |
michael@0 | 7120 | // parent, so that the text frame will receive the event (selection |
michael@0 | 7121 | // and friends are the ones who care about that anyway) |
michael@0 | 7122 | // |
michael@0 | 7123 | // We use weak pointers because during this tight loop, the node |
michael@0 | 7124 | // will *not* go away. And this happens on every mousemove. |
michael@0 | 7125 | while (targetElement && !targetElement->IsElement()) { |
michael@0 | 7126 | targetElement = targetElement->GetParent(); |
michael@0 | 7127 | } |
michael@0 | 7128 | |
michael@0 | 7129 | // If we found an element, target it. Otherwise, target *nothing*. |
michael@0 | 7130 | if (!targetElement) { |
michael@0 | 7131 | mCurrentEventContent = nullptr; |
michael@0 | 7132 | mCurrentEventFrame = nullptr; |
michael@0 | 7133 | } else if (targetElement != mCurrentEventContent) { |
michael@0 | 7134 | mCurrentEventContent = targetElement; |
michael@0 | 7135 | } |
michael@0 | 7136 | } |
michael@0 | 7137 | } |
michael@0 | 7138 | |
michael@0 | 7139 | if (GetCurrentEventFrame()) { |
michael@0 | 7140 | rv = HandleEventInternal(aEvent, aEventStatus); |
michael@0 | 7141 | } |
michael@0 | 7142 | |
michael@0 | 7143 | #ifdef DEBUG |
michael@0 | 7144 | ShowEventTargetDebug(); |
michael@0 | 7145 | #endif |
michael@0 | 7146 | PopCurrentEventInfo(); |
michael@0 | 7147 | return rv; |
michael@0 | 7148 | } |
michael@0 | 7149 | |
michael@0 | 7150 | nsresult |
michael@0 | 7151 | PresShell::HandleEventWithTarget(WidgetEvent* aEvent, nsIFrame* aFrame, |
michael@0 | 7152 | nsIContent* aContent, nsEventStatus* aStatus) |
michael@0 | 7153 | { |
michael@0 | 7154 | #if DEBUG |
michael@0 | 7155 | MOZ_ASSERT(!aFrame || aFrame->PresContext()->GetPresShell() == this, |
michael@0 | 7156 | "wrong shell"); |
michael@0 | 7157 | if (aContent) { |
michael@0 | 7158 | nsIDocument* doc = aContent->GetCurrentDoc(); |
michael@0 | 7159 | NS_ASSERTION(doc, "event for content that isn't in a document"); |
michael@0 | 7160 | NS_ASSERTION(!doc || doc->GetShell() == this, "wrong shell"); |
michael@0 | 7161 | } |
michael@0 | 7162 | #endif |
michael@0 | 7163 | NS_ENSURE_STATE(!aContent || aContent->GetCurrentDoc() == mDocument); |
michael@0 | 7164 | |
michael@0 | 7165 | PushCurrentEventInfo(aFrame, aContent); |
michael@0 | 7166 | nsresult rv = HandleEventInternal(aEvent, aStatus); |
michael@0 | 7167 | PopCurrentEventInfo(); |
michael@0 | 7168 | return rv; |
michael@0 | 7169 | } |
michael@0 | 7170 | |
michael@0 | 7171 | nsresult |
michael@0 | 7172 | PresShell::HandleEventInternal(WidgetEvent* aEvent, nsEventStatus* aStatus) |
michael@0 | 7173 | { |
michael@0 | 7174 | nsRefPtr<EventStateManager> manager = mPresContext->EventStateManager(); |
michael@0 | 7175 | nsresult rv = NS_OK; |
michael@0 | 7176 | |
michael@0 | 7177 | if (!NS_EVENT_NEEDS_FRAME(aEvent) || GetCurrentEventFrame()) { |
michael@0 | 7178 | bool touchIsNew = false; |
michael@0 | 7179 | bool isHandlingUserInput = false; |
michael@0 | 7180 | |
michael@0 | 7181 | // XXX How about IME events and input events for plugins? |
michael@0 | 7182 | if (aEvent->mFlags.mIsTrusted) { |
michael@0 | 7183 | switch (aEvent->message) { |
michael@0 | 7184 | case NS_KEY_PRESS: |
michael@0 | 7185 | case NS_KEY_DOWN: |
michael@0 | 7186 | case NS_KEY_UP: { |
michael@0 | 7187 | nsIDocument* doc = GetCurrentEventContent() ? |
michael@0 | 7188 | mCurrentEventContent->OwnerDoc() : nullptr; |
michael@0 | 7189 | nsIDocument* fullscreenAncestor = nullptr; |
michael@0 | 7190 | if (aEvent->AsKeyboardEvent()->keyCode == NS_VK_ESCAPE) { |
michael@0 | 7191 | if ((fullscreenAncestor = nsContentUtils::GetFullscreenAncestor(doc))) { |
michael@0 | 7192 | // Prevent default action on ESC key press when exiting |
michael@0 | 7193 | // DOM fullscreen mode. This prevents the browser ESC key |
michael@0 | 7194 | // handler from stopping all loads in the document, which |
michael@0 | 7195 | // would cause <video> loads to stop. |
michael@0 | 7196 | aEvent->mFlags.mDefaultPrevented = true; |
michael@0 | 7197 | aEvent->mFlags.mOnlyChromeDispatch = true; |
michael@0 | 7198 | |
michael@0 | 7199 | if (aEvent->message == NS_KEY_UP) { |
michael@0 | 7200 | // ESC key released while in DOM fullscreen mode. |
michael@0 | 7201 | // If fullscreen is running in content-only mode, exit the target |
michael@0 | 7202 | // doctree branch from fullscreen, otherwise fully exit all |
michael@0 | 7203 | // browser windows and documents from fullscreen mode. |
michael@0 | 7204 | // Note: in the content-only fullscreen case, we pass the |
michael@0 | 7205 | // fullscreenAncestor since |doc| may not actually be fullscreen |
michael@0 | 7206 | // here, and ExitFullscreen() has no affect when passed a |
michael@0 | 7207 | // non-fullscreen document. |
michael@0 | 7208 | nsIDocument::ExitFullscreen( |
michael@0 | 7209 | nsContentUtils::IsFullscreenApiContentOnly() ? fullscreenAncestor : nullptr, |
michael@0 | 7210 | /* async */ true); |
michael@0 | 7211 | } |
michael@0 | 7212 | } |
michael@0 | 7213 | nsCOMPtr<nsIDocument> pointerLockedDoc = |
michael@0 | 7214 | do_QueryReferent(EventStateManager::sPointerLockedDoc); |
michael@0 | 7215 | if (pointerLockedDoc) { |
michael@0 | 7216 | aEvent->mFlags.mDefaultPrevented = true; |
michael@0 | 7217 | aEvent->mFlags.mOnlyChromeDispatch = true; |
michael@0 | 7218 | if (aEvent->message == NS_KEY_UP) { |
michael@0 | 7219 | nsIDocument::UnlockPointer(); |
michael@0 | 7220 | } |
michael@0 | 7221 | } |
michael@0 | 7222 | } |
michael@0 | 7223 | // Else not full-screen mode or key code is unrestricted, fall |
michael@0 | 7224 | // through to normal handling. |
michael@0 | 7225 | } |
michael@0 | 7226 | case NS_MOUSE_BUTTON_DOWN: |
michael@0 | 7227 | case NS_MOUSE_BUTTON_UP: |
michael@0 | 7228 | isHandlingUserInput = true; |
michael@0 | 7229 | break; |
michael@0 | 7230 | case NS_TOUCH_START: { |
michael@0 | 7231 | WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent(); |
michael@0 | 7232 | // if there is only one touch in this touchstart event, assume that it is |
michael@0 | 7233 | // the start of a new touch session and evict any old touches in the |
michael@0 | 7234 | // queue |
michael@0 | 7235 | if (touchEvent->touches.Length() == 1) { |
michael@0 | 7236 | nsTArray< nsRefPtr<dom::Touch> > touches; |
michael@0 | 7237 | gCaptureTouchList->Enumerate(&AppendToTouchList, (void *)&touches); |
michael@0 | 7238 | for (uint32_t i = 0; i < touches.Length(); ++i) { |
michael@0 | 7239 | EvictTouchPoint(touches[i]); |
michael@0 | 7240 | } |
michael@0 | 7241 | } |
michael@0 | 7242 | // Add any new touches to the queue |
michael@0 | 7243 | for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) { |
michael@0 | 7244 | dom::Touch* touch = touchEvent->touches[i]; |
michael@0 | 7245 | int32_t id = touch->Identifier(); |
michael@0 | 7246 | if (!gCaptureTouchList->Get(id, nullptr)) { |
michael@0 | 7247 | // If it is not already in the queue, it is a new touch |
michael@0 | 7248 | touch->mChanged = true; |
michael@0 | 7249 | } |
michael@0 | 7250 | touch->mMessage = aEvent->message; |
michael@0 | 7251 | gCaptureTouchList->Put(id, touch); |
michael@0 | 7252 | } |
michael@0 | 7253 | break; |
michael@0 | 7254 | } |
michael@0 | 7255 | case NS_TOUCH_CANCEL: |
michael@0 | 7256 | case NS_TOUCH_END: { |
michael@0 | 7257 | // Remove the changed touches |
michael@0 | 7258 | // need to make sure we only remove touches that are ending here |
michael@0 | 7259 | WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent(); |
michael@0 | 7260 | nsTArray< nsRefPtr<dom::Touch> >& touches = touchEvent->touches; |
michael@0 | 7261 | for (uint32_t i = 0; i < touches.Length(); ++i) { |
michael@0 | 7262 | dom::Touch* touch = touches[i]; |
michael@0 | 7263 | if (!touch) { |
michael@0 | 7264 | continue; |
michael@0 | 7265 | } |
michael@0 | 7266 | touch->mMessage = aEvent->message; |
michael@0 | 7267 | touch->mChanged = true; |
michael@0 | 7268 | |
michael@0 | 7269 | int32_t id = touch->Identifier(); |
michael@0 | 7270 | nsRefPtr<dom::Touch> oldTouch = gCaptureTouchList->GetWeak(id); |
michael@0 | 7271 | if (!oldTouch) { |
michael@0 | 7272 | continue; |
michael@0 | 7273 | } |
michael@0 | 7274 | nsCOMPtr<EventTarget> targetPtr = oldTouch->mTarget; |
michael@0 | 7275 | |
michael@0 | 7276 | mCurrentEventContent = do_QueryInterface(targetPtr); |
michael@0 | 7277 | touch->SetTarget(targetPtr); |
michael@0 | 7278 | gCaptureTouchList->Remove(id); |
michael@0 | 7279 | } |
michael@0 | 7280 | // add any touches left in the touch list, but ensure changed=false |
michael@0 | 7281 | gCaptureTouchList->Enumerate(&AppendToTouchList, (void *)&touches); |
michael@0 | 7282 | break; |
michael@0 | 7283 | } |
michael@0 | 7284 | case NS_TOUCH_MOVE: { |
michael@0 | 7285 | // Check for touches that changed. Mark them add to queue |
michael@0 | 7286 | WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent(); |
michael@0 | 7287 | nsTArray< nsRefPtr<dom::Touch> >& touches = touchEvent->touches; |
michael@0 | 7288 | bool haveChanged = false; |
michael@0 | 7289 | for (int32_t i = touches.Length(); i; ) { |
michael@0 | 7290 | --i; |
michael@0 | 7291 | dom::Touch* touch = touches[i]; |
michael@0 | 7292 | if (!touch) { |
michael@0 | 7293 | continue; |
michael@0 | 7294 | } |
michael@0 | 7295 | int32_t id = touch->Identifier(); |
michael@0 | 7296 | touch->mMessage = aEvent->message; |
michael@0 | 7297 | |
michael@0 | 7298 | nsRefPtr<dom::Touch> oldTouch = gCaptureTouchList->GetWeak(id); |
michael@0 | 7299 | if (!oldTouch) { |
michael@0 | 7300 | touches.RemoveElementAt(i); |
michael@0 | 7301 | continue; |
michael@0 | 7302 | } |
michael@0 | 7303 | if (!touch->Equals(oldTouch)) { |
michael@0 | 7304 | touch->mChanged = true; |
michael@0 | 7305 | haveChanged = true; |
michael@0 | 7306 | } |
michael@0 | 7307 | |
michael@0 | 7308 | nsCOMPtr<dom::EventTarget> targetPtr = oldTouch->mTarget; |
michael@0 | 7309 | if (!targetPtr) { |
michael@0 | 7310 | touches.RemoveElementAt(i); |
michael@0 | 7311 | continue; |
michael@0 | 7312 | } |
michael@0 | 7313 | touch->SetTarget(targetPtr); |
michael@0 | 7314 | |
michael@0 | 7315 | gCaptureTouchList->Put(id, touch); |
michael@0 | 7316 | // if we're moving from touchstart to touchmove for this touch |
michael@0 | 7317 | // we allow preventDefault to prevent mouse events |
michael@0 | 7318 | if (oldTouch->mMessage != touch->mMessage) { |
michael@0 | 7319 | touchIsNew = true; |
michael@0 | 7320 | } |
michael@0 | 7321 | } |
michael@0 | 7322 | // is nothing has changed, we should just return |
michael@0 | 7323 | if (!haveChanged) { |
michael@0 | 7324 | if (gPreventMouseEvents) { |
michael@0 | 7325 | *aStatus = nsEventStatus_eConsumeNoDefault; |
michael@0 | 7326 | } |
michael@0 | 7327 | return NS_OK; |
michael@0 | 7328 | } |
michael@0 | 7329 | break; |
michael@0 | 7330 | } |
michael@0 | 7331 | case NS_DRAGDROP_DROP: |
michael@0 | 7332 | nsCOMPtr<nsIDragSession> session = nsContentUtils::GetDragSession(); |
michael@0 | 7333 | if (session) { |
michael@0 | 7334 | bool onlyChromeDrop = false; |
michael@0 | 7335 | session->GetOnlyChromeDrop(&onlyChromeDrop); |
michael@0 | 7336 | if (onlyChromeDrop) { |
michael@0 | 7337 | aEvent->mFlags.mOnlyChromeDispatch = true; |
michael@0 | 7338 | } |
michael@0 | 7339 | } |
michael@0 | 7340 | break; |
michael@0 | 7341 | } |
michael@0 | 7342 | } |
michael@0 | 7343 | |
michael@0 | 7344 | if (aEvent->message == NS_CONTEXTMENU) { |
michael@0 | 7345 | WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); |
michael@0 | 7346 | if (mouseEvent->context == WidgetMouseEvent::eContextMenuKey && |
michael@0 | 7347 | !AdjustContextMenuKeyEvent(mouseEvent)) { |
michael@0 | 7348 | return NS_OK; |
michael@0 | 7349 | } |
michael@0 | 7350 | if (mouseEvent->IsShift()) { |
michael@0 | 7351 | aEvent->mFlags.mOnlyChromeDispatch = true; |
michael@0 | 7352 | aEvent->mFlags.mRetargetToNonNativeAnonymous = true; |
michael@0 | 7353 | } |
michael@0 | 7354 | } |
michael@0 | 7355 | |
michael@0 | 7356 | AutoHandlingUserInputStatePusher userInpStatePusher(isHandlingUserInput, |
michael@0 | 7357 | aEvent, mDocument); |
michael@0 | 7358 | |
michael@0 | 7359 | if (aEvent->mFlags.mIsTrusted && aEvent->message == NS_MOUSE_MOVE) { |
michael@0 | 7360 | nsIPresShell::AllowMouseCapture( |
michael@0 | 7361 | EventStateManager::GetActiveEventStateManager() == manager); |
michael@0 | 7362 | } |
michael@0 | 7363 | |
michael@0 | 7364 | nsAutoPopupStatePusher popupStatePusher( |
michael@0 | 7365 | Event::GetEventPopupControlState(aEvent)); |
michael@0 | 7366 | |
michael@0 | 7367 | // FIXME. If the event was reused, we need to clear the old target, |
michael@0 | 7368 | // bug 329430 |
michael@0 | 7369 | aEvent->target = nullptr; |
michael@0 | 7370 | |
michael@0 | 7371 | // 1. Give event to event manager for pre event state changes and |
michael@0 | 7372 | // generation of synthetic events. |
michael@0 | 7373 | rv = manager->PreHandleEvent(mPresContext, aEvent, mCurrentEventFrame, aStatus); |
michael@0 | 7374 | |
michael@0 | 7375 | // 2. Give event to the DOM for third party and JS use. |
michael@0 | 7376 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 7377 | bool wasHandlingKeyBoardEvent = |
michael@0 | 7378 | nsContentUtils::IsHandlingKeyBoardEvent(); |
michael@0 | 7379 | if (aEvent->eventStructType == NS_KEY_EVENT) { |
michael@0 | 7380 | nsContentUtils::SetIsHandlingKeyBoardEvent(true); |
michael@0 | 7381 | } |
michael@0 | 7382 | if (aEvent->IsAllowedToDispatchDOMEvent()) { |
michael@0 | 7383 | nsPresShellEventCB eventCB(this); |
michael@0 | 7384 | if (aEvent->eventStructType == NS_TOUCH_EVENT) { |
michael@0 | 7385 | DispatchTouchEvent(aEvent, aStatus, &eventCB, touchIsNew); |
michael@0 | 7386 | } else { |
michael@0 | 7387 | nsCOMPtr<nsINode> eventTarget = mCurrentEventContent.get(); |
michael@0 | 7388 | nsPresShellEventCB* eventCBPtr = &eventCB; |
michael@0 | 7389 | if (!eventTarget) { |
michael@0 | 7390 | nsCOMPtr<nsIContent> targetContent; |
michael@0 | 7391 | if (mCurrentEventFrame) { |
michael@0 | 7392 | rv = mCurrentEventFrame-> |
michael@0 | 7393 | GetContentForEvent(aEvent, getter_AddRefs(targetContent)); |
michael@0 | 7394 | } |
michael@0 | 7395 | if (NS_SUCCEEDED(rv) && targetContent) { |
michael@0 | 7396 | eventTarget = do_QueryInterface(targetContent); |
michael@0 | 7397 | } else if (mDocument) { |
michael@0 | 7398 | eventTarget = do_QueryInterface(mDocument); |
michael@0 | 7399 | // If we don't have any content, the callback wouldn't probably |
michael@0 | 7400 | // do nothing. |
michael@0 | 7401 | eventCBPtr = nullptr; |
michael@0 | 7402 | } |
michael@0 | 7403 | } |
michael@0 | 7404 | if (eventTarget) { |
michael@0 | 7405 | if (aEvent->eventStructType == NS_COMPOSITION_EVENT || |
michael@0 | 7406 | aEvent->eventStructType == NS_TEXT_EVENT) { |
michael@0 | 7407 | IMEStateManager::DispatchCompositionEvent(eventTarget, |
michael@0 | 7408 | mPresContext, aEvent, aStatus, eventCBPtr); |
michael@0 | 7409 | } else { |
michael@0 | 7410 | EventDispatcher::Dispatch(eventTarget, mPresContext, |
michael@0 | 7411 | aEvent, nullptr, aStatus, eventCBPtr); |
michael@0 | 7412 | } |
michael@0 | 7413 | } |
michael@0 | 7414 | } |
michael@0 | 7415 | } |
michael@0 | 7416 | |
michael@0 | 7417 | nsContentUtils::SetIsHandlingKeyBoardEvent(wasHandlingKeyBoardEvent); |
michael@0 | 7418 | |
michael@0 | 7419 | // 3. Give event to event manager for post event state changes and |
michael@0 | 7420 | // generation of synthetic events. |
michael@0 | 7421 | if (!mIsDestroying && NS_SUCCEEDED(rv)) { |
michael@0 | 7422 | rv = manager->PostHandleEvent(mPresContext, aEvent, |
michael@0 | 7423 | GetCurrentEventFrame(), aStatus); |
michael@0 | 7424 | } |
michael@0 | 7425 | } |
michael@0 | 7426 | |
michael@0 | 7427 | if (aEvent->message == NS_MOUSE_BUTTON_UP) { |
michael@0 | 7428 | // reset the capturing content now that the mouse button is up |
michael@0 | 7429 | SetCapturingContent(nullptr, 0); |
michael@0 | 7430 | } else if (aEvent->message == NS_MOUSE_MOVE) { |
michael@0 | 7431 | nsIPresShell::AllowMouseCapture(false); |
michael@0 | 7432 | } |
michael@0 | 7433 | } |
michael@0 | 7434 | return rv; |
michael@0 | 7435 | } |
michael@0 | 7436 | |
michael@0 | 7437 | void |
michael@0 | 7438 | nsIPresShell::DispatchGotOrLostPointerCaptureEvent(bool aIsGotCapture, |
michael@0 | 7439 | uint32_t aPointerId, |
michael@0 | 7440 | nsIContent* aCaptureTarget) |
michael@0 | 7441 | { |
michael@0 | 7442 | PointerEventInit init; |
michael@0 | 7443 | init.mPointerId = aPointerId; |
michael@0 | 7444 | init.mBubbles = true; |
michael@0 | 7445 | nsRefPtr<mozilla::dom::PointerEvent> event; |
michael@0 | 7446 | event = PointerEvent::Constructor(aCaptureTarget, |
michael@0 | 7447 | aIsGotCapture |
michael@0 | 7448 | ? NS_LITERAL_STRING("gotpointercapture") |
michael@0 | 7449 | : NS_LITERAL_STRING("lostpointercapture"), |
michael@0 | 7450 | init); |
michael@0 | 7451 | if (event) { |
michael@0 | 7452 | bool dummy; |
michael@0 | 7453 | aCaptureTarget->DispatchEvent(event->InternalDOMEvent(), &dummy); |
michael@0 | 7454 | } |
michael@0 | 7455 | } |
michael@0 | 7456 | |
michael@0 | 7457 | void |
michael@0 | 7458 | PresShell::DispatchTouchEvent(WidgetEvent* aEvent, |
michael@0 | 7459 | nsEventStatus* aStatus, |
michael@0 | 7460 | nsPresShellEventCB* aEventCB, |
michael@0 | 7461 | bool aTouchIsNew) |
michael@0 | 7462 | { |
michael@0 | 7463 | // calling preventDefault on touchstart or the first touchmove for a |
michael@0 | 7464 | // point prevents mouse events |
michael@0 | 7465 | bool canPrevent = aEvent->message == NS_TOUCH_START || |
michael@0 | 7466 | (aEvent->message == NS_TOUCH_MOVE && aTouchIsNew); |
michael@0 | 7467 | bool preventDefault = false; |
michael@0 | 7468 | nsEventStatus tmpStatus = nsEventStatus_eIgnore; |
michael@0 | 7469 | WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent(); |
michael@0 | 7470 | |
michael@0 | 7471 | // loop over all touches and dispatch events on any that have changed |
michael@0 | 7472 | for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) { |
michael@0 | 7473 | dom::Touch* touch = touchEvent->touches[i]; |
michael@0 | 7474 | if (!touch || !touch->mChanged) { |
michael@0 | 7475 | continue; |
michael@0 | 7476 | } |
michael@0 | 7477 | |
michael@0 | 7478 | nsCOMPtr<EventTarget> targetPtr = touch->mTarget; |
michael@0 | 7479 | nsCOMPtr<nsIContent> content = do_QueryInterface(targetPtr); |
michael@0 | 7480 | if (!content) { |
michael@0 | 7481 | continue; |
michael@0 | 7482 | } |
michael@0 | 7483 | |
michael@0 | 7484 | nsIDocument* doc = content->OwnerDoc(); |
michael@0 | 7485 | nsIContent* capturingContent = GetCapturingContent(); |
michael@0 | 7486 | if (capturingContent) { |
michael@0 | 7487 | if (capturingContent->OwnerDoc() != doc) { |
michael@0 | 7488 | // Wrong document, don't dispatch anything. |
michael@0 | 7489 | continue; |
michael@0 | 7490 | } |
michael@0 | 7491 | content = capturingContent; |
michael@0 | 7492 | } |
michael@0 | 7493 | // copy the event |
michael@0 | 7494 | WidgetTouchEvent newEvent(touchEvent->mFlags.mIsTrusted, |
michael@0 | 7495 | touchEvent->message, touchEvent->widget); |
michael@0 | 7496 | newEvent.AssignTouchEventData(*touchEvent, false); |
michael@0 | 7497 | newEvent.target = targetPtr; |
michael@0 | 7498 | |
michael@0 | 7499 | nsRefPtr<PresShell> contentPresShell; |
michael@0 | 7500 | if (doc == mDocument) { |
michael@0 | 7501 | contentPresShell = static_cast<PresShell*>(doc->GetShell()); |
michael@0 | 7502 | if (contentPresShell) { |
michael@0 | 7503 | //XXXsmaug huge hack. Pushing possibly capturing content, |
michael@0 | 7504 | // even though event target is something else. |
michael@0 | 7505 | contentPresShell->PushCurrentEventInfo( |
michael@0 | 7506 | content->GetPrimaryFrame(), content); |
michael@0 | 7507 | } |
michael@0 | 7508 | } |
michael@0 | 7509 | |
michael@0 | 7510 | nsIPresShell *presShell = doc->GetShell(); |
michael@0 | 7511 | if (!presShell) { |
michael@0 | 7512 | continue; |
michael@0 | 7513 | } |
michael@0 | 7514 | |
michael@0 | 7515 | nsPresContext *context = presShell->GetPresContext(); |
michael@0 | 7516 | |
michael@0 | 7517 | tmpStatus = nsEventStatus_eIgnore; |
michael@0 | 7518 | EventDispatcher::Dispatch(targetPtr, context, |
michael@0 | 7519 | &newEvent, nullptr, &tmpStatus, aEventCB); |
michael@0 | 7520 | if (nsEventStatus_eConsumeNoDefault == tmpStatus || |
michael@0 | 7521 | newEvent.mFlags.mMultipleActionsPrevented) { |
michael@0 | 7522 | preventDefault = true; |
michael@0 | 7523 | } |
michael@0 | 7524 | |
michael@0 | 7525 | if (newEvent.mFlags.mMultipleActionsPrevented) { |
michael@0 | 7526 | touchEvent->mFlags.mMultipleActionsPrevented = true; |
michael@0 | 7527 | } |
michael@0 | 7528 | |
michael@0 | 7529 | if (contentPresShell) { |
michael@0 | 7530 | contentPresShell->PopCurrentEventInfo(); |
michael@0 | 7531 | } |
michael@0 | 7532 | } |
michael@0 | 7533 | |
michael@0 | 7534 | // if preventDefault was called on any of the events dispatched |
michael@0 | 7535 | // and this is touchstart, or the first touchmove, widget should consume |
michael@0 | 7536 | // other events that would be associated with this touch session |
michael@0 | 7537 | if (preventDefault && canPrevent) { |
michael@0 | 7538 | gPreventMouseEvents = true; |
michael@0 | 7539 | } |
michael@0 | 7540 | |
michael@0 | 7541 | if (gPreventMouseEvents) { |
michael@0 | 7542 | *aStatus = nsEventStatus_eConsumeNoDefault; |
michael@0 | 7543 | } else { |
michael@0 | 7544 | *aStatus = nsEventStatus_eIgnore; |
michael@0 | 7545 | } |
michael@0 | 7546 | } |
michael@0 | 7547 | |
michael@0 | 7548 | // Dispatch event to content only (NOT full processing) |
michael@0 | 7549 | // See also HandleEventWithTarget which does full event processing. |
michael@0 | 7550 | nsresult |
michael@0 | 7551 | PresShell::HandleDOMEventWithTarget(nsIContent* aTargetContent, |
michael@0 | 7552 | WidgetEvent* aEvent, |
michael@0 | 7553 | nsEventStatus* aStatus) |
michael@0 | 7554 | { |
michael@0 | 7555 | nsresult rv = NS_OK; |
michael@0 | 7556 | |
michael@0 | 7557 | PushCurrentEventInfo(nullptr, aTargetContent); |
michael@0 | 7558 | |
michael@0 | 7559 | // Bug 41013: Check if the event should be dispatched to content. |
michael@0 | 7560 | // It's possible that we are in the middle of destroying the window |
michael@0 | 7561 | // and the js context is out of date. This check detects the case |
michael@0 | 7562 | // that caused a crash in bug 41013, but there may be a better way |
michael@0 | 7563 | // to handle this situation! |
michael@0 | 7564 | nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak(); |
michael@0 | 7565 | if (container) { |
michael@0 | 7566 | |
michael@0 | 7567 | // Dispatch event to content |
michael@0 | 7568 | rv = EventDispatcher::Dispatch(aTargetContent, mPresContext, aEvent, |
michael@0 | 7569 | nullptr, aStatus); |
michael@0 | 7570 | } |
michael@0 | 7571 | |
michael@0 | 7572 | PopCurrentEventInfo(); |
michael@0 | 7573 | return rv; |
michael@0 | 7574 | } |
michael@0 | 7575 | |
michael@0 | 7576 | // See the method above. |
michael@0 | 7577 | nsresult |
michael@0 | 7578 | PresShell::HandleDOMEventWithTarget(nsIContent* aTargetContent, |
michael@0 | 7579 | nsIDOMEvent* aEvent, |
michael@0 | 7580 | nsEventStatus* aStatus) |
michael@0 | 7581 | { |
michael@0 | 7582 | nsresult rv = NS_OK; |
michael@0 | 7583 | |
michael@0 | 7584 | PushCurrentEventInfo(nullptr, aTargetContent); |
michael@0 | 7585 | nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak(); |
michael@0 | 7586 | if (container) { |
michael@0 | 7587 | rv = EventDispatcher::DispatchDOMEvent(aTargetContent, nullptr, aEvent, |
michael@0 | 7588 | mPresContext, aStatus); |
michael@0 | 7589 | } |
michael@0 | 7590 | |
michael@0 | 7591 | PopCurrentEventInfo(); |
michael@0 | 7592 | return rv; |
michael@0 | 7593 | } |
michael@0 | 7594 | |
michael@0 | 7595 | bool |
michael@0 | 7596 | PresShell::AdjustContextMenuKeyEvent(WidgetMouseEvent* aEvent) |
michael@0 | 7597 | { |
michael@0 | 7598 | #ifdef MOZ_XUL |
michael@0 | 7599 | // if a menu is open, open the context menu relative to the active item on the menu. |
michael@0 | 7600 | nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
michael@0 | 7601 | if (pm) { |
michael@0 | 7602 | nsIFrame* popupFrame = pm->GetTopPopup(ePopupTypeMenu); |
michael@0 | 7603 | if (popupFrame) { |
michael@0 | 7604 | nsIFrame* itemFrame = |
michael@0 | 7605 | (static_cast<nsMenuPopupFrame *>(popupFrame))->GetCurrentMenuItem(); |
michael@0 | 7606 | if (!itemFrame) |
michael@0 | 7607 | itemFrame = popupFrame; |
michael@0 | 7608 | |
michael@0 | 7609 | nsCOMPtr<nsIWidget> widget = popupFrame->GetNearestWidget(); |
michael@0 | 7610 | aEvent->widget = widget; |
michael@0 | 7611 | nsIntPoint widgetPoint = widget->WidgetToScreenOffset(); |
michael@0 | 7612 | aEvent->refPoint = LayoutDeviceIntPoint::FromUntyped( |
michael@0 | 7613 | itemFrame->GetScreenRect().BottomLeft() - widgetPoint); |
michael@0 | 7614 | |
michael@0 | 7615 | mCurrentEventContent = itemFrame->GetContent(); |
michael@0 | 7616 | mCurrentEventFrame = itemFrame; |
michael@0 | 7617 | |
michael@0 | 7618 | return true; |
michael@0 | 7619 | } |
michael@0 | 7620 | } |
michael@0 | 7621 | #endif |
michael@0 | 7622 | |
michael@0 | 7623 | // If we're here because of the key-equiv for showing context menus, we |
michael@0 | 7624 | // have to twiddle with the NS event to make sure the context menu comes |
michael@0 | 7625 | // up in the upper left of the relevant content area before we create |
michael@0 | 7626 | // the DOM event. Since we never call InitMouseEvent() on the event, |
michael@0 | 7627 | // the client X/Y will be 0,0. We can make use of that if the widget is null. |
michael@0 | 7628 | // Use the root view manager's widget since it's most likely to have one, |
michael@0 | 7629 | // and the coordinates returned by GetCurrentItemAndPositionForElement |
michael@0 | 7630 | // are relative to the widget of the root of the root view manager. |
michael@0 | 7631 | nsRootPresContext* rootPC = mPresContext->GetRootPresContext(); |
michael@0 | 7632 | aEvent->refPoint.x = 0; |
michael@0 | 7633 | aEvent->refPoint.y = 0; |
michael@0 | 7634 | if (rootPC) { |
michael@0 | 7635 | rootPC->PresShell()->GetViewManager()-> |
michael@0 | 7636 | GetRootWidget(getter_AddRefs(aEvent->widget)); |
michael@0 | 7637 | |
michael@0 | 7638 | if (aEvent->widget) { |
michael@0 | 7639 | // default the refpoint to the topleft of our document |
michael@0 | 7640 | nsPoint offset(0, 0); |
michael@0 | 7641 | nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); |
michael@0 | 7642 | if (rootFrame) { |
michael@0 | 7643 | nsView* view = rootFrame->GetClosestView(&offset); |
michael@0 | 7644 | offset += view->GetOffsetToWidget(aEvent->widget); |
michael@0 | 7645 | aEvent->refPoint = |
michael@0 | 7646 | LayoutDeviceIntPoint::FromAppUnitsToNearest(offset, mPresContext->AppUnitsPerDevPixel()); |
michael@0 | 7647 | } |
michael@0 | 7648 | } |
michael@0 | 7649 | } else { |
michael@0 | 7650 | aEvent->widget = nullptr; |
michael@0 | 7651 | } |
michael@0 | 7652 | |
michael@0 | 7653 | // see if we should use the caret position for the popup |
michael@0 | 7654 | nsIntPoint caretPoint; |
michael@0 | 7655 | // Beware! This may flush notifications via synchronous |
michael@0 | 7656 | // ScrollSelectionIntoView. |
michael@0 | 7657 | if (PrepareToUseCaretPosition(aEvent->widget, caretPoint)) { |
michael@0 | 7658 | // caret position is good |
michael@0 | 7659 | aEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(caretPoint); |
michael@0 | 7660 | return true; |
michael@0 | 7661 | } |
michael@0 | 7662 | |
michael@0 | 7663 | // If we're here because of the key-equiv for showing context menus, we |
michael@0 | 7664 | // have to reset the event target to the currently focused element. Get it |
michael@0 | 7665 | // from the focus controller. |
michael@0 | 7666 | nsCOMPtr<nsIDOMElement> currentFocus; |
michael@0 | 7667 | nsIFocusManager* fm = nsFocusManager::GetFocusManager(); |
michael@0 | 7668 | if (fm) |
michael@0 | 7669 | fm->GetFocusedElement(getter_AddRefs(currentFocus)); |
michael@0 | 7670 | |
michael@0 | 7671 | // Reset event coordinates relative to focused frame in view |
michael@0 | 7672 | if (currentFocus) { |
michael@0 | 7673 | nsCOMPtr<nsIContent> currentPointElement; |
michael@0 | 7674 | GetCurrentItemAndPositionForElement(currentFocus, |
michael@0 | 7675 | getter_AddRefs(currentPointElement), |
michael@0 | 7676 | aEvent->refPoint, |
michael@0 | 7677 | aEvent->widget); |
michael@0 | 7678 | if (currentPointElement) { |
michael@0 | 7679 | mCurrentEventContent = currentPointElement; |
michael@0 | 7680 | mCurrentEventFrame = nullptr; |
michael@0 | 7681 | GetCurrentEventFrame(); |
michael@0 | 7682 | } |
michael@0 | 7683 | } |
michael@0 | 7684 | |
michael@0 | 7685 | return true; |
michael@0 | 7686 | } |
michael@0 | 7687 | |
michael@0 | 7688 | // PresShell::PrepareToUseCaretPosition |
michael@0 | 7689 | // |
michael@0 | 7690 | // This checks to see if we should use the caret position for popup context |
michael@0 | 7691 | // menus. Returns true if the caret position should be used, and the |
michael@0 | 7692 | // coordinates of that position is returned in aTargetPt. This function |
michael@0 | 7693 | // will also scroll the window as needed to make the caret visible. |
michael@0 | 7694 | // |
michael@0 | 7695 | // The event widget should be the widget that generated the event, and |
michael@0 | 7696 | // whose coordinate system the resulting event's refPoint should be |
michael@0 | 7697 | // relative to. The returned point is in device pixels realtive to the |
michael@0 | 7698 | // widget passed in. |
michael@0 | 7699 | bool |
michael@0 | 7700 | PresShell::PrepareToUseCaretPosition(nsIWidget* aEventWidget, nsIntPoint& aTargetPt) |
michael@0 | 7701 | { |
michael@0 | 7702 | nsresult rv; |
michael@0 | 7703 | |
michael@0 | 7704 | // check caret visibility |
michael@0 | 7705 | nsRefPtr<nsCaret> caret = GetCaret(); |
michael@0 | 7706 | NS_ENSURE_TRUE(caret, false); |
michael@0 | 7707 | |
michael@0 | 7708 | bool caretVisible = false; |
michael@0 | 7709 | rv = caret->GetCaretVisible(&caretVisible); |
michael@0 | 7710 | if (NS_FAILED(rv) || ! caretVisible) |
michael@0 | 7711 | return false; |
michael@0 | 7712 | |
michael@0 | 7713 | // caret selection, this is a temporary weak reference, so no refcounting is |
michael@0 | 7714 | // needed |
michael@0 | 7715 | nsISelection* domSelection = caret->GetCaretDOMSelection(); |
michael@0 | 7716 | NS_ENSURE_TRUE(domSelection, false); |
michael@0 | 7717 | |
michael@0 | 7718 | // since the match could be an anonymous textnode inside a |
michael@0 | 7719 | // <textarea> or text <input>, we need to get the outer frame |
michael@0 | 7720 | // note: frames are not refcounted |
michael@0 | 7721 | nsIFrame* frame = nullptr; // may be nullptr |
michael@0 | 7722 | nsCOMPtr<nsIDOMNode> node; |
michael@0 | 7723 | rv = domSelection->GetFocusNode(getter_AddRefs(node)); |
michael@0 | 7724 | NS_ENSURE_SUCCESS(rv, false); |
michael@0 | 7725 | NS_ENSURE_TRUE(node, false); |
michael@0 | 7726 | nsCOMPtr<nsIContent> content(do_QueryInterface(node)); |
michael@0 | 7727 | if (content) { |
michael@0 | 7728 | nsIContent* nonNative = content->FindFirstNonChromeOnlyAccessContent(); |
michael@0 | 7729 | content = nonNative; |
michael@0 | 7730 | } |
michael@0 | 7731 | |
michael@0 | 7732 | if (content) { |
michael@0 | 7733 | // It seems like ScrollSelectionIntoView should be enough, but it's |
michael@0 | 7734 | // not. The problem is that scrolling the selection into view when it is |
michael@0 | 7735 | // below the current viewport will align the top line of the frame exactly |
michael@0 | 7736 | // with the bottom of the window. This is fine, BUT, the popup event causes |
michael@0 | 7737 | // the control to be re-focused which does this exact call to |
michael@0 | 7738 | // ScrollContentIntoView, which has a one-pixel disagreement of whether the |
michael@0 | 7739 | // frame is actually in view. The result is that the frame is aligned with |
michael@0 | 7740 | // the top of the window, but the menu is still at the bottom. |
michael@0 | 7741 | // |
michael@0 | 7742 | // Doing this call first forces the frame to be in view, eliminating the |
michael@0 | 7743 | // problem. The only difference in the result is that if your cursor is in |
michael@0 | 7744 | // an edit box below the current view, you'll get the edit box aligned with |
michael@0 | 7745 | // the top of the window. This is arguably better behavior anyway. |
michael@0 | 7746 | rv = ScrollContentIntoView(content, |
michael@0 | 7747 | nsIPresShell::ScrollAxis( |
michael@0 | 7748 | nsIPresShell::SCROLL_MINIMUM, |
michael@0 | 7749 | nsIPresShell::SCROLL_IF_NOT_VISIBLE), |
michael@0 | 7750 | nsIPresShell::ScrollAxis( |
michael@0 | 7751 | nsIPresShell::SCROLL_MINIMUM, |
michael@0 | 7752 | nsIPresShell::SCROLL_IF_NOT_VISIBLE), |
michael@0 | 7753 | nsIPresShell::SCROLL_OVERFLOW_HIDDEN); |
michael@0 | 7754 | NS_ENSURE_SUCCESS(rv, false); |
michael@0 | 7755 | frame = content->GetPrimaryFrame(); |
michael@0 | 7756 | NS_WARN_IF_FALSE(frame, "No frame for focused content?"); |
michael@0 | 7757 | } |
michael@0 | 7758 | |
michael@0 | 7759 | // Actually scroll the selection (ie caret) into view. Note that this must |
michael@0 | 7760 | // be synchronous since we will be checking the caret position on the screen. |
michael@0 | 7761 | // |
michael@0 | 7762 | // Be easy about errors, and just don't scroll in those cases. Better to have |
michael@0 | 7763 | // the correct menu at a weird place than the wrong menu. |
michael@0 | 7764 | // After ScrollSelectionIntoView(), the pending notifications might be |
michael@0 | 7765 | // flushed and PresShell/PresContext/Frames may be dead. See bug 418470. |
michael@0 | 7766 | nsCOMPtr<nsISelectionController> selCon; |
michael@0 | 7767 | if (frame) |
michael@0 | 7768 | frame->GetSelectionController(GetPresContext(), getter_AddRefs(selCon)); |
michael@0 | 7769 | else |
michael@0 | 7770 | selCon = static_cast<nsISelectionController *>(this); |
michael@0 | 7771 | if (selCon) { |
michael@0 | 7772 | rv = selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, |
michael@0 | 7773 | nsISelectionController::SELECTION_FOCUS_REGION, |
michael@0 | 7774 | nsISelectionController::SCROLL_SYNCHRONOUS); |
michael@0 | 7775 | NS_ENSURE_SUCCESS(rv, false); |
michael@0 | 7776 | } |
michael@0 | 7777 | |
michael@0 | 7778 | nsPresContext* presContext = GetPresContext(); |
michael@0 | 7779 | |
michael@0 | 7780 | // get caret position relative to the closest view |
michael@0 | 7781 | nsRect caretCoords; |
michael@0 | 7782 | nsIFrame* caretFrame = caret->GetGeometry(domSelection, &caretCoords); |
michael@0 | 7783 | if (!caretFrame) |
michael@0 | 7784 | return false; |
michael@0 | 7785 | nsPoint viewOffset; |
michael@0 | 7786 | nsView* view = caretFrame->GetClosestView(&viewOffset); |
michael@0 | 7787 | if (!view) |
michael@0 | 7788 | return false; |
michael@0 | 7789 | // and then get the caret coords relative to the event widget |
michael@0 | 7790 | if (aEventWidget) { |
michael@0 | 7791 | viewOffset += view->GetOffsetToWidget(aEventWidget); |
michael@0 | 7792 | } |
michael@0 | 7793 | caretCoords.MoveBy(viewOffset); |
michael@0 | 7794 | |
michael@0 | 7795 | // caret coordinates are in app units, convert to pixels |
michael@0 | 7796 | aTargetPt.x = |
michael@0 | 7797 | presContext->AppUnitsToDevPixels(caretCoords.x + caretCoords.width); |
michael@0 | 7798 | aTargetPt.y = |
michael@0 | 7799 | presContext->AppUnitsToDevPixels(caretCoords.y + caretCoords.height); |
michael@0 | 7800 | |
michael@0 | 7801 | // make sure rounding doesn't return a pixel which is outside the caret |
michael@0 | 7802 | // (e.g. one line lower) |
michael@0 | 7803 | aTargetPt.y -= 1; |
michael@0 | 7804 | |
michael@0 | 7805 | return true; |
michael@0 | 7806 | } |
michael@0 | 7807 | |
michael@0 | 7808 | void |
michael@0 | 7809 | PresShell::GetCurrentItemAndPositionForElement(nsIDOMElement *aCurrentEl, |
michael@0 | 7810 | nsIContent** aTargetToUse, |
michael@0 | 7811 | LayoutDeviceIntPoint& aTargetPt, |
michael@0 | 7812 | nsIWidget *aRootWidget) |
michael@0 | 7813 | { |
michael@0 | 7814 | nsCOMPtr<nsIContent> focusedContent(do_QueryInterface(aCurrentEl)); |
michael@0 | 7815 | ScrollContentIntoView(focusedContent, |
michael@0 | 7816 | ScrollAxis(), |
michael@0 | 7817 | ScrollAxis(), |
michael@0 | 7818 | nsIPresShell::SCROLL_OVERFLOW_HIDDEN); |
michael@0 | 7819 | |
michael@0 | 7820 | nsPresContext* presContext = GetPresContext(); |
michael@0 | 7821 | |
michael@0 | 7822 | bool istree = false, checkLineHeight = true; |
michael@0 | 7823 | nscoord extraTreeY = 0; |
michael@0 | 7824 | |
michael@0 | 7825 | #ifdef MOZ_XUL |
michael@0 | 7826 | // Set the position to just underneath the current item for multi-select |
michael@0 | 7827 | // lists or just underneath the selected item for single-select lists. If |
michael@0 | 7828 | // the element is not a list, or there is no selection, leave the position |
michael@0 | 7829 | // as is. |
michael@0 | 7830 | nsCOMPtr<nsIDOMXULSelectControlItemElement> item; |
michael@0 | 7831 | nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect = |
michael@0 | 7832 | do_QueryInterface(aCurrentEl); |
michael@0 | 7833 | if (multiSelect) { |
michael@0 | 7834 | checkLineHeight = false; |
michael@0 | 7835 | |
michael@0 | 7836 | int32_t currentIndex; |
michael@0 | 7837 | multiSelect->GetCurrentIndex(¤tIndex); |
michael@0 | 7838 | if (currentIndex >= 0) { |
michael@0 | 7839 | nsCOMPtr<nsIDOMXULElement> xulElement(do_QueryInterface(aCurrentEl)); |
michael@0 | 7840 | if (xulElement) { |
michael@0 | 7841 | nsCOMPtr<nsIBoxObject> box; |
michael@0 | 7842 | xulElement->GetBoxObject(getter_AddRefs(box)); |
michael@0 | 7843 | nsCOMPtr<nsITreeBoxObject> treeBox(do_QueryInterface(box)); |
michael@0 | 7844 | // Tree view special case (tree items have no frames) |
michael@0 | 7845 | // Get the focused row and add its coordinates, which are already in pixels |
michael@0 | 7846 | // XXX Boris, should we create a new interface so that this doesn't |
michael@0 | 7847 | // need to know about trees? Something like nsINodelessChildCreator which |
michael@0 | 7848 | // could provide the current focus coordinates? |
michael@0 | 7849 | if (treeBox) { |
michael@0 | 7850 | treeBox->EnsureRowIsVisible(currentIndex); |
michael@0 | 7851 | int32_t firstVisibleRow, rowHeight; |
michael@0 | 7852 | treeBox->GetFirstVisibleRow(&firstVisibleRow); |
michael@0 | 7853 | treeBox->GetRowHeight(&rowHeight); |
michael@0 | 7854 | |
michael@0 | 7855 | extraTreeY += presContext->CSSPixelsToAppUnits( |
michael@0 | 7856 | (currentIndex - firstVisibleRow + 1) * rowHeight); |
michael@0 | 7857 | istree = true; |
michael@0 | 7858 | |
michael@0 | 7859 | nsCOMPtr<nsITreeColumns> cols; |
michael@0 | 7860 | treeBox->GetColumns(getter_AddRefs(cols)); |
michael@0 | 7861 | if (cols) { |
michael@0 | 7862 | nsCOMPtr<nsITreeColumn> col; |
michael@0 | 7863 | cols->GetFirstColumn(getter_AddRefs(col)); |
michael@0 | 7864 | if (col) { |
michael@0 | 7865 | nsCOMPtr<nsIDOMElement> colElement; |
michael@0 | 7866 | col->GetElement(getter_AddRefs(colElement)); |
michael@0 | 7867 | nsCOMPtr<nsIContent> colContent(do_QueryInterface(colElement)); |
michael@0 | 7868 | if (colContent) { |
michael@0 | 7869 | nsIFrame* frame = colContent->GetPrimaryFrame(); |
michael@0 | 7870 | if (frame) { |
michael@0 | 7871 | extraTreeY += frame->GetSize().height; |
michael@0 | 7872 | } |
michael@0 | 7873 | } |
michael@0 | 7874 | } |
michael@0 | 7875 | } |
michael@0 | 7876 | } |
michael@0 | 7877 | else { |
michael@0 | 7878 | multiSelect->GetCurrentItem(getter_AddRefs(item)); |
michael@0 | 7879 | } |
michael@0 | 7880 | } |
michael@0 | 7881 | } |
michael@0 | 7882 | } |
michael@0 | 7883 | else { |
michael@0 | 7884 | // don't check menulists as the selected item will be inside a popup. |
michael@0 | 7885 | nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(aCurrentEl); |
michael@0 | 7886 | if (!menulist) { |
michael@0 | 7887 | nsCOMPtr<nsIDOMXULSelectControlElement> select = |
michael@0 | 7888 | do_QueryInterface(aCurrentEl); |
michael@0 | 7889 | if (select) { |
michael@0 | 7890 | checkLineHeight = false; |
michael@0 | 7891 | select->GetSelectedItem(getter_AddRefs(item)); |
michael@0 | 7892 | } |
michael@0 | 7893 | } |
michael@0 | 7894 | } |
michael@0 | 7895 | |
michael@0 | 7896 | if (item) |
michael@0 | 7897 | focusedContent = do_QueryInterface(item); |
michael@0 | 7898 | #endif |
michael@0 | 7899 | |
michael@0 | 7900 | nsIFrame *frame = focusedContent->GetPrimaryFrame(); |
michael@0 | 7901 | if (frame) { |
michael@0 | 7902 | NS_ASSERTION(frame->PresContext() == GetPresContext(), |
michael@0 | 7903 | "handling event for focused content that is not in our document?"); |
michael@0 | 7904 | |
michael@0 | 7905 | nsPoint frameOrigin(0, 0); |
michael@0 | 7906 | |
michael@0 | 7907 | // Get the frame's origin within its view |
michael@0 | 7908 | nsView *view = frame->GetClosestView(&frameOrigin); |
michael@0 | 7909 | NS_ASSERTION(view, "No view for frame"); |
michael@0 | 7910 | |
michael@0 | 7911 | // View's origin relative the widget |
michael@0 | 7912 | if (aRootWidget) { |
michael@0 | 7913 | frameOrigin += view->GetOffsetToWidget(aRootWidget); |
michael@0 | 7914 | } |
michael@0 | 7915 | |
michael@0 | 7916 | // Start context menu down and to the right from top left of frame |
michael@0 | 7917 | // use the lineheight. This is a good distance to move the context |
michael@0 | 7918 | // menu away from the top left corner of the frame. If we always |
michael@0 | 7919 | // used the frame height, the context menu could end up far away, |
michael@0 | 7920 | // for example when we're focused on linked images. |
michael@0 | 7921 | // On the other hand, we want to use the frame height if it's less |
michael@0 | 7922 | // than the current line height, so that the context menu appears |
michael@0 | 7923 | // associated with the correct frame. |
michael@0 | 7924 | nscoord extra = 0; |
michael@0 | 7925 | if (!istree) { |
michael@0 | 7926 | extra = frame->GetSize().height; |
michael@0 | 7927 | if (checkLineHeight) { |
michael@0 | 7928 | nsIScrollableFrame *scrollFrame = |
michael@0 | 7929 | nsLayoutUtils::GetNearestScrollableFrame(frame); |
michael@0 | 7930 | if (scrollFrame) { |
michael@0 | 7931 | nsSize scrollAmount = scrollFrame->GetLineScrollAmount(); |
michael@0 | 7932 | nsIFrame* f = do_QueryFrame(scrollFrame); |
michael@0 | 7933 | int32_t APD = presContext->AppUnitsPerDevPixel(); |
michael@0 | 7934 | int32_t scrollAPD = f->PresContext()->AppUnitsPerDevPixel(); |
michael@0 | 7935 | scrollAmount = scrollAmount.ConvertAppUnits(scrollAPD, APD); |
michael@0 | 7936 | if (extra > scrollAmount.height) { |
michael@0 | 7937 | extra = scrollAmount.height; |
michael@0 | 7938 | } |
michael@0 | 7939 | } |
michael@0 | 7940 | } |
michael@0 | 7941 | } |
michael@0 | 7942 | |
michael@0 | 7943 | aTargetPt.x = presContext->AppUnitsToDevPixels(frameOrigin.x); |
michael@0 | 7944 | aTargetPt.y = presContext->AppUnitsToDevPixels( |
michael@0 | 7945 | frameOrigin.y + extra + extraTreeY); |
michael@0 | 7946 | } |
michael@0 | 7947 | |
michael@0 | 7948 | NS_IF_ADDREF(*aTargetToUse = focusedContent); |
michael@0 | 7949 | } |
michael@0 | 7950 | |
michael@0 | 7951 | bool |
michael@0 | 7952 | PresShell::ShouldIgnoreInvalidation() |
michael@0 | 7953 | { |
michael@0 | 7954 | return mPaintingSuppressed || !mIsActive || mIsNeverPainting; |
michael@0 | 7955 | } |
michael@0 | 7956 | |
michael@0 | 7957 | void |
michael@0 | 7958 | PresShell::WillPaint() |
michael@0 | 7959 | { |
michael@0 | 7960 | nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext(); |
michael@0 | 7961 | if (!rootPresContext) { |
michael@0 | 7962 | // In some edge cases, such as when we don't have a root frame yet, |
michael@0 | 7963 | // we can't find the root prescontext. There's nothing to do in that |
michael@0 | 7964 | // case. |
michael@0 | 7965 | return; |
michael@0 | 7966 | } |
michael@0 | 7967 | |
michael@0 | 7968 | // Don't bother doing anything if some viewmanager in our tree is painting |
michael@0 | 7969 | // while we still have painting suppressed or we are not active. |
michael@0 | 7970 | if (mPaintingSuppressed || !mIsActive || !IsVisible()) { |
michael@0 | 7971 | return; |
michael@0 | 7972 | } |
michael@0 | 7973 | |
michael@0 | 7974 | rootPresContext->FlushWillPaintObservers(); |
michael@0 | 7975 | if (mIsDestroying) |
michael@0 | 7976 | return; |
michael@0 | 7977 | |
michael@0 | 7978 | // Process reflows, if we have them, to reduce flicker due to invalidates and |
michael@0 | 7979 | // reflow being interspersed. Note that we _do_ allow this to be |
michael@0 | 7980 | // interruptible; if we can't do all the reflows it's better to flicker a bit |
michael@0 | 7981 | // than to freeze up. |
michael@0 | 7982 | FlushPendingNotifications(ChangesToFlush(Flush_InterruptibleLayout, false)); |
michael@0 | 7983 | } |
michael@0 | 7984 | |
michael@0 | 7985 | void |
michael@0 | 7986 | PresShell::WillPaintWindow() |
michael@0 | 7987 | { |
michael@0 | 7988 | nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext(); |
michael@0 | 7989 | if (rootPresContext != mPresContext) { |
michael@0 | 7990 | // This could be a popup's presshell. We don't allow plugins in popups |
michael@0 | 7991 | // so there's nothing to do here. |
michael@0 | 7992 | return; |
michael@0 | 7993 | } |
michael@0 | 7994 | |
michael@0 | 7995 | #ifndef XP_MACOSX |
michael@0 | 7996 | rootPresContext->ApplyPluginGeometryUpdates(); |
michael@0 | 7997 | #endif |
michael@0 | 7998 | } |
michael@0 | 7999 | |
michael@0 | 8000 | void |
michael@0 | 8001 | PresShell::DidPaintWindow() |
michael@0 | 8002 | { |
michael@0 | 8003 | nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext(); |
michael@0 | 8004 | if (rootPresContext != mPresContext) { |
michael@0 | 8005 | // This could be a popup's presshell. No point in notifying XPConnect |
michael@0 | 8006 | // about compositing of popups. |
michael@0 | 8007 | return; |
michael@0 | 8008 | } |
michael@0 | 8009 | |
michael@0 | 8010 | if (nsContentUtils::XPConnect()) { |
michael@0 | 8011 | nsContentUtils::XPConnect()->NotifyDidPaint(); |
michael@0 | 8012 | } |
michael@0 | 8013 | } |
michael@0 | 8014 | |
michael@0 | 8015 | bool |
michael@0 | 8016 | PresShell::IsVisible() |
michael@0 | 8017 | { |
michael@0 | 8018 | if (!mViewManager) |
michael@0 | 8019 | return false; |
michael@0 | 8020 | |
michael@0 | 8021 | nsView* view = mViewManager->GetRootView(); |
michael@0 | 8022 | if (!view) |
michael@0 | 8023 | return true; |
michael@0 | 8024 | |
michael@0 | 8025 | // inner view of subdoc frame |
michael@0 | 8026 | view = view->GetParent(); |
michael@0 | 8027 | if (!view) |
michael@0 | 8028 | return true; |
michael@0 | 8029 | |
michael@0 | 8030 | // subdoc view |
michael@0 | 8031 | view = view->GetParent(); |
michael@0 | 8032 | if (!view) |
michael@0 | 8033 | return true; |
michael@0 | 8034 | |
michael@0 | 8035 | nsIFrame* frame = view->GetFrame(); |
michael@0 | 8036 | if (!frame) |
michael@0 | 8037 | return true; |
michael@0 | 8038 | |
michael@0 | 8039 | return frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY); |
michael@0 | 8040 | } |
michael@0 | 8041 | |
michael@0 | 8042 | nsresult |
michael@0 | 8043 | PresShell::GetAgentStyleSheets(nsCOMArray<nsIStyleSheet>& aSheets) |
michael@0 | 8044 | { |
michael@0 | 8045 | aSheets.Clear(); |
michael@0 | 8046 | int32_t sheetCount = mStyleSet->SheetCount(nsStyleSet::eAgentSheet); |
michael@0 | 8047 | |
michael@0 | 8048 | for (int32_t i = 0; i < sheetCount; ++i) { |
michael@0 | 8049 | nsIStyleSheet *sheet = mStyleSet->StyleSheetAt(nsStyleSet::eAgentSheet, i); |
michael@0 | 8050 | if (!aSheets.AppendObject(sheet)) |
michael@0 | 8051 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 8052 | } |
michael@0 | 8053 | |
michael@0 | 8054 | return NS_OK; |
michael@0 | 8055 | } |
michael@0 | 8056 | |
michael@0 | 8057 | nsresult |
michael@0 | 8058 | PresShell::SetAgentStyleSheets(const nsCOMArray<nsIStyleSheet>& aSheets) |
michael@0 | 8059 | { |
michael@0 | 8060 | return mStyleSet->ReplaceSheets(nsStyleSet::eAgentSheet, aSheets); |
michael@0 | 8061 | } |
michael@0 | 8062 | |
michael@0 | 8063 | nsresult |
michael@0 | 8064 | PresShell::AddOverrideStyleSheet(nsIStyleSheet *aSheet) |
michael@0 | 8065 | { |
michael@0 | 8066 | return mStyleSet->PrependStyleSheet(nsStyleSet::eOverrideSheet, aSheet); |
michael@0 | 8067 | } |
michael@0 | 8068 | |
michael@0 | 8069 | nsresult |
michael@0 | 8070 | PresShell::RemoveOverrideStyleSheet(nsIStyleSheet *aSheet) |
michael@0 | 8071 | { |
michael@0 | 8072 | return mStyleSet->RemoveStyleSheet(nsStyleSet::eOverrideSheet, aSheet); |
michael@0 | 8073 | } |
michael@0 | 8074 | |
michael@0 | 8075 | static void |
michael@0 | 8076 | FreezeElement(nsIContent *aContent, void * /* unused */) |
michael@0 | 8077 | { |
michael@0 | 8078 | nsCOMPtr<nsIObjectLoadingContent> olc(do_QueryInterface(aContent)); |
michael@0 | 8079 | if (olc) { |
michael@0 | 8080 | olc->StopPluginInstance(); |
michael@0 | 8081 | } |
michael@0 | 8082 | } |
michael@0 | 8083 | |
michael@0 | 8084 | static bool |
michael@0 | 8085 | FreezeSubDocument(nsIDocument *aDocument, void *aData) |
michael@0 | 8086 | { |
michael@0 | 8087 | nsIPresShell *shell = aDocument->GetShell(); |
michael@0 | 8088 | if (shell) |
michael@0 | 8089 | shell->Freeze(); |
michael@0 | 8090 | |
michael@0 | 8091 | return true; |
michael@0 | 8092 | } |
michael@0 | 8093 | |
michael@0 | 8094 | void |
michael@0 | 8095 | PresShell::Freeze() |
michael@0 | 8096 | { |
michael@0 | 8097 | mUpdateImageVisibilityEvent.Revoke(); |
michael@0 | 8098 | |
michael@0 | 8099 | MaybeReleaseCapturingContent(); |
michael@0 | 8100 | |
michael@0 | 8101 | mDocument->EnumerateFreezableElements(FreezeElement, nullptr); |
michael@0 | 8102 | |
michael@0 | 8103 | if (mCaret) { |
michael@0 | 8104 | mCaret->SetCaretVisible(false); |
michael@0 | 8105 | } |
michael@0 | 8106 | |
michael@0 | 8107 | mPaintingSuppressed = true; |
michael@0 | 8108 | |
michael@0 | 8109 | if (mDocument) { |
michael@0 | 8110 | mDocument->EnumerateSubDocuments(FreezeSubDocument, nullptr); |
michael@0 | 8111 | } |
michael@0 | 8112 | |
michael@0 | 8113 | nsPresContext* presContext = GetPresContext(); |
michael@0 | 8114 | if (presContext && |
michael@0 | 8115 | presContext->RefreshDriver()->PresContext() == presContext) { |
michael@0 | 8116 | presContext->RefreshDriver()->Freeze(); |
michael@0 | 8117 | } |
michael@0 | 8118 | |
michael@0 | 8119 | mFrozen = true; |
michael@0 | 8120 | if (mDocument) { |
michael@0 | 8121 | UpdateImageLockingState(); |
michael@0 | 8122 | } |
michael@0 | 8123 | } |
michael@0 | 8124 | |
michael@0 | 8125 | void |
michael@0 | 8126 | PresShell::FireOrClearDelayedEvents(bool aFireEvents) |
michael@0 | 8127 | { |
michael@0 | 8128 | mNoDelayedMouseEvents = false; |
michael@0 | 8129 | mNoDelayedKeyEvents = false; |
michael@0 | 8130 | if (!aFireEvents) { |
michael@0 | 8131 | mDelayedEvents.Clear(); |
michael@0 | 8132 | return; |
michael@0 | 8133 | } |
michael@0 | 8134 | |
michael@0 | 8135 | if (mDocument) { |
michael@0 | 8136 | nsCOMPtr<nsIDocument> doc = mDocument; |
michael@0 | 8137 | while (!mIsDestroying && mDelayedEvents.Length() && |
michael@0 | 8138 | !doc->EventHandlingSuppressed()) { |
michael@0 | 8139 | nsAutoPtr<DelayedEvent> ev(mDelayedEvents[0].forget()); |
michael@0 | 8140 | mDelayedEvents.RemoveElementAt(0); |
michael@0 | 8141 | ev->Dispatch(); |
michael@0 | 8142 | } |
michael@0 | 8143 | if (!doc->EventHandlingSuppressed()) { |
michael@0 | 8144 | mDelayedEvents.Clear(); |
michael@0 | 8145 | } |
michael@0 | 8146 | } |
michael@0 | 8147 | } |
michael@0 | 8148 | |
michael@0 | 8149 | static void |
michael@0 | 8150 | ThawElement(nsIContent *aContent, void *aShell) |
michael@0 | 8151 | { |
michael@0 | 8152 | nsCOMPtr<nsIObjectLoadingContent> olc(do_QueryInterface(aContent)); |
michael@0 | 8153 | if (olc) { |
michael@0 | 8154 | olc->AsyncStartPluginInstance(); |
michael@0 | 8155 | } |
michael@0 | 8156 | } |
michael@0 | 8157 | |
michael@0 | 8158 | static bool |
michael@0 | 8159 | ThawSubDocument(nsIDocument *aDocument, void *aData) |
michael@0 | 8160 | { |
michael@0 | 8161 | nsIPresShell *shell = aDocument->GetShell(); |
michael@0 | 8162 | if (shell) |
michael@0 | 8163 | shell->Thaw(); |
michael@0 | 8164 | |
michael@0 | 8165 | return true; |
michael@0 | 8166 | } |
michael@0 | 8167 | |
michael@0 | 8168 | void |
michael@0 | 8169 | PresShell::Thaw() |
michael@0 | 8170 | { |
michael@0 | 8171 | nsPresContext* presContext = GetPresContext(); |
michael@0 | 8172 | if (presContext && |
michael@0 | 8173 | presContext->RefreshDriver()->PresContext() == presContext) { |
michael@0 | 8174 | presContext->RefreshDriver()->Thaw(); |
michael@0 | 8175 | } |
michael@0 | 8176 | |
michael@0 | 8177 | mDocument->EnumerateFreezableElements(ThawElement, this); |
michael@0 | 8178 | |
michael@0 | 8179 | if (mDocument) |
michael@0 | 8180 | mDocument->EnumerateSubDocuments(ThawSubDocument, nullptr); |
michael@0 | 8181 | |
michael@0 | 8182 | // Get the activeness of our presshell, as this might have changed |
michael@0 | 8183 | // while we were in the bfcache |
michael@0 | 8184 | QueryIsActive(); |
michael@0 | 8185 | |
michael@0 | 8186 | // We're now unfrozen |
michael@0 | 8187 | mFrozen = false; |
michael@0 | 8188 | UpdateImageLockingState(); |
michael@0 | 8189 | |
michael@0 | 8190 | UnsuppressPainting(); |
michael@0 | 8191 | } |
michael@0 | 8192 | |
michael@0 | 8193 | //-------------------------------------------------------- |
michael@0 | 8194 | // Start of protected and private methods on the PresShell |
michael@0 | 8195 | //-------------------------------------------------------- |
michael@0 | 8196 | |
michael@0 | 8197 | void |
michael@0 | 8198 | PresShell::MaybeScheduleReflow() |
michael@0 | 8199 | { |
michael@0 | 8200 | ASSERT_REFLOW_SCHEDULED_STATE(); |
michael@0 | 8201 | if (mReflowScheduled || mIsDestroying || mIsReflowing || |
michael@0 | 8202 | mDirtyRoots.Length() == 0) |
michael@0 | 8203 | return; |
michael@0 | 8204 | |
michael@0 | 8205 | if (!mPresContext->HasPendingInterrupt() || !ScheduleReflowOffTimer()) { |
michael@0 | 8206 | ScheduleReflow(); |
michael@0 | 8207 | } |
michael@0 | 8208 | |
michael@0 | 8209 | ASSERT_REFLOW_SCHEDULED_STATE(); |
michael@0 | 8210 | } |
michael@0 | 8211 | |
michael@0 | 8212 | void |
michael@0 | 8213 | PresShell::ScheduleReflow() |
michael@0 | 8214 | { |
michael@0 | 8215 | NS_PRECONDITION(!mReflowScheduled, "Why are we trying to schedule a reflow?"); |
michael@0 | 8216 | ASSERT_REFLOW_SCHEDULED_STATE(); |
michael@0 | 8217 | |
michael@0 | 8218 | if (GetPresContext()->RefreshDriver()->AddLayoutFlushObserver(this)) { |
michael@0 | 8219 | mReflowScheduled = true; |
michael@0 | 8220 | } |
michael@0 | 8221 | |
michael@0 | 8222 | ASSERT_REFLOW_SCHEDULED_STATE(); |
michael@0 | 8223 | } |
michael@0 | 8224 | |
michael@0 | 8225 | nsresult |
michael@0 | 8226 | PresShell::DidCauseReflow() |
michael@0 | 8227 | { |
michael@0 | 8228 | NS_ASSERTION(mChangeNestCount != 0, "Unexpected call to DidCauseReflow()"); |
michael@0 | 8229 | --mChangeNestCount; |
michael@0 | 8230 | nsContentUtils::RemoveScriptBlocker(); |
michael@0 | 8231 | |
michael@0 | 8232 | return NS_OK; |
michael@0 | 8233 | } |
michael@0 | 8234 | |
michael@0 | 8235 | void |
michael@0 | 8236 | PresShell::WillDoReflow() |
michael@0 | 8237 | { |
michael@0 | 8238 | // We just reflowed, tell the caret that its frame might have moved. |
michael@0 | 8239 | // XXXbz that comment makes no sense |
michael@0 | 8240 | if (mCaret) { |
michael@0 | 8241 | mCaret->InvalidateOutsideCaret(); |
michael@0 | 8242 | } |
michael@0 | 8243 | |
michael@0 | 8244 | mPresContext->FlushUserFontSet(); |
michael@0 | 8245 | |
michael@0 | 8246 | mFrameConstructor->BeginUpdate(); |
michael@0 | 8247 | |
michael@0 | 8248 | mLastReflowStart = GetPerformanceNow(); |
michael@0 | 8249 | } |
michael@0 | 8250 | |
michael@0 | 8251 | void |
michael@0 | 8252 | PresShell::DidDoReflow(bool aInterruptible, bool aWasInterrupted) |
michael@0 | 8253 | { |
michael@0 | 8254 | mFrameConstructor->EndUpdate(); |
michael@0 | 8255 | |
michael@0 | 8256 | HandlePostedReflowCallbacks(aInterruptible); |
michael@0 | 8257 | |
michael@0 | 8258 | nsCOMPtr<nsIDocShell> docShell = mPresContext->GetDocShell(); |
michael@0 | 8259 | if (docShell) { |
michael@0 | 8260 | DOMHighResTimeStamp now = GetPerformanceNow(); |
michael@0 | 8261 | docShell->NotifyReflowObservers(aInterruptible, mLastReflowStart, now); |
michael@0 | 8262 | } |
michael@0 | 8263 | |
michael@0 | 8264 | if (sSynthMouseMove) { |
michael@0 | 8265 | SynthesizeMouseMove(false); |
michael@0 | 8266 | } |
michael@0 | 8267 | if (mCaret) { |
michael@0 | 8268 | // Update the caret's position now to account for any changes created by |
michael@0 | 8269 | // the reflow. |
michael@0 | 8270 | mCaret->InvalidateOutsideCaret(); |
michael@0 | 8271 | mCaret->UpdateCaretPosition(); |
michael@0 | 8272 | } |
michael@0 | 8273 | |
michael@0 | 8274 | if (!aWasInterrupted) { |
michael@0 | 8275 | ClearReflowOnZoomPending(); |
michael@0 | 8276 | } |
michael@0 | 8277 | } |
michael@0 | 8278 | |
michael@0 | 8279 | DOMHighResTimeStamp |
michael@0 | 8280 | PresShell::GetPerformanceNow() |
michael@0 | 8281 | { |
michael@0 | 8282 | DOMHighResTimeStamp now = 0; |
michael@0 | 8283 | nsPIDOMWindow* window = mDocument->GetInnerWindow(); |
michael@0 | 8284 | |
michael@0 | 8285 | if (window) { |
michael@0 | 8286 | nsPerformance* perf = window->GetPerformance(); |
michael@0 | 8287 | |
michael@0 | 8288 | if (perf) { |
michael@0 | 8289 | now = perf->Now(); |
michael@0 | 8290 | } |
michael@0 | 8291 | } |
michael@0 | 8292 | |
michael@0 | 8293 | return now; |
michael@0 | 8294 | } |
michael@0 | 8295 | |
michael@0 | 8296 | static PLDHashOperator |
michael@0 | 8297 | MarkFramesDirtyToRoot(nsPtrHashKey<nsIFrame>* p, void* closure) |
michael@0 | 8298 | { |
michael@0 | 8299 | nsIFrame* target = static_cast<nsIFrame*>(closure); |
michael@0 | 8300 | for (nsIFrame* f = p->GetKey(); f && !NS_SUBTREE_DIRTY(f); |
michael@0 | 8301 | f = f->GetParent()) { |
michael@0 | 8302 | f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); |
michael@0 | 8303 | |
michael@0 | 8304 | if (f == target) { |
michael@0 | 8305 | break; |
michael@0 | 8306 | } |
michael@0 | 8307 | } |
michael@0 | 8308 | |
michael@0 | 8309 | return PL_DHASH_NEXT; |
michael@0 | 8310 | } |
michael@0 | 8311 | |
michael@0 | 8312 | void |
michael@0 | 8313 | PresShell::sReflowContinueCallback(nsITimer* aTimer, void* aPresShell) |
michael@0 | 8314 | { |
michael@0 | 8315 | nsRefPtr<PresShell> self = static_cast<PresShell*>(aPresShell); |
michael@0 | 8316 | |
michael@0 | 8317 | NS_PRECONDITION(aTimer == self->mReflowContinueTimer, "Unexpected timer"); |
michael@0 | 8318 | self->mReflowContinueTimer = nullptr; |
michael@0 | 8319 | self->ScheduleReflow(); |
michael@0 | 8320 | } |
michael@0 | 8321 | |
michael@0 | 8322 | bool |
michael@0 | 8323 | PresShell::ScheduleReflowOffTimer() |
michael@0 | 8324 | { |
michael@0 | 8325 | NS_PRECONDITION(!mReflowScheduled, "Shouldn't get here"); |
michael@0 | 8326 | ASSERT_REFLOW_SCHEDULED_STATE(); |
michael@0 | 8327 | |
michael@0 | 8328 | if (!mReflowContinueTimer) { |
michael@0 | 8329 | mReflowContinueTimer = do_CreateInstance("@mozilla.org/timer;1"); |
michael@0 | 8330 | if (!mReflowContinueTimer || |
michael@0 | 8331 | NS_FAILED(mReflowContinueTimer-> |
michael@0 | 8332 | InitWithFuncCallback(sReflowContinueCallback, this, 30, |
michael@0 | 8333 | nsITimer::TYPE_ONE_SHOT))) { |
michael@0 | 8334 | return false; |
michael@0 | 8335 | } |
michael@0 | 8336 | } |
michael@0 | 8337 | return true; |
michael@0 | 8338 | } |
michael@0 | 8339 | |
michael@0 | 8340 | bool |
michael@0 | 8341 | PresShell::DoReflow(nsIFrame* target, bool aInterruptible) |
michael@0 | 8342 | { |
michael@0 | 8343 | if (mIsZombie) { |
michael@0 | 8344 | return true; |
michael@0 | 8345 | } |
michael@0 | 8346 | |
michael@0 | 8347 | gfxTextPerfMetrics* tp = mPresContext->GetTextPerfMetrics(); |
michael@0 | 8348 | TimeStamp timeStart; |
michael@0 | 8349 | if (tp) { |
michael@0 | 8350 | tp->Accumulate(); |
michael@0 | 8351 | tp->reflowCount++; |
michael@0 | 8352 | timeStart = TimeStamp::Now(); |
michael@0 | 8353 | } |
michael@0 | 8354 | |
michael@0 | 8355 | target->SchedulePaint(); |
michael@0 | 8356 | nsIFrame *parent = nsLayoutUtils::GetCrossDocParentFrame(target); |
michael@0 | 8357 | while (parent) { |
michael@0 | 8358 | nsSVGEffects::InvalidateDirectRenderingObservers(parent); |
michael@0 | 8359 | parent = nsLayoutUtils::GetCrossDocParentFrame(parent); |
michael@0 | 8360 | } |
michael@0 | 8361 | |
michael@0 | 8362 | nsAutoCString docURL("N/A"); |
michael@0 | 8363 | nsIURI *uri = mDocument->GetDocumentURI(); |
michael@0 | 8364 | if (uri) |
michael@0 | 8365 | uri->GetSpec(docURL); |
michael@0 | 8366 | PROFILER_LABEL_PRINTF("layout", "DoReflow", "(%s)", docURL.get()); |
michael@0 | 8367 | |
michael@0 | 8368 | if (mReflowContinueTimer) { |
michael@0 | 8369 | mReflowContinueTimer->Cancel(); |
michael@0 | 8370 | mReflowContinueTimer = nullptr; |
michael@0 | 8371 | } |
michael@0 | 8372 | |
michael@0 | 8373 | nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); |
michael@0 | 8374 | |
michael@0 | 8375 | nsRefPtr<nsRenderingContext> rcx = CreateReferenceRenderingContext(); |
michael@0 | 8376 | |
michael@0 | 8377 | #ifdef DEBUG |
michael@0 | 8378 | mCurrentReflowRoot = target; |
michael@0 | 8379 | #endif |
michael@0 | 8380 | |
michael@0 | 8381 | target->WillReflow(mPresContext); |
michael@0 | 8382 | |
michael@0 | 8383 | // If the target frame is the root of the frame hierarchy, then |
michael@0 | 8384 | // use all the available space. If it's simply a `reflow root', |
michael@0 | 8385 | // then use the target frame's size as the available space. |
michael@0 | 8386 | nsSize size; |
michael@0 | 8387 | if (target == rootFrame) { |
michael@0 | 8388 | size = mPresContext->GetVisibleArea().Size(); |
michael@0 | 8389 | } else { |
michael@0 | 8390 | size = target->GetSize(); |
michael@0 | 8391 | } |
michael@0 | 8392 | |
michael@0 | 8393 | NS_ASSERTION(!target->GetNextInFlow() && !target->GetPrevInFlow(), |
michael@0 | 8394 | "reflow roots should never split"); |
michael@0 | 8395 | |
michael@0 | 8396 | // Don't pass size directly to the reflow state, since a |
michael@0 | 8397 | // constrained height implies page/column breaking. |
michael@0 | 8398 | nsSize reflowSize(size.width, NS_UNCONSTRAINEDSIZE); |
michael@0 | 8399 | nsHTMLReflowState reflowState(mPresContext, target, rcx, reflowSize, |
michael@0 | 8400 | nsHTMLReflowState::CALLER_WILL_INIT); |
michael@0 | 8401 | |
michael@0 | 8402 | if (rootFrame == target) { |
michael@0 | 8403 | reflowState.Init(mPresContext); |
michael@0 | 8404 | |
michael@0 | 8405 | // When the root frame is being reflowed with unconstrained height |
michael@0 | 8406 | // (which happens when we're called from |
michael@0 | 8407 | // nsDocumentViewer::SizeToContent), we're effectively doing a |
michael@0 | 8408 | // vertical resize, since it changes the meaning of percentage |
michael@0 | 8409 | // heights even if no heights actually changed. The same applies |
michael@0 | 8410 | // when we reflow again after that computation. This is an unusual |
michael@0 | 8411 | // case, and isn't caught by nsHTMLReflowState::InitResizeFlags. |
michael@0 | 8412 | bool hasUnconstrainedHeight = size.height == NS_UNCONSTRAINEDSIZE; |
michael@0 | 8413 | |
michael@0 | 8414 | if (hasUnconstrainedHeight || mLastRootReflowHadUnconstrainedHeight) { |
michael@0 | 8415 | reflowState.mFlags.mVResize = true; |
michael@0 | 8416 | } |
michael@0 | 8417 | |
michael@0 | 8418 | mLastRootReflowHadUnconstrainedHeight = hasUnconstrainedHeight; |
michael@0 | 8419 | } else { |
michael@0 | 8420 | // Initialize reflow state with current used border and padding, |
michael@0 | 8421 | // in case this was set specially by the parent frame when the reflow root |
michael@0 | 8422 | // was reflowed by its parent. |
michael@0 | 8423 | nsMargin currentBorder = target->GetUsedBorder(); |
michael@0 | 8424 | nsMargin currentPadding = target->GetUsedPadding(); |
michael@0 | 8425 | reflowState.Init(mPresContext, -1, -1, ¤tBorder, ¤tPadding); |
michael@0 | 8426 | } |
michael@0 | 8427 | |
michael@0 | 8428 | // fix the computed height |
michael@0 | 8429 | NS_ASSERTION(reflowState.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0), |
michael@0 | 8430 | "reflow state should not set margin for reflow roots"); |
michael@0 | 8431 | if (size.height != NS_UNCONSTRAINEDSIZE) { |
michael@0 | 8432 | nscoord computedHeight = |
michael@0 | 8433 | size.height - reflowState.ComputedPhysicalBorderPadding().TopBottom(); |
michael@0 | 8434 | computedHeight = std::max(computedHeight, 0); |
michael@0 | 8435 | reflowState.SetComputedHeight(computedHeight); |
michael@0 | 8436 | } |
michael@0 | 8437 | NS_ASSERTION(reflowState.ComputedWidth() == |
michael@0 | 8438 | size.width - |
michael@0 | 8439 | reflowState.ComputedPhysicalBorderPadding().LeftRight(), |
michael@0 | 8440 | "reflow state computed incorrect width"); |
michael@0 | 8441 | |
michael@0 | 8442 | mPresContext->ReflowStarted(aInterruptible); |
michael@0 | 8443 | mIsReflowing = true; |
michael@0 | 8444 | |
michael@0 | 8445 | nsReflowStatus status; |
michael@0 | 8446 | nsHTMLReflowMetrics desiredSize(reflowState); |
michael@0 | 8447 | target->Reflow(mPresContext, desiredSize, reflowState, status); |
michael@0 | 8448 | |
michael@0 | 8449 | // If an incremental reflow is initiated at a frame other than the |
michael@0 | 8450 | // root frame, then its desired size had better not change! If it's |
michael@0 | 8451 | // initiated at the root, then the size better not change unless its |
michael@0 | 8452 | // height was unconstrained to start with. |
michael@0 | 8453 | nsRect boundsRelativeToTarget = nsRect(0, 0, desiredSize.Width(), desiredSize.Height()); |
michael@0 | 8454 | NS_ASSERTION((target == rootFrame && size.height == NS_UNCONSTRAINEDSIZE) || |
michael@0 | 8455 | (desiredSize.Width() == size.width && |
michael@0 | 8456 | desiredSize.Height() == size.height), |
michael@0 | 8457 | "non-root frame's desired size changed during an " |
michael@0 | 8458 | "incremental reflow"); |
michael@0 | 8459 | NS_ASSERTION(target == rootFrame || |
michael@0 | 8460 | desiredSize.VisualOverflow().IsEqualInterior(boundsRelativeToTarget), |
michael@0 | 8461 | "non-root reflow roots must not have visible overflow"); |
michael@0 | 8462 | NS_ASSERTION(target == rootFrame || |
michael@0 | 8463 | desiredSize.ScrollableOverflow().IsEqualEdges(boundsRelativeToTarget), |
michael@0 | 8464 | "non-root reflow roots must not have scrollable overflow"); |
michael@0 | 8465 | NS_ASSERTION(status == NS_FRAME_COMPLETE, |
michael@0 | 8466 | "reflow roots should never split"); |
michael@0 | 8467 | |
michael@0 | 8468 | target->SetSize(boundsRelativeToTarget.Size()); |
michael@0 | 8469 | |
michael@0 | 8470 | // Always use boundsRelativeToTarget here, not desiredSize.GetVisualOverflowArea(), |
michael@0 | 8471 | // because for root frames (where they could be different, since root frames |
michael@0 | 8472 | // are allowed to have overflow) the root view bounds need to match the |
michael@0 | 8473 | // viewport bounds; the view manager "window dimensions" code depends on it. |
michael@0 | 8474 | nsContainerFrame::SyncFrameViewAfterReflow(mPresContext, target, |
michael@0 | 8475 | target->GetView(), |
michael@0 | 8476 | boundsRelativeToTarget); |
michael@0 | 8477 | nsContainerFrame::SyncWindowProperties(mPresContext, target, |
michael@0 | 8478 | target->GetView(), rcx); |
michael@0 | 8479 | |
michael@0 | 8480 | target->DidReflow(mPresContext, nullptr, nsDidReflowStatus::FINISHED); |
michael@0 | 8481 | if (target == rootFrame && size.height == NS_UNCONSTRAINEDSIZE) { |
michael@0 | 8482 | mPresContext->SetVisibleArea(boundsRelativeToTarget); |
michael@0 | 8483 | } |
michael@0 | 8484 | |
michael@0 | 8485 | #ifdef DEBUG |
michael@0 | 8486 | mCurrentReflowRoot = nullptr; |
michael@0 | 8487 | #endif |
michael@0 | 8488 | |
michael@0 | 8489 | NS_ASSERTION(mPresContext->HasPendingInterrupt() || |
michael@0 | 8490 | mFramesToDirty.Count() == 0, |
michael@0 | 8491 | "Why do we need to dirty anything if not interrupted?"); |
michael@0 | 8492 | |
michael@0 | 8493 | mIsReflowing = false; |
michael@0 | 8494 | bool interrupted = mPresContext->HasPendingInterrupt(); |
michael@0 | 8495 | if (interrupted) { |
michael@0 | 8496 | // Make sure target gets reflowed again. |
michael@0 | 8497 | mFramesToDirty.EnumerateEntries(&MarkFramesDirtyToRoot, target); |
michael@0 | 8498 | NS_ASSERTION(NS_SUBTREE_DIRTY(target), "Why is the target not dirty?"); |
michael@0 | 8499 | mDirtyRoots.AppendElement(target); |
michael@0 | 8500 | mDocument->SetNeedLayoutFlush(); |
michael@0 | 8501 | |
michael@0 | 8502 | // Clear mFramesToDirty after we've done the NS_SUBTREE_DIRTY(target) |
michael@0 | 8503 | // assertion so that if it fails it's easier to see what's going on. |
michael@0 | 8504 | #ifdef NOISY_INTERRUPTIBLE_REFLOW |
michael@0 | 8505 | printf("mFramesToDirty.Count() == %u\n", mFramesToDirty.Count()); |
michael@0 | 8506 | #endif /* NOISY_INTERRUPTIBLE_REFLOW */ |
michael@0 | 8507 | mFramesToDirty.Clear(); |
michael@0 | 8508 | |
michael@0 | 8509 | // Any FlushPendingNotifications with interruptible reflows |
michael@0 | 8510 | // should be suppressed now. We don't want to do extra reflow work |
michael@0 | 8511 | // before our reflow event happens. |
michael@0 | 8512 | mSuppressInterruptibleReflows = true; |
michael@0 | 8513 | MaybeScheduleReflow(); |
michael@0 | 8514 | } |
michael@0 | 8515 | |
michael@0 | 8516 | #ifdef PR_LOGGING |
michael@0 | 8517 | // dump text perf metrics for reflows with significant text processing |
michael@0 | 8518 | if (tp) { |
michael@0 | 8519 | if (tp->current.numChars > 100) { |
michael@0 | 8520 | TimeDuration reflowTime = TimeStamp::Now() - timeStart; |
michael@0 | 8521 | LogTextPerfStats(tp, this, tp->current, |
michael@0 | 8522 | reflowTime.ToMilliseconds(), eLog_reflow, nullptr); |
michael@0 | 8523 | } |
michael@0 | 8524 | tp->Accumulate(); |
michael@0 | 8525 | } |
michael@0 | 8526 | #endif |
michael@0 | 8527 | |
michael@0 | 8528 | return !interrupted; |
michael@0 | 8529 | } |
michael@0 | 8530 | |
michael@0 | 8531 | #ifdef DEBUG |
michael@0 | 8532 | void |
michael@0 | 8533 | PresShell::DoVerifyReflow() |
michael@0 | 8534 | { |
michael@0 | 8535 | if (GetVerifyReflowEnable()) { |
michael@0 | 8536 | // First synchronously render what we have so far so that we can |
michael@0 | 8537 | // see it. |
michael@0 | 8538 | nsView* rootView = mViewManager->GetRootView(); |
michael@0 | 8539 | mViewManager->InvalidateView(rootView); |
michael@0 | 8540 | |
michael@0 | 8541 | FlushPendingNotifications(Flush_Layout); |
michael@0 | 8542 | mInVerifyReflow = true; |
michael@0 | 8543 | bool ok = VerifyIncrementalReflow(); |
michael@0 | 8544 | mInVerifyReflow = false; |
michael@0 | 8545 | if (VERIFY_REFLOW_ALL & gVerifyReflowFlags) { |
michael@0 | 8546 | printf("ProcessReflowCommands: finished (%s)\n", |
michael@0 | 8547 | ok ? "ok" : "failed"); |
michael@0 | 8548 | } |
michael@0 | 8549 | |
michael@0 | 8550 | if (!mDirtyRoots.IsEmpty()) { |
michael@0 | 8551 | printf("XXX yikes! reflow commands queued during verify-reflow\n"); |
michael@0 | 8552 | } |
michael@0 | 8553 | } |
michael@0 | 8554 | } |
michael@0 | 8555 | #endif |
michael@0 | 8556 | |
michael@0 | 8557 | // used with Telemetry metrics |
michael@0 | 8558 | #define NS_LONG_REFLOW_TIME_MS 5000 |
michael@0 | 8559 | |
michael@0 | 8560 | bool |
michael@0 | 8561 | PresShell::ProcessReflowCommands(bool aInterruptible) |
michael@0 | 8562 | { |
michael@0 | 8563 | if (mDirtyRoots.IsEmpty() && !mShouldUnsuppressPainting) { |
michael@0 | 8564 | // Nothing to do; bail out |
michael@0 | 8565 | return true; |
michael@0 | 8566 | } |
michael@0 | 8567 | |
michael@0 | 8568 | mozilla::TimeStamp timerStart = mozilla::TimeStamp::Now(); |
michael@0 | 8569 | bool interrupted = false; |
michael@0 | 8570 | if (!mDirtyRoots.IsEmpty()) { |
michael@0 | 8571 | |
michael@0 | 8572 | #ifdef DEBUG |
michael@0 | 8573 | if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) { |
michael@0 | 8574 | printf("ProcessReflowCommands: begin incremental reflow\n"); |
michael@0 | 8575 | } |
michael@0 | 8576 | #endif |
michael@0 | 8577 | |
michael@0 | 8578 | // If reflow is interruptible, then make a note of our deadline. |
michael@0 | 8579 | const PRIntervalTime deadline = aInterruptible |
michael@0 | 8580 | ? PR_IntervalNow() + PR_MicrosecondsToInterval(gMaxRCProcessingTime) |
michael@0 | 8581 | : (PRIntervalTime)0; |
michael@0 | 8582 | |
michael@0 | 8583 | // Scope for the reflow entry point |
michael@0 | 8584 | { |
michael@0 | 8585 | nsAutoScriptBlocker scriptBlocker; |
michael@0 | 8586 | WillDoReflow(); |
michael@0 | 8587 | AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow); |
michael@0 | 8588 | nsViewManager::AutoDisableRefresh refreshBlocker(mViewManager); |
michael@0 | 8589 | |
michael@0 | 8590 | do { |
michael@0 | 8591 | // Send an incremental reflow notification to the target frame. |
michael@0 | 8592 | int32_t idx = mDirtyRoots.Length() - 1; |
michael@0 | 8593 | nsIFrame *target = mDirtyRoots[idx]; |
michael@0 | 8594 | mDirtyRoots.RemoveElementAt(idx); |
michael@0 | 8595 | |
michael@0 | 8596 | if (!NS_SUBTREE_DIRTY(target)) { |
michael@0 | 8597 | // It's not dirty anymore, which probably means the notification |
michael@0 | 8598 | // was posted in the middle of a reflow (perhaps with a reflow |
michael@0 | 8599 | // root in the middle). Don't do anything. |
michael@0 | 8600 | continue; |
michael@0 | 8601 | } |
michael@0 | 8602 | |
michael@0 | 8603 | interrupted = !DoReflow(target, aInterruptible); |
michael@0 | 8604 | |
michael@0 | 8605 | // Keep going until we're out of reflow commands, or we've run |
michael@0 | 8606 | // past our deadline, or we're interrupted. |
michael@0 | 8607 | } while (!interrupted && !mDirtyRoots.IsEmpty() && |
michael@0 | 8608 | (!aInterruptible || PR_IntervalNow() < deadline)); |
michael@0 | 8609 | |
michael@0 | 8610 | interrupted = !mDirtyRoots.IsEmpty(); |
michael@0 | 8611 | } |
michael@0 | 8612 | |
michael@0 | 8613 | // Exiting the scriptblocker might have killed us |
michael@0 | 8614 | if (!mIsDestroying) { |
michael@0 | 8615 | DidDoReflow(aInterruptible, interrupted); |
michael@0 | 8616 | } |
michael@0 | 8617 | |
michael@0 | 8618 | // DidDoReflow might have killed us |
michael@0 | 8619 | if (!mIsDestroying) { |
michael@0 | 8620 | #ifdef DEBUG |
michael@0 | 8621 | if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) { |
michael@0 | 8622 | printf("\nPresShell::ProcessReflowCommands() finished: this=%p\n", |
michael@0 | 8623 | (void*)this); |
michael@0 | 8624 | } |
michael@0 | 8625 | DoVerifyReflow(); |
michael@0 | 8626 | #endif |
michael@0 | 8627 | |
michael@0 | 8628 | // If any new reflow commands were enqueued during the reflow, schedule |
michael@0 | 8629 | // another reflow event to process them. Note that we want to do this |
michael@0 | 8630 | // after DidDoReflow(), since that method can change whether there are |
michael@0 | 8631 | // dirty roots around by flushing, and there's no point in posting a |
michael@0 | 8632 | // reflow event just to have the flush revoke it. |
michael@0 | 8633 | if (!mDirtyRoots.IsEmpty()) { |
michael@0 | 8634 | MaybeScheduleReflow(); |
michael@0 | 8635 | // And tell our document that we might need flushing |
michael@0 | 8636 | mDocument->SetNeedLayoutFlush(); |
michael@0 | 8637 | } |
michael@0 | 8638 | } |
michael@0 | 8639 | } |
michael@0 | 8640 | |
michael@0 | 8641 | if (!mIsDestroying && mShouldUnsuppressPainting && |
michael@0 | 8642 | mDirtyRoots.IsEmpty()) { |
michael@0 | 8643 | // We only unlock if we're out of reflows. It's pointless |
michael@0 | 8644 | // to unlock if reflows are still pending, since reflows |
michael@0 | 8645 | // are just going to thrash the frames around some more. By |
michael@0 | 8646 | // waiting we avoid an overeager "jitter" effect. |
michael@0 | 8647 | mShouldUnsuppressPainting = false; |
michael@0 | 8648 | UnsuppressAndInvalidate(); |
michael@0 | 8649 | } |
michael@0 | 8650 | |
michael@0 | 8651 | if (mDocument->GetRootElement()) { |
michael@0 | 8652 | TimeDuration elapsed = TimeStamp::Now() - timerStart; |
michael@0 | 8653 | int32_t intElapsed = int32_t(elapsed.ToMilliseconds()); |
michael@0 | 8654 | |
michael@0 | 8655 | Telemetry::ID id; |
michael@0 | 8656 | if (mDocument->GetRootElement()->IsXUL()) { |
michael@0 | 8657 | id = mIsActive |
michael@0 | 8658 | ? Telemetry::XUL_FOREGROUND_REFLOW_MS |
michael@0 | 8659 | : Telemetry::XUL_BACKGROUND_REFLOW_MS; |
michael@0 | 8660 | } else { |
michael@0 | 8661 | id = mIsActive |
michael@0 | 8662 | ? Telemetry::HTML_FOREGROUND_REFLOW_MS_2 |
michael@0 | 8663 | : Telemetry::HTML_BACKGROUND_REFLOW_MS_2; |
michael@0 | 8664 | } |
michael@0 | 8665 | Telemetry::Accumulate(id, intElapsed); |
michael@0 | 8666 | if (intElapsed > NS_LONG_REFLOW_TIME_MS) { |
michael@0 | 8667 | Telemetry::Accumulate(Telemetry::LONG_REFLOW_INTERRUPTIBLE, |
michael@0 | 8668 | aInterruptible ? 1 : 0); |
michael@0 | 8669 | } |
michael@0 | 8670 | } |
michael@0 | 8671 | |
michael@0 | 8672 | return !interrupted; |
michael@0 | 8673 | } |
michael@0 | 8674 | |
michael@0 | 8675 | void |
michael@0 | 8676 | PresShell::WindowSizeMoveDone() |
michael@0 | 8677 | { |
michael@0 | 8678 | if (mPresContext) { |
michael@0 | 8679 | EventStateManager::ClearGlobalActiveContent(nullptr); |
michael@0 | 8680 | ClearMouseCapture(nullptr); |
michael@0 | 8681 | } |
michael@0 | 8682 | } |
michael@0 | 8683 | |
michael@0 | 8684 | #ifdef MOZ_XUL |
michael@0 | 8685 | /* |
michael@0 | 8686 | * It's better to add stuff to the |DidSetStyleContext| method of the |
michael@0 | 8687 | * relevant frames than adding it here. These methods should (ideally, |
michael@0 | 8688 | * anyway) go away. |
michael@0 | 8689 | */ |
michael@0 | 8690 | |
michael@0 | 8691 | // Return value says whether to walk children. |
michael@0 | 8692 | typedef bool (* frameWalkerFn)(nsIFrame *aFrame, void *aClosure); |
michael@0 | 8693 | |
michael@0 | 8694 | static bool |
michael@0 | 8695 | ReResolveMenusAndTrees(nsIFrame *aFrame, void *aClosure) |
michael@0 | 8696 | { |
michael@0 | 8697 | // Trees have a special style cache that needs to be flushed when |
michael@0 | 8698 | // the theme changes. |
michael@0 | 8699 | nsTreeBodyFrame *treeBody = do_QueryFrame(aFrame); |
michael@0 | 8700 | if (treeBody) |
michael@0 | 8701 | treeBody->ClearStyleAndImageCaches(); |
michael@0 | 8702 | |
michael@0 | 8703 | // We deliberately don't re-resolve style on a menu's popup |
michael@0 | 8704 | // sub-content, since doing so slows menus to a crawl. That means we |
michael@0 | 8705 | // have to special-case them on a skin switch, and ensure that the |
michael@0 | 8706 | // popup frames just get destroyed completely. |
michael@0 | 8707 | nsMenuFrame* menu = do_QueryFrame(aFrame); |
michael@0 | 8708 | if (menu) |
michael@0 | 8709 | menu->CloseMenu(true); |
michael@0 | 8710 | return true; |
michael@0 | 8711 | } |
michael@0 | 8712 | |
michael@0 | 8713 | static bool |
michael@0 | 8714 | ReframeImageBoxes(nsIFrame *aFrame, void *aClosure) |
michael@0 | 8715 | { |
michael@0 | 8716 | nsStyleChangeList *list = static_cast<nsStyleChangeList*>(aClosure); |
michael@0 | 8717 | if (aFrame->GetType() == nsGkAtoms::imageBoxFrame) { |
michael@0 | 8718 | list->AppendChange(aFrame, aFrame->GetContent(), |
michael@0 | 8719 | NS_STYLE_HINT_FRAMECHANGE); |
michael@0 | 8720 | return false; // don't walk descendants |
michael@0 | 8721 | } |
michael@0 | 8722 | return true; // walk descendants |
michael@0 | 8723 | } |
michael@0 | 8724 | |
michael@0 | 8725 | static void |
michael@0 | 8726 | WalkFramesThroughPlaceholders(nsPresContext *aPresContext, nsIFrame *aFrame, |
michael@0 | 8727 | frameWalkerFn aFunc, void *aClosure) |
michael@0 | 8728 | { |
michael@0 | 8729 | bool walkChildren = (*aFunc)(aFrame, aClosure); |
michael@0 | 8730 | if (!walkChildren) |
michael@0 | 8731 | return; |
michael@0 | 8732 | |
michael@0 | 8733 | nsIFrame::ChildListIterator lists(aFrame); |
michael@0 | 8734 | for (; !lists.IsDone(); lists.Next()) { |
michael@0 | 8735 | nsFrameList::Enumerator childFrames(lists.CurrentList()); |
michael@0 | 8736 | for (; !childFrames.AtEnd(); childFrames.Next()) { |
michael@0 | 8737 | nsIFrame* child = childFrames.get(); |
michael@0 | 8738 | if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) { |
michael@0 | 8739 | // only do frames that are in flow, and recur through the |
michael@0 | 8740 | // out-of-flows of placeholders. |
michael@0 | 8741 | WalkFramesThroughPlaceholders(aPresContext, |
michael@0 | 8742 | nsPlaceholderFrame::GetRealFrameFor(child), |
michael@0 | 8743 | aFunc, aClosure); |
michael@0 | 8744 | } |
michael@0 | 8745 | } |
michael@0 | 8746 | } |
michael@0 | 8747 | } |
michael@0 | 8748 | #endif |
michael@0 | 8749 | |
michael@0 | 8750 | NS_IMETHODIMP |
michael@0 | 8751 | PresShell::Observe(nsISupports* aSubject, |
michael@0 | 8752 | const char* aTopic, |
michael@0 | 8753 | const char16_t* aData) |
michael@0 | 8754 | { |
michael@0 | 8755 | #ifdef MOZ_XUL |
michael@0 | 8756 | if (!nsCRT::strcmp(aTopic, "chrome-flush-skin-caches")) { |
michael@0 | 8757 | nsIFrame *rootFrame = mFrameConstructor->GetRootFrame(); |
michael@0 | 8758 | // Need to null-check because "chrome-flush-skin-caches" can happen |
michael@0 | 8759 | // at interesting times during startup. |
michael@0 | 8760 | if (rootFrame) { |
michael@0 | 8761 | NS_ASSERTION(mViewManager, "View manager must exist"); |
michael@0 | 8762 | |
michael@0 | 8763 | nsWeakFrame weakRoot(rootFrame); |
michael@0 | 8764 | // Have to make sure that the content notifications are flushed before we |
michael@0 | 8765 | // start messing with the frame model; otherwise we can get content doubling. |
michael@0 | 8766 | mDocument->FlushPendingNotifications(Flush_ContentAndNotify); |
michael@0 | 8767 | |
michael@0 | 8768 | if (weakRoot.IsAlive()) { |
michael@0 | 8769 | WalkFramesThroughPlaceholders(mPresContext, rootFrame, |
michael@0 | 8770 | &ReResolveMenusAndTrees, nullptr); |
michael@0 | 8771 | |
michael@0 | 8772 | // Because "chrome:" URL equality is messy, reframe image box |
michael@0 | 8773 | // frames (hack!). |
michael@0 | 8774 | nsStyleChangeList changeList; |
michael@0 | 8775 | WalkFramesThroughPlaceholders(mPresContext, rootFrame, |
michael@0 | 8776 | ReframeImageBoxes, &changeList); |
michael@0 | 8777 | // Mark ourselves as not safe to flush while we're doing frame |
michael@0 | 8778 | // construction. |
michael@0 | 8779 | { |
michael@0 | 8780 | nsAutoScriptBlocker scriptBlocker; |
michael@0 | 8781 | ++mChangeNestCount; |
michael@0 | 8782 | RestyleManager* restyleManager = mPresContext->RestyleManager(); |
michael@0 | 8783 | restyleManager->ProcessRestyledFrames(changeList); |
michael@0 | 8784 | restyleManager->FlushOverflowChangedTracker(); |
michael@0 | 8785 | --mChangeNestCount; |
michael@0 | 8786 | } |
michael@0 | 8787 | } |
michael@0 | 8788 | } |
michael@0 | 8789 | return NS_OK; |
michael@0 | 8790 | } |
michael@0 | 8791 | #endif |
michael@0 | 8792 | |
michael@0 | 8793 | if (!nsCRT::strcmp(aTopic, "agent-sheet-added") && mStyleSet) { |
michael@0 | 8794 | AddAgentSheet(aSubject); |
michael@0 | 8795 | return NS_OK; |
michael@0 | 8796 | } |
michael@0 | 8797 | |
michael@0 | 8798 | if (!nsCRT::strcmp(aTopic, "user-sheet-added") && mStyleSet) { |
michael@0 | 8799 | AddUserSheet(aSubject); |
michael@0 | 8800 | return NS_OK; |
michael@0 | 8801 | } |
michael@0 | 8802 | |
michael@0 | 8803 | if (!nsCRT::strcmp(aTopic, "author-sheet-added") && mStyleSet) { |
michael@0 | 8804 | AddAuthorSheet(aSubject); |
michael@0 | 8805 | return NS_OK; |
michael@0 | 8806 | } |
michael@0 | 8807 | |
michael@0 | 8808 | if (!nsCRT::strcmp(aTopic, "agent-sheet-removed") && mStyleSet) { |
michael@0 | 8809 | RemoveSheet(nsStyleSet::eAgentSheet, aSubject); |
michael@0 | 8810 | return NS_OK; |
michael@0 | 8811 | } |
michael@0 | 8812 | |
michael@0 | 8813 | if (!nsCRT::strcmp(aTopic, "user-sheet-removed") && mStyleSet) { |
michael@0 | 8814 | RemoveSheet(nsStyleSet::eUserSheet, aSubject); |
michael@0 | 8815 | return NS_OK; |
michael@0 | 8816 | } |
michael@0 | 8817 | |
michael@0 | 8818 | if (!nsCRT::strcmp(aTopic, "author-sheet-removed") && mStyleSet) { |
michael@0 | 8819 | RemoveSheet(nsStyleSet::eDocSheet, aSubject); |
michael@0 | 8820 | return NS_OK; |
michael@0 | 8821 | } |
michael@0 | 8822 | |
michael@0 | 8823 | NS_WARNING("unrecognized topic in PresShell::Observe"); |
michael@0 | 8824 | return NS_ERROR_FAILURE; |
michael@0 | 8825 | } |
michael@0 | 8826 | |
michael@0 | 8827 | bool |
michael@0 | 8828 | nsIPresShell::AddRefreshObserverInternal(nsARefreshObserver* aObserver, |
michael@0 | 8829 | mozFlushType aFlushType) |
michael@0 | 8830 | { |
michael@0 | 8831 | nsPresContext* presContext = GetPresContext(); |
michael@0 | 8832 | return presContext && |
michael@0 | 8833 | presContext->RefreshDriver()->AddRefreshObserver(aObserver, aFlushType); |
michael@0 | 8834 | } |
michael@0 | 8835 | |
michael@0 | 8836 | /* virtual */ bool |
michael@0 | 8837 | nsIPresShell::AddRefreshObserverExternal(nsARefreshObserver* aObserver, |
michael@0 | 8838 | mozFlushType aFlushType) |
michael@0 | 8839 | { |
michael@0 | 8840 | return AddRefreshObserverInternal(aObserver, aFlushType); |
michael@0 | 8841 | } |
michael@0 | 8842 | |
michael@0 | 8843 | bool |
michael@0 | 8844 | nsIPresShell::RemoveRefreshObserverInternal(nsARefreshObserver* aObserver, |
michael@0 | 8845 | mozFlushType aFlushType) |
michael@0 | 8846 | { |
michael@0 | 8847 | nsPresContext* presContext = GetPresContext(); |
michael@0 | 8848 | return presContext && |
michael@0 | 8849 | presContext->RefreshDriver()->RemoveRefreshObserver(aObserver, aFlushType); |
michael@0 | 8850 | } |
michael@0 | 8851 | |
michael@0 | 8852 | /* virtual */ bool |
michael@0 | 8853 | nsIPresShell::RemoveRefreshObserverExternal(nsARefreshObserver* aObserver, |
michael@0 | 8854 | mozFlushType aFlushType) |
michael@0 | 8855 | { |
michael@0 | 8856 | return RemoveRefreshObserverInternal(aObserver, aFlushType); |
michael@0 | 8857 | } |
michael@0 | 8858 | |
michael@0 | 8859 | /* virtual */ bool |
michael@0 | 8860 | nsIPresShell::AddPostRefreshObserver(nsAPostRefreshObserver* aObserver) |
michael@0 | 8861 | { |
michael@0 | 8862 | nsPresContext* presContext = GetPresContext(); |
michael@0 | 8863 | if (!presContext) { |
michael@0 | 8864 | return false; |
michael@0 | 8865 | } |
michael@0 | 8866 | presContext->RefreshDriver()->AddPostRefreshObserver(aObserver); |
michael@0 | 8867 | return true; |
michael@0 | 8868 | } |
michael@0 | 8869 | |
michael@0 | 8870 | /* virtual */ bool |
michael@0 | 8871 | nsIPresShell::RemovePostRefreshObserver(nsAPostRefreshObserver* aObserver) |
michael@0 | 8872 | { |
michael@0 | 8873 | nsPresContext* presContext = GetPresContext(); |
michael@0 | 8874 | if (!presContext) { |
michael@0 | 8875 | return false; |
michael@0 | 8876 | } |
michael@0 | 8877 | presContext->RefreshDriver()->RemovePostRefreshObserver(aObserver); |
michael@0 | 8878 | return true; |
michael@0 | 8879 | } |
michael@0 | 8880 | |
michael@0 | 8881 | //------------------------------------------------------ |
michael@0 | 8882 | // End of protected and private methods on the PresShell |
michael@0 | 8883 | //------------------------------------------------------ |
michael@0 | 8884 | |
michael@0 | 8885 | //------------------------------------------------------------------ |
michael@0 | 8886 | //-- Delayed event Classes Impls |
michael@0 | 8887 | //------------------------------------------------------------------ |
michael@0 | 8888 | |
michael@0 | 8889 | PresShell::DelayedInputEvent::DelayedInputEvent() : |
michael@0 | 8890 | DelayedEvent(), |
michael@0 | 8891 | mEvent(nullptr) |
michael@0 | 8892 | { |
michael@0 | 8893 | } |
michael@0 | 8894 | |
michael@0 | 8895 | PresShell::DelayedInputEvent::~DelayedInputEvent() |
michael@0 | 8896 | { |
michael@0 | 8897 | delete mEvent; |
michael@0 | 8898 | } |
michael@0 | 8899 | |
michael@0 | 8900 | void |
michael@0 | 8901 | PresShell::DelayedInputEvent::Dispatch() |
michael@0 | 8902 | { |
michael@0 | 8903 | if (!mEvent || !mEvent->widget) { |
michael@0 | 8904 | return; |
michael@0 | 8905 | } |
michael@0 | 8906 | nsCOMPtr<nsIWidget> widget = mEvent->widget; |
michael@0 | 8907 | nsEventStatus status; |
michael@0 | 8908 | widget->DispatchEvent(mEvent, status); |
michael@0 | 8909 | } |
michael@0 | 8910 | |
michael@0 | 8911 | PresShell::DelayedMouseEvent::DelayedMouseEvent(WidgetMouseEvent* aEvent) : |
michael@0 | 8912 | DelayedInputEvent() |
michael@0 | 8913 | { |
michael@0 | 8914 | WidgetMouseEvent* mouseEvent = |
michael@0 | 8915 | new WidgetMouseEvent(aEvent->mFlags.mIsTrusted, |
michael@0 | 8916 | aEvent->message, |
michael@0 | 8917 | aEvent->widget, |
michael@0 | 8918 | aEvent->reason, |
michael@0 | 8919 | aEvent->context); |
michael@0 | 8920 | mouseEvent->AssignMouseEventData(*aEvent, false); |
michael@0 | 8921 | mEvent = mouseEvent; |
michael@0 | 8922 | } |
michael@0 | 8923 | |
michael@0 | 8924 | PresShell::DelayedKeyEvent::DelayedKeyEvent(WidgetKeyboardEvent* aEvent) : |
michael@0 | 8925 | DelayedInputEvent() |
michael@0 | 8926 | { |
michael@0 | 8927 | WidgetKeyboardEvent* keyEvent = |
michael@0 | 8928 | new WidgetKeyboardEvent(aEvent->mFlags.mIsTrusted, |
michael@0 | 8929 | aEvent->message, |
michael@0 | 8930 | aEvent->widget); |
michael@0 | 8931 | keyEvent->AssignKeyEventData(*aEvent, false); |
michael@0 | 8932 | keyEvent->mFlags.mIsSynthesizedForTests = aEvent->mFlags.mIsSynthesizedForTests; |
michael@0 | 8933 | mEvent = keyEvent; |
michael@0 | 8934 | } |
michael@0 | 8935 | |
michael@0 | 8936 | // Start of DEBUG only code |
michael@0 | 8937 | |
michael@0 | 8938 | #ifdef DEBUG |
michael@0 | 8939 | |
michael@0 | 8940 | static void |
michael@0 | 8941 | LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg) |
michael@0 | 8942 | { |
michael@0 | 8943 | nsAutoString n1, n2; |
michael@0 | 8944 | if (k1) { |
michael@0 | 8945 | k1->GetFrameName(n1); |
michael@0 | 8946 | } else { |
michael@0 | 8947 | n1.Assign(NS_LITERAL_STRING("(null)")); |
michael@0 | 8948 | } |
michael@0 | 8949 | |
michael@0 | 8950 | if (k2) { |
michael@0 | 8951 | k2->GetFrameName(n2); |
michael@0 | 8952 | } else { |
michael@0 | 8953 | n2.Assign(NS_LITERAL_STRING("(null)")); |
michael@0 | 8954 | } |
michael@0 | 8955 | |
michael@0 | 8956 | printf("verifyreflow: %s %p != %s %p %s\n", |
michael@0 | 8957 | NS_LossyConvertUTF16toASCII(n1).get(), (void*)k1, |
michael@0 | 8958 | NS_LossyConvertUTF16toASCII(n2).get(), (void*)k2, aMsg); |
michael@0 | 8959 | } |
michael@0 | 8960 | |
michael@0 | 8961 | static void |
michael@0 | 8962 | LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg, |
michael@0 | 8963 | const nsRect& r1, const nsRect& r2) |
michael@0 | 8964 | { |
michael@0 | 8965 | printf("VerifyReflow Error:\n"); |
michael@0 | 8966 | nsAutoString name; |
michael@0 | 8967 | |
michael@0 | 8968 | if (k1) { |
michael@0 | 8969 | k1->GetFrameName(name); |
michael@0 | 8970 | printf(" %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k1); |
michael@0 | 8971 | } |
michael@0 | 8972 | printf("{%d, %d, %d, %d} != \n", r1.x, r1.y, r1.width, r1.height); |
michael@0 | 8973 | |
michael@0 | 8974 | if (k2) { |
michael@0 | 8975 | k2->GetFrameName(name); |
michael@0 | 8976 | printf(" %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k2); |
michael@0 | 8977 | } |
michael@0 | 8978 | printf("{%d, %d, %d, %d}\n %s\n", |
michael@0 | 8979 | r2.x, r2.y, r2.width, r2.height, aMsg); |
michael@0 | 8980 | } |
michael@0 | 8981 | |
michael@0 | 8982 | static void |
michael@0 | 8983 | LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg, |
michael@0 | 8984 | const nsIntRect& r1, const nsIntRect& r2) |
michael@0 | 8985 | { |
michael@0 | 8986 | printf("VerifyReflow Error:\n"); |
michael@0 | 8987 | nsAutoString name; |
michael@0 | 8988 | |
michael@0 | 8989 | if (k1) { |
michael@0 | 8990 | k1->GetFrameName(name); |
michael@0 | 8991 | printf(" %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k1); |
michael@0 | 8992 | } |
michael@0 | 8993 | printf("{%d, %d, %d, %d} != \n", r1.x, r1.y, r1.width, r1.height); |
michael@0 | 8994 | |
michael@0 | 8995 | if (k2) { |
michael@0 | 8996 | k2->GetFrameName(name); |
michael@0 | 8997 | printf(" %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k2); |
michael@0 | 8998 | } |
michael@0 | 8999 | printf("{%d, %d, %d, %d}\n %s\n", |
michael@0 | 9000 | r2.x, r2.y, r2.width, r2.height, aMsg); |
michael@0 | 9001 | } |
michael@0 | 9002 | |
michael@0 | 9003 | static bool |
michael@0 | 9004 | CompareTrees(nsPresContext* aFirstPresContext, nsIFrame* aFirstFrame, |
michael@0 | 9005 | nsPresContext* aSecondPresContext, nsIFrame* aSecondFrame) |
michael@0 | 9006 | { |
michael@0 | 9007 | if (!aFirstPresContext || !aFirstFrame || !aSecondPresContext || !aSecondFrame) |
michael@0 | 9008 | return true; |
michael@0 | 9009 | // XXX Evil hack to reduce false positives; I can't seem to figure |
michael@0 | 9010 | // out how to flush scrollbar changes correctly |
michael@0 | 9011 | //if (aFirstFrame->GetType() == nsGkAtoms::scrollbarFrame) |
michael@0 | 9012 | // return true; |
michael@0 | 9013 | bool ok = true; |
michael@0 | 9014 | nsIFrame::ChildListIterator lists1(aFirstFrame); |
michael@0 | 9015 | nsIFrame::ChildListIterator lists2(aSecondFrame); |
michael@0 | 9016 | do { |
michael@0 | 9017 | const nsFrameList& kids1 = !lists1.IsDone() ? lists1.CurrentList() : nsFrameList(); |
michael@0 | 9018 | const nsFrameList& kids2 = !lists2.IsDone() ? lists2.CurrentList() : nsFrameList(); |
michael@0 | 9019 | int32_t l1 = kids1.GetLength(); |
michael@0 | 9020 | int32_t l2 = kids2.GetLength();; |
michael@0 | 9021 | if (l1 != l2) { |
michael@0 | 9022 | ok = false; |
michael@0 | 9023 | LogVerifyMessage(kids1.FirstChild(), kids2.FirstChild(), |
michael@0 | 9024 | "child counts don't match: "); |
michael@0 | 9025 | printf("%d != %d\n", l1, l2); |
michael@0 | 9026 | if (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags)) { |
michael@0 | 9027 | break; |
michael@0 | 9028 | } |
michael@0 | 9029 | } |
michael@0 | 9030 | |
michael@0 | 9031 | nsIntRect r1, r2; |
michael@0 | 9032 | nsView* v1, *v2; |
michael@0 | 9033 | for (nsFrameList::Enumerator e1(kids1), e2(kids2); |
michael@0 | 9034 | ; |
michael@0 | 9035 | e1.Next(), e2.Next()) { |
michael@0 | 9036 | nsIFrame* k1 = e1.get(); |
michael@0 | 9037 | nsIFrame* k2 = e2.get(); |
michael@0 | 9038 | if (((nullptr == k1) && (nullptr != k2)) || |
michael@0 | 9039 | ((nullptr != k1) && (nullptr == k2))) { |
michael@0 | 9040 | ok = false; |
michael@0 | 9041 | LogVerifyMessage(k1, k2, "child lists are different\n"); |
michael@0 | 9042 | break; |
michael@0 | 9043 | } |
michael@0 | 9044 | else if (nullptr != k1) { |
michael@0 | 9045 | // Verify that the frames are the same size |
michael@0 | 9046 | if (!k1->GetRect().IsEqualInterior(k2->GetRect())) { |
michael@0 | 9047 | ok = false; |
michael@0 | 9048 | LogVerifyMessage(k1, k2, "(frame rects)", k1->GetRect(), k2->GetRect()); |
michael@0 | 9049 | } |
michael@0 | 9050 | |
michael@0 | 9051 | // Make sure either both have views or neither have views; if they |
michael@0 | 9052 | // do have views, make sure the views are the same size. If the |
michael@0 | 9053 | // views have widgets, make sure they both do or neither does. If |
michael@0 | 9054 | // they do, make sure the widgets are the same size. |
michael@0 | 9055 | v1 = k1->GetView(); |
michael@0 | 9056 | v2 = k2->GetView(); |
michael@0 | 9057 | if (((nullptr == v1) && (nullptr != v2)) || |
michael@0 | 9058 | ((nullptr != v1) && (nullptr == v2))) { |
michael@0 | 9059 | ok = false; |
michael@0 | 9060 | LogVerifyMessage(k1, k2, "child views are not matched\n"); |
michael@0 | 9061 | } |
michael@0 | 9062 | else if (nullptr != v1) { |
michael@0 | 9063 | if (!v1->GetBounds().IsEqualInterior(v2->GetBounds())) { |
michael@0 | 9064 | LogVerifyMessage(k1, k2, "(view rects)", v1->GetBounds(), v2->GetBounds()); |
michael@0 | 9065 | } |
michael@0 | 9066 | |
michael@0 | 9067 | nsIWidget* w1 = v1->GetWidget(); |
michael@0 | 9068 | nsIWidget* w2 = v2->GetWidget(); |
michael@0 | 9069 | if (((nullptr == w1) && (nullptr != w2)) || |
michael@0 | 9070 | ((nullptr != w1) && (nullptr == w2))) { |
michael@0 | 9071 | ok = false; |
michael@0 | 9072 | LogVerifyMessage(k1, k2, "child widgets are not matched\n"); |
michael@0 | 9073 | } |
michael@0 | 9074 | else if (nullptr != w1) { |
michael@0 | 9075 | w1->GetBounds(r1); |
michael@0 | 9076 | w2->GetBounds(r2); |
michael@0 | 9077 | if (!r1.IsEqualEdges(r2)) { |
michael@0 | 9078 | LogVerifyMessage(k1, k2, "(widget rects)", r1, r2); |
michael@0 | 9079 | } |
michael@0 | 9080 | } |
michael@0 | 9081 | } |
michael@0 | 9082 | if (!ok && (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags))) { |
michael@0 | 9083 | break; |
michael@0 | 9084 | } |
michael@0 | 9085 | |
michael@0 | 9086 | // XXX Should perhaps compare their float managers. |
michael@0 | 9087 | |
michael@0 | 9088 | // Compare the sub-trees too |
michael@0 | 9089 | if (!CompareTrees(aFirstPresContext, k1, aSecondPresContext, k2)) { |
michael@0 | 9090 | ok = false; |
michael@0 | 9091 | if (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags)) { |
michael@0 | 9092 | break; |
michael@0 | 9093 | } |
michael@0 | 9094 | } |
michael@0 | 9095 | } |
michael@0 | 9096 | else { |
michael@0 | 9097 | break; |
michael@0 | 9098 | } |
michael@0 | 9099 | } |
michael@0 | 9100 | if (!ok && (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags))) { |
michael@0 | 9101 | break; |
michael@0 | 9102 | } |
michael@0 | 9103 | |
michael@0 | 9104 | lists1.Next(); |
michael@0 | 9105 | lists2.Next(); |
michael@0 | 9106 | if (lists1.IsDone() != lists2.IsDone() || |
michael@0 | 9107 | (!lists1.IsDone() && lists1.CurrentID() != lists2.CurrentID())) { |
michael@0 | 9108 | if (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags)) { |
michael@0 | 9109 | ok = false; |
michael@0 | 9110 | } |
michael@0 | 9111 | LogVerifyMessage(kids1.FirstChild(), kids2.FirstChild(), |
michael@0 | 9112 | "child list names are not matched: "); |
michael@0 | 9113 | fprintf(stdout, "%s != %s\n", |
michael@0 | 9114 | !lists1.IsDone() ? mozilla::layout::ChildListName(lists1.CurrentID()) : "(null)", |
michael@0 | 9115 | !lists2.IsDone() ? mozilla::layout::ChildListName(lists2.CurrentID()) : "(null)"); |
michael@0 | 9116 | break; |
michael@0 | 9117 | } |
michael@0 | 9118 | } while (ok && !lists1.IsDone()); |
michael@0 | 9119 | |
michael@0 | 9120 | return ok; |
michael@0 | 9121 | } |
michael@0 | 9122 | #endif |
michael@0 | 9123 | |
michael@0 | 9124 | #if 0 |
michael@0 | 9125 | static nsIFrame* |
michael@0 | 9126 | FindTopFrame(nsIFrame* aRoot) |
michael@0 | 9127 | { |
michael@0 | 9128 | if (aRoot) { |
michael@0 | 9129 | nsIContent* content = aRoot->GetContent(); |
michael@0 | 9130 | if (content) { |
michael@0 | 9131 | nsIAtom* tag; |
michael@0 | 9132 | content->GetTag(tag); |
michael@0 | 9133 | if (nullptr != tag) { |
michael@0 | 9134 | NS_RELEASE(tag); |
michael@0 | 9135 | return aRoot; |
michael@0 | 9136 | } |
michael@0 | 9137 | } |
michael@0 | 9138 | |
michael@0 | 9139 | // Try one of the children |
michael@0 | 9140 | nsIFrame* kid = aRoot->GetFirstPrincipalChild(); |
michael@0 | 9141 | while (nullptr != kid) { |
michael@0 | 9142 | nsIFrame* result = FindTopFrame(kid); |
michael@0 | 9143 | if (nullptr != result) { |
michael@0 | 9144 | return result; |
michael@0 | 9145 | } |
michael@0 | 9146 | kid = kid->GetNextSibling(); |
michael@0 | 9147 | } |
michael@0 | 9148 | } |
michael@0 | 9149 | return nullptr; |
michael@0 | 9150 | } |
michael@0 | 9151 | #endif |
michael@0 | 9152 | |
michael@0 | 9153 | |
michael@0 | 9154 | #ifdef DEBUG |
michael@0 | 9155 | |
michael@0 | 9156 | nsStyleSet* |
michael@0 | 9157 | PresShell::CloneStyleSet(nsStyleSet* aSet) |
michael@0 | 9158 | { |
michael@0 | 9159 | nsStyleSet *clone = new nsStyleSet(); |
michael@0 | 9160 | |
michael@0 | 9161 | int32_t i, n = aSet->SheetCount(nsStyleSet::eOverrideSheet); |
michael@0 | 9162 | for (i = 0; i < n; i++) { |
michael@0 | 9163 | nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eOverrideSheet, i); |
michael@0 | 9164 | if (ss) |
michael@0 | 9165 | clone->AppendStyleSheet(nsStyleSet::eOverrideSheet, ss); |
michael@0 | 9166 | } |
michael@0 | 9167 | |
michael@0 | 9168 | // The document expects to insert document stylesheets itself |
michael@0 | 9169 | #if 0 |
michael@0 | 9170 | n = aSet->SheetCount(nsStyleSet::eDocSheet); |
michael@0 | 9171 | for (i = 0; i < n; i++) { |
michael@0 | 9172 | nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eDocSheet, i); |
michael@0 | 9173 | if (ss) |
michael@0 | 9174 | clone->AddDocStyleSheet(ss, mDocument); |
michael@0 | 9175 | } |
michael@0 | 9176 | #endif |
michael@0 | 9177 | |
michael@0 | 9178 | n = aSet->SheetCount(nsStyleSet::eUserSheet); |
michael@0 | 9179 | for (i = 0; i < n; i++) { |
michael@0 | 9180 | nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eUserSheet, i); |
michael@0 | 9181 | if (ss) |
michael@0 | 9182 | clone->AppendStyleSheet(nsStyleSet::eUserSheet, ss); |
michael@0 | 9183 | } |
michael@0 | 9184 | |
michael@0 | 9185 | n = aSet->SheetCount(nsStyleSet::eAgentSheet); |
michael@0 | 9186 | for (i = 0; i < n; i++) { |
michael@0 | 9187 | nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eAgentSheet, i); |
michael@0 | 9188 | if (ss) |
michael@0 | 9189 | clone->AppendStyleSheet(nsStyleSet::eAgentSheet, ss); |
michael@0 | 9190 | } |
michael@0 | 9191 | return clone; |
michael@0 | 9192 | } |
michael@0 | 9193 | |
michael@0 | 9194 | #ifdef DEBUG_Eli |
michael@0 | 9195 | static nsresult |
michael@0 | 9196 | DumpToPNG(nsIPresShell* shell, nsAString& name) { |
michael@0 | 9197 | int32_t width=1000, height=1000; |
michael@0 | 9198 | nsRect r(0, 0, shell->GetPresContext()->DevPixelsToAppUnits(width), |
michael@0 | 9199 | shell->GetPresContext()->DevPixelsToAppUnits(height)); |
michael@0 | 9200 | |
michael@0 | 9201 | nsRefPtr<gfxImageSurface> imgSurface = |
michael@0 | 9202 | new gfxImageSurface(gfxIntSize(width, height), |
michael@0 | 9203 | gfxImageFormat::ARGB32); |
michael@0 | 9204 | |
michael@0 | 9205 | nsRefPtr<gfxContext> imgContext = new gfxContext(imgSurface); |
michael@0 | 9206 | |
michael@0 | 9207 | nsRefPtr<gfxASurface> surface = |
michael@0 | 9208 | gfxPlatform::GetPlatform()-> |
michael@0 | 9209 | CreateOffscreenSurface(IntSize(width, height), |
michael@0 | 9210 | gfxASurface::ContentFromFormat(gfxImageFormat::ARGB32)); |
michael@0 | 9211 | NS_ENSURE_TRUE(surface, NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 9212 | |
michael@0 | 9213 | nsRefPtr<gfxContext> context = new gfxContext(surface); |
michael@0 | 9214 | |
michael@0 | 9215 | shell->RenderDocument(r, 0, NS_RGB(255, 255, 0), context); |
michael@0 | 9216 | |
michael@0 | 9217 | imgContext->DrawSurface(surface, gfxSize(width, height)); |
michael@0 | 9218 | |
michael@0 | 9219 | nsCOMPtr<imgIEncoder> encoder = do_CreateInstance("@mozilla.org/image/encoder;2?type=image/png"); |
michael@0 | 9220 | NS_ENSURE_TRUE(encoder, NS_ERROR_FAILURE); |
michael@0 | 9221 | encoder->InitFromData(imgSurface->Data(), imgSurface->Stride() * height, |
michael@0 | 9222 | width, height, imgSurface->Stride(), |
michael@0 | 9223 | imgIEncoder::INPUT_FORMAT_HOSTARGB, EmptyString()); |
michael@0 | 9224 | |
michael@0 | 9225 | // XXX not sure if this is the right way to write to a file |
michael@0 | 9226 | nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1"); |
michael@0 | 9227 | NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); |
michael@0 | 9228 | rv = file->InitWithPath(name); |
michael@0 | 9229 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 9230 | |
michael@0 | 9231 | uint64_t length64; |
michael@0 | 9232 | rv = encoder->Available(&length64); |
michael@0 | 9233 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 9234 | if (length64 > UINT32_MAX) |
michael@0 | 9235 | return NS_ERROR_FILE_TOO_BIG; |
michael@0 | 9236 | |
michael@0 | 9237 | uint32_t length = (uint32_t)length64; |
michael@0 | 9238 | |
michael@0 | 9239 | nsCOMPtr<nsIOutputStream> outputStream; |
michael@0 | 9240 | rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file); |
michael@0 | 9241 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 9242 | |
michael@0 | 9243 | nsCOMPtr<nsIOutputStream> bufferedOutputStream; |
michael@0 | 9244 | rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), |
michael@0 | 9245 | outputStream, length); |
michael@0 | 9246 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 9247 | |
michael@0 | 9248 | uint32_t numWritten; |
michael@0 | 9249 | rv = bufferedOutputStream->WriteFrom(encoder, length, &numWritten); |
michael@0 | 9250 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 9251 | |
michael@0 | 9252 | return NS_OK; |
michael@0 | 9253 | } |
michael@0 | 9254 | #endif |
michael@0 | 9255 | |
michael@0 | 9256 | // After an incremental reflow, we verify the correctness by doing a |
michael@0 | 9257 | // full reflow into a fresh frame tree. |
michael@0 | 9258 | bool |
michael@0 | 9259 | PresShell::VerifyIncrementalReflow() |
michael@0 | 9260 | { |
michael@0 | 9261 | if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) { |
michael@0 | 9262 | printf("Building Verification Tree...\n"); |
michael@0 | 9263 | } |
michael@0 | 9264 | |
michael@0 | 9265 | // Create a presentation context to view the new frame tree |
michael@0 | 9266 | nsRefPtr<nsPresContext> cx = |
michael@0 | 9267 | new nsRootPresContext(mDocument, mPresContext->IsPaginated() ? |
michael@0 | 9268 | nsPresContext::eContext_PrintPreview : |
michael@0 | 9269 | nsPresContext::eContext_Galley); |
michael@0 | 9270 | NS_ENSURE_TRUE(cx, false); |
michael@0 | 9271 | |
michael@0 | 9272 | nsDeviceContext *dc = mPresContext->DeviceContext(); |
michael@0 | 9273 | nsresult rv = cx->Init(dc); |
michael@0 | 9274 | NS_ENSURE_SUCCESS(rv, false); |
michael@0 | 9275 | |
michael@0 | 9276 | // Get our scrolling preference |
michael@0 | 9277 | nsView* rootView = mViewManager->GetRootView(); |
michael@0 | 9278 | NS_ENSURE_TRUE(rootView->HasWidget(), false); |
michael@0 | 9279 | nsIWidget* parentWidget = rootView->GetWidget(); |
michael@0 | 9280 | |
michael@0 | 9281 | // Create a new view manager. |
michael@0 | 9282 | nsRefPtr<nsViewManager> vm = new nsViewManager(); |
michael@0 | 9283 | NS_ENSURE_TRUE(vm, false); |
michael@0 | 9284 | rv = vm->Init(dc); |
michael@0 | 9285 | NS_ENSURE_SUCCESS(rv, false); |
michael@0 | 9286 | |
michael@0 | 9287 | // Create a child window of the parent that is our "root view/window" |
michael@0 | 9288 | // Create a view |
michael@0 | 9289 | nsRect tbounds = mPresContext->GetVisibleArea(); |
michael@0 | 9290 | nsView* view = vm->CreateView(tbounds, nullptr); |
michael@0 | 9291 | NS_ENSURE_TRUE(view, false); |
michael@0 | 9292 | |
michael@0 | 9293 | //now create the widget for the view |
michael@0 | 9294 | rv = view->CreateWidgetForParent(parentWidget, nullptr, true); |
michael@0 | 9295 | NS_ENSURE_SUCCESS(rv, false); |
michael@0 | 9296 | |
michael@0 | 9297 | // Setup hierarchical relationship in view manager |
michael@0 | 9298 | vm->SetRootView(view); |
michael@0 | 9299 | |
michael@0 | 9300 | // Make the new presentation context the same size as our |
michael@0 | 9301 | // presentation context. |
michael@0 | 9302 | nsRect r = mPresContext->GetVisibleArea(); |
michael@0 | 9303 | cx->SetVisibleArea(r); |
michael@0 | 9304 | |
michael@0 | 9305 | // Create a new presentation shell to view the document. Use the |
michael@0 | 9306 | // exact same style information that this document has. |
michael@0 | 9307 | nsAutoPtr<nsStyleSet> newSet(CloneStyleSet(mStyleSet)); |
michael@0 | 9308 | nsCOMPtr<nsIPresShell> sh = mDocument->CreateShell(cx, vm, newSet); |
michael@0 | 9309 | NS_ENSURE_TRUE(sh, false); |
michael@0 | 9310 | newSet.forget(); |
michael@0 | 9311 | // Note that after we create the shell, we must make sure to destroy it |
michael@0 | 9312 | sh->SetVerifyReflowEnable(false); // turn off verify reflow while we're reflowing the test frame tree |
michael@0 | 9313 | vm->SetPresShell(sh); |
michael@0 | 9314 | { |
michael@0 | 9315 | nsAutoCauseReflowNotifier crNotifier(this); |
michael@0 | 9316 | sh->Initialize(r.width, r.height); |
michael@0 | 9317 | } |
michael@0 | 9318 | mDocument->BindingManager()->ProcessAttachedQueue(); |
michael@0 | 9319 | sh->FlushPendingNotifications(Flush_Layout); |
michael@0 | 9320 | sh->SetVerifyReflowEnable(true); // turn on verify reflow again now that we're done reflowing the test frame tree |
michael@0 | 9321 | // Force the non-primary presshell to unsuppress; it doesn't want to normally |
michael@0 | 9322 | // because it thinks it's hidden |
michael@0 | 9323 | ((PresShell*)sh.get())->mPaintingSuppressed = false; |
michael@0 | 9324 | if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) { |
michael@0 | 9325 | printf("Verification Tree built, comparing...\n"); |
michael@0 | 9326 | } |
michael@0 | 9327 | |
michael@0 | 9328 | // Now that the document has been reflowed, use its frame tree to |
michael@0 | 9329 | // compare against our frame tree. |
michael@0 | 9330 | nsIFrame* root1 = mFrameConstructor->GetRootFrame(); |
michael@0 | 9331 | nsIFrame* root2 = sh->GetRootFrame(); |
michael@0 | 9332 | bool ok = CompareTrees(mPresContext, root1, cx, root2); |
michael@0 | 9333 | if (!ok && (VERIFY_REFLOW_NOISY & gVerifyReflowFlags)) { |
michael@0 | 9334 | printf("Verify reflow failed, primary tree:\n"); |
michael@0 | 9335 | root1->List(stdout, 0); |
michael@0 | 9336 | printf("Verification tree:\n"); |
michael@0 | 9337 | root2->List(stdout, 0); |
michael@0 | 9338 | } |
michael@0 | 9339 | |
michael@0 | 9340 | #ifdef DEBUG_Eli |
michael@0 | 9341 | // Sample code for dumping page to png |
michael@0 | 9342 | // XXX Needs to be made more flexible |
michael@0 | 9343 | if (!ok) { |
michael@0 | 9344 | nsString stra; |
michael@0 | 9345 | static int num = 0; |
michael@0 | 9346 | stra.AppendLiteral("C:\\mozilla\\mozilla\\debug\\filea"); |
michael@0 | 9347 | stra.AppendInt(num); |
michael@0 | 9348 | stra.AppendLiteral(".png"); |
michael@0 | 9349 | DumpToPNG(sh, stra); |
michael@0 | 9350 | nsString strb; |
michael@0 | 9351 | strb.AppendLiteral("C:\\mozilla\\mozilla\\debug\\fileb"); |
michael@0 | 9352 | strb.AppendInt(num); |
michael@0 | 9353 | strb.AppendLiteral(".png"); |
michael@0 | 9354 | DumpToPNG(this, strb); |
michael@0 | 9355 | ++num; |
michael@0 | 9356 | } |
michael@0 | 9357 | #endif |
michael@0 | 9358 | |
michael@0 | 9359 | sh->EndObservingDocument(); |
michael@0 | 9360 | sh->Destroy(); |
michael@0 | 9361 | if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) { |
michael@0 | 9362 | printf("Finished Verifying Reflow...\n"); |
michael@0 | 9363 | } |
michael@0 | 9364 | |
michael@0 | 9365 | return ok; |
michael@0 | 9366 | } |
michael@0 | 9367 | |
michael@0 | 9368 | // Layout debugging hooks |
michael@0 | 9369 | void |
michael@0 | 9370 | PresShell::ListStyleContexts(nsIFrame *aRootFrame, FILE *out, int32_t aIndent) |
michael@0 | 9371 | { |
michael@0 | 9372 | nsStyleContext *sc = aRootFrame->StyleContext(); |
michael@0 | 9373 | if (sc) |
michael@0 | 9374 | sc->List(out, aIndent); |
michael@0 | 9375 | } |
michael@0 | 9376 | |
michael@0 | 9377 | void |
michael@0 | 9378 | PresShell::ListStyleSheets(FILE *out, int32_t aIndent) |
michael@0 | 9379 | { |
michael@0 | 9380 | int32_t sheetCount = mStyleSet->SheetCount(nsStyleSet::eDocSheet); |
michael@0 | 9381 | for (int32_t i = 0; i < sheetCount; ++i) { |
michael@0 | 9382 | mStyleSet->StyleSheetAt(nsStyleSet::eDocSheet, i)->List(out, aIndent); |
michael@0 | 9383 | fputs("\n", out); |
michael@0 | 9384 | } |
michael@0 | 9385 | } |
michael@0 | 9386 | |
michael@0 | 9387 | void |
michael@0 | 9388 | PresShell::VerifyStyleTree() |
michael@0 | 9389 | { |
michael@0 | 9390 | VERIFY_STYLE_TREE; |
michael@0 | 9391 | } |
michael@0 | 9392 | #endif |
michael@0 | 9393 | |
michael@0 | 9394 | //============================================================= |
michael@0 | 9395 | //============================================================= |
michael@0 | 9396 | //-- Debug Reflow Counts |
michael@0 | 9397 | //============================================================= |
michael@0 | 9398 | //============================================================= |
michael@0 | 9399 | #ifdef MOZ_REFLOW_PERF |
michael@0 | 9400 | //------------------------------------------------------------- |
michael@0 | 9401 | void |
michael@0 | 9402 | PresShell::DumpReflows() |
michael@0 | 9403 | { |
michael@0 | 9404 | if (mReflowCountMgr) { |
michael@0 | 9405 | nsAutoCString uriStr; |
michael@0 | 9406 | if (mDocument) { |
michael@0 | 9407 | nsIURI *uri = mDocument->GetDocumentURI(); |
michael@0 | 9408 | if (uri) { |
michael@0 | 9409 | uri->GetPath(uriStr); |
michael@0 | 9410 | } |
michael@0 | 9411 | } |
michael@0 | 9412 | mReflowCountMgr->DisplayTotals(uriStr.get()); |
michael@0 | 9413 | mReflowCountMgr->DisplayHTMLTotals(uriStr.get()); |
michael@0 | 9414 | mReflowCountMgr->DisplayDiffsInTotals("Differences"); |
michael@0 | 9415 | } |
michael@0 | 9416 | } |
michael@0 | 9417 | |
michael@0 | 9418 | //------------------------------------------------------------- |
michael@0 | 9419 | void |
michael@0 | 9420 | PresShell::CountReflows(const char * aName, nsIFrame * aFrame) |
michael@0 | 9421 | { |
michael@0 | 9422 | if (mReflowCountMgr) { |
michael@0 | 9423 | mReflowCountMgr->Add(aName, aFrame); |
michael@0 | 9424 | } |
michael@0 | 9425 | } |
michael@0 | 9426 | |
michael@0 | 9427 | //------------------------------------------------------------- |
michael@0 | 9428 | void |
michael@0 | 9429 | PresShell::PaintCount(const char * aName, |
michael@0 | 9430 | nsRenderingContext* aRenderingContext, |
michael@0 | 9431 | nsPresContext* aPresContext, |
michael@0 | 9432 | nsIFrame * aFrame, |
michael@0 | 9433 | const nsPoint& aOffset, |
michael@0 | 9434 | uint32_t aColor) |
michael@0 | 9435 | { |
michael@0 | 9436 | if (mReflowCountMgr) { |
michael@0 | 9437 | mReflowCountMgr->PaintCount(aName, aRenderingContext, aPresContext, |
michael@0 | 9438 | aFrame, aOffset, aColor); |
michael@0 | 9439 | } |
michael@0 | 9440 | } |
michael@0 | 9441 | |
michael@0 | 9442 | //------------------------------------------------------------- |
michael@0 | 9443 | void |
michael@0 | 9444 | PresShell::SetPaintFrameCount(bool aPaintFrameCounts) |
michael@0 | 9445 | { |
michael@0 | 9446 | if (mReflowCountMgr) { |
michael@0 | 9447 | mReflowCountMgr->SetPaintFrameCounts(aPaintFrameCounts); |
michael@0 | 9448 | } |
michael@0 | 9449 | } |
michael@0 | 9450 | |
michael@0 | 9451 | bool |
michael@0 | 9452 | PresShell::IsPaintingFrameCounts() |
michael@0 | 9453 | { |
michael@0 | 9454 | if (mReflowCountMgr) |
michael@0 | 9455 | return mReflowCountMgr->IsPaintingFrameCounts(); |
michael@0 | 9456 | return false; |
michael@0 | 9457 | } |
michael@0 | 9458 | |
michael@0 | 9459 | //------------------------------------------------------------------ |
michael@0 | 9460 | //-- Reflow Counter Classes Impls |
michael@0 | 9461 | //------------------------------------------------------------------ |
michael@0 | 9462 | |
michael@0 | 9463 | //------------------------------------------------------------------ |
michael@0 | 9464 | ReflowCounter::ReflowCounter(ReflowCountMgr * aMgr) : |
michael@0 | 9465 | mMgr(aMgr) |
michael@0 | 9466 | { |
michael@0 | 9467 | ClearTotals(); |
michael@0 | 9468 | SetTotalsCache(); |
michael@0 | 9469 | } |
michael@0 | 9470 | |
michael@0 | 9471 | //------------------------------------------------------------------ |
michael@0 | 9472 | ReflowCounter::~ReflowCounter() |
michael@0 | 9473 | { |
michael@0 | 9474 | |
michael@0 | 9475 | } |
michael@0 | 9476 | |
michael@0 | 9477 | //------------------------------------------------------------------ |
michael@0 | 9478 | void ReflowCounter::ClearTotals() |
michael@0 | 9479 | { |
michael@0 | 9480 | mTotal = 0; |
michael@0 | 9481 | } |
michael@0 | 9482 | |
michael@0 | 9483 | //------------------------------------------------------------------ |
michael@0 | 9484 | void ReflowCounter::SetTotalsCache() |
michael@0 | 9485 | { |
michael@0 | 9486 | mCacheTotal = mTotal; |
michael@0 | 9487 | } |
michael@0 | 9488 | |
michael@0 | 9489 | //------------------------------------------------------------------ |
michael@0 | 9490 | void ReflowCounter::CalcDiffInTotals() |
michael@0 | 9491 | { |
michael@0 | 9492 | mCacheTotal = mTotal - mCacheTotal; |
michael@0 | 9493 | } |
michael@0 | 9494 | |
michael@0 | 9495 | //------------------------------------------------------------------ |
michael@0 | 9496 | void ReflowCounter::DisplayTotals(const char * aStr) |
michael@0 | 9497 | { |
michael@0 | 9498 | DisplayTotals(mTotal, aStr?aStr:"Totals"); |
michael@0 | 9499 | } |
michael@0 | 9500 | |
michael@0 | 9501 | //------------------------------------------------------------------ |
michael@0 | 9502 | void ReflowCounter::DisplayDiffTotals(const char * aStr) |
michael@0 | 9503 | { |
michael@0 | 9504 | DisplayTotals(mCacheTotal, aStr?aStr:"Diff Totals"); |
michael@0 | 9505 | } |
michael@0 | 9506 | |
michael@0 | 9507 | //------------------------------------------------------------------ |
michael@0 | 9508 | void ReflowCounter::DisplayHTMLTotals(const char * aStr) |
michael@0 | 9509 | { |
michael@0 | 9510 | DisplayHTMLTotals(mTotal, aStr?aStr:"Totals"); |
michael@0 | 9511 | } |
michael@0 | 9512 | |
michael@0 | 9513 | //------------------------------------------------------------------ |
michael@0 | 9514 | void ReflowCounter::DisplayTotals(uint32_t aTotal, const char * aTitle) |
michael@0 | 9515 | { |
michael@0 | 9516 | // figure total |
michael@0 | 9517 | if (aTotal == 0) { |
michael@0 | 9518 | return; |
michael@0 | 9519 | } |
michael@0 | 9520 | ReflowCounter * gTots = (ReflowCounter *)mMgr->LookUp(kGrandTotalsStr); |
michael@0 | 9521 | |
michael@0 | 9522 | printf("%25s\t", aTitle); |
michael@0 | 9523 | printf("%d\t", aTotal); |
michael@0 | 9524 | if (gTots != this && aTotal > 0) { |
michael@0 | 9525 | gTots->Add(aTotal); |
michael@0 | 9526 | } |
michael@0 | 9527 | } |
michael@0 | 9528 | |
michael@0 | 9529 | //------------------------------------------------------------------ |
michael@0 | 9530 | void ReflowCounter::DisplayHTMLTotals(uint32_t aTotal, const char * aTitle) |
michael@0 | 9531 | { |
michael@0 | 9532 | if (aTotal == 0) { |
michael@0 | 9533 | return; |
michael@0 | 9534 | } |
michael@0 | 9535 | |
michael@0 | 9536 | ReflowCounter * gTots = (ReflowCounter *)mMgr->LookUp(kGrandTotalsStr); |
michael@0 | 9537 | FILE * fd = mMgr->GetOutFile(); |
michael@0 | 9538 | if (!fd) { |
michael@0 | 9539 | return; |
michael@0 | 9540 | } |
michael@0 | 9541 | |
michael@0 | 9542 | fprintf(fd, "<tr><td><center>%s</center></td>", aTitle); |
michael@0 | 9543 | fprintf(fd, "<td><center>%d</center></td></tr>\n", aTotal); |
michael@0 | 9544 | |
michael@0 | 9545 | if (gTots != this && aTotal > 0) { |
michael@0 | 9546 | gTots->Add(aTotal); |
michael@0 | 9547 | } |
michael@0 | 9548 | } |
michael@0 | 9549 | |
michael@0 | 9550 | //------------------------------------------------------------------ |
michael@0 | 9551 | //-- ReflowCountMgr |
michael@0 | 9552 | //------------------------------------------------------------------ |
michael@0 | 9553 | |
michael@0 | 9554 | #define KEY_BUF_SIZE_FOR_PTR 24 // adequate char[] buffer to sprintf a pointer |
michael@0 | 9555 | |
michael@0 | 9556 | ReflowCountMgr::ReflowCountMgr() |
michael@0 | 9557 | { |
michael@0 | 9558 | mCounts = PL_NewHashTable(10, PL_HashString, PL_CompareStrings, |
michael@0 | 9559 | PL_CompareValues, nullptr, nullptr); |
michael@0 | 9560 | mIndiFrameCounts = PL_NewHashTable(10, PL_HashString, PL_CompareStrings, |
michael@0 | 9561 | PL_CompareValues, nullptr, nullptr); |
michael@0 | 9562 | mCycledOnce = false; |
michael@0 | 9563 | mDumpFrameCounts = false; |
michael@0 | 9564 | mDumpFrameByFrameCounts = false; |
michael@0 | 9565 | mPaintFrameByFrameCounts = false; |
michael@0 | 9566 | } |
michael@0 | 9567 | |
michael@0 | 9568 | //------------------------------------------------------------------ |
michael@0 | 9569 | ReflowCountMgr::~ReflowCountMgr() |
michael@0 | 9570 | { |
michael@0 | 9571 | CleanUp(); |
michael@0 | 9572 | } |
michael@0 | 9573 | |
michael@0 | 9574 | //------------------------------------------------------------------ |
michael@0 | 9575 | ReflowCounter * ReflowCountMgr::LookUp(const char * aName) |
michael@0 | 9576 | { |
michael@0 | 9577 | if (nullptr != mCounts) { |
michael@0 | 9578 | ReflowCounter * counter = (ReflowCounter *)PL_HashTableLookup(mCounts, aName); |
michael@0 | 9579 | return counter; |
michael@0 | 9580 | } |
michael@0 | 9581 | return nullptr; |
michael@0 | 9582 | |
michael@0 | 9583 | } |
michael@0 | 9584 | |
michael@0 | 9585 | //------------------------------------------------------------------ |
michael@0 | 9586 | void ReflowCountMgr::Add(const char * aName, nsIFrame * aFrame) |
michael@0 | 9587 | { |
michael@0 | 9588 | NS_ASSERTION(aName != nullptr, "Name shouldn't be null!"); |
michael@0 | 9589 | |
michael@0 | 9590 | if (mDumpFrameCounts && nullptr != mCounts) { |
michael@0 | 9591 | ReflowCounter * counter = (ReflowCounter *)PL_HashTableLookup(mCounts, aName); |
michael@0 | 9592 | if (counter == nullptr) { |
michael@0 | 9593 | counter = new ReflowCounter(this); |
michael@0 | 9594 | char * name = NS_strdup(aName); |
michael@0 | 9595 | NS_ASSERTION(name != nullptr, "null ptr"); |
michael@0 | 9596 | PL_HashTableAdd(mCounts, name, counter); |
michael@0 | 9597 | } |
michael@0 | 9598 | counter->Add(); |
michael@0 | 9599 | } |
michael@0 | 9600 | |
michael@0 | 9601 | if ((mDumpFrameByFrameCounts || mPaintFrameByFrameCounts) && |
michael@0 | 9602 | nullptr != mIndiFrameCounts && |
michael@0 | 9603 | aFrame != nullptr) { |
michael@0 | 9604 | char key[KEY_BUF_SIZE_FOR_PTR]; |
michael@0 | 9605 | sprintf(key, "%p", (void*)aFrame); |
michael@0 | 9606 | IndiReflowCounter * counter = (IndiReflowCounter *)PL_HashTableLookup(mIndiFrameCounts, key); |
michael@0 | 9607 | if (counter == nullptr) { |
michael@0 | 9608 | counter = new IndiReflowCounter(this); |
michael@0 | 9609 | counter->mFrame = aFrame; |
michael@0 | 9610 | counter->mName.AssignASCII(aName); |
michael@0 | 9611 | PL_HashTableAdd(mIndiFrameCounts, NS_strdup(key), counter); |
michael@0 | 9612 | } |
michael@0 | 9613 | // this eliminates extra counts from super classes |
michael@0 | 9614 | if (counter != nullptr && counter->mName.EqualsASCII(aName)) { |
michael@0 | 9615 | counter->mCount++; |
michael@0 | 9616 | counter->mCounter.Add(1); |
michael@0 | 9617 | } |
michael@0 | 9618 | } |
michael@0 | 9619 | } |
michael@0 | 9620 | |
michael@0 | 9621 | //------------------------------------------------------------------ |
michael@0 | 9622 | void ReflowCountMgr::PaintCount(const char* aName, |
michael@0 | 9623 | nsRenderingContext* aRenderingContext, |
michael@0 | 9624 | nsPresContext* aPresContext, |
michael@0 | 9625 | nsIFrame* aFrame, |
michael@0 | 9626 | const nsPoint& aOffset, |
michael@0 | 9627 | uint32_t aColor) |
michael@0 | 9628 | { |
michael@0 | 9629 | if (mPaintFrameByFrameCounts && |
michael@0 | 9630 | nullptr != mIndiFrameCounts && |
michael@0 | 9631 | aFrame != nullptr) { |
michael@0 | 9632 | char key[KEY_BUF_SIZE_FOR_PTR]; |
michael@0 | 9633 | sprintf(key, "%p", (void*)aFrame); |
michael@0 | 9634 | IndiReflowCounter * counter = |
michael@0 | 9635 | (IndiReflowCounter *)PL_HashTableLookup(mIndiFrameCounts, key); |
michael@0 | 9636 | if (counter != nullptr && counter->mName.EqualsASCII(aName)) { |
michael@0 | 9637 | aRenderingContext->PushState(); |
michael@0 | 9638 | aRenderingContext->Translate(aOffset); |
michael@0 | 9639 | nsFont font("Times", NS_FONT_STYLE_NORMAL, NS_FONT_VARIANT_NORMAL, |
michael@0 | 9640 | NS_FONT_WEIGHT_NORMAL, NS_FONT_STRETCH_NORMAL, 0, |
michael@0 | 9641 | nsPresContext::CSSPixelsToAppUnits(11)); |
michael@0 | 9642 | |
michael@0 | 9643 | nsRefPtr<nsFontMetrics> fm; |
michael@0 | 9644 | aPresContext->DeviceContext()->GetMetricsFor(font, |
michael@0 | 9645 | // We have one frame, therefore we must have a root... |
michael@0 | 9646 | aPresContext->GetPresShell()->GetRootFrame()-> |
michael@0 | 9647 | StyleFont()->mLanguage, |
michael@0 | 9648 | aPresContext->GetUserFontSet(), |
michael@0 | 9649 | aPresContext->GetTextPerfMetrics(), |
michael@0 | 9650 | *getter_AddRefs(fm)); |
michael@0 | 9651 | |
michael@0 | 9652 | aRenderingContext->SetFont(fm); |
michael@0 | 9653 | char buf[16]; |
michael@0 | 9654 | sprintf(buf, "%d", counter->mCount); |
michael@0 | 9655 | nscoord x = 0, y = fm->MaxAscent(); |
michael@0 | 9656 | nscoord width, height = fm->MaxHeight(); |
michael@0 | 9657 | aRenderingContext->SetTextRunRTL(false); |
michael@0 | 9658 | width = aRenderingContext->GetWidth(buf); |
michael@0 | 9659 | |
michael@0 | 9660 | uint32_t color; |
michael@0 | 9661 | uint32_t color2; |
michael@0 | 9662 | if (aColor != 0) { |
michael@0 | 9663 | color = aColor; |
michael@0 | 9664 | color2 = NS_RGB(0,0,0); |
michael@0 | 9665 | } else { |
michael@0 | 9666 | uint8_t rc = 0, gc = 0, bc = 0; |
michael@0 | 9667 | if (counter->mCount < 5) { |
michael@0 | 9668 | rc = 255; |
michael@0 | 9669 | gc = 255; |
michael@0 | 9670 | } else if ( counter->mCount < 11) { |
michael@0 | 9671 | gc = 255; |
michael@0 | 9672 | } else { |
michael@0 | 9673 | rc = 255; |
michael@0 | 9674 | } |
michael@0 | 9675 | color = NS_RGB(rc,gc,bc); |
michael@0 | 9676 | color2 = NS_RGB(rc/2,gc/2,bc/2); |
michael@0 | 9677 | } |
michael@0 | 9678 | |
michael@0 | 9679 | nsRect rect(0,0, width+15, height+15); |
michael@0 | 9680 | aRenderingContext->SetColor(NS_RGB(0,0,0)); |
michael@0 | 9681 | aRenderingContext->FillRect(rect); |
michael@0 | 9682 | aRenderingContext->SetColor(color2); |
michael@0 | 9683 | aRenderingContext->DrawString(buf, strlen(buf), x+15,y+15); |
michael@0 | 9684 | aRenderingContext->SetColor(color); |
michael@0 | 9685 | aRenderingContext->DrawString(buf, strlen(buf), x,y); |
michael@0 | 9686 | |
michael@0 | 9687 | aRenderingContext->PopState(); |
michael@0 | 9688 | } |
michael@0 | 9689 | } |
michael@0 | 9690 | } |
michael@0 | 9691 | |
michael@0 | 9692 | //------------------------------------------------------------------ |
michael@0 | 9693 | int ReflowCountMgr::RemoveItems(PLHashEntry *he, int i, void *arg) |
michael@0 | 9694 | { |
michael@0 | 9695 | char *str = (char *)he->key; |
michael@0 | 9696 | ReflowCounter * counter = (ReflowCounter *)he->value; |
michael@0 | 9697 | delete counter; |
michael@0 | 9698 | NS_Free(str); |
michael@0 | 9699 | |
michael@0 | 9700 | return HT_ENUMERATE_REMOVE; |
michael@0 | 9701 | } |
michael@0 | 9702 | |
michael@0 | 9703 | //------------------------------------------------------------------ |
michael@0 | 9704 | int ReflowCountMgr::RemoveIndiItems(PLHashEntry *he, int i, void *arg) |
michael@0 | 9705 | { |
michael@0 | 9706 | char *str = (char *)he->key; |
michael@0 | 9707 | IndiReflowCounter * counter = (IndiReflowCounter *)he->value; |
michael@0 | 9708 | delete counter; |
michael@0 | 9709 | NS_Free(str); |
michael@0 | 9710 | |
michael@0 | 9711 | return HT_ENUMERATE_REMOVE; |
michael@0 | 9712 | } |
michael@0 | 9713 | |
michael@0 | 9714 | //------------------------------------------------------------------ |
michael@0 | 9715 | void ReflowCountMgr::CleanUp() |
michael@0 | 9716 | { |
michael@0 | 9717 | if (nullptr != mCounts) { |
michael@0 | 9718 | PL_HashTableEnumerateEntries(mCounts, RemoveItems, nullptr); |
michael@0 | 9719 | PL_HashTableDestroy(mCounts); |
michael@0 | 9720 | mCounts = nullptr; |
michael@0 | 9721 | } |
michael@0 | 9722 | |
michael@0 | 9723 | if (nullptr != mIndiFrameCounts) { |
michael@0 | 9724 | PL_HashTableEnumerateEntries(mIndiFrameCounts, RemoveIndiItems, nullptr); |
michael@0 | 9725 | PL_HashTableDestroy(mIndiFrameCounts); |
michael@0 | 9726 | mIndiFrameCounts = nullptr; |
michael@0 | 9727 | } |
michael@0 | 9728 | } |
michael@0 | 9729 | |
michael@0 | 9730 | //------------------------------------------------------------------ |
michael@0 | 9731 | int ReflowCountMgr::DoSingleTotal(PLHashEntry *he, int i, void *arg) |
michael@0 | 9732 | { |
michael@0 | 9733 | char *str = (char *)he->key; |
michael@0 | 9734 | ReflowCounter * counter = (ReflowCounter *)he->value; |
michael@0 | 9735 | |
michael@0 | 9736 | counter->DisplayTotals(str); |
michael@0 | 9737 | |
michael@0 | 9738 | return HT_ENUMERATE_NEXT; |
michael@0 | 9739 | } |
michael@0 | 9740 | |
michael@0 | 9741 | //------------------------------------------------------------------ |
michael@0 | 9742 | void ReflowCountMgr::DoGrandTotals() |
michael@0 | 9743 | { |
michael@0 | 9744 | if (nullptr != mCounts) { |
michael@0 | 9745 | ReflowCounter * gTots = (ReflowCounter *)PL_HashTableLookup(mCounts, kGrandTotalsStr); |
michael@0 | 9746 | if (gTots == nullptr) { |
michael@0 | 9747 | gTots = new ReflowCounter(this); |
michael@0 | 9748 | PL_HashTableAdd(mCounts, NS_strdup(kGrandTotalsStr), gTots); |
michael@0 | 9749 | } else { |
michael@0 | 9750 | gTots->ClearTotals(); |
michael@0 | 9751 | } |
michael@0 | 9752 | |
michael@0 | 9753 | printf("\t\t\t\tTotal\n"); |
michael@0 | 9754 | for (uint32_t i=0;i<78;i++) { |
michael@0 | 9755 | printf("-"); |
michael@0 | 9756 | } |
michael@0 | 9757 | printf("\n"); |
michael@0 | 9758 | PL_HashTableEnumerateEntries(mCounts, DoSingleTotal, this); |
michael@0 | 9759 | } |
michael@0 | 9760 | } |
michael@0 | 9761 | |
michael@0 | 9762 | static void RecurseIndiTotals(nsPresContext* aPresContext, |
michael@0 | 9763 | PLHashTable * aHT, |
michael@0 | 9764 | nsIFrame * aParentFrame, |
michael@0 | 9765 | int32_t aLevel) |
michael@0 | 9766 | { |
michael@0 | 9767 | if (aParentFrame == nullptr) { |
michael@0 | 9768 | return; |
michael@0 | 9769 | } |
michael@0 | 9770 | |
michael@0 | 9771 | char key[KEY_BUF_SIZE_FOR_PTR]; |
michael@0 | 9772 | sprintf(key, "%p", (void*)aParentFrame); |
michael@0 | 9773 | IndiReflowCounter * counter = (IndiReflowCounter *)PL_HashTableLookup(aHT, key); |
michael@0 | 9774 | if (counter) { |
michael@0 | 9775 | counter->mHasBeenOutput = true; |
michael@0 | 9776 | char * name = ToNewCString(counter->mName); |
michael@0 | 9777 | for (int32_t i=0;i<aLevel;i++) printf(" "); |
michael@0 | 9778 | printf("%s - %p [%d][", name, (void*)aParentFrame, counter->mCount); |
michael@0 | 9779 | printf("%d", counter->mCounter.GetTotal()); |
michael@0 | 9780 | printf("]\n"); |
michael@0 | 9781 | nsMemory::Free(name); |
michael@0 | 9782 | } |
michael@0 | 9783 | |
michael@0 | 9784 | nsIFrame* child = aParentFrame->GetFirstPrincipalChild(); |
michael@0 | 9785 | while (child) { |
michael@0 | 9786 | RecurseIndiTotals(aPresContext, aHT, child, aLevel+1); |
michael@0 | 9787 | child = child->GetNextSibling(); |
michael@0 | 9788 | } |
michael@0 | 9789 | |
michael@0 | 9790 | } |
michael@0 | 9791 | |
michael@0 | 9792 | //------------------------------------------------------------------ |
michael@0 | 9793 | int ReflowCountMgr::DoSingleIndi(PLHashEntry *he, int i, void *arg) |
michael@0 | 9794 | { |
michael@0 | 9795 | IndiReflowCounter * counter = (IndiReflowCounter *)he->value; |
michael@0 | 9796 | if (counter && !counter->mHasBeenOutput) { |
michael@0 | 9797 | char * name = ToNewCString(counter->mName); |
michael@0 | 9798 | printf("%s - %p [%d][", name, (void*)counter->mFrame, counter->mCount); |
michael@0 | 9799 | printf("%d", counter->mCounter.GetTotal()); |
michael@0 | 9800 | printf("]\n"); |
michael@0 | 9801 | nsMemory::Free(name); |
michael@0 | 9802 | } |
michael@0 | 9803 | return HT_ENUMERATE_NEXT; |
michael@0 | 9804 | } |
michael@0 | 9805 | |
michael@0 | 9806 | //------------------------------------------------------------------ |
michael@0 | 9807 | void ReflowCountMgr::DoIndiTotalsTree() |
michael@0 | 9808 | { |
michael@0 | 9809 | if (nullptr != mCounts) { |
michael@0 | 9810 | printf("\n------------------------------------------------\n"); |
michael@0 | 9811 | printf("-- Individual Frame Counts\n"); |
michael@0 | 9812 | printf("------------------------------------------------\n"); |
michael@0 | 9813 | |
michael@0 | 9814 | if (mPresShell) { |
michael@0 | 9815 | nsIFrame * rootFrame = mPresShell->FrameManager()->GetRootFrame(); |
michael@0 | 9816 | RecurseIndiTotals(mPresContext, mIndiFrameCounts, rootFrame, 0); |
michael@0 | 9817 | printf("------------------------------------------------\n"); |
michael@0 | 9818 | printf("-- Individual Counts of Frames not in Root Tree\n"); |
michael@0 | 9819 | printf("------------------------------------------------\n"); |
michael@0 | 9820 | PL_HashTableEnumerateEntries(mIndiFrameCounts, DoSingleIndi, this); |
michael@0 | 9821 | } |
michael@0 | 9822 | } |
michael@0 | 9823 | } |
michael@0 | 9824 | |
michael@0 | 9825 | //------------------------------------------------------------------ |
michael@0 | 9826 | int ReflowCountMgr::DoSingleHTMLTotal(PLHashEntry *he, int i, void *arg) |
michael@0 | 9827 | { |
michael@0 | 9828 | char *str = (char *)he->key; |
michael@0 | 9829 | ReflowCounter * counter = (ReflowCounter *)he->value; |
michael@0 | 9830 | |
michael@0 | 9831 | counter->DisplayHTMLTotals(str); |
michael@0 | 9832 | |
michael@0 | 9833 | return HT_ENUMERATE_NEXT; |
michael@0 | 9834 | } |
michael@0 | 9835 | |
michael@0 | 9836 | //------------------------------------------------------------------ |
michael@0 | 9837 | void ReflowCountMgr::DoGrandHTMLTotals() |
michael@0 | 9838 | { |
michael@0 | 9839 | if (nullptr != mCounts) { |
michael@0 | 9840 | ReflowCounter * gTots = (ReflowCounter *)PL_HashTableLookup(mCounts, kGrandTotalsStr); |
michael@0 | 9841 | if (gTots == nullptr) { |
michael@0 | 9842 | gTots = new ReflowCounter(this); |
michael@0 | 9843 | PL_HashTableAdd(mCounts, NS_strdup(kGrandTotalsStr), gTots); |
michael@0 | 9844 | } else { |
michael@0 | 9845 | gTots->ClearTotals(); |
michael@0 | 9846 | } |
michael@0 | 9847 | |
michael@0 | 9848 | static const char * title[] = {"Class", "Reflows"}; |
michael@0 | 9849 | fprintf(mFD, "<tr>"); |
michael@0 | 9850 | for (uint32_t i=0; i < ArrayLength(title); i++) { |
michael@0 | 9851 | fprintf(mFD, "<td><center><b>%s<b></center></td>", title[i]); |
michael@0 | 9852 | } |
michael@0 | 9853 | fprintf(mFD, "</tr>\n"); |
michael@0 | 9854 | PL_HashTableEnumerateEntries(mCounts, DoSingleHTMLTotal, this); |
michael@0 | 9855 | } |
michael@0 | 9856 | } |
michael@0 | 9857 | |
michael@0 | 9858 | //------------------------------------ |
michael@0 | 9859 | void ReflowCountMgr::DisplayTotals(const char * aStr) |
michael@0 | 9860 | { |
michael@0 | 9861 | #ifdef DEBUG_rods |
michael@0 | 9862 | printf("%s\n", aStr?aStr:"No name"); |
michael@0 | 9863 | #endif |
michael@0 | 9864 | if (mDumpFrameCounts) { |
michael@0 | 9865 | DoGrandTotals(); |
michael@0 | 9866 | } |
michael@0 | 9867 | if (mDumpFrameByFrameCounts) { |
michael@0 | 9868 | DoIndiTotalsTree(); |
michael@0 | 9869 | } |
michael@0 | 9870 | |
michael@0 | 9871 | } |
michael@0 | 9872 | //------------------------------------ |
michael@0 | 9873 | void ReflowCountMgr::DisplayHTMLTotals(const char * aStr) |
michael@0 | 9874 | { |
michael@0 | 9875 | #ifdef WIN32x // XXX NOT XP! |
michael@0 | 9876 | char name[1024]; |
michael@0 | 9877 | |
michael@0 | 9878 | char * sptr = strrchr(aStr, '/'); |
michael@0 | 9879 | if (sptr) { |
michael@0 | 9880 | sptr++; |
michael@0 | 9881 | strcpy(name, sptr); |
michael@0 | 9882 | char * eptr = strrchr(name, '.'); |
michael@0 | 9883 | if (eptr) { |
michael@0 | 9884 | *eptr = 0; |
michael@0 | 9885 | } |
michael@0 | 9886 | strcat(name, "_stats.html"); |
michael@0 | 9887 | } |
michael@0 | 9888 | mFD = fopen(name, "w"); |
michael@0 | 9889 | if (mFD) { |
michael@0 | 9890 | fprintf(mFD, "<html><head><title>Reflow Stats</title></head><body>\n"); |
michael@0 | 9891 | const char * title = aStr?aStr:"No name"; |
michael@0 | 9892 | fprintf(mFD, "<center><b>%s</b><br><table border=1 style=\"background-color:#e0e0e0\">", title); |
michael@0 | 9893 | DoGrandHTMLTotals(); |
michael@0 | 9894 | fprintf(mFD, "</center></table>\n"); |
michael@0 | 9895 | fprintf(mFD, "</body></html>\n"); |
michael@0 | 9896 | fclose(mFD); |
michael@0 | 9897 | mFD = nullptr; |
michael@0 | 9898 | } |
michael@0 | 9899 | #endif // not XP! |
michael@0 | 9900 | } |
michael@0 | 9901 | |
michael@0 | 9902 | //------------------------------------------------------------------ |
michael@0 | 9903 | int ReflowCountMgr::DoClearTotals(PLHashEntry *he, int i, void *arg) |
michael@0 | 9904 | { |
michael@0 | 9905 | ReflowCounter * counter = (ReflowCounter *)he->value; |
michael@0 | 9906 | counter->ClearTotals(); |
michael@0 | 9907 | |
michael@0 | 9908 | return HT_ENUMERATE_NEXT; |
michael@0 | 9909 | } |
michael@0 | 9910 | |
michael@0 | 9911 | //------------------------------------------------------------------ |
michael@0 | 9912 | void ReflowCountMgr::ClearTotals() |
michael@0 | 9913 | { |
michael@0 | 9914 | PL_HashTableEnumerateEntries(mCounts, DoClearTotals, this); |
michael@0 | 9915 | } |
michael@0 | 9916 | |
michael@0 | 9917 | //------------------------------------------------------------------ |
michael@0 | 9918 | void ReflowCountMgr::ClearGrandTotals() |
michael@0 | 9919 | { |
michael@0 | 9920 | if (nullptr != mCounts) { |
michael@0 | 9921 | ReflowCounter * gTots = (ReflowCounter *)PL_HashTableLookup(mCounts, kGrandTotalsStr); |
michael@0 | 9922 | if (gTots == nullptr) { |
michael@0 | 9923 | gTots = new ReflowCounter(this); |
michael@0 | 9924 | PL_HashTableAdd(mCounts, NS_strdup(kGrandTotalsStr), gTots); |
michael@0 | 9925 | } else { |
michael@0 | 9926 | gTots->ClearTotals(); |
michael@0 | 9927 | gTots->SetTotalsCache(); |
michael@0 | 9928 | } |
michael@0 | 9929 | } |
michael@0 | 9930 | } |
michael@0 | 9931 | |
michael@0 | 9932 | //------------------------------------------------------------------ |
michael@0 | 9933 | int ReflowCountMgr::DoDisplayDiffTotals(PLHashEntry *he, int i, void *arg) |
michael@0 | 9934 | { |
michael@0 | 9935 | bool cycledOnce = (arg != 0); |
michael@0 | 9936 | |
michael@0 | 9937 | char *str = (char *)he->key; |
michael@0 | 9938 | ReflowCounter * counter = (ReflowCounter *)he->value; |
michael@0 | 9939 | |
michael@0 | 9940 | if (cycledOnce) { |
michael@0 | 9941 | counter->CalcDiffInTotals(); |
michael@0 | 9942 | counter->DisplayDiffTotals(str); |
michael@0 | 9943 | } |
michael@0 | 9944 | counter->SetTotalsCache(); |
michael@0 | 9945 | |
michael@0 | 9946 | return HT_ENUMERATE_NEXT; |
michael@0 | 9947 | } |
michael@0 | 9948 | |
michael@0 | 9949 | //------------------------------------------------------------------ |
michael@0 | 9950 | void ReflowCountMgr::DisplayDiffsInTotals(const char * aStr) |
michael@0 | 9951 | { |
michael@0 | 9952 | if (mCycledOnce) { |
michael@0 | 9953 | printf("Differences\n"); |
michael@0 | 9954 | for (int32_t i=0;i<78;i++) { |
michael@0 | 9955 | printf("-"); |
michael@0 | 9956 | } |
michael@0 | 9957 | printf("\n"); |
michael@0 | 9958 | ClearGrandTotals(); |
michael@0 | 9959 | } |
michael@0 | 9960 | PL_HashTableEnumerateEntries(mCounts, DoDisplayDiffTotals, (void *)mCycledOnce); |
michael@0 | 9961 | |
michael@0 | 9962 | mCycledOnce = true; |
michael@0 | 9963 | } |
michael@0 | 9964 | |
michael@0 | 9965 | #endif // MOZ_REFLOW_PERF |
michael@0 | 9966 | |
michael@0 | 9967 | // make a color string like #RRGGBB |
michael@0 | 9968 | void ColorToString(nscolor aColor, nsAutoString &aString) |
michael@0 | 9969 | { |
michael@0 | 9970 | char buf[8]; |
michael@0 | 9971 | |
michael@0 | 9972 | PR_snprintf(buf, sizeof(buf), "#%02x%02x%02x", |
michael@0 | 9973 | NS_GET_R(aColor), NS_GET_G(aColor), NS_GET_B(aColor)); |
michael@0 | 9974 | CopyASCIItoUTF16(buf, aString); |
michael@0 | 9975 | } |
michael@0 | 9976 | |
michael@0 | 9977 | nsIFrame* nsIPresShell::GetAbsoluteContainingBlock(nsIFrame *aFrame) |
michael@0 | 9978 | { |
michael@0 | 9979 | return FrameConstructor()->GetAbsoluteContainingBlock(aFrame, |
michael@0 | 9980 | nsCSSFrameConstructor::ABS_POS); |
michael@0 | 9981 | } |
michael@0 | 9982 | |
michael@0 | 9983 | #ifdef ACCESSIBILITY |
michael@0 | 9984 | bool |
michael@0 | 9985 | nsIPresShell::IsAccessibilityActive() |
michael@0 | 9986 | { |
michael@0 | 9987 | return GetAccService() != nullptr; |
michael@0 | 9988 | } |
michael@0 | 9989 | |
michael@0 | 9990 | nsAccessibilityService* |
michael@0 | 9991 | nsIPresShell::AccService() |
michael@0 | 9992 | { |
michael@0 | 9993 | return GetAccService(); |
michael@0 | 9994 | } |
michael@0 | 9995 | #endif |
michael@0 | 9996 | |
michael@0 | 9997 | void nsIPresShell::InitializeStatics() |
michael@0 | 9998 | { |
michael@0 | 9999 | NS_ASSERTION(!gCaptureTouchList, "InitializeStatics called multiple times!"); |
michael@0 | 10000 | gCaptureTouchList = new nsRefPtrHashtable<nsUint32HashKey, dom::Touch>; |
michael@0 | 10001 | gPointerCaptureList = new nsRefPtrHashtable<nsUint32HashKey, nsIContent>; |
michael@0 | 10002 | gActivePointersIds = new nsClassHashtable<nsUint32HashKey, PointerInfo>; |
michael@0 | 10003 | } |
michael@0 | 10004 | |
michael@0 | 10005 | void nsIPresShell::ReleaseStatics() |
michael@0 | 10006 | { |
michael@0 | 10007 | NS_ASSERTION(gCaptureTouchList, "ReleaseStatics called without Initialize!"); |
michael@0 | 10008 | delete gCaptureTouchList; |
michael@0 | 10009 | gCaptureTouchList = nullptr; |
michael@0 | 10010 | delete gPointerCaptureList; |
michael@0 | 10011 | gPointerCaptureList = nullptr; |
michael@0 | 10012 | delete gActivePointersIds; |
michael@0 | 10013 | gActivePointersIds = nullptr; |
michael@0 | 10014 | } |
michael@0 | 10015 | |
michael@0 | 10016 | // Asks our docshell whether we're active. |
michael@0 | 10017 | void PresShell::QueryIsActive() |
michael@0 | 10018 | { |
michael@0 | 10019 | nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak(); |
michael@0 | 10020 | if (mDocument) { |
michael@0 | 10021 | nsIDocument* displayDoc = mDocument->GetDisplayDocument(); |
michael@0 | 10022 | if (displayDoc) { |
michael@0 | 10023 | // Ok, we're an external resource document -- we need to use our display |
michael@0 | 10024 | // document's docshell to determine "IsActive" status, since we lack |
michael@0 | 10025 | // a container. |
michael@0 | 10026 | NS_ABORT_IF_FALSE(!container, |
michael@0 | 10027 | "external resource doc shouldn't have " |
michael@0 | 10028 | "its own container"); |
michael@0 | 10029 | |
michael@0 | 10030 | nsIPresShell* displayPresShell = displayDoc->GetShell(); |
michael@0 | 10031 | if (displayPresShell) { |
michael@0 | 10032 | container = displayPresShell->GetPresContext()->GetContainerWeak(); |
michael@0 | 10033 | } |
michael@0 | 10034 | } |
michael@0 | 10035 | } |
michael@0 | 10036 | |
michael@0 | 10037 | nsCOMPtr<nsIDocShell> docshell(do_QueryInterface(container)); |
michael@0 | 10038 | if (docshell) { |
michael@0 | 10039 | bool isActive; |
michael@0 | 10040 | nsresult rv = docshell->GetIsActive(&isActive); |
michael@0 | 10041 | if (NS_SUCCEEDED(rv)) |
michael@0 | 10042 | SetIsActive(isActive); |
michael@0 | 10043 | } |
michael@0 | 10044 | } |
michael@0 | 10045 | |
michael@0 | 10046 | // Helper for propagating mIsActive changes to external resources |
michael@0 | 10047 | static bool |
michael@0 | 10048 | SetExternalResourceIsActive(nsIDocument* aDocument, void* aClosure) |
michael@0 | 10049 | { |
michael@0 | 10050 | nsIPresShell* shell = aDocument->GetShell(); |
michael@0 | 10051 | if (shell) { |
michael@0 | 10052 | shell->SetIsActive(*static_cast<bool*>(aClosure)); |
michael@0 | 10053 | } |
michael@0 | 10054 | return true; |
michael@0 | 10055 | } |
michael@0 | 10056 | |
michael@0 | 10057 | static void |
michael@0 | 10058 | SetPluginIsActive(nsIContent* aContent, void* aClosure) |
michael@0 | 10059 | { |
michael@0 | 10060 | nsIFrame *frame = aContent->GetPrimaryFrame(); |
michael@0 | 10061 | nsIObjectFrame *objectFrame = do_QueryFrame(frame); |
michael@0 | 10062 | if (objectFrame) { |
michael@0 | 10063 | objectFrame->SetIsDocumentActive(*static_cast<bool*>(aClosure)); |
michael@0 | 10064 | } |
michael@0 | 10065 | } |
michael@0 | 10066 | |
michael@0 | 10067 | nsresult |
michael@0 | 10068 | PresShell::SetIsActive(bool aIsActive) |
michael@0 | 10069 | { |
michael@0 | 10070 | NS_PRECONDITION(mDocument, "should only be called with a document"); |
michael@0 | 10071 | |
michael@0 | 10072 | mIsActive = aIsActive; |
michael@0 | 10073 | nsPresContext* presContext = GetPresContext(); |
michael@0 | 10074 | if (presContext && |
michael@0 | 10075 | presContext->RefreshDriver()->PresContext() == presContext) { |
michael@0 | 10076 | presContext->RefreshDriver()->SetThrottled(!mIsActive); |
michael@0 | 10077 | } |
michael@0 | 10078 | |
michael@0 | 10079 | // Propagate state-change to my resource documents' PresShells |
michael@0 | 10080 | mDocument->EnumerateExternalResources(SetExternalResourceIsActive, |
michael@0 | 10081 | &aIsActive); |
michael@0 | 10082 | mDocument->EnumerateFreezableElements(SetPluginIsActive, |
michael@0 | 10083 | &aIsActive); |
michael@0 | 10084 | nsresult rv = UpdateImageLockingState(); |
michael@0 | 10085 | #ifdef ACCESSIBILITY |
michael@0 | 10086 | if (aIsActive) { |
michael@0 | 10087 | nsAccessibilityService* accService = AccService(); |
michael@0 | 10088 | if (accService) { |
michael@0 | 10089 | accService->PresShellActivated(this); |
michael@0 | 10090 | } |
michael@0 | 10091 | } |
michael@0 | 10092 | #endif |
michael@0 | 10093 | |
michael@0 | 10094 | // We have this odd special case here because remote content behaves |
michael@0 | 10095 | // differently from same-process content when "hidden". In |
michael@0 | 10096 | // desktop-type "browser UIs", hidden "tabs" have documents that are |
michael@0 | 10097 | // part of the chrome tree. When the tabs are hidden, their content |
michael@0 | 10098 | // is no longer part of the visible document tree, and the layers |
michael@0 | 10099 | // for the content are naturally released. |
michael@0 | 10100 | // |
michael@0 | 10101 | // Remote content is its own top-level tree in its subprocess. When |
michael@0 | 10102 | // it's "hidden", there's no transaction in which the document |
michael@0 | 10103 | // thinks it's not visible, so layers can be retained forever. This |
michael@0 | 10104 | // is problematic when those layers uselessly hold on to precious |
michael@0 | 10105 | // resources like directly texturable memory. |
michael@0 | 10106 | // |
michael@0 | 10107 | // PresShell::SetIsActive() is the first C++ entry point at which we |
michael@0 | 10108 | // (i) know that our parent process wants our content to be hidden; |
michael@0 | 10109 | // and (ii) has easy access to the TabChild. So we use this |
michael@0 | 10110 | // notification to signal the TabChild to drop its layer tree and |
michael@0 | 10111 | // stop trying to repaint. |
michael@0 | 10112 | if (TabChild* tab = TabChild::GetFrom(this)) { |
michael@0 | 10113 | if (aIsActive) { |
michael@0 | 10114 | tab->MakeVisible(); |
michael@0 | 10115 | if (!mIsZombie) { |
michael@0 | 10116 | if (nsIFrame* root = mFrameConstructor->GetRootFrame()) { |
michael@0 | 10117 | FrameLayerBuilder::InvalidateAllLayersForFrame( |
michael@0 | 10118 | nsLayoutUtils::GetDisplayRootFrame(root)); |
michael@0 | 10119 | root->SchedulePaint(); |
michael@0 | 10120 | } |
michael@0 | 10121 | } |
michael@0 | 10122 | } else { |
michael@0 | 10123 | tab->MakeHidden(); |
michael@0 | 10124 | } |
michael@0 | 10125 | } |
michael@0 | 10126 | |
michael@0 | 10127 | return rv; |
michael@0 | 10128 | } |
michael@0 | 10129 | |
michael@0 | 10130 | /* |
michael@0 | 10131 | * Determines the current image locking state. Called when one of the |
michael@0 | 10132 | * dependent factors changes. |
michael@0 | 10133 | */ |
michael@0 | 10134 | nsresult |
michael@0 | 10135 | PresShell::UpdateImageLockingState() |
michael@0 | 10136 | { |
michael@0 | 10137 | // We're locked if we're both thawed and active. |
michael@0 | 10138 | return mDocument->SetImageLockingState(!mFrozen && mIsActive); |
michael@0 | 10139 | } |
michael@0 | 10140 | |
michael@0 | 10141 | PresShell* |
michael@0 | 10142 | PresShell::GetRootPresShell() |
michael@0 | 10143 | { |
michael@0 | 10144 | if (mPresContext) { |
michael@0 | 10145 | nsPresContext* rootPresContext = mPresContext->GetRootPresContext(); |
michael@0 | 10146 | if (rootPresContext) { |
michael@0 | 10147 | return static_cast<PresShell*>(rootPresContext->PresShell()); |
michael@0 | 10148 | } |
michael@0 | 10149 | } |
michael@0 | 10150 | return nullptr; |
michael@0 | 10151 | } |
michael@0 | 10152 | |
michael@0 | 10153 | void |
michael@0 | 10154 | PresShell::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, |
michael@0 | 10155 | nsArenaMemoryStats *aArenaObjectsSize, |
michael@0 | 10156 | size_t *aPresShellSize, |
michael@0 | 10157 | size_t *aStyleSetsSize, |
michael@0 | 10158 | size_t *aTextRunsSize, |
michael@0 | 10159 | size_t *aPresContextSize) |
michael@0 | 10160 | { |
michael@0 | 10161 | mFrameArena.AddSizeOfExcludingThis(aMallocSizeOf, aArenaObjectsSize); |
michael@0 | 10162 | *aPresShellSize += aMallocSizeOf(this); |
michael@0 | 10163 | *aPresShellSize += aArenaObjectsSize->mOther; |
michael@0 | 10164 | |
michael@0 | 10165 | *aStyleSetsSize += StyleSet()->SizeOfIncludingThis(aMallocSizeOf); |
michael@0 | 10166 | |
michael@0 | 10167 | *aTextRunsSize += SizeOfTextRuns(aMallocSizeOf); |
michael@0 | 10168 | |
michael@0 | 10169 | *aPresContextSize += mPresContext->SizeOfIncludingThis(aMallocSizeOf); |
michael@0 | 10170 | } |
michael@0 | 10171 | |
michael@0 | 10172 | size_t |
michael@0 | 10173 | PresShell::SizeOfTextRuns(MallocSizeOf aMallocSizeOf) const |
michael@0 | 10174 | { |
michael@0 | 10175 | nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); |
michael@0 | 10176 | if (!rootFrame) { |
michael@0 | 10177 | return 0; |
michael@0 | 10178 | } |
michael@0 | 10179 | |
michael@0 | 10180 | // clear the TEXT_RUN_MEMORY_ACCOUNTED flags |
michael@0 | 10181 | nsLayoutUtils::SizeOfTextRunsForFrames(rootFrame, nullptr, |
michael@0 | 10182 | /* clear = */true); |
michael@0 | 10183 | |
michael@0 | 10184 | // collect the total memory in use for textruns |
michael@0 | 10185 | return nsLayoutUtils::SizeOfTextRunsForFrames(rootFrame, aMallocSizeOf, |
michael@0 | 10186 | /* clear = */false); |
michael@0 | 10187 | } |
michael@0 | 10188 | |
michael@0 | 10189 | void |
michael@0 | 10190 | nsIPresShell::MarkFixedFramesForReflow(IntrinsicDirty aIntrinsicDirty) |
michael@0 | 10191 | { |
michael@0 | 10192 | nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); |
michael@0 | 10193 | if (rootFrame) { |
michael@0 | 10194 | const nsFrameList& childList = rootFrame->GetChildList(nsIFrame::kFixedList); |
michael@0 | 10195 | for (nsFrameList::Enumerator e(childList); !e.AtEnd(); e.Next()) { |
michael@0 | 10196 | FrameNeedsReflow(e.get(), aIntrinsicDirty, NS_FRAME_IS_DIRTY); |
michael@0 | 10197 | } |
michael@0 | 10198 | } |
michael@0 | 10199 | } |
michael@0 | 10200 | |
michael@0 | 10201 | void |
michael@0 | 10202 | nsIPresShell::SetScrollPositionClampingScrollPortSize(nscoord aWidth, nscoord aHeight) |
michael@0 | 10203 | { |
michael@0 | 10204 | if (!mScrollPositionClampingScrollPortSizeSet || |
michael@0 | 10205 | mScrollPositionClampingScrollPortSize.width != aWidth || |
michael@0 | 10206 | mScrollPositionClampingScrollPortSize.height != aHeight) { |
michael@0 | 10207 | mScrollPositionClampingScrollPortSizeSet = true; |
michael@0 | 10208 | mScrollPositionClampingScrollPortSize.width = aWidth; |
michael@0 | 10209 | mScrollPositionClampingScrollPortSize.height = aHeight; |
michael@0 | 10210 | |
michael@0 | 10211 | MarkFixedFramesForReflow(eResize); |
michael@0 | 10212 | } |
michael@0 | 10213 | } |
michael@0 | 10214 | |
michael@0 | 10215 | void |
michael@0 | 10216 | nsIPresShell::SetContentDocumentFixedPositionMargins(const nsMargin& aMargins) |
michael@0 | 10217 | { |
michael@0 | 10218 | if (mContentDocumentFixedPositionMargins == aMargins) { |
michael@0 | 10219 | return; |
michael@0 | 10220 | } |
michael@0 | 10221 | |
michael@0 | 10222 | mContentDocumentFixedPositionMargins = aMargins; |
michael@0 | 10223 | |
michael@0 | 10224 | MarkFixedFramesForReflow(eResize); |
michael@0 | 10225 | } |
michael@0 | 10226 | |
michael@0 | 10227 | void |
michael@0 | 10228 | PresShell::SetupFontInflation() |
michael@0 | 10229 | { |
michael@0 | 10230 | mFontSizeInflationEmPerLine = nsLayoutUtils::FontSizeInflationEmPerLine(); |
michael@0 | 10231 | mFontSizeInflationMinTwips = nsLayoutUtils::FontSizeInflationMinTwips(); |
michael@0 | 10232 | mFontSizeInflationLineThreshold = nsLayoutUtils::FontSizeInflationLineThreshold(); |
michael@0 | 10233 | mFontSizeInflationForceEnabled = nsLayoutUtils::FontSizeInflationForceEnabled(); |
michael@0 | 10234 | mFontSizeInflationDisabledInMasterProcess = nsLayoutUtils::FontSizeInflationDisabledInMasterProcess(); |
michael@0 | 10235 | |
michael@0 | 10236 | NotifyFontSizeInflationEnabledIsDirty(); |
michael@0 | 10237 | } |
michael@0 | 10238 | |
michael@0 | 10239 | void |
michael@0 | 10240 | nsIPresShell::RecomputeFontSizeInflationEnabled() |
michael@0 | 10241 | { |
michael@0 | 10242 | mFontSizeInflationEnabledIsDirty = false; |
michael@0 | 10243 | |
michael@0 | 10244 | MOZ_ASSERT(mPresContext, "our pres context should not be null"); |
michael@0 | 10245 | if ((FontSizeInflationEmPerLine() == 0 && |
michael@0 | 10246 | FontSizeInflationMinTwips() == 0) || mPresContext->IsChrome()) { |
michael@0 | 10247 | mFontSizeInflationEnabled = false; |
michael@0 | 10248 | return; |
michael@0 | 10249 | } |
michael@0 | 10250 | |
michael@0 | 10251 | // Force-enabling font inflation always trumps the heuristics here. |
michael@0 | 10252 | if (!FontSizeInflationForceEnabled()) { |
michael@0 | 10253 | if (TabChild* tab = TabChild::GetFrom(this)) { |
michael@0 | 10254 | // We're in a child process. Cancel inflation if we're not |
michael@0 | 10255 | // async-pan zoomed. |
michael@0 | 10256 | if (!tab->IsAsyncPanZoomEnabled()) { |
michael@0 | 10257 | mFontSizeInflationEnabled = false; |
michael@0 | 10258 | return; |
michael@0 | 10259 | } |
michael@0 | 10260 | } else if (XRE_GetProcessType() == GeckoProcessType_Default) { |
michael@0 | 10261 | // We're in the master process. Cancel inflation if it's been |
michael@0 | 10262 | // explicitly disabled. |
michael@0 | 10263 | if (FontSizeInflationDisabledInMasterProcess()) { |
michael@0 | 10264 | mFontSizeInflationEnabled = false; |
michael@0 | 10265 | return; |
michael@0 | 10266 | } |
michael@0 | 10267 | } |
michael@0 | 10268 | } |
michael@0 | 10269 | |
michael@0 | 10270 | // XXXjwir3: |
michael@0 | 10271 | // See bug 706918, comment 23 for more information on this particular section |
michael@0 | 10272 | // of the code. We're using "screen size" in place of the size of the content |
michael@0 | 10273 | // area, because on mobile, these are close or equal. This will work for our |
michael@0 | 10274 | // purposes (bug 706198), but it will need to be changed in the future to be |
michael@0 | 10275 | // more correct when we bring the rest of the viewport code into platform. |
michael@0 | 10276 | // We actually want the size of the content area, in the event that we don't |
michael@0 | 10277 | // have any metadata about the width and/or height. On mobile, the screen size |
michael@0 | 10278 | // and the size of the content area are very close, or the same value. |
michael@0 | 10279 | // In XUL fennec, the content area is the size of the <browser> widget, but |
michael@0 | 10280 | // in native fennec, the content area is the size of the Gecko LayerView |
michael@0 | 10281 | // object. |
michael@0 | 10282 | |
michael@0 | 10283 | // TODO: |
michael@0 | 10284 | // Once bug 716575 has been resolved, this code should be changed so that it |
michael@0 | 10285 | // does the right thing on all platforms. |
michael@0 | 10286 | nsresult rv; |
michael@0 | 10287 | nsCOMPtr<nsIScreenManager> screenMgr = |
michael@0 | 10288 | do_GetService("@mozilla.org/gfx/screenmanager;1", &rv); |
michael@0 | 10289 | if (!NS_SUCCEEDED(rv)) { |
michael@0 | 10290 | mFontSizeInflationEnabled = false; |
michael@0 | 10291 | return; |
michael@0 | 10292 | } |
michael@0 | 10293 | |
michael@0 | 10294 | nsCOMPtr<nsIScreen> screen; |
michael@0 | 10295 | screenMgr->GetPrimaryScreen(getter_AddRefs(screen)); |
michael@0 | 10296 | if (screen) { |
michael@0 | 10297 | int32_t screenLeft, screenTop, screenWidth, screenHeight; |
michael@0 | 10298 | screen->GetRect(&screenLeft, &screenTop, &screenWidth, &screenHeight); |
michael@0 | 10299 | |
michael@0 | 10300 | nsViewportInfo vInf = |
michael@0 | 10301 | nsContentUtils::GetViewportInfo(GetDocument(), ScreenIntSize(screenWidth, screenHeight)); |
michael@0 | 10302 | |
michael@0 | 10303 | if (vInf.GetDefaultZoom() >= CSSToScreenScale(1.0f) || vInf.IsAutoSizeEnabled()) { |
michael@0 | 10304 | mFontSizeInflationEnabled = false; |
michael@0 | 10305 | return; |
michael@0 | 10306 | } |
michael@0 | 10307 | } |
michael@0 | 10308 | |
michael@0 | 10309 | mFontSizeInflationEnabled = true; |
michael@0 | 10310 | } |
michael@0 | 10311 | |
michael@0 | 10312 | bool |
michael@0 | 10313 | nsIPresShell::FontSizeInflationEnabled() |
michael@0 | 10314 | { |
michael@0 | 10315 | if (mFontSizeInflationEnabledIsDirty) { |
michael@0 | 10316 | RecomputeFontSizeInflationEnabled(); |
michael@0 | 10317 | } |
michael@0 | 10318 | |
michael@0 | 10319 | return mFontSizeInflationEnabled; |
michael@0 | 10320 | } |
michael@0 | 10321 | |
michael@0 | 10322 | void |
michael@0 | 10323 | nsIPresShell::SetMaxLineBoxWidth(nscoord aMaxLineBoxWidth) |
michael@0 | 10324 | { |
michael@0 | 10325 | NS_ASSERTION(aMaxLineBoxWidth >= 0, "attempting to set max line box width to a negative value"); |
michael@0 | 10326 | |
michael@0 | 10327 | if (mMaxLineBoxWidth != aMaxLineBoxWidth) { |
michael@0 | 10328 | mMaxLineBoxWidth = aMaxLineBoxWidth; |
michael@0 | 10329 | mReflowOnZoomPending = true; |
michael@0 | 10330 | FrameNeedsReflow(GetRootFrame(), eResize, NS_FRAME_HAS_DIRTY_CHILDREN); |
michael@0 | 10331 | } |
michael@0 | 10332 | } |
michael@0 | 10333 | |
michael@0 | 10334 | void |
michael@0 | 10335 | PresShell::PausePainting() |
michael@0 | 10336 | { |
michael@0 | 10337 | if (GetPresContext()->RefreshDriver()->PresContext() != GetPresContext()) |
michael@0 | 10338 | return; |
michael@0 | 10339 | |
michael@0 | 10340 | mPaintingIsFrozen = true; |
michael@0 | 10341 | GetPresContext()->RefreshDriver()->Freeze(); |
michael@0 | 10342 | } |
michael@0 | 10343 | |
michael@0 | 10344 | void |
michael@0 | 10345 | PresShell::ResumePainting() |
michael@0 | 10346 | { |
michael@0 | 10347 | if (GetPresContext()->RefreshDriver()->PresContext() != GetPresContext()) |
michael@0 | 10348 | return; |
michael@0 | 10349 | |
michael@0 | 10350 | mPaintingIsFrozen = false; |
michael@0 | 10351 | GetPresContext()->RefreshDriver()->Thaw(); |
michael@0 | 10352 | } |