tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 //---------------------------------------------------------------