dom/browser-element/BrowserElementParent.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/browser-element/BrowserElementParent.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,359 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +#include "TabParent.h"
     1.9 +
    1.10 +// TabParent.h transitively includes <windows.h>, which does
    1.11 +//   #define CreateEvent CreateEventW
    1.12 +// That messes up our call to EventDispatcher::CreateEvent below.
    1.13 +
    1.14 +#ifdef CreateEvent
    1.15 +#undef CreateEvent
    1.16 +#endif
    1.17 +
    1.18 +#include "BrowserElementParent.h"
    1.19 +#include "mozilla/EventDispatcher.h"
    1.20 +#include "mozilla/dom/HTMLIFrameElement.h"
    1.21 +#include "nsIDOMCustomEvent.h"
    1.22 +#include "nsIInterfaceRequestorUtils.h"
    1.23 +#include "nsVariant.h"
    1.24 +#include "mozilla/dom/BrowserElementDictionariesBinding.h"
    1.25 +#include "nsCxPusher.h"
    1.26 +#include "GeneratedEventClasses.h"
    1.27 +
    1.28 +using namespace mozilla;
    1.29 +using namespace mozilla::dom;
    1.30 +
    1.31 +namespace {
    1.32 +
    1.33 +using mozilla::BrowserElementParent;
    1.34 +/**
    1.35 + * Create an <iframe mozbrowser> owned by the same document as
    1.36 + * aOpenerFrameElement.
    1.37 + */
    1.38 +already_AddRefed<HTMLIFrameElement>
    1.39 +CreateIframe(Element* aOpenerFrameElement, const nsAString& aName, bool aRemote)
    1.40 +{
    1.41 +  nsNodeInfoManager *nodeInfoManager =
    1.42 +    aOpenerFrameElement->OwnerDoc()->NodeInfoManager();
    1.43 +
    1.44 +  nsCOMPtr<nsINodeInfo> nodeInfo =
    1.45 +    nodeInfoManager->GetNodeInfo(nsGkAtoms::iframe,
    1.46 +                                 /* aPrefix = */ nullptr,
    1.47 +                                 kNameSpaceID_XHTML,
    1.48 +                                 nsIDOMNode::ELEMENT_NODE);
    1.49 +
    1.50 +  nsRefPtr<HTMLIFrameElement> popupFrameElement =
    1.51 +    static_cast<HTMLIFrameElement*>(
    1.52 +      NS_NewHTMLIFrameElement(nodeInfo.forget(), mozilla::dom::NOT_FROM_PARSER));
    1.53 +
    1.54 +  popupFrameElement->SetMozbrowser(true);
    1.55 +
    1.56 +  // Copy the opener frame's mozapp attribute to the popup frame.
    1.57 +  if (aOpenerFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozapp)) {
    1.58 +    nsAutoString mozapp;
    1.59 +    aOpenerFrameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::mozapp, mozapp);
    1.60 +    popupFrameElement->SetAttr(kNameSpaceID_None, nsGkAtoms::mozapp,
    1.61 +                               mozapp, /* aNotify = */ false);
    1.62 +  }
    1.63 +
    1.64 +  // Copy the opener frame's parentApp attribute to the popup frame.
    1.65 +  if (aOpenerFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::parentapp)) {
    1.66 +    nsAutoString parentApp;
    1.67 +    aOpenerFrameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::parentapp,
    1.68 +                                 parentApp);
    1.69 +    popupFrameElement->SetAttr(kNameSpaceID_None, nsGkAtoms::parentapp,
    1.70 +                               parentApp, /* aNotify = */ false);
    1.71 +  }
    1.72 +
    1.73 +  // Copy the window name onto the iframe.
    1.74 +  popupFrameElement->SetAttr(kNameSpaceID_None, nsGkAtoms::name,
    1.75 +                             aName, /* aNotify = */ false);
    1.76 +
    1.77 +  // Indicate whether the iframe is should be remote.
    1.78 +  popupFrameElement->SetAttr(kNameSpaceID_None, nsGkAtoms::Remote,
    1.79 +                             aRemote ? NS_LITERAL_STRING("true") :
    1.80 +                                       NS_LITERAL_STRING("false"),
    1.81 +                             /* aNotify = */ false);
    1.82 +
    1.83 +  return popupFrameElement.forget();
    1.84 +}
    1.85 +
    1.86 +bool
    1.87 +DispatchCustomDOMEvent(Element* aFrameElement, const nsAString& aEventName,
    1.88 +                       JSContext* cx, JS::Handle<JS::Value> aDetailValue,
    1.89 +                       nsEventStatus *aStatus)
    1.90 +{
    1.91 +  NS_ENSURE_TRUE(aFrameElement, false);
    1.92 +  nsIPresShell *shell = aFrameElement->OwnerDoc()->GetShell();
    1.93 +  nsRefPtr<nsPresContext> presContext;
    1.94 +  if (shell) {
    1.95 +    presContext = shell->GetPresContext();
    1.96 +  }
    1.97 +
    1.98 +  nsCOMPtr<nsIDOMEvent> domEvent;
    1.99 +  EventDispatcher::CreateEvent(aFrameElement, presContext, nullptr,
   1.100 +                               NS_LITERAL_STRING("customevent"),
   1.101 +                               getter_AddRefs(domEvent));
   1.102 +  NS_ENSURE_TRUE(domEvent, false);
   1.103 +
   1.104 +  nsCOMPtr<nsIDOMCustomEvent> customEvent = do_QueryInterface(domEvent);
   1.105 +  NS_ENSURE_TRUE(customEvent, false);
   1.106 +  ErrorResult res;
   1.107 +  CustomEvent* event = static_cast<CustomEvent*>(customEvent.get());
   1.108 +  event->InitCustomEvent(cx,
   1.109 +                         aEventName,
   1.110 +                         /* bubbles = */ true,
   1.111 +                         /* cancelable = */ true,
   1.112 +                         aDetailValue,
   1.113 +                         res);
   1.114 +  if (res.Failed()) {
   1.115 +    return false;
   1.116 +  }
   1.117 +  customEvent->SetTrusted(true);
   1.118 +  // Dispatch the event.
   1.119 +  *aStatus = nsEventStatus_eConsumeNoDefault;
   1.120 +  nsresult rv =
   1.121 +    EventDispatcher::DispatchDOMEvent(aFrameElement, nullptr,
   1.122 +                                      domEvent, presContext, aStatus);
   1.123 +  return NS_SUCCEEDED(rv);
   1.124 +}
   1.125 +
   1.126 +} // anonymous namespace
   1.127 +
   1.128 +namespace mozilla {
   1.129 +
   1.130 +/**
   1.131 + * Dispatch a mozbrowseropenwindow event to the given opener frame element.
   1.132 + * The "popup iframe" (event.detail.frameElement) will be |aPopupFrameElement|.
   1.133 + *
   1.134 + * Returns true iff there were no unexpected failures and the window.open call
   1.135 + * was accepted by the embedder.
   1.136 + */
   1.137 +/*static*/
   1.138 +BrowserElementParent::OpenWindowResult
   1.139 +BrowserElementParent::DispatchOpenWindowEvent(Element* aOpenerFrameElement,
   1.140 +                        Element* aPopupFrameElement,
   1.141 +                        const nsAString& aURL,
   1.142 +                        const nsAString& aName,
   1.143 +                        const nsAString& aFeatures)
   1.144 +{
   1.145 +  // Dispatch a CustomEvent at aOpenerFrameElement with a detail object
   1.146 +  // (OpenWindowEventDetail) containing aPopupFrameElement, aURL, aName, and
   1.147 +  // aFeatures.
   1.148 +
   1.149 +  // Create the event's detail object.
   1.150 +  OpenWindowEventDetail detail;
   1.151 +  detail.mUrl = aURL;
   1.152 +  detail.mName = aName;
   1.153 +  detail.mFeatures = aFeatures;
   1.154 +  detail.mFrameElement = aPopupFrameElement;
   1.155 +
   1.156 +  AutoJSContext cx;
   1.157 +  JS::Rooted<JS::Value> val(cx);
   1.158 +
   1.159 +  nsIGlobalObject* sgo = aPopupFrameElement->OwnerDoc()->GetScopeObject();
   1.160 +  if (!sgo) {
   1.161 +    return BrowserElementParent::OPEN_WINDOW_IGNORED;
   1.162 +  }
   1.163 +
   1.164 +  JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject());
   1.165 +  JSAutoCompartment ac(cx, global);
   1.166 +  if (!detail.ToObject(cx, &val)) {
   1.167 +    MOZ_CRASH("Failed to convert dictionary to JS::Value due to OOM.");
   1.168 +    return BrowserElementParent::OPEN_WINDOW_IGNORED;
   1.169 +  }
   1.170 +
   1.171 +  nsEventStatus status;
   1.172 +  bool dispatchSucceeded =
   1.173 +    DispatchCustomDOMEvent(aOpenerFrameElement,
   1.174 +                           NS_LITERAL_STRING("mozbrowseropenwindow"),
   1.175 +                           cx,
   1.176 +                           val, &status);
   1.177 +
   1.178 +  if (dispatchSucceeded) {
   1.179 +    if (aPopupFrameElement->IsInDoc()) {
   1.180 +      return BrowserElementParent::OPEN_WINDOW_ADDED;
   1.181 +    } else if (status == nsEventStatus_eConsumeNoDefault) {
   1.182 +      // If the frame was not added to a document, report to callers whether
   1.183 +      // preventDefault was called on or not
   1.184 +      return BrowserElementParent::OPEN_WINDOW_CANCELLED;
   1.185 +    }
   1.186 +  }
   1.187 +
   1.188 +  return BrowserElementParent::OPEN_WINDOW_IGNORED;
   1.189 +}
   1.190 +
   1.191 +/*static*/
   1.192 +BrowserElementParent::OpenWindowResult
   1.193 +BrowserElementParent::OpenWindowOOP(TabParent* aOpenerTabParent,
   1.194 +                                    TabParent* aPopupTabParent,
   1.195 +                                    const nsAString& aURL,
   1.196 +                                    const nsAString& aName,
   1.197 +                                    const nsAString& aFeatures)
   1.198 +{
   1.199 +  // Create an iframe owned by the same document which owns openerFrameElement.
   1.200 +  nsCOMPtr<Element> openerFrameElement = aOpenerTabParent->GetOwnerElement();
   1.201 +  NS_ENSURE_TRUE(openerFrameElement,
   1.202 +                 BrowserElementParent::OPEN_WINDOW_IGNORED);
   1.203 +  nsRefPtr<HTMLIFrameElement> popupFrameElement =
   1.204 +    CreateIframe(openerFrameElement, aName, /* aRemote = */ true);
   1.205 +
   1.206 +  // Normally an <iframe> element will try to create a frameLoader when the
   1.207 +  // page touches iframe.contentWindow or sets iframe.src.
   1.208 +  //
   1.209 +  // But in our case, we want to delay the creation of the frameLoader until
   1.210 +  // we've verified that the popup has gone through successfully.  If the popup
   1.211 +  // is "blocked" by the embedder, we don't want to load the popup's url.
   1.212 +  //
   1.213 +  // Therefore we call DisallowCreateFrameLoader() on the element and call
   1.214 +  // AllowCreateFrameLoader() only after we've verified that the popup was
   1.215 +  // allowed.
   1.216 +  popupFrameElement->DisallowCreateFrameLoader();
   1.217 +
   1.218 +  OpenWindowResult opened =
   1.219 +    DispatchOpenWindowEvent(openerFrameElement, popupFrameElement,
   1.220 +                            aURL, aName, aFeatures);
   1.221 +
   1.222 +  if (opened != BrowserElementParent::OPEN_WINDOW_ADDED) {
   1.223 +    return opened;
   1.224 +  }
   1.225 +
   1.226 +  // The popup was not blocked, so hook up the frame element and the popup tab
   1.227 +  // parent, and return success.
   1.228 +  aPopupTabParent->SetOwnerElement(popupFrameElement);
   1.229 +  popupFrameElement->AllowCreateFrameLoader();
   1.230 +  popupFrameElement->CreateRemoteFrameLoader(aPopupTabParent);
   1.231 +  return opened;
   1.232 +}
   1.233 +
   1.234 +/* static */
   1.235 +BrowserElementParent::OpenWindowResult
   1.236 +BrowserElementParent::OpenWindowInProcess(nsIDOMWindow* aOpenerWindow,
   1.237 +                                          nsIURI* aURI,
   1.238 +                                          const nsAString& aName,
   1.239 +                                          const nsACString& aFeatures,
   1.240 +                                          nsIDOMWindow** aReturnWindow)
   1.241 +{
   1.242 +  *aReturnWindow = nullptr;
   1.243 +
   1.244 +  // If we call window.open from an <iframe> inside an <iframe mozbrowser>,
   1.245 +  // it's as though the top-level document inside the <iframe mozbrowser>
   1.246 +  // called window.open.  (Indeed, in the OOP case, the inner <iframe> lives
   1.247 +  // out-of-process, so we couldn't touch it if we tried.)
   1.248 +  //
   1.249 +  // GetScriptableTop gets us the <iframe mozbrowser>'s window; we'll use its
   1.250 +  // frame element, rather than aOpenerWindow's frame element, as our "opener
   1.251 +  // frame element" below.
   1.252 +  nsCOMPtr<nsIDOMWindow> topWindow;
   1.253 +  aOpenerWindow->GetScriptableTop(getter_AddRefs(topWindow));
   1.254 +
   1.255 +  nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(topWindow);
   1.256 +
   1.257 +  nsCOMPtr<Element> openerFrameElement = win->GetFrameElementInternal();
   1.258 +  NS_ENSURE_TRUE(openerFrameElement, BrowserElementParent::OPEN_WINDOW_IGNORED);
   1.259 +
   1.260 +
   1.261 +  nsRefPtr<HTMLIFrameElement> popupFrameElement =
   1.262 +    CreateIframe(openerFrameElement, aName, /* aRemote = */ false);
   1.263 +  NS_ENSURE_TRUE(popupFrameElement, BrowserElementParent::OPEN_WINDOW_IGNORED);
   1.264 +
   1.265 +  nsAutoCString spec;
   1.266 +  if (aURI) {
   1.267 +    aURI->GetSpec(spec);
   1.268 +  }
   1.269 +
   1.270 +  OpenWindowResult opened =
   1.271 +    DispatchOpenWindowEvent(openerFrameElement, popupFrameElement,
   1.272 +                            NS_ConvertUTF8toUTF16(spec),
   1.273 +                            aName,
   1.274 +                            NS_ConvertUTF8toUTF16(aFeatures));
   1.275 +
   1.276 +  if (opened != BrowserElementParent::OPEN_WINDOW_ADDED) {
   1.277 +    return opened;
   1.278 +  }
   1.279 +
   1.280 +  // Return popupFrameElement's window.
   1.281 +  nsCOMPtr<nsIFrameLoader> frameLoader;
   1.282 +  popupFrameElement->GetFrameLoader(getter_AddRefs(frameLoader));
   1.283 +  NS_ENSURE_TRUE(frameLoader, BrowserElementParent::OPEN_WINDOW_IGNORED);
   1.284 +
   1.285 +  nsCOMPtr<nsIDocShell> docshell;
   1.286 +  frameLoader->GetDocShell(getter_AddRefs(docshell));
   1.287 +  NS_ENSURE_TRUE(docshell, BrowserElementParent::OPEN_WINDOW_IGNORED);
   1.288 +
   1.289 +  nsCOMPtr<nsIDOMWindow> window = do_GetInterface(docshell);
   1.290 +  window.forget(aReturnWindow);
   1.291 +
   1.292 +  return !!*aReturnWindow ? opened : BrowserElementParent::OPEN_WINDOW_CANCELLED;
   1.293 +}
   1.294 +
   1.295 +class DispatchAsyncScrollEventRunnable : public nsRunnable
   1.296 +{
   1.297 +public:
   1.298 +  DispatchAsyncScrollEventRunnable(TabParent* aTabParent,
   1.299 +                                   const CSSRect& aContentRect,
   1.300 +                                   const CSSSize& aContentSize)
   1.301 +    : mTabParent(aTabParent)
   1.302 +    , mContentRect(aContentRect)
   1.303 +    , mContentSize(aContentSize)
   1.304 +  {}
   1.305 +
   1.306 +  NS_IMETHOD Run();
   1.307 +
   1.308 +private:
   1.309 +  nsRefPtr<TabParent> mTabParent;
   1.310 +  const CSSRect mContentRect;
   1.311 +  const CSSSize mContentSize;
   1.312 +};
   1.313 +
   1.314 +NS_IMETHODIMP DispatchAsyncScrollEventRunnable::Run()
   1.315 +{
   1.316 +  nsCOMPtr<Element> frameElement = mTabParent->GetOwnerElement();
   1.317 +  NS_ENSURE_STATE(frameElement);
   1.318 +  nsIDocument *doc = frameElement->OwnerDoc();
   1.319 +  nsCOMPtr<nsIGlobalObject> globalObject = doc->GetScopeObject();
   1.320 +  NS_ENSURE_TRUE(globalObject, NS_ERROR_UNEXPECTED);
   1.321 +
   1.322 +  // Create the event's detail object.
   1.323 +  AsyncScrollEventDetail detail;
   1.324 +  detail.mLeft = mContentRect.x;
   1.325 +  detail.mTop = mContentRect.y;
   1.326 +  detail.mWidth = mContentRect.width;
   1.327 +  detail.mHeight = mContentRect.height;
   1.328 +  detail.mScrollWidth = mContentRect.width;
   1.329 +  detail.mScrollHeight = mContentRect.height;
   1.330 +
   1.331 +  AutoSafeJSContext cx;
   1.332 +  JS::Rooted<JSObject*> globalJSObject(cx, globalObject->GetGlobalJSObject());
   1.333 +  NS_ENSURE_TRUE(globalJSObject, NS_ERROR_UNEXPECTED);
   1.334 +
   1.335 +  JSAutoCompartment ac(cx, globalJSObject);
   1.336 +  JS::Rooted<JS::Value> val(cx);
   1.337 +
   1.338 +  if (!detail.ToObject(cx, &val)) {
   1.339 +    MOZ_CRASH("Failed to convert dictionary to JS::Value due to OOM.");
   1.340 +    return NS_ERROR_FAILURE;
   1.341 +  }
   1.342 +
   1.343 +  nsEventStatus status = nsEventStatus_eIgnore;
   1.344 +  DispatchCustomDOMEvent(frameElement,
   1.345 +                         NS_LITERAL_STRING("mozbrowserasyncscroll"),
   1.346 +                         cx,
   1.347 +                         val, &status);
   1.348 +  return NS_OK;
   1.349 +}
   1.350 +
   1.351 +bool
   1.352 +BrowserElementParent::DispatchAsyncScrollEvent(TabParent* aTabParent,
   1.353 +                                               const CSSRect& aContentRect,
   1.354 +                                               const CSSSize& aContentSize)
   1.355 +{
   1.356 +  nsRefPtr<DispatchAsyncScrollEventRunnable> runnable =
   1.357 +    new DispatchAsyncScrollEventRunnable(aTabParent, aContentRect,
   1.358 +                                         aContentSize);
   1.359 +  return NS_SUCCEEDED(NS_DispatchToMainThread(runnable));
   1.360 +}
   1.361 +
   1.362 +} // namespace mozilla

mercurial