nsLayoutDebuggingTools.cpp (10423B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "nsLayoutDebuggingTools.h" 8 9 #include "ErrorList.h" 10 #include "RetainedDisplayListBuilder.h" 11 #include "mozilla/Preferences.h" 12 #include "mozilla/PresShell.h" 13 #include "mozilla/dom/ChildIterator.h" 14 #include "mozilla/dom/Document.h" 15 #include "mozilla/dom/Element.h" 16 #include "mozilla/dom/TreeIterator.h" 17 #include "nsAtom.h" 18 #include "nsCSSFrameConstructor.h" 19 #include "nsCounterManager.h" 20 #include "nsDisplayList.h" 21 #include "nsIContent.h" 22 #include "nsIDocShell.h" 23 #include "nsIDocumentViewer.h" 24 #include "nsIFrame.h" 25 #include "nsIPrintSettings.h" 26 #include "nsIPrintSettingsService.h" 27 #include "nsLayoutUtils.h" 28 #include "nsPIDOMWindow.h" 29 30 using namespace mozilla; 31 using mozilla::dom::Document; 32 33 static already_AddRefed<nsIDocumentViewer> doc_viewer(nsIDocShell* aDocShell) { 34 if (!aDocShell) { 35 return nullptr; 36 } 37 nsCOMPtr<nsIDocumentViewer> viewer; 38 aDocShell->GetDocViewer(getter_AddRefs(viewer)); 39 return viewer.forget(); 40 } 41 42 static PresShell* GetPresShell(nsIDocShell* aDocShell) { 43 nsCOMPtr<nsIDocumentViewer> viewer = doc_viewer(aDocShell); 44 if (!viewer) { 45 return nullptr; 46 } 47 return viewer->GetPresShell(); 48 } 49 50 #ifdef DEBUG 51 static already_AddRefed<Document> document(nsIDocShell* aDocShell) { 52 nsCOMPtr<nsIDocumentViewer> viewer(doc_viewer(aDocShell)); 53 if (!viewer) { 54 return nullptr; 55 } 56 return do_AddRef(viewer->GetDocument()); 57 } 58 #endif 59 60 nsLayoutDebuggingTools::nsLayoutDebuggingTools() { ForceRefresh(); } 61 62 nsLayoutDebuggingTools::~nsLayoutDebuggingTools() = default; 63 64 NS_IMPL_ISUPPORTS(nsLayoutDebuggingTools, nsILayoutDebuggingTools) 65 66 NS_IMETHODIMP 67 nsLayoutDebuggingTools::Init(mozIDOMWindow* aWin) { 68 if (!Preferences::GetService()) { 69 return NS_ERROR_UNEXPECTED; 70 } 71 72 { 73 if (!aWin) { 74 return NS_ERROR_UNEXPECTED; 75 } 76 auto* window = nsPIDOMWindowInner::From(aWin); 77 mDocShell = window->GetDocShell(); 78 } 79 NS_ENSURE_TRUE(mDocShell, NS_ERROR_UNEXPECTED); 80 81 return NS_OK; 82 } 83 84 NS_IMETHODIMP 85 nsLayoutDebuggingTools::SetReflowCounts(bool aShow) { 86 NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED); 87 #ifdef MOZ_REFLOW_PERF 88 if (PresShell* presShell = GetPresShell(mDocShell)) { 89 presShell->SetPaintFrameCount(aShow); 90 } 91 #else 92 printf("************************************************\n"); 93 printf("Sorry, you have not built with MOZ_REFLOW_PERF=1\n"); 94 printf("************************************************\n"); 95 #endif 96 return NS_OK; 97 } 98 99 NS_IMETHODIMP 100 nsLayoutDebuggingTools::SetPagedMode(bool aPagedMode) { 101 nsCOMPtr<nsIPrintSettingsService> printSettingsService = 102 do_GetService("@mozilla.org/gfx/printsettings-service;1"); 103 nsCOMPtr<nsIPrintSettings> printSettings; 104 105 printSettingsService->CreateNewPrintSettings(getter_AddRefs(printSettings)); 106 107 // Use the same setup as setupPrintMode() in reftest-content.js. 108 printSettings->SetPaperWidth(5); 109 printSettings->SetPaperHeight(3); 110 111 nsIntMargin unwriteableMargin(0, 0, 0, 0); 112 printSettings->SetUnwriteableMarginInTwips(unwriteableMargin); 113 114 printSettings->SetHeaderStrLeft(u""_ns); 115 printSettings->SetHeaderStrCenter(u""_ns); 116 printSettings->SetHeaderStrRight(u""_ns); 117 118 printSettings->SetFooterStrLeft(u""_ns); 119 printSettings->SetFooterStrCenter(u""_ns); 120 printSettings->SetFooterStrRight(u""_ns); 121 122 printSettings->SetPrintBGColors(true); 123 printSettings->SetPrintBGImages(true); 124 125 nsCOMPtr<nsIDocumentViewer> docViewer(doc_viewer(mDocShell)); 126 docViewer->SetPageModeForTesting(aPagedMode, printSettings); 127 128 ForceRefresh(); 129 return NS_OK; 130 } 131 132 static void DumpContentRecur(nsIDocShell* aDocShell, FILE* out, 133 bool aAnonymousSubtrees) { 134 #ifdef DEBUG 135 if (!aDocShell) { 136 return; 137 } 138 139 fprintf(out, "docshell=%p \n", static_cast<void*>(aDocShell)); 140 RefPtr<Document> doc(document(aDocShell)); 141 if (!doc) { 142 fputs("no document\n", out); 143 return; 144 } 145 146 dom::Element* root = doc->GetRootElement(); 147 if (!root) { 148 fputs("no root element\n", out); 149 return; 150 } 151 152 // The content tree (without anonymous subtrees). 153 root->List(out); 154 155 // The anonymous subtrees. 156 if (aAnonymousSubtrees) { 157 dom::TreeIterator<dom::StyleChildrenIterator> iter(*root); 158 while (nsIContent* current = iter.GetNext()) { 159 if (!current->IsRootOfNativeAnonymousSubtree()) { 160 continue; 161 } 162 163 fputs("--\n", out); 164 if (current->IsElement() && 165 current->AsElement()->GetPseudoElementType() == 166 PseudoStyleType::mozSnapshotContainingBlock) { 167 fprintf(out, 168 "View Transition Tree " 169 "[parent=%p][active-view-transition=%p]:\n", 170 (void*)current->GetParent(), 171 (void*)doc->GetActiveViewTransition()); 172 } else { 173 fprintf(out, "Anonymous Subtree [parent=%p]:\n", 174 (void*)current->GetParent()); 175 } 176 current->List(out); 177 } 178 } 179 #endif 180 } 181 182 NS_IMETHODIMP 183 nsLayoutDebuggingTools::DumpContent(bool aAnonymousSubtrees) { 184 NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED); 185 DumpContentRecur(mDocShell, stdout, aAnonymousSubtrees); 186 return NS_OK; 187 } 188 189 static void DumpFramesRecur( 190 nsIDocShell* aDocShell, FILE* out, 191 nsIFrame::ListFlags aFlags = nsIFrame::ListFlags()) { 192 if (aFlags.contains(nsIFrame::ListFlag::DisplayInCSSPixels)) { 193 fprintf(out, "Frame tree in CSS pixels:\n"); 194 } else { 195 fprintf(out, "Frame tree in app units:\n"); 196 } 197 198 fprintf(out, "docshell=%p \n", aDocShell); 199 if (PresShell* presShell = GetPresShell(aDocShell)) { 200 nsIFrame* root = presShell->GetRootFrame(); 201 if (root) { 202 root->List(out, "", aFlags); 203 } 204 } else { 205 fputs("null pres shell\n", out); 206 } 207 } 208 209 static void DumpTextRunsRecur(nsIDocShell* aDocShell, FILE* out) { 210 fprintf(out, "Text runs:\n"); 211 212 fprintf(out, "docshell=%p \n", aDocShell); 213 if (PresShell* presShell = GetPresShell(aDocShell)) { 214 nsIFrame* root = presShell->GetRootFrame(); 215 if (root) { 216 root->ListTextRuns(out); 217 } 218 } else { 219 fputs("null pres shell\n", out); 220 } 221 } 222 223 NS_IMETHODIMP 224 nsLayoutDebuggingTools::DumpFrames(uint8_t aFlags) { 225 NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED); 226 nsIFrame::ListFlags flags{}; 227 if (aFlags & nsILayoutDebuggingTools::DUMP_FRAME_FLAGS_CSS_PIXELS) { 228 flags += nsIFrame::ListFlag::DisplayInCSSPixels; 229 } 230 if (aFlags & nsILayoutDebuggingTools::DUMP_FRAME_FLAGS_DETERMINISTIC) { 231 flags += nsIFrame::ListFlag::OnlyListDeterministicInfo; 232 } 233 DumpFramesRecur(mDocShell, stdout, flags); 234 return NS_OK; 235 } 236 237 NS_IMETHODIMP 238 nsLayoutDebuggingTools::DumpTextRuns() { 239 NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED); 240 DumpTextRunsRecur(mDocShell, stdout); 241 return NS_OK; 242 } 243 244 NS_IMETHODIMP 245 nsLayoutDebuggingTools::DumpCounterManager() { 246 NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED); 247 if (PresShell* presShell = GetPresShell(mDocShell)) { 248 presShell->FrameConstructor()->GetContainStyleScopeManager().DumpCounters(); 249 } 250 return NS_OK; 251 } 252 253 NS_IMETHODIMP 254 nsLayoutDebuggingTools::DumpRetainedDisplayList() { 255 NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED); 256 PresShell* presShell = GetPresShell(mDocShell); 257 if (!presShell) { 258 fputs("null pres shell\n", stdout); 259 return NS_OK; 260 } 261 262 if (!nsLayoutUtils::AreRetainedDisplayListsEnabled()) { 263 fputs("Retained display list is not enabled\n", stdout); 264 return NS_OK; 265 } 266 267 nsIFrame* root = presShell->GetRootFrame(); 268 auto* RDLBuilder = nsLayoutUtils::GetRetainedDisplayListBuilder(root); 269 if (!RDLBuilder) { 270 fputs("no retained display list\n", stdout); 271 return NS_OK; 272 } 273 nsDisplayListBuilder* builder = RDLBuilder->Builder(); 274 const nsDisplayList* list = RDLBuilder->List(); 275 if (!builder || !list) { 276 fputs("no retained display list\n", stdout); 277 return NS_OK; 278 } 279 280 fprintf(stdout, "Retained Display List (rootframe=%p) visible=%s:\n", 281 nsLayoutUtils::GetDisplayRootFrame(root), 282 ToString(builder->GetVisibleRect()).c_str()); 283 fputs("<\n", stdout); 284 nsIFrame::PrintDisplayList(builder, *list, 1, false); 285 fputs(">\n", stdout); 286 return NS_OK; 287 } 288 289 NS_IMETHODIMP 290 nsLayoutDebuggingTools::DumpStyleSheets() { 291 NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED); 292 #if defined(DEBUG) || defined(MOZ_LAYOUT_DEBUGGER) 293 FILE* out = stdout; 294 if (PresShell* presShell = GetPresShell(mDocShell)) { 295 presShell->ListStyleSheets(out); 296 } else { 297 fputs("null pres shell\n", out); 298 } 299 #endif 300 return NS_OK; 301 } 302 303 NS_IMETHODIMP nsLayoutDebuggingTools::DumpMatchedRules() { 304 NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED); 305 FILE* out = stdout; 306 if (PresShell* presShell = GetPresShell(mDocShell)) { 307 nsIFrame* root = presShell->GetRootFrame(); 308 if (root) { 309 root->ListWithMatchedRules(out); 310 } 311 } else { 312 fputs("null pres shell\n", out); 313 } 314 return NS_OK; 315 } 316 317 NS_IMETHODIMP 318 nsLayoutDebuggingTools::DumpComputedStyles() { 319 NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED); 320 #ifdef DEBUG 321 FILE* out = stdout; 322 if (PresShell* presShell = GetPresShell(mDocShell)) { 323 presShell->ListComputedStyles(out); 324 } else { 325 fputs("null pres shell\n", out); 326 } 327 #endif 328 return NS_OK; 329 } 330 331 NS_IMETHODIMP 332 nsLayoutDebuggingTools::DumpReflowStats() { 333 NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED); 334 #ifdef DEBUG 335 if (RefPtr<PresShell> presShell = GetPresShell(mDocShell)) { 336 # ifdef MOZ_REFLOW_PERF 337 presShell->DumpReflows(); 338 # else 339 printf("************************************************\n"); 340 printf("Sorry, you have not built with MOZ_REFLOW_PERF=1\n"); 341 printf("************************************************\n"); 342 # endif 343 } 344 #endif 345 return NS_OK; 346 } 347 348 nsresult nsLayoutDebuggingTools::ForceRefresh() { 349 return NS_ERROR_NOT_IMPLEMENTED; 350 } 351 352 nsresult nsLayoutDebuggingTools::SetBoolPrefAndRefresh(const char* aPrefName, 353 bool aNewVal) { 354 NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED); 355 356 nsIPrefService* prefService = Preferences::GetService(); 357 NS_ENSURE_TRUE(prefService && aPrefName, NS_OK); 358 359 Preferences::SetBool(aPrefName, aNewVal); 360 prefService->SavePrefFile(nullptr); 361 362 ForceRefresh(); 363 364 return NS_OK; 365 }