Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : // vim:cindent:tabstop=4:expandtab:shiftwidth=4:
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 "nsIDocShell.h"
10 : #include "nsPIDOMWindow.h"
11 : #include "nsIContentViewer.h"
12 :
13 : #include "nsIServiceManager.h"
14 : #include "nsIAtom.h"
15 : #include "nsQuickSort.h"
16 :
17 : #include "nsIContent.h"
18 : #include "nsIDocument.h"
19 : #include "nsIDOMDocument.h"
20 :
21 : #include "nsIPresShell.h"
22 : #include "nsViewManager.h"
23 : #include "nsIFrame.h"
24 :
25 : #include "nsILayoutDebugger.h"
26 : #include "nsLayoutCID.h"
27 : static NS_DEFINE_CID(kLayoutDebuggerCID, NS_LAYOUT_DEBUGGER_CID);
28 :
29 : #include "nsISelectionController.h"
30 : #include "mozilla/dom/Element.h"
31 : #include "mozilla/Preferences.h"
32 :
33 : using namespace mozilla;
34 :
35 : static already_AddRefed<nsIContentViewer>
36 0 : doc_viewer(nsIDocShell *aDocShell)
37 : {
38 0 : if (!aDocShell)
39 0 : return nullptr;
40 0 : nsCOMPtr<nsIContentViewer> result;
41 0 : aDocShell->GetContentViewer(getter_AddRefs(result));
42 0 : return result.forget();
43 : }
44 :
45 : static already_AddRefed<nsIPresShell>
46 0 : pres_shell(nsIDocShell *aDocShell)
47 : {
48 0 : nsCOMPtr<nsIContentViewer> cv = doc_viewer(aDocShell);
49 0 : if (!cv)
50 0 : return nullptr;
51 0 : nsCOMPtr<nsIPresShell> result;
52 0 : cv->GetPresShell(getter_AddRefs(result));
53 0 : return result.forget();
54 : }
55 :
56 : static nsViewManager*
57 0 : view_manager(nsIDocShell *aDocShell)
58 : {
59 0 : nsCOMPtr<nsIPresShell> shell(pres_shell(aDocShell));
60 0 : if (!shell)
61 0 : return nullptr;
62 0 : return shell->GetViewManager();
63 : }
64 :
65 : #ifdef DEBUG
66 : static already_AddRefed<nsIDocument>
67 0 : document(nsIDocShell *aDocShell)
68 : {
69 0 : nsCOMPtr<nsIContentViewer> cv(doc_viewer(aDocShell));
70 0 : if (!cv)
71 0 : return nullptr;
72 0 : nsCOMPtr<nsIDOMDocument> domDoc;
73 0 : cv->GetDOMDocument(getter_AddRefs(domDoc));
74 0 : if (!domDoc)
75 0 : return nullptr;
76 0 : nsCOMPtr<nsIDocument> result = do_QueryInterface(domDoc);
77 0 : return result.forget();
78 : }
79 : #endif
80 :
81 0 : nsLayoutDebuggingTools::nsLayoutDebuggingTools()
82 : : mPaintFlashing(false),
83 : mPaintDumping(false),
84 : mInvalidateDumping(false),
85 : mEventDumping(false),
86 : mMotionEventDumping(false),
87 : mCrossingEventDumping(false),
88 0 : mReflowCounts(false)
89 : {
90 0 : NewURILoaded();
91 0 : }
92 :
93 0 : nsLayoutDebuggingTools::~nsLayoutDebuggingTools()
94 : {
95 0 : }
96 :
97 0 : NS_IMPL_ISUPPORTS(nsLayoutDebuggingTools, nsILayoutDebuggingTools)
98 :
99 : NS_IMETHODIMP
100 0 : nsLayoutDebuggingTools::Init(mozIDOMWindow* aWin)
101 : {
102 0 : if (!Preferences::GetService()) {
103 0 : return NS_ERROR_UNEXPECTED;
104 : }
105 :
106 : {
107 0 : if (!aWin)
108 0 : return NS_ERROR_UNEXPECTED;
109 0 : auto* window = nsPIDOMWindowInner::From(aWin);
110 0 : mDocShell = window->GetDocShell();
111 : }
112 0 : NS_ENSURE_TRUE(mDocShell, NS_ERROR_UNEXPECTED);
113 :
114 0 : mPaintFlashing =
115 0 : Preferences::GetBool("nglayout.debug.paint_flashing", mPaintFlashing);
116 0 : mPaintDumping =
117 0 : Preferences::GetBool("nglayout.debug.paint_dumping", mPaintDumping);
118 0 : mInvalidateDumping =
119 0 : Preferences::GetBool("nglayout.debug.invalidate_dumping", mInvalidateDumping);
120 0 : mEventDumping =
121 0 : Preferences::GetBool("nglayout.debug.event_dumping", mEventDumping);
122 0 : mMotionEventDumping =
123 0 : Preferences::GetBool("nglayout.debug.motion_event_dumping",
124 0 : mMotionEventDumping);
125 0 : mCrossingEventDumping =
126 0 : Preferences::GetBool("nglayout.debug.crossing_event_dumping",
127 0 : mCrossingEventDumping);
128 0 : mReflowCounts =
129 0 : Preferences::GetBool("layout.reflow.showframecounts", mReflowCounts);
130 :
131 : {
132 0 : nsCOMPtr<nsILayoutDebugger> ld = do_GetService(kLayoutDebuggerCID);
133 0 : if (ld) {
134 0 : ld->GetShowFrameBorders(&mVisualDebugging);
135 0 : ld->GetShowEventTargetFrameBorder(&mVisualEventDebugging);
136 : }
137 : }
138 :
139 0 : return NS_OK;
140 : }
141 :
142 : NS_IMETHODIMP
143 0 : nsLayoutDebuggingTools::NewURILoaded()
144 : {
145 0 : NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED);
146 : // Reset all the state that should be reset between pages.
147 :
148 : // XXX Some of these should instead be transferred between pages!
149 0 : mEditorMode = false;
150 0 : mVisualDebugging = false;
151 0 : mVisualEventDebugging = false;
152 :
153 0 : mReflowCounts = false;
154 :
155 0 : ForceRefresh();
156 0 : return NS_OK;
157 : }
158 :
159 : NS_IMETHODIMP
160 0 : nsLayoutDebuggingTools::GetVisualDebugging(bool *aVisualDebugging)
161 : {
162 0 : *aVisualDebugging = mVisualDebugging;
163 0 : return NS_OK;
164 : }
165 :
166 : NS_IMETHODIMP
167 0 : nsLayoutDebuggingTools::SetVisualDebugging(bool aVisualDebugging)
168 : {
169 0 : nsCOMPtr<nsILayoutDebugger> ld = do_GetService(kLayoutDebuggerCID);
170 0 : if (!ld)
171 0 : return NS_ERROR_UNEXPECTED;
172 0 : mVisualDebugging = aVisualDebugging;
173 0 : ld->SetShowFrameBorders(aVisualDebugging);
174 0 : ForceRefresh();
175 0 : return NS_OK;
176 : }
177 :
178 : NS_IMETHODIMP
179 0 : nsLayoutDebuggingTools::GetVisualEventDebugging(bool *aVisualEventDebugging)
180 : {
181 0 : *aVisualEventDebugging = mVisualEventDebugging;
182 0 : return NS_OK;
183 : }
184 :
185 : NS_IMETHODIMP
186 0 : nsLayoutDebuggingTools::SetVisualEventDebugging(bool aVisualEventDebugging)
187 : {
188 0 : nsCOMPtr<nsILayoutDebugger> ld = do_GetService(kLayoutDebuggerCID);
189 0 : if (!ld)
190 0 : return NS_ERROR_UNEXPECTED;
191 0 : mVisualEventDebugging = aVisualEventDebugging;
192 0 : ld->SetShowEventTargetFrameBorder(aVisualEventDebugging);
193 0 : ForceRefresh();
194 0 : return NS_OK;
195 : }
196 :
197 : NS_IMETHODIMP
198 0 : nsLayoutDebuggingTools::GetPaintFlashing(bool *aPaintFlashing)
199 : {
200 0 : *aPaintFlashing = mPaintFlashing;
201 0 : return NS_OK;
202 : }
203 :
204 : NS_IMETHODIMP
205 0 : nsLayoutDebuggingTools::SetPaintFlashing(bool aPaintFlashing)
206 : {
207 0 : mPaintFlashing = aPaintFlashing;
208 0 : return SetBoolPrefAndRefresh("nglayout.debug.paint_flashing", mPaintFlashing);
209 : }
210 :
211 : NS_IMETHODIMP
212 0 : nsLayoutDebuggingTools::GetPaintDumping(bool *aPaintDumping)
213 : {
214 0 : *aPaintDumping = mPaintDumping;
215 0 : return NS_OK;
216 : }
217 :
218 : NS_IMETHODIMP
219 0 : nsLayoutDebuggingTools::SetPaintDumping(bool aPaintDumping)
220 : {
221 0 : mPaintDumping = aPaintDumping;
222 0 : return SetBoolPrefAndRefresh("nglayout.debug.paint_dumping", mPaintDumping);
223 : }
224 :
225 : NS_IMETHODIMP
226 0 : nsLayoutDebuggingTools::GetInvalidateDumping(bool *aInvalidateDumping)
227 : {
228 0 : *aInvalidateDumping = mInvalidateDumping;
229 0 : return NS_OK;
230 : }
231 :
232 : NS_IMETHODIMP
233 0 : nsLayoutDebuggingTools::SetInvalidateDumping(bool aInvalidateDumping)
234 : {
235 0 : mInvalidateDumping = aInvalidateDumping;
236 0 : return SetBoolPrefAndRefresh("nglayout.debug.invalidate_dumping", mInvalidateDumping);
237 : }
238 :
239 : NS_IMETHODIMP
240 0 : nsLayoutDebuggingTools::GetEventDumping(bool *aEventDumping)
241 : {
242 0 : *aEventDumping = mEventDumping;
243 0 : return NS_OK;
244 : }
245 :
246 : NS_IMETHODIMP
247 0 : nsLayoutDebuggingTools::SetEventDumping(bool aEventDumping)
248 : {
249 0 : mEventDumping = aEventDumping;
250 0 : return SetBoolPrefAndRefresh("nglayout.debug.event_dumping", mEventDumping);
251 : }
252 :
253 : NS_IMETHODIMP
254 0 : nsLayoutDebuggingTools::GetMotionEventDumping(bool *aMotionEventDumping)
255 : {
256 0 : *aMotionEventDumping = mMotionEventDumping;
257 0 : return NS_OK;
258 : }
259 :
260 : NS_IMETHODIMP
261 0 : nsLayoutDebuggingTools::SetMotionEventDumping(bool aMotionEventDumping)
262 : {
263 0 : mMotionEventDumping = aMotionEventDumping;
264 0 : return SetBoolPrefAndRefresh("nglayout.debug.motion_event_dumping", mMotionEventDumping);
265 : }
266 :
267 : NS_IMETHODIMP
268 0 : nsLayoutDebuggingTools::GetCrossingEventDumping(bool *aCrossingEventDumping)
269 : {
270 0 : *aCrossingEventDumping = mCrossingEventDumping;
271 0 : return NS_OK;
272 : }
273 :
274 : NS_IMETHODIMP
275 0 : nsLayoutDebuggingTools::SetCrossingEventDumping(bool aCrossingEventDumping)
276 : {
277 0 : mCrossingEventDumping = aCrossingEventDumping;
278 0 : return SetBoolPrefAndRefresh("nglayout.debug.crossing_event_dumping", mCrossingEventDumping);
279 : }
280 :
281 : NS_IMETHODIMP
282 0 : nsLayoutDebuggingTools::GetReflowCounts(bool* aShow)
283 : {
284 0 : *aShow = mReflowCounts;
285 0 : return NS_OK;
286 : }
287 :
288 : NS_IMETHODIMP
289 0 : nsLayoutDebuggingTools::SetReflowCounts(bool aShow)
290 : {
291 0 : NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED);
292 0 : nsCOMPtr<nsIPresShell> shell(pres_shell(mDocShell));
293 0 : if (shell) {
294 : #ifdef MOZ_REFLOW_PERF
295 0 : shell->SetPaintFrameCount(aShow);
296 0 : SetBoolPrefAndRefresh("layout.reflow.showframecounts", aShow);
297 0 : mReflowCounts = aShow;
298 : #else
299 : printf("************************************************\n");
300 : printf("Sorry, you have not built with MOZ_REFLOW_PERF=1\n");
301 : printf("************************************************\n");
302 : #endif
303 : }
304 0 : return NS_OK;
305 : }
306 :
307 0 : static void DumpAWebShell(nsIDocShellTreeItem* aShellItem, FILE* out, int32_t aIndent)
308 : {
309 0 : nsString name;
310 0 : nsCOMPtr<nsIDocShellTreeItem> parent;
311 : int32_t i, n;
312 :
313 0 : for (i = aIndent; --i >= 0; )
314 0 : fprintf(out, " ");
315 :
316 0 : fprintf(out, "%p '", static_cast<void*>(aShellItem));
317 0 : aShellItem->GetName(name);
318 0 : aShellItem->GetSameTypeParent(getter_AddRefs(parent));
319 0 : fputs(NS_LossyConvertUTF16toASCII(name).get(), out);
320 0 : fprintf(out, "' parent=%p <\n", static_cast<void*>(parent));
321 :
322 0 : ++aIndent;
323 0 : aShellItem->GetChildCount(&n);
324 0 : for (i = 0; i < n; ++i) {
325 0 : nsCOMPtr<nsIDocShellTreeItem> child;
326 0 : aShellItem->GetChildAt(i, getter_AddRefs(child));
327 0 : if (child) {
328 0 : DumpAWebShell(child, out, aIndent);
329 : }
330 : }
331 0 : --aIndent;
332 0 : for (i = aIndent; --i >= 0; )
333 0 : fprintf(out, " ");
334 0 : fputs(">\n", out);
335 0 : }
336 :
337 : NS_IMETHODIMP
338 0 : nsLayoutDebuggingTools::DumpWebShells()
339 : {
340 0 : NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED);
341 0 : DumpAWebShell(mDocShell, stdout, 0);
342 0 : return NS_OK;
343 : }
344 :
345 : static
346 : void
347 0 : DumpContentRecur(nsIDocShell* aDocShell, FILE* out)
348 : {
349 : #ifdef DEBUG
350 0 : if (nullptr != aDocShell) {
351 0 : fprintf(out, "docshell=%p \n", static_cast<void*>(aDocShell));
352 0 : nsCOMPtr<nsIDocument> doc(document(aDocShell));
353 0 : if (doc) {
354 0 : dom::Element *root = doc->GetRootElement();
355 0 : if (root) {
356 0 : root->List(out);
357 : }
358 : }
359 : else {
360 0 : fputs("no document\n", out);
361 : }
362 : // dump the frames of the sub documents
363 : int32_t i, n;
364 0 : aDocShell->GetChildCount(&n);
365 0 : for (i = 0; i < n; ++i) {
366 0 : nsCOMPtr<nsIDocShellTreeItem> child;
367 0 : aDocShell->GetChildAt(i, getter_AddRefs(child));
368 0 : nsCOMPtr<nsIDocShell> childAsShell(do_QueryInterface(child));
369 0 : if (child) {
370 0 : DumpContentRecur(childAsShell, out);
371 : }
372 : }
373 : }
374 : #endif
375 0 : }
376 :
377 : NS_IMETHODIMP
378 0 : nsLayoutDebuggingTools::DumpContent()
379 : {
380 0 : NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED);
381 0 : DumpContentRecur(mDocShell, stdout);
382 0 : return NS_OK;
383 : }
384 :
385 : static void
386 0 : DumpFramesRecur(nsIDocShell* aDocShell, FILE* out)
387 : {
388 : #ifdef DEBUG
389 0 : fprintf(out, "webshell=%p \n", static_cast<void*>(aDocShell));
390 0 : nsCOMPtr<nsIPresShell> shell(pres_shell(aDocShell));
391 0 : if (shell) {
392 0 : nsIFrame* root = shell->GetRootFrame();
393 0 : if (root) {
394 0 : root->List(out);
395 : }
396 : }
397 : else {
398 0 : fputs("null pres shell\n", out);
399 : }
400 :
401 : // dump the frames of the sub documents
402 : int32_t i, n;
403 0 : aDocShell->GetChildCount(&n);
404 0 : for (i = 0; i < n; ++i) {
405 0 : nsCOMPtr<nsIDocShellTreeItem> child;
406 0 : aDocShell->GetChildAt(i, getter_AddRefs(child));
407 0 : nsCOMPtr<nsIDocShell> childAsShell(do_QueryInterface(child));
408 0 : if (childAsShell) {
409 0 : DumpFramesRecur(childAsShell, out);
410 : }
411 : }
412 : #endif
413 0 : }
414 :
415 : NS_IMETHODIMP
416 0 : nsLayoutDebuggingTools::DumpFrames()
417 : {
418 0 : NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED);
419 0 : DumpFramesRecur(mDocShell, stdout);
420 0 : return NS_OK;
421 : }
422 :
423 : static
424 : void
425 0 : DumpViewsRecur(nsIDocShell* aDocShell, FILE* out)
426 : {
427 : #ifdef DEBUG
428 0 : fprintf(out, "docshell=%p \n", static_cast<void*>(aDocShell));
429 0 : RefPtr<nsViewManager> vm(view_manager(aDocShell));
430 0 : if (vm) {
431 0 : nsView* root = vm->GetRootView();
432 0 : if (root) {
433 0 : root->List(out);
434 : }
435 : }
436 : else {
437 0 : fputs("null view manager\n", out);
438 : }
439 :
440 : // dump the views of the sub documents
441 : int32_t i, n;
442 0 : aDocShell->GetChildCount(&n);
443 0 : for (i = 0; i < n; i++) {
444 0 : nsCOMPtr<nsIDocShellTreeItem> child;
445 0 : aDocShell->GetChildAt(i, getter_AddRefs(child));
446 0 : nsCOMPtr<nsIDocShell> childAsShell(do_QueryInterface(child));
447 0 : if (childAsShell) {
448 0 : DumpViewsRecur(childAsShell, out);
449 : }
450 : }
451 : #endif // DEBUG
452 0 : }
453 :
454 : NS_IMETHODIMP
455 0 : nsLayoutDebuggingTools::DumpViews()
456 : {
457 0 : NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED);
458 0 : DumpViewsRecur(mDocShell, stdout);
459 0 : return NS_OK;
460 : }
461 :
462 : NS_IMETHODIMP
463 0 : nsLayoutDebuggingTools::DumpStyleSheets()
464 : {
465 0 : NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED);
466 : #ifdef DEBUG
467 0 : FILE *out = stdout;
468 0 : nsCOMPtr<nsIPresShell> shell(pres_shell(mDocShell));
469 0 : if (shell)
470 0 : shell->ListStyleSheets(out);
471 : else
472 0 : fputs("null pres shell\n", out);
473 : #endif
474 0 : return NS_OK;
475 : }
476 :
477 : NS_IMETHODIMP
478 0 : nsLayoutDebuggingTools::DumpStyleContexts()
479 : {
480 0 : NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED);
481 : #ifdef DEBUG
482 0 : FILE *out = stdout;
483 0 : nsCOMPtr<nsIPresShell> shell(pres_shell(mDocShell));
484 0 : if (shell) {
485 0 : shell->ListStyleContexts(out);
486 : } else {
487 0 : fputs("null pres shell\n", out);
488 : }
489 : #endif
490 0 : return NS_OK;
491 : }
492 :
493 : NS_IMETHODIMP
494 0 : nsLayoutDebuggingTools::DumpReflowStats()
495 : {
496 0 : NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED);
497 : #ifdef DEBUG
498 0 : nsCOMPtr<nsIPresShell> shell(pres_shell(mDocShell));
499 0 : if (shell) {
500 : #ifdef MOZ_REFLOW_PERF
501 0 : shell->DumpReflows();
502 : #else
503 : printf("************************************************\n");
504 : printf("Sorry, you have not built with MOZ_REFLOW_PERF=1\n");
505 : printf("************************************************\n");
506 : #endif
507 : }
508 : #endif
509 0 : return NS_OK;
510 : }
511 :
512 0 : void nsLayoutDebuggingTools::ForceRefresh()
513 : {
514 0 : RefPtr<nsViewManager> vm(view_manager(mDocShell));
515 0 : if (!vm)
516 0 : return;
517 0 : nsView* root = vm->GetRootView();
518 0 : if (root) {
519 0 : vm->InvalidateView(root);
520 : }
521 : }
522 :
523 : nsresult
524 0 : nsLayoutDebuggingTools::SetBoolPrefAndRefresh(const char * aPrefName,
525 : bool aNewVal)
526 : {
527 0 : NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED);
528 :
529 0 : nsIPrefService* prefService = Preferences::GetService();
530 0 : NS_ENSURE_TRUE(prefService && aPrefName, NS_OK);
531 :
532 0 : Preferences::SetBool(aPrefName, aNewVal);
533 0 : prefService->SavePrefFile(nullptr);
534 :
535 0 : ForceRefresh();
536 :
537 0 : return NS_OK;
538 : }
|