Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/printing/nsPrintJob.cpp
Line
Count
Source (jump to first uncovered line)
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 "nsIStringBundle.h"
10
#include "nsReadableUtils.h"
11
#include "nsCRT.h"
12
13
#include "mozilla/AsyncEventDispatcher.h"
14
#include "mozilla/ComputedStyleInlines.h"
15
#include "mozilla/dom/Selection.h"
16
#include "mozilla/dom/CustomEvent.h"
17
#include "mozilla/dom/ScriptSettings.h"
18
#include "nsIScriptGlobalObject.h"
19
#include "nsPIDOMWindow.h"
20
#include "nsIDocShell.h"
21
#include "nsIURI.h"
22
#include "nsITextToSubURI.h"
23
#include "nsError.h"
24
25
#include "nsView.h"
26
#include <algorithm>
27
28
// Print Options
29
#include "nsIPrintSettings.h"
30
#include "nsIPrintSettingsService.h"
31
#include "nsIPrintSession.h"
32
#include "nsGfxCIID.h"
33
#include "nsIServiceManager.h"
34
#include "nsGkAtoms.h"
35
#include "nsXPCOM.h"
36
#include "nsISupportsPrimitives.h"
37
38
static const char sPrintSettingsServiceContractID[] = "@mozilla.org/gfx/printsettings-service;1";
39
40
// Printing Events
41
#include "nsPrintPreviewListener.h"
42
#include "nsThreadUtils.h"
43
44
// Printing
45
#include "nsIWebBrowserPrint.h"
46
47
// Print Preview
48
#include "imgIContainer.h" // image animation mode constants
49
#include "nsIWebBrowserPrint.h" // needed for PrintPreview Navigation constants
50
51
// Print Progress
52
#include "nsIPrintProgress.h"
53
#include "nsIPrintProgressParams.h"
54
#include "nsIObserver.h"
55
56
// Print error dialog
57
#include "nsIPrompt.h"
58
#include "nsIWindowWatcher.h"
59
60
// Printing Prompts
61
#include "nsIPrintingPromptService.h"
62
static const char kPrintingPromptService[] = "@mozilla.org/embedcomp/printingprompt-service;1";
63
64
// Printing Timer
65
#include "nsPagePrintTimer.h"
66
67
// FrameSet
68
#include "nsIDocument.h"
69
#include "nsIDocumentInlines.h"
70
71
// Focus
72
#include "nsISelectionController.h"
73
74
// Misc
75
#include "gfxContext.h"
76
#include "mozilla/gfx/DrawEventRecorder.h"
77
#include "mozilla/layout/RemotePrintJobChild.h"
78
#include "nsISupportsUtils.h"
79
#include "nsIScriptContext.h"
80
#include "nsIDocumentObserver.h"
81
#include "nsISelectionListener.h"
82
#include "nsContentCID.h"
83
#include "nsLayoutCID.h"
84
#include "nsContentUtils.h"
85
#include "nsIPresShell.h"
86
#include "nsLayoutStylesheetCache.h"
87
#include "nsLayoutUtils.h"
88
#include "mozilla/Preferences.h"
89
#include "Text.h"
90
91
#include "nsWidgetsCID.h"
92
#include "nsIDeviceContextSpec.h"
93
#include "nsDeviceContextSpecProxy.h"
94
#include "nsViewManager.h"
95
#include "nsView.h"
96
97
#include "nsIPageSequenceFrame.h"
98
#include "nsIURL.h"
99
#include "nsIContentViewerEdit.h"
100
#include "nsIInterfaceRequestor.h"
101
#include "nsIInterfaceRequestorUtils.h"
102
#include "nsIDocShellTreeOwner.h"
103
#include "nsIWebBrowserChrome.h"
104
#include "nsIBaseWindow.h"
105
#include "nsILayoutHistoryState.h"
106
#include "nsFrameManager.h"
107
#include "mozilla/ReflowInput.h"
108
#include "nsIContentViewer.h"
109
#include "nsIDocumentViewerPrint.h"
110
111
#include "nsFocusManager.h"
112
#include "nsRange.h"
113
#include "nsCDefaultURIFixup.h"
114
#include "nsIURIFixup.h"
115
#include "mozilla/dom/Element.h"
116
#include "mozilla/dom/HTMLFrameElement.h"
117
#include "nsContentList.h"
118
#include "nsIChannel.h"
119
#include "xpcpublic.h"
120
#include "nsVariant.h"
121
#include "mozilla/ServoStyleSet.h"
122
123
using namespace mozilla;
124
using namespace mozilla::dom;
125
126
//-----------------------------------------------------
127
// PR LOGGING
128
#include "mozilla/Logging.h"
129
130
#ifdef DEBUG
131
// PR_LOGGING is force to always be on (even in release builds)
132
// but we only want some of it on,
133
//#define EXTENDED_DEBUG_PRINTING
134
#endif
135
136
// this log level turns on the dumping of each document's layout info
137
#define DUMP_LAYOUT_LEVEL (static_cast<mozilla::LogLevel>(9))
138
139
#ifndef PR_PL
140
static mozilla::LazyLogModule gPrintingLog("printing")
141
142
#define PR_PL(_p1)  MOZ_LOG(gPrintingLog, mozilla::LogLevel::Debug, _p1);
143
#endif
144
145
#ifdef EXTENDED_DEBUG_PRINTING
146
static uint32_t gDumpFileNameCnt   = 0;
147
static uint32_t gDumpLOFileNameCnt = 0;
148
#endif
149
150
#define PRT_YESNO(_p) ((_p)?"YES":"NO")
151
static const char * gFrameTypesStr[]       = {"eDoc", "eFrame", "eIFrame", "eFrameSet"};
152
static const char * gPrintFrameTypeStr[]   = {"kNoFrames", "kFramesAsIs", "kSelectedFrame", "kEachFrameSep"};
153
static const char * gFrameHowToEnableStr[] = {"kFrameEnableNone", "kFrameEnableAll", "kFrameEnableAsIsAndEach"};
154
static const char * gPrintRangeStr[]       = {"kRangeAllPages", "kRangeSpecifiedPageRange", "kRangeSelection", "kRangeFocusFrame"};
155
156
// This processes the selection on aOrigDoc and creates an inverted selection on
157
// aDoc, which it then deletes. If the start or end of the inverted selection
158
// ranges occur in text nodes then an ellipsis is added.
159
static nsresult DeleteUnselectedNodes(nsIDocument* aOrigDoc, nsIDocument* aDoc);
160
161
#ifdef EXTENDED_DEBUG_PRINTING
162
// Forward Declarations
163
static void DumpPrintObjectsListStart(const char * aStr, const nsTArray<nsPrintObject*>& aDocList);
164
static void DumpPrintObjectsTree(nsPrintObject * aPO, int aLevel= 0, FILE* aFD = nullptr);
165
static void DumpPrintObjectsTreeLayout(const UniquePtr<nsPrintObject>& aPO,
166
                                       nsDeviceContext * aDC, int aLevel = 0,
167
                                       FILE * aFD = nullptr);
168
169
#define DUMP_DOC_LIST(_title) DumpPrintObjectsListStart((_title), mPrt->mPrintDocList);
170
#define DUMP_DOC_TREE DumpPrintObjectsTree(mPrt->mPrintObject.get());
171
#define DUMP_DOC_TREELAYOUT DumpPrintObjectsTreeLayout(mPrt->mPrintObject, mPrt->mPrintDC);
172
#else
173
#define DUMP_DOC_LIST(_title)
174
#define DUMP_DOC_TREE
175
#define DUMP_DOC_TREELAYOUT
176
#endif
177
178
class nsScriptSuppressor
179
{
180
public:
181
  explicit nsScriptSuppressor(nsPrintJob* aPrintJob)
182
    : mPrintJob(aPrintJob)
183
    , mSuppressed(false)
184
0
  {}
185
186
0
  ~nsScriptSuppressor() { Unsuppress(); }
187
188
  void Suppress()
189
0
  {
190
0
    if (mPrintJob) {
191
0
      mSuppressed = true;
192
0
      mPrintJob->TurnScriptingOn(false);
193
0
    }
194
0
  }
195
196
  void Unsuppress()
197
0
  {
198
0
    if (mPrintJob && mSuppressed) {
199
0
      mPrintJob->TurnScriptingOn(true);
200
0
    }
201
0
    mSuppressed = false;
202
0
  }
203
204
0
  void Disconnect() { mPrintJob = nullptr; }
205
protected:
206
  RefPtr<nsPrintJob>      mPrintJob;
207
  bool                    mSuppressed;
208
};
209
210
// -------------------------------------------------------
211
// Helpers
212
// -------------------------------------------------------
213
214
static bool
215
HasFramesetChild(nsIContent* aContent)
216
0
{
217
0
  if (!aContent) {
218
0
    return false;
219
0
  }
220
0
221
0
  // do a breadth search across all siblings
222
0
  for (nsIContent* child = aContent->GetFirstChild();
223
0
       child;
224
0
       child = child->GetNextSibling()) {
225
0
    if (child->IsHTMLElement(nsGkAtoms::frameset)) {
226
0
      return true;
227
0
    }
228
0
  }
229
0
230
0
  return false;
231
0
}
232
233
static bool
234
IsParentAFrameSet(nsIDocShell* aParent)
235
0
{
236
0
  // See if the incoming doc is the root document
237
0
  if (!aParent) return false;
238
0
239
0
  // When it is the top level document we need to check
240
0
  // to see if it contains a frameset. If it does, then
241
0
  // we only want to print the doc's children and not the document itself
242
0
  // For anything else we always print all the children and the document
243
0
  // for example, if the doc contains an IFRAME we eant to print the child
244
0
  // document (the IFRAME) and then the rest of the document.
245
0
  //
246
0
  // XXX we really need to search the frame tree, and not the content
247
0
  // but there is no way to distinguish between IFRAMEs and FRAMEs
248
0
  // with the GetFrameType call.
249
0
  // Bug 53459 has been files so we can eventually distinguish
250
0
  // between IFRAME frames and FRAME frames
251
0
  bool isFrameSet = false;
252
0
  // only check to see if there is a frameset if there is
253
0
  // NO parent doc for this doc. meaning this parent is the root doc
254
0
  nsCOMPtr<nsIDocument> doc = aParent->GetDocument();
255
0
  if (doc) {
256
0
    nsIContent *rootElement = doc->GetRootElement();
257
0
    if (rootElement) {
258
0
      isFrameSet = HasFramesetChild(rootElement);
259
0
    }
260
0
  }
261
0
  return isFrameSet;
262
0
}
263
264
static nsPrintObject*
265
FindPrintObjectByDOMWin(nsPrintObject* aPO,
266
                        nsPIDOMWindowOuter* aDOMWin)
267
0
{
268
0
  NS_ASSERTION(aPO, "Pointer is null!");
269
0
270
0
  // Often the CurFocused DOMWindow is passed in
271
0
  // andit is valid for it to be null, so short circut
272
0
  if (!aDOMWin) {
273
0
    return nullptr;
274
0
  }
275
0
276
0
  nsCOMPtr<nsIDocument> doc = aDOMWin->GetDoc();
277
0
  if (aPO->mDocument && aPO->mDocument->GetOriginalDocument() == doc) {
278
0
    return aPO;
279
0
  }
280
0
281
0
  for (const UniquePtr<nsPrintObject>& kid : aPO->mKids) {
282
0
    nsPrintObject* po = FindPrintObjectByDOMWin(kid.get(), aDOMWin);
283
0
    if (po) {
284
0
      return po;
285
0
    }
286
0
  }
287
0
288
0
  return nullptr;
289
0
}
290
291
static void
292
GetDocumentTitleAndURL(nsIDocument* aDoc,
293
                       nsAString& aTitle,
294
                       nsAString& aURLStr)
295
0
{
296
0
  NS_ASSERTION(aDoc, "Pointer is null!");
297
0
298
0
  aTitle.Truncate();
299
0
  aURLStr.Truncate();
300
0
301
0
  aDoc->GetTitle(aTitle);
302
0
303
0
  nsIURI* url = aDoc->GetDocumentURI();
304
0
  if (!url) return;
305
0
306
0
  nsCOMPtr<nsIURIFixup> urifixup(do_GetService(NS_URIFIXUP_CONTRACTID));
307
0
  if (!urifixup) return;
308
0
309
0
  nsCOMPtr<nsIURI> exposableURI;
310
0
  urifixup->CreateExposableURI(url, getter_AddRefs(exposableURI));
311
0
312
0
  if (!exposableURI) return;
313
0
314
0
  nsAutoCString urlCStr;
315
0
  nsresult rv = exposableURI->GetSpec(urlCStr);
316
0
  if (NS_FAILED(rv)) return;
317
0
318
0
  nsCOMPtr<nsITextToSubURI> textToSubURI =
319
0
    do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
320
0
  if (NS_FAILED(rv)) return;
321
0
322
0
  textToSubURI->UnEscapeURIForUI(NS_LITERAL_CSTRING("UTF-8"),
323
0
                                 urlCStr, aURLStr);
324
0
}
325
326
static nsresult
327
GetSeqFrameAndCountPagesInternal(const UniquePtr<nsPrintObject>& aPO,
328
                                 nsIFrame*& aSeqFrame,
329
                                 int32_t& aCount)
330
0
{
331
0
  NS_ENSURE_ARG_POINTER(aPO);
332
0
333
0
  // This is sometimes incorrectly called before the pres shell has been created
334
0
  // (bug 1141756). MOZ_DIAGNOSTIC_ASSERT so we'll still see the crash in
335
0
  // Nightly/Aurora in case the other patch fixes this.
336
0
  if (!aPO->mPresShell) {
337
0
    MOZ_DIAGNOSTIC_ASSERT(false,
338
0
                          "GetSeqFrameAndCountPages needs a non-null pres shell");
339
0
    return NS_ERROR_FAILURE;
340
0
  }
341
0
342
0
  // Finds the SimplePageSequencer frame
343
0
  nsIPageSequenceFrame* seqFrame = aPO->mPresShell->GetPageSequenceFrame();
344
0
  aSeqFrame = do_QueryFrame(seqFrame);
345
0
  if (!aSeqFrame) {
346
0
    return NS_ERROR_FAILURE;
347
0
  }
348
0
349
0
  // count the total number of pages
350
0
  aCount = aSeqFrame->PrincipalChildList().GetLength();
351
0
352
0
  return NS_OK;
353
0
}
354
355
/**
356
 * Recursively sets the PO items to be printed "As Is"
357
 * from the given item down into the treei
358
 */
359
static void
360
SetPrintAsIs(nsPrintObject* aPO, bool aAsIs = true)
361
0
{
362
0
  NS_ASSERTION(aPO, "Pointer is null!");
363
0
364
0
  aPO->mPrintAsIs = aAsIs;
365
0
  for (const UniquePtr<nsPrintObject>& kid : aPO->mKids) {
366
0
    SetPrintAsIs(kid.get(), aAsIs);
367
0
  }
368
0
}
369
370
/**
371
 * This method is key to the entire print mechanism.
372
 *
373
 * This "maps" or figures out which sub-doc represents a
374
 * given Frame or IFrame in its parent sub-doc.
375
 *
376
 * So the Mcontent pointer in the child sub-doc points to the
377
 * content in the its parent document, that caused it to be printed.
378
 * This is used later to (after reflow) to find the absolute location
379
 * of the sub-doc on its parent's page frame so it can be
380
 * printed in the correct location.
381
 *
382
 * This method recursvely "walks" the content for a document finding
383
 * all the Frames and IFrames, then sets the "mFrameType" data member
384
 * which tells us what type of PO we have
385
 */
386
static void
387
MapContentForPO(const UniquePtr<nsPrintObject>& aPO,
388
                nsIContent* aContent)
389
0
{
390
0
  MOZ_ASSERT(aPO && aContent, "Null argument");
391
0
392
0
  nsIDocument* doc = aContent->GetComposedDoc();
393
0
394
0
  NS_ASSERTION(doc, "Content without a document from a document tree?");
395
0
396
0
  nsIDocument* subDoc = doc->GetSubDocumentFor(aContent);
397
0
398
0
  if (subDoc) {
399
0
    nsCOMPtr<nsIDocShell> docShell(subDoc->GetDocShell());
400
0
401
0
    if (docShell) {
402
0
      nsPrintObject * po = nullptr;
403
0
      for (const UniquePtr<nsPrintObject>& kid : aPO->mKids) {
404
0
        if (kid->mDocument == subDoc) {
405
0
          po = kid.get();
406
0
          break;
407
0
        }
408
0
      }
409
0
410
0
      // XXX If a subdocument has no onscreen presentation, there will be no PO
411
0
      //     This is even if there should be a print presentation
412
0
      if (po) {
413
0
        // "frame" elements not in a frameset context should be treated
414
0
        // as iframes
415
0
        if (aContent->IsHTMLElement(nsGkAtoms::frame) && po->mParent->mFrameType == eFrameSet) {
416
0
          po->mFrameType = eFrame;
417
0
        } else {
418
0
          // Assume something iframe-like, i.e. iframe, object, or embed
419
0
          po->mFrameType = eIFrame;
420
0
          SetPrintAsIs(po, true);
421
0
          NS_ASSERTION(po->mParent, "The root must be a parent");
422
0
          po->mParent->mPrintAsIs = true;
423
0
        }
424
0
      }
425
0
    }
426
0
  }
427
0
428
0
  // walk children content
429
0
  for (nsIContent* child = aContent->GetFirstChild();
430
0
       child;
431
0
       child = child->GetNextSibling()) {
432
0
    MapContentForPO(aPO, child);
433
0
  }
434
0
}
435
436
/**
437
 * The walks the PO tree and for each document it walks the content
438
 * tree looking for any content that are sub-shells
439
 *
440
 * It then sets the mContent pointer in the "found" PO object back to the
441
 * the document that contained it.
442
 */
443
static void
444
MapContentToWebShells(const UniquePtr<nsPrintObject>& aRootPO,
445
                      const UniquePtr<nsPrintObject>& aPO)
446
0
{
447
0
  NS_ASSERTION(aRootPO, "Pointer is null!");
448
0
  NS_ASSERTION(aPO, "Pointer is null!");
449
0
450
0
  // Recursively walk the content from the root item
451
0
  // XXX Would be faster to enumerate the subdocuments, although right now
452
0
  //     nsIDocument doesn't expose quite what would be needed.
453
0
  nsCOMPtr<nsIContentViewer> viewer;
454
0
  aPO->mDocShell->GetContentViewer(getter_AddRefs(viewer));
455
0
  if (!viewer) return;
456
0
457
0
  nsCOMPtr<nsIDocument> doc = viewer->GetDocument();
458
0
  if (!doc) return;
459
0
460
0
  Element* rootElement = doc->GetRootElement();
461
0
  if (rootElement) {
462
0
    MapContentForPO(aPO, rootElement);
463
0
  } else {
464
0
    NS_WARNING("Null root content on (sub)document.");
465
0
  }
466
0
467
0
  // Continue recursively walking the chilren of this PO
468
0
  for (const UniquePtr<nsPrintObject>& kid : aPO->mKids) {
469
0
    MapContentToWebShells(aRootPO, kid);
470
0
  }
471
0
472
0
}
473
474
475
//-------------------------------------------------------
476
477
NS_IMPL_ISUPPORTS(nsPrintJob, nsIWebProgressListener,
478
                  nsISupportsWeakReference, nsIObserver)
479
480
//-------------------------------------------------------
481
nsPrintJob::~nsPrintJob()
482
0
{
483
0
  Destroy(); // for insurance
484
0
  DisconnectPagePrintTimer();
485
0
}
486
487
//-------------------------------------------------------
488
void
489
nsPrintJob::Destroy()
490
0
{
491
0
  if (mIsDestroying) {
492
0
    return;
493
0
  }
494
0
  mIsDestroying = true;
495
0
496
0
  mPrt = nullptr;
497
0
498
0
#ifdef NS_PRINT_PREVIEW
499
0
  mPrtPreview = nullptr;
500
0
  mOldPrtPreview = nullptr;
501
0
#endif
502
0
  mDocViewerPrint = nullptr;
503
0
}
504
505
//-------------------------------------------------------
506
void
507
nsPrintJob::DestroyPrintingData()
508
0
{
509
0
  mPrt = nullptr;
510
0
}
511
512
//---------------------------------------------------------------------------------
513
//-- Section: Methods needed by the DocViewer
514
//---------------------------------------------------------------------------------
515
516
//--------------------------------------------------------
517
nsresult
518
nsPrintJob::Initialize(nsIDocumentViewerPrint* aDocViewerPrint,
519
                       nsIDocShell*            aContainer,
520
                       nsIDocument*            aDocument,
521
                       float                   aScreenDPI)
522
0
{
523
0
  NS_ENSURE_ARG_POINTER(aDocViewerPrint);
524
0
  NS_ENSURE_ARG_POINTER(aContainer);
525
0
  NS_ENSURE_ARG_POINTER(aDocument);
526
0
527
0
  mDocViewerPrint = aDocViewerPrint;
528
0
  mContainer      = do_GetWeakReference(aContainer);
529
0
  mDocument       = aDocument;
530
0
  mScreenDPI      = aScreenDPI;
531
0
532
0
  return NS_OK;
533
0
}
534
535
//-------------------------------------------------------
536
bool
537
nsPrintJob::CheckBeforeDestroy()
538
0
{
539
0
  if (mPrt && mPrt->mPreparingForPrint) {
540
0
    mPrt->mDocWasToBeDestroyed = true;
541
0
    return true;
542
0
  }
543
0
  return false;
544
0
}
545
546
//-------------------------------------------------------
547
nsresult
548
nsPrintJob::Cancelled()
549
0
{
550
0
  if (mPrt && mPrt->mPrintSettings) {
551
0
    return mPrt->mPrintSettings->SetIsCancelled(true);
552
0
  }
553
0
  return NS_ERROR_FAILURE;
554
0
}
555
556
//-------------------------------------------------------
557
// Install our event listeners on the document to prevent
558
// some events from being processed while in PrintPreview
559
//
560
// No return code - if this fails, there isn't much we can do
561
void
562
nsPrintJob::InstallPrintPreviewListener()
563
0
{
564
0
  if (!mPrt->mPPEventListeners) {
565
0
    nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mContainer);
566
0
    if (!docShell) {
567
0
      return;
568
0
    }
569
0
570
0
    if (nsPIDOMWindowOuter* win = docShell->GetWindow()) {
571
0
      nsCOMPtr<EventTarget> target = win->GetFrameElementInternal();
572
0
      mPrt->mPPEventListeners = new nsPrintPreviewListener(target);
573
0
      mPrt->mPPEventListeners->AddListeners();
574
0
    }
575
0
  }
576
0
}
577
578
//-----------------------------------------------------------------
579
nsresult
580
nsPrintJob::GetSeqFrameAndCountPages(nsIFrame*& aSeqFrame, int32_t& aCount)
581
0
{
582
0
  MOZ_ASSERT(mPrtPreview);
583
0
  // Guarantee that mPrintPreview->mPrintObject won't be deleted during a call
584
0
  // of GetSeqFrameAndCountPagesInternal().
585
0
  RefPtr<nsPrintData> printDataForPrintPreview = mPrtPreview;
586
0
  return GetSeqFrameAndCountPagesInternal(
587
0
           printDataForPrintPreview->mPrintObject, aSeqFrame, aCount);
588
0
}
589
//---------------------------------------------------------------------------------
590
//-- Done: Methods needed by the DocViewer
591
//---------------------------------------------------------------------------------
592
593
594
//---------------------------------------------------------------------------------
595
//-- Section: nsIWebBrowserPrint
596
//---------------------------------------------------------------------------------
597
598
// Foward decl for Debug Helper Functions
599
#ifdef EXTENDED_DEBUG_PRINTING
600
#ifdef XP_WIN
601
static int RemoveFilesInDir(const char * aDir);
602
#endif
603
static void GetDocTitleAndURL(const UniquePtr<nsPrintObject>& aPO,
604
                              nsACString& aDocStr,
605
                              nsACString& aURLStr);
606
static void DumpPrintObjectsTree(nsPrintObject * aPO, int aLevel, FILE* aFD);
607
static void DumpPrintObjectsList(const nsTArray<nsPrintObject*>& aDocList);
608
static void RootFrameList(nsPresContext* aPresContext, FILE* out,
609
                          const char* aPrefix);
610
static void DumpViews(nsIDocShell* aDocShell, FILE* out);
611
static void DumpLayoutData(const char* aTitleStr, const char* aURLStr,
612
                           nsPresContext* aPresContext,
613
                           nsDeviceContext * aDC, nsIFrame * aRootFrame,
614
                           nsIDocShell * aDocShell, FILE* aFD);
615
#endif
616
617
//--------------------------------------------------------------------------------
618
619
nsresult
620
nsPrintJob::CommonPrint(bool                    aIsPrintPreview,
621
                        nsIPrintSettings*       aPrintSettings,
622
                        nsIWebProgressListener* aWebProgressListener,
623
                        nsIDocument* aDoc)
624
0
{
625
0
  // Callers must hold a strong reference to |this| to ensure that we stay
626
0
  // alive for the duration of this method, because our main owning reference
627
0
  // (on nsDocumentViewer) might be cleared during this function (if we cause
628
0
  // script to run and it cancels the print operation).
629
0
630
0
  nsresult rv = DoCommonPrint(aIsPrintPreview, aPrintSettings,
631
0
                              aWebProgressListener, aDoc);
632
0
  if (NS_FAILED(rv)) {
633
0
    if (aIsPrintPreview) {
634
0
      mIsCreatingPrintPreview = false;
635
0
      SetIsPrintPreview(false);
636
0
    } else {
637
0
      SetIsPrinting(false);
638
0
    }
639
0
    if (mProgressDialogIsShown)
640
0
      CloseProgressDialog(aWebProgressListener);
641
0
    if (rv != NS_ERROR_ABORT && rv != NS_ERROR_OUT_OF_MEMORY) {
642
0
      FirePrintingErrorEvent(rv);
643
0
    }
644
0
    mPrt = nullptr;
645
0
  }
646
0
647
0
  return rv;
648
0
}
649
650
nsresult
651
nsPrintJob::DoCommonPrint(bool                    aIsPrintPreview,
652
                          nsIPrintSettings*       aPrintSettings,
653
                          nsIWebProgressListener* aWebProgressListener,
654
                          nsIDocument*            aDoc)
655
0
{
656
0
  nsresult rv;
657
0
658
0
  if (aIsPrintPreview) {
659
0
    // The WebProgressListener can be QI'ed to nsIPrintingPromptService
660
0
    // then that means the progress dialog is already being shown.
661
0
    nsCOMPtr<nsIPrintingPromptService> pps(do_QueryInterface(aWebProgressListener));
662
0
    mProgressDialogIsShown = pps != nullptr;
663
0
664
0
    if (mIsDoingPrintPreview) {
665
0
      mOldPrtPreview = std::move(mPrtPreview);
666
0
    }
667
0
  } else {
668
0
    mProgressDialogIsShown = false;
669
0
  }
670
0
671
0
  // Grab the new instance with local variable to guarantee that it won't be
672
0
  // deleted during this method.
673
0
  mPrt = new nsPrintData(aIsPrintPreview ? nsPrintData::eIsPrintPreview :
674
0
                                           nsPrintData::eIsPrinting);
675
0
  RefPtr<nsPrintData> printData = mPrt;
676
0
677
0
  // if they don't pass in a PrintSettings, then get the Global PS
678
0
  printData->mPrintSettings = aPrintSettings;
679
0
  if (!printData->mPrintSettings) {
680
0
    rv = GetGlobalPrintSettings(getter_AddRefs(printData->mPrintSettings));
681
0
    NS_ENSURE_SUCCESS(rv, rv);
682
0
  }
683
0
684
0
  rv = CheckForPrinters(printData->mPrintSettings);
685
0
  NS_ENSURE_SUCCESS(rv, rv);
686
0
687
0
  printData->mPrintSettings->SetIsCancelled(false);
688
0
  printData->mPrintSettings->GetShrinkToFit(&printData->mShrinkToFit);
689
0
690
0
  if (aIsPrintPreview) {
691
0
    mIsCreatingPrintPreview = true;
692
0
    SetIsPrintPreview(true);
693
0
    nsCOMPtr<nsIContentViewer> viewer =
694
0
      do_QueryInterface(mDocViewerPrint);
695
0
    if (viewer) {
696
0
      viewer->SetTextZoom(1.0f);
697
0
      viewer->SetFullZoom(1.0f);
698
0
      viewer->SetMinFontSize(0);
699
0
    }
700
0
  }
701
0
702
0
  // Create a print session and let the print settings know about it.
703
0
  // Don't overwrite an existing print session.
704
0
  // The print settings hold an nsWeakPtr to the session so it does not
705
0
  // need to be cleared from the settings at the end of the job.
706
0
  // XXX What lifetime does the printSession need to have?
707
0
  nsCOMPtr<nsIPrintSession> printSession;
708
0
  bool remotePrintJobListening = false;
709
0
  if (!aIsPrintPreview) {
710
0
    rv = printData->mPrintSettings->GetPrintSession(
711
0
                                      getter_AddRefs(printSession));
712
0
    if (NS_FAILED(rv) || !printSession) {
713
0
      printSession = do_CreateInstance("@mozilla.org/gfx/printsession;1", &rv);
714
0
      NS_ENSURE_SUCCESS(rv, rv);
715
0
      printData->mPrintSettings->SetPrintSession(printSession);
716
0
    } else {
717
0
      RefPtr<mozilla::layout::RemotePrintJobChild> remotePrintJob;
718
0
      printSession->GetRemotePrintJob(getter_AddRefs(remotePrintJob));
719
0
      if (NS_SUCCEEDED(rv) && remotePrintJob) {
720
0
        // If we have a RemotePrintJob add it to the print progress listeners,
721
0
        // so it can forward to the parent.
722
0
        printData->mPrintProgressListeners.AppendElement(remotePrintJob);
723
0
        remotePrintJobListening = true;
724
0
      }
725
0
    }
726
0
727
0
  }
728
0
729
0
  if (aWebProgressListener != nullptr) {
730
0
    printData->mPrintProgressListeners.AppendObject(aWebProgressListener);
731
0
  }
732
0
733
0
  // Get the currently focused window and cache it
734
0
  // because the Print Dialog will "steal" focus and later when you try
735
0
  // to get the currently focused windows it will be nullptr
736
0
  printData->mCurrentFocusWin = FindFocusedDOMWindow();
737
0
738
0
  // Check to see if there is a "regular" selection
739
0
  bool isSelection = IsThereARangeSelection(printData->mCurrentFocusWin);
740
0
741
0
  // Get the docshell for this documentviewer
742
0
  nsCOMPtr<nsIDocShell> webContainer(do_QueryReferent(mContainer, &rv));
743
0
  NS_ENSURE_SUCCESS(rv, rv);
744
0
745
0
  {
746
0
    if (aIsPrintPreview) {
747
0
      nsCOMPtr<nsIContentViewer> viewer;
748
0
      webContainer->GetContentViewer(getter_AddRefs(viewer));
749
0
      if (viewer && viewer->GetDocument() && viewer->GetDocument()->IsShowing()) {
750
0
        viewer->GetDocument()->OnPageHide(false, nullptr);
751
0
      }
752
0
    }
753
0
754
0
    nsAutoScriptBlocker scriptBlocker;
755
0
    printData->mPrintObject = MakeUnique<nsPrintObject>();
756
0
    rv = printData->mPrintObject->Init(webContainer, aDoc, aIsPrintPreview);
757
0
    NS_ENSURE_SUCCESS(rv, rv);
758
0
759
0
    NS_ENSURE_TRUE(printData->mPrintDocList.AppendElement(
760
0
                                              printData->mPrintObject.get()),
761
0
                   NS_ERROR_OUT_OF_MEMORY);
762
0
763
0
    printData->mIsParentAFrameSet = IsParentAFrameSet(webContainer);
764
0
    printData->mPrintObject->mFrameType =
765
0
      printData->mIsParentAFrameSet ? eFrameSet : eDoc;
766
0
767
0
    // Build the "tree" of PrintObjects
768
0
    BuildDocTree(printData->mPrintObject->mDocShell, &printData->mPrintDocList,
769
0
                 printData->mPrintObject);
770
0
  }
771
0
772
0
  // The nsAutoScriptBlocker above will now have been destroyed, which may
773
0
  // cause our print/print-preview operation to finish. In this case, we
774
0
  // should immediately return an error code so that the root caller knows
775
0
  // it shouldn't continue to do anything with this instance.
776
0
  if (mIsDestroying || (aIsPrintPreview && !mIsCreatingPrintPreview)) {
777
0
    return NS_ERROR_FAILURE;
778
0
  }
779
0
780
0
  if (!aIsPrintPreview) {
781
0
    SetIsPrinting(true);
782
0
  }
783
0
784
0
  // XXX This isn't really correct...
785
0
  if (!printData->mPrintObject->mDocument ||
786
0
      !printData->mPrintObject->mDocument->GetRootElement())
787
0
    return NS_ERROR_GFX_PRINTER_STARTDOC;
788
0
789
0
  // Create the linkage from the sub-docs back to the content element
790
0
  // in the parent document
791
0
  MapContentToWebShells(printData->mPrintObject, printData->mPrintObject);
792
0
793
0
  printData->mIsIFrameSelected =
794
0
    IsThereAnIFrameSelected(webContainer, printData->mCurrentFocusWin,
795
0
                            printData->mIsParentAFrameSet);
796
0
797
0
  // Setup print options for UI
798
0
  if (printData->mIsParentAFrameSet) {
799
0
    if (printData->mCurrentFocusWin) {
800
0
      printData->mPrintSettings->SetHowToEnableFrameUI(nsIPrintSettings::kFrameEnableAll);
801
0
    } else {
802
0
      printData->mPrintSettings->SetHowToEnableFrameUI(nsIPrintSettings::kFrameEnableAsIsAndEach);
803
0
    }
804
0
  } else {
805
0
    printData->mPrintSettings->SetHowToEnableFrameUI(nsIPrintSettings::kFrameEnableNone);
806
0
  }
807
0
  // Now determine how to set up the Frame print UI
808
0
  printData->mPrintSettings->SetPrintOptions(
809
0
                               nsIPrintSettings::kEnableSelectionRB,
810
0
                               isSelection || printData->mIsIFrameSelected);
811
0
812
0
  bool printingViaParent = XRE_IsContentProcess() &&
813
0
                           Preferences::GetBool("print.print_via_parent");
814
0
  nsCOMPtr<nsIDeviceContextSpec> devspec;
815
0
  if (printingViaParent) {
816
0
    devspec = new nsDeviceContextSpecProxy();
817
0
  } else {
818
0
    devspec = do_CreateInstance("@mozilla.org/gfx/devicecontextspec;1", &rv);
819
0
    NS_ENSURE_SUCCESS(rv, rv);
820
0
  }
821
0
822
0
  nsScriptSuppressor scriptSuppressor(this);
823
0
  // If printing via parent we still call ShowPrintDialog even for print preview
824
0
  // because we use that to retrieve the print settings from the printer.
825
0
  // The dialog is not shown, but this means we don't need to access the printer
826
0
  // driver from the child, which causes sandboxing issues.
827
0
  if (!aIsPrintPreview || printingViaParent) {
828
0
    scriptSuppressor.Suppress();
829
0
    bool printSilently;
830
0
    printData->mPrintSettings->GetPrintSilent(&printSilently);
831
0
832
0
    // Check prefs for a default setting as to whether we should print silently
833
0
    printSilently =
834
0
      Preferences::GetBool("print.always_print_silent", printSilently);
835
0
836
0
    // Ask dialog to be Print Shown via the Plugable Printing Dialog Service
837
0
    // This service is for the Print Dialog and the Print Progress Dialog
838
0
    // If printing silently or you can't get the service continue on
839
0
    // If printing via the parent then we need to confirm that the pref is set
840
0
    // and get a remote print job, but the parent won't display a prompt.
841
0
    if (!printSilently || printingViaParent) {
842
0
      nsCOMPtr<nsIPrintingPromptService> printPromptService(do_GetService(kPrintingPromptService));
843
0
      if (printPromptService) {
844
0
        nsPIDOMWindowOuter* domWin = nullptr;
845
0
        // We leave domWin as nullptr to indicate a call for print preview.
846
0
        if (!aIsPrintPreview) {
847
0
          domWin = mDocument->GetWindow();
848
0
          NS_ENSURE_TRUE(domWin, NS_ERROR_FAILURE);
849
0
        }
850
0
851
0
        // Platforms not implementing a given dialog for the service may
852
0
        // return NS_ERROR_NOT_IMPLEMENTED or an error code.
853
0
        //
854
0
        // NS_ERROR_NOT_IMPLEMENTED indicates they want default behavior
855
0
        // Any other error code means we must bail out
856
0
        //
857
0
        nsCOMPtr<nsIWebBrowserPrint> wbp(do_QueryInterface(mDocViewerPrint));
858
0
        rv = printPromptService->ShowPrintDialog(domWin, wbp,
859
0
                                                 printData->mPrintSettings);
860
0
        //
861
0
        // ShowPrintDialog triggers an event loop which means we can't assume
862
0
        // that the state of this->{anything} matches the state we've checked
863
0
        // above. Including that a given {thing} is non null.
864
0
        if (NS_WARN_IF(mPrt != printData)) {
865
0
          return NS_ERROR_FAILURE;
866
0
        }
867
0
868
0
        if (NS_SUCCEEDED(rv)) {
869
0
          // since we got the dialog and it worked then make sure we
870
0
          // are telling GFX we want to print silent
871
0
          printSilently = true;
872
0
873
0
          if (printData->mPrintSettings && !aIsPrintPreview) {
874
0
            // The user might have changed shrink-to-fit in the print dialog, so update our copy of its state
875
0
            printData->mPrintSettings->GetShrinkToFit(&printData->mShrinkToFit);
876
0
877
0
            // If we haven't already added the RemotePrintJob as a listener,
878
0
            // add it now if there is one.
879
0
            if (!remotePrintJobListening) {
880
0
              RefPtr<mozilla::layout::RemotePrintJobChild> remotePrintJob;
881
0
              printSession->GetRemotePrintJob(getter_AddRefs(remotePrintJob));
882
0
              if (NS_SUCCEEDED(rv) && remotePrintJob) {
883
0
                printData->mPrintProgressListeners.AppendElement(
884
0
                                                     remotePrintJob);
885
0
                remotePrintJobListening = true;
886
0
              }
887
0
            }
888
0
          }
889
0
        } else if (rv == NS_ERROR_NOT_IMPLEMENTED) {
890
0
          // This means the Dialog service was there,
891
0
          // but they choose not to implement this dialog and
892
0
          // are looking for default behavior from the toolkit
893
0
          rv = NS_OK;
894
0
        }
895
0
      } else {
896
0
        // No dialog service available
897
0
        rv = NS_ERROR_NOT_IMPLEMENTED;
898
0
      }
899
0
    } else {
900
0
      // Call any code that requires a run of the event loop.
901
0
      rv = printData->mPrintSettings->SetupSilentPrinting();
902
0
    }
903
0
    // Check explicitly for abort because it's expected
904
0
    if (rv == NS_ERROR_ABORT)
905
0
      return rv;
906
0
    NS_ENSURE_SUCCESS(rv, rv);
907
0
  }
908
0
909
0
  rv = devspec->Init(nullptr, printData->mPrintSettings, aIsPrintPreview);
910
0
  NS_ENSURE_SUCCESS(rv, rv);
911
0
912
0
  printData->mPrintDC = new nsDeviceContext();
913
0
  rv = printData->mPrintDC->InitForPrinting(devspec);
914
0
  NS_ENSURE_SUCCESS(rv, rv);
915
0
916
0
  if (XRE_IsParentProcess() && !printData->mPrintDC->IsSyncPagePrinting()) {
917
0
    RefPtr<nsPrintJob> self(this);
918
0
    printData->mPrintDC->RegisterPageDoneCallback([self](nsresult aResult) { self->PageDone(aResult); });
919
0
  }
920
0
921
0
  if (aIsPrintPreview) {
922
0
    printData->mPrintSettings->SetPrintFrameType(nsIPrintSettings::kFramesAsIs);
923
0
924
0
    // override any UI that wants to PrintPreview any selection or page range
925
0
    // we want to view every page in PrintPreview each time
926
0
    printData->mPrintSettings->SetPrintRange(nsIPrintSettings::kRangeAllPages);
927
0
  } else {
928
0
    // Always check and set the print settings first and then fall back
929
0
    // onto the PrintService if there isn't a PrintSettings
930
0
    //
931
0
    // Posiible Usage values:
932
0
    //   nsIPrintSettings::kUseInternalDefault
933
0
    //   nsIPrintSettings::kUseSettingWhenPossible
934
0
    //
935
0
    // NOTE: The consts are the same for PrintSettings and PrintSettings
936
0
    int16_t printFrameTypeUsage = nsIPrintSettings::kUseSettingWhenPossible;
937
0
    printData->mPrintSettings->GetPrintFrameTypeUsage(&printFrameTypeUsage);
938
0
939
0
    // Ok, see if we are going to use our value and override the default
940
0
    if (printFrameTypeUsage == nsIPrintSettings::kUseSettingWhenPossible) {
941
0
      // Get the Print Options/Settings PrintFrameType to see what is preferred
942
0
      int16_t printFrameType = nsIPrintSettings::kEachFrameSep;
943
0
      printData->mPrintSettings->GetPrintFrameType(&printFrameType);
944
0
945
0
      // Don't let anybody do something stupid like try to set it to
946
0
      // kNoFrames when we are printing a FrameSet
947
0
      if (printFrameType == nsIPrintSettings::kNoFrames) {
948
0
        printData->mPrintFrameType = nsIPrintSettings::kEachFrameSep;
949
0
        printData->mPrintSettings->SetPrintFrameType(
950
0
                                     printData->mPrintFrameType);
951
0
      } else {
952
0
        // First find out from the PrinService what options are available
953
0
        // to us for Printing FrameSets
954
0
        int16_t howToEnableFrameUI;
955
0
        printData->mPrintSettings->GetHowToEnableFrameUI(&howToEnableFrameUI);
956
0
        if (howToEnableFrameUI != nsIPrintSettings::kFrameEnableNone) {
957
0
          switch (howToEnableFrameUI) {
958
0
          case nsIPrintSettings::kFrameEnableAll:
959
0
            printData->mPrintFrameType = printFrameType;
960
0
            break;
961
0
962
0
          case nsIPrintSettings::kFrameEnableAsIsAndEach:
963
0
            if (printFrameType != nsIPrintSettings::kSelectedFrame) {
964
0
              printData->mPrintFrameType = printFrameType;
965
0
            } else { // revert back to a good value
966
0
              printData->mPrintFrameType = nsIPrintSettings::kEachFrameSep;
967
0
            }
968
0
            break;
969
0
          } // switch
970
0
          printData->mPrintSettings->SetPrintFrameType(
971
0
                                       printData->mPrintFrameType);
972
0
        }
973
0
      }
974
0
    } else {
975
0
      printData->mPrintSettings->GetPrintFrameType(&printData->mPrintFrameType);
976
0
    }
977
0
  }
978
0
979
0
  if (printData->mPrintFrameType == nsIPrintSettings::kEachFrameSep) {
980
0
    CheckForChildFrameSets(printData->mPrintObject);
981
0
  }
982
0
983
0
  if (NS_FAILED(EnablePOsForPrinting())) {
984
0
    return NS_ERROR_FAILURE;
985
0
  }
986
0
987
0
  // Attach progressListener to catch network requests.
988
0
  nsCOMPtr<nsIWebProgress> webProgress =
989
0
    do_QueryInterface(printData->mPrintObject->mDocShell);
990
0
  webProgress->AddProgressListener(
991
0
    static_cast<nsIWebProgressListener*>(this),
992
0
    nsIWebProgress::NOTIFY_STATE_REQUEST);
993
0
994
0
  mLoadCounter = 0;
995
0
  mDidLoadDataForPrinting = false;
996
0
997
0
  if (aIsPrintPreview) {
998
0
    bool notifyOnInit = false;
999
0
    ShowPrintProgress(false, notifyOnInit);
1000
0
1001
0
    // Very important! Turn Off scripting
1002
0
    TurnScriptingOn(false);
1003
0
1004
0
    if (!notifyOnInit) {
1005
0
      InstallPrintPreviewListener();
1006
0
      rv = InitPrintDocConstruction(false);
1007
0
    } else {
1008
0
      rv = NS_OK;
1009
0
    }
1010
0
  } else {
1011
0
    bool doNotify;
1012
0
    ShowPrintProgress(true, doNotify);
1013
0
    if (!doNotify) {
1014
0
      // Print listener setup...
1015
0
      printData->OnStartPrinting();
1016
0
1017
0
      rv = InitPrintDocConstruction(false);
1018
0
    }
1019
0
  }
1020
0
1021
0
  // We will enable scripting later after printing has finished.
1022
0
  scriptSuppressor.Disconnect();
1023
0
1024
0
  return NS_OK;
1025
0
}
1026
1027
//---------------------------------------------------------------------------------
1028
NS_IMETHODIMP
1029
nsPrintJob::Print(nsIPrintSettings*       aPrintSettings,
1030
                  nsIWebProgressListener* aWebProgressListener)
1031
0
{
1032
0
  // If we have a print preview document, use that instead of the original
1033
0
  // mDocument. That way animated images etc. get printed using the same state
1034
0
  // as in print preview.
1035
0
  nsIDocument* doc =
1036
0
    mPrtPreview && mPrtPreview->mPrintObject ?
1037
0
      mPrtPreview->mPrintObject->mDocument : mDocument;
1038
0
1039
0
  return CommonPrint(false, aPrintSettings, aWebProgressListener, doc);
1040
0
}
1041
1042
NS_IMETHODIMP
1043
nsPrintJob::PrintPreview(nsIPrintSettings* aPrintSettings,
1044
                         mozIDOMWindowProxy* aChildDOMWin,
1045
                         nsIWebProgressListener* aWebProgressListener)
1046
0
{
1047
0
  // Get the DocShell and see if it is busy
1048
0
  // (We can't Print Preview this document if it is still busy)
1049
0
  nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mContainer));
1050
0
  NS_ENSURE_STATE(docShell);
1051
0
1052
0
  uint32_t busyFlags = nsIDocShell::BUSY_FLAGS_NONE;
1053
0
  if (NS_FAILED(docShell->GetBusyFlags(&busyFlags)) ||
1054
0
      busyFlags != nsIDocShell::BUSY_FLAGS_NONE) {
1055
0
    CloseProgressDialog(aWebProgressListener);
1056
0
    FirePrintingErrorEvent(NS_ERROR_GFX_PRINTER_DOC_IS_BUSY);
1057
0
    return NS_ERROR_FAILURE;
1058
0
  }
1059
0
1060
0
  auto* window = nsPIDOMWindowOuter::From(aChildDOMWin);
1061
0
  NS_ENSURE_STATE(window);
1062
0
  nsCOMPtr<nsIDocument> doc = window->GetDoc();
1063
0
  NS_ENSURE_STATE(doc);
1064
0
1065
0
  // Document is not busy -- go ahead with the Print Preview
1066
0
  return CommonPrint(true, aPrintSettings, aWebProgressListener, doc);
1067
0
}
1068
1069
//----------------------------------------------------------------------------------
1070
NS_IMETHODIMP
1071
nsPrintJob::GetIsFramesetDocument(bool* aIsFramesetDocument)
1072
0
{
1073
0
  nsCOMPtr<nsIDocShell> webContainer(do_QueryReferent(mContainer));
1074
0
  *aIsFramesetDocument = IsParentAFrameSet(webContainer);
1075
0
  return NS_OK;
1076
0
}
1077
1078
//----------------------------------------------------------------------------------
1079
NS_IMETHODIMP
1080
nsPrintJob::GetIsIFrameSelected(bool* aIsIFrameSelected)
1081
0
{
1082
0
  *aIsIFrameSelected = false;
1083
0
1084
0
  // Get the docshell for this documentviewer
1085
0
  nsCOMPtr<nsIDocShell> webContainer(do_QueryReferent(mContainer));
1086
0
  // Get the currently focused window
1087
0
  nsCOMPtr<nsPIDOMWindowOuter> currentFocusWin = FindFocusedDOMWindow();
1088
0
  if (currentFocusWin && webContainer) {
1089
0
    // Get whether the doc contains a frameset
1090
0
    // Also, check to see if the currently focus docshell
1091
0
    // is a child of this docshell
1092
0
    bool isParentFrameSet;
1093
0
    *aIsIFrameSelected = IsThereAnIFrameSelected(webContainer, currentFocusWin, isParentFrameSet);
1094
0
  }
1095
0
  return NS_OK;
1096
0
}
1097
1098
//----------------------------------------------------------------------------------
1099
NS_IMETHODIMP
1100
nsPrintJob::GetIsRangeSelection(bool* aIsRangeSelection)
1101
0
{
1102
0
  // Get the currently focused window
1103
0
  nsCOMPtr<nsPIDOMWindowOuter> currentFocusWin = FindFocusedDOMWindow();
1104
0
  *aIsRangeSelection = IsThereARangeSelection(currentFocusWin);
1105
0
  return NS_OK;
1106
0
}
1107
1108
//----------------------------------------------------------------------------------
1109
NS_IMETHODIMP
1110
nsPrintJob::GetIsFramesetFrameSelected(bool* aIsFramesetFrameSelected)
1111
0
{
1112
0
  // Get the currently focused window
1113
0
  nsCOMPtr<nsPIDOMWindowOuter> currentFocusWin = FindFocusedDOMWindow();
1114
0
  *aIsFramesetFrameSelected = currentFocusWin != nullptr;
1115
0
  return NS_OK;
1116
0
}
1117
1118
//----------------------------------------------------------------------------------
1119
NS_IMETHODIMP
1120
nsPrintJob::GetPrintPreviewNumPages(int32_t* aPrintPreviewNumPages)
1121
0
{
1122
0
  NS_ENSURE_ARG_POINTER(aPrintPreviewNumPages);
1123
0
1124
0
  nsIFrame* seqFrame  = nullptr;
1125
0
  *aPrintPreviewNumPages = 0;
1126
0
1127
0
  // When calling this function, the FinishPrintPreview() function might not
1128
0
  // been called as there are still some
1129
0
  RefPtr<nsPrintData> printData = mPrtPreview ? mPrtPreview : mPrt;
1130
0
  if (NS_WARN_IF(!printData)) {
1131
0
    return NS_ERROR_FAILURE;
1132
0
  }
1133
0
  nsresult rv =
1134
0
    GetSeqFrameAndCountPagesInternal(printData->mPrintObject, seqFrame,
1135
0
                                     *aPrintPreviewNumPages);
1136
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1137
0
    return NS_ERROR_FAILURE;
1138
0
  }
1139
0
  return NS_OK;
1140
0
}
1141
1142
//----------------------------------------------------------------------------------
1143
// Enumerate all the documents for their titles
1144
NS_IMETHODIMP
1145
nsPrintJob::EnumerateDocumentNames(uint32_t* aCount,
1146
                                   char16_t*** aResult)
1147
0
{
1148
0
  NS_ENSURE_ARG(aCount);
1149
0
  NS_ENSURE_ARG_POINTER(aResult);
1150
0
1151
0
  *aCount = 0;
1152
0
  *aResult = nullptr;
1153
0
1154
0
  int32_t     numDocs = mPrt->mPrintDocList.Length();
1155
0
  char16_t** array   = (char16_t**) moz_xmalloc(numDocs * sizeof(char16_t*));
1156
0
1157
0
  for (int32_t i=0;i<numDocs;i++) {
1158
0
    nsPrintObject* po = mPrt->mPrintDocList.ElementAt(i);
1159
0
    NS_ASSERTION(po, "nsPrintObject can't be null!");
1160
0
    nsAutoString docTitleStr;
1161
0
    nsAutoString docURLStr;
1162
0
    GetDocumentTitleAndURL(po->mDocument, docTitleStr, docURLStr);
1163
0
1164
0
    // Use the URL if the doc is empty
1165
0
    if (docTitleStr.IsEmpty() && !docURLStr.IsEmpty()) {
1166
0
      docTitleStr = docURLStr;
1167
0
    }
1168
0
    array[i] = ToNewUnicode(docTitleStr);
1169
0
  }
1170
0
  *aCount  = numDocs;
1171
0
  *aResult = array;
1172
0
1173
0
  return NS_OK;
1174
0
1175
0
}
1176
1177
//----------------------------------------------------------------------------------
1178
nsresult
1179
nsPrintJob::GetGlobalPrintSettings(nsIPrintSettings** aGlobalPrintSettings)
1180
0
{
1181
0
  NS_ENSURE_ARG_POINTER(aGlobalPrintSettings);
1182
0
1183
0
  nsresult rv = NS_ERROR_FAILURE;
1184
0
  nsCOMPtr<nsIPrintSettingsService> printSettingsService =
1185
0
    do_GetService(sPrintSettingsServiceContractID, &rv);
1186
0
  if (NS_SUCCEEDED(rv)) {
1187
0
    rv = printSettingsService->GetGlobalPrintSettings(aGlobalPrintSettings);
1188
0
  }
1189
0
  return rv;
1190
0
}
1191
1192
//----------------------------------------------------------------------------------
1193
NS_IMETHODIMP
1194
nsPrintJob::GetDoingPrint(bool* aDoingPrint)
1195
0
{
1196
0
  NS_ENSURE_ARG_POINTER(aDoingPrint);
1197
0
  *aDoingPrint = mIsDoingPrinting;
1198
0
  return NS_OK;
1199
0
}
1200
1201
//----------------------------------------------------------------------------------
1202
NS_IMETHODIMP
1203
nsPrintJob::GetDoingPrintPreview(bool* aDoingPrintPreview)
1204
0
{
1205
0
  NS_ENSURE_ARG_POINTER(aDoingPrintPreview);
1206
0
  *aDoingPrintPreview = mIsDoingPrintPreview;
1207
0
  return NS_OK;
1208
0
}
1209
1210
//----------------------------------------------------------------------------------
1211
NS_IMETHODIMP
1212
nsPrintJob::GetCurrentPrintSettings(nsIPrintSettings** aCurrentPrintSettings)
1213
0
{
1214
0
  NS_ENSURE_ARG_POINTER(aCurrentPrintSettings);
1215
0
1216
0
  if (mPrt) {
1217
0
    *aCurrentPrintSettings = mPrt->mPrintSettings;
1218
0
1219
0
  } else if (mPrtPreview) {
1220
0
    *aCurrentPrintSettings = mPrtPreview->mPrintSettings;
1221
0
1222
0
  } else {
1223
0
    *aCurrentPrintSettings = nullptr;
1224
0
  }
1225
0
  NS_IF_ADDREF(*aCurrentPrintSettings);
1226
0
  return NS_OK;
1227
0
}
1228
1229
//-----------------------------------------------------------------
1230
//-- Section: Pre-Reflow Methods
1231
//-----------------------------------------------------------------
1232
1233
//---------------------------------------------------------------------
1234
// This method checks to see if there is at least one printer defined
1235
// and if so, it sets the first printer in the list as the default name
1236
// in the PrintSettings which is then used for Printer Preview
1237
nsresult
1238
nsPrintJob::CheckForPrinters(nsIPrintSettings* aPrintSettings)
1239
0
{
1240
#if defined(XP_MACOSX) || defined(ANDROID)
1241
  // Mac doesn't support retrieving a printer list.
1242
  return NS_OK;
1243
#else
1244
#if defined(MOZ_X11)
1245
0
  // On Linux, default printer name should be requested on the parent side.
1246
0
  // Unless we are in the parent, we ignore this function
1247
0
  if (!XRE_IsParentProcess()) {
1248
0
    return NS_OK;
1249
0
  }
1250
0
#endif
1251
0
  NS_ENSURE_ARG_POINTER(aPrintSettings);
1252
0
1253
0
  // See if aPrintSettings already has a printer
1254
0
  nsString printerName;
1255
0
  nsresult rv = aPrintSettings->GetPrinterName(printerName);
1256
0
  if (NS_SUCCEEDED(rv) && !printerName.IsEmpty()) {
1257
0
    return NS_OK;
1258
0
  }
1259
0
1260
0
  // aPrintSettings doesn't have a printer set. Try to fetch the default.
1261
0
  nsCOMPtr<nsIPrintSettingsService> printSettingsService =
1262
0
    do_GetService(sPrintSettingsServiceContractID, &rv);
1263
0
  NS_ENSURE_SUCCESS(rv, rv);
1264
0
1265
0
  rv = printSettingsService->GetDefaultPrinterName(printerName);
1266
0
  if (NS_SUCCEEDED(rv) && !printerName.IsEmpty()) {
1267
0
    rv = aPrintSettings->SetPrinterName(printerName);
1268
0
  }
1269
0
  return rv;
1270
0
#endif
1271
0
}
1272
1273
//----------------------------------------------------------------------
1274
// Set up to use the "pluggable" Print Progress Dialog
1275
void
1276
nsPrintJob::ShowPrintProgress(bool aIsForPrinting, bool& aDoNotify)
1277
0
{
1278
0
  // default to not notifying, that if something here goes wrong
1279
0
  // or we aren't going to show the progress dialog we can straight into
1280
0
  // reflowing the doc for printing.
1281
0
  aDoNotify = false;
1282
0
1283
0
  // Assume we can't do progress and then see if we can
1284
0
  bool showProgresssDialog = false;
1285
0
1286
0
  // if it is already being shown then don't bother to find out if it should be
1287
0
  // so skip this and leave mShowProgressDialog set to FALSE
1288
0
  if (!mProgressDialogIsShown) {
1289
0
    showProgresssDialog = Preferences::GetBool("print.show_print_progress");
1290
0
  }
1291
0
1292
0
  // Guarantee that mPrt and the objects it owns won't be deleted.  If this
1293
0
  // method shows a progress dialog and spins the event loop.  So, mPrt may be
1294
0
  // cleared or recreated.
1295
0
  RefPtr<nsPrintData> printData = mPrt;
1296
0
1297
0
  // Turning off the showing of Print Progress in Prefs overrides
1298
0
  // whether the calling PS desire to have it on or off, so only check PS if
1299
0
  // prefs says it's ok to be on.
1300
0
  if (showProgresssDialog) {
1301
0
    printData->mPrintSettings->GetShowPrintProgress(&showProgresssDialog);
1302
0
  }
1303
0
1304
0
  // Now open the service to get the progress dialog
1305
0
  // If we don't get a service, that's ok, then just don't show progress
1306
0
  if (showProgresssDialog) {
1307
0
    nsCOMPtr<nsIPrintingPromptService> printPromptService(do_GetService(kPrintingPromptService));
1308
0
    if (printPromptService) {
1309
0
      nsPIDOMWindowOuter* domWin = mDocument->GetWindow();
1310
0
      if (!domWin) return;
1311
0
1312
0
      nsCOMPtr<nsIDocShell> docShell = domWin->GetDocShell();
1313
0
      if (!docShell) return;
1314
0
      nsCOMPtr<nsIDocShellTreeOwner> owner;
1315
0
      docShell->GetTreeOwner(getter_AddRefs(owner));
1316
0
      nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(owner);
1317
0
      if (!browserChrome) return;
1318
0
      bool isModal = true;
1319
0
      browserChrome->IsWindowModal(&isModal);
1320
0
      if (isModal) {
1321
0
        // Showing a print progress dialog when printing a modal window
1322
0
        // isn't supported. See bug 301560.
1323
0
        return;
1324
0
      }
1325
0
1326
0
      nsCOMPtr<nsIWebProgressListener> printProgressListener;
1327
0
1328
0
      nsCOMPtr<nsIWebBrowserPrint> wbp(do_QueryInterface(mDocViewerPrint));
1329
0
      nsresult rv =
1330
0
        printPromptService->ShowProgress(
1331
0
                              domWin, wbp, printData->mPrintSettings, this,
1332
0
                              aIsForPrinting,
1333
0
                              getter_AddRefs(printProgressListener),
1334
0
                              getter_AddRefs(printData->mPrintProgressParams),
1335
0
                              &aDoNotify);
1336
0
      if (NS_SUCCEEDED(rv)) {
1337
0
        if (printProgressListener) {
1338
0
          printData->mPrintProgressListeners.AppendObject(
1339
0
                                               printProgressListener);
1340
0
        }
1341
0
1342
0
        if (printData->mPrintProgressParams) {
1343
0
          SetDocAndURLIntoProgress(printData->mPrintObject,
1344
0
                                   printData->mPrintProgressParams);
1345
0
        }
1346
0
      }
1347
0
    }
1348
0
  }
1349
0
}
1350
1351
//---------------------------------------------------------------------
1352
bool
1353
nsPrintJob::IsThereARangeSelection(nsPIDOMWindowOuter* aDOMWin)
1354
0
{
1355
0
  if (mDisallowSelectionPrint)
1356
0
    return false;
1357
0
1358
0
  nsCOMPtr<nsIPresShell> presShell;
1359
0
  if (aDOMWin) {
1360
0
    presShell = aDOMWin->GetDocShell()->GetPresShell();
1361
0
  }
1362
0
1363
0
  if (!presShell)
1364
0
    return false;
1365
0
1366
0
  // check here to see if there is a range selection
1367
0
  // so we know whether to turn on the "Selection" radio button
1368
0
  Selection* selection = presShell->GetCurrentSelection(SelectionType::eNormal);
1369
0
  if (!selection) {
1370
0
    return false;
1371
0
  }
1372
0
1373
0
  int32_t rangeCount = selection->RangeCount();
1374
0
  if (!rangeCount) {
1375
0
    return false;
1376
0
  }
1377
0
1378
0
  if (rangeCount > 1) {
1379
0
    return true;
1380
0
  }
1381
0
1382
0
  // check to make sure it isn't an insertion selection
1383
0
  return selection->GetRangeAt(0) && !selection->IsCollapsed();
1384
0
}
1385
1386
//---------------------------------------------------------------------
1387
// Recursively build a list of sub documents to be printed
1388
// that mirrors the document tree
1389
void
1390
nsPrintJob::BuildDocTree(nsIDocShell*      aParentNode,
1391
                         nsTArray<nsPrintObject*>* aDocList,
1392
                         const UniquePtr<nsPrintObject>& aPO)
1393
0
{
1394
0
  NS_ASSERTION(aParentNode, "Pointer is null!");
1395
0
  NS_ASSERTION(aDocList, "Pointer is null!");
1396
0
  NS_ASSERTION(aPO, "Pointer is null!");
1397
0
1398
0
  int32_t childWebshellCount;
1399
0
  aParentNode->GetChildCount(&childWebshellCount);
1400
0
  if (childWebshellCount > 0) {
1401
0
    for (int32_t i=0;i<childWebshellCount;i++) {
1402
0
      nsCOMPtr<nsIDocShellTreeItem> child;
1403
0
      aParentNode->GetChildAt(i, getter_AddRefs(child));
1404
0
      nsCOMPtr<nsIDocShell> childAsShell(do_QueryInterface(child));
1405
0
1406
0
      nsCOMPtr<nsIContentViewer>  viewer;
1407
0
      childAsShell->GetContentViewer(getter_AddRefs(viewer));
1408
0
      if (viewer) {
1409
0
        nsCOMPtr<nsIDocument> doc = do_GetInterface(childAsShell);
1410
0
        auto po = MakeUnique<nsPrintObject>();
1411
0
        po->mParent = aPO.get();
1412
0
        nsresult rv = po->Init(childAsShell, doc, aPO->mPrintPreview);
1413
0
        if (NS_FAILED(rv))
1414
0
          MOZ_ASSERT_UNREACHABLE("Init failed?");
1415
0
        aPO->mKids.AppendElement(std::move(po));
1416
0
        aDocList->AppendElement(aPO->mKids.LastElement().get());
1417
0
        BuildDocTree(childAsShell, aDocList, aPO->mKids.LastElement());
1418
0
      }
1419
0
    }
1420
0
  }
1421
0
}
1422
1423
//-------------------------------------------------------
1424
// A Frame's sub-doc may contain content or a FrameSet
1425
// When it contains a FrameSet the mFrameType for the PrintObject
1426
// is always set to an eFrame. Which is fine when printing "AsIs"
1427
// but is incorrect when when printing "Each Frame Separately".
1428
// When printing "Each Frame Separately" the Frame really acts like
1429
// a frameset.
1430
//
1431
// This method walks the PO tree and checks to see if the PrintObject is
1432
// an eFrame and has children that are eFrames (meaning it's a Frame containing a FrameSet)
1433
// If so, then the mFrameType need to be changed to eFrameSet
1434
//
1435
// Also note: We only want to call this we are printing "Each Frame Separately"
1436
//            when printing "As Is" leave it as an eFrame
1437
void
1438
nsPrintJob::CheckForChildFrameSets(const UniquePtr<nsPrintObject>& aPO)
1439
0
{
1440
0
  NS_ASSERTION(aPO, "Pointer is null!");
1441
0
1442
0
  // Continue recursively walking the chilren of this PO
1443
0
  bool hasChildFrames = false;
1444
0
  for (const UniquePtr<nsPrintObject>& po : aPO->mKids) {
1445
0
    if (po->mFrameType == eFrame) {
1446
0
      hasChildFrames = true;
1447
0
      CheckForChildFrameSets(po);
1448
0
    }
1449
0
  }
1450
0
1451
0
  if (hasChildFrames && aPO->mFrameType == eFrame) {
1452
0
    aPO->mFrameType = eFrameSet;
1453
0
  }
1454
0
}
1455
1456
//---------------------------------------------------------------------
1457
bool
1458
nsPrintJob::IsThereAnIFrameSelected(nsIDocShell* aDocShell,
1459
                                    nsPIDOMWindowOuter* aDOMWin,
1460
                                    bool& aIsParentFrameSet)
1461
0
{
1462
0
  aIsParentFrameSet = IsParentAFrameSet(aDocShell);
1463
0
  bool iFrameIsSelected = false;
1464
0
  if (mPrt && mPrt->mPrintObject) {
1465
0
    nsPrintObject* po = FindPrintObjectByDOMWin(mPrt->mPrintObject.get(), aDOMWin);
1466
0
    iFrameIsSelected = po && po->mFrameType == eIFrame;
1467
0
  } else {
1468
0
    // First, check to see if we are a frameset
1469
0
    if (!aIsParentFrameSet) {
1470
0
      // Check to see if there is a currenlt focused frame
1471
0
      // if so, it means the selected frame is either the main docshell
1472
0
      // or an IFRAME
1473
0
      if (aDOMWin) {
1474
0
        // Get the main docshell's DOMWin to see if it matches
1475
0
        // the frame that is selected
1476
0
        nsPIDOMWindowOuter* domWin = aDocShell ? aDocShell->GetWindow() : nullptr;
1477
0
        if (domWin != aDOMWin) {
1478
0
          iFrameIsSelected = true; // we have a selected IFRAME
1479
0
        }
1480
0
      }
1481
0
    }
1482
0
  }
1483
0
1484
0
  return iFrameIsSelected;
1485
0
}
1486
1487
//---------------------------------------------------------------------
1488
// Recursively sets all the PO items to be printed
1489
// from the given item down into the tree
1490
void
1491
nsPrintJob::SetPrintPO(nsPrintObject* aPO, bool aPrint)
1492
0
{
1493
0
  NS_ASSERTION(aPO, "Pointer is null!");
1494
0
1495
0
  // Set whether to print flag
1496
0
  aPO->mDontPrint = !aPrint;
1497
0
1498
0
  for (const UniquePtr<nsPrintObject>& kid : aPO->mKids) {
1499
0
    SetPrintPO(kid.get(), aPrint);
1500
0
  }
1501
0
}
1502
1503
//---------------------------------------------------------------------
1504
// This will first use a Title and/or URL from the PrintSettings
1505
// if one isn't set then it uses the one from the document
1506
// then if not title is there we will make sure we send something back
1507
// depending on the situation.
1508
void
1509
nsPrintJob::GetDisplayTitleAndURL(const UniquePtr<nsPrintObject>& aPO,
1510
                                  nsAString& aTitle,
1511
                                  nsAString& aURLStr,
1512
                                  eDocTitleDefault aDefType)
1513
0
{
1514
0
  NS_ASSERTION(aPO, "Pointer is null!");
1515
0
1516
0
  if (!mPrt)
1517
0
    return;
1518
0
1519
0
  aTitle.Truncate();
1520
0
  aURLStr.Truncate();
1521
0
1522
0
  // First check to see if the PrintSettings has defined an alternate title
1523
0
  // and use that if it did
1524
0
  if (mPrt->mPrintSettings) {
1525
0
    mPrt->mPrintSettings->GetTitle(aTitle);
1526
0
    mPrt->mPrintSettings->GetDocURL(aURLStr);
1527
0
  }
1528
0
1529
0
  nsAutoString docTitle;
1530
0
  nsAutoString docUrl;
1531
0
  GetDocumentTitleAndURL(aPO->mDocument, docTitle, docUrl);
1532
0
1533
0
  if (aURLStr.IsEmpty() && !docUrl.IsEmpty()) {
1534
0
    aURLStr = docUrl;
1535
0
  }
1536
0
1537
0
  if (aTitle.IsEmpty()) {
1538
0
    if (!docTitle.IsEmpty()) {
1539
0
      aTitle = docTitle;
1540
0
    } else {
1541
0
      if (aDefType == eDocTitleDefURLDoc) {
1542
0
        if (!aURLStr.IsEmpty()) {
1543
0
          aTitle = aURLStr;
1544
0
        } else if (!mPrt->mBrandName.IsEmpty()) {
1545
0
          aTitle = mPrt->mBrandName;
1546
0
        }
1547
0
      }
1548
0
    }
1549
0
  }
1550
0
}
1551
1552
//---------------------------------------------------------------------
1553
nsresult
1554
nsPrintJob::DocumentReadyForPrinting()
1555
0
{
1556
0
  if (mPrt->mPrintFrameType == nsIPrintSettings::kEachFrameSep) {
1557
0
    // Guarantee that mPrt->mPrintObject won't be deleted during a call of
1558
0
    // CheckForChildFrameSets().
1559
0
    RefPtr<nsPrintData> printData = mPrt;
1560
0
    CheckForChildFrameSets(printData->mPrintObject);
1561
0
  }
1562
0
1563
0
  //
1564
0
  // Send the document to the printer...
1565
0
  //
1566
0
  nsresult rv = SetupToPrintContent();
1567
0
  if (NS_FAILED(rv)) {
1568
0
    // The print job was canceled or there was a problem
1569
0
    // So remove all other documents from the print list
1570
0
    DonePrintingPages(nullptr, rv);
1571
0
  }
1572
0
  return rv;
1573
0
}
1574
1575
/** ---------------------------------------------------
1576
 *  Cleans up when an error occurred
1577
 */
1578
nsresult
1579
nsPrintJob::CleanupOnFailure(nsresult aResult, bool aIsPrinting)
1580
0
{
1581
0
  PR_PL(("****  Failed %s - rv 0x%" PRIX32, aIsPrinting?"Printing":"Print Preview",
1582
0
         static_cast<uint32_t>(aResult)));
1583
0
1584
0
  /* cleanup... */
1585
0
  if (mPagePrintTimer) {
1586
0
    mPagePrintTimer->Stop();
1587
0
    DisconnectPagePrintTimer();
1588
0
  }
1589
0
1590
0
  if (aIsPrinting) {
1591
0
    SetIsPrinting(false);
1592
0
  } else {
1593
0
    SetIsPrintPreview(false);
1594
0
    mIsCreatingPrintPreview = false;
1595
0
  }
1596
0
1597
0
  /* cleanup done, let's fire-up an error dialog to notify the user
1598
0
   * what went wrong...
1599
0
   *
1600
0
   * When rv == NS_ERROR_ABORT, it means we want out of the
1601
0
   * print job without displaying any error messages
1602
0
   */
1603
0
  if (aResult != NS_ERROR_ABORT) {
1604
0
    FirePrintingErrorEvent(aResult);
1605
0
  }
1606
0
1607
0
  FirePrintCompletionEvent();
1608
0
1609
0
  return aResult;
1610
0
1611
0
}
1612
1613
//---------------------------------------------------------------------
1614
void
1615
nsPrintJob::FirePrintingErrorEvent(nsresult aPrintError)
1616
0
{
1617
0
  nsCOMPtr<nsIContentViewer> cv = do_QueryInterface(mDocViewerPrint);
1618
0
  if (NS_WARN_IF(!cv)) {
1619
0
    return;
1620
0
  }
1621
0
1622
0
  nsCOMPtr<nsIDocument> doc = cv->GetDocument();
1623
0
  RefPtr<CustomEvent> event =
1624
0
    NS_NewDOMCustomEvent(doc, nullptr, nullptr);
1625
0
1626
0
  MOZ_ASSERT(event);
1627
0
1628
0
  AutoJSAPI jsapi;
1629
0
  if (!jsapi.Init(event->GetParentObject())) {
1630
0
    return;
1631
0
  }
1632
0
  JSContext* cx = jsapi.cx();
1633
0
1634
0
  JS::Rooted<JS::Value> detail(cx,
1635
0
    JS::NumberValue(static_cast<double>(aPrintError)));
1636
0
  event->InitCustomEvent(cx, NS_LITERAL_STRING("PrintingError"), false, false,
1637
0
                         detail);
1638
0
  event->SetTrusted(true);
1639
0
1640
0
  RefPtr<AsyncEventDispatcher> asyncDispatcher =
1641
0
    new AsyncEventDispatcher(doc, event);
1642
0
  asyncDispatcher->mOnlyChromeDispatch = ChromeOnlyDispatch::eYes;
1643
0
  asyncDispatcher->RunDOMEventWhenSafe();
1644
0
1645
0
  // Inform any progress listeners of the Error.
1646
0
  if (mPrt) {
1647
0
    // Note that nsPrintData::DoOnStatusChange() will call some listeners.
1648
0
    // So, mPrt can be cleared or recreated.
1649
0
    RefPtr<nsPrintData> printData = mPrt;
1650
0
    printData->DoOnStatusChange(aPrintError);
1651
0
  }
1652
0
}
1653
1654
//-----------------------------------------------------------------
1655
//-- Section: Reflow Methods
1656
//-----------------------------------------------------------------
1657
1658
nsresult
1659
nsPrintJob::ReconstructAndReflow(bool doSetPixelScale)
1660
0
{
1661
0
  if (NS_WARN_IF(!mPrt)) {
1662
0
    return NS_ERROR_FAILURE;
1663
0
  }
1664
0
1665
#if defined(XP_WIN) && defined(EXTENDED_DEBUG_PRINTING)
1666
  // We need to clear all the output files here
1667
  // because they will be re-created with second reflow of the docs
1668
  if (MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
1669
    RemoveFilesInDir(".\\");
1670
    gDumpFileNameCnt   = 0;
1671
    gDumpLOFileNameCnt = 0;
1672
  }
1673
#endif
1674
1675
0
  // In this loop, it's conceivable that one of our helpers might clear mPrt,
1676
0
  // while we're using it & its members!  So we capture it in an owning local
1677
0
  // reference & use that instead of using mPrt directly.
1678
0
  RefPtr<nsPrintData> printData = mPrt;
1679
0
  for (uint32_t i = 0; i < printData->mPrintDocList.Length(); ++i) {
1680
0
    nsPrintObject* po = printData->mPrintDocList.ElementAt(i);
1681
0
    NS_ASSERTION(po, "nsPrintObject can't be null!");
1682
0
1683
0
    if (po->mDontPrint || po->mInvisible) {
1684
0
      continue;
1685
0
    }
1686
0
1687
0
    // When the print object has been marked as "print the document" (i.e,
1688
0
    // po->mDontPrint is false), mPresContext and mPresShell should be
1689
0
    // non-nullptr (i.e., should've been created for the print) since they
1690
0
    // are necessary to print the document.
1691
0
    MOZ_ASSERT(po->mPresContext && po->mPresShell,
1692
0
      "mPresContext and mPresShell shouldn't be nullptr when the print object "
1693
0
      "has been marked as \"print the document\"");
1694
0
1695
0
    UpdateZoomRatio(po, doSetPixelScale);
1696
0
1697
0
    po->mPresContext->SetPageScale(po->mZoomRatio);
1698
0
1699
0
    // Calculate scale factor from printer to screen
1700
0
    float printDPI = float(AppUnitsPerCSSInch()) /
1701
0
                       float(printData->mPrintDC->AppUnitsPerDevPixel());
1702
0
    po->mPresContext->SetPrintPreviewScale(mScreenDPI / printDPI);
1703
0
1704
0
    po->mPresShell->ReconstructFrames();
1705
0
1706
0
    // If the printing was canceled or restarted with different data,
1707
0
    // let's stop doing this printing.
1708
0
    if (NS_WARN_IF(mPrt != printData)) {
1709
0
      return NS_ERROR_FAILURE;
1710
0
    }
1711
0
1712
0
    // For all views except the first one, setup the root view.
1713
0
    // ??? Can there be multiple po for the top-level-document?
1714
0
    bool documentIsTopLevel = true;
1715
0
    if (i != 0) {
1716
0
      nsSize adjSize;
1717
0
      bool doReturn;
1718
0
      nsresult rv = SetRootView(po, doReturn, documentIsTopLevel, adjSize);
1719
0
1720
0
      MOZ_ASSERT(!documentIsTopLevel, "How could this happen?");
1721
0
1722
0
      if (NS_FAILED(rv) || doReturn) {
1723
0
        return rv;
1724
0
      }
1725
0
    }
1726
0
1727
0
    po->mPresShell->FlushPendingNotifications(FlushType::Layout);
1728
0
1729
0
    // If the printing was canceled or restarted with different data,
1730
0
    // let's stop doing this printing.
1731
0
    if (NS_WARN_IF(mPrt != printData)) {
1732
0
      return NS_ERROR_FAILURE;
1733
0
    }
1734
0
1735
0
    nsresult rv = UpdateSelectionAndShrinkPrintObject(po, documentIsTopLevel);
1736
0
    NS_ENSURE_SUCCESS(rv, rv);
1737
0
  }
1738
0
  return NS_OK;
1739
0
}
1740
1741
//-------------------------------------------------------
1742
nsresult
1743
nsPrintJob::SetupToPrintContent()
1744
0
{
1745
0
  // This method may be called while DoCommonPrint() initializes the instance
1746
0
  // when its script blocker goes out of scope.  In such case, this cannot do
1747
0
  // its job as expected because some objects in mPrt have not been initialized
1748
0
  // yet but they are necessary.
1749
0
  // Note: it shouldn't be possible for mPrt->mPrintObject to be null; we check
1750
0
  // it for good measure (after we check its owner) before we start
1751
0
  // dereferencing it below.
1752
0
  if (NS_WARN_IF(!mPrt) ||
1753
0
      NS_WARN_IF(!mPrt->mPrintObject)) {
1754
0
    return NS_ERROR_FAILURE;
1755
0
  }
1756
0
1757
0
  // If this is creating print preview, mPrt->mPrintObject->mPresContext and
1758
0
  // mPrt->mPrintObject->mPresShell need to be non-nullptr because this cannot
1759
0
  // initialize page sequence frame without them at end of this method since
1760
0
  // page sequence frame has already been destroyed or not been created yet.
1761
0
  if (mIsCreatingPrintPreview &&
1762
0
      (NS_WARN_IF(!mPrt->mPrintObject->mPresContext) ||
1763
0
       NS_WARN_IF(!mPrt->mPrintObject->mPresShell))) {
1764
0
    return NS_ERROR_FAILURE;
1765
0
  }
1766
0
1767
0
  // If this is printing some documents (not print-previewing the documents),
1768
0
  // mPrt->mPrintObject->mPresContext and mPrt->mPrintObject->mPresShell can be
1769
0
  // nullptr only when mPrt->mPrintObject->mDontPrint is set to true.  E.g., if
1770
0
  // the document has a <frameset> element and it's printing only content in a
1771
0
  // <frame> element or all <frame> elements separately.
1772
0
  MOZ_ASSERT(
1773
0
    (!mIsCreatingPrintPreview && !mPrt->mPrintObject->IsPrintable()) ||
1774
0
    (mPrt->mPrintObject->mPresContext && mPrt->mPrintObject->mPresShell),
1775
0
    "mPresContext and mPresShell shouldn't be nullptr when printing the "
1776
0
    "document or creating print-preview");
1777
0
1778
0
  bool didReconstruction = false;
1779
0
1780
0
  // This method works with mPrt->mPrintObject.  So, we need to guarantee that
1781
0
  // it won't be deleted in this method.  We achieve this by holding a strong
1782
0
  // local reference to mPrt, which in turn keeps mPrintObject alive.
1783
0
  RefPtr<nsPrintData> printData = mPrt;
1784
0
1785
0
  // If some new content got loaded since the initial reflow rebuild
1786
0
  // everything.
1787
0
  if (mDidLoadDataForPrinting) {
1788
0
    nsresult rv = ReconstructAndReflow(DoSetPixelScale());
1789
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1790
0
      return rv;
1791
0
    }
1792
0
    // If the printing was canceled or restarted with different data,
1793
0
    // let's stop doing this printing.
1794
0
    if (NS_WARN_IF(mPrt != printData)) {
1795
0
      return NS_ERROR_FAILURE;
1796
0
    }
1797
0
    didReconstruction = true;
1798
0
  }
1799
0
1800
0
  // Here is where we figure out if extra reflow for shrinking the content
1801
0
  // is required.
1802
0
  // But skip this step if we are in PrintPreview
1803
0
  bool ppIsShrinkToFit = mPrtPreview && mPrtPreview->mShrinkToFit;
1804
0
  if (printData->mShrinkToFit && !ppIsShrinkToFit) {
1805
0
    // Now look for the PO that has the smallest percent for shrink to fit
1806
0
    if (printData->mPrintDocList.Length() > 1 &&
1807
0
        printData->mPrintObject->mFrameType == eFrameSet) {
1808
0
      nsPrintObject* smallestPO = FindSmallestSTF();
1809
0
      NS_ASSERTION(smallestPO, "There must always be an XMost PO!");
1810
0
      if (smallestPO) {
1811
0
        // Calc the shrinkage based on the entire content area
1812
0
        printData->mShrinkRatio = smallestPO->mShrinkRatio;
1813
0
      }
1814
0
    } else {
1815
0
      // Single document so use the Shrink as calculated for the PO
1816
0
      printData->mShrinkRatio = printData->mPrintObject->mShrinkRatio;
1817
0
    }
1818
0
1819
0
    if (printData->mShrinkRatio < 0.998f) {
1820
0
      nsresult rv = ReconstructAndReflow(true);
1821
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
1822
0
        return rv;
1823
0
      }
1824
0
      // If the printing was canceled or restarted with different data,
1825
0
      // let's stop doing this printing.
1826
0
      if (NS_WARN_IF(mPrt != printData)) {
1827
0
        return NS_ERROR_FAILURE;
1828
0
      }
1829
0
      didReconstruction = true;
1830
0
    }
1831
0
1832
0
    if (MOZ_LOG_TEST(gPrintingLog, LogLevel::Debug)) {
1833
0
      float calcRatio = 0.0f;
1834
0
      if (printData->mPrintDocList.Length() > 1 &&
1835
0
          printData->mPrintObject->mFrameType == eFrameSet) {
1836
0
        nsPrintObject* smallestPO = FindSmallestSTF();
1837
0
        NS_ASSERTION(smallestPO, "There must always be an XMost PO!");
1838
0
        if (smallestPO) {
1839
0
          // Calc the shrinkage based on the entire content area
1840
0
          calcRatio = smallestPO->mShrinkRatio;
1841
0
        }
1842
0
      } else {
1843
0
        // Single document so use the Shrink as calculated for the PO
1844
0
        calcRatio = printData->mPrintObject->mShrinkRatio;
1845
0
      }
1846
0
      PR_PL(("**************************************************************************\n"));
1847
0
      PR_PL(("STF Ratio is: %8.5f Effective Ratio: %8.5f Diff: %8.5f\n",
1848
0
             printData->mShrinkRatio, calcRatio,
1849
0
             printData->mShrinkRatio-calcRatio));
1850
0
      PR_PL(("**************************************************************************\n"));
1851
0
    }
1852
0
  }
1853
0
1854
0
  // If the frames got reconstructed and reflowed the number of pages might
1855
0
  // has changed.
1856
0
  if (didReconstruction) {
1857
0
    FirePrintPreviewUpdateEvent();
1858
0
    // If the printing was canceled or restarted with different data,
1859
0
    // let's stop doing this printing.
1860
0
    if (NS_WARN_IF(mPrt != printData)) {
1861
0
      return NS_ERROR_FAILURE;
1862
0
    }
1863
0
  }
1864
0
1865
0
  DUMP_DOC_LIST(("\nAfter Reflow------------------------------------------"));
1866
0
  PR_PL(("\n"));
1867
0
  PR_PL(("-------------------------------------------------------\n"));
1868
0
  PR_PL(("\n"));
1869
0
1870
0
  CalcNumPrintablePages(printData->mNumPrintablePages);
1871
0
1872
0
  PR_PL(("--- Printing %d pages\n", printData->mNumPrintablePages));
1873
0
  DUMP_DOC_TREELAYOUT;
1874
0
1875
0
  // Print listener setup...
1876
0
  printData->OnStartPrinting();
1877
0
1878
0
  // If the printing was canceled or restarted with different data,
1879
0
  // let's stop doing this printing.
1880
0
  if (NS_WARN_IF(mPrt != printData)) {
1881
0
    return NS_ERROR_FAILURE;
1882
0
  }
1883
0
1884
0
  nsAutoString fileNameStr;
1885
0
  // check to see if we are printing to a file
1886
0
  bool isPrintToFile = false;
1887
0
  printData->mPrintSettings->GetPrintToFile(&isPrintToFile);
1888
0
  if (isPrintToFile) {
1889
0
    // On some platforms The BeginDocument needs to know the name of the file.
1890
0
    printData->mPrintSettings->GetToFileName(fileNameStr);
1891
0
  }
1892
0
1893
0
  nsAutoString docTitleStr;
1894
0
  nsAutoString docURLStr;
1895
0
  GetDisplayTitleAndURL(printData->mPrintObject, docTitleStr, docURLStr,
1896
0
                        eDocTitleDefURLDoc);
1897
0
1898
0
  int32_t startPage = 1;
1899
0
  int32_t endPage = printData->mNumPrintablePages;
1900
0
1901
0
  int16_t printRangeType = nsIPrintSettings::kRangeAllPages;
1902
0
  printData->mPrintSettings->GetPrintRange(&printRangeType);
1903
0
  if (printRangeType == nsIPrintSettings::kRangeSpecifiedPageRange) {
1904
0
    printData->mPrintSettings->GetStartPageRange(&startPage);
1905
0
    printData->mPrintSettings->GetEndPageRange(&endPage);
1906
0
    if (endPage > printData->mNumPrintablePages) {
1907
0
      endPage = printData->mNumPrintablePages;
1908
0
    }
1909
0
  }
1910
0
1911
0
  nsresult rv = NS_OK;
1912
0
  // BeginDocument may pass back a FAILURE code
1913
0
  // i.e. On Windows, if you are printing to a file and hit "Cancel"
1914
0
  //      to the "File Name" dialog, this comes back as an error
1915
0
  // Don't start printing when regression test are executed
1916
0
  if (mIsDoingPrinting) {
1917
0
    rv = printData->mPrintDC->BeginDocument(docTitleStr, fileNameStr, startPage,
1918
0
                                            endPage);
1919
0
  }
1920
0
1921
0
  if (mIsCreatingPrintPreview) {
1922
0
    // Copy docTitleStr and docURLStr to the pageSequenceFrame, to be displayed
1923
0
    // in the header
1924
0
    nsIPageSequenceFrame* seqFrame =
1925
0
      printData->mPrintObject->mPresShell->GetPageSequenceFrame();
1926
0
    if (seqFrame) {
1927
0
      seqFrame->StartPrint(printData->mPrintObject->mPresContext,
1928
0
                           printData->mPrintSettings, docTitleStr, docURLStr);
1929
0
    }
1930
0
  }
1931
0
1932
0
  PR_PL(("****************** Begin Document ************************\n"));
1933
0
1934
0
  if (NS_FAILED(rv)) {
1935
0
    NS_WARNING_ASSERTION(rv == NS_ERROR_ABORT,
1936
0
                         "Failed to begin document for printing");
1937
0
    return rv;
1938
0
  }
1939
0
1940
0
  // This will print the docshell document
1941
0
  // when it completes asynchronously in the DonePrintingPages method
1942
0
  // it will check to see if there are more docshells to be printed and
1943
0
  // then PrintDocContent will be called again.
1944
0
1945
0
  if (mIsDoingPrinting) {
1946
0
    PrintDocContent(printData->mPrintObject, rv); // ignore return value
1947
0
  }
1948
0
1949
0
  return rv;
1950
0
}
1951
1952
//-------------------------------------------------------
1953
// Recursively reflow each sub-doc and then calc
1954
// all the frame locations of the sub-docs
1955
nsresult
1956
nsPrintJob::ReflowDocList(const UniquePtr<nsPrintObject>& aPO,
1957
                          bool aSetPixelScale)
1958
0
{
1959
0
  NS_ENSURE_ARG_POINTER(aPO);
1960
0
1961
0
  // Check to see if the subdocument's element has been hidden by the parent document
1962
0
  if (aPO->mParent && aPO->mParent->mPresShell) {
1963
0
    nsIFrame* frame = aPO->mContent ? aPO->mContent->GetPrimaryFrame() : nullptr;
1964
0
    if (!frame || !frame->StyleVisibility()->IsVisible()) {
1965
0
      SetPrintPO(aPO.get(), false);
1966
0
      aPO->mInvisible = true;
1967
0
      return NS_OK;
1968
0
    }
1969
0
  }
1970
0
1971
0
  UpdateZoomRatio(aPO.get(), aSetPixelScale);
1972
0
1973
0
  nsresult rv;
1974
0
  // Reflow the PO
1975
0
  rv = ReflowPrintObject(aPO);
1976
0
  NS_ENSURE_SUCCESS(rv, rv);
1977
0
1978
0
  for (const UniquePtr<nsPrintObject>& kid : aPO->mKids) {
1979
0
    rv = ReflowDocList(kid, aSetPixelScale);
1980
0
    NS_ENSURE_SUCCESS(rv, rv);
1981
0
  }
1982
0
  return NS_OK;
1983
0
}
1984
1985
void
1986
nsPrintJob::FirePrintPreviewUpdateEvent()
1987
0
{
1988
0
  // Dispatch the event only while in PrintPreview. When printing, there is no
1989
0
  // listener bound to this event and therefore no need to dispatch it.
1990
0
  if (mIsDoingPrintPreview && !mIsDoingPrinting) {
1991
0
    nsCOMPtr<nsIContentViewer> cv = do_QueryInterface(mDocViewerPrint);
1992
0
    (new AsyncEventDispatcher(
1993
0
       cv->GetDocument(), NS_LITERAL_STRING("printPreviewUpdate"),
1994
0
       CanBubble::eYes, ChromeOnlyDispatch::eYes
1995
0
    ))->RunDOMEventWhenSafe();
1996
0
  }
1997
0
}
1998
1999
nsresult
2000
nsPrintJob::InitPrintDocConstruction(bool aHandleError)
2001
0
{
2002
0
  nsresult rv;
2003
0
  // Guarantee that mPrt->mPrintObject won't be deleted.  It's owned by mPrt.
2004
0
  // So, we should grab it with local variable.
2005
0
  RefPtr<nsPrintData> printData = mPrt;
2006
0
  rv = ReflowDocList(printData->mPrintObject, DoSetPixelScale());
2007
0
  NS_ENSURE_SUCCESS(rv, rv);
2008
0
2009
0
  FirePrintPreviewUpdateEvent();
2010
0
2011
0
  if (mLoadCounter == 0) {
2012
0
    AfterNetworkPrint(aHandleError);
2013
0
  }
2014
0
  return rv;
2015
0
}
2016
2017
nsresult
2018
nsPrintJob::AfterNetworkPrint(bool aHandleError)
2019
0
{
2020
0
  // If Destroy() has already been called, mPtr is nullptr.  Then, the instance
2021
0
  // needs to do nothing anymore in this method.
2022
0
  // Note: it shouldn't be possible for mPrt->mPrintObject to be null; we
2023
0
  // just check it for good measure, as we check its owner.
2024
0
  // Note: it shouldn't be possible for mPrt->mPrintObject->mDocShell to be
2025
0
  // null; we just check it for good measure, as we check its owner.
2026
0
  if (!mPrt ||
2027
0
      NS_WARN_IF(!mPrt->mPrintObject) ||
2028
0
      NS_WARN_IF(!mPrt->mPrintObject->mDocShell)) {
2029
0
    return NS_ERROR_FAILURE;
2030
0
  }
2031
0
2032
0
  nsCOMPtr<nsIWebProgress> webProgress = do_QueryInterface(mPrt->mPrintObject->mDocShell);
2033
0
2034
0
  webProgress->RemoveProgressListener(
2035
0
    static_cast<nsIWebProgressListener*>(this));
2036
0
2037
0
  nsresult rv;
2038
0
  if (mIsDoingPrinting) {
2039
0
    rv = DocumentReadyForPrinting();
2040
0
  } else {
2041
0
    rv = FinishPrintPreview();
2042
0
  }
2043
0
2044
0
  /* cleaup on failure + notify user */
2045
0
  if (aHandleError && NS_FAILED(rv)) {
2046
0
    NS_WARNING_ASSERTION(rv == NS_ERROR_ABORT,
2047
0
                         "nsPrintJob::AfterNetworkPrint failed");
2048
0
    CleanupOnFailure(rv, !mIsDoingPrinting);
2049
0
  }
2050
0
2051
0
  return rv;
2052
0
}
2053
2054
////////////////////////////////////////////////////////////////////////////////
2055
// nsIWebProgressListener
2056
2057
NS_IMETHODIMP
2058
nsPrintJob::OnStateChange(nsIWebProgress* aWebProgress,
2059
                          nsIRequest* aRequest,
2060
                          uint32_t aStateFlags,
2061
                          nsresult aStatus)
2062
0
{
2063
0
  nsAutoCString name;
2064
0
  aRequest->GetName(name);
2065
0
  if (name.EqualsLiteral("about:document-onload-blocker")) {
2066
0
    return NS_OK;
2067
0
  }
2068
0
  if (aStateFlags & STATE_START) {
2069
0
    ++mLoadCounter;
2070
0
  } else if (aStateFlags & STATE_STOP) {
2071
0
    mDidLoadDataForPrinting = true;
2072
0
    --mLoadCounter;
2073
0
2074
0
    // If all resources are loaded, then do a small timeout and if there
2075
0
    // are still no new requests, then another reflow.
2076
0
    if (mLoadCounter == 0) {
2077
0
      AfterNetworkPrint(true);
2078
0
    }
2079
0
  }
2080
0
  return NS_OK;
2081
0
}
2082
2083
2084
NS_IMETHODIMP
2085
nsPrintJob::OnProgressChange(nsIWebProgress* aWebProgress,
2086
                             nsIRequest* aRequest,
2087
                             int32_t aCurSelfProgress,
2088
                             int32_t aMaxSelfProgress,
2089
                             int32_t aCurTotalProgress,
2090
                             int32_t aMaxTotalProgress)
2091
0
{
2092
0
  MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
2093
0
  return NS_OK;
2094
0
}
2095
2096
NS_IMETHODIMP
2097
nsPrintJob::OnLocationChange(nsIWebProgress* aWebProgress,
2098
                             nsIRequest* aRequest,
2099
                             nsIURI* aLocation,
2100
                             uint32_t aFlags)
2101
0
{
2102
0
  MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
2103
0
  return NS_OK;
2104
0
}
2105
2106
NS_IMETHODIMP
2107
nsPrintJob::OnStatusChange(nsIWebProgress* aWebProgress,
2108
                           nsIRequest* aRequest,
2109
                           nsresult aStatus,
2110
                           const char16_t* aMessage)
2111
0
{
2112
0
  MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
2113
0
  return NS_OK;
2114
0
}
2115
2116
NS_IMETHODIMP
2117
nsPrintJob::OnSecurityChange(nsIWebProgress* aWebProgress,
2118
                             nsIRequest* aRequest,
2119
                             uint32_t aState)
2120
0
{
2121
0
  MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
2122
0
  return NS_OK;
2123
0
}
2124
2125
//-------------------------------------------------------
2126
2127
void
2128
nsPrintJob::UpdateZoomRatio(nsPrintObject* aPO, bool aSetPixelScale)
2129
0
{
2130
0
  // Here is where we set the shrinkage value into the DC
2131
0
  // and this is what actually makes it shrink
2132
0
  if (aSetPixelScale && aPO->mFrameType != eIFrame) {
2133
0
    float ratio;
2134
0
    if (mPrt->mPrintFrameType == nsIPrintSettings::kFramesAsIs || mPrt->mPrintFrameType == nsIPrintSettings::kNoFrames) {
2135
0
      ratio = mPrt->mShrinkRatio - 0.005f; // round down
2136
0
    } else {
2137
0
      ratio = aPO->mShrinkRatio - 0.005f; // round down
2138
0
    }
2139
0
    aPO->mZoomRatio = ratio;
2140
0
  } else if (!mPrt->mShrinkToFit) {
2141
0
    double scaling;
2142
0
    mPrt->mPrintSettings->GetScaling(&scaling);
2143
0
    aPO->mZoomRatio = float(scaling);
2144
0
  }
2145
0
}
2146
2147
nsresult
2148
nsPrintJob::UpdateSelectionAndShrinkPrintObject(nsPrintObject* aPO,
2149
                                                bool aDocumentIsTopLevel)
2150
0
{
2151
0
  nsCOMPtr<nsIPresShell> displayShell = aPO->mDocShell->GetPresShell();
2152
0
  // Transfer Selection Ranges to the new Print PresShell
2153
0
  RefPtr<Selection> selection, selectionPS;
2154
0
  // It's okay if there is no display shell, just skip copying the selection
2155
0
  if (displayShell) {
2156
0
    selection = displayShell->GetCurrentSelection(SelectionType::eNormal);
2157
0
  }
2158
0
  selectionPS = aPO->mPresShell->GetCurrentSelection(SelectionType::eNormal);
2159
0
2160
0
  // Reset all existing selection ranges that might have been added by calling
2161
0
  // this function before.
2162
0
  if (selectionPS) {
2163
0
    selectionPS->RemoveAllRanges(IgnoreErrors());
2164
0
  }
2165
0
  if (selection && selectionPS) {
2166
0
    int32_t cnt = selection->RangeCount();
2167
0
    int32_t inx;
2168
0
    for (inx = 0; inx < cnt; ++inx) {
2169
0
      selectionPS->AddRange(*selection->GetRangeAt(inx), IgnoreErrors());
2170
0
    }
2171
0
  }
2172
0
2173
0
  // If we are trying to shrink the contents to fit on the page
2174
0
  // we must first locate the "pageContent" frame
2175
0
  // Then we walk the frame tree and look for the "xmost" frame
2176
0
  // this is the frame where the right-hand side of the frame extends
2177
0
  // the furthest
2178
0
  if (mPrt->mShrinkToFit && aDocumentIsTopLevel) {
2179
0
    nsIPageSequenceFrame* pageSequence = aPO->mPresShell->GetPageSequenceFrame();
2180
0
    NS_ENSURE_STATE(pageSequence);
2181
0
    pageSequence->GetSTFPercent(aPO->mShrinkRatio);
2182
0
    // Limit the shrink-to-fit scaling for some text-ish type of documents.
2183
0
    nsAutoString contentType;
2184
0
    aPO->mPresShell->GetDocument()->GetContentType(contentType);
2185
0
    if (contentType.EqualsLiteral("application/xhtml+xml") ||
2186
0
        StringBeginsWith(contentType, NS_LITERAL_STRING("text/"))) {
2187
0
      int32_t limitPercent =
2188
0
        Preferences::GetInt("print.shrink-to-fit.scale-limit-percent", 20);
2189
0
      limitPercent = std::max(0, limitPercent);
2190
0
      limitPercent = std::min(100, limitPercent);
2191
0
      float minShrinkRatio = float(limitPercent) / 100;
2192
0
      aPO->mShrinkRatio = std::max(aPO->mShrinkRatio, minShrinkRatio);
2193
0
    }
2194
0
  }
2195
0
  return NS_OK;
2196
0
}
2197
2198
bool
2199
nsPrintJob::DoSetPixelScale()
2200
0
{
2201
0
  // This is an Optimization
2202
0
  // If we are in PP then we already know all the shrinkage information
2203
0
  // so just transfer it to the PrintData and we will skip the extra shrinkage reflow
2204
0
  //
2205
0
  // doSetPixelScale tells Reflow whether to set the shrinkage value into the DC
2206
0
  // The first time we do not want to do this, the second time through we do
2207
0
  bool doSetPixelScale = false;
2208
0
  bool ppIsShrinkToFit = mPrtPreview && mPrtPreview->mShrinkToFit;
2209
0
  if (ppIsShrinkToFit) {
2210
0
    mPrt->mShrinkRatio = mPrtPreview->mShrinkRatio;
2211
0
    doSetPixelScale = true;
2212
0
  }
2213
0
  return doSetPixelScale;
2214
0
}
2215
2216
nsView*
2217
nsPrintJob::GetParentViewForRoot()
2218
0
{
2219
0
  if (mIsCreatingPrintPreview) {
2220
0
    nsCOMPtr<nsIContentViewer> cv = do_QueryInterface(mDocViewerPrint);
2221
0
    if (cv) {
2222
0
      return cv->FindContainerView();
2223
0
    }
2224
0
  }
2225
0
  return nullptr;
2226
0
}
2227
2228
nsresult
2229
nsPrintJob::SetRootView(nsPrintObject* aPO,
2230
                        bool& doReturn,
2231
                        bool& documentIsTopLevel,
2232
                        nsSize& adjSize)
2233
0
{
2234
0
  bool canCreateScrollbars = true;
2235
0
2236
0
  nsView* rootView;
2237
0
  nsView* parentView = nullptr;
2238
0
2239
0
  doReturn = false;
2240
0
2241
0
  if (aPO->mParent && aPO->mParent->IsPrintable()) {
2242
0
    nsIFrame* frame = aPO->mContent ? aPO->mContent->GetPrimaryFrame() : nullptr;
2243
0
    // Without a frame, this document can't be displayed; therefore, there is no
2244
0
    // point to reflowing it
2245
0
    if (!frame) {
2246
0
      SetPrintPO(aPO, false);
2247
0
      doReturn = true;
2248
0
      return NS_OK;
2249
0
    }
2250
0
2251
0
    //XXX If printing supported printing document hierarchies with non-constant
2252
0
    // zoom this would be wrong as we use the same mPrt->mPrintDC for all
2253
0
    // subdocuments.
2254
0
    adjSize = frame->GetContentRect().Size();
2255
0
    documentIsTopLevel = false;
2256
0
    // presshell exists because parent is printable
2257
0
2258
0
    // the top nsPrintObject's widget will always have scrollbars
2259
0
    if (frame && frame->IsSubDocumentFrame()) {
2260
0
      nsView* view = frame->GetView();
2261
0
      NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
2262
0
      view = view->GetFirstChild();
2263
0
      NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
2264
0
      parentView = view;
2265
0
      canCreateScrollbars = false;
2266
0
    }
2267
0
  } else {
2268
0
    nscoord pageWidth, pageHeight;
2269
0
    mPrt->mPrintDC->GetDeviceSurfaceDimensions(pageWidth, pageHeight);
2270
0
    adjSize = nsSize(pageWidth, pageHeight);
2271
0
    documentIsTopLevel = true;
2272
0
    parentView = GetParentViewForRoot();
2273
0
  }
2274
0
2275
0
  if (aPO->mViewManager->GetRootView()) {
2276
0
    // Reuse the root view that is already on the root frame.
2277
0
    rootView = aPO->mViewManager->GetRootView();
2278
0
    // Remove it from its existing parent if necessary
2279
0
    aPO->mViewManager->RemoveChild(rootView);
2280
0
    rootView->SetParent(parentView);
2281
0
  } else {
2282
0
    // Create a child window of the parent that is our "root view/window"
2283
0
    nsRect tbounds = nsRect(nsPoint(0, 0), adjSize);
2284
0
    rootView = aPO->mViewManager->CreateView(tbounds, parentView);
2285
0
    NS_ENSURE_TRUE(rootView, NS_ERROR_OUT_OF_MEMORY);
2286
0
  }
2287
0
2288
0
  if (mIsCreatingPrintPreview && documentIsTopLevel) {
2289
0
    aPO->mPresContext->SetPaginatedScrolling(canCreateScrollbars);
2290
0
  }
2291
0
2292
0
  // Setup hierarchical relationship in view manager
2293
0
  aPO->mViewManager->SetRootView(rootView);
2294
0
2295
0
  return NS_OK;
2296
0
}
2297
2298
// Reflow a nsPrintObject
2299
nsresult
2300
nsPrintJob::ReflowPrintObject(const UniquePtr<nsPrintObject>& aPO)
2301
0
{
2302
0
  NS_ENSURE_STATE(aPO);
2303
0
2304
0
  if (!aPO->IsPrintable()) {
2305
0
    return NS_OK;
2306
0
  }
2307
0
2308
0
  NS_ASSERTION(!aPO->mPresContext, "Recreating prescontext");
2309
0
2310
0
  // Guarantee that mPrt and the objects it owns won't be deleted in this method
2311
0
  // because it might be cleared if other modules called from here may fire
2312
0
  // events, notifying observers and/or listeners.
2313
0
  RefPtr<nsPrintData> printData = mPrt;
2314
0
2315
0
  // create the PresContext
2316
0
  nsPresContext::nsPresContextType type =
2317
0
      mIsCreatingPrintPreview ? nsPresContext::eContext_PrintPreview:
2318
0
                                nsPresContext::eContext_Print;
2319
0
  nsView* parentView =
2320
0
    aPO->mParent && aPO->mParent->IsPrintable() ? nullptr : GetParentViewForRoot();
2321
0
  aPO->mPresContext = parentView ?
2322
0
      new nsPresContext(aPO->mDocument, type) :
2323
0
      new nsRootPresContext(aPO->mDocument, type);
2324
0
  NS_ENSURE_TRUE(aPO->mPresContext, NS_ERROR_OUT_OF_MEMORY);
2325
0
  aPO->mPresContext->SetPrintSettings(printData->mPrintSettings);
2326
0
2327
0
  // set the presentation context to the value in the print settings
2328
0
  bool printBGColors;
2329
0
  printData->mPrintSettings->GetPrintBGColors(&printBGColors);
2330
0
  aPO->mPresContext->SetBackgroundColorDraw(printBGColors);
2331
0
  printData->mPrintSettings->GetPrintBGImages(&printBGColors);
2332
0
  aPO->mPresContext->SetBackgroundImageDraw(printBGColors);
2333
0
2334
0
  // init it with the DC
2335
0
  nsresult rv = aPO->mPresContext->Init(printData->mPrintDC);
2336
0
  NS_ENSURE_SUCCESS(rv, rv);
2337
0
2338
0
  aPO->mViewManager = new nsViewManager();
2339
0
2340
0
  rv = aPO->mViewManager->Init(printData->mPrintDC);
2341
0
  NS_ENSURE_SUCCESS(rv,rv);
2342
0
2343
0
  UniquePtr<ServoStyleSet> styleSet =
2344
0
    mDocViewerPrint->CreateStyleSet(aPO->mDocument);
2345
0
2346
0
  aPO->mPresShell = aPO->mDocument->CreateShell(aPO->mPresContext,
2347
0
                                                aPO->mViewManager,
2348
0
                                                std::move(styleSet));
2349
0
  if (!aPO->mPresShell) {
2350
0
    return NS_ERROR_FAILURE;
2351
0
  }
2352
0
2353
0
  // If we're printing selection then remove the unselected nodes from our
2354
0
  // cloned document.
2355
0
  int16_t printRangeType = nsIPrintSettings::kRangeAllPages;
2356
0
  printData->mPrintSettings->GetPrintRange(&printRangeType);
2357
0
  if (printRangeType == nsIPrintSettings::kRangeSelection) {
2358
0
    DeleteUnselectedNodes(aPO->mDocument->GetOriginalDocument(), aPO->mDocument);
2359
0
  }
2360
0
2361
0
  // The pres shell now owns the style set object.
2362
0
2363
0
  bool doReturn = false;;
2364
0
  bool documentIsTopLevel = false;
2365
0
  nsSize adjSize;
2366
0
2367
0
  rv = SetRootView(aPO.get(), doReturn, documentIsTopLevel, adjSize);
2368
0
2369
0
  if (NS_FAILED(rv) || doReturn) {
2370
0
    return rv;
2371
0
  }
2372
0
2373
0
  PR_PL(("In DV::ReflowPrintObject PO: %p pS: %p (%9s) Setting w,h to %d,%d\n",
2374
0
         aPO.get(), aPO->mPresShell.get(),
2375
0
         gFrameTypesStr[aPO->mFrameType], adjSize.width, adjSize.height));
2376
0
2377
0
2378
0
  // This docshell stuff is weird; will go away when we stop having multiple
2379
0
  // presentations per document
2380
0
  aPO->mPresContext->SetContainer(aPO->mDocShell);
2381
0
2382
0
  aPO->mPresShell->BeginObservingDocument();
2383
0
2384
0
  aPO->mPresContext->SetPageSize(adjSize);
2385
0
  aPO->mPresContext->SetVisibleArea(nsRect(0, 0, adjSize.width, adjSize.height));
2386
0
  aPO->mPresContext->SetIsRootPaginatedDocument(documentIsTopLevel);
2387
0
  aPO->mPresContext->SetPageScale(aPO->mZoomRatio);
2388
0
  // Calculate scale factor from printer to screen
2389
0
  float printDPI = float(AppUnitsPerCSSInch()) /
2390
0
                     float(printData->mPrintDC->AppUnitsPerDevPixel());
2391
0
  aPO->mPresContext->SetPrintPreviewScale(mScreenDPI / printDPI);
2392
0
2393
0
  if (mIsCreatingPrintPreview && documentIsTopLevel) {
2394
0
    mDocViewerPrint->SetPrintPreviewPresentation(aPO->mViewManager,
2395
0
                                                 aPO->mPresContext,
2396
0
                                                 aPO->mPresShell);
2397
0
  }
2398
0
2399
0
  rv = aPO->mPresShell->Initialize();
2400
0
2401
0
  NS_ENSURE_SUCCESS(rv, rv);
2402
0
  NS_ASSERTION(aPO->mPresShell, "Presshell should still be here");
2403
0
2404
0
  // Process the reflow event Initialize posted
2405
0
  aPO->mPresShell->FlushPendingNotifications(FlushType::Layout);
2406
0
2407
0
  rv = UpdateSelectionAndShrinkPrintObject(aPO.get(), documentIsTopLevel);
2408
0
  NS_ENSURE_SUCCESS(rv, rv);
2409
0
2410
#ifdef EXTENDED_DEBUG_PRINTING
2411
    if (MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
2412
      nsAutoCString docStr;
2413
      nsAutoCString urlStr;
2414
      GetDocTitleAndURL(aPO, docStr, urlStr);
2415
      char filename[256];
2416
      sprintf(filename, "print_dump_%d.txt", gDumpFileNameCnt++);
2417
      // Dump all the frames and view to a a file
2418
      FILE * fd = fopen(filename, "w");
2419
      if (fd) {
2420
        nsIFrame *theRootFrame =
2421
          aPO->mPresShell->GetRootFrame();
2422
        fprintf(fd, "Title: %s\n", docStr.get());
2423
        fprintf(fd, "URL:   %s\n", urlStr.get());
2424
        fprintf(fd, "--------------- Frames ----------------\n");
2425
        //RefPtr<gfxContext> renderingContext =
2426
        //  printData->mPrintDocDC->CreateRenderingContext();
2427
        RootFrameList(aPO->mPresContext, fd, 0);
2428
        //DumpFrames(fd, aPO->mPresContext, renderingContext, theRootFrame, 0);
2429
        fprintf(fd, "---------------------------------------\n\n");
2430
        fprintf(fd, "--------------- Views From Root Frame----------------\n");
2431
        nsView* v = theRootFrame->GetView();
2432
        if (v) {
2433
          v->List(fd);
2434
        } else {
2435
          printf("View is null!\n");
2436
        }
2437
        if (aPO->mDocShell) {
2438
          fprintf(fd, "--------------- All Views ----------------\n");
2439
          DumpViews(aPO->mDocShell, fd);
2440
          fprintf(fd, "---------------------------------------\n\n");
2441
        }
2442
        fclose(fd);
2443
      }
2444
    }
2445
#endif
2446
2447
0
  return NS_OK;
2448
0
}
2449
2450
//-------------------------------------------------------
2451
// Figure out how many documents and how many total pages we are printing
2452
void
2453
nsPrintJob::CalcNumPrintablePages(int32_t& aNumPages)
2454
0
{
2455
0
  aNumPages = 0;
2456
0
  // Count the number of printable documents
2457
0
  // and printable pages
2458
0
  for (uint32_t i=0; i<mPrt->mPrintDocList.Length(); i++) {
2459
0
    nsPrintObject* po = mPrt->mPrintDocList.ElementAt(i);
2460
0
    NS_ASSERTION(po, "nsPrintObject can't be null!");
2461
0
    // Note: The po->mPresContext null-check below is necessary, because it's
2462
0
    // possible po->mPresContext might never have been set.  (e.g., if
2463
0
    // IsPrintable() returns false, ReflowPrintObject bails before setting
2464
0
    // mPresContext)
2465
0
    if (po->mPresContext && po->mPresContext->IsRootPaginatedDocument()) {
2466
0
      nsIPageSequenceFrame* pageSequence = po->mPresShell->GetPageSequenceFrame();
2467
0
      nsIFrame * seqFrame = do_QueryFrame(pageSequence);
2468
0
      if (seqFrame) {
2469
0
        aNumPages += seqFrame->PrincipalChildList().GetLength();
2470
0
      }
2471
0
    }
2472
0
  }
2473
0
}
2474
2475
//-----------------------------------------------------------------
2476
//-- Done: Reflow Methods
2477
//-----------------------------------------------------------------
2478
2479
//-----------------------------------------------------------------
2480
//-- Section: Printing Methods
2481
//-----------------------------------------------------------------
2482
2483
//-------------------------------------------------------
2484
// Called for each DocShell that needs to be printed
2485
bool
2486
nsPrintJob::PrintDocContent(const UniquePtr<nsPrintObject>& aPO,
2487
                            nsresult& aStatus)
2488
0
{
2489
0
  NS_ASSERTION(aPO, "Pointer is null!");
2490
0
  aStatus = NS_OK;
2491
0
2492
0
  if (!aPO->mHasBeenPrinted && aPO->IsPrintable()) {
2493
0
    aStatus = DoPrint(aPO);
2494
0
    return true;
2495
0
  }
2496
0
2497
0
  // If |aPO->mPrintAsIs| and |aPO->mHasBeenPrinted| are true,
2498
0
  // the kids frames are already processed in |PrintPage|.
2499
0
  if (!aPO->mInvisible && !(aPO->mPrintAsIs && aPO->mHasBeenPrinted)) {
2500
0
    for (const UniquePtr<nsPrintObject>& po : aPO->mKids) {
2501
0
      bool printed = PrintDocContent(po, aStatus);
2502
0
      if (printed || NS_FAILED(aStatus)) {
2503
0
        return true;
2504
0
      }
2505
0
    }
2506
0
  }
2507
0
  return false;
2508
0
}
2509
2510
static nsINode*
2511
GetCorrespondingNodeInDocument(const nsINode* aNode, nsIDocument* aDoc)
2512
0
{
2513
0
  MOZ_ASSERT(aNode);
2514
0
  MOZ_ASSERT(aDoc);
2515
0
2516
0
  // Selections in anonymous subtrees aren't supported.
2517
0
  if (aNode->IsInAnonymousSubtree()) {
2518
0
    return nullptr;
2519
0
  }
2520
0
2521
0
  nsTArray<int32_t> indexArray;
2522
0
  const nsINode* child = aNode;
2523
0
  while (const nsINode* parent = child->GetParentNode()) {
2524
0
    int32_t index = parent->ComputeIndexOf(child);
2525
0
    MOZ_ASSERT(index >= 0);
2526
0
    indexArray.AppendElement(index);
2527
0
    child = parent;
2528
0
  }
2529
0
  MOZ_ASSERT(child->IsDocument());
2530
0
2531
0
  nsINode* correspondingNode = aDoc;
2532
0
  for (int32_t i = indexArray.Length() - 1; i >= 0; --i) {
2533
0
    correspondingNode = correspondingNode->GetChildAt_Deprecated(indexArray[i]);
2534
0
    NS_ENSURE_TRUE(correspondingNode, nullptr);
2535
0
  }
2536
0
2537
0
  return correspondingNode;
2538
0
}
2539
2540
static NS_NAMED_LITERAL_STRING(kEllipsis, u"\x2026");
2541
2542
static nsresult
2543
DeleteUnselectedNodes(nsIDocument* aOrigDoc, nsIDocument* aDoc)
2544
0
{
2545
0
  nsIPresShell* origShell = aOrigDoc->GetShell();
2546
0
  nsIPresShell* shell = aDoc->GetShell();
2547
0
  NS_ENSURE_STATE(origShell && shell);
2548
0
2549
0
  RefPtr<Selection> origSelection =
2550
0
    origShell->GetCurrentSelection(SelectionType::eNormal);
2551
0
  RefPtr<Selection> selection =
2552
0
    shell->GetCurrentSelection(SelectionType::eNormal);
2553
0
  NS_ENSURE_STATE(origSelection && selection);
2554
0
2555
0
  nsINode* bodyNode = aDoc->GetBodyElement();
2556
0
  nsINode* startNode = bodyNode;
2557
0
  uint32_t startOffset = 0;
2558
0
  uint32_t ellipsisOffset = 0;
2559
0
2560
0
  int32_t rangeCount = origSelection->RangeCount();
2561
0
  for (int32_t i = 0; i < rangeCount; ++i) {
2562
0
    nsRange* origRange = origSelection->GetRangeAt(i);
2563
0
2564
0
    // New end is start of original range.
2565
0
    nsINode* endNode =
2566
0
      GetCorrespondingNodeInDocument(origRange->GetStartContainer(), aDoc);
2567
0
2568
0
    // If we're no longer in the same text node reset the ellipsis offset.
2569
0
    if (endNode != startNode) {
2570
0
      ellipsisOffset = 0;
2571
0
    }
2572
0
    uint32_t endOffset = origRange->StartOffset() + ellipsisOffset;
2573
0
2574
0
    // Create the range that we want to remove. Note that if startNode or
2575
0
    // endNode are null CreateRange will fail and we won't remove that section.
2576
0
    RefPtr<nsRange> range;
2577
0
    nsresult rv = nsRange::CreateRange(startNode, startOffset, endNode,
2578
0
                                       endOffset, getter_AddRefs(range));
2579
0
2580
0
    if (NS_SUCCEEDED(rv) && !range->Collapsed()) {
2581
0
      selection->AddRange(*range, IgnoreErrors());
2582
0
2583
0
      // Unless we've already added an ellipsis at the start, if we ended mid
2584
0
      // text node then add ellipsis.
2585
0
      Text* text = endNode->GetAsText();
2586
0
      if (!ellipsisOffset && text && endOffset && endOffset < text->Length()) {
2587
0
        text->InsertData(endOffset, kEllipsis, IgnoreErrors());
2588
0
        ellipsisOffset += kEllipsis.Length();
2589
0
      }
2590
0
    }
2591
0
2592
0
    // Next new start is end of original range.
2593
0
    startNode =
2594
0
      GetCorrespondingNodeInDocument(origRange->GetEndContainer(), aDoc);
2595
0
2596
0
    // If we're no longer in the same text node reset the ellipsis offset.
2597
0
    if (startNode != endNode) {
2598
0
      ellipsisOffset = 0;
2599
0
    }
2600
0
    startOffset = origRange->EndOffset() + ellipsisOffset;
2601
0
2602
0
    // If the next node will start mid text node then add ellipsis.
2603
0
    Text* text = startNode ? startNode->GetAsText() : nullptr;
2604
0
    if (text && startOffset && startOffset < text->Length()) {
2605
0
      text->InsertData(startOffset, kEllipsis, IgnoreErrors());
2606
0
      startOffset += kEllipsis.Length();
2607
0
      ellipsisOffset += kEllipsis.Length();
2608
0
    }
2609
0
  }
2610
0
2611
0
  // Add in the last range to the end of the body.
2612
0
  RefPtr<nsRange> lastRange;
2613
0
  nsresult rv = nsRange::CreateRange(startNode, startOffset, bodyNode,
2614
0
                                     bodyNode->GetChildCount(),
2615
0
                                     getter_AddRefs(lastRange));
2616
0
  if (NS_SUCCEEDED(rv) && !lastRange->Collapsed()) {
2617
0
    selection->AddRange(*lastRange, IgnoreErrors());
2618
0
  }
2619
0
2620
0
  selection->DeleteFromDocument(IgnoreErrors());
2621
0
  return NS_OK;
2622
0
}
2623
2624
//-------------------------------------------------------
2625
nsresult
2626
nsPrintJob::DoPrint(const UniquePtr<nsPrintObject>& aPO)
2627
0
{
2628
0
  PR_PL(("\n"));
2629
0
  PR_PL(("**************************** %s ****************************\n", gFrameTypesStr[aPO->mFrameType]));
2630
0
  PR_PL(("****** In DV::DoPrint   PO: %p \n", aPO.get()));
2631
0
2632
0
  nsIPresShell*   poPresShell   = aPO->mPresShell;
2633
0
  nsPresContext*  poPresContext = aPO->mPresContext;
2634
0
2635
0
  NS_ASSERTION(poPresContext, "PrintObject has not been reflowed");
2636
0
  NS_ASSERTION(poPresContext->Type() != nsPresContext::eContext_PrintPreview,
2637
0
               "How did this context end up here?");
2638
0
2639
0
  // Guarantee that mPrt and the objects it owns won't be deleted in this method
2640
0
  // because it might be cleared if other modules called from here may fire
2641
0
  // events, notifying observers and/or listeners.
2642
0
  RefPtr<nsPrintData> printData = mPrt;
2643
0
2644
0
  if (printData->mPrintProgressParams) {
2645
0
    SetDocAndURLIntoProgress(aPO, printData->mPrintProgressParams);
2646
0
  }
2647
0
2648
0
  {
2649
0
    // Ask the page sequence frame to print all the pages
2650
0
    nsIPageSequenceFrame* pageSequence = poPresShell->GetPageSequenceFrame();
2651
0
    NS_ASSERTION(nullptr != pageSequence, "no page sequence frame");
2652
0
2653
0
    // We are done preparing for printing, so we can turn this off
2654
0
    printData->mPreparingForPrint = false;
2655
0
2656
#ifdef EXTENDED_DEBUG_PRINTING
2657
    nsIFrame* rootFrame = poPresShell->GetRootFrame();
2658
    if (aPO->IsPrintable()) {
2659
      nsAutoCString docStr;
2660
      nsAutoCString urlStr;
2661
      GetDocTitleAndURL(aPO, docStr, urlStr);
2662
      DumpLayoutData(docStr.get(), urlStr.get(), poPresContext,
2663
                     printData->mPrintDC, rootFrame, aPO->mDocShell, nullptr);
2664
    }
2665
#endif
2666
2667
0
    if (!printData->mPrintSettings) {
2668
0
      // not sure what to do here!
2669
0
      SetIsPrinting(false);
2670
0
      return NS_ERROR_FAILURE;
2671
0
    }
2672
0
2673
0
    nsAutoString docTitleStr;
2674
0
    nsAutoString docURLStr;
2675
0
    GetDisplayTitleAndURL(aPO, docTitleStr, docURLStr, eDocTitleDefBlank);
2676
0
2677
0
    nsIFrame * seqFrame = do_QueryFrame(pageSequence);
2678
0
    if (!seqFrame) {
2679
0
      SetIsPrinting(false);
2680
0
      return NS_ERROR_FAILURE;
2681
0
    }
2682
0
2683
0
    mPageSeqFrame = seqFrame;
2684
0
    pageSequence->StartPrint(poPresContext, printData->mPrintSettings,
2685
0
                             docTitleStr, docURLStr);
2686
0
2687
0
    // Schedule Page to Print
2688
0
    PR_PL(("Scheduling Print of PO: %p (%s) \n", aPO.get(), gFrameTypesStr[aPO->mFrameType]));
2689
0
    StartPagePrintTimer(aPO);
2690
0
  }
2691
0
2692
0
  return NS_OK;
2693
0
}
2694
2695
//---------------------------------------------------------------------
2696
void
2697
nsPrintJob::SetDocAndURLIntoProgress(const UniquePtr<nsPrintObject>& aPO,
2698
                                     nsIPrintProgressParams* aParams)
2699
0
{
2700
0
  NS_ASSERTION(aPO, "Must have valid nsPrintObject");
2701
0
  NS_ASSERTION(aParams, "Must have valid nsIPrintProgressParams");
2702
0
2703
0
  if (!aPO || !aPO->mDocShell || !aParams) {
2704
0
    return;
2705
0
  }
2706
0
  const uint32_t kTitleLength = 64;
2707
0
2708
0
  nsAutoString docTitleStr;
2709
0
  nsAutoString docURLStr;
2710
0
  GetDisplayTitleAndURL(aPO, docTitleStr, docURLStr, eDocTitleDefURLDoc);
2711
0
2712
0
  // Make sure the Titles & URLS don't get too long for the progress dialog
2713
0
  EllipseLongString(docTitleStr, kTitleLength, false);
2714
0
  EllipseLongString(docURLStr, kTitleLength, true);
2715
0
2716
0
  aParams->SetDocTitle(docTitleStr);
2717
0
  aParams->SetDocURL(docURLStr);
2718
0
}
2719
2720
//---------------------------------------------------------------------
2721
void
2722
nsPrintJob::EllipseLongString(nsAString& aStr, const uint32_t aLen, bool aDoFront)
2723
0
{
2724
0
  // Make sure the URLS don't get too long for the progress dialog
2725
0
  if (aLen >= 3 && aStr.Length() > aLen) {
2726
0
    if (aDoFront) {
2727
0
      nsAutoString newStr;
2728
0
      newStr.AppendLiteral("...");
2729
0
      newStr += Substring(aStr, aStr.Length() - (aLen - 3), aLen - 3);
2730
0
      aStr = newStr;
2731
0
    } else {
2732
0
      aStr.SetLength(aLen - 3);
2733
0
      aStr.AppendLiteral("...");
2734
0
    }
2735
0
  }
2736
0
}
2737
2738
static bool
2739
DocHasPrintCallbackCanvas(nsIDocument* aDoc, void* aData)
2740
0
{
2741
0
  if (!aDoc) {
2742
0
    return true;
2743
0
  }
2744
0
  Element* root = aDoc->GetRootElement();
2745
0
  if (!root) {
2746
0
    return true;
2747
0
  }
2748
0
  RefPtr<nsContentList> canvases = NS_GetContentList(root,
2749
0
                                                       kNameSpaceID_XHTML,
2750
0
                                                       NS_LITERAL_STRING("canvas"));
2751
0
  uint32_t canvasCount = canvases->Length(true);
2752
0
  for (uint32_t i = 0; i < canvasCount; ++i) {
2753
0
    HTMLCanvasElement* canvas = HTMLCanvasElement::FromNodeOrNull(canvases->Item(i, false));
2754
0
    if (canvas && canvas->GetMozPrintCallback()) {
2755
0
      // This subdocument has a print callback. Set result and return false to
2756
0
      // stop iteration.
2757
0
      *static_cast<bool*>(aData) = true;
2758
0
      return false;
2759
0
    }
2760
0
  }
2761
0
  return true;
2762
0
}
2763
2764
static bool
2765
DocHasPrintCallbackCanvas(nsIDocument* aDoc)
2766
0
{
2767
0
  bool result = false;
2768
0
  aDoc->EnumerateSubDocuments(&DocHasPrintCallbackCanvas, static_cast<void*>(&result));
2769
0
  return result;
2770
0
}
2771
2772
/**
2773
 * Checks to see if the document this print engine is associated with has any
2774
 * canvases that have a mozPrintCallback.
2775
 * https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement#Properties
2776
 */
2777
bool
2778
nsPrintJob::HasPrintCallbackCanvas()
2779
0
{
2780
0
  if (!mDocument) {
2781
0
    return false;
2782
0
  }
2783
0
  // First check this mDocument.
2784
0
  bool result = false;
2785
0
  DocHasPrintCallbackCanvas(mDocument, static_cast<void*>(&result));
2786
0
  // Also check the sub documents.
2787
0
  return result || DocHasPrintCallbackCanvas(mDocument);
2788
0
}
2789
2790
//-------------------------------------------------------
2791
bool
2792
nsPrintJob::PrePrintPage()
2793
0
{
2794
0
  NS_ASSERTION(mPageSeqFrame.IsAlive(), "mPageSeqFrame is not alive!");
2795
0
  NS_ASSERTION(mPrt,           "mPrt is null!");
2796
0
2797
0
  // Although these should NEVER be nullptr
2798
0
  // This is added insurance, to make sure we don't crash in optimized builds
2799
0
  if (!mPrt || !mPageSeqFrame.IsAlive()) {
2800
0
    return true; // means we are done preparing the page.
2801
0
  }
2802
0
2803
0
  // Guarantee that mPrt won't be deleted during a call of
2804
0
  // FirePrintingErrorEvent().
2805
0
  RefPtr<nsPrintData> printData = mPrt;
2806
0
2807
0
  // Check setting to see if someone request it be cancelled
2808
0
  bool isCancelled = false;
2809
0
  printData->mPrintSettings->GetIsCancelled(&isCancelled);
2810
0
  if (isCancelled)
2811
0
    return true;
2812
0
2813
0
  // Ask mPageSeqFrame if the page is ready to be printed.
2814
0
  // If the page doesn't get printed at all, the |done| will be |true|.
2815
0
  bool done = false;
2816
0
  nsIPageSequenceFrame* pageSeqFrame = do_QueryFrame(mPageSeqFrame.GetFrame());
2817
0
  nsresult rv = pageSeqFrame->PrePrintNextPage(mPagePrintTimer, &done);
2818
0
  if (NS_FAILED(rv)) {
2819
0
    // ??? ::PrintPage doesn't set |printData->mIsAborted = true| if
2820
0
    // rv != NS_ERROR_ABORT, but I don't really understand why this should be
2821
0
    // the right thing to do?  Shouldn't |printData->mIsAborted| set to true
2822
0
    // all the time if something went wrong?
2823
0
    if (rv != NS_ERROR_ABORT) {
2824
0
      FirePrintingErrorEvent(rv);
2825
0
      printData->mIsAborted = true;
2826
0
    }
2827
0
    done = true;
2828
0
  }
2829
0
  return done;
2830
0
}
2831
2832
bool
2833
nsPrintJob::PrintPage(nsPrintObject* aPO,
2834
                      bool& aInRange)
2835
0
{
2836
0
  NS_ASSERTION(aPO,            "aPO is null!");
2837
0
  NS_ASSERTION(mPageSeqFrame.IsAlive(), "mPageSeqFrame is not alive!");
2838
0
  NS_ASSERTION(mPrt,           "mPrt is null!");
2839
0
2840
0
  // Although these should NEVER be nullptr
2841
0
  // This is added insurance, to make sure we don't crash in optimized builds
2842
0
  if (!mPrt || !aPO || !mPageSeqFrame.IsAlive()) {
2843
0
    FirePrintingErrorEvent(NS_ERROR_FAILURE);
2844
0
    return true; // means we are done printing
2845
0
  }
2846
0
2847
0
  // Guarantee that mPrt won't be deleted during a call of
2848
0
  // nsPrintData::DoOnProgressChange() which runs some listeners,
2849
0
  // which may clear (& might otherwise destroy).
2850
0
  RefPtr<nsPrintData> printData = mPrt;
2851
0
2852
0
  PR_PL(("-----------------------------------\n"));
2853
0
  PR_PL(("------ In DV::PrintPage PO: %p (%s)\n", aPO, gFrameTypesStr[aPO->mFrameType]));
2854
0
2855
0
  // Check setting to see if someone request it be cancelled
2856
0
  bool isCancelled = false;
2857
0
  printData->mPrintSettings->GetIsCancelled(&isCancelled);
2858
0
  if (isCancelled || printData->mIsAborted) {
2859
0
    return true;
2860
0
  }
2861
0
2862
0
  int32_t pageNum, numPages, endPage;
2863
0
  nsIPageSequenceFrame* pageSeqFrame = do_QueryFrame(mPageSeqFrame.GetFrame());
2864
0
  pageSeqFrame->GetCurrentPageNum(&pageNum);
2865
0
  pageSeqFrame->GetNumPages(&numPages);
2866
0
2867
0
  bool donePrinting;
2868
0
  bool isDoingPrintRange;
2869
0
  pageSeqFrame->IsDoingPrintRange(&isDoingPrintRange);
2870
0
  if (isDoingPrintRange) {
2871
0
    int32_t fromPage;
2872
0
    int32_t toPage;
2873
0
    pageSeqFrame->GetPrintRange(&fromPage, &toPage);
2874
0
2875
0
    if (fromPage > numPages) {
2876
0
      return true;
2877
0
    }
2878
0
    if (toPage > numPages) {
2879
0
      toPage = numPages;
2880
0
    }
2881
0
2882
0
    PR_PL(("****** Printing Page %d printing from %d to page %d\n", pageNum, fromPage, toPage));
2883
0
2884
0
    donePrinting = pageNum >= toPage;
2885
0
    aInRange = pageNum >= fromPage && pageNum <= toPage;
2886
0
    endPage = (toPage - fromPage)+1;
2887
0
  } else {
2888
0
    PR_PL(("****** Printing Page %d of %d page(s)\n", pageNum, numPages));
2889
0
2890
0
    donePrinting = pageNum >= numPages;
2891
0
    endPage = numPages;
2892
0
    aInRange = true;
2893
0
  }
2894
0
2895
0
  // XXX This is wrong, but the actual behavior in the presence of a print
2896
0
  // range sucks.
2897
0
  if (printData->mPrintFrameType == nsIPrintSettings::kEachFrameSep) {
2898
0
    endPage = printData->mNumPrintablePages;
2899
0
  }
2900
0
2901
0
  printData->DoOnProgressChange(++printData->mNumPagesPrinted,
2902
0
                                endPage, false, 0);
2903
0
  if (NS_WARN_IF(mPrt != printData)) {
2904
0
    // If current printing is canceled or new print is started, let's return
2905
0
    // true to notify the caller of current printing is done.
2906
0
    return true;
2907
0
  }
2908
0
2909
0
  if (XRE_IsParentProcess() && !printData->mPrintDC->IsSyncPagePrinting()) {
2910
0
    mPagePrintTimer->WaitForRemotePrint();
2911
0
  }
2912
0
2913
0
  // Print the Page
2914
0
  // if a print job was cancelled externally, an EndPage or BeginPage may
2915
0
  // fail and the failure is passed back here.
2916
0
  // Returning true means we are done printing.
2917
0
  //
2918
0
  // When rv == NS_ERROR_ABORT, it means we want out of the
2919
0
  // print job without displaying any error messages
2920
0
  nsresult rv = pageSeqFrame->PrintNextPage();
2921
0
  if (NS_FAILED(rv)) {
2922
0
    if (rv != NS_ERROR_ABORT) {
2923
0
      FirePrintingErrorEvent(rv);
2924
0
      printData->mIsAborted = true;
2925
0
    }
2926
0
    return true;
2927
0
  }
2928
0
2929
0
  pageSeqFrame->DoPageEnd();
2930
0
2931
0
  return donePrinting;
2932
0
}
2933
2934
void
2935
nsPrintJob::PageDone(nsresult aResult)
2936
0
{
2937
0
  MOZ_ASSERT(mIsDoingPrinting);
2938
0
2939
0
  // mPagePrintTimer might be released during RemotePrintFinished, keep a
2940
0
  // reference here to make sure it lives long enough.
2941
0
  RefPtr<nsPagePrintTimer> timer = mPagePrintTimer;
2942
0
  timer->RemotePrintFinished();
2943
0
}
2944
2945
//-----------------------------------------------------------------
2946
//-- Done: Printing Methods
2947
//-----------------------------------------------------------------
2948
2949
2950
//-----------------------------------------------------------------
2951
//-- Section: Misc Support Methods
2952
//-----------------------------------------------------------------
2953
2954
//---------------------------------------------------------------------
2955
void
2956
nsPrintJob::SetIsPrinting(bool aIsPrinting)
2957
0
{
2958
0
  mIsDoingPrinting = aIsPrinting;
2959
0
  // Calling SetIsPrinting while in print preview confuses the document viewer
2960
0
  // This is safe because we prevent exiting print preview while printing
2961
0
  if (!mIsDoingPrintPreview && mDocViewerPrint) {
2962
0
    mDocViewerPrint->SetIsPrinting(aIsPrinting);
2963
0
  }
2964
0
  if (mPrt && aIsPrinting) {
2965
0
    mPrt->mPreparingForPrint = true;
2966
0
  }
2967
0
}
2968
2969
//---------------------------------------------------------------------
2970
void
2971
nsPrintJob::SetIsPrintPreview(bool aIsPrintPreview)
2972
0
{
2973
0
  mIsDoingPrintPreview = aIsPrintPreview;
2974
0
2975
0
  if (mDocViewerPrint) {
2976
0
    mDocViewerPrint->SetIsPrintPreview(aIsPrintPreview);
2977
0
  }
2978
0
}
2979
2980
//---------------------------------------------------------------------
2981
void
2982
nsPrintJob::CleanupDocTitleArray(char16_t**& aArray, int32_t& aCount)
2983
0
{
2984
0
  for (int32_t i = aCount - 1; i >= 0; i--) {
2985
0
    free(aArray[i]);
2986
0
  }
2987
0
  free(aArray);
2988
0
  aArray = nullptr;
2989
0
  aCount = 0;
2990
0
}
2991
2992
/** ---------------------------------------------------
2993
 *  Get the Focused Frame for a documentviewer
2994
 */
2995
already_AddRefed<nsPIDOMWindowOuter>
2996
nsPrintJob::FindFocusedDOMWindow()
2997
0
{
2998
0
  nsIFocusManager* fm = nsFocusManager::GetFocusManager();
2999
0
  NS_ENSURE_TRUE(fm, nullptr);
3000
0
3001
0
  nsPIDOMWindowOuter* window = mDocument->GetWindow();
3002
0
  NS_ENSURE_TRUE(window, nullptr);
3003
0
3004
0
  nsCOMPtr<nsPIDOMWindowOuter> rootWindow = window->GetPrivateRoot();
3005
0
  NS_ENSURE_TRUE(rootWindow, nullptr);
3006
0
3007
0
  nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
3008
0
  nsFocusManager::GetFocusedDescendant(rootWindow,
3009
0
                                       nsFocusManager::eIncludeAllDescendants,
3010
0
                                       getter_AddRefs(focusedWindow));
3011
0
  NS_ENSURE_TRUE(focusedWindow, nullptr);
3012
0
3013
0
  if (IsWindowsInOurSubTree(focusedWindow)) {
3014
0
    return focusedWindow.forget();
3015
0
  }
3016
0
3017
0
  return nullptr;
3018
0
}
3019
3020
//---------------------------------------------------------------------
3021
bool
3022
nsPrintJob::IsWindowsInOurSubTree(nsPIDOMWindowOuter* window)
3023
0
{
3024
0
  bool found = false;
3025
0
3026
0
  // now check to make sure it is in "our" tree of docshells
3027
0
  if (window) {
3028
0
    nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
3029
0
3030
0
    if (docShell) {
3031
0
      // get this DocViewer docshell
3032
0
      nsCOMPtr<nsIDocShell> thisDVDocShell(do_QueryReferent(mContainer));
3033
0
      while (!found) {
3034
0
        if (docShell) {
3035
0
          if (docShell == thisDVDocShell) {
3036
0
            found = true;
3037
0
            break;
3038
0
          }
3039
0
        } else {
3040
0
          break; // at top of tree
3041
0
        }
3042
0
        nsCOMPtr<nsIDocShellTreeItem> docShellItemParent;
3043
0
        docShell->GetSameTypeParent(getter_AddRefs(docShellItemParent));
3044
0
        docShell = do_QueryInterface(docShellItemParent);
3045
0
      } // while
3046
0
    }
3047
0
  } // scriptobj
3048
0
3049
0
  return found;
3050
0
}
3051
3052
//-------------------------------------------------------
3053
bool
3054
nsPrintJob::DonePrintingPages(nsPrintObject* aPO, nsresult aResult)
3055
0
{
3056
0
  //NS_ASSERTION(aPO, "Pointer is null!");
3057
0
  PR_PL(("****** In DV::DonePrintingPages PO: %p (%s)\n", aPO, aPO?gFrameTypesStr[aPO->mFrameType]:""));
3058
0
3059
0
  // If there is a pageSeqFrame, make sure there are no more printCanvas active
3060
0
  // that might call |Notify| on the pagePrintTimer after things are cleaned up
3061
0
  // and printing was marked as being done.
3062
0
  if (mPageSeqFrame.IsAlive()) {
3063
0
    nsIPageSequenceFrame* pageSeqFrame = do_QueryFrame(mPageSeqFrame.GetFrame());
3064
0
    pageSeqFrame->ResetPrintCanvasList();
3065
0
  }
3066
0
3067
0
  // Guarantee that mPrt and mPrt->mPrintObject won't be deleted during a
3068
0
  // call of PrintDocContent() and FirePrintCompletionEvent().
3069
0
  RefPtr<nsPrintData> printData = mPrt;
3070
0
3071
0
  if (aPO && !printData->mIsAborted) {
3072
0
    aPO->mHasBeenPrinted = true;
3073
0
    nsresult rv;
3074
0
    bool didPrint = PrintDocContent(printData->mPrintObject, rv);
3075
0
    if (NS_SUCCEEDED(rv) && didPrint) {
3076
0
      PR_PL(("****** In DV::DonePrintingPages PO: %p (%s) didPrint:%s (Not Done Printing)\n", aPO, gFrameTypesStr[aPO->mFrameType], PRT_YESNO(didPrint)));
3077
0
      return false;
3078
0
    }
3079
0
  }
3080
0
3081
0
  printData->mPrintDC->UnregisterPageDoneCallback();
3082
0
3083
0
  if (NS_SUCCEEDED(aResult)) {
3084
0
    FirePrintCompletionEvent();
3085
0
    // XXX mPrt may be cleared or replaced with new instance here.
3086
0
    //     However, the following methods will clean up with new mPrt or will
3087
0
    //     do nothing due to no proper nsPrintData instance.
3088
0
  }
3089
0
3090
0
  TurnScriptingOn(true);
3091
0
  SetIsPrinting(false);
3092
0
3093
0
  // Release reference to mPagePrintTimer; the timer object destroys itself
3094
0
  // after this returns true
3095
0
  DisconnectPagePrintTimer();
3096
0
3097
0
  return true;
3098
0
}
3099
3100
//-------------------------------------------------------
3101
nsresult
3102
nsPrintJob::EnablePOsForPrinting()
3103
0
{
3104
0
  // Guarantee that mPrt and the objects it owns won't be deleted.
3105
0
  RefPtr<nsPrintData> printData = mPrt;
3106
0
3107
0
  // NOTE: All POs have been "turned off" for printing
3108
0
  // this is where we decided which POs get printed.
3109
0
3110
0
  if (!printData->mPrintSettings) {
3111
0
    return NS_ERROR_FAILURE;
3112
0
  }
3113
0
3114
0
  printData->mPrintFrameType = nsIPrintSettings::kNoFrames;
3115
0
  printData->mPrintSettings->GetPrintFrameType(&printData->mPrintFrameType);
3116
0
3117
0
  int16_t printHowEnable = nsIPrintSettings::kFrameEnableNone;
3118
0
  printData->mPrintSettings->GetHowToEnableFrameUI(&printHowEnable);
3119
0
3120
0
  int16_t printRangeType = nsIPrintSettings::kRangeAllPages;
3121
0
  printData->mPrintSettings->GetPrintRange(&printRangeType);
3122
0
3123
0
  PR_PL(("\n"));
3124
0
  PR_PL(("********* nsPrintJob::EnablePOsForPrinting *********\n"));
3125
0
  PR_PL(("PrintFrameType:     %s \n",
3126
0
         gPrintFrameTypeStr[printData->mPrintFrameType]));
3127
0
  PR_PL(("HowToEnableFrameUI: %s \n", gFrameHowToEnableStr[printHowEnable]));
3128
0
  PR_PL(("PrintRange:         %s \n", gPrintRangeStr[printRangeType]));
3129
0
  PR_PL(("----\n"));
3130
0
3131
0
  // ***** This is the ultimate override *****
3132
0
  // if we are printing the selection (either an IFrame or selection range)
3133
0
  // then set the mPrintFrameType as if it were the selected frame
3134
0
  if (printRangeType == nsIPrintSettings::kRangeSelection) {
3135
0
    printData->mPrintFrameType = nsIPrintSettings::kSelectedFrame;
3136
0
    printHowEnable = nsIPrintSettings::kFrameEnableNone;
3137
0
  }
3138
0
3139
0
  // This tells us that the "Frame" UI has turned off,
3140
0
  // so therefore there are no FrameSets/Frames/IFrames to be printed
3141
0
  //
3142
0
  // This means there are not FrameSets,
3143
0
  // but the document could contain an IFrame
3144
0
  if (printHowEnable == nsIPrintSettings::kFrameEnableNone) {
3145
0
    // Print all the pages or a sub range of pages
3146
0
    if (printRangeType == nsIPrintSettings::kRangeAllPages ||
3147
0
        printRangeType == nsIPrintSettings::kRangeSpecifiedPageRange) {
3148
0
      SetPrintPO(printData->mPrintObject.get(), true);
3149
0
3150
0
      // Set the children so they are PrinAsIs
3151
0
      // In this case, the children are probably IFrames
3152
0
      if (printData->mPrintObject->mKids.Length() > 0) {
3153
0
        for (const UniquePtr<nsPrintObject>& po :
3154
0
               printData->mPrintObject->mKids) {
3155
0
          NS_ASSERTION(po, "nsPrintObject can't be null!");
3156
0
          SetPrintAsIs(po.get());
3157
0
        }
3158
0
3159
0
        // ***** Another override *****
3160
0
        printData->mPrintFrameType = nsIPrintSettings::kFramesAsIs;
3161
0
      }
3162
0
      PR_PL(("PrintFrameType:     %s \n",
3163
0
             gPrintFrameTypeStr[printData->mPrintFrameType]));
3164
0
      PR_PL(("HowToEnableFrameUI: %s \n",
3165
0
             gFrameHowToEnableStr[printHowEnable]));
3166
0
      PR_PL(("PrintRange:         %s \n", gPrintRangeStr[printRangeType]));
3167
0
      return NS_OK;
3168
0
    }
3169
0
3170
0
    // This means we are either printed a selected IFrame or
3171
0
    // we are printing the current selection
3172
0
    if (printRangeType == nsIPrintSettings::kRangeSelection) {
3173
0
      // If the currentFocusDOMWin can'r be null if something is selected
3174
0
      if (printData->mCurrentFocusWin) {
3175
0
        // Find the selected IFrame
3176
0
        nsPrintObject* po =
3177
0
          FindPrintObjectByDOMWin(printData->mPrintObject.get(),
3178
0
                                  printData->mCurrentFocusWin);
3179
0
        if (po) {
3180
0
          // Makes sure all of its children are be printed "AsIs"
3181
0
          SetPrintAsIs(po);
3182
0
3183
0
          // Now, only enable this POs (the selected PO) and all of its children
3184
0
          SetPrintPO(po, true);
3185
0
3186
0
          // check to see if we have a range selection,
3187
0
          // as oppose to a insert selection
3188
0
          // this means if the user just clicked on the IFrame then
3189
0
          // there will not be a selection so we want the entire page to print
3190
0
          //
3191
0
          // XXX this is sort of a hack right here to make the page
3192
0
          // not try to reposition itself when printing selection
3193
0
          nsPIDOMWindowOuter* domWin =
3194
0
            po->mDocument->GetOriginalDocument()->GetWindow();
3195
0
          if (!IsThereARangeSelection(domWin)) {
3196
0
            printRangeType = nsIPrintSettings::kRangeAllPages;
3197
0
            printData->mPrintSettings->SetPrintRange(printRangeType);
3198
0
          }
3199
0
          PR_PL(("PrintFrameType:     %s \n",
3200
0
                 gPrintFrameTypeStr[printData->mPrintFrameType]));
3201
0
          PR_PL(("HowToEnableFrameUI: %s \n",
3202
0
                 gFrameHowToEnableStr[printHowEnable]));
3203
0
          PR_PL(("PrintRange:         %s \n",
3204
0
                 gPrintRangeStr[printRangeType]));
3205
0
          return NS_OK;
3206
0
        }
3207
0
      } else {
3208
0
        for (uint32_t i = 0; i < printData->mPrintDocList.Length(); i++) {
3209
0
          nsPrintObject* po = printData->mPrintDocList.ElementAt(i);
3210
0
          NS_ASSERTION(po, "nsPrintObject can't be null!");
3211
0
          nsCOMPtr<nsPIDOMWindowOuter> domWin = po->mDocShell->GetWindow();
3212
0
          if (IsThereARangeSelection(domWin)) {
3213
0
            printData->mCurrentFocusWin = domWin.forget();
3214
0
            SetPrintPO(po, true);
3215
0
            break;
3216
0
          }
3217
0
        }
3218
0
        return NS_OK;
3219
0
      }
3220
0
    }
3221
0
  }
3222
0
3223
0
  // check to see if there is a selection when a FrameSet is present
3224
0
  if (printRangeType == nsIPrintSettings::kRangeSelection) {
3225
0
    // If the currentFocusDOMWin can'r be null if something is selected
3226
0
    if (printData->mCurrentFocusWin) {
3227
0
      // Find the selected IFrame
3228
0
      nsPrintObject* po =
3229
0
        FindPrintObjectByDOMWin(printData->mPrintObject.get(),
3230
0
                                printData->mCurrentFocusWin);
3231
0
      if (po) {
3232
0
        // Makes sure all of its children are be printed "AsIs"
3233
0
        SetPrintAsIs(po);
3234
0
3235
0
        // Now, only enable this POs (the selected PO) and all of its children
3236
0
        SetPrintPO(po, true);
3237
0
3238
0
        // check to see if we have a range selection,
3239
0
        // as oppose to a insert selection
3240
0
        // this means if the user just clicked on the IFrame then
3241
0
        // there will not be a selection so we want the entire page to print
3242
0
        //
3243
0
        // XXX this is sort of a hack right here to make the page
3244
0
        // not try to reposition itself when printing selection
3245
0
        nsCOMPtr<nsPIDOMWindowOuter> domWin = po->mDocument->GetOriginalDocument()->GetWindow();
3246
0
        if (!IsThereARangeSelection(domWin)) {
3247
0
          printRangeType = nsIPrintSettings::kRangeAllPages;
3248
0
          printData->mPrintSettings->SetPrintRange(printRangeType);
3249
0
        }
3250
0
        PR_PL(("PrintFrameType:     %s \n",
3251
0
               gPrintFrameTypeStr[printData->mPrintFrameType]));
3252
0
        PR_PL(("HowToEnableFrameUI: %s \n",
3253
0
               gFrameHowToEnableStr[printHowEnable]));
3254
0
        PR_PL(("PrintRange:         %s \n", gPrintRangeStr[printRangeType]));
3255
0
        return NS_OK;
3256
0
      }
3257
0
    }
3258
0
  }
3259
0
3260
0
  // If we are printing "AsIs" then sets all the POs to be printed as is
3261
0
  if (printData->mPrintFrameType == nsIPrintSettings::kFramesAsIs) {
3262
0
    SetPrintAsIs(printData->mPrintObject.get());
3263
0
    SetPrintPO(printData->mPrintObject.get(), true);
3264
0
    return NS_OK;
3265
0
  }
3266
0
3267
0
  // If we are printing the selected Frame then
3268
0
  // find that PO for that selected DOMWin and set it all of its
3269
0
  // children to be printed
3270
0
  if (printData->mPrintFrameType == nsIPrintSettings::kSelectedFrame) {
3271
0
    if ((printData->mIsParentAFrameSet && printData->mCurrentFocusWin) ||
3272
0
        printData->mIsIFrameSelected) {
3273
0
      nsPrintObject* po =
3274
0
        FindPrintObjectByDOMWin(printData->mPrintObject.get(),
3275
0
                                printData->mCurrentFocusWin);
3276
0
      if (po) {
3277
0
        // NOTE: Calling this sets the "po" and
3278
0
        // we don't want to do this for documents that have no children,
3279
0
        // because then the "DoEndPage" gets called and it shouldn't
3280
0
        if (po->mKids.Length() > 0) {
3281
0
          // Makes sure that itself, and all of its children are printed "AsIs"
3282
0
          SetPrintAsIs(po);
3283
0
        }
3284
0
3285
0
        // Now, only enable this POs (the selected PO) and all of its children
3286
0
        SetPrintPO(po, true);
3287
0
      }
3288
0
    }
3289
0
    return NS_OK;
3290
0
  }
3291
0
3292
0
  // If we are print each subdoc separately,
3293
0
  // then don't print any of the FraneSet Docs
3294
0
  if (printData->mPrintFrameType == nsIPrintSettings::kEachFrameSep) {
3295
0
    SetPrintPO(printData->mPrintObject.get(), true);
3296
0
    int32_t cnt = printData->mPrintDocList.Length();
3297
0
    for (int32_t i=0;i<cnt;i++) {
3298
0
      nsPrintObject* po = printData->mPrintDocList.ElementAt(i);
3299
0
      NS_ASSERTION(po, "nsPrintObject can't be null!");
3300
0
      if (po->mFrameType == eFrameSet) {
3301
0
        po->mDontPrint = true;
3302
0
      }
3303
0
    }
3304
0
  }
3305
0
3306
0
  return NS_OK;
3307
0
}
3308
3309
//-------------------------------------------------------
3310
// Return the nsPrintObject with that is XMost (The widest frameset frame) AND
3311
// contains the XMost (widest) layout frame
3312
nsPrintObject*
3313
nsPrintJob::FindSmallestSTF()
3314
0
{
3315
0
  float smallestRatio = 1.0f;
3316
0
  nsPrintObject* smallestPO = nullptr;
3317
0
3318
0
  for (uint32_t i=0;i<mPrt->mPrintDocList.Length();i++) {
3319
0
    nsPrintObject* po = mPrt->mPrintDocList.ElementAt(i);
3320
0
    NS_ASSERTION(po, "nsPrintObject can't be null!");
3321
0
    if (po->mFrameType != eFrameSet && po->mFrameType != eIFrame) {
3322
0
      if (po->mShrinkRatio < smallestRatio) {
3323
0
        smallestRatio = po->mShrinkRatio;
3324
0
        smallestPO    = po;
3325
0
      }
3326
0
    }
3327
0
  }
3328
0
3329
#ifdef EXTENDED_DEBUG_PRINTING
3330
  if (smallestPO) printf("*PO: %p  Type: %d  %10.3f\n", smallestPO, smallestPO->mFrameType, smallestPO->mShrinkRatio);
3331
#endif
3332
  return smallestPO;
3333
0
}
3334
3335
//-------------------------------------------------------
3336
void
3337
nsPrintJob::TurnScriptingOn(bool aDoTurnOn)
3338
0
{
3339
0
  if (mIsDoingPrinting && aDoTurnOn && mDocViewerPrint &&
3340
0
      mDocViewerPrint->GetIsPrintPreview()) {
3341
0
    // We don't want to turn scripting on if print preview is shown still after
3342
0
    // printing.
3343
0
    return;
3344
0
  }
3345
0
3346
0
  // The following for loop uses nsPrintObject instances that are owned by
3347
0
  // mPrt or mPrtPreview.  Therefore, this method needs to guarantee that
3348
0
  // they won't be deleted in this method.
3349
0
  RefPtr<nsPrintData> printData = mPrt ? mPrt : mPrtPreview;
3350
0
  if (!printData) {
3351
0
    return;
3352
0
  }
3353
0
3354
0
  NS_ASSERTION(mDocument, "We MUST have a document.");
3355
0
  // First, get the script global object from the document...
3356
0
3357
0
  for (uint32_t i = 0; i < printData->mPrintDocList.Length(); i++) {
3358
0
    nsPrintObject* po = printData->mPrintDocList.ElementAt(i);
3359
0
    MOZ_ASSERT(po);
3360
0
3361
0
    nsIDocument* doc = po->mDocument;
3362
0
    if (!doc) {
3363
0
      continue;
3364
0
    }
3365
0
3366
0
    if (nsCOMPtr<nsPIDOMWindowInner> window = doc->GetInnerWindow()) {
3367
0
      nsCOMPtr<nsIGlobalObject> go = window->AsGlobal();
3368
0
      NS_WARNING_ASSERTION(go->GetGlobalJSObject(), "Can't get global");
3369
0
      nsresult propThere = NS_PROPTABLE_PROP_NOT_THERE;
3370
0
      doc->GetProperty(nsGkAtoms::scriptEnabledBeforePrintOrPreview,
3371
0
                       &propThere);
3372
0
      if (aDoTurnOn) {
3373
0
        if (propThere != NS_PROPTABLE_PROP_NOT_THERE) {
3374
0
          doc->DeleteProperty(nsGkAtoms::scriptEnabledBeforePrintOrPreview);
3375
0
          if (go->GetGlobalJSObject()) {
3376
0
            xpc::Scriptability::Get(go->GetGlobalJSObject()).Unblock();
3377
0
          }
3378
0
          window->Resume();
3379
0
        }
3380
0
      } else {
3381
0
        // Have to be careful, because people call us over and over again with
3382
0
        // aDoTurnOn == false.  So don't set the property if it's already
3383
0
        // set, since in that case we'd set it to the wrong value.
3384
0
        if (propThere == NS_PROPTABLE_PROP_NOT_THERE) {
3385
0
          // Stash the current value of IsScriptEnabled on the document, so
3386
0
          // that layout code running in print preview doesn't get confused.
3387
0
          doc->SetProperty(nsGkAtoms::scriptEnabledBeforePrintOrPreview,
3388
0
                           NS_INT32_TO_PTR(doc->IsScriptEnabled()));
3389
0
          if (go && go->GetGlobalJSObject()) {
3390
0
            xpc::Scriptability::Get(go->GetGlobalJSObject()).Block();
3391
0
          }
3392
0
          window->Suspend();
3393
0
        }
3394
0
      }
3395
0
    }
3396
0
  }
3397
0
}
3398
3399
//-----------------------------------------------------------------
3400
//-- Done: Misc Support Methods
3401
//-----------------------------------------------------------------
3402
3403
3404
//-----------------------------------------------------------------
3405
//-- Section: Finishing up or Cleaning up
3406
//-----------------------------------------------------------------
3407
3408
//-----------------------------------------------------------------
3409
void
3410
nsPrintJob::CloseProgressDialog(nsIWebProgressListener* aWebProgressListener)
3411
0
{
3412
0
  if (aWebProgressListener) {
3413
0
    aWebProgressListener->OnStateChange(nullptr, nullptr, nsIWebProgressListener::STATE_STOP|nsIWebProgressListener::STATE_IS_DOCUMENT, NS_OK);
3414
0
  }
3415
0
}
3416
3417
//-----------------------------------------------------------------
3418
nsresult
3419
nsPrintJob::FinishPrintPreview()
3420
0
{
3421
0
  nsresult rv = NS_OK;
3422
0
3423
0
#ifdef NS_PRINT_PREVIEW
3424
0
3425
0
  if (!mPrt) {
3426
0
    /* we're already finished with print preview */
3427
0
    return rv;
3428
0
  }
3429
0
3430
0
  rv = DocumentReadyForPrinting();
3431
0
3432
0
  // Note that this method may be called while the instance is being
3433
0
  // initialized.  Some methods which initialize the instance (e.g.,
3434
0
  // DoCommonPrint) may need to stop initializing and return error if
3435
0
  // this is called.  Therefore it's important to set mIsCreatingPrintPreview
3436
0
  // state to false here.  If you need to stop setting that here, you need to
3437
0
  // keep them being able to check whether the owner stopped using this
3438
0
  // instance.
3439
0
  mIsCreatingPrintPreview = false;
3440
0
3441
0
  // mPrt may be cleared during a call of nsPrintData::OnEndPrinting()
3442
0
  // because that method invokes some arbitrary listeners.
3443
0
  RefPtr<nsPrintData> printData = mPrt;
3444
0
  if (NS_FAILED(rv)) {
3445
0
    /* cleanup done, let's fire-up an error dialog to notify the user
3446
0
     * what went wrong...
3447
0
     */
3448
0
    printData->OnEndPrinting();
3449
0
    // XXX mPrt may be nullptr here.  So, Shouldn't TurnScriptingOn() take
3450
0
    //     nsPrintData as an argument?
3451
0
    TurnScriptingOn(true);
3452
0
3453
0
    return rv;
3454
0
  }
3455
0
3456
0
  // At this point we are done preparing everything
3457
0
  // before it is to be created
3458
0
3459
0
  if (mIsDoingPrintPreview && mOldPrtPreview) {
3460
0
    mOldPrtPreview = nullptr;
3461
0
  }
3462
0
3463
0
  printData->OnEndPrinting();
3464
0
  // XXX If mPrt becomes nullptr or different instance here, what should we
3465
0
  //     do?
3466
0
3467
0
  // PrintPreview was built using the mPrt (code reuse)
3468
0
  // then we assign it over
3469
0
  mPrtPreview = std::move(mPrt);
3470
0
3471
0
#endif // NS_PRINT_PREVIEW
3472
0
3473
0
  return NS_OK;
3474
0
}
3475
3476
//-----------------------------------------------------------------
3477
//-- Done: Finishing up or Cleaning up
3478
//-----------------------------------------------------------------
3479
3480
3481
/*=============== Timer Related Code ======================*/
3482
nsresult
3483
nsPrintJob::StartPagePrintTimer(const UniquePtr<nsPrintObject>& aPO)
3484
0
{
3485
0
  if (!mPagePrintTimer) {
3486
0
    // Get the delay time in between the printing of each page
3487
0
    // this gives the user more time to press cancel
3488
0
    int32_t printPageDelay = 50;
3489
0
    mPrt->mPrintSettings->GetPrintPageDelay(&printPageDelay);
3490
0
3491
0
    nsCOMPtr<nsIContentViewer> cv = do_QueryInterface(mDocViewerPrint);
3492
0
    NS_ENSURE_TRUE(cv, NS_ERROR_FAILURE);
3493
0
    nsCOMPtr<nsIDocument> doc = cv->GetDocument();
3494
0
    NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
3495
0
3496
0
    RefPtr<nsPagePrintTimer> timer =
3497
0
      new nsPagePrintTimer(this, mDocViewerPrint, doc, printPageDelay);
3498
0
    timer.forget(&mPagePrintTimer);
3499
0
3500
0
    nsCOMPtr<nsIPrintSession> printSession;
3501
0
    nsresult rv = mPrt->mPrintSettings->GetPrintSession(getter_AddRefs(printSession));
3502
0
    if (NS_SUCCEEDED(rv) && printSession) {
3503
0
      RefPtr<mozilla::layout::RemotePrintJobChild> remotePrintJob;
3504
0
      printSession->GetRemotePrintJob(getter_AddRefs(remotePrintJob));
3505
0
      if (NS_SUCCEEDED(rv) && remotePrintJob) {
3506
0
        remotePrintJob->SetPagePrintTimer(mPagePrintTimer);
3507
0
        remotePrintJob->SetPrintJob(this);
3508
0
      }
3509
0
    }
3510
0
  }
3511
0
3512
0
  return mPagePrintTimer->Start(aPO.get());
3513
0
}
3514
3515
/*=============== nsIObserver Interface ======================*/
3516
NS_IMETHODIMP
3517
nsPrintJob::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
3518
0
{
3519
0
  // Only process a null topic which means the progress dialog is open.
3520
0
  if (aTopic) {
3521
0
    return NS_OK;
3522
0
  }
3523
0
3524
0
  nsresult rv = InitPrintDocConstruction(true);
3525
0
  if (!mIsDoingPrinting && mPrtPreview) {
3526
0
    RefPtr<nsPrintData> printDataOfPrintPreview = mPrtPreview;
3527
0
    printDataOfPrintPreview->OnEndPrinting();
3528
0
  }
3529
0
3530
0
  return rv;
3531
0
}
3532
3533
//---------------------------------------------------------------
3534
//-- PLEvent Notification
3535
//---------------------------------------------------------------
3536
class nsPrintCompletionEvent : public Runnable {
3537
public:
3538
  explicit nsPrintCompletionEvent(nsIDocumentViewerPrint* docViewerPrint)
3539
    : mozilla::Runnable("nsPrintCompletionEvent")
3540
    , mDocViewerPrint(docViewerPrint)
3541
0
  {
3542
0
    NS_ASSERTION(mDocViewerPrint, "mDocViewerPrint is null.");
3543
0
  }
3544
3545
0
  NS_IMETHOD Run() override {
3546
0
    if (mDocViewerPrint)
3547
0
      mDocViewerPrint->OnDonePrinting();
3548
0
    return NS_OK;
3549
0
  }
3550
3551
private:
3552
  nsCOMPtr<nsIDocumentViewerPrint> mDocViewerPrint;
3553
};
3554
3555
//-----------------------------------------------------------
3556
void
3557
nsPrintJob::FirePrintCompletionEvent()
3558
0
{
3559
0
  MOZ_ASSERT(NS_IsMainThread());
3560
0
  nsCOMPtr<nsIRunnable> event = new nsPrintCompletionEvent(mDocViewerPrint);
3561
0
  nsCOMPtr<nsIContentViewer> cv = do_QueryInterface(mDocViewerPrint);
3562
0
  NS_ENSURE_TRUE_VOID(cv);
3563
0
  nsCOMPtr<nsIDocument> doc = cv->GetDocument();
3564
0
  NS_ENSURE_TRUE_VOID(doc);
3565
0
3566
0
  NS_ENSURE_SUCCESS_VOID(doc->Dispatch(TaskCategory::Other, event.forget()));
3567
0
}
3568
3569
void
3570
nsPrintJob::DisconnectPagePrintTimer()
3571
0
{
3572
0
  if (mPagePrintTimer) {
3573
0
    mPagePrintTimer->Disconnect();
3574
0
    NS_RELEASE(mPagePrintTimer);
3575
0
  }
3576
0
}
3577
3578
//---------------------------------------------------------------
3579
//---------------------------------------------------------------
3580
//-- Debug helper routines
3581
//---------------------------------------------------------------
3582
//---------------------------------------------------------------
3583
#if defined(XP_WIN) && defined(EXTENDED_DEBUG_PRINTING)
3584
#include "windows.h"
3585
#include "process.h"
3586
#include "direct.h"
3587
3588
#define MY_FINDFIRST(a,b) FindFirstFile(a,b)
3589
#define MY_FINDNEXT(a,b) FindNextFile(a,b)
3590
#define ISDIR(a) (a.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
3591
#define MY_FINDCLOSE(a) FindClose(a)
3592
#define MY_FILENAME(a) a.cFileName
3593
#define MY_FILESIZE(a) (a.nFileSizeHigh * MAXDWORD) + a.nFileSizeLow
3594
3595
int RemoveFilesInDir(const char * aDir)
3596
{
3597
  WIN32_FIND_DATA data_ptr;
3598
  HANDLE find_handle;
3599
3600
  char path[MAX_PATH];
3601
3602
  strcpy(path, aDir);
3603
3604
  // Append slash to the end of the directory names if not there
3605
  if (path[strlen(path)-1] != '\\')
3606
    strcat(path, "\\");
3607
3608
  char findPath[MAX_PATH];
3609
  strcpy(findPath, path);
3610
  strcat(findPath, "*.*");
3611
3612
  find_handle = MY_FINDFIRST(findPath, &data_ptr);
3613
3614
  if (find_handle != INVALID_HANDLE_VALUE) {
3615
    do  {
3616
      if (ISDIR(data_ptr)
3617
        && (stricmp(MY_FILENAME(data_ptr),"."))
3618
        && (stricmp(MY_FILENAME(data_ptr),".."))) {
3619
          // skip
3620
      }
3621
      else if (!ISDIR(data_ptr)) {
3622
        if (!strncmp(MY_FILENAME(data_ptr), "print_dump", 10)) {
3623
          char fileName[MAX_PATH];
3624
          strcpy(fileName, aDir);
3625
          strcat(fileName, "\\");
3626
          strcat(fileName, MY_FILENAME(data_ptr));
3627
          printf("Removing %s\n", fileName);
3628
          remove(fileName);
3629
        }
3630
      }
3631
    } while(MY_FINDNEXT(find_handle,&data_ptr));
3632
    MY_FINDCLOSE(find_handle);
3633
  }
3634
  return TRUE;
3635
}
3636
#endif
3637
3638
#ifdef EXTENDED_DEBUG_PRINTING
3639
3640
/** ---------------------------------------------------
3641
 *  Dumps Frames for Printing
3642
 */
3643
static void RootFrameList(nsPresContext* aPresContext, FILE* out,
3644
                          const char* aPrefix)
3645
{
3646
  if (!aPresContext || !out)
3647
    return;
3648
3649
  nsIPresShell *shell = aPresContext->GetPresShell();
3650
  if (shell) {
3651
    nsIFrame* frame = shell->GetRootFrame();
3652
    if (frame) {
3653
      frame->List(out, aPrefix);
3654
    }
3655
  }
3656
}
3657
3658
/** ---------------------------------------------------
3659
 *  Dumps Frames for Printing
3660
 */
3661
static void DumpFrames(FILE*                 out,
3662
                       nsPresContext*       aPresContext,
3663
                       gfxContext * aRendContext,
3664
                       nsIFrame *            aFrame,
3665
                       int32_t               aLevel)
3666
{
3667
  NS_ASSERTION(out, "Pointer is null!");
3668
  NS_ASSERTION(aPresContext, "Pointer is null!");
3669
  NS_ASSERTION(aRendContext, "Pointer is null!");
3670
  NS_ASSERTION(aFrame, "Pointer is null!");
3671
3672
  nsIFrame* child = aFrame->PrincipalChildList().FirstChild();
3673
  while (child != nullptr) {
3674
    for (int32_t i=0;i<aLevel;i++) {
3675
     fprintf(out, "  ");
3676
    }
3677
    nsAutoString tmp;
3678
    child->GetFrameName(tmp);
3679
    fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
3680
    bool isSelected;
3681
    if (child->IsVisibleForPainting()) {
3682
      fprintf(out, " %p %s", child, isSelected?"VIS":"UVS");
3683
      nsRect rect = child->GetRect();
3684
      fprintf(out, "[%d,%d,%d,%d] ", rect.x, rect.y, rect.width, rect.height);
3685
      fprintf(out, "v: %p ", (void*)child->GetView());
3686
      fprintf(out, "\n");
3687
      DumpFrames(out, aPresContext, aRendContext, child, aLevel+1);
3688
      child = child->GetNextSibling();
3689
    }
3690
  }
3691
}
3692
3693
3694
/** ---------------------------------------------------
3695
 *  Dumps the Views from the DocShell
3696
 */
3697
static void
3698
DumpViews(nsIDocShell* aDocShell, FILE* out)
3699
{
3700
  NS_ASSERTION(aDocShell, "Pointer is null!");
3701
  NS_ASSERTION(out, "Pointer is null!");
3702
3703
  if (nullptr != aDocShell) {
3704
    fprintf(out, "docshell=%p \n", aDocShell);
3705
    nsIPresShell* shell = aDocShell->GetPresShell();
3706
    if (shell) {
3707
      nsViewManager* vm = shell->GetViewManager();
3708
      if (vm) {
3709
        nsView* root = vm->GetRootView();
3710
        if (root) {
3711
          root->List(out);
3712
        }
3713
      }
3714
    }
3715
    else {
3716
      fputs("null pres shell\n", out);
3717
    }
3718
3719
    // dump the views of the sub documents
3720
    int32_t i, n;
3721
    aDocShell->GetChildCount(&n);
3722
    for (i = 0; i < n; i++) {
3723
      nsCOMPtr<nsIDocShellTreeItem> child;
3724
      aDocShell->GetChildAt(i, getter_AddRefs(child));
3725
      nsCOMPtr<nsIDocShell> childAsShell(do_QueryInterface(child));
3726
      if (childAsShell) {
3727
        DumpViews(childAsShell, out);
3728
      }
3729
    }
3730
  }
3731
}
3732
3733
/** ---------------------------------------------------
3734
 *  Dumps the Views and Frames
3735
 */
3736
void DumpLayoutData(const char*        aTitleStr,
3737
                    const char*        aURLStr,
3738
                    nsPresContext*     aPresContext,
3739
                    nsDeviceContext*   aDC,
3740
                    nsIFrame*          aRootFrame,
3741
                    nsIDocShell*       aDocShell,
3742
                    FILE*              aFD = nullptr)
3743
{
3744
  if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
3745
    return;
3746
  }
3747
3748
  if (aPresContext == nullptr || aDC == nullptr) {
3749
    return;
3750
  }
3751
3752
#ifdef NS_PRINT_PREVIEW
3753
  if (aPresContext->Type() == nsPresContext::eContext_PrintPreview) {
3754
    return;
3755
  }
3756
#endif
3757
3758
  NS_ASSERTION(aRootFrame, "Pointer is null!");
3759
  NS_ASSERTION(aDocShell, "Pointer is null!");
3760
3761
  // Dump all the frames and view to a a file
3762
  char filename[256];
3763
  sprintf(filename, "print_dump_layout_%d.txt", gDumpLOFileNameCnt++);
3764
  FILE * fd = aFD?aFD:fopen(filename, "w");
3765
  if (fd) {
3766
    fprintf(fd, "Title: %s\n", aTitleStr?aTitleStr:"");
3767
    fprintf(fd, "URL:   %s\n", aURLStr?aURLStr:"");
3768
    fprintf(fd, "--------------- Frames ----------------\n");
3769
    fprintf(fd, "--------------- Frames ----------------\n");
3770
    //RefPtr<gfxContext> renderingContext =
3771
    //  aDC->CreateRenderingContext();
3772
    RootFrameList(aPresContext, fd, "");
3773
    //DumpFrames(fd, aPresContext, renderingContext, aRootFrame, 0);
3774
    fprintf(fd, "---------------------------------------\n\n");
3775
    fprintf(fd, "--------------- Views From Root Frame----------------\n");
3776
    nsView* v = aRootFrame->GetView();
3777
    if (v) {
3778
      v->List(fd);
3779
    } else {
3780
      printf("View is null!\n");
3781
    }
3782
    if (aDocShell) {
3783
      fprintf(fd, "--------------- All Views ----------------\n");
3784
      DumpViews(aDocShell, fd);
3785
      fprintf(fd, "---------------------------------------\n\n");
3786
    }
3787
    if (aFD == nullptr) {
3788
      fclose(fd);
3789
    }
3790
  }
3791
}
3792
3793
//-------------------------------------------------------------
3794
static void DumpPrintObjectsList(const nsTArray<nsPrintObject*>& aDocList)
3795
{
3796
  if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
3797
    return;
3798
  }
3799
3800
  const char types[][3] = {"DC", "FR", "IF", "FS"};
3801
  PR_PL(("Doc List\n***************************************************\n"));
3802
  PR_PL(("T  P A H    PO    DocShell   Seq     Page      Root     Page#    Rect\n"));
3803
  for (nsPrintObject* po : aDocList) {
3804
    NS_ASSERTION(po, "nsPrintObject can't be null!");
3805
    nsIFrame* rootFrame = nullptr;
3806
    if (po->mPresShell) {
3807
      rootFrame = po->mPresShell->GetRootFrame();
3808
      while (rootFrame != nullptr) {
3809
        nsIPageSequenceFrame * sqf = do_QueryFrame(rootFrame);
3810
        if (sqf) {
3811
          break;
3812
        }
3813
        rootFrame = rootFrame->PrincipalChildList().FirstChild();
3814
      }
3815
    }
3816
3817
    PR_PL(("%s %d %d %d %p %p %p\n", types[po->mFrameType],
3818
            po->IsPrintable(), po->mPrintAsIs, po->mHasBeenPrinted, po,
3819
            po->mDocShell.get(), rootFrame));
3820
  }
3821
}
3822
3823
//-------------------------------------------------------------
3824
static void DumpPrintObjectsTree(nsPrintObject * aPO, int aLevel, FILE* aFD)
3825
{
3826
  if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
3827
    return;
3828
  }
3829
3830
  NS_ASSERTION(aPO, "Pointer is null!");
3831
3832
  FILE * fd = aFD?aFD:stdout;
3833
  const char types[][3] = {"DC", "FR", "IF", "FS"};
3834
  if (aLevel == 0) {
3835
    fprintf(fd, "DocTree\n***************************************************\n");
3836
    fprintf(fd, "T     PO    DocShell   Seq      Page     Page#    Rect\n");
3837
  }
3838
  for (const auto& po : aPO->mKids) {
3839
    NS_ASSERTION(po, "nsPrintObject can't be null!");
3840
    for (int32_t k=0;k<aLevel;k++) fprintf(fd, "  ");
3841
    fprintf(fd, "%s %p %p\n", types[po->mFrameType], po.get(),
3842
            po->mDocShell.get());
3843
  }
3844
}
3845
3846
//-------------------------------------------------------------
3847
static void GetDocTitleAndURL(const UniquePtr<nsPrintObject>& aPO,
3848
                              nsACString& aDocStr,
3849
                              nsACString& aURLStr)
3850
{
3851
  nsAutoString docTitleStr;
3852
  nsAutoString docURLStr;
3853
  GetDocumentTitleAndURL(aPO->mDocument, docTitleStr, docURLStr);
3854
  aDocStr = NS_ConvertUTF16toUTF8(docTitleStr);
3855
  aURLStr = NS_ConvertUTF16toUTF8(docURLStr);
3856
}
3857
3858
//-------------------------------------------------------------
3859
static void DumpPrintObjectsTreeLayout(const UniquePtr<nsPrintObject>& aPO,
3860
                                       nsDeviceContext * aDC,
3861
                                       int aLevel, FILE * aFD)
3862
{
3863
  if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
3864
    return;
3865
  }
3866
3867
  NS_ASSERTION(aPO, "Pointer is null!");
3868
  NS_ASSERTION(aDC, "Pointer is null!");
3869
3870
  const char types[][3] = {"DC", "FR", "IF", "FS"};
3871
  FILE * fd = nullptr;
3872
  if (aLevel == 0) {
3873
    fd = fopen("tree_layout.txt", "w");
3874
    fprintf(fd, "DocTree\n***************************************************\n");
3875
    fprintf(fd, "***************************************************\n");
3876
    fprintf(fd, "T     PO    DocShell   Seq      Page     Page#    Rect\n");
3877
  } else {
3878
    fd = aFD;
3879
  }
3880
  if (fd) {
3881
    nsIFrame* rootFrame = nullptr;
3882
    if (aPO->mPresShell) {
3883
      rootFrame = aPO->mPresShell->GetRootFrame();
3884
    }
3885
    for (int32_t k=0;k<aLevel;k++) fprintf(fd, "  ");
3886
    fprintf(fd, "%s %p %p\n", types[aPO->mFrameType], aPO.get(),
3887
            aPO->mDocShell.get());
3888
    if (aPO->IsPrintable()) {
3889
      nsAutoCString docStr;
3890
      nsAutoCString urlStr;
3891
      GetDocTitleAndURL(aPO, docStr, urlStr);
3892
      DumpLayoutData(docStr.get(), urlStr.get(), aPO->mPresContext, aDC, rootFrame, aPO->mDocShell, fd);
3893
    }
3894
    fprintf(fd, "<***************************************************>\n");
3895
3896
    for (const auto& po : aPO->mKids) {
3897
      NS_ASSERTION(po, "nsPrintObject can't be null!");
3898
      DumpPrintObjectsTreeLayout(po, aDC, aLevel+1, fd);
3899
    }
3900
  }
3901
  if (aLevel == 0 && fd) {
3902
    fclose(fd);
3903
  }
3904
}
3905
3906
//-------------------------------------------------------------
3907
static void DumpPrintObjectsListStart(const char * aStr,
3908
                                      const nsTArray<nsPrintObject*>& aDocList)
3909
{
3910
  if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
3911
    return;
3912
  }
3913
3914
  NS_ASSERTION(aStr, "Pointer is null!");
3915
3916
  PR_PL(("%s\n", aStr));
3917
  DumpPrintObjectsList(aDocList);
3918
}
3919
3920
#endif
3921
3922
//---------------------------------------------------------------
3923
//---------------------------------------------------------------
3924
//-- End of debug helper routines
3925
//---------------------------------------------------------------