1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set tw=80 expandtab softtabstop=2 ts=2 sw=2: */
3 :
4 : /* This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this file,
6 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 :
8 : #include "nsGenericHTMLFrameElement.h"
9 : #include "nsIWebProgress.h"
10 : #include "nsIPrivateDOMEvent.h"
11 : #include "nsIDOMCustomEvent.h"
12 : #include "nsIVariant.h"
13 : #include "nsIInterfaceRequestorUtils.h"
14 : #include "nsWeakPtr.h"
15 : #include "nsVariant.h"
16 : #include "nsContentUtils.h"
17 : #include "nsEventDispatcher.h"
18 : #include "nsContentUtils.h"
19 : #include "nsAsyncDOMEvent.h"
20 : #include "mozilla/Preferences.h"
21 :
22 : using namespace mozilla;
23 : using namespace mozilla::dom;
24 :
25 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsGenericHTMLFrameElement)
26 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGenericHTMLFrameElement,
27 : nsGenericHTMLElement)
28 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mFrameLoader, nsIFrameLoader)
29 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
30 :
31 0 : NS_INTERFACE_TABLE_HEAD(nsGenericHTMLFrameElement)
32 0 : NS_INTERFACE_TABLE_INHERITED4(nsGenericHTMLFrameElement,
33 : nsIFrameLoaderOwner,
34 : nsIDOMMozBrowserFrame,
35 : nsIMozBrowserFrame,
36 : nsIWebProgressListener)
37 0 : NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsGenericHTMLFrameElement)
38 0 : NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
39 :
40 0 : NS_IMPL_INT_ATTR(nsGenericHTMLFrameElement, TabIndex, tabindex)
41 :
42 0 : nsGenericHTMLFrameElement::~nsGenericHTMLFrameElement()
43 : {
44 0 : if (mTitleChangedListener) {
45 0 : mTitleChangedListener->Unregister();
46 : }
47 :
48 0 : if (mFrameLoader) {
49 0 : mFrameLoader->Destroy();
50 : }
51 0 : }
52 :
53 : nsresult
54 0 : nsGenericHTMLFrameElement::GetContentDocument(nsIDOMDocument** aContentDocument)
55 : {
56 0 : NS_PRECONDITION(aContentDocument, "Null out param");
57 0 : *aContentDocument = nsnull;
58 :
59 0 : nsCOMPtr<nsIDOMWindow> win;
60 0 : GetContentWindow(getter_AddRefs(win));
61 :
62 0 : if (!win) {
63 0 : return NS_OK;
64 : }
65 :
66 0 : return win->GetDocument(aContentDocument);
67 : }
68 :
69 : nsresult
70 0 : nsGenericHTMLFrameElement::GetContentWindow(nsIDOMWindow** aContentWindow)
71 : {
72 0 : NS_PRECONDITION(aContentWindow, "Null out param");
73 0 : *aContentWindow = nsnull;
74 :
75 0 : nsresult rv = EnsureFrameLoader();
76 0 : NS_ENSURE_SUCCESS(rv, rv);
77 :
78 0 : if (!mFrameLoader) {
79 0 : return NS_OK;
80 : }
81 :
82 0 : bool depthTooGreat = false;
83 0 : mFrameLoader->GetDepthTooGreat(&depthTooGreat);
84 0 : if (depthTooGreat) {
85 : // Claim to have no contentWindow
86 0 : return NS_OK;
87 : }
88 :
89 0 : nsCOMPtr<nsIDocShell> doc_shell;
90 0 : mFrameLoader->GetDocShell(getter_AddRefs(doc_shell));
91 :
92 0 : nsCOMPtr<nsPIDOMWindow> win(do_GetInterface(doc_shell));
93 :
94 0 : if (!win) {
95 0 : return NS_OK;
96 : }
97 :
98 0 : NS_ASSERTION(win->IsOuterWindow(),
99 : "Uh, this window should always be an outer window!");
100 :
101 0 : return CallQueryInterface(win, aContentWindow);
102 : }
103 :
104 : nsresult
105 0 : nsGenericHTMLFrameElement::EnsureFrameLoader()
106 : {
107 0 : if (!GetParent() || !IsInDoc() || mFrameLoader) {
108 : // If frame loader is there, we just keep it around, cached
109 0 : return NS_OK;
110 : }
111 :
112 0 : mFrameLoader = nsFrameLoader::Create(this, mNetworkCreated);
113 0 : if (!mFrameLoader) {
114 : // Strangely enough, this method doesn't actually ensure that the
115 : // frameloader exists. It's more of a best-effort kind of thing.
116 0 : return NS_OK;
117 : }
118 :
119 0 : MaybeEnsureBrowserFrameListenersRegistered();
120 :
121 0 : return NS_OK;
122 : }
123 :
124 : NS_IMETHODIMP
125 0 : nsGenericHTMLFrameElement::GetFrameLoader(nsIFrameLoader **aFrameLoader)
126 : {
127 0 : NS_IF_ADDREF(*aFrameLoader = mFrameLoader);
128 0 : return NS_OK;
129 : }
130 :
131 : NS_IMETHODIMP_(already_AddRefed<nsFrameLoader>)
132 0 : nsGenericHTMLFrameElement::GetFrameLoader()
133 : {
134 0 : nsRefPtr<nsFrameLoader> loader = mFrameLoader;
135 0 : return loader.forget();
136 : }
137 :
138 : NS_IMETHODIMP
139 0 : nsGenericHTMLFrameElement::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherOwner)
140 : {
141 : // We don't support this yet
142 0 : return NS_ERROR_NOT_IMPLEMENTED;
143 : }
144 :
145 : nsresult
146 0 : nsGenericHTMLFrameElement::LoadSrc()
147 : {
148 0 : nsresult rv = EnsureFrameLoader();
149 0 : NS_ENSURE_SUCCESS(rv, rv);
150 :
151 0 : if (!mFrameLoader) {
152 0 : return NS_OK;
153 : }
154 :
155 0 : rv = mFrameLoader->LoadFrame();
156 : #ifdef DEBUG
157 0 : if (NS_FAILED(rv)) {
158 0 : NS_WARNING("failed to load URL");
159 : }
160 : #endif
161 :
162 0 : return rv;
163 : }
164 :
165 : nsresult
166 0 : nsGenericHTMLFrameElement::BindToTree(nsIDocument* aDocument,
167 : nsIContent* aParent,
168 : nsIContent* aBindingParent,
169 : bool aCompileEventHandlers)
170 : {
171 : nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
172 : aBindingParent,
173 0 : aCompileEventHandlers);
174 0 : NS_ENSURE_SUCCESS(rv, rv);
175 :
176 0 : if (aDocument) {
177 0 : NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
178 : "Missing a script blocker!");
179 : // We're in a document now. Kick off the frame load.
180 0 : LoadSrc();
181 : }
182 :
183 : // We're now in document and scripts may move us, so clear
184 : // the mNetworkCreated flag.
185 0 : mNetworkCreated = false;
186 0 : return rv;
187 : }
188 :
189 : void
190 0 : nsGenericHTMLFrameElement::UnbindFromTree(bool aDeep, bool aNullParent)
191 : {
192 0 : if (mFrameLoader) {
193 : // This iframe is being taken out of the document, destroy the
194 : // iframe's frame loader (doing that will tear down the window in
195 : // this iframe).
196 : // XXXbz we really want to only partially destroy the frame
197 : // loader... we don't want to tear down the docshell. Food for
198 : // later bug.
199 0 : mFrameLoader->Destroy();
200 0 : mFrameLoader = nsnull;
201 : }
202 :
203 0 : nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
204 0 : }
205 :
206 : nsresult
207 0 : nsGenericHTMLFrameElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
208 : nsIAtom* aPrefix, const nsAString& aValue,
209 : bool aNotify)
210 : {
211 : nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix,
212 0 : aValue, aNotify);
213 0 : NS_ENSURE_SUCCESS(rv, rv);
214 :
215 0 : if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::src) {
216 : // Don't propagate error here. The attribute was successfully set, that's
217 : // what we should reflect.
218 0 : LoadSrc();
219 : }
220 :
221 0 : return NS_OK;
222 : }
223 :
224 : void
225 0 : nsGenericHTMLFrameElement::DestroyContent()
226 : {
227 0 : if (mFrameLoader) {
228 0 : mFrameLoader->Destroy();
229 0 : mFrameLoader = nsnull;
230 : }
231 :
232 0 : nsGenericHTMLElement::DestroyContent();
233 0 : }
234 :
235 : nsresult
236 0 : nsGenericHTMLFrameElement::CopyInnerTo(nsGenericElement* aDest) const
237 : {
238 0 : nsresult rv = nsGenericHTMLElement::CopyInnerTo(aDest);
239 0 : NS_ENSURE_SUCCESS(rv, rv);
240 :
241 0 : nsIDocument* doc = aDest->OwnerDoc();
242 0 : if (doc->IsStaticDocument() && mFrameLoader) {
243 : nsGenericHTMLFrameElement* dest =
244 0 : static_cast<nsGenericHTMLFrameElement*>(aDest);
245 0 : nsFrameLoader* fl = nsFrameLoader::Create(dest, false);
246 0 : NS_ENSURE_STATE(fl);
247 0 : dest->mFrameLoader = fl;
248 0 : static_cast<nsFrameLoader*>(mFrameLoader.get())->CreateStaticClone(fl);
249 : }
250 :
251 0 : return rv;
252 : }
253 :
254 : bool
255 0 : nsGenericHTMLFrameElement::IsHTMLFocusable(bool aWithMouse,
256 : bool *aIsFocusable,
257 : PRInt32 *aTabIndex)
258 : {
259 0 : if (nsGenericHTMLElement::IsHTMLFocusable(aWithMouse, aIsFocusable, aTabIndex)) {
260 0 : return true;
261 : }
262 :
263 0 : *aIsFocusable = nsContentUtils::IsSubDocumentTabbable(this);
264 :
265 0 : if (!*aIsFocusable && aTabIndex) {
266 0 : *aTabIndex = -1;
267 : }
268 :
269 0 : return false;
270 : }
271 :
272 : NS_IMETHODIMP
273 0 : nsGenericHTMLFrameElement::GetMozbrowser(bool *aValue)
274 : {
275 0 : return GetBoolAttr(nsGkAtoms::mozbrowser, aValue);
276 : }
277 :
278 : NS_IMETHODIMP
279 0 : nsGenericHTMLFrameElement::SetMozbrowser(bool aValue)
280 : {
281 0 : nsresult rv = SetBoolAttr(nsGkAtoms::mozbrowser, aValue);
282 0 : if (NS_SUCCEEDED(rv)) {
283 0 : MaybeEnsureBrowserFrameListenersRegistered();
284 : }
285 0 : return rv;
286 : }
287 :
288 : /*
289 : * If this frame element is allowed to be a browser frame (i.e.,
290 : * GetReallyIsBrowser returns true), then make sure that it has the appropriate
291 : * event listeners enabled.
292 : */
293 : void
294 0 : nsGenericHTMLFrameElement::MaybeEnsureBrowserFrameListenersRegistered()
295 : {
296 0 : if (mBrowserFrameListenersRegistered) {
297 0 : return;
298 : }
299 :
300 : // If this frame passes the browser frame security check, ensure that its
301 : // listeners are active.
302 0 : if (!GetReallyIsBrowser()) {
303 0 : return;
304 : }
305 :
306 : // Not much we can do without a frameLoader. But EnsureFrameLoader will call
307 : // this function, so we'll get a chance to pass this test.
308 0 : if (!mFrameLoader) {
309 0 : return;
310 : }
311 :
312 0 : mBrowserFrameListenersRegistered = true;
313 :
314 : // Register ourselves as a web progress listener on the frameloader's
315 : // docshell.
316 0 : nsCOMPtr<nsIDocShell> docShell;
317 0 : mFrameLoader->GetDocShell(getter_AddRefs(docShell));
318 0 : nsCOMPtr<nsIWebProgress> webProgress = do_QueryInterface(docShell);
319 :
320 : // This adds a weak ref, so we don't have to worry about unregistering.
321 0 : if (webProgress) {
322 0 : webProgress->AddProgressListener(this,
323 : nsIWebProgress::NOTIFY_LOCATION |
324 0 : nsIWebProgress::NOTIFY_STATE_WINDOW);
325 : }
326 :
327 : // Register a listener for DOMTitleChanged on the window's chrome event
328 : // handler. The chrome event handler outlives this iframe, so we'll have to
329 : // unregister when the iframe is destroyed.
330 :
331 0 : nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(docShell);
332 0 : if (!window) {
333 : return;
334 : }
335 0 : MOZ_ASSERT(window->IsOuterWindow());
336 :
337 0 : nsIDOMEventTarget *chromeHandler = window->GetChromeEventHandler();
338 0 : if (!chromeHandler) {
339 : return;
340 : }
341 :
342 0 : MOZ_ASSERT(!mTitleChangedListener);
343 0 : mTitleChangedListener = new TitleChangedListener(this, chromeHandler);
344 0 : chromeHandler->AddSystemEventListener(NS_LITERAL_STRING("DOMTitleChanged"),
345 : mTitleChangedListener,
346 : /* useCapture = */ false,
347 0 : /* wantsUntrusted = */ false);
348 : }
349 :
350 : /**
351 : * Return true if this frame element has permission to send mozbrowser
352 : * events, and false otherwise.
353 : */
354 : bool
355 0 : nsGenericHTMLFrameElement::GetReallyIsBrowser()
356 : {
357 : // Fail if browser frames are globally disabled.
358 0 : if (!Preferences::GetBool("dom.mozBrowserFramesEnabled")) {
359 0 : return false;
360 : }
361 :
362 : // Fail if this frame doesn't have the mozbrowser attribute.
363 0 : bool isBrowser = false;
364 0 : GetMozbrowser(&isBrowser);
365 0 : if (!isBrowser) {
366 0 : return false;
367 : }
368 :
369 : // Fail if the node principal isn't trusted.
370 0 : nsIPrincipal *principal = NodePrincipal();
371 0 : nsCOMPtr<nsIURI> principalURI;
372 0 : principal->GetURI(getter_AddRefs(principalURI));
373 0 : if (!nsContentUtils::URIIsChromeOrInPref(principalURI,
374 0 : "dom.mozBrowserFramesWhitelist")) {
375 0 : return false;
376 : }
377 :
378 : // Otherwise, succeed.
379 0 : return true;
380 : }
381 :
382 : NS_IMETHODIMP
383 0 : nsGenericHTMLFrameElement::GetReallyIsBrowser(bool *aResult)
384 : {
385 0 : *aResult = GetReallyIsBrowser();
386 0 : return NS_OK;
387 : }
388 :
389 : /**
390 : * Fire a mozbrowser event, if we have permission.
391 : *
392 : * @param aEventName the event name (e.g. "locationchange"). "mozbrowser" is
393 : * added to the beginning of aEventName automatically.
394 : * @param aEventType the event type. Must be either "event" or "customevent".
395 : * @param aValue the value passed along with the event. This value will be
396 : * set as the event's "detail" property. This must be empty if
397 : * aEventType is "event".
398 : */
399 : nsresult
400 0 : nsGenericHTMLFrameElement::MaybeFireBrowserEvent(
401 : const nsAString &aEventName,
402 : const nsAString &aEventType,
403 : const nsAString &aValue /* = EmptyString() */)
404 : {
405 0 : MOZ_ASSERT(aEventType.EqualsLiteral("event") ||
406 0 : aEventType.EqualsLiteral("customevent"));
407 0 : MOZ_ASSERT_IF(aEventType.EqualsLiteral("event"),
408 0 : aValue.IsEmpty());
409 :
410 0 : if (!GetReallyIsBrowser()) {
411 0 : return NS_OK;
412 : }
413 :
414 0 : nsAutoString eventName;
415 0 : eventName.AppendLiteral("mozbrowser");
416 0 : eventName.Append(aEventName);
417 :
418 0 : nsCOMPtr<nsIDOMEvent> domEvent;
419 : nsEventDispatcher::CreateEvent(GetPresContext(), nsnull,
420 0 : aEventType, getter_AddRefs(domEvent));
421 :
422 0 : nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(domEvent);
423 0 : NS_ENSURE_STATE(privateEvent);
424 :
425 0 : nsresult rv = privateEvent->SetTrusted(true);
426 0 : NS_ENSURE_SUCCESS(rv, rv);
427 :
428 0 : if (aEventType.EqualsLiteral("customevent")) {
429 0 : nsCOMPtr<nsIDOMCustomEvent> customEvent = do_QueryInterface(domEvent);
430 0 : NS_ENSURE_STATE(customEvent);
431 :
432 0 : nsCOMPtr<nsIWritableVariant> value = new nsVariant();
433 0 : value->SetAsAString(aValue);
434 :
435 0 : rv = customEvent->InitCustomEvent(eventName,
436 : /* bubbles = */ false,
437 : /* cancelable = */ false,
438 0 : value);
439 0 : NS_ENSURE_SUCCESS(rv, rv);
440 : }
441 : else {
442 0 : rv = domEvent->InitEvent(eventName,
443 : /* bubbles = */ false,
444 0 : /* cancelable = */ false);
445 0 : NS_ENSURE_SUCCESS(rv, rv);
446 : }
447 :
448 0 : return (new nsAsyncDOMEvent(this, domEvent))->PostDOMEvent();
449 : }
450 :
451 : NS_IMETHODIMP
452 0 : nsGenericHTMLFrameElement::OnLocationChange(nsIWebProgress* aWebProgress,
453 : nsIRequest* aRequest,
454 : nsIURI* aURI,
455 : PRUint32 aFlags)
456 : {
457 : // aURI may be null, but that indicates an error case we don't care about.
458 0 : if (!aURI) {
459 0 : return NS_OK;
460 : }
461 :
462 0 : nsCAutoString spec;
463 0 : aURI->GetSpec(spec);
464 :
465 0 : MaybeFireBrowserEvent(NS_LITERAL_STRING("locationchange"),
466 0 : NS_LITERAL_STRING("customevent"),
467 0 : NS_ConvertUTF8toUTF16(spec));
468 0 : return NS_OK;
469 : }
470 :
471 : NS_IMETHODIMP
472 0 : nsGenericHTMLFrameElement::OnStateChange(nsIWebProgress* aProgress,
473 : nsIRequest* aRequest,
474 : PRUint32 aProgressStateFlags,
475 : nsresult aStatus)
476 : {
477 0 : if (!(aProgressStateFlags & STATE_IS_WINDOW)) {
478 0 : return NS_OK;
479 : }
480 :
481 0 : nsAutoString status;
482 0 : if (aProgressStateFlags & STATE_START) {
483 0 : MaybeFireBrowserEvent(NS_LITERAL_STRING("loadstart"),
484 0 : NS_LITERAL_STRING("event"));
485 : }
486 0 : else if (aProgressStateFlags & STATE_STOP) {
487 0 : MaybeFireBrowserEvent(NS_LITERAL_STRING("loadend"),
488 0 : NS_LITERAL_STRING("event"));
489 : }
490 :
491 0 : return NS_OK;
492 : }
493 :
494 : NS_IMETHODIMP
495 0 : nsGenericHTMLFrameElement::OnProgressChange(nsIWebProgress* aProgress,
496 : nsIRequest* aRequest,
497 : PRInt32 aCurSelfProgress,
498 : PRInt32 aMaxSelfProgress,
499 : PRInt32 aCurTotalProgress,
500 : PRInt32 aMaxTotalProgress)
501 : {
502 0 : return NS_OK;
503 : }
504 :
505 : NS_IMETHODIMP
506 0 : nsGenericHTMLFrameElement::OnStatusChange(nsIWebProgress* aWebProgress,
507 : nsIRequest* aRequest,
508 : nsresult aStatus,
509 : const PRUnichar* aMessage)
510 : {
511 0 : return NS_OK;
512 : }
513 :
514 : NS_IMETHODIMP
515 0 : nsGenericHTMLFrameElement::OnSecurityChange(nsIWebProgress *aWebProgress,
516 : nsIRequest *aRequest,
517 : PRUint32 state)
518 : {
519 0 : return NS_OK;
520 : }
521 :
522 0 : NS_IMPL_ISUPPORTS1(nsGenericHTMLFrameElement::TitleChangedListener,
523 : nsIDOMEventListener)
524 :
525 0 : nsGenericHTMLFrameElement::TitleChangedListener::TitleChangedListener(
526 : nsGenericHTMLFrameElement *aElement,
527 0 : nsIDOMEventTarget *aChromeHandler)
528 : {
529 : mElement =
530 0 : do_GetWeakReference(NS_ISUPPORTS_CAST(nsIDOMMozBrowserFrame*, aElement));
531 0 : mChromeHandler = do_GetWeakReference(aChromeHandler);
532 0 : }
533 :
534 : NS_IMETHODIMP
535 0 : nsGenericHTMLFrameElement::TitleChangedListener::HandleEvent(nsIDOMEvent *aEvent)
536 : {
537 : #ifdef DEBUG
538 : {
539 0 : nsString eventType;
540 0 : aEvent->GetType(eventType);
541 0 : MOZ_ASSERT(eventType.EqualsLiteral("DOMTitleChanged"));
542 : }
543 : #endif
544 :
545 0 : nsCOMPtr<nsIDOMMozBrowserFrame> element = do_QueryReferent(mElement);
546 0 : if (!element) {
547 : // Hm, our element is gone, but somehow we weren't unregistered?
548 0 : Unregister();
549 0 : return NS_OK;
550 : }
551 :
552 : nsGenericHTMLFrameElement* frameElement =
553 0 : static_cast<nsGenericHTMLFrameElement*>(element.get());
554 :
555 0 : nsCOMPtr<nsIDOMDocument> frameDocument;
556 0 : frameElement->GetContentDocument(getter_AddRefs(frameDocument));
557 0 : NS_ENSURE_STATE(frameDocument);
558 :
559 0 : nsCOMPtr<nsIDOMEventTarget> target;
560 0 : aEvent->GetTarget(getter_AddRefs(target));
561 0 : nsCOMPtr<nsIDOMDocument> targetDocument = do_QueryInterface(target);
562 0 : NS_ENSURE_STATE(targetDocument);
563 :
564 0 : if (frameDocument != targetDocument) {
565 : // This is a titlechange event for the wrong document!
566 0 : return NS_OK;
567 : }
568 :
569 0 : nsString newTitle;
570 0 : nsresult rv = targetDocument->GetTitle(newTitle);
571 0 : NS_ENSURE_SUCCESS(rv, rv);
572 :
573 : frameElement->MaybeFireBrowserEvent(
574 0 : NS_LITERAL_STRING("titlechange"),
575 0 : NS_LITERAL_STRING("customevent"),
576 0 : newTitle);
577 :
578 0 : return NS_OK;
579 : }
580 :
581 : void
582 0 : nsGenericHTMLFrameElement::TitleChangedListener::Unregister()
583 : {
584 0 : nsCOMPtr<nsIDOMEventTarget> chromeHandler = do_QueryReferent(mChromeHandler);
585 0 : if (!chromeHandler) {
586 : return;
587 : }
588 :
589 0 : chromeHandler->RemoveSystemEventListener(NS_LITERAL_STRING("DOMTitleChanged"),
590 0 : this, /* useCapture = */ false);
591 :
592 : // Careful; the call above may have removed the last strong reference to this
593 : // class, so don't dereference |this| here.
594 4392 : }
|