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

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

mercurial