dom/browser-element/BrowserElementParent.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include "TabParent.h"
     7 // TabParent.h transitively includes <windows.h>, which does
     8 //   #define CreateEvent CreateEventW
     9 // That messes up our call to EventDispatcher::CreateEvent below.
    11 #ifdef CreateEvent
    12 #undef CreateEvent
    13 #endif
    15 #include "BrowserElementParent.h"
    16 #include "mozilla/EventDispatcher.h"
    17 #include "mozilla/dom/HTMLIFrameElement.h"
    18 #include "nsIDOMCustomEvent.h"
    19 #include "nsIInterfaceRequestorUtils.h"
    20 #include "nsVariant.h"
    21 #include "mozilla/dom/BrowserElementDictionariesBinding.h"
    22 #include "nsCxPusher.h"
    23 #include "GeneratedEventClasses.h"
    25 using namespace mozilla;
    26 using namespace mozilla::dom;
    28 namespace {
    30 using mozilla::BrowserElementParent;
    31 /**
    32  * Create an <iframe mozbrowser> owned by the same document as
    33  * aOpenerFrameElement.
    34  */
    35 already_AddRefed<HTMLIFrameElement>
    36 CreateIframe(Element* aOpenerFrameElement, const nsAString& aName, bool aRemote)
    37 {
    38   nsNodeInfoManager *nodeInfoManager =
    39     aOpenerFrameElement->OwnerDoc()->NodeInfoManager();
    41   nsCOMPtr<nsINodeInfo> nodeInfo =
    42     nodeInfoManager->GetNodeInfo(nsGkAtoms::iframe,
    43                                  /* aPrefix = */ nullptr,
    44                                  kNameSpaceID_XHTML,
    45                                  nsIDOMNode::ELEMENT_NODE);
    47   nsRefPtr<HTMLIFrameElement> popupFrameElement =
    48     static_cast<HTMLIFrameElement*>(
    49       NS_NewHTMLIFrameElement(nodeInfo.forget(), mozilla::dom::NOT_FROM_PARSER));
    51   popupFrameElement->SetMozbrowser(true);
    53   // Copy the opener frame's mozapp attribute to the popup frame.
    54   if (aOpenerFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozapp)) {
    55     nsAutoString mozapp;
    56     aOpenerFrameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::mozapp, mozapp);
    57     popupFrameElement->SetAttr(kNameSpaceID_None, nsGkAtoms::mozapp,
    58                                mozapp, /* aNotify = */ false);
    59   }
    61   // Copy the opener frame's parentApp attribute to the popup frame.
    62   if (aOpenerFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::parentapp)) {
    63     nsAutoString parentApp;
    64     aOpenerFrameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::parentapp,
    65                                  parentApp);
    66     popupFrameElement->SetAttr(kNameSpaceID_None, nsGkAtoms::parentapp,
    67                                parentApp, /* aNotify = */ false);
    68   }
    70   // Copy the window name onto the iframe.
    71   popupFrameElement->SetAttr(kNameSpaceID_None, nsGkAtoms::name,
    72                              aName, /* aNotify = */ false);
    74   // Indicate whether the iframe is should be remote.
    75   popupFrameElement->SetAttr(kNameSpaceID_None, nsGkAtoms::Remote,
    76                              aRemote ? NS_LITERAL_STRING("true") :
    77                                        NS_LITERAL_STRING("false"),
    78                              /* aNotify = */ false);
    80   return popupFrameElement.forget();
    81 }
    83 bool
    84 DispatchCustomDOMEvent(Element* aFrameElement, const nsAString& aEventName,
    85                        JSContext* cx, JS::Handle<JS::Value> aDetailValue,
    86                        nsEventStatus *aStatus)
    87 {
    88   NS_ENSURE_TRUE(aFrameElement, false);
    89   nsIPresShell *shell = aFrameElement->OwnerDoc()->GetShell();
    90   nsRefPtr<nsPresContext> presContext;
    91   if (shell) {
    92     presContext = shell->GetPresContext();
    93   }
    95   nsCOMPtr<nsIDOMEvent> domEvent;
    96   EventDispatcher::CreateEvent(aFrameElement, presContext, nullptr,
    97                                NS_LITERAL_STRING("customevent"),
    98                                getter_AddRefs(domEvent));
    99   NS_ENSURE_TRUE(domEvent, false);
   101   nsCOMPtr<nsIDOMCustomEvent> customEvent = do_QueryInterface(domEvent);
   102   NS_ENSURE_TRUE(customEvent, false);
   103   ErrorResult res;
   104   CustomEvent* event = static_cast<CustomEvent*>(customEvent.get());
   105   event->InitCustomEvent(cx,
   106                          aEventName,
   107                          /* bubbles = */ true,
   108                          /* cancelable = */ true,
   109                          aDetailValue,
   110                          res);
   111   if (res.Failed()) {
   112     return false;
   113   }
   114   customEvent->SetTrusted(true);
   115   // Dispatch the event.
   116   *aStatus = nsEventStatus_eConsumeNoDefault;
   117   nsresult rv =
   118     EventDispatcher::DispatchDOMEvent(aFrameElement, nullptr,
   119                                       domEvent, presContext, aStatus);
   120   return NS_SUCCEEDED(rv);
   121 }
   123 } // anonymous namespace
   125 namespace mozilla {
   127 /**
   128  * Dispatch a mozbrowseropenwindow event to the given opener frame element.
   129  * The "popup iframe" (event.detail.frameElement) will be |aPopupFrameElement|.
   130  *
   131  * Returns true iff there were no unexpected failures and the window.open call
   132  * was accepted by the embedder.
   133  */
   134 /*static*/
   135 BrowserElementParent::OpenWindowResult
   136 BrowserElementParent::DispatchOpenWindowEvent(Element* aOpenerFrameElement,
   137                         Element* aPopupFrameElement,
   138                         const nsAString& aURL,
   139                         const nsAString& aName,
   140                         const nsAString& aFeatures)
   141 {
   142   // Dispatch a CustomEvent at aOpenerFrameElement with a detail object
   143   // (OpenWindowEventDetail) containing aPopupFrameElement, aURL, aName, and
   144   // aFeatures.
   146   // Create the event's detail object.
   147   OpenWindowEventDetail detail;
   148   detail.mUrl = aURL;
   149   detail.mName = aName;
   150   detail.mFeatures = aFeatures;
   151   detail.mFrameElement = aPopupFrameElement;
   153   AutoJSContext cx;
   154   JS::Rooted<JS::Value> val(cx);
   156   nsIGlobalObject* sgo = aPopupFrameElement->OwnerDoc()->GetScopeObject();
   157   if (!sgo) {
   158     return BrowserElementParent::OPEN_WINDOW_IGNORED;
   159   }
   161   JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject());
   162   JSAutoCompartment ac(cx, global);
   163   if (!detail.ToObject(cx, &val)) {
   164     MOZ_CRASH("Failed to convert dictionary to JS::Value due to OOM.");
   165     return BrowserElementParent::OPEN_WINDOW_IGNORED;
   166   }
   168   nsEventStatus status;
   169   bool dispatchSucceeded =
   170     DispatchCustomDOMEvent(aOpenerFrameElement,
   171                            NS_LITERAL_STRING("mozbrowseropenwindow"),
   172                            cx,
   173                            val, &status);
   175   if (dispatchSucceeded) {
   176     if (aPopupFrameElement->IsInDoc()) {
   177       return BrowserElementParent::OPEN_WINDOW_ADDED;
   178     } else if (status == nsEventStatus_eConsumeNoDefault) {
   179       // If the frame was not added to a document, report to callers whether
   180       // preventDefault was called on or not
   181       return BrowserElementParent::OPEN_WINDOW_CANCELLED;
   182     }
   183   }
   185   return BrowserElementParent::OPEN_WINDOW_IGNORED;
   186 }
   188 /*static*/
   189 BrowserElementParent::OpenWindowResult
   190 BrowserElementParent::OpenWindowOOP(TabParent* aOpenerTabParent,
   191                                     TabParent* aPopupTabParent,
   192                                     const nsAString& aURL,
   193                                     const nsAString& aName,
   194                                     const nsAString& aFeatures)
   195 {
   196   // Create an iframe owned by the same document which owns openerFrameElement.
   197   nsCOMPtr<Element> openerFrameElement = aOpenerTabParent->GetOwnerElement();
   198   NS_ENSURE_TRUE(openerFrameElement,
   199                  BrowserElementParent::OPEN_WINDOW_IGNORED);
   200   nsRefPtr<HTMLIFrameElement> popupFrameElement =
   201     CreateIframe(openerFrameElement, aName, /* aRemote = */ true);
   203   // Normally an <iframe> element will try to create a frameLoader when the
   204   // page touches iframe.contentWindow or sets iframe.src.
   205   //
   206   // But in our case, we want to delay the creation of the frameLoader until
   207   // we've verified that the popup has gone through successfully.  If the popup
   208   // is "blocked" by the embedder, we don't want to load the popup's url.
   209   //
   210   // Therefore we call DisallowCreateFrameLoader() on the element and call
   211   // AllowCreateFrameLoader() only after we've verified that the popup was
   212   // allowed.
   213   popupFrameElement->DisallowCreateFrameLoader();
   215   OpenWindowResult opened =
   216     DispatchOpenWindowEvent(openerFrameElement, popupFrameElement,
   217                             aURL, aName, aFeatures);
   219   if (opened != BrowserElementParent::OPEN_WINDOW_ADDED) {
   220     return opened;
   221   }
   223   // The popup was not blocked, so hook up the frame element and the popup tab
   224   // parent, and return success.
   225   aPopupTabParent->SetOwnerElement(popupFrameElement);
   226   popupFrameElement->AllowCreateFrameLoader();
   227   popupFrameElement->CreateRemoteFrameLoader(aPopupTabParent);
   228   return opened;
   229 }
   231 /* static */
   232 BrowserElementParent::OpenWindowResult
   233 BrowserElementParent::OpenWindowInProcess(nsIDOMWindow* aOpenerWindow,
   234                                           nsIURI* aURI,
   235                                           const nsAString& aName,
   236                                           const nsACString& aFeatures,
   237                                           nsIDOMWindow** aReturnWindow)
   238 {
   239   *aReturnWindow = nullptr;
   241   // If we call window.open from an <iframe> inside an <iframe mozbrowser>,
   242   // it's as though the top-level document inside the <iframe mozbrowser>
   243   // called window.open.  (Indeed, in the OOP case, the inner <iframe> lives
   244   // out-of-process, so we couldn't touch it if we tried.)
   245   //
   246   // GetScriptableTop gets us the <iframe mozbrowser>'s window; we'll use its
   247   // frame element, rather than aOpenerWindow's frame element, as our "opener
   248   // frame element" below.
   249   nsCOMPtr<nsIDOMWindow> topWindow;
   250   aOpenerWindow->GetScriptableTop(getter_AddRefs(topWindow));
   252   nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(topWindow);
   254   nsCOMPtr<Element> openerFrameElement = win->GetFrameElementInternal();
   255   NS_ENSURE_TRUE(openerFrameElement, BrowserElementParent::OPEN_WINDOW_IGNORED);
   258   nsRefPtr<HTMLIFrameElement> popupFrameElement =
   259     CreateIframe(openerFrameElement, aName, /* aRemote = */ false);
   260   NS_ENSURE_TRUE(popupFrameElement, BrowserElementParent::OPEN_WINDOW_IGNORED);
   262   nsAutoCString spec;
   263   if (aURI) {
   264     aURI->GetSpec(spec);
   265   }
   267   OpenWindowResult opened =
   268     DispatchOpenWindowEvent(openerFrameElement, popupFrameElement,
   269                             NS_ConvertUTF8toUTF16(spec),
   270                             aName,
   271                             NS_ConvertUTF8toUTF16(aFeatures));
   273   if (opened != BrowserElementParent::OPEN_WINDOW_ADDED) {
   274     return opened;
   275   }
   277   // Return popupFrameElement's window.
   278   nsCOMPtr<nsIFrameLoader> frameLoader;
   279   popupFrameElement->GetFrameLoader(getter_AddRefs(frameLoader));
   280   NS_ENSURE_TRUE(frameLoader, BrowserElementParent::OPEN_WINDOW_IGNORED);
   282   nsCOMPtr<nsIDocShell> docshell;
   283   frameLoader->GetDocShell(getter_AddRefs(docshell));
   284   NS_ENSURE_TRUE(docshell, BrowserElementParent::OPEN_WINDOW_IGNORED);
   286   nsCOMPtr<nsIDOMWindow> window = do_GetInterface(docshell);
   287   window.forget(aReturnWindow);
   289   return !!*aReturnWindow ? opened : BrowserElementParent::OPEN_WINDOW_CANCELLED;
   290 }
   292 class DispatchAsyncScrollEventRunnable : public nsRunnable
   293 {
   294 public:
   295   DispatchAsyncScrollEventRunnable(TabParent* aTabParent,
   296                                    const CSSRect& aContentRect,
   297                                    const CSSSize& aContentSize)
   298     : mTabParent(aTabParent)
   299     , mContentRect(aContentRect)
   300     , mContentSize(aContentSize)
   301   {}
   303   NS_IMETHOD Run();
   305 private:
   306   nsRefPtr<TabParent> mTabParent;
   307   const CSSRect mContentRect;
   308   const CSSSize mContentSize;
   309 };
   311 NS_IMETHODIMP DispatchAsyncScrollEventRunnable::Run()
   312 {
   313   nsCOMPtr<Element> frameElement = mTabParent->GetOwnerElement();
   314   NS_ENSURE_STATE(frameElement);
   315   nsIDocument *doc = frameElement->OwnerDoc();
   316   nsCOMPtr<nsIGlobalObject> globalObject = doc->GetScopeObject();
   317   NS_ENSURE_TRUE(globalObject, NS_ERROR_UNEXPECTED);
   319   // Create the event's detail object.
   320   AsyncScrollEventDetail detail;
   321   detail.mLeft = mContentRect.x;
   322   detail.mTop = mContentRect.y;
   323   detail.mWidth = mContentRect.width;
   324   detail.mHeight = mContentRect.height;
   325   detail.mScrollWidth = mContentRect.width;
   326   detail.mScrollHeight = mContentRect.height;
   328   AutoSafeJSContext cx;
   329   JS::Rooted<JSObject*> globalJSObject(cx, globalObject->GetGlobalJSObject());
   330   NS_ENSURE_TRUE(globalJSObject, NS_ERROR_UNEXPECTED);
   332   JSAutoCompartment ac(cx, globalJSObject);
   333   JS::Rooted<JS::Value> val(cx);
   335   if (!detail.ToObject(cx, &val)) {
   336     MOZ_CRASH("Failed to convert dictionary to JS::Value due to OOM.");
   337     return NS_ERROR_FAILURE;
   338   }
   340   nsEventStatus status = nsEventStatus_eIgnore;
   341   DispatchCustomDOMEvent(frameElement,
   342                          NS_LITERAL_STRING("mozbrowserasyncscroll"),
   343                          cx,
   344                          val, &status);
   345   return NS_OK;
   346 }
   348 bool
   349 BrowserElementParent::DispatchAsyncScrollEvent(TabParent* aTabParent,
   350                                                const CSSRect& aContentRect,
   351                                                const CSSSize& aContentSize)
   352 {
   353   nsRefPtr<DispatchAsyncScrollEventRunnable> runnable =
   354     new DispatchAsyncScrollEventRunnable(aTabParent, aContentRect,
   355                                          aContentSize);
   356   return NS_SUCCEEDED(NS_DispatchToMainThread(runnable));
   357 }
   359 } // namespace mozilla

mercurial