author | David Flanagan <dflanagan@mozilla.com> |
Tue, 17 Jul 2012 23:20:19 -0700 | |
changeset 100305 | 5e2318fc6d1afb9fe9a79cd8a5b408e931fcb6b1 |
parent 100304 | f08e8bc175f2ccdd6092d9f62a6b9421d0698942 |
child 100306 | 511943aecd6000868f5f480a63c078904ca9b267 |
push id | 23175 |
push user | emorley@mozilla.com |
push date | Wed, 25 Jul 2012 15:03:49 +0000 |
treeherder | mozilla-central@75d16b99e8ab [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | vingtetun |
bugs | 762362 |
milestone | 17.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/b2g/chrome/content/shell.js +++ b/b2g/chrome/content/shell.js @@ -117,21 +117,24 @@ var shell = { document.getElementById('shell').appendChild(browserFrame); browserFrame.contentWindow .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIWebNavigation) .sessionHistory = Cc["@mozilla.org/browser/shistory;1"] .createInstance(Ci.nsISHistory); - ['keydown', 'keypress', 'keyup'].forEach((function listenKey(type) { - window.addEventListener(type, this, false, true); - window.addEventListener(type, this, true, true); - }).bind(this)); - + // Capture all key events so we can filter out hardware buttons + // And send them to Gaia via mozChromeEvents. + // Ideally, hardware buttons wouldn't generate key events at all, or + // if they did, they would use keycodes that conform to DOM 3 Events. + // See discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=762362 + window.addEventListener('keydown', this, true); + window.addEventListener('keypress', this, true); + window.addEventListener('keyup', this, true); window.addEventListener('MozApplicationManifest', this); window.addEventListener('mozfullscreenchange', this); window.addEventListener('sizemodechange', this); this.contentBrowser.addEventListener('mozbrowserloadstart', this, true); // Until the volume can be set from the content side, set it to a // a specific value when the device starts. This way the front-end // can display a notification when the volume change and show a volume @@ -160,58 +163,98 @@ var shell = { SettingsListener.observe("debug.paint-flashing.enabled", false, function(value) { Services.prefs.setBoolPref("nglayout.debug.paint_flashing", value); }); this.contentBrowser.src = homeURL; }, stop: function shell_stop() { - ['keydown', 'keypress', 'keyup'].forEach((function unlistenKey(type) { - window.removeEventListener(type, this, false, true); - window.removeEventListener(type, this, true, true); - }).bind(this)); - + window.removeEventListener('keydown', this, true); + window.removeEventListener('keypress', this, true); + window.removeEventListener('keyup', this, true); window.removeEventListener('MozApplicationManifest', this); window.removeEventListener('mozfullscreenchange', this); window.removeEventListener('sizemodechange', this); this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true); #ifndef MOZ_WIDGET_GONK delete Services.audioManager; #endif }, - forwardKeyToContent: function shell_forwardKeyToContent(evt) { - let content = shell.contentBrowser.contentWindow; - let generatedEvent = content.document.createEvent('KeyboardEvent'); - generatedEvent.initKeyEvent(evt.type, true, true, evt.view, evt.ctrlKey, - evt.altKey, evt.shiftKey, evt.metaKey, - evt.keyCode, evt.charCode); + // If this key event actually represents a hardware button, filter it here + // and send a mozChromeEvent with detail.type set to xxx-button-press or + // xxx-button-release instead. + filterHardwareKeys: function shell_filterHardwareKeys(evt) { + var type; + switch (evt.keyCode) { + case evt.DOM_VK_HOME: // Home button + type = 'home-button'; + break; + case evt.DOM_VK_SLEEP: // Sleep button + type = 'sleep-button'; + break; + case evt.DOM_VK_PAGE_UP: // Volume up button + type = 'volume-up-button'; + break; + case evt.DOM_VK_PAGE_DOWN: // Volume down button + type = 'volume-down-button'; + break; + case evt.DOM_VK_ESCAPE: // Back button (should be disabled) + type = 'back-button'; + break; + case evt.DOM_VK_CONTEXT_MENU: // Menu button + type = 'menu-button'; + break; + default: // Anything else is a real key + return; // Don't filter it at all; let it propagate to Gaia + } - content.document.documentElement.dispatchEvent(generatedEvent); + // If we didn't return, then the key event represents a hardware key + // and we need to prevent it from propagating to Gaia + evt.stopImmediatePropagation(); + evt.preventDefault(); // Prevent keypress events (when #501496 is fixed). + + // If it is a key down or key up event, we send a chrome event to Gaia. + // If it is a keypress event we just ignore it. + switch (evt.type) { + case 'keydown': + type = type + '-press'; + break; + case 'keyup': + type = type + '-release'; + break; + case 'keypress': + return; + } + + // On my device, the physical hardware buttons (sleep and volume) + // send multiple events (press press release release), but the + // soft home button just sends one. This hack is to manually + // "debounce" the keys. If the type of this event is the same as + // the type of the last one, then don't send it. We'll never send + // two presses or two releases in a row. + // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=761067 + if (type !== this.lastHardwareButtonEventType) { + this.lastHardwareButtonEventType = type; + this.sendChromeEvent({type: type}); + } }, + + lastHardwareButtonEventType: null, // property for the hack above handleEvent: function shell_handleEvent(evt) { let content = this.contentBrowser.contentWindow; switch (evt.type) { case 'keydown': case 'keyup': case 'keypress': - // Redirect the HOME key to System app and stop the applications from - // handling it. - let rootContentEvt = (evt.target.ownerDocument.defaultView == content); - if (!rootContentEvt && evt.eventPhase == evt.CAPTURING_PHASE && - evt.keyCode == evt.DOM_VK_HOME) { - this.forwardKeyToContent(evt); - evt.preventDefault(); - evt.stopImmediatePropagation(); - } + this.filterHardwareKeys(evt); break; - case 'mozfullscreenchange': // When the screen goes fullscreen make sure to set the focus to the // main window so noboby can prevent the ESC key to get out fullscreen // mode if (document.mozFullScreen) Services.fm.focusedWindow = window; break; case 'sizemodechange': @@ -267,16 +310,19 @@ var shell = { } break; } }, sendEvent: function shell_sendEvent(content, type, details) { let event = content.document.createEvent('CustomEvent'); event.initCustomEvent(type, true, true, details ? details : {}); content.dispatchEvent(event); + }, + sendChromeEvent: function shell_sendChromeEvent(details) { + this.sendEvent(getContentWindow(), "mozChromeEvent", details); } }; function nsBrowserAccess() { } nsBrowserAccess.prototype = { QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow]), @@ -305,17 +351,17 @@ nsBrowserAccess.prototype = { return contentWindow == window; } }; // Listen for system messages and relay them to Gaia. Services.obs.addObserver(function onSystemMessage(subject, topic, data) { let msg = JSON.parse(data); let origin = Services.io.newURI(msg.manifest, null, null).prePath; - shell.sendEvent(shell.contentBrowser.contentWindow, 'mozChromeEvent', { + shell.sendChromeEvent({ type: 'open-app', url: msg.uri, origin: origin, manifest: msg.manifest }); }, 'system-messages-open-app', false); (function Repl() { @@ -410,22 +456,32 @@ var AlertsHelper = { }, registerListener: function alert_registerListener(cookie, alertListener) { let id = "alert" + this._count++; this._listeners[id] = { observer: alertListener, cookie: cookie }; return id; }, - showAlertNotification: function alert_showAlertNotification(imageUrl, title, text, textClickable, - cookie, alertListener, name) { + showAlertNotification: function alert_showAlertNotification(imageUrl, + title, + text, + textClickable, + cookie, + alertListener, + name) + { let id = this.registerListener(cookie, alertListener); - let content = shell.contentBrowser.contentWindow; - shell.sendEvent(content, "mozChromeEvent", { type: "desktop-notification", id: id, icon: imageUrl, - title: title, text: text } ); + shell.sendChromeEvent({ + type: "desktop-notification", + id: id, + icon: imageUrl, + title: title, + text: text + }); } } var WebappsHelper = { _installers: {}, _count: 0, init: function webapps_init() { @@ -451,35 +507,38 @@ var WebappsHelper = { break; case "webapps-install-denied": DOMApplicationRegistry.denyInstall(installer); break; } }, observe: function webapps_observe(subject, topic, data) { - let content = shell.contentBrowser.contentWindow; let json = JSON.parse(data); switch(topic) { case "webapps-launch": DOMApplicationRegistry.getManifestFor(json.origin, function(aManifest) { if (!aManifest) return; let manifest = new DOMApplicationManifest(aManifest, json.origin); - shell.sendEvent(content, "mozChromeEvent", { + shell.sendChromeEvent({ "type": "webapps-launch", "url": manifest.fullLaunchPath(json.startPoint), "origin": json.origin }); }); break; case "webapps-ask-install": let id = this.registerInstaller(json); - shell.sendEvent(content, "mozChromeEvent", { type: "webapps-ask-install", id: id, app: json.app } ); + shell.sendChromeEvent({ + type: "webapps-ask-install", + id: id, + app: json.app + }); break; } } } // Start the debugger server. function startDebugger() { if (!DebuggerServer.initialized) { @@ -524,21 +583,21 @@ window.addEventListener('ContentStart', var context = canvas.getContext('2d'); var flags = context.DRAWWINDOW_DRAW_CARET | context.DRAWWINDOW_DRAW_VIEW | context.DRAWWINDOW_USE_WIDGET_LAYERS; context.drawWindow(window, 0, 0, width, height, 'rgb(255,255,255)', flags); - shell.sendEvent(content, 'mozChromeEvent', { - type: 'take-screenshot-success', - file: canvas.mozGetAsFile('screenshot', 'image/png') + shell.sendChromeEvent({ + type: 'take-screenshot-success', + file: canvas.mozGetAsFile('screenshot', 'image/png') }); } catch (e) { dump('exception while creating screenshot: ' + e + '\n'); - shell.sendEvent(content, 'mozChromeEvent', { + shell.sendChromeEvent({ type: 'take-screenshot-error', error: String(e) }); } }); });