layout/base/nsDocumentViewer.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:4d350e279d5a
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et 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 /* container for a document and its presentation */
8
9 #include "nscore.h"
10 #include "nsCOMPtr.h"
11 #include "nsCRT.h"
12 #include "nsString.h"
13 #include "nsReadableUtils.h"
14 #include "nsIContent.h"
15 #include "nsIContentViewerContainer.h"
16 #include "nsIContentViewer.h"
17 #include "nsIDocumentViewerPrint.h"
18 #include "nsIDOMBeforeUnloadEvent.h"
19 #include "nsIDocument.h"
20 #include "nsIDOMWindowUtils.h"
21 #include "nsPresContext.h"
22 #include "nsIPresShell.h"
23 #include "nsStyleSet.h"
24 #include "nsCSSStyleSheet.h"
25 #include "nsIFrame.h"
26 #include "nsIWritablePropertyBag2.h"
27 #include "nsSubDocumentFrame.h"
28
29 #include "nsILinkHandler.h"
30 #include "nsIDOMDocument.h"
31 #include "nsISelectionListener.h"
32 #include "nsISelectionPrivate.h"
33 #include "nsIDOMHTMLDocument.h"
34 #include "nsIDOMHTMLElement.h"
35 #include "nsContentUtils.h"
36 #include "nsLayoutStylesheetCache.h"
37 #ifdef ACCESSIBILITY
38 #include "mozilla/a11y/DocAccessible.h"
39 #endif
40 #include "mozilla/BasicEvents.h"
41 #include "mozilla/Preferences.h"
42 #include "mozilla/dom/EncodingUtils.h"
43 #include "mozilla/WeakPtr.h"
44
45 #include "nsViewManager.h"
46 #include "nsView.h"
47
48 #include "nsIPageSequenceFrame.h"
49 #include "nsNetUtil.h"
50 #include "nsIContentViewerEdit.h"
51 #include "nsIContentViewerFile.h"
52 #include "mozilla/css/Loader.h"
53 #include "nsIMarkupDocumentViewer.h"
54 #include "nsIInterfaceRequestor.h"
55 #include "nsIInterfaceRequestorUtils.h"
56 #include "nsDocShell.h"
57 #include "nsIBaseWindow.h"
58 #include "nsILayoutHistoryState.h"
59 #include "nsCharsetSource.h"
60 #include "nsHTMLReflowState.h"
61 #include "nsIImageLoadingContent.h"
62 #include "nsCopySupport.h"
63 #include "nsIDOMHTMLFrameSetElement.h"
64 #ifdef MOZ_XUL
65 #include "nsIXULDocument.h"
66 #include "nsXULPopupManager.h"
67 #endif
68
69 #include "nsIClipboardHelper.h"
70
71 #include "nsPIDOMWindow.h"
72 #include "nsDOMNavigationTiming.h"
73 #include "nsPIWindowRoot.h"
74 #include "nsJSEnvironment.h"
75 #include "nsFocusManager.h"
76
77 #include "nsIScrollableFrame.h"
78 #include "nsStyleSheetService.h"
79 #include "nsRenderingContext.h"
80 #include "nsILoadContext.h"
81
82 #include "nsIPrompt.h"
83 #include "imgIContainer.h" // image animation mode constants
84
85 //--------------------------
86 // Printing Include
87 //---------------------------
88 #ifdef NS_PRINTING
89
90 #include "nsIWebBrowserPrint.h"
91
92 #include "nsPrintEngine.h"
93
94 // Print Options
95 #include "nsIPrintSettings.h"
96 #include "nsIPrintOptions.h"
97 #include "nsISimpleEnumerator.h"
98
99 #ifdef DEBUG
100 // PrintOptions is now implemented by PrintSettingsService
101 static const char sPrintOptionsContractID[] =
102 "@mozilla.org/gfx/printsettings-service;1";
103 #endif // DEBUG
104
105 #include "nsIPluginDocument.h"
106
107 #endif // NS_PRINTING
108
109 //focus
110 #include "nsIDOMEventTarget.h"
111 #include "nsIDOMEventListener.h"
112 #include "nsISelectionController.h"
113
114 #include "mozilla/EventDispatcher.h"
115 #include "nsISHEntry.h"
116 #include "nsISHistory.h"
117 #include "nsISHistoryInternal.h"
118 #include "nsIWebNavigation.h"
119 #include "nsXMLHttpRequest.h"
120
121 //paint forcing
122 #include <stdio.h>
123
124 #include "mozilla/dom/Element.h"
125
126 using namespace mozilla;
127 using namespace mozilla::dom;
128
129 #define BEFOREUNLOAD_DISABLED_PREFNAME "dom.disable_beforeunload"
130
131 //-----------------------------------------------------
132 // PR LOGGING
133 #ifdef MOZ_LOGGING
134 #define FORCE_PR_LOG /* Allow logging in the release build */
135 #endif
136
137 #include "prlog.h"
138
139 #ifdef PR_LOGGING
140
141 #ifdef NS_PRINTING
142 static PRLogModuleInfo *
143 GetPrintingLog()
144 {
145 static PRLogModuleInfo *sLog;
146 if (!sLog)
147 sLog = PR_NewLogModule("printing");
148 return sLog;
149 }
150 #define PR_PL(_p1) PR_LOG(GetPrintingLog(), PR_LOG_DEBUG, _p1);
151 #endif // NS_PRINTING
152
153 #define PRT_YESNO(_p) ((_p)?"YES":"NO")
154 #else
155 #define PRT_YESNO(_p)
156 #define PR_PL(_p1)
157 #endif
158 //-----------------------------------------------------
159
160 class nsDocumentViewer;
161 class nsPrintEventDispatcher;
162
163 // a small delegate class used to avoid circular references
164
165 class nsDocViewerSelectionListener : public nsISelectionListener
166 {
167 public:
168
169 // nsISupports interface...
170 NS_DECL_ISUPPORTS
171
172 // nsISelectionListerner interface
173 NS_DECL_NSISELECTIONLISTENER
174
175 nsDocViewerSelectionListener()
176 : mDocViewer(nullptr)
177 , mGotSelectionState(false)
178 , mSelectionWasCollapsed(false)
179 {
180 }
181
182 virtual ~nsDocViewerSelectionListener() {}
183
184 nsresult Init(nsDocumentViewer *aDocViewer);
185
186 protected:
187
188 nsDocumentViewer* mDocViewer;
189 bool mGotSelectionState;
190 bool mSelectionWasCollapsed;
191
192 };
193
194
195 /** editor Implementation of the FocusListener interface
196 */
197 class nsDocViewerFocusListener : public nsIDOMEventListener
198 {
199 public:
200 /** default constructor
201 */
202 nsDocViewerFocusListener();
203 /** default destructor
204 */
205 virtual ~nsDocViewerFocusListener();
206
207 NS_DECL_ISUPPORTS
208 NS_DECL_NSIDOMEVENTLISTENER
209
210 nsresult Init(nsDocumentViewer *aDocViewer);
211
212 private:
213 nsDocumentViewer* mDocViewer;
214 };
215
216
217 //-------------------------------------------------------------
218 class nsDocumentViewer : public nsIContentViewer,
219 public nsIContentViewerEdit,
220 public nsIContentViewerFile,
221 public nsIMarkupDocumentViewer,
222 public nsIDocumentViewerPrint
223
224 #ifdef NS_PRINTING
225 , public nsIWebBrowserPrint
226 #endif
227
228 {
229 friend class nsDocViewerSelectionListener;
230 friend class nsPagePrintTimer;
231 friend class nsPrintEngine;
232
233 public:
234 nsDocumentViewer();
235
236 NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
237
238 // nsISupports interface...
239 NS_DECL_ISUPPORTS
240
241 // nsIContentViewer interface...
242 NS_DECL_NSICONTENTVIEWER
243
244 // nsIContentViewerEdit
245 NS_DECL_NSICONTENTVIEWEREDIT
246
247 // nsIContentViewerFile
248 NS_DECL_NSICONTENTVIEWERFILE
249
250 // nsIMarkupDocumentViewer
251 NS_DECL_NSIMARKUPDOCUMENTVIEWER
252
253 #ifdef NS_PRINTING
254 // nsIWebBrowserPrint
255 NS_DECL_NSIWEBBROWSERPRINT
256 #endif
257
258 typedef void (*CallChildFunc)(nsIMarkupDocumentViewer* aViewer,
259 void* aClosure);
260 void CallChildren(CallChildFunc aFunc, void* aClosure);
261
262 // nsIDocumentViewerPrint Printing Methods
263 NS_DECL_NSIDOCUMENTVIEWERPRINT
264
265
266 static void DispatchBeforePrint(nsIDocument* aTop)
267 {
268 DispatchEventToWindowTree(aTop, NS_LITERAL_STRING("beforeprint"));
269 }
270 static void DispatchAfterPrint(nsIDocument* aTop)
271 {
272 DispatchEventToWindowTree(aTop, NS_LITERAL_STRING("afterprint"));
273 }
274 static void DispatchEventToWindowTree(nsIDocument* aTop,
275 const nsAString& aEvent);
276
277 protected:
278 virtual ~nsDocumentViewer();
279
280 private:
281 /**
282 * Creates a view manager, root view, and widget for the root view, setting
283 * mViewManager and mWindow.
284 * @param aSize the initial size in appunits
285 * @param aContainerView the container view to hook our root view up
286 * to as a child, or null if this will be the root view manager
287 */
288 nsresult MakeWindow(const nsSize& aSize, nsView* aContainerView);
289
290 /**
291 * Create our device context
292 */
293 nsresult CreateDeviceContext(nsView* aContainerView);
294
295 /**
296 * If aDoCreation is true, this creates the device context, creates a
297 * prescontext if necessary, and calls MakeWindow.
298 *
299 * If aForceSetNewDocument is false, then SetNewDocument won't be
300 * called if the window's current document is already mDocument.
301 */
302 nsresult InitInternal(nsIWidget* aParentWidget,
303 nsISupports *aState,
304 const nsIntRect& aBounds,
305 bool aDoCreation,
306 bool aNeedMakeCX = true,
307 bool aForceSetNewDocument = true);
308 /**
309 * @param aDoInitialReflow set to true if you want to kick off the initial
310 * reflow
311 */
312 nsresult InitPresentationStuff(bool aDoInitialReflow);
313
314 nsresult GetPopupNode(nsIDOMNode** aNode);
315 nsresult GetPopupLinkNode(nsIDOMNode** aNode);
316 nsresult GetPopupImageNode(nsIImageLoadingContent** aNode);
317
318 void PrepareToStartLoad(void);
319
320 nsresult SyncParentSubDocMap();
321
322 nsresult GetDocumentSelection(nsISelection **aSelection);
323
324 void DestroyPresShell();
325 void DestroyPresContext();
326
327 #ifdef NS_PRINTING
328 // Called when the DocViewer is notified that the state
329 // of Printing or PP has changed
330 void SetIsPrintingInDocShellTree(nsIDocShellTreeItem* aParentNode,
331 bool aIsPrintingOrPP,
332 bool aStartAtTop);
333 #endif // NS_PRINTING
334
335 // Whether we should attach to the top level widget. This is true if we
336 // are sharing/recycling a single base widget and not creating multiple
337 // child widgets.
338 bool ShouldAttachToTopLevel();
339
340 protected:
341 // These return the current shell/prescontext etc.
342 nsIPresShell* GetPresShell();
343 nsPresContext* GetPresContext();
344 nsViewManager* GetViewManager();
345
346 void DetachFromTopLevelWidget();
347
348 // IMPORTANT: The ownership implicit in the following member
349 // variables has been explicitly checked and set using nsCOMPtr
350 // for owning pointers and raw COM interface pointers for weak
351 // (ie, non owning) references. If you add any members to this
352 // class, please make the ownership explicit (pinkerton, scc).
353
354 WeakPtr<nsDocShell> mContainer; // it owns me!
355 nsWeakPtr mTopContainerWhilePrinting;
356 nsRefPtr<nsDeviceContext> mDeviceContext; // We create and own this baby
357
358 // the following six items are explicitly in this order
359 // so they will be destroyed in the reverse order (pinkerton, scc)
360 nsCOMPtr<nsIDocument> mDocument;
361 nsCOMPtr<nsIWidget> mWindow; // may be null
362 nsRefPtr<nsViewManager> mViewManager;
363 nsRefPtr<nsPresContext> mPresContext;
364 nsCOMPtr<nsIPresShell> mPresShell;
365
366 nsCOMPtr<nsISelectionListener> mSelectionListener;
367 nsRefPtr<nsDocViewerFocusListener> mFocusListener;
368
369 nsCOMPtr<nsIContentViewer> mPreviousViewer;
370 nsCOMPtr<nsISHEntry> mSHEntry;
371
372 nsIWidget* mParentWidget; // purposely won't be ref counted. May be null
373 bool mAttachedToParent; // view is attached to the parent widget
374
375 nsIntRect mBounds;
376
377 // mTextZoom/mPageZoom record the textzoom/pagezoom of the first (galley)
378 // presshell only.
379 float mTextZoom; // Text zoom, defaults to 1.0
380 float mPageZoom;
381 int mMinFontSize;
382
383 int16_t mNumURLStarts;
384 int16_t mDestroyRefCount; // a second "refcount" for the document viewer's "destroy"
385
386 unsigned mStopped : 1;
387 unsigned mLoaded : 1;
388 unsigned mDeferredWindowClose : 1;
389 // document management data
390 // these items are specific to markup documents (html and xml)
391 // may consider splitting these out into a subclass
392 unsigned mIsSticky : 1;
393 unsigned mInPermitUnload : 1;
394 unsigned mInPermitUnloadPrompt: 1;
395
396 #ifdef NS_PRINTING
397 unsigned mClosingWhilePrinting : 1;
398
399 #if NS_PRINT_PREVIEW
400 unsigned mPrintPreviewZoomed : 1;
401
402 // These data members support delayed printing when the document is loading
403 unsigned mPrintIsPending : 1;
404 unsigned mPrintDocIsFullyLoaded : 1;
405 nsCOMPtr<nsIPrintSettings> mCachedPrintSettings;
406 nsCOMPtr<nsIWebProgressListener> mCachedPrintWebProgressListner;
407
408 nsRefPtr<nsPrintEngine> mPrintEngine;
409 float mOriginalPrintPreviewScale;
410 float mPrintPreviewZoom;
411 nsAutoPtr<nsPrintEventDispatcher> mBeforeAndAfterPrint;
412 #endif // NS_PRINT_PREVIEW
413
414 #ifdef DEBUG
415 FILE* mDebugFile;
416 #endif // DEBUG
417 #endif // NS_PRINTING
418
419 /* character set member data */
420 int32_t mHintCharsetSource;
421 nsCString mHintCharset;
422 nsCString mForceCharacterSet;
423
424 bool mIsPageMode;
425 bool mCallerIsClosingWindow;
426 bool mInitializedForPrintPreview;
427 bool mHidden;
428 };
429
430 class nsPrintEventDispatcher
431 {
432 public:
433 nsPrintEventDispatcher(nsIDocument* aTop) : mTop(aTop)
434 {
435 nsDocumentViewer::DispatchBeforePrint(mTop);
436 }
437 ~nsPrintEventDispatcher()
438 {
439 nsDocumentViewer::DispatchAfterPrint(mTop);
440 }
441
442 nsCOMPtr<nsIDocument> mTop;
443 };
444
445 class nsDocumentShownDispatcher : public nsRunnable
446 {
447 public:
448 nsDocumentShownDispatcher(nsCOMPtr<nsIDocument> aDocument)
449 : mDocument(aDocument) {}
450
451 NS_IMETHOD Run() MOZ_OVERRIDE;
452
453 private:
454 nsCOMPtr<nsIDocument> mDocument;
455 };
456
457
458 //------------------------------------------------------------------
459 // nsDocumentViewer
460 //------------------------------------------------------------------
461
462 //------------------------------------------------------------------
463 already_AddRefed<nsIContentViewer>
464 NS_NewContentViewer()
465 {
466 nsRefPtr<nsDocumentViewer> viewer = new nsDocumentViewer();
467 return viewer.forget();
468 }
469
470 void nsDocumentViewer::PrepareToStartLoad()
471 {
472 mStopped = false;
473 mLoaded = false;
474 mAttachedToParent = false;
475 mDeferredWindowClose = false;
476 mCallerIsClosingWindow = false;
477
478 #ifdef NS_PRINTING
479 mPrintIsPending = false;
480 mPrintDocIsFullyLoaded = false;
481 mClosingWhilePrinting = false;
482
483 // Make sure we have destroyed it and cleared the data member
484 if (mPrintEngine) {
485 mPrintEngine->Destroy();
486 mPrintEngine = nullptr;
487 #ifdef NS_PRINT_PREVIEW
488 SetIsPrintPreview(false);
489 #endif
490 }
491
492 #ifdef DEBUG
493 mDebugFile = nullptr;
494 #endif
495
496 #endif // NS_PRINTING
497 }
498
499 // Note: operator new zeros our memory, so no need to init things to null.
500 nsDocumentViewer::nsDocumentViewer()
501 : mTextZoom(1.0), mPageZoom(1.0), mMinFontSize(0),
502 mIsSticky(true),
503 #ifdef NS_PRINT_PREVIEW
504 mPrintPreviewZoom(1.0),
505 #endif
506 mHintCharsetSource(kCharsetUninitialized),
507 mInitializedForPrintPreview(false),
508 mHidden(false)
509 {
510 PrepareToStartLoad();
511 }
512
513 NS_IMPL_ADDREF(nsDocumentViewer)
514 NS_IMPL_RELEASE(nsDocumentViewer)
515
516 NS_INTERFACE_MAP_BEGIN(nsDocumentViewer)
517 NS_INTERFACE_MAP_ENTRY(nsIContentViewer)
518 NS_INTERFACE_MAP_ENTRY(nsIMarkupDocumentViewer)
519 NS_INTERFACE_MAP_ENTRY(nsIContentViewerFile)
520 NS_INTERFACE_MAP_ENTRY(nsIContentViewerEdit)
521 NS_INTERFACE_MAP_ENTRY(nsIDocumentViewerPrint)
522 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentViewer)
523 #ifdef NS_PRINTING
524 NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPrint)
525 #endif
526 NS_INTERFACE_MAP_END
527
528 nsDocumentViewer::~nsDocumentViewer()
529 {
530 if (mDocument) {
531 Close(nullptr);
532 mDocument->Destroy();
533 }
534
535 NS_ASSERTION(!mPresShell && !mPresContext,
536 "User did not call nsIContentViewer::Destroy");
537 if (mPresShell || mPresContext) {
538 // Make sure we don't hand out a reference to the content viewer to
539 // the SHEntry!
540 mSHEntry = nullptr;
541
542 Destroy();
543 }
544
545 // XXX(?) Revoke pending invalidate events
546 }
547
548 /*
549 * This method is called by the Document Loader once a document has
550 * been created for a particular data stream... The content viewer
551 * must cache this document for later use when Init(...) is called.
552 *
553 * This method is also called when an out of band document.write() happens.
554 * In that case, the document passed in is the same as the previous document.
555 */
556 /* virtual */ void
557 nsDocumentViewer::LoadStart(nsIDocument* aDocument)
558 {
559 MOZ_ASSERT(aDocument);
560
561 if (!mDocument) {
562 mDocument = aDocument;
563 }
564 }
565
566 nsresult
567 nsDocumentViewer::SyncParentSubDocMap()
568 {
569 nsCOMPtr<nsIDocShellTreeItem> item(mContainer);
570 nsCOMPtr<nsPIDOMWindow> pwin(do_GetInterface(item));
571 nsCOMPtr<nsIContent> content;
572
573 if (mDocument && pwin) {
574 content = do_QueryInterface(pwin->GetFrameElementInternal());
575 }
576
577 if (content) {
578 nsCOMPtr<nsIDocShellTreeItem> parent;
579 item->GetParent(getter_AddRefs(parent));
580
581 nsCOMPtr<nsIDOMWindow> parent_win(do_GetInterface(parent));
582
583 if (parent_win) {
584 nsCOMPtr<nsIDOMDocument> dom_doc;
585 parent_win->GetDocument(getter_AddRefs(dom_doc));
586
587 nsCOMPtr<nsIDocument> parent_doc(do_QueryInterface(dom_doc));
588
589 if (parent_doc) {
590 if (mDocument &&
591 parent_doc->GetSubDocumentFor(content) != mDocument) {
592 mDocument->SuppressEventHandling(nsIDocument::eEvents,
593 parent_doc->EventHandlingSuppressed());
594 }
595 return parent_doc->SetSubDocumentFor(content->AsElement(), mDocument);
596 }
597 }
598 }
599
600 return NS_OK;
601 }
602
603 NS_IMETHODIMP
604 nsDocumentViewer::SetContainer(nsIDocShell* aContainer)
605 {
606 mContainer = static_cast<nsDocShell*>(aContainer)->asWeakPtr();
607 if (mPresContext) {
608 mPresContext->SetContainer(mContainer);
609 }
610
611 // We're loading a new document into the window where this document
612 // viewer lives, sync the parent document's frame element -> sub
613 // document map
614
615 return SyncParentSubDocMap();
616 }
617
618 NS_IMETHODIMP
619 nsDocumentViewer::GetContainer(nsIDocShell** aResult)
620 {
621 NS_ENSURE_ARG_POINTER(aResult);
622
623 nsCOMPtr<nsIDocShell> container(mContainer);
624 container.swap(*aResult);
625 return NS_OK;
626 }
627
628 NS_IMETHODIMP
629 nsDocumentViewer::Init(nsIWidget* aParentWidget,
630 const nsIntRect& aBounds)
631 {
632 return InitInternal(aParentWidget, nullptr, aBounds, true);
633 }
634
635 nsresult
636 nsDocumentViewer::InitPresentationStuff(bool aDoInitialReflow)
637 {
638 if (GetIsPrintPreview())
639 return NS_OK;
640
641 NS_ASSERTION(!mPresShell,
642 "Someone should have destroyed the presshell!");
643
644 // Create the style set...
645 nsStyleSet *styleSet;
646 nsresult rv = CreateStyleSet(mDocument, &styleSet);
647 NS_ENSURE_SUCCESS(rv, rv);
648
649 // Now make the shell for the document
650 mPresShell = mDocument->CreateShell(mPresContext, mViewManager, styleSet);
651 if (!mPresShell) {
652 delete styleSet;
653 return NS_ERROR_FAILURE;
654 }
655
656 // We're done creating the style set
657 styleSet->EndUpdate();
658
659 if (aDoInitialReflow) {
660 // Since Initialize() will create frames for *all* items
661 // that are currently in the document tree, we need to flush
662 // any pending notifications to prevent the content sink from
663 // duplicating layout frames for content it has added to the tree
664 // but hasn't notified the document about. (Bug 154018)
665 //
666 // Note that we are flushing before we add mPresShell as an observer
667 // to avoid bogus notifications.
668
669 mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
670 }
671
672 mPresShell->BeginObservingDocument();
673
674 // Initialize our view manager
675 int32_t p2a = mPresContext->AppUnitsPerDevPixel();
676 MOZ_ASSERT(p2a == mPresContext->DeviceContext()->UnscaledAppUnitsPerDevPixel());
677 nscoord width = p2a * mBounds.width;
678 nscoord height = p2a * mBounds.height;
679
680 mViewManager->SetWindowDimensions(width, height);
681 mPresContext->SetTextZoom(mTextZoom);
682 mPresContext->SetFullZoom(mPageZoom);
683 mPresContext->SetBaseMinFontSize(mMinFontSize);
684
685 p2a = mPresContext->AppUnitsPerDevPixel(); // zoom may have changed it
686 width = p2a * mBounds.width;
687 height = p2a * mBounds.height;
688 if (aDoInitialReflow) {
689 nsCOMPtr<nsIPresShell> shellGrip = mPresShell;
690 // Initial reflow
691 mPresShell->Initialize(width, height);
692 } else {
693 // Store the visible area so it's available for other callers of
694 // Initialize, like nsContentSink::StartLayout.
695 mPresContext->SetVisibleArea(nsRect(0, 0, width, height));
696 }
697
698 // now register ourselves as a selection listener, so that we get
699 // called when the selection changes in the window
700 if (!mSelectionListener) {
701 nsDocViewerSelectionListener *selectionListener =
702 new nsDocViewerSelectionListener();
703
704 selectionListener->Init(this);
705
706 // mSelectionListener is a owning reference
707 mSelectionListener = selectionListener;
708 }
709
710 nsCOMPtr<nsISelection> selection;
711 rv = GetDocumentSelection(getter_AddRefs(selection));
712 NS_ENSURE_SUCCESS(rv, rv);
713
714 nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(selection));
715 rv = selPrivate->AddSelectionListener(mSelectionListener);
716 if (NS_FAILED(rv))
717 return rv;
718
719 // Save old listener so we can unregister it
720 nsRefPtr<nsDocViewerFocusListener> oldFocusListener = mFocusListener;
721
722 // focus listener
723 //
724 // now register ourselves as a focus listener, so that we get called
725 // when the focus changes in the window
726 nsDocViewerFocusListener *focusListener = new nsDocViewerFocusListener();
727
728 focusListener->Init(this);
729
730 // mFocusListener is a strong reference
731 mFocusListener = focusListener;
732
733 if (mDocument) {
734 mDocument->AddEventListener(NS_LITERAL_STRING("focus"),
735 mFocusListener,
736 false, false);
737 mDocument->AddEventListener(NS_LITERAL_STRING("blur"),
738 mFocusListener,
739 false, false);
740
741 if (oldFocusListener) {
742 mDocument->RemoveEventListener(NS_LITERAL_STRING("focus"),
743 oldFocusListener, false);
744 mDocument->RemoveEventListener(NS_LITERAL_STRING("blur"),
745 oldFocusListener, false);
746 }
747 }
748
749 if (aDoInitialReflow && mDocument) {
750 mDocument->ScrollToRef();
751 }
752
753 return NS_OK;
754 }
755
756 static nsPresContext*
757 CreatePresContext(nsIDocument* aDocument,
758 nsPresContext::nsPresContextType aType,
759 nsView* aContainerView)
760 {
761 if (aContainerView)
762 return new nsPresContext(aDocument, aType);
763 return new nsRootPresContext(aDocument, aType);
764 }
765
766 //-----------------------------------------------
767 // This method can be used to initial the "presentation"
768 // The aDoCreation indicates whether it should create
769 // all the new objects or just initialize the existing ones
770 nsresult
771 nsDocumentViewer::InitInternal(nsIWidget* aParentWidget,
772 nsISupports *aState,
773 const nsIntRect& aBounds,
774 bool aDoCreation,
775 bool aNeedMakeCX /*= true*/,
776 bool aForceSetNewDocument /* = true*/)
777 {
778 if (mIsPageMode) {
779 // XXXbz should the InitInternal in SetPageMode just pass false
780 // here itself?
781 aForceSetNewDocument = false;
782 }
783
784 // We don't want any scripts to run here. That can cause flushing,
785 // which can cause reentry into initialization of this document viewer,
786 // which would be disastrous.
787 nsAutoScriptBlocker blockScripts;
788
789 mParentWidget = aParentWidget; // not ref counted
790 mBounds = aBounds;
791
792 nsresult rv = NS_OK;
793 NS_ENSURE_TRUE(mDocument, NS_ERROR_NULL_POINTER);
794
795 nsView* containerView = FindContainerView();
796
797 bool makeCX = false;
798 if (aDoCreation) {
799 nsresult rv = CreateDeviceContext(containerView);
800 NS_ENSURE_SUCCESS(rv, rv);
801
802 // XXXbz this is a nasty hack to do with the fact that we create
803 // presentations both in Init() and in Show()... Ideally we would only do
804 // it in one place (Show()) and require that callers call init(), open(),
805 // show() in that order or something.
806 if (!mPresContext &&
807 (aParentWidget || containerView || mDocument->IsBeingUsedAsImage() ||
808 (mDocument->GetDisplayDocument() &&
809 mDocument->GetDisplayDocument()->GetShell()))) {
810 // Create presentation context
811 if (mIsPageMode) {
812 //Presentation context already created in SetPageMode which is calling this method
813 } else {
814 mPresContext = CreatePresContext(mDocument,
815 nsPresContext::eContext_Galley, containerView);
816 }
817 NS_ENSURE_TRUE(mPresContext, NS_ERROR_OUT_OF_MEMORY);
818
819 nsresult rv = mPresContext->Init(mDeviceContext);
820 if (NS_FAILED(rv)) {
821 mPresContext = nullptr;
822 return rv;
823 }
824
825 #if defined(NS_PRINTING) && defined(NS_PRINT_PREVIEW)
826 makeCX = !GetIsPrintPreview() && aNeedMakeCX; // needs to be true except when we are already in PP or we are enabling/disabling paginated mode.
827 #else
828 makeCX = true;
829 #endif
830 }
831
832 if (mPresContext) {
833 // Create the ViewManager and Root View...
834
835 // We must do this before we tell the script global object about
836 // this new document since doing that will cause us to re-enter
837 // into nsSubDocumentFrame code through reflows caused by
838 // FlushPendingNotifications() calls down the road...
839
840 rv = MakeWindow(nsSize(mPresContext->DevPixelsToAppUnits(aBounds.width),
841 mPresContext->DevPixelsToAppUnits(aBounds.height)),
842 containerView);
843 NS_ENSURE_SUCCESS(rv, rv);
844 Hide();
845
846 #ifdef NS_PRINT_PREVIEW
847 if (mIsPageMode) {
848 // I'm leaving this in a broken state for the moment; we should
849 // be measuring/scaling with the print device context, not the
850 // screen device context, but this is good enough to allow
851 // printing reftests to work.
852 double pageWidth = 0, pageHeight = 0;
853 mPresContext->GetPrintSettings()->GetEffectivePageSize(&pageWidth,
854 &pageHeight);
855 mPresContext->SetPageSize(
856 nsSize(mPresContext->CSSTwipsToAppUnits(NSToIntFloor(pageWidth)),
857 mPresContext->CSSTwipsToAppUnits(NSToIntFloor(pageHeight))));
858 mPresContext->SetIsRootPaginatedDocument(true);
859 mPresContext->SetPageScale(1.0f);
860 }
861 #endif
862 } else {
863 // Avoid leaking the old viewer.
864 if (mPreviousViewer) {
865 mPreviousViewer->Destroy();
866 mPreviousViewer = nullptr;
867 }
868 }
869 }
870
871 nsCOMPtr<nsIInterfaceRequestor> requestor(mContainer);
872 if (requestor) {
873 if (mPresContext) {
874 nsCOMPtr<nsILinkHandler> linkHandler;
875 requestor->GetInterface(NS_GET_IID(nsILinkHandler),
876 getter_AddRefs(linkHandler));
877
878 mPresContext->SetContainer(mContainer);
879 mPresContext->SetLinkHandler(linkHandler);
880 }
881
882 // Set script-context-owner in the document
883
884 nsCOMPtr<nsPIDOMWindow> window;
885 requestor->GetInterface(NS_GET_IID(nsPIDOMWindow),
886 getter_AddRefs(window));
887
888 if (window) {
889 nsCOMPtr<nsIDocument> curDoc = window->GetExtantDoc();
890 if (aForceSetNewDocument || curDoc != mDocument) {
891 window->SetNewDocument(mDocument, aState, false);
892 nsJSContext::LoadStart();
893 }
894 }
895 }
896
897 if (aDoCreation && mPresContext) {
898 // The ViewManager and Root View was created above (in
899 // MakeWindow())...
900
901 rv = InitPresentationStuff(!makeCX);
902 }
903
904 return rv;
905 }
906
907 void nsDocumentViewer::SetNavigationTiming(nsDOMNavigationTiming* timing)
908 {
909 NS_ASSERTION(mDocument, "Must have a document to set navigation timing.");
910 if (mDocument) {
911 mDocument->SetNavigationTiming(timing);
912 }
913 }
914
915 //
916 // LoadComplete(aStatus)
917 //
918 // aStatus - The status returned from loading the document.
919 //
920 // This method is called by the container when the document has been
921 // completely loaded.
922 //
923 NS_IMETHODIMP
924 nsDocumentViewer::LoadComplete(nsresult aStatus)
925 {
926 /* We need to protect ourself against auto-destruction in case the
927 window is closed while processing the OnLoad event. See bug
928 http://bugzilla.mozilla.org/show_bug.cgi?id=78445 for more
929 explanation.
930 */
931 nsRefPtr<nsDocumentViewer> kungFuDeathGrip(this);
932
933 // Flush out layout so it's up-to-date by the time onload is called.
934 // Note that this could destroy the window, so do this before
935 // checking for our mDocument and its window.
936 if (mPresShell && !mStopped) {
937 // Hold strong ref because this could conceivably run script
938 nsCOMPtr<nsIPresShell> shell = mPresShell;
939 shell->FlushPendingNotifications(Flush_Layout);
940 }
941
942 nsresult rv = NS_OK;
943 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
944
945 // First, get the window from the document...
946 nsCOMPtr<nsPIDOMWindow> window = mDocument->GetWindow();
947
948 mLoaded = true;
949
950 // Now, fire either an OnLoad or OnError event to the document...
951 bool restoring = false;
952 // XXXbz imagelib kills off the document load for a full-page image with
953 // NS_ERROR_PARSED_DATA_CACHED if it's in the cache. So we want to treat
954 // that one as a success code; otherwise whether we fire onload for the image
955 // will depend on whether it's cached!
956 if(window &&
957 (NS_SUCCEEDED(aStatus) || aStatus == NS_ERROR_PARSED_DATA_CACHED)) {
958 nsEventStatus status = nsEventStatus_eIgnore;
959 WidgetEvent event(true, NS_LOAD);
960 event.mFlags.mBubbles = false;
961 // XXX Dispatching to |window|, but using |document| as the target.
962 event.target = mDocument;
963
964 // If the document presentation is being restored, we don't want to fire
965 // onload to the document content since that would likely confuse scripts
966 // on the page.
967
968 nsIDocShell *docShell = window->GetDocShell();
969 NS_ENSURE_TRUE(docShell, NS_ERROR_UNEXPECTED);
970
971 docShell->GetRestoringDocument(&restoring);
972 if (!restoring) {
973 NS_ASSERTION(mDocument->IsXUL() || // readyState for XUL is bogus
974 mDocument->GetReadyStateEnum() ==
975 nsIDocument::READYSTATE_INTERACTIVE ||
976 // test_stricttransportsecurity.html has old-style
977 // docshell-generated about:blank docs reach this code!
978 (mDocument->GetReadyStateEnum() ==
979 nsIDocument::READYSTATE_UNINITIALIZED &&
980 NS_IsAboutBlank(mDocument->GetDocumentURI())),
981 "Bad readystate");
982 nsCOMPtr<nsIDocument> d = mDocument;
983 mDocument->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE);
984
985 nsRefPtr<nsDOMNavigationTiming> timing(d->GetNavigationTiming());
986 if (timing) {
987 timing->NotifyLoadEventStart();
988 }
989
990 // Dispatch observer notification to notify observers document load is complete.
991 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
992 nsIPrincipal *principal = d->NodePrincipal();
993 os->NotifyObservers(d,
994 nsContentUtils::IsSystemPrincipal(principal) ?
995 "chrome-document-loaded" :
996 "content-document-loaded",
997 nullptr);
998
999 EventDispatcher::Dispatch(window, mPresContext, &event, nullptr, &status);
1000 if (timing) {
1001 timing->NotifyLoadEventEnd();
1002 }
1003 }
1004 } else {
1005 // XXX: Should fire error event to the document...
1006 }
1007
1008 // Notify the document that it has been shown (regardless of whether
1009 // it was just loaded). Note: mDocument may be null now if the above
1010 // firing of onload caused the document to unload.
1011 if (mDocument) {
1012 // Re-get window, since it might have changed during above firing of onload
1013 window = mDocument->GetWindow();
1014 if (window) {
1015 nsIDocShell *docShell = window->GetDocShell();
1016 bool isInUnload;
1017 if (docShell && NS_SUCCEEDED(docShell->GetIsInUnload(&isInUnload)) &&
1018 !isInUnload) {
1019 mDocument->OnPageShow(restoring, nullptr);
1020 }
1021 }
1022 }
1023
1024 if (!mStopped) {
1025 if (mDocument) {
1026 mDocument->ScrollToRef();
1027 }
1028
1029 // Now that the document has loaded, we can tell the presshell
1030 // to unsuppress painting.
1031 if (mPresShell) {
1032 nsCOMPtr<nsIPresShell> shellDeathGrip(mPresShell);
1033 mPresShell->UnsuppressPainting();
1034 // mPresShell could have been removed now, see bug 378682/421432
1035 if (mPresShell) {
1036 mPresShell->LoadComplete();
1037 }
1038 }
1039 }
1040
1041 nsJSContext::LoadEnd();
1042
1043 #ifdef NS_PRINTING
1044 // Check to see if someone tried to print during the load
1045 if (mPrintIsPending) {
1046 mPrintIsPending = false;
1047 mPrintDocIsFullyLoaded = true;
1048 Print(mCachedPrintSettings, mCachedPrintWebProgressListner);
1049 mCachedPrintSettings = nullptr;
1050 mCachedPrintWebProgressListner = nullptr;
1051 }
1052 #endif
1053
1054 return rv;
1055 }
1056
1057 NS_IMETHODIMP
1058 nsDocumentViewer::PermitUnload(bool aCallerClosesWindow,
1059 bool *aPermitUnload)
1060 {
1061 bool shouldPrompt = true;
1062 return PermitUnloadInternal(aCallerClosesWindow, &shouldPrompt,
1063 aPermitUnload);
1064 }
1065
1066
1067 nsresult
1068 nsDocumentViewer::PermitUnloadInternal(bool aCallerClosesWindow,
1069 bool *aShouldPrompt,
1070 bool *aPermitUnload)
1071 {
1072 AutoDontWarnAboutSyncXHR disableSyncXHRWarning;
1073
1074 *aPermitUnload = true;
1075
1076 if (!mDocument
1077 || mInPermitUnload
1078 || mCallerIsClosingWindow
1079 || mInPermitUnloadPrompt) {
1080 return NS_OK;
1081 }
1082
1083 static bool sIsBeforeUnloadDisabled;
1084 static bool sBeforeUnloadPrefCached = false;
1085
1086 if (!sBeforeUnloadPrefCached ) {
1087 sBeforeUnloadPrefCached = true;
1088 Preferences::AddBoolVarCache(&sIsBeforeUnloadDisabled,
1089 BEFOREUNLOAD_DISABLED_PREFNAME);
1090 }
1091
1092 // If the user has turned off onbeforeunload warnings, no need to check.
1093 if (sIsBeforeUnloadDisabled) {
1094 return NS_OK;
1095 }
1096
1097 // First, get the script global object from the document...
1098 nsPIDOMWindow *window = mDocument->GetWindow();
1099
1100 if (!window) {
1101 // This is odd, but not fatal
1102 NS_WARNING("window not set for document!");
1103 return NS_OK;
1104 }
1105
1106 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), "This is unsafe");
1107
1108 // Now, fire an BeforeUnload event to the document and see if it's ok
1109 // to unload...
1110 nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(mDocument);
1111 nsCOMPtr<nsIDOMEvent> event;
1112 domDoc->CreateEvent(NS_LITERAL_STRING("beforeunloadevent"),
1113 getter_AddRefs(event));
1114 nsCOMPtr<nsIDOMBeforeUnloadEvent> beforeUnload = do_QueryInterface(event);
1115 NS_ENSURE_STATE(beforeUnload);
1116 nsresult rv = event->InitEvent(NS_LITERAL_STRING("beforeunload"),
1117 false, true);
1118 NS_ENSURE_SUCCESS(rv, rv);
1119
1120 // Dispatching to |window|, but using |document| as the target.
1121 event->SetTarget(mDocument);
1122 event->SetTrusted(true);
1123
1124 // In evil cases we might be destroyed while handling the
1125 // onbeforeunload event, don't let that happen. (see also bug#331040)
1126 nsRefPtr<nsDocumentViewer> kungFuDeathGrip(this);
1127
1128 {
1129 // Never permit popups from the beforeunload handler, no matter
1130 // how we get here.
1131 nsAutoPopupStatePusher popupStatePusher(openAbused, true);
1132
1133 // Never permit dialogs from the beforeunload handler
1134 nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(window);
1135 bool dialogsWereEnabled = false;
1136 utils->AreDialogsEnabled(&dialogsWereEnabled);
1137 utils->DisableDialogs();
1138
1139 mInPermitUnload = true;
1140 EventDispatcher::DispatchDOMEvent(window, nullptr, event, mPresContext,
1141 nullptr);
1142 mInPermitUnload = false;
1143 if (dialogsWereEnabled) {
1144 utils->EnableDialogs();
1145 }
1146 }
1147
1148 nsCOMPtr<nsIDocShell> docShell(mContainer);
1149 nsAutoString text;
1150 beforeUnload->GetReturnValue(text);
1151 if (*aShouldPrompt && (event->GetInternalNSEvent()->mFlags.mDefaultPrevented ||
1152 !text.IsEmpty())) {
1153 // Ask the user if it's ok to unload the current page
1154
1155 nsCOMPtr<nsIPrompt> prompt = do_GetInterface(docShell);
1156
1157 if (prompt) {
1158 nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt);
1159 if (promptBag) {
1160 bool isTabModalPromptAllowed;
1161 GetIsTabModalPromptAllowed(&isTabModalPromptAllowed);
1162 promptBag->SetPropertyAsBool(NS_LITERAL_STRING("allowTabModal"),
1163 isTabModalPromptAllowed);
1164 }
1165
1166 nsXPIDLString title, message, stayLabel, leaveLabel;
1167 rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
1168 "OnBeforeUnloadTitle",
1169 title);
1170 nsresult tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
1171 "OnBeforeUnloadMessage",
1172 message);
1173 if (NS_FAILED(tmp)) {
1174 rv = tmp;
1175 }
1176 tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
1177 "OnBeforeUnloadLeaveButton",
1178 leaveLabel);
1179 if (NS_FAILED(tmp)) {
1180 rv = tmp;
1181 }
1182 tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
1183 "OnBeforeUnloadStayButton",
1184 stayLabel);
1185 if (NS_FAILED(tmp)) {
1186 rv = tmp;
1187 }
1188
1189 if (NS_FAILED(rv) || !title || !message || !stayLabel || !leaveLabel) {
1190 NS_ERROR("Failed to get strings from dom.properties!");
1191 return NS_OK;
1192 }
1193
1194 // Although the exact value is ignored, we must not pass invalid
1195 // bool values through XPConnect.
1196 bool dummy = false;
1197 int32_t buttonPressed = 0;
1198 uint32_t buttonFlags = (nsIPrompt::BUTTON_POS_0_DEFAULT |
1199 (nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_0) |
1200 (nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_1));
1201
1202 nsAutoSyncOperation sync(mDocument);
1203 mInPermitUnloadPrompt = true;
1204 rv = prompt->ConfirmEx(title, message, buttonFlags,
1205 leaveLabel, stayLabel, nullptr, nullptr,
1206 &dummy, &buttonPressed);
1207 mInPermitUnloadPrompt = false;
1208
1209 // If the prompt aborted, we tell our consumer that it is not allowed
1210 // to unload the page. One reason that prompts abort is that the user
1211 // performed some action that caused the page to unload while our prompt
1212 // was active. In those cases we don't want our consumer to also unload
1213 // the page.
1214 //
1215 // XXX: Are there other cases where prompts can abort? Is it ok to
1216 // prevent unloading the page in those cases?
1217 if (NS_FAILED(rv)) {
1218 *aPermitUnload = false;
1219 return NS_OK;
1220 }
1221
1222 // Button 0 == leave, button 1 == stay
1223 *aPermitUnload = (buttonPressed == 0);
1224 // If the user decided to go ahead, make sure not to prompt the user again
1225 // by toggling the internal prompting bool to false:
1226 if (*aPermitUnload) {
1227 *aShouldPrompt = false;
1228 }
1229 }
1230 }
1231
1232 if (docShell) {
1233 int32_t childCount;
1234 docShell->GetChildCount(&childCount);
1235
1236 for (int32_t i = 0; i < childCount && *aPermitUnload; ++i) {
1237 nsCOMPtr<nsIDocShellTreeItem> item;
1238 docShell->GetChildAt(i, getter_AddRefs(item));
1239
1240 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(item));
1241
1242 if (docShell) {
1243 nsCOMPtr<nsIContentViewer> cv;
1244 docShell->GetContentViewer(getter_AddRefs(cv));
1245
1246 if (cv) {
1247 cv->PermitUnloadInternal(aCallerClosesWindow, aShouldPrompt,
1248 aPermitUnload);
1249 }
1250 }
1251 }
1252 }
1253
1254 if (aCallerClosesWindow && *aPermitUnload)
1255 mCallerIsClosingWindow = true;
1256
1257 return NS_OK;
1258 }
1259
1260 NS_IMETHODIMP
1261 nsDocumentViewer::GetBeforeUnloadFiring(bool* aInEvent)
1262 {
1263 *aInEvent = mInPermitUnload;
1264 return NS_OK;
1265 }
1266
1267 NS_IMETHODIMP
1268 nsDocumentViewer::GetInPermitUnload(bool* aInEvent)
1269 {
1270 *aInEvent = mInPermitUnloadPrompt;
1271 return NS_OK;
1272 }
1273
1274 NS_IMETHODIMP
1275 nsDocumentViewer::ResetCloseWindow()
1276 {
1277 mCallerIsClosingWindow = false;
1278
1279 nsCOMPtr<nsIDocShell> docShell(mContainer);
1280 if (docShell) {
1281 int32_t childCount;
1282 docShell->GetChildCount(&childCount);
1283
1284 for (int32_t i = 0; i < childCount; ++i) {
1285 nsCOMPtr<nsIDocShellTreeItem> item;
1286 docShell->GetChildAt(i, getter_AddRefs(item));
1287
1288 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(item));
1289
1290 if (docShell) {
1291 nsCOMPtr<nsIContentViewer> cv;
1292 docShell->GetContentViewer(getter_AddRefs(cv));
1293
1294 if (cv) {
1295 cv->ResetCloseWindow();
1296 }
1297 }
1298 }
1299 }
1300 return NS_OK;
1301 }
1302
1303 NS_IMETHODIMP
1304 nsDocumentViewer::PageHide(bool aIsUnload)
1305 {
1306 AutoDontWarnAboutSyncXHR disableSyncXHRWarning;
1307
1308 mHidden = true;
1309
1310 if (!mDocument) {
1311 return NS_ERROR_NULL_POINTER;
1312 }
1313
1314 mDocument->OnPageHide(!aIsUnload, nullptr);
1315
1316 // inform the window so that the focus state is reset.
1317 NS_ENSURE_STATE(mDocument);
1318 nsPIDOMWindow *window = mDocument->GetWindow();
1319 if (window)
1320 window->PageHidden();
1321
1322 if (aIsUnload) {
1323 // Poke the GC. The window might be collectable garbage now.
1324 nsJSContext::PokeGC(JS::gcreason::PAGE_HIDE, NS_GC_DELAY * 2);
1325
1326 // if Destroy() was called during OnPageHide(), mDocument is nullptr.
1327 NS_ENSURE_STATE(mDocument);
1328
1329 // First, get the window from the document...
1330 nsPIDOMWindow *window = mDocument->GetWindow();
1331
1332 if (!window) {
1333 // Fail if no window is available...
1334 NS_WARNING("window not set for document!");
1335 return NS_ERROR_NULL_POINTER;
1336 }
1337
1338 // Now, fire an Unload event to the document...
1339 nsEventStatus status = nsEventStatus_eIgnore;
1340 WidgetEvent event(true, NS_PAGE_UNLOAD);
1341 event.mFlags.mBubbles = false;
1342 // XXX Dispatching to |window|, but using |document| as the target.
1343 event.target = mDocument;
1344
1345 // Never permit popups from the unload handler, no matter how we get
1346 // here.
1347 nsAutoPopupStatePusher popupStatePusher(openAbused, true);
1348
1349 EventDispatcher::Dispatch(window, mPresContext, &event, nullptr, &status);
1350 }
1351
1352 #ifdef MOZ_XUL
1353 // look for open menupopups and close them after the unload event, in case
1354 // the unload event listeners open any new popups
1355 nsContentUtils::HidePopupsInDocument(mDocument);
1356 #endif
1357
1358 return NS_OK;
1359 }
1360
1361 static void
1362 AttachContainerRecurse(nsIDocShell* aShell)
1363 {
1364 nsCOMPtr<nsIContentViewer> viewer;
1365 aShell->GetContentViewer(getter_AddRefs(viewer));
1366 if (viewer) {
1367 nsIDocument* doc = viewer->GetDocument();
1368 if (doc) {
1369 doc->SetContainer(static_cast<nsDocShell*>(aShell));
1370 }
1371 nsRefPtr<nsPresContext> pc;
1372 viewer->GetPresContext(getter_AddRefs(pc));
1373 if (pc) {
1374 pc->SetContainer(static_cast<nsDocShell*>(aShell));
1375 pc->SetLinkHandler(nsCOMPtr<nsILinkHandler>(do_QueryInterface(aShell)));
1376 }
1377 nsCOMPtr<nsIPresShell> presShell;
1378 viewer->GetPresShell(getter_AddRefs(presShell));
1379 if (presShell) {
1380 presShell->SetForwardingContainer(WeakPtr<nsDocShell>());
1381 }
1382 }
1383
1384 // Now recurse through the children
1385 int32_t childCount;
1386 aShell->GetChildCount(&childCount);
1387 for (int32_t i = 0; i < childCount; ++i) {
1388 nsCOMPtr<nsIDocShellTreeItem> childItem;
1389 aShell->GetChildAt(i, getter_AddRefs(childItem));
1390 AttachContainerRecurse(nsCOMPtr<nsIDocShell>(do_QueryInterface(childItem)));
1391 }
1392 }
1393
1394 NS_IMETHODIMP
1395 nsDocumentViewer::Open(nsISupports *aState, nsISHEntry *aSHEntry)
1396 {
1397 NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
1398
1399 if (mDocument)
1400 mDocument->SetContainer(mContainer);
1401
1402 nsresult rv = InitInternal(mParentWidget, aState, mBounds, false);
1403 NS_ENSURE_SUCCESS(rv, rv);
1404
1405 mHidden = false;
1406
1407 if (mPresShell)
1408 mPresShell->SetForwardingContainer(WeakPtr<nsDocShell>());
1409
1410 // Rehook the child presentations. The child shells are still in
1411 // session history, so get them from there.
1412
1413 if (aSHEntry) {
1414 nsCOMPtr<nsIDocShellTreeItem> item;
1415 int32_t itemIndex = 0;
1416 while (NS_SUCCEEDED(aSHEntry->ChildShellAt(itemIndex++,
1417 getter_AddRefs(item))) && item) {
1418 AttachContainerRecurse(nsCOMPtr<nsIDocShell>(do_QueryInterface(item)));
1419 }
1420 }
1421
1422 SyncParentSubDocMap();
1423
1424 if (mFocusListener && mDocument) {
1425 mDocument->AddEventListener(NS_LITERAL_STRING("focus"), mFocusListener,
1426 false, false);
1427 mDocument->AddEventListener(NS_LITERAL_STRING("blur"), mFocusListener,
1428 false, false);
1429 }
1430
1431 // XXX re-enable image animations once that works correctly
1432
1433 PrepareToStartLoad();
1434
1435 // When loading a page from the bfcache with puppet widgets, we do the
1436 // widget attachment here (it is otherwise done in MakeWindow, which is
1437 // called for non-bfcache pages in the history, but not bfcache pages).
1438 // Attachment is necessary, since we get detached when another page
1439 // is browsed to. That is, if we are one page A, then when we go to
1440 // page B, we detach. So page A's view has no widget. If we then go
1441 // back to it, and it is in the bfcache, we will use that view, which
1442 // doesn't have a widget. The attach call here will properly attach us.
1443 if (nsIWidget::UsePuppetWidgets() && mPresContext &&
1444 ShouldAttachToTopLevel()) {
1445 // If the old view is already attached to our parent, detach
1446 DetachFromTopLevelWidget();
1447
1448 nsViewManager *vm = GetViewManager();
1449 NS_ABORT_IF_FALSE(vm, "no view manager");
1450 nsView* v = vm->GetRootView();
1451 NS_ABORT_IF_FALSE(v, "no root view");
1452 NS_ABORT_IF_FALSE(mParentWidget, "no mParentWidget to set");
1453 v->AttachToTopLevelWidget(mParentWidget);
1454
1455 mAttachedToParent = true;
1456 }
1457
1458 return NS_OK;
1459 }
1460
1461 NS_IMETHODIMP
1462 nsDocumentViewer::Close(nsISHEntry *aSHEntry)
1463 {
1464 // All callers are supposed to call close to break circular
1465 // references. If we do this stuff in the destructor, the
1466 // destructor might never be called (especially if we're being
1467 // used from JS.
1468
1469 mSHEntry = aSHEntry;
1470
1471 // Close is also needed to disable scripts during paint suppression,
1472 // since we transfer the existing global object to the new document
1473 // that is loaded. In the future, the global object may become a proxy
1474 // for an object that can be switched in and out so that we don't need
1475 // to disable scripts during paint suppression.
1476
1477 if (!mDocument)
1478 return NS_OK;
1479
1480 #if defined(NS_PRINTING) && defined(NS_PRINT_PREVIEW)
1481 // Turn scripting back on
1482 // after PrintPreview had turned it off
1483 if (GetIsPrintPreview() && mPrintEngine) {
1484 mPrintEngine->TurnScriptingOn(true);
1485 }
1486 #endif
1487
1488 #ifdef NS_PRINTING
1489 // A Close was called while we were printing
1490 // so don't clear the ScriptGlobalObject
1491 // or clear the mDocument below
1492 if (mPrintEngine && !mClosingWhilePrinting) {
1493 mClosingWhilePrinting = true;
1494 } else
1495 #endif
1496 {
1497 // out of band cleanup of docshell
1498 mDocument->SetScriptGlobalObject(nullptr);
1499
1500 if (!mSHEntry && mDocument)
1501 mDocument->RemovedFromDocShell();
1502 }
1503
1504 if (mFocusListener && mDocument) {
1505 mDocument->RemoveEventListener(NS_LITERAL_STRING("focus"), mFocusListener,
1506 false);
1507 mDocument->RemoveEventListener(NS_LITERAL_STRING("blur"), mFocusListener,
1508 false);
1509 }
1510
1511 return NS_OK;
1512 }
1513
1514 static void
1515 DetachContainerRecurse(nsIDocShell *aShell)
1516 {
1517 // Unhook this docshell's presentation
1518 nsCOMPtr<nsIContentViewer> viewer;
1519 aShell->GetContentViewer(getter_AddRefs(viewer));
1520 if (viewer) {
1521 nsIDocument* doc = viewer->GetDocument();
1522 if (doc) {
1523 doc->SetContainer(nullptr);
1524 }
1525 nsRefPtr<nsPresContext> pc;
1526 viewer->GetPresContext(getter_AddRefs(pc));
1527 if (pc) {
1528 pc->Detach();
1529 }
1530 nsCOMPtr<nsIPresShell> presShell;
1531 viewer->GetPresShell(getter_AddRefs(presShell));
1532 if (presShell) {
1533 auto weakShell = static_cast<nsDocShell*>(aShell)->asWeakPtr();
1534 presShell->SetForwardingContainer(weakShell);
1535 }
1536 }
1537
1538 // Now recurse through the children
1539 int32_t childCount;
1540 aShell->GetChildCount(&childCount);
1541 for (int32_t i = 0; i < childCount; ++i) {
1542 nsCOMPtr<nsIDocShellTreeItem> childItem;
1543 aShell->GetChildAt(i, getter_AddRefs(childItem));
1544 DetachContainerRecurse(nsCOMPtr<nsIDocShell>(do_QueryInterface(childItem)));
1545 }
1546 }
1547
1548 NS_IMETHODIMP
1549 nsDocumentViewer::Destroy()
1550 {
1551 NS_ASSERTION(mDocument, "No document in Destroy()!");
1552
1553 #ifdef NS_PRINTING
1554 // Here is where we check to see if the document was still being prepared
1555 // for printing when it was asked to be destroy from someone externally
1556 // This usually happens if the document is unloaded while the user is in the
1557 // Print Dialog
1558 //
1559 // So we flip the bool to remember that the document is going away
1560 // and we can clean up and abort later after returning from the Print Dialog
1561 if (mPrintEngine) {
1562 if (mPrintEngine->CheckBeforeDestroy()) {
1563 return NS_OK;
1564 }
1565 }
1566 mBeforeAndAfterPrint = nullptr;
1567 #endif
1568
1569 // Don't let the document get unloaded while we are printing.
1570 // this could happen if we hit the back button during printing.
1571 // We also keep the viewer from being cached in session history, since
1572 // we require all documents there to be sanitized.
1573 if (mDestroyRefCount != 0) {
1574 --mDestroyRefCount;
1575 return NS_OK;
1576 }
1577
1578 // If we were told to put ourselves into session history instead of destroy
1579 // the presentation, do that now.
1580 if (mSHEntry) {
1581 if (mPresShell)
1582 mPresShell->Freeze();
1583
1584 // Make sure the presentation isn't torn down by Hide().
1585 mSHEntry->SetSticky(mIsSticky);
1586 mIsSticky = true;
1587
1588 bool savePresentation = mDocument ? mDocument->IsBFCachingAllowed() : true;
1589
1590 // Remove our root view from the view hierarchy.
1591 if (mPresShell) {
1592 nsViewManager *vm = mPresShell->GetViewManager();
1593 if (vm) {
1594 nsView *rootView = vm->GetRootView();
1595
1596 if (rootView) {
1597 nsView *rootViewParent = rootView->GetParent();
1598 if (rootViewParent) {
1599 nsViewManager *parentVM = rootViewParent->GetViewManager();
1600 if (parentVM) {
1601 parentVM->RemoveChild(rootView);
1602 }
1603 }
1604 }
1605 }
1606 }
1607
1608 Hide();
1609
1610 // This is after Hide() so that the user doesn't see the inputs clear.
1611 if (mDocument) {
1612 mDocument->Sanitize();
1613 }
1614
1615 // Reverse ownership. Do this *after* calling sanitize so that sanitize
1616 // doesn't cause mutations that make the SHEntry drop the presentation
1617
1618 // Grab a reference to mSHEntry before calling into things like
1619 // SyncPresentationState that might mess with our members.
1620 nsCOMPtr<nsISHEntry> shEntry = mSHEntry; // we'll need this below
1621 mSHEntry = nullptr;
1622
1623 if (savePresentation) {
1624 shEntry->SetContentViewer(this);
1625 }
1626
1627 // Always sync the presentation state. That way even if someone screws up
1628 // and shEntry has no window state at this point we'll be ok; we just won't
1629 // cache ourselves.
1630 shEntry->SyncPresentationState();
1631
1632 // Shut down accessibility for the document before we start to tear it down.
1633 #ifdef ACCESSIBILITY
1634 if (mPresShell) {
1635 a11y::DocAccessible* docAcc = mPresShell->GetDocAccessible();
1636 if (docAcc) {
1637 docAcc->Shutdown();
1638 }
1639 }
1640 #endif
1641
1642 // Break the link from the document/presentation to the docshell, so that
1643 // link traversals cannot affect the currently-loaded document.
1644 // When the presentation is restored, Open() and InitInternal() will reset
1645 // these pointers to their original values.
1646
1647 if (mDocument) {
1648 mDocument->SetContainer(nullptr);
1649 }
1650 if (mPresContext) {
1651 mPresContext->Detach();
1652 }
1653 if (mPresShell) {
1654 mPresShell->SetForwardingContainer(mContainer);
1655 }
1656
1657 // Do the same for our children. Note that we need to get the child
1658 // docshells from the SHEntry now; the docshell will have cleared them.
1659 nsCOMPtr<nsIDocShellTreeItem> item;
1660 int32_t itemIndex = 0;
1661 while (NS_SUCCEEDED(shEntry->ChildShellAt(itemIndex++,
1662 getter_AddRefs(item))) && item) {
1663 DetachContainerRecurse(nsCOMPtr<nsIDocShell>(do_QueryInterface(item)));
1664 }
1665
1666 return NS_OK;
1667 }
1668
1669 // The document was not put in the bfcache
1670
1671 if (mPresShell) {
1672 DestroyPresShell();
1673 }
1674 if (mDocument) {
1675 mDocument->Destroy();
1676 mDocument = nullptr;
1677 }
1678
1679 // All callers are supposed to call destroy to break circular
1680 // references. If we do this stuff in the destructor, the
1681 // destructor might never be called (especially if we're being
1682 // used from JS.
1683
1684 #ifdef NS_PRINTING
1685 if (mPrintEngine) {
1686 #ifdef NS_PRINT_PREVIEW
1687 bool doingPrintPreview;
1688 mPrintEngine->GetDoingPrintPreview(&doingPrintPreview);
1689 if (doingPrintPreview) {
1690 mPrintEngine->FinishPrintPreview();
1691 }
1692 #endif
1693
1694 mPrintEngine->Destroy();
1695 mPrintEngine = nullptr;
1696 }
1697 #endif
1698
1699 // Avoid leaking the old viewer.
1700 if (mPreviousViewer) {
1701 mPreviousViewer->Destroy();
1702 mPreviousViewer = nullptr;
1703 }
1704
1705 mDeviceContext = nullptr;
1706
1707 if (mPresContext) {
1708 DestroyPresContext();
1709 }
1710
1711 mWindow = nullptr;
1712 mViewManager = nullptr;
1713 mContainer = WeakPtr<nsDocShell>();
1714
1715 return NS_OK;
1716 }
1717
1718 NS_IMETHODIMP
1719 nsDocumentViewer::Stop(void)
1720 {
1721 NS_ASSERTION(mDocument, "Stop called too early or too late");
1722 if (mDocument) {
1723 mDocument->StopDocumentLoad();
1724 }
1725
1726 if (!mHidden && (mLoaded || mStopped) && mPresContext && !mSHEntry)
1727 mPresContext->SetImageAnimationMode(imgIContainer::kDontAnimMode);
1728
1729 mStopped = true;
1730
1731 if (!mLoaded && mPresShell) {
1732 // Well, we might as well paint what we have so far.
1733 nsCOMPtr<nsIPresShell> shellDeathGrip(mPresShell); // bug 378682
1734 mPresShell->UnsuppressPainting();
1735 }
1736
1737 return NS_OK;
1738 }
1739
1740 NS_IMETHODIMP
1741 nsDocumentViewer::GetDOMDocument(nsIDOMDocument **aResult)
1742 {
1743 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
1744 return CallQueryInterface(mDocument, aResult);
1745 }
1746
1747 NS_IMETHODIMP_(nsIDocument *)
1748 nsDocumentViewer::GetDocument()
1749 {
1750 return mDocument;
1751 }
1752
1753 NS_IMETHODIMP
1754 nsDocumentViewer::SetDOMDocument(nsIDOMDocument *aDocument)
1755 {
1756 // Assumptions:
1757 //
1758 // 1) this document viewer has been initialized with a call to Init().
1759 // 2) the stylesheets associated with the document have been added
1760 // to the document.
1761
1762 // XXX Right now, this method assumes that the layout of the current
1763 // document hasn't started yet. More cleanup will probably be
1764 // necessary to make this method work for the case when layout *has*
1765 // occurred for the current document.
1766 // That work can happen when and if it is needed.
1767
1768 if (!aDocument)
1769 return NS_ERROR_NULL_POINTER;
1770
1771 nsCOMPtr<nsIDocument> newDoc = do_QueryInterface(aDocument);
1772 NS_ENSURE_TRUE(newDoc, NS_ERROR_UNEXPECTED);
1773
1774 return SetDocumentInternal(newDoc, false);
1775 }
1776
1777 NS_IMETHODIMP
1778 nsDocumentViewer::SetDocumentInternal(nsIDocument* aDocument,
1779 bool aForceReuseInnerWindow)
1780 {
1781 MOZ_ASSERT(aDocument);
1782
1783 // Set new container
1784 aDocument->SetContainer(mContainer);
1785
1786 if (mDocument != aDocument) {
1787 if (mDocument->IsStaticDocument()) {
1788 mDocument->SetScriptGlobalObject(nullptr);
1789 mDocument->Destroy();
1790 }
1791 // Replace the old document with the new one. Do this only when
1792 // the new document really is a new document.
1793 mDocument = aDocument;
1794
1795 // Set the script global object on the new document
1796 nsCOMPtr<nsPIDOMWindow> window =
1797 do_GetInterface(static_cast<nsIDocShell*>(mContainer.get()));
1798 if (window) {
1799 window->SetNewDocument(aDocument, nullptr, aForceReuseInnerWindow);
1800 }
1801
1802 // Clear the list of old child docshells. Child docshells for the new
1803 // document will be constructed as frames are created.
1804 if (!aDocument->IsStaticDocument()) {
1805 nsCOMPtr<nsIDocShell> node(mContainer);
1806 if (node) {
1807 int32_t count;
1808 node->GetChildCount(&count);
1809 for (int32_t i = 0; i < count; ++i) {
1810 nsCOMPtr<nsIDocShellTreeItem> child;
1811 node->GetChildAt(0, getter_AddRefs(child));
1812 node->RemoveChild(child);
1813 }
1814 }
1815 }
1816 }
1817
1818 nsresult rv = SyncParentSubDocMap();
1819 NS_ENSURE_SUCCESS(rv, rv);
1820
1821 // Replace the current pres shell with a new shell for the new document
1822
1823 if (mPresShell) {
1824 DestroyPresShell();
1825 }
1826
1827 if (mPresContext) {
1828 DestroyPresContext();
1829
1830 mWindow = nullptr;
1831 InitInternal(mParentWidget, nullptr, mBounds, true, true, false);
1832 }
1833
1834 return rv;
1835 }
1836
1837 nsIPresShell*
1838 nsDocumentViewer::GetPresShell()
1839 {
1840 return mPresShell;
1841 }
1842
1843 nsPresContext*
1844 nsDocumentViewer::GetPresContext()
1845 {
1846 return mPresContext;
1847 }
1848
1849 nsViewManager*
1850 nsDocumentViewer::GetViewManager()
1851 {
1852 return mViewManager;
1853 }
1854
1855 NS_IMETHODIMP
1856 nsDocumentViewer::GetPresShell(nsIPresShell** aResult)
1857 {
1858 nsIPresShell* shell = GetPresShell();
1859 NS_IF_ADDREF(*aResult = shell);
1860 return NS_OK;
1861 }
1862
1863 NS_IMETHODIMP
1864 nsDocumentViewer::GetPresContext(nsPresContext** aResult)
1865 {
1866 nsPresContext* pc = GetPresContext();
1867 NS_IF_ADDREF(*aResult = pc);
1868 return NS_OK;
1869 }
1870
1871 NS_IMETHODIMP
1872 nsDocumentViewer::GetBounds(nsIntRect& aResult)
1873 {
1874 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
1875 aResult = mBounds;
1876 return NS_OK;
1877 }
1878
1879 NS_IMETHODIMP
1880 nsDocumentViewer::GetPreviousViewer(nsIContentViewer** aViewer)
1881 {
1882 *aViewer = mPreviousViewer;
1883 NS_IF_ADDREF(*aViewer);
1884 return NS_OK;
1885 }
1886
1887 NS_IMETHODIMP
1888 nsDocumentViewer::SetPreviousViewer(nsIContentViewer* aViewer)
1889 {
1890 // NOTE: |Show| sets |mPreviousViewer| to null without calling this
1891 // function.
1892
1893 if (aViewer) {
1894 NS_ASSERTION(!mPreviousViewer,
1895 "can't set previous viewer when there already is one");
1896
1897 // In a multiple chaining situation (which occurs when running a thrashing
1898 // test like i-bench or jrgm's tests with no delay), we can build up a
1899 // whole chain of viewers. In order to avoid this, we always set our previous
1900 // viewer to the MOST previous viewer in the chain, and then dump the intermediate
1901 // link from the chain. This ensures that at most only 2 documents are alive
1902 // and undestroyed at any given time (the one that is showing and the one that
1903 // is loading with painting suppressed).
1904 // It's very important that if this ever gets changed the code
1905 // before the RestorePresentation call in nsDocShell::InternalLoad
1906 // be changed accordingly.
1907 nsCOMPtr<nsIContentViewer> prevViewer;
1908 aViewer->GetPreviousViewer(getter_AddRefs(prevViewer));
1909 if (prevViewer) {
1910 aViewer->SetPreviousViewer(nullptr);
1911 aViewer->Destroy();
1912 return SetPreviousViewer(prevViewer);
1913 }
1914 }
1915
1916 mPreviousViewer = aViewer;
1917 return NS_OK;
1918 }
1919
1920 NS_IMETHODIMP
1921 nsDocumentViewer::SetBounds(const nsIntRect& aBounds)
1922 {
1923 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
1924
1925 mBounds = aBounds;
1926 if (mWindow) {
1927 if (!mAttachedToParent) {
1928 // Don't have the widget repaint. Layout will generate repaint requests
1929 // during reflow.
1930 mWindow->Resize(aBounds.x, aBounds.y,
1931 aBounds.width, aBounds.height,
1932 false);
1933 }
1934 } else if (mPresContext && mViewManager) {
1935 int32_t p2a = mPresContext->AppUnitsPerDevPixel();
1936 mViewManager->SetWindowDimensions(NSIntPixelsToAppUnits(mBounds.width, p2a),
1937 NSIntPixelsToAppUnits(mBounds.height, p2a));
1938 }
1939
1940 // If there's a previous viewer, it's the one that's actually showing,
1941 // so be sure to resize it as well so it paints over the right area.
1942 // This may slow down the performance of the new page load, but resize
1943 // during load is also probably a relatively unusual condition
1944 // relating to things being hidden while something is loaded. It so
1945 // happens that Firefox does this a good bit with its infobar, and it
1946 // looks ugly if we don't do this.
1947 if (mPreviousViewer) {
1948 nsCOMPtr<nsIContentViewer> previousViewer = mPreviousViewer;
1949 previousViewer->SetBounds(aBounds);
1950 }
1951
1952 return NS_OK;
1953 }
1954
1955 NS_IMETHODIMP
1956 nsDocumentViewer::Move(int32_t aX, int32_t aY)
1957 {
1958 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
1959 mBounds.MoveTo(aX, aY);
1960 if (mWindow) {
1961 mWindow->Move(aX, aY);
1962 }
1963 return NS_OK;
1964 }
1965
1966 NS_IMETHODIMP
1967 nsDocumentViewer::Show(void)
1968 {
1969 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
1970
1971 // We don't need the previous viewer anymore since we're not
1972 // displaying it.
1973 if (mPreviousViewer) {
1974 // This little dance *may* only be to keep
1975 // PresShell::EndObservingDocument happy, but I'm not sure.
1976 nsCOMPtr<nsIContentViewer> prevViewer(mPreviousViewer);
1977 mPreviousViewer = nullptr;
1978 prevViewer->Destroy();
1979
1980 // Make sure we don't have too many cached ContentViewers
1981 nsCOMPtr<nsIDocShellTreeItem> treeItem(mContainer);
1982 if (treeItem) {
1983 // We need to find the root DocShell since only that object has an
1984 // SHistory and we need the SHistory to evict content viewers
1985 nsCOMPtr<nsIDocShellTreeItem> root;
1986 treeItem->GetSameTypeRootTreeItem(getter_AddRefs(root));
1987 nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(root);
1988 nsCOMPtr<nsISHistory> history;
1989 webNav->GetSessionHistory(getter_AddRefs(history));
1990 nsCOMPtr<nsISHistoryInternal> historyInt = do_QueryInterface(history);
1991 if (historyInt) {
1992 int32_t prevIndex,loadedIndex;
1993 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(treeItem);
1994 docShell->GetPreviousTransIndex(&prevIndex);
1995 docShell->GetLoadedTransIndex(&loadedIndex);
1996 #ifdef DEBUG_PAGE_CACHE
1997 printf("About to evict content viewers: prev=%d, loaded=%d\n",
1998 prevIndex, loadedIndex);
1999 #endif
2000 historyInt->EvictOutOfRangeContentViewers(loadedIndex);
2001 }
2002 }
2003 }
2004
2005 if (mWindow) {
2006 // When attached to a top level xul window, we do not need to call
2007 // Show on the widget. Underlying window management code handles
2008 // this when the window is initialized.
2009 if (!mAttachedToParent) {
2010 mWindow->Show(true);
2011 }
2012 }
2013
2014 if (mDocument && !mPresShell) {
2015 NS_ASSERTION(!mWindow, "Window already created but no presshell?");
2016
2017 nsCOMPtr<nsIBaseWindow> base_win(mContainer);
2018 if (base_win) {
2019 base_win->GetParentWidget(&mParentWidget);
2020 if (mParentWidget) {
2021 mParentWidget->Release(); // GetParentWidget AddRefs, but mParentWidget is weak
2022 }
2023 }
2024
2025 nsView* containerView = FindContainerView();
2026
2027 nsresult rv = CreateDeviceContext(containerView);
2028 NS_ENSURE_SUCCESS(rv, rv);
2029
2030 // Create presentation context
2031 NS_ASSERTION(!mPresContext, "Shouldn't have a prescontext if we have no shell!");
2032 mPresContext = CreatePresContext(mDocument,
2033 nsPresContext::eContext_Galley, containerView);
2034 NS_ENSURE_TRUE(mPresContext, NS_ERROR_OUT_OF_MEMORY);
2035
2036 rv = mPresContext->Init(mDeviceContext);
2037 if (NS_FAILED(rv)) {
2038 mPresContext = nullptr;
2039 return rv;
2040 }
2041
2042 rv = MakeWindow(nsSize(mPresContext->DevPixelsToAppUnits(mBounds.width),
2043 mPresContext->DevPixelsToAppUnits(mBounds.height)),
2044 containerView);
2045 if (NS_FAILED(rv))
2046 return rv;
2047
2048 if (mPresContext && base_win) {
2049 nsCOMPtr<nsILinkHandler> linkHandler(do_GetInterface(base_win));
2050
2051 if (linkHandler) {
2052 mPresContext->SetLinkHandler(linkHandler);
2053 }
2054
2055 mPresContext->SetContainer(mContainer);
2056 }
2057
2058 if (mPresContext) {
2059 Hide();
2060
2061 rv = InitPresentationStuff(mDocument->MayStartLayout());
2062 }
2063
2064 // If we get here the document load has already started and the
2065 // window is shown because some JS on the page caused it to be
2066 // shown...
2067
2068 if (mPresShell) {
2069 nsCOMPtr<nsIPresShell> shellDeathGrip(mPresShell); // bug 378682
2070 mPresShell->UnsuppressPainting();
2071 }
2072 }
2073
2074 // Notify observers that a new page has been shown. This will get run
2075 // from the event loop after we actually draw the page.
2076 NS_DispatchToMainThread(new nsDocumentShownDispatcher(mDocument));
2077
2078 return NS_OK;
2079 }
2080
2081 NS_IMETHODIMP
2082 nsDocumentViewer::Hide(void)
2083 {
2084 if (!mAttachedToParent && mWindow) {
2085 mWindow->Show(false);
2086 }
2087
2088 if (!mPresShell)
2089 return NS_OK;
2090
2091 NS_ASSERTION(mPresContext, "Can't have a presshell and no prescontext!");
2092
2093 // Avoid leaking the old viewer.
2094 if (mPreviousViewer) {
2095 mPreviousViewer->Destroy();
2096 mPreviousViewer = nullptr;
2097 }
2098
2099 if (mIsSticky) {
2100 // This window is sticky, that means that it might be shown again
2101 // and we don't want the presshell n' all that to be thrown away
2102 // just because the window is hidden.
2103
2104 return NS_OK;
2105 }
2106
2107 nsCOMPtr<nsIDocShell> docShell(mContainer);
2108 if (docShell) {
2109 nsCOMPtr<nsILayoutHistoryState> layoutState;
2110 mPresShell->CaptureHistoryState(getter_AddRefs(layoutState));
2111 }
2112
2113 DestroyPresShell();
2114
2115 DestroyPresContext();
2116
2117 mViewManager = nullptr;
2118 mWindow = nullptr;
2119 mDeviceContext = nullptr;
2120 mParentWidget = nullptr;
2121
2122 nsCOMPtr<nsIBaseWindow> base_win(mContainer);
2123
2124 if (base_win && !mAttachedToParent) {
2125 base_win->SetParentWidget(nullptr);
2126 }
2127
2128 return NS_OK;
2129 }
2130
2131 NS_IMETHODIMP
2132 nsDocumentViewer::GetSticky(bool *aSticky)
2133 {
2134 *aSticky = mIsSticky;
2135
2136 return NS_OK;
2137 }
2138
2139 NS_IMETHODIMP
2140 nsDocumentViewer::SetSticky(bool aSticky)
2141 {
2142 mIsSticky = aSticky;
2143
2144 return NS_OK;
2145 }
2146
2147 NS_IMETHODIMP
2148 nsDocumentViewer::RequestWindowClose(bool* aCanClose)
2149 {
2150 #ifdef NS_PRINTING
2151 if (mPrintIsPending || (mPrintEngine && mPrintEngine->GetIsPrinting())) {
2152 *aCanClose = false;
2153 mDeferredWindowClose = true;
2154 } else
2155 #endif
2156 *aCanClose = true;
2157
2158 return NS_OK;
2159 }
2160
2161 static bool
2162 AppendAgentSheet(nsIStyleSheet *aSheet, void *aData)
2163 {
2164 nsStyleSet *styleSet = static_cast<nsStyleSet*>(aData);
2165 styleSet->AppendStyleSheet(nsStyleSet::eAgentSheet, aSheet);
2166 return true;
2167 }
2168
2169 static bool
2170 PrependUserSheet(nsIStyleSheet *aSheet, void *aData)
2171 {
2172 nsStyleSet *styleSet = static_cast<nsStyleSet*>(aData);
2173 styleSet->PrependStyleSheet(nsStyleSet::eUserSheet, aSheet);
2174 return true;
2175 }
2176
2177 nsresult
2178 nsDocumentViewer::CreateStyleSet(nsIDocument* aDocument,
2179 nsStyleSet** aStyleSet)
2180 {
2181 // Make sure this does the same thing as PresShell::AddSheet wrt ordering.
2182
2183 // this should eventually get expanded to allow for creating
2184 // different sets for different media
2185 nsStyleSet *styleSet = new nsStyleSet();
2186
2187 styleSet->BeginUpdate();
2188
2189 // The document will fill in the document sheets when we create the presshell
2190
2191 // Handle the user sheets.
2192 nsCSSStyleSheet* sheet = nullptr;
2193 if (nsContentUtils::IsInChromeDocshell(aDocument)) {
2194 sheet = nsLayoutStylesheetCache::UserChromeSheet();
2195 }
2196 else {
2197 sheet = nsLayoutStylesheetCache::UserContentSheet();
2198 }
2199
2200 if (sheet)
2201 styleSet->AppendStyleSheet(nsStyleSet::eUserSheet, sheet);
2202
2203 // Append chrome sheets (scrollbars + forms).
2204 bool shouldOverride = false;
2205 // We don't want a docshell here for external resource docs, so just
2206 // look at mContainer.
2207 nsCOMPtr<nsIDocShell> ds(mContainer);
2208 nsCOMPtr<nsIDOMEventTarget> chromeHandler;
2209 nsCOMPtr<nsIURI> uri;
2210 nsRefPtr<nsCSSStyleSheet> csssheet;
2211
2212 if (ds) {
2213 ds->GetChromeEventHandler(getter_AddRefs(chromeHandler));
2214 }
2215 if (chromeHandler) {
2216 nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(chromeHandler));
2217 nsCOMPtr<nsIContent> content(do_QueryInterface(elt));
2218 if (elt && content) {
2219 nsCOMPtr<nsIURI> baseURI = content->GetBaseURI();
2220
2221 nsAutoString sheets;
2222 elt->GetAttribute(NS_LITERAL_STRING("usechromesheets"), sheets);
2223 if (!sheets.IsEmpty() && baseURI) {
2224 nsRefPtr<mozilla::css::Loader> cssLoader = new mozilla::css::Loader();
2225
2226 char *str = ToNewCString(sheets);
2227 char *newStr = str;
2228 char *token;
2229 while ( (token = nsCRT::strtok(newStr, ", ", &newStr)) ) {
2230 NS_NewURI(getter_AddRefs(uri), nsDependentCString(token), nullptr,
2231 baseURI);
2232 if (!uri) continue;
2233
2234 cssLoader->LoadSheetSync(uri, getter_AddRefs(csssheet));
2235 if (!csssheet) continue;
2236
2237 styleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, csssheet);
2238 shouldOverride = true;
2239 }
2240 nsMemory::Free(str);
2241 }
2242 }
2243 }
2244
2245 if (!shouldOverride) {
2246 sheet = nsLayoutStylesheetCache::ScrollbarsSheet();
2247 if (sheet) {
2248 styleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, sheet);
2249 }
2250 }
2251
2252 sheet = nsLayoutStylesheetCache::NumberControlSheet();
2253 if (sheet) {
2254 styleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, sheet);
2255 }
2256
2257 sheet = nsLayoutStylesheetCache::FormsSheet();
2258 if (sheet) {
2259 styleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, sheet);
2260 }
2261
2262 sheet = nsLayoutStylesheetCache::FullScreenOverrideSheet();
2263 if (sheet) {
2264 styleSet->PrependStyleSheet(nsStyleSet::eOverrideSheet, sheet);
2265 }
2266
2267 // Make sure to clone the quirk sheet so that it can be usefully
2268 // enabled/disabled as needed.
2269 nsRefPtr<nsCSSStyleSheet> quirkClone;
2270 nsCSSStyleSheet* quirkSheet;
2271 if (!nsLayoutStylesheetCache::UASheet() ||
2272 !(quirkSheet = nsLayoutStylesheetCache::QuirkSheet()) ||
2273 !(quirkClone = quirkSheet->Clone(nullptr, nullptr, nullptr, nullptr)) ||
2274 !sheet) {
2275 delete styleSet;
2276 return NS_ERROR_OUT_OF_MEMORY;
2277 }
2278 // quirk.css needs to come after the regular UA sheet (or more precisely,
2279 // after the html.css and so forth that the UA sheet imports).
2280 styleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, quirkClone);
2281 styleSet->SetQuirkStyleSheet(quirkClone);
2282 styleSet->PrependStyleSheet(nsStyleSet::eAgentSheet,
2283 nsLayoutStylesheetCache::UASheet());
2284
2285 nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
2286 if (sheetService) {
2287 sheetService->AgentStyleSheets()->EnumerateForwards(AppendAgentSheet,
2288 styleSet);
2289 sheetService->UserStyleSheets()->EnumerateBackwards(PrependUserSheet,
2290 styleSet);
2291 }
2292
2293 // Caller will handle calling EndUpdate, per contract.
2294 *aStyleSet = styleSet;
2295 return NS_OK;
2296 }
2297
2298 NS_IMETHODIMP
2299 nsDocumentViewer::ClearHistoryEntry()
2300 {
2301 mSHEntry = nullptr;
2302 return NS_OK;
2303 }
2304
2305 //-------------------------------------------------------
2306
2307 nsresult
2308 nsDocumentViewer::MakeWindow(const nsSize& aSize, nsView* aContainerView)
2309 {
2310 if (GetIsPrintPreview())
2311 return NS_OK;
2312
2313 bool shouldAttach = ShouldAttachToTopLevel();
2314
2315 if (shouldAttach) {
2316 // If the old view is already attached to our parent, detach
2317 DetachFromTopLevelWidget();
2318 }
2319
2320 mViewManager = new nsViewManager();
2321
2322 nsDeviceContext *dx = mPresContext->DeviceContext();
2323
2324 nsresult rv = mViewManager->Init(dx);
2325 if (NS_FAILED(rv))
2326 return rv;
2327
2328 // The root view is always at 0,0.
2329 nsRect tbounds(nsPoint(0, 0), aSize);
2330 // Create a view
2331 nsView* view = mViewManager->CreateView(tbounds, aContainerView);
2332 if (!view)
2333 return NS_ERROR_OUT_OF_MEMORY;
2334
2335 // Create a widget if we were given a parent widget or don't have a
2336 // container view that we can hook up to without a widget.
2337 // Don't create widgets for ResourceDocs (external resources & svg images),
2338 // because when they're displayed, they're painted into *another* document's
2339 // widget.
2340 if (!mDocument->IsResourceDoc() &&
2341 (mParentWidget || !aContainerView)) {
2342 // pass in a native widget to be the parent widget ONLY if the view hierarchy will stand alone.
2343 // otherwise the view will find its own parent widget and "do the right thing" to
2344 // establish a parent/child widget relationship
2345 nsWidgetInitData initData;
2346 nsWidgetInitData* initDataPtr;
2347 if (!mParentWidget) {
2348 initDataPtr = &initData;
2349 initData.mWindowType = eWindowType_invisible;
2350 } else {
2351 initDataPtr = nullptr;
2352 }
2353
2354 if (shouldAttach) {
2355 // Reuse the top level parent widget.
2356 rv = view->AttachToTopLevelWidget(mParentWidget);
2357 mAttachedToParent = true;
2358 }
2359 else if (!aContainerView && mParentWidget) {
2360 rv = view->CreateWidgetForParent(mParentWidget, initDataPtr,
2361 true, false);
2362 }
2363 else {
2364 rv = view->CreateWidget(initDataPtr, true, false);
2365 }
2366 if (NS_FAILED(rv))
2367 return rv;
2368 }
2369
2370 // Setup hierarchical relationship in view manager
2371 mViewManager->SetRootView(view);
2372
2373 mWindow = view->GetWidget();
2374
2375 // This SetFocus is necessary so the Arrow Key and Page Key events
2376 // go to the scrolled view as soon as the Window is created instead of going to
2377 // the browser window (this enables keyboard scrolling of the document)
2378 // mWindow->SetFocus();
2379
2380 return rv;
2381 }
2382
2383 void
2384 nsDocumentViewer::DetachFromTopLevelWidget()
2385 {
2386 if (mViewManager) {
2387 nsView* oldView = mViewManager->GetRootView();
2388 if (oldView && oldView->IsAttachedToTopLevel()) {
2389 oldView->DetachFromTopLevelWidget();
2390 }
2391 }
2392 mAttachedToParent = false;
2393 }
2394
2395 nsView*
2396 nsDocumentViewer::FindContainerView()
2397 {
2398 nsView* containerView = nullptr;
2399
2400 if (mContainer) {
2401 nsCOMPtr<nsIDocShellTreeItem> docShellItem(mContainer);
2402 nsCOMPtr<nsPIDOMWindow> pwin(do_GetInterface(docShellItem));
2403 if (pwin) {
2404 nsCOMPtr<nsIContent> containerElement = do_QueryInterface(pwin->GetFrameElementInternal());
2405 if (!containerElement) {
2406 return nullptr;
2407 }
2408 nsCOMPtr<nsIPresShell> parentPresShell;
2409 if (docShellItem) {
2410 nsCOMPtr<nsIDocShellTreeItem> parentDocShellItem;
2411 docShellItem->GetParent(getter_AddRefs(parentDocShellItem));
2412 if (parentDocShellItem) {
2413 nsCOMPtr<nsIDocShell> parentDocShell = do_QueryInterface(parentDocShellItem);
2414 parentPresShell = parentDocShell->GetPresShell();
2415 }
2416 }
2417 if (!parentPresShell) {
2418 nsCOMPtr<nsIDocument> parentDoc = containerElement->GetCurrentDoc();
2419 if (parentDoc) {
2420 parentPresShell = parentDoc->GetShell();
2421 }
2422 }
2423 if (!parentPresShell) {
2424 NS_WARNING("Subdocument container has no presshell");
2425 } else {
2426 nsIFrame* f = parentPresShell->GetRealPrimaryFrameFor(containerElement);
2427 if (f) {
2428 nsIFrame* subdocFrame = f->GetContentInsertionFrame();
2429 // subdocFrame might not be a subdocument frame; the frame
2430 // constructor can treat a <frame> as an inline in some XBL
2431 // cases. Treat that as display:none, the document is not
2432 // displayed.
2433 if (subdocFrame->GetType() == nsGkAtoms::subDocumentFrame) {
2434 NS_ASSERTION(subdocFrame->GetView(), "Subdoc frames must have views");
2435 nsView* innerView =
2436 static_cast<nsSubDocumentFrame*>(subdocFrame)->EnsureInnerView();
2437 containerView = innerView;
2438 } else {
2439 NS_WARNING("Subdocument container has non-subdocument frame");
2440 }
2441 } else {
2442 NS_WARNING("Subdocument container has no frame");
2443 }
2444 }
2445 }
2446 }
2447
2448 return containerView;
2449 }
2450
2451 nsresult
2452 nsDocumentViewer::CreateDeviceContext(nsView* aContainerView)
2453 {
2454 NS_PRECONDITION(!mPresShell && !mWindow,
2455 "This will screw up our existing presentation");
2456 NS_PRECONDITION(mDocument, "Gotta have a document here");
2457
2458 nsIDocument* doc = mDocument->GetDisplayDocument();
2459 if (doc) {
2460 NS_ASSERTION(!aContainerView, "External resource document embedded somewhere?");
2461 // We want to use our display document's device context if possible
2462 nsIPresShell* shell = doc->GetShell();
2463 if (shell) {
2464 nsPresContext* ctx = shell->GetPresContext();
2465 if (ctx) {
2466 mDeviceContext = ctx->DeviceContext();
2467 return NS_OK;
2468 }
2469 }
2470 }
2471
2472 // Create a device context even if we already have one, since our widget
2473 // might have changed.
2474 nsIWidget* widget = nullptr;
2475 if (aContainerView) {
2476 widget = aContainerView->GetNearestWidget(nullptr);
2477 }
2478 if (!widget) {
2479 widget = mParentWidget;
2480 }
2481 if (widget) {
2482 widget = widget->GetTopLevelWidget();
2483 }
2484
2485 mDeviceContext = new nsDeviceContext();
2486 mDeviceContext->Init(widget);
2487 return NS_OK;
2488 }
2489
2490 // Return the selection for the document. Note that text fields have their
2491 // own selection, which cannot be accessed with this method.
2492 nsresult nsDocumentViewer::GetDocumentSelection(nsISelection **aSelection)
2493 {
2494 NS_ENSURE_ARG_POINTER(aSelection);
2495 if (!mPresShell) {
2496 return NS_ERROR_NOT_INITIALIZED;
2497 }
2498
2499 nsCOMPtr<nsISelectionController> selcon;
2500 selcon = do_QueryInterface(mPresShell);
2501 if (selcon)
2502 return selcon->GetSelection(nsISelectionController::SELECTION_NORMAL,
2503 aSelection);
2504 return NS_ERROR_FAILURE;
2505 }
2506
2507 /* ========================================================================================
2508 * nsIContentViewerEdit
2509 * ======================================================================================== */
2510
2511 NS_IMETHODIMP nsDocumentViewer::ClearSelection()
2512 {
2513 nsresult rv;
2514 nsCOMPtr<nsISelection> selection;
2515
2516 // use nsCopySupport::GetSelectionForCopy() ?
2517 rv = GetDocumentSelection(getter_AddRefs(selection));
2518 if (NS_FAILED(rv)) return rv;
2519
2520 return selection->CollapseToStart();
2521 }
2522
2523 NS_IMETHODIMP nsDocumentViewer::SelectAll()
2524 {
2525 // XXX this is a temporary implementation copied from nsWebShell
2526 // for now. I think nsDocument and friends should have some helper
2527 // functions to make this easier.
2528 nsCOMPtr<nsISelection> selection;
2529 nsresult rv;
2530
2531 // use nsCopySupport::GetSelectionForCopy() ?
2532 rv = GetDocumentSelection(getter_AddRefs(selection));
2533 if (NS_FAILED(rv)) return rv;
2534
2535 nsCOMPtr<nsIDOMHTMLDocument> htmldoc = do_QueryInterface(mDocument);
2536 nsCOMPtr<nsIDOMNode> bodyNode;
2537
2538 if (htmldoc)
2539 {
2540 nsCOMPtr<nsIDOMHTMLElement>bodyElement;
2541 rv = htmldoc->GetBody(getter_AddRefs(bodyElement));
2542 if (NS_FAILED(rv) || !bodyElement) return rv;
2543
2544 bodyNode = do_QueryInterface(bodyElement);
2545 }
2546 else if (mDocument)
2547 {
2548 bodyNode = do_QueryInterface(mDocument->GetRootElement());
2549 }
2550 if (!bodyNode) return NS_ERROR_FAILURE;
2551
2552 rv = selection->RemoveAllRanges();
2553 if (NS_FAILED(rv)) return rv;
2554
2555 rv = selection->SelectAllChildren(bodyNode);
2556 return rv;
2557 }
2558
2559 NS_IMETHODIMP nsDocumentViewer::CopySelection()
2560 {
2561 nsCopySupport::FireClipboardEvent(NS_COPY, nsIClipboard::kGlobalClipboard, mPresShell, nullptr);
2562 return NS_OK;
2563 }
2564
2565 NS_IMETHODIMP nsDocumentViewer::CopyLinkLocation()
2566 {
2567 NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
2568 nsCOMPtr<nsIDOMNode> node;
2569 GetPopupLinkNode(getter_AddRefs(node));
2570 // make noise if we're not in a link
2571 NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
2572
2573 nsCOMPtr<dom::Element> elm(do_QueryInterface(node));
2574 NS_ENSURE_TRUE(elm, NS_ERROR_FAILURE);
2575
2576 nsAutoString locationText;
2577 nsContentUtils::GetLinkLocation(elm, locationText);
2578 if (locationText.IsEmpty())
2579 return NS_ERROR_FAILURE;
2580
2581 nsresult rv = NS_OK;
2582 nsCOMPtr<nsIClipboardHelper> clipboard(do_GetService("@mozilla.org/widget/clipboardhelper;1", &rv));
2583 NS_ENSURE_SUCCESS(rv, rv);
2584
2585 // copy the href onto the clipboard
2586 nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(mDocument);
2587 return clipboard->CopyString(locationText, doc);
2588 }
2589
2590 NS_IMETHODIMP nsDocumentViewer::CopyImage(int32_t aCopyFlags)
2591 {
2592 NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
2593 nsCOMPtr<nsIImageLoadingContent> node;
2594 GetPopupImageNode(getter_AddRefs(node));
2595 // make noise if we're not in an image
2596 NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
2597
2598 nsCOMPtr<nsILoadContext> loadContext(mContainer);
2599 return nsCopySupport::ImageCopy(node, loadContext, aCopyFlags);
2600 }
2601
2602
2603 NS_IMETHODIMP nsDocumentViewer::GetCopyable(bool *aCopyable)
2604 {
2605 NS_ENSURE_ARG_POINTER(aCopyable);
2606 *aCopyable = nsCopySupport::CanCopy(mDocument);
2607 return NS_OK;
2608 }
2609
2610 /* AString getContents (in string mimeType, in boolean selectionOnly); */
2611 NS_IMETHODIMP nsDocumentViewer::GetContents(const char *mimeType, bool selectionOnly, nsAString& aOutValue)
2612 {
2613 aOutValue.Truncate();
2614
2615 NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
2616 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED);
2617
2618 // Now we have the selection. Make sure it's nonzero:
2619 nsCOMPtr<nsISelection> sel;
2620 if (selectionOnly) {
2621 nsCopySupport::GetSelectionForCopy(mDocument, getter_AddRefs(sel));
2622 NS_ENSURE_TRUE(sel, NS_ERROR_FAILURE);
2623
2624 bool isCollapsed;
2625 sel->GetIsCollapsed(&isCollapsed);
2626 if (isCollapsed)
2627 return NS_OK;
2628 }
2629
2630 // call the copy code
2631 return nsCopySupport::GetContents(nsDependentCString(mimeType), 0, sel,
2632 mDocument, aOutValue);
2633 }
2634
2635 /* readonly attribute boolean canGetContents; */
2636 NS_IMETHODIMP nsDocumentViewer::GetCanGetContents(bool *aCanGetContents)
2637 {
2638 NS_ENSURE_ARG_POINTER(aCanGetContents);
2639 *aCanGetContents = false;
2640 NS_ENSURE_STATE(mDocument);
2641 *aCanGetContents = nsCopySupport::CanCopy(mDocument);
2642 return NS_OK;
2643 }
2644
2645
2646 /* ========================================================================================
2647 * nsIContentViewerFile
2648 * ======================================================================================== */
2649 /** ---------------------------------------------------
2650 * See documentation above in the nsIContentViewerfile class definition
2651 * @update 01/24/00 dwc
2652 */
2653 NS_IMETHODIMP
2654 nsDocumentViewer::Print(bool aSilent,
2655 FILE * aDebugFile,
2656 nsIPrintSettings* aPrintSettings)
2657 {
2658 #ifdef NS_PRINTING
2659 nsCOMPtr<nsIPrintSettings> printSettings;
2660
2661 #ifdef DEBUG
2662 nsresult rv = NS_ERROR_FAILURE;
2663
2664 mDebugFile = aDebugFile;
2665 // if they don't pass in a PrintSettings, then make one
2666 // it will have all the default values
2667 printSettings = aPrintSettings;
2668 nsCOMPtr<nsIPrintOptions> printOptions = do_GetService(sPrintOptionsContractID, &rv);
2669 if (NS_SUCCEEDED(rv)) {
2670 // if they don't pass in a PrintSettings, then make one
2671 if (printSettings == nullptr) {
2672 printOptions->CreatePrintSettings(getter_AddRefs(printSettings));
2673 }
2674 NS_ASSERTION(printSettings, "You can't PrintPreview without a PrintSettings!");
2675 }
2676 if (printSettings) printSettings->SetPrintSilent(aSilent);
2677 if (printSettings) printSettings->SetShowPrintProgress(false);
2678 #endif
2679
2680
2681 return Print(printSettings, nullptr);
2682 #else
2683 return NS_ERROR_FAILURE;
2684 #endif
2685 }
2686
2687 // nsIContentViewerFile interface
2688 NS_IMETHODIMP
2689 nsDocumentViewer::GetPrintable(bool *aPrintable)
2690 {
2691 NS_ENSURE_ARG_POINTER(aPrintable);
2692
2693 *aPrintable = !GetIsPrinting();
2694
2695 return NS_OK;
2696 }
2697
2698 //*****************************************************************************
2699 // nsIMarkupDocumentViewer
2700 //*****************************************************************************
2701
2702 NS_IMETHODIMP nsDocumentViewer::ScrollToNode(nsIDOMNode* aNode)
2703 {
2704 NS_ENSURE_ARG(aNode);
2705 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
2706 nsCOMPtr<nsIPresShell> presShell;
2707 NS_ENSURE_SUCCESS(GetPresShell(getter_AddRefs(presShell)), NS_ERROR_FAILURE);
2708
2709 // Get the nsIContent interface, because that's what we need to
2710 // get the primary frame
2711
2712 nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
2713 NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
2714
2715 // Tell the PresShell to scroll to the primary frame of the content.
2716 NS_ENSURE_SUCCESS(
2717 presShell->ScrollContentIntoView(content,
2718 nsIPresShell::ScrollAxis(
2719 nsIPresShell::SCROLL_TOP,
2720 nsIPresShell::SCROLL_ALWAYS),
2721 nsIPresShell::ScrollAxis(),
2722 nsIPresShell::SCROLL_OVERFLOW_HIDDEN),
2723 NS_ERROR_FAILURE);
2724 return NS_OK;
2725 }
2726
2727 void
2728 nsDocumentViewer::CallChildren(CallChildFunc aFunc, void* aClosure)
2729 {
2730 nsCOMPtr<nsIDocShell> docShell(mContainer);
2731 if (docShell)
2732 {
2733 int32_t i;
2734 int32_t n;
2735 docShell->GetChildCount(&n);
2736 for (i=0; i < n; i++)
2737 {
2738 nsCOMPtr<nsIDocShellTreeItem> child;
2739 docShell->GetChildAt(i, getter_AddRefs(child));
2740 nsCOMPtr<nsIDocShell> childAsShell(do_QueryInterface(child));
2741 NS_ASSERTION(childAsShell, "null child in docshell");
2742 if (childAsShell)
2743 {
2744 nsCOMPtr<nsIContentViewer> childCV;
2745 childAsShell->GetContentViewer(getter_AddRefs(childCV));
2746 if (childCV)
2747 {
2748 nsCOMPtr<nsIMarkupDocumentViewer> markupCV = do_QueryInterface(childCV);
2749 if (markupCV) {
2750 (*aFunc)(markupCV, aClosure);
2751 }
2752 }
2753 }
2754 }
2755 }
2756 }
2757
2758 struct LineBoxInfo
2759 {
2760 nscoord mMaxLineBoxWidth;
2761 };
2762
2763 static void
2764 ChangeChildPaintingEnabled(nsIMarkupDocumentViewer* aChild, void* aClosure)
2765 {
2766 bool* enablePainting = (bool*) aClosure;
2767 if (*enablePainting) {
2768 aChild->ResumePainting();
2769 } else {
2770 aChild->PausePainting();
2771 }
2772 }
2773
2774 static void
2775 ChangeChildMaxLineBoxWidth(nsIMarkupDocumentViewer* aChild, void* aClosure)
2776 {
2777 struct LineBoxInfo* lbi = (struct LineBoxInfo*) aClosure;
2778 aChild->ChangeMaxLineBoxWidth(lbi->mMaxLineBoxWidth);
2779 }
2780
2781 struct ZoomInfo
2782 {
2783 float mZoom;
2784 };
2785
2786 static void
2787 SetChildTextZoom(nsIMarkupDocumentViewer* aChild, void* aClosure)
2788 {
2789 struct ZoomInfo* ZoomInfo = (struct ZoomInfo*) aClosure;
2790 aChild->SetTextZoom(ZoomInfo->mZoom);
2791 }
2792
2793 static void
2794 SetChildMinFontSize(nsIMarkupDocumentViewer* aChild, void* aClosure)
2795 {
2796 nsCOMPtr<nsIMarkupDocumentViewer> branch =
2797 do_QueryInterface(aChild);
2798 branch->SetMinFontSize(NS_PTR_TO_INT32(aClosure));
2799 }
2800
2801 static void
2802 SetChildFullZoom(nsIMarkupDocumentViewer* aChild, void* aClosure)
2803 {
2804 struct ZoomInfo* ZoomInfo = (struct ZoomInfo*) aClosure;
2805 aChild->SetFullZoom(ZoomInfo->mZoom);
2806 }
2807
2808 static bool
2809 SetExtResourceTextZoom(nsIDocument* aDocument, void* aClosure)
2810 {
2811 // Would it be better to enumerate external resource viewers instead?
2812 nsIPresShell* shell = aDocument->GetShell();
2813 if (shell) {
2814 nsPresContext* ctxt = shell->GetPresContext();
2815 if (ctxt) {
2816 struct ZoomInfo* ZoomInfo = static_cast<struct ZoomInfo*>(aClosure);
2817 ctxt->SetTextZoom(ZoomInfo->mZoom);
2818 }
2819 }
2820
2821 return true;
2822 }
2823
2824 static bool
2825 SetExtResourceMinFontSize(nsIDocument* aDocument, void* aClosure)
2826 {
2827 nsIPresShell* shell = aDocument->GetShell();
2828 if (shell) {
2829 nsPresContext* ctxt = shell->GetPresContext();
2830 if (ctxt) {
2831 ctxt->SetBaseMinFontSize(NS_PTR_TO_INT32(aClosure));
2832 }
2833 }
2834
2835 return true;
2836 }
2837
2838 static bool
2839 SetExtResourceFullZoom(nsIDocument* aDocument, void* aClosure)
2840 {
2841 // Would it be better to enumerate external resource viewers instead?
2842 nsIPresShell* shell = aDocument->GetShell();
2843 if (shell) {
2844 nsPresContext* ctxt = shell->GetPresContext();
2845 if (ctxt) {
2846 struct ZoomInfo* ZoomInfo = static_cast<struct ZoomInfo*>(aClosure);
2847 ctxt->SetFullZoom(ZoomInfo->mZoom);
2848 }
2849 }
2850
2851 return true;
2852 }
2853
2854 NS_IMETHODIMP
2855 nsDocumentViewer::SetTextZoom(float aTextZoom)
2856 {
2857 // If we don't have a document, then we need to bail.
2858 if (!mDocument) {
2859 return NS_ERROR_FAILURE;
2860 }
2861
2862 if (GetIsPrintPreview()) {
2863 return NS_OK;
2864 }
2865
2866 mTextZoom = aTextZoom;
2867
2868 // Set the text zoom on all children of mContainer (even if our zoom didn't
2869 // change, our children's zoom may be different, though it would be unusual).
2870 // Do this first, in case kids are auto-sizing and post reflow commands on
2871 // our presshell (which should be subsumed into our own style change reflow).
2872 struct ZoomInfo ZoomInfo = { aTextZoom };
2873 CallChildren(SetChildTextZoom, &ZoomInfo);
2874
2875 // Now change our own zoom
2876 nsPresContext* pc = GetPresContext();
2877 if (pc && aTextZoom != mPresContext->TextZoom()) {
2878 pc->SetTextZoom(aTextZoom);
2879 }
2880
2881 // And do the external resources
2882 mDocument->EnumerateExternalResources(SetExtResourceTextZoom, &ZoomInfo);
2883
2884 nsContentUtils::DispatchChromeEvent(mDocument, static_cast<nsIDocument*>(mDocument),
2885 NS_LITERAL_STRING("TextZoomChange"),
2886 true, true);
2887
2888 return NS_OK;
2889 }
2890
2891 NS_IMETHODIMP
2892 nsDocumentViewer::GetTextZoom(float* aTextZoom)
2893 {
2894 NS_ENSURE_ARG_POINTER(aTextZoom);
2895 nsPresContext* pc = GetPresContext();
2896 *aTextZoom = pc ? pc->TextZoom() : 1.0f;
2897 return NS_OK;
2898 }
2899
2900 NS_IMETHODIMP
2901 nsDocumentViewer::SetMinFontSize(int32_t aMinFontSize)
2902 {
2903 // If we don't have a document, then we need to bail.
2904 if (!mDocument) {
2905 return NS_ERROR_FAILURE;
2906 }
2907
2908 if (GetIsPrintPreview()) {
2909 return NS_OK;
2910 }
2911
2912 mMinFontSize = aMinFontSize;
2913
2914 // Set the min font on all children of mContainer (even if our min font didn't
2915 // change, our children's min font may be different, though it would be unusual).
2916 // Do this first, in case kids are auto-sizing and post reflow commands on
2917 // our presshell (which should be subsumed into our own style change reflow).
2918 CallChildren(SetChildMinFontSize, NS_INT32_TO_PTR(aMinFontSize));
2919
2920 // Now change our own min font
2921 nsPresContext* pc = GetPresContext();
2922 if (pc && aMinFontSize != mPresContext->MinFontSize(nullptr)) {
2923 pc->SetBaseMinFontSize(aMinFontSize);
2924 }
2925
2926 // And do the external resources
2927 mDocument->EnumerateExternalResources(SetExtResourceMinFontSize,
2928 NS_INT32_TO_PTR(aMinFontSize));
2929
2930 return NS_OK;
2931 }
2932
2933 NS_IMETHODIMP
2934 nsDocumentViewer::GetMinFontSize(int32_t* aMinFontSize)
2935 {
2936 NS_ENSURE_ARG_POINTER(aMinFontSize);
2937 nsPresContext* pc = GetPresContext();
2938 *aMinFontSize = pc ? pc->BaseMinFontSize() : 0;
2939 return NS_OK;
2940 }
2941
2942 NS_IMETHODIMP
2943 nsDocumentViewer::SetFullZoom(float aFullZoom)
2944 {
2945 #ifdef NS_PRINT_PREVIEW
2946 if (GetIsPrintPreview()) {
2947 nsPresContext* pc = GetPresContext();
2948 NS_ENSURE_TRUE(pc, NS_OK);
2949 nsCOMPtr<nsIPresShell> shell = pc->GetPresShell();
2950 NS_ENSURE_TRUE(shell, NS_OK);
2951
2952 if (!mPrintPreviewZoomed) {
2953 mOriginalPrintPreviewScale = pc->GetPrintPreviewScale();
2954 mPrintPreviewZoomed = true;
2955 }
2956
2957 mPrintPreviewZoom = aFullZoom;
2958 pc->SetPrintPreviewScale(aFullZoom * mOriginalPrintPreviewScale);
2959 nsIPageSequenceFrame* pf = shell->GetPageSequenceFrame();
2960 if (pf) {
2961 nsIFrame* f = do_QueryFrame(pf);
2962 shell->FrameNeedsReflow(f, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
2963 }
2964
2965 nsIFrame* rootFrame = shell->GetRootFrame();
2966 if (rootFrame) {
2967 rootFrame->InvalidateFrame();
2968 }
2969 return NS_OK;
2970 }
2971 #endif
2972
2973 // If we don't have a document, then we need to bail.
2974 if (!mDocument) {
2975 return NS_ERROR_FAILURE;
2976 }
2977
2978 mPageZoom = aFullZoom;
2979
2980 struct ZoomInfo ZoomInfo = { aFullZoom };
2981 CallChildren(SetChildFullZoom, &ZoomInfo);
2982
2983 nsPresContext* pc = GetPresContext();
2984 if (pc) {
2985 pc->SetFullZoom(aFullZoom);
2986 }
2987
2988 // And do the external resources
2989 mDocument->EnumerateExternalResources(SetExtResourceFullZoom, &ZoomInfo);
2990
2991 nsContentUtils::DispatchChromeEvent(mDocument, static_cast<nsIDocument*>(mDocument),
2992 NS_LITERAL_STRING("FullZoomChange"),
2993 true, true);
2994
2995 return NS_OK;
2996 }
2997
2998 NS_IMETHODIMP
2999 nsDocumentViewer::GetFullZoom(float* aFullZoom)
3000 {
3001 NS_ENSURE_ARG_POINTER(aFullZoom);
3002 #ifdef NS_PRINT_PREVIEW
3003 if (GetIsPrintPreview()) {
3004 *aFullZoom = mPrintPreviewZoom;
3005 return NS_OK;
3006 }
3007 #endif
3008 // Check the prescontext first because it might have a temporary
3009 // setting for print-preview
3010 nsPresContext* pc = GetPresContext();
3011 *aFullZoom = pc ? pc->GetFullZoom() : mPageZoom;
3012 return NS_OK;
3013 }
3014
3015 static void
3016 SetChildAuthorStyleDisabled(nsIMarkupDocumentViewer* aChild, void* aClosure)
3017 {
3018 bool styleDisabled = *static_cast<bool*>(aClosure);
3019 aChild->SetAuthorStyleDisabled(styleDisabled);
3020 }
3021
3022
3023 NS_IMETHODIMP
3024 nsDocumentViewer::SetAuthorStyleDisabled(bool aStyleDisabled)
3025 {
3026 if (mPresShell) {
3027 mPresShell->SetAuthorStyleDisabled(aStyleDisabled);
3028 }
3029 CallChildren(SetChildAuthorStyleDisabled, &aStyleDisabled);
3030 return NS_OK;
3031 }
3032
3033 NS_IMETHODIMP
3034 nsDocumentViewer::GetAuthorStyleDisabled(bool* aStyleDisabled)
3035 {
3036 if (mPresShell) {
3037 *aStyleDisabled = mPresShell->GetAuthorStyleDisabled();
3038 } else {
3039 *aStyleDisabled = false;
3040 }
3041 return NS_OK;
3042 }
3043
3044 static bool
3045 ExtResourceEmulateMedium(nsIDocument* aDocument, void* aClosure)
3046 {
3047 nsIPresShell* shell = aDocument->GetShell();
3048 if (shell) {
3049 nsPresContext* ctxt = shell->GetPresContext();
3050 if (ctxt) {
3051 const nsAString* mediaType = static_cast<nsAString*>(aClosure);
3052 ctxt->EmulateMedium(*mediaType);
3053 }
3054 }
3055
3056 return true;
3057 }
3058
3059 static void
3060 ChildEmulateMedium(nsIMarkupDocumentViewer* aChild, void* aClosure)
3061 {
3062 const nsAString* mediaType = static_cast<nsAString*>(aClosure);
3063 aChild->EmulateMedium(*mediaType);
3064 }
3065
3066 NS_IMETHODIMP
3067 nsDocumentViewer::EmulateMedium(const nsAString& aMediaType)
3068 {
3069 if (mPresContext) {
3070 mPresContext->EmulateMedium(aMediaType);
3071 }
3072 CallChildren(ChildEmulateMedium, const_cast<nsAString*>(&aMediaType));
3073
3074 if (mDocument) {
3075 mDocument->EnumerateExternalResources(ExtResourceEmulateMedium,
3076 const_cast<nsAString*>(&aMediaType));
3077 }
3078
3079 return NS_OK;
3080 }
3081
3082 static bool
3083 ExtResourceStopEmulatingMedium(nsIDocument* aDocument, void* aClosure)
3084 {
3085 nsIPresShell* shell = aDocument->GetShell();
3086 if (shell) {
3087 nsPresContext* ctxt = shell->GetPresContext();
3088 if (ctxt) {
3089 ctxt->StopEmulatingMedium();
3090 }
3091 }
3092
3093 return true;
3094 }
3095
3096 static void
3097 ChildStopEmulatingMedium(nsIMarkupDocumentViewer* aChild, void* aClosure)
3098 {
3099 aChild->StopEmulatingMedium();
3100 }
3101
3102 NS_IMETHODIMP
3103 nsDocumentViewer::StopEmulatingMedium()
3104 {
3105 if (mPresContext) {
3106 mPresContext->StopEmulatingMedium();
3107 }
3108 CallChildren(ChildStopEmulatingMedium, nullptr);
3109
3110 if (mDocument) {
3111 mDocument->EnumerateExternalResources(ExtResourceStopEmulatingMedium,
3112 nullptr);
3113 }
3114
3115 return NS_OK;
3116 }
3117
3118 NS_IMETHODIMP nsDocumentViewer::GetForceCharacterSet(nsACString& aForceCharacterSet)
3119 {
3120 aForceCharacterSet = mForceCharacterSet;
3121 return NS_OK;
3122 }
3123
3124 static void
3125 SetChildForceCharacterSet(nsIMarkupDocumentViewer* aChild, void* aClosure)
3126 {
3127 const nsACString* charset = static_cast<nsACString*>(aClosure);
3128 aChild->SetForceCharacterSet(*charset);
3129 }
3130
3131 NS_IMETHODIMP
3132 nsDocumentViewer::SetForceCharacterSet(const nsACString& aForceCharacterSet)
3133 {
3134 mForceCharacterSet = aForceCharacterSet;
3135 // now set the force char set on all children of mContainer
3136 CallChildren(SetChildForceCharacterSet, (void*) &aForceCharacterSet);
3137 return NS_OK;
3138 }
3139
3140 NS_IMETHODIMP nsDocumentViewer::GetHintCharacterSet(nsACString& aHintCharacterSet)
3141 {
3142
3143 if(kCharsetUninitialized == mHintCharsetSource) {
3144 aHintCharacterSet.Truncate();
3145 } else {
3146 aHintCharacterSet = mHintCharset;
3147 // this can't possibly be right. we can't set a value just because somebody got a related value!
3148 //mHintCharsetSource = kCharsetUninitialized;
3149 }
3150 return NS_OK;
3151 }
3152
3153 NS_IMETHODIMP nsDocumentViewer::GetHintCharacterSetSource(int32_t *aHintCharacterSetSource)
3154 {
3155 NS_ENSURE_ARG_POINTER(aHintCharacterSetSource);
3156
3157 *aHintCharacterSetSource = mHintCharsetSource;
3158 return NS_OK;
3159 }
3160
3161 static void
3162 SetChildHintCharacterSetSource(nsIMarkupDocumentViewer* aChild, void* aClosure)
3163 {
3164 aChild->SetHintCharacterSetSource(NS_PTR_TO_INT32(aClosure));
3165 }
3166
3167 NS_IMETHODIMP
3168 nsDocumentViewer::SetHintCharacterSetSource(int32_t aHintCharacterSetSource)
3169 {
3170 mHintCharsetSource = aHintCharacterSetSource;
3171 // now set the hint char set source on all children of mContainer
3172 CallChildren(SetChildHintCharacterSetSource,
3173 NS_INT32_TO_PTR(aHintCharacterSetSource));
3174 return NS_OK;
3175 }
3176
3177 static void
3178 SetChildHintCharacterSet(nsIMarkupDocumentViewer* aChild, void* aClosure)
3179 {
3180 const nsACString* charset = static_cast<nsACString*>(aClosure);
3181 aChild->SetHintCharacterSet(*charset);
3182 }
3183
3184 NS_IMETHODIMP
3185 nsDocumentViewer::SetHintCharacterSet(const nsACString& aHintCharacterSet)
3186 {
3187 mHintCharset = aHintCharacterSet;
3188 // now set the hint char set on all children of mContainer
3189 CallChildren(SetChildHintCharacterSet, (void*) &aHintCharacterSet);
3190 return NS_OK;
3191 }
3192
3193 static void
3194 AppendChildSubtree(nsIMarkupDocumentViewer* aChild, void* aClosure)
3195 {
3196 nsTArray<nsCOMPtr<nsIMarkupDocumentViewer> >& array =
3197 *static_cast<nsTArray<nsCOMPtr<nsIMarkupDocumentViewer> >*>(aClosure);
3198 aChild->AppendSubtree(array);
3199 }
3200
3201 NS_IMETHODIMP nsDocumentViewer::AppendSubtree(nsTArray<nsCOMPtr<nsIMarkupDocumentViewer> >& aArray)
3202 {
3203 aArray.AppendElement(this);
3204 CallChildren(AppendChildSubtree, &aArray);
3205 return NS_OK;
3206 }
3207
3208 NS_IMETHODIMP
3209 nsDocumentViewer::PausePainting()
3210 {
3211 bool enablePaint = false;
3212 CallChildren(ChangeChildPaintingEnabled, &enablePaint);
3213
3214 nsIPresShell* presShell = GetPresShell();
3215 if (presShell) {
3216 presShell->PausePainting();
3217 }
3218
3219 return NS_OK;
3220 }
3221
3222 NS_IMETHODIMP
3223 nsDocumentViewer::ResumePainting()
3224 {
3225 bool enablePaint = true;
3226 CallChildren(ChangeChildPaintingEnabled, &enablePaint);
3227
3228 nsIPresShell* presShell = GetPresShell();
3229 if (presShell) {
3230 presShell->ResumePainting();
3231 }
3232
3233 return NS_OK;
3234 }
3235
3236 NS_IMETHODIMP
3237 nsDocumentViewer::ChangeMaxLineBoxWidth(int32_t aMaxLineBoxWidth)
3238 {
3239 // Change the max line box width for all children.
3240 struct LineBoxInfo lbi = { aMaxLineBoxWidth };
3241 CallChildren(ChangeChildMaxLineBoxWidth, &lbi);
3242
3243 // Now, change our max line box width.
3244 // Convert to app units, since our input is in CSS pixels.
3245 nscoord mlbw = nsPresContext::CSSPixelsToAppUnits(aMaxLineBoxWidth);
3246 nsIPresShell* presShell = GetPresShell();
3247 if (presShell) {
3248 presShell->SetMaxLineBoxWidth(mlbw);
3249 }
3250
3251 return NS_OK;
3252 }
3253
3254 NS_IMETHODIMP
3255 nsDocumentViewer::GetContentSize(int32_t* aWidth, int32_t* aHeight)
3256 {
3257 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
3258
3259 // Skip doing this on docshell-less documents for now
3260 nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(mContainer);
3261 NS_ENSURE_TRUE(docShellAsItem, NS_ERROR_NOT_AVAILABLE);
3262
3263 nsCOMPtr<nsIDocShellTreeItem> docShellParent;
3264 docShellAsItem->GetSameTypeParent(getter_AddRefs(docShellParent));
3265
3266 // It's only valid to access this from a top frame. Doesn't work from
3267 // sub-frames.
3268 NS_ENSURE_TRUE(!docShellParent, NS_ERROR_FAILURE);
3269
3270 nsCOMPtr<nsIPresShell> presShell;
3271 GetPresShell(getter_AddRefs(presShell));
3272 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
3273
3274 // Flush out all content and style updates. We can't use a resize reflow
3275 // because it won't change some sizes that a style change reflow will.
3276 mDocument->FlushPendingNotifications(Flush_Layout);
3277
3278 nsIFrame *root = presShell->GetRootFrame();
3279 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
3280
3281 nscoord prefWidth;
3282 {
3283 nsRefPtr<nsRenderingContext> rcx =
3284 presShell->CreateReferenceRenderingContext();
3285 prefWidth = root->GetPrefWidth(rcx);
3286 }
3287
3288 nsresult rv = presShell->ResizeReflow(prefWidth, NS_UNCONSTRAINEDSIZE);
3289 NS_ENSURE_SUCCESS(rv, rv);
3290
3291 nsRefPtr<nsPresContext> presContext;
3292 GetPresContext(getter_AddRefs(presContext));
3293 NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
3294
3295 // so how big is it?
3296 nsRect shellArea = presContext->GetVisibleArea();
3297 // Protect against bogus returns here
3298 NS_ENSURE_TRUE(shellArea.width != NS_UNCONSTRAINEDSIZE &&
3299 shellArea.height != NS_UNCONSTRAINEDSIZE,
3300 NS_ERROR_FAILURE);
3301
3302 *aWidth = presContext->AppUnitsToDevPixels(shellArea.width);
3303 *aHeight = presContext->AppUnitsToDevPixels(shellArea.height);
3304
3305 return NS_OK;
3306 }
3307
3308
3309 NS_IMPL_ISUPPORTS(nsDocViewerSelectionListener, nsISelectionListener)
3310
3311 nsresult nsDocViewerSelectionListener::Init(nsDocumentViewer *aDocViewer)
3312 {
3313 mDocViewer = aDocViewer;
3314 return NS_OK;
3315 }
3316
3317 /*
3318 * GetPopupNode, GetPopupLinkNode and GetPopupImageNode are helpers
3319 * for the cmd_copyLink / cmd_copyImageLocation / cmd_copyImageContents family
3320 * of commands. The focus controller stores the popup node, these retrieve
3321 * them and munge appropriately. Note that we have to store the popup node
3322 * rather than retrieving it from EventStateManager::GetFocusedContent because
3323 * not all content (images included) can receive focus.
3324 */
3325
3326 nsresult
3327 nsDocumentViewer::GetPopupNode(nsIDOMNode** aNode)
3328 {
3329 NS_ENSURE_ARG_POINTER(aNode);
3330
3331 *aNode = nullptr;
3332
3333 // get the document
3334 nsIDocument* document = GetDocument();
3335 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
3336
3337 // get the private dom window
3338 nsCOMPtr<nsPIDOMWindow> window(document->GetWindow());
3339 NS_ENSURE_TRUE(window, NS_ERROR_NOT_AVAILABLE);
3340 if (window) {
3341 nsCOMPtr<nsPIWindowRoot> root = window->GetTopWindowRoot();
3342 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
3343
3344 // get the popup node
3345 nsCOMPtr<nsIDOMNode> node = root->GetPopupNode();
3346 #ifdef MOZ_XUL
3347 if (!node) {
3348 nsPIDOMWindow* rootWindow = root->GetWindow();
3349 if (rootWindow) {
3350 nsCOMPtr<nsIDocument> rootDoc = rootWindow->GetExtantDoc();
3351 if (rootDoc) {
3352 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
3353 if (pm) {
3354 node = pm->GetLastTriggerPopupNode(rootDoc);
3355 }
3356 }
3357 }
3358 }
3359 #endif
3360 node.swap(*aNode);
3361 }
3362
3363 return NS_OK;
3364 }
3365
3366 // GetPopupLinkNode: return popup link node or fail
3367 nsresult
3368 nsDocumentViewer::GetPopupLinkNode(nsIDOMNode** aNode)
3369 {
3370 NS_ENSURE_ARG_POINTER(aNode);
3371
3372 // you get null unless i say so
3373 *aNode = nullptr;
3374
3375 // find popup node
3376 nsCOMPtr<nsIDOMNode> node;
3377 nsresult rv = GetPopupNode(getter_AddRefs(node));
3378 NS_ENSURE_SUCCESS(rv, rv);
3379
3380 // find out if we have a link in our ancestry
3381 while (node) {
3382
3383 nsCOMPtr<nsIContent> content(do_QueryInterface(node));
3384 if (content) {
3385 nsCOMPtr<nsIURI> hrefURI = content->GetHrefURI();
3386 if (hrefURI) {
3387 *aNode = node;
3388 NS_IF_ADDREF(*aNode); // addref
3389 return NS_OK;
3390 }
3391 }
3392
3393 // get our parent and keep trying...
3394 nsCOMPtr<nsIDOMNode> parentNode;
3395 node->GetParentNode(getter_AddRefs(parentNode));
3396 node = parentNode;
3397 }
3398
3399 // if we have no node, fail
3400 return NS_ERROR_FAILURE;
3401 }
3402
3403 // GetPopupLinkNode: return popup image node or fail
3404 nsresult
3405 nsDocumentViewer::GetPopupImageNode(nsIImageLoadingContent** aNode)
3406 {
3407 NS_ENSURE_ARG_POINTER(aNode);
3408
3409 // you get null unless i say so
3410 *aNode = nullptr;
3411
3412 // find popup node
3413 nsCOMPtr<nsIDOMNode> node;
3414 nsresult rv = GetPopupNode(getter_AddRefs(node));
3415 NS_ENSURE_SUCCESS(rv, rv);
3416
3417 if (node)
3418 CallQueryInterface(node, aNode);
3419
3420 return NS_OK;
3421 }
3422
3423 /*
3424 * XXX dr
3425 * ------
3426 * These two functions -- GetInLink and GetInImage -- are kind of annoying
3427 * in that they only get called from the controller (in
3428 * nsDOMWindowController::IsCommandEnabled). The actual construction of the
3429 * context menus in communicator (nsContextMenu.js) has its own, redundant
3430 * tests. No big deal, but good to keep in mind if we ever clean context
3431 * menus.
3432 */
3433
3434 NS_IMETHODIMP nsDocumentViewer::GetInLink(bool* aInLink)
3435 {
3436 #ifdef DEBUG_dr
3437 printf("dr :: nsDocumentViewer::GetInLink\n");
3438 #endif
3439
3440 NS_ENSURE_ARG_POINTER(aInLink);
3441
3442 // we're not in a link unless i say so
3443 *aInLink = false;
3444
3445 // get the popup link
3446 nsCOMPtr<nsIDOMNode> node;
3447 nsresult rv = GetPopupLinkNode(getter_AddRefs(node));
3448 if (NS_FAILED(rv)) return rv;
3449 NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
3450
3451 // if we made it here, we're in a link
3452 *aInLink = true;
3453 return NS_OK;
3454 }
3455
3456 NS_IMETHODIMP nsDocumentViewer::GetInImage(bool* aInImage)
3457 {
3458 #ifdef DEBUG_dr
3459 printf("dr :: nsDocumentViewer::GetInImage\n");
3460 #endif
3461
3462 NS_ENSURE_ARG_POINTER(aInImage);
3463
3464 // we're not in an image unless i say so
3465 *aInImage = false;
3466
3467 // get the popup image
3468 nsCOMPtr<nsIImageLoadingContent> node;
3469 nsresult rv = GetPopupImageNode(getter_AddRefs(node));
3470 if (NS_FAILED(rv)) return rv;
3471 NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
3472
3473 // if we made it here, we're in an image
3474 *aInImage = true;
3475 return NS_OK;
3476 }
3477
3478 NS_IMETHODIMP nsDocViewerSelectionListener::NotifySelectionChanged(nsIDOMDocument *, nsISelection *, int16_t)
3479 {
3480 NS_ASSERTION(mDocViewer, "Should have doc viewer!");
3481
3482 // get the selection state
3483 nsCOMPtr<nsISelection> selection;
3484 nsresult rv = mDocViewer->GetDocumentSelection(getter_AddRefs(selection));
3485 if (NS_FAILED(rv)) return rv;
3486
3487 bool selectionCollapsed;
3488 selection->GetIsCollapsed(&selectionCollapsed);
3489 // we only call UpdateCommands when the selection changes from collapsed
3490 // to non-collapsed or vice versa. We might need another update string
3491 // for simple selection changes, but that would be expenseive.
3492 if (!mGotSelectionState || mSelectionWasCollapsed != selectionCollapsed)
3493 {
3494 nsIDocument* theDoc = mDocViewer->GetDocument();
3495 if (!theDoc) return NS_ERROR_FAILURE;
3496
3497 nsPIDOMWindow *domWindow = theDoc->GetWindow();
3498 if (!domWindow) return NS_ERROR_FAILURE;
3499
3500 domWindow->UpdateCommands(NS_LITERAL_STRING("select"));
3501 mGotSelectionState = true;
3502 mSelectionWasCollapsed = selectionCollapsed;
3503 }
3504
3505 return NS_OK;
3506 }
3507
3508 //nsDocViewerFocusListener
3509 NS_IMPL_ISUPPORTS(nsDocViewerFocusListener,
3510 nsIDOMEventListener)
3511
3512 nsDocViewerFocusListener::nsDocViewerFocusListener()
3513 :mDocViewer(nullptr)
3514 {
3515 }
3516
3517 nsDocViewerFocusListener::~nsDocViewerFocusListener(){}
3518
3519 nsresult
3520 nsDocViewerFocusListener::HandleEvent(nsIDOMEvent* aEvent)
3521 {
3522 NS_ENSURE_STATE(mDocViewer);
3523
3524 nsCOMPtr<nsIPresShell> shell;
3525 mDocViewer->GetPresShell(getter_AddRefs(shell));
3526 NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
3527
3528 nsCOMPtr<nsISelectionController> selCon = do_QueryInterface(shell);
3529 int16_t selectionStatus;
3530 selCon->GetDisplaySelection(&selectionStatus);
3531
3532 nsAutoString eventType;
3533 aEvent->GetType(eventType);
3534 if (eventType.EqualsLiteral("focus")) {
3535 // If selection was disabled, re-enable it.
3536 if(selectionStatus == nsISelectionController::SELECTION_DISABLED ||
3537 selectionStatus == nsISelectionController::SELECTION_HIDDEN) {
3538 selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
3539 selCon->RepaintSelection(nsISelectionController::SELECTION_NORMAL);
3540 }
3541 } else {
3542 NS_ABORT_IF_FALSE(eventType.EqualsLiteral("blur"),
3543 "Unexpected event type");
3544 // If selection was on, disable it.
3545 if(selectionStatus == nsISelectionController::SELECTION_ON ||
3546 selectionStatus == nsISelectionController::SELECTION_ATTENTION) {
3547 selCon->SetDisplaySelection(nsISelectionController::SELECTION_DISABLED);
3548 selCon->RepaintSelection(nsISelectionController::SELECTION_NORMAL);
3549 }
3550 }
3551
3552 return NS_OK;
3553 }
3554
3555 nsresult
3556 nsDocViewerFocusListener::Init(nsDocumentViewer *aDocViewer)
3557 {
3558 mDocViewer = aDocViewer;
3559 return NS_OK;
3560 }
3561
3562 /** ---------------------------------------------------
3563 * From nsIWebBrowserPrint
3564 */
3565
3566 #ifdef NS_PRINTING
3567
3568 NS_IMETHODIMP
3569 nsDocumentViewer::Print(nsIPrintSettings* aPrintSettings,
3570 nsIWebProgressListener* aWebProgressListener)
3571 {
3572 // Printing XUL documents is not supported.
3573 nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocument));
3574 if (xulDoc) {
3575 return NS_ERROR_FAILURE;
3576 }
3577
3578 if (!mContainer) {
3579 PR_PL(("Container was destroyed yet we are still trying to use it!"));
3580 return NS_ERROR_FAILURE;
3581 }
3582
3583 nsCOMPtr<nsIDocShell> docShell(mContainer);
3584 NS_ENSURE_STATE(docShell);
3585
3586 // Check to see if this document is still busy
3587 // If it is busy and we aren't already "queued" up to print then
3588 // Indicate there is a print pending and cache the args for later
3589 uint32_t busyFlags = nsIDocShell::BUSY_FLAGS_NONE;
3590 if ((NS_FAILED(docShell->GetBusyFlags(&busyFlags)) ||
3591 (busyFlags != nsIDocShell::BUSY_FLAGS_NONE && busyFlags & nsIDocShell::BUSY_FLAGS_PAGE_LOADING)) &&
3592 !mPrintDocIsFullyLoaded) {
3593 if (!mPrintIsPending) {
3594 mCachedPrintSettings = aPrintSettings;
3595 mCachedPrintWebProgressListner = aWebProgressListener;
3596 mPrintIsPending = true;
3597 }
3598 PR_PL(("Printing Stopped - document is still busy!"));
3599 return NS_ERROR_GFX_PRINTER_DOC_IS_BUSY;
3600 }
3601
3602 if (!mDocument || !mDeviceContext) {
3603 PR_PL(("Can't Print without a document and a device context"));
3604 return NS_ERROR_FAILURE;
3605 }
3606
3607 nsresult rv;
3608
3609 // if we are printing another URL, then exit
3610 // the reason we check here is because this method can be called while
3611 // another is still in here (the printing dialog is a good example).
3612 // the only time we can print more than one job at a time is the regression tests
3613 if (GetIsPrinting()) {
3614 // Let the user know we are not ready to print.
3615 rv = NS_ERROR_NOT_AVAILABLE;
3616 nsPrintEngine::ShowPrintErrorDialog(rv);
3617 return rv;
3618 }
3619
3620 nsAutoPtr<nsPrintEventDispatcher> beforeAndAfterPrint(
3621 new nsPrintEventDispatcher(mDocument));
3622 NS_ENSURE_STATE(!GetIsPrinting());
3623 // If we are hosting a full-page plugin, tell it to print
3624 // first. It shows its own native print UI.
3625 nsCOMPtr<nsIPluginDocument> pDoc(do_QueryInterface(mDocument));
3626 if (pDoc)
3627 return pDoc->Print();
3628
3629 if (!mPrintEngine) {
3630 NS_ENSURE_STATE(mDeviceContext);
3631 mPrintEngine = new nsPrintEngine();
3632
3633 rv = mPrintEngine->Initialize(this, mContainer, mDocument,
3634 float(mDeviceContext->AppUnitsPerCSSInch()) /
3635 float(mDeviceContext->AppUnitsPerDevPixel()) /
3636 mPageZoom,
3637 #ifdef DEBUG
3638 mDebugFile
3639 #else
3640 nullptr
3641 #endif
3642 );
3643 if (NS_FAILED(rv)) {
3644 mPrintEngine->Destroy();
3645 mPrintEngine = nullptr;
3646 return rv;
3647 }
3648 }
3649 if (mPrintEngine->HasPrintCallbackCanvas()) {
3650 mBeforeAndAfterPrint = beforeAndAfterPrint;
3651 }
3652 dom::Element* root = mDocument->GetRootElement();
3653 if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdisallowselectionprint)) {
3654 mPrintEngine->SetDisallowSelectionPrint(true);
3655 }
3656 if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::moznomarginboxes)) {
3657 mPrintEngine->SetNoMarginBoxes(true);
3658 }
3659 rv = mPrintEngine->Print(aPrintSettings, aWebProgressListener);
3660 if (NS_FAILED(rv)) {
3661 OnDonePrinting();
3662 }
3663 return rv;
3664 }
3665
3666 NS_IMETHODIMP
3667 nsDocumentViewer::PrintPreview(nsIPrintSettings* aPrintSettings,
3668 nsIDOMWindow *aChildDOMWin,
3669 nsIWebProgressListener* aWebProgressListener)
3670 {
3671 #if defined(NS_PRINTING) && defined(NS_PRINT_PREVIEW)
3672 NS_WARN_IF_FALSE(IsInitializedForPrintPreview(),
3673 "Using docshell.printPreview is the preferred way for print previewing!");
3674
3675 NS_ENSURE_ARG_POINTER(aChildDOMWin);
3676 nsresult rv = NS_OK;
3677
3678 if (GetIsPrinting()) {
3679 nsPrintEngine::CloseProgressDialog(aWebProgressListener);
3680 return NS_ERROR_FAILURE;
3681 }
3682
3683 // Printing XUL documents is not supported.
3684 nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocument));
3685 if (xulDoc) {
3686 nsPrintEngine::CloseProgressDialog(aWebProgressListener);
3687 return NS_ERROR_FAILURE;
3688 }
3689
3690 nsCOMPtr<nsIDocShell> docShell(mContainer);
3691 if (!docShell || !mDeviceContext) {
3692 PR_PL(("Can't Print Preview without device context and docshell"));
3693 return NS_ERROR_FAILURE;
3694 }
3695
3696 nsCOMPtr<nsIDOMDocument> domDoc;
3697 aChildDOMWin->GetDocument(getter_AddRefs(domDoc));
3698 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
3699 NS_ENSURE_STATE(doc);
3700
3701 nsAutoPtr<nsPrintEventDispatcher> beforeAndAfterPrint(
3702 new nsPrintEventDispatcher(doc));
3703 NS_ENSURE_STATE(!GetIsPrinting());
3704 if (!mPrintEngine) {
3705 mPrintEngine = new nsPrintEngine();
3706
3707 rv = mPrintEngine->Initialize(this, mContainer, doc,
3708 float(mDeviceContext->AppUnitsPerCSSInch()) /
3709 float(mDeviceContext->AppUnitsPerDevPixel()) /
3710 mPageZoom,
3711 #ifdef DEBUG
3712 mDebugFile
3713 #else
3714 nullptr
3715 #endif
3716 );
3717 if (NS_FAILED(rv)) {
3718 mPrintEngine->Destroy();
3719 mPrintEngine = nullptr;
3720 return rv;
3721 }
3722 }
3723 if (mPrintEngine->HasPrintCallbackCanvas()) {
3724 mBeforeAndAfterPrint = beforeAndAfterPrint;
3725 }
3726 dom::Element* root = doc->GetRootElement();
3727 if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdisallowselectionprint)) {
3728 PR_PL(("PrintPreview: found mozdisallowselectionprint"));
3729 mPrintEngine->SetDisallowSelectionPrint(true);
3730 }
3731 if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::moznomarginboxes)) {
3732 PR_PL(("PrintPreview: found moznomarginboxes"));
3733 mPrintEngine->SetNoMarginBoxes(true);
3734 }
3735 rv = mPrintEngine->PrintPreview(aPrintSettings, aChildDOMWin, aWebProgressListener);
3736 mPrintPreviewZoomed = false;
3737 if (NS_FAILED(rv)) {
3738 OnDonePrinting();
3739 }
3740 return rv;
3741 #else
3742 return NS_ERROR_FAILURE;
3743 #endif
3744 }
3745
3746 //----------------------------------------------------------------------
3747 NS_IMETHODIMP
3748 nsDocumentViewer::PrintPreviewNavigate(int16_t aType, int32_t aPageNum)
3749 {
3750 if (!GetIsPrintPreview() ||
3751 mPrintEngine->GetIsCreatingPrintPreview())
3752 return NS_ERROR_FAILURE;
3753
3754 nsIScrollableFrame* sf =
3755 mPrintEngine->GetPrintPreviewPresShell()->GetRootScrollFrameAsScrollable();
3756 if (!sf)
3757 return NS_OK;
3758
3759 // Check to see if we can short circut scrolling to the top
3760 if (aType == nsIWebBrowserPrint::PRINTPREVIEW_HOME ||
3761 (aType == nsIWebBrowserPrint::PRINTPREVIEW_GOTO_PAGENUM && aPageNum == 1)) {
3762 sf->ScrollTo(nsPoint(0, 0), nsIScrollableFrame::INSTANT);
3763 return NS_OK;
3764 }
3765
3766 // Finds the SimplePageSequencer frame
3767 // in PP mPrtPreview->mPrintObject->mSeqFrame is null
3768 nsIFrame* seqFrame = nullptr;
3769 int32_t pageCount = 0;
3770 if (NS_FAILED(mPrintEngine->GetSeqFrameAndCountPages(seqFrame, pageCount))) {
3771 return NS_ERROR_FAILURE;
3772 }
3773
3774 // Figure where we are currently scrolled to
3775 nsPoint pt = sf->GetScrollPosition();
3776
3777 int32_t pageNum = 1;
3778 nsIFrame * fndPageFrame = nullptr;
3779 nsIFrame * currentPage = nullptr;
3780
3781 // If it is "End" then just do a "goto" to the last page
3782 if (aType == nsIWebBrowserPrint::PRINTPREVIEW_END) {
3783 aType = nsIWebBrowserPrint::PRINTPREVIEW_GOTO_PAGENUM;
3784 aPageNum = pageCount;
3785 }
3786
3787 // Now, locate the current page we are on and
3788 // and the page of the page number
3789 nsIFrame* pageFrame = seqFrame->GetFirstPrincipalChild();
3790 while (pageFrame != nullptr) {
3791 nsRect pageRect = pageFrame->GetRect();
3792 if (pageRect.Contains(pageRect.x, pt.y)) {
3793 currentPage = pageFrame;
3794 }
3795 if (pageNum == aPageNum) {
3796 fndPageFrame = pageFrame;
3797 break;
3798 }
3799 pageNum++;
3800 pageFrame = pageFrame->GetNextSibling();
3801 }
3802
3803 if (aType == nsIWebBrowserPrint::PRINTPREVIEW_PREV_PAGE) {
3804 if (currentPage) {
3805 fndPageFrame = currentPage->GetPrevInFlow();
3806 if (!fndPageFrame) {
3807 return NS_OK;
3808 }
3809 } else {
3810 return NS_OK;
3811 }
3812 } else if (aType == nsIWebBrowserPrint::PRINTPREVIEW_NEXT_PAGE) {
3813 if (currentPage) {
3814 fndPageFrame = currentPage->GetNextInFlow();
3815 if (!fndPageFrame) {
3816 return NS_OK;
3817 }
3818 } else {
3819 return NS_OK;
3820 }
3821 } else { // If we get here we are doing "GoTo"
3822 if (aPageNum < 0 || aPageNum > pageCount) {
3823 return NS_OK;
3824 }
3825 }
3826
3827 if (fndPageFrame) {
3828 nscoord newYPosn =
3829 nscoord(mPrintEngine->GetPrintPreviewScale() * fndPageFrame->GetPosition().y);
3830 sf->ScrollTo(nsPoint(pt.x, newYPosn), nsIScrollableFrame::INSTANT);
3831 }
3832 return NS_OK;
3833
3834 }
3835
3836 /* readonly attribute nsIPrintSettings globalPrintSettings; */
3837 NS_IMETHODIMP
3838 nsDocumentViewer::GetGlobalPrintSettings(nsIPrintSettings * *aGlobalPrintSettings)
3839 {
3840 return nsPrintEngine::GetGlobalPrintSettings(aGlobalPrintSettings);
3841 }
3842
3843 /* readonly attribute boolean doingPrint; */
3844 // XXX This always returns false for subdocuments
3845 NS_IMETHODIMP
3846 nsDocumentViewer::GetDoingPrint(bool *aDoingPrint)
3847 {
3848 NS_ENSURE_ARG_POINTER(aDoingPrint);
3849
3850 *aDoingPrint = false;
3851 if (mPrintEngine) {
3852 // XXX shouldn't this be GetDoingPrint() ?
3853 return mPrintEngine->GetDoingPrintPreview(aDoingPrint);
3854 }
3855 return NS_OK;
3856 }
3857
3858 /* readonly attribute boolean doingPrintPreview; */
3859 // XXX This always returns false for subdocuments
3860 NS_IMETHODIMP
3861 nsDocumentViewer::GetDoingPrintPreview(bool *aDoingPrintPreview)
3862 {
3863 NS_ENSURE_ARG_POINTER(aDoingPrintPreview);
3864
3865 *aDoingPrintPreview = false;
3866 if (mPrintEngine) {
3867 return mPrintEngine->GetDoingPrintPreview(aDoingPrintPreview);
3868 }
3869 return NS_OK;
3870 }
3871
3872 /* readonly attribute nsIPrintSettings currentPrintSettings; */
3873 NS_IMETHODIMP
3874 nsDocumentViewer::GetCurrentPrintSettings(nsIPrintSettings * *aCurrentPrintSettings)
3875 {
3876 NS_ENSURE_ARG_POINTER(aCurrentPrintSettings);
3877
3878 *aCurrentPrintSettings = nullptr;
3879 NS_ENSURE_TRUE(mPrintEngine, NS_ERROR_FAILURE);
3880
3881 return mPrintEngine->GetCurrentPrintSettings(aCurrentPrintSettings);
3882 }
3883
3884
3885 /* readonly attribute nsIDOMWindow currentChildDOMWindow; */
3886 NS_IMETHODIMP
3887 nsDocumentViewer::GetCurrentChildDOMWindow(nsIDOMWindow * *aCurrentChildDOMWindow)
3888 {
3889 NS_ENSURE_ARG_POINTER(aCurrentChildDOMWindow);
3890 *aCurrentChildDOMWindow = nullptr;
3891 return NS_ERROR_NOT_IMPLEMENTED;
3892 }
3893
3894 /* void cancel (); */
3895 NS_IMETHODIMP
3896 nsDocumentViewer::Cancel()
3897 {
3898 NS_ENSURE_TRUE(mPrintEngine, NS_ERROR_FAILURE);
3899 return mPrintEngine->Cancelled();
3900 }
3901
3902 /* void exitPrintPreview (); */
3903 NS_IMETHODIMP
3904 nsDocumentViewer::ExitPrintPreview()
3905 {
3906 if (GetIsPrinting())
3907 return NS_ERROR_FAILURE;
3908 NS_ENSURE_TRUE(mPrintEngine, NS_ERROR_FAILURE);
3909
3910 if (GetIsPrintPreview()) {
3911 ReturnToGalleyPresentation();
3912 }
3913 return NS_OK;
3914 }
3915
3916 //----------------------------------------------------------------------------------
3917 // Enumerate all the documents for their titles
3918 NS_IMETHODIMP
3919 nsDocumentViewer::EnumerateDocumentNames(uint32_t* aCount,
3920 char16_t*** aResult)
3921 {
3922 #ifdef NS_PRINTING
3923 NS_ENSURE_ARG(aCount);
3924 NS_ENSURE_ARG_POINTER(aResult);
3925 NS_ENSURE_TRUE(mPrintEngine, NS_ERROR_FAILURE);
3926
3927 return mPrintEngine->EnumerateDocumentNames(aCount, aResult);
3928 #else
3929 return NS_ERROR_FAILURE;
3930 #endif
3931 }
3932
3933 /* readonly attribute boolean isFramesetFrameSelected; */
3934 NS_IMETHODIMP
3935 nsDocumentViewer::GetIsFramesetFrameSelected(bool *aIsFramesetFrameSelected)
3936 {
3937 #ifdef NS_PRINTING
3938 *aIsFramesetFrameSelected = false;
3939 NS_ENSURE_TRUE(mPrintEngine, NS_ERROR_FAILURE);
3940
3941 return mPrintEngine->GetIsFramesetFrameSelected(aIsFramesetFrameSelected);
3942 #else
3943 return NS_ERROR_FAILURE;
3944 #endif
3945 }
3946
3947 /* readonly attribute long printPreviewNumPages; */
3948 NS_IMETHODIMP
3949 nsDocumentViewer::GetPrintPreviewNumPages(int32_t *aPrintPreviewNumPages)
3950 {
3951 #ifdef NS_PRINTING
3952 NS_ENSURE_ARG_POINTER(aPrintPreviewNumPages);
3953 NS_ENSURE_TRUE(mPrintEngine, NS_ERROR_FAILURE);
3954
3955 return mPrintEngine->GetPrintPreviewNumPages(aPrintPreviewNumPages);
3956 #else
3957 return NS_ERROR_FAILURE;
3958 #endif
3959 }
3960
3961 /* readonly attribute boolean isFramesetDocument; */
3962 NS_IMETHODIMP
3963 nsDocumentViewer::GetIsFramesetDocument(bool *aIsFramesetDocument)
3964 {
3965 #ifdef NS_PRINTING
3966 *aIsFramesetDocument = false;
3967 NS_ENSURE_TRUE(mPrintEngine, NS_ERROR_FAILURE);
3968
3969 return mPrintEngine->GetIsFramesetDocument(aIsFramesetDocument);
3970 #else
3971 return NS_ERROR_FAILURE;
3972 #endif
3973 }
3974
3975 /* readonly attribute boolean isIFrameSelected; */
3976 NS_IMETHODIMP
3977 nsDocumentViewer::GetIsIFrameSelected(bool *aIsIFrameSelected)
3978 {
3979 #ifdef NS_PRINTING
3980 *aIsIFrameSelected = false;
3981 NS_ENSURE_TRUE(mPrintEngine, NS_ERROR_FAILURE);
3982
3983 return mPrintEngine->GetIsIFrameSelected(aIsIFrameSelected);
3984 #else
3985 return NS_ERROR_FAILURE;
3986 #endif
3987 }
3988
3989 /* readonly attribute boolean isRangeSelection; */
3990 NS_IMETHODIMP
3991 nsDocumentViewer::GetIsRangeSelection(bool *aIsRangeSelection)
3992 {
3993 #ifdef NS_PRINTING
3994 *aIsRangeSelection = false;
3995 NS_ENSURE_TRUE(mPrintEngine, NS_ERROR_FAILURE);
3996
3997 return mPrintEngine->GetIsRangeSelection(aIsRangeSelection);
3998 #else
3999 return NS_ERROR_FAILURE;
4000 #endif
4001 }
4002
4003 //----------------------------------------------------------------------------------
4004 // Printing/Print Preview Helpers
4005 //----------------------------------------------------------------------------------
4006
4007 //----------------------------------------------------------------------------------
4008 // Walks the document tree and tells each DocShell whether Printing/PP is happening
4009 void
4010 nsDocumentViewer::SetIsPrintingInDocShellTree(nsIDocShellTreeItem* aParentNode,
4011 bool aIsPrintingOrPP,
4012 bool aStartAtTop)
4013 {
4014 nsCOMPtr<nsIDocShellTreeItem> parentItem(do_QueryInterface(aParentNode));
4015
4016 // find top of "same parent" tree
4017 if (aStartAtTop) {
4018 if (aIsPrintingOrPP) {
4019 while (parentItem) {
4020 nsCOMPtr<nsIDocShellTreeItem> parent;
4021 parentItem->GetSameTypeParent(getter_AddRefs(parent));
4022 if (!parent) {
4023 break;
4024 }
4025 parentItem = do_QueryInterface(parent);
4026 }
4027 mTopContainerWhilePrinting = do_GetWeakReference(parentItem);
4028 } else {
4029 parentItem = do_QueryReferent(mTopContainerWhilePrinting);
4030 }
4031 }
4032
4033 // Check to see if the DocShell's ContentViewer is printing/PP
4034 nsCOMPtr<nsIContentViewerContainer> viewerContainer(do_QueryInterface(parentItem));
4035 if (viewerContainer) {
4036 viewerContainer->SetIsPrinting(aIsPrintingOrPP);
4037 }
4038
4039 if (!aParentNode) {
4040 return;
4041 }
4042
4043 // Traverse children to see if any of them are printing.
4044 int32_t n;
4045 aParentNode->GetChildCount(&n);
4046 for (int32_t i=0; i < n; i++) {
4047 nsCOMPtr<nsIDocShellTreeItem> child;
4048 aParentNode->GetChildAt(i, getter_AddRefs(child));
4049 NS_ASSERTION(child, "child isn't nsIDocShell");
4050 if (child) {
4051 SetIsPrintingInDocShellTree(child, aIsPrintingOrPP, false);
4052 }
4053 }
4054
4055 }
4056 #endif // NS_PRINTING
4057
4058 bool
4059 nsDocumentViewer::ShouldAttachToTopLevel()
4060 {
4061 if (!mParentWidget)
4062 return false;
4063
4064 nsCOMPtr<nsIDocShellTreeItem> containerItem(mContainer);
4065 if (!containerItem)
4066 return false;
4067
4068 // We always attach when using puppet widgets
4069 if (nsIWidget::UsePuppetWidgets())
4070 return true;
4071
4072 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
4073 // On windows, in the parent process we also attach, but just to
4074 // chrome items
4075 nsWindowType winType = mParentWidget->WindowType();
4076 if ((winType == eWindowType_toplevel ||
4077 winType == eWindowType_dialog ||
4078 winType == eWindowType_invisible) &&
4079 containerItem->ItemType() == nsIDocShellTreeItem::typeChrome) {
4080 return true;
4081 }
4082 #endif
4083
4084 return false;
4085 }
4086
4087 bool CollectDocuments(nsIDocument* aDocument, void* aData)
4088 {
4089 if (aDocument) {
4090 static_cast<nsCOMArray<nsIDocument>*>(aData)->AppendObject(aDocument);
4091 aDocument->EnumerateSubDocuments(CollectDocuments, aData);
4092 }
4093 return true;
4094 }
4095
4096 void
4097 nsDocumentViewer::DispatchEventToWindowTree(nsIDocument* aDoc,
4098 const nsAString& aEvent)
4099 {
4100 nsCOMArray<nsIDocument> targets;
4101 CollectDocuments(aDoc, &targets);
4102 for (int32_t i = 0; i < targets.Count(); ++i) {
4103 nsIDocument* d = targets[i];
4104 nsContentUtils::DispatchTrustedEvent(d, d->GetWindow(),
4105 aEvent, false, false, nullptr);
4106 }
4107 }
4108
4109 //------------------------------------------------------------
4110 // XXX this always returns false for subdocuments
4111 bool
4112 nsDocumentViewer::GetIsPrinting()
4113 {
4114 #ifdef NS_PRINTING
4115 if (mPrintEngine) {
4116 return mPrintEngine->GetIsPrinting();
4117 }
4118 #endif
4119 return false;
4120 }
4121
4122 //------------------------------------------------------------
4123 // Notification from the PrintEngine of the current Printing status
4124 void
4125 nsDocumentViewer::SetIsPrinting(bool aIsPrinting)
4126 {
4127 #ifdef NS_PRINTING
4128 // Set all the docShells in the docshell tree to be printing.
4129 // that way if anyone of them tries to "navigate" it can't
4130 nsCOMPtr<nsIDocShell> docShell(mContainer);
4131 if (docShell || !aIsPrinting) {
4132 SetIsPrintingInDocShellTree(docShell, aIsPrinting, true);
4133 } else {
4134 NS_WARNING("Did you close a window before printing?");
4135 }
4136
4137 if (!aIsPrinting) {
4138 mBeforeAndAfterPrint = nullptr;
4139 }
4140 #endif
4141 }
4142
4143 //------------------------------------------------------------
4144 // The PrintEngine holds the current value
4145 // this called from inside the DocViewer.
4146 // XXX it always returns false for subdocuments
4147 bool
4148 nsDocumentViewer::GetIsPrintPreview()
4149 {
4150 #ifdef NS_PRINTING
4151 if (mPrintEngine) {
4152 return mPrintEngine->GetIsPrintPreview();
4153 }
4154 #endif
4155 return false;
4156 }
4157
4158 //------------------------------------------------------------
4159 // Notification from the PrintEngine of the current PP status
4160 void
4161 nsDocumentViewer::SetIsPrintPreview(bool aIsPrintPreview)
4162 {
4163 #ifdef NS_PRINTING
4164 // Set all the docShells in the docshell tree to be printing.
4165 // that way if anyone of them tries to "navigate" it can't
4166 nsCOMPtr<nsIDocShell> docShell(mContainer);
4167 if (docShell || !aIsPrintPreview) {
4168 SetIsPrintingInDocShellTree(docShell, aIsPrintPreview, true);
4169 }
4170 if (!aIsPrintPreview) {
4171 mBeforeAndAfterPrint = nullptr;
4172 }
4173 #endif
4174 if (!aIsPrintPreview) {
4175 if (mPresShell) {
4176 DestroyPresShell();
4177 }
4178 mWindow = nullptr;
4179 mViewManager = nullptr;
4180 mPresContext = nullptr;
4181 mPresShell = nullptr;
4182 }
4183 }
4184
4185 //----------------------------------------------------------------------------------
4186 // nsIDocumentViewerPrint IFace
4187 //----------------------------------------------------------------------------------
4188
4189 //------------------------------------------------------------
4190 void
4191 nsDocumentViewer::IncrementDestroyRefCount()
4192 {
4193 ++mDestroyRefCount;
4194 }
4195
4196 //------------------------------------------------------------
4197
4198 #if defined(NS_PRINTING) && defined(NS_PRINT_PREVIEW)
4199 //------------------------------------------------------------
4200 // Reset ESM focus for all descendent doc shells.
4201 static void
4202 ResetFocusState(nsIDocShell* aDocShell)
4203 {
4204 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
4205 if (!fm)
4206 return;
4207
4208 nsCOMPtr<nsISimpleEnumerator> docShellEnumerator;
4209 aDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeContent,
4210 nsIDocShell::ENUMERATE_FORWARDS,
4211 getter_AddRefs(docShellEnumerator));
4212
4213 nsCOMPtr<nsISupports> currentContainer;
4214 bool hasMoreDocShells;
4215 while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMoreDocShells))
4216 && hasMoreDocShells) {
4217 docShellEnumerator->GetNext(getter_AddRefs(currentContainer));
4218 nsCOMPtr<nsIDOMWindow> win = do_GetInterface(currentContainer);
4219 if (win)
4220 fm->ClearFocus(win);
4221 }
4222 }
4223 #endif // NS_PRINTING && NS_PRINT_PREVIEW
4224
4225 void
4226 nsDocumentViewer::ReturnToGalleyPresentation()
4227 {
4228 #if defined(NS_PRINTING) && defined(NS_PRINT_PREVIEW)
4229 if (!GetIsPrintPreview()) {
4230 NS_ERROR("Wow, we should never get here!");
4231 return;
4232 }
4233
4234 SetIsPrintPreview(false);
4235
4236 mPrintEngine->TurnScriptingOn(true);
4237 mPrintEngine->Destroy();
4238 mPrintEngine = nullptr;
4239
4240 nsCOMPtr<nsIDocShell> docShell(mContainer);
4241 ResetFocusState(docShell);
4242
4243 SetTextZoom(mTextZoom);
4244 SetFullZoom(mPageZoom);
4245 SetMinFontSize(mMinFontSize);
4246 Show();
4247
4248 #endif // NS_PRINTING && NS_PRINT_PREVIEW
4249 }
4250
4251 //------------------------------------------------------------
4252 // This called ONLY when printing has completed and the DV
4253 // is being notified that it should get rid of the PrintEngine.
4254 //
4255 // BUT, if we are in Print Preview then we want to ignore the
4256 // notification (we do not get rid of the PrintEngine)
4257 //
4258 // One small caveat:
4259 // This IS called from two places in this module for cleaning
4260 // up when an error occurred during the start up printing
4261 // and print preview
4262 //
4263 void
4264 nsDocumentViewer::OnDonePrinting()
4265 {
4266 #if defined(NS_PRINTING) && defined(NS_PRINT_PREVIEW)
4267 if (mPrintEngine) {
4268 nsRefPtr<nsPrintEngine> pe = mPrintEngine;
4269 if (GetIsPrintPreview()) {
4270 pe->DestroyPrintingData();
4271 } else {
4272 mPrintEngine = nullptr;
4273 pe->Destroy();
4274 }
4275
4276 // We are done printing, now cleanup
4277 if (mDeferredWindowClose) {
4278 mDeferredWindowClose = false;
4279 nsCOMPtr<nsIDOMWindow> win =
4280 do_GetInterface(static_cast<nsIDocShell*>(mContainer));
4281 if (win)
4282 win->Close();
4283 } else if (mClosingWhilePrinting) {
4284 if (mDocument) {
4285 mDocument->SetScriptGlobalObject(nullptr);
4286 mDocument->Destroy();
4287 mDocument = nullptr;
4288 }
4289 mClosingWhilePrinting = false;
4290 }
4291 }
4292 #endif // NS_PRINTING && NS_PRINT_PREVIEW
4293 }
4294
4295 NS_IMETHODIMP nsDocumentViewer::SetPageMode(bool aPageMode, nsIPrintSettings* aPrintSettings)
4296 {
4297 // XXX Page mode is only partially working; it's currently used for
4298 // reftests that require a paginated context
4299 mIsPageMode = aPageMode;
4300
4301 if (mPresShell) {
4302 DestroyPresShell();
4303 }
4304
4305 if (mPresContext) {
4306 DestroyPresContext();
4307 }
4308
4309 mViewManager = nullptr;
4310 mWindow = nullptr;
4311
4312 NS_ENSURE_STATE(mDocument);
4313 if (aPageMode)
4314 {
4315 mPresContext = CreatePresContext(mDocument,
4316 nsPresContext::eContext_PageLayout, FindContainerView());
4317 NS_ENSURE_TRUE(mPresContext, NS_ERROR_OUT_OF_MEMORY);
4318 mPresContext->SetPaginatedScrolling(true);
4319 mPresContext->SetPrintSettings(aPrintSettings);
4320 nsresult rv = mPresContext->Init(mDeviceContext);
4321 NS_ENSURE_SUCCESS(rv, rv);
4322 }
4323 InitInternal(mParentWidget, nullptr, mBounds, true, false);
4324
4325 Show();
4326 return NS_OK;
4327 }
4328
4329 NS_IMETHODIMP
4330 nsDocumentViewer::GetHistoryEntry(nsISHEntry **aHistoryEntry)
4331 {
4332 NS_IF_ADDREF(*aHistoryEntry = mSHEntry);
4333 return NS_OK;
4334 }
4335
4336 NS_IMETHODIMP
4337 nsDocumentViewer::GetIsTabModalPromptAllowed(bool *aAllowed)
4338 {
4339 *aAllowed = !mHidden;
4340 return NS_OK;
4341 }
4342
4343 NS_IMETHODIMP
4344 nsDocumentViewer::GetIsHidden(bool *aHidden)
4345 {
4346 *aHidden = mHidden;
4347 return NS_OK;
4348 }
4349
4350 void
4351 nsDocumentViewer::DestroyPresShell()
4352 {
4353 // Break circular reference (or something)
4354 mPresShell->EndObservingDocument();
4355
4356 nsCOMPtr<nsISelection> selection;
4357 GetDocumentSelection(getter_AddRefs(selection));
4358 nsCOMPtr<nsISelectionPrivate> selPrivate = do_QueryInterface(selection);
4359 if (selPrivate && mSelectionListener)
4360 selPrivate->RemoveSelectionListener(mSelectionListener);
4361
4362 nsAutoScriptBlocker scriptBlocker;
4363 mPresShell->Destroy();
4364 mPresShell = nullptr;
4365 }
4366
4367 void
4368 nsDocumentViewer::DestroyPresContext()
4369 {
4370 mPresContext->Detach();
4371 mPresContext = nullptr;
4372 }
4373
4374 bool
4375 nsDocumentViewer::IsInitializedForPrintPreview()
4376 {
4377 return mInitializedForPrintPreview;
4378 }
4379
4380 void
4381 nsDocumentViewer::InitializeForPrintPreview()
4382 {
4383 mInitializedForPrintPreview = true;
4384 }
4385
4386 void
4387 nsDocumentViewer::SetPrintPreviewPresentation(nsViewManager* aViewManager,
4388 nsPresContext* aPresContext,
4389 nsIPresShell* aPresShell)
4390 {
4391 if (mPresShell) {
4392 DestroyPresShell();
4393 }
4394
4395 mWindow = nullptr;
4396 mViewManager = aViewManager;
4397 mPresContext = aPresContext;
4398 mPresShell = aPresShell;
4399 }
4400
4401 // Fires the "document-shown" event so that interested parties are aware of it.
4402 NS_IMETHODIMP
4403 nsDocumentShownDispatcher::Run()
4404 {
4405 nsCOMPtr<nsIObserverService> observerService =
4406 mozilla::services::GetObserverService();
4407 if (observerService) {
4408 observerService->NotifyObservers(mDocument, "document-shown", nullptr);
4409 }
4410 return NS_OK;
4411 }
4412

mercurial