michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: // vim:cindent:tabstop=4:expandtab:shiftwidth=4: michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsLayoutDebuggingTools.h" michael@0: michael@0: #include "nsIDocShell.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsIContentViewer.h" michael@0: michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIAtom.h" michael@0: #include "nsQuickSort.h" michael@0: michael@0: #include "nsIContent.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIDOMDocument.h" michael@0: michael@0: #include "nsIPresShell.h" michael@0: #include "nsViewManager.h" michael@0: #include "nsIFrame.h" michael@0: michael@0: #include "nsILayoutDebugger.h" michael@0: #include "nsLayoutCID.h" michael@0: static NS_DEFINE_CID(kLayoutDebuggerCID, NS_LAYOUT_DEBUGGER_CID); michael@0: michael@0: #include "nsISelectionController.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "mozilla/Preferences.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: static already_AddRefed michael@0: doc_viewer(nsIDocShell *aDocShell) michael@0: { michael@0: if (!aDocShell) michael@0: return nullptr; michael@0: nsCOMPtr result; michael@0: aDocShell->GetContentViewer(getter_AddRefs(result)); michael@0: return result.forget(); michael@0: } michael@0: michael@0: static already_AddRefed michael@0: pres_shell(nsIDocShell *aDocShell) michael@0: { michael@0: nsCOMPtr cv = doc_viewer(aDocShell); michael@0: if (!cv) michael@0: return nullptr; michael@0: nsCOMPtr result; michael@0: cv->GetPresShell(getter_AddRefs(result)); michael@0: return result.forget(); michael@0: } michael@0: michael@0: static nsViewManager* michael@0: view_manager(nsIDocShell *aDocShell) michael@0: { michael@0: nsCOMPtr shell(pres_shell(aDocShell)); michael@0: if (!shell) michael@0: return nullptr; michael@0: return shell->GetViewManager(); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: static already_AddRefed michael@0: document(nsIDocShell *aDocShell) michael@0: { michael@0: nsCOMPtr cv(doc_viewer(aDocShell)); michael@0: if (!cv) michael@0: return nullptr; michael@0: nsCOMPtr domDoc; michael@0: cv->GetDOMDocument(getter_AddRefs(domDoc)); michael@0: if (!domDoc) michael@0: return nullptr; michael@0: nsCOMPtr result = do_QueryInterface(domDoc); michael@0: return result.forget(); michael@0: } michael@0: #endif michael@0: michael@0: nsLayoutDebuggingTools::nsLayoutDebuggingTools() michael@0: : mPaintFlashing(false), michael@0: mPaintDumping(false), michael@0: mInvalidateDumping(false), michael@0: mEventDumping(false), michael@0: mMotionEventDumping(false), michael@0: mCrossingEventDumping(false), michael@0: mReflowCounts(false) michael@0: { michael@0: NewURILoaded(); michael@0: } michael@0: michael@0: nsLayoutDebuggingTools::~nsLayoutDebuggingTools() michael@0: { michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsLayoutDebuggingTools, nsILayoutDebuggingTools) michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::Init(nsIDOMWindow *aWin) michael@0: { michael@0: if (!Preferences::GetService()) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: { michael@0: nsCOMPtr window = do_QueryInterface(aWin); michael@0: if (!window) michael@0: return NS_ERROR_UNEXPECTED; michael@0: mDocShell = window->GetDocShell(); michael@0: } michael@0: NS_ENSURE_TRUE(mDocShell, NS_ERROR_UNEXPECTED); michael@0: michael@0: mPaintFlashing = michael@0: Preferences::GetBool("nglayout.debug.paint_flashing", mPaintFlashing); michael@0: mPaintDumping = michael@0: Preferences::GetBool("nglayout.debug.paint_dumping", mPaintDumping); michael@0: mInvalidateDumping = michael@0: Preferences::GetBool("nglayout.debug.invalidate_dumping", mInvalidateDumping); michael@0: mEventDumping = michael@0: Preferences::GetBool("nglayout.debug.event_dumping", mEventDumping); michael@0: mMotionEventDumping = michael@0: Preferences::GetBool("nglayout.debug.motion_event_dumping", michael@0: mMotionEventDumping); michael@0: mCrossingEventDumping = michael@0: Preferences::GetBool("nglayout.debug.crossing_event_dumping", michael@0: mCrossingEventDumping); michael@0: mReflowCounts = michael@0: Preferences::GetBool("layout.reflow.showframecounts", mReflowCounts); michael@0: michael@0: { michael@0: nsCOMPtr ld = do_GetService(kLayoutDebuggerCID); michael@0: if (ld) { michael@0: ld->GetShowFrameBorders(&mVisualDebugging); michael@0: ld->GetShowEventTargetFrameBorder(&mVisualEventDebugging); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::NewURILoaded() michael@0: { michael@0: NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED); michael@0: // Reset all the state that should be reset between pages. michael@0: michael@0: // XXX Some of these should instead be transferred between pages! michael@0: mEditorMode = false; michael@0: mVisualDebugging = false; michael@0: mVisualEventDebugging = false; michael@0: michael@0: mReflowCounts = false; michael@0: michael@0: ForceRefresh(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::GetVisualDebugging(bool *aVisualDebugging) michael@0: { michael@0: *aVisualDebugging = mVisualDebugging; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::SetVisualDebugging(bool aVisualDebugging) michael@0: { michael@0: nsCOMPtr ld = do_GetService(kLayoutDebuggerCID); michael@0: if (!ld) michael@0: return NS_ERROR_UNEXPECTED; michael@0: mVisualDebugging = aVisualDebugging; michael@0: ld->SetShowFrameBorders(aVisualDebugging); michael@0: ForceRefresh(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::GetVisualEventDebugging(bool *aVisualEventDebugging) michael@0: { michael@0: *aVisualEventDebugging = mVisualEventDebugging; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::SetVisualEventDebugging(bool aVisualEventDebugging) michael@0: { michael@0: nsCOMPtr ld = do_GetService(kLayoutDebuggerCID); michael@0: if (!ld) michael@0: return NS_ERROR_UNEXPECTED; michael@0: mVisualEventDebugging = aVisualEventDebugging; michael@0: ld->SetShowEventTargetFrameBorder(aVisualEventDebugging); michael@0: ForceRefresh(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::GetPaintFlashing(bool *aPaintFlashing) michael@0: { michael@0: *aPaintFlashing = mPaintFlashing; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::SetPaintFlashing(bool aPaintFlashing) michael@0: { michael@0: mPaintFlashing = aPaintFlashing; michael@0: return SetBoolPrefAndRefresh("nglayout.debug.paint_flashing", mPaintFlashing); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::GetPaintDumping(bool *aPaintDumping) michael@0: { michael@0: *aPaintDumping = mPaintDumping; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::SetPaintDumping(bool aPaintDumping) michael@0: { michael@0: mPaintDumping = aPaintDumping; michael@0: return SetBoolPrefAndRefresh("nglayout.debug.paint_dumping", mPaintDumping); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::GetInvalidateDumping(bool *aInvalidateDumping) michael@0: { michael@0: *aInvalidateDumping = mInvalidateDumping; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::SetInvalidateDumping(bool aInvalidateDumping) michael@0: { michael@0: mInvalidateDumping = aInvalidateDumping; michael@0: return SetBoolPrefAndRefresh("nglayout.debug.invalidate_dumping", mInvalidateDumping); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::GetEventDumping(bool *aEventDumping) michael@0: { michael@0: *aEventDumping = mEventDumping; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::SetEventDumping(bool aEventDumping) michael@0: { michael@0: mEventDumping = aEventDumping; michael@0: return SetBoolPrefAndRefresh("nglayout.debug.event_dumping", mEventDumping); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::GetMotionEventDumping(bool *aMotionEventDumping) michael@0: { michael@0: *aMotionEventDumping = mMotionEventDumping; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::SetMotionEventDumping(bool aMotionEventDumping) michael@0: { michael@0: mMotionEventDumping = aMotionEventDumping; michael@0: return SetBoolPrefAndRefresh("nglayout.debug.motion_event_dumping", mMotionEventDumping); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::GetCrossingEventDumping(bool *aCrossingEventDumping) michael@0: { michael@0: *aCrossingEventDumping = mCrossingEventDumping; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::SetCrossingEventDumping(bool aCrossingEventDumping) michael@0: { michael@0: mCrossingEventDumping = aCrossingEventDumping; michael@0: return SetBoolPrefAndRefresh("nglayout.debug.crossing_event_dumping", mCrossingEventDumping); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::GetReflowCounts(bool* aShow) michael@0: { michael@0: *aShow = mReflowCounts; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::SetReflowCounts(bool aShow) michael@0: { michael@0: NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED); michael@0: nsCOMPtr shell(pres_shell(mDocShell)); michael@0: if (shell) { michael@0: #ifdef MOZ_REFLOW_PERF michael@0: shell->SetPaintFrameCount(aShow); michael@0: SetBoolPrefAndRefresh("layout.reflow.showframecounts", aShow); michael@0: mReflowCounts = aShow; michael@0: #else michael@0: printf("************************************************\n"); michael@0: printf("Sorry, you have not built with MOZ_REFLOW_PERF=1\n"); michael@0: printf("************************************************\n"); michael@0: #endif michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: static void DumpAWebShell(nsIDocShellTreeItem* aShellItem, FILE* out, int32_t aIndent) michael@0: { michael@0: nsString name; michael@0: nsCOMPtr parent; michael@0: int32_t i, n; michael@0: michael@0: for (i = aIndent; --i >= 0; ) michael@0: fprintf(out, " "); michael@0: michael@0: fprintf(out, "%p '", static_cast(aShellItem)); michael@0: aShellItem->GetName(name); michael@0: aShellItem->GetSameTypeParent(getter_AddRefs(parent)); michael@0: fputs(NS_LossyConvertUTF16toASCII(name).get(), out); michael@0: fprintf(out, "' parent=%p <\n", static_cast(parent)); michael@0: michael@0: ++aIndent; michael@0: aShellItem->GetChildCount(&n); michael@0: for (i = 0; i < n; ++i) { michael@0: nsCOMPtr child; michael@0: aShellItem->GetChildAt(i, getter_AddRefs(child)); michael@0: if (child) { michael@0: DumpAWebShell(child, out, aIndent); michael@0: } michael@0: } michael@0: --aIndent; michael@0: for (i = aIndent; --i >= 0; ) michael@0: fprintf(out, " "); michael@0: fputs(">\n", out); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::DumpWebShells() michael@0: { michael@0: NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED); michael@0: DumpAWebShell(mDocShell, stdout, 0); michael@0: return NS_OK; michael@0: } michael@0: michael@0: static michael@0: void michael@0: DumpContentRecur(nsIDocShell* aDocShell, FILE* out) michael@0: { michael@0: #ifdef DEBUG michael@0: if (nullptr != aDocShell) { michael@0: fprintf(out, "docshell=%p \n", static_cast(aDocShell)); michael@0: nsCOMPtr doc(document(aDocShell)); michael@0: if (doc) { michael@0: dom::Element *root = doc->GetRootElement(); michael@0: if (root) { michael@0: root->List(out); michael@0: } michael@0: } michael@0: else { michael@0: fputs("no document\n", out); michael@0: } michael@0: // dump the frames of the sub documents michael@0: int32_t i, n; michael@0: aDocShell->GetChildCount(&n); michael@0: for (i = 0; i < n; ++i) { michael@0: nsCOMPtr child; michael@0: aDocShell->GetChildAt(i, getter_AddRefs(child)); michael@0: nsCOMPtr childAsShell(do_QueryInterface(child)); michael@0: if (child) { michael@0: DumpContentRecur(childAsShell, out); michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::DumpContent() michael@0: { michael@0: NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED); michael@0: DumpContentRecur(mDocShell, stdout); michael@0: return NS_OK; michael@0: } michael@0: michael@0: static void michael@0: DumpFramesRecur(nsIDocShell* aDocShell, FILE* out) michael@0: { michael@0: #ifdef DEBUG michael@0: fprintf(out, "webshell=%p \n", static_cast(aDocShell)); michael@0: nsCOMPtr shell(pres_shell(aDocShell)); michael@0: if (shell) { michael@0: nsIFrame* root = shell->GetRootFrame(); michael@0: if (root) { michael@0: root->List(out); michael@0: } michael@0: } michael@0: else { michael@0: fputs("null pres shell\n", out); michael@0: } michael@0: michael@0: // dump the frames of the sub documents michael@0: int32_t i, n; michael@0: aDocShell->GetChildCount(&n); michael@0: for (i = 0; i < n; ++i) { michael@0: nsCOMPtr child; michael@0: aDocShell->GetChildAt(i, getter_AddRefs(child)); michael@0: nsCOMPtr childAsShell(do_QueryInterface(child)); michael@0: if (childAsShell) { michael@0: DumpFramesRecur(childAsShell, out); michael@0: } michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::DumpFrames() michael@0: { michael@0: NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED); michael@0: DumpFramesRecur(mDocShell, stdout); michael@0: return NS_OK; michael@0: } michael@0: michael@0: static michael@0: void michael@0: DumpViewsRecur(nsIDocShell* aDocShell, FILE* out) michael@0: { michael@0: #ifdef DEBUG michael@0: fprintf(out, "docshell=%p \n", static_cast(aDocShell)); michael@0: nsRefPtr vm(view_manager(aDocShell)); michael@0: if (vm) { michael@0: nsView* root = vm->GetRootView(); michael@0: if (root) { michael@0: root->List(out); michael@0: } michael@0: } michael@0: else { michael@0: fputs("null view manager\n", out); michael@0: } michael@0: michael@0: // dump the views of the sub documents michael@0: int32_t i, n; michael@0: aDocShell->GetChildCount(&n); michael@0: for (i = 0; i < n; i++) { michael@0: nsCOMPtr child; michael@0: aDocShell->GetChildAt(i, getter_AddRefs(child)); michael@0: nsCOMPtr childAsShell(do_QueryInterface(child)); michael@0: if (childAsShell) { michael@0: DumpViewsRecur(childAsShell, out); michael@0: } michael@0: } michael@0: #endif // DEBUG michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::DumpViews() michael@0: { michael@0: NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED); michael@0: DumpViewsRecur(mDocShell, stdout); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::DumpStyleSheets() michael@0: { michael@0: NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED); michael@0: #ifdef DEBUG michael@0: FILE *out = stdout; michael@0: nsCOMPtr shell(pres_shell(mDocShell)); michael@0: if (shell) michael@0: shell->ListStyleSheets(out); michael@0: else michael@0: fputs("null pres shell\n", out); michael@0: #endif michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::DumpStyleContexts() michael@0: { michael@0: NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED); michael@0: #ifdef DEBUG michael@0: FILE *out = stdout; michael@0: nsCOMPtr shell(pres_shell(mDocShell)); michael@0: if (shell) { michael@0: nsIFrame* root = shell->GetRootFrame(); michael@0: if (!root) { michael@0: fputs("null root frame\n", out); michael@0: } else { michael@0: shell->ListStyleContexts(root, out); michael@0: } michael@0: } else { michael@0: fputs("null pres shell\n", out); michael@0: } michael@0: #endif michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLayoutDebuggingTools::DumpReflowStats() michael@0: { michael@0: NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED); michael@0: #ifdef DEBUG michael@0: nsCOMPtr shell(pres_shell(mDocShell)); michael@0: if (shell) { michael@0: #ifdef MOZ_REFLOW_PERF michael@0: shell->DumpReflows(); michael@0: #else michael@0: printf("************************************************\n"); michael@0: printf("Sorry, you have not built with MOZ_REFLOW_PERF=1\n"); michael@0: printf("************************************************\n"); michael@0: #endif michael@0: } michael@0: #endif michael@0: return NS_OK; michael@0: } michael@0: michael@0: void nsLayoutDebuggingTools::ForceRefresh() michael@0: { michael@0: nsRefPtr vm(view_manager(mDocShell)); michael@0: if (!vm) michael@0: return; michael@0: nsView* root = vm->GetRootView(); michael@0: if (root) { michael@0: vm->InvalidateView(root); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsLayoutDebuggingTools::SetBoolPrefAndRefresh(const char * aPrefName, michael@0: bool aNewVal) michael@0: { michael@0: NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: nsIPrefService* prefService = Preferences::GetService(); michael@0: NS_ENSURE_TRUE(prefService && aPrefName, NS_OK); michael@0: michael@0: Preferences::SetBool(aPrefName, aNewVal); michael@0: prefService->SavePrefFile(nullptr); michael@0: michael@0: ForceRefresh(); michael@0: michael@0: return NS_OK; michael@0: }