layout/xul/nsMenuBarListener.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "nsMenuBarListener.h"
michael@0 7 #include "nsMenuBarFrame.h"
michael@0 8 #include "nsMenuPopupFrame.h"
michael@0 9 #include "nsIDOMEvent.h"
michael@0 10
michael@0 11 // Drag & Drop, Clipboard
michael@0 12 #include "nsIServiceManager.h"
michael@0 13 #include "nsWidgetsCID.h"
michael@0 14 #include "nsCOMPtr.h"
michael@0 15 #include "nsIDOMKeyEvent.h"
michael@0 16 #include "nsIContent.h"
michael@0 17 #include "nsIDOMNode.h"
michael@0 18 #include "nsIDOMElement.h"
michael@0 19
michael@0 20 #include "nsContentUtils.h"
michael@0 21 #include "mozilla/Preferences.h"
michael@0 22 #include "mozilla/TextEvents.h"
michael@0 23
michael@0 24 using namespace mozilla;
michael@0 25
michael@0 26 /*
michael@0 27 * nsMenuBarListener implementation
michael@0 28 */
michael@0 29
michael@0 30 NS_IMPL_ISUPPORTS(nsMenuBarListener, nsIDOMEventListener)
michael@0 31
michael@0 32 #define MODIFIER_SHIFT 1
michael@0 33 #define MODIFIER_CONTROL 2
michael@0 34 #define MODIFIER_ALT 4
michael@0 35 #define MODIFIER_META 8
michael@0 36 #define MODIFIER_OS 16
michael@0 37
michael@0 38 ////////////////////////////////////////////////////////////////////////
michael@0 39
michael@0 40 int32_t nsMenuBarListener::mAccessKey = -1;
michael@0 41 uint32_t nsMenuBarListener::mAccessKeyMask = 0;
michael@0 42 bool nsMenuBarListener::mAccessKeyFocuses = false;
michael@0 43
michael@0 44 nsMenuBarListener::nsMenuBarListener(nsMenuBarFrame* aMenuBar)
michael@0 45 :mAccessKeyDown(false), mAccessKeyDownCanceled(false)
michael@0 46 {
michael@0 47 mMenuBarFrame = aMenuBar;
michael@0 48 }
michael@0 49
michael@0 50 ////////////////////////////////////////////////////////////////////////
michael@0 51 nsMenuBarListener::~nsMenuBarListener()
michael@0 52 {
michael@0 53 }
michael@0 54
michael@0 55 void
michael@0 56 nsMenuBarListener::InitializeStatics()
michael@0 57 {
michael@0 58 Preferences::AddBoolVarCache(&mAccessKeyFocuses,
michael@0 59 "ui.key.menuAccessKeyFocuses");
michael@0 60 }
michael@0 61
michael@0 62 nsresult
michael@0 63 nsMenuBarListener::GetMenuAccessKey(int32_t* aAccessKey)
michael@0 64 {
michael@0 65 if (!aAccessKey)
michael@0 66 return NS_ERROR_INVALID_POINTER;
michael@0 67 InitAccessKey();
michael@0 68 *aAccessKey = mAccessKey;
michael@0 69 return NS_OK;
michael@0 70 }
michael@0 71
michael@0 72 void nsMenuBarListener::InitAccessKey()
michael@0 73 {
michael@0 74 if (mAccessKey >= 0)
michael@0 75 return;
michael@0 76
michael@0 77 // Compiled-in defaults, in case we can't get LookAndFeel --
michael@0 78 // mac doesn't have menu shortcuts, other platforms use alt.
michael@0 79 #ifdef XP_MACOSX
michael@0 80 mAccessKey = 0;
michael@0 81 mAccessKeyMask = 0;
michael@0 82 #else
michael@0 83 mAccessKey = nsIDOMKeyEvent::DOM_VK_ALT;
michael@0 84 mAccessKeyMask = MODIFIER_ALT;
michael@0 85 #endif
michael@0 86
michael@0 87 // Get the menu access key value from prefs, overriding the default:
michael@0 88 mAccessKey = Preferences::GetInt("ui.key.menuAccessKey", mAccessKey);
michael@0 89 if (mAccessKey == nsIDOMKeyEvent::DOM_VK_SHIFT)
michael@0 90 mAccessKeyMask = MODIFIER_SHIFT;
michael@0 91 else if (mAccessKey == nsIDOMKeyEvent::DOM_VK_CONTROL)
michael@0 92 mAccessKeyMask = MODIFIER_CONTROL;
michael@0 93 else if (mAccessKey == nsIDOMKeyEvent::DOM_VK_ALT)
michael@0 94 mAccessKeyMask = MODIFIER_ALT;
michael@0 95 else if (mAccessKey == nsIDOMKeyEvent::DOM_VK_META)
michael@0 96 mAccessKeyMask = MODIFIER_META;
michael@0 97 else if (mAccessKey == nsIDOMKeyEvent::DOM_VK_WIN)
michael@0 98 mAccessKeyMask = MODIFIER_OS;
michael@0 99 }
michael@0 100
michael@0 101 void
michael@0 102 nsMenuBarListener::ToggleMenuActiveState()
michael@0 103 {
michael@0 104 nsMenuFrame* closemenu = mMenuBarFrame->ToggleMenuActiveState();
michael@0 105 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
michael@0 106 if (pm && closemenu) {
michael@0 107 nsMenuPopupFrame* popupFrame = closemenu->GetPopup();
michael@0 108 if (popupFrame)
michael@0 109 pm->HidePopup(popupFrame->GetContent(), false, false, true, false);
michael@0 110 }
michael@0 111 }
michael@0 112
michael@0 113 ////////////////////////////////////////////////////////////////////////
michael@0 114 nsresult
michael@0 115 nsMenuBarListener::KeyUp(nsIDOMEvent* aKeyEvent)
michael@0 116 {
michael@0 117 nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aKeyEvent);
michael@0 118 if (!keyEvent) {
michael@0 119 return NS_OK;
michael@0 120 }
michael@0 121
michael@0 122 InitAccessKey();
michael@0 123
michael@0 124 //handlers shouldn't be triggered by non-trusted events.
michael@0 125 bool trustedEvent = false;
michael@0 126 aKeyEvent->GetIsTrusted(&trustedEvent);
michael@0 127
michael@0 128 if (!trustedEvent) {
michael@0 129 return NS_OK;
michael@0 130 }
michael@0 131
michael@0 132 if (mAccessKey && mAccessKeyFocuses)
michael@0 133 {
michael@0 134 bool defaultPrevented = false;
michael@0 135 aKeyEvent->GetDefaultPrevented(&defaultPrevented);
michael@0 136
michael@0 137 // On a press of the ALT key by itself, we toggle the menu's
michael@0 138 // active/inactive state.
michael@0 139 // Get the ascii key code.
michael@0 140 uint32_t theChar;
michael@0 141 keyEvent->GetKeyCode(&theChar);
michael@0 142
michael@0 143 if (!defaultPrevented && mAccessKeyDown && !mAccessKeyDownCanceled &&
michael@0 144 (int32_t)theChar == mAccessKey)
michael@0 145 {
michael@0 146 // The access key was down and is now up, and no other
michael@0 147 // keys were pressed in between.
michael@0 148 if (!mMenuBarFrame->IsActive()) {
michael@0 149 mMenuBarFrame->SetActiveByKeyboard();
michael@0 150 }
michael@0 151 ToggleMenuActiveState();
michael@0 152 }
michael@0 153 mAccessKeyDown = false;
michael@0 154 mAccessKeyDownCanceled = false;
michael@0 155
michael@0 156 bool active = mMenuBarFrame->IsActive();
michael@0 157 if (active) {
michael@0 158 aKeyEvent->StopPropagation();
michael@0 159 aKeyEvent->PreventDefault();
michael@0 160 return NS_OK; // I am consuming event
michael@0 161 }
michael@0 162 }
michael@0 163
michael@0 164 return NS_OK; // means I am NOT consuming event
michael@0 165 }
michael@0 166
michael@0 167 ////////////////////////////////////////////////////////////////////////
michael@0 168 nsresult
michael@0 169 nsMenuBarListener::KeyPress(nsIDOMEvent* aKeyEvent)
michael@0 170 {
michael@0 171 // if event has already been handled, bail
michael@0 172 if (aKeyEvent) {
michael@0 173 bool eventHandled = false;
michael@0 174 aKeyEvent->GetDefaultPrevented(&eventHandled);
michael@0 175 if (eventHandled) {
michael@0 176 return NS_OK; // don't consume event
michael@0 177 }
michael@0 178 }
michael@0 179
michael@0 180 //handlers shouldn't be triggered by non-trusted events.
michael@0 181 bool trustedEvent = false;
michael@0 182 if (aKeyEvent) {
michael@0 183 aKeyEvent->GetIsTrusted(&trustedEvent);
michael@0 184 }
michael@0 185
michael@0 186 if (!trustedEvent)
michael@0 187 return NS_OK;
michael@0 188
michael@0 189 nsresult retVal = NS_OK; // default is to not consume event
michael@0 190
michael@0 191 InitAccessKey();
michael@0 192
michael@0 193 if (mAccessKey)
michael@0 194 {
michael@0 195 bool preventDefault;
michael@0 196 aKeyEvent->GetDefaultPrevented(&preventDefault);
michael@0 197 if (!preventDefault) {
michael@0 198 nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aKeyEvent);
michael@0 199 uint32_t keyCode, charCode;
michael@0 200 keyEvent->GetKeyCode(&keyCode);
michael@0 201 keyEvent->GetCharCode(&charCode);
michael@0 202
michael@0 203 bool hasAccessKeyCandidates = charCode != 0;
michael@0 204 if (!hasAccessKeyCandidates) {
michael@0 205 WidgetKeyboardEvent* nativeKeyEvent =
michael@0 206 aKeyEvent->GetInternalNSEvent()->AsKeyboardEvent();
michael@0 207 if (nativeKeyEvent) {
michael@0 208 nsAutoTArray<uint32_t, 10> keys;
michael@0 209 nsContentUtils::GetAccessKeyCandidates(nativeKeyEvent, keys);
michael@0 210 hasAccessKeyCandidates = !keys.IsEmpty();
michael@0 211 }
michael@0 212 }
michael@0 213
michael@0 214 // Cancel the access key flag unless we are pressing the access key.
michael@0 215 if (keyCode != (uint32_t)mAccessKey) {
michael@0 216 mAccessKeyDownCanceled = true;
michael@0 217 }
michael@0 218
michael@0 219 if (IsAccessKeyPressed(keyEvent) && hasAccessKeyCandidates) {
michael@0 220 // Do shortcut navigation.
michael@0 221 // A letter was pressed. We want to see if a shortcut gets matched. If
michael@0 222 // so, we'll know the menu got activated.
michael@0 223 nsMenuFrame* result = mMenuBarFrame->FindMenuWithShortcut(keyEvent);
michael@0 224 if (result) {
michael@0 225 mMenuBarFrame->SetActiveByKeyboard();
michael@0 226 mMenuBarFrame->SetActive(true);
michael@0 227 result->OpenMenu(true);
michael@0 228
michael@0 229 // The opened menu will listen next keyup event.
michael@0 230 // Therefore, we should clear the keydown flags here.
michael@0 231 mAccessKeyDown = mAccessKeyDownCanceled = false;
michael@0 232
michael@0 233 aKeyEvent->StopPropagation();
michael@0 234 aKeyEvent->PreventDefault();
michael@0 235 retVal = NS_OK; // I am consuming event
michael@0 236 }
michael@0 237 }
michael@0 238 #ifndef XP_MACOSX
michael@0 239 // Also need to handle F10 specially on Non-Mac platform.
michael@0 240 else if (keyCode == NS_VK_F10) {
michael@0 241 if ((GetModifiers(keyEvent) & ~MODIFIER_CONTROL) == 0) {
michael@0 242 // The F10 key just went down by itself or with ctrl pressed.
michael@0 243 // In Windows, both of these activate the menu bar.
michael@0 244 mMenuBarFrame->SetActiveByKeyboard();
michael@0 245 ToggleMenuActiveState();
michael@0 246
michael@0 247 if (mMenuBarFrame->IsActive()) {
michael@0 248 #ifdef MOZ_WIDGET_GTK
michael@0 249 // In GTK, this also opens the first menu.
michael@0 250 mMenuBarFrame->GetCurrentMenuItem()->OpenMenu(true);
michael@0 251 #endif
michael@0 252 aKeyEvent->StopPropagation();
michael@0 253 aKeyEvent->PreventDefault();
michael@0 254 return NS_OK; // consume the event
michael@0 255 }
michael@0 256 }
michael@0 257 }
michael@0 258 #endif // !XP_MACOSX
michael@0 259 }
michael@0 260 }
michael@0 261
michael@0 262 return retVal;
michael@0 263 }
michael@0 264
michael@0 265 bool
michael@0 266 nsMenuBarListener::IsAccessKeyPressed(nsIDOMKeyEvent* aKeyEvent)
michael@0 267 {
michael@0 268 InitAccessKey();
michael@0 269 // No other modifiers are allowed to be down except for Shift.
michael@0 270 uint32_t modifiers = GetModifiers(aKeyEvent);
michael@0 271
michael@0 272 return (mAccessKeyMask != MODIFIER_SHIFT &&
michael@0 273 (modifiers & mAccessKeyMask) &&
michael@0 274 (modifiers & ~(mAccessKeyMask | MODIFIER_SHIFT)) == 0);
michael@0 275 }
michael@0 276
michael@0 277 uint32_t
michael@0 278 nsMenuBarListener::GetModifiers(nsIDOMKeyEvent* aKeyEvent)
michael@0 279 {
michael@0 280 uint32_t modifiers = 0;
michael@0 281 WidgetInputEvent* inputEvent =
michael@0 282 aKeyEvent->GetInternalNSEvent()->AsInputEvent();
michael@0 283 MOZ_ASSERT(inputEvent);
michael@0 284
michael@0 285 if (inputEvent->IsShift()) {
michael@0 286 modifiers |= MODIFIER_SHIFT;
michael@0 287 }
michael@0 288
michael@0 289 if (inputEvent->IsControl()) {
michael@0 290 modifiers |= MODIFIER_CONTROL;
michael@0 291 }
michael@0 292
michael@0 293 if (inputEvent->IsAlt()) {
michael@0 294 modifiers |= MODIFIER_ALT;
michael@0 295 }
michael@0 296
michael@0 297 if (inputEvent->IsMeta()) {
michael@0 298 modifiers |= MODIFIER_META;
michael@0 299 }
michael@0 300
michael@0 301 if (inputEvent->IsOS()) {
michael@0 302 modifiers |= MODIFIER_OS;
michael@0 303 }
michael@0 304
michael@0 305 return modifiers;
michael@0 306 }
michael@0 307
michael@0 308 ////////////////////////////////////////////////////////////////////////
michael@0 309 nsresult
michael@0 310 nsMenuBarListener::KeyDown(nsIDOMEvent* aKeyEvent)
michael@0 311 {
michael@0 312 InitAccessKey();
michael@0 313
michael@0 314 //handlers shouldn't be triggered by non-trusted events.
michael@0 315 bool trustedEvent = false;
michael@0 316 if (aKeyEvent) {
michael@0 317 aKeyEvent->GetIsTrusted(&trustedEvent);
michael@0 318 }
michael@0 319
michael@0 320 if (!trustedEvent)
michael@0 321 return NS_OK;
michael@0 322
michael@0 323 if (mAccessKey && mAccessKeyFocuses)
michael@0 324 {
michael@0 325 bool defaultPrevented = false;
michael@0 326 aKeyEvent->GetDefaultPrevented(&defaultPrevented);
michael@0 327
michael@0 328 nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aKeyEvent);
michael@0 329 uint32_t theChar;
michael@0 330 keyEvent->GetKeyCode(&theChar);
michael@0 331
michael@0 332 // No other modifiers can be down.
michael@0 333 // Especially CTRL. CTRL+ALT == AltGR, and we'll fuck up on non-US
michael@0 334 // enhanced 102-key keyboards if we don't check this.
michael@0 335 bool isAccessKeyDownEvent =
michael@0 336 ((theChar == (uint32_t)mAccessKey) &&
michael@0 337 (GetModifiers(keyEvent) & ~mAccessKeyMask) == 0);
michael@0 338
michael@0 339 if (!mAccessKeyDown) {
michael@0 340 // If accesskey isn't being pressed and the key isn't the accesskey,
michael@0 341 // ignore the event.
michael@0 342 if (!isAccessKeyDownEvent) {
michael@0 343 return NS_OK;
michael@0 344 }
michael@0 345
michael@0 346 // Otherwise, accept the accesskey state.
michael@0 347 mAccessKeyDown = true;
michael@0 348 // If default is prevented already, cancel the access key down.
michael@0 349 mAccessKeyDownCanceled = defaultPrevented;
michael@0 350 return NS_OK;
michael@0 351 }
michael@0 352
michael@0 353 // If the pressed accesskey was canceled already or the event was
michael@0 354 // consumed already, ignore the event.
michael@0 355 if (mAccessKeyDownCanceled || defaultPrevented) {
michael@0 356 return NS_OK;
michael@0 357 }
michael@0 358
michael@0 359 // Some key other than the access key just went down,
michael@0 360 // so we won't activate the menu bar when the access key is released.
michael@0 361 mAccessKeyDownCanceled = !isAccessKeyDownEvent;
michael@0 362 }
michael@0 363
michael@0 364 return NS_OK; // means I am NOT consuming event
michael@0 365 }
michael@0 366
michael@0 367 ////////////////////////////////////////////////////////////////////////
michael@0 368
michael@0 369 nsresult
michael@0 370 nsMenuBarListener::Blur(nsIDOMEvent* aEvent)
michael@0 371 {
michael@0 372 if (!mMenuBarFrame->IsMenuOpen() && mMenuBarFrame->IsActive()) {
michael@0 373 ToggleMenuActiveState();
michael@0 374 }
michael@0 375 // Reset the accesskey state because we cannot receive the keyup event for
michael@0 376 // the pressing accesskey.
michael@0 377 mAccessKeyDown = false;
michael@0 378 mAccessKeyDownCanceled = false;
michael@0 379 return NS_OK; // means I am NOT consuming event
michael@0 380 }
michael@0 381
michael@0 382 ////////////////////////////////////////////////////////////////////////
michael@0 383 nsresult
michael@0 384 nsMenuBarListener::MouseDown(nsIDOMEvent* aMouseEvent)
michael@0 385 {
michael@0 386 // NOTE: MouseDown method listens all phases
michael@0 387
michael@0 388 // Even if the mousedown event is canceled, it means the user don't want
michael@0 389 // to activate the menu. Therefore, we need to record it at capturing (or
michael@0 390 // target) phase.
michael@0 391 if (mAccessKeyDown) {
michael@0 392 mAccessKeyDownCanceled = true;
michael@0 393 }
michael@0 394
michael@0 395 uint16_t phase = 0;
michael@0 396 nsresult rv = aMouseEvent->GetEventPhase(&phase);
michael@0 397 NS_ENSURE_SUCCESS(rv, rv);
michael@0 398 // Don't do anything at capturing phase, any behavior should be cancelable.
michael@0 399 if (phase == nsIDOMEvent::CAPTURING_PHASE) {
michael@0 400 return NS_OK;
michael@0 401 }
michael@0 402
michael@0 403 if (!mMenuBarFrame->IsMenuOpen() && mMenuBarFrame->IsActive())
michael@0 404 ToggleMenuActiveState();
michael@0 405
michael@0 406 return NS_OK; // means I am NOT consuming event
michael@0 407 }
michael@0 408
michael@0 409 ////////////////////////////////////////////////////////////////////////
michael@0 410 nsresult
michael@0 411 nsMenuBarListener::HandleEvent(nsIDOMEvent* aEvent)
michael@0 412 {
michael@0 413 nsAutoString eventType;
michael@0 414 aEvent->GetType(eventType);
michael@0 415
michael@0 416 if (eventType.EqualsLiteral("keyup")) {
michael@0 417 return KeyUp(aEvent);
michael@0 418 }
michael@0 419 if (eventType.EqualsLiteral("keydown")) {
michael@0 420 return KeyDown(aEvent);
michael@0 421 }
michael@0 422 if (eventType.EqualsLiteral("keypress")) {
michael@0 423 return KeyPress(aEvent);
michael@0 424 }
michael@0 425 if (eventType.EqualsLiteral("blur")) {
michael@0 426 return Blur(aEvent);
michael@0 427 }
michael@0 428 if (eventType.EqualsLiteral("mousedown")) {
michael@0 429 return MouseDown(aEvent);
michael@0 430 }
michael@0 431
michael@0 432 NS_ABORT();
michael@0 433
michael@0 434 return NS_OK;
michael@0 435 }

mercurial