~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

Viewing changes to mozilla/content/xul/content/src/nsXULPopupListener.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 
2
/* ***** BEGIN LICENSE BLOCK *****
 
3
 * Version: NPL 1.1/GPL 2.0/LGPL 2.1
 
4
 *
 
5
 * The contents of this file are subject to the Netscape Public License
 
6
 * Version 1.1 (the "License"); you may not use this file except in
 
7
 * compliance with the License. You may obtain a copy of the License at
 
8
 * http://www.mozilla.org/NPL/
 
9
 *
 
10
 * Software distributed under the License is distributed on an "AS IS" basis,
 
11
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 
12
 * for the specific language governing rights and limitations under the
 
13
 * License.
 
14
 *
 
15
 * The Original Code is mozilla.org code.
 
16
 *
 
17
 * The Initial Developer of the Original Code is 
 
18
 * Netscape Communications Corporation.
 
19
 * Portions created by the Initial Developer are Copyright (C) 1998
 
20
 * the Initial Developer. All Rights Reserved.
 
21
 *
 
22
 * Contributor(s):
 
23
 * Original Author: David W. Hyatt (hyatt@netscape.com)
 
24
 *   Dean Tessman <dean_tessman@hotmail.com>
 
25
 *   Pierre Phaneuf <pp@ludusdesign.com>
 
26
 *   Robert O'Callahan <roc+moz@cs.cmu.edu>
 
27
 *
 
28
 *
 
29
 * Alternatively, the contents of this file may be used under the terms of
 
30
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 
31
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 
32
 * in which case the provisions of the GPL or the LGPL are applicable instead
 
33
 * of those above. If you wish to allow use of your version of this file only
 
34
 * under the terms of either the GPL or the LGPL, and not to allow others to
 
35
 * use your version of this file under the terms of the NPL, indicate your
 
36
 * decision by deleting the provisions above and replace them with the notice
 
37
 * and other provisions required by the GPL or the LGPL. If you do not delete
 
38
 * the provisions above, a recipient may use your version of this file under
 
39
 * the terms of any one of the NPL, the GPL or the LGPL.
 
40
 *
 
41
 * ***** END LICENSE BLOCK ***** */
 
42
 
 
43
/*
 
44
  This file provides the implementation for xul popup listener which
 
45
  tracks xul popups and context menus
 
46
 */
 
47
 
 
48
#include "nsCOMPtr.h"
 
49
#include "nsXULAtoms.h"
 
50
#include "nsIDOMElement.h"
 
51
#include "nsIDOMXULElement.h"
 
52
#include "nsIDOMNodeList.h"
 
53
#include "nsIDOMDocument.h"
 
54
#include "nsIDOMDocumentXBL.h"
 
55
#include "nsIXULPopupListener.h"
 
56
#include "nsIDOMMouseListener.h"
 
57
#include "nsIDOMContextMenuListener.h"
 
58
#include "nsContentCID.h"
 
59
#include "nsContentUtils.h"
 
60
 
 
61
#include "nsIScriptGlobalObject.h"
 
62
#include "nsIScriptContext.h"
 
63
#include "nsIDOMWindowInternal.h"
 
64
#include "nsIDOMXULDocument.h"
 
65
#include "nsIDocument.h"
 
66
#include "nsIContent.h"
 
67
#include "nsIDOMMouseEvent.h"
 
68
#include "nsIDOMNSUIEvent.h"
 
69
#include "nsIDOMEventTarget.h"
 
70
#include "nsIDOMNSEvent.h"
 
71
#include "nsIPrefService.h"
 
72
#include "nsIPrefBranch.h"
 
73
#include "nsIServiceManagerUtils.h"
 
74
#include "nsIPrincipal.h"
 
75
#include "nsIScriptSecurityManager.h"
 
76
 
 
77
#include "nsIBoxObject.h"
 
78
#include "nsIPopupBoxObject.h"
 
79
 
 
80
// for event firing in context menus
 
81
#include "nsIPresContext.h"
 
82
#include "nsIPresShell.h"
 
83
#include "nsIEventStateManager.h"
 
84
 
 
85
#include "nsIFrame.h"
 
86
 
 
87
// on win32 and os/2, context menus come up on mouse up. On other platforms,
 
88
// they appear on mouse down. Certain bits of code care about this difference.
 
89
#if !defined(XP_WIN) && !defined(XP_OS2)
 
90
#define NS_CONTEXT_MENU_IS_MOUSEUP 1
 
91
#endif
 
92
 
 
93
 
 
94
////////////////////////////////////////////////////////////////////////
 
95
// PopupListenerImpl
 
96
//
 
97
//   This is the popup listener implementation for popup menus and context menus.
 
98
//
 
99
class XULPopupListenerImpl : public nsIXULPopupListener,
 
100
                             public nsIDOMMouseListener,
 
101
                             public nsIDOMContextMenuListener
 
102
{
 
103
public:
 
104
    XULPopupListenerImpl(void);
 
105
    virtual ~XULPopupListenerImpl(void);
 
106
 
 
107
public:
 
108
    // nsISupports
 
109
    NS_DECL_ISUPPORTS
 
110
 
 
111
    // nsIXULPopupListener
 
112
    NS_IMETHOD Init(nsIDOMElement* aElement, const XULPopupType& popupType);
 
113
 
 
114
    // nsIDOMMouseListener
 
115
    NS_IMETHOD MouseDown(nsIDOMEvent* aMouseEvent);
 
116
    NS_IMETHOD MouseUp(nsIDOMEvent* aMouseEvent) { return NS_OK; };
 
117
    NS_IMETHOD MouseClick(nsIDOMEvent* aMouseEvent) { return NS_OK; };
 
118
    NS_IMETHOD MouseDblClick(nsIDOMEvent* aMouseEvent) { return NS_OK; };
 
119
    NS_IMETHOD MouseOver(nsIDOMEvent* aMouseEvent) { return NS_OK; };
 
120
    NS_IMETHOD MouseOut(nsIDOMEvent* aMouseEvent) { return NS_OK; };
 
121
 
 
122
    // nsIDOMContextMenuListener
 
123
    NS_IMETHOD ContextMenu(nsIDOMEvent* aContextMenuEvent);
 
124
 
 
125
    // nsIDOMEventListener
 
126
    NS_IMETHOD HandleEvent(nsIDOMEvent* anEvent) { return NS_OK; };
 
127
 
 
128
protected:
 
129
 
 
130
    virtual nsresult LaunchPopup(nsIDOMEvent* anEvent);
 
131
    virtual nsresult LaunchPopup(PRInt32 aClientX, PRInt32 aClientY) ;
 
132
    virtual void ClosePopup();
 
133
 
 
134
private:
 
135
 
 
136
    nsresult PreLaunchPopup(nsIDOMEvent* aMouseEvent);
 
137
    nsresult FireFocusOnTargetContent(nsIDOMNode* aTargetNode);
 
138
 
 
139
    // |mElement| is the node to which this listener is attached.
 
140
    nsIDOMElement* mElement;               // Weak ref. The element will go away first.
 
141
 
 
142
    // The popup that is getting shown on top of mElement.
 
143
    nsIDOMElement* mPopupContent; 
 
144
 
 
145
    // The type of the popup
 
146
    XULPopupType popupType;
 
147
    
 
148
};
 
149
 
 
150
////////////////////////////////////////////////////////////////////////
 
151
      
 
152
XULPopupListenerImpl::XULPopupListenerImpl(void)
 
153
  : mElement(nsnull), mPopupContent(nsnull)
 
154
{
 
155
}
 
156
 
 
157
XULPopupListenerImpl::~XULPopupListenerImpl(void)
 
158
{
 
159
  ClosePopup();
 
160
  
 
161
#ifdef DEBUG_REFS
 
162
    --gInstanceCount;
 
163
    fprintf(stdout, "%d - RDF: XULPopupListenerImpl\n", gInstanceCount);
 
164
#endif
 
165
}
 
166
 
 
167
NS_IMPL_ADDREF(XULPopupListenerImpl)
 
168
NS_IMPL_RELEASE(XULPopupListenerImpl)
 
169
 
 
170
 
 
171
NS_INTERFACE_MAP_BEGIN(XULPopupListenerImpl)
 
172
  NS_INTERFACE_MAP_ENTRY(nsIXULPopupListener)
 
173
  NS_INTERFACE_MAP_ENTRY(nsIDOMMouseListener)
 
174
  NS_INTERFACE_MAP_ENTRY(nsIDOMContextMenuListener)
 
175
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMMouseListener)
 
176
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULPopupListener)
 
177
NS_INTERFACE_MAP_END
 
178
 
 
179
NS_IMETHODIMP
 
180
XULPopupListenerImpl::Init(nsIDOMElement* aElement, const XULPopupType& popup)
 
181
{
 
182
  mElement = aElement; // Weak reference. Don't addref it.
 
183
  popupType = popup;
 
184
  return NS_OK;
 
185
}
 
186
 
 
187
////////////////////////////////////////////////////////////////
 
188
// nsIDOMMouseListener
 
189
 
 
190
nsresult
 
191
XULPopupListenerImpl::MouseDown(nsIDOMEvent* aMouseEvent)
 
192
{
 
193
  if(popupType != eXULPopupType_context)
 
194
    return PreLaunchPopup(aMouseEvent);
 
195
  else
 
196
    return NS_OK;
 
197
}
 
198
 
 
199
nsresult
 
200
XULPopupListenerImpl::ContextMenu(nsIDOMEvent* aMouseEvent)
 
201
{
 
202
  if(popupType == eXULPopupType_context)
 
203
    return PreLaunchPopup(aMouseEvent);
 
204
  else 
 
205
    return NS_OK;
 
206
}
 
207
 
 
208
nsresult
 
209
XULPopupListenerImpl::PreLaunchPopup(nsIDOMEvent* aMouseEvent)
 
210
{
 
211
  PRUint16 button;
 
212
 
 
213
  nsCOMPtr<nsIDOMMouseEvent> mouseEvent;
 
214
  mouseEvent = do_QueryInterface(aMouseEvent);
 
215
  if (!mouseEvent) {
 
216
    //non-ui event passed in.  bad things.
 
217
    return NS_OK;
 
218
  }
 
219
 
 
220
  // check if someone has attempted to prevent this action.
 
221
  nsCOMPtr<nsIDOMNSUIEvent> nsUIEvent;
 
222
  nsUIEvent = do_QueryInterface(mouseEvent);
 
223
  if (!nsUIEvent) {
 
224
    return NS_OK;
 
225
  }
 
226
 
 
227
  // Get the node that was clicked on.
 
228
  nsCOMPtr<nsIDOMEventTarget> target;
 
229
  mouseEvent->GetTarget(getter_AddRefs(target));
 
230
  nsCOMPtr<nsIDOMNode> targetNode = do_QueryInterface(target);
 
231
 
 
232
  PRBool preventDefault;
 
233
  nsUIEvent->GetPreventDefault(&preventDefault);
 
234
  if (preventDefault && targetNode && popupType == eXULPopupType_context) {
 
235
    // Someone called preventDefault on a context menu.
 
236
    // Let's make sure they are allowed to do so.
 
237
    nsCOMPtr<nsIPrefService> prefService =
 
238
      do_GetService(NS_PREFSERVICE_CONTRACTID);
 
239
 
 
240
    NS_ENSURE_TRUE(prefService, NS_ERROR_FAILURE);
 
241
 
 
242
    nsCOMPtr<nsIPrefBranch> prefBranch;
 
243
    prefService->GetBranch(nsnull, getter_AddRefs(prefBranch));
 
244
 
 
245
    PRBool eventEnabled;
 
246
    nsresult rv = prefBranch->GetBoolPref("dom.event.contextmenu.enabled",
 
247
                                          &eventEnabled);
 
248
    if (NS_SUCCEEDED(rv) && !eventEnabled) {
 
249
      // The user wants his contextmenus.  Let's make sure that this is a website
 
250
      // and not chrome since there could be places in chrome which don't want
 
251
      // contextmenus.
 
252
      nsCOMPtr<nsIDocument> doc;
 
253
      nsCOMPtr<nsIPrincipal> prin;
 
254
      nsContentUtils::GetDocumentAndPrincipal(targetNode,
 
255
                                              getter_AddRefs(doc),
 
256
                                              getter_AddRefs(prin));
 
257
      if (prin) {
 
258
        nsCOMPtr<nsIPrincipal> system;
 
259
        nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(system));
 
260
        if (prin != system) {
 
261
          // This isn't chrome.  Cancel the preventDefault() and
 
262
          // let the event go forth.
 
263
          preventDefault = PR_FALSE;
 
264
        }
 
265
      }
 
266
    }
 
267
  }
 
268
 
 
269
  if (preventDefault) {
 
270
    // someone called preventDefault. bail.
 
271
    return NS_OK;
 
272
  }
 
273
 
 
274
  // This is a gross hack to deal with a recursive popup situation happening in AIM code. 
 
275
  // See http://bugzilla.mozilla.org/show_bug.cgi?id=96920.
 
276
  // If a menu item child was clicked on that leads to a popup needing
 
277
  // to show, we know (guaranteed) that we're dealing with a menu or
 
278
  // submenu of an already-showing popup.  We don't need to do anything at all.
 
279
  if (popupType == eXULPopupType_popup) {
 
280
    nsCOMPtr<nsIContent> targetContent = do_QueryInterface(target);
 
281
    nsIAtom *tag = targetContent->Tag();
 
282
    if (tag == nsXULAtoms::menu || tag == nsXULAtoms::menuitem)
 
283
      return NS_OK;
 
284
  }
 
285
 
 
286
  // Get the document with the popup.
 
287
  nsCOMPtr<nsIContent> content = do_QueryInterface(mElement);
 
288
 
 
289
  // Turn the document into a XUL document so we can use SetPopupNode.
 
290
  nsCOMPtr<nsIDOMXULDocument> xulDocument = do_QueryInterface(content->GetDocument());
 
291
  if (!xulDocument) {
 
292
    NS_ERROR("Popup attached to an element that isn't in XUL!");
 
293
    return NS_ERROR_FAILURE;
 
294
  }
 
295
 
 
296
  // Store clicked-on node in xul document for context menus and menu popups.
 
297
  xulDocument->SetPopupNode( targetNode );
 
298
 
 
299
  nsCOMPtr<nsIDOMNSEvent> nsevent(do_QueryInterface(aMouseEvent));
 
300
 
 
301
  switch (popupType) {
 
302
    case eXULPopupType_popup:
 
303
      // Check for left mouse button down
 
304
      mouseEvent->GetButton(&button);
 
305
      if (button == 0) {
 
306
        // Time to launch a popup menu.
 
307
        LaunchPopup(aMouseEvent);
 
308
 
 
309
        if (nsevent) {
 
310
            nsevent->PreventBubble();
 
311
        }
 
312
 
 
313
        aMouseEvent->PreventDefault();
 
314
      }
 
315
      break;
 
316
    case eXULPopupType_context:
 
317
 
 
318
    // Time to launch a context menu
 
319
#ifndef NS_CONTEXT_MENU_IS_MOUSEUP
 
320
    // If the context menu launches on mousedown,
 
321
    // we have to fire focus on the content we clicked on
 
322
    FireFocusOnTargetContent(targetNode);
 
323
#endif
 
324
    LaunchPopup(aMouseEvent);
 
325
 
 
326
    if (nsevent) {
 
327
        nsevent->PreventBubble();
 
328
    }
 
329
 
 
330
    aMouseEvent->PreventDefault();
 
331
    break;
 
332
  }
 
333
  return NS_OK;
 
334
}
 
335
 
 
336
nsresult
 
337
XULPopupListenerImpl::FireFocusOnTargetContent(nsIDOMNode* aTargetNode)
 
338
{
 
339
  nsresult rv;
 
340
  nsCOMPtr<nsIDOMDocument> domDoc;
 
341
  rv = aTargetNode->GetOwnerDocument(getter_AddRefs(domDoc));
 
342
  if(NS_SUCCEEDED(rv) && domDoc)
 
343
  {
 
344
    nsCOMPtr<nsIPresContext> context;
 
345
    nsCOMPtr<nsIDocument> tempdoc = do_QueryInterface(domDoc);
 
346
 
 
347
    // Get nsIDOMElement for targetNode
 
348
    nsIPresShell *shell = tempdoc->GetShellAt(0);
 
349
    if (!shell)
 
350
      return NS_ERROR_FAILURE;
 
351
 
 
352
    shell->GetPresContext(getter_AddRefs(context));
 
353
 
 
354
    nsCOMPtr<nsIContent> content = do_QueryInterface(aTargetNode);
 
355
    nsIFrame* targetFrame;
 
356
    shell->GetPrimaryFrameFor(content, &targetFrame);
 
357
    if (!targetFrame) return NS_ERROR_FAILURE;
 
358
      
 
359
    PRBool suppressBlur = PR_FALSE;
 
360
    const nsStyleUserInterface* ui = targetFrame->GetStyleUserInterface();
 
361
    suppressBlur = (ui->mUserFocus == NS_STYLE_USER_FOCUS_IGNORE);
 
362
 
 
363
    nsCOMPtr<nsIDOMElement> element;
 
364
    nsCOMPtr<nsIContent> newFocus = do_QueryInterface(content);
 
365
 
 
366
    nsIFrame* currFrame = targetFrame;
 
367
    // Look for the nearest enclosing focusable frame.
 
368
    while (currFrame) {
 
369
        const nsStyleUserInterface* ui = currFrame->GetStyleUserInterface();
 
370
        if ((ui->mUserFocus != NS_STYLE_USER_FOCUS_IGNORE) &&
 
371
            (ui->mUserFocus != NS_STYLE_USER_FOCUS_NONE)) 
 
372
        {
 
373
          newFocus = currFrame->GetContent();
 
374
          nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(newFocus));
 
375
          if (domElement) {
 
376
            element = domElement;
 
377
            break;
 
378
          }
 
379
        }
 
380
        currFrame = currFrame->GetParent();
 
381
    } 
 
382
    nsCOMPtr<nsIContent> focusableContent = do_QueryInterface(element);
 
383
    nsIEventStateManager *esm = context->EventStateManager();
 
384
 
 
385
    if (focusableContent)
 
386
      focusableContent->SetFocus(context);
 
387
    else if (!suppressBlur)
 
388
      esm->SetContentState(nsnull, NS_EVENT_STATE_FOCUS);
 
389
 
 
390
    esm->SetContentState(focusableContent, NS_EVENT_STATE_ACTIVE);
 
391
  }
 
392
  return rv;
 
393
}
 
394
 
 
395
//
 
396
// ClosePopup
 
397
//
 
398
// Do everything needed to shut down the popup.
 
399
//
 
400
// NOTE: This routine is safe to call even if the popup is already closed.
 
401
//
 
402
void
 
403
XULPopupListenerImpl :: ClosePopup ( )
 
404
{
 
405
  if ( mPopupContent ) {
 
406
    nsCOMPtr<nsIDOMXULElement> popupElement(do_QueryInterface(mPopupContent));
 
407
    nsCOMPtr<nsIBoxObject> boxObject;
 
408
    if (popupElement)
 
409
      popupElement->GetBoxObject(getter_AddRefs(boxObject));
 
410
    nsCOMPtr<nsIPopupBoxObject> popupObject(do_QueryInterface(boxObject));
 
411
    if (popupObject)
 
412
      popupObject->HidePopup();
 
413
 
 
414
    mPopupContent = nsnull;  // release the popup
 
415
  }
 
416
 
 
417
} // ClosePopup
 
418
 
 
419
//
 
420
// LaunchPopup
 
421
//
 
422
nsresult
 
423
XULPopupListenerImpl::LaunchPopup ( nsIDOMEvent* anEvent )
 
424
{
 
425
  // Retrieve our x and y position.
 
426
  nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(anEvent) );
 
427
  if (!mouseEvent) {
 
428
    //non-ui event passed in.  bad things.
 
429
    return NS_OK;
 
430
  }
 
431
 
 
432
  PRInt32 xPos, yPos;
 
433
  mouseEvent->GetClientX(&xPos); 
 
434
  mouseEvent->GetClientY(&yPos); 
 
435
 
 
436
  return LaunchPopup(xPos, yPos);
 
437
}
 
438
 
 
439
 
 
440
static void
 
441
GetImmediateChild(nsIContent* aContent, nsIAtom *aTag, nsIContent** aResult) 
 
442
{
 
443
  *aResult = nsnull;
 
444
  PRInt32 childCount = aContent->GetChildCount();
 
445
  for (PRInt32 i = 0; i < childCount; i++) {
 
446
    nsIContent *child = aContent->GetChildAt(i);
 
447
    if (child->Tag() == aTag) {
 
448
      *aResult = child;
 
449
      NS_ADDREF(*aResult);
 
450
      return;
 
451
    }
 
452
  }
 
453
 
 
454
  return;
 
455
}
 
456
 
 
457
static void ConvertPosition(nsIDOMElement* aPopupElt, nsString& aAnchor, nsString& aAlign, PRInt32& aY)
 
458
{
 
459
  nsAutoString position;
 
460
  aPopupElt->GetAttribute(NS_LITERAL_STRING("position"), position);
 
461
  if (position.IsEmpty())
 
462
    return;
 
463
 
 
464
  if (position.Equals(NS_LITERAL_STRING("before_start"))) {
 
465
    aAnchor.Assign(NS_LITERAL_STRING("topleft"));
 
466
    aAlign.Assign(NS_LITERAL_STRING("bottomleft"));
 
467
  }
 
468
  else if (position.Equals(NS_LITERAL_STRING("before_end"))) {
 
469
    aAnchor.Assign(NS_LITERAL_STRING("topright"));
 
470
    aAlign.Assign(NS_LITERAL_STRING("bottomright"));
 
471
  }
 
472
  else if (position.Equals(NS_LITERAL_STRING("after_start"))) {
 
473
    aAnchor.Assign(NS_LITERAL_STRING("bottomleft"));
 
474
    aAlign.Assign(NS_LITERAL_STRING("topleft"));
 
475
  }
 
476
  else if (position.Equals(NS_LITERAL_STRING("after_end"))) {
 
477
    aAnchor.Assign(NS_LITERAL_STRING("bottomright"));
 
478
    aAlign.Assign(NS_LITERAL_STRING("topright"));
 
479
  }
 
480
  else if (position.Equals(NS_LITERAL_STRING("start_before"))) {
 
481
    aAnchor.Assign(NS_LITERAL_STRING("topleft"));
 
482
    aAlign.Assign(NS_LITERAL_STRING("topright"));
 
483
  }
 
484
  else if (position.Equals(NS_LITERAL_STRING("start_after"))) {
 
485
    aAnchor.Assign(NS_LITERAL_STRING("bottomleft"));
 
486
    aAlign.Assign(NS_LITERAL_STRING("bottomright"));
 
487
  }
 
488
  else if (position.Equals(NS_LITERAL_STRING("end_before"))) {
 
489
    aAnchor.Assign(NS_LITERAL_STRING("topright"));
 
490
    aAlign.Assign(NS_LITERAL_STRING("topleft"));
 
491
  }
 
492
  else if (position.Equals(NS_LITERAL_STRING("end_after"))) {
 
493
    aAnchor.Assign(NS_LITERAL_STRING("bottomright"));
 
494
    aAlign.Assign(NS_LITERAL_STRING("bottomleft"));
 
495
  }
 
496
  else if (position.Equals(NS_LITERAL_STRING("overlap"))) {
 
497
    aAnchor.Assign(NS_LITERAL_STRING("topleft"));
 
498
    aAlign.Assign(NS_LITERAL_STRING("topleft"));
 
499
  }
 
500
  else if (position.Equals(NS_LITERAL_STRING("after_pointer")))
 
501
    aY += 21;
 
502
}
 
503
 
 
504
//
 
505
// LaunchPopup
 
506
//
 
507
// Given the element on which the event was triggered and the mouse locations in
 
508
// Client and widget coordinates, popup a new window showing the appropriate 
 
509
// content.
 
510
//
 
511
// This looks for an attribute on |aElement| of the appropriate popup type 
 
512
// (popup, context) and uses that attribute's value as an ID for
 
513
// the popup content in the document.
 
514
//
 
515
nsresult
 
516
XULPopupListenerImpl::LaunchPopup(PRInt32 aClientX, PRInt32 aClientY)
 
517
{
 
518
  nsresult rv = NS_OK;
 
519
 
 
520
  nsAutoString type(NS_LITERAL_STRING("popup"));
 
521
  if ( popupType == eXULPopupType_context ) {
 
522
    type.Assign(NS_LITERAL_STRING("context"));
 
523
    
 
524
    // position the menu two pixels down and to the right from the current
 
525
    // mouse position. This makes it easier to dismiss the menu by just
 
526
    // clicking.
 
527
    aClientX += 2;
 
528
    aClientY += 2;
 
529
  }
 
530
 
 
531
  nsAutoString identifier;
 
532
  mElement->GetAttribute(type, identifier);
 
533
 
 
534
  if (identifier.IsEmpty()) {
 
535
    if (type.Equals(NS_LITERAL_STRING("popup")))
 
536
      mElement->GetAttribute(NS_LITERAL_STRING("menu"), identifier);
 
537
    else if (type.Equals(NS_LITERAL_STRING("context")))
 
538
      mElement->GetAttribute(NS_LITERAL_STRING("contextmenu"), identifier);
 
539
    if (identifier.IsEmpty())
 
540
      return rv;
 
541
  }
 
542
 
 
543
  // Try to find the popup content and the document.
 
544
  nsCOMPtr<nsIContent> content = do_QueryInterface(mElement);
 
545
  nsCOMPtr<nsIDocument> document = content->GetDocument();
 
546
 
 
547
  // Turn the document into a DOM document so we can use getElementById
 
548
  nsCOMPtr<nsIDOMDocument> domDocument = do_QueryInterface(document);
 
549
  if (!domDocument) {
 
550
    NS_ERROR("Popup attached to an element that isn't in XUL!");
 
551
    return NS_ERROR_FAILURE;
 
552
  }
 
553
 
 
554
  // Handle the _child case for popups and context menus
 
555
  nsCOMPtr<nsIDOMElement> popupContent;
 
556
 
 
557
  if (identifier == NS_LITERAL_STRING("_child")) {
 
558
    nsCOMPtr<nsIContent> popup;
 
559
 
 
560
    GetImmediateChild(content, nsXULAtoms::menupopup, getter_AddRefs(popup));
 
561
    if (popup)
 
562
      popupContent = do_QueryInterface(popup);
 
563
    else {
 
564
      nsCOMPtr<nsIDOMDocumentXBL> nsDoc(do_QueryInterface(domDocument));
 
565
      nsCOMPtr<nsIDOMNodeList> list;
 
566
      nsDoc->GetAnonymousNodes(mElement, getter_AddRefs(list));
 
567
      if (list) {
 
568
        PRUint32 ctr,listLength;
 
569
        nsCOMPtr<nsIDOMNode> node;
 
570
        list->GetLength(&listLength);
 
571
        for (ctr = 0; ctr < listLength; ctr++) {
 
572
          list->Item(ctr, getter_AddRefs(node));
 
573
          nsCOMPtr<nsIContent> childContent(do_QueryInterface(node));
 
574
 
 
575
          nsINodeInfo *ni = childContent->GetNodeInfo();
 
576
 
 
577
          if (ni && ni->Equals(nsXULAtoms::menupopup, kNameSpaceID_XUL)) {
 
578
            popupContent = do_QueryInterface(childContent);
 
579
            break;
 
580
          }
 
581
        }
 
582
      }
 
583
    }
 
584
  }
 
585
  else if (NS_FAILED(rv = domDocument->GetElementById(identifier,
 
586
                                              getter_AddRefs(popupContent)))) {
 
587
    // Use getElementById to obtain the popup content and gracefully fail if 
 
588
    // we didn't find any popup content in the document. 
 
589
    NS_ERROR("GetElementById had some kind of spasm.");
 
590
    return rv;
 
591
  }
 
592
  if ( !popupContent )
 
593
    return NS_OK;
 
594
 
 
595
  // We have some popup content. Obtain our window.
 
596
  nsCOMPtr<nsIDOMWindowInternal> domWindow =
 
597
    do_QueryInterface(document->GetScriptGlobalObject());
 
598
 
 
599
  if (domWindow) {
 
600
    // Find out if we're anchored.
 
601
    mPopupContent = popupContent.get();
 
602
 
 
603
    nsAutoString anchorAlignment;
 
604
    mPopupContent->GetAttribute(NS_LITERAL_STRING("popupanchor"), anchorAlignment);
 
605
 
 
606
    nsAutoString popupAlignment;
 
607
    mPopupContent->GetAttribute(NS_LITERAL_STRING("popupalign"), popupAlignment);
 
608
 
 
609
    PRInt32 xPos = aClientX, yPos = aClientY;
 
610
 
 
611
    ConvertPosition(mPopupContent, anchorAlignment, popupAlignment, yPos);
 
612
    if (!anchorAlignment.IsEmpty() && !popupAlignment.IsEmpty())
 
613
      xPos = yPos = -1;
 
614
 
 
615
    nsCOMPtr<nsIBoxObject> popupBox;
 
616
    nsCOMPtr<nsIDOMXULElement> xulPopupElt(do_QueryInterface(mPopupContent));
 
617
    xulPopupElt->GetBoxObject(getter_AddRefs(popupBox));
 
618
    nsCOMPtr<nsIPopupBoxObject> popupBoxObject(do_QueryInterface(popupBox));
 
619
    if (popupBoxObject)
 
620
      popupBoxObject->ShowPopup(mElement, mPopupContent, xPos, yPos, 
 
621
                                type.get(), anchorAlignment.get(), 
 
622
                                popupAlignment.get());
 
623
  }
 
624
 
 
625
  return NS_OK;
 
626
}
 
627
 
 
628
////////////////////////////////////////////////////////////////
 
629
nsresult
 
630
NS_NewXULPopupListener(nsIXULPopupListener** pop)
 
631
{
 
632
    XULPopupListenerImpl* popup = new XULPopupListenerImpl();
 
633
    if (!popup)
 
634
      return NS_ERROR_OUT_OF_MEMORY;
 
635
    
 
636
    NS_ADDREF(popup);
 
637
    *pop = popup;
 
638
    return NS_OK;
 
639
}