tor-browser

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

nsFrameLoader.h (19360B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /*
      8 * Class for managing loading of a subframe (creation of the docshell,
      9 * handling of loads in it, recursion-checking).
     10 */
     11 
     12 #ifndef nsFrameLoader_h_
     13 #define nsFrameLoader_h_
     14 
     15 #include <cstdint>
     16 
     17 #include "ErrorList.h"
     18 #include "Units.h"
     19 #include "js/RootingAPI.h"
     20 #include "mozilla/AlreadyAddRefed.h"
     21 #include "mozilla/Assertions.h"
     22 #include "mozilla/Attributes.h"
     23 #include "mozilla/LinkedList.h"
     24 #include "mozilla/RefPtr.h"
     25 #include "mozilla/dom/BrowsingContext.h"
     26 #include "mozilla/dom/MessageManagerCallback.h"
     27 #include "mozilla/dom/Nullable.h"
     28 #include "mozilla/dom/Promise.h"
     29 #include "mozilla/dom/ReferrerPolicyBinding.h"
     30 #include "mozilla/dom/WindowProxyHolder.h"
     31 #include "mozilla/dom/ipc/IdType.h"
     32 #include "mozilla/layers/LayersTypes.h"
     33 #include "nsCOMPtr.h"
     34 #include "nsCycleCollectionParticipant.h"
     35 #include "nsDocShell.h"
     36 #include "nsID.h"
     37 #include "nsIFrame.h"
     38 #include "nsIMutationObserver.h"
     39 #include "nsISupports.h"
     40 #include "nsRect.h"
     41 #include "nsStringFwd.h"
     42 #include "nsStubMutationObserver.h"
     43 #include "nsWrapperCache.h"
     44 
     45 class nsIURI;
     46 class nsSubDocumentFrame;
     47 class AutoResetInShow;
     48 class AutoResetInFrameSwap;
     49 class nsFrameLoaderOwner;
     50 class nsIRemoteTab;
     51 class nsIDocShellTreeItem;
     52 class nsIDocShellTreeOwner;
     53 class nsILoadContext;
     54 class nsIPrintSettings;
     55 class nsIWebBrowserPersistDocumentReceiver;
     56 class nsIWebProgressListener;
     57 class nsIOpenWindowInfo;
     58 
     59 namespace mozilla {
     60 
     61 class OriginAttributes;
     62 
     63 namespace dom {
     64 class ChromeMessageSender;
     65 class ContentParent;
     66 class Document;
     67 class Element;
     68 class InProcessBrowserChildMessageManager;
     69 class MessageSender;
     70 class ProcessMessageManager;
     71 class BrowserParent;
     72 class MutableTabContext;
     73 class BrowserBridgeChild;
     74 class RemoteBrowser;
     75 struct RemotenessOptions;
     76 struct NavigationIsolationOptions;
     77 class SessionStoreChild;
     78 class SessionStoreParent;
     79 
     80 struct LazyLoadFrameResumptionState {
     81  RefPtr<nsIURI> mBaseURI;
     82  ReferrerPolicy mReferrerPolicy = ReferrerPolicy::_empty;
     83 
     84  void Clear() {
     85    mBaseURI = nullptr;
     86    mReferrerPolicy = ReferrerPolicy::_empty;
     87  }
     88 };
     89 
     90 namespace ipc {
     91 class StructuredCloneData;
     92 }  // namespace ipc
     93 
     94 }  // namespace dom
     95 
     96 namespace ipc {
     97 class MessageChannel;
     98 }  // namespace ipc
     99 }  // namespace mozilla
    100 
    101 #if defined(MOZ_WIDGET_GTK)
    102 typedef struct _GtkWidget GtkWidget;
    103 #endif
    104 
    105 // IID for nsFrameLoader, because some places want to QI to it.
    106 #define NS_FRAMELOADER_IID \
    107  {0x297fd0ea, 0x1b4a, 0x4c9a, {0xa4, 0x04, 0xe5, 0x8b, 0xe8, 0x95, 0x10, 0x50}}
    108 
    109 class nsFrameLoader final : public nsStubMutationObserver,
    110                            public mozilla::dom::ipc::MessageManagerCallback,
    111                            public nsWrapperCache,
    112                            public mozilla::LinkedListElement<nsFrameLoader> {
    113  friend class AutoResetInShow;
    114  friend class AutoResetInFrameSwap;
    115  friend class nsFrameLoaderOwner;
    116  using Document = mozilla::dom::Document;
    117  using Element = mozilla::dom::Element;
    118  using BrowserParent = mozilla::dom::BrowserParent;
    119  using BrowserBridgeChild = mozilla::dom::BrowserBridgeChild;
    120  using BrowsingContext = mozilla::dom::BrowsingContext;
    121  using BrowsingContextGroup = mozilla::dom::BrowsingContextGroup;
    122  using Promise = mozilla::dom::Promise;
    123 
    124 public:
    125  // Called by Frame Elements to create a new FrameLoader.
    126  static already_AddRefed<nsFrameLoader> Create(
    127      Element* aOwner, bool aNetworkCreated,
    128      nsIOpenWindowInfo* aOpenWindowInfo = nullptr);
    129 
    130  // Called by nsFrameLoaderOwner::ChangeRemoteness when switching out
    131  // FrameLoaders.
    132  static already_AddRefed<nsFrameLoader> Recreate(
    133      Element* aOwner, BrowsingContext* aContext, BrowsingContextGroup* aGroup,
    134      const mozilla::dom::NavigationIsolationOptions& aRemotenessOptions,
    135      bool aIsRemote, bool aNetworkCreated, bool aPreserveContext);
    136 
    137  NS_INLINE_DECL_STATIC_IID(NS_FRAMELOADER_IID)
    138 
    139  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    140  NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(nsFrameLoader)
    141 
    142  NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
    143  nsresult CheckForRecursiveLoad(nsIURI* aURI);
    144  nsresult ReallyStartLoading();
    145  void StartDestroy(bool aForProcessSwitch);
    146  void DestroyDocShell();
    147  void DestroyComplete();
    148  nsDocShell* GetExistingDocShell() const { return mDocShell; }
    149  mozilla::dom::InProcessBrowserChildMessageManager*
    150  GetBrowserChildMessageManager() const {
    151    return mChildMessageManager;
    152  }
    153  nsresult UpdatePositionAndSize(nsSubDocumentFrame* aFrame);
    154  void PropagateIsUnderHiddenEmbedderElement(
    155      bool aIsUnderHiddenEmbedderElement);
    156 
    157  void UpdateRemoteStyle(mozilla::StyleImageRendering aImageRendering);
    158 
    159  // When creating a nsFrameLoaderOwner which is a static clone, a
    160  // `nsFrameLoader` is not immediately attached to it. Instead, it is added to
    161  // the static clone document's `PendingFrameStaticClones` list.
    162  //
    163  // After the parent document has been fully cloned, a new frameloader will be
    164  // created for the cloned iframe, and `FinishStaticClone` will be called on
    165  // it, which will clone the inner document of the source nsFrameLoader.
    166  nsresult FinishStaticClone(nsFrameLoader* aStaticCloneOf,
    167                             nsIPrintSettings* aPrintSettings,
    168                             bool* aOutHasInProcessPrintCallbacks);
    169 
    170  nsresult DoRemoteStaticClone(nsFrameLoader* aStaticCloneOf,
    171                               nsIPrintSettings* aPrintSettings);
    172 
    173  // WebIDL methods
    174 
    175  nsDocShell* GetDocShell(mozilla::ErrorResult& aRv);
    176 
    177  already_AddRefed<nsIRemoteTab> GetRemoteTab();
    178 
    179  already_AddRefed<nsILoadContext> GetLoadContext();
    180 
    181  mozilla::dom::BrowsingContext* GetBrowsingContext();
    182  mozilla::dom::BrowsingContext* GetExtantBrowsingContext();
    183  mozilla::dom::BrowsingContext* GetMaybePendingBrowsingContext() {
    184    return mPendingBrowsingContext;
    185  }
    186 
    187  /**
    188   * Start loading the frame. This method figures out what to load
    189   * from the owner content in the frame loader.
    190   */
    191  void LoadFrame(bool aOriginalSrc, bool aShouldCheckForRecursion);
    192 
    193  /**
    194   * Loads the specified URI in this frame. Behaves identically to loadFrame,
    195   * except that this method allows specifying the URI to load.
    196   *
    197   * @param aURI The URI to load.
    198   * @param aTriggeringPrincipal The triggering principal for the load. May be
    199   *        null, in which case the node principal of the owner content will be
    200   *        used.
    201   * @param aPolicyContainer The policyContainer to be used for the load. That
    202   * is not the policyContainer to be applied to subresources within the frame,
    203   * but to the iframe load itself. E.g. if the policyContainer's CSP holds
    204   * upgrade-insecure-requests the the frame load is upgraded from http to
    205   * https.
    206   */
    207  nsresult LoadURI(nsIURI* aURI, nsIPrincipal* aTriggeringPrincipal,
    208                   nsIPolicyContainer* aPolicyContainer, bool aOriginalSrc,
    209                   bool aShouldCheckForRecursion);
    210 
    211  /**
    212   * Resume a redirected load within this frame.
    213   *
    214   * @param aPendingSwitchID ID of a process-switching load to be reusmed
    215   *        within this frame.
    216   */
    217  void ResumeLoad(uint64_t aPendingSwitchID);
    218 
    219  /**
    220   * Destroy the frame loader and everything inside it. This will
    221   * clear the weak owner content reference.
    222   */
    223  void Destroy(bool aForProcessSwitch = false);
    224 
    225  void AsyncDestroy() {
    226    mNeedsAsyncDestroy = true;
    227    Destroy();
    228  }
    229 
    230  void RequestUpdatePosition(mozilla::ErrorResult& aRv);
    231 
    232  already_AddRefed<Promise> RequestTabStateFlush(mozilla::ErrorResult& aRv);
    233 
    234  void RequestEpochUpdate(uint32_t aEpoch);
    235 
    236  void RequestSHistoryUpdate();
    237 
    238  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> PrintPreview(
    239      nsIPrintSettings* aPrintSettings, BrowsingContext* aSourceBC,
    240      mozilla::ErrorResult& aRv);
    241 
    242  void ExitPrintPreview();
    243 
    244  void StartPersistence(BrowsingContext* aContext,
    245                        nsIWebBrowserPersistDocumentReceiver* aRecv,
    246                        mozilla::ErrorResult& aRv);
    247 
    248  // WebIDL getters
    249 
    250  already_AddRefed<mozilla::dom::MessageSender> GetMessageManager();
    251 
    252  already_AddRefed<Element> GetOwnerElement();
    253 
    254  uint32_t LazyWidth() const;
    255 
    256  uint32_t LazyHeight() const;
    257 
    258  uint64_t ChildID() const { return mChildID; }
    259 
    260  bool DepthTooGreat() const { return mDepthTooGreat; }
    261 
    262  bool IsDead() const { return mDestroyCalled; }
    263 
    264  bool IsNetworkCreated() const { return mNetworkCreated; }
    265 
    266  nsIContent* GetParentObject() const;
    267 
    268  /**
    269   * MessageManagerCallback methods that we override.
    270   */
    271  virtual bool DoLoadMessageManagerScript(const nsAString& aURL,
    272                                          bool aRunInGlobalScope) override;
    273  virtual nsresult DoSendAsyncMessage(
    274      const nsAString& aMessage,
    275      mozilla::dom::ipc::StructuredCloneData& aData) override;
    276 
    277  /**
    278   * Called from the layout frame associated with this frame loader;
    279   * this notifies us to hook up with the widget and view.
    280   */
    281  MOZ_CAN_RUN_SCRIPT_BOUNDARY bool Show(nsSubDocumentFrame*);
    282 
    283  void MaybeShowFrame();
    284 
    285  /**
    286   * Called when the margin properties of the containing frame are changed.
    287   */
    288  void MarginsChanged();
    289 
    290  /**
    291   * Called from the layout frame associated with this frame loader, when
    292   * the frame is being torn down; this notifies us that out widget and view
    293   * are going away and we should unhook from them.
    294   */
    295  void Hide();
    296 
    297  // Used when content is causing a FrameLoader to be created, and
    298  // needs to try forcing layout to flush in order to get accurate
    299  // dimensions for the content area.
    300  MOZ_CAN_RUN_SCRIPT_BOUNDARY void ForceLayoutIfNecessary();
    301 
    302  // The guts of an nsFrameLoaderOwner::SwapFrameLoader implementation.  A
    303  // frame loader owner needs to call this, and pass in the two references to
    304  // nsRefPtrs for frame loaders that need to be swapped.
    305  nsresult SwapWithOtherLoader(nsFrameLoader* aOther,
    306                               nsFrameLoaderOwner* aThisOwner,
    307                               nsFrameLoaderOwner* aOtherOwner);
    308 
    309  nsresult SwapWithOtherRemoteLoader(nsFrameLoader* aOther,
    310                                     nsFrameLoaderOwner* aThisOwner,
    311                                     nsFrameLoaderOwner* aOtherOwner);
    312 
    313  /**
    314   * Return the primary frame for our owning content, or null if it
    315   * can't be found.
    316   */
    317  nsIFrame* GetPrimaryFrameOfOwningContent() const;
    318 
    319  /**
    320   * Return the document that owns this, or null if we don't have
    321   * an owner.
    322   */
    323  Document* GetOwnerDoc() const;
    324 
    325  /**
    326   * Returns whether this frame is a remote frame.
    327   *
    328   * This is true for either a top-level remote browser in the parent process,
    329   * or a remote subframe in the child process.
    330   */
    331  bool IsRemoteFrame() const {
    332    MOZ_ASSERT_IF(mIsRemoteFrame, !GetDocShell());
    333    return mIsRemoteFrame;
    334  }
    335 
    336  mozilla::dom::RemoteBrowser* GetRemoteBrowser() const {
    337    return mRemoteBrowser;
    338  }
    339 
    340  bool HasRemoteBrowserBeenSized() const { return mRemoteBrowserSized; }
    341 
    342  /**
    343   * Returns the IPDL actor used if this is a top-level remote browser, or null
    344   * otherwise.
    345   */
    346  BrowserParent* GetBrowserParent() const;
    347 
    348  /**
    349   * Returns the IPDL actor used if this is an out-of-process iframe, or null
    350   * otherwise.
    351   */
    352  BrowserBridgeChild* GetBrowserBridgeChild() const;
    353 
    354  /**
    355   * Returns the layers ID that this remote frame is using to render.
    356   *
    357   * This must only be called if this is a remote frame.
    358   */
    359  mozilla::layers::LayersId GetLayersId() const;
    360 
    361  mozilla::dom::ChromeMessageSender* GetFrameMessageManager() {
    362    return mMessageManager;
    363  }
    364 
    365  mozilla::dom::Element* GetOwnerContent() { return mOwnerContent; }
    366 
    367  /**
    368   * Stashes a list of detached pres shells on the frame loader. We do this when
    369   * we're destroying the nsSubDocumentFrame. If the nsSubdocumentFrame is being
    370   * reframed we'll restore the detached shells when they're recreated,
    371   * otherwise we'll discard the old presentation and clear these.
    372   */
    373  using WeakPresShellArray = nsTArray<nsWeakPtr>;
    374  void SetDetachedSubdocs(WeakPresShellArray&&);
    375  WeakPresShellArray TakeDetachedSubdocs();
    376  const WeakPresShellArray& GetDetachedSubdocs() const {
    377    return mDetachedSubdocs;
    378  }
    379 
    380  /**
    381   * Applies a new set of sandbox flags. These are merged with the sandbox
    382   * flags from our owning content's owning document with a logical OR, this
    383   * ensures that we can only add restrictions and never remove them.
    384   */
    385  void ApplySandboxFlags(uint32_t sandboxFlags);
    386 
    387  void GetURL(nsString& aURL, nsIPrincipal** aTriggeringPrincipal,
    388              nsIPolicyContainer** aPolicyContainer);
    389 
    390  // Properly retrieves documentSize of any subdocument type.
    391  nsresult GetWindowDimensions(mozilla::LayoutDeviceIntRect& aRect);
    392 
    393  virtual mozilla::dom::ProcessMessageManager* GetProcessMessageManager()
    394      const override;
    395 
    396  // public because a callback needs these.
    397  RefPtr<mozilla::dom::ChromeMessageSender> mMessageManager;
    398  RefPtr<mozilla::dom::InProcessBrowserChildMessageManager>
    399      mChildMessageManager;
    400 
    401  virtual JSObject* WrapObject(JSContext* cx,
    402                               JS::Handle<JSObject*> aGivenProto) override;
    403 
    404  void SetWillChangeProcess();
    405 
    406  // Configure which remote process should be used to host the remote browser
    407  // created in `TryRemoteBrowser`. This method _must_ be called before
    408  // `TryRemoteBrowser`, and a script blocker must be on the stack.
    409  //
    410  // |aContentParent|, if set, must have the remote type |aRemoteType|.
    411  void ConfigRemoteProcess(const nsACString& aRemoteType,
    412                           mozilla::dom::ContentParent* aContentParent);
    413 
    414  // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
    415  MOZ_CAN_RUN_SCRIPT_BOUNDARY void MaybeNotifyCrashed(
    416      mozilla::dom::BrowsingContext* aBrowsingContext,
    417      mozilla::dom::ContentParentId aChildID,
    418      mozilla::ipc::MessageChannel* aChannel);
    419 
    420  void FireErrorEvent();
    421 
    422  mozilla::dom::SessionStoreChild* GetSessionStoreChild() {
    423    return mSessionStoreChild;
    424  }
    425 
    426  mozilla::dom::SessionStoreParent* GetSessionStoreParent();
    427 
    428 private:
    429  nsFrameLoader(mozilla::dom::Element* aOwner,
    430                mozilla::dom::BrowsingContext* aBrowsingContext, bool aIsRemote,
    431                bool aNetworkCreated);
    432  ~nsFrameLoader();
    433 
    434  void SetOwnerContent(mozilla::dom::Element* aContent);
    435 
    436  /**
    437   * Get our owning element's app manifest URL, or return the empty string if
    438   * our owning element doesn't have an app manifest URL.
    439   */
    440  void GetOwnerAppManifestURL(nsAString& aOut);
    441 
    442  /**
    443   * If we are an IPC frame, set mRemoteFrame. Otherwise, create and
    444   * initialize mDocShell.
    445   */
    446  nsresult MaybeCreateDocShell();
    447  nsresult EnsureMessageManager();
    448  nsresult ReallyLoadFrameScripts();
    449  nsDocShell* GetDocShell() const { return mDocShell; }
    450 
    451  void AssertSafeToInit();
    452 
    453  /**
    454   * Checks whether a load of the given URI should be allowed, and returns an
    455   * error result if it should not.
    456   *
    457   * @param aURI The URI to check.
    458   * @param aTriggeringPrincipal The triggering principal for the load. May be
    459   *        null, in which case the node principal of the owner content is used.
    460   */
    461  nsresult CheckURILoad(nsIURI* aURI, nsIPrincipal* aTriggeringPrincipal);
    462  nsresult ReallyStartLoadingInternal();
    463 
    464  // Returns true if we have a remote browser or else attempts to create a
    465  // remote browser and returns true if successful.
    466  bool EnsureRemoteBrowser();
    467 
    468  // Return true if remote browser created; nothing else to do
    469  bool TryRemoteBrowser();
    470  bool TryRemoteBrowserInternal();
    471 
    472  // Tell the remote browser that it's now "virtually visible"
    473  bool ShowRemoteFrame(nsSubDocumentFrame* aFrame);
    474 
    475  void AddTreeItemToTreeOwner(nsIDocShellTreeItem* aItem,
    476                              nsIDocShellTreeOwner* aOwner);
    477 
    478  nsresult GetNewTabContext(mozilla::dom::MutableTabContext* aTabContext,
    479                            nsIURI* aURI = nullptr);
    480 
    481  enum BrowserParentChange { eBrowserParentRemoved, eBrowserParentChanged };
    482  void MaybeUpdatePrimaryBrowserParent(BrowserParentChange aChange);
    483 
    484  nsresult PopulateOriginContextIdsFromAttributes(
    485      mozilla::OriginAttributes& aAttr);
    486 
    487  bool EnsureBrowsingContextAttached();
    488 
    489  // Invoke the callback from nsOpenWindowInfo to indicate that a
    490  // browsing context for a newly opened tab/window is ready.
    491  void InvokeBrowsingContextReadyCallback();
    492 
    493  void RequestFinalTabStateFlush();
    494 
    495  const mozilla::dom::LazyLoadFrameResumptionState&
    496  GetLazyLoadFrameResumptionState();
    497 
    498  RefPtr<mozilla::dom::BrowsingContext> mPendingBrowsingContext;
    499  nsCOMPtr<nsIURI> mURIToLoad;
    500  nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
    501  nsCOMPtr<nsIPolicyContainer> mPolicyContainer;
    502  nsCOMPtr<nsIOpenWindowInfo> mOpenWindowInfo;
    503  mozilla::dom::Element* mOwnerContent;  // WEAK
    504 
    505  // After the frameloader has been removed from the DOM but before all of the
    506  // messages from the frame have been received, we keep a strong reference to
    507  // our <browser> element.
    508  RefPtr<mozilla::dom::Element> mOwnerContentStrong;
    509 
    510  // Stores the detached pres shells of subdocuments.
    511  // Used to restore the presentation after reframing.
    512  WeakPresShellArray mDetachedSubdocs;
    513 
    514  // When performing a process switch, this value is used rather than mURIToLoad
    515  // to identify the process-switching load which should be resumed in the
    516  // target process.
    517  uint64_t mPendingSwitchID;
    518 
    519  uint64_t mChildID;
    520  RefPtr<mozilla::dom::RemoteBrowser> mRemoteBrowser;
    521  RefPtr<nsDocShell> mDocShell;
    522 
    523  // Holds the last known size of the frame.
    524  mozilla::LayoutDeviceIntSize mLazySize;
    525 
    526  // Actor for collecting session store data from content children. This will be
    527  // cleared and set to null eagerly when taking down the frameloader to break
    528  // refcounted cycles early.
    529  RefPtr<mozilla::dom::SessionStoreChild> mSessionStoreChild;
    530 
    531  nsCString mRemoteType;
    532 
    533  bool mInitialized : 1;
    534  bool mDepthTooGreat : 1;
    535  bool mIsTopLevelContent : 1;
    536  bool mDestroyCalled : 1;
    537  bool mNeedsAsyncDestroy : 1;
    538  bool mInSwap : 1;
    539  bool mInShow : 1;
    540  bool mHideCalled : 1;
    541  // True when the object is created for an element which the parser has
    542  // created using NS_FROM_PARSER_NETWORK flag. If the element is modified,
    543  // it may lose the flag.
    544  bool mNetworkCreated : 1;
    545 
    546  // True if a pending load corresponds to the original src (or srcdoc)
    547  // attribute of the frame element.
    548  bool mLoadingOriginalSrc : 1;
    549 
    550  // True if a pending load corresponds to the src attribute being changed.
    551  bool mShouldCheckForRecursion : 1;
    552 
    553  bool mRemoteBrowserShown : 1;
    554  bool mRemoteBrowserSized : 1;
    555  bool mIsRemoteFrame : 1;
    556  // If true, the FrameLoader will be re-created with the same BrowsingContext,
    557  // but for a different process, after it is destroyed.
    558  bool mWillChangeProcess : 1;
    559  bool mObservingOwnerContent : 1;
    560  // Whether we had a (possibly dead now) mDetachedSubdocFrame.
    561  bool mHadDetachedFrame : 1;
    562 
    563  // When an out-of-process nsFrameLoader crashes, an event is fired on the
    564  // frame. To ensure this is only fired once, this bit is checked.
    565  bool mTabProcessCrashFired : 1;
    566 };
    567 
    568 inline nsISupports* ToSupports(nsFrameLoader* aFrameLoader) {
    569  return aFrameLoader;
    570 }
    571 
    572 #endif