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  * A base class implementing nsIObjectLoadingContent for use by
9  * various content nodes that want to provide plugin/document/image
10  * loading functionality (eg <embed>, <object>, etc).
11  */
12 
13 #ifndef NSOBJECTLOADINGCONTENT_H_
14 #define NSOBJECTLOADINGCONTENT_H_
15 
16 #include "mozilla/Attributes.h"
17 #include "mozilla/Maybe.h"
18 #include "mozilla/dom/BindingDeclarations.h"
19 #include "nsIFrame.h"  // for WeakFrame only
20 #include "nsImageLoadingContent.h"
21 #include "nsIStreamListener.h"
22 #include "nsIChannelEventSink.h"
23 #include "nsIObjectLoadingContent.h"
24 #include "nsIRunnable.h"
25 #include "nsFrameLoaderOwner.h"
26 
27 class nsAsyncInstantiateEvent;
28 class nsStopPluginRunnable;
29 class AutoSetInstantiatingToFalse;
30 class nsIPrincipal;
31 class nsFrameLoader;
32 
33 namespace mozilla {
34 namespace dom {
35 struct BindContext;
36 template <typename T>
37 class Sequence;
38 struct MozPluginParameter;
39 class HTMLIFrameElement;
40 template <typename T>
41 struct Nullable;
42 class WindowProxyHolder;
43 class XULFrameElement;
44 }  // namespace dom
45 }  // namespace mozilla
46 
47 class nsObjectLoadingContent : public nsImageLoadingContent,
48                                public nsIStreamListener,
49                                public nsFrameLoaderOwner,
50                                public nsIObjectLoadingContent,
51                                public nsIChannelEventSink {
52   friend class AutoSetInstantiatingToFalse;
53   friend class AutoSetLoadingToFalse;
54   friend class CheckPluginStopEvent;
55   friend class nsStopPluginRunnable;
56   friend class nsAsyncInstantiateEvent;
57 
58  public:
59   // This enum's values must be the same as the constants on
60   // nsIObjectLoadingContent
61   enum ObjectType {
62     // Loading, type not yet known. We may be waiting for a channel to open.
63     eType_Loading = TYPE_LOADING,
64     // Content is a *non-svg* image
65     eType_Image = TYPE_IMAGE,
66     // Content is a "special" plugin.  Plugins are removed but these MIME
67     // types display an transparent region in their place.
68     // (Special plugins that have an HTML fallback are eType_Null)
69     eType_Fallback = TYPE_FALLBACK,
70     // Content is a fake plugin, which loads as a document but behaves as a
71     // plugin (see nsPluginHost::CreateFakePlugin).  Currently only used for
72     // pdf.js.
73     eType_FakePlugin = TYPE_FAKE_PLUGIN,
74     // Content is a subdocument, possibly SVG
75     eType_Document = TYPE_DOCUMENT,
76     // Content is unknown and should be represented by an empty element,
77     // unless an HTML fallback is available.
78     eType_Null = TYPE_NULL
79   };
80 
81   nsObjectLoadingContent();
82   virtual ~nsObjectLoadingContent();
83 
84   NS_DECL_NSIREQUESTOBSERVER
85   NS_DECL_NSISTREAMLISTENER
86   NS_DECL_NSIOBJECTLOADINGCONTENT
87   NS_DECL_NSICHANNELEVENTSINK
88 
89   /**
90    * Object state. This is a bitmask of NS_EVENT_STATEs epresenting the
91    * current state of the object.
92    */
93   mozilla::EventStates ObjectState() const;
94 
Type()95   ObjectType Type() const { return mType; }
96 
SetIsNetworkCreated(bool aNetworkCreated)97   void SetIsNetworkCreated(bool aNetworkCreated) {
98     mNetworkCreated = aNetworkCreated;
99   }
100 
101   /**
102    * When the object is loaded, the attributes and all nested <param>
103    * elements are cached as name:value string pairs to be passed as
104    * parameters when instantiating the plugin.
105    *
106    * Note: these cached values can be overriden for different quirk cases.
107    */
108   // Returns the cached attributes array.
109   void GetPluginAttributes(
110       nsTArray<mozilla::dom::MozPluginParameter>& aAttributes);
111 
112   // Returns the cached <param> array.
113   void GetPluginParameters(
114       nsTArray<mozilla::dom::MozPluginParameter>& aParameters);
115 
116   /**
117    * Immediately instantiate a plugin instance. This is a no-op if mType !=
118    * eType_Plugin or a plugin is already running.
119    *
120    * aIsLoading indicates that we are in the loading code, and we can bypass
121    * the mIsLoading check.
122    */
123   nsresult InstantiatePluginInstance(bool aIsLoading = false);
124 
125   /**
126    * Notify this class the document state has changed
127    * Called by Document so we may suspend plugins in inactive documents)
128    */
129   void NotifyOwnerDocumentActivityChanged();
130 
131   // Helper for WebIDL NeedResolve
132   bool DoResolve(
133       JSContext* aCx, JS::Handle<JSObject*> aObject, JS::Handle<jsid> aId,
134       JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> aDesc);
135   // The return value is whether DoResolve might end up resolving the given
136   // id.  If in doubt, return true.
137   static bool MayResolve(jsid aId);
138 
139   // Helper for WebIDL enumeration
140   void GetOwnPropertyNames(JSContext* aCx,
141                            JS::MutableHandleVector<jsid> /* unused */,
142                            bool /* unused */, mozilla::ErrorResult& aRv);
143 
144   // WebIDL API
145   mozilla::dom::Document* GetContentDocument(nsIPrincipal& aSubjectPrincipal);
GetActualType(nsAString & aType)146   void GetActualType(nsAString& aType) const {
147     CopyUTF8toUTF16(mContentType, aType);
148   }
DisplayedType()149   uint32_t DisplayedType() const { return mType; }
GetContentTypeForMIMEType(const nsAString & aMIMEType)150   uint32_t GetContentTypeForMIMEType(const nsAString& aMIMEType) {
151     return GetTypeOfContent(NS_ConvertUTF16toUTF8(aMIMEType), false);
152   }
Reload(bool aClearActivation,mozilla::ErrorResult & aRv)153   void Reload(bool aClearActivation, mozilla::ErrorResult& aRv) {
154     aRv = Reload(aClearActivation);
155   }
GetSrcURI()156   nsIURI* GetSrcURI() const { return mURI; }
157 
158   // FIXME rename this
SkipFakePlugins(mozilla::ErrorResult & aRv)159   void SkipFakePlugins(mozilla::ErrorResult& aRv) { aRv = SkipFakePlugins(); }
SwapFrameLoaders(mozilla::dom::HTMLIFrameElement & aOtherLoaderOwner,mozilla::ErrorResult & aRv)160   void SwapFrameLoaders(mozilla::dom::HTMLIFrameElement& aOtherLoaderOwner,
161                         mozilla::ErrorResult& aRv) {
162     aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
163   }
SwapFrameLoaders(mozilla::dom::XULFrameElement & aOtherLoaderOwner,mozilla::ErrorResult & aRv)164   void SwapFrameLoaders(mozilla::dom::XULFrameElement& aOtherLoaderOwner,
165                         mozilla::ErrorResult& aRv) {
166     aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
167   }
168 
169   uint32_t GetRunID(mozilla::dom::SystemCallerGuarantee,
170                     mozilla::ErrorResult& aRv);
171 
IsRewrittenYoutubeEmbed()172   bool IsRewrittenYoutubeEmbed() const { return mRewrittenYoutubeEmbed; }
173 
174   void PresetOpenerWindow(const mozilla::dom::Nullable<
175                               mozilla::dom::WindowProxyHolder>& aOpenerWindow,
176                           mozilla::ErrorResult& aRv);
177 
GetSubdocumentIntrinsicSize()178   const mozilla::Maybe<mozilla::IntrinsicSize>& GetSubdocumentIntrinsicSize()
179       const {
180     return mSubdocumentIntrinsicSize;
181   }
182 
GetSubdocumentIntrinsicRatio()183   const mozilla::Maybe<mozilla::AspectRatio>& GetSubdocumentIntrinsicRatio()
184       const {
185     return mSubdocumentIntrinsicRatio;
186   }
187 
188   void SubdocumentIntrinsicSizeOrRatioChanged(
189       const mozilla::Maybe<mozilla::IntrinsicSize>& aIntrinsicSize,
190       const mozilla::Maybe<mozilla::AspectRatio>& aIntrinsicRatio);
191 
192  protected:
193   /**
194    * Begins loading the object when called
195    *
196    * Attributes of |this| QI'd to nsIContent will be inspected, depending on
197    * the node type. This function currently assumes it is a <object> or
198    * <embed> tag.
199    *
200    * The instantiated plugin depends on:
201    * - The URI (<embed src>, <object data>)
202    * - The type 'hint' (type attribute)
203    * - The mime type returned by opening the URI
204    * - Enabled plugins claiming the ultimate mime type
205    * - The capabilities returned by GetCapabilities
206    * - The classid attribute, if eFallbackIfClassIDPresent is among the
207    * capabilities
208    *
209    * If eAllowPluginSkipChannel is true, we may skip opening the URI if our
210    * type hint points to a valid plugin, deferring that responsibility to the
211    * plugin.
212    * Similarly, if no URI is provided, but a type hint for a valid plugin is
213    * present, that plugin will be instantiated
214    *
215    * Otherwise a request to that URI is made and the type sent by the server
216    * is used to find a suitable handler, EXCEPT when:
217    *  - The type hint refers to a *supported* plugin, in which case that
218    *    plugin will be instantiated regardless of the server provided type
219    *  - The server returns a binary-stream type, and our type hint refers to
220    *    a valid non-document type, we will use the type hint
221    *
222    * @param aNotify    If we should send notifications. If false, content
223    *                   loading may be deferred while appropriate frames are
224    *                   created
225    * @param aForceLoad If we should reload this content (and re-attempt the
226    *                   channel open) even if our parameters did not change
227    */
228   nsresult LoadObject(bool aNotify, bool aForceLoad = false);
229 
230   enum Capabilities {
231     eSupportImages = 1u << 0,     // Images are supported (imgILoader)
232     eSupportPlugins = 1u << 1,    // Plugins are supported (nsIPluginHost)
233     eSupportDocuments = 1u << 2,  // Documents are supported
234                                   // (DocumentLoaderFactory)
235                                   // This flag always includes SVG
236 
237     // Node supports class ID as an attribute, and should fallback if it is
238     // present, as class IDs are not supported.
239     eFallbackIfClassIDPresent = 1u << 3,
240 
241     // If possible to get a *plugin* type from the type attribute *or* file
242     // extension, we can use that type and begin loading the plugin before
243     // opening a channel.
244     // A side effect of this is if the channel fails, the plugin is still
245     // running.
246     eAllowPluginSkipChannel = 1u << 4
247   };
248 
249   /**
250    * Returns the list of capabilities this content node supports. This is a
251    * bitmask consisting of flags from the Capabilities enum.
252    *
253    * The default implementation supports all types but not
254    * eSupportClassID or eAllowPluginSkipChannel
255    */
256   virtual uint32_t GetCapabilities() const;
257 
258   /**
259    * Destroys all loaded documents/plugins and releases references
260    */
261   void Destroy();
262 
263   // Subclasses should call cycle collection methods from the respective
264   // traverse / unlink.
265   static void Traverse(nsObjectLoadingContent* tmp,
266                        nsCycleCollectionTraversalCallback& cb);
267   static void Unlink(nsObjectLoadingContent* tmp);
268 
269   void CreateStaticClone(nsObjectLoadingContent* aDest) const;
270 
BindToTree(mozilla::dom::BindContext & aCxt,nsINode & aParent)271   nsresult BindToTree(mozilla::dom::BindContext& aCxt, nsINode& aParent) {
272     nsImageLoadingContent::BindToTree(aCxt, aParent);
273     return NS_OK;
274   }
275   void UnbindFromTree(bool aNullParent = true);
276 
277   /**
278    * Return the content policy type used for loading the element.
279    */
280   virtual nsContentPolicyType GetContentPolicyType() const = 0;
281 
282   /**
283    * Decides whether we should load <embed>/<object> node content.
284    *
285    * If this is an <embed> or <object> node there are cases in which we should
286    * not try to load the content:
287    *
288    * - If the node is the child of a media element
289    * - If the node is the child of an <object> node that already has
290    *   content being loaded.
291    *
292    * In these cases, this function will return false, which will cause
293    * us to skip calling LoadObject.
294    */
295   bool BlockEmbedOrObjectContentLoading();
296 
297  private:
298   // Object parameter changes returned by UpdateObjectParameters
299   enum ParameterUpdateFlags {
300     eParamNoChange = 0,
301     // Parameters that potentially affect the channel changed
302     // - mOriginalURI, mOriginalContentType
303     eParamChannelChanged = 1u << 0,
304     // Parameters that affect displayed content changed
305     // - mURI, mContentType, mType, mBaseURI
306     eParamStateChanged = 1u << 1,
307     // The effective content type changed, independant of object type. This
308     // can happen when changing from Loading -> Final type, but doesn't
309     // necessarily happen when changing between object types. E.g., if a PDF
310     // handler was installed between the last load of this object and now, we
311     // might change from eType_Document -> eType_Plugin without changing
312     // ContentType
313     eParamContentTypeChanged = 1u << 2
314   };
315 
316   /**
317    * Getter for child <param> elements that are not nested in another plugin
318    * dom element.
319    * This is an internal helper function and should not be used directly for
320    * passing parameters to the plugin instance.
321    *
322    * See GetPluginParameters and GetPluginAttributes, which also handle
323    * quirk-overrides.
324    *
325    * @param aParameters     The array containing pairs of name/value strings
326    *                        from nested <param> objects.
327    */
328   void GetNestedParams(nsTArray<mozilla::dom::MozPluginParameter>& aParameters);
329 
330   [[nodiscard]] nsresult BuildParametersArray();
331 
332   /**
333    * Configure fallback for deprecated plugin and broken elements.
334    */
335   void ConfigureFallback();
336 
337   /**
338    * Internal version of LoadObject that should only be used by this class
339    * aLoadingChannel is passed by the LoadObject call from OnStartRequest,
340    * primarily for sanity-preservation
341    */
342   nsresult LoadObject(bool aNotify, bool aForceLoad,
343                       nsIRequest* aLoadingChannel);
344 
345   /**
346    * Inspects the object and sets the following member variables:
347    * - mOriginalContentType : This is the type attribute on the element
348    * - mOriginalURI         : The src or data attribute on the element
349    * - mURI                 : The final URI, considering mChannel if
350    *                          mChannelLoaded is set
351    * - mContentType         : The final content type, considering mChannel if
352    *                          mChannelLoaded is set
353    * - mBaseURI             : The object's base URI, which may be set by the
354    *                          object
355    * - mType                : The type the object is determined to be based
356    *                          on the above
357    *
358    * NOTE The class assumes that mType is the currently loaded type at various
359    *      points, so the caller of this function must take the appropriate
360    *      actions to ensure this
361    *
362    * NOTE This function does not perform security checks, only determining the
363    *      requested type and parameters of the object.
364    *
365    * @return Returns a bitmask of ParameterUpdateFlags values
366    */
367   ParameterUpdateFlags UpdateObjectParameters();
368 
369   /**
370    * Queue a CheckPluginStopEvent and track it in mPendingCheckPluginStopEvent
371    */
372   void QueueCheckPluginStopEvent();
373 
374  public:
375   bool IsAboutBlankLoadOntoInitialAboutBlank(nsIURI* aURI,
376                                              bool aInheritPrincipal,
377                                              nsIPrincipal* aPrincipalToInherit);
378 
379  private:
380   /**
381    * Opens the channel pointed to by mURI into mChannel.
382    */
383   nsresult OpenChannel();
384 
385   /**
386    * Closes and releases references to mChannel and, if opened, mFinalListener
387    */
388   nsresult CloseChannel();
389 
390   /**
391    * If this object should be tested against blocking list.
392    */
393   bool ShouldBlockContent();
394 
395   /**
396    * This method tells the final answer on whether this object's fallback
397    * content should be used instead of the original plugin content.
398    *
399    * @param aIsPluginClickToPlay Whether this object instance is CTP.
400    */
401   bool PreferFallback(bool aIsPluginClickToPlay);
402 
403   /**
404    * Helper to check if our current URI passes policy
405    *
406    * @param aContentPolicy [out] The result of the content policy decision
407    *
408    * @return true if call succeeded and NS_CP_ACCEPTED(*aContentPolicy)
409    */
410   bool CheckLoadPolicy(int16_t* aContentPolicy);
411 
412   /**
413    * Helper to check if the object passes process policy. Assumes we have a
414    * final determined type.
415    *
416    * @param aContentPolicy [out] The result of the content policy decision
417    *
418    * @return true if call succeeded and NS_CP_ACCEPTED(*aContentPolicy)
419    */
420   bool CheckProcessPolicy(int16_t* aContentPolicy);
421 
422   void SetupFrameLoader(int32_t aJSPluginId);
423 
424   /**
425    * Helper to spawn mFrameLoader and return a pointer to its docshell
426    *
427    * @param aURI URI we intend to load for the recursive load check (does not
428    *             actually load anything)
429    */
430   already_AddRefed<nsIDocShell> SetupDocShell(nsIURI* aRecursionCheckURI);
431 
432   /**
433    * Unloads all content and resets the object to a completely unloaded state
434    *
435    * NOTE Calls StopPluginInstance() and may spin the event loop
436    *
437    * @param aResetState Reset the object type to 'loading' and destroy channel
438    *                    as well
439    */
440   void UnloadObject(bool aResetState = true);
441 
442   /**
443    * Notifies document observes about a new type/state of this object.
444    * Triggers frame construction as needed. mType must be set correctly when
445    * this method is called. This method is cheap if the type and state didn't
446    * actually change.
447    *
448    * @param aNotify if false, only need to update the state of our element.
449    */
450   void NotifyStateChanged(ObjectType aOldType, mozilla::EventStates aOldState,
451                           bool aNotify);
452 
453   /**
454    * Returns a ObjectType value corresponding to the type of content we would
455    * support the given MIME type as, taking capabilities and plugin state
456    * into account
457    *
458    * @param aNoFakePlugin Don't select a fake plugin handler as a valid type,
459    *                      as when SkipFakePlugins() is called.
460    * @return The ObjectType enum value that we would attempt to load
461    *
462    * NOTE this does not consider whether the content would be suppressed by
463    *      click-to-play or other content policy checks
464    */
465   ObjectType GetTypeOfContent(const nsCString& aMIMEType, bool aNoFakePlugin);
466 
467   /**
468    * Used for identifying whether we can rewrite a youtube flash embed to
469    * possibly use HTML5 instead.
470    *
471    * Returns true if plugin.rewrite_youtube_embeds pref is true and the
472    * element this nsObjectLoadingContent instance represents:
473    *
474    * - is an embed or object node
475    * - has a URL pointing at the youtube.com domain, using "/v/" style video
476    *   path reference.
477    *
478    * Having the enablejsapi flag means the document that contains the element
479    * could possibly be manipulating the youtube video elsewhere on the page
480    * via javascript. In the context of embed elements, this usage has been
481    * deprecated by youtube, so we can just rewrite as normal.
482    *
483    * If we can rewrite the URL, we change the "/v/" to "/embed/", and change
484    * our type to eType_Document so that we render similarly to an iframe
485    * embed.
486    */
487   void MaybeRewriteYoutubeEmbed(nsIURI* aURI, nsIURI* aBaseURI,
488                                 nsIURI** aRewrittenURI);
489 
490   // Utility for firing an error event, if we're an <object>.
491   void MaybeFireErrorEvent();
492 
493   // The final listener for mChannel (uriloader, pluginstreamlistener, etc.)
494   nsCOMPtr<nsIStreamListener> mFinalListener;
495 
496   // Track if we have a pending AsyncInstantiateEvent
497   nsCOMPtr<nsIRunnable> mPendingInstantiateEvent;
498 
499   // Tracks if we have a pending CheckPluginStopEvent
500   nsCOMPtr<nsIRunnable> mPendingCheckPluginStopEvent;
501 
502   // The content type of our current load target, updated by
503   // UpdateObjectParameters(). Takes the channel's type into account once
504   // opened.
505   //
506   // May change if a channel is opened, does not imply a loaded state
507   nsCString mContentType;
508 
509   // The content type 'hint' provided by the element's type attribute. May
510   // or may not be used as a final type
511   nsCString mOriginalContentType;
512 
513   // The channel that's currently being loaded. If set, but mChannelLoaded is
514   // false, has not yet reached OnStartRequest
515   nsCOMPtr<nsIChannel> mChannel;
516 
517   // The URI of the current content.
518   // May change as we open channels and encounter redirects - does not imply
519   // a loaded type
520   nsCOMPtr<nsIURI> mURI;
521 
522   // The original URI obtained from inspecting the element. May differ from
523   // mURI due to redirects
524   nsCOMPtr<nsIURI> mOriginalURI;
525 
526   // The baseURI used for constructing mURI.
527   nsCOMPtr<nsIURI> mBaseURI;
528 
529   // Type of the currently-loaded content.
530   ObjectType mType : 8;
531 
532   uint32_t mRunID;
533   bool mHasRunID : 1;
534 
535   // If true, we have opened a channel as the listener and it has reached
536   // OnStartRequest. Does not get set for channels that are passed directly to
537   // the plugin listener.
538   bool mChannelLoaded : 1;
539 
540   // Whether we are about to call instantiate on our frame. If we aren't,
541   // SetFrame needs to asynchronously call Instantiate.
542   bool mInstantiating : 1;
543 
544   // True when the object is created for an element which the parser has
545   // created using NS_FROM_PARSER_NETWORK flag. If the element is modified,
546   // it may lose the flag.
547   bool mNetworkCreated : 1;
548 
549   // Whether content blocking is enabled or not for this object.
550   bool mContentBlockingEnabled : 1;
551 
552   // If we should not use fake plugins until the next type change
553   bool mSkipFakePlugins : 1;
554 
555   // Protects DoStopPlugin from reentry (bug 724781).
556   bool mIsStopping : 1;
557 
558   // Protects LoadObject from re-entry
559   bool mIsLoading : 1;
560 
561   // For plugin stand-in types (click-to-play) tracks whether content js has
562   // tried to access the plugin script object.
563   bool mScriptRequested : 1;
564 
565   // True if object represents an object/embed tag pointing to a flash embed
566   // for a youtube video. When possible (see IsRewritableYoutubeEmbed function
567   // comments for details), we change these to try to load HTML5 versions of
568   // videos.
569   bool mRewrittenYoutubeEmbed : 1;
570 
571   nsTArray<mozilla::dom::MozPluginParameter> mCachedAttributes;
572   nsTArray<mozilla::dom::MozPluginParameter> mCachedParameters;
573 
574   // The intrinsic size and aspect ratio from a child SVG document that
575   // we should use.  These are only set when we are an <object> or <embed>
576   // and the inner document is SVG.
577   //
578   // We store these here rather than on nsSubDocumentFrame since we are
579   // sometimes notified of our child's intrinsics before we've constructed
580   // our own frame.
581   mozilla::Maybe<mozilla::IntrinsicSize> mSubdocumentIntrinsicSize;
582   mozilla::Maybe<mozilla::AspectRatio> mSubdocumentIntrinsicRatio;
583 };
584 
585 #endif
586