nsPrintJob.cpp (82501B)
1 /* -*- Mode: C++; tab-width: 8; 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 "nsPrintJob.h" 8 9 #include <algorithm> 10 11 #include "mozilla/AsyncEventDispatcher.h" 12 #include "mozilla/ComputedStyleInlines.h" 13 #include "mozilla/IntegerRange.h" 14 #include "mozilla/PresShell.h" 15 #include "mozilla/PresShellInlines.h" 16 #include "mozilla/StaticPrefs_print.h" 17 #include "mozilla/Try.h" 18 #include "mozilla/dom/AnimationTimelinesController.h" 19 #include "mozilla/dom/BrowsingContext.h" 20 #include "mozilla/dom/ContentChild.h" 21 #include "mozilla/dom/CustomEvent.h" 22 #include "mozilla/dom/DocumentTimeline.h" 23 #include "mozilla/dom/HTMLCanvasElement.h" 24 #include "mozilla/dom/PBrowser.h" 25 #include "mozilla/dom/ScriptSettings.h" 26 #include "mozilla/dom/Selection.h" 27 #include "mozilla/dom/ShadowRoot.h" 28 #include "mozilla/glean/PrintingMetrics.h" 29 #include "nsDebug.h" 30 #include "nsDocShell.h" 31 #include "nsError.h" 32 #include "nsIBrowserChild.h" 33 #include "nsIDocShell.h" 34 #include "nsIOService.h" 35 #include "nsIScriptGlobalObject.h" 36 #include "nsIStringBundle.h" 37 #include "nsITextToSubURI.h" 38 #include "nsIURI.h" 39 #include "nsPIDOMWindow.h" 40 #include "nsPrintData.h" 41 #include "nsPrintObject.h" 42 #include "nsQueryObject.h" 43 #include "nsReadableUtils.h" 44 #include "nsSubDocumentFrame.h" 45 46 // Print Options 47 #include "nsGkAtoms.h" 48 #include "nsIPrintSettings.h" 49 #include "nsIPrintSettingsService.h" 50 #include "nsXPCOM.h" 51 52 static const char sPrintSettingsServiceContractID[] = 53 "@mozilla.org/gfx/printsettings-service;1"; 54 55 // Printing Timer 56 #include "nsPagePrintTimer.h" 57 58 // FrameSet 59 #include "mozilla/dom/Document.h" 60 #include "mozilla/dom/DocumentInlines.h" 61 62 // Misc 63 #include "Text.h" 64 #include "gfxContext.h" 65 #include "mozilla/Components.h" 66 #include "mozilla/Preferences.h" 67 #include "mozilla/PresShell.h" 68 #include "mozilla/ReflowInput.h" 69 #include "mozilla/ServoStyleSet.h" 70 #include "mozilla/dom/Element.h" 71 #include "mozilla/dom/HTMLFrameElement.h" 72 #include "mozilla/gfx/DrawEventRecorder.h" 73 #include "mozilla/layout/RemotePrintJobChild.h" 74 #include "nsComponentManagerUtils.h" 75 #include "nsDeviceContextSpecProxy.h" 76 #include "nsFocusManager.h" 77 #include "nsIDeviceContextSpec.h" 78 #include "nsIDocumentViewer.h" 79 #include "nsIDocumentViewerPrint.h" 80 #include "nsIInterfaceRequestor.h" 81 #include "nsIInterfaceRequestorUtils.h" 82 #include "nsIScriptContext.h" 83 #include "nsISupportsUtils.h" 84 #include "nsIWebBrowserChrome.h" 85 #include "nsPageSequenceFrame.h" 86 #include "nsRange.h" 87 88 using namespace mozilla; 89 using namespace mozilla::dom; 90 91 //----------------------------------------------------- 92 // PR LOGGING 93 #include "mozilla/Logging.h" 94 95 #ifdef DEBUG 96 // PR_LOGGING is force to always be on (even in release builds) 97 // but we only want some of it on, 98 // #define EXTENDED_DEBUG_PRINTING 99 #endif 100 101 // this log level turns on the dumping of each document's layout info 102 #define DUMP_LAYOUT_LEVEL (static_cast<mozilla::LogLevel>(9)) 103 104 #ifndef PR_PL 105 static mozilla::LazyLogModule gPrintingLog("printing"); 106 107 # define PR_PL(_p1) MOZ_LOG(gPrintingLog, mozilla::LogLevel::Debug, _p1); 108 #endif 109 110 #ifdef EXTENDED_DEBUG_PRINTING 111 static uint32_t gDumpFileNameCnt = 0; 112 static uint32_t gDumpLOFileNameCnt = 0; 113 #endif 114 115 #define PRT_YESNO(_p) ((_p) ? "YES" : "NO") 116 117 inline const char* LoggableTypeOfPO(const nsPrintObject* aPO) { 118 // TODO(dholbert): These strings used to represent actual enum values, but 119 // the enum type has been removed. We could replace them with more 120 // descriptive names, if anyone uses this logging and cares to do so. 121 return aPO->mParent ? "eIFrame" : "eDoc"; 122 } 123 124 inline const char* ShortLoggableTypeOfPO(const nsPrintObject* aPO) { 125 return aPO->mParent ? "IF" : "DC"; 126 } 127 128 // This processes the selection on aOrigDoc and creates an inverted selection on 129 // aDoc, which it then deletes. If the start or end of the inverted selection 130 // ranges occur in text nodes then an ellipsis is added. 131 static nsresult DeleteNonSelectedNodes(Document& aDoc); 132 133 #ifdef EXTENDED_DEBUG_PRINTING 134 // Forward Declarations 135 static void DumpPrintObjectsListStart(const char* aStr, 136 const nsTArray<nsPrintObject*>& aDocList); 137 static void DumpPrintObjectsTree(nsPrintObject* aPO, int aLevel = 0, 138 FILE* aFD = nullptr); 139 static void DumpPrintObjectsTreeLayout(const UniquePtr<nsPrintObject>& aPO, 140 nsDeviceContext* aDC, int aLevel = 0, 141 FILE* aFD = nullptr); 142 143 # define DUMP_DOC_LIST(_title) \ 144 DumpPrintObjectsListStart((_title), mPrintDocList); 145 # define DUMP_DOC_TREE DumpPrintObjectsTree(mPrintObject.get()); 146 # define DUMP_DOC_TREELAYOUT \ 147 DumpPrintObjectsTreeLayout(mPrintObject, mPrt->mPrintDC); 148 #else 149 # define DUMP_DOC_LIST(_title) 150 # define DUMP_DOC_TREE 151 # define DUMP_DOC_TREELAYOUT 152 #endif 153 154 // ------------------------------------------------------- 155 // Helpers 156 // ------------------------------------------------------- 157 158 /** 159 * Build a tree of nsPrintObjects under aPO. It also appends a (depth first) 160 * flat list of all the nsPrintObjects created to mPrintDocList. If 161 * one of the nsPrintObject's document is the focused document, then the print 162 * object is set as mSelectionRoot. 163 * @param aParentPO The parent nsPrintObject to populate, must not be null. 164 */ 165 void nsPrintJob::BuildNestedPrintObjects( 166 const UniquePtr<nsPrintObject>& aParentPO) { 167 MOZ_ASSERT(aParentPO); 168 169 // If aParentPO is for an iframe and its original document was the document 170 // that had focus then always set as the selection root. 171 if (aParentPO->mParent && 172 aParentPO->mDocument->GetProperty(nsGkAtoms::printisfocuseddoc)) { 173 mSelectionRoot = aParentPO.get(); 174 } else if (!mSelectionRoot && aParentPO->HasSelection()) { 175 // If there is no focused iframe but there is a selection in one or more 176 // frames then we want to set the root nsPrintObject as the focus root so 177 // that later EnablePrintingSelectionOnly can search for and enable all 178 // nsPrintObjects containing selections. 179 mSelectionRoot = mPrintObject.get(); 180 } 181 182 for (auto& bc : aParentPO->mDocShell->GetBrowsingContext()->Children()) { 183 nsCOMPtr<nsIDocShell> docShell = bc->GetDocShell(); 184 if (!docShell) { 185 if (auto* cc = dom::ContentChild::GetSingleton()) { 186 nsCOMPtr<nsIPrintSettingsService> printSettingsService = 187 do_GetService(sPrintSettingsServiceContractID); 188 embedding::PrintData printData; 189 printSettingsService->SerializeToPrintData(mPrintSettings, &printData); 190 (void)cc->SendUpdateRemotePrintSettings(bc, printData); 191 } 192 continue; 193 } 194 195 RefPtr<Document> doc = docShell->GetDocument(); 196 MOZ_DIAGNOSTIC_ASSERT(doc); 197 // We might find non-static documents here if the fission remoteness change 198 // hasn't happened / finished yet. In that case, just skip them, the same 199 // way we do for remote frames above. 200 MOZ_DIAGNOSTIC_ASSERT(doc->IsStaticDocument() || doc->IsInitialDocument()); 201 if (!doc || !doc->IsStaticDocument()) { 202 continue; 203 } 204 205 // Note: docShell and doc are known-non-null at this point; they've been 206 // null-checked above (with null leading to 'continue' statements). 207 auto childPO = MakeUnique<nsPrintObject>(*docShell, *doc, aParentPO.get()); 208 209 mPrintDocList.AppendElement(childPO.get()); 210 BuildNestedPrintObjects(childPO); 211 aParentPO->mKids.AppendElement(std::move(childPO)); 212 } 213 } 214 215 static nsresult GetDefaultPrintSettings(nsIPrintSettings** aSettings) { 216 *aSettings = nullptr; 217 218 nsresult rv = NS_ERROR_FAILURE; 219 nsCOMPtr<nsIPrintSettingsService> printSettingsService = 220 do_GetService(sPrintSettingsServiceContractID, &rv); 221 NS_ENSURE_SUCCESS(rv, rv); 222 223 return printSettingsService->GetDefaultPrintSettingsForPrinting(aSettings); 224 } 225 226 //------------------------------------------------------- 227 228 NS_IMPL_ISUPPORTS(nsPrintJob, nsIWebProgressListener, nsISupportsWeakReference) 229 230 //------------------------------------------------------- 231 nsPrintJob::~nsPrintJob() { 232 Destroy(); // for insurance 233 DisconnectPagePrintTimer(); 234 } 235 236 bool nsPrintJob::CheckBeforeDestroy() const { return mPreparingForPrint; } 237 238 //------------------------------------------------------- 239 void nsPrintJob::Destroy() { 240 if (mIsDestroying) { 241 return; 242 } 243 mIsDestroying = true; 244 245 DestroyPrintingData(); 246 247 mDocViewerPrint = nullptr; 248 } 249 250 //------------------------------------------------------- 251 void nsPrintJob::DestroyPrintingData() { 252 mPrintObject = nullptr; 253 mPrt = nullptr; 254 } 255 256 nsPrintJob::nsPrintJob(nsIDocumentViewerPrint& aDocViewerPrint, 257 nsIDocShell& aDocShell, Document& aOriginalDoc, 258 float aScreenDPI) 259 : mDocViewerPrint(&aDocViewerPrint), 260 mDocShell(do_GetWeakReference(&aDocShell)), 261 mScreenDPI(aScreenDPI) { 262 // Any state that we need from aOriginalDoc must be fetched and stored 263 // here, since the document that the user selected to print may mutate 264 // across consecutive PrintPreview() calls. 265 266 Element* root = aOriginalDoc.GetRootElement(); 267 mDisallowSelectionPrint = 268 root && root->HasAttr(nsGkAtoms::mozdisallowselectionprint); 269 } 270 271 //----------------------------------------------------------------- 272 std::tuple<nsPageSequenceFrame*, int32_t> 273 nsPrintJob::GetSeqFrameAndCountSheets() const { 274 if (NS_WARN_IF(!mPrt)) { 275 return {nullptr, 0}; 276 } 277 278 const nsPrintObject* po = mPrintObject.get(); 279 if (NS_WARN_IF(!po)) { 280 return {nullptr, 0}; 281 } 282 283 // This is sometimes incorrectly called before the pres shell has been created 284 // (bug 1141756). MOZ_DIAGNOSTIC_ASSERT so we'll still see the crash in 285 // Nightly/Aurora in case the other patch fixes this. 286 if (!po->mPresShell) { 287 MOZ_DIAGNOSTIC_ASSERT( 288 false, "GetSeqFrameAndCountSheets needs a non-null pres shell"); 289 return {nullptr, 0}; 290 } 291 292 nsPageSequenceFrame* seqFrame = po->mPresShell->GetPageSequenceFrame(); 293 if (!seqFrame) { 294 return {nullptr, 0}; 295 } 296 297 // count the total number of sheets 298 return {seqFrame, seqFrame->PrincipalChildList().GetLength()}; 299 } 300 301 // Foward decl for Debug Helper Functions 302 #ifdef EXTENDED_DEBUG_PRINTING 303 # ifdef XP_WIN 304 static int RemoveFilesInDir(const char* aDir); 305 # endif 306 static void GetDocTitleAndURL(const UniquePtr<nsPrintObject>& aPO, 307 nsACString& aDocStr, nsACString& aURLStr); 308 static void DumpPrintObjectsTree(nsPrintObject* aPO, int aLevel, FILE* aFD); 309 static void DumpPrintObjectsList(const nsTArray<nsPrintObject*>& aDocList); 310 static void RootFrameList(nsPresContext* aPresContext, FILE* out, 311 const char* aPrefix); 312 static void DumpLayoutData(const char* aTitleStr, const char* aURLStr, 313 nsPresContext* aPresContext, nsDeviceContext* aDC, 314 nsIFrame* aRootFrame, nsIDocShell* aDocShell, 315 FILE* aFD); 316 #endif 317 318 //-------------------------------------------------------------------------------- 319 320 nsresult nsPrintJob::CommonPrint(bool aIsPrintPreview, 321 nsIPrintSettings* aPrintSettings, 322 nsIWebProgressListener* aWebProgressListener, 323 Document& aSourceDoc) { 324 // Callers must hold a strong reference to |this| to ensure that we stay 325 // alive for the duration of this method, because our main owning reference 326 // (on nsDocumentViewer) might be cleared during this function (if we cause 327 // script to run and it cancels the print operation). 328 329 nsresult rv = DoCommonPrint(aIsPrintPreview, aPrintSettings, 330 aWebProgressListener, aSourceDoc); 331 if (NS_FAILED(rv)) { 332 if (aIsPrintPreview) { 333 mIsCreatingPrintPreview = false; 334 SetIsPrintPreview(false); 335 } else { 336 SetIsPrinting(false); 337 } 338 if (rv != NS_ERROR_ABORT && rv != NS_ERROR_OUT_OF_MEMORY) { 339 FirePrintingErrorEvent(rv); 340 } 341 DestroyPrintingData(); 342 } 343 344 return rv; 345 } 346 347 nsresult nsPrintJob::DoCommonPrint(bool aIsPrintPreview, 348 nsIPrintSettings* aPrintSettings, 349 nsIWebProgressListener* aWebProgressListener, 350 Document& aDoc) { 351 MOZ_ASSERT(aDoc.IsStaticDocument()); 352 353 nsresult rv; 354 355 // Grab the new instance with local variable to guarantee that it won't be 356 // deleted during this method. 357 // Note: Methods we call early below rely on mPrt being set. 358 mPrt = new nsPrintData(aIsPrintPreview ? nsPrintData::eIsPrintPreview 359 : nsPrintData::eIsPrinting); 360 RefPtr<nsPrintData> printData = mPrt; 361 362 if (aIsPrintPreview) { 363 mIsCreatingPrintPreview = true; 364 SetIsPrintPreview(true); 365 } else { 366 SetIsPrinting(true); 367 } 368 369 if (aWebProgressListener) { 370 printData->mPrintProgressListeners.AppendObject(aWebProgressListener); 371 } 372 if (mRemotePrintJob) { 373 // If we have a RemotePrintJob add it to the print progress listeners, 374 // so it can forward to the parent. 375 printData->mPrintProgressListeners.AppendElement(mRemotePrintJob); 376 } 377 378 // Get the docshell for this documentviewer 379 nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell, &rv)); 380 NS_ENSURE_SUCCESS(rv, rv); 381 382 // if they don't pass in a PrintSettings, then get the Global PS 383 mPrintSettings = aPrintSettings; 384 if (!mPrintSettings) { 385 MOZ_TRY(GetDefaultPrintSettings(getter_AddRefs(mPrintSettings))); 386 } 387 388 { 389 nsAutoScriptBlocker scriptBlocker; 390 // Note: docShell is implicitly non-null via do_QueryReferent necessarily 391 // having succeeded (if we got here). 392 mPrintObject = MakeUnique<nsPrintObject>(*docShell, aDoc); 393 mPrintDocList.AppendElement(mPrintObject.get()); 394 395 BuildNestedPrintObjects(mPrintObject); 396 } 397 398 // The nsAutoScriptBlocker above will now have been destroyed, which may 399 // cause our print/print-preview operation to finish. In this case, we 400 // should immediately return an error code so that the root caller knows 401 // it shouldn't continue to do anything with this instance. 402 if (mIsDestroying) { 403 return NS_ERROR_FAILURE; 404 } 405 406 // XXX This isn't really correct... 407 if (!mPrintObject->mDocument || !mPrintObject->mDocument->GetRootElement()) { 408 return NS_ERROR_GFX_PRINTER_STARTDOC; 409 } 410 411 mPrintSettings->GetShrinkToFit(&mShrinkToFit); 412 413 nsCOMPtr<nsIDeviceContextSpec> devspec; 414 if (XRE_IsContentProcess()) { 415 devspec = new nsDeviceContextSpecProxy(mRemotePrintJob); 416 } else { 417 devspec = do_CreateInstance("@mozilla.org/gfx/devicecontextspec;1", &rv); 418 NS_ENSURE_SUCCESS(rv, rv); 419 } 420 421 bool printSilently = false; 422 mPrintSettings->GetPrintSilent(&printSilently); 423 if (StaticPrefs::print_always_print_silent()) { 424 printSilently = true; 425 } 426 427 if (mIsDoingPrinting && printSilently) { 428 glean::printing::silent_print.Add(1); 429 } 430 431 MOZ_TRY(devspec->Init(mPrintSettings, mIsCreatingPrintPreview)); 432 433 printData->mPrintDC = new nsDeviceContext(); 434 MOZ_TRY(printData->mPrintDC->InitForPrinting(devspec)); 435 436 MOZ_TRY(EnablePOsForPrinting()); 437 438 if (!mIsCreatingPrintPreview) { 439 printData->OnStartPrinting(); 440 } 441 InitPrintDocConstruction(false); 442 443 return NS_OK; 444 } 445 446 //--------------------------------------------------------------------------------- 447 nsresult nsPrintJob::Print(Document& aDoc, nsIPrintSettings* aPrintSettings, 448 RemotePrintJobChild* aRemotePrintJob, 449 nsIWebProgressListener* aWebProgressListener) { 450 mRemotePrintJob = aRemotePrintJob; 451 return CommonPrint(false, aPrintSettings, aWebProgressListener, aDoc); 452 } 453 454 nsresult nsPrintJob::PrintPreview(Document& aDoc, 455 nsIPrintSettings* aPrintSettings, 456 nsIWebProgressListener* aWebProgressListener, 457 PrintPreviewResolver&& aCallback) { 458 // Take ownership of aCallback, otherwise a function further up the call 459 // stack will call it to signal failure (by passing zero). 460 mPrintPreviewCallback = std::move(aCallback); 461 462 nsresult rv = CommonPrint(true, aPrintSettings, aWebProgressListener, aDoc); 463 if (NS_FAILED(rv)) { 464 if (mPrintPreviewCallback) { 465 // signal error 466 mPrintPreviewCallback( 467 PrintPreviewResultInfo(0, 0, false, false, false, {}, {}, {})); 468 mPrintPreviewCallback = nullptr; 469 } 470 } 471 return rv; 472 } 473 474 int32_t nsPrintJob::GetRawNumPages() const { 475 auto [seqFrame, numSheets] = GetSeqFrameAndCountSheets(); 476 (void)numSheets; 477 return seqFrame ? seqFrame->GetRawNumPages() : 0; 478 } 479 480 bool nsPrintJob::GetIsEmpty() const { 481 auto [seqFrame, numSheets] = GetSeqFrameAndCountSheets(); 482 if (!seqFrame) { 483 return true; 484 } 485 if (numSheets > 1) { 486 return false; 487 } 488 return !seqFrame->GetPagesInFirstSheet(); 489 } 490 491 int32_t nsPrintJob::GetPrintPreviewNumSheets() const { 492 auto [seqFrame, numSheets] = GetSeqFrameAndCountSheets(); 493 (void)seqFrame; 494 return numSheets; 495 } 496 497 //----------------------------------------------------------------- 498 //-- Section: Pre-Reflow Methods 499 //----------------------------------------------------------------- 500 501 // static 502 void nsPrintJob::GetDisplayTitleAndURL(Document& aDoc, 503 nsIPrintSettings* aSettings, 504 DocTitleDefault aTitleDefault, 505 nsAString& aTitle, nsAString& aURLStr) { 506 aTitle.Truncate(); 507 aURLStr.Truncate(); 508 509 if (aSettings) { 510 aSettings->GetTitle(aTitle); 511 aSettings->GetDocURL(aURLStr); 512 } 513 514 if (aTitle.IsEmpty()) { 515 aDoc.GetTitle(aTitle); 516 if (aTitle.IsEmpty()) { 517 if (!aURLStr.IsEmpty() && 518 aTitleDefault == DocTitleDefault::eDocURLElseFallback) { 519 aTitle = aURLStr; 520 } else { 521 nsCOMPtr<nsIStringBundle> brandBundle; 522 nsCOMPtr<nsIStringBundleService> svc = 523 mozilla::components::StringBundle::Service(); 524 if (svc) { 525 svc->CreateBundle("chrome://branding/locale/brand.properties", 526 getter_AddRefs(brandBundle)); 527 if (brandBundle) { 528 brandBundle->GetStringFromName("brandShortName", aTitle); 529 } 530 } 531 if (aTitle.IsEmpty()) { 532 aTitle.AssignLiteral(u"Mozilla Document"); 533 } 534 } 535 } 536 } 537 538 if (aURLStr.IsEmpty()) { 539 nsIURI* url = aDoc.GetDocumentURI(); 540 if (!url) { 541 return; 542 } 543 544 nsCOMPtr<nsIURI> exposableURI = net::nsIOService::CreateExposableURI(url); 545 nsAutoCString urlCStr; 546 nsresult rv = exposableURI->GetSpec(urlCStr); 547 if (NS_FAILED(rv)) { 548 return; 549 } 550 551 nsCOMPtr<nsITextToSubURI> textToSubURI = 552 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv); 553 if (NS_FAILED(rv)) { 554 return; 555 } 556 557 textToSubURI->UnEscapeURIForUI(urlCStr, aURLStr); 558 } 559 } 560 561 //--------------------------------------------------------------------- 562 nsresult nsPrintJob::DocumentReadyForPrinting() { 563 // Send the document to the printer... 564 nsresult rv = SetupToPrintContent(); 565 if (NS_FAILED(rv)) { 566 // The print job was canceled or there was a problem 567 // So remove all other documents from the print list 568 DonePrintingSheets(nullptr, rv); 569 } 570 return rv; 571 } 572 573 /** --------------------------------------------------- 574 * Cleans up when an error occurred 575 */ 576 nsresult nsPrintJob::CleanupOnFailure(nsresult aResult, bool aIsPrinting) { 577 PR_PL(("**** Failed %s - rv 0x%" PRIX32, 578 aIsPrinting ? "Printing" : "Print Preview", 579 static_cast<uint32_t>(aResult))); 580 PROFILER_MARKER_TEXT("PrintJob", LAYOUT_Printing, MarkerStack::Capture(), 581 "nsPrintJob::CleanupOnFailure"_ns); 582 583 /* cleanup... */ 584 if (mPagePrintTimer) { 585 mPagePrintTimer->Stop(); 586 DisconnectPagePrintTimer(); 587 } 588 589 if (aIsPrinting) { 590 SetIsPrinting(false); 591 } else { 592 SetIsPrintPreview(false); 593 mIsCreatingPrintPreview = false; 594 } 595 596 /* cleanup done, let's fire-up an error dialog to notify the user 597 * what went wrong... 598 * 599 * When rv == NS_ERROR_ABORT, it means we want out of the 600 * print job without displaying any error messages 601 */ 602 if (aResult != NS_ERROR_ABORT) { 603 FirePrintingErrorEvent(aResult); 604 } 605 606 FirePrintCompletionEvent(); 607 608 return aResult; 609 } 610 611 //--------------------------------------------------------------------- 612 void nsPrintJob::FirePrintingErrorEvent(nsresult aPrintError) { 613 PROFILER_MARKER_TEXT("PrintJob", LAYOUT_Printing, MarkerStack::Capture(), 614 "nsPrintJob::FirePrintingErrorEvent"_ns); 615 616 if (mPrintPreviewCallback) { 617 // signal error 618 mPrintPreviewCallback( 619 PrintPreviewResultInfo(0, 0, false, false, false, {}, {}, {})); 620 mPrintPreviewCallback = nullptr; 621 } 622 623 nsCOMPtr<nsIDocumentViewer> viewer = do_QueryInterface(mDocViewerPrint); 624 if (NS_WARN_IF(!viewer)) { 625 return; 626 } 627 628 const RefPtr<Document> doc = viewer->GetDocument(); 629 const RefPtr<CustomEvent> event = NS_NewDOMCustomEvent(doc, nullptr, nullptr); 630 631 MOZ_ASSERT(event); 632 633 AutoJSAPI jsapi; 634 if (!jsapi.Init(event->GetParentObject())) { 635 return; 636 } 637 JSContext* cx = jsapi.cx(); 638 639 JS::Rooted<JS::Value> detail( 640 cx, JS::NumberValue(static_cast<double>(aPrintError))); 641 event->InitCustomEvent(cx, u"PrintingError"_ns, false, false, detail); 642 event->SetTrusted(true); 643 644 // Event listeners in chrome shouldn't delete this. 645 AsyncEventDispatcher::RunDOMEventWhenSafe(*doc, *event, 646 ChromeOnlyDispatch::eYes); 647 648 // Inform any progress listeners of the Error. 649 if (mPrt) { 650 // Note that nsPrintData::DoOnStatusChange() will call some listeners. 651 // So, mPrt can be cleared or recreated. 652 RefPtr<nsPrintData> printData = mPrt; 653 printData->DoOnStatusChange(aPrintError); 654 } 655 } 656 657 //----------------------------------------------------------------- 658 //-- Section: Reflow Methods 659 //----------------------------------------------------------------- 660 661 nsresult nsPrintJob::ReconstructAndReflow() { 662 if (NS_WARN_IF(!mPrt)) { 663 return NS_ERROR_FAILURE; 664 } 665 666 #if defined(XP_WIN) && defined(EXTENDED_DEBUG_PRINTING) 667 // We need to clear all the output files here 668 // because they will be re-created with second reflow of the docs 669 if (MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) { 670 RemoveFilesInDir(".\\"); 671 gDumpFileNameCnt = 0; 672 gDumpLOFileNameCnt = 0; 673 } 674 #endif 675 676 // In this loop, it's conceivable that one of our helpers might clear mPrt, 677 // while we're using it & its members! So we capture it in an owning local 678 // reference & use that instead of using mPrt directly. 679 RefPtr<nsPrintData> printData = mPrt; 680 for (nsPrintObject* po : mPrintDocList) { 681 if (!po->PrintingIsEnabled() || po->mInvisible) { 682 continue; 683 } 684 685 // When the print object has been marked as "print the document" (i.e, 686 // po->PrintingIsEnabled() is true), mPresContext and mPresShell should be 687 // non-nullptr (i.e., should've been created for the print) since they 688 // are necessary to print the document. 689 MOZ_ASSERT(po->mPresContext && po->mPresShell, 690 "mPresContext and mPresShell shouldn't be nullptr when the " 691 "print object " 692 "has been marked as \"print the document\""); 693 694 UpdateZoomRatio(po); 695 696 po->mPresContext->SetPageScale(po->mZoomRatio); 697 698 // Calculate scale factor from printer to screen 699 float printDPI = float(AppUnitsPerCSSInch()) / 700 float(printData->mPrintDC->AppUnitsPerDevPixel()); 701 po->mPresContext->SetPrintPreviewScale(mScreenDPI / printDPI); 702 703 RefPtr<PresShell> presShell(po->mPresShell); 704 if (NS_WARN_IF(presShell->IsDestroying())) { 705 return NS_ERROR_FAILURE; 706 } 707 708 presShell->ReconstructFrames(); 709 710 // If the printing was canceled or restarted with different data, 711 // let's stop doing this printing. 712 if (NS_WARN_IF(mPrt != printData)) { 713 return NS_ERROR_FAILURE; 714 } 715 716 // For all views except the first one, setup the root view. 717 // ??? Can there be multiple po for the top-level-document? 718 bool documentIsTopLevel = true; 719 if (po->mParent) { 720 nsSize adjSize; 721 bool doReturn = false; 722 documentIsTopLevel = false; 723 nsresult rv = SetRootView(po, documentIsTopLevel, doReturn, adjSize); 724 if (NS_FAILED(rv) || doReturn) { 725 return rv; 726 } 727 } 728 729 presShell->FlushPendingNotifications(FlushType::Layout); 730 731 if (NS_WARN_IF(presShell->IsDestroying())) { 732 return NS_ERROR_FAILURE; 733 } 734 735 // If the printing was canceled or restarted with different data, 736 // let's stop doing this printing. 737 if (NS_WARN_IF(mPrt != printData)) { 738 return NS_ERROR_FAILURE; 739 } 740 741 po->mDocument->UpdateRemoteFrameEffects(); 742 MOZ_TRY(UpdateSelectionAndShrinkPrintObject(po, documentIsTopLevel)); 743 } 744 return NS_OK; 745 } 746 747 //------------------------------------------------------- 748 nsresult nsPrintJob::SetupToPrintContent() { 749 // This method may be called while DoCommonPrint() initializes the instance 750 // when its script blocker goes out of scope. In such case, this cannot do 751 // its job as expected because some objects in mPrt have not been initialized 752 // yet but they are necessary. 753 // Note: it shouldn't be possible for mPrintObject to be null; we check 754 // it for good measure (after we check its owner) before we start 755 // dereferencing it below. 756 if (NS_WARN_IF(!mPrt) || NS_WARN_IF(!mPrintObject)) { 757 return NS_ERROR_FAILURE; 758 } 759 760 // If this is creating print preview, mPrintObject->mPresContext and 761 // mPrintObject->mPresShell need to be non-nullptr because this cannot 762 // initialize page sequence frame without them at end of this method since 763 // page sequence frame has already been destroyed or not been created yet. 764 if (mIsCreatingPrintPreview && (NS_WARN_IF(!mPrintObject->mPresContext) || 765 NS_WARN_IF(!mPrintObject->mPresShell))) { 766 return NS_ERROR_FAILURE; 767 } 768 769 // If this is printing some documents (not print-previewing the documents), 770 // mPrintObject->mPresContext and mPrintObject->mPresShell can be 771 // nullptr only when mPrintObject->PrintingIsEnabled() is false. E.g., 772 // if the document has a <frameset> element and it's printing only content in 773 // a <frame> element or all <frame> elements separately. 774 MOZ_ASSERT( 775 (!mIsCreatingPrintPreview && !mPrintObject->PrintingIsEnabled()) || 776 (mPrintObject->mPresContext && mPrintObject->mPresShell), 777 "mPresContext and mPresShell shouldn't be nullptr when printing the " 778 "document or creating print-preview"); 779 780 bool didReconstruction = false; 781 782 // This method works with mPrintObject. So, we need to guarantee that 783 // it won't be deleted in this method. We achieve this by holding a strong 784 // local reference to mPrt, which in turn keeps mPrintObject alive. 785 RefPtr<nsPrintData> printData = mPrt; 786 787 // If some new content got loaded since the initial reflow rebuild 788 // everything. 789 if (mDidLoadDataForPrinting) { 790 nsresult rv = ReconstructAndReflow(); 791 if (NS_WARN_IF(NS_FAILED(rv))) { 792 return rv; 793 } 794 // If the printing was canceled or restarted with different data, 795 // let's stop doing this printing. 796 if (NS_WARN_IF(mPrt != printData)) { 797 return NS_ERROR_FAILURE; 798 } 799 didReconstruction = true; 800 } 801 802 // Here is where we figure out if extra reflow for shrinking the content 803 // is required. 804 if (mShrinkToFit) { 805 mShrinkToFitFactor = mPrintObject->mShrinkRatio; 806 807 if (mShrinkToFitFactor < 0.998f) { 808 nsresult rv = ReconstructAndReflow(); 809 if (NS_WARN_IF(NS_FAILED(rv))) { 810 return rv; 811 } 812 // If the printing was canceled or restarted with different data, 813 // let's stop doing this printing. 814 if (NS_WARN_IF(mPrt != printData)) { 815 return NS_ERROR_FAILURE; 816 } 817 didReconstruction = true; 818 } 819 820 if (MOZ_LOG_TEST(gPrintingLog, LogLevel::Debug)) { 821 float calcRatio = mPrintObject->mShrinkRatio; 822 PR_PL( 823 ("*******************************************************************" 824 "*******\n")); 825 PR_PL(("STF Ratio is: %8.5f Effective Ratio: %8.5f Diff: %8.5f\n", 826 mShrinkToFitFactor, calcRatio, mShrinkToFitFactor - calcRatio)); 827 PR_PL( 828 ("*******************************************************************" 829 "*******\n")); 830 } 831 } 832 833 // If the frames got reconstructed and reflowed the number of pages might 834 // has changed. 835 if (didReconstruction) { 836 FirePrintPreviewUpdateEvent(); 837 // If the printing was canceled or restarted with different data, 838 // let's stop doing this printing. 839 if (NS_WARN_IF(mPrt != printData)) { 840 return NS_ERROR_FAILURE; 841 } 842 } 843 844 DUMP_DOC_LIST(("\nAfter Reflow------------------------------------------")); 845 PR_PL(("\n")); 846 PR_PL(("-------------------------------------------------------\n")); 847 PR_PL(("\n")); 848 849 CalcNumPrintablePages(mNumPrintablePages); 850 851 PR_PL(("--- Printing %d pages\n", mNumPrintablePages)); 852 DUMP_DOC_TREELAYOUT; 853 854 // Print listener setup... 855 printData->OnStartPrinting(); 856 857 // If the printing was canceled or restarted with different data, 858 // let's stop doing this printing. 859 if (NS_WARN_IF(mPrt != printData)) { 860 return NS_ERROR_FAILURE; 861 } 862 863 nsAutoString fileNameStr; 864 // check to see if we are printing to a file 865 if (mPrintSettings->GetOutputDestination() == 866 nsIPrintSettings::kOutputDestinationFile) { 867 // On some platforms the BeginDocument needs to know the name of the file. 868 mPrintSettings->GetToFileName(fileNameStr); 869 } 870 871 nsAutoString docTitleStr; 872 nsAutoString docURLStr; 873 GetDisplayTitleAndURL(*mPrintObject->mDocument, mPrintSettings, 874 DocTitleDefault::eDocURLElseFallback, docTitleStr, 875 docURLStr); 876 877 int32_t startPage = 1; 878 int32_t endPage = mNumPrintablePages; 879 880 nsTArray<int32_t> ranges; 881 mPrintSettings->GetPageRanges(ranges); 882 for (size_t i = 0; i < ranges.Length(); i += 2) { 883 startPage = std::max(1, std::min(startPage, ranges[i])); 884 endPage = std::min(mNumPrintablePages, std::max(endPage, ranges[i + 1])); 885 } 886 887 nsresult rv = NS_OK; 888 // BeginDocument may pass back a FAILURE code 889 // i.e. On Windows, if you are printing to a file and hit "Cancel" 890 // to the "File Name" dialog, this comes back as an error 891 // Don't start printing when regression test are executed 892 if (mIsDoingPrinting) { 893 rv = printData->mPrintDC->BeginDocument(docTitleStr, fileNameStr, startPage, 894 endPage); 895 } 896 897 if (mIsCreatingPrintPreview) { 898 // Copy docTitleStr and docURLStr to the pageSequenceFrame, to be displayed 899 // in the header 900 nsPageSequenceFrame* seqFrame = 901 mPrintObject->mPresShell->GetPageSequenceFrame(); 902 if (seqFrame) { 903 seqFrame->StartPrint(mPrintObject->mPresContext, mPrintSettings, 904 docTitleStr, docURLStr); 905 } 906 } 907 908 PR_PL(("****************** Begin Document ************************\n")); 909 910 if (NS_FAILED(rv)) { 911 NS_WARNING_ASSERTION(rv == NS_ERROR_ABORT, 912 "Failed to begin document for printing"); 913 return rv; 914 } 915 916 // This will print the docshell document 917 // when it completes asynchronously in the DonePrintingSheets method 918 // it will check to see if there are more docshells to be printed and 919 // then PrintDocContent will be called again. 920 921 if (mIsDoingPrinting) { 922 // Double-check that mPrintObject is non-null, because it could have 923 // gotten cleared while running script since we last checked it. 924 if (NS_WARN_IF(!mPrintObject)) { 925 return NS_ERROR_FAILURE; 926 } 927 928 PrintDocContent(mPrintObject, rv); // ignore return value 929 } 930 931 return rv; 932 } 933 934 //------------------------------------------------------- 935 // Recursively reflow each sub-doc and then calc 936 // all the frame locations of the sub-docs 937 nsresult nsPrintJob::ReflowDocList(const UniquePtr<nsPrintObject>& aPO) { 938 NS_ENSURE_ARG_POINTER(aPO); 939 940 // Check to see if the subdocument's element has been hidden by the parent 941 // document 942 if (aPO->mParent && aPO->mParent->mPresShell) { 943 nsIFrame* frame = 944 aPO->mContent ? aPO->mContent->GetPrimaryFrame() : nullptr; 945 if (!frame || !frame->StyleVisibility()->IsVisible()) { 946 aPO->EnablePrinting(false); 947 aPO->mInvisible = true; 948 return NS_OK; 949 } 950 } 951 952 UpdateZoomRatio(aPO.get()); 953 954 // Reflow the PO 955 MOZ_TRY(ReflowPrintObject(aPO)); 956 957 for (const UniquePtr<nsPrintObject>& kid : aPO->mKids) { 958 MOZ_TRY(ReflowDocList(kid)); 959 } 960 return NS_OK; 961 } 962 963 void nsPrintJob::FirePrintPreviewUpdateEvent() { 964 // Dispatch the event only while in PrintPreview. When printing, there is no 965 // listener bound to this event and therefore no need to dispatch it. 966 if (mCreatedForPrintPreview && !mIsDoingPrinting) { 967 nsCOMPtr<nsIDocumentViewer> viewer = do_QueryInterface(mDocViewerPrint); 968 if (Document* document = viewer->GetDocument()) { 969 AsyncEventDispatcher::RunDOMEventWhenSafe( 970 *document, u"printPreviewUpdate"_ns, CanBubble::eYes, 971 ChromeOnlyDispatch::eYes); 972 } 973 } 974 } 975 976 nsresult nsPrintJob::InitPrintDocConstruction(bool aHandleError) { 977 // Guarantee that mPrintObject won't be deleted. 978 RefPtr<nsPrintData> printData = mPrt; 979 980 if (NS_WARN_IF(!printData)) { 981 return NS_ERROR_FAILURE; 982 } 983 984 // Attach progressListener to catch network requests. 985 mDidLoadDataForPrinting = false; 986 987 { 988 AutoRestore<bool> restore{mDoingInitialReflow}; 989 mDoingInitialReflow = true; 990 991 nsCOMPtr<nsIWebProgress> webProgress = 992 do_QueryInterface(mPrintObject->mDocShell); 993 webProgress->AddProgressListener(static_cast<nsIWebProgressListener*>(this), 994 nsIWebProgress::NOTIFY_STATE_REQUEST); 995 996 MOZ_TRY(ReflowDocList(mPrintObject)); 997 998 FirePrintPreviewUpdateEvent(); 999 } 1000 1001 MaybeResumePrintAfterResourcesLoaded(aHandleError); 1002 return NS_OK; 1003 } 1004 1005 bool nsPrintJob::ShouldResumePrint() const { 1006 if (mDoingInitialReflow) { 1007 return false; 1008 } 1009 Document* doc = mPrintObject->mDocument; 1010 MOZ_ASSERT(doc); 1011 NS_ENSURE_TRUE(doc, true); 1012 nsCOMPtr<nsILoadGroup> lg = doc->GetDocumentLoadGroup(); 1013 NS_ENSURE_TRUE(lg, true); 1014 bool pending = false; 1015 nsresult rv = lg->IsPending(&pending); 1016 NS_ENSURE_SUCCESS(rv, true); 1017 return !pending; 1018 } 1019 1020 nsresult nsPrintJob::MaybeResumePrintAfterResourcesLoaded( 1021 bool aCleanupOnError) { 1022 if (!ShouldResumePrint()) { 1023 mDidLoadDataForPrinting = true; 1024 return NS_OK; 1025 } 1026 // If Destroy() has already been called, mPtr is nullptr. Then, the instance 1027 // needs to do nothing anymore in this method. 1028 // Note: it shouldn't be possible for mPrintObject to be null; we 1029 // just check it for good measure, as we check its owner. 1030 // Note: it shouldn't be possible for mPrintObject->mDocShell to be 1031 // null; we just check it for good measure, as we check its owner. 1032 if (!mPrt || NS_WARN_IF(!mPrintObject) || 1033 NS_WARN_IF(!mPrintObject->mDocShell)) { 1034 return NS_ERROR_FAILURE; 1035 } 1036 1037 nsCOMPtr<nsIWebProgress> webProgress = 1038 do_QueryInterface(mPrintObject->mDocShell); 1039 1040 webProgress->RemoveProgressListener( 1041 static_cast<nsIWebProgressListener*>(this)); 1042 1043 nsresult rv; 1044 if (mIsDoingPrinting) { 1045 rv = DocumentReadyForPrinting(); 1046 } else { 1047 rv = FinishPrintPreview(); 1048 } 1049 1050 /* cleaup on failure + notify user */ 1051 if (aCleanupOnError && NS_FAILED(rv)) { 1052 NS_WARNING_ASSERTION(rv == NS_ERROR_ABORT, 1053 "nsPrintJob::ResumePrintAfterResourcesLoaded failed"); 1054 CleanupOnFailure(rv, !mIsDoingPrinting); 1055 } 1056 1057 return rv; 1058 } 1059 1060 //////////////////////////////////////////////////////////////////////////////// 1061 // nsIWebProgressListener 1062 1063 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP 1064 nsPrintJob::OnStateChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, 1065 uint32_t aStateFlags, nsresult aStatus) { 1066 if (aStateFlags & STATE_STOP) { 1067 // If all resources are loaded, then finish and reflow. 1068 MaybeResumePrintAfterResourcesLoaded(/* aCleanupOnError */ true); 1069 } 1070 return NS_OK; 1071 } 1072 1073 NS_IMETHODIMP 1074 nsPrintJob::OnProgressChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, 1075 int32_t aCurSelfProgress, int32_t aMaxSelfProgress, 1076 int32_t aCurTotalProgress, 1077 int32_t aMaxTotalProgress) { 1078 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)"); 1079 return NS_OK; 1080 } 1081 1082 NS_IMETHODIMP 1083 nsPrintJob::OnLocationChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, 1084 nsIURI* aLocation, uint32_t aFlags) { 1085 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)"); 1086 return NS_OK; 1087 } 1088 1089 NS_IMETHODIMP 1090 nsPrintJob::OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, 1091 nsresult aStatus, const char16_t* aMessage) { 1092 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)"); 1093 return NS_OK; 1094 } 1095 1096 NS_IMETHODIMP 1097 nsPrintJob::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, 1098 uint32_t aState) { 1099 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)"); 1100 return NS_OK; 1101 } 1102 1103 NS_IMETHODIMP 1104 nsPrintJob::OnContentBlockingEvent(nsIWebProgress* aWebProgress, 1105 nsIRequest* aRequest, uint32_t aEvent) { 1106 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)"); 1107 return NS_OK; 1108 } 1109 1110 //------------------------------------------------------- 1111 1112 void nsPrintJob::UpdateZoomRatio(nsPrintObject* aPO) { 1113 if (!aPO->mParent) { 1114 if (mShrinkToFit) { 1115 aPO->mZoomRatio = mShrinkToFitFactor; 1116 // If we're actually going to scale (the factor is less than 1), we 1117 // reduce the scale factor slightly to avoid the possibility of floating 1118 // point rounding error causing slight clipping of the longest lines. 1119 if (aPO->mZoomRatio != 1.0f) { 1120 aPO->mZoomRatio -= 0.005f; 1121 } 1122 } else { 1123 double scaling; 1124 mPrintSettings->GetScaling(&scaling); 1125 aPO->mZoomRatio = float(scaling); 1126 } 1127 } 1128 } 1129 1130 nsresult nsPrintJob::UpdateSelectionAndShrinkPrintObject( 1131 nsPrintObject* aPO, bool aDocumentIsTopLevel) { 1132 PresShell* displayPresShell = aPO->mDocShell->GetPresShell(); 1133 // Transfer Selection Ranges to the new Print PresShell 1134 RefPtr<Selection> selection, selectionPS; 1135 // It's okay if there is no display shell, just skip copying the selection 1136 if (displayPresShell) { 1137 selection = displayPresShell->GetCurrentSelection(SelectionType::eNormal); 1138 } 1139 selectionPS = aPO->mPresShell->GetCurrentSelection(SelectionType::eNormal); 1140 1141 // Reset all existing selection ranges that might have been added by calling 1142 // this function before. 1143 if (selectionPS) { 1144 selectionPS->RemoveAllRanges(IgnoreErrors()); 1145 } 1146 if (selection && selectionPS) { 1147 const uint32_t rangeCount = selection->RangeCount(); 1148 for (const uint32_t inx : IntegerRange(rangeCount)) { 1149 MOZ_ASSERT(selection->RangeCount() == rangeCount); 1150 const RefPtr<nsRange> range{selection->GetRangeAt(inx)}; 1151 selectionPS->AddRangeAndSelectFramesAndNotifyListeners(*range, 1152 IgnoreErrors()); 1153 } 1154 } 1155 1156 // If we are trying to shrink the contents to fit on the page 1157 // we must first locate the "pageContent" frame 1158 // Then we walk the frame tree and look for the "xmost" frame 1159 // this is the frame where the right-hand side of the frame extends 1160 // the furthest 1161 if (mShrinkToFit && aDocumentIsTopLevel) { 1162 nsPageSequenceFrame* pageSeqFrame = aPO->mPresShell->GetPageSequenceFrame(); 1163 NS_ENSURE_STATE(pageSeqFrame); 1164 aPO->mShrinkRatio = pageSeqFrame->GetSTFPercent(); 1165 // Limit the shrink-to-fit scaling for some text-ish type of documents. 1166 nsAutoString contentType; 1167 aPO->mPresShell->GetDocument()->GetContentType(contentType); 1168 if (contentType.EqualsLiteral("application/xhtml+xml") || 1169 StringBeginsWith(contentType, u"text/"_ns)) { 1170 int32_t limitPercent = 1171 Preferences::GetInt("print.shrink-to-fit.scale-limit-percent", 20); 1172 limitPercent = std::max(0, limitPercent); 1173 limitPercent = std::min(100, limitPercent); 1174 float minShrinkRatio = float(limitPercent) / 100; 1175 aPO->mShrinkRatio = std::max(aPO->mShrinkRatio, minShrinkRatio); 1176 } 1177 } 1178 return NS_OK; 1179 } 1180 1181 nsresult nsPrintJob::SetRootView(nsPrintObject* aPO, bool aDocumentIsTopLevel, 1182 bool& doReturn, nsSize& adjSize) { 1183 bool canCreateScrollbars = true; 1184 1185 if (!aDocumentIsTopLevel) { 1186 nsIFrame* frame = 1187 aPO->mContent ? aPO->mContent->GetPrimaryFrame() : nullptr; 1188 // Without a frame, this document can't be displayed; therefore, there is no 1189 // point to reflowing it 1190 if (!frame) { 1191 aPO->EnablePrinting(false); 1192 doReturn = true; 1193 return NS_OK; 1194 } 1195 1196 // XXX If printing supported printing document hierarchies with non-constant 1197 // zoom this would be wrong as we use the same mPrt->mPrintDC for all 1198 // subdocuments. 1199 adjSize = frame->GetContentRect().Size(); 1200 // presshell exists because parent is printable 1201 1202 // the top nsPrintObject's widget will always have scrollbars 1203 if (frame && frame->IsSubDocumentFrame()) { 1204 canCreateScrollbars = false; 1205 } 1206 } else { 1207 adjSize = mPrt->mPrintDC->GetDeviceSurfaceDimensions(); 1208 } 1209 1210 if (mIsCreatingPrintPreview && aDocumentIsTopLevel) { 1211 aPO->mPresContext->SetPaginatedScrolling(canCreateScrollbars); 1212 } 1213 1214 return NS_OK; 1215 } 1216 1217 // Reflow a nsPrintObject 1218 nsresult nsPrintJob::ReflowPrintObject(const UniquePtr<nsPrintObject>& aPO) { 1219 NS_ENSURE_STATE(aPO); 1220 1221 if (!aPO->PrintingIsEnabled()) { 1222 return NS_OK; 1223 } 1224 1225 NS_ASSERTION(!aPO->mPresContext, "Recreating prescontext"); 1226 1227 // Guarantee that mPrt and the objects it owns won't be deleted in this method 1228 // because it might be cleared if other modules called from here may fire 1229 // events, notifying observers and/or listeners. 1230 RefPtr<nsPrintData> printData = mPrt; 1231 1232 // create the PresContext 1233 nsPresContext::nsPresContextType type = 1234 mIsCreatingPrintPreview ? nsPresContext::eContext_PrintPreview 1235 : nsPresContext::eContext_Print; 1236 const bool documentIsTopLevel = 1237 !aPO->mParent || !aPO->mParent->PrintingIsEnabled(); 1238 auto* embedderFrame = [&]() -> nsSubDocumentFrame* { 1239 if (documentIsTopLevel) { 1240 if (nsCOMPtr<nsIDocumentViewer> viewer = 1241 do_QueryInterface(mDocViewerPrint)) { 1242 return viewer->FindContainerFrame(); 1243 } 1244 } else if (aPO->mContent) { 1245 return do_QueryFrame(aPO->mContent->GetPrimaryFrame()); 1246 } 1247 return nullptr; 1248 }(); 1249 1250 const bool shouldBeRoot = documentIsTopLevel && !embedderFrame; 1251 aPO->mPresContext = shouldBeRoot ? new nsRootPresContext(aPO->mDocument, type) 1252 : new nsPresContext(aPO->mDocument, type); 1253 aPO->mPresContext->SetPrintSettings(mPrintSettings); 1254 1255 // init it with the DC 1256 MOZ_TRY(aPO->mPresContext->Init(printData->mPrintDC)); 1257 1258 bool doReturn = false; 1259 nsSize adjSize; 1260 nsresult rv = SetRootView(aPO.get(), documentIsTopLevel, doReturn, adjSize); 1261 if (NS_FAILED(rv) || doReturn) { 1262 return rv; 1263 } 1264 1265 // Here, we inform nsPresContext of the page size. Note that 'adjSize' is 1266 // *usually* the page size, but we need to check. Strictly speaking, adjSize 1267 // is the *device output size*, which is really the dimensions of a "sheet" 1268 // rather than a "page" (an important distinction in an N-pages-per-sheet 1269 // scenario). For some pages-per-sheet values, the pages are orthogonal to 1270 // the sheet; we adjust for that here by swapping the width with the height. 1271 nsSize pageSize = adjSize; 1272 if (mPrintSettings->HasOrthogonalPagesPerSheet()) { 1273 std::swap(pageSize.width, pageSize.height); 1274 } 1275 // XXXalaskanemily: Is this actually necessary? We set it again before the 1276 // first reflow. 1277 aPO->mPresContext->SetPageSize(pageSize); 1278 1279 int32_t p2a = aPO->mPresContext->DeviceContext()->AppUnitsPerDevPixel(); 1280 if (documentIsTopLevel && mIsCreatingPrintPreview) { 1281 if (nsCOMPtr<nsIDocumentViewer> viewer = 1282 do_QueryInterface(mDocViewerPrint)) { 1283 // If we're print-previewing and the top level document, use the bounds 1284 // from our doc viewer. Page bounds is not what we want. 1285 LayoutDeviceIntRect bounds; 1286 viewer->GetBounds(bounds); 1287 adjSize = nsSize(bounds.width * p2a, bounds.height * p2a); 1288 } 1289 } 1290 aPO->mPresContext->SetIsRootPaginatedDocument(documentIsTopLevel); 1291 aPO->mPresContext->SetVisibleArea(nsRect(nsPoint(), adjSize)); 1292 aPO->mPresContext->SetPageScale(aPO->mZoomRatio); 1293 // Calculate scale factor from printer to screen 1294 float printDPI = float(AppUnitsPerCSSInch()) / float(p2a); 1295 aPO->mPresContext->SetPrintPreviewScale(mScreenDPI / printDPI); 1296 1297 // Do CreatePresShell() after we setup the page size, the visible area, and 1298 // the flag |mIsRootPaginatedDocument|, to make sure we can resolve the 1299 // correct viewport size for the print preview page when notifying the media 1300 // feature values changed. See au_viewport_size_for_viewport_unit_resolution() 1301 // in media_queries.rs for more details. 1302 RefPtr<Document> doc = aPO->mDocument; 1303 RefPtr<nsPresContext> presContext = aPO->mPresContext; 1304 1305 aPO->mPresShell = doc->CreatePresShell(presContext, embedderFrame); 1306 if (!aPO->mPresShell) { 1307 return NS_ERROR_FAILURE; 1308 } 1309 1310 // If we're printing selection then remove the nonselected nodes from our 1311 // cloned document. 1312 if (mPrintSettings->GetPrintSelectionOnly()) { 1313 // If we fail to remove the nodes then we should fail to print, because if 1314 // the user was trying to print a small selection from a large document, 1315 // sending the whole document to a real printer would be very frustrating. 1316 MOZ_TRY(DeleteNonSelectedNodes(*aPO->mDocument)); 1317 } 1318 1319 aPO->mPresShell->BeginObservingDocument(); 1320 1321 PR_PL( 1322 ("In DV::ReflowPrintObject PO: %p pS: %p (%9s) Setting page size w,h to " 1323 "%d,%d\n", 1324 aPO.get(), aPO->mPresShell.get(), LoggableTypeOfPO(aPO.get()), 1325 pageSize.width, pageSize.height)); 1326 1327 if (mIsCreatingPrintPreview && documentIsTopLevel) { 1328 mDocViewerPrint->SetPrintPreviewPresentation(aPO->mPresContext, 1329 aPO->mPresShell.get()); 1330 } 1331 1332 MOZ_TRY(aPO->mPresShell->Initialize()); 1333 NS_ASSERTION(aPO->mPresShell, "Presshell should still be here"); 1334 1335 RefPtr<PresShell> presShell = aPO->mPresShell; 1336 { 1337 const ServoStyleSet::PageSizeAndOrientation sizeAndOrientation = 1338 presShell->StyleSet()->GetDefaultPageSizeAndOrientation(); 1339 // XXX Should we enable this for known save-to-PDF pseudo-printers once 1340 // bug 1826301 is fixed? 1341 if (mPrintSettings->GetOutputFormat() == 1342 nsIPrintSettings::kOutputFormatPDF && 1343 StaticPrefs:: 1344 print_save_as_pdf_use_page_rule_size_as_paper_size_enabled()) { 1345 mMaybeCSSPageSize = sizeAndOrientation.size; 1346 if (sizeAndOrientation.size) { 1347 pageSize = sizeAndOrientation.size.value(); 1348 aPO->mPresContext->SetPageSize(pageSize); 1349 } 1350 } 1351 1352 // If the document has a specified CSS page-size, we rotate the page to 1353 // reflect this. Changing the orientation is reflected by the result of 1354 // FinishPrintPreview, so that the frontend can reflect this. 1355 // The new document has not yet been reflowed, so we have to query the 1356 // original document for any CSS page-size. 1357 if (sizeAndOrientation.orientation) { 1358 switch (sizeAndOrientation.orientation.value()) { 1359 case StylePageSizeOrientation::Landscape: 1360 if (pageSize.width < pageSize.height) { 1361 // Paper is in portrait, CSS page size is landscape. 1362 std::swap(pageSize.width, pageSize.height); 1363 } 1364 break; 1365 case StylePageSizeOrientation::Portrait: 1366 if (pageSize.width > pageSize.height) { 1367 // Paper is in landscape, CSS page size is portrait. 1368 std::swap(pageSize.width, pageSize.height); 1369 } 1370 break; 1371 } 1372 mMaybeCSSPageLandscape = Some(sizeAndOrientation.orientation.value() == 1373 StylePageSizeOrientation::Landscape); 1374 aPO->mPresContext->SetPageSize(pageSize); 1375 } 1376 } 1377 // Make sure animations are active. 1378 aPO->mDocument->TimelinesController().TriggerAllPendingAnimationsNow(); 1379 // Process the reflow event Initialize posted 1380 presShell->FlushPendingNotifications(FlushType::Layout); 1381 aPO->mDocument->UpdateRemoteFrameEffects(); 1382 1383 MOZ_TRY(UpdateSelectionAndShrinkPrintObject(aPO.get(), documentIsTopLevel)); 1384 1385 #ifdef EXTENDED_DEBUG_PRINTING 1386 if (MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) { 1387 nsAutoCString docStr; 1388 nsAutoCString urlStr; 1389 GetDocTitleAndURL(aPO, docStr, urlStr); 1390 char filename[256]; 1391 sprintf(filename, "print_dump_%d.txt", gDumpFileNameCnt++); 1392 // Dump all the frames and view to a a file 1393 FILE* fd = fopen(filename, "w"); 1394 if (fd) { 1395 nsIFrame* theRootFrame = aPO->mPresShell->GetRootFrame(); 1396 fprintf(fd, "Title: %s\n", docStr.get()); 1397 fprintf(fd, "URL: %s\n", urlStr.get()); 1398 fprintf(fd, "--------------- Frames ----------------\n"); 1399 // RefPtr<gfxContext> renderingContext = 1400 // printData->mPrintDocDC->CreateRenderingContext(); 1401 RootFrameList(aPO->mPresContext, fd, 0); 1402 // DumpFrames(fd, aPO->mPresContext, renderingContext, theRootFrame, 0); 1403 fprintf(fd, "---------------------------------------\n\n"); 1404 fclose(fd); 1405 } 1406 } 1407 #endif 1408 1409 return NS_OK; 1410 } 1411 1412 //------------------------------------------------------- 1413 // Figure out how many documents and how many total pages we are printing 1414 void nsPrintJob::CalcNumPrintablePages(int32_t& aNumPages) { 1415 aNumPages = 0; 1416 // Count the number of printable documents and printable pages 1417 for (nsPrintObject* po : mPrintDocList) { 1418 // Note: The po->mPresContext null-check below is necessary, because it's 1419 // possible po->mPresContext might never have been set. (e.g., if 1420 // PrintingIsEnabled() returns false, ReflowPrintObject bails before setting 1421 // mPresContext) 1422 if (po->mPresContext && po->mPresContext->IsRootPaginatedDocument()) { 1423 nsPageSequenceFrame* seqFrame = po->mPresShell->GetPageSequenceFrame(); 1424 if (seqFrame) { 1425 aNumPages += seqFrame->PrincipalChildList().GetLength(); 1426 } 1427 } 1428 } 1429 } 1430 1431 //----------------------------------------------------------------- 1432 //-- Done: Reflow Methods 1433 //----------------------------------------------------------------- 1434 1435 //----------------------------------------------------------------- 1436 //-- Section: Printing Methods 1437 //----------------------------------------------------------------- 1438 1439 //------------------------------------------------------- 1440 // Called for each DocShell that needs to be printed 1441 bool nsPrintJob::PrintDocContent(const UniquePtr<nsPrintObject>& aPO, 1442 nsresult& aStatus) { 1443 NS_ASSERTION(aPO, "Pointer is null!"); 1444 aStatus = NS_OK; 1445 1446 if (!aPO->mHasBeenPrinted && aPO->PrintingIsEnabled()) { 1447 aStatus = DoPrint(aPO); 1448 return true; 1449 } 1450 1451 // If |aPO->mHasBeenPrinted| is true, 1452 // the kids frames are already processed in |PrintPage|. 1453 // XXX This should be removed. Since bug 1552785 it has no longer been 1454 // possible for us to have to print multiple subdocuments consecutively. 1455 if (!aPO->mHasBeenPrinted && !aPO->mInvisible) { 1456 for (const UniquePtr<nsPrintObject>& po : aPO->mKids) { 1457 bool printed = PrintDocContent(po, aStatus); 1458 if (printed || NS_FAILED(aStatus)) { 1459 return true; 1460 } 1461 } 1462 } 1463 return false; 1464 } 1465 1466 // A helper struct to aid with DeleteNonSelectedNodes. 1467 struct MOZ_STACK_CLASS SelectionRangeState { 1468 explicit SelectionRangeState(RefPtr<Selection> aSelection) 1469 : mSelection(std::move(aSelection)) { 1470 MOZ_ASSERT(mSelection); 1471 MOZ_ASSERT(!mSelection->RangeCount()); 1472 } 1473 1474 // Selects all the nodes that are _not_ included in a given set of ranges. 1475 MOZ_CAN_RUN_SCRIPT void SelectComplementOf(Span<const RefPtr<nsRange>>); 1476 // Removes the selected ranges from the document. 1477 MOZ_CAN_RUN_SCRIPT void RemoveSelectionFromDocument(); 1478 1479 private: 1480 struct Position { 1481 nsINode* mNode; 1482 uint32_t mOffset; 1483 }; 1484 1485 MOZ_CAN_RUN_SCRIPT void SelectRange(nsRange*); 1486 MOZ_CAN_RUN_SCRIPT void SelectNodesExcept(const Position& aStart, 1487 const Position& aEnd); 1488 MOZ_CAN_RUN_SCRIPT void SelectNodesExceptInSubtree(const Position& aStart, 1489 const Position& aEnd); 1490 1491 // A map from subtree root (document or shadow root) to the start position of 1492 // the non-selected content (so far). 1493 nsTHashMap<nsPtrHashKey<nsINode>, Position> mPositions; 1494 1495 // The selection we're adding the ranges to. 1496 const RefPtr<Selection> mSelection; 1497 }; 1498 1499 void SelectionRangeState::SelectComplementOf( 1500 Span<const RefPtr<nsRange>> aRanges) { 1501 for (const auto& range : aRanges) { 1502 auto start = Position{range->GetMayCrossShadowBoundaryStartContainer(), 1503 range->MayCrossShadowBoundaryStartOffset()}; 1504 auto end = Position{range->GetMayCrossShadowBoundaryEndContainer(), 1505 range->MayCrossShadowBoundaryEndOffset()}; 1506 SelectNodesExcept(start, end); 1507 } 1508 } 1509 1510 void SelectionRangeState::SelectRange(nsRange* aRange) { 1511 if (aRange && !aRange->AreNormalRangeAndCrossShadowBoundaryRangeCollapsed()) { 1512 mSelection->AddRangeAndSelectFramesAndNotifyListeners(*aRange, 1513 IgnoreErrors()); 1514 } 1515 } 1516 1517 void SelectionRangeState::SelectNodesExcept(const Position& aStart, 1518 const Position& aEnd) { 1519 SelectNodesExceptInSubtree(aStart, aEnd); 1520 if (!StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) { 1521 if (auto* shadow = ShadowRoot::FromNode(aStart.mNode->SubtreeRoot())) { 1522 auto* host = shadow->Host(); 1523 // Can't just select other nodes except the host, because other nodes that 1524 // are not in this particular shadow tree could also be selected 1525 SelectNodesExcept(Position{host, 0}, 1526 Position{host, host->GetChildCount()}); 1527 } else { 1528 MOZ_ASSERT(aStart.mNode->IsInUncomposedDoc()); 1529 } 1530 } 1531 } 1532 1533 void SelectionRangeState::SelectNodesExceptInSubtree(const Position& aStart, 1534 const Position& aEnd) { 1535 static constexpr auto kEllipsis = u"\x2026"_ns; 1536 1537 // Finish https://bugzilla.mozilla.org/show_bug.cgi?id=1903871 once the pref 1538 // is shipped, so that we only need one position. 1539 nsINode* root = StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() 1540 ? aStart.mNode->OwnerDoc() 1541 : aStart.mNode->SubtreeRoot(); 1542 auto& start = 1543 mPositions.WithEntryHandle(root, [&](auto&& entry) -> Position& { 1544 return entry.OrInsertWith([&] { return Position{root, 0}; }); 1545 }); 1546 1547 bool ellipsizedStart = false; 1548 if (auto* text = Text::FromNode(aStart.mNode)) { 1549 if (start.mNode != text && aStart.mOffset && 1550 aStart.mOffset < text->Length()) { 1551 // Only insert ellipsis if there is any non-whitespace prior to selection. 1552 if (!text->TextStartsWithOnlyWhitespace(aStart.mOffset)) { 1553 text->InsertData(aStart.mOffset, kEllipsis, IgnoreErrors()); 1554 ellipsizedStart = true; 1555 } 1556 } 1557 } 1558 1559 RefPtr<nsRange> range = nsRange::Create( 1560 start.mNode, start.mOffset, aStart.mNode, aStart.mOffset, IgnoreErrors(), 1561 StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() 1562 ? AllowRangeCrossShadowBoundary::Yes 1563 : AllowRangeCrossShadowBoundary::No); 1564 SelectRange(range); 1565 1566 start = aEnd; 1567 1568 // If we added an ellipsis at the start and the end position was relative to 1569 // the same node account for it here. 1570 if (ellipsizedStart && aStart.mNode == aEnd.mNode) { 1571 start.mOffset += kEllipsis.Length(); 1572 } 1573 1574 // If the end is mid text then add an ellipsis. 1575 if (auto* text = Text::FromNode(start.mNode)) { 1576 if (start.mOffset && start.mOffset < text->Length()) { 1577 // Only insert ellipsis if there is any non-whitespace after selection. 1578 if (!text->TextEndsWithOnlyWhitespace(start.mOffset)) { 1579 text->InsertData(start.mOffset, kEllipsis, IgnoreErrors()); 1580 start.mOffset += kEllipsis.Length(); 1581 } 1582 } 1583 } 1584 } 1585 1586 void SelectionRangeState::RemoveSelectionFromDocument() { 1587 for (auto& entry : mPositions) { 1588 const Position& pos = entry.GetData(); 1589 nsINode* root = entry.GetKey(); 1590 RefPtr<nsRange> range = nsRange::Create( 1591 pos.mNode, pos.mOffset, root, root->GetChildCount(), IgnoreErrors(), 1592 StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() 1593 ? AllowRangeCrossShadowBoundary::Yes 1594 : AllowRangeCrossShadowBoundary::No); 1595 SelectRange(range); 1596 } 1597 for (uint32_t i = 0; i < mSelection->RangeCount(); i++) { 1598 auto* range = mSelection->GetRangeAt(i); 1599 range->SuppressContentsForPrintSelection(IgnoreErrors()); 1600 } 1601 } 1602 1603 /** 1604 * Builds the complement set of ranges and adds those to the selection. 1605 * Deletes all of the nodes contained in the complement set of ranges 1606 * leaving behind only nodes that were originally selected. 1607 * Adds ellipses to a selected node's text if text is truncated by a range. 1608 * This is used to implement the "Print Selection Only" user interface option. 1609 */ 1610 MOZ_CAN_RUN_SCRIPT_BOUNDARY static nsresult DeleteNonSelectedNodes( 1611 Document& aDoc) { 1612 MOZ_ASSERT(aDoc.IsStaticDocument()); 1613 const auto* printRanges = static_cast<nsTArray<RefPtr<nsRange>>*>( 1614 aDoc.GetProperty(nsGkAtoms::printselectionranges)); 1615 if (!printRanges) { 1616 return NS_OK; 1617 } 1618 1619 PresShell* presShell = aDoc.GetPresShell(); 1620 NS_ENSURE_STATE(presShell); 1621 RefPtr<Selection> selection = 1622 presShell->GetCurrentSelection(SelectionType::eNormal); 1623 NS_ENSURE_STATE(selection); 1624 1625 SelectionRangeState state(std::move(selection)); 1626 state.SelectComplementOf(*printRanges); 1627 state.RemoveSelectionFromDocument(); 1628 return NS_OK; 1629 } 1630 1631 //------------------------------------------------------- 1632 nsresult nsPrintJob::DoPrint(const UniquePtr<nsPrintObject>& aPO) { 1633 PR_PL(("\n")); 1634 PR_PL(("**************************** %s ****************************\n", 1635 LoggableTypeOfPO(aPO.get()))); 1636 PR_PL(("****** In DV::DoPrint PO: %p \n", aPO.get())); 1637 1638 PresShell* poPresShell = aPO->mPresShell; 1639 nsPresContext* poPresContext = aPO->mPresContext; 1640 1641 NS_ASSERTION(poPresContext, "PrintObject has not been reflowed"); 1642 NS_ASSERTION(poPresContext->Type() != nsPresContext::eContext_PrintPreview, 1643 "How did this context end up here?"); 1644 1645 // Guarantee that mPrt and the objects it owns won't be deleted in this method 1646 // because it might be cleared if other modules called from here may fire 1647 // events, notifying observers and/or listeners. 1648 RefPtr<nsPrintData> printData = mPrt; 1649 if (NS_WARN_IF(!printData)) { 1650 return NS_ERROR_FAILURE; 1651 } 1652 1653 { 1654 // Ask the page sequence frame to print all the pages 1655 nsPageSequenceFrame* seqFrame = poPresShell->GetPageSequenceFrame(); 1656 MOZ_ASSERT(seqFrame, "no page sequence frame"); 1657 1658 // We are done preparing for printing, so we can turn this off 1659 mPreparingForPrint = false; 1660 1661 #ifdef EXTENDED_DEBUG_PRINTING 1662 nsIFrame* rootFrame = poPresShell->GetRootFrame(); 1663 if (aPO->PrintingIsEnabled()) { 1664 nsAutoCString docStr; 1665 nsAutoCString urlStr; 1666 GetDocTitleAndURL(aPO, docStr, urlStr); 1667 DumpLayoutData(docStr.get(), urlStr.get(), poPresContext, 1668 printData->mPrintDC, rootFrame, aPO->mDocShell, nullptr); 1669 } 1670 #endif 1671 1672 if (!mPrintSettings) { 1673 // not sure what to do here! 1674 SetIsPrinting(false); 1675 return NS_ERROR_FAILURE; 1676 } 1677 1678 nsAutoString docTitleStr; 1679 nsAutoString docURLStr; 1680 GetDisplayTitleAndURL(*aPO->mDocument, mPrintSettings, 1681 DocTitleDefault::eFallback, docTitleStr, docURLStr); 1682 1683 if (!seqFrame) { 1684 SetIsPrinting(false); 1685 return NS_ERROR_FAILURE; 1686 } 1687 1688 mPageSeqFrame = seqFrame; 1689 seqFrame->StartPrint(poPresContext, mPrintSettings, docTitleStr, docURLStr); 1690 1691 // Schedule Page to Print 1692 PR_PL(("Scheduling Print of PO: %p (%s) \n", aPO.get(), 1693 LoggableTypeOfPO(aPO.get()))); 1694 StartPagePrintTimer(aPO); 1695 } 1696 1697 return NS_OK; 1698 } 1699 1700 //------------------------------------------------------- 1701 bool nsPrintJob::PrePrintSheet() { 1702 NS_ASSERTION(mPageSeqFrame.IsAlive(), "mPageSeqFrame is not alive!"); 1703 NS_ASSERTION(mPrt, "mPrt is null!"); 1704 1705 // Although these should NEVER be nullptr 1706 // This is added insurance, to make sure we don't crash in optimized builds 1707 if (!mPrt || !mPageSeqFrame.IsAlive()) { 1708 return true; // means we are done preparing the sheet. 1709 } 1710 1711 // Guarantee that mPrt won't be deleted during a call of 1712 // FirePrintingErrorEvent(). 1713 RefPtr<nsPrintData> printData = mPrt; 1714 1715 // Ask mPageSeqFrame if the sheet is ready to be printed. 1716 // If the sheet doesn't get printed at all, the |done| will be |true|. 1717 bool done = false; 1718 nsPageSequenceFrame* pageSeqFrame = do_QueryFrame(mPageSeqFrame.GetFrame()); 1719 nsresult rv = pageSeqFrame->PrePrintNextSheet(mPagePrintTimer, &done); 1720 if (NS_FAILED(rv)) { 1721 // ??? ::PrintSheet doesn't set |printData->mIsAborted = true| if 1722 // rv != NS_ERROR_ABORT, but I don't really understand why this should be 1723 // the right thing to do? Shouldn't |printData->mIsAborted| set to true 1724 // all the time if something went wrong? 1725 if (rv != NS_ERROR_ABORT) { 1726 FirePrintingErrorEvent(rv); 1727 printData->mIsAborted = true; 1728 } 1729 done = true; 1730 } 1731 return done; 1732 } 1733 1734 bool nsPrintJob::PrintSheet(nsPrintObject* aPO) { 1735 NS_ASSERTION(aPO, "aPO is null!"); 1736 NS_ASSERTION(mPageSeqFrame.IsAlive(), "mPageSeqFrame is not alive!"); 1737 NS_ASSERTION(mPrt, "mPrt is null!"); 1738 1739 // Although these should NEVER be nullptr 1740 // This is added insurance, to make sure we don't crash in optimized builds 1741 if (!mPrt || !aPO || !mPageSeqFrame.IsAlive()) { 1742 FirePrintingErrorEvent(NS_ERROR_FAILURE); 1743 return true; // means we are done printing 1744 } 1745 1746 // Guarantee that mPrt won't be deleted during a call of 1747 // nsPrintData::DoOnProgressChange() which runs some listeners, 1748 // which may clear (& might otherwise destroy). 1749 RefPtr<nsPrintData> printData = mPrt; 1750 1751 PR_PL(("-----------------------------------\n")); 1752 PR_PL(("------ In DV::PrintSheet PO: %p (%s)\n", aPO, LoggableTypeOfPO(aPO))); 1753 1754 if (printData->mIsAborted) { 1755 return true; 1756 } 1757 1758 nsPageSequenceFrame* pageSeqFrame = do_QueryFrame(mPageSeqFrame.GetFrame()); 1759 const uint32_t sheetIdx = pageSeqFrame->GetCurrentSheetIdx(); 1760 const uint32_t numSheets = pageSeqFrame->PrincipalChildList().GetLength(); 1761 1762 PR_PL(("****** Printing sheet index %d of %d sheets(s)\n", sheetIdx, 1763 numSheets)); 1764 1765 MOZ_ASSERT(numSheets > 0, "print operations must have at least 1 sheet"); 1766 MOZ_ASSERT(sheetIdx < numSheets, 1767 "sheetIdx shouldn't be allowed to go out of bounds"); 1768 printData->DoOnProgressChange(sheetIdx, numSheets, false, 0); 1769 if (NS_WARN_IF(mPrt != printData)) { 1770 // If current printing is canceled or new print is started, let's return 1771 // true to notify the caller of current printing is done. 1772 return true; 1773 } 1774 1775 // Print the sheet 1776 // if a print job was cancelled externally, an EndPage or BeginPage may 1777 // fail and the failure is passed back here. 1778 // Returning true means we are done printing. 1779 // 1780 // When rv == NS_ERROR_ABORT, it means we want out of the 1781 // print job without displaying any error messages 1782 nsresult rv = pageSeqFrame->PrintNextSheet(); 1783 if (NS_FAILED(rv)) { 1784 if (rv != NS_ERROR_ABORT) { 1785 FirePrintingErrorEvent(rv); 1786 printData->mIsAborted = true; 1787 } 1788 return true; 1789 } 1790 1791 pageSeqFrame->DoPageEnd(); 1792 1793 // If we just printed the final sheet (the one with index "numSheets-1"), 1794 // then we're done! 1795 return (sheetIdx == numSheets - 1); 1796 } 1797 1798 void nsPrintJob::PageDone(nsresult aResult) { 1799 MOZ_ASSERT(mIsDoingPrinting); 1800 1801 // mPagePrintTimer might be released during RemotePrintFinished, keep a 1802 // reference here to make sure it lives long enough. 1803 RefPtr<nsPagePrintTimer> timer = mPagePrintTimer; 1804 timer->RemotePrintFinished(); 1805 } 1806 1807 //----------------------------------------------------------------- 1808 //-- Done: Printing Methods 1809 //----------------------------------------------------------------- 1810 1811 //----------------------------------------------------------------- 1812 //-- Section: Misc Support Methods 1813 //----------------------------------------------------------------- 1814 1815 //--------------------------------------------------------------------- 1816 void nsPrintJob::SetIsPrinting(bool aIsPrinting) { 1817 mIsDoingPrinting = aIsPrinting; 1818 if (aIsPrinting) { 1819 mPreparingForPrint = true; 1820 } 1821 } 1822 1823 //--------------------------------------------------------------------- 1824 void nsPrintJob::SetIsPrintPreview(bool aIsPrintPreview) { 1825 mCreatedForPrintPreview = aIsPrintPreview; 1826 1827 if (mDocViewerPrint) { 1828 mDocViewerPrint->SetIsPrintPreview(aIsPrintPreview); 1829 } 1830 } 1831 1832 //------------------------------------------------------- 1833 bool nsPrintJob::DonePrintingSheets(nsPrintObject* aPO, nsresult aResult) { 1834 // NS_ASSERTION(aPO, "Pointer is null!"); 1835 PR_PL(("****** In DV::DonePrintingSheets PO: %p (%s)\n", aPO, 1836 aPO ? LoggableTypeOfPO(aPO) : "")); 1837 1838 // If there is a pageSeqFrame, make sure there are no more printCanvas active 1839 // that might call |Notify| on the pagePrintTimer after things are cleaned up 1840 // and printing was marked as being done. 1841 if (mPageSeqFrame.IsAlive()) { 1842 nsPageSequenceFrame* pageSeqFrame = do_QueryFrame(mPageSeqFrame.GetFrame()); 1843 pageSeqFrame->ResetPrintCanvasList(); 1844 } 1845 1846 // Guarantee that mPrt and mPrintObject won't be deleted during a 1847 // call of PrintDocContent() and FirePrintCompletionEvent(). 1848 RefPtr<nsPrintData> printData = mPrt; 1849 1850 if (aPO && !printData->mIsAborted) { 1851 aPO->mHasBeenPrinted = true; 1852 nsresult rv; 1853 bool didPrint = PrintDocContent(mPrintObject, rv); 1854 if (NS_SUCCEEDED(rv) && didPrint) { 1855 PR_PL( 1856 ("****** In DV::DonePrintingSheets PO: %p (%s) didPrint:%s (Not Done " 1857 "Printing)\n", 1858 aPO, LoggableTypeOfPO(aPO), PRT_YESNO(didPrint))); 1859 return false; 1860 } 1861 } 1862 1863 if (NS_SUCCEEDED(aResult)) { 1864 FirePrintCompletionEvent(); 1865 // XXX mPrt may be cleared or replaced with new instance here. 1866 // However, the following methods will clean up with new mPrt or will 1867 // do nothing due to no proper nsPrintData instance. 1868 } 1869 1870 SetIsPrinting(false); 1871 1872 // Release reference to mPagePrintTimer; the timer object destroys itself 1873 // after this returns true 1874 DisconnectPagePrintTimer(); 1875 1876 return true; 1877 } 1878 1879 //------------------------------------------------------- 1880 nsresult nsPrintJob::EnablePOsForPrinting() { 1881 // Guarantee that mPrt and the objects it owns won't be deleted. 1882 RefPtr<nsPrintData> printData = mPrt; 1883 1884 // NOTE: All POs have been "turned off" for printing 1885 // this is where we decided which POs get printed. 1886 1887 if (!printData || !mPrintSettings) { 1888 return NS_ERROR_FAILURE; 1889 } 1890 1891 PR_PL(("\n")); 1892 PR_PL(("********* nsPrintJob::EnablePOsForPrinting *********\n")); 1893 1894 if (!mPrintSettings->GetPrintSelectionOnly()) { 1895 mPrintObject->EnablePrinting(true); 1896 return NS_OK; 1897 } 1898 1899 // This means we are either printing a selected iframe or 1900 // we are printing the current selection. 1901 NS_ENSURE_STATE(!mDisallowSelectionPrint && mSelectionRoot); 1902 1903 // If mSelectionRoot is a selected iframe without a selection, then just 1904 // enable normally from that point. 1905 if (mSelectionRoot->mParent && !mSelectionRoot->HasSelection()) { 1906 mSelectionRoot->EnablePrinting(true); 1907 } else { 1908 // Otherwise, only enable nsPrintObjects that have a selection. 1909 mSelectionRoot->EnablePrintingSelectionOnly(); 1910 } 1911 return NS_OK; 1912 } 1913 1914 //----------------------------------------------------------------- 1915 //-- Done: Misc Support Methods 1916 //----------------------------------------------------------------- 1917 1918 //----------------------------------------------------------------- 1919 //-- Section: Finishing up or Cleaning up 1920 //----------------------------------------------------------------- 1921 1922 //----------------------------------------------------------------- 1923 nsresult nsPrintJob::FinishPrintPreview() { 1924 nsresult rv = NS_OK; 1925 1926 #ifdef NS_PRINT_PREVIEW 1927 1928 // If mPrt is null we've already finished with print preview. If mPrt is not 1929 // null but mIsCreatingPrintPreview is false FinishPrintPreview must have 1930 // already failed due to DocumentReadyForPrinting failing. 1931 if (!mPrt || !mIsCreatingPrintPreview) { 1932 return rv; 1933 } 1934 1935 rv = DocumentReadyForPrinting(); 1936 1937 // Note that this method may be called while the instance is being 1938 // initialized. Some methods which initialize the instance (e.g., 1939 // DoCommonPrint) may need to stop initializing and return error if 1940 // this is called. Therefore it's important to set mIsCreatingPrintPreview 1941 // state to false here. If you need to stop setting that here, you need to 1942 // keep them being able to check whether the owner stopped using this 1943 // instance. 1944 mIsCreatingPrintPreview = false; 1945 1946 // mPrt may be cleared during a call of nsPrintData::OnEndPrinting() 1947 // because that method invokes some arbitrary listeners. 1948 // TODO(dshin): Does any listener attach to print preview? Doesn't seem like 1949 // we call matching `OnStartPrinting()` for previews. 1950 RefPtr<nsPrintData> printData = mPrt; 1951 if (NS_FAILED(rv)) { 1952 printData->OnEndPrinting(); 1953 1954 return rv; 1955 } 1956 1957 if (mPrintPreviewCallback) { 1958 const bool hasSelection = !mDisallowSelectionPrint && mSelectionRoot; 1959 1960 Maybe<float> pageWidth; 1961 Maybe<float> pageHeight; 1962 if (mMaybeCSSPageSize) { 1963 nsSize cssPageSize = *mMaybeCSSPageSize; 1964 pageWidth = Some(float(cssPageSize.width) / float(AppUnitsPerCSSInch())); 1965 pageHeight = 1966 Some(float(cssPageSize.height) / float(AppUnitsPerCSSInch())); 1967 } 1968 1969 mPrintPreviewCallback(PrintPreviewResultInfo( 1970 GetPrintPreviewNumSheets(), GetRawNumPages(), GetIsEmpty(), 1971 hasSelection, hasSelection && mPrintObject->HasSelection(), 1972 mMaybeCSSPageLandscape, pageWidth, pageHeight)); 1973 mPrintPreviewCallback = nullptr; 1974 } 1975 1976 // At this point we are done preparing everything 1977 // before it is to be created 1978 1979 printData->OnEndPrinting(); 1980 1981 #endif // NS_PRINT_PREVIEW 1982 1983 return NS_OK; 1984 } 1985 1986 //----------------------------------------------------------------- 1987 //-- Done: Finishing up or Cleaning up 1988 //----------------------------------------------------------------- 1989 1990 /*=============== Timer Related Code ======================*/ 1991 nsresult nsPrintJob::StartPagePrintTimer(const UniquePtr<nsPrintObject>& aPO) { 1992 if (!mPagePrintTimer) { 1993 // Get the delay time in between the printing of each page 1994 // this gives the user more time to press cancel 1995 int32_t printPageDelay = mPrintSettings->GetPrintPageDelay(); 1996 1997 nsCOMPtr<nsIDocumentViewer> viewer = do_QueryInterface(mDocViewerPrint); 1998 NS_ENSURE_TRUE(viewer, NS_ERROR_FAILURE); 1999 nsCOMPtr<Document> doc = viewer->GetDocument(); 2000 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); 2001 2002 mPagePrintTimer = 2003 new nsPagePrintTimer(this, mDocViewerPrint, doc, printPageDelay); 2004 2005 if (mRemotePrintJob) { 2006 mRemotePrintJob->SetPagePrintTimer(mPagePrintTimer); 2007 mRemotePrintJob->SetPrintJob(this); 2008 } 2009 } 2010 2011 return mPagePrintTimer->Start(aPO.get()); 2012 } 2013 2014 //--------------------------------------------------------------- 2015 //-- PLEvent Notification 2016 //--------------------------------------------------------------- 2017 class nsPrintCompletionEvent : public Runnable { 2018 public: 2019 explicit nsPrintCompletionEvent(nsIDocumentViewerPrint* docViewerPrint) 2020 : mozilla::Runnable("nsPrintCompletionEvent"), 2021 mDocViewerPrint(docViewerPrint) { 2022 NS_ASSERTION(mDocViewerPrint, "mDocViewerPrint is null."); 2023 } 2024 2025 NS_IMETHOD Run() override { 2026 if (mDocViewerPrint) { 2027 mDocViewerPrint->OnDonePrinting(); 2028 } 2029 return NS_OK; 2030 } 2031 2032 private: 2033 nsCOMPtr<nsIDocumentViewerPrint> mDocViewerPrint; 2034 }; 2035 2036 //----------------------------------------------------------- 2037 void nsPrintJob::FirePrintCompletionEvent() { 2038 MOZ_ASSERT(NS_IsMainThread()); 2039 nsCOMPtr<nsIRunnable> event = new nsPrintCompletionEvent(mDocViewerPrint); 2040 nsCOMPtr<nsIDocumentViewer> viewer = do_QueryInterface(mDocViewerPrint); 2041 NS_ENSURE_TRUE_VOID(viewer); 2042 nsCOMPtr<Document> doc = viewer->GetDocument(); 2043 NS_ENSURE_TRUE_VOID(doc); 2044 NS_ENSURE_SUCCESS_VOID(doc->Dispatch(event.forget())); 2045 } 2046 2047 void nsPrintJob::DisconnectPagePrintTimer() { 2048 if (mPagePrintTimer) { 2049 mPagePrintTimer->Disconnect(); 2050 mPagePrintTimer = nullptr; 2051 } 2052 } 2053 2054 //--------------------------------------------------------------- 2055 //--------------------------------------------------------------- 2056 //-- Debug helper routines 2057 //--------------------------------------------------------------- 2058 //--------------------------------------------------------------- 2059 #if defined(XP_WIN) && defined(EXTENDED_DEBUG_PRINTING) 2060 # include <direct.h> 2061 # include <process.h> 2062 # include <windows.h> 2063 2064 # define MY_FINDFIRST(a, b) FindFirstFile(a, b) 2065 # define MY_FINDNEXT(a, b) FindNextFile(a, b) 2066 # define ISDIR(a) (a.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 2067 # define MY_FINDCLOSE(a) FindClose(a) 2068 # define MY_FILENAME(a) a.cFileName 2069 # define MY_FILESIZE(a) (a.nFileSizeHigh * MAXDWORD) + a.nFileSizeLow 2070 2071 int RemoveFilesInDir(const char* aDir) { 2072 WIN32_FIND_DATA data_ptr; 2073 HANDLE find_handle; 2074 2075 char path[MAX_PATH]; 2076 2077 strcpy(path, aDir); 2078 2079 // Append slash to the end of the directory names if not there 2080 if (path[strlen(path) - 1] != '\\') strcat(path, "\\"); 2081 2082 char findPath[MAX_PATH]; 2083 strcpy(findPath, path); 2084 strcat(findPath, "*.*"); 2085 2086 find_handle = MY_FINDFIRST(findPath, &data_ptr); 2087 2088 if (find_handle != INVALID_HANDLE_VALUE) { 2089 do { 2090 if (ISDIR(data_ptr) && (stricmp(MY_FILENAME(data_ptr), ".")) && 2091 (stricmp(MY_FILENAME(data_ptr), ".."))) { 2092 // skip 2093 } else if (!ISDIR(data_ptr)) { 2094 if (!strncmp(MY_FILENAME(data_ptr), "print_dump", 10)) { 2095 char fileName[MAX_PATH]; 2096 strcpy(fileName, aDir); 2097 strcat(fileName, "\\"); 2098 strcat(fileName, MY_FILENAME(data_ptr)); 2099 printf("Removing %s\n", fileName); 2100 remove(fileName); 2101 } 2102 } 2103 } while (MY_FINDNEXT(find_handle, &data_ptr)); 2104 MY_FINDCLOSE(find_handle); 2105 } 2106 return TRUE; 2107 } 2108 #endif 2109 2110 #ifdef EXTENDED_DEBUG_PRINTING 2111 2112 /** --------------------------------------------------- 2113 * Dumps Frames for Printing 2114 */ 2115 static void RootFrameList(nsPresContext* aPresContext, FILE* out, 2116 const char* aPrefix) { 2117 if (!aPresContext || !out) return; 2118 2119 if (PresShell* presShell = aPresContext->GetPresShell()) { 2120 nsIFrame* frame = presShell->GetRootFrame(); 2121 if (frame) { 2122 frame->List(out, aPrefix); 2123 } 2124 } 2125 } 2126 2127 /** --------------------------------------------------- 2128 * Dumps Frames for Printing 2129 */ 2130 static void DumpFrames(FILE* out, nsPresContext* aPresContext, 2131 gfxContext* aRendContext, nsIFrame* aFrame, 2132 int32_t aLevel) { 2133 NS_ASSERTION(out, "Pointer is null!"); 2134 NS_ASSERTION(aPresContext, "Pointer is null!"); 2135 NS_ASSERTION(aRendContext, "Pointer is null!"); 2136 NS_ASSERTION(aFrame, "Pointer is null!"); 2137 2138 nsIFrame* child = aFrame->PrincipalChildList().FirstChild(); 2139 while (child != nullptr) { 2140 for (int32_t i = 0; i < aLevel; i++) { 2141 fprintf(out, " "); 2142 } 2143 nsAutoString tmp; 2144 child->GetFrameName(tmp); 2145 fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out); 2146 bool isSelected; 2147 if (child->IsVisibleForPainting()) { 2148 fprintf(out, " %p %s", child, isSelected ? "VIS" : "UVS"); 2149 nsRect rect = child->GetRect(); 2150 fprintf(out, "[%d,%d,%d,%d] ", rect.x, rect.y, rect.width, rect.height); 2151 fprintf(out, "v: %p ", (void*)child->GetView()); 2152 fprintf(out, "\n"); 2153 DumpFrames(out, aPresContext, aRendContext, child, aLevel + 1); 2154 child = child->GetNextSibling(); 2155 } 2156 } 2157 } 2158 2159 /** --------------------------------------------------- 2160 * Dumps the Views and Frames 2161 */ 2162 void DumpLayoutData(const char* aTitleStr, const char* aURLStr, 2163 nsPresContext* aPresContext, nsDeviceContext* aDC, 2164 nsIFrame* aRootFrame, nsIDocShell* aDocShell, 2165 FILE* aFD = nullptr) { 2166 if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) { 2167 return; 2168 } 2169 2170 if (aPresContext == nullptr || aDC == nullptr) { 2171 return; 2172 } 2173 2174 # ifdef NS_PRINT_PREVIEW 2175 if (aPresContext->Type() == nsPresContext::eContext_PrintPreview) { 2176 return; 2177 } 2178 # endif 2179 2180 NS_ASSERTION(aRootFrame, "Pointer is null!"); 2181 NS_ASSERTION(aDocShell, "Pointer is null!"); 2182 2183 // Dump all the frames and view to a a file 2184 char filename[256]; 2185 sprintf(filename, "print_dump_layout_%d.txt", gDumpLOFileNameCnt++); 2186 FILE* fd = aFD ? aFD : fopen(filename, "w"); 2187 if (fd) { 2188 fprintf(fd, "Title: %s\n", aTitleStr ? aTitleStr : ""); 2189 fprintf(fd, "URL: %s\n", aURLStr ? aURLStr : ""); 2190 fprintf(fd, "--------------- Frames ----------------\n"); 2191 fprintf(fd, "--------------- Frames ----------------\n"); 2192 // RefPtr<gfxContext> renderingContext = 2193 // aDC->CreateRenderingContext(); 2194 RootFrameList(aPresContext, fd, ""); 2195 // DumpFrames(fd, aPresContext, renderingContext, aRootFrame, 0); 2196 fprintf(fd, "---------------------------------------\n\n"); 2197 fprintf(fd, "--------------- Views From Root Frame----------------\n"); 2198 nsView* v = aRootFrame->GetView(); 2199 if (v) { 2200 v->List(fd); 2201 } else { 2202 printf("View is null!\n"); 2203 } 2204 if (aFD == nullptr) { 2205 fclose(fd); 2206 } 2207 } 2208 } 2209 2210 //------------------------------------------------------------- 2211 static void DumpPrintObjectsList(const nsTArray<nsPrintObject*>& aDocList) { 2212 if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) { 2213 return; 2214 } 2215 2216 PR_PL(("Doc List\n***************************************************\n")); 2217 PR_PL( 2218 ("T P A H PO DocShell Seq Page Root Page# " 2219 "Rect\n")); 2220 for (nsPrintObject* po : aDocList) { 2221 NS_ASSERTION(po, "nsPrintObject can't be null!"); 2222 nsIFrame* rootFrame = nullptr; 2223 if (po->mPresShell) { 2224 rootFrame = po->mPresShell->GetRootFrame(); 2225 while (rootFrame != nullptr) { 2226 nsPageSequenceFrame* sqf = do_QueryFrame(rootFrame); 2227 if (sqf) { 2228 break; 2229 } 2230 rootFrame = rootFrame->PrincipalChildList().FirstChild(); 2231 } 2232 } 2233 2234 PR_PL(("%s %d %d %p %p %p\n", ShortLoggableTypeOfPO(po), 2235 po->PrintingIsEnabled(), po->mHasBeenPrinted, po, 2236 po->mDocShell.get(), rootFrame)); 2237 } 2238 } 2239 2240 //------------------------------------------------------------- 2241 static void DumpPrintObjectsTree(nsPrintObject* aPO, int aLevel, FILE* aFD) { 2242 if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) { 2243 return; 2244 } 2245 2246 NS_ASSERTION(aPO, "Pointer is null!"); 2247 2248 FILE* fd = aFD ? aFD : stdout; 2249 if (aLevel == 0) { 2250 fprintf(fd, 2251 "DocTree\n***************************************************\n"); 2252 fprintf(fd, "T PO DocShell Seq Page Page# Rect\n"); 2253 } 2254 for (const auto& po : aPO->mKids) { 2255 NS_ASSERTION(po, "nsPrintObject can't be null!"); 2256 for (int32_t k = 0; k < aLevel; k++) fprintf(fd, " "); 2257 fprintf(fd, "%s %p %p\n", ShortLoggableTypeOfPO(po.get()), po.get(), 2258 po->mDocShell.get()); 2259 } 2260 } 2261 2262 //------------------------------------------------------------- 2263 static void GetDocTitleAndURL(const UniquePtr<nsPrintObject>& aPO, 2264 nsACString& aDocStr, nsACString& aURLStr) { 2265 nsAutoString docTitleStr; 2266 nsAutoString docURLStr; 2267 GetDocumentTitleAndURL(aPO->mDocument, docTitleStr, docURLStr); 2268 CopyUTF16toUTF8(docTitleStr, aDocStr); 2269 CopyUTF16toUTF8(docURLStr, aURLStr); 2270 } 2271 2272 //------------------------------------------------------------- 2273 static void DumpPrintObjectsTreeLayout(const UniquePtr<nsPrintObject>& aPO, 2274 nsDeviceContext* aDC, int aLevel, 2275 FILE* aFD) { 2276 if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) { 2277 return; 2278 } 2279 2280 NS_ASSERTION(aPO, "Pointer is null!"); 2281 NS_ASSERTION(aDC, "Pointer is null!"); 2282 2283 FILE* fd = nullptr; 2284 if (aLevel == 0) { 2285 fd = fopen("tree_layout.txt", "w"); 2286 fprintf(fd, 2287 "DocTree\n***************************************************\n"); 2288 fprintf(fd, "***************************************************\n"); 2289 fprintf(fd, "T PO DocShell Seq Page Page# Rect\n"); 2290 } else { 2291 fd = aFD; 2292 } 2293 if (fd) { 2294 nsIFrame* rootFrame = nullptr; 2295 if (aPO->mPresShell) { 2296 rootFrame = aPO->mPresShell->GetRootFrame(); 2297 } 2298 for (int32_t k = 0; k < aLevel; k++) fprintf(fd, " "); 2299 fprintf(fd, "%s %p %p\n", ShortLoggableTypeOfPO(aPO.get()), aPO.get(), 2300 aPO->mDocShell.get()); 2301 if (aPO->PrintingIsEnabled()) { 2302 nsAutoCString docStr; 2303 nsAutoCString urlStr; 2304 GetDocTitleAndURL(aPO, docStr, urlStr); 2305 DumpLayoutData(docStr.get(), urlStr.get(), aPO->mPresContext, aDC, 2306 rootFrame, aPO->mDocShell, fd); 2307 } 2308 fprintf(fd, "<***************************************************>\n"); 2309 2310 for (const auto& po : aPO->mKids) { 2311 NS_ASSERTION(po, "nsPrintObject can't be null!"); 2312 DumpPrintObjectsTreeLayout(po, aDC, aLevel + 1, fd); 2313 } 2314 } 2315 if (aLevel == 0 && fd) { 2316 fclose(fd); 2317 } 2318 } 2319 2320 //------------------------------------------------------------- 2321 static void DumpPrintObjectsListStart( 2322 const char* aStr, const nsTArray<nsPrintObject*>& aDocList) { 2323 if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) { 2324 return; 2325 } 2326 2327 NS_ASSERTION(aStr, "Pointer is null!"); 2328 2329 PR_PL(("%s\n", aStr)); 2330 DumpPrintObjectsList(aDocList); 2331 } 2332 2333 #endif 2334 2335 //--------------------------------------------------------------- 2336 //--------------------------------------------------------------- 2337 //-- End of debug helper routines 2338 //---------------------------------------------------------------