Back to index

lightning-sunbird  0.9+nobinonly
nsLayoutDebuggingTools.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 // vim:cindent:tabstop=4:expandtab:shiftwidth=4:
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is layout debugging code.
00017  *
00018  * The Initial Developer of the Original Code is 
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 2002
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   L. David Baron <dbaron@dbaron.org> (original author)
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsLayoutDebuggingTools.h"
00041 
00042 #include "nsIDocShell.h"
00043 #include "nsIDocShellTreeNode.h"
00044 #include "nsIDocShellTreeItem.h"
00045 #include "nsIDOMWindow.h"
00046 #include "nsIScriptGlobalObject.h"
00047 #include "nsIDocumentViewer.h"
00048 
00049 #include "nsIServiceManager.h"
00050 #include "nsIAtom.h"
00051 #include "nsQuickSort.h"
00052 #include "nsIPref.h"
00053 
00054 #include "nsIContent.h"
00055 #include "nsIDocument.h"
00056 #include "nsIDOMDocument.h"
00057 
00058 #include "nsIPresShell.h"
00059 #include "nsIViewManager.h"
00060 #include "nsIFrame.h"
00061 #include "nsIFrameDebug.h"
00062 
00063 #include "nsILayoutDebugger.h"
00064 #include "nsLayoutCID.h"
00065 static NS_DEFINE_CID(kLayoutDebuggerCID, NS_LAYOUT_DEBUGGER_CID);
00066 
00067 #include "nsISelectionController.h"
00068 
00069 static already_AddRefed<nsIContentViewer>
00070 doc_viewer(nsIDocShell *aDocShell)
00071 {
00072     if (!aDocShell)
00073         return nsnull;
00074     nsIContentViewer *result = nsnull;
00075     aDocShell->GetContentViewer(&result);
00076     return result;
00077 }
00078 
00079 static already_AddRefed<nsIPresShell>
00080 pres_shell(nsIDocShell *aDocShell)
00081 {
00082     nsCOMPtr<nsIDocumentViewer> dv =
00083         do_QueryInterface(nsCOMPtr<nsIContentViewer>(doc_viewer(aDocShell)));
00084     if (!dv)
00085         return nsnull;
00086     nsIPresShell *result = nsnull;
00087     dv->GetPresShell(&result);
00088     return result;
00089 }
00090 
00091 #if 0 // not currently needed
00092 static already_AddRefed<nsPresContext>
00093 pres_context(nsIDocShell *aDocShell)
00094 {
00095     nsCOMPtr<nsIDocumentViewer> dv =
00096         do_QueryInterface(nsCOMPtr<nsIContentViewer>(doc_viewer(aDocShell)));
00097     if (!dv)
00098         return nsnull;
00099     nsPresContext *result = nsnull;
00100     dv->GetPresContext(result);
00101     return result;
00102 }
00103 #endif
00104 
00105 static nsIViewManager*
00106 view_manager(nsIDocShell *aDocShell)
00107 {
00108     nsCOMPtr<nsIPresShell> shell(pres_shell(aDocShell));
00109     if (!shell)
00110         return nsnull;
00111     return shell->GetViewManager();
00112 }
00113 
00114 #ifdef DEBUG
00115 static already_AddRefed<nsIDocument>
00116 document(nsIDocShell *aDocShell)
00117 {
00118     nsCOMPtr<nsIContentViewer> cv(doc_viewer(aDocShell));
00119     if (!cv)
00120         return nsnull;
00121     nsCOMPtr<nsIDOMDocument> domDoc;
00122     cv->GetDOMDocument(getter_AddRefs(domDoc));
00123     if (!domDoc)
00124         return nsnull;
00125     nsIDocument *result = nsnull;
00126     CallQueryInterface(domDoc, &result);
00127     return result;
00128 }
00129 #endif
00130 
00131 nsLayoutDebuggingTools::nsLayoutDebuggingTools()
00132   : mPaintFlashing(PR_FALSE),
00133     mPaintDumping(PR_FALSE),
00134     mInvalidateDumping(PR_FALSE),
00135     mEventDumping(PR_FALSE),
00136     mMotionEventDumping(PR_FALSE),
00137     mCrossingEventDumping(PR_FALSE),
00138     mReflowCounts(PR_FALSE)
00139 {
00140     NewURILoaded();
00141 }
00142 
00143 nsLayoutDebuggingTools::~nsLayoutDebuggingTools()
00144 {
00145 }
00146 
00147 NS_IMPL_ISUPPORTS1(nsLayoutDebuggingTools, nsILayoutDebuggingTools)
00148 
00149 NS_IMETHODIMP
00150 nsLayoutDebuggingTools::Init(nsIDOMWindow *aWin)
00151 {
00152     {
00153         nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(aWin);
00154         if (!global)
00155             return NS_ERROR_UNEXPECTED;
00156         mDocShell = global->GetDocShell();
00157     }
00158 
00159     mPrefs = do_GetService(NS_PREF_CONTRACTID);
00160 
00161     GetBoolPref("nglayout.debug.paint_flashing", &mPaintFlashing);
00162     GetBoolPref("nglayout.debug.paint_dumping", &mPaintDumping);
00163     GetBoolPref("nglayout.debug.invalidate_dumping", &mInvalidateDumping);
00164     GetBoolPref("nglayout.debug.event_dumping", &mEventDumping);
00165     GetBoolPref("nglayout.debug.motion_event_dumping", &mMotionEventDumping);
00166     GetBoolPref("nglayout.debug.crossing_event_dumping", &mCrossingEventDumping);
00167     GetBoolPref("layout.reflow.showframecounts", &mReflowCounts);
00168 
00169     {
00170         nsCOMPtr<nsILayoutDebugger> ld = do_GetService(kLayoutDebuggerCID);
00171         if (ld) {
00172             ld->GetShowFrameBorders(&mVisualDebugging);
00173             ld->GetShowEventTargetFrameBorder(&mVisualEventDebugging);
00174         }
00175     }
00176 
00177     return NS_OK;
00178 }
00179 
00180 NS_IMETHODIMP
00181 nsLayoutDebuggingTools::NewURILoaded()
00182 {
00183     // Reset all the state that should be reset between pages.
00184 
00185     // XXX Some of these should instead be transferred between pages!
00186     mEditorMode = PR_FALSE;
00187     mVisualDebugging = PR_FALSE;
00188     mVisualEventDebugging = PR_FALSE;
00189 
00190     mReflowCounts = PR_FALSE;
00191 
00192     ForceRefresh();
00193     return NS_OK;
00194 }
00195 
00196 NS_IMETHODIMP
00197 nsLayoutDebuggingTools::GetVisualDebugging(PRBool *aVisualDebugging)
00198 {
00199     *aVisualDebugging = mVisualDebugging;
00200     return NS_OK;
00201 }
00202 
00203 NS_IMETHODIMP
00204 nsLayoutDebuggingTools::SetVisualDebugging(PRBool aVisualDebugging)
00205 {
00206     nsCOMPtr<nsILayoutDebugger> ld = do_GetService(kLayoutDebuggerCID);
00207     if (!ld)
00208         return NS_ERROR_UNEXPECTED;
00209     mVisualDebugging = aVisualDebugging;
00210     ld->SetShowFrameBorders(aVisualDebugging);
00211     ForceRefresh();
00212     return NS_OK;
00213 }
00214 
00215 NS_IMETHODIMP
00216 nsLayoutDebuggingTools::GetVisualEventDebugging(PRBool *aVisualEventDebugging)
00217 {
00218     *aVisualEventDebugging = mVisualEventDebugging;
00219     return NS_OK;
00220 }
00221 
00222 NS_IMETHODIMP
00223 nsLayoutDebuggingTools::SetVisualEventDebugging(PRBool aVisualEventDebugging)
00224 {
00225     nsCOMPtr<nsILayoutDebugger> ld = do_GetService(kLayoutDebuggerCID);
00226     if (!ld)
00227         return NS_ERROR_UNEXPECTED;
00228     mVisualEventDebugging = aVisualEventDebugging;
00229     ld->SetShowEventTargetFrameBorder(aVisualEventDebugging);
00230     ForceRefresh();
00231     return NS_OK;
00232 }
00233 
00234 NS_IMETHODIMP
00235 nsLayoutDebuggingTools::GetPaintFlashing(PRBool *aPaintFlashing)
00236 {
00237     *aPaintFlashing = mPaintFlashing;
00238     return NS_OK;
00239 }
00240 
00241 NS_IMETHODIMP
00242 nsLayoutDebuggingTools::SetPaintFlashing(PRBool aPaintFlashing)
00243 {
00244     mPaintFlashing = aPaintFlashing;
00245     return SetBoolPrefAndRefresh("nglayout.debug.paint_flashing", mPaintFlashing);
00246 }
00247 
00248 NS_IMETHODIMP
00249 nsLayoutDebuggingTools::GetPaintDumping(PRBool *aPaintDumping)
00250 {
00251     *aPaintDumping = mPaintDumping;
00252     return NS_OK;
00253 }
00254 
00255 NS_IMETHODIMP
00256 nsLayoutDebuggingTools::SetPaintDumping(PRBool aPaintDumping)
00257 {
00258     mPaintDumping = aPaintDumping;
00259     return SetBoolPrefAndRefresh("nglayout.debug.paint_dumping", mPaintDumping);
00260 }
00261 
00262 NS_IMETHODIMP
00263 nsLayoutDebuggingTools::GetInvalidateDumping(PRBool *aInvalidateDumping)
00264 {
00265     *aInvalidateDumping = mInvalidateDumping;
00266     return NS_OK;
00267 }
00268 
00269 NS_IMETHODIMP
00270 nsLayoutDebuggingTools::SetInvalidateDumping(PRBool aInvalidateDumping)
00271 {
00272     mInvalidateDumping = aInvalidateDumping;
00273     return SetBoolPrefAndRefresh("nglayout.debug.invalidate_dumping", mInvalidateDumping);
00274 }
00275 
00276 NS_IMETHODIMP
00277 nsLayoutDebuggingTools::GetEventDumping(PRBool *aEventDumping)
00278 {
00279     *aEventDumping = mEventDumping;
00280     return NS_OK;
00281 }
00282 
00283 NS_IMETHODIMP
00284 nsLayoutDebuggingTools::SetEventDumping(PRBool aEventDumping)
00285 {
00286     mEventDumping = aEventDumping;
00287     return SetBoolPrefAndRefresh("nglayout.debug.event_dumping", mEventDumping);
00288 }
00289 
00290 NS_IMETHODIMP
00291 nsLayoutDebuggingTools::GetMotionEventDumping(PRBool *aMotionEventDumping)
00292 {
00293     *aMotionEventDumping = mMotionEventDumping;
00294     return NS_OK;
00295 }
00296 
00297 NS_IMETHODIMP
00298 nsLayoutDebuggingTools::SetMotionEventDumping(PRBool aMotionEventDumping)
00299 {
00300     mMotionEventDumping = aMotionEventDumping;
00301     return SetBoolPrefAndRefresh("nglayout.debug.motion_event_dumping", mMotionEventDumping);
00302 }
00303 
00304 NS_IMETHODIMP
00305 nsLayoutDebuggingTools::GetCrossingEventDumping(PRBool *aCrossingEventDumping)
00306 {
00307     *aCrossingEventDumping = mCrossingEventDumping;
00308     return NS_OK;
00309 }
00310 
00311 NS_IMETHODIMP
00312 nsLayoutDebuggingTools::SetCrossingEventDumping(PRBool aCrossingEventDumping)
00313 {
00314     mCrossingEventDumping = aCrossingEventDumping;
00315     return SetBoolPrefAndRefresh("nglayout.debug.crossing_event_dumping", mCrossingEventDumping);
00316 }
00317 
00318 NS_IMETHODIMP
00319 nsLayoutDebuggingTools::GetReflowCounts(PRBool* aShow)
00320 {
00321     *aShow = mReflowCounts;
00322     return NS_OK;
00323 }
00324 
00325 NS_IMETHODIMP
00326 nsLayoutDebuggingTools::SetReflowCounts(PRBool aShow)
00327 {
00328     nsCOMPtr<nsIPresShell> shell(pres_shell(mDocShell)); 
00329     if (shell) {
00330 #ifdef MOZ_REFLOW_PERF
00331         shell->SetPaintFrameCount(aShow);
00332         SetBoolPrefAndRefresh("layout.reflow.showframecounts", aShow);
00333         mReflowCounts = aShow;
00334 #else
00335         printf("************************************************\n");
00336         printf("Sorry, you have not built with MOZ_REFLOW_PERF=1\n");
00337         printf("************************************************\n");
00338 #endif
00339     }
00340     return NS_OK;
00341 }
00342 
00343 static void DumpAWebShell(nsIDocShellTreeItem* aShellItem, FILE* out, PRInt32 aIndent)
00344 {
00345     nsXPIDLString name;
00346     nsCOMPtr<nsIDocShellTreeItem> parent;
00347     PRInt32 i, n;
00348 
00349     for (i = aIndent; --i >= 0; )
00350         fprintf(out, "  ");
00351 
00352     fprintf(out, "%p '", NS_STATIC_CAST(void*, aShellItem));
00353     aShellItem->GetName(getter_Copies(name));
00354     aShellItem->GetSameTypeParent(getter_AddRefs(parent));
00355     fputs(NS_LossyConvertUCS2toASCII(name).get(), out);
00356     fprintf(out, "' parent=%p <\n", NS_STATIC_CAST(void*, parent));
00357 
00358     ++aIndent;
00359     nsCOMPtr<nsIDocShellTreeNode> shellAsNode(do_QueryInterface(aShellItem));
00360     shellAsNode->GetChildCount(&n);
00361     for (i = 0; i < n; ++i) {
00362         nsCOMPtr<nsIDocShellTreeItem> child;
00363         shellAsNode->GetChildAt(i, getter_AddRefs(child));
00364         if (child) {
00365             DumpAWebShell(child, out, aIndent);
00366         }
00367     }
00368     --aIndent;
00369     for (i = aIndent; --i >= 0; )
00370         fprintf(out, "  ");
00371     fputs(">\n", out);
00372 }
00373 
00374 NS_IMETHODIMP
00375 nsLayoutDebuggingTools::DumpWebShells()
00376 {
00377     nsCOMPtr<nsIDocShellTreeItem> shellAsItem(do_QueryInterface(mDocShell));
00378     DumpAWebShell(shellAsItem, stdout, 0);
00379     return NS_OK;
00380 }
00381 
00382 static
00383 void
00384 DumpContentRecur(nsIDocShell* aDocShell, FILE* out)
00385 {
00386 #ifdef DEBUG
00387     if (nsnull != aDocShell) {
00388         fprintf(out, "docshell=%p \n", NS_STATIC_CAST(void*, aDocShell));
00389         nsCOMPtr<nsIDocument> doc(document(aDocShell));
00390         if (doc) {
00391             nsIContent *root = doc->GetRootContent();
00392             if (root) {
00393                 root->List(out);
00394             }
00395         }
00396         else {
00397             fputs("no document\n", out);
00398         }
00399         // dump the frames of the sub documents
00400         PRInt32 i, n;
00401         nsCOMPtr<nsIDocShellTreeNode> docShellAsNode(do_QueryInterface(aDocShell));
00402         docShellAsNode->GetChildCount(&n);
00403         for (i = 0; i < n; ++i) {
00404             nsCOMPtr<nsIDocShellTreeItem> child;
00405             docShellAsNode->GetChildAt(i, getter_AddRefs(child));
00406             nsCOMPtr<nsIDocShell> childAsShell(do_QueryInterface(child));
00407             if (child) {
00408                 DumpContentRecur(childAsShell, out);
00409             }
00410         }
00411     }
00412 #endif
00413 }
00414 
00415 NS_IMETHODIMP
00416 nsLayoutDebuggingTools::DumpContent()
00417 {
00418     DumpContentRecur(mDocShell, stdout);
00419     return NS_OK;
00420 }
00421 
00422 static void
00423 DumpFramesRecur(nsIDocShell* aDocShell, FILE* out)
00424 {
00425     if (nsnull != aDocShell) {
00426         fprintf(out, "webshell=%p \n", NS_STATIC_CAST(void*, aDocShell));
00427         nsCOMPtr<nsIPresShell> shell(pres_shell(aDocShell));
00428         if (shell) {
00429             nsIFrame* root = shell->GetRootFrame();
00430             if (root) {
00431                 nsIFrameDebug* fdbg;
00432                 if (NS_SUCCEEDED(CallQueryInterface(root, &fdbg))) {
00433                     fdbg->List(shell->GetPresContext(), out, 0);
00434                 }
00435             }
00436         }
00437         else {
00438             fputs("null pres shell\n", out);
00439         }
00440 
00441         // dump the frames of the sub documents
00442         PRInt32 i, n;
00443         nsCOMPtr<nsIDocShellTreeNode> docShellAsNode(do_QueryInterface(aDocShell));
00444         docShellAsNode->GetChildCount(&n);
00445         for (i = 0; i < n; ++i) {
00446             nsCOMPtr<nsIDocShellTreeItem> child;
00447             docShellAsNode->GetChildAt(i, getter_AddRefs(child));
00448             nsCOMPtr<nsIDocShell> childAsShell(do_QueryInterface(child));
00449             if (childAsShell) {
00450                 DumpFramesRecur(childAsShell, out);
00451             }
00452         }
00453     }
00454 }
00455 
00456 NS_IMETHODIMP
00457 nsLayoutDebuggingTools::DumpFrames()
00458 {
00459     DumpFramesRecur(mDocShell, stdout);
00460     return NS_OK;
00461 }
00462 
00463 static
00464 void
00465 DumpViewsRecur(nsIDocShell* aDocShell, FILE* out)
00466 {
00467 #ifdef DEBUG
00468     if (aDocShell) {
00469         fprintf(out, "docshell=%p \n", NS_STATIC_CAST(void*, aDocShell));
00470         nsCOMPtr<nsIViewManager> vm(view_manager(aDocShell));
00471         if (vm) {
00472             nsIView* root;
00473             vm->GetRootView(root);
00474             if (nsnull != root) {
00475                 root->List(out);
00476             }
00477         }
00478         else {
00479             fputs("null view manager\n", out);
00480         }
00481 
00482         // dump the views of the sub documents
00483         PRInt32 i, n;
00484         nsCOMPtr<nsIDocShellTreeNode> docShellAsNode(do_QueryInterface(aDocShell));
00485         docShellAsNode->GetChildCount(&n);
00486         for (i = 0; i < n; i++) {
00487             nsCOMPtr<nsIDocShellTreeItem> child;
00488             docShellAsNode->GetChildAt(i, getter_AddRefs(child));
00489             nsCOMPtr<nsIDocShell> childAsShell(do_QueryInterface(child));
00490             if (childAsShell) {
00491                 DumpViewsRecur(childAsShell, out);
00492             }
00493         }
00494     }
00495 #endif // DEBUG
00496 }
00497 
00498 NS_IMETHODIMP
00499 nsLayoutDebuggingTools::DumpViews()
00500 {
00501     DumpViewsRecur(mDocShell, stdout);
00502     return NS_OK;
00503 }
00504 
00505 NS_IMETHODIMP
00506 nsLayoutDebuggingTools::DumpStyleSheets()
00507 {
00508 #ifdef DEBUG
00509     FILE *out = stdout;
00510     nsCOMPtr<nsIPresShell> shell(pres_shell(mDocShell)); 
00511     if (shell)
00512         shell->ListStyleSheets(out);
00513     else
00514         fputs("null pres shell\n", out);
00515 #endif
00516     return NS_OK;
00517 }
00518 
00519 NS_IMETHODIMP
00520 nsLayoutDebuggingTools::DumpStyleContexts()
00521 {
00522 #ifdef DEBUG
00523     FILE *out = stdout;
00524     nsCOMPtr<nsIPresShell> shell(pres_shell(mDocShell)); 
00525     if (shell) {
00526         nsIFrame* root = shell->GetRootFrame();
00527         if (!root) {
00528             fputs("null root frame\n", out);
00529         } else {
00530             shell->ListStyleContexts(root, out);
00531         }
00532     } else {
00533         fputs("null pres shell\n", out);
00534     }
00535 #endif
00536     return NS_OK;
00537 }
00538 
00539 NS_IMETHODIMP
00540 nsLayoutDebuggingTools::DumpReflowStats()
00541 {
00542 #ifdef DEBUG
00543     nsCOMPtr<nsIPresShell> shell(pres_shell(mDocShell)); 
00544     if (shell) {
00545 #ifdef MOZ_REFLOW_PERF
00546         shell->DumpReflows();
00547 #else
00548         printf("************************************************\n");
00549         printf("Sorry, you have not built with MOZ_REFLOW_PERF=1\n");
00550         printf("************************************************\n");
00551 #endif
00552     }
00553 #endif
00554     return NS_OK;
00555 }
00556 
00557 void nsLayoutDebuggingTools::ForceRefresh()
00558 {
00559     nsCOMPtr<nsIViewManager> vm(view_manager(mDocShell));
00560     if (!vm)
00561         return;
00562     nsIView* root = nsnull;
00563     vm->GetRootView(root);
00564     if (root) {
00565         vm->UpdateView(root, NS_VMREFRESH_IMMEDIATE);
00566     }
00567 }
00568 
00569 nsresult
00570 nsLayoutDebuggingTools::SetBoolPrefAndRefresh(const char * aPrefName,
00571                                               PRBool aNewVal)
00572 {
00573     NS_ENSURE_TRUE(mPrefs && aPrefName, NS_OK);
00574 
00575     mPrefs->SetBoolPref(aPrefName, aNewVal);
00576     mPrefs->SavePrefFile(nsnull);
00577 
00578     ForceRefresh();
00579 
00580     return NS_OK;
00581 }
00582 
00583 nsresult
00584 nsLayoutDebuggingTools::GetBoolPref(const char * aPrefName,
00585                                     PRBool *aValue)
00586 {
00587     NS_ENSURE_TRUE(mPrefs && aPrefName, NS_OK);
00588 
00589     mPrefs->GetBoolPref(aPrefName, aValue);
00590 
00591     return NS_OK;
00592 }