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 #ifndef nsJSEnvironment_h
7 #define nsJSEnvironment_h
8 
9 #include "nsIScriptContext.h"
10 #include "nsIScriptGlobalObject.h"
11 #include "nsCOMPtr.h"
12 #include "prtime.h"
13 #include "nsCycleCollectionParticipant.h"
14 #include "nsIArray.h"
15 #include "mozilla/Attributes.h"
16 #include "mozilla/TimeStamp.h"
17 #include "nsThreadUtils.h"
18 #include "xpcpublic.h"
19 
20 class nsICycleCollectorListener;
21 class nsIDocShell;
22 
23 namespace mozilla {
24 
25 template <class>
26 class Maybe;
27 struct CycleCollectorResults;
28 
29 static const uint32_t kMajorForgetSkippableCalls = 5;
30 
31 }  // namespace mozilla
32 
33 class nsJSContext : public nsIScriptContext {
34  public:
35   nsJSContext(bool aGCOnDestruction, nsIScriptGlobalObject* aGlobalObject);
36 
37   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
38   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSContext,
39                                                          nsIScriptContext)
40 
41   virtual nsIScriptGlobalObject* GetGlobalObject() override;
GetGlobalObjectRef()42   inline nsIScriptGlobalObject* GetGlobalObjectRef() {
43     return mGlobalObjectRef;
44   }
45 
46   virtual nsresult SetProperty(JS::Handle<JSObject*> aTarget,
47                                const char* aPropName,
48                                nsISupports* aVal) override;
49 
50   virtual bool GetProcessingScriptTag() override;
51   virtual void SetProcessingScriptTag(bool aResult) override;
52 
53   virtual nsresult InitClasses(JS::Handle<JSObject*> aGlobalObj) override;
54 
55   virtual void SetWindowProxy(JS::Handle<JSObject*> aWindowProxy) override;
56   virtual JSObject* GetWindowProxy() override;
57 
58   enum IsShrinking { ShrinkingGC, NonShrinkingGC };
59 
60   enum IsIncremental { IncrementalGC, NonIncrementalGC };
61 
62   // Setup all the statics etc - safe to call multiple times after Startup().
63   static void EnsureStatics();
64 
65   static void SetLowMemoryState(bool aState);
66 
67   static void GarbageCollectNow(JS::GCReason reason,
68                                 IsIncremental aIncremental = NonIncrementalGC,
69                                 IsShrinking aShrinking = NonShrinkingGC,
70                                 int64_t aSliceMillis = 0);
71 
72   static void CycleCollectNow(nsICycleCollectorListener* aListener = nullptr);
73 
74   // Finish up any in-progress incremental GC.
75   static void PrepareForCycleCollectionSlice(mozilla::TimeStamp aDeadline);
76 
77   // Run a cycle collector slice, using a heuristic to decide how long to run
78   // it.
79   static void RunCycleCollectorSlice(mozilla::TimeStamp aDeadline);
80 
81   // Run a cycle collector slice, using the given work budget.
82   static void RunCycleCollectorWorkSlice(int64_t aWorkBudget);
83 
84   static void BeginCycleCollectionCallback();
85   static void EndCycleCollectionCallback(
86       mozilla::CycleCollectorResults& aResults);
87 
88   // Return the longest CC slice time since ClearMaxCCSliceTime() was last
89   // called.
90   static uint32_t GetMaxCCSliceTimeSinceClear();
91   static void ClearMaxCCSliceTime();
92 
93   // If there is some pending CC or GC timer/runner, this will run it.
94   static void RunNextCollectorTimer(
95       JS::GCReason aReason,
96       mozilla::TimeStamp aDeadline = mozilla::TimeStamp());
97   // If user has been idle and aDocShell is for an iframe being loaded in an
98   // already loaded top level docshell, this will run a CC or GC
99   // timer/runner if there is such pending.
100   static void MaybeRunNextCollectorSlice(nsIDocShell* aDocShell,
101                                          JS::GCReason aReason);
102 
103   // The GC should probably run soon, in the zone of object aObj (if given).
104   static void PokeGC(JS::GCReason aReason, JSObject* aObj, uint32_t aDelay = 0);
105 
106   // Immediately perform a non-incremental shrinking GC and CC.
107   static void DoLowMemoryGC();
108 
109   // Perform a non-incremental shrinking GC and CC according to
110   // IdleScheduler.
111   static void LowMemoryGC();
112 
113   static void MaybePokeCC();
114 
115   // Calling LikelyShortLivingObjectCreated() makes a GC more likely.
116   static void LikelyShortLivingObjectCreated();
117 
118   static bool HasHadCleanupSinceLastGC();
119 
GetCachedGlobalObject()120   nsIScriptGlobalObject* GetCachedGlobalObject() {
121     // Verify that we have a global so that this
122     // does always return a null when GetGlobalObject() is null.
123     JSObject* global = GetWindowProxy();
124     return global ? mGlobalObjectRef.get() : nullptr;
125   }
126 
127  protected:
128   virtual ~nsJSContext();
129 
130   // Helper to convert xpcom datatypes to jsvals.
131   nsresult ConvertSupportsTojsvals(JSContext* aCx, nsISupports* aArgs,
132                                    JS::Handle<JSObject*> aScope,
133                                    JS::MutableHandleVector<JS::Value> aArgsOut);
134 
135   nsresult AddSupportsPrimitiveTojsvals(JSContext* aCx, nsISupports* aArg,
136                                         JS::Value* aArgv);
137 
138  private:
139   void Destroy();
140 
141   JS::Heap<JSObject*> mWindowProxy;
142 
143   bool mGCOnDestruction;
144   bool mProcessingScriptTag;
145 
146   // mGlobalObjectRef ensures that the outer window stays alive as long as the
147   // context does. It is eventually collected by the cycle collector.
148   nsCOMPtr<nsIScriptGlobalObject> mGlobalObjectRef;
149 
150   static bool DOMOperationCallback(JSContext* cx);
151 };
152 
153 namespace mozilla {
154 namespace dom {
155 
156 class SerializedStackHolder;
157 
158 void StartupJSEnvironment();
159 void ShutdownJSEnvironment();
160 
161 // Runnable that's used to do async error reporting
162 class AsyncErrorReporter final : public mozilla::Runnable {
163  public:
164   explicit AsyncErrorReporter(xpc::ErrorReport* aReport);
165   // SerializeStack is suitable for main or worklet thread use.
166   // Stacks from worker threads are not supported.
167   // See https://bugzilla.mozilla.org/show_bug.cgi?id=1578968
168   void SerializeStack(JSContext* aCx, JS::Handle<JSObject*> aStack);
169 
170   // Set the exception value associated with this error report.
171   // Should only be called from the main thread.
172   void SetException(JSContext* aCx, JS::Handle<JS::Value> aException);
173 
174  protected:
175   NS_IMETHOD Run() override;
176 
177   // This is only used on main thread!
178   JS::PersistentRootedValue mException;
179   bool mHasException = false;
180 
181   RefPtr<xpc::ErrorReport> mReport;
182   // This may be used to marshal a stack from an arbitrary thread/runtime into
183   // the main thread/runtime where the console service runs.
184   UniquePtr<SerializedStackHolder> mStackHolder;
185 };
186 
187 }  // namespace dom
188 }  // namespace mozilla
189 
190 // An interface for fast and native conversion to/from nsIArray. If an object
191 // supports this interface, JS can reach directly in for the argv, and avoid
192 // nsISupports conversion. If this interface is not supported, the object will
193 // be queried for nsIArray, and everything converted via xpcom objects.
194 #define NS_IJSARGARRAY_IID                           \
195   {                                                  \
196     0xb6acdac8, 0xf5c6, 0x432c, {                    \
197       0xa8, 0x6e, 0x33, 0xee, 0xb1, 0xb0, 0xcd, 0xdc \
198     }                                                \
199   }
200 
201 class nsIJSArgArray : public nsIArray {
202  public:
203   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IJSARGARRAY_IID)
204   // Bug 312003 describes why this must be "void **", but after calling argv
205   // may be cast to JS::Value* and the args found at:
206   //    ((JS::Value*)argv)[0], ..., ((JS::Value*)argv)[argc - 1]
207   virtual nsresult GetArgs(uint32_t* argc, void** argv) = 0;
208 };
209 
210 NS_DEFINE_STATIC_IID_ACCESSOR(nsIJSArgArray, NS_IJSARGARRAY_IID)
211 
212 #endif /* nsJSEnvironment_h */
213