nsWindowWatcher.cpp 85 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. //#define USEWEAKREFS // (haven't quite figured that out yet)
  6. #include "nsWindowWatcher.h"
  7. #include "nsAutoWindowStateHelper.h"
  8. #include "nsCRT.h"
  9. #include "nsNetUtil.h"
  10. #include "nsIAuthPrompt.h"
  11. #include "nsIAuthPrompt2.h"
  12. #include "nsISimpleEnumerator.h"
  13. #include "nsIInterfaceRequestorUtils.h"
  14. #include "nsJSUtils.h"
  15. #include "plstr.h"
  16. #include "nsDocShell.h"
  17. #include "nsGlobalWindow.h"
  18. #include "nsIBaseWindow.h"
  19. #include "nsIBrowserDOMWindow.h"
  20. #include "nsIDocShell.h"
  21. #include "nsIDocShellLoadInfo.h"
  22. #include "nsIDocShellTreeItem.h"
  23. #include "nsIDocShellTreeOwner.h"
  24. #include "nsIDocumentLoader.h"
  25. #include "nsIDocument.h"
  26. #include "nsIDOMDocument.h"
  27. #include "nsIDOMWindow.h"
  28. #include "nsIDOMChromeWindow.h"
  29. #include "nsIDOMModalContentWindow.h"
  30. #include "nsIPrompt.h"
  31. #include "nsIScriptObjectPrincipal.h"
  32. #include "nsIScreen.h"
  33. #include "nsIScreenManager.h"
  34. #include "nsIScriptContext.h"
  35. #include "nsIObserverService.h"
  36. #include "nsIScriptSecurityManager.h"
  37. #include "nsXPCOM.h"
  38. #include "nsIURI.h"
  39. #include "nsIWebBrowser.h"
  40. #include "nsIWebBrowserChrome.h"
  41. #include "nsIWebNavigation.h"
  42. #include "nsIWindowCreator.h"
  43. #include "nsIWindowCreator2.h"
  44. #include "nsIXPConnect.h"
  45. #include "nsIXULRuntime.h"
  46. #include "nsPIDOMWindow.h"
  47. #include "nsIContentViewer.h"
  48. #include "nsIWindowProvider.h"
  49. #include "nsIMutableArray.h"
  50. #include "nsIDOMStorageManager.h"
  51. #include "nsIWidget.h"
  52. #include "nsFocusManager.h"
  53. #include "nsIPresShell.h"
  54. #include "nsPresContext.h"
  55. #include "nsContentUtils.h"
  56. #include "nsIPrefBranch.h"
  57. #include "nsIPrefService.h"
  58. #include "nsSandboxFlags.h"
  59. #include "mozilla/Preferences.h"
  60. #include "mozilla/dom/DOMStorage.h"
  61. #include "mozilla/dom/ScriptSettings.h"
  62. #include "mozilla/dom/TabParent.h"
  63. #include "mozilla/dom/DocGroup.h"
  64. #include "mozilla/dom/TabGroup.h"
  65. #include "nsIXULWindow.h"
  66. #include "nsIXULBrowserWindow.h"
  67. #include "nsGlobalWindow.h"
  68. #ifdef USEWEAKREFS
  69. #include "nsIWeakReference.h"
  70. #endif
  71. using namespace mozilla;
  72. using namespace mozilla::dom;
  73. /****************************************************************
  74. ******************** nsWatcherWindowEntry **********************
  75. ****************************************************************/
  76. class nsWindowWatcher;
  77. struct nsWatcherWindowEntry
  78. {
  79. nsWatcherWindowEntry(mozIDOMWindowProxy* aWindow, nsIWebBrowserChrome* aChrome)
  80. : mChrome(nullptr)
  81. {
  82. #ifdef USEWEAKREFS
  83. mWindow = do_GetWeakReference(aWindow);
  84. #else
  85. mWindow = aWindow;
  86. #endif
  87. nsCOMPtr<nsISupportsWeakReference> supportsweak(do_QueryInterface(aChrome));
  88. if (supportsweak) {
  89. supportsweak->GetWeakReference(getter_AddRefs(mChromeWeak));
  90. } else {
  91. mChrome = aChrome;
  92. mChromeWeak = nullptr;
  93. }
  94. ReferenceSelf();
  95. }
  96. ~nsWatcherWindowEntry() {}
  97. void InsertAfter(nsWatcherWindowEntry* aOlder);
  98. void Unlink();
  99. void ReferenceSelf();
  100. #ifdef USEWEAKREFS
  101. nsCOMPtr<nsIWeakReference> mWindow;
  102. #else // still not an owning ref
  103. mozIDOMWindowProxy* mWindow;
  104. #endif
  105. nsIWebBrowserChrome* mChrome;
  106. nsWeakPtr mChromeWeak;
  107. // each struct is in a circular, doubly-linked list
  108. nsWatcherWindowEntry* mYounger; // next younger in sequence
  109. nsWatcherWindowEntry* mOlder;
  110. };
  111. void
  112. nsWatcherWindowEntry::InsertAfter(nsWatcherWindowEntry* aOlder)
  113. {
  114. if (aOlder) {
  115. mOlder = aOlder;
  116. mYounger = aOlder->mYounger;
  117. mOlder->mYounger = this;
  118. if (mOlder->mOlder == mOlder) {
  119. mOlder->mOlder = this;
  120. }
  121. mYounger->mOlder = this;
  122. if (mYounger->mYounger == mYounger) {
  123. mYounger->mYounger = this;
  124. }
  125. }
  126. }
  127. void
  128. nsWatcherWindowEntry::Unlink()
  129. {
  130. mOlder->mYounger = mYounger;
  131. mYounger->mOlder = mOlder;
  132. ReferenceSelf();
  133. }
  134. void
  135. nsWatcherWindowEntry::ReferenceSelf()
  136. {
  137. mYounger = this;
  138. mOlder = this;
  139. }
  140. /****************************************************************
  141. ****************** nsWatcherWindowEnumerator *******************
  142. ****************************************************************/
  143. class nsWatcherWindowEnumerator : public nsISimpleEnumerator
  144. {
  145. public:
  146. explicit nsWatcherWindowEnumerator(nsWindowWatcher* aWatcher);
  147. NS_IMETHOD HasMoreElements(bool* aResult) override;
  148. NS_IMETHOD GetNext(nsISupports** aResult) override;
  149. NS_DECL_ISUPPORTS
  150. protected:
  151. virtual ~nsWatcherWindowEnumerator();
  152. private:
  153. friend class nsWindowWatcher;
  154. nsWatcherWindowEntry* FindNext();
  155. void WindowRemoved(nsWatcherWindowEntry* aInfo);
  156. nsWindowWatcher* mWindowWatcher;
  157. nsWatcherWindowEntry* mCurrentPosition;
  158. };
  159. NS_IMPL_ADDREF(nsWatcherWindowEnumerator)
  160. NS_IMPL_RELEASE(nsWatcherWindowEnumerator)
  161. NS_IMPL_QUERY_INTERFACE(nsWatcherWindowEnumerator, nsISimpleEnumerator)
  162. nsWatcherWindowEnumerator::nsWatcherWindowEnumerator(nsWindowWatcher* aWatcher)
  163. : mWindowWatcher(aWatcher)
  164. , mCurrentPosition(aWatcher->mOldestWindow)
  165. {
  166. mWindowWatcher->AddEnumerator(this);
  167. mWindowWatcher->AddRef();
  168. }
  169. nsWatcherWindowEnumerator::~nsWatcherWindowEnumerator()
  170. {
  171. mWindowWatcher->RemoveEnumerator(this);
  172. mWindowWatcher->Release();
  173. }
  174. NS_IMETHODIMP
  175. nsWatcherWindowEnumerator::HasMoreElements(bool* aResult)
  176. {
  177. if (!aResult) {
  178. return NS_ERROR_INVALID_ARG;
  179. }
  180. *aResult = !!mCurrentPosition;
  181. return NS_OK;
  182. }
  183. NS_IMETHODIMP
  184. nsWatcherWindowEnumerator::GetNext(nsISupports** aResult)
  185. {
  186. if (!aResult) {
  187. return NS_ERROR_INVALID_ARG;
  188. }
  189. *aResult = nullptr;
  190. #ifdef USEWEAKREFS
  191. while (mCurrentPosition) {
  192. CallQueryReferent(mCurrentPosition->mWindow, aResult);
  193. if (*aResult) {
  194. mCurrentPosition = FindNext();
  195. break;
  196. } else { // window is gone!
  197. mWindowWatcher->RemoveWindow(mCurrentPosition);
  198. }
  199. }
  200. NS_IF_ADDREF(*aResult);
  201. #else
  202. if (mCurrentPosition) {
  203. CallQueryInterface(mCurrentPosition->mWindow, aResult);
  204. mCurrentPosition = FindNext();
  205. }
  206. #endif
  207. return NS_OK;
  208. }
  209. nsWatcherWindowEntry*
  210. nsWatcherWindowEnumerator::FindNext()
  211. {
  212. nsWatcherWindowEntry* info;
  213. if (!mCurrentPosition) {
  214. return 0;
  215. }
  216. info = mCurrentPosition->mYounger;
  217. return info == mWindowWatcher->mOldestWindow ? 0 : info;
  218. }
  219. // if a window is being removed adjust the iterator's current position
  220. void
  221. nsWatcherWindowEnumerator::WindowRemoved(nsWatcherWindowEntry* aInfo)
  222. {
  223. if (mCurrentPosition == aInfo) {
  224. mCurrentPosition =
  225. mCurrentPosition != aInfo->mYounger ? aInfo->mYounger : 0;
  226. }
  227. }
  228. /****************************************************************
  229. *********************** nsWindowWatcher ************************
  230. ****************************************************************/
  231. NS_IMPL_ADDREF(nsWindowWatcher)
  232. NS_IMPL_RELEASE(nsWindowWatcher)
  233. NS_IMPL_QUERY_INTERFACE(nsWindowWatcher,
  234. nsIWindowWatcher,
  235. nsIPromptFactory,
  236. nsPIWindowWatcher)
  237. nsWindowWatcher::nsWindowWatcher()
  238. : mEnumeratorList()
  239. , mOldestWindow(0)
  240. , mListLock("nsWindowWatcher.mListLock")
  241. {
  242. }
  243. nsWindowWatcher::~nsWindowWatcher()
  244. {
  245. // delete data
  246. while (mOldestWindow) {
  247. RemoveWindow(mOldestWindow);
  248. }
  249. }
  250. nsresult
  251. nsWindowWatcher::Init()
  252. {
  253. return NS_OK;
  254. }
  255. /**
  256. * Convert aArguments into either an nsIArray or nullptr.
  257. *
  258. * - If aArguments is nullptr, return nullptr.
  259. * - If aArguments is an nsArray, return nullptr if it's empty, or otherwise
  260. * return the array.
  261. * - If aArguments is an nsIArray, return nullptr if it's empty, or
  262. * otherwise just return the array.
  263. * - Otherwise, return an nsIArray with one element: aArguments.
  264. */
  265. static already_AddRefed<nsIArray>
  266. ConvertArgsToArray(nsISupports* aArguments)
  267. {
  268. if (!aArguments) {
  269. return nullptr;
  270. }
  271. nsCOMPtr<nsIArray> array = do_QueryInterface(aArguments);
  272. if (array) {
  273. uint32_t argc = 0;
  274. array->GetLength(&argc);
  275. if (argc == 0) {
  276. return nullptr;
  277. }
  278. return array.forget();
  279. }
  280. nsCOMPtr<nsIMutableArray> singletonArray =
  281. do_CreateInstance(NS_ARRAY_CONTRACTID);
  282. NS_ENSURE_TRUE(singletonArray, nullptr);
  283. nsresult rv = singletonArray->AppendElement(aArguments, /* aWeak = */ false);
  284. NS_ENSURE_SUCCESS(rv, nullptr);
  285. return singletonArray.forget();
  286. }
  287. NS_IMETHODIMP
  288. nsWindowWatcher::OpenWindow(mozIDOMWindowProxy* aParent,
  289. const char* aUrl,
  290. const char* aName,
  291. const char* aFeatures,
  292. nsISupports* aArguments,
  293. mozIDOMWindowProxy** aResult)
  294. {
  295. nsCOMPtr<nsIArray> argv = ConvertArgsToArray(aArguments);
  296. uint32_t argc = 0;
  297. if (argv) {
  298. argv->GetLength(&argc);
  299. }
  300. bool dialog = (argc != 0);
  301. return OpenWindowInternal(aParent, aUrl, aName, aFeatures,
  302. /* calledFromJS = */ false, dialog,
  303. /* navigate = */ true, argv,
  304. /* aIsPopupSpam = */ false,
  305. /* aForceNoOpener = */ false,
  306. /* aLoadInfo = */ nullptr,
  307. aResult);
  308. }
  309. struct SizeSpec
  310. {
  311. SizeSpec()
  312. : mLeft(0)
  313. , mTop(0)
  314. , mOuterWidth(0)
  315. , mOuterHeight(0)
  316. , mInnerWidth(0)
  317. , mInnerHeight(0)
  318. , mLeftSpecified(false)
  319. , mTopSpecified(false)
  320. , mOuterWidthSpecified(false)
  321. , mOuterHeightSpecified(false)
  322. , mInnerWidthSpecified(false)
  323. , mInnerHeightSpecified(false)
  324. , mUseDefaultWidth(false)
  325. , mUseDefaultHeight(false)
  326. {
  327. }
  328. int32_t mLeft;
  329. int32_t mTop;
  330. int32_t mOuterWidth; // Total window width
  331. int32_t mOuterHeight; // Total window height
  332. int32_t mInnerWidth; // Content area width
  333. int32_t mInnerHeight; // Content area height
  334. bool mLeftSpecified;
  335. bool mTopSpecified;
  336. bool mOuterWidthSpecified;
  337. bool mOuterHeightSpecified;
  338. bool mInnerWidthSpecified;
  339. bool mInnerHeightSpecified;
  340. // If these booleans are true, don't look at the corresponding width values
  341. // even if they're specified -- they'll be bogus
  342. bool mUseDefaultWidth;
  343. bool mUseDefaultHeight;
  344. bool PositionSpecified() const
  345. {
  346. return mLeftSpecified || mTopSpecified;
  347. }
  348. bool SizeSpecified() const
  349. {
  350. return mOuterWidthSpecified || mOuterHeightSpecified ||
  351. mInnerWidthSpecified || mInnerHeightSpecified;
  352. }
  353. };
  354. NS_IMETHODIMP
  355. nsWindowWatcher::OpenWindow2(mozIDOMWindowProxy* aParent,
  356. const char* aUrl,
  357. const char* aName,
  358. const char* aFeatures,
  359. bool aCalledFromScript,
  360. bool aDialog,
  361. bool aNavigate,
  362. nsISupports* aArguments,
  363. bool aIsPopupSpam,
  364. bool aForceNoOpener,
  365. nsIDocShellLoadInfo* aLoadInfo,
  366. mozIDOMWindowProxy** aResult)
  367. {
  368. nsCOMPtr<nsIArray> argv = ConvertArgsToArray(aArguments);
  369. uint32_t argc = 0;
  370. if (argv) {
  371. argv->GetLength(&argc);
  372. }
  373. // This is extremely messed up, but this behavior is necessary because
  374. // callers lie about whether they're a dialog window and whether they're
  375. // called from script. Fixing this is bug 779939.
  376. bool dialog = aDialog;
  377. if (!aCalledFromScript) {
  378. dialog = argc > 0;
  379. }
  380. return OpenWindowInternal(aParent, aUrl, aName, aFeatures,
  381. aCalledFromScript, dialog,
  382. aNavigate, argv, aIsPopupSpam,
  383. aForceNoOpener, aLoadInfo, aResult);
  384. }
  385. // This static function checks if the aDocShell uses an UserContextId equal to
  386. // the userContextId of subjectPrincipal, if not null.
  387. static bool
  388. CheckUserContextCompatibility(nsIDocShell* aDocShell)
  389. {
  390. MOZ_ASSERT(aDocShell);
  391. uint32_t userContextId =
  392. static_cast<nsDocShell*>(aDocShell)->GetOriginAttributes().mUserContextId;
  393. nsCOMPtr<nsIPrincipal> subjectPrincipal =
  394. nsContentUtils::GetCurrentJSContext()
  395. ? nsContentUtils::SubjectPrincipal() : nullptr;
  396. // If we don't have a valid principal, probably we are in e10s mode, parent
  397. // side.
  398. if (!subjectPrincipal) {
  399. return true;
  400. }
  401. // DocShell can have UsercontextID set but loading a document with system
  402. // principal. In this case, we consider everything ok.
  403. if (nsContentUtils::IsSystemPrincipal(subjectPrincipal)) {
  404. return true;
  405. }
  406. return subjectPrincipal->GetUserContextId() == userContextId;
  407. }
  408. NS_IMETHODIMP
  409. nsWindowWatcher::OpenWindowWithoutParent(nsITabParent** aResult)
  410. {
  411. return OpenWindowWithTabParent(nullptr, EmptyCString(), true, 1.0f, aResult);
  412. }
  413. nsresult
  414. nsWindowWatcher::CreateChromeWindow(const nsACString& aFeatures,
  415. nsIWebBrowserChrome* aParentChrome,
  416. uint32_t aChromeFlags,
  417. uint32_t aContextFlags,
  418. nsITabParent* aOpeningTabParent,
  419. mozIDOMWindowProxy* aOpener,
  420. nsIWebBrowserChrome** aResult)
  421. {
  422. nsCOMPtr<nsIWindowCreator2> windowCreator2(do_QueryInterface(mWindowCreator));
  423. if (NS_WARN_IF(!windowCreator2)) {
  424. return NS_ERROR_UNEXPECTED;
  425. }
  426. bool cancel = false;
  427. nsCOMPtr<nsIWebBrowserChrome> newWindowChrome;
  428. nsresult rv =
  429. windowCreator2->CreateChromeWindow2(aParentChrome, aChromeFlags, aContextFlags,
  430. aOpeningTabParent, aOpener, &cancel,
  431. getter_AddRefs(newWindowChrome));
  432. if (NS_SUCCEEDED(rv) && cancel) {
  433. newWindowChrome = nullptr;
  434. return NS_ERROR_ABORT;
  435. }
  436. newWindowChrome.forget(aResult);
  437. return NS_OK;
  438. }
  439. /**
  440. * Disable persistence of size/position in popups (determined by
  441. * determining whether the features parameter specifies width or height
  442. * in any way). We consider any overriding of the window's size or position
  443. * in the open call as disabling persistence of those attributes.
  444. * Popup windows (which should not persist size or position) generally set
  445. * the size.
  446. *
  447. * @param aFeatures
  448. * The features string that was used to open the window.
  449. * @param aTreeOwner
  450. * The nsIDocShellTreeOwner of the newly opened window. If null,
  451. * this function is a no-op.
  452. */
  453. void
  454. nsWindowWatcher::MaybeDisablePersistence(const nsACString& aFeatures,
  455. nsIDocShellTreeOwner* aTreeOwner)
  456. {
  457. if (!aTreeOwner) {
  458. return;
  459. }
  460. // At the moment, the strings "height=" or "width=" never happen
  461. // outside a size specification, so we can do this the Q&D way.
  462. if (PL_strcasestr(aFeatures.BeginReading(), "width=") ||
  463. PL_strcasestr(aFeatures.BeginReading(), "height=")) {
  464. aTreeOwner->SetPersistence(false, false, false);
  465. }
  466. }
  467. NS_IMETHODIMP
  468. nsWindowWatcher::OpenWindowWithTabParent(nsITabParent* aOpeningTabParent,
  469. const nsACString& aFeatures,
  470. bool aCalledFromJS,
  471. float aOpenerFullZoom,
  472. nsITabParent** aResult)
  473. {
  474. MOZ_ASSERT(XRE_IsParentProcess());
  475. MOZ_ASSERT(mWindowCreator);
  476. if (!nsContentUtils::IsSafeToRunScript()) {
  477. nsContentUtils::WarnScriptWasIgnored(nullptr);
  478. return NS_ERROR_FAILURE;
  479. }
  480. if (NS_WARN_IF(!mWindowCreator)) {
  481. return NS_ERROR_UNEXPECTED;
  482. }
  483. bool isPrivateBrowsingWindow =
  484. Preferences::GetBool("browser.privatebrowsing.autostart");
  485. nsCOMPtr<nsPIDOMWindowOuter> parentWindowOuter;
  486. if (aOpeningTabParent) {
  487. // We need to examine the window that aOpeningTabParent belongs to in
  488. // order to inform us of what kind of window we're going to open.
  489. TabParent* openingTab = TabParent::GetFrom(aOpeningTabParent);
  490. parentWindowOuter = openingTab->GetParentWindowOuter();
  491. // Propagate the privacy status of the parent window, if
  492. // available, to the child.
  493. if (!isPrivateBrowsingWindow) {
  494. nsCOMPtr<nsILoadContext> parentContext = openingTab->GetLoadContext();
  495. if (parentContext) {
  496. isPrivateBrowsingWindow = parentContext->UsePrivateBrowsing();
  497. }
  498. }
  499. }
  500. if (!parentWindowOuter) {
  501. // We couldn't find a browser window for the opener, so either we
  502. // never were passed aOpeningTabParent, the window is closed,
  503. // or it's in the process of closing. Either way, we'll use
  504. // the most recently opened browser window instead.
  505. parentWindowOuter = nsContentUtils::GetMostRecentNonPBWindow();
  506. }
  507. if (NS_WARN_IF(!parentWindowOuter)) {
  508. return NS_ERROR_UNEXPECTED;
  509. }
  510. nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
  511. GetWindowTreeOwner(parentWindowOuter, getter_AddRefs(parentTreeOwner));
  512. if (NS_WARN_IF(!parentTreeOwner)) {
  513. return NS_ERROR_UNEXPECTED;
  514. }
  515. nsCOMPtr<nsIWindowCreator2> windowCreator2(do_QueryInterface(mWindowCreator));
  516. if (NS_WARN_IF(!windowCreator2)) {
  517. return NS_ERROR_UNEXPECTED;
  518. }
  519. uint32_t contextFlags = 0;
  520. if (parentWindowOuter->IsLoadingOrRunningTimeout()) {
  521. contextFlags |=
  522. nsIWindowCreator2::PARENT_IS_LOADING_OR_RUNNING_TIMEOUT;
  523. }
  524. uint32_t chromeFlags = CalculateChromeFlagsForChild(aFeatures);
  525. // A content process has asked for a new window, which implies
  526. // that the new window will need to be remote.
  527. chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
  528. nsCOMPtr<nsIWebBrowserChrome> parentChrome(do_GetInterface(parentTreeOwner));
  529. nsCOMPtr<nsIWebBrowserChrome> newWindowChrome;
  530. CreateChromeWindow(aFeatures, parentChrome, chromeFlags, contextFlags,
  531. aOpeningTabParent, nullptr, getter_AddRefs(newWindowChrome));
  532. if (NS_WARN_IF(!newWindowChrome)) {
  533. return NS_ERROR_UNEXPECTED;
  534. }
  535. nsCOMPtr<nsIDocShellTreeItem> chromeTreeItem = do_GetInterface(newWindowChrome);
  536. if (NS_WARN_IF(!chromeTreeItem)) {
  537. return NS_ERROR_UNEXPECTED;
  538. }
  539. nsCOMPtr<nsIDocShellTreeOwner> chromeTreeOwner;
  540. chromeTreeItem->GetTreeOwner(getter_AddRefs(chromeTreeOwner));
  541. if (NS_WARN_IF(!chromeTreeOwner)) {
  542. return NS_ERROR_UNEXPECTED;
  543. }
  544. nsCOMPtr<nsILoadContext> chromeContext = do_QueryInterface(chromeTreeItem);
  545. if (NS_WARN_IF(!chromeContext)) {
  546. return NS_ERROR_UNEXPECTED;
  547. }
  548. chromeContext->SetPrivateBrowsing(isPrivateBrowsingWindow);
  549. // Tabs opened from a content process can only open new windows
  550. // that will also run with out-of-process tabs.
  551. chromeContext->SetRemoteTabs(true);
  552. MaybeDisablePersistence(aFeatures, chromeTreeOwner);
  553. SizeSpec sizeSpec;
  554. CalcSizeSpec(aFeatures, sizeSpec);
  555. SizeOpenedWindow(chromeTreeOwner, parentWindowOuter, false, sizeSpec,
  556. Some(aOpenerFullZoom));
  557. nsCOMPtr<nsITabParent> newTabParent;
  558. chromeTreeOwner->GetPrimaryTabParent(getter_AddRefs(newTabParent));
  559. if (NS_WARN_IF(!newTabParent)) {
  560. return NS_ERROR_UNEXPECTED;
  561. }
  562. newTabParent.forget(aResult);
  563. return NS_OK;
  564. }
  565. nsresult
  566. nsWindowWatcher::OpenWindowInternal(mozIDOMWindowProxy* aParent,
  567. const char* aUrl,
  568. const char* aName,
  569. const char* aFeatures,
  570. bool aCalledFromJS,
  571. bool aDialog,
  572. bool aNavigate,
  573. nsIArray* aArgv,
  574. bool aIsPopupSpam,
  575. bool aForceNoOpener,
  576. nsIDocShellLoadInfo* aLoadInfo,
  577. mozIDOMWindowProxy** aResult)
  578. {
  579. nsresult rv = NS_OK;
  580. bool isNewToplevelWindow = false;
  581. bool windowIsNew = false;
  582. bool windowNeedsName = false;
  583. bool windowIsModal = false;
  584. bool uriToLoadIsChrome = false;
  585. uint32_t chromeFlags;
  586. nsAutoString name; // string version of aName
  587. nsAutoCString features; // string version of aFeatures
  588. nsCOMPtr<nsIURI> uriToLoad; // from aUrl, if any
  589. nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner; // from the parent window, if any
  590. nsCOMPtr<nsIDocShellTreeItem> newDocShellItem; // from the new window
  591. nsCOMPtr<nsPIDOMWindowOuter> parent =
  592. aParent ? nsPIDOMWindowOuter::From(aParent) : nullptr;
  593. NS_ENSURE_ARG_POINTER(aResult);
  594. *aResult = 0;
  595. if (!nsContentUtils::IsSafeToRunScript()) {
  596. nsContentUtils::WarnScriptWasIgnored(nullptr);
  597. return NS_ERROR_FAILURE;
  598. }
  599. GetWindowTreeOwner(parent, getter_AddRefs(parentTreeOwner));
  600. // We expect TabParent to have provided us the absolute URI of the window
  601. // we're to open, so there's no need to call URIfromURL (or more importantly,
  602. // to check for a chrome URI, which cannot be opened from a remote tab).
  603. if (aUrl) {
  604. rv = URIfromURL(aUrl, aParent, getter_AddRefs(uriToLoad));
  605. if (NS_FAILED(rv)) {
  606. return rv;
  607. }
  608. uriToLoad->SchemeIs("chrome", &uriToLoadIsChrome);
  609. }
  610. bool nameSpecified = false;
  611. if (aName) {
  612. CopyUTF8toUTF16(aName, name);
  613. nameSpecified = true;
  614. } else {
  615. name.SetIsVoid(true);
  616. }
  617. if (aFeatures) {
  618. features.Assign(aFeatures);
  619. features.StripWhitespace();
  620. } else {
  621. features.SetIsVoid(true);
  622. }
  623. // try to find an extant window with the given name
  624. nsCOMPtr<nsPIDOMWindowOuter> foundWindow =
  625. SafeGetWindowByName(name, aForceNoOpener, aParent);
  626. GetWindowTreeItem(foundWindow, getter_AddRefs(newDocShellItem));
  627. // Do sandbox checks here, instead of waiting until nsIDocShell::LoadURI.
  628. // The state of the window can change before this call and if we are blocked
  629. // because of sandboxing, we wouldn't want that to happen.
  630. nsCOMPtr<nsPIDOMWindowOuter> parentWindow =
  631. aParent ? nsPIDOMWindowOuter::From(aParent) : nullptr;
  632. nsCOMPtr<nsIDocShell> parentDocShell;
  633. if (parentWindow) {
  634. parentDocShell = parentWindow->GetDocShell();
  635. if (parentDocShell) {
  636. nsCOMPtr<nsIDocShell> foundDocShell = do_QueryInterface(newDocShellItem);
  637. if (parentDocShell->IsSandboxedFrom(foundDocShell)) {
  638. return NS_ERROR_DOM_INVALID_ACCESS_ERR;
  639. }
  640. }
  641. }
  642. // no extant window? make a new one.
  643. // If no parent, consider it chrome when running in the parent process.
  644. bool hasChromeParent = XRE_IsContentProcess() ? false : true;
  645. if (aParent) {
  646. // Check if the parent document has chrome privileges.
  647. nsIDocument* doc = parentWindow->GetDoc();
  648. hasChromeParent = doc && nsContentUtils::IsChromeDoc(doc);
  649. }
  650. bool isCallerChrome = nsContentUtils::LegacyIsCallerChromeOrNativeCode();
  651. // Make sure we calculate the chromeFlags *before* we push the
  652. // callee context onto the context stack so that
  653. // the calculation sees the actual caller when doing its
  654. // security checks.
  655. if (isCallerChrome && XRE_IsParentProcess()) {
  656. chromeFlags = CalculateChromeFlagsForParent(aParent, features,
  657. aDialog, uriToLoadIsChrome,
  658. hasChromeParent, aCalledFromJS);
  659. } else {
  660. chromeFlags = CalculateChromeFlagsForChild(features);
  661. if (aDialog) {
  662. MOZ_ASSERT(XRE_IsParentProcess());
  663. chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG;
  664. }
  665. }
  666. SizeSpec sizeSpec;
  667. CalcSizeSpec(features, sizeSpec);
  668. nsCOMPtr<nsIScriptSecurityManager> sm(
  669. do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
  670. // XXXbz Why is an AutoJSAPI good enough here? Wouldn't AutoEntryScript (so
  671. // we affect the entry global) make more sense? Or do we just want to affect
  672. // GetSubjectPrincipal()?
  673. dom::AutoJSAPI jsapiChromeGuard;
  674. bool windowTypeIsChrome =
  675. chromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
  676. if (isCallerChrome && !hasChromeParent && !windowTypeIsChrome) {
  677. // open() is called from chrome on a non-chrome window, initialize an
  678. // AutoJSAPI with the callee to prevent the caller's privileges from leaking
  679. // into code that runs while opening the new window.
  680. //
  681. // The reasoning for this is in bug 289204. Basically, chrome sometimes does
  682. // someContentWindow.open(untrustedURL), and wants to be insulated from nasty
  683. // javascript: URLs and such. But there are also cases where we create a
  684. // window parented to a content window (such as a download dialog), usually
  685. // directly with nsIWindowWatcher. In those cases, we want the principal of
  686. // the initial about:blank document to be system, so that the subsequent XUL
  687. // load can reuse the inner window and avoid blowing away expandos. As such,
  688. // we decide whether to load with the principal of the caller or of the parent
  689. // based on whether the docshell type is chrome or content.
  690. nsCOMPtr<nsIGlobalObject> parentGlobalObject = do_QueryInterface(aParent);
  691. if (!aParent) {
  692. jsapiChromeGuard.Init();
  693. } else if (NS_WARN_IF(!jsapiChromeGuard.Init(parentGlobalObject))) {
  694. return NS_ERROR_UNEXPECTED;
  695. }
  696. }
  697. uint32_t activeDocsSandboxFlags = 0;
  698. if (!newDocShellItem) {
  699. // We're going to either open up a new window ourselves or ask a
  700. // nsIWindowProvider for one. In either case, we'll want to set the right
  701. // name on it.
  702. windowNeedsName = true;
  703. // If the parent trying to open a new window is sandboxed
  704. // without 'allow-popups', this is not allowed and we fail here.
  705. if (aParent) {
  706. if (nsIDocument* doc = parentWindow->GetDoc()) {
  707. // Save sandbox flags for copying to new browsing context (docShell).
  708. activeDocsSandboxFlags = doc->GetSandboxFlags();
  709. if (activeDocsSandboxFlags & SANDBOXED_AUXILIARY_NAVIGATION) {
  710. return NS_ERROR_DOM_INVALID_ACCESS_ERR;
  711. }
  712. }
  713. }
  714. // Now check whether it's ok to ask a window provider for a window. Don't
  715. // do it if we're opening a dialog or if our parent is a chrome window or
  716. // if we're opening something that has modal, dialog, or chrome flags set.
  717. nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(aParent);
  718. if (!aDialog && !chromeWin &&
  719. !(chromeFlags & (nsIWebBrowserChrome::CHROME_MODAL |
  720. nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
  721. nsIWebBrowserChrome::CHROME_OPENAS_CHROME))) {
  722. nsCOMPtr<nsIWindowProvider> provider;
  723. if (parentTreeOwner) {
  724. provider = do_GetInterface(parentTreeOwner);
  725. } else if (XRE_IsContentProcess()) {
  726. // we're in a content process but we don't have a tabchild we can
  727. // use.
  728. provider = nsContentUtils::GetWindowProviderForContentProcess();
  729. }
  730. if (provider) {
  731. nsCOMPtr<mozIDOMWindowProxy> newWindow;
  732. rv = provider->ProvideWindow(aParent, chromeFlags, aCalledFromJS,
  733. sizeSpec.PositionSpecified(),
  734. sizeSpec.SizeSpecified(),
  735. uriToLoad, name, features, aForceNoOpener,
  736. &windowIsNew, getter_AddRefs(newWindow));
  737. if (NS_SUCCEEDED(rv)) {
  738. GetWindowTreeItem(newWindow, getter_AddRefs(newDocShellItem));
  739. if (windowIsNew && newDocShellItem) {
  740. // Make sure to stop any loads happening in this window that the
  741. // window provider might have started. Otherwise if our caller
  742. // manipulates the window it just opened and then the load
  743. // completes their stuff will get blown away.
  744. nsCOMPtr<nsIWebNavigation> webNav =
  745. do_QueryInterface(newDocShellItem);
  746. webNav->Stop(nsIWebNavigation::STOP_NETWORK);
  747. }
  748. // If this is a new window, but it's incompatible with the current
  749. // userContextId, we ignore it and we pretend that nothing has been
  750. // returned by ProvideWindow.
  751. if (!windowIsNew && newDocShellItem) {
  752. nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(newDocShellItem);
  753. if (!CheckUserContextCompatibility(docShell)) {
  754. newWindow = nullptr;
  755. newDocShellItem = nullptr;
  756. windowIsNew = false;
  757. }
  758. }
  759. } else if (rv == NS_ERROR_ABORT) {
  760. // NS_ERROR_ABORT means the window provider has flat-out rejected
  761. // the open-window call and we should bail. Don't return an error
  762. // here, because our caller may propagate that error, which might
  763. // cause e.g. window.open to throw! Just return null for our out
  764. // param.
  765. return NS_OK;
  766. }
  767. }
  768. }
  769. }
  770. bool newWindowShouldBeModal = false;
  771. bool parentIsModal = false;
  772. if (!newDocShellItem) {
  773. windowIsNew = true;
  774. isNewToplevelWindow = true;
  775. nsCOMPtr<nsIWebBrowserChrome> parentChrome(do_GetInterface(parentTreeOwner));
  776. // is the parent (if any) modal? if so, we must be, too.
  777. bool weAreModal = (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL) != 0;
  778. newWindowShouldBeModal = weAreModal;
  779. if (!weAreModal && parentChrome) {
  780. parentChrome->IsWindowModal(&weAreModal);
  781. parentIsModal = weAreModal;
  782. }
  783. if (weAreModal) {
  784. windowIsModal = true;
  785. // in case we added this because weAreModal
  786. chromeFlags |= nsIWebBrowserChrome::CHROME_MODAL |
  787. nsIWebBrowserChrome::CHROME_DEPENDENT;
  788. }
  789. // Make sure to not create modal windows if our parent is invisible and
  790. // isn't a chrome window. Otherwise we can end up in a bizarre situation
  791. // where we can't shut down because an invisible window is open. If
  792. // someone tries to do this, throw.
  793. if (!hasChromeParent && (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL)) {
  794. nsCOMPtr<nsIBaseWindow> parentWindow(do_GetInterface(parentTreeOwner));
  795. nsCOMPtr<nsIWidget> parentWidget;
  796. if (parentWindow) {
  797. parentWindow->GetMainWidget(getter_AddRefs(parentWidget));
  798. }
  799. // NOTE: the logic for this visibility check is duplicated in
  800. // nsIDOMWindowUtils::isParentWindowMainWidgetVisible - if we change
  801. // how a window is determined "visible" in this context then we should
  802. // also adjust that attribute and/or any consumers of it...
  803. if (parentWidget && !parentWidget->IsVisible()) {
  804. return NS_ERROR_NOT_AVAILABLE;
  805. }
  806. }
  807. NS_ASSERTION(mWindowCreator,
  808. "attempted to open a new window with no WindowCreator");
  809. rv = NS_ERROR_FAILURE;
  810. if (mWindowCreator) {
  811. nsCOMPtr<nsIWebBrowserChrome> newChrome;
  812. nsCOMPtr<nsPIDOMWindowInner> parentTopInnerWindow;
  813. if (parentWindow) {
  814. nsCOMPtr<nsPIDOMWindowOuter> parentTopWindow = parentWindow->GetTop();
  815. if (parentTopWindow) {
  816. parentTopInnerWindow = parentTopWindow->GetCurrentInnerWindow();
  817. }
  818. }
  819. if (parentTopInnerWindow) {
  820. parentTopInnerWindow->Suspend();
  821. }
  822. /* If the window creator is an nsIWindowCreator2, we can give it
  823. some hints. The only hint at this time is whether the opening window
  824. is in a situation that's likely to mean this is an unrequested
  825. popup window we're creating. However we're not completely honest:
  826. we clear that indicator if the opener is chrome, so that the
  827. downstream consumer can treat the indicator to mean simply
  828. that the new window is subject to popup control. */
  829. nsCOMPtr<nsIWindowCreator2> windowCreator2(
  830. do_QueryInterface(mWindowCreator));
  831. if (windowCreator2) {
  832. uint32_t contextFlags = 0;
  833. bool popupConditions = false;
  834. // is the parent under popup conditions?
  835. if (parentWindow) {
  836. popupConditions = parentWindow->IsLoadingOrRunningTimeout();
  837. }
  838. // chrome is always allowed, so clear the flag if the opener is chrome
  839. if (popupConditions) {
  840. popupConditions = !isCallerChrome;
  841. }
  842. if (popupConditions) {
  843. contextFlags |=
  844. nsIWindowCreator2::PARENT_IS_LOADING_OR_RUNNING_TIMEOUT;
  845. }
  846. mozIDOMWindowProxy* openerWindow = aForceNoOpener ? nullptr : aParent;
  847. rv = CreateChromeWindow(features, parentChrome, chromeFlags, contextFlags,
  848. nullptr, openerWindow, getter_AddRefs(newChrome));
  849. } else {
  850. rv = mWindowCreator->CreateChromeWindow(parentChrome, chromeFlags,
  851. getter_AddRefs(newChrome));
  852. }
  853. if (parentTopInnerWindow) {
  854. parentTopInnerWindow->Resume();
  855. }
  856. if (newChrome) {
  857. nsCOMPtr<nsIXULWindow> xulWin = do_GetInterface(newChrome);
  858. if (xulWin) {
  859. nsCOMPtr<nsIXULBrowserWindow> xulBrowserWin;
  860. xulWin->GetXULBrowserWindow(getter_AddRefs(xulBrowserWin));
  861. if (xulBrowserWin) {
  862. nsPIDOMWindowOuter* openerWindow = aForceNoOpener ? nullptr : parentWindow.get();
  863. xulBrowserWin->ForceInitialBrowserNonRemote(openerWindow);
  864. }
  865. }
  866. /* It might be a chrome nsXULWindow, in which case it won't have
  867. an nsIDOMWindow (primary content shell). But in that case, it'll
  868. be able to hand over an nsIDocShellTreeItem directly. */
  869. nsCOMPtr<nsPIDOMWindowOuter> newWindow(do_GetInterface(newChrome));
  870. if (newWindow) {
  871. GetWindowTreeItem(newWindow, getter_AddRefs(newDocShellItem));
  872. }
  873. if (!newDocShellItem) {
  874. newDocShellItem = do_GetInterface(newChrome);
  875. }
  876. if (!newDocShellItem) {
  877. rv = NS_ERROR_FAILURE;
  878. }
  879. }
  880. }
  881. }
  882. // better have a window to use by this point
  883. if (!newDocShellItem) {
  884. return rv;
  885. }
  886. nsCOMPtr<nsIDocShell> newDocShell(do_QueryInterface(newDocShellItem));
  887. NS_ENSURE_TRUE(newDocShell, NS_ERROR_UNEXPECTED);
  888. // If our parent is sandboxed, set it as the one permitted sandboxed navigator
  889. // on the new window we're opening.
  890. if (activeDocsSandboxFlags && parentWindow) {
  891. newDocShell->SetOnePermittedSandboxedNavigator(
  892. parentWindow->GetDocShell());
  893. }
  894. // Copy sandbox flags to the new window if activeDocsSandboxFlags says to do
  895. // so. Note that it's only nonzero if the window is new, so clobbering
  896. // sandbox flags on the window makes sense in that case.
  897. if (activeDocsSandboxFlags &
  898. SANDBOX_PROPAGATES_TO_AUXILIARY_BROWSING_CONTEXTS) {
  899. newDocShell->SetSandboxFlags(activeDocsSandboxFlags);
  900. }
  901. rv = ReadyOpenedDocShellItem(newDocShellItem, parentWindow, windowIsNew,
  902. aForceNoOpener, aResult);
  903. if (NS_FAILED(rv)) {
  904. return rv;
  905. }
  906. if (isNewToplevelWindow) {
  907. nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
  908. newDocShellItem->GetTreeOwner(getter_AddRefs(newTreeOwner));
  909. MaybeDisablePersistence(features, newTreeOwner);
  910. }
  911. if (aDialog && aArgv) {
  912. // Set the args on the new window.
  913. nsCOMPtr<nsPIDOMWindowOuter> piwin(do_QueryInterface(*aResult));
  914. NS_ENSURE_TRUE(piwin, NS_ERROR_UNEXPECTED);
  915. rv = piwin->SetArguments(aArgv);
  916. NS_ENSURE_SUCCESS(rv, rv);
  917. }
  918. /* allow a window that we found by name to keep its name (important for cases
  919. like _self where the given name is different (and invalid)). Also, _blank
  920. is not a window name. */
  921. if (windowNeedsName) {
  922. if (nameSpecified && !name.LowerCaseEqualsLiteral("_blank")) {
  923. newDocShellItem->SetName(name);
  924. } else {
  925. newDocShellItem->SetName(EmptyString());
  926. }
  927. }
  928. // Now we have to set the right opener principal on the new window. Note
  929. // that we have to do this _before_ starting any URI loads, thanks to the
  930. // sync nature of javascript: loads.
  931. //
  932. // Note: The check for the current JSContext isn't necessarily sensical.
  933. // It's just designed to preserve old semantics during a mass-conversion
  934. // patch.
  935. nsCOMPtr<nsIPrincipal> subjectPrincipal =
  936. nsContentUtils::GetCurrentJSContext() ? nsContentUtils::SubjectPrincipal() :
  937. nullptr;
  938. bool isPrivateBrowsingWindow = false;
  939. if (windowIsNew) {
  940. auto* docShell = static_cast<nsDocShell*>(newDocShell.get());
  941. // If this is not a chrome docShell, we apply originAttributes from the
  942. // subjectPrincipal unless if it's an expanded or system principal.
  943. if (subjectPrincipal &&
  944. !nsContentUtils::IsSystemOrExpandedPrincipal(subjectPrincipal) &&
  945. docShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
  946. DocShellOriginAttributes attrs;
  947. attrs.InheritFromDocToChildDocShell(BasePrincipal::Cast(subjectPrincipal)->OriginAttributesRef());
  948. isPrivateBrowsingWindow = !!attrs.mPrivateBrowsingId;
  949. docShell->SetOriginAttributes(attrs);
  950. } else {
  951. nsCOMPtr<nsIDocShellTreeItem> parentItem;
  952. GetWindowTreeItem(aParent, getter_AddRefs(parentItem));
  953. nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(parentItem);
  954. if (parentContext) {
  955. isPrivateBrowsingWindow = parentContext->UsePrivateBrowsing();
  956. }
  957. }
  958. bool autoPrivateBrowsing =
  959. Preferences::GetBool("browser.privatebrowsing.autostart");
  960. if (!autoPrivateBrowsing &&
  961. (chromeFlags & nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW)) {
  962. isPrivateBrowsingWindow = false;
  963. } else if (autoPrivateBrowsing ||
  964. (chromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW)) {
  965. isPrivateBrowsingWindow = true;
  966. }
  967. // Now set the opener principal on the new window. Note that we need to do
  968. // this no matter whether we were opened from JS; if there is nothing on
  969. // the JS stack, just use the principal of our parent window. In those
  970. // cases we do _not_ set the parent window principal as the owner of the
  971. // load--since we really don't know who the owner is, just leave it null.
  972. nsCOMPtr<nsPIDOMWindowOuter> newWindow = do_QueryInterface(*aResult);
  973. NS_ASSERTION(newWindow == newDocShell->GetWindow(), "Different windows??");
  974. // The principal of the initial about:blank document gets set up in
  975. // nsWindowWatcher::AddWindow. Make sure to call it. In the common case
  976. // this call already happened when the window was created, but
  977. // SetInitialPrincipalToSubject is safe to call multiple times.
  978. if (newWindow) {
  979. newWindow->SetInitialPrincipalToSubject();
  980. if (aIsPopupSpam) {
  981. nsGlobalWindow* globalWin = nsGlobalWindow::Cast(newWindow);
  982. MOZ_ASSERT(!globalWin->IsPopupSpamWindow(),
  983. "Who marked it as popup spam already???");
  984. if (!globalWin->IsPopupSpamWindow()) { // Make sure we don't mess up our
  985. // counter even if the above
  986. // assert fails.
  987. globalWin->SetIsPopupSpamWindow(true);
  988. }
  989. }
  990. }
  991. }
  992. // We rely on CalculateChromeFlags to decide whether remote (out-of-process)
  993. // tabs should be used.
  994. bool isRemoteWindow =
  995. !!(chromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW);
  996. if (isNewToplevelWindow) {
  997. nsCOMPtr<nsIDocShellTreeItem> childRoot;
  998. newDocShellItem->GetRootTreeItem(getter_AddRefs(childRoot));
  999. nsCOMPtr<nsILoadContext> childContext = do_QueryInterface(childRoot);
  1000. if (childContext) {
  1001. childContext->SetPrivateBrowsing(isPrivateBrowsingWindow);
  1002. childContext->SetRemoteTabs(isRemoteWindow);
  1003. }
  1004. } else if (windowIsNew) {
  1005. nsCOMPtr<nsILoadContext> childContext = do_QueryInterface(newDocShellItem);
  1006. if (childContext) {
  1007. childContext->SetPrivateBrowsing(isPrivateBrowsingWindow);
  1008. childContext->SetRemoteTabs(isRemoteWindow);
  1009. }
  1010. }
  1011. nsCOMPtr<nsIDocShellLoadInfo> loadInfo = aLoadInfo;
  1012. if (uriToLoad && aNavigate && !loadInfo) {
  1013. newDocShell->CreateLoadInfo(getter_AddRefs(loadInfo));
  1014. NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE);
  1015. if (subjectPrincipal) {
  1016. loadInfo->SetTriggeringPrincipal(subjectPrincipal);
  1017. }
  1018. /* use the URL from the *extant* document, if any. The usual accessor
  1019. GetDocument will synchronously create an about:blank document if
  1020. it has no better answer, and we only care about a real document.
  1021. Also using GetDocument to force document creation seems to
  1022. screw up focus in the hidden window; see bug 36016.
  1023. */
  1024. nsCOMPtr<nsIDocument> doc = GetEntryDocument();
  1025. if (!doc && parentWindow) {
  1026. doc = parentWindow->GetExtantDoc();
  1027. }
  1028. if (doc) {
  1029. // Set the referrer
  1030. loadInfo->SetReferrer(doc->GetDocumentURI());
  1031. loadInfo->SetReferrerPolicy(doc->GetReferrerPolicy());
  1032. }
  1033. }
  1034. if (isNewToplevelWindow) {
  1035. // Notify observers that the window is open and ready.
  1036. // The window has not yet started to load a document.
  1037. nsCOMPtr<nsIObserverService> obsSvc =
  1038. mozilla::services::GetObserverService();
  1039. if (obsSvc) {
  1040. obsSvc->NotifyObservers(*aResult, "toplevel-window-ready", nullptr);
  1041. }
  1042. }
  1043. // Before loading the URI we want to be 100% sure that we use the correct
  1044. // userContextId.
  1045. MOZ_ASSERT(CheckUserContextCompatibility(newDocShell));
  1046. if (uriToLoad && aNavigate) {
  1047. newDocShell->LoadURI(
  1048. uriToLoad,
  1049. loadInfo,
  1050. windowIsNew ?
  1051. static_cast<uint32_t>(nsIWebNavigation::LOAD_FLAGS_FIRST_LOAD) :
  1052. static_cast<uint32_t>(nsIWebNavigation::LOAD_FLAGS_NONE),
  1053. true);
  1054. }
  1055. // Copy the current session storage for the current domain.
  1056. if (subjectPrincipal && parentDocShell) {
  1057. nsCOMPtr<nsIDOMStorageManager> parentStorageManager =
  1058. do_QueryInterface(parentDocShell);
  1059. nsCOMPtr<nsIDOMStorageManager> newStorageManager =
  1060. do_QueryInterface(newDocShell);
  1061. if (parentStorageManager && newStorageManager) {
  1062. nsCOMPtr<nsIDOMStorage> storage;
  1063. nsCOMPtr<nsPIDOMWindowInner> pInnerWin = parentWindow->GetCurrentInnerWindow();
  1064. parentStorageManager->GetStorage(pInnerWin, subjectPrincipal,
  1065. isPrivateBrowsingWindow,
  1066. getter_AddRefs(storage));
  1067. if (storage) {
  1068. newStorageManager->CloneStorage(storage);
  1069. }
  1070. }
  1071. }
  1072. if (isNewToplevelWindow) {
  1073. nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
  1074. newDocShellItem->GetTreeOwner(getter_AddRefs(newTreeOwner));
  1075. SizeOpenedWindow(newTreeOwner, aParent, isCallerChrome, sizeSpec);
  1076. }
  1077. if (windowIsModal) {
  1078. nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
  1079. newDocShellItem->GetTreeOwner(getter_AddRefs(newTreeOwner));
  1080. nsCOMPtr<nsIWebBrowserChrome> newChrome(do_GetInterface(newTreeOwner));
  1081. // Throw an exception here if no web browser chrome is available,
  1082. // we need that to show a modal window.
  1083. NS_ENSURE_TRUE(newChrome, NS_ERROR_NOT_AVAILABLE);
  1084. // Dispatch dialog events etc, but we only want to do that if
  1085. // we're opening a modal content window (the helper classes are
  1086. // no-ops if given no window), for chrome dialogs we don't want to
  1087. // do any of that (it's done elsewhere for us).
  1088. // Make sure we maintain the state on an outer window, because
  1089. // that's where it lives; inner windows assert if you try to
  1090. // maintain the state on them.
  1091. nsAutoWindowStateHelper windowStateHelper(
  1092. parentWindow ? parentWindow->GetOuterWindow() : nullptr);
  1093. if (!windowStateHelper.DefaultEnabled()) {
  1094. // Default to cancel not opening the modal window.
  1095. NS_RELEASE(*aResult);
  1096. return NS_OK;
  1097. }
  1098. bool isAppModal = false;
  1099. nsCOMPtr<nsIBaseWindow> parentWindow(do_GetInterface(newTreeOwner));
  1100. nsCOMPtr<nsIWidget> parentWidget;
  1101. if (parentWindow) {
  1102. parentWindow->GetMainWidget(getter_AddRefs(parentWidget));
  1103. if (parentWidget) {
  1104. isAppModal = parentWidget->IsRunningAppModal();
  1105. }
  1106. }
  1107. if (parentWidget &&
  1108. ((!newWindowShouldBeModal && parentIsModal) || isAppModal)) {
  1109. parentWidget->SetFakeModal(true);
  1110. } else {
  1111. // Reset popup state while opening a modal dialog, and firing
  1112. // events about the dialog, to prevent the current state from
  1113. // being active the whole time a modal dialog is open.
  1114. nsAutoPopupStatePusher popupStatePusher(openAbused);
  1115. newChrome->ShowAsModal();
  1116. }
  1117. }
  1118. // If a website opens a popup exit DOM fullscreen
  1119. if (windowIsNew && aCalledFromJS && !hasChromeParent && !isCallerChrome &&
  1120. parentWindow) {
  1121. nsIDocument::AsyncExitFullscreen(parentWindow->GetDoc());
  1122. }
  1123. if (aForceNoOpener && windowIsNew) {
  1124. NS_RELEASE(*aResult);
  1125. }
  1126. return NS_OK;
  1127. }
  1128. NS_IMETHODIMP
  1129. nsWindowWatcher::RegisterNotification(nsIObserver* aObserver)
  1130. {
  1131. // just a convenience method; it delegates to nsIObserverService
  1132. if (!aObserver) {
  1133. return NS_ERROR_INVALID_ARG;
  1134. }
  1135. nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  1136. if (!os) {
  1137. return NS_ERROR_FAILURE;
  1138. }
  1139. nsresult rv = os->AddObserver(aObserver, "domwindowopened", false);
  1140. if (NS_SUCCEEDED(rv)) {
  1141. rv = os->AddObserver(aObserver, "domwindowclosed", false);
  1142. }
  1143. return rv;
  1144. }
  1145. NS_IMETHODIMP
  1146. nsWindowWatcher::UnregisterNotification(nsIObserver* aObserver)
  1147. {
  1148. // just a convenience method; it delegates to nsIObserverService
  1149. if (!aObserver) {
  1150. return NS_ERROR_INVALID_ARG;
  1151. }
  1152. nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  1153. if (!os) {
  1154. return NS_ERROR_FAILURE;
  1155. }
  1156. os->RemoveObserver(aObserver, "domwindowopened");
  1157. os->RemoveObserver(aObserver, "domwindowclosed");
  1158. return NS_OK;
  1159. }
  1160. NS_IMETHODIMP
  1161. nsWindowWatcher::GetWindowEnumerator(nsISimpleEnumerator** aResult)
  1162. {
  1163. if (!aResult) {
  1164. return NS_ERROR_INVALID_ARG;
  1165. }
  1166. MutexAutoLock lock(mListLock);
  1167. nsWatcherWindowEnumerator* enumerator = new nsWatcherWindowEnumerator(this);
  1168. if (enumerator) {
  1169. return CallQueryInterface(enumerator, aResult);
  1170. }
  1171. return NS_ERROR_OUT_OF_MEMORY;
  1172. }
  1173. NS_IMETHODIMP
  1174. nsWindowWatcher::GetNewPrompter(mozIDOMWindowProxy* aParent, nsIPrompt** aResult)
  1175. {
  1176. // This is for backwards compat only. Callers should just use the prompt
  1177. // service directly.
  1178. nsresult rv;
  1179. nsCOMPtr<nsIPromptFactory> factory =
  1180. do_GetService("@mozilla.org/prompter;1", &rv);
  1181. NS_ENSURE_SUCCESS(rv, rv);
  1182. return factory->GetPrompt(aParent, NS_GET_IID(nsIPrompt),
  1183. reinterpret_cast<void**>(aResult));
  1184. }
  1185. NS_IMETHODIMP
  1186. nsWindowWatcher::GetNewAuthPrompter(mozIDOMWindowProxy* aParent,
  1187. nsIAuthPrompt** aResult)
  1188. {
  1189. // This is for backwards compat only. Callers should just use the prompt
  1190. // service directly.
  1191. nsresult rv;
  1192. nsCOMPtr<nsIPromptFactory> factory =
  1193. do_GetService("@mozilla.org/prompter;1", &rv);
  1194. NS_ENSURE_SUCCESS(rv, rv);
  1195. return factory->GetPrompt(aParent, NS_GET_IID(nsIAuthPrompt),
  1196. reinterpret_cast<void**>(aResult));
  1197. }
  1198. NS_IMETHODIMP
  1199. nsWindowWatcher::GetPrompt(mozIDOMWindowProxy* aParent, const nsIID& aIID,
  1200. void** aResult)
  1201. {
  1202. // This is for backwards compat only. Callers should just use the prompt
  1203. // service directly.
  1204. nsresult rv;
  1205. nsCOMPtr<nsIPromptFactory> factory =
  1206. do_GetService("@mozilla.org/prompter;1", &rv);
  1207. NS_ENSURE_SUCCESS(rv, rv);
  1208. rv = factory->GetPrompt(aParent, aIID, aResult);
  1209. // Allow for an embedding implementation to not support nsIAuthPrompt2.
  1210. if (rv == NS_NOINTERFACE && aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
  1211. nsCOMPtr<nsIAuthPrompt> oldPrompt;
  1212. rv = factory->GetPrompt(
  1213. aParent, NS_GET_IID(nsIAuthPrompt), getter_AddRefs(oldPrompt));
  1214. NS_ENSURE_SUCCESS(rv, rv);
  1215. NS_WrapAuthPrompt(oldPrompt, reinterpret_cast<nsIAuthPrompt2**>(aResult));
  1216. if (!*aResult) {
  1217. rv = NS_ERROR_NOT_AVAILABLE;
  1218. }
  1219. }
  1220. return rv;
  1221. }
  1222. NS_IMETHODIMP
  1223. nsWindowWatcher::SetWindowCreator(nsIWindowCreator* aCreator)
  1224. {
  1225. mWindowCreator = aCreator;
  1226. return NS_OK;
  1227. }
  1228. NS_IMETHODIMP
  1229. nsWindowWatcher::HasWindowCreator(bool* aResult)
  1230. {
  1231. *aResult = mWindowCreator;
  1232. return NS_OK;
  1233. }
  1234. NS_IMETHODIMP
  1235. nsWindowWatcher::GetActiveWindow(mozIDOMWindowProxy** aActiveWindow)
  1236. {
  1237. *aActiveWindow = nullptr;
  1238. nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
  1239. if (fm) {
  1240. return fm->GetActiveWindow(aActiveWindow);
  1241. }
  1242. return NS_OK;
  1243. }
  1244. NS_IMETHODIMP
  1245. nsWindowWatcher::SetActiveWindow(mozIDOMWindowProxy* aActiveWindow)
  1246. {
  1247. nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
  1248. if (fm) {
  1249. return fm->SetActiveWindow(aActiveWindow);
  1250. }
  1251. return NS_OK;
  1252. }
  1253. NS_IMETHODIMP
  1254. nsWindowWatcher::AddWindow(mozIDOMWindowProxy* aWindow, nsIWebBrowserChrome* aChrome)
  1255. {
  1256. if (!aWindow) {
  1257. return NS_ERROR_INVALID_ARG;
  1258. }
  1259. #ifdef DEBUG
  1260. {
  1261. nsCOMPtr<nsPIDOMWindowOuter> win(do_QueryInterface(aWindow));
  1262. NS_ASSERTION(win->IsOuterWindow(),
  1263. "Uh, the active window must be an outer window!");
  1264. }
  1265. #endif
  1266. {
  1267. nsWatcherWindowEntry* info;
  1268. MutexAutoLock lock(mListLock);
  1269. // if we already have an entry for this window, adjust
  1270. // its chrome mapping and return
  1271. info = FindWindowEntry(aWindow);
  1272. if (info) {
  1273. nsCOMPtr<nsISupportsWeakReference> supportsweak(
  1274. do_QueryInterface(aChrome));
  1275. if (supportsweak) {
  1276. supportsweak->GetWeakReference(getter_AddRefs(info->mChromeWeak));
  1277. } else {
  1278. info->mChrome = aChrome;
  1279. info->mChromeWeak = nullptr;
  1280. }
  1281. return NS_OK;
  1282. }
  1283. // create a window info struct and add it to the list of windows
  1284. info = new nsWatcherWindowEntry(aWindow, aChrome);
  1285. if (!info) {
  1286. return NS_ERROR_OUT_OF_MEMORY;
  1287. }
  1288. if (mOldestWindow) {
  1289. info->InsertAfter(mOldestWindow->mOlder);
  1290. } else {
  1291. mOldestWindow = info;
  1292. }
  1293. } // leave the mListLock
  1294. // a window being added to us signifies a newly opened window.
  1295. // send notifications.
  1296. nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  1297. if (!os) {
  1298. return NS_ERROR_FAILURE;
  1299. }
  1300. nsCOMPtr<nsISupports> domwin(do_QueryInterface(aWindow));
  1301. return os->NotifyObservers(domwin, "domwindowopened", 0);
  1302. }
  1303. NS_IMETHODIMP
  1304. nsWindowWatcher::RemoveWindow(mozIDOMWindowProxy* aWindow)
  1305. {
  1306. // find the corresponding nsWatcherWindowEntry, remove it
  1307. if (!aWindow) {
  1308. return NS_ERROR_INVALID_ARG;
  1309. }
  1310. nsWatcherWindowEntry* info = FindWindowEntry(aWindow);
  1311. if (info) {
  1312. RemoveWindow(info);
  1313. return NS_OK;
  1314. }
  1315. NS_WARNING("requested removal of nonexistent window");
  1316. return NS_ERROR_INVALID_ARG;
  1317. }
  1318. nsWatcherWindowEntry*
  1319. nsWindowWatcher::FindWindowEntry(mozIDOMWindowProxy* aWindow)
  1320. {
  1321. // find the corresponding nsWatcherWindowEntry
  1322. nsWatcherWindowEntry* info;
  1323. nsWatcherWindowEntry* listEnd;
  1324. #ifdef USEWEAKREFS
  1325. nsresult rv;
  1326. bool found;
  1327. #endif
  1328. info = mOldestWindow;
  1329. listEnd = 0;
  1330. #ifdef USEWEAKREFS
  1331. rv = NS_OK;
  1332. found = false;
  1333. while (info != listEnd && NS_SUCCEEDED(rv)) {
  1334. nsCOMPtr<mozIDOMWindowProxy> infoWindow(do_QueryReferent(info->mWindow));
  1335. if (!infoWindow) { // clean up dangling reference, while we're here
  1336. rv = RemoveWindow(info);
  1337. } else if (infoWindow.get() == aWindow) {
  1338. return info;
  1339. }
  1340. info = info->mYounger;
  1341. listEnd = mOldestWindow;
  1342. }
  1343. return 0;
  1344. #else
  1345. while (info != listEnd) {
  1346. if (info->mWindow == aWindow) {
  1347. return info;
  1348. }
  1349. info = info->mYounger;
  1350. listEnd = mOldestWindow;
  1351. }
  1352. return 0;
  1353. #endif
  1354. }
  1355. nsresult
  1356. nsWindowWatcher::RemoveWindow(nsWatcherWindowEntry* aInfo)
  1357. {
  1358. uint32_t count = mEnumeratorList.Length();
  1359. {
  1360. // notify the enumerators
  1361. MutexAutoLock lock(mListLock);
  1362. for (uint32_t ctr = 0; ctr < count; ++ctr) {
  1363. mEnumeratorList[ctr]->WindowRemoved(aInfo);
  1364. }
  1365. // remove the element from the list
  1366. if (aInfo == mOldestWindow) {
  1367. mOldestWindow = aInfo->mYounger == mOldestWindow ? 0 : aInfo->mYounger;
  1368. }
  1369. aInfo->Unlink();
  1370. }
  1371. // a window being removed from us signifies a newly closed window.
  1372. // send notifications.
  1373. nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  1374. if (os) {
  1375. #ifdef USEWEAKREFS
  1376. nsCOMPtr<nsISupports> domwin(do_QueryReferent(aInfo->mWindow));
  1377. if (domwin) {
  1378. os->NotifyObservers(domwin, "domwindowclosed", 0);
  1379. }
  1380. // else bummer. since the window is gone, there's nothing to notify with.
  1381. #else
  1382. nsCOMPtr<nsISupports> domwin(do_QueryInterface(aInfo->mWindow));
  1383. os->NotifyObservers(domwin, "domwindowclosed", 0);
  1384. #endif
  1385. }
  1386. delete aInfo;
  1387. return NS_OK;
  1388. }
  1389. NS_IMETHODIMP
  1390. nsWindowWatcher::GetChromeForWindow(mozIDOMWindowProxy* aWindow,
  1391. nsIWebBrowserChrome** aResult)
  1392. {
  1393. if (!aWindow || !aResult) {
  1394. return NS_ERROR_INVALID_ARG;
  1395. }
  1396. *aResult = 0;
  1397. MutexAutoLock lock(mListLock);
  1398. nsWatcherWindowEntry* info = FindWindowEntry(aWindow);
  1399. if (info) {
  1400. if (info->mChromeWeak) {
  1401. return info->mChromeWeak->QueryReferent(
  1402. NS_GET_IID(nsIWebBrowserChrome), reinterpret_cast<void**>(aResult));
  1403. }
  1404. *aResult = info->mChrome;
  1405. NS_IF_ADDREF(*aResult);
  1406. }
  1407. return NS_OK;
  1408. }
  1409. NS_IMETHODIMP
  1410. nsWindowWatcher::GetWindowByName(const nsAString& aTargetName,
  1411. mozIDOMWindowProxy* aCurrentWindow,
  1412. mozIDOMWindowProxy** aResult)
  1413. {
  1414. if (!aResult) {
  1415. return NS_ERROR_INVALID_ARG;
  1416. }
  1417. *aResult = nullptr;
  1418. nsPIDOMWindowOuter* currentWindow =
  1419. aCurrentWindow ? nsPIDOMWindowOuter::From(aCurrentWindow) : nullptr;
  1420. nsCOMPtr<nsIDocShellTreeItem> treeItem;
  1421. nsCOMPtr<nsIDocShellTreeItem> startItem;
  1422. GetWindowTreeItem(currentWindow, getter_AddRefs(startItem));
  1423. if (startItem) {
  1424. // Note: original requestor is null here, per idl comments
  1425. startItem->FindItemWithName(aTargetName, nullptr, nullptr,
  1426. getter_AddRefs(treeItem));
  1427. } else {
  1428. // Note: original requestor is null here, per idl comments
  1429. FindItemWithName(aTargetName, nullptr, nullptr, getter_AddRefs(treeItem));
  1430. }
  1431. if (treeItem) {
  1432. nsCOMPtr<nsPIDOMWindowOuter> domWindow = treeItem->GetWindow();
  1433. domWindow.forget(aResult);
  1434. }
  1435. return NS_OK;
  1436. }
  1437. bool
  1438. nsWindowWatcher::AddEnumerator(nsWatcherWindowEnumerator* aEnumerator)
  1439. {
  1440. // (requires a lock; assumes it's called by someone holding the lock)
  1441. return mEnumeratorList.AppendElement(aEnumerator) != nullptr;
  1442. }
  1443. bool
  1444. nsWindowWatcher::RemoveEnumerator(nsWatcherWindowEnumerator* aEnumerator)
  1445. {
  1446. // (requires a lock; assumes it's called by someone holding the lock)
  1447. return mEnumeratorList.RemoveElement(aEnumerator);
  1448. }
  1449. nsresult
  1450. nsWindowWatcher::URIfromURL(const char* aURL,
  1451. mozIDOMWindowProxy* aParent,
  1452. nsIURI** aURI)
  1453. {
  1454. // Build the URI relative to the entry global.
  1455. nsCOMPtr<nsPIDOMWindowInner> baseWindow = do_QueryInterface(GetEntryGlobal());
  1456. // failing that, build it relative to the parent window, if possible
  1457. if (!baseWindow && aParent) {
  1458. baseWindow = nsPIDOMWindowOuter::From(aParent)->GetCurrentInnerWindow();
  1459. }
  1460. // failing that, use the given URL unmodified. It had better not be relative.
  1461. nsIURI* baseURI = nullptr;
  1462. // get baseWindow's document URI
  1463. if (baseWindow) {
  1464. if (nsIDocument* doc = baseWindow->GetDoc()) {
  1465. baseURI = doc->GetDocBaseURI();
  1466. }
  1467. }
  1468. // build and return the absolute URI
  1469. return NS_NewURI(aURI, aURL, baseURI);
  1470. }
  1471. #define NS_CALCULATE_CHROME_FLAG_FOR(feature, flag) \
  1472. prefBranch->GetBoolPref(feature, &forceEnable); \
  1473. if (forceEnable && !aDialog && !aHasChromeParent && !aChromeURL) { \
  1474. chromeFlags |= flag; \
  1475. } else { \
  1476. chromeFlags |= \
  1477. WinHasOption(aFeatures, feature, 0, &presenceFlag) ? flag : 0; \
  1478. }
  1479. // static
  1480. uint32_t
  1481. nsWindowWatcher::CalculateChromeFlagsHelper(uint32_t aInitialFlags,
  1482. const nsACString& aFeatures,
  1483. bool& presenceFlag,
  1484. bool aDialog,
  1485. bool aHasChromeParent,
  1486. bool aChromeURL)
  1487. {
  1488. uint32_t chromeFlags = aInitialFlags;
  1489. nsresult rv;
  1490. nsCOMPtr<nsIPrefBranch> prefBranch;
  1491. nsCOMPtr<nsIPrefService> prefs =
  1492. do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
  1493. NS_ENSURE_SUCCESS(rv, nsIWebBrowserChrome::CHROME_DEFAULT);
  1494. rv = prefs->GetBranch("dom.disable_window_open_feature.",
  1495. getter_AddRefs(prefBranch));
  1496. NS_ENSURE_SUCCESS(rv, nsIWebBrowserChrome::CHROME_DEFAULT);
  1497. // NS_CALCULATE_CHROME_FLAG_FOR requires aFeatures, forceEnable, aDialog
  1498. // aHasChromeParent, aChromeURL, presenceFlag and chromeFlags to be in
  1499. // scope.
  1500. bool forceEnable = false;
  1501. NS_CALCULATE_CHROME_FLAG_FOR("titlebar",
  1502. nsIWebBrowserChrome::CHROME_TITLEBAR);
  1503. NS_CALCULATE_CHROME_FLAG_FOR("close",
  1504. nsIWebBrowserChrome::CHROME_WINDOW_CLOSE);
  1505. NS_CALCULATE_CHROME_FLAG_FOR("toolbar",
  1506. nsIWebBrowserChrome::CHROME_TOOLBAR);
  1507. NS_CALCULATE_CHROME_FLAG_FOR("location",
  1508. nsIWebBrowserChrome::CHROME_LOCATIONBAR);
  1509. NS_CALCULATE_CHROME_FLAG_FOR("personalbar",
  1510. nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR);
  1511. NS_CALCULATE_CHROME_FLAG_FOR("status",
  1512. nsIWebBrowserChrome::CHROME_STATUSBAR);
  1513. NS_CALCULATE_CHROME_FLAG_FOR("menubar",
  1514. nsIWebBrowserChrome::CHROME_MENUBAR);
  1515. NS_CALCULATE_CHROME_FLAG_FOR("resizable",
  1516. nsIWebBrowserChrome::CHROME_WINDOW_RESIZE);
  1517. NS_CALCULATE_CHROME_FLAG_FOR("minimizable",
  1518. nsIWebBrowserChrome::CHROME_WINDOW_MIN);
  1519. // default scrollbar to "on," unless explicitly turned off
  1520. if (WinHasOption(aFeatures, "scrollbars", 1, &presenceFlag) || !presenceFlag) {
  1521. chromeFlags |= nsIWebBrowserChrome::CHROME_SCROLLBARS;
  1522. }
  1523. return chromeFlags;
  1524. }
  1525. // static
  1526. uint32_t
  1527. nsWindowWatcher::EnsureFlagsSafeForContent(uint32_t aChromeFlags,
  1528. bool aChromeURL)
  1529. {
  1530. aChromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR;
  1531. aChromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE;
  1532. aChromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_LOWERED;
  1533. aChromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_RAISED;
  1534. aChromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_POPUP;
  1535. /* Untrusted script is allowed to pose modal windows with a chrome
  1536. scheme. This check could stand to be better. But it effectively
  1537. prevents untrusted script from opening modal windows in general
  1538. while still allowing alerts and the like. */
  1539. if (!aChromeURL) {
  1540. aChromeFlags &= ~(nsIWebBrowserChrome::CHROME_MODAL |
  1541. nsIWebBrowserChrome::CHROME_OPENAS_CHROME);
  1542. }
  1543. if (!(aChromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME)) {
  1544. aChromeFlags &= ~nsIWebBrowserChrome::CHROME_DEPENDENT;
  1545. }
  1546. return aChromeFlags;
  1547. }
  1548. /**
  1549. * Calculate the chrome bitmask from a string list of features requested
  1550. * from a child process. Feature strings that are restricted to the parent
  1551. * process are ignored here.
  1552. * @param aFeatures a string containing a list of named features
  1553. * @return the chrome bitmask
  1554. */
  1555. // static
  1556. uint32_t
  1557. nsWindowWatcher::CalculateChromeFlagsForChild(const nsACString& aFeatures)
  1558. {
  1559. if (aFeatures.IsVoid()) {
  1560. return nsIWebBrowserChrome::CHROME_ALL;
  1561. }
  1562. bool presenceFlag = false;
  1563. uint32_t chromeFlags = CalculateChromeFlagsHelper(
  1564. nsIWebBrowserChrome::CHROME_WINDOW_BORDERS, aFeatures, presenceFlag);
  1565. return EnsureFlagsSafeForContent(chromeFlags);
  1566. }
  1567. /**
  1568. * Calculate the chrome bitmask from a string list of features for a new
  1569. * privileged window.
  1570. * @param aParent the opener window
  1571. * @param aFeatures a string containing a list of named chrome features
  1572. * @param aDialog affects the assumptions made about unnamed features
  1573. * @param aChromeURL true if the window is being sent to a chrome:// URL
  1574. * @param aHasChromeParent true if the parent window is privileged
  1575. * @param aCalledFromJS true if the window open request came from script.
  1576. * @return the chrome bitmask
  1577. */
  1578. // static
  1579. uint32_t
  1580. nsWindowWatcher::CalculateChromeFlagsForParent(mozIDOMWindowProxy* aParent,
  1581. const nsACString& aFeatures,
  1582. bool aDialog,
  1583. bool aChromeURL,
  1584. bool aHasChromeParent,
  1585. bool aCalledFromJS)
  1586. {
  1587. MOZ_ASSERT(XRE_IsParentProcess());
  1588. MOZ_ASSERT(nsContentUtils::LegacyIsCallerChromeOrNativeCode());
  1589. uint32_t chromeFlags = 0;
  1590. // The features string is made void by OpenWindowInternal
  1591. // if nullptr was originally passed as the features string.
  1592. if (aFeatures.IsVoid()) {
  1593. chromeFlags = nsIWebBrowserChrome::CHROME_ALL;
  1594. if (aDialog) {
  1595. chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
  1596. nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
  1597. }
  1598. } else {
  1599. chromeFlags = nsIWebBrowserChrome::CHROME_WINDOW_BORDERS;
  1600. }
  1601. /* This function has become complicated since browser windows and
  1602. dialogs diverged. The difference is, browser windows assume all
  1603. chrome not explicitly mentioned is off, if the features string
  1604. is not null. Exceptions are some OS border chrome new with Mozilla.
  1605. Dialogs interpret a (mostly) empty features string to mean
  1606. "OS's choice," and also support an "all" flag explicitly disallowed
  1607. in the standards-compliant window.(normal)open. */
  1608. bool presenceFlag = false;
  1609. if (aDialog && WinHasOption(aFeatures, "all", 0, &presenceFlag)) {
  1610. chromeFlags = nsIWebBrowserChrome::CHROME_ALL;
  1611. }
  1612. /* Next, allow explicitly named options to override the initial settings */
  1613. chromeFlags = CalculateChromeFlagsHelper(chromeFlags, aFeatures, presenceFlag,
  1614. aDialog, aHasChromeParent, aChromeURL);
  1615. // Determine whether the window is a private browsing window
  1616. chromeFlags |= WinHasOption(aFeatures, "private", 0, &presenceFlag) ?
  1617. nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW : 0;
  1618. chromeFlags |= WinHasOption(aFeatures, "non-private", 0, &presenceFlag) ?
  1619. nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW : 0;
  1620. // Determine whether the window should have remote tabs.
  1621. bool remote = BrowserTabsRemoteAutostart();
  1622. if (remote) {
  1623. remote = !WinHasOption(aFeatures, "non-remote", 0, &presenceFlag);
  1624. } else {
  1625. remote = WinHasOption(aFeatures, "remote", 0, &presenceFlag);
  1626. }
  1627. if (remote) {
  1628. chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
  1629. }
  1630. chromeFlags |= WinHasOption(aFeatures, "popup", 0, &presenceFlag) ?
  1631. nsIWebBrowserChrome::CHROME_WINDOW_POPUP : 0;
  1632. /* OK.
  1633. Normal browser windows, in spite of a stated pattern of turning off
  1634. all chrome not mentioned explicitly, will want the new OS chrome (window
  1635. borders, titlebars, closebox) on, unless explicitly turned off.
  1636. Dialogs, on the other hand, take the absence of any explicit settings
  1637. to mean "OS' choice." */
  1638. // default titlebar and closebox to "on," if not mentioned at all
  1639. if (!(chromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_POPUP)) {
  1640. if (!PL_strcasestr(aFeatures.BeginReading(), "titlebar")) {
  1641. chromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR;
  1642. }
  1643. if (!PL_strcasestr(aFeatures.BeginReading(), "close")) {
  1644. chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE;
  1645. }
  1646. }
  1647. if (aDialog && !aFeatures.IsVoid() && !presenceFlag) {
  1648. chromeFlags = nsIWebBrowserChrome::CHROME_DEFAULT;
  1649. }
  1650. /* Finally, once all the above normal chrome has been divined, deal
  1651. with the features that are more operating hints than appearance
  1652. instructions. (Note modality implies dependence.) */
  1653. if (WinHasOption(aFeatures, "alwaysLowered", 0, nullptr) ||
  1654. WinHasOption(aFeatures, "z-lock", 0, nullptr)) {
  1655. chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_LOWERED;
  1656. } else if (WinHasOption(aFeatures, "alwaysRaised", 0, nullptr)) {
  1657. chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_RAISED;
  1658. }
  1659. chromeFlags |= WinHasOption(aFeatures, "macsuppressanimation", 0, nullptr) ?
  1660. nsIWebBrowserChrome::CHROME_MAC_SUPPRESS_ANIMATION : 0;
  1661. chromeFlags |= WinHasOption(aFeatures, "chrome", 0, nullptr) ?
  1662. nsIWebBrowserChrome::CHROME_OPENAS_CHROME : 0;
  1663. chromeFlags |= WinHasOption(aFeatures, "extrachrome", 0, nullptr) ?
  1664. nsIWebBrowserChrome::CHROME_EXTRA : 0;
  1665. chromeFlags |= WinHasOption(aFeatures, "centerscreen", 0, nullptr) ?
  1666. nsIWebBrowserChrome::CHROME_CENTER_SCREEN : 0;
  1667. chromeFlags |= WinHasOption(aFeatures, "dependent", 0, nullptr) ?
  1668. nsIWebBrowserChrome::CHROME_DEPENDENT : 0;
  1669. chromeFlags |= WinHasOption(aFeatures, "modal", 0, nullptr) ?
  1670. (nsIWebBrowserChrome::CHROME_MODAL | nsIWebBrowserChrome::CHROME_DEPENDENT) : 0;
  1671. /* On mobile we want to ignore the dialog window feature, since the mobile UI
  1672. does not provide any affordance for dialog windows. This does not interfere
  1673. with dialog windows created through openDialog. */
  1674. bool disableDialogFeature = false;
  1675. nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
  1676. branch->GetBoolPref("dom.disable_window_open_dialog_feature",
  1677. &disableDialogFeature);
  1678. if (!disableDialogFeature) {
  1679. chromeFlags |= WinHasOption(aFeatures, "dialog", 0, nullptr) ?
  1680. nsIWebBrowserChrome::CHROME_OPENAS_DIALOG : 0;
  1681. }
  1682. /* and dialogs need to have the last word. assume dialogs are dialogs,
  1683. and opened as chrome, unless explicitly told otherwise. */
  1684. if (aDialog) {
  1685. if (!PL_strcasestr(aFeatures.BeginReading(), "dialog")) {
  1686. chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG;
  1687. }
  1688. if (!PL_strcasestr(aFeatures.BeginReading(), "chrome")) {
  1689. chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
  1690. }
  1691. }
  1692. /* missing
  1693. chromeFlags->copy_history
  1694. */
  1695. // Check security state for use in determing window dimensions
  1696. if (!aHasChromeParent) {
  1697. chromeFlags = EnsureFlagsSafeForContent(chromeFlags, aChromeURL);
  1698. }
  1699. // Disable CHROME_OPENAS_DIALOG if the window is inside <iframe mozbrowser>.
  1700. // It's up to the embedder to interpret what dialog=1 means.
  1701. nsCOMPtr<nsIDocShell> docshell = do_GetInterface(aParent);
  1702. if (docshell && docshell->GetIsInMozBrowserOrApp()) {
  1703. chromeFlags &= ~nsIWebBrowserChrome::CHROME_OPENAS_DIALOG;
  1704. }
  1705. return chromeFlags;
  1706. }
  1707. // static
  1708. int32_t
  1709. nsWindowWatcher::WinHasOption(const nsACString& aOptions, const char* aName,
  1710. int32_t aDefault, bool* aPresenceFlag)
  1711. {
  1712. if (aOptions.IsEmpty()) {
  1713. return 0;
  1714. }
  1715. const char* options = aOptions.BeginReading();
  1716. char* comma;
  1717. char* equal;
  1718. int32_t found = 0;
  1719. #ifdef DEBUG
  1720. NS_ASSERTION(nsAutoCString(aOptions).FindCharInSet(" \n\r\t") == kNotFound,
  1721. "There should be no whitespace in this string!");
  1722. #endif
  1723. while (true) {
  1724. comma = PL_strchr(options, ',');
  1725. if (comma) {
  1726. *comma = '\0';
  1727. }
  1728. equal = PL_strchr(options, '=');
  1729. if (equal) {
  1730. *equal = '\0';
  1731. }
  1732. if (nsCRT::strcasecmp(options, aName) == 0) {
  1733. if (aPresenceFlag) {
  1734. *aPresenceFlag = true;
  1735. }
  1736. if (equal)
  1737. if (*(equal + 1) == '*') {
  1738. found = aDefault;
  1739. } else if (nsCRT::strcasecmp(equal + 1, "yes") == 0) {
  1740. found = 1;
  1741. } else {
  1742. found = atoi(equal + 1);
  1743. }
  1744. else {
  1745. found = 1;
  1746. }
  1747. }
  1748. if (equal) {
  1749. *equal = '=';
  1750. }
  1751. if (comma) {
  1752. *comma = ',';
  1753. }
  1754. if (found || !comma) {
  1755. break;
  1756. }
  1757. options = comma + 1;
  1758. }
  1759. return found;
  1760. }
  1761. /* try to find an nsIDocShellTreeItem with the given name in any
  1762. known open window. a failure to find the item will not
  1763. necessarily return a failure method value. check aFoundItem.
  1764. */
  1765. NS_IMETHODIMP
  1766. nsWindowWatcher::FindItemWithName(const nsAString& aName,
  1767. nsIDocShellTreeItem* aRequestor,
  1768. nsIDocShellTreeItem* aOriginalRequestor,
  1769. nsIDocShellTreeItem** aFoundItem)
  1770. {
  1771. *aFoundItem = nullptr;
  1772. if (aName.IsEmpty()) {
  1773. return NS_OK;
  1774. }
  1775. if (aName.LowerCaseEqualsLiteral("_blank") ||
  1776. aName.LowerCaseEqualsLiteral("_top") ||
  1777. aName.LowerCaseEqualsLiteral("_parent") ||
  1778. aName.LowerCaseEqualsLiteral("_self")) {
  1779. return NS_OK;
  1780. }
  1781. // If we are looking for an item and we don't have a docshell we are checking
  1782. // on, let's just look in the chrome tab group!
  1783. return TabGroup::GetChromeTabGroup()->FindItemWithName(aName,
  1784. aRequestor,
  1785. aOriginalRequestor,
  1786. aFoundItem);
  1787. }
  1788. already_AddRefed<nsIDocShellTreeItem>
  1789. nsWindowWatcher::GetCallerTreeItem(nsIDocShellTreeItem* aParentItem)
  1790. {
  1791. nsCOMPtr<nsIWebNavigation> callerWebNav = do_GetInterface(GetEntryGlobal());
  1792. nsCOMPtr<nsIDocShellTreeItem> callerItem = do_QueryInterface(callerWebNav);
  1793. if (!callerItem) {
  1794. callerItem = aParentItem;
  1795. }
  1796. return callerItem.forget();
  1797. }
  1798. nsPIDOMWindowOuter*
  1799. nsWindowWatcher::SafeGetWindowByName(const nsAString& aName,
  1800. bool aForceNoOpener,
  1801. mozIDOMWindowProxy* aCurrentWindow)
  1802. {
  1803. if (aForceNoOpener) {
  1804. if (!aName.LowerCaseEqualsLiteral("_self") &&
  1805. !aName.LowerCaseEqualsLiteral("_top") &&
  1806. !aName.LowerCaseEqualsLiteral("_parent")) {
  1807. // Ignore all other names in the noopener case.
  1808. return nullptr;
  1809. }
  1810. }
  1811. nsCOMPtr<nsIDocShellTreeItem> startItem;
  1812. GetWindowTreeItem(aCurrentWindow, getter_AddRefs(startItem));
  1813. nsCOMPtr<nsIDocShellTreeItem> callerItem = GetCallerTreeItem(startItem);
  1814. nsCOMPtr<nsIDocShellTreeItem> foundItem;
  1815. if (startItem) {
  1816. startItem->FindItemWithName(aName, nullptr, callerItem,
  1817. getter_AddRefs(foundItem));
  1818. } else {
  1819. FindItemWithName(aName, nullptr, callerItem,
  1820. getter_AddRefs(foundItem));
  1821. }
  1822. return foundItem ? foundItem->GetWindow() : nullptr;
  1823. }
  1824. /* Fetch the nsIDOMWindow corresponding to the given nsIDocShellTreeItem.
  1825. This forces the creation of a script context, if one has not already
  1826. been created. Note it also sets the window's opener to the parent,
  1827. if applicable -- because it's just convenient, that's all. null aParent
  1828. is acceptable. */
  1829. nsresult
  1830. nsWindowWatcher::ReadyOpenedDocShellItem(nsIDocShellTreeItem* aOpenedItem,
  1831. nsPIDOMWindowOuter* aParent,
  1832. bool aWindowIsNew,
  1833. bool aForceNoOpener,
  1834. mozIDOMWindowProxy** aOpenedWindow)
  1835. {
  1836. nsresult rv = NS_ERROR_FAILURE;
  1837. NS_ENSURE_ARG(aOpenedWindow);
  1838. *aOpenedWindow = 0;
  1839. nsCOMPtr<nsPIDOMWindowOuter> piOpenedWindow = aOpenedItem->GetWindow();
  1840. if (piOpenedWindow) {
  1841. if (!aForceNoOpener) {
  1842. piOpenedWindow->SetOpenerWindow(aParent, aWindowIsNew); // damnit
  1843. } else if (aParent && aParent != piOpenedWindow) {
  1844. MOZ_ASSERT(piOpenedWindow->TabGroup() != aParent->TabGroup(),
  1845. "If we're forcing no opener, they should be in different tab groups");
  1846. }
  1847. if (aWindowIsNew) {
  1848. #ifdef DEBUG
  1849. // Assert that we're not loading things right now. If we are, when
  1850. // that load completes it will clobber whatever principals we set up
  1851. // on this new window!
  1852. nsCOMPtr<nsIDocumentLoader> docloader = do_QueryInterface(aOpenedItem);
  1853. NS_ASSERTION(docloader, "How can we not have a docloader here?");
  1854. nsCOMPtr<nsIChannel> chan;
  1855. docloader->GetDocumentChannel(getter_AddRefs(chan));
  1856. NS_ASSERTION(!chan, "Why is there a document channel?");
  1857. #endif
  1858. nsCOMPtr<nsIDocument> doc = piOpenedWindow->GetExtantDoc();
  1859. if (doc) {
  1860. doc->SetIsInitialDocument(true);
  1861. }
  1862. }
  1863. rv = CallQueryInterface(piOpenedWindow, aOpenedWindow);
  1864. }
  1865. return rv;
  1866. }
  1867. // static
  1868. void
  1869. nsWindowWatcher::CalcSizeSpec(const nsACString& aFeatures, SizeSpec& aResult)
  1870. {
  1871. // Parse position spec, if any, from aFeatures
  1872. bool present;
  1873. int32_t temp;
  1874. present = false;
  1875. if ((temp = WinHasOption(aFeatures, "left", 0, &present)) || present) {
  1876. aResult.mLeft = temp;
  1877. } else if ((temp = WinHasOption(aFeatures, "screenX", 0, &present)) ||
  1878. present) {
  1879. aResult.mLeft = temp;
  1880. }
  1881. aResult.mLeftSpecified = present;
  1882. present = false;
  1883. if ((temp = WinHasOption(aFeatures, "top", 0, &present)) || present) {
  1884. aResult.mTop = temp;
  1885. } else if ((temp = WinHasOption(aFeatures, "screenY", 0, &present)) ||
  1886. present) {
  1887. aResult.mTop = temp;
  1888. }
  1889. aResult.mTopSpecified = present;
  1890. // Parse size spec, if any. Chrome size overrides content size.
  1891. if ((temp = WinHasOption(aFeatures, "outerWidth", INT32_MIN, nullptr))) {
  1892. if (temp == INT32_MIN) {
  1893. aResult.mUseDefaultWidth = true;
  1894. } else {
  1895. aResult.mOuterWidth = temp;
  1896. }
  1897. aResult.mOuterWidthSpecified = true;
  1898. } else if ((temp = WinHasOption(aFeatures, "width", INT32_MIN, nullptr)) ||
  1899. (temp = WinHasOption(aFeatures, "innerWidth", INT32_MIN,
  1900. nullptr))) {
  1901. if (temp == INT32_MIN) {
  1902. aResult.mUseDefaultWidth = true;
  1903. } else {
  1904. aResult.mInnerWidth = temp;
  1905. }
  1906. aResult.mInnerWidthSpecified = true;
  1907. }
  1908. if ((temp = WinHasOption(aFeatures, "outerHeight", INT32_MIN, nullptr))) {
  1909. if (temp == INT32_MIN) {
  1910. aResult.mUseDefaultHeight = true;
  1911. } else {
  1912. aResult.mOuterHeight = temp;
  1913. }
  1914. aResult.mOuterHeightSpecified = true;
  1915. } else if ((temp = WinHasOption(aFeatures, "height", INT32_MIN,
  1916. nullptr)) ||
  1917. (temp = WinHasOption(aFeatures, "innerHeight", INT32_MIN,
  1918. nullptr))) {
  1919. if (temp == INT32_MIN) {
  1920. aResult.mUseDefaultHeight = true;
  1921. } else {
  1922. aResult.mInnerHeight = temp;
  1923. }
  1924. aResult.mInnerHeightSpecified = true;
  1925. }
  1926. }
  1927. /* Size and position a new window according to aSizeSpec. This method
  1928. is assumed to be called after the window has already been given
  1929. a default position and size; thus its current position and size are
  1930. accurate defaults. The new window is made visible at method end.
  1931. @param aTreeOwner
  1932. The top-level nsIDocShellTreeOwner of the newly opened window.
  1933. @param aParent (optional)
  1934. The parent window from which to inherit zoom factors from if
  1935. aOpenerFullZoom is none.
  1936. @param aIsCallerChrome
  1937. True if the code requesting the new window is privileged.
  1938. @param aSizeSpec
  1939. The size that the new window should be.
  1940. @param aOpenerFullZoom
  1941. If not nothing, a zoom factor to scale the content to.
  1942. */
  1943. void
  1944. nsWindowWatcher::SizeOpenedWindow(nsIDocShellTreeOwner* aTreeOwner,
  1945. mozIDOMWindowProxy* aParent,
  1946. bool aIsCallerChrome,
  1947. const SizeSpec& aSizeSpec,
  1948. Maybe<float> aOpenerFullZoom)
  1949. {
  1950. // We should only be sizing top-level windows if we're in the parent
  1951. // process.
  1952. MOZ_ASSERT(XRE_IsParentProcess());
  1953. // position and size of window
  1954. int32_t left = 0, top = 0, width = 100, height = 100;
  1955. // difference between chrome and content size
  1956. int32_t chromeWidth = 0, chromeHeight = 0;
  1957. // whether the window size spec refers to chrome or content
  1958. bool sizeChromeWidth = true, sizeChromeHeight = true;
  1959. // get various interfaces for aDocShellItem, used throughout this method
  1960. nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(aTreeOwner));
  1961. if (!treeOwnerAsWin) { // we'll need this to actually size the docshell
  1962. return;
  1963. }
  1964. double openerZoom = aOpenerFullZoom.valueOr(1.0);
  1965. if (aParent && aOpenerFullZoom.isNothing()) {
  1966. nsCOMPtr<nsPIDOMWindowOuter> piWindow = nsPIDOMWindowOuter::From(aParent);
  1967. if (nsIDocument* doc = piWindow->GetDoc()) {
  1968. if (nsIPresShell* shell = doc->GetShell()) {
  1969. if (nsPresContext* presContext = shell->GetPresContext()) {
  1970. openerZoom = presContext->GetFullZoom();
  1971. }
  1972. }
  1973. }
  1974. }
  1975. double scale = 1.0;
  1976. treeOwnerAsWin->GetUnscaledDevicePixelsPerCSSPixel(&scale);
  1977. /* The current position and size will be unchanged if not specified
  1978. (and they fit entirely onscreen). Also, calculate the difference
  1979. between chrome and content sizes on aDocShellItem's window.
  1980. This latter point becomes important if chrome and content
  1981. specifications are mixed in aFeatures, and when bringing the window
  1982. back from too far off the right or bottom edges of the screen. */
  1983. treeOwnerAsWin->GetPositionAndSize(&left, &top, &width, &height);
  1984. left = NSToIntRound(left / scale);
  1985. top = NSToIntRound(top / scale);
  1986. width = NSToIntRound(width / scale);
  1987. height = NSToIntRound(height / scale);
  1988. {
  1989. int32_t contentWidth, contentHeight;
  1990. bool hasPrimaryContent = false;
  1991. aTreeOwner->GetHasPrimaryContent(&hasPrimaryContent);
  1992. if (hasPrimaryContent) {
  1993. aTreeOwner->GetPrimaryContentSize(&contentWidth, &contentHeight);
  1994. } else {
  1995. aTreeOwner->GetRootShellSize(&contentWidth, &contentHeight);
  1996. }
  1997. chromeWidth = width - contentWidth;
  1998. chromeHeight = height - contentHeight;
  1999. }
  2000. // Set up left/top
  2001. if (aSizeSpec.mLeftSpecified) {
  2002. left = NSToIntRound(aSizeSpec.mLeft * openerZoom);
  2003. }
  2004. if (aSizeSpec.mTopSpecified) {
  2005. top = NSToIntRound(aSizeSpec.mTop * openerZoom);
  2006. }
  2007. // Set up width
  2008. if (aSizeSpec.mOuterWidthSpecified) {
  2009. if (!aSizeSpec.mUseDefaultWidth) {
  2010. width = NSToIntRound(aSizeSpec.mOuterWidth * openerZoom);
  2011. } // Else specified to default; just use our existing width
  2012. } else if (aSizeSpec.mInnerWidthSpecified) {
  2013. sizeChromeWidth = false;
  2014. if (aSizeSpec.mUseDefaultWidth) {
  2015. width = width - chromeWidth;
  2016. } else {
  2017. width = NSToIntRound(aSizeSpec.mInnerWidth * openerZoom);
  2018. }
  2019. }
  2020. // Set up height
  2021. if (aSizeSpec.mOuterHeightSpecified) {
  2022. if (!aSizeSpec.mUseDefaultHeight) {
  2023. height = NSToIntRound(aSizeSpec.mOuterHeight * openerZoom);
  2024. } // Else specified to default; just use our existing height
  2025. } else if (aSizeSpec.mInnerHeightSpecified) {
  2026. sizeChromeHeight = false;
  2027. if (aSizeSpec.mUseDefaultHeight) {
  2028. height = height - chromeHeight;
  2029. } else {
  2030. height = NSToIntRound(aSizeSpec.mInnerHeight * openerZoom);
  2031. }
  2032. }
  2033. bool positionSpecified = aSizeSpec.PositionSpecified();
  2034. // Check security state for use in determing window dimensions
  2035. bool enabled = false;
  2036. if (aIsCallerChrome) {
  2037. // Only enable special priveleges for chrome when chrome calls
  2038. // open() on a chrome window
  2039. nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(aParent));
  2040. enabled = !aParent || chromeWin;
  2041. }
  2042. if (!enabled) {
  2043. // Security check failed. Ensure all args meet minimum reqs.
  2044. int32_t oldTop = top, oldLeft = left;
  2045. // We'll also need the screen dimensions
  2046. nsCOMPtr<nsIScreen> screen;
  2047. nsCOMPtr<nsIScreenManager> screenMgr(
  2048. do_GetService("@mozilla.org/gfx/screenmanager;1"));
  2049. if (screenMgr)
  2050. screenMgr->ScreenForRect(left, top, width, height,
  2051. getter_AddRefs(screen));
  2052. if (screen) {
  2053. int32_t screenLeft, screenTop, screenWidth, screenHeight;
  2054. int32_t winWidth = width + (sizeChromeWidth ? 0 : chromeWidth),
  2055. winHeight = height + (sizeChromeHeight ? 0 : chromeHeight);
  2056. // Get screen dimensions (in device pixels)
  2057. screen->GetAvailRect(&screenLeft, &screenTop, &screenWidth,
  2058. &screenHeight);
  2059. // Convert them to CSS pixels
  2060. screenLeft = NSToIntRound(screenLeft / scale);
  2061. screenTop = NSToIntRound(screenTop / scale);
  2062. screenWidth = NSToIntRound(screenWidth / scale);
  2063. screenHeight = NSToIntRound(screenHeight / scale);
  2064. if (aSizeSpec.SizeSpecified()) {
  2065. /* Unlike position, force size out-of-bounds check only if
  2066. size actually was specified. Otherwise, intrinsically sized
  2067. windows are broken. */
  2068. if (height < 100) {
  2069. height = 100;
  2070. winHeight = height + (sizeChromeHeight ? 0 : chromeHeight);
  2071. }
  2072. if (winHeight > screenHeight) {
  2073. height = screenHeight - (sizeChromeHeight ? 0 : chromeHeight);
  2074. }
  2075. if (width < 100) {
  2076. width = 100;
  2077. winWidth = width + (sizeChromeWidth ? 0 : chromeWidth);
  2078. }
  2079. if (winWidth > screenWidth) {
  2080. width = screenWidth - (sizeChromeWidth ? 0 : chromeWidth);
  2081. }
  2082. }
  2083. if (left + winWidth > screenLeft + screenWidth ||
  2084. left + winWidth < left) {
  2085. left = screenLeft + screenWidth - winWidth;
  2086. }
  2087. if (left < screenLeft) {
  2088. left = screenLeft;
  2089. }
  2090. if (top + winHeight > screenTop + screenHeight || top + winHeight < top) {
  2091. top = screenTop + screenHeight - winHeight;
  2092. }
  2093. if (top < screenTop) {
  2094. top = screenTop;
  2095. }
  2096. if (top != oldTop || left != oldLeft) {
  2097. positionSpecified = true;
  2098. }
  2099. }
  2100. }
  2101. // size and position the window
  2102. if (positionSpecified) {
  2103. // Get the scale factor appropriate for the screen we're actually
  2104. // positioning on.
  2105. nsCOMPtr<nsIScreen> screen;
  2106. nsCOMPtr<nsIScreenManager> screenMgr(
  2107. do_GetService("@mozilla.org/gfx/screenmanager;1"));
  2108. if (screenMgr) {
  2109. screenMgr->ScreenForRect(left, top, 1, 1, getter_AddRefs(screen));
  2110. }
  2111. if (screen) {
  2112. double cssToDevPixScale, desktopToDevPixScale;
  2113. screen->GetDefaultCSSScaleFactor(&cssToDevPixScale);
  2114. screen->GetContentsScaleFactor(&desktopToDevPixScale);
  2115. double cssToDesktopScale = cssToDevPixScale / desktopToDevPixScale;
  2116. int32_t screenLeft, screenTop, screenWd, screenHt;
  2117. screen->GetRectDisplayPix(&screenLeft, &screenTop, &screenWd, &screenHt);
  2118. // Adjust by desktop-pixel origin of the target screen when scaling
  2119. // to convert from per-screen CSS-px coords to global desktop coords.
  2120. treeOwnerAsWin->SetPositionDesktopPix(
  2121. (left - screenLeft) * cssToDesktopScale + screenLeft,
  2122. (top - screenTop) * cssToDesktopScale + screenTop);
  2123. } else {
  2124. // Couldn't find screen? This shouldn't happen.
  2125. treeOwnerAsWin->SetPosition(left * scale, top * scale);
  2126. }
  2127. // This shouldn't be necessary, given the screen check above, but in case
  2128. // moving the window didn't put it where we expected (e.g. due to issues
  2129. // at the widget level, or whatever), let's re-fetch the scale factor for
  2130. // wherever it really ended up
  2131. treeOwnerAsWin->GetUnscaledDevicePixelsPerCSSPixel(&scale);
  2132. }
  2133. if (aSizeSpec.SizeSpecified()) {
  2134. /* Prefer to trust the interfaces, which think in terms of pure
  2135. chrome or content sizes. If we have a mix, use the chrome size
  2136. adjusted by the chrome/content differences calculated earlier. */
  2137. if (!sizeChromeWidth && !sizeChromeHeight) {
  2138. bool hasPrimaryContent = false;
  2139. aTreeOwner->GetHasPrimaryContent(&hasPrimaryContent);
  2140. if (hasPrimaryContent) {
  2141. aTreeOwner->SetPrimaryContentSize(width * scale, height * scale);
  2142. } else {
  2143. aTreeOwner->SetRootShellSize(width * scale, height * scale);
  2144. }
  2145. } else {
  2146. if (!sizeChromeWidth) {
  2147. width += chromeWidth;
  2148. }
  2149. if (!sizeChromeHeight) {
  2150. height += chromeHeight;
  2151. }
  2152. treeOwnerAsWin->SetSize(width * scale, height * scale, false);
  2153. }
  2154. }
  2155. treeOwnerAsWin->SetVisibility(true);
  2156. }
  2157. void
  2158. nsWindowWatcher::GetWindowTreeItem(mozIDOMWindowProxy* aWindow,
  2159. nsIDocShellTreeItem** aResult)
  2160. {
  2161. *aResult = 0;
  2162. if (aWindow) {
  2163. nsIDocShell* docshell = nsPIDOMWindowOuter::From(aWindow)->GetDocShell();
  2164. if (docshell) {
  2165. CallQueryInterface(docshell, aResult);
  2166. }
  2167. }
  2168. }
  2169. void
  2170. nsWindowWatcher::GetWindowTreeOwner(nsPIDOMWindowOuter* aWindow,
  2171. nsIDocShellTreeOwner** aResult)
  2172. {
  2173. *aResult = 0;
  2174. nsCOMPtr<nsIDocShellTreeItem> treeItem;
  2175. GetWindowTreeItem(aWindow, getter_AddRefs(treeItem));
  2176. if (treeItem) {
  2177. treeItem->GetTreeOwner(aResult);
  2178. }
  2179. }
  2180. /* static */
  2181. int32_t
  2182. nsWindowWatcher::GetWindowOpenLocation(nsPIDOMWindowOuter* aParent,
  2183. uint32_t aChromeFlags,
  2184. bool aCalledFromJS,
  2185. bool aPositionSpecified,
  2186. bool aSizeSpecified)
  2187. {
  2188. bool isFullScreen = aParent->GetFullScreen();
  2189. // Where should we open this?
  2190. int32_t containerPref;
  2191. if (NS_FAILED(Preferences::GetInt("browser.link.open_newwindow",
  2192. &containerPref))) {
  2193. // We couldn't read the user preference, so fall back on the default.
  2194. return nsIBrowserDOMWindow::OPEN_NEWTAB;
  2195. }
  2196. bool isDisabledOpenNewWindow =
  2197. isFullScreen &&
  2198. Preferences::GetBool("browser.link.open_newwindow.disabled_in_fullscreen");
  2199. if (isDisabledOpenNewWindow &&
  2200. (containerPref == nsIBrowserDOMWindow::OPEN_NEWWINDOW)) {
  2201. containerPref = nsIBrowserDOMWindow::OPEN_NEWTAB;
  2202. }
  2203. if (containerPref != nsIBrowserDOMWindow::OPEN_NEWTAB &&
  2204. containerPref != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) {
  2205. // Just open a window normally
  2206. return nsIBrowserDOMWindow::OPEN_NEWWINDOW;
  2207. }
  2208. if (aCalledFromJS) {
  2209. /* Now check our restriction pref. The restriction pref is a power-user's
  2210. fine-tuning pref. values:
  2211. 0: no restrictions - divert everything
  2212. 1: don't divert window.open at all
  2213. 2: don't divert window.open with features
  2214. */
  2215. int32_t restrictionPref =
  2216. Preferences::GetInt("browser.link.open_newwindow.restriction", 2);
  2217. if (restrictionPref < 0 || restrictionPref > 2) {
  2218. restrictionPref = 2; // Sane default behavior
  2219. }
  2220. if (isDisabledOpenNewWindow) {
  2221. // In browser fullscreen, the window should be opened
  2222. // in the current window with no features (see bug 803675)
  2223. restrictionPref = 0;
  2224. }
  2225. if (restrictionPref == 1) {
  2226. return nsIBrowserDOMWindow::OPEN_NEWWINDOW;
  2227. }
  2228. if (restrictionPref == 2) {
  2229. // Only continue if there are no size/position features and no special
  2230. // chrome flags - with the exception of the remoteness and private flags,
  2231. // which might have been automatically flipped by Gecko.
  2232. int32_t uiChromeFlags = aChromeFlags;
  2233. uiChromeFlags &= ~(nsIWebBrowserChrome::CHROME_REMOTE_WINDOW |
  2234. nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW |
  2235. nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW |
  2236. nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME);
  2237. if (uiChromeFlags != nsIWebBrowserChrome::CHROME_ALL ||
  2238. aPositionSpecified || aSizeSpecified) {
  2239. return nsIBrowserDOMWindow::OPEN_NEWWINDOW;
  2240. }
  2241. }
  2242. }
  2243. return containerPref;
  2244. }