diff --git a/accessible/atk/AccessibleWrap.cpp b/accessible/atk/AccessibleWrap.cpp index 7a57d99d3495..e379b371370e 100644 --- a/accessible/atk/AccessibleWrap.cpp +++ b/accessible/atk/AccessibleWrap.cpp @@ -1089,19 +1089,19 @@ GetInterfacesForProxy(ProxyAccessible* aProxy, uint32_t aInterfaces) | (1 << MAI_INTERFACE_EDITABLE_TEXT); if (aInterfaces & Interfaces::HYPERLINK) - interfaces |= MAI_INTERFACE_HYPERLINK_IMPL; + interfaces |= 1 << MAI_INTERFACE_HYPERLINK_IMPL; if (aInterfaces & Interfaces::VALUE) - interfaces |= MAI_INTERFACE_VALUE; + interfaces |= 1 << MAI_INTERFACE_VALUE; if (aInterfaces & Interfaces::TABLE) - interfaces |= MAI_INTERFACE_TABLE; + interfaces |= 1 << MAI_INTERFACE_TABLE; if (aInterfaces & Interfaces::IMAGE) - interfaces |= MAI_INTERFACE_IMAGE; + interfaces |= 1 << MAI_INTERFACE_IMAGE; if (aInterfaces & Interfaces::DOCUMENT) - interfaces |= MAI_INTERFACE_DOCUMENT; + interfaces |= 1 << MAI_INTERFACE_DOCUMENT; return interfaces; } diff --git a/accessible/jsat/AccessFu.jsm b/accessible/jsat/AccessFu.jsm index 7dfcba89492c..a3cf74f0fa52 100644 --- a/accessible/jsat/AccessFu.jsm +++ b/accessible/jsat/AccessFu.jsm @@ -134,6 +134,8 @@ this.AccessFu = { // jshint ignore:line Services.obs.addObserver(this, 'Accessibility:Focus', false); Services.obs.addObserver(this, 'Accessibility:ActivateObject', false); Services.obs.addObserver(this, 'Accessibility:LongPress', false); + Services.obs.addObserver(this, 'Accessibility:ScrollForward', false); + Services.obs.addObserver(this, 'Accessibility:ScrollBackward', false); Services.obs.addObserver(this, 'Accessibility:MoveByGranularity', false); Utils.win.addEventListener('TabOpen', this); Utils.win.addEventListener('TabClose', this); @@ -187,6 +189,8 @@ this.AccessFu = { // jshint ignore:line Services.obs.removeObserver(this, 'Accessibility:Focus'); Services.obs.removeObserver(this, 'Accessibility:ActivateObject'); Services.obs.removeObserver(this, 'Accessibility:LongPress'); + Services.obs.removeObserver(this, 'Accessibility:ScrollForward'); + Services.obs.removeObserver(this, 'Accessibility:ScrollBackward'); Services.obs.removeObserver(this, 'Accessibility:MoveByGranularity'); delete this._quicknavModesPref; @@ -315,6 +319,12 @@ this.AccessFu = { // jshint ignore:line case 'Accessibility:LongPress': this.Input.sendContextMenuMessage(); break; + case 'Accessibility:ScrollForward': + this.Input.androidScroll('forward'); + break; + case 'Accessibility:ScrollBackward': + this.Input.androidScroll('backward'); + break; case 'Accessibility:Focus': this._focused = JSON.parse(aData); if (this._focused) { @@ -837,6 +847,12 @@ var Input = { adjustRange: aAdjustRange }); }, + androidScroll: function androidScroll(aDirection) { + let mm = Utils.getMessageManager(Utils.CurrentBrowser); + mm.sendAsyncMessage('AccessFu:AndroidScroll', + { direction: aDirection, origin: 'top' }); + }, + moveByGranularity: function moveByGranularity(aDetails) { const MOVEMENT_GRANULARITY_PARAGRAPH = 8; diff --git a/accessible/jsat/ContentControl.jsm b/accessible/jsat/ContentControl.jsm index 11b75a0fb306..c726fa52b7a2 100644 --- a/accessible/jsat/ContentControl.jsm +++ b/accessible/jsat/ContentControl.jsm @@ -38,7 +38,8 @@ this.ContentControl.prototype = { 'AccessFu:AutoMove', 'AccessFu:Activate', 'AccessFu:MoveCaret', - 'AccessFu:MoveByGranularity'], + 'AccessFu:MoveByGranularity', + 'AccessFu:AndroidScroll'], start: function cc_start() { let cs = this._contentScope.get(); @@ -91,6 +92,27 @@ this.ContentControl.prototype = { } }, + handleAndroidScroll: function cc_handleAndroidScroll(aMessage) { + let vc = this.vc; + let position = vc.position; + + if (aMessage.json.origin != 'child' && this.sendToChild(vc, aMessage)) { + // Forwarded succesfully to child cursor. + return; + } + + // Counter-intuitive, but scrolling backward (ie. up), actually should + // increase range values. + if (this.adjustRange(position, aMessage.json.direction === 'backward')) { + return; + } + + this._contentScope.get().sendAsyncMessage('AccessFu:DoScroll', + { bounds: Utils.getBounds(position, true), + page: aMessage.json.direction === 'forward' ? 1 : -1, + horizontal: false }); + }, + handleMoveCursor: function cc_handleMoveCursor(aMessage) { let origin = aMessage.json.origin; let action = aMessage.json.action; diff --git a/accessible/tests/mochitest/jsat/jsatcommon.js b/accessible/tests/mochitest/jsat/jsatcommon.js index f1de7ac8c99e..706e75df7e1a 100644 --- a/accessible/tests/mochitest/jsat/jsatcommon.js +++ b/accessible/tests/mochitest/jsat/jsatcommon.js @@ -391,6 +391,20 @@ var ContentMessages = { } }, + androidScrollForward: function adjustUp() { + return { + name: 'AccessFu:AndroidScroll', + json: { origin: 'top', direction: 'forward' } + }; + }, + + androidScrollBackward: function adjustDown() { + return { + name: 'AccessFu:AndroidScroll', + json: { origin: 'top', direction: 'backward' } + }; + }, + focusSelector: function focusSelector(aSelector, aBlur) { return { name: 'AccessFuTest:Focus', diff --git a/accessible/tests/mochitest/jsat/test_content_integration.html b/accessible/tests/mochitest/jsat/test_content_integration.html index a21a6bc06072..229102ef976a 100644 --- a/accessible/tests/mochitest/jsat/test_content_integration.html +++ b/accessible/tests/mochitest/jsat/test_content_integration.html @@ -81,6 +81,8 @@ [ContentMessages.simpleMovePrevious, new ExpectedCursorChange(['much range', '6', {'string': 'slider'}, 'such app'])], [ContentMessages.moveOrAdjustDown(), new ExpectedValueChange('5')], + [ContentMessages.androidScrollForward(), new ExpectedValueChange('6')], + [ContentMessages.androidScrollBackward(), new ExpectedValueChange('5')], [ContentMessages.simpleMovePrevious, new ExpectedCursorChange(['much range', {'string': 'label'}])], [ContentMessages.simpleMovePrevious, diff --git a/addon-sdk/source/test/context-menu/test-helper.js b/addon-sdk/source/test/context-menu/test-helper.js index 14180249e0ea..97948098505d 100644 --- a/addon-sdk/source/test/context-menu/test-helper.js +++ b/addon-sdk/source/test/context-menu/test-helper.js @@ -30,7 +30,7 @@ const TEST_DOC_URL = module.uri.replace(/context-menu\/test-helper\.js$/, "test- function TestHelper(assert, done) { // Methods on the wrapped test can be called on this object. for (var prop in assert) - this[prop] = () => assert[prop].apply(assert, arguments); + this[prop] = (...args) => assert[prop].apply(assert, args); this.assert = assert; this.end = done; this.loaders = []; diff --git a/addon-sdk/source/test/event/helpers.js b/addon-sdk/source/test/event/helpers.js index ec120b79dd9a..35833b35f372 100644 --- a/addon-sdk/source/test/event/helpers.js +++ b/addon-sdk/source/test/event/helpers.js @@ -26,7 +26,7 @@ const { defer } = require("sdk/core/promise"); * A promise resolved once the delay given is passed, or the object * receives the event specified */ -const wait = (target, type, capture) => { +const wait = function(target, type, capture) { let { promise, resolve, reject } = defer(); if (!arguments.length) { @@ -105,4 +105,4 @@ exports.FIFO = scenario(function(target, expected, actual) { return expected.reduce(function(result, value) { return result.concat(value + "#1", value + "#2", value + "#3"); }, []); -}); \ No newline at end of file +}); diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index ad2fde7c9000..7274a67d61f6 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -352,11 +352,6 @@ pref("dom.w3c_touch_events.enabled", 1); pref("dom.w3c_touch_events.safetyX", 0); // escape borders in units of 1/240" pref("dom.w3c_touch_events.safetyY", 120); // escape borders in units of 1/240" -// W3C draft pointer events -pref("dom.w3c_pointer_events.enabled", false); -// W3C touch-action css property (related to touch and pointer events) -pref("layout.css.touch_action.enabled", false); - #ifdef MOZ_SAFE_BROWSING // Safe browsing does nothing unless this pref is set pref("browser.safebrowsing.enabled", false); @@ -603,7 +598,7 @@ pref("app.update.incompatible.mode", 0); pref("app.update.staging.enabled", true); pref("app.update.service.enabled", true); -pref("app.update.url", "https://aus4.mozilla.org/update/3/%PRODUCT%/%VERSION%/%BUILD_ID%/%PRODUCT_DEVICE%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml"); +pref("app.update.url", "https://aus5.mozilla.org/update/3/%PRODUCT%/%VERSION%/%BUILD_ID%/%PRODUCT_DEVICE%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml"); pref("app.update.channel", "@MOZ_UPDATE_CHANNEL@"); // Interval at which update manifest is fetched. In units of seconds. diff --git a/b2g/dev/app/mulet.js b/b2g/dev/app/mulet.js index 5d59c1e18760..7be519855249 100644 --- a/b2g/dev/app/mulet.js +++ b/b2g/dev/app/mulet.js @@ -18,8 +18,3 @@ pref("devtools.toolbox.sidebar.width", 800); pref("browser.tabs.remote.autostart", false); pref("browser.tabs.remote.autostart.1", false); pref("browser.tabs.remote.autostart.2", false); - -// W3C draft pointer events -pref("dom.w3c_pointer_events.enabled", false); -// W3C touch-action css property (related to touch and pointer events) -pref("layout.css.touch_action.enabled", false); diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 363cea9d38f0..fd70598c1fd7 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -142,7 +142,7 @@ pref("app.update.badge", false); pref("app.update.staging.enabled", true); // Update service URL: -pref("app.update.url", "https://aus4.mozilla.org/update/3/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml"); +pref("app.update.url", "https://aus5.mozilla.org/update/3/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml"); // app.update.url.manual is in branding section // app.update.url.details is in branding section @@ -489,17 +489,10 @@ pref("dom.disable_window_move_resize", false); // prevent JS from monkeying with window focus, etc pref("dom.disable_window_flip", true); -// Disable touch events on Desktop Firefox by default -// until they are properly supported (bug 736048) +// Disable touch events on Desktop Firefox by default until they are properly +// supported (bug 736048) pref("dom.w3c_touch_events.enabled", 0); -#ifdef NIGHTLY_BUILD -// W3C draft pointer events -pref("dom.w3c_pointer_events.enabled", true); -// W3C touch-action css property (related to touch and pointer events) -pref("layout.css.touch_action.enabled", true); -#endif - // popups.policy 1=allow,2=reject pref("privacy.popups.policy", 1); pref("privacy.popups.usecustom", true); @@ -1184,7 +1177,12 @@ pref("security.sandbox.windows.log", false); // 3 - the strongest settings we seem to be able to use without breaking // everything, but will probably cause some functionality restrictions pref("dom.ipc.plugins.sandbox-level.default", 0); +#if defined(_AMD64_) +// The lines in PluginModuleParent.cpp should be changed in line with this. +pref("dom.ipc.plugins.sandbox-level.flash", 2); +#else pref("dom.ipc.plugins.sandbox-level.flash", 0); +#endif #if defined(MOZ_CONTENT_SANDBOX) // This controls the strength of the Windows content process sandbox for testing diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index ecc852166570..fde36c6ee001 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -3077,6 +3077,11 @@ // Map from tabs to STATE_* (below). tabState: new Map(), + // Keep an exact list of content processes (tabParent) in which + // we're actively suppressing the display port. This gives a robust + // way to make sure we don't forget to un-suppress. + activeSuppressDisplayport: new Set(), + // Set of tabs that might be visible right now. We maintain // this set because we can't be sure when a tab is actually // drawn. A tab is added to this set when we ask to make it @@ -3140,6 +3145,11 @@ window.removeEventListener("TabRemotenessChange", this); this.tabbrowser._switcher = null; + + this.activeSuppressDisplayport.forEach(function(tabParent) { + tabParent.suppressDisplayport(false); + }); + this.activeSuppressDisplayport.clear(); }, finish: function() { @@ -3438,6 +3448,13 @@ this.requestedTab = tab; + let browser = this.requestedTab.linkedBrowser; + let fl = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader; + if (fl && fl.tabParent && !this.activeSuppressDisplayport.has(fl.tabParent)) { + fl.tabParent.suppressDisplayport(true); + this.activeSuppressDisplayport.add(fl.tabParent); + } + this.preActions(); clearTimeout(this.unloadTimer); diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index 0a33f5249cc7..a192bbb08ab5 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -136,7 +136,6 @@ skip-if = e10s # Bug 1093153 - no about:home support yet [browser_search_favicon.js] [browser_alltabslistener.js] [browser_audioTabIcon.js] -skip-if = e10s # Bug 1192449 [browser_autocomplete_a11y_label.js] skip-if = e10s # Bug 1101993 - times out for unknown reasons when run in the dir (works on its own) [browser_autocomplete_cursor.js] diff --git a/browser/base/content/test/general/browser_audioTabIcon.js b/browser/base/content/test/general/browser_audioTabIcon.js index f56dd09c961f..742509687881 100644 --- a/browser/base/content/test/general/browser_audioTabIcon.js +++ b/browser/base/content/test/general/browser_audioTabIcon.js @@ -173,9 +173,6 @@ function* test_browser_swapping(tab, browser) { }, function*(newBrowser) { yield test_swapped_browser(tab, newBrowser, true) - // FIXME: this is needed to work around bug 1190903. - yield new Promise(resolve => setTimeout(resolve, 3000)); - // Now, test swapping with a muted but not playing tab. // Note that the tab remains muted, so we only need to pause playback. tab = gBrowser.getTabForBrowser(newBrowser); diff --git a/browser/base/content/test/general/browser_selectpopup.js b/browser/base/content/test/general/browser_selectpopup.js index 253bc9a35ff7..6e043d9139d6 100644 --- a/browser/base/content/test/general/browser_selectpopup.js +++ b/browser/base/content/test/general/browser_selectpopup.js @@ -5,6 +5,8 @@ // in a child process. This is different than single-process as a is used // to implement the dropdown list. +const XHTML_DTD = ''; + const PAGECONTENT = "" + "" + ""; @@ -58,11 +60,11 @@ function getChangeEvents() }); } -function doSelectTests(contentType) +function doSelectTests(contentType, dtd) { let tab = gBrowser.selectedTab = gBrowser.addTab(); let browser = gBrowser.getBrowserForTab(tab); - yield promiseTabLoadEvent(tab, "data:" + contentType + "," + escape(PAGECONTENT)); + yield promiseTabLoadEvent(tab, "data:" + contentType + "," + escape(dtd + "\n" + PAGECONTENT)); yield SimpleTest.promiseFocus(browser.contentWindow); @@ -127,15 +129,17 @@ function doSelectTests(contentType) EventUtils.synthesizeKey("VK_TAB", { shiftKey: true }); is((yield getChangeEvents()), isWindows ? 2 : 1, "Tab away from select with change - number of change events"); + is(selectPopup.lastChild.previousSibling.label, "Seven", "Spaces collapsed"); + is(selectPopup.lastChild.label, "\xA0\xA0Eight\xA0\xA0", "Non-breaking spaces not collapsed"); + gBrowser.removeCurrentTab(); } add_task(function*() { - yield doSelectTests("text/html"); + yield doSelectTests("text/html", ""); }); add_task(function*() { - yield doSelectTests("application/xhtml+xml"); + yield doSelectTests("application/xhtml+xml", XHTML_DTD); }); - diff --git a/browser/base/content/test/general/head.js b/browser/base/content/test/general/head.js index f866138e5e0b..2eec4942dc25 100644 --- a/browser/base/content/test/general/head.js +++ b/browser/base/content/test/general/head.js @@ -558,8 +558,8 @@ let FullZoomHelper = { let didPs = false; let didZoom = false; - gBrowser.addEventListener("pageshow", function (event) { - gBrowser.removeEventListener("pageshow", arguments.callee, true); + gBrowser.addEventListener("pageshow", function listener(event) { + gBrowser.removeEventListener("pageshow", listener, true); didPs = true; if (didZoom) resolve(); @@ -718,9 +718,9 @@ function assertWebRTCIndicatorStatus(expected) { let win = Services.wm.getMostRecentWindow("Browser:WebRTCGlobalIndicator"); if (win) { yield new Promise((resolve, reject) => { - win.addEventListener("unload", (e) => { + win.addEventListener("unload", function listener(e) { if (e.target == win.document) { - win.removeEventListener("unload", arguments.callee); + win.removeEventListener("unload", listener); resolve(); } }, false); diff --git a/browser/components/places/tests/browser/browser_library_commands.js b/browser/components/places/tests/browser/browser_library_commands.js index 891a1f4a3057..5775990df15a 100644 --- a/browser/components/places/tests/browser/browser_library_commands.js +++ b/browser/components/places/tests/browser/browser_library_commands.js @@ -60,7 +60,7 @@ add_task(function* test_date_container() { // Execute the delete command and check visit has been removed. let promiseURIRemoved = promiseHistoryNotification("onDeleteURI", - () => TEST_URI.equals(arguments[0])); + v => TEST_URI.equals(v)); PO._places.controller.doCommand("cmd_delete"); yield promiseURIRemoved; @@ -125,7 +125,7 @@ add_task(function* test_query_on_toolbar() { // Execute the delete command and check bookmark has been removed. let promiseItemRemoved = promiseBookmarksNotification("onItemRemoved", - () => query.guid == arguments[5]); + (...args) => query.guid == args[5]); PO._places.controller.doCommand("cmd_delete"); yield promiseItemRemoved; diff --git a/browser/components/places/tests/browser/head.js b/browser/components/places/tests/browser/head.js index e5722d653722..f1800b33912e 100644 --- a/browser/components/places/tests/browser/head.js +++ b/browser/components/places/tests/browser/head.js @@ -170,13 +170,13 @@ function promiseBookmarksNotification(notification, conditionFn) { return XPCOMUtils.generateQI([ Ci.nsINavBookmarkObserver ]); info(`promiseBookmarksNotification: got ${name} notification`); if (name == notification) - return () => { - if (conditionFn.apply(this, arguments)) { + return (...args) => { + if (conditionFn.apply(this, args)) { clearTimeout(timeout); PlacesUtils.bookmarks.removeObserver(proxifiedObserver, false); executeSoon(resolve); } else { - info(`promiseBookmarksNotification: skip cause condition doesn't apply to ${JSON.stringify(arguments)}`); + info(`promiseBookmarksNotification: skip cause condition doesn't apply to ${JSON.stringify(args)}`); } } return () => {}; @@ -198,8 +198,8 @@ function promiseHistoryNotification(notification, conditionFn) { if (name == "QueryInterface") return XPCOMUtils.generateQI([ Ci.nsINavHistoryObserver ]); if (name == notification) - return () => { - if (conditionFn.apply(this, arguments)) { + return (...args) => { + if (conditionFn.apply(this, args)) { clearTimeout(timeout); PlacesUtils.history.removeObserver(proxifiedObserver, false); executeSoon(resolve); diff --git a/browser/devtools/webconsole/test/browser_webconsole_hpkp_invalid-headers.js b/browser/devtools/webconsole/test/browser_webconsole_hpkp_invalid-headers.js index 4535e4aaf708..8eb6993d5704 100644 --- a/browser/devtools/webconsole/test/browser_webconsole_hpkp_invalid-headers.js +++ b/browser/devtools/webconsole/test/browser_webconsole_hpkp_invalid-headers.js @@ -18,9 +18,6 @@ let test = asyncTest(function* () { registerCleanupFunction(() => { Services.prefs.clearUserPref(NON_BUILTIN_ROOT_PREF); }); - // The root used for mochitests is not built-in, so set the relevant pref to - // true to force all pinning error messages to appear. - Services.prefs.setBoolPref(NON_BUILTIN_ROOT_PREF, true); yield loadTab(TEST_URI); @@ -75,12 +72,26 @@ let test = asyncTest(function* () { "multiple 'report-uri' directives." }, hud); + // The root used for mochitests is not built-in, so set the relevant pref to + // true to have the PKP implementation return more specific errors. + Services.prefs.setBoolPref(NON_BUILTIN_ROOT_PREF, true); + yield* checkForMessage({ url: SJS_URL + "?pinsetDoesNotMatch", name: "Non-matching pinset error displayed successfully", text: "Public-Key-Pins: The site specified a header that did not include " + "a matching pin." }, hud); + + Services.prefs.setBoolPref(NON_BUILTIN_ROOT_PREF, false); + + yield* checkForMessage({ + url: SJS_URL + "?pinsetDoesNotMatch", + name: "Non-built-in root error displayed successfully", + text: "Public-Key-Pins: The certificate used by the site was not issued " + + "by a certificate in the default root certificate store. To " + + "prevent accidental breakage, the specified header was ignored." + }, hud); }); function* checkForMessage(curTest, hud) { diff --git a/browser/themes/linux/feeds/subscribe.css b/browser/themes/linux/feeds/subscribe.css index 4cd70faa7001..03b534a50a32 100644 --- a/browser/themes/linux/feeds/subscribe.css +++ b/browser/themes/linux/feeds/subscribe.css @@ -20,6 +20,7 @@ html { border-radius: 10px; margin: -4em auto 0 auto; background-color: InfoBackground; + -moz-appearance: -moz-gtk-info-bar; } #feedHeader { @@ -29,19 +30,19 @@ html { -moz-margin-end: 1em; -moz-padding-start: 2.9em; font-size: 110%; - color: InfoText; + color: -moz-gtk-info-bar-text; } .feedBackground { - background: url("chrome://browser/skin/feeds/feedIcon.png") 0% 10% no-repeat InfoBackground; + background: url("chrome://browser/skin/feeds/feedIcon.png") 0% 10% no-repeat; } .videoPodcastBackground { - background: url("chrome://browser/skin/feeds/videoFeedIcon.png") 0% 10% no-repeat InfoBackground; + background: url("chrome://browser/skin/feeds/videoFeedIcon.png") 0% 10% no-repeat; } .audioPodcastBackground { - background: url("chrome://browser/skin/feeds/audioFeedIcon.png") 0% 10% no-repeat InfoBackground; + background: url("chrome://browser/skin/feeds/audioFeedIcon.png") 0% 10% no-repeat; } #feedHeader[dir="rtl"] { diff --git a/config/external/lgpllibs/moz.build b/config/external/lgpllibs/moz.build index 9812b79c36e2..27df35dbf831 100644 --- a/config/external/lgpllibs/moz.build +++ b/config/external/lgpllibs/moz.build @@ -10,7 +10,7 @@ # # Any library added here should also be reflected in the about:license page. -SharedLibrary('lgpllibs') +GeckoSharedLibrary('lgpllibs', linkage=None) SHARED_LIBRARY_NAME = 'lgpllibs' if CONFIG['MOZ_LIBAV_FFT']: diff --git a/config/msvc-stl-wrapper.template.h b/config/msvc-stl-wrapper.template.h index 0a01a6dd1b5f..c42da266ed0d 100644 --- a/config/msvc-stl-wrapper.template.h +++ b/config/msvc-stl-wrapper.template.h @@ -8,6 +8,9 @@ #ifndef mozilla_${HEADER}_h #define mozilla_${HEADER}_h +#ifndef MOZ_HAVE_INCLUDED_ALLOC +#define MOZ_HAVE_INCLUDED_ALLOC + #if _HAS_EXCEPTIONS # error "STL code can only be used with -fno-exceptions" #endif @@ -32,6 +35,7 @@ #else # error "STL code can only be used with infallible ::operator new()" #endif +#endif /* MOZ_HAVE_INCLUDED_ALLOC */ #ifdef _DEBUG // From diff --git a/config/stl-headers b/config/stl-headers index dc7f6f0fb7ad..a1f77ac5ba67 100644 --- a/config/stl-headers +++ b/config/stl-headers @@ -36,6 +36,7 @@ utility vector cassert climits +cmath cstdarg cstdio cstdlib diff --git a/configure.in b/configure.in index f2c1f289de6d..522a81c23eb2 100644 --- a/configure.in +++ b/configure.in @@ -663,66 +663,30 @@ See https://developer.mozilla.org/en/Windows_Build_Prerequisites.]) unset _MSVC_VER_FILTER - AC_CACHE_CHECK(for std::_Throw, ac_cv_have_std__Throw, + AC_CACHE_CHECK(for overridable _RAISE, + ac_cv_have__RAISE, [ AC_LANG_SAVE AC_LANG_CPLUSPLUS _SAVE_CXXFLAGS="$CXXFLAGS" CXXFLAGS="${CXXFLAGS} -D_HAS_EXCEPTIONS=0" - AC_TRY_COMPILE([#include ], - [std::_Throw(std::exception()); return 0;], - ac_cv_have_std__Throw="yes", - ac_cv_have_std__Throw="no") + AC_TRY_COMPILE([#include + #undef _RAISE + #define _RAISE(x) externallyDefinedFunction((x).what()) + #include + ], + [std::vector v; return v.at(1);], + ac_cv_have__RAISE="no", + ac_cv_have__RAISE="yes") CXXFLAGS="$_SAVE_CXXFLAGS" AC_LANG_RESTORE ]) - - if test "$ac_cv_have_std__Throw" = "yes"; then - AC_CACHE_CHECK(for |class __declspec(dllimport) exception| bug, - ac_cv_have_dllimport_exception_bug, - [ - AC_LANG_SAVE - AC_LANG_CPLUSPLUS - _SAVE_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="${CXXFLAGS} -D_HAS_EXCEPTIONS=0" - AC_TRY_LINK([#include ], - [std::vector v; return v.at(1);], - ac_cv_have_dllimport_exception_bug="no", - ac_cv_have_dllimport_exception_bug="yes") - CXXFLAGS="$_SAVE_CXXFLAGS" - AC_LANG_RESTORE - ]) - if test "$ac_cv_have_dllimport_exception_bug" = "no"; then - WRAP_STL_INCLUDES=1 - MOZ_MSVC_STL_WRAP_Throw=1 - AC_DEFINE(MOZ_MSVC_STL_WRAP_Throw) - fi + if test "$ac_cv_have__RAISE" = "yes"; then + WRAP_STL_INCLUDES=1 + MOZ_MSVC_STL_WRAP_RAISE=1 + AC_DEFINE(MOZ_MSVC_STL_WRAP_RAISE) else - AC_CACHE_CHECK(for overridable _RAISE, - ac_cv_have__RAISE, - [ - AC_LANG_SAVE - AC_LANG_CPLUSPLUS - _SAVE_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="${CXXFLAGS} -D_HAS_EXCEPTIONS=0" - AC_TRY_COMPILE([#include - #undef _RAISE - #define _RAISE(x) externallyDefinedFunction((x).what()) - #include - ], - [std::vector v; return v.at(1);], - ac_cv_have__RAISE="no", - ac_cv_have__RAISE="yes") - CXXFLAGS="$_SAVE_CXXFLAGS" - AC_LANG_RESTORE - ]) - if test "$ac_cv_have__RAISE" = "yes"; then - WRAP_STL_INCLUDES=1 - MOZ_MSVC_STL_WRAP_RAISE=1 - AC_DEFINE(MOZ_MSVC_STL_WRAP_RAISE) - else - AC_MSG_ERROR([Gecko exception wrapping doesn't understand your your MSVC/SDK. Please file a bug describing this error and your build configuration.]) - fi + AC_MSG_ERROR([Gecko exception wrapping doesn't understand your your MSVC/SDK. Please file a bug describing this error and your build configuration.]) fi if test "$WRAP_STL_INCLUDES" = "1"; then @@ -839,7 +803,6 @@ AC_SUBST(INTEL_CXX) AC_SUBST(STL_FLAGS) AC_SUBST(WRAP_STL_INCLUDES) -AC_SUBST(MOZ_MSVC_STL_WRAP_Throw) AC_SUBST(MOZ_MSVC_STL_WRAP_RAISE) dnl ======================================================== diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 931afeaccbff..d4b6986a016c 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -5936,7 +5936,10 @@ nsDocShell::SetIsActive(bool aIsActive) if (mScriptGlobal) { mScriptGlobal->SetIsBackground(!aIsActive); if (nsCOMPtr doc = mScriptGlobal->GetExtantDoc()) { - if (aIsActive) { + // Update orientation when the top-level browsing context becomes active. + // We make an exception for apps because they currently rely on + // orientation locks persisting across browsing contexts. + if (aIsActive && !GetIsApp()) { nsCOMPtr parent; GetSameTypeParent(getter_AddRefs(parent)); if (!parent) { @@ -10071,7 +10074,9 @@ nsDocShell::InternalLoad(nsIURI* aURI, // lock the orientation of the document to the document's default // orientation. We don't explicitly check for a top-level browsing context // here because orientation is only set on top-level browsing contexts. - if (OrientationLock() != eScreenOrientation_None) { + // We make an exception for apps because they currently rely on + // orientation locks persisting across browsing contexts. + if (OrientationLock() != eScreenOrientation_None && !GetIsApp()) { #ifdef DEBUG nsCOMPtr parent; GetSameTypeParent(getter_AddRefs(parent)); diff --git a/docshell/test/navigation/mochitest.ini b/docshell/test/navigation/mochitest.ini index 99333a1bb5b7..a20600b04fab 100644 --- a/docshell/test/navigation/mochitest.ini +++ b/docshell/test/navigation/mochitest.ini @@ -22,15 +22,15 @@ support-files = parent.html [test_bug13871.html] -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' || e10s #RANDOM # Bug 1136180 disabled on B2G Desktop and Mulet for intermittent failures +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' #RANDOM # Bug 1136180 disabled on B2G Desktop and Mulet for intermittent failures [test_bug270414.html] -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == "android" || e10s # Bug 1136180 disabled on B2G Desktop and Mulet for intermittent failures +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == "android" # Bug 1136180 disabled on B2G Desktop and Mulet for intermittent failures [test_bug278916.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_bug279495.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_bug344861.html] -skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s +skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' [test_bug386782.html] skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) [test_bug430624.html] diff --git a/docshell/test/navigation/parent.html b/docshell/test/navigation/parent.html index 46028aad9353..74722b8bdfdf 100644 --- a/docshell/test/navigation/parent.html +++ b/docshell/test/navigation/parent.html @@ -5,6 +5,9 @@
diff --git a/docshell/test/navigation/test_bug13871.html b/docshell/test/navigation/test_bug13871.html index 9ff0b9bb553f..e0b563a4a83d 100644 --- a/docshell/test/navigation/test_bug13871.html +++ b/docshell/test/navigation/test_bug13871.html @@ -9,7 +9,7 @@ iframe { width: 90%; height: 50px; } diff --git a/dom/apps/PermissionsTable.jsm b/dom/apps/PermissionsTable.jsm index f97d3bfe52bc..1037aee332f3 100644 --- a/dom/apps/PermissionsTable.jsm +++ b/dom/apps/PermissionsTable.jsm @@ -492,6 +492,12 @@ this.PermissionsTable = { geolocation: { privileged: DENY_ACTION, certified: ALLOW_ACTION }, + "moz-extremely-unstable-and-will-change-webcomponents": { + app: DENY_ACTION, + trusted: DENY_ACTION, + privileged: ALLOW_ACTION, + certified: ALLOW_ACTION + } }; /** diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index 00e1f2318a22..a010c2f2bee5 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -2850,9 +2850,9 @@ Element::CheckHandleEventForLinksPrecondition(EventChainVisitor& aVisitor, { if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault || (!aVisitor.mEvent->mFlags.mIsTrusted && - (aVisitor.mEvent->message != NS_MOUSE_CLICK) && - (aVisitor.mEvent->message != NS_KEY_PRESS) && - (aVisitor.mEvent->message != NS_UI_ACTIVATE)) || + (aVisitor.mEvent->mMessage != NS_MOUSE_CLICK) && + (aVisitor.mEvent->mMessage != NS_KEY_PRESS) && + (aVisitor.mEvent->mMessage != NS_UI_ACTIVATE)) || !aVisitor.mPresContext || aVisitor.mEvent->mFlags.mMultipleActionsPrevented) { return false; @@ -2867,7 +2867,7 @@ Element::PreHandleEventForLinks(EventChainPreVisitor& aVisitor) { // Optimisation: return early if this event doesn't interest us. // IMPORTANT: this switch and the switch below it must be kept in sync! - switch (aVisitor.mEvent->message) { + switch (aVisitor.mEvent->mMessage) { case NS_MOUSE_OVER: case NS_FOCUS_CONTENT: case NS_MOUSE_OUT: @@ -2887,7 +2887,7 @@ Element::PreHandleEventForLinks(EventChainPreVisitor& aVisitor) // We do the status bar updates in PreHandleEvent so that the status bar gets // updated even if the event is consumed before we have a chance to set it. - switch (aVisitor.mEvent->message) { + switch (aVisitor.mEvent->mMessage) { // Set the status bar similarly for mouseover and focus case NS_MOUSE_OVER: aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; @@ -2928,7 +2928,7 @@ Element::PostHandleEventForLinks(EventChainPostVisitor& aVisitor) { // Optimisation: return early if this event doesn't interest us. // IMPORTANT: this switch and the switch below it must be kept in sync! - switch (aVisitor.mEvent->message) { + switch (aVisitor.mEvent->mMessage) { case NS_MOUSE_BUTTON_DOWN: case NS_MOUSE_CLICK: case NS_UI_ACTIVATE: @@ -2946,7 +2946,7 @@ Element::PostHandleEventForLinks(EventChainPostVisitor& aVisitor) nsresult rv = NS_OK; - switch (aVisitor.mEvent->message) { + switch (aVisitor.mEvent->mMessage) { case NS_MOUSE_BUTTON_DOWN: { if (aVisitor.mEvent->AsMouseEvent()->button == @@ -2983,7 +2983,9 @@ Element::PostHandleEventForLinks(EventChainPostVisitor& aVisitor) if (shell) { // single-click nsEventStatus status = nsEventStatus_eIgnore; - InternalUIEvent actEvent(mouseEvent->mFlags.mIsTrusted, NS_UI_ACTIVATE); + // DOMActive event should be trusted since the activation is actually + // occurred even if the cause is an untrusted click event. + InternalUIEvent actEvent(true, NS_UI_ACTIVATE, mouseEvent); actEvent.detail = 1; rv = shell->HandleDOMEventWithTarget(this, &actEvent, &status); @@ -2999,9 +3001,10 @@ Element::PostHandleEventForLinks(EventChainPostVisitor& aVisitor) if (aVisitor.mEvent->originalTarget == this) { nsAutoString target; GetLinkTarget(target); + const InternalUIEvent* activeEvent = aVisitor.mEvent->AsUIEvent(); + MOZ_ASSERT(activeEvent); nsContentUtils::TriggerLink(this, aVisitor.mPresContext, absURI, target, - true, true, - aVisitor.mEvent->mFlags.mIsTrusted); + true, true, activeEvent->IsTrustable()); aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; } } diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index 33b3b7cafaa5..7d607be35103 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -677,10 +677,10 @@ nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor) // Don't propagate mouseover and mouseout events when mouse is moving // inside chrome access only content. bool isAnonForEvents = IsRootOfChromeAccessOnlySubtree(); - if ((aVisitor.mEvent->message == NS_MOUSE_OVER || - aVisitor.mEvent->message == NS_MOUSE_OUT || - aVisitor.mEvent->message == NS_POINTER_OVER || - aVisitor.mEvent->message == NS_POINTER_OUT) && + if ((aVisitor.mEvent->mMessage == NS_MOUSE_OVER || + aVisitor.mEvent->mMessage == NS_MOUSE_OUT || + aVisitor.mEvent->mMessage == NS_POINTER_OVER || + aVisitor.mEvent->mMessage == NS_POINTER_OUT) && // Check if we should stop event propagation when event has just been // dispatched or when we're about to propagate from // chrome access only subtree or if we are about to propagate out of @@ -739,7 +739,7 @@ nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor) printf("Stopping %s propagation:" "\n\toriginalTarget=%s \n\tcurrentTarget=%s %s" "\n\trelatedTarget=%s %s \n%s", - (aVisitor.mEvent->message == NS_MOUSE_OVER) + (aVisitor.mEvent->mMessage == NS_MOUSE_OVER) ? "mouseover" : "mouseout", NS_ConvertUTF16toUTF8(ot).get(), NS_ConvertUTF16toUTF8(ct).get(), @@ -805,7 +805,7 @@ nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor) // scroll // selectstart bool stopEvent = false; - switch (aVisitor.mEvent->message) { + switch (aVisitor.mEvent->mMessage) { case NS_IMAGE_ABORT: case NS_LOAD_ERROR: case NS_FORM_SELECTED: @@ -841,7 +841,7 @@ nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor) // The load event is special in that we don't ever propagate it // to chrome. nsCOMPtr win = OwnerDoc()->GetWindow(); - EventTarget* parentTarget = win && aVisitor.mEvent->message != NS_LOAD + EventTarget* parentTarget = win && aVisitor.mEvent->mMessage != NS_LOAD ? win->GetParentTarget() : nullptr; aVisitor.mParentTarget = parentTarget; diff --git a/dom/base/PerformanceCompositeTiming.cpp b/dom/base/PerformanceCompositeTiming.cpp new file mode 100644 index 000000000000..48362ce2bd60 --- /dev/null +++ b/dom/base/PerformanceCompositeTiming.cpp @@ -0,0 +1,30 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "PerformanceCompositeTiming.h" +#include "mozilla/dom/PerformanceCompositeTimingBinding.h" + +using namespace mozilla::dom; + +PerformanceCompositeTiming::PerformanceCompositeTiming(nsISupports* aParent, + const nsAString& aName, + const DOMHighResTimeStamp& aStartTime, + uint32_t aSourceFrameNumber) +: PerformanceEntry(aParent, aName, NS_LITERAL_STRING("composite")) +, mStartTime(aStartTime) +, mSourceFrameNumber(aSourceFrameNumber) +{ + MOZ_ASSERT(aParent, "Parent performance object should be provided"); +} + +PerformanceCompositeTiming::~PerformanceCompositeTiming() +{ +} + +JSObject* +PerformanceCompositeTiming::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return PerformanceCompositeTimingBinding::Wrap(aCx, this, aGivenProto); +} diff --git a/dom/base/PerformanceCompositeTiming.h b/dom/base/PerformanceCompositeTiming.h new file mode 100644 index 000000000000..deee1598ad46 --- /dev/null +++ b/dom/base/PerformanceCompositeTiming.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_performancecompositetiming_h___ +#define mozilla_dom_performancecompositetiming_h___ + +#include "mozilla/dom/PerformanceEntry.h" + +namespace mozilla { +namespace dom { + +// http://www.w3.org/TR/frame-timing/#performancecompositetiming +class PerformanceCompositeTiming final : public PerformanceEntry +{ +public: + PerformanceCompositeTiming(nsISupports* aParent, + const nsAString& aName, + const DOMHighResTimeStamp& aStartTime, + uint32_t aSourceFrameNumber); + + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + virtual DOMHighResTimeStamp StartTime() const override + { + return mStartTime; + } + + uint32_t SourceFrameNumber() const + { + return mSourceFrameNumber; + } + +protected: + virtual ~PerformanceCompositeTiming(); + DOMHighResTimeStamp mStartTime; + uint32_t mSourceFrameNumber; +}; + +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_performancecompositetiming_h___ */ diff --git a/dom/base/PerformanceObserver.cpp b/dom/base/PerformanceObserver.cpp index 21d7741b1bbc..f3f9017f74c8 100644 --- a/dom/base/PerformanceObserver.cpp +++ b/dom/base/PerformanceObserver.cpp @@ -38,6 +38,7 @@ PerformanceObserver::PerformanceObserver(nsPIDOMWindow* aOwner, PerformanceObserverCallback& aCb) : mOwner(aOwner) , mCallback(&aCb) + , mConnected(false) { MOZ_ASSERT(mOwner); mPerformance = aOwner->GetPerformance(); @@ -46,6 +47,7 @@ PerformanceObserver::PerformanceObserver(nsPIDOMWindow* aOwner, PerformanceObserver::PerformanceObserver(WorkerPrivate* aWorkerPrivate, PerformanceObserverCallback& aCb) : mCallback(&aCb) + , mConnected(false) { MOZ_ASSERT(aWorkerPrivate); mPerformance = aWorkerPrivate->GlobalScope()->GetPerformance(); @@ -53,6 +55,8 @@ PerformanceObserver::PerformanceObserver(WorkerPrivate* aWorkerPrivate, PerformanceObserver::~PerformanceObserver() { + Disconnect(); + MOZ_ASSERT(!mConnected); } // static @@ -145,10 +149,15 @@ PerformanceObserver::Observe(const PerformanceObserverInit& aOptions, mEntryTypes = validEntryTypes; mPerformance->AddObserver(this); + mConnected = true; } void PerformanceObserver::Disconnect() { - mPerformance->RemoveObserver(this); + if (mConnected) { + MOZ_ASSERT(mPerformance); + mPerformance->RemoveObserver(this); + mConnected = false; + } } diff --git a/dom/base/PerformanceObserver.h b/dom/base/PerformanceObserver.h index 0a7fcd60330f..85de6ea02d53 100644 --- a/dom/base/PerformanceObserver.h +++ b/dom/base/PerformanceObserver.h @@ -68,6 +68,7 @@ class PerformanceObserver final : public nsISupports, nsRefPtr mCallback; nsRefPtr mPerformance; nsTArray mEntryTypes; + bool mConnected; }; } // namespace dom diff --git a/dom/base/PerformanceRenderTiming.cpp b/dom/base/PerformanceRenderTiming.cpp new file mode 100644 index 000000000000..c4f04f1dda93 --- /dev/null +++ b/dom/base/PerformanceRenderTiming.cpp @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "PerformanceRenderTiming.h" +#include "mozilla/dom/PerformanceRenderTimingBinding.h" + +using namespace mozilla::dom; + +PerformanceRenderTiming::PerformanceRenderTiming(nsISupports* aParent, + const nsAString& aName, + const DOMHighResTimeStamp& aStartTime, + const DOMHighResTimeStamp& aDuration, + uint32_t aSourceFrameNumber) +: PerformanceEntry(aParent, aName, NS_LITERAL_STRING("render")) +, mStartTime(aStartTime) +, mDuration(aDuration) +, mSourceFrameNumber(aSourceFrameNumber) +{ + MOZ_ASSERT(aParent, "Parent performance object should be provided"); +} + +PerformanceRenderTiming::~PerformanceRenderTiming() +{ +} + +JSObject* +PerformanceRenderTiming::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return PerformanceRenderTimingBinding::Wrap(aCx, this, aGivenProto); +} diff --git a/dom/base/PerformanceRenderTiming.h b/dom/base/PerformanceRenderTiming.h new file mode 100644 index 000000000000..63eeb106d931 --- /dev/null +++ b/dom/base/PerformanceRenderTiming.h @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_performancerendertiming_h___ +#define mozilla_dom_performancerendertiming_h___ + +#include "mozilla/dom/PerformanceEntry.h" + +namespace mozilla { +namespace dom { + +// http://www.w3.org/TR/frame-timing/#performancerendertiming +class PerformanceRenderTiming final : public PerformanceEntry +{ +public: + PerformanceRenderTiming(nsISupports* aParent, + const nsAString& aName, + const DOMHighResTimeStamp& aStartTime, + const DOMHighResTimeStamp& aDuration, + uint32_t aSourceFrameNumber); + + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + virtual DOMHighResTimeStamp StartTime() const override + { + return mStartTime; + } + + virtual DOMHighResTimeStamp Duration() const override + { + return mDuration; + } + + uint32_t SourceFrameNumber() const + { + return mSourceFrameNumber; + } + +protected: + virtual ~PerformanceRenderTiming(); + DOMHighResTimeStamp mStartTime; + DOMHighResTimeStamp mDuration; + uint32_t mSourceFrameNumber; +}; + +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_performancerendertiming_h___ */ diff --git a/dom/base/TextInputProcessor.cpp b/dom/base/TextInputProcessor.cpp index 1f13e1ba0f9c..7df54a2327cb 100644 --- a/dom/base/TextInputProcessor.cpp +++ b/dom/base/TextInputProcessor.cpp @@ -248,10 +248,10 @@ TextInputProcessor::IsValidEventTypeForComposition( const WidgetKeyboardEvent& aKeyboardEvent) const { // The key event type of composition methods must be "" or "keydown". - if (aKeyboardEvent.message == NS_KEY_DOWN) { + if (aKeyboardEvent.mMessage == NS_KEY_DOWN) { return true; } - if (aKeyboardEvent.message == NS_USER_DEFINED_EVENT && + if (aKeyboardEvent.mMessage == NS_USER_DEFINED_EVENT && aKeyboardEvent.userType && nsDependentAtomString(aKeyboardEvent.userType).EqualsLiteral("on")) { return true; @@ -306,9 +306,9 @@ TextInputProcessor::MaybeDispatchKeyupForComposition( return result; } - // If the message is NS_KEY_DOWN, the caller doesn't want TIP to dispatch + // If the mMessage is NS_KEY_DOWN, the caller doesn't want TIP to dispatch // keyup event. - if (aKeyboardEvent->message == NS_KEY_DOWN) { + if (aKeyboardEvent->mMessage == NS_KEY_DOWN) { return result; } diff --git a/dom/base/gen-usecounters.py b/dom/base/gen-usecounters.py index ee1299a8f59f..5b17c22e440d 100755 --- a/dom/base/gen-usecounters.py +++ b/dom/base/gen-usecounters.py @@ -13,7 +13,7 @@ import usecounters -AUTOGENERATED_WARNING_COMMENT = "/* THIS FILE IS AUTOGENERATED BY gen-usercounters.py - DO NOT EDIT */" +AUTOGENERATED_WARNING_COMMENT = "/* THIS FILE IS AUTOGENERATED BY gen-usecounters.py - DO NOT EDIT */" def generate_list(f, counters): def print_optional_macro_declare(name): diff --git a/dom/base/moz.build b/dom/base/moz.build index 406a640c89ea..fc4bae3054eb 100644 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -186,11 +186,13 @@ EXPORTS.mozilla.dom += [ 'NodeInfo.h', 'NodeInfoInlines.h', 'NodeIterator.h', + 'PerformanceCompositeTiming.h', 'PerformanceEntry.h', 'PerformanceMark.h', 'PerformanceMeasure.h', 'PerformanceObserver.h', 'PerformanceObserverEntryList.h', + 'PerformanceRenderTiming.h', 'PerformanceResourceTiming.h', 'ProcessGlobal.h', 'ResponsiveImageSelector.h', @@ -327,11 +329,13 @@ UNIFIED_SOURCES += [ 'nsXMLContentSerializer.cpp', 'nsXMLHttpRequest.cpp', 'nsXMLNameSpaceMap.cpp', + 'PerformanceCompositeTiming.cpp', 'PerformanceEntry.cpp', 'PerformanceMark.cpp', 'PerformanceMeasure.cpp', 'PerformanceObserver.cpp', 'PerformanceObserverEntryList.cpp', + 'PerformanceRenderTiming.cpp', 'PerformanceResourceTiming.cpp', 'PostMessageEvent.cpp', 'ProcessGlobal.cpp', diff --git a/dom/base/nsContentAreaDragDrop.cpp b/dom/base/nsContentAreaDragDrop.cpp index 6ded419e2b6e..0256e9f4710e 100644 --- a/dom/base/nsContentAreaDragDrop.cpp +++ b/dom/base/nsContentAreaDragDrop.cpp @@ -534,6 +534,14 @@ DragDataProducer::Produce(DataTransfer* aDataTransfer, // grab the href as the url, use alt text as the title of the // area if it's there. the drag data is the image tag and src // attribute. + nsCOMPtr imageURI; + image->GetCurrentURI(getter_AddRefs(imageURI)); + if (imageURI) { + nsAutoCString spec; + imageURI->GetSpec(spec); + CopyUTF8toUTF16(spec, mUrlString); + } + nsCOMPtr imageElement(do_QueryInterface(image)); // XXXbz Shouldn't we use the "title" attr for title? Using // "alt" seems very wrong.... @@ -541,10 +549,13 @@ DragDataProducer::Produce(DataTransfer* aDataTransfer, imageElement->GetAttribute(NS_LITERAL_STRING("alt"), mTitleString); } - mUrlString.Truncate(); + if (mTitleString.IsEmpty()) { + mTitleString = mUrlString; + } - // grab the image data, and its request. nsCOMPtr imgRequest; + + // grab the image data, and its request. nsCOMPtr img = nsContentUtils::GetImageFromContent(image, getter_AddRefs(imgRequest)); @@ -555,7 +566,7 @@ DragDataProducer::Produce(DataTransfer* aDataTransfer, // Fix the file extension in the URL if necessary if (imgRequest && mimeService) { nsCOMPtr imgUri; - imgRequest->GetCurrentURI(getter_AddRefs(imgUri)); + imgRequest->GetURI(getter_AddRefs(imgUri)); nsCOMPtr imgUrl(do_QueryInterface(imgUri)); @@ -576,7 +587,6 @@ DragDataProducer::Produce(DataTransfer* aDataTransfer, // pass out the image source string CopyUTF8toUTF16(spec, mImageSourceString); - mUrlString = mImageSourceString; bool validExtension; if (extension.IsEmpty() || @@ -611,18 +621,6 @@ DragDataProducer::Produce(DataTransfer* aDataTransfer, } } } - if (mUrlString.IsEmpty()) { - nsCOMPtr imageURI; - image->GetCurrentURI(getter_AddRefs(imageURI)); - if (imageURI) { - nsAutoCString spec; - imageURI->GetSpec(spec); - CopyUTF8toUTF16(spec, mUrlString); - } - } - if (mTitleString.IsEmpty()) { - mTitleString = mUrlString; - } if (parentLink) { // If we are dragging around an image in an anchor, then we diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index a357e13247b0..973abb83b019 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -254,6 +254,7 @@ bool nsContentUtils::sInitialized = false; bool nsContentUtils::sIsFullScreenApiEnabled = false; bool nsContentUtils::sTrustedFullScreenOnly = true; bool nsContentUtils::sIsCutCopyAllowed = true; +bool nsContentUtils::sIsFrameTimingPrefEnabled = false; bool nsContentUtils::sIsPerformanceTimingEnabled = false; bool nsContentUtils::sIsResourceTimingEnabled = false; bool nsContentUtils::sIsUserTimingLoggingEnabled = false; @@ -536,6 +537,9 @@ nsContentUtils::Init() Preferences::AddBoolVarCache(&sIsUserTimingLoggingEnabled, "dom.performance.enable_user_timing_logging", false); + Preferences::AddBoolVarCache(&sIsFrameTimingPrefEnabled, + "dom.enable_frame_timing", true); + Preferences::AddBoolVarCache(&sIsExperimentalAutocompleteEnabled, "dom.forms.autocomplete.experimental", false); @@ -5442,8 +5446,8 @@ nsContentUtils::SetDataTransferInEvent(WidgetDragEvent* aDragEvent) // For draggesture and dragstart events, the data transfer object is // created before the event fires, so it should already be set. For other // drag events, get the object from the drag session. - NS_ASSERTION(aDragEvent->message != NS_DRAGDROP_GESTURE && - aDragEvent->message != NS_DRAGDROP_START, + NS_ASSERTION(aDragEvent->mMessage != NS_DRAGDROP_GESTURE && + aDragEvent->mMessage != NS_DRAGDROP_START, "draggesture event created without a dataTransfer"); nsCOMPtr dragSession = GetDragSession(); @@ -5462,20 +5466,22 @@ nsContentUtils::SetDataTransferInEvent(WidgetDragEvent* aDragEvent) // means, for instance calling the drag service directly, or a drag // from another application. In either case, a new dataTransfer should // be created that reflects the data. - initialDataTransfer = new DataTransfer(aDragEvent->target, aDragEvent->message, true, -1); + initialDataTransfer = + new DataTransfer(aDragEvent->target, aDragEvent->mMessage, true, -1); // now set it in the drag session so we don't need to create it again dragSession->SetDataTransfer(initialDataTransfer); } bool isCrossDomainSubFrameDrop = false; - if (aDragEvent->message == NS_DRAGDROP_DROP || - aDragEvent->message == NS_DRAGDROP_DRAGDROP) { + if (aDragEvent->mMessage == NS_DRAGDROP_DROP || + aDragEvent->mMessage == NS_DRAGDROP_DRAGDROP) { isCrossDomainSubFrameDrop = CheckForSubFrameDrop(dragSession, aDragEvent); } // each event should use a clone of the original dataTransfer. - initialDataTransfer->Clone(aDragEvent->target, aDragEvent->message, aDragEvent->userCancelled, + initialDataTransfer->Clone(aDragEvent->target, aDragEvent->mMessage, + aDragEvent->userCancelled, isCrossDomainSubFrameDrop, getter_AddRefs(aDragEvent->dataTransfer)); NS_ENSURE_TRUE(aDragEvent->dataTransfer, NS_ERROR_OUT_OF_MEMORY); @@ -5483,16 +5489,16 @@ nsContentUtils::SetDataTransferInEvent(WidgetDragEvent* aDragEvent) // for the dragenter and dragover events, initialize the drop effect // from the drop action, which platform specific widget code sets before // the event is fired based on the keyboard state. - if (aDragEvent->message == NS_DRAGDROP_ENTER || - aDragEvent->message == NS_DRAGDROP_OVER) { + if (aDragEvent->mMessage == NS_DRAGDROP_ENTER || + aDragEvent->mMessage == NS_DRAGDROP_OVER) { uint32_t action, effectAllowed; dragSession->GetDragAction(&action); aDragEvent->dataTransfer->GetEffectAllowedInt(&effectAllowed); aDragEvent->dataTransfer->SetDropEffectInt(FilterDropEffect(action, effectAllowed)); } - else if (aDragEvent->message == NS_DRAGDROP_DROP || - aDragEvent->message == NS_DRAGDROP_DRAGDROP || - aDragEvent->message == NS_DRAGDROP_END) { + else if (aDragEvent->mMessage == NS_DRAGDROP_DROP || + aDragEvent->mMessage == NS_DRAGDROP_DRAGDROP || + aDragEvent->mMessage == NS_DRAGDROP_END) { // For the drop and dragend events, set the drop effect based on the // last value that the dropEffect had. This will have been set in // EventStateManager::PostHandleEvent for the last dragenter or @@ -6776,6 +6782,13 @@ nsContentUtils::IsCutCopyAllowed() IsCallerChrome(); } +/* static */ +bool +nsContentUtils::IsFrameTimingEnabled() +{ + return sIsFrameTimingPrefEnabled; +} + /* static */ bool nsContentUtils::HaveEqualPrincipals(nsIDocument* aDoc1, nsIDocument* aDoc2) diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 19f64898aa26..6f8d6f5ca385 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -1977,6 +1977,11 @@ class nsContentUtils return sSendPerformanceTimingNotifications; } + /* + * Returns true if the frame timing APIs are enabled. + */ + static bool IsFrameTimingEnabled(); + /* * Returns true if URL setters should percent encode the Hash/Ref segment * and getters should return the percent decoded value of the segment @@ -2574,6 +2579,7 @@ class nsContentUtils static bool sIsPerformanceTimingEnabled; static bool sIsResourceTimingEnabled; static bool sIsUserTimingLoggingEnabled; + static bool sIsFrameTimingPrefEnabled; static bool sIsExperimentalAutocompleteEnabled; static bool sEncodeDecodeURLHash; static bool sGettersDecodeURLHash; diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 3f089d895529..2e0dee9c92c2 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -92,6 +92,7 @@ #include "nsIAuthPrompt2.h" #include "nsIScriptSecurityManager.h" +#include "nsIPermissionManager.h" #include "nsIPrincipal.h" #include "nsIDOMWindow.h" @@ -5832,8 +5833,32 @@ bool nsDocument::IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject) { JS::Rooted obj(aCx, aObject); - return Preferences::GetBool("dom.webcomponents.enabled") || - IsInCertifiedApp(aCx, obj); + + if (Preferences::GetBool("dom.webcomponents.enabled")) { + return true; + } + + // Check for the webcomponents permission. See Bug 1181555. + JSAutoCompartment ac(aCx, obj); + JS::Rooted global(aCx, JS_GetGlobalForObject(aCx, obj)); + nsCOMPtr window = + do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(global)); + + if (window) { + nsresult rv; + nsCOMPtr permMgr = + do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, false); + + uint32_t perm; + rv = permMgr->TestPermissionFromWindow( + window, "moz-extremely-unstable-and-will-change-webcomponents", &perm); + NS_ENSURE_SUCCESS(rv, false); + + return perm == nsIPermissionManager::ALLOW_ACTION; + } + + return false; } nsresult @@ -8113,7 +8138,7 @@ nsDocument::PreHandleEvent(EventChainPreVisitor& aVisitor) aVisitor.mForceContentDispatch = true; // Load events must not propagate to |window| object, see bug 335251. - if (aVisitor.mEvent->message != NS_LOAD) { + if (aVisitor.mEvent->mMessage != NS_LOAD) { nsGlobalWindow* window = static_cast(GetWindow()); aVisitor.mParentTarget = window ? window->GetTargetForEventTargetChain() : nullptr; diff --git a/dom/base/nsFrameMessageManager.cpp b/dom/base/nsFrameMessageManager.cpp index dd673f8ffd64..747d864871c2 100644 --- a/dom/base/nsFrameMessageManager.cpp +++ b/dom/base/nsFrameMessageManager.cpp @@ -1753,7 +1753,7 @@ nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript( NS_NewChannel(getter_AddRefs(channel), uri, nsContentUtils::GetSystemPrincipal(), - nsILoadInfo::SEC_NORMAL, + nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, nsIContentPolicy::TYPE_OTHER); if (!channel) { @@ -1761,7 +1761,8 @@ nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript( } nsCOMPtr input; - channel->Open(getter_AddRefs(input)); + rv = channel->Open2(getter_AddRefs(input)); + NS_ENSURE_SUCCESS_VOID(rv); nsString dataString; char16_t* dataStringBuf = nullptr; size_t dataStringLength = 0; diff --git a/dom/base/nsGenConImageContent.cpp b/dom/base/nsGenConImageContent.cpp index 74b017560e99..98fcae28fde3 100644 --- a/dom/base/nsGenConImageContent.cpp +++ b/dom/base/nsGenConImageContent.cpp @@ -49,8 +49,8 @@ class nsGenConImageContent final : public nsXMLElement, virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override { MOZ_ASSERT(IsInNativeAnonymousSubtree()); - if (aVisitor.mEvent->message == NS_LOAD || - aVisitor.mEvent->message == NS_LOAD_ERROR) { + if (aVisitor.mEvent->mMessage == NS_LOAD || + aVisitor.mEvent->mMessage == NS_LOAD_ERROR) { // Don't propagate the events to the parent. return NS_OK; } diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 854e1cdd74c4..91097df314ba 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1918,10 +1918,7 @@ nsGlobalWindow::UnmarkGrayTimers() if (timeout->mScriptHandler) { Function* f = timeout->mScriptHandler->GetCallback(); if (f) { - // Callable() already does xpc_UnmarkGrayObject. - DebugOnly > o = f->Callable(); - MOZ_ASSERT(!JS::ObjectIsMarkedGray(o.value), - "Should have been unmarked"); + f->MarkForCC(); } } } @@ -3047,7 +3044,7 @@ nsGlobalWindow::PreHandleEvent(EventChainPreVisitor& aVisitor) { NS_PRECONDITION(IsInnerWindow(), "PreHandleEvent is used on outer window!?"); static uint32_t count = 0; - uint32_t msg = aVisitor.mEvent->message; + uint32_t msg = aVisitor.mEvent->mMessage; aVisitor.mCanHandle = true; aVisitor.mForceContentDispatch = true; //FIXME! Bug 329119 @@ -3255,7 +3252,7 @@ nsGlobalWindow::PostHandleEvent(EventChainPostVisitor& aVisitor) NS_PRECONDITION(IsInnerWindow(), "PostHandleEvent is used on outer window!?"); // Return early if there is nothing to do. - switch (aVisitor.mEvent->message) { + switch (aVisitor.mEvent->mMessage) { case NS_RESIZE_EVENT: case NS_PAGE_UNLOAD: case NS_LOAD: @@ -3270,9 +3267,9 @@ nsGlobalWindow::PostHandleEvent(EventChainPostVisitor& aVisitor) nsCOMPtr kungFuDeathGrip1(mChromeEventHandler); nsCOMPtr kungFuDeathGrip2(GetContextInternal()); - if (aVisitor.mEvent->message == NS_RESIZE_EVENT) { + if (aVisitor.mEvent->mMessage == NS_RESIZE_EVENT) { mIsHandlingResizeEvent = false; - } else if (aVisitor.mEvent->message == NS_PAGE_UNLOAD && + } else if (aVisitor.mEvent->mMessage == NS_PAGE_UNLOAD && aVisitor.mEvent->mFlags.mIsTrusted) { // Execute bindingdetached handlers before we tear ourselves // down. @@ -3280,7 +3277,7 @@ nsGlobalWindow::PostHandleEvent(EventChainPostVisitor& aVisitor) mDoc->BindingManager()->ExecuteDetachedHandlers(); } mIsDocumentLoaded = false; - } else if (aVisitor.mEvent->message == NS_LOAD && + } else if (aVisitor.mEvent->mMessage == NS_LOAD && aVisitor.mEvent->mFlags.mIsTrusted) { // This is page load event since load events don't propagate to |window|. // @see nsDocument::PreHandleEvent. diff --git a/dom/base/nsHostObjectProtocolHandler.cpp b/dom/base/nsHostObjectProtocolHandler.cpp index 5bf823cbb573..85316b5e6966 100644 --- a/dom/base/nsHostObjectProtocolHandler.cpp +++ b/dom/base/nsHostObjectProtocolHandler.cpp @@ -5,17 +5,19 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsHostObjectProtocolHandler.h" -#include "nsHostObjectURI.h" -#include "nsError.h" -#include "nsClassHashtable.h" -#include "nsNetUtil.h" -#include "nsIPrincipal.h" + #include "DOMMediaStream.h" -#include "mozilla/dom/MediaSource.h" -#include "nsIMemoryReporter.h" #include "mozilla/dom/File.h" -#include "mozilla/Preferences.h" +#include "mozilla/dom/MediaSource.h" #include "mozilla/LoadInfo.h" +#include "mozilla/Preferences.h" +#include "nsClassHashtable.h" +#include "nsError.h" +#include "nsHostObjectURI.h" +#include "nsIMemoryReporter.h" +#include "nsIPrincipal.h" +#include "nsIUUIDGenerator.h" +#include "nsNetUtil.h" using mozilla::dom::BlobImpl; using mozilla::ErrorResult; diff --git a/dom/base/nsQueryContentEventResult.cpp b/dom/base/nsQueryContentEventResult.cpp index 21fbc999394d..a882aeeabee1 100644 --- a/dom/base/nsQueryContentEventResult.cpp +++ b/dom/base/nsQueryContentEventResult.cpp @@ -159,7 +159,7 @@ void nsQueryContentEventResult::SetEventResult(nsIWidget* aWidget, const WidgetQueryContentEvent &aEvent) { - mEventID = aEvent.message; + mEventID = aEvent.mMessage; mSucceeded = aEvent.mSucceeded; mReversed = aEvent.mReply.mReversed; mRect = aEvent.mReply.mRect; diff --git a/dom/base/nsScreen.cpp b/dom/base/nsScreen.cpp index 06d86d47536a..5dd1fbdb33d8 100644 --- a/dom/base/nsScreen.cpp +++ b/dom/base/nsScreen.cpp @@ -197,6 +197,29 @@ nsScreen::GetSlowMozOrientation(nsAString& aOrientation) return NS_OK; } +static void +UpdateDocShellOrientationLock(nsPIDOMWindow* aWindow, + ScreenOrientationInternal aOrientation) +{ + if (!aWindow) { + return; + } + + nsCOMPtr docShell = aWindow->GetDocShell(); + if (!docShell) { + return; + } + + nsCOMPtr root; + docShell->GetSameTypeRootTreeItem(getter_AddRefs(root)); + nsCOMPtr rootShell(do_QueryInterface(root)); + if (!rootShell) { + return; + } + + rootShell->SetOrientationLock(aOrientation); +} + bool nsScreen::MozLockOrientation(const nsAString& aOrientation, ErrorResult& aRv) { @@ -245,8 +268,10 @@ nsScreen::MozLockOrientation(const Sequence& aOrientations, case ScreenOrientation::LOCK_DENIED: return false; case ScreenOrientation::LOCK_ALLOWED: + UpdateDocShellOrientationLock(GetOwner(), orientation); return mScreenOrientation->LockDeviceOrientation(orientation, false, aRv); case ScreenOrientation::FULLSCREEN_LOCK_ALLOWED: + UpdateDocShellOrientationLock(GetOwner(), orientation); return mScreenOrientation->LockDeviceOrientation(orientation, true, aRv); } @@ -258,6 +283,7 @@ nsScreen::MozLockOrientation(const Sequence& aOrientations, void nsScreen::MozUnlockOrientation() { + UpdateDocShellOrientationLock(GetOwner(), eScreenOrientation_None); mScreenOrientation->UnlockDeviceOrientation(); } diff --git a/dom/base/nsScriptNameSpaceManager.h b/dom/base/nsScriptNameSpaceManager.h index 84e10b9bb355..c9d5e7711be4 100644 --- a/dom/base/nsScriptNameSpaceManager.h +++ b/dom/base/nsScriptNameSpaceManager.h @@ -22,6 +22,7 @@ #define nsScriptNameSpaceManager_h__ #include "mozilla/MemoryReporting.h" +#include "nsBaseHashtable.h" #include "nsIMemoryReporter.h" #include "nsIScriptNameSpaceManager.h" #include "nsString.h" diff --git a/dom/base/nsStyleLinkElement.cpp b/dom/base/nsStyleLinkElement.cpp index c7a175c021d2..04734cc4ffeb 100644 --- a/dom/base/nsStyleLinkElement.cpp +++ b/dom/base/nsStyleLinkElement.cpp @@ -31,6 +31,16 @@ #include "nsStyleUtil.h" #include "nsQueryObject.h" +static PRLogModuleInfo* +GetSriLog() +{ + static PRLogModuleInfo *gSriPRLog; + if (!gSriPRLog) { + gSriPRLog = PR_NewLogModule("SRI"); + } + return gSriPRLog; +} + using namespace mozilla; using namespace mozilla::dom; diff --git a/dom/base/nsStyledElement.cpp b/dom/base/nsStyledElement.cpp index a8d4d984aec9..fd0d1df8ddc4 100644 --- a/dom/base/nsStyledElement.cpp +++ b/dom/base/nsStyledElement.cpp @@ -151,8 +151,10 @@ nsStyledElementNotElementCSSInlineStyle::ParseStyleAttribute(const nsAString& aV bool aForceInDataDoc) { nsIDocument* doc = OwnerDoc(); + bool isNativeAnon = IsInNativeAnonymousSubtree(); - if (!nsStyleUtil::CSPAllowsInlineStyle(nullptr, NodePrincipal(), + if (!isNativeAnon && + !nsStyleUtil::CSPAllowsInlineStyle(nullptr, NodePrincipal(), doc->GetDocumentURI(), 0, aValue, nullptr)) return; @@ -162,8 +164,7 @@ nsStyledElementNotElementCSSInlineStyle::ParseStyleAttribute(const nsAString& aV doc->IsStaticDocument()) { bool isCSS = true; // assume CSS until proven otherwise - if (!IsInNativeAnonymousSubtree()) { // native anonymous content - // always assumes CSS + if (!isNativeAnon) { // native anonymous content always assumes CSS nsAutoString styleType; doc->GetHeaderData(nsGkAtoms::headerContentStyleType, styleType); if (!styleType.IsEmpty()) { diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 4b5c98110ebf..1a6d5c19b90b 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -252,11 +252,13 @@ support-files = iframe_postMessages.html test_performance_observer.js performance_observer.html + test_anonymousContent_style_csp.html^headers^ [test_anonymousContent_api.html] [test_anonymousContent_append_after_reflow.html] [test_anonymousContent_insert.html] [test_anonymousContent_manipulate_content.html] +[test_anonymousContent_style_csp.html] [test_appname_override.html] [test_async_setTimeout_stack.html] [test_async_setTimeout_stack_across_globals.html] diff --git a/dom/base/test/performance_observer.html b/dom/base/test/performance_observer.html index d194c17e42df..b24479d8c924 100644 --- a/dom/base/test/performance_observer.html +++ b/dom/base/test/performance_observer.html @@ -48,6 +48,7 @@ observedEntryList = list; }); observer.observe({entryTypes: ['resource']}); + t.add_cleanup(() => observer.disconnect()); assert_equals(observedEntries.length, 0); diff --git a/dom/base/test/test_anonymousContent_style_csp.html b/dom/base/test/test_anonymousContent_style_csp.html new file mode 100644 index 000000000000..69d133aad233 --- /dev/null +++ b/dom/base/test/test_anonymousContent_style_csp.html @@ -0,0 +1,28 @@ + + + + + + Test for Bug 1185351 - Make sure that we don't enforce CSP on styles for AnonymousContent + + + + +
+
text content
+
+ + + diff --git a/dom/base/test/test_anonymousContent_style_csp.html^headers^ b/dom/base/test/test_anonymousContent_style_csp.html^headers^ new file mode 100644 index 000000000000..b7b3c8a4f9ea --- /dev/null +++ b/dom/base/test/test_anonymousContent_style_csp.html^headers^ @@ -0,0 +1 @@ +Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' diff --git a/dom/base/test/test_performance_observer.js b/dom/base/test/test_performance_observer.js index 5eb43c10ceec..cef0f33cd178 100644 --- a/dom/base/test/test_performance_observer.js +++ b/dom/base/test/test_performance_observer.js @@ -44,6 +44,7 @@ test(t => { list.getEntries().forEach(entry => observedEntries.push(entry)); }); observer.observe({entryTypes: ['mark', 'measure']}); + t.add_cleanup(() => observer.disconnect()); assert_equals(observedEntries.length, 0, "User timing entries should never be observed."); @@ -107,6 +108,7 @@ test(t => { observedEntryList = list; }); observer.observe({entryTypes: ['mark']}); + t.add_cleanup(() => observer.disconnect()); performance.mark("test"); assert_array_equals(observedEntryList.getEntries({"entryType": "mark"}), @@ -150,6 +152,7 @@ test(t => { observedEntryList = list; }); observer.observe({entryTypes: ['mark', 'measure']}); + t.add_cleanup(() => observer.disconnect()); performance.mark("test"); assert_array_equals(observedEntryList.getEntriesByType("mark"), @@ -169,6 +172,7 @@ test(t => { observedEntryList = list; }); observer.observe({entryTypes: ['mark', 'measure']}); + t.add_cleanup(() => observer.disconnect()); performance.mark("test"); assert_array_equals(observedEntryList.getEntriesByName("test"), @@ -190,6 +194,7 @@ test(t => { observer.observe({entryTypes: ['mark', 'measure']}); observer.observe({entryTypes: ['mark', 'measure']}); + t.add_cleanup(() => observer.disconnect()); performance.mark("test-start"); performance.mark("test-end"); @@ -210,6 +215,7 @@ test(t => { observer.observe({entryTypes: ['mark', 'measure']}); observer.observe({entryTypes: ['mark']}); + t.add_cleanup(() => observer.disconnect()); performance.mark("test-start"); performance.mark("test-end"); @@ -230,6 +236,7 @@ test(t => { observer.observe({entryTypes: ['mark']}); observer.observe({entryTypes: ['measure']}); + t.add_cleanup(() => observer.disconnect()); performance.mark("test-start"); performance.mark("test-end"); diff --git a/dom/bindings/CallbackObject.h b/dom/bindings/CallbackObject.h index ff1bdcbf039f..99958876c990 100644 --- a/dom/bindings/CallbackObject.h +++ b/dom/bindings/CallbackObject.h @@ -83,6 +83,16 @@ class CallbackObject : public nsISupports return result; } + void MarkForCC() + { + if (mCallback) { + JS::ExposeObjectToActiveJS(mCallback); + } + if (mCreationStack) { + JS::ExposeObjectToActiveJS(mCreationStack); + } + } + /* * This getter does not change the color of the JSObject meaning that the * object returned is not guaranteed to be kept alive past the next CC. diff --git a/dom/events/ClipboardEvent.cpp b/dom/events/ClipboardEvent.cpp index c44fcf38ab96..37d4148d64ec 100644 --- a/dom/events/ClipboardEvent.cpp +++ b/dom/events/ClipboardEvent.cpp @@ -108,8 +108,8 @@ ClipboardEvent::GetClipboardData() new DataTransfer(ToSupports(this), NS_COPY, false, -1); } else { event->clipboardData = - new DataTransfer(ToSupports(this), event->message, - event->message == NS_PASTE, + new DataTransfer(ToSupports(this), event->mMessage, + event->mMessage == NS_PASTE, nsIClipboard::kGlobalClipboard); } } diff --git a/dom/events/DataTransfer.h b/dom/events/DataTransfer.h index 8615bbf15ad4..4fd5ff7d155a 100644 --- a/dom/events/DataTransfer.h +++ b/dom/events/DataTransfer.h @@ -255,7 +255,7 @@ class DataTransfer final : public nsIDOMDataTransfer, nsCOMPtr mParent; // the event type this data transfer is for. This will correspond to an - // event->message value. + // event->mMessage value. uint32_t mEventType; // the drop effect and effect allowed diff --git a/dom/events/Event.cpp b/dom/events/Event.cpp index ce3ae0da624c..0f3ec3442cbb 100644 --- a/dom/events/Event.cpp +++ b/dom/events/Event.cpp @@ -265,12 +265,12 @@ Event::GetType(nsAString& aType) aType = mEvent->typeString; return NS_OK; } - const char* name = GetEventName(mEvent->message); + const char* name = GetEventName(mEvent->mMessage); if (name) { CopyASCIItoUTF16(name, aType); return NS_OK; - } else if (mEvent->message == NS_USER_DEFINED_EVENT && mEvent->userType) { + } else if (mEvent->mMessage == NS_USER_DEFINED_EVENT && mEvent->userType) { aType = Substring(nsDependentAtomString(mEvent->userType), 2); // Remove "on" mEvent->typeString = aType; return NS_OK; @@ -564,10 +564,10 @@ Event::SetEventType(const nsAString& aEventTypeArg) mEvent->typeString.Truncate(); mEvent->userType = nsContentUtils::GetEventIdAndAtom(aEventTypeArg, mEvent->mClass, - &(mEvent->message)); + &(mEvent->mMessage)); } else { mEvent->userType = nullptr; - mEvent->message = NS_USER_DEFINED_EVENT; + mEvent->mMessage = NS_USER_DEFINED_EVENT; mEvent->typeString = aEventTypeArg; } } @@ -715,7 +715,7 @@ Event::GetEventPopupControlState(WidgetEvent* aEvent, nsIDOMEvent* aDOMEvent) // triggered while handling user input. See // nsPresShell::HandleEventInternal() for details. if (EventStateManager::IsHandlingUserInput()) { - switch(aEvent->message) { + switch(aEvent->mMessage) { case NS_FORM_SELECTED : if (PopupAllowedForEvent("select")) { abuse = openControlled; @@ -734,7 +734,7 @@ Event::GetEventPopupControlState(WidgetEvent* aEvent, nsIDOMEvent* aDOMEvent) // while handling user input. See // nsPresShell::HandleEventInternal() for details. if (EventStateManager::IsHandlingUserInput()) { - switch(aEvent->message) { + switch(aEvent->mMessage) { case NS_EDITOR_INPUT: if (PopupAllowedForEvent("input")) { abuse = openControlled; @@ -748,7 +748,7 @@ Event::GetEventPopupControlState(WidgetEvent* aEvent, nsIDOMEvent* aDOMEvent) // while handling user input. See // nsPresShell::HandleEventInternal() for details. if (EventStateManager::IsHandlingUserInput()) { - switch(aEvent->message) { + switch(aEvent->mMessage) { case NS_FORM_CHANGE : if (PopupAllowedForEvent("change")) { abuse = openControlled; @@ -763,7 +763,7 @@ Event::GetEventPopupControlState(WidgetEvent* aEvent, nsIDOMEvent* aDOMEvent) case eKeyboardEventClass: if (aEvent->mFlags.mIsTrusted) { uint32_t key = aEvent->AsKeyboardEvent()->keyCode; - switch(aEvent->message) { + switch(aEvent->mMessage) { case NS_KEY_PRESS : // return key on focused button. see note at NS_MOUSE_CLICK. if (key == nsIDOMKeyEvent::DOM_VK_RETURN) { @@ -790,7 +790,7 @@ Event::GetEventPopupControlState(WidgetEvent* aEvent, nsIDOMEvent* aDOMEvent) break; case eTouchEventClass: if (aEvent->mFlags.mIsTrusted) { - switch (aEvent->message) { + switch (aEvent->mMessage) { case NS_TOUCH_START : if (PopupAllowedForEvent("touchstart")) { abuse = openControlled; @@ -807,7 +807,7 @@ Event::GetEventPopupControlState(WidgetEvent* aEvent, nsIDOMEvent* aDOMEvent) case eMouseEventClass: if (aEvent->mFlags.mIsTrusted && aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) { - switch(aEvent->message) { + switch(aEvent->mMessage) { case NS_MOUSE_BUTTON_UP : if (PopupAllowedForEvent("mouseup")) { abuse = openControlled; @@ -840,7 +840,7 @@ Event::GetEventPopupControlState(WidgetEvent* aEvent, nsIDOMEvent* aDOMEvent) // triggered while handling user input. See // nsPresShell::HandleEventInternal() for details. if (EventStateManager::IsHandlingUserInput()) { - switch(aEvent->message) { + switch(aEvent->mMessage) { case NS_FORM_SUBMIT : if (PopupAllowedForEvent("submit")) { abuse = openControlled; diff --git a/dom/events/Event.h b/dom/events/Event.h index 11b72e39d0a5..c5f261778d50 100644 --- a/dom/events/Event.h +++ b/dom/events/Event.h @@ -270,7 +270,7 @@ class Event : public EventBase, bool mPrivateDataDuplicated; bool mIsMainThreadEvent; // True when popup control check should rely on event.type, not - // WidgetEvent.message. + // WidgetEvent.mMessage. bool mWantsPopupControlCheck; }; diff --git a/dom/events/EventDispatcher.cpp b/dom/events/EventDispatcher.cpp index a347acceabc0..d4a28bcd3f2c 100644 --- a/dom/events/EventDispatcher.cpp +++ b/dom/events/EventDispatcher.cpp @@ -420,12 +420,12 @@ EventDispatcher::Dispatch(nsISupports* aTarget, NS_ASSERTION(aEvent, "Trying to dispatch without WidgetEvent!"); NS_ENSURE_TRUE(!aEvent->mFlags.mIsBeingDispatched, NS_ERROR_DOM_INVALID_STATE_ERR); - NS_ASSERTION(!aTargets || !aEvent->message, "Wrong parameters!"); + NS_ASSERTION(!aTargets || !aEvent->mMessage, "Wrong parameters!"); // If we're dispatching an already created DOMEvent object, make // sure it is initialized! // If aTargets is non-null, the event isn't going to be dispatched. - NS_ENSURE_TRUE(aEvent->message || !aDOMEvent || aTargets, + NS_ENSURE_TRUE(aEvent->mMessage || !aDOMEvent || aTargets, NS_ERROR_DOM_INVALID_STATE_ERR); #ifdef MOZ_TASK_TRACER @@ -493,7 +493,8 @@ EventDispatcher::Dispatch(nsISupports* aTarget, } #ifdef DEBUG - if (aEvent->message != NS_EVENT_NULL && !nsContentUtils::IsSafeToRunScript()) { + if (aEvent->mMessage != NS_EVENT_NULL && + !nsContentUtils::IsSafeToRunScript()) { nsresult rv = NS_ERROR_FAILURE; if (target->GetContextForEventHandlers(&rv) || NS_FAILED(rv)) { diff --git a/dom/events/EventDispatcher.h b/dom/events/EventDispatcher.h index 1e5329b5bd4c..945ff010a266 100644 --- a/dom/events/EventDispatcher.h +++ b/dom/events/EventDispatcher.h @@ -246,7 +246,7 @@ class EventDispatcher * Neither aTarget nor aEvent is allowed to be nullptr. * * If aTargets is non-null, event target chain will be created, but - * event won't be handled. In this case aEvent->message should be + * event won't be handled. In this case aEvent->mMessage should be * NS_EVENT_NULL. * @note Use this method when dispatching a WidgetEvent. */ diff --git a/dom/events/EventListenerManager.cpp b/dom/events/EventListenerManager.cpp index e6a824d4a438..3c6029c94b59 100644 --- a/dom/events/EventListenerManager.cpp +++ b/dom/events/EventListenerManager.cpp @@ -558,20 +558,20 @@ EventListenerManager::ListenerCanHandle(Listener* aListener, WidgetEvent* aEvent) { // This is slightly different from EVENT_TYPE_EQUALS in that it returns - // true even when aEvent->message == NS_USER_DEFINED_EVENT and + // true even when aEvent->mMessage == NS_USER_DEFINED_EVENT and // aListener=>mEventType != NS_USER_DEFINED_EVENT as long as the atoms are // the same if (aListener->mAllEvents) { return true; } - if (aEvent->message == NS_USER_DEFINED_EVENT) { + if (aEvent->mMessage == NS_USER_DEFINED_EVENT) { if (mIsMainThreadELM) { return aListener->mTypeAtom == aEvent->userType; } return aListener->mTypeString.Equals(aEvent->typeString); } MOZ_ASSERT(mIsMainThreadELM); - return aListener->mEventType == aEvent->message; + return aListener->mEventType == aEvent->mMessage; } void @@ -1163,7 +1163,7 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext, aEvent->currentTarget = nullptr; if (mIsMainThreadELM && !hasListener) { - mNoListenerForEvent = aEvent->message; + mNoListenerForEvent = aEvent->mMessage; mNoListenerForEventAtom = aEvent->userType; } @@ -1469,13 +1469,12 @@ EventListenerManager::MarkForCC() const TypedEventHandler& typedHandler = jsEventHandler->GetTypedEventHandler(); if (typedHandler.HasEventHandler()) { - JS::ExposeObjectToActiveJS(typedHandler.Ptr()->Callable()); + typedHandler.Ptr()->MarkForCC(); } } else if (listener.mListenerType == Listener::eWrappedJSListener) { xpc_TryUnmarkWrappedGrayObject(listener.mListener.GetXPCOMCallback()); } else if (listener.mListenerType == Listener::eWebIDLListener) { - // Callback() unmarks gray - listener.mListener.GetWebIDLCallback()->Callback(); + listener.mListener.GetWebIDLCallback()->MarkForCC(); } } if (mRefCnt.IsPurple()) { diff --git a/dom/events/EventListenerManager.h b/dom/events/EventListenerManager.h index c55f1f53eda5..c898e99a240a 100644 --- a/dom/events/EventListenerManager.h +++ b/dom/events/EventListenerManager.h @@ -321,7 +321,7 @@ class EventListenerManager final } // Check if we already know that there is no event listener for the event. - if (mNoListenerForEvent == aEvent->message && + if (mNoListenerForEvent == aEvent->mMessage && (mNoListenerForEvent != NS_USER_DEFINED_EVENT || mNoListenerForEventAtom == aEvent->userType)) { return; diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index 2fff07faa2ca..dc7d542960b5 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -499,8 +499,8 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext, WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); if (aEvent->mFlags.mIsTrusted && ((mouseEvent && mouseEvent->IsReal() && - mouseEvent->message != NS_MOUSE_ENTER_WIDGET && - mouseEvent->message != NS_MOUSE_EXIT_WIDGET) || + mouseEvent->mMessage != NS_MOUSE_ENTER_WIDGET && + mouseEvent->mMessage != NS_MOUSE_EXIT_WIDGET) || aEvent->mClass == eWheelEventClass || aEvent->mClass == eKeyboardEventClass)) { if (gMouseOrKeyboardEventCounter == 0) { @@ -542,7 +542,7 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext, *aStatus = nsEventStatus_eIgnore; - switch (aEvent->message) { + switch (aEvent->mMessage) { case NS_CONTEXTMENU: if (sIsPointerLocked) { return NS_ERROR_DOM_INVALID_STATE_ERR; @@ -615,7 +615,7 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext, // Treat it as a synthetic move so we don't generate spurious // "exit" or "move" events. Any necessary "out" or "over" events // will be generated by GenerateMouseEnterExit - mouseEvent->message = NS_MOUSE_MOVE; + mouseEvent->mMessage = NS_MOUSE_MOVE; mouseEvent->reason = WidgetMouseEvent::eSynthesized; // then fall through... } else { @@ -625,7 +625,7 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext, } GenerateMouseEnterExit(mouseEvent); //This is a window level mouse exit event and should stop here - aEvent->message = 0; + aEvent->mMessage = 0; break; } case NS_MOUSE_MOVE: @@ -717,7 +717,7 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext, mCurrentTargetContent = content; } - if (aEvent->message != NS_WHEEL_WHEEL) { + if (aEvent->mMessage != NS_WHEEL_WHEEL) { break; } @@ -1167,7 +1167,7 @@ CrossProcessSafeEvent(const WidgetEvent& aEvent) case eWheelEventClass: return true; case eMouseEventClass: - switch (aEvent.message) { + switch (aEvent.mMessage) { case NS_MOUSE_BUTTON_DOWN: case NS_MOUSE_BUTTON_UP: case NS_MOUSE_MOVE: @@ -1179,7 +1179,7 @@ CrossProcessSafeEvent(const WidgetEvent& aEvent) return false; } case eTouchEventClass: - switch (aEvent.message) { + switch (aEvent.mMessage) { case NS_TOUCH_START: case NS_TOUCH_MOVE: case NS_TOUCH_END: @@ -1189,7 +1189,7 @@ CrossProcessSafeEvent(const WidgetEvent& aEvent) return false; } case eDragEventClass: - switch (aEvent.message) { + switch (aEvent.mMessage) { case NS_DRAGDROP_OVER: case NS_DRAGDROP_EXIT: case NS_DRAGDROP_DROP: @@ -1215,7 +1215,7 @@ EventStateManager::HandleCrossProcessEvent(WidgetEvent* aEvent, // NB: the elements of |targets| must be unique, for correctness. nsAutoTArray, 1> targets; if (aEvent->mClass != eTouchEventClass || - aEvent->message == NS_TOUCH_START) { + aEvent->mMessage == NS_TOUCH_START) { // If this event only has one target, and it's remote, add it to // the array. nsIFrame* frame = GetEventTarget(); @@ -2588,7 +2588,7 @@ EventStateManager::DecideGestureEvent(WidgetGestureNotifyEvent* aEvent, nsIFrame* targetFrame) { - NS_ASSERTION(aEvent->message == NS_GESTURENOTIFY_EVENT_START, + NS_ASSERTION(aEvent->mMessage == NS_GESTURENOTIFY_EVENT_START, "DecideGestureEvent called with a non-gesture event"); /* Check the ancestor tree to decide if any frame is willing* to receive @@ -2791,8 +2791,8 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext, // Most of the events we handle below require a frame. // Add special cases here. - if (!mCurrentTarget && aEvent->message != NS_MOUSE_BUTTON_UP && - aEvent->message != NS_MOUSE_BUTTON_DOWN) { + if (!mCurrentTarget && aEvent->mMessage != NS_MOUSE_BUTTON_UP && + aEvent->mMessage != NS_MOUSE_BUTTON_DOWN) { return NS_OK; } @@ -2800,7 +2800,7 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext, nsRefPtr presContext = aPresContext; nsresult ret = NS_OK; - switch (aEvent->message) { + switch (aEvent->mMessage) { case NS_MOUSE_BUTTON_DOWN: { WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); @@ -3073,7 +3073,7 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext, ScrollbarsForWheel::PrepareToScrollText(this, aTargetFrame, wheelEvent); - if (aEvent->message != NS_WHEEL_WHEEL || + if (aEvent->mMessage != NS_WHEEL_WHEEL || (!wheelEvent->deltaX && !wheelEvent->deltaY)) { break; } @@ -3237,13 +3237,13 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext, // For now, do this only for dragover. //XXXsmaug dragenter needs some more work. - if (aEvent->message == NS_DRAGDROP_OVER && !isChromeDoc) { + if (aEvent->mMessage == NS_DRAGDROP_OVER && !isChromeDoc) { // Someone has called preventDefault(), check whether is was on // content or chrome. dragSession->SetOnlyChromeDrop( !dragEvent->mDefaultPreventedOnContent); } - } else if (aEvent->message == NS_DRAGDROP_OVER && !isChromeDoc) { + } else if (aEvent->mMessage == NS_DRAGDROP_OVER && !isChromeDoc) { // No one called preventDefault(), so handle drop only in chrome. dragSession->SetOnlyChromeDrop(true); } @@ -4058,7 +4058,7 @@ void EventStateManager::GeneratePointerEnterExit(uint32_t aMessage, WidgetMouseEvent* aEvent) { WidgetPointerEvent pointerEvent(*aEvent); - pointerEvent.message = aMessage; + pointerEvent.mMessage = aMessage; GenerateMouseEnterExit(&pointerEvent); } @@ -4072,7 +4072,7 @@ EventStateManager::GenerateMouseEnterExit(WidgetMouseEvent* aMouseEvent) // Hold onto old target content through the event and reset after. nsCOMPtr targetBeforeEvent = mCurrentTargetContent; - switch(aMouseEvent->message) { + switch(aMouseEvent->mMessage) { case NS_MOUSE_MOVE: { // Mouse movement is reported on the MouseEvent.movement{X,Y} fields. @@ -4268,7 +4268,7 @@ EventStateManager::GenerateDragDropEnterExit(nsPresContext* aPresContext, //Hold onto old target content through the event and reset after. nsCOMPtr targetBeforeEvent = mCurrentTargetContent; - switch(aDragEvent->message) { + switch(aDragEvent->mMessage) { case NS_DRAGDROP_OVER: { // when dragging from one frame to another, events are fired in the @@ -4420,10 +4420,10 @@ EventStateManager::SetClickCount(nsPresContext* aPresContext, switch (aEvent->button) { case WidgetMouseEvent::eLeftButton: - if (aEvent->message == NS_MOUSE_BUTTON_DOWN) { + if (aEvent->mMessage == NS_MOUSE_BUTTON_DOWN) { mLastLeftMouseDownContent = mouseContent; mLastLeftMouseDownContentParent = mouseContentParent; - } else if (aEvent->message == NS_MOUSE_BUTTON_UP) { + } else if (aEvent->mMessage == NS_MOUSE_BUTTON_UP) { if (mLastLeftMouseDownContent == mouseContent || mLastLeftMouseDownContentParent == mouseContent || mLastLeftMouseDownContent == mouseContentParent) { @@ -4438,10 +4438,10 @@ EventStateManager::SetClickCount(nsPresContext* aPresContext, break; case WidgetMouseEvent::eMiddleButton: - if (aEvent->message == NS_MOUSE_BUTTON_DOWN) { + if (aEvent->mMessage == NS_MOUSE_BUTTON_DOWN) { mLastMiddleMouseDownContent = mouseContent; mLastMiddleMouseDownContentParent = mouseContentParent; - } else if (aEvent->message == NS_MOUSE_BUTTON_UP) { + } else if (aEvent->mMessage == NS_MOUSE_BUTTON_UP) { if (mLastMiddleMouseDownContent == mouseContent || mLastMiddleMouseDownContentParent == mouseContent || mLastMiddleMouseDownContent == mouseContentParent) { @@ -4456,10 +4456,10 @@ EventStateManager::SetClickCount(nsPresContext* aPresContext, break; case WidgetMouseEvent::eRightButton: - if (aEvent->message == NS_MOUSE_BUTTON_DOWN) { + if (aEvent->mMessage == NS_MOUSE_BUTTON_DOWN) { mLastRightMouseDownContent = mouseContent; mLastRightMouseDownContentParent = mouseContentParent; - } else if (aEvent->message == NS_MOUSE_BUTTON_UP) { + } else if (aEvent->mMessage == NS_MOUSE_BUTTON_UP) { if (mLastRightMouseDownContent == mouseContent || mLastRightMouseDownContentParent == mouseContent || mLastRightMouseDownContent == mouseContentParent) { @@ -4573,8 +4573,8 @@ already_AddRefed EventStateManager::GetEventTargetContent(WidgetEvent* aEvent) { if (aEvent && - (aEvent->message == NS_FOCUS_CONTENT || - aEvent->message == NS_BLUR_CONTENT)) { + (aEvent->mMessage == NS_FOCUS_CONTENT || + aEvent->mMessage == NS_BLUR_CONTENT)) { nsCOMPtr content = GetFocusedContent(); return content.forget(); } @@ -4942,7 +4942,7 @@ EventStateManager::ContentRemoved(nsIDocument* aDocument, nsIContent* aContent) bool EventStateManager::EventStatusOK(WidgetGUIEvent* aEvent) { - return !(aEvent->message == NS_MOUSE_BUTTON_DOWN && + return !(aEvent->mMessage == NS_MOUSE_BUTTON_DOWN && aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton && !sNormalLMouseEventInProcess); } @@ -5038,7 +5038,7 @@ EventStateManager::DoContentCommandEvent(WidgetContentCommandEvent* aEvent) nsCOMPtr root = window->GetTopWindowRoot(); NS_ENSURE_TRUE(root, NS_ERROR_FAILURE); const char* cmd; - switch (aEvent->message) { + switch (aEvent->mMessage) { case NS_CONTENT_COMMAND_CUT: cmd = "cmd_cut"; break; @@ -5076,7 +5076,7 @@ EventStateManager::DoContentCommandEvent(WidgetContentCommandEvent* aEvent) NS_ENSURE_SUCCESS(rv, rv); aEvent->mIsEnabled = canDoIt; if (canDoIt && !aEvent->mOnlyEnabledCheck) { - switch (aEvent->message) { + switch (aEvent->mMessage) { case NS_CONTENT_COMMAND_PASTE_TRANSFERABLE: { nsCOMPtr commandController = do_QueryInterface(controller); NS_ENSURE_STATE(commandController); @@ -5730,7 +5730,7 @@ AutoHandlingUserInputStatePusher::AutoHandlingUserInputStatePusher( WidgetEvent* aEvent, nsIDocument* aDocument) : mIsHandlingUserInput(aIsHandlingUserInput), - mIsMouseDown(aEvent && aEvent->message == NS_MOUSE_BUTTON_DOWN), + mIsMouseDown(aEvent && aEvent->mMessage == NS_MOUSE_BUTTON_DOWN), mResetFMMouseButtonHandlingState(false) { if (!aIsHandlingUserInput) { @@ -5744,8 +5744,9 @@ AutoHandlingUserInputStatePusher::AutoHandlingUserInputStatePusher( if (!aDocument || !aEvent || !aEvent->mFlags.mIsTrusted) { return; } - mResetFMMouseButtonHandlingState = (aEvent->message == NS_MOUSE_BUTTON_DOWN || - aEvent->message == NS_MOUSE_BUTTON_UP); + mResetFMMouseButtonHandlingState = + (aEvent->mMessage == NS_MOUSE_BUTTON_DOWN || + aEvent->mMessage == NS_MOUSE_BUTTON_UP); if (mResetFMMouseButtonHandlingState) { nsFocusManager* fm = nsFocusManager::GetFocusManager(); NS_ENSURE_TRUE_VOID(fm); diff --git a/dom/events/EventStateManager.h b/dom/events/EventStateManager.h index 4acf246a1a96..8291ec20c379 100644 --- a/dom/events/EventStateManager.h +++ b/dom/events/EventStateManager.h @@ -957,7 +957,7 @@ class AutoHandlingUserInputStatePusher // has no frame. This is required for Web compatibility. #define NS_EVENT_NEEDS_FRAME(event) \ (!(event)->HasPluginActivationEventMessage() && \ - (event)->message != NS_MOUSE_CLICK && \ - (event)->message != NS_MOUSE_DOUBLECLICK) + (event)->mMessage != NS_MOUSE_CLICK && \ + (event)->mMessage != NS_MOUSE_DOUBLECLICK) #endif // mozilla_EventStateManager_h_ diff --git a/dom/events/IMEContentObserver.cpp b/dom/events/IMEContentObserver.cpp index ec5d2f3b1976..f5cb2d600a39 100644 --- a/dom/events/IMEContentObserver.cpp +++ b/dom/events/IMEContentObserver.cpp @@ -409,16 +409,11 @@ IMEContentObserver::NotifySelectionChanged(nsIDOMDocument* aDOMDocument, nsISelection* aSelection, int16_t aReason) { - bool causedByComposition = IsEditorHandlingEventForComposition(); - if (causedByComposition && - !mUpdatePreference.WantChangesCausedByComposition()) { - return NS_OK; - } - int32_t count = 0; nsresult rv = aSelection->GetRangeCount(&count); NS_ENSURE_SUCCESS(rv, rv); if (count > 0 && mWidget) { + bool causedByComposition = IsEditorHandlingEventForComposition(); bool causedBySelectionEvent = TextComposition::IsHandlingSelectionEvent(); MaybeNotifyIMEOfSelectionChange(causedByComposition, causedBySelectionEvent); @@ -461,7 +456,7 @@ IMEContentObserver::OnMouseButtonEvent(nsPresContext* aPresContext, return false; } // Now, we need to notify only mouse down and mouse up event. - switch (aMouseEvent->message) { + switch (aMouseEvent->mMessage) { case NS_MOUSE_BUTTON_UP: case NS_MOUSE_BUTTON_DOWN: break; @@ -506,7 +501,7 @@ IMEContentObserver::OnMouseButtonEvent(nsPresContext* aPresContext, } IMENotification notification(NOTIFY_IME_OF_MOUSE_BUTTON_EVENT); - notification.mMouseButtonEventData.mEventMessage = aMouseEvent->message; + notification.mMouseButtonEventData.mEventMessage = aMouseEvent->mMessage; notification.mMouseButtonEventData.mOffset = charAtPt.mReply.mOffset; notification.mMouseButtonEventData.mCursorPos.Set( LayoutDeviceIntPoint::ToUntyped(charAtPt.refPoint)); @@ -852,6 +847,35 @@ IMEContentObserver::PostSelectionChangeNotification( mIsSelectionChangeEventPending = true; } +bool +IMEContentObserver::UpdateSelectionCache() +{ + MOZ_ASSERT(IsSafeToNotifyIME()); + + if (!mUpdatePreference.WantSelectionChange()) { + return false; + } + + mSelectionData.Clear(); + + // XXX Cannot we cache some information for reducing the cost to compute + // selection offset and writing mode? + WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT, mWidget); + ContentEventHandler handler(GetPresContext()); + handler.OnQuerySelectedText(&selection); + if (NS_WARN_IF(!selection.mSucceeded)) { + return false; + } + + mSelectionData.mOffset = selection.mReply.mOffset; + *mSelectionData.mString = selection.mReply.mString; + mSelectionData.SetWritingMode(selection.GetWritingMode()); + mSelectionData.mReversed = selection.mReply.mReversed; + mSelectionData.mCausedByComposition = false; + mSelectionData.mCausedBySelectionEvent = false; + return mSelectionData.IsValid(); +} + void IMEContentObserver::PostPositionChangeNotification() { @@ -1038,6 +1062,8 @@ IMEContentObserver::FocusSetEvent::Run() } mIMEContentObserver->mIMEHasFocus = true; + // Initialize selection cache with the first selection data. + mIMEContentObserver->UpdateSelectionCache(); IMEStateManager::NotifyIME(IMENotification(NOTIFY_IME_OF_FOCUS), mIMEContentObserver->mWidget); return NS_OK; @@ -1060,13 +1086,16 @@ IMEContentObserver::SelectionChangeEvent::Run() return NS_OK; } - // XXX Cannot we cache some information for reducing the cost to compute - // selection offset and writing mode? - WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT, - mIMEContentObserver->mWidget); - ContentEventHandler handler(mIMEContentObserver->GetPresContext()); - handler.OnQuerySelectedText(&selection); - if (NS_WARN_IF(!selection.mSucceeded)) { + SelectionChangeData lastSelChangeData = mIMEContentObserver->mSelectionData; + if (NS_WARN_IF(!mIMEContentObserver->UpdateSelectionCache())) { + return NS_OK; + } + + // If the IME doesn't want selection change notifications caused by + // composition, we should do nothing anymore. + if (mCausedByComposition && + !mIMEContentObserver-> + mUpdatePreference.WantChangesCausedByComposition()) { return NS_OK; } @@ -1075,16 +1104,20 @@ IMEContentObserver::SelectionChangeEvent::Run() return NS_OK; } + // If the selection isn't changed actually, we shouldn't notify IME of + // selection change. + SelectionChangeData& newSelChangeData = mIMEContentObserver->mSelectionData; + if (lastSelChangeData.IsValid() && + lastSelChangeData.mOffset == newSelChangeData.mOffset && + lastSelChangeData.String() == newSelChangeData.String() && + lastSelChangeData.GetWritingMode() == newSelChangeData.GetWritingMode() && + lastSelChangeData.mReversed == newSelChangeData.mReversed) { + return NS_OK; + } + IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE); - notification.mSelectionChangeData.mOffset = selection.mReply.mOffset; - *notification.mSelectionChangeData.mString = selection.mReply.mString; - notification.mSelectionChangeData.SetWritingMode( - selection.GetWritingMode()); - notification.mSelectionChangeData.mReversed = selection.mReply.mReversed; - notification.mSelectionChangeData.mCausedByComposition = - mCausedByComposition; - notification.mSelectionChangeData.mCausedBySelectionEvent = - mCausedBySelectionEvent; + notification.SetData(mIMEContentObserver->mSelectionData, + mCausedByComposition, mCausedBySelectionEvent); IMEStateManager::NotifyIME(notification, mIMEContentObserver->mWidget); return NS_OK; } @@ -1106,7 +1139,7 @@ IMEContentObserver::TextChangeEvent::Run() } IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE); - notification.mTextChangeData = mTextChangeData; + notification.SetData(mTextChangeData); IMEStateManager::NotifyIME(notification, mIMEContentObserver->mWidget); return NS_OK; } diff --git a/dom/events/IMEContentObserver.h b/dom/events/IMEContentObserver.h index 77e09f8408a6..b22c6d7e88a6 100644 --- a/dom/events/IMEContentObserver.h +++ b/dom/events/IMEContentObserver.h @@ -40,6 +40,7 @@ class IMEContentObserver final : public nsISelectionListener , public nsIEditorObserver { public: + typedef widget::IMENotification::SelectionChangeData SelectionChangeData; typedef widget::IMENotification::TextChangeData TextChangeData; typedef widget::IMENotification::TextChangeDataBase TextChangeDataBase; @@ -167,6 +168,15 @@ class IMEContentObserver final : public nsISelectionListener mTextChangeData.Clear(); } + /** + * UpdateSelectionCache() updates mSelectionData with the latest selection. + * This should be called only when IsSafeToNotifyIME() returns true. + * + * Note that this does nothing if mUpdatePreference.WantSelectionChange() + * returns false. + */ + bool UpdateSelectionCache(); + nsCOMPtr mWidget; nsCOMPtr mSelection; nsCOMPtr mRootContent; @@ -230,6 +240,11 @@ class IMEContentObserver final : public nsISelectionListener TextChangeData mTextChangeData; + // mSelectionData is the last selection data which was notified. This is + // modified by UpdateSelectionCache(). Note that mCausedBy* are always + // false. Do NOT refer them. + SelectionChangeData mSelectionData; + EventStateManager* mESM; nsIMEUpdatePreference mUpdatePreference; diff --git a/dom/events/IMEStateManager.cpp b/dom/events/IMEStateManager.cpp index ef563f3f7e39..335664241e93 100644 --- a/dom/events/IMEStateManager.cpp +++ b/dom/events/IMEStateManager.cpp @@ -1137,7 +1137,7 @@ IMEStateManager::DispatchCompositionEvent( "mFlags={ mIsTrusted=%s, mPropagationStopped=%s } }, " "aIsSynthesized=%s), tabParent=%p", aEventTargetNode, aPresContext, - GetEventMessageName(aCompositionEvent->message), + GetEventMessageName(aCompositionEvent->mMessage), GetBoolName(aCompositionEvent->mFlags.mIsTrusted), GetBoolName(aCompositionEvent->mFlags.mPropagationStopped), GetBoolName(aIsSynthesized), tabParent.get())); @@ -1147,7 +1147,7 @@ IMEStateManager::DispatchCompositionEvent( return; } - MOZ_ASSERT(aCompositionEvent->message != NS_COMPOSITION_UPDATE, + MOZ_ASSERT(aCompositionEvent->mMessage != NS_COMPOSITION_UPDATE, "compositionupdate event shouldn't be dispatched manually"); EnsureTextCompositionArray(); @@ -1163,7 +1163,7 @@ IMEStateManager::DispatchCompositionEvent( MOZ_LOG(sISMLog, LogLevel::Debug, ("ISM: IMEStateManager::DispatchCompositionEvent(), " "adding new TextComposition to the array")); - MOZ_ASSERT(aCompositionEvent->message == NS_COMPOSITION_START); + MOZ_ASSERT(aCompositionEvent->mMessage == NS_COMPOSITION_START); composition = new TextComposition(aPresContext, aEventTargetNode, tabParent, aCompositionEvent); @@ -1171,7 +1171,7 @@ IMEStateManager::DispatchCompositionEvent( } #ifdef DEBUG else { - MOZ_ASSERT(aCompositionEvent->message != NS_COMPOSITION_START); + MOZ_ASSERT(aCompositionEvent->mMessage != NS_COMPOSITION_START); } #endif // #ifdef DEBUG @@ -1232,10 +1232,10 @@ IMEStateManager::HandleSelectionEvent(nsPresContext* aPresContext, MOZ_LOG(sISMLog, LogLevel::Info, ("ISM: IMEStateManager::HandleSelectionEvent(aPresContext=0x%p, " - "aEventTargetContent=0x%p, aSelectionEvent={ message=%s, " + "aEventTargetContent=0x%p, aSelectionEvent={ mMessage=%s, " "mFlags={ mIsTrusted=%s } }), tabParent=%p", aPresContext, aEventTargetContent, - GetEventMessageName(aSelectionEvent->message), + GetEventMessageName(aSelectionEvent->mMessage), GetBoolName(aSelectionEvent->mFlags.mIsTrusted), tabParent.get())); @@ -1267,8 +1267,8 @@ IMEStateManager::OnCompositionEventDiscarded( MOZ_LOG(sISMLog, LogLevel::Info, ("ISM: IMEStateManager::OnCompositionEventDiscarded(aCompositionEvent={ " - "message=%s, mFlags={ mIsTrusted=%s } })", - GetEventMessageName(aCompositionEvent->message), + "mMessage=%s, mFlags={ mIsTrusted=%s } })", + GetEventMessageName(aCompositionEvent->mMessage), GetBoolName(aCompositionEvent->mFlags.mIsTrusted))); if (!aCompositionEvent->mFlags.mIsTrusted) { @@ -1277,7 +1277,7 @@ IMEStateManager::OnCompositionEventDiscarded( // Ignore compositionstart for now because sTextCompositions may not have // been created yet. - if (aCompositionEvent->message == NS_COMPOSITION_START) { + if (aCompositionEvent->mMessage == NS_COMPOSITION_START) { return; } diff --git a/dom/events/InternalMutationEvent.h b/dom/events/InternalMutationEvent.h index e44c3d009e5b..75639bc2ab20 100644 --- a/dom/events/InternalMutationEvent.h +++ b/dom/events/InternalMutationEvent.h @@ -30,7 +30,7 @@ class InternalMutationEvent : public WidgetEvent { MOZ_ASSERT(mClass == eMutationEventClass, "Duplicate() must be overridden by sub class"); - InternalMutationEvent* result = new InternalMutationEvent(false, message); + InternalMutationEvent* result = new InternalMutationEvent(false, mMessage); result->AssignMutationEventData(*this, true); result->mFlags = mFlags; return result; diff --git a/dom/events/KeyboardEvent.cpp b/dom/events/KeyboardEvent.cpp index 99ab35e9bae8..793adf94b304 100644 --- a/dom/events/KeyboardEvent.cpp +++ b/dom/events/KeyboardEvent.cpp @@ -151,7 +151,7 @@ KeyboardEvent::CharCode() return mEvent->AsKeyboardEvent()->charCode; } - switch (mEvent->message) { + switch (mEvent->mMessage) { case NS_KEY_BEFORE_DOWN: case NS_KEY_DOWN: case NS_KEY_AFTER_DOWN: @@ -195,7 +195,7 @@ KeyboardEvent::Which() return mInitializedWhichValue; } - switch (mEvent->message) { + switch (mEvent->mMessage) { case NS_KEY_BEFORE_DOWN: case NS_KEY_DOWN: case NS_KEY_AFTER_DOWN: diff --git a/dom/events/NotifyPaintEvent.cpp b/dom/events/NotifyPaintEvent.cpp index 797e3c798303..b14fabf552b6 100644 --- a/dom/events/NotifyPaintEvent.cpp +++ b/dom/events/NotifyPaintEvent.cpp @@ -23,7 +23,7 @@ NotifyPaintEvent::NotifyPaintEvent(EventTarget* aOwner, : Event(aOwner, aPresContext, aEvent) { if (mEvent) { - mEvent->message = aEventType; + mEvent->mMessage = aEventType; } if (aInvalidateRequests) { mInvalidateRequests.AppendElements(Move(aInvalidateRequests->mRequests)); diff --git a/dom/events/TextComposition.cpp b/dom/events/TextComposition.cpp index 487fb35e1bf3..3ec0d06bb42f 100644 --- a/dom/events/TextComposition.cpp +++ b/dom/events/TextComposition.cpp @@ -233,7 +233,7 @@ TextComposition::DispatchCompositionEvent( RemoveControlCharactersFrom(aCompositionEvent->mData, aCompositionEvent->mRanges); } - if (aCompositionEvent->message == NS_COMPOSITION_COMMIT_AS_IS) { + if (aCompositionEvent->mMessage == NS_COMPOSITION_COMMIT_AS_IS) { NS_ASSERTION(!aCompositionEvent->mRanges, "mRanges of NS_COMPOSITION_COMMIT_AS_IS should be null"); aCompositionEvent->mRanges = nullptr; @@ -247,7 +247,7 @@ TextComposition::DispatchCompositionEvent( } else { aCompositionEvent->mData = mLastData; } - } else if (aCompositionEvent->message == NS_COMPOSITION_COMMIT) { + } else if (aCompositionEvent->mMessage == NS_COMPOSITION_COMMIT) { NS_ASSERTION(!aCompositionEvent->mRanges, "mRanges of NS_COMPOSITION_COMMIT should be null"); aCompositionEvent->mRanges = nullptr; @@ -282,7 +282,7 @@ TextComposition::DispatchCompositionEvent( // 2. non-empty string is committed at requesting cancel. if (!aIsSynthesized && (mIsRequestingCommit || mIsRequestingCancel)) { nsString* committingData = nullptr; - switch (aCompositionEvent->message) { + switch (aCompositionEvent->mMessage) { case NS_COMPOSITION_END: case NS_COMPOSITION_CHANGE: case NS_COMPOSITION_COMMIT_AS_IS: @@ -312,7 +312,7 @@ TextComposition::DispatchCompositionEvent( // composition string empty or didn't have clause information), we don't // need to dispatch redundant DOM text event. if (dispatchDOMTextEvent && - aCompositionEvent->message != NS_COMPOSITION_CHANGE && + aCompositionEvent->mMessage != NS_COMPOSITION_CHANGE && !mIsComposing && mLastData == aCompositionEvent->mData) { dispatchEvent = dispatchDOMTextEvent = false; } @@ -321,7 +321,7 @@ TextComposition::DispatchCompositionEvent( // which modifies neither composition string, clauses nor caret // position. In such case, we shouldn't dispatch DOM events. if (dispatchDOMTextEvent && - aCompositionEvent->message == NS_COMPOSITION_CHANGE && + aCompositionEvent->mMessage == NS_COMPOSITION_CHANGE && mLastData == aCompositionEvent->mData && mRanges && aCompositionEvent->mRanges && mRanges->Equals(*aCompositionEvent->mRanges)) { @@ -340,7 +340,7 @@ TextComposition::DispatchCompositionEvent( // the limitation of mapping between event messages and DOM event types, // we cannot map multiple event messages to a DOM event type. if (dispatchDOMTextEvent && - aCompositionEvent->message != NS_COMPOSITION_CHANGE) { + aCompositionEvent->mMessage != NS_COMPOSITION_CHANGE) { aCompositionEvent->mFlags = CloneAndDispatchAs(aCompositionEvent, NS_COMPOSITION_CHANGE, aStatus, aCallBack); @@ -365,7 +365,7 @@ TextComposition::DispatchCompositionEvent( if (aCompositionEvent->CausesDOMCompositionEndEvent()) { // Dispatch a compositionend event if it's necessary. - if (aCompositionEvent->message != NS_COMPOSITION_END) { + if (aCompositionEvent->mMessage != NS_COMPOSITION_END) { CloneAndDispatchAs(aCompositionEvent, NS_COMPOSITION_END); } MOZ_ASSERT(!mIsComposing, "Why is the editor still composing?"); @@ -411,7 +411,7 @@ TextComposition::NotityUpdateComposition( // When compositon start, notify the rect of first offset character. // When not compositon start, notify the rect of selected composition // string if compositionchange event. - if (aCompositionEvent->message == NS_COMPOSITION_START) { + if (aCompositionEvent->mMessage == NS_COMPOSITION_START) { nsCOMPtr widget = mPresContext->GetRootWidget(); // Update composition start offset WidgetQueryContentEvent selectedTextEvent(true, diff --git a/dom/events/TouchEvent.cpp b/dom/events/TouchEvent.cpp index 8416dbddbf06..59da7b6e0e4a 100644 --- a/dom/events/TouchEvent.cpp +++ b/dom/events/TouchEvent.cpp @@ -121,7 +121,8 @@ TouchEvent::Touches() { if (!mTouches) { WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent(); - if (mEvent->message == NS_TOUCH_END || mEvent->message == NS_TOUCH_CANCEL) { + if (mEvent->mMessage == NS_TOUCH_END || + mEvent->mMessage == NS_TOUCH_CANCEL) { // for touchend events, remove any changed touches from the touches array WidgetTouchEvent::AutoTouchArray unchangedTouches; const WidgetTouchEvent::TouchArray& touches = touchEvent->touches; @@ -148,8 +149,8 @@ TouchEvent::TargetTouches() for (uint32_t i = 0; i < touches.Length(); ++i) { // for touchend/cancel events, don't append to the target list if this is a // touch that is ending - if ((mEvent->message != NS_TOUCH_END && - mEvent->message != NS_TOUCH_CANCEL) || !touches[i]->mChanged) { + if ((mEvent->mMessage != NS_TOUCH_END && + mEvent->mMessage != NS_TOUCH_CANCEL) || !touches[i]->mChanged) { if (touches[i]->mTarget == mEvent->originalTarget) { targetTouches.AppendElement(touches[i]); } diff --git a/dom/events/UIEvent.cpp b/dom/events/UIEvent.cpp index 5cc516e497af..3ce69a6455e9 100644 --- a/dom/events/UIEvent.cpp +++ b/dom/events/UIEvent.cpp @@ -29,7 +29,7 @@ UIEvent::UIEvent(EventTarget* aOwner, nsPresContext* aPresContext, WidgetGUIEvent* aEvent) : Event(aOwner, aPresContext, - aEvent ? aEvent : new InternalUIEvent(false, 0)) + aEvent ? aEvent : new InternalUIEvent(false, 0, nullptr)) , mClientPoint(0, 0) , mLayerPoint(0, 0) , mPagePoint(0, 0) diff --git a/dom/events/WheelHandlingHelper.cpp b/dom/events/WheelHandlingHelper.cpp index e2835094e7a8..7012ff12a03d 100644 --- a/dom/events/WheelHandlingHelper.cpp +++ b/dom/events/WheelHandlingHelper.cpp @@ -94,7 +94,7 @@ WheelTransaction::BeginTransaction(nsIFrame* aTargetFrame, WidgetWheelEvent* aEvent) { NS_ASSERTION(!sTargetFrame, "previous transaction is not finished!"); - MOZ_ASSERT(aEvent->message == NS_WHEEL_WHEEL, + MOZ_ASSERT(aEvent->mMessage == NS_WHEEL_WHEEL, "Transaction must be started with a wheel event"); ScrollbarsForWheel::OwnWheelTransaction(false); sTargetFrame = aTargetFrame; @@ -175,7 +175,7 @@ WheelTransaction::OnEvent(WidgetEvent* aEvent) return; } - switch (aEvent->message) { + switch (aEvent->mMessage) { case NS_WHEEL_WHEEL: if (sMouseMoved != 0 && OutOfTime(sMouseMoved, GetIgnoreMoveDelayTime())) { @@ -413,7 +413,7 @@ ScrollbarsForWheel::PrepareToScrollText(EventStateManager* aESM, nsIFrame* aTargetFrame, WidgetWheelEvent* aEvent) { - if (aEvent->message == NS_WHEEL_START) { + if (aEvent->mMessage == NS_WHEEL_START) { WheelTransaction::OwnScrollbars(false); if (!IsActive()) { TemporarilyActivateAllPossibleScrollTargets(aESM, aTargetFrame, aEvent); diff --git a/dom/events/test/mochitest.ini b/dom/events/test/mochitest.ini index f96dbb810917..e28fce3e70f8 100644 --- a/dom/events/test/mochitest.ini +++ b/dom/events/test/mochitest.ini @@ -193,3 +193,4 @@ skip-if = toolkit == 'android' || buildapp == 'b2g' || buildapp == 'mulet' [test_eventhandler_scoping.html] [test_bug1013412.html] skip-if = buildapp == 'b2g' # no wheel events on b2g +[test_dom_activate_event.html] diff --git a/dom/events/test/test_bug226361.xhtml b/dom/events/test/test_bug226361.xhtml index 65364cd30e17..5095bb193bde 100644 --- a/dom/events/test/test_bug226361.xhtml +++ b/dom/events/test/test_bug226361.xhtml @@ -29,12 +29,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=226361 // accessibility.tabfocus must be set to value 7 before running test also // on a mac. -function setOrRestoreTabFocus(newValue) { - if (!newValue) { - SpecialPowers.clearUserPref("accessibility.tabfocus"); - } else { - SpecialPowers.setIntPref("accessibility.tabfocus", newValue); - } +function setTabFocus() { + SpecialPowers.pushPrefEnv({ set: [[ "accessibility.tabfocus", 7 ]] }, doTest); } // ================================= @@ -57,36 +53,27 @@ function tab_iframe() { function doTest() { + window.getSelection().removeAllRanges(); + document.getElementById('body1').focus(); + is(document.activeElement.id, document.body.id, "body element should be focused"); - setOrRestoreTabFocus(7); - - try { - window.getSelection().removeAllRanges(); - document.getElementById('body1').focus(); - is(document.activeElement.id, document.body.id, "body element should be focused"); - - doc = document; - tab_to('b1'); - - tab_iframe(); - - doc=document - document.getElementById('iframe').focus() - tab_to('b2'); - // Change tabindex so the next TAB goes back to the IFRAME - document.getElementById('iframe').setAttribute('tabindex','4'); + doc = document; + tab_to('b1'); - tab_iframe(); + tab_iframe(); - } finally { - setOrRestoreTabFocus(0); - } + doc=document + document.getElementById('iframe').focus() + tab_to('b2'); + // Change tabindex so the next TAB goes back to the IFRAME + document.getElementById('iframe').setAttribute('tabindex','4'); + tab_iframe(); SimpleTest.finish(); } SimpleTest.waitForExplicitFinish(); -addLoadEvent(doTest); +addLoadEvent(setTabFocus); ]]> diff --git a/dom/events/test/test_dom_activate_event.html b/dom/events/test/test_dom_activate_event.html new file mode 100644 index 000000000000..22e32d98809b --- /dev/null +++ b/dom/events/test/test_dom_activate_event.html @@ -0,0 +1,89 @@ + + + + Test DOMActivate event + + + + + +

+link + + + + + + +

+ +
+
+
+ + diff --git a/dom/fetch/FetchDriver.cpp b/dom/fetch/FetchDriver.cpp index a45428f42573..d812c6fd2e40 100644 --- a/dom/fetch/FetchDriver.cpp +++ b/dom/fetch/FetchDriver.cpp @@ -160,6 +160,10 @@ FetchDriver::ContinueFetch(bool aCORSFlag) (mRequest->UnsafeRequest() && (!mRequest->HasSimpleMethod() || !mRequest->Headers()->HasOnlySimpleHeaders()))) { corsPreflight = true; } + // The Request constructor should ensure that no-cors requests have simple + // method and headers, so we should never attempt to preflight for such + // Requests. + MOZ_ASSERT_IF(mRequest->Mode() == RequestMode::No_cors, !corsPreflight); mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_CORS); return HttpFetch(true /* aCORSFlag */, corsPreflight); @@ -541,6 +545,8 @@ FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthentica // unsafeHeaders so they can be verified against the response's // "Access-Control-Allow-Headers" header. if (aCORSPreflightFlag) { + MOZ_ASSERT(mRequest->Mode() != RequestMode::No_cors, + "FetchDriver::ContinueFetch() should ensure that the request is not no-cors"); nsCOMPtr preflightChannel; nsAutoTArray unsafeHeaders; mRequest->Headers()->GetUnsafeHeaders(unsafeHeaders); diff --git a/dom/fetch/InternalRequest.h b/dom/fetch/InternalRequest.h index e871d827db10..c50af67c851a 100644 --- a/dom/fetch/InternalRequest.h +++ b/dom/fetch/InternalRequest.h @@ -319,7 +319,7 @@ class InternalRequest final SetBody(nsIInputStream* aStream) { // A request's body may not be reset once set. - MOZ_ASSERT(!mBodyStream); + MOZ_ASSERT_IF(aStream, !mBodyStream); mBodyStream = aStream; } diff --git a/dom/fetch/Request.cpp b/dom/fetch/Request.cpp index 0d64ac747be4..dac0778bf8c3 100644 --- a/dom/fetch/Request.cpp +++ b/dom/fetch/Request.cpp @@ -163,8 +163,8 @@ Request::Constructor(const GlobalObject& aGlobal, const RequestOrUSVString& aInput, const RequestInit& aInit, ErrorResult& aRv) { + nsCOMPtr temporaryBody; nsRefPtr request; - bool inputRequestHasBody = false; nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports()); @@ -173,13 +173,11 @@ Request::Constructor(const GlobalObject& aGlobal, nsCOMPtr body; inputReq->GetBody(getter_AddRefs(body)); if (body) { - inputRequestHasBody = true; if (inputReq->BodyUsed()) { aRv.ThrowTypeError(MSG_FETCH_BODY_CONSUMED_ERROR); return nullptr; - } else { - inputReq->SetBodyUsed(); } + temporaryBody = body; } request = inputReq->GetInternalRequest(); @@ -327,7 +325,7 @@ Request::Constructor(const GlobalObject& aGlobal, return nullptr; } - if (aInit.mBody.WasPassed() || inputRequestHasBody) { + if (aInit.mBody.WasPassed() || temporaryBody) { // HEAD and GET are not allowed to have a body. nsAutoCString method; request->GetMethod(method); @@ -347,8 +345,8 @@ Request::Constructor(const GlobalObject& aGlobal, if (NS_WARN_IF(aRv.Failed())) { return nullptr; } - request->ClearCreatedByFetchEvent(); - request->SetBody(stream); + + temporaryBody = stream; if (!contentType.IsVoid() && !requestHeaders->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) { @@ -356,13 +354,26 @@ Request::Constructor(const GlobalObject& aGlobal, contentType, aRv); } - if (aRv.Failed()) { + if (NS_WARN_IF(aRv.Failed())) { return nullptr; } + + request->ClearCreatedByFetchEvent(); + request->SetBody(temporaryBody); } nsRefPtr domRequest = new Request(global, request); domRequest->SetMimeType(); + + if (aInput.IsRequest()) { + nsRefPtr inputReq = &aInput.GetAsRequest(); + nsCOMPtr body; + inputReq->GetBody(getter_AddRefs(body)); + if (body) { + inputReq->SetBody(nullptr); + inputReq->SetBodyUsed(); + } + } return domRequest.forget(); } diff --git a/dom/fetch/Request.h b/dom/fetch/Request.h index 98bf3c303d93..da926545e064 100644 --- a/dom/fetch/Request.h +++ b/dom/fetch/Request.h @@ -105,6 +105,9 @@ class Request final : public nsISupports void GetBody(nsIInputStream** aStream) { return mRequest->GetBody(aStream); } + void + SetBody(nsIInputStream* aStream) { return mRequest->SetBody(aStream); } + static already_AddRefed Constructor(const GlobalObject& aGlobal, const RequestOrUSVString& aInput, const RequestInit& aInit, ErrorResult& rv); diff --git a/dom/html/HTMLButtonElement.cpp b/dom/html/HTMLButtonElement.cpp index 9ced076e53d6..1b0f74b215b8 100644 --- a/dom/html/HTMLButtonElement.cpp +++ b/dom/html/HTMLButtonElement.cpp @@ -211,7 +211,7 @@ nsresult HTMLButtonElement::PreHandleEvent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = false; - if (IsDisabledForEvents(aVisitor.mEvent->message)) { + if (IsDisabledForEvents(aVisitor.mEvent->mMessage)) { return NS_OK; } @@ -222,7 +222,7 @@ HTMLButtonElement::PreHandleEvent(EventChainPreVisitor& aVisitor) WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent(); bool outerActivateEvent = ((mouseEvent && mouseEvent->IsLeftClickEvent()) || - (aVisitor.mEvent->message == NS_UI_ACTIVATE && + (aVisitor.mEvent->mMessage == NS_UI_ACTIVATE && !mInInternalActivate)); if (outerActivateEvent) { @@ -250,10 +250,9 @@ HTMLButtonElement::PostHandleEvent(EventChainPostVisitor& aVisitor) if (aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault) { WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent(); if (mouseEvent && mouseEvent->IsLeftClickEvent()) { - // XXX Activating actually occurs even if it's caused by untrusted event. - // Therefore, shouldn't this be always trusted event? - InternalUIEvent actEvent(aVisitor.mEvent->mFlags.mIsTrusted, - NS_UI_ACTIVATE); + // DOMActive event should be trusted since the activation is actually + // occurred even if the cause is an untrusted click event. + InternalUIEvent actEvent(true, NS_UI_ACTIVATE, mouseEvent); actEvent.detail = 1; nsCOMPtr shell = aVisitor.mPresContext->GetPresShell(); @@ -282,7 +281,7 @@ HTMLButtonElement::PostHandleEvent(EventChainPostVisitor& aVisitor) } if (nsEventStatus_eIgnore == aVisitor.mEventStatus) { - switch (aVisitor.mEvent->message) { + switch (aVisitor.mEvent->mMessage) { case NS_KEY_PRESS: case NS_KEY_UP: { @@ -290,9 +289,9 @@ HTMLButtonElement::PostHandleEvent(EventChainPostVisitor& aVisitor) // (bug 25300) WidgetKeyboardEvent* keyEvent = aVisitor.mEvent->AsKeyboardEvent(); if ((keyEvent->keyCode == NS_VK_RETURN && - NS_KEY_PRESS == aVisitor.mEvent->message) || + NS_KEY_PRESS == aVisitor.mEvent->mMessage) || (keyEvent->keyCode == NS_VK_SPACE && - NS_KEY_UP == aVisitor.mEvent->message)) { + NS_KEY_UP == aVisitor.mEvent->mMessage)) { nsEventStatus status = nsEventStatus_eIgnore; WidgetMouseEvent event(aVisitor.mEvent->mFlags.mIsTrusted, @@ -383,7 +382,7 @@ HTMLButtonElement::PostHandleEvent(EventChainPostVisitor& aVisitor) // // Using presShell to dispatch the event. It makes sure that // event is not handled if the window is being destroyed. - if (presShell && (event.message != NS_FORM_SUBMIT || + if (presShell && (event.mMessage != NS_FORM_SUBMIT || mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate) || // We know the element is a submit control, if this check is moved, // make sure formnovalidate is used only if it's a submit control. diff --git a/dom/html/HTMLFieldSetElement.cpp b/dom/html/HTMLFieldSetElement.cpp index 1f50418106cc..d929d4cc1fd9 100644 --- a/dom/html/HTMLFieldSetElement.cpp +++ b/dom/html/HTMLFieldSetElement.cpp @@ -74,7 +74,7 @@ HTMLFieldSetElement::PreHandleEvent(EventChainPreVisitor& aVisitor) { // Do not process any DOM events if the element is disabled. aVisitor.mCanHandle = false; - if (IsDisabledForEvents(aVisitor.mEvent->message)) { + if (IsDisabledForEvents(aVisitor.mEvent->mMessage)) { return NS_OK; } diff --git a/dom/html/HTMLFormElement.cpp b/dom/html/HTMLFormElement.cpp index 43eed3a91535..1721d88d0179 100644 --- a/dom/html/HTMLFormElement.cpp +++ b/dom/html/HTMLFormElement.cpp @@ -492,7 +492,7 @@ HTMLFormElement::PreHandleEvent(EventChainPreVisitor& aVisitor) { aVisitor.mWantsWillHandleEvent = true; if (aVisitor.mEvent->originalTarget == static_cast(this)) { - uint32_t msg = aVisitor.mEvent->message; + uint32_t msg = aVisitor.mEvent->mMessage; if (msg == NS_FORM_SUBMIT) { if (mGeneratingSubmit) { aVisitor.mCanHandle = false; @@ -522,8 +522,8 @@ HTMLFormElement::WillHandleEvent(EventChainPostVisitor& aVisitor) // If this is the bubble stage and there is a nested form below us which // received a submit event we do *not* want to handle the submit event // for this form too. - if ((aVisitor.mEvent->message == NS_FORM_SUBMIT || - aVisitor.mEvent->message == NS_FORM_RESET) && + if ((aVisitor.mEvent->mMessage == NS_FORM_SUBMIT || + aVisitor.mEvent->mMessage == NS_FORM_RESET) && aVisitor.mEvent->mFlags.mInBubblingPhase && aVisitor.mEvent->originalTarget != static_cast(this)) { aVisitor.mEvent->mFlags.mPropagationStopped = true; @@ -535,7 +535,7 @@ nsresult HTMLFormElement::PostHandleEvent(EventChainPostVisitor& aVisitor) { if (aVisitor.mEvent->originalTarget == static_cast(this)) { - uint32_t msg = aVisitor.mEvent->message; + uint32_t msg = aVisitor.mEvent->mMessage; if (msg == NS_FORM_SUBMIT) { // let the form know not to defer subsequent submissions mDeferSubmission = false; diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index 046e18f7face..e1e8265397d7 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -3025,7 +3025,7 @@ HTMLInputElement::NeedToInitializeEditorForEvent( return false; } - switch (aVisitor.mEvent->message) { + switch (aVisitor.mEvent->mMessage) { case NS_MOUSE_MOVE: case NS_MOUSE_ENTER_WIDGET: case NS_MOUSE_EXIT_WIDGET: @@ -3050,7 +3050,7 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) { // Do not process any DOM events if the element is disabled aVisitor.mCanHandle = false; - if (IsDisabledForEvents(aVisitor.mEvent->message)) { + if (IsDisabledForEvents(aVisitor.mEvent->mMessage)) { return NS_OK; } @@ -3088,7 +3088,7 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent(); bool outerActivateEvent = ((mouseEvent && mouseEvent->IsLeftClickEvent()) || - (aVisitor.mEvent->message == NS_UI_ACTIVATE && !mInInternalActivate)); + (aVisitor.mEvent->mMessage == NS_UI_ACTIVATE && !mInInternalActivate)); if (outerActivateEvent) { aVisitor.mItemFlags |= NS_OUTER_ACTIVATE_EVENT; @@ -3153,7 +3153,7 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) aVisitor.mItemFlags |= NS_NO_CONTENT_DISPATCH; } if (IsSingleLineTextControl(false) && - aVisitor.mEvent->message == NS_MOUSE_CLICK && + aVisitor.mEvent->mMessage == NS_MOUSE_CLICK && aVisitor.mEvent->AsMouseEvent()->button == WidgetMouseEvent::eMiddleButton) { aVisitor.mEvent->mFlags.mNoContentDispatch = false; @@ -3163,7 +3163,7 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) aVisitor.mItemFlags |= mType; // Fire onchange (if necessary), before we do the blur, bug 357684. - if (aVisitor.mEvent->message == NS_BLUR_CONTENT) { + if (aVisitor.mEvent->mMessage == NS_BLUR_CONTENT) { // Experimental mobile types rely on the system UI to prevent users to not // set invalid values but we have to be extra-careful. Especially if the // option has been enabled on desktop. @@ -3178,8 +3178,8 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) } if (mType == NS_FORM_INPUT_RANGE && - (aVisitor.mEvent->message == NS_FOCUS_CONTENT || - aVisitor.mEvent->message == NS_BLUR_CONTENT)) { + (aVisitor.mEvent->mMessage == NS_FOCUS_CONTENT || + aVisitor.mEvent->mMessage == NS_BLUR_CONTENT)) { // Just as nsGenericHTMLFormElementWithState::PreHandleEvent calls // nsIFormControlFrame::SetFocus, we handle focus here. nsIFrame* frame = GetPrimaryFrame(); @@ -3197,7 +3197,7 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) // we want to end the spin. We do this here (rather than in // PostHandleEvent) because we don't want to let content preventDefault() // the end of the spin. - if (aVisitor.mEvent->message == NS_MOUSE_MOVE) { + if (aVisitor.mEvent->mMessage == NS_MOUSE_MOVE) { // Be aggressive about stopping the spin: bool stopSpin = true; nsNumberControlFrame* numberControlFrame = @@ -3228,13 +3228,13 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) if (stopSpin) { StopNumberControlSpinnerSpin(); } - } else if (aVisitor.mEvent->message == NS_MOUSE_BUTTON_UP) { + } else if (aVisitor.mEvent->mMessage == NS_MOUSE_BUTTON_UP) { StopNumberControlSpinnerSpin(); } } - if (aVisitor.mEvent->message == NS_FOCUS_CONTENT || - aVisitor.mEvent->message == NS_BLUR_CONTENT) { - if (aVisitor.mEvent->message == NS_FOCUS_CONTENT) { + if (aVisitor.mEvent->mMessage == NS_FOCUS_CONTENT || + aVisitor.mEvent->mMessage == NS_BLUR_CONTENT) { + if (aVisitor.mEvent->mMessage == NS_FOCUS_CONTENT) { // Tell our frame it's getting focus so that it can make sure focus // is moved to our anonymous text control. nsNumberControlFrame* numberControlFrame = @@ -3254,7 +3254,7 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) // that). frame->InvalidateFrame(); } - } else if (aVisitor.mEvent->message == NS_KEY_UP) { + } else if (aVisitor.mEvent->mMessage == NS_KEY_UP) { WidgetKeyboardEvent* keyEvent = aVisitor.mEvent->AsKeyboardEvent(); if ((keyEvent->keyCode == NS_VK_UP || keyEvent->keyCode == NS_VK_DOWN) && !(keyEvent->IsShift() || keyEvent->IsControl() || @@ -3286,7 +3286,7 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) textControl = numberControlFrame->GetAnonTextControl(); } if (textControl && aVisitor.mEvent->originalTarget == textControl) { - if (aVisitor.mEvent->message == NS_EDITOR_INPUT) { + if (aVisitor.mEvent->mMessage == NS_EDITOR_INPUT) { // Propogate the anon text control's new value to our HTMLInputElement: nsAutoString value; numberControlFrame->GetValueOfAnonTextControl(value); @@ -3300,7 +3300,7 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) numberControlFrame->HandlingInputEvent(false); } } - else if (aVisitor.mEvent->message == NS_FORM_CHANGE) { + else if (aVisitor.mEvent->mMessage == NS_FORM_CHANGE) { // We cancel the DOM 'change' event that is fired for any change to our // anonymous text control since we fire our own 'change' events and // content shouldn't be seeing two 'change' events. Besides that we @@ -3588,15 +3588,15 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) return MaybeInitPickers(aVisitor); } - if (aVisitor.mEvent->message == NS_FOCUS_CONTENT || - aVisitor.mEvent->message == NS_BLUR_CONTENT) { - if (aVisitor.mEvent->message == NS_FOCUS_CONTENT && + if (aVisitor.mEvent->mMessage == NS_FOCUS_CONTENT || + aVisitor.mEvent->mMessage == NS_BLUR_CONTENT) { + if (aVisitor.mEvent->mMessage == NS_FOCUS_CONTENT && MayFireChangeOnBlur() && !mIsDraggingRange) { // StartRangeThumbDrag already set mFocusedValue GetValue(mFocusedValue); } - if (aVisitor.mEvent->message == NS_BLUR_CONTENT) { + if (aVisitor.mEvent->mMessage == NS_BLUR_CONTENT) { if (mIsDraggingRange) { FinishRangeThumbDrag(); } else if (mNumberControlSpinnerIsSpinning) { @@ -3604,7 +3604,7 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) } } - UpdateValidityUIBits(aVisitor.mEvent->message == NS_FOCUS_CONTENT); + UpdateValidityUIBits(aVisitor.mEvent->mMessage == NS_FOCUS_CONTENT); UpdateState(true); } @@ -3629,10 +3629,9 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent(); if (mouseEvent && mouseEvent->IsLeftClickEvent() && !ShouldPreventDOMActivateDispatch(aVisitor.mEvent->originalTarget)) { - // XXX Activating actually occurs even if it's caused by untrusted event. - // Therefore, shouldn't this be always trusted event? - InternalUIEvent actEvent(aVisitor.mEvent->mFlags.mIsTrusted, - NS_UI_ACTIVATE); + // DOMActive event should be trusted since the activation is actually + // occurred even if the cause is an untrusted click event. + InternalUIEvent actEvent(true, NS_UI_ACTIVATE, mouseEvent); actEvent.detail = 1; nsCOMPtr shell = aVisitor.mPresContext->GetPresShell(); @@ -3721,7 +3720,7 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) if (NS_SUCCEEDED(rv)) { WidgetKeyboardEvent* keyEvent = aVisitor.mEvent->AsKeyboardEvent(); if (mType == NS_FORM_INPUT_NUMBER && - keyEvent && keyEvent->message == NS_KEY_PRESS && + keyEvent && keyEvent->mMessage == NS_KEY_PRESS && aVisitor.mEvent->mFlags.mIsTrusted && (keyEvent->keyCode == NS_VK_UP || keyEvent->keyCode == NS_VK_DOWN) && !(keyEvent->IsShift() || keyEvent->IsControl() || @@ -3744,7 +3743,7 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; } } else if (nsEventStatus_eIgnore == aVisitor.mEventStatus) { - switch (aVisitor.mEvent->message) { + switch (aVisitor.mEvent->mMessage) { case NS_FOCUS_CONTENT: { @@ -3779,9 +3778,9 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) // For backwards compat, trigger checks/radios/buttons with // space or enter (bug 25300) WidgetKeyboardEvent* keyEvent = aVisitor.mEvent->AsKeyboardEvent(); - if ((aVisitor.mEvent->message == NS_KEY_PRESS && + if ((aVisitor.mEvent->mMessage == NS_KEY_PRESS && keyEvent->keyCode == NS_VK_RETURN) || - (aVisitor.mEvent->message == NS_KEY_UP && + (aVisitor.mEvent->mMessage == NS_KEY_UP && keyEvent->keyCode == NS_VK_SPACE)) { switch(mType) { case NS_FORM_INPUT_CHECKBOX: @@ -3814,7 +3813,7 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) } // case } // switch } - if (aVisitor.mEvent->message == NS_KEY_PRESS && + if (aVisitor.mEvent->mMessage == NS_KEY_PRESS && mType == NS_FORM_INPUT_RADIO && !keyEvent->IsAlt() && !keyEvent->IsControl() && !keyEvent->IsMeta()) { bool isMovingBack = false; @@ -3866,7 +3865,7 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) * not submit, period. */ - if (aVisitor.mEvent->message == NS_KEY_PRESS && + if (aVisitor.mEvent->mMessage == NS_KEY_PRESS && keyEvent->keyCode == NS_VK_RETURN && (IsSingleLineTextControl(false, mType) || mType == NS_FORM_INPUT_NUMBER || @@ -3876,7 +3875,7 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) NS_ENSURE_SUCCESS(rv, rv); } - if (aVisitor.mEvent->message == NS_KEY_PRESS && + if (aVisitor.mEvent->mMessage == NS_KEY_PRESS && mType == NS_FORM_INPUT_RANGE && !keyEvent->IsAlt() && !keyEvent->IsControl() && !keyEvent->IsMeta() && (keyEvent->keyCode == NS_VK_LEFT || @@ -3966,7 +3965,7 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) nsNumberControlFrame* numberControlFrame = do_QueryFrame(GetPrimaryFrame()); if (numberControlFrame) { - if (aVisitor.mEvent->message == NS_MOUSE_BUTTON_DOWN && + if (aVisitor.mEvent->mMessage == NS_MOUSE_BUTTON_DOWN && IsMutable()) { switch (numberControlFrame->GetSpinButtonForPointerEvent( aVisitor.mEvent->AsMouseEvent())) { @@ -4028,7 +4027,7 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) // pres shell. See bug 125624. // TODO: removing this code and have the submit event sent by the // form, see bug 592124. - if (presShell && (event.message != NS_FORM_SUBMIT || + if (presShell && (event.mMessage != NS_FORM_SUBMIT || mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate) || // We know the element is a submit control, if this check is moved, // make sure formnovalidate is used only if it's a submit control. @@ -4083,7 +4082,7 @@ HTMLInputElement::PostHandleEventForRangeThumb(EventChainPostVisitor& aVisitor) return; } - switch (aVisitor.mEvent->message) + switch (aVisitor.mEvent->mMessage) { case NS_MOUSE_BUTTON_DOWN: case NS_TOUCH_START: { @@ -4100,7 +4099,7 @@ HTMLInputElement::PostHandleEventForRangeThumb(EventChainPostVisitor& aVisitor) inputEvent->IsOS()) { break; // ignore } - if (aVisitor.mEvent->message == NS_MOUSE_BUTTON_DOWN) { + if (aVisitor.mEvent->mMessage == NS_MOUSE_BUTTON_DOWN) { if (aVisitor.mEvent->AsMouseEvent()->buttons == WidgetMouseEvent::eLeftButtonFlag) { StartRangeThumbDrag(inputEvent); diff --git a/dom/html/HTMLLabelElement.cpp b/dom/html/HTMLLabelElement.cpp index 6576623aa985..cd41c9bcb18d 100644 --- a/dom/html/HTMLLabelElement.cpp +++ b/dom/html/HTMLLabelElement.cpp @@ -104,7 +104,7 @@ HTMLLabelElement::PostHandleEvent(EventChainPostVisitor& aVisitor) WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent(); if (mHandlingEvent || (!(mouseEvent && mouseEvent->IsLeftClickEvent()) && - aVisitor.mEvent->message != NS_MOUSE_BUTTON_DOWN) || + aVisitor.mEvent->mMessage != NS_MOUSE_BUTTON_DOWN) || aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault || !aVisitor.mPresContext || // Don't handle the event if it's already been handled by another label @@ -122,7 +122,7 @@ HTMLLabelElement::PostHandleEvent(EventChainPostVisitor& aVisitor) if (content) { mHandlingEvent = true; - switch (aVisitor.mEvent->message) { + switch (aVisitor.mEvent->mMessage) { case NS_MOUSE_BUTTON_DOWN: if (mouseEvent->button == WidgetMouseEvent::eLeftButton) { // We reset the mouse-down point on every event because there is diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index bd470b58f8c4..56b22a639b8f 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -4006,7 +4006,15 @@ bool HTMLMediaElement::IsBeingDestroyed() void HTMLMediaElement::NotifyOwnerDocumentActivityChanged() { bool pauseElement = NotifyOwnerDocumentActivityChangedInternal(); - if (pauseElement && mAudioChannelAgent) { + if (pauseElement && mAudioChannelAgent +#ifdef PAUSE_MEDIA_ELEMENT_FROM_AUDIOCHANNEL + // On B2G, NotifyOwnerDocumentActivityChangedInternal may return true for + // two reasons: the document no longer being active, or the element being + // paused by the audio channel. However we are only interested in the + // first case here, so we need to filter out the second case. + && !ComputedMuted() +#endif + ) { // If the element is being paused since we are navigating away from the // document, notify the audio channel agent. // Be careful to ignore this event during a docshell frame swap. diff --git a/dom/html/HTMLMenuItemElement.cpp b/dom/html/HTMLMenuItemElement.cpp index 2ac74b0cf005..31bb2ffce99c 100644 --- a/dom/html/HTMLMenuItemElement.cpp +++ b/dom/html/HTMLMenuItemElement.cpp @@ -254,7 +254,7 @@ HTMLMenuItemElement::SetChecked(bool aChecked) nsresult HTMLMenuItemElement::PreHandleEvent(EventChainPreVisitor& aVisitor) { - if (aVisitor.mEvent->message == NS_MOUSE_CLICK) { + if (aVisitor.mEvent->mMessage == NS_MOUSE_CLICK) { bool originalCheckedValue = false; switch (mType) { @@ -290,7 +290,7 @@ nsresult HTMLMenuItemElement::PostHandleEvent(EventChainPostVisitor& aVisitor) { // Check to see if the event was cancelled. - if (aVisitor.mEvent->message == NS_MOUSE_CLICK && + if (aVisitor.mEvent->mMessage == NS_MOUSE_CLICK && aVisitor.mItemFlags & NS_CHECKED_IS_TOGGLED && aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault) { bool originalCheckedValue = diff --git a/dom/html/HTMLObjectElement.cpp b/dom/html/HTMLObjectElement.cpp index 3714c2839fef..4623e73abf02 100644 --- a/dom/html/HTMLObjectElement.cpp +++ b/dom/html/HTMLObjectElement.cpp @@ -215,7 +215,7 @@ HTMLObjectElement::HandleFocusBlurPlugin(Element* aElement, if (!aEvent->mFlags.mIsTrusted) { return; } - switch (aEvent->message) { + switch (aEvent->mMessage) { case NS_FOCUS_CONTENT: { OnFocusBlurPlugin(aElement, true); break; diff --git a/dom/html/HTMLSelectElement.cpp b/dom/html/HTMLSelectElement.cpp index 766d5ecab12c..8b1c433ed36b 100644 --- a/dom/html/HTMLSelectElement.cpp +++ b/dom/html/HTMLSelectElement.cpp @@ -1501,7 +1501,7 @@ nsresult HTMLSelectElement::PreHandleEvent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = false; - if (IsDisabledForEvents(aVisitor.mEvent->message)) { + if (IsDisabledForEvents(aVisitor.mEvent->mMessage)) { return NS_OK; } @@ -1511,7 +1511,7 @@ HTMLSelectElement::PreHandleEvent(EventChainPreVisitor& aVisitor) nsresult HTMLSelectElement::PostHandleEvent(EventChainPostVisitor& aVisitor) { - if (aVisitor.mEvent->message == NS_FOCUS_CONTENT) { + if (aVisitor.mEvent->mMessage == NS_FOCUS_CONTENT) { // If the invalid UI is shown, we should show it while focused and // update the invalid/valid UI. mCanShowInvalidUI = !IsValid() && ShouldShowValidityUI(); @@ -1522,7 +1522,7 @@ HTMLSelectElement::PostHandleEvent(EventChainPostVisitor& aVisitor) // We don't have to update NS_EVENT_STATE_MOZ_UI_INVALID nor // NS_EVENT_STATE_MOZ_UI_VALID given that the states should not change. - } else if (aVisitor.mEvent->message == NS_BLUR_CONTENT) { + } else if (aVisitor.mEvent->mMessage == NS_BLUR_CONTENT) { mCanShowInvalidUI = true; mCanShowValidUI = true; diff --git a/dom/html/HTMLTextAreaElement.cpp b/dom/html/HTMLTextAreaElement.cpp index 7c2a38d7c861..9f8b8e4752a1 100644 --- a/dom/html/HTMLTextAreaElement.cpp +++ b/dom/html/HTMLTextAreaElement.cpp @@ -480,13 +480,13 @@ nsresult HTMLTextAreaElement::PreHandleEvent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = false; - if (IsDisabledForEvents(aVisitor.mEvent->message)) { + if (IsDisabledForEvents(aVisitor.mEvent->mMessage)) { return NS_OK; } // Don't dispatch a second select event if we are already handling // one. - if (aVisitor.mEvent->message == NS_FORM_SELECTED) { + if (aVisitor.mEvent->mMessage == NS_FORM_SELECTED) { if (mHandlingSelect) { return NS_OK; } @@ -499,14 +499,14 @@ HTMLTextAreaElement::PreHandleEvent(EventChainPreVisitor& aVisitor) if (aVisitor.mEvent->mFlags.mNoContentDispatch) { aVisitor.mItemFlags |= NS_NO_CONTENT_DISPATCH; } - if (aVisitor.mEvent->message == NS_MOUSE_CLICK && + if (aVisitor.mEvent->mMessage == NS_MOUSE_CLICK && aVisitor.mEvent->AsMouseEvent()->button == WidgetMouseEvent::eMiddleButton) { aVisitor.mEvent->mFlags.mNoContentDispatch = false; } // Fire onchange (if necessary), before we do the blur, bug 370521. - if (aVisitor.mEvent->message == NS_BLUR_CONTENT) { + if (aVisitor.mEvent->mMessage == NS_BLUR_CONTENT) { FireChangeEventIfNeeded(); } @@ -534,13 +534,13 @@ HTMLTextAreaElement::FireChangeEventIfNeeded() nsresult HTMLTextAreaElement::PostHandleEvent(EventChainPostVisitor& aVisitor) { - if (aVisitor.mEvent->message == NS_FORM_SELECTED) { + if (aVisitor.mEvent->mMessage == NS_FORM_SELECTED) { mHandlingSelect = false; } - if (aVisitor.mEvent->message == NS_FOCUS_CONTENT || - aVisitor.mEvent->message == NS_BLUR_CONTENT) { - if (aVisitor.mEvent->message == NS_FOCUS_CONTENT) { + if (aVisitor.mEvent->mMessage == NS_FOCUS_CONTENT || + aVisitor.mEvent->mMessage == NS_BLUR_CONTENT) { + if (aVisitor.mEvent->mMessage == NS_FOCUS_CONTENT) { // If the invalid UI is shown, we should show it while focusing (and // update). Otherwise, we should not. GetValueInternal(mFocusedValue, true); diff --git a/dom/html/HTMLTrackElement.cpp b/dom/html/HTMLTrackElement.cpp index bd375fb5eef4..abdda2fa8c2f 100644 --- a/dom/html/HTMLTrackElement.cpp +++ b/dom/html/HTMLTrackElement.cpp @@ -70,7 +70,7 @@ static MOZ_CONSTEXPR nsAttrValue::EnumTable kKindTable[] = { }; // The default value for kKindTable is "subtitles" -static MOZ_CONSTEXPR const char* kKindTableDefaultString = kKindTable->tag; +static MOZ_CONSTEXPR const char* kKindTableDefaultString = kKindTable[0].tag; /** HTMLTrackElement */ HTMLTrackElement::HTMLTrackElement(already_AddRefed& aNodeInfo) diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index fc4711033848..9ad5f912b4e9 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -2250,7 +2250,7 @@ nsresult nsGenericHTMLFormElement::PreHandleEvent(EventChainPreVisitor& aVisitor) { if (aVisitor.mEvent->mFlags.mIsTrusted) { - switch (aVisitor.mEvent->message) { + switch (aVisitor.mEvent->mMessage) { case NS_FOCUS_CONTENT: { // Check to see if focus has bubbled up from a form control's diff --git a/dom/html/nsTextEditorState.cpp b/dom/html/nsTextEditorState.cpp index b3354dc74ae1..6bc87b3774e2 100644 --- a/dom/html/nsTextEditorState.cpp +++ b/dom/html/nsTextEditorState.cpp @@ -904,7 +904,7 @@ nsTextInputListener::HandleEvent(nsIDOMEvent* aEvent) return NS_ERROR_UNEXPECTED; } - if (keyEvent->message != NS_KEY_PRESS) { + if (keyEvent->mMessage != NS_KEY_PRESS) { return NS_OK; } diff --git a/dom/html/test/mochitest.ini b/dom/html/test/mochitest.ini index 4a0a7660ae32..562c8a750e14 100644 --- a/dom/html/test/mochitest.ini +++ b/dom/html/test/mochitest.ini @@ -286,7 +286,7 @@ skip-if = toolkit == 'android' [test_bug518122.html] [test_bug519987.html] [test_bug523771.html] -skip-if = buildapp == 'b2g' || e10s # b2g(onload of iframe not firing, because submit not working?) b2g-debug(onload of iframe not firing, because submit not working?) b2g-desktop(onload of iframe not firing, because submit not working?) +skip-if = buildapp == 'b2g' # b2g(onload of iframe not firing, because submit not working?) b2g-debug(onload of iframe not firing, because submit not working?) b2g-desktop(onload of iframe not firing, because submit not working?) [test_bug529819.html] [test_bug529859.html] [test_bug535043.html] @@ -310,7 +310,7 @@ skip-if = toolkit == 'android' #TIMED_OUT [test_bug560112.html] [test_bug561634.html] [test_bug561636.html] -skip-if = buildapp == 'b2g' || e10s # b2g(observerservice not working) b2g-debug(observerservice not working) b2g-desktop(observerservice not working) +skip-if = buildapp == 'b2g' # b2g(observerservice not working) b2g-debug(observerservice not working) b2g-desktop(observerservice not working) [test_bug561640.html] [test_bug564001.html] [test_bug566046.html] @@ -335,10 +335,9 @@ skip-if = buildapp == 'b2g' || e10s # b2g(observerservice not working) b2g-debug [test_bug592802.html] [test_bug593689.html] [test_bug595429.html] -skip-if = e10s [test_bug595447.html] [test_bug595449.html] -skip-if = (toolkit == 'gonk' && debug) || e10s #debug-only failure +skip-if = (toolkit == 'gonk' && debug) #debug-only failure [test_bug596350.html] [test_bug596511.html] [test_bug598643.html] @@ -359,7 +358,7 @@ skip-if = buildapp == 'mulet' # TC: Bug 1144079 - Re-enable Mulet mochitests and [test_bug612730.html] skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' # b2g(form control not selected/checked with synthesizeMouse, also fails on Android) b2g-debug(form control not selected/checked with synthesizeMouse, also fails on Android) b2g-desktop(form control not selected/checked with synthesizeMouse, also fails on Android) [test_bug613113.html] -skip-if = buildapp == 'b2g' || e10s # b2g(bug 587671, need an invalidformsubmit observer) b2g-debug(bug 587671, need an invalidformsubmit observer) b2g-desktop(bug 587671, need an invalidformsubmit observer) +skip-if = buildapp == 'b2g' # b2g(bug 587671, need an invalidformsubmit observer) b2g-debug(bug 587671, need an invalidformsubmit observer) b2g-desktop(bug 587671, need an invalidformsubmit observer) [test_bug613722.html] [test_bug613979.html] [test_bug615595.html] @@ -367,29 +366,22 @@ skip-if = buildapp == 'b2g' || e10s # b2g(bug 587671, need an invalidformsubmit skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT # b2g(form control not selected/checked with synthesizeMouse, also fails on Android) b2g-debug(form control not selected/checked with synthesizeMouse, also fails on Android) b2g-desktop(form control not selected/checked with synthesizeMouse, also fails on Android) [test_bug617528.html] [test_bug618948.html] -skip-if = buildapp == 'b2g' || e10s # b2g(bug 587671, need an invalidformsubmit observer) b2g-debug(bug 587671, need an invalidformsubmit observer) b2g-desktop(bug 587671, need an invalidformsubmit observer) +skip-if = buildapp == 'b2g' # b2g(bug 587671, need an invalidformsubmit observer) b2g-debug(bug 587671, need an invalidformsubmit observer) b2g-desktop(bug 587671, need an invalidformsubmit observer) [test_bug619278.html] -skip-if = buildapp == 'b2g' || e10s # b2g(bug 587671, need an invalidformsubmit observer) b2g-debug(bug 587671, need an invalidformsubmit observer) b2g-desktop(bug 587671, need an invalidformsubmit observer) +skip-if = buildapp == 'b2g' # b2g(bug 587671, need an invalidformsubmit observer) b2g-debug(bug 587671, need an invalidformsubmit observer) b2g-desktop(bug 587671, need an invalidformsubmit observer) [test_bug622558.html] -skip-if = e10s [test_bug622597.html] -skip-if = buildapp == 'b2g' || e10s # b2g(bug 587671, need an invalidformsubmit observer) b2g-debug(bug 587671, need an invalidformsubmit observer) b2g-desktop(bug 587671, need an invalidformsubmit observer) +skip-if = buildapp == 'b2g' # b2g(bug 587671, need an invalidformsubmit observer) b2g-debug(bug 587671, need an invalidformsubmit observer) b2g-desktop(bug 587671, need an invalidformsubmit observer) [test_bug623291.html] -skip-if = e10s [test_bug6296.html] -skip-if = e10s [test_bug629801.html] -skip-if = e10s [test_bug633058.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || e10s #Bug 931116, b2g desktop specific, initial triage +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_bug636336.html] -skip-if = e10s [test_bug641219.html] -skip-if = e10s [test_bug643051.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || e10s #Bug 931116, b2g desktop specific, initial triage +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_bug646157.html] -skip-if = e10s [test_bug649134.html] # This extra subdirectory is needed due to the nature of this test. # With the bug, the test loads the base URL of the bug649134/file_*.sjs @@ -441,7 +433,7 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop spec [test_bug839913.html] [test_bug840877.html] [test_bug841466.html] -skip-if = (toolkit == 'gonk' && debug) || e10s #debug-only failure +skip-if = (toolkit == 'gonk' && debug) #debug-only failure [test_bug845057.html] [test_bug869040.html] [test_bug870787.html] @@ -538,7 +530,7 @@ skip-if = toolkit == 'android' || (toolkit == 'gonk' && debug) #bug 871015, bug [test_bug1823.html] [test_bug57600.html] [test_bug196523.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || e10s #Bug 931116, b2g desktop specific, initial triage +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_bug199692.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit == 'android' || e10s #bug 811644 #Bug 931116, b2g desktop specific, initial triage [test_bug172261.html] @@ -589,7 +581,7 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e1 skip-if = buildapp == 'b2g' || e10s [test_bug765780.html] [test_bug871161.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || e10s #Bug 931116, b2g desktop specific, initial triage +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage support-files = file_bug871161-1.html file_bug871161-2.html [test_bug1013316.html] [test_hash_encoded.html] diff --git a/dom/html/test/test_bug523771.html b/dom/html/test/test_bug523771.html index 497b5e63ff82..80527ff93a57 100644 --- a/dom/html/test/test_bug523771.html +++ b/dom/html/test/test_bug523771.html @@ -20,7 +20,6 @@
 
 
 
diff --git a/dom/imptests/testharness.js b/dom/imptests/testharness.js
index 9433d12bdb0c..f4c66aae6219 100644
--- a/dom/imptests/testharness.js
+++ b/dom/imptests/testharness.js
@@ -517,19 +517,27 @@ policies and contribution forms [3].
 
     function promise_test(func, name, properties) {
         var test = async_test(name, properties);
-        Promise.resolve(test.step(func, test, test))
-            .then(
-                function() {
-                    test.done();
-                })
-            .catch(test.step_func(
-                function(value) {
-                    if (value instanceof AssertionError) {
-                        throw value;
-                    }
-                    assert(false, "promise_test", null,
-                           "Unhandled rejection with value: ${value}", {value:value});
-                }));
+        // If there is no promise tests queue make one.
+        test.step(function() {
+            if (!tests.promise_tests) {
+                tests.promise_tests = Promise.resolve();
+            }
+        });
+        tests.promise_tests = tests.promise_tests.then(function() {
+            return Promise.resolve(test.step(func, test, test))
+                .then(
+                    function() {
+                        test.done();
+                    })
+                .catch(test.step_func(
+                    function(value) {
+                        if (value instanceof AssertionError) {
+                            throw value;
+                        }
+                        assert(false, "promise_test", null,
+                               "Unhandled rejection with value: ${value}", {value:value});
+                    }));
+        });
     }
 
     function promise_rejects(test, expected, promise) {
@@ -650,6 +658,14 @@ policies and contribution forms [3].
         object.addEventListener(event, callback, false);
     }
 
+    function step_timeout(f, t) {
+        var outer_this = this;
+        var args = Array.prototype.slice.call(arguments, 2);
+        return setTimeout(function() {
+            f.apply(outer_this, args);
+        }, t * tests.timeout_multiplier);
+    }
+
     expose(test, 'test');
     expose(async_test, 'async_test');
     expose(promise_test, 'promise_test');
@@ -658,6 +674,7 @@ policies and contribution forms [3].
     expose(setup, 'setup');
     expose(done, 'done');
     expose(on_event, 'on_event');
+    expose(step_timeout, 'step_timeout');
 
     /*
      * Return a string truncated to the given length, with ... added at the end
@@ -1414,6 +1431,14 @@ policies and contribution forms [3].
         });
     };
 
+    Test.prototype.step_timeout = function(f, timeout) {
+        var test_this = this;
+        var args = Array.prototype.slice.call(arguments, 2);
+        return setTimeout(this.step_func(function() {
+            return f.apply(test_this, args);
+        }, timeout * tests.timeout_multiplier));
+    }
+
     Test.prototype.add_cleanup = function(callback) {
         this.cleanup_callbacks.push(callback);
     };
@@ -2048,28 +2073,11 @@ policies and contribution forms [3].
             log.removeChild(log.lastChild);
         }
 
-        var script_prefix = null;
-        var scripts = document.getElementsByTagName("script");
-        for (var i = 0; i < scripts.length; i++) {
-            var src;
-            if (scripts[i].src) {
-                src = scripts[i].src;
-            } else if (scripts[i].href) {
-                //SVG case
-                src = scripts[i].href.baseVal;
-            }
-
-            var matches = src && src.match(/^(.*\/|)testharness\.js$/);
-            if (matches) {
-                script_prefix = matches[1];
-                break;
-            }
-        }
-
-        if (script_prefix !== null) {
+        var harness_url = get_harness_url();
+        if (harness_url !== null) {
             var stylesheet = output_document.createElementNS(xhtml_ns, "link");
             stylesheet.setAttribute("rel", "stylesheet");
-            stylesheet.setAttribute("href", script_prefix + "testharness.css");
+            stylesheet.setAttribute("href", harness_url + "testharness.css");
             var heads = output_document.getElementsByTagName("head");
             if (heads.length) {
                 heads[0].appendChild(stylesheet);
@@ -2425,6 +2433,7 @@ policies and contribution forms [3].
 
     AssertionError.prototype.get_stack = function() {
         var stack = new Error().stack;
+        // IE11 does not initialize 'Error.stack' until the object is thrown.
         if (!stack) {
             try {
                 throw new Error();
@@ -2432,18 +2441,30 @@ policies and contribution forms [3].
                 stack = e.stack;
             }
         }
+
         var lines = stack.split("\n");
-        var rv = [];
-        var re = /\/resources\/testharness\.js/;
+
+        // Create a pattern to match stack frames originating within testharness.js.  These include the
+        // script URL, followed by the line/col (e.g., '/resources/testharness.js:120:21').
+        var re = new RegExp((get_script_url() || "\\btestharness.js") + ":\\d+:\\d+");
+
+        // Some browsers include a preamble that specifies the type of the error object.  Skip this by
+        // advancing until we find the first stack frame originating from testharness.js.
         var i = 0;
-        // Fire remove any preamble that doesn't match the regexp
-        while (!re.test(lines[i])) {
-            i++
+        while (!re.test(lines[i]) && i < lines.length) {
+            i++;
+        }
+
+        // Then skip the top frames originating from testharness.js to begin the stack at the test code.
+        while (re.test(lines[i]) && i < lines.length) {
+            i++;
         }
-        // Then remove top frames in testharness.js itself
-        while (re.test(lines[i])) {
-            i++
+
+        // Paranoid check that we didn't skip all frames.  If so, return the original stack unmodified.
+        if (i >= lines.length) {
+            return stack;
         }
+
         return lines.slice(i).join("\n");
     }
 
@@ -2491,7 +2512,7 @@ policies and contribution forms [3].
         Array.prototype.push.apply(array, items);
     }
 
-    function forEach (array, callback, thisObj)
+    function forEach(array, callback, thisObj)
     {
         for (var i = 0; i < array.length; i++) {
             if (array.hasOwnProperty(i)) {
@@ -2535,11 +2556,46 @@ policies and contribution forms [3].
         }
     }
 
+    /** Returns the 'src' URL of the first 
 
-
+
   
 
 
diff --git a/dom/security/test/csp/test_base-uri.html b/dom/security/test/csp/test_base-uri.html
index 7e326401d3df..be455a51e706 100644
--- a/dom/security/test/csp/test_base-uri.html
+++ b/dom/security/test/csp/test_base-uri.html
@@ -23,45 +23,47 @@
 
 SimpleTest.waitForExplicitFinish();
 
-var testPolicies = [
- "base-uri http://example.com",
- "base-uri https:",
- "base-uri 'none'",
+var tests = [
+  {
+    policy: "base-uri http://mochi.test;",
+    result: "http://mochi.test"
+  },
+  {
+    policy: "base-uri http://example.com;",
+    result: "http://example.com"
+  },
+  {
+    policy: "base-uri https:",
+    result: "http://example.com"
+  },
+  {
+    policy: "base-uri 'none'",
+    result: "http://example.com"
+  }
 ];
 
 // initializing to -1 so we start at index 0 when we start the test
 var counter = -1;
 
-function examiner() {
-  SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
-}
-examiner.prototype  = {
-  observe: function(subject, topic, data) {
-    if (topic === "csp-on-violate-policy") {
-      var spec = SpecialPowers.getPrivilegedProps(
-                   SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
-
-      if (spec === "http://mochi.test/") {
-        // 'data' holds the violated directive
-        is(data, testPolicies[counter], "Disallowed setting the base-uri in test " + counter + "!");
-        loadNextTest();
-      }
-    }
-  },
-  remove: function() {
-    SpecialPowers.removeObserver(this, "csp-on-violate-policy");
-  }
-}
-window.BaseURIExaminer = new examiner();
-
 function finishTest() {
-  window.BaseURIExaminer.remove();
+  window.removeEventListener("message", receiveMessage, false);
   SimpleTest.finish();
 }
 
+// a postMessage handler that is used by sandboxed iframes without
+// 'allow-same-origin' to bubble up results back to this main page.
+window.addEventListener("message", receiveMessage, false);
+function receiveMessage(event) {
+  var result = event.data.result;
+  // we only care about the base uri, so instead of comparing the complete uri
+  // we just make sure that the base is correct which is sufficient here.
+  ok(result.startsWith(tests[counter].result), "Restricting base-uri in test " + counter + "!");
+  loadNextTest();
+}
+
 function loadNextTest() {
   counter++;
-  if (counter == testPolicies.length) {
+  if (counter == tests.length) {
     finishTest();
     return;
   }
@@ -69,7 +71,8 @@
   // append the file that should be served
   src += "?file=" + escape("tests/dom/security/test/csp/file_base-uri.html");
   // append the CSP that should be used to serve the file
-  src += "&csp=" + escape(testPolicies[counter]);
+  // please note that we have to include 'unsafe-inline' to permit sending the postMessage
+  src += "&csp=" + escape("script-src 'unsafe-inline'; " + tests[counter].policy);
   document.getElementById("testframe").src = src;
 }
 
diff --git a/dom/svg/SVGSVGElement.cpp b/dom/svg/SVGSVGElement.cpp
index 3c538565b5ac..21210547ec74 100644
--- a/dom/svg/SVGSVGElement.cpp
+++ b/dom/svg/SVGSVGElement.cpp
@@ -590,7 +590,7 @@ SVGSVGElement::IsAttributeMapped(const nsIAtom* name) const
 nsresult
 SVGSVGElement::PreHandleEvent(EventChainPreVisitor& aVisitor)
 {
-  if (aVisitor.mEvent->message == NS_SVG_LOAD) {
+  if (aVisitor.mEvent->mMessage == NS_SVG_LOAD) {
     if (mTimedDocumentRoot) {
       mTimedDocumentRoot->Begin();
       // Set 'resample needed' flag, so that if any script calls a DOM method
diff --git a/dom/system/gonk/AudioManager.cpp b/dom/system/gonk/AudioManager.cpp
index 6a7552fc329c..314d838e7924 100644
--- a/dom/system/gonk/AudioManager.cpp
+++ b/dom/system/gonk/AudioManager.cpp
@@ -65,8 +65,6 @@ using namespace mozilla::dom::bluetooth;
 #define AUDIO_POLICY_SERVICE_NAME     "media.audio_policy"
 #define SETTINGS_SERVICE              "@mozilla.org/settingsService;1"
 
-static void BinderDeadCallback(status_t aErr);
-static void InternalSetAudioRoutes(SwitchState aState);
 // Refer AudioService.java from Android
 static const uint32_t sMaxStreamVolumeTbl[AUDIO_STREAM_CNT] = {
   5,   // voice call
@@ -99,22 +97,14 @@ static const nsAttrValue::EnumTable kAudioOutputProfilesTable[] = {
   { nullptr }
 };
 
-// A bitwise variable for recording what kind of headset is attached.
-static int sHeadsetState;
-#if defined(MOZ_B2G_BT) || ANDROID_VERSION >= 17
-static bool sBluetoothA2dpEnabled;
-#endif
 static const int kBtSampleRate = 8000;
-static bool sSwitchDone = true;
-#ifdef MOZ_B2G_BT
-static bool sA2dpSwitchDone = true;
-#endif
 
 typedef MozPromise VolumeInitPromise;
 
 namespace mozilla {
 namespace dom {
 namespace gonk {
+
 static const VolumeData gVolumeData[VOLUME_TOTAL_NUMBER] = {
   {"audio.volume.content",      VOLUME_MEDIA},
   {"audio.volume.notification", VOLUME_NOTIFICATION},
@@ -123,6 +113,20 @@ static const VolumeData gVolumeData[VOLUME_TOTAL_NUMBER] = {
   {"audio.volume.bt_sco",       VOLUME_BLUETOOTH_SCO}
 };
 
+class RunnableCallTask : public Task
+{
+public:
+  explicit RunnableCallTask(nsIRunnable* aRunnable)
+    : mRunnable(aRunnable) {}
+
+  void Run() override
+  {
+    mRunnable->Run();
+  }
+protected:
+  nsCOMPtr mRunnable;
+};
+
 class AudioProfileData final
 {
 public:
@@ -156,54 +160,48 @@ class AudioProfileData final
   bool mActive;
 };
 
-class RecoverTask : public nsRunnable
+void
+AudioManager::HandleAudioFlingerDied()
 {
-public:
-  RecoverTask() {}
-  NS_IMETHODIMP Run() {
-    nsCOMPtr amService = do_GetService(NS_AUDIOMANAGER_CONTRACTID);
-    NS_ENSURE_TRUE(amService, NS_OK);
-    AudioManager *am = static_cast(amService.get());
-
-    uint32_t attempt;
-    for (attempt = 0; attempt < 50; attempt++) {
-      if (defaultServiceManager()->checkService(String16(AUDIO_POLICY_SERVICE_NAME)) != 0) {
-        break;
-      }
-
-      LOG("AudioPolicyService is dead! attempt=%d", attempt);
-      usleep(1000 * 200);
+  uint32_t attempt;
+  for (attempt = 0; attempt < 50; attempt++) {
+    if (defaultServiceManager()->checkService(String16(AUDIO_POLICY_SERVICE_NAME)) != 0) {
+      break;
     }
 
-    MOZ_RELEASE_ASSERT(attempt < 50);
+    LOG("AudioPolicyService is dead! attempt=%d", attempt);
+    usleep(1000 * 200);
+  }
 
-    for (uint32_t loop = 0; loop < AUDIO_STREAM_CNT; ++loop) {
-      AudioSystem::initStreamVolume(static_cast(loop), 0,
-                                   sMaxStreamVolumeTbl[loop]);
-      uint32_t index;
-      am->GetStreamVolumeIndex(loop, &index);
-      am->SetStreamVolumeIndex(loop, index);
-    }
+  MOZ_RELEASE_ASSERT(attempt < 50);
+
+  for (uint32_t loop = 0; loop < AUDIO_STREAM_CNT; ++loop) {
+    AudioSystem::initStreamVolume(static_cast(loop),
+                                  0,
+                                  sMaxStreamVolumeTbl[loop]);
+    uint32_t index;
+    GetStreamVolumeIndex(loop, &index);
+    SetStreamVolumeIndex(loop, index);
+  }
 
-    if (sHeadsetState & AUDIO_DEVICE_OUT_WIRED_HEADSET)
-      InternalSetAudioRoutes(SWITCH_STATE_HEADSET);
-    else if (sHeadsetState & AUDIO_DEVICE_OUT_WIRED_HEADPHONE)
-      InternalSetAudioRoutes(SWITCH_STATE_HEADPHONE);
-    else
-      InternalSetAudioRoutes(SWITCH_STATE_OFF);
+  if (mHeadsetState & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
+    UpdateHeadsetConnectionState(SWITCH_STATE_HEADSET);
+  } else if (mHeadsetState & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) {
+    UpdateHeadsetConnectionState(SWITCH_STATE_HEADPHONE);
+  } else {
+    UpdateHeadsetConnectionState(SWITCH_STATE_OFF);
+  }
 
-    int32_t phoneState = nsIAudioManager::PHONE_STATE_INVALID;
-    am->GetPhoneState(&phoneState);
+  int32_t phoneState = nsIAudioManager::PHONE_STATE_INVALID;
+  GetPhoneState(&phoneState);
 #if ANDROID_VERSION < 17
-    AudioSystem::setPhoneState(phoneState);
+  AudioSystem::setPhoneState(phoneState);
 #else
-    AudioSystem::setPhoneState(static_cast(phoneState));
+  AudioSystem::setPhoneState(static_cast(phoneState));
 #endif
 
-    AudioSystem::get_audio_flinger();
-    return NS_OK;
-  }
-};
+  AudioSystem::get_audio_flinger();
+}
 
 class VolumeInitCallback final : public nsISettingsServiceCallback
 {
@@ -277,89 +275,65 @@ class VolumeInitCallback final : public nsISettingsServiceCallback
 };
 
 NS_IMPL_ISUPPORTS(VolumeInitCallback, nsISettingsServiceCallback)
-} /* namespace gonk */
-} /* namespace dom */
-} /* namespace mozilla */
 
 static void
 BinderDeadCallback(status_t aErr)
 {
-  if (aErr == DEAD_OBJECT) {
-    NS_DispatchToMainThread(new RecoverTask());
+  if (aErr != DEAD_OBJECT) {
+    return;
   }
+
+  nsCOMPtr runnable =
+    NS_NewRunnableFunction([]() {
+      MOZ_ASSERT(NS_IsMainThread());
+      nsRefPtr audioManager = AudioManager::GetInstance();
+      NS_ENSURE_TRUE(audioManager.get(), );
+      audioManager->HandleAudioFlingerDied();
+    });
+
+  NS_DispatchToMainThread(runnable);
 }
 
 static bool
 IsDeviceOn(audio_devices_t device)
 {
-  if (static_cast<
-      audio_policy_dev_state_t (*) (audio_devices_t, const char *)
-      >(AudioSystem::getDeviceConnectionState))
-    return AudioSystem::getDeviceConnectionState(device, "") ==
+#if ANDROID_VERSION >= 15
+  return AudioSystem::getDeviceConnectionState(device, "") ==
            AUDIO_POLICY_DEVICE_STATE_AVAILABLE;
-
+#else
   return false;
-}
-
-static void ProcessDelayedAudioRoute(SwitchState aState)
-{
-  if (sSwitchDone)
-    return;
-  InternalSetAudioRoutes(aState);
-  sSwitchDone = true;
-}
-
-#ifdef MOZ_B2G_BT
-static void ProcessDelayedA2dpRoute(audio_policy_dev_state_t aState, const nsCString aAddress)
-{
-  if (sA2dpSwitchDone)
-    return;
-  AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
-                                        aState, aAddress.get());
-  String8 cmd("bluetooth_enabled=false");
-  AudioSystem::setParameters(0, cmd);
-  cmd.setTo("A2dpSuspended=true");
-  AudioSystem::setParameters(0, cmd);
-  sA2dpSwitchDone = true;
-}
 #endif
+}
 
 NS_IMPL_ISUPPORTS(AudioManager, nsIAudioManager, nsIObserver)
 
-static void
-InternalSetAudioRoutesICS(SwitchState aState)
+void
+AudioManager::UpdateHeadsetConnectionState(hal::SwitchState aState)
 {
+#if ANDROID_VERSION >= 15
   if (aState == SWITCH_STATE_HEADSET) {
     AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_WIRED_HEADSET,
                                           AUDIO_POLICY_DEVICE_STATE_AVAILABLE, "");
-    sHeadsetState |= AUDIO_DEVICE_OUT_WIRED_HEADSET;
+    mHeadsetState |= AUDIO_DEVICE_OUT_WIRED_HEADSET;
   } else if (aState == SWITCH_STATE_HEADPHONE) {
     AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
                                           AUDIO_POLICY_DEVICE_STATE_AVAILABLE, "");
-    sHeadsetState |= AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
+    mHeadsetState |= AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
   } else if (aState == SWITCH_STATE_OFF) {
-    if (sHeadsetState & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
+    if (mHeadsetState & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
       AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_WIRED_HEADSET,
                                             AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, "");
     }
-    if (sHeadsetState & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) {
+    if (mHeadsetState & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) {
       AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
                                             AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, "");
     }
-    sHeadsetState = 0;
+    mHeadsetState = 0;
   }
-}
 
-static void
-InternalSetAudioRoutes(SwitchState aState)
-{
-  if (static_cast<
-    status_t (*)(audio_devices_t, audio_policy_dev_state_t, const char*)
-    >(AudioSystem::setDeviceConnectionState)) {
-    InternalSetAudioRoutesICS(aState);
-  } else {
-    NS_NOTREACHED("Doesn't support audio routing on GB version");
-  }
+#else
+  NS_NOTREACHED("Doesn't support audio routing on GB version");
+#endif
 }
 
 void
@@ -399,10 +373,26 @@ AudioManager::HandleBluetoothStatusChanged(nsISupports* aSubject,
       SwitchProfileData(DEVICE_BLUETOOTH, false);
     }
   } else if (!strcmp(aTopic, BLUETOOTH_A2DP_STATUS_CHANGED_ID)) {
-    if (audioState == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE && sA2dpSwitchDone) {
+    if (audioState == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE && mA2dpSwitchDone) {
+      nsRefPtr self = this;
+      nsCOMPtr runnable =
+        NS_NewRunnableFunction([self, audioState, aAddress]() {
+          if (self->mA2dpSwitchDone) {
+            return;
+          }
+          AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
+                                                audioState,
+                                                aAddress.get());
+          String8 cmd("bluetooth_enabled=false");
+          AudioSystem::setParameters(0, cmd);
+          cmd.setTo("A2dpSuspended=true");
+          AudioSystem::setParameters(0, cmd);
+          self->mA2dpSwitchDone = true;
+        });
       MessageLoop::current()->PostDelayedTask(
-        FROM_HERE, NewRunnableFunction(&ProcessDelayedA2dpRoute, audioState, aAddress), 1000);
-      sA2dpSwitchDone = false;
+        FROM_HERE, new RunnableCallTask(runnable), 1000);
+
+      mA2dpSwitchDone = false;
       SwitchProfileData(DEVICE_BLUETOOTH, false);
     } else {
       AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
@@ -411,7 +401,7 @@ AudioManager::HandleBluetoothStatusChanged(nsISupports* aSubject,
       AudioSystem::setParameters(0, cmd);
       cmd.setTo("A2dpSuspended=false");
       AudioSystem::setParameters(0, cmd);
-      sA2dpSwitchDone = true;
+      mA2dpSwitchDone = true;
       SwitchProfileData(DEVICE_BLUETOOTH, true);
 #if ANDROID_VERSION >= 17
       if (AudioSystem::getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) == AUDIO_POLICY_FORCE_NO_BT_A2DP) {
@@ -419,7 +409,7 @@ AudioManager::HandleBluetoothStatusChanged(nsISupports* aSubject,
       }
 #endif
     }
-    sBluetoothA2dpEnabled = audioState == AUDIO_POLICY_DEVICE_STATE_AVAILABLE;
+    mBluetoothA2dpEnabled = audioState == AUDIO_POLICY_DEVICE_STATE_AVAILABLE;
   } else if (!strcmp(aTopic, BLUETOOTH_HFP_STATUS_CHANGED_ID)) {
     AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
                                           audioState, aAddress.get());
@@ -537,46 +527,68 @@ NotifyHeadphonesStatus(SwitchState aState)
 class HeadphoneSwitchObserver : public SwitchObserver
 {
 public:
-  HeadphoneSwitchObserver(AudioManager* aAudioManager)
-  : mAudioManager(aAudioManager) { }
-  void Notify(const SwitchEvent& aEvent) {
-    NotifyHeadphonesStatus(aEvent.status());
-    // When user pulled out the headset, a delay of routing here can avoid the leakage of audio from speaker.
-    if (aEvent.status() == SWITCH_STATE_OFF && sSwitchDone) {
-      MessageLoop::current()->PostDelayedTask(
-        FROM_HERE, NewRunnableFunction(&ProcessDelayedAudioRoute, SWITCH_STATE_OFF), 1000);
-      mAudioManager->SwitchProfileData(DEVICE_HEADSET, false);
-      sSwitchDone = false;
-    } else if (aEvent.status() != SWITCH_STATE_OFF) {
-      InternalSetAudioRoutes(aEvent.status());
-      mAudioManager->SwitchProfileData(DEVICE_HEADSET, true);
-      sSwitchDone = true;
-    }
-    // Handle the coexistence of a2dp / headset device, latest one wins.
-#if ANDROID_VERSION >= 17
-    int32_t forceUse = 0;
-    mAudioManager->GetForceForUse(AUDIO_POLICY_FORCE_FOR_MEDIA, &forceUse);
-    if (aEvent.status() != SWITCH_STATE_OFF && sBluetoothA2dpEnabled) {
-      mAudioManager->SetForceForUse(AUDIO_POLICY_FORCE_FOR_MEDIA, AUDIO_POLICY_FORCE_NO_BT_A2DP);
-    } else if (forceUse == AUDIO_POLICY_FORCE_NO_BT_A2DP) {
-      mAudioManager->SetForceForUse(AUDIO_POLICY_FORCE_FOR_MEDIA, AUDIO_POLICY_FORCE_NONE);
-    }
-#endif
+  void Notify(const hal::SwitchEvent& aEvent) {
+    nsRefPtr audioManager = AudioManager::GetInstance();
+    MOZ_ASSERT(audioManager);
+    audioManager->HandleHeadphoneSwitchEvent(aEvent);
   }
-private:
-  AudioManager* mAudioManager;
 };
 
+void
+AudioManager::HandleHeadphoneSwitchEvent(const hal::SwitchEvent& aEvent)
+{
+  NotifyHeadphonesStatus(aEvent.status());
+  // When user pulled out the headset, a delay of routing here can avoid the leakage of audio from speaker.
+  if (aEvent.status() == SWITCH_STATE_OFF && mSwitchDone) {
+
+    nsRefPtr self = this;
+    nsCOMPtr runnable =
+      NS_NewRunnableFunction([self]() {
+        if (self->mSwitchDone) {
+          return;
+        }
+        self->UpdateHeadsetConnectionState(SWITCH_STATE_OFF);
+        self->mSwitchDone = true;
+    });
+    MessageLoop::current()->PostDelayedTask(FROM_HERE, new RunnableCallTask(runnable), 1000);
+
+    SwitchProfileData(DEVICE_HEADSET, false);
+    mSwitchDone = false;
+  } else if (aEvent.status() != SWITCH_STATE_OFF) {
+    UpdateHeadsetConnectionState(aEvent.status());
+    SwitchProfileData(DEVICE_HEADSET, true);
+    mSwitchDone = true;
+  }
+  // Handle the coexistence of a2dp / headset device, latest one wins.
+#if ANDROID_VERSION >= 17
+  int32_t forceUse = 0;
+  GetForceForUse(AUDIO_POLICY_FORCE_FOR_MEDIA, &forceUse);
+  if (aEvent.status() != SWITCH_STATE_OFF && mBluetoothA2dpEnabled) {
+    SetForceForUse(AUDIO_POLICY_FORCE_FOR_MEDIA, AUDIO_POLICY_FORCE_NO_BT_A2DP);
+  } else if (forceUse == AUDIO_POLICY_FORCE_NO_BT_A2DP) {
+    SetForceForUse(AUDIO_POLICY_FORCE_FOR_MEDIA, AUDIO_POLICY_FORCE_NONE);
+  }
+#endif
+}
+
 AudioManager::AudioManager()
   : mPhoneState(PHONE_STATE_CURRENT)
-  , mObserver(new HeadphoneSwitchObserver(this))
+  , mHeadsetState(0)
+  , mSwitchDone(true)
+#if defined(MOZ_B2G_BT) || ANDROID_VERSION >= 17
+  , mBluetoothA2dpEnabled(false)
+#endif
+#ifdef MOZ_B2G_BT
+  , mA2dpSwitchDone(true)
+#endif
+  , mObserver(new HeadphoneSwitchObserver())
 #ifdef MOZ_B2G_RIL
   , mMuteCallToRIL(false)
 #endif
 {
   RegisterSwitchObserver(SWITCH_HEADPHONES, mObserver);
 
-  InternalSetAudioRoutes(GetCurrentSwitchState(SWITCH_HEADPHONES));
+  UpdateHeadsetConnectionState(GetCurrentSwitchState(SWITCH_HEADPHONES));
   NotifyHeadphonesStatus(GetCurrentSwitchState(SWITCH_HEADPHONES));
 
   for (uint32_t loop = 0; loop < AUDIO_STREAM_CNT; ++loop) {
@@ -746,32 +758,26 @@ AudioManager::SetPhoneState(int32_t aState)
 NS_IMETHODIMP
 AudioManager::SetForceForUse(int32_t aUsage, int32_t aForce)
 {
-  if (static_cast<
-             status_t (*)(audio_policy_force_use_t, audio_policy_forced_cfg_t)
-             >(AudioSystem::setForceUse)) {
-    // Dynamically resolved the ICS signature.
-    status_t status = AudioSystem::setForceUse(
-                        (audio_policy_force_use_t)aUsage,
-                        (audio_policy_forced_cfg_t)aForce);
-    return status ? NS_ERROR_FAILURE : NS_OK;
-  }
-
+#if ANDROID_VERSION >= 15
+  status_t status = AudioSystem::setForceUse(
+                      (audio_policy_force_use_t)aUsage,
+                      (audio_policy_forced_cfg_t)aForce);
+  return status ? NS_ERROR_FAILURE : NS_OK;
+#else
   NS_NOTREACHED("Doesn't support force routing on GB version");
   return NS_ERROR_UNEXPECTED;
+#endif
 }
 
 NS_IMETHODIMP
 AudioManager::GetForceForUse(int32_t aUsage, int32_t* aForce) {
-  if (static_cast<
-      audio_policy_forced_cfg_t (*)(audio_policy_force_use_t)
-      >(AudioSystem::getForceUse)) {
-    // Dynamically resolved the ICS signature.
-    *aForce = AudioSystem::getForceUse((audio_policy_force_use_t)aUsage);
-    return NS_OK;
-  }
-
+#if ANDROID_VERSION >= 15
+   *aForce = AudioSystem::getForceUse((audio_policy_force_use_t)aUsage);
+   return NS_OK;
+#else
   NS_NOTREACHED("Doesn't support force routing on GB version");
   return NS_ERROR_UNEXPECTED;
+#endif
 }
 
 NS_IMETHODIMP
@@ -787,7 +793,7 @@ AudioManager::SetFmRadioAudioEnabled(bool aFmRadioAudioEnabled)
   AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_FM,
     aFmRadioAudioEnabled ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE :
     AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, "");
-  InternalSetAudioRoutes(GetCurrentSwitchState(SWITCH_HEADPHONES));
+  UpdateHeadsetConnectionState(GetCurrentSwitchState(SWITCH_HEADPHONES));
   // sync volume with music after powering on fm radio
   if (aFmRadioAudioEnabled) {
     uint32_t volIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_MUSIC];
@@ -1253,3 +1259,7 @@ AudioManager::UpdateVolumeFromProfile(AudioProfileData* aProfileData)
                         aProfileData->mVolumeTable[gVolumeData[idx].mCategory]);
   }
 }
+
+} /* namespace gonk */
+} /* namespace dom */
+} /* namespace mozilla */
diff --git a/dom/system/gonk/AudioManager.h b/dom/system/gonk/AudioManager.h
index 0ab3ba8f220d..dd16c49beb1e 100644
--- a/dom/system/gonk/AudioManager.h
+++ b/dom/system/gonk/AudioManager.h
@@ -16,6 +16,7 @@
 #ifndef mozilla_dom_system_b2g_audiomanager_h__
 #define mozilla_dom_system_b2g_audiomanager_h__
 
+#include "mozilla/HalTypes.h"
 #include "mozilla/Observer.h"
 #include "nsAutoPtr.h"
 #include "nsIAudioManager.h"
@@ -98,8 +99,25 @@ class AudioManager final : public nsIAudioManager
   // Validate whether the volume index is within the range
   nsresult ValidateVolumeIndex(uint32_t aCategory, uint32_t aIndex) const;
 
+  // Called when android AudioFlinger in mediaserver is died
+  void HandleAudioFlingerDied();
+
+  void HandleHeadphoneSwitchEvent(const hal::SwitchEvent& aEvent);
+
 protected:
   int32_t mPhoneState;
+
+  // A bitwise variable for recording what kind of headset/headphone is attached.
+  int32_t mHeadsetState;
+
+  bool mSwitchDone;
+
+#if defined(MOZ_B2G_BT) || ANDROID_VERSION >= 17
+  bool mBluetoothA2dpEnabled;
+#endif
+#ifdef MOZ_B2G_BT
+  bool mA2dpSwitchDone;
+#endif
   uint32_t mCurrentStreamVolumeTbl[AUDIO_STREAM_CNT];
 
   nsresult SetStreamVolumeIndex(int32_t aStream, uint32_t aIndex);
@@ -156,6 +174,8 @@ class AudioManager final : public nsIAudioManager
   void InitProfileVolumeSucceeded();
   void InitProfileVolumeFailed(const char* aError);
 
+  void UpdateHeadsetConnectionState(hal::SwitchState aState);
+
   AudioManager();
   ~AudioManager();
 };
diff --git a/dom/telephony/Telephony.cpp b/dom/telephony/Telephony.cpp
index 4ca27cab41d7..6aafc045ff4e 100644
--- a/dom/telephony/Telephony.cpp
+++ b/dom/telephony/Telephony.cpp
@@ -7,6 +7,7 @@
 #include "Telephony.h"
 
 #include "mozilla/Preferences.h"
+#include "mozilla/dom/AudioChannelBinding.h"
 #include "mozilla/dom/CallEvent.h"
 #include "mozilla/dom/MozMobileConnectionBinding.h"
 #include "mozilla/dom/Promise.h"
@@ -20,6 +21,7 @@
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 
+#include "AudioChannelService.h"
 #include "CallsList.h"
 #include "TelephonyCall.h"
 #include "TelephonyCallGroup.h"
@@ -62,8 +64,12 @@ class Telephony::Listener : public nsITelephonyListener
 };
 
 Telephony::Telephony(nsPIDOMWindow* aOwner)
-  : DOMEventTargetHelper(aOwner)
+  : DOMEventTargetHelper(aOwner),
+    mIsAudioStartPlaying(false),
+    mAudioAgentNotify(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY),
+    mHaveDispatchedInterruptBeginEvent(false)
 {
+  MOZ_ASSERT(aOwner);
   nsCOMPtr global = do_QueryInterface(aOwner);
   MOZ_ASSERT(global);
 
@@ -72,6 +78,7 @@ Telephony::Telephony(nsPIDOMWindow* aOwner)
   MOZ_ASSERT(!rv.Failed());
 
   mReadyPromise = promise;
+  mMuted = AudioChannelService::IsAudioChannelMutedByDefault();
 }
 
 Telephony::~Telephony()
@@ -518,6 +525,61 @@ Telephony::StopTone(const Optional& aServiceId, ErrorResult& aRv)
   aRv = mService->StopTone(serviceId);
 }
 
+void
+Telephony::OwnAudioChannel(ErrorResult& aRv)
+{
+  if (mAudioAgent) {
+    return;
+  }
+
+  mAudioAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1");
+  MOZ_ASSERT(mAudioAgent);
+  aRv = mAudioAgent->Init(GetParentObject(),
+                         (int32_t)AudioChannel::Telephony, this);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+  aRv = HandleAudioAgentState();
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+}
+
+nsresult
+Telephony::HandleAudioAgentState()
+{
+  if (!mAudioAgent) {
+    return NS_OK;
+  }
+
+  Nullable activeCall;
+  GetActive(activeCall);
+  nsresult rv;
+  // Only stop agent when the call is disconnected.
+  if ((!mCalls.Length() && !mGroup->CallsArray().Length()) &&
+       mIsAudioStartPlaying) {
+    mIsAudioStartPlaying = false;
+    rv = mAudioAgent->NotifyStoppedPlaying(mAudioAgentNotify);
+    mAudioAgent = nullptr;
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  } else if (!activeCall.IsNull() && !mIsAudioStartPlaying) {
+    mIsAudioStartPlaying = true;
+    float volume = 1.0;
+    bool muted = false;
+    rv = mAudioAgent->NotifyStartedPlaying(mAudioAgentNotify, &volume, &muted);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    rv = WindowVolumeChanged(volume, muted);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+  return NS_OK;
+}
+
 bool
 Telephony::GetMuted(ErrorResult& aRv) const
 {
@@ -591,13 +653,78 @@ Telephony::GetReady(ErrorResult& aRv) const
   return promise.forget();
 }
 
+// nsIAudioChannelAgentCallback
+
+NS_IMETHODIMP
+Telephony::WindowVolumeChanged(float aVolume, bool aMuted)
+{
+  // Check the limitation of the network connection
+  if (mCalls.Length() > 1 ||
+     (mCalls.Length() == 1 && mGroup->CallsArray().Length())) {
+    return NS_ERROR_FAILURE;
+  }
+
+  ErrorResult rv;
+  nsCOMPtr global = do_QueryInterface(GetOwner());
+  nsRefPtr promise = Promise::Create(global, rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    return rv.StealNSResult();
+  }
+
+  // Check the single call or conference call
+  bool isSingleCall = mCalls.Length();
+  nsCOMPtr callback = new TelephonyCallback(promise);
+  if (isSingleCall) {
+    rv = aMuted ? mCalls[0]->Hold(callback) : mCalls[0]->Resume(callback);
+  } else {
+    rv = aMuted ? mGroup->Hold(callback) : mGroup->Resume(callback);
+  }
+  if (NS_WARN_IF(rv.Failed())) {
+    return rv.StealNSResult();
+  }
+
+  // These events will be triggered when the telephony is interrupted by other
+  // audio channel.
+  if (mMuted != aMuted) {
+    mMuted = aMuted;
+    // We should not dispatch "mozinterruptend" when the system app initializes
+    // the telephony audio from muted to unmuted at the first time. The event
+    // "mozinterruptend" must be dispatched after the "mozinterruptbegin".
+    if (!mHaveDispatchedInterruptBeginEvent && mMuted) {
+      DispatchTrustedEvent(NS_LITERAL_STRING("mozinterruptbegin"));
+      mHaveDispatchedInterruptBeginEvent = mMuted;
+    } else if (mHaveDispatchedInterruptBeginEvent && !mMuted) {
+      DispatchTrustedEvent(NS_LITERAL_STRING("mozinterruptend"));
+      mHaveDispatchedInterruptBeginEvent = mMuted;
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+Telephony::WindowAudioCaptureChanged()
+{
+  // Do nothing
+  return NS_OK;
+}
+
 // nsITelephonyListener
 
 NS_IMETHODIMP
 Telephony::CallStateChanged(uint32_t aLength, nsITelephonyCallInfo** aAllInfo)
 {
+  nsresult rv;
   for (uint32_t i = 0; i < aLength; ++i) {
-    HandleCallInfo(aAllInfo[i]);
+    rv = HandleCallInfo(aAllInfo[i]);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  rv = HandleAudioAgentState();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
   }
   return NS_OK;
 }
@@ -605,7 +732,8 @@ Telephony::CallStateChanged(uint32_t aLength, nsITelephonyCallInfo** aAllInfo)
 NS_IMETHODIMP
 Telephony::EnumerateCallState(nsITelephonyCallInfo* aInfo)
 {
-  return HandleCallInfo(aInfo);
+  uint32_t currentCallNum = 1;
+  return CallStateChanged(currentCallNum, &aInfo);
 }
 
 NS_IMETHODIMP
diff --git a/dom/telephony/Telephony.h b/dom/telephony/Telephony.h
index 0ef8ce27704d..bf99917facb9 100644
--- a/dom/telephony/Telephony.h
+++ b/dom/telephony/Telephony.h
@@ -11,6 +11,7 @@
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/telephony/TelephonyCommon.h"
 
+#include "nsIAudioChannelAgent.h"
 #include "nsITelephonyCallInfo.h"
 #include "nsITelephonyService.h"
 
@@ -31,6 +32,7 @@ class TelephonyDialCallback;
 class OwningTelephonyCallOrTelephonyCallGroup;
 
 class Telephony final : public DOMEventTargetHelper,
+                        public nsIAudioChannelAgentCallback,
                         private nsITelephonyListener
 {
   /**
@@ -44,6 +46,8 @@ class Telephony final : public DOMEventTargetHelper,
 
   friend class telephony::TelephonyDialCallback;
 
+  // The audio agent is needed to communicate with the audio channel service.
+  nsCOMPtr mAudioAgent;
   nsCOMPtr mService;
   nsRefPtr mListener;
 
@@ -54,8 +58,15 @@ class Telephony final : public DOMEventTargetHelper,
 
   nsRefPtr mReadyPromise;
 
+  bool mIsAudioStartPlaying;
+
+  uint32_t mAudioAgentNotify;
+  bool mHaveDispatchedInterruptBeginEvent;
+  bool mMuted;
+
 public:
   NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIAUDIOCHANNELAGENTCALLBACK
   NS_DECL_NSITELEPHONYLISTENER
   NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper)
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Telephony,
@@ -94,6 +105,15 @@ class Telephony final : public DOMEventTargetHelper,
   void
   StopTone(const Optional& aServiceId, ErrorResult& aRv);
 
+  // In the audio channel architecture, the system app needs to know the state
+  // of every audio channel, including the telephony. Therefore, when a
+  // telephony call is activated , the audio channel service would notify the
+  // system app about that. And we need a agent to communicate with the audio
+  // channel service. We would follow the call states to make a correct
+  // notification.
+  void
+  OwnAudioChannel(ErrorResult& aRv);
+
   bool
   GetMuted(ErrorResult& aRv) const;
 
@@ -213,6 +233,10 @@ class Telephony final : public DOMEventTargetHelper,
 
   nsresult
   HandleCallInfo(nsITelephonyCallInfo* aInfo);
+
+  // Check the call states to decide whether need to send the notificaiton.
+  nsresult
+  HandleAudioAgentState();
 };
 
 } // namespace dom
diff --git a/dom/telephony/TelephonyCall.cpp b/dom/telephony/TelephonyCall.cpp
index cfe87cbb6397..d1ab58f7d49f 100644
--- a/dom/telephony/TelephonyCall.cpp
+++ b/dom/telephony/TelephonyCall.cpp
@@ -324,69 +324,94 @@ TelephonyCall::Hold(ErrorResult& aRv)
     return nullptr;
   }
 
+  nsCOMPtr callback = new TelephonyCallback(promise);
+  aRv = Hold(callback);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return promise.forget();
+}
+
+already_AddRefed
+TelephonyCall::Resume(ErrorResult& aRv)
+{
+  nsRefPtr promise = CreatePromise(aRv);
+  if (!promise) {
+    return nullptr;
+  }
+
+  nsCOMPtr callback = new TelephonyCallback(promise);
+  aRv = Resume(callback);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return promise.forget();
+}
+
+nsresult
+TelephonyCall::Hold(nsITelephonyCallback* aCallback)
+{
   if (mCallState != nsITelephonyService::CALL_STATE_CONNECTED) {
     NS_WARNING(nsPrintfCString("Hold non-connected call is rejected!"
                                " (State: %u)", mCallState).get());
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return promise.forget();
+    aCallback->NotifyError(NS_LITERAL_STRING("InvalidStateError"));
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   if (mGroup) {
     NS_WARNING("Hold a call in conference is rejected!");
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return promise.forget();
+    aCallback->NotifyError(NS_LITERAL_STRING("InvalidStateError"));
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   if (!mSwitchable) {
     NS_WARNING("Hold a non-switchable call is rejected!");
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return promise.forget();
+    aCallback->NotifyError(NS_LITERAL_STRING("InvalidStateError"));
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
-  nsCOMPtr callback = new TelephonyCallback(promise);
-  aRv = mTelephony->Service()->HoldCall(mServiceId, mCallIndex, callback);
-  NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
+  nsresult rv = mTelephony->Service()->HoldCall(mServiceId, mCallIndex, aCallback);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   if (mSecondId) {
     // No state transition when we switch two numbers within one TelephonyCall
     // object. Otherwise, the state here will be inconsistent with the backend
     // RIL and will never be right.
-    return promise.forget();
+    return NS_OK;
   }
 
-  return promise.forget();
+  return NS_OK;
 }
 
-already_AddRefed
-TelephonyCall::Resume(ErrorResult& aRv)
+nsresult
+TelephonyCall::Resume(nsITelephonyCallback* aCallback)
 {
-  nsRefPtr promise = CreatePromise(aRv);
-  if (!promise) {
-    return nullptr;
-  }
-
   if (mCallState != nsITelephonyService::CALL_STATE_HELD) {
-    NS_WARNING(nsPrintfCString("Resume non-held call is rejected!"
-                               " (State: %u)", mCallState).get());
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return promise.forget();
+    NS_WARNING("Resume non-held call is rejected!");
+    aCallback->NotifyError(NS_LITERAL_STRING("InvalidStateError"));
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   if (mGroup) {
     NS_WARNING("Resume a call in conference is rejected!");
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return promise.forget();
+    aCallback->NotifyError(NS_LITERAL_STRING("InvalidStateError"));
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   if (!mSwitchable) {
     NS_WARNING("Resume a non-switchable call is rejected!");
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return promise.forget();
+    aCallback->NotifyError(NS_LITERAL_STRING("InvalidStateError"));
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
-  nsCOMPtr callback = new TelephonyCallback(promise);
-  aRv = mTelephony->Service()->ResumeCall(mServiceId, mCallIndex, callback);
-  NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
+  nsresult rv = mTelephony->Service()->ResumeCall(mServiceId, mCallIndex, aCallback);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
-  return promise.forget();
-}
+  return NS_OK;
+}
\ No newline at end of file
diff --git a/dom/telephony/TelephonyCall.h b/dom/telephony/TelephonyCall.h
index dbb53f444bb9..4d4bae031de2 100644
--- a/dom/telephony/TelephonyCall.h
+++ b/dom/telephony/TelephonyCall.h
@@ -12,6 +12,7 @@
 #include "mozilla/dom/TelephonyCallBinding.h"
 #include "mozilla/dom/TelephonyCallId.h"
 #include "mozilla/dom/telephony/TelephonyCommon.h"
+#include "nsITelephonyService.h"
 
 class nsPIDOMWindow;
 
@@ -185,6 +186,12 @@ class TelephonyCall final : public DOMEventTargetHelper
 
   ~TelephonyCall();
 
+  nsresult
+  Hold(nsITelephonyCallback* aCallback);
+
+  nsresult
+  Resume(nsITelephonyCallback* aCallback);
+
   void
   ChangeStateInternal(uint16_t aCallState, bool aFireEvents);
 
diff --git a/dom/telephony/TelephonyCallGroup.cpp b/dom/telephony/TelephonyCallGroup.cpp
index 541cc134a79b..addfe94cfa48 100644
--- a/dom/telephony/TelephonyCallGroup.cpp
+++ b/dom/telephony/TelephonyCallGroup.cpp
@@ -347,16 +347,12 @@ TelephonyCallGroup::Hold(ErrorResult& aRv)
     return nullptr;
   }
 
-  if (mCallState != nsITelephonyService::CALL_STATE_CONNECTED) {
-    NS_WARNING("Holding a non-connected call is rejected!");
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return promise.forget();
+  nsCOMPtr callback = new TelephonyCallback(promise);
+  aRv = Hold(callback);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
   }
 
-  nsCOMPtr callback = new TelephonyCallback(promise);
-  aRv = mTelephony->Service()->HoldConference(mCalls[0]->ServiceId(),
-                                              callback);
-  NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
   return promise.forget();
 }
 
@@ -370,15 +366,47 @@ TelephonyCallGroup::Resume(ErrorResult& aRv)
     return nullptr;
   }
 
+  nsCOMPtr callback = new TelephonyCallback(promise);
+  aRv = Resume(callback);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return promise.forget();
+}
+
+nsresult
+TelephonyCallGroup::Hold(nsITelephonyCallback* aCallback)
+{
+  if (mCallState != nsITelephonyService::CALL_STATE_CONNECTED) {
+    NS_WARNING("Holding a non-connected call is rejected!");
+    aCallback->NotifyError(NS_LITERAL_STRING("InvalidStateError"));
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  }
+
+  nsresult rv = mTelephony->Service()->HoldConference(mCalls[0]->ServiceId(),
+                                                      aCallback);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+TelephonyCallGroup::Resume(nsITelephonyCallback* aCallback)
+{
   if (mCallState != nsITelephonyService::CALL_STATE_HELD) {
     NS_WARNING("Resuming a non-held call is rejected!");
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return promise.forget();
+    aCallback->NotifyError(NS_LITERAL_STRING("InvalidStateError"));
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
-  nsCOMPtr callback = new TelephonyCallback(promise);
-  aRv = mTelephony->Service()->ResumeConference(mCalls[0]->ServiceId(),
-                                                callback);
-  NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
-  return promise.forget();
+  nsresult rv = mTelephony->Service()->ResumeConference(mCalls[0]->ServiceId(),
+                                                        aCallback);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
 }
diff --git a/dom/telephony/TelephonyCallGroup.h b/dom/telephony/TelephonyCallGroup.h
index 38c1ba1474cd..8e473b919951 100644
--- a/dom/telephony/TelephonyCallGroup.h
+++ b/dom/telephony/TelephonyCallGroup.h
@@ -30,6 +30,8 @@ class TelephonyCallGroup final : public DOMEventTargetHelper
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TelephonyCallGroup,
                                            DOMEventTargetHelper)
 
+  friend class Telephony;
+
   nsPIDOMWindow*
   GetParentObject() const
   {
@@ -108,6 +110,12 @@ class TelephonyCallGroup final : public DOMEventTargetHelper
   explicit TelephonyCallGroup(nsPIDOMWindow* aOwner);
   ~TelephonyCallGroup();
 
+  nsresult
+  Hold(nsITelephonyCallback* aCallback);
+
+  nsresult
+  Resume(nsITelephonyCallback* aCallback);
+
   nsresult
   NotifyCallsChanged(TelephonyCall* aCall);
 
diff --git a/dom/tests/mochitest/fetch/test_request.js b/dom/tests/mochitest/fetch/test_request.js
index 3c74a0c4bb9a..c2b3fffcc3c5 100644
--- a/dom/tests/mochitest/fetch/test_request.js
+++ b/dom/tests/mochitest/fetch/test_request.js
@@ -456,6 +456,17 @@ function testBug1154268() {
   });
 }
 
+function testRequestConsumedByFailedConstructor(){
+  var r1 = new Request('http://example.com', { method: 'POST', body: 'hello world' });
+  try{
+    var r2 = new Request(r1, { method: 'GET' });
+    ok(false, 'GET Request copied from POST Request with body should fail.');
+  } catch(e) {
+    ok(true, 'GET Request copied from POST Request with body should fail.');
+  }
+  ok(!r1.bodyUsed, 'Initial request should not be consumed by failed Request constructor');
+}
+
 function runTest() {
   testDefaultCtor();
   testSimpleUrlParse();
@@ -465,6 +476,7 @@ function runTest() {
   testHeaderGuard();
   testModeCorsPreflightEnumValue();
   testBug1154268();
+  testRequestConsumedByFailedConstructor();
 
   return Promise.resolve()
     .then(testBodyCreation)
diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html
index 352265c038c4..7aa79b57425c 100644
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -910,7 +910,7 @@
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PluginArray",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "PointerEvent", nightly: true, desktop: true},
+    {name: "PointerEvent", nightly: true, desktop: true, disabled: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PopStateEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
diff --git a/dom/webidl/ExtendableEvent.webidl b/dom/webidl/ExtendableEvent.webidl
index 47a192ba6ff8..01dc545efd4e 100644
--- a/dom/webidl/ExtendableEvent.webidl
+++ b/dom/webidl/ExtendableEvent.webidl
@@ -11,6 +11,7 @@
  Exposed=ServiceWorker]
 interface ExtendableEvent : Event {
   // https://github.com/slightlyoff/ServiceWorker/issues/261
+  [Throws]
   void waitUntil(Promise p);
 };
 
diff --git a/dom/webidl/PerformanceCompositeTiming.webidl b/dom/webidl/PerformanceCompositeTiming.webidl
new file mode 100644
index 000000000000..c7ccf10e4903
--- /dev/null
+++ b/dom/webidl/PerformanceCompositeTiming.webidl
@@ -0,0 +1,19 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://www.w3.org/TR/frame-timing/#performancecompositetiming
+ *
+ * Copyright © 2015 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+[Pref="dom.enable_frame_timing"]
+interface PerformanceCompositeTiming : PerformanceEntry
+{
+  readonly attribute unsigned long sourceFrameNumber;
+
+  jsonifier;
+};
diff --git a/dom/webidl/PerformanceRenderTiming.webidl b/dom/webidl/PerformanceRenderTiming.webidl
new file mode 100644
index 000000000000..4ea4ebdd559b
--- /dev/null
+++ b/dom/webidl/PerformanceRenderTiming.webidl
@@ -0,0 +1,19 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://www.w3.org/TR/frame-timing/#performancerendertiming
+ *
+ * Copyright © 2015 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+[Pref="dom.enable_frame_timing"]
+interface PerformanceRenderTiming : PerformanceEntry
+{
+  readonly attribute unsigned long sourceFrameNumber;
+
+  jsonifier;
+};
diff --git a/dom/webidl/ServiceWorkerRegistration.webidl b/dom/webidl/ServiceWorkerRegistration.webidl
index c82f217147bf..83ce3a6c3568 100644
--- a/dom/webidl/ServiceWorkerRegistration.webidl
+++ b/dom/webidl/ServiceWorkerRegistration.webidl
@@ -17,9 +17,10 @@ interface ServiceWorkerRegistration : EventTarget {
 
   readonly attribute USVString scope;
 
-  void update();
+  [Throws, NewObject]
+  Promise update();
 
-  [Throws]
+  [Throws, NewObject]
   Promise unregister();
 
   // event
diff --git a/dom/webidl/Telephony.webidl b/dom/webidl/Telephony.webidl
index 5eb460b74bbc..19fd9dd80c41 100644
--- a/dom/webidl/Telephony.webidl
+++ b/dom/webidl/Telephony.webidl
@@ -49,6 +49,12 @@ interface Telephony : EventTarget {
   [Throws]
   void stopTone(optional unsigned long serviceId);
 
+  // Calling this method, the app will be treated as owner of the telephony
+  // calls from the AudioChannel policy.
+  [Throws,
+   CheckAllPermissions="audio-channel-telephony"]
+  void ownAudioChannel();
+
   [Throws]
   attribute boolean muted;
 
diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build
index ebc780f40713..495557770e1c 100644
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -349,12 +349,14 @@ WEBIDL_FILES = [
     'PannerNode.webidl',
     'ParentNode.webidl',
     'Performance.webidl',
+    'PerformanceCompositeTiming.webidl',
     'PerformanceEntry.webidl',
     'PerformanceMark.webidl',
     'PerformanceMeasure.webidl',
     'PerformanceNavigation.webidl',
     'PerformanceObserver.webidl',
     'PerformanceObserverEntryList.webidl',
+    'PerformanceRenderTiming.webidl',
     'PerformanceResourceTiming.webidl',
     'PerformanceTiming.webidl',
     'PeriodicWave.webidl',
diff --git a/dom/workers/Performance.cpp b/dom/workers/Performance.cpp
index 25bc92ed2029..9590469b249e 100644
--- a/dom/workers/Performance.cpp
+++ b/dom/workers/Performance.cpp
@@ -68,8 +68,13 @@ void
 Performance::InsertUserEntry(PerformanceEntry* aEntry)
 {
   if (mWorkerPrivate->PerformanceLoggingEnabled()) {
-    PerformanceBase::LogEntry(aEntry,
-                              NS_ConvertUTF16toUTF8(mWorkerPrivate->ScriptURL()));
+    nsAutoCString uri;
+    nsCOMPtr scriptURI = mWorkerPrivate->GetResolvedScriptURI();
+    if (!scriptURI || NS_FAILED(scriptURI->GetHost(uri))) {
+      // If we have no URI, just put in "none".
+      uri.AssignLiteral("none");
+    }
+    PerformanceBase::LogEntry(aEntry, uri);
   }
   PerformanceBase::InsertUserEntry(aEntry);
 }
diff --git a/dom/workers/ServiceWorkerContainer.cpp b/dom/workers/ServiceWorkerContainer.cpp
index ee3faddb8077..7bf3cdc53c63 100644
--- a/dom/workers/ServiceWorkerContainer.cpp
+++ b/dom/workers/ServiceWorkerContainer.cpp
@@ -113,13 +113,32 @@ ServiceWorkerContainer::Register(const nsAString& aScriptURL,
     return nullptr;
   }
 
-  nsCOMPtr window = GetOwner();
-  MOZ_ASSERT(window);
+  nsCOMPtr baseURI;
+
+  nsIDocument* doc = GetEntryDocument();
+  if (doc) {
+    baseURI = doc->GetBaseURI();
+  } else {
+    // XXXnsm. One of our devtools browser test calls register() from a content
+    // script where there is no valid entry document. Use the window to resolve
+    // the uri in that case.
+    nsCOMPtr window = GetOwner();
+    nsCOMPtr outerWindow;
+    if (window && (outerWindow = window->GetOuterWindow()) &&
+        outerWindow->GetServiceWorkersTestingEnabled()) {
+      baseURI = window->GetDocBaseURI();
+    }
+  }
+
+
+  if (!baseURI) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
 
   nsresult rv;
   nsCOMPtr scriptURI;
-  rv = NS_NewURI(getter_AddRefs(scriptURI), aScriptURL, nullptr,
-                 window->GetDocBaseURI());
+  rv = NS_NewURI(getter_AddRefs(scriptURI), aScriptURL, nullptr, baseURI);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.ThrowTypeError(MSG_INVALID_URL, &aScriptURL);
     return nullptr;
@@ -137,23 +156,27 @@ ServiceWorkerContainer::Register(const nsAString& aScriptURL,
     if (NS_WARN_IF(NS_FAILED(rv))) {
       nsAutoCString spec;
       scriptURI->GetSpec(spec);
-      aRv.ThrowTypeError(MSG_INVALID_SCOPE, &defaultScope, &spec);
+      NS_ConvertUTF8toUTF16 wSpec(spec);
+      aRv.ThrowTypeError(MSG_INVALID_SCOPE, &defaultScope, &wSpec);
       return nullptr;
     }
   } else {
     // Step 5. Parse against entry settings object's base URL.
     rv = NS_NewURI(getter_AddRefs(scopeURI), aOptions.mScope.Value(),
-                   nullptr, window->GetDocBaseURI());
+                   nullptr, baseURI);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       nsAutoCString spec;
-      if (window->GetDocBaseURI()) {
-        window->GetDocBaseURI()->GetSpec(spec);
-      }
-      aRv.ThrowTypeError(MSG_INVALID_SCOPE, &aOptions.mScope.Value(), &spec);
+      baseURI->GetSpec(spec);
+      NS_ConvertUTF8toUTF16 wSpec(spec);
+      aRv.ThrowTypeError(MSG_INVALID_SCOPE, &aOptions.mScope.Value(), &wSpec);
       return nullptr;
     }
   }
 
+  // The spec says that the "client" passed to Register() must be the global
+  // where the ServiceWorkerContainer was retrieved from.
+  nsCOMPtr window = GetOwner();
+  MOZ_ASSERT(window);
   aRv = swm->Register(window, scopeURI, scriptURI, getter_AddRefs(promise));
   if (aRv.Failed()) {
     return nullptr;
diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp
index 75203a49f5bc..21321a59cc5d 100644
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -395,10 +395,15 @@ ExtendableEvent::ExtendableEvent(EventTarget* aOwner)
 }
 
 void
-ExtendableEvent::WaitUntil(Promise& aPromise)
+ExtendableEvent::WaitUntil(Promise& aPromise, ErrorResult& aRv)
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
+  if (EventPhase() == nsIDOMEvent::NONE) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
   // Only first caller counts.
   if (EventPhase() == AT_TARGET && !mPromise) {
     mPromise = &aPromise;
diff --git a/dom/workers/ServiceWorkerEvents.h b/dom/workers/ServiceWorkerEvents.h
index a9ca3c8534fd..0ffd9ef67e25 100644
--- a/dom/workers/ServiceWorkerEvents.h
+++ b/dom/workers/ServiceWorkerEvents.h
@@ -141,7 +141,7 @@ class ExtendableEvent : public Event
   }
 
   void
-  WaitUntil(Promise& aPromise);
+  WaitUntil(Promise& aPromise, ErrorResult& aRv);
 
   already_AddRefed
   GetPromise() const
diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
index 3511a7101ac7..3d637f954634 100644
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -7,7 +7,6 @@
 #include "ServiceWorkerManager.h"
 
 #include "mozIApplication.h"
-#include "mozIApplicationClearPrivateDataParams.h"
 #include "nsIAppsService.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDocument.h"
@@ -83,7 +82,7 @@ BEGIN_WORKERS_NAMESPACE
 
 #define PURGE_DOMAIN_DATA "browser:purge-domain-data"
 #define PURGE_SESSION_HISTORY "browser:purge-session-history"
-#define WEBAPPS_CLEAR_DATA "webapps-clear-data"
+#define CLEAR_ORIGIN_DATA "clear-origin-data"
 
 static_assert(nsIHttpChannelInternal::CORS_MODE_SAME_ORIGIN == static_cast(RequestMode::Same_origin),
               "RequestMode enumeration value should match Necko CORS mode value.");
@@ -431,7 +430,7 @@ ServiceWorkerManager::Init()
       MOZ_ASSERT(NS_SUCCEEDED(rv));
       rv = obs->AddObserver(this, PURGE_DOMAIN_DATA, false /* ownsWeak */);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
-      rv = obs->AddObserver(this, WEBAPPS_CLEAR_DATA, false /* ownsWeak */);
+      rv = obs->AddObserver(this, CLEAR_ORIGIN_DATA, false /* ownsWeak */);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
     }
   }
@@ -542,28 +541,6 @@ class LifecycleEventWorkerRunnable final : public WorkerRunnable
 
 };
 
-class ServiceWorkerUpdateFinishCallback
-{
-protected:
-  virtual ~ServiceWorkerUpdateFinishCallback()
-  { }
-
-public:
-  NS_INLINE_DECL_REFCOUNTING(ServiceWorkerUpdateFinishCallback)
-
-  virtual
-  void UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo)
-  { }
-
-  virtual
-  void UpdateFailed(nsresult aStatus)
-  { }
-
-  virtual
-  void UpdateFailed(const ErrorEventInit& aDesc)
-  { }
-};
-
 class ServiceWorkerResolveWindowPromiseOnUpdateCallback final : public ServiceWorkerUpdateFinishCallback
 {
   nsRefPtr mWindow;
@@ -4175,7 +4152,8 @@ ServiceWorkerManager::InvalidateServiceWorkerRegistrationWorker(ServiceWorkerReg
 
 void
 ServiceWorkerManager::SoftUpdate(nsIPrincipal* aPrincipal,
-                                 const nsACString& aScope)
+                                 const nsACString& aScope,
+                                 ServiceWorkerUpdateFinishCallback* aCallback)
 {
   MOZ_ASSERT(aPrincipal);
 
@@ -4185,21 +4163,23 @@ ServiceWorkerManager::SoftUpdate(nsIPrincipal* aPrincipal,
     return;
   }
 
-  SoftUpdate(scopeKey, aScope);
+  SoftUpdate(scopeKey, aScope, aCallback);
 }
 
 void
 ServiceWorkerManager::SoftUpdate(const OriginAttributes& aOriginAttributes,
-                                 const nsACString& aScope)
+                                 const nsACString& aScope,
+                                 ServiceWorkerUpdateFinishCallback* aCallback)
 {
   nsAutoCString scopeKey;
   aOriginAttributes.CreateSuffix(scopeKey);
-  SoftUpdate(scopeKey, aScope);
+  SoftUpdate(scopeKey, aScope, aCallback);
 }
 
 void
 ServiceWorkerManager::SoftUpdate(const nsACString& aScopeKey,
-                                 const nsACString& aScope)
+                                 const nsACString& aScope,
+                                 ServiceWorkerUpdateFinishCallback* aCallback)
 {
   nsRefPtr registration =
     GetRegistration(aScopeKey, aScope);
@@ -4232,8 +4212,10 @@ ServiceWorkerManager::SoftUpdate(const nsACString& aScopeKey,
     GetOrCreateJobQueue(aScopeKey, aScope);
   MOZ_ASSERT(queue);
 
-  nsRefPtr cb =
-    new ServiceWorkerUpdateFinishCallback();
+  nsRefPtr cb(aCallback);
+  if (!cb) {
+    cb = new ServiceWorkerUpdateFinishCallback();
+  }
 
   // "Invoke Update algorithm, or its equivalent, with client, registration as
   // its argument."
@@ -4663,46 +4645,36 @@ UnregisterIfMatchesHostPerPrincipal(const nsACString& aKey,
 }
 
 PLDHashOperator
-UnregisterIfMatchesClearPrivateDataParams(const nsACString& aScope,
-                                          ServiceWorkerRegistrationInfo* aReg,
-                                          void* aPtr)
+UnregisterIfMatchesClearOriginParams(const nsACString& aScope,
+                                     ServiceWorkerRegistrationInfo* aReg,
+                                     void* aPtr)
 {
   UnregisterIfMatchesUserData* data =
     static_cast(aPtr);
+  MOZ_ASSERT(data);
 
   if (data->mUserData) {
-    mozIApplicationClearPrivateDataParams *params =
-      static_cast(data->mUserData);
+    OriginAttributes* params = static_cast(data->mUserData);
     MOZ_ASSERT(params);
+    MOZ_ASSERT(aReg);
     MOZ_ASSERT(aReg->mPrincipal);
 
-    uint32_t appId;
-    nsresult rv = params->GetAppId(&appId);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return PL_DHASH_NEXT;
-    }
-
-    bool browserOnly;
-    rv = params->GetBrowserOnly(&browserOnly);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return PL_DHASH_NEXT;
-    }
-
     bool equals = false;
 
-    if (browserOnly) {
+    if (params->mInBrowser) {
       // When we do a system wide "clear cookies and stored data" on B2G we get
-      // the "webapps-clear-data" notification with the System app appID and
+      // the "clear-origin-data" notification with the System app appID and
       // the browserOnly flag set to true.
       // Web sites registering a service worker on B2G have a principal with the
       // following information: web site origin + System app appId + inBrowser=1
       // So we need to check if the service worker registration info contains
       // the System app appID and the enabled inBrowser flag and in that case
       // remove it from the registry.
-      equals = (appId == aReg->mPrincipal->GetAppId()) &&
-               aReg->mPrincipal->GetIsInBrowserElement();
+      OriginAttributes attrs =
+        mozilla::BasePrincipal::Cast(aReg->mPrincipal)->OriginAttributesRef();
+      equals = attrs == *params;
     } else {
-      // If we get the "webapps-clear-data" notification because of an app
+      // If we get the "clear-origin-data" notification because of an app
       // uninstallation, we need to check the full principal to get the match
       // in the service workers registry. If we find a match, we unregister the
       // worker.
@@ -4713,7 +4685,7 @@ UnregisterIfMatchesClearPrivateDataParams(const nsACString& aScope,
       }
 
       nsCOMPtr app;
-      appsService->GetAppByLocalId(appId, getter_AddRefs(app));
+      appsService->GetAppByLocalId(params->mAppId, getter_AddRefs(app));
       if (NS_WARN_IF(!app)) {
         return PL_DHASH_NEXT;
       }
@@ -4737,7 +4709,7 @@ UnregisterIfMatchesClearPrivateDataParams(const nsACString& aScope,
 }
 
 PLDHashOperator
-UnregisterIfMatchesClearPrivateDataParams(const nsACString& aKey,
+UnregisterIfMatchesClearOriginParams(const nsACString& aKey,
                              ServiceWorkerManager::RegistrationDataPerPrincipal* aData,
                              void* aUserData)
 {
@@ -4745,7 +4717,7 @@ UnregisterIfMatchesClearPrivateDataParams(const nsACString& aKey,
   // We can use EnumerateRead because ForceUnregister (and Unregister) are async.
   // Otherwise doing some R/W operations on an hashtable during an EnumerateRead
   // will crash.
-  aData->mInfos.EnumerateRead(UnregisterIfMatchesClearPrivateDataParams,
+  aData->mInfos.EnumerateRead(UnregisterIfMatchesClearOriginParams,
                               &data);
   return PL_DHASH_NEXT;
 }
@@ -4963,14 +4935,13 @@ UpdateEachRegistration(const nsACString& aKey,
 }
 
 void
-ServiceWorkerManager::RemoveAllRegistrations(
-    mozIApplicationClearPrivateDataParams* aParams)
+ServiceWorkerManager::RemoveAllRegistrations(OriginAttributes* aParams)
 {
   AssertIsOnMainThread();
 
   MOZ_ASSERT(aParams);
 
-  mRegistrationInfos.EnumerateRead(UnregisterIfMatchesClearPrivateDataParams,
+  mRegistrationInfos.EnumerateRead(UnregisterIfMatchesClearOriginParams,
                                    aParams);
 }
 
@@ -5011,15 +4982,12 @@ ServiceWorkerManager::Observe(nsISupports* aSubject,
     return NS_OK;
   }
 
-  if (strcmp(aTopic, WEBAPPS_CLEAR_DATA) == 0) {
+  if (strcmp(aTopic, CLEAR_ORIGIN_DATA) == 0) {
     MOZ_ASSERT(XRE_IsParentProcess());
-    nsCOMPtr params =
-      do_QueryInterface(aSubject);
-    if (NS_WARN_IF(!params)) {
-      return NS_OK;
-    }
+    OriginAttributes attrs;
+    MOZ_ALWAYS_TRUE(attrs.Init(nsAutoString(aData)));
 
-    RemoveAllRegistrations(params);
+    RemoveAllRegistrations(&attrs);
     return NS_OK;
   }
 
@@ -5033,7 +5001,7 @@ ServiceWorkerManager::Observe(nsISupports* aSubject,
       if (XRE_IsParentProcess()) {
         obs->RemoveObserver(this, PURGE_SESSION_HISTORY);
         obs->RemoveObserver(this, PURGE_DOMAIN_DATA);
-        obs->RemoveObserver(this, WEBAPPS_CLEAR_DATA);
+        obs->RemoveObserver(this, CLEAR_ORIGIN_DATA);
       }
     }
 
diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h
index a9538389f062..d9c6f74dad43 100644
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -130,6 +130,28 @@ class ServiceWorkerRegistrationInfo final : public nsISupports
 
 };
 
+class ServiceWorkerUpdateFinishCallback
+{
+protected:
+  virtual ~ServiceWorkerUpdateFinishCallback()
+  { }
+
+public:
+  NS_INLINE_DECL_REFCOUNTING(ServiceWorkerUpdateFinishCallback)
+
+  virtual
+  void UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo)
+  { }
+
+  virtual
+  void UpdateFailed(nsresult aStatus)
+  { }
+
+  virtual
+  void UpdateFailed(const ErrorEventInit& aDesc)
+  { }
+};
+
 /*
  * Wherever the spec treats a worker instance and a description of said worker
  * as the same thing; i.e. "Resolve foo with
@@ -301,10 +323,14 @@ class ServiceWorkerManager final
                      ErrorResult& aRv);
 
   void
-  SoftUpdate(nsIPrincipal* aPrincipal, const nsACString& aScope);
+  SoftUpdate(nsIPrincipal* aPrincipal,
+             const nsACString& aScope,
+             ServiceWorkerUpdateFinishCallback* aCallback = nullptr);
 
   void
-  SoftUpdate(const OriginAttributes& aOriginAttributes, const nsACString& aScope);
+  SoftUpdate(const OriginAttributes& aOriginAttributes,
+             const nsACString& aScope,
+             ServiceWorkerUpdateFinishCallback* aCallback = nullptr);
 
   void
   PropagateSoftUpdate(const OriginAttributes& aOriginAttributes,
@@ -404,7 +430,9 @@ class ServiceWorkerManager final
   MaybeRemoveRegistrationInfo(const nsACString& aScopeKey);
 
   void
-  SoftUpdate(const nsACString& aScopeKey, const nsACString& aScope);
+  SoftUpdate(const nsACString& aScopeKey,
+             const nsACString& aScope,
+             ServiceWorkerUpdateFinishCallback* aCallback = nullptr);
 
   already_AddRefed
   GetRegistration(const nsACString& aScopeKey,
@@ -552,7 +580,7 @@ class ServiceWorkerManager final
   // Removes all service worker registrations that matches the given
   // mozIApplicationClearPrivateDataParams.
   void
-  RemoveAllRegistrations(mozIApplicationClearPrivateDataParams* aParams);
+  RemoveAllRegistrations(OriginAttributes* aParams);
 
   nsRefPtr mActor;
 
diff --git a/dom/workers/ServiceWorkerManagerChild.cpp b/dom/workers/ServiceWorkerManagerChild.cpp
index 1fa263d30cf0..5dbc97258af5 100644
--- a/dom/workers/ServiceWorkerManagerChild.cpp
+++ b/dom/workers/ServiceWorkerManagerChild.cpp
@@ -42,7 +42,7 @@ ServiceWorkerManagerChild::RecvNotifySoftUpdate(
   nsRefPtr swm = ServiceWorkerManager::GetInstance();
   MOZ_ASSERT(swm);
 
-  swm->SoftUpdate(aOriginAttributes, NS_ConvertUTF16toUTF8(aScope));
+  swm->SoftUpdate(aOriginAttributes, NS_ConvertUTF16toUTF8(aScope), nullptr);
   return true;
 }
 
diff --git a/dom/workers/ServiceWorkerRegistrar.cpp b/dom/workers/ServiceWorkerRegistrar.cpp
index ccef21f565da..a215d220960a 100644
--- a/dom/workers/ServiceWorkerRegistrar.cpp
+++ b/dom/workers/ServiceWorkerRegistrar.cpp
@@ -322,25 +322,17 @@ ServiceWorkerRegistrar::ReadData()
       return NS_ERROR_FAILURE;                        \
     }
 
-    GET_LINE(line);
-
-    uint32_t appId = line.ToInteger(&rv);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    GET_LINE(line);
+    nsAutoCString suffix;
+    GET_LINE(suffix);
 
-    if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
-        !line.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
-      return NS_ERROR_FAILURE;
+    OriginAttributes attrs;
+    if (!attrs.PopulateFromSuffix(suffix)) {
+      return NS_ERROR_INVALID_ARG;
     }
 
-    bool isInBrowserElement = line.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
-
     GET_LINE(line);
     entry->principal() =
-      mozilla::ipc::ContentPrincipalInfo(appId, isInBrowserElement, line);
+      mozilla::ipc::ContentPrincipalInfo(attrs.mAppId, attrs.mInBrowser, line);
 
     GET_LINE(entry->scope());
     GET_LINE(entry->scriptSpec());
@@ -555,19 +547,15 @@ ServiceWorkerRegistrar::WriteData()
     const mozilla::ipc::ContentPrincipalInfo& cInfo =
       info.get_ContentPrincipalInfo();
 
+    OriginAttributes attrs(cInfo.appId(), cInfo.isInBrowserElement());
+    nsAutoCString suffix;
+    attrs.CreateSuffix(suffix);
+
     buffer.Truncate();
-    buffer.AppendInt(cInfo.appId());
+    buffer.Append(suffix.get());
     buffer.Append('\n');
 
-    if (cInfo.isInBrowserElement()) {
-      buffer.AppendLiteral(SERVICEWORKERREGISTRAR_TRUE);
-    } else {
-      buffer.AppendLiteral(SERVICEWORKERREGISTRAR_FALSE);
-    }
-
-    buffer.Append('\n');
     buffer.Append(cInfo.spec());
-
     buffer.Append('\n');
 
     buffer.Append(data[i].scope());
diff --git a/dom/workers/ServiceWorkerRegistrar.h b/dom/workers/ServiceWorkerRegistrar.h
index fcdc3b981566..13f0e471d7ed 100644
--- a/dom/workers/ServiceWorkerRegistrar.h
+++ b/dom/workers/ServiceWorkerRegistrar.h
@@ -16,7 +16,7 @@
 #include "nsTArray.h"
 
 #define SERVICEWORKERREGISTRAR_FILE "serviceworker.txt"
-#define SERVICEWORKERREGISTRAR_VERSION "1"
+#define SERVICEWORKERREGISTRAR_VERSION "2"
 #define SERVICEWORKERREGISTRAR_TERMINATOR "#"
 #define SERVICEWORKERREGISTRAR_TRUE "true"
 #define SERVICEWORKERREGISTRAR_FALSE "false"
diff --git a/dom/workers/ServiceWorkerRegistration.cpp b/dom/workers/ServiceWorkerRegistration.cpp
index afb74a6603a3..d51e0e42d900 100644
--- a/dom/workers/ServiceWorkerRegistration.cpp
+++ b/dom/workers/ServiceWorkerRegistration.cpp
@@ -239,103 +239,167 @@ ServiceWorkerRegistrationMainThread::InvalidateWorkers(WhichServiceWorker aWhich
 namespace {
 
 void
-UpdateInternal(nsIPrincipal* aPrincipal, const nsAString& aScope)
+UpdateInternal(nsIPrincipal* aPrincipal,
+               const nsAString& aScope,
+               ServiceWorkerUpdateFinishCallback* aCallback)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aPrincipal);
+  MOZ_ASSERT(aCallback);
 
   nsRefPtr swm = ServiceWorkerManager::GetInstance();
   MOZ_ASSERT(swm);
 
   // The spec defines ServiceWorkerRegistration.update() exactly as Soft Update.
-  swm->SoftUpdate(aPrincipal, NS_ConvertUTF16toUTF8(aScope));
+  swm->SoftUpdate(aPrincipal, NS_ConvertUTF16toUTF8(aScope), aCallback);
 }
 
-// This Runnable needs to have a valid WorkerPrivate. For this reason it is also
-// a WorkerFeature that is registered before dispatching itself to the
-// main-thread and it's removed with ReleaseRunnable when the operation is
-// completed. This will keep the worker alive as long as necessary.
-class UpdateRunnable final : public nsRunnable
-                           , public WorkerFeature
+class MainThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallback
 {
-public:
-  UpdateRunnable(WorkerPrivate* aWorkerPrivate, const nsAString& aScope)
-    : mWorkerPrivate(aWorkerPrivate)
-    , mScope(aScope)
-  {}
+  nsRefPtr mPromise;
 
-  NS_IMETHOD
-  Run() override
+  ~MainThreadUpdateCallback()
+  { }
+
+public:
+  explicit MainThreadUpdateCallback(Promise* aPromise)
+    : mPromise(aPromise)
   {
     AssertIsOnMainThread();
-    UpdateInternal(mWorkerPrivate->GetPrincipal(), mScope);
+  }
 
-    class ReleaseRunnable final : public MainThreadWorkerControlRunnable
-    {
-      nsRefPtr mFeature;
-
-    public:
-      ReleaseRunnable(WorkerPrivate* aWorkerPrivate,
-                      UpdateRunnable* aFeature)
-        : MainThreadWorkerControlRunnable(aWorkerPrivate)
-        , mFeature(aFeature)
-      {
-        MOZ_ASSERT(aFeature);
-      }
+  void
+  UpdateSucceeded(ServiceWorkerRegistrationInfo* aRegistration) override
+  {
+    mPromise->MaybeResolve(JS::UndefinedHandleValue);
+  }
 
-      virtual bool
-      WorkerRun(JSContext* aCx,
-                workers::WorkerPrivate* aWorkerPrivate) override
-      {
-        MOZ_ASSERT(aWorkerPrivate);
-        aWorkerPrivate->AssertIsOnWorkerThread();
+  using ServiceWorkerUpdateFinishCallback::UpdateFailed;
 
-        aWorkerPrivate->RemoveFeature(aCx, mFeature);
-        return true;
-      }
+  void
+  UpdateFailed(nsresult aStatus) override
+  {
+    mPromise->MaybeReject(aStatus);
+  }
+};
 
-    private:
-      ~ReleaseRunnable()
-      {}
-    };
+class UpdateResultRunnable final : public WorkerRunnable
+{
+  nsRefPtr mPromiseProxy;
+  nsresult mStatus;
 
-    nsRefPtr runnable =
-      new ReleaseRunnable(mWorkerPrivate, this);
-    runnable->Dispatch(nullptr);
+  ~UpdateResultRunnable()
+  {}
 
-    return NS_OK;
-  }
+public:
+  UpdateResultRunnable(PromiseWorkerProxy* aPromiseProxy, nsresult aStatus)
+    : WorkerRunnable(aPromiseProxy->GetWorkerPrivate(), WorkerThreadModifyBusyCount)
+    , mPromiseProxy(aPromiseProxy)
+    , mStatus(aStatus)
+  { }
 
-  virtual bool Notify(JSContext* aCx, workers::Status aStatus) override
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
-    // We don't care about the notification. We just want to keep the
-    // mWorkerPrivate alive.
+    Promise* promise = mPromiseProxy->GetWorkerPromise();
+    if (NS_SUCCEEDED(mStatus)) {
+      promise->MaybeResolve(JS::UndefinedHandleValue);
+    } else {
+      promise->MaybeReject(mStatus);
+    }
+    mPromiseProxy->CleanUp(aCx);
     return true;
   }
+};
 
-  bool
-  Dispatch()
+class WorkerThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallback
+{
+  nsRefPtr mPromiseProxy;
+
+  ~WorkerThreadUpdateCallback()
   {
-    mWorkerPrivate->AssertIsOnWorkerThread();
+    Finish(NS_ERROR_FAILURE);
+  }
+
+public:
+  explicit WorkerThreadUpdateCallback(PromiseWorkerProxy* aPromiseProxy)
+    : mPromiseProxy(aPromiseProxy)
+  {
+    AssertIsOnMainThread();
+  }
+
+  void
+  UpdateSucceeded(ServiceWorkerRegistrationInfo* aRegistration) override
+  {
+    Finish(NS_OK);
+  }
+
+  using ServiceWorkerUpdateFinishCallback::UpdateFailed;
+
+  void
+  UpdateFailed(nsresult aStatus) override
+  {
+    Finish(aStatus);
+  }
+
+  void
+  Finish(nsresult aStatus)
+  {
+    if (!mPromiseProxy) {
+      return;
+    }
 
-    JSContext* cx = mWorkerPrivate->GetJSContext();
+    nsRefPtr proxy = mPromiseProxy.forget();
 
-    if (NS_WARN_IF(!mWorkerPrivate->AddFeature(cx, this))) {
-      return false;
+    MutexAutoLock lock(proxy->GetCleanUpLock());
+    if (proxy->IsClean()) {
+      return;
     }
 
-    nsCOMPtr that(this);
-    if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(this)))) {
-      NS_ASSERTION(false, "Failed to dispatch update back to MainThread in ServiceWorker");
+    AutoJSAPI jsapi;
+    jsapi.Init();
+
+    nsRefPtr r =
+      new UpdateResultRunnable(proxy, aStatus);
+    if (!r->Dispatch(jsapi.cx())) {
+      nsRefPtr r =
+        new PromiseWorkerProxyControlRunnable(proxy->GetWorkerPrivate(), proxy);
+      r->Dispatch(jsapi.cx());
     }
-    return true;
+  }
+};
+
+class UpdateRunnable final : public nsRunnable
+{
+public:
+  UpdateRunnable(PromiseWorkerProxy* aPromiseProxy,
+                 const nsAString& aScope)
+    : mPromiseProxy(aPromiseProxy)
+    , mScope(aScope)
+  {}
+
+  NS_IMETHOD
+  Run() override
+  {
+    AssertIsOnMainThread();
+    ErrorResult result;
+
+    MutexAutoLock lock(mPromiseProxy->GetCleanUpLock());
+    if (mPromiseProxy->IsClean()) {
+      return NS_OK;
+    }
+
+    nsRefPtr cb =
+      new WorkerThreadUpdateCallback(mPromiseProxy);
+    UpdateInternal(mPromiseProxy->GetWorkerPrivate()->GetPrincipal(), mScope, cb);
+    return NS_OK;
   }
 
 private:
   ~UpdateRunnable()
   {}
 
-  WorkerPrivate* mWorkerPrivate;
+  nsRefPtr mPromiseProxy;
   const nsString mScope;
 };
 
@@ -525,13 +589,29 @@ class StartUnregisterRunnable final : public nsRunnable
 };
 } // namespace
 
-void
-ServiceWorkerRegistrationMainThread::Update()
+already_AddRefed
+ServiceWorkerRegistrationMainThread::Update(ErrorResult& aRv)
 {
+  AssertIsOnMainThread();
+  nsCOMPtr go = do_QueryInterface(GetOwner());
+  if (!go) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  nsRefPtr promise = Promise::Create(go, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
   nsCOMPtr doc = GetOwner()->GetExtantDoc();
   MOZ_ASSERT(doc);
 
-  UpdateInternal(doc->NodePrincipal(), mScope);
+  nsRefPtr cb =
+    new MainThreadUpdateCallback(promise);
+  UpdateInternal(doc->NodePrincipal(), mScope, cb);
+
+  return promise.forget();
 }
 
 already_AddRefed
@@ -868,17 +948,28 @@ ServiceWorkerRegistrationWorkerThread::GetActive()
   return nullptr;
 }
 
-void
-ServiceWorkerRegistrationWorkerThread::Update()
+already_AddRefed
+ServiceWorkerRegistrationWorkerThread::Update(ErrorResult& aRv)
 {
   WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(worker);
   worker->AssertIsOnWorkerThread();
 
-  // XXX: this pattern guarantees we won't know which thread UpdateRunnable
-  // will die on (here or MainThread)
-  nsRefPtr r = new UpdateRunnable(worker, mScope);
-  r->Dispatch();
+  nsRefPtr promise = Promise::Create(worker->GlobalScope(), aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  nsRefPtr proxy = PromiseWorkerProxy::Create(worker, promise);
+  if (!proxy) {
+    promise->MaybeResolve(NS_ERROR_DOM_ABORT_ERR);
+    return promise.forget();
+  }
+
+  nsRefPtr r = new UpdateRunnable(proxy, mScope);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
+
+  return promise.forget();
 }
 
 already_AddRefed
diff --git a/dom/workers/ServiceWorkerRegistration.h b/dom/workers/ServiceWorkerRegistration.h
index 72abe132e04e..a986ca58de26 100644
--- a/dom/workers/ServiceWorkerRegistration.h
+++ b/dom/workers/ServiceWorkerRegistration.h
@@ -106,8 +106,8 @@ class ServiceWorkerRegistrationMainThread final : public ServiceWorkerRegistrati
   ServiceWorkerRegistrationMainThread(nsPIDOMWindow* aWindow,
                                       const nsAString& aScope);
 
-  void
-  Update();
+  already_AddRefed
+  Update(ErrorResult& aRv);
 
   already_AddRefed
   Unregister(ErrorResult& aRv);
@@ -195,8 +195,8 @@ class ServiceWorkerRegistrationWorkerThread final : public ServiceWorkerRegistra
   ServiceWorkerRegistrationWorkerThread(workers::WorkerPrivate* aWorkerPrivate,
                                         const nsAString& aScope);
 
-  void
-  Update();
+  already_AddRefed
+  Update(ErrorResult& aRv);
 
   already_AddRefed
   Unregister(ErrorResult& aRv);
diff --git a/dom/workers/test/gtest/TestReadWrite.cpp b/dom/workers/test/gtest/TestReadWrite.cpp
index 38ad9aa64abb..9955dec89527 100644
--- a/dom/workers/test/gtest/TestReadWrite.cpp
+++ b/dom/workers/test/gtest/TestReadWrite.cpp
@@ -5,6 +5,7 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gtest/gtest.h"
+#include "mozilla/BasePrincipal.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/ServiceWorkerRegistrarTypes.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
@@ -140,11 +141,11 @@ TEST(ServiceWorkerRegistrar, TestReadData)
 {
   nsAutoCString buffer(SERVICEWORKERREGISTRAR_VERSION "\n");
 
-  buffer.Append("123\n" SERVICEWORKERREGISTRAR_TRUE "\n");
+  buffer.Append("^appId=123&inBrowser=1\n");
   buffer.Append("spec 0\nscope 0\nscriptSpec 0\ncurrentWorkerURL 0\nactiveCache 0\nwaitingCache 0\n");
   buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n");
 
-  buffer.Append("0\n" SERVICEWORKERREGISTRAR_FALSE "\n");
+  buffer.Append("\n");
   buffer.Append("spec 1\nscope 1\nscriptSpec 1\ncurrentWorkerURL 1\nactiveCache 1\nwaitingCache 1\n");
   buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n");
 
@@ -162,8 +163,11 @@ TEST(ServiceWorkerRegistrar, TestReadData)
   ASSERT_EQ(info0.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content";
   const mozilla::ipc::ContentPrincipalInfo& cInfo0 = data[0].principal();
 
-  ASSERT_EQ((uint32_t)123, cInfo0.appId());
-  ASSERT_EQ((uint32_t)true, cInfo0.isInBrowserElement());
+  mozilla::OriginAttributes attrs0(cInfo0.appId(), cInfo0.isInBrowserElement());
+  nsAutoCString suffix0;
+  attrs0.CreateSuffix(suffix0);
+
+  ASSERT_STREQ("^appId=123&inBrowser=1", suffix0.get());
   ASSERT_STREQ("spec 0", cInfo0.spec().get());
   ASSERT_STREQ("scope 0", data[0].scope().get());
   ASSERT_STREQ("scriptSpec 0", data[0].scriptSpec().get());
@@ -175,8 +179,11 @@ TEST(ServiceWorkerRegistrar, TestReadData)
   ASSERT_EQ(info1.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content";
   const mozilla::ipc::ContentPrincipalInfo& cInfo1 = data[1].principal();
 
-  ASSERT_EQ((uint32_t)0, cInfo1.appId());
-  ASSERT_EQ((uint32_t)false, cInfo1.isInBrowserElement());
+  mozilla::OriginAttributes attrs1(cInfo1.appId(), cInfo1.isInBrowserElement());
+  nsAutoCString suffix1;
+  attrs1.CreateSuffix(suffix1);
+
+  ASSERT_STREQ("", suffix1.get());
   ASSERT_STREQ("spec 1", cInfo1.spec().get());
   ASSERT_STREQ("scope 1", data[1].scope().get());
   ASSERT_STREQ("scriptSpec 1", data[1].scriptSpec().get());
diff --git a/dom/workers/test/serviceworkers/test_workerUpdate.html b/dom/workers/test/serviceworkers/test_workerUpdate.html
index eeccbc5d56af..a27e9cad6133 100644
--- a/dom/workers/test/serviceworkers/test_workerUpdate.html
+++ b/dom/workers/test/serviceworkers/test_workerUpdate.html
@@ -23,6 +23,9 @@
         if (e.data === "FINISH") {
           ok(true, "The worker has updated itself");
           resolve();
+        } else if (e.data === "FAIL") {
+          ok(false, "The worker failed to update itself");
+          resolve();
         }
       }
     });
diff --git a/dom/workers/test/serviceworkers/worker_update.js b/dom/workers/test/serviceworkers/worker_update.js
index 8d1ce1c61096..9f3e55b18a3b 100644
--- a/dom/workers/test/serviceworkers/worker_update.js
+++ b/dom/workers/test/serviceworkers/worker_update.js
@@ -2,13 +2,18 @@
 // job queueing works properly when called from the worker thread. We should
 // test actual update scenarios with a SJS test.
 onmessage = function(e) {
-  self.registration.update();
-  clients.matchAll().then(function(c) {
-    if (c.length == 0) {
-      // We cannot proceed.
-      return;
-    }
+  self.registration.update().then(function(v) {
+    return v === undefined ? 'FINISH' : 'FAIL';
+  }).catch(function(e) {
+    return 'FAIL';
+  }).then(function(result) {
+    clients.matchAll().then(function(c) {
+      if (c.length == 0) {
+        dump("!!!!!!!!!!! WORKER HAS NO CLIENTS TO FINISH TEST !!!!!!!!!!!!\n");
+        return;
+      }
 
-    c[0].postMessage('FINISH');
+      c[0].postMessage(result);
+    });
   });
 }
diff --git a/dom/xul/nsXULElement.cpp b/dom/xul/nsXULElement.cpp
index 3abc01943be6..736ef6086717 100644
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -1265,18 +1265,18 @@ nsXULElement::PreHandleEvent(EventChainPreVisitor& aVisitor)
     aVisitor.mForceContentDispatch = true; //FIXME! Bug 329119
     if (IsRootOfNativeAnonymousSubtree() &&
         (IsAnyOfXULElements(nsGkAtoms::scrollbar, nsGkAtoms::scrollcorner)) &&
-        (aVisitor.mEvent->message == NS_MOUSE_CLICK ||
-         aVisitor.mEvent->message == NS_MOUSE_DOUBLECLICK ||
-         aVisitor.mEvent->message == NS_XUL_COMMAND ||
-         aVisitor.mEvent->message == NS_CONTEXTMENU ||
-         aVisitor.mEvent->message == NS_DRAGDROP_START ||
-         aVisitor.mEvent->message == NS_DRAGDROP_GESTURE)) {
+        (aVisitor.mEvent->mMessage == NS_MOUSE_CLICK ||
+         aVisitor.mEvent->mMessage == NS_MOUSE_DOUBLECLICK ||
+         aVisitor.mEvent->mMessage == NS_XUL_COMMAND ||
+         aVisitor.mEvent->mMessage == NS_CONTEXTMENU ||
+         aVisitor.mEvent->mMessage == NS_DRAGDROP_START ||
+         aVisitor.mEvent->mMessage == NS_DRAGDROP_GESTURE)) {
         // Don't propagate these events from native anonymous scrollbar.
         aVisitor.mCanHandle = true;
         aVisitor.mParentTarget = nullptr;
         return NS_OK;
     }
-    if (aVisitor.mEvent->message == NS_XUL_COMMAND &&
+    if (aVisitor.mEvent->mMessage == NS_XUL_COMMAND &&
         aVisitor.mEvent->mClass == eInputEventClass &&
         aVisitor.mEvent->originalTarget == static_cast(this) &&
         !IsXULElement(nsGkAtoms::command)) {
diff --git a/editor/libeditor/nsEditor.cpp b/editor/libeditor/nsEditor.cpp
index 6792c47d7f24..774ec140a724 100644
--- a/editor/libeditor/nsEditor.cpp
+++ b/editor/libeditor/nsEditor.cpp
@@ -4695,7 +4695,7 @@ nsEditor::HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent)
   WidgetKeyboardEvent* nativeKeyEvent =
     aKeyEvent->GetInternalNSEvent()->AsKeyboardEvent();
   NS_ENSURE_TRUE(nativeKeyEvent, NS_ERROR_UNEXPECTED);
-  NS_ASSERTION(nativeKeyEvent->message == NS_KEY_PRESS,
+  NS_ASSERTION(nativeKeyEvent->mMessage == NS_KEY_PRESS,
                "HandleKeyPressEvent gets non-keypress event");
 
   // if we are readonly or disabled, then do nothing.
@@ -5151,7 +5151,7 @@ nsEditor::IsAcceptableInputEvent(nsIDOMEvent* aEvent)
   // strange event order.
   bool needsWidget = false;
   WidgetGUIEvent* widgetGUIEvent = nullptr;
-  switch (widgetEvent->message) {
+  switch (widgetEvent->mMessage) {
     case NS_USER_DEFINED_EVENT:
       // If events are not created with proper event interface, their message
       // are initialized with NS_USER_DEFINED_EVENT.  Let's ignore such event.
diff --git a/editor/libeditor/nsEditorEventListener.cpp b/editor/libeditor/nsEditorEventListener.cpp
index bff856781ca3..08cb464c8d0c 100644
--- a/editor/libeditor/nsEditorEventListener.cpp
+++ b/editor/libeditor/nsEditorEventListener.cpp
@@ -369,7 +369,7 @@ nsEditorEventListener::HandleEvent(nsIDOMEvent* aEvent)
   //       calling it, this queries the specific interface.  If it would fail,
   //       each event handler would just ignore the event.  So, in this method,
   //       you don't need to check if the QI succeeded before each call.
-  switch (internalEvent->message) {
+  switch (internalEvent->mMessage) {
     // dragenter
     case NS_DRAGDROP_ENTER: {
       nsCOMPtr dragEvent = do_QueryInterface(aEvent);
diff --git a/editor/libeditor/nsHTMLDataTransfer.cpp b/editor/libeditor/nsHTMLDataTransfer.cpp
index 41ca3a1212a9..9a8be0c21d1b 100644
--- a/editor/libeditor/nsHTMLDataTransfer.cpp
+++ b/editor/libeditor/nsHTMLDataTransfer.cpp
@@ -1062,10 +1062,10 @@ nsresult nsHTMLEditor::InsertObject(const char* aType, nsISupports* aObject, boo
       rv = NS_NewChannel(getter_AddRefs(channel),
                          fileURI,
                          nsContentUtils::GetSystemPrincipal(),
-                         nsILoadInfo::SEC_NORMAL,
+                         nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
                          nsIContentPolicy::TYPE_OTHER);
       NS_ENSURE_SUCCESS(rv, rv);
-      rv = channel->Open(getter_AddRefs(imageStream));
+      rv = channel->Open2(getter_AddRefs(imageStream));
       NS_ENSURE_SUCCESS(rv, rv);
     } else {
       imageStream = do_QueryInterface(aObject);
diff --git a/editor/libeditor/nsHTMLEditor.cpp b/editor/libeditor/nsHTMLEditor.cpp
index 8e0f57ec0b8d..dc403cee0f7d 100644
--- a/editor/libeditor/nsHTMLEditor.cpp
+++ b/editor/libeditor/nsHTMLEditor.cpp
@@ -594,7 +594,7 @@ nsHTMLEditor::HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent)
   WidgetKeyboardEvent* nativeKeyEvent =
     aKeyEvent->GetInternalNSEvent()->AsKeyboardEvent();
   NS_ENSURE_TRUE(nativeKeyEvent, NS_ERROR_UNEXPECTED);
-  NS_ASSERTION(nativeKeyEvent->message == NS_KEY_PRESS,
+  NS_ASSERTION(nativeKeyEvent->mMessage == NS_KEY_PRESS,
                "HandleKeyPressEvent gets non-keypress event");
 
   switch (nativeKeyEvent->keyCode) {
diff --git a/editor/libeditor/nsPlaintextEditor.cpp b/editor/libeditor/nsPlaintextEditor.cpp
index bb6136bb1876..9a055abe64e1 100644
--- a/editor/libeditor/nsPlaintextEditor.cpp
+++ b/editor/libeditor/nsPlaintextEditor.cpp
@@ -362,7 +362,7 @@ nsPlaintextEditor::HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent)
   WidgetKeyboardEvent* nativeKeyEvent =
     aKeyEvent->GetInternalNSEvent()->AsKeyboardEvent();
   NS_ENSURE_TRUE(nativeKeyEvent, NS_ERROR_UNEXPECTED);
-  NS_ASSERTION(nativeKeyEvent->message == NS_KEY_PRESS,
+  NS_ASSERTION(nativeKeyEvent->mMessage == NS_KEY_PRESS,
                "HandleKeyPressEvent gets non-keypress event");
 
   switch (nativeKeyEvent->keyCode) {
@@ -845,7 +845,7 @@ nsPlaintextEditor::UpdateIMEComposition(nsIDOMEvent* aDOMTextEvent)
   WidgetCompositionEvent* compositionChangeEvent =
     aDOMTextEvent->GetInternalNSEvent()->AsCompositionEvent();
   NS_ENSURE_TRUE(compositionChangeEvent, NS_ERROR_INVALID_ARG);
-  MOZ_ASSERT(compositionChangeEvent->message == NS_COMPOSITION_CHANGE,
+  MOZ_ASSERT(compositionChangeEvent->mMessage == NS_COMPOSITION_CHANGE,
              "The internal event should be NS_COMPOSITION_CHANGE");
 
   EnsureComposition(compositionChangeEvent);
diff --git a/gfx/angle/src/libGLESv2/renderer/d3d/VertexBuffer.cpp b/gfx/angle/src/libGLESv2/renderer/d3d/VertexBuffer.cpp
index 4f85eb94fa7d..34b2bbaf5c59 100644
--- a/gfx/angle/src/libGLESv2/renderer/d3d/VertexBuffer.cpp
+++ b/gfx/angle/src/libGLESv2/renderer/d3d/VertexBuffer.cpp
@@ -101,7 +101,12 @@ gl::Error VertexBufferInterface::storeVertexAttributes(const gl::VertexAttribute
         return error;
     }
 
-    if (mWritePosition + spaceRequired < mWritePosition)
+    // Align to 16-byte boundary
+    unsigned int alignedSpaceRequired = roundUp(spaceRequired, 16u);
+
+    // Protect against integer overflow
+    if (!IsUnsignedAdditionSafe(mWritePosition, alignedSpaceRequired) ||
+        alignedSpaceRequired < spaceRequired)
     {
         return gl::Error(GL_OUT_OF_MEMORY, "Internal error, new vertex buffer write position would overflow.");
     }
@@ -124,10 +129,7 @@ gl::Error VertexBufferInterface::storeVertexAttributes(const gl::VertexAttribute
         *outStreamOffset = mWritePosition;
     }
 
-    mWritePosition += spaceRequired;
-
-    // Align to 16-byte boundary
-    mWritePosition = rx::roundUp(mWritePosition, 16u);
+    mWritePosition += alignedSpaceRequired;
 
     return gl::Error(GL_NO_ERROR);
 }
@@ -143,17 +145,18 @@ gl::Error VertexBufferInterface::reserveVertexSpace(const gl::VertexAttribute &a
         return error;
     }
 
+    // Align to 16-byte boundary
+    unsigned int alignedRequiredSpace = roundUp(requiredSpace, 16u);
+
     // Protect against integer overflow
-    if (mReservedSpace + requiredSpace < mReservedSpace)
+    if (!IsUnsignedAdditionSafe(mReservedSpace, alignedRequiredSpace) ||
+        alignedRequiredSpace < requiredSpace)
     {
         return gl::Error(GL_OUT_OF_MEMORY, "Unable to reserve %u extra bytes in internal vertex buffer, "
                          "it would result in an overflow.", requiredSpace);
     }
 
-    mReservedSpace += requiredSpace;
-
-    // Align to 16-byte boundary
-    mReservedSpace = rx::roundUp(mReservedSpace, 16u);
+    mReservedSpace += alignedRequiredSpace;
 
     return gl::Error(GL_NO_ERROR);
 }
diff --git a/gfx/gl/AndroidSurfaceTexture.cpp b/gfx/gl/AndroidSurfaceTexture.cpp
index 2537046571d1..e7fa1cc21c7b 100644
--- a/gfx/gl/AndroidSurfaceTexture.cpp
+++ b/gfx/gl/AndroidSurfaceTexture.cpp
@@ -11,13 +11,13 @@
 #include 
 #include "AndroidSurfaceTexture.h"
 #include "gfxImageSurface.h"
+#include "gfxPrefs.h"
 #include "AndroidBridge.h"
 #include "nsThreadUtils.h"
 #include "mozilla/gfx/Matrix.h"
 #include "GeneratedJNIWrappers.h"
 #include "SurfaceTexture.h"
 #include "GLContext.h"
-#include "mozilla/Preferences.h"
 
 using namespace mozilla;
 using namespace mozilla::jni;
@@ -132,7 +132,7 @@ AndroidSurfaceTexture::UpdateCanDetach()
   // The API for attach/detach only exists on 16+, and PowerVR has some sort of
   // fencing issue. Additionally, attach/detach seems to be busted on at least some
   // Mali adapters (400MP2 for sure, bug 1131793)
-  bool canDetach = Preferences::GetBool("gfx.SurfaceTexture.detach.enabled", true);
+  bool canDetach = gfxPrefs::SurfaceTextureDetachEnabled();
 
   mCanDetach = AndroidBridge::Bridge()->GetAPIVersion() >= 16 &&
     (!mAttachedContext || mAttachedContext->Vendor() != GLVendor::Imagination) &&
diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp
index 763dabcdf822..8f464c4ba59c 100644
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -854,7 +854,7 @@ APZCTreeManager::UpdateWheelTransaction(WidgetInputEvent& aEvent)
     return;
   }
 
-  switch (aEvent.message) {
+  switch (aEvent.mMessage) {
    case NS_MOUSE_MOVE:
    case NS_DRAGDROP_OVER: {
      WidgetMouseEvent* mouseEvent = aEvent.AsMouseEvent();
diff --git a/gfx/layers/apz/util/APZCCallbackHelper.cpp b/gfx/layers/apz/util/APZCCallbackHelper.cpp
index da4377ff25fb..dbb4e81d4afb 100644
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -810,6 +810,27 @@ APZCCallbackHelper::NotifyFlushComplete()
   observerService->NotifyObservers(nullptr, "apz-repaints-flushed", nullptr);
 }
 
+static int32_t sActiveSuppressDisplayport = 0;
+
+void
+APZCCallbackHelper::SuppressDisplayport(const bool& aEnabled)
+{
+  if (aEnabled) {
+    sActiveSuppressDisplayport++;
+  } else {
+    sActiveSuppressDisplayport--;
+  }
+
+  MOZ_ASSERT(sActiveSuppressDisplayport >= 0);
+}
+
+bool
+APZCCallbackHelper::IsDisplayportSuppressed()
+{
+  return sActiveSuppressDisplayport > 0;
+}
+
+
 } // namespace layers
 } // namespace mozilla
 
diff --git a/gfx/layers/apz/util/APZCCallbackHelper.h b/gfx/layers/apz/util/APZCCallbackHelper.h
index a2d8f49f668b..a655f08a6e75 100644
--- a/gfx/layers/apz/util/APZCCallbackHelper.h
+++ b/gfx/layers/apz/util/APZCCallbackHelper.h
@@ -169,6 +169,10 @@ class APZCCallbackHelper
 
     /* Notify content that the repaint flush is complete. */
     static void NotifyFlushComplete();
+
+    /* Temporarily ignore the Displayport for better paint performance. */
+    static void SuppressDisplayport(const bool& aEnabled);
+    static bool IsDisplayportSuppressed();
 };
 
 } // namespace layers
diff --git a/gfx/layers/apz/util/APZEventState.cpp b/gfx/layers/apz/util/APZEventState.cpp
index 0c67d71b0622..78ae8547643e 100644
--- a/gfx/layers/apz/util/APZEventState.cpp
+++ b/gfx/layers/apz/util/APZEventState.cpp
@@ -251,14 +251,14 @@ APZEventState::ProcessTouchEvent(const WidgetTouchEvent& aEvent,
                                  uint64_t aInputBlockId,
                                  nsEventStatus aApzResponse)
 {
-  if (aEvent.message == NS_TOUCH_START && aEvent.touches.Length() > 0) {
+  if (aEvent.mMessage == NS_TOUCH_START && aEvent.touches.Length() > 0) {
     mActiveElementManager->SetTargetElement(aEvent.touches[0]->GetTarget());
   }
 
   bool isTouchPrevented = TouchManager::gPreventMouseEvents ||
       aEvent.mFlags.mMultipleActionsPrevented;
   bool sentContentResponse = false;
-  switch (aEvent.message) {
+  switch (aEvent.mMessage) {
   case NS_TOUCH_START: {
     mTouchEndCancelled = false;
     if (mPendingTouchPreventedResponse) {
@@ -305,8 +305,8 @@ APZEventState::ProcessTouchEvent(const WidgetTouchEvent& aEvent,
         aApzResponse == nsEventStatus_eConsumeDoDefault &&
         gfxPrefs::PointerEventsEnabled()) {
     WidgetTouchEvent cancelEvent(aEvent);
-    cancelEvent.message = NS_TOUCH_CANCEL;
-    cancelEvent.mFlags.mCancelable = false; // message != NS_TOUCH_CANCEL;
+    cancelEvent.mMessage = NS_TOUCH_CANCEL;
+    cancelEvent.mFlags.mCancelable = false; // mMessage != NS_TOUCH_CANCEL;
     for (uint32_t i = 0; i < cancelEvent.touches.Length(); ++i) {
       if (mozilla::dom::Touch* touch = cancelEvent.touches[i]) {
         touch->convertToPointer = true;
diff --git a/gfx/layers/composite/ContainerLayerComposite.cpp b/gfx/layers/composite/ContainerLayerComposite.cpp
index 3c97f839cebb..4792e0f4b051 100755
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -520,6 +520,9 @@ RenderLayers(ContainerT* aContainer,
     gfxRGBA color;
     if ((layer->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
         LayerHasCheckerboardingAPZC(layer, &color)) {
+      if (gfxPrefs::APZHighlightCheckerboardedAreas()) {
+        color = gfxRGBA(255 / 255.0, 188 / 255.0, 217 / 255.0, 1);  // "Cotton Candy"
+      }
       // Ideally we would want to intersect the checkerboard region from the APZ with the layer bounds
       // and only fill in that area. However the layer bounds takes into account the base translation
       // for the painted layer whereas the checkerboard region does not. One does not simply
diff --git a/gfx/layers/d3d11/CompositorD3D11.cpp b/gfx/layers/d3d11/CompositorD3D11.cpp
index 87eac4d19c3c..e6ba57717673 100644
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -1105,7 +1105,7 @@ CompositorD3D11::EndFrame()
         i++;
       }
 
-      params.pDirtyRects = rects.data();
+      params.pDirtyRects = params.DirtyRectsCount ? rects.data() : nullptr;
       chain->Present1(presentInterval, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0, ¶ms);
     } else {
       mSwapChain->Present(presentInterval, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0);
diff --git a/gfx/layers/d3d11/ReadbackManagerD3D11.cpp b/gfx/layers/d3d11/ReadbackManagerD3D11.cpp
index baa82d2a7bc1..28d7f5146ddc 100644
--- a/gfx/layers/d3d11/ReadbackManagerD3D11.cpp
+++ b/gfx/layers/d3d11/ReadbackManagerD3D11.cpp
@@ -44,12 +44,13 @@ class ReadbackResultWriterD3D11 final : public nsIRunnable
     mTask->mReadbackTexture->GetDesc(&desc);
 
     D3D10_MAPPED_TEXTURE2D mappedTex;
-    // We know this map will immediately succeed, as we've already mapped this
-    // copied data on our task thread.
+    // Unless there is an error this map should succeed immediately, as we've
+    // recently mapped (and unmapped) this copied data on our task thread.
     HRESULT hr = mTask->mReadbackTexture->Map(0, D3D10_MAP_READ, 0, &mappedTex);
 
     if (FAILED(hr)) {
       mTask->mSink->ProcessReadback(nullptr);
+      return NS_OK;
     }
 
     {
diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp
index 578ad6a8ca34..eb7b7bc99000 100644
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -224,81 +224,14 @@ static void SetThreadPriority()
   hal::SetCurrentThreadPriority(hal::THREAD_PRIORITY_COMPOSITOR);
 }
 
-CompositorScheduler::CompositorScheduler(CompositorParent* aCompositorParent)
-  : mCompositorParent(aCompositorParent)
-  , mCurrentCompositeTask(nullptr)
-{
-}
-
-CompositorScheduler::~CompositorScheduler()
-{
-  MOZ_ASSERT(!mCompositorParent);
-}
-
-void
-CompositorScheduler::CancelCurrentCompositeTask()
-{
-  if (mCurrentCompositeTask) {
-    mCurrentCompositeTask->Cancel();
-    mCurrentCompositeTask = nullptr;
-  }
-}
-
-void
-CompositorScheduler::ScheduleTask(CancelableTask* aTask, int aTime)
-{
-  MOZ_ASSERT(CompositorParent::CompositorLoop());
-  MOZ_ASSERT(aTime >= 0);
-  CompositorParent::CompositorLoop()->PostDelayedTask(FROM_HERE, aTask, aTime);
-}
-
-void
-CompositorScheduler::ResumeComposition()
-{
-  mLastCompose = TimeStamp::Now();
-  ComposeToTarget(nullptr);
-}
-
-void
-CompositorScheduler::ForceComposeToTarget(gfx::DrawTarget* aTarget, const IntRect* aRect)
-{
-  mLastCompose = TimeStamp::Now();
-  ComposeToTarget(aTarget, aRect);
-}
-
-void
-CompositorScheduler::ComposeToTarget(gfx::DrawTarget* aTarget, const IntRect* aRect)
-{
-  MOZ_ASSERT(CompositorParent::IsInCompositorThread());
-  MOZ_ASSERT(mCompositorParent);
-  mCompositorParent->CompositeToTarget(aTarget, aRect);
-}
-
-void
-CompositorScheduler::Destroy()
-{
-  MOZ_ASSERT(CompositorParent::IsInCompositorThread());
-  CancelCurrentCompositeTask();
-  mCompositorParent = nullptr;
-}
-
-CompositorSoftwareTimerScheduler::CompositorSoftwareTimerScheduler(CompositorParent* aCompositorParent)
-  : CompositorScheduler(aCompositorParent)
-{
-}
-
-CompositorSoftwareTimerScheduler::~CompositorSoftwareTimerScheduler()
-{
-  MOZ_ASSERT(!mCurrentCompositeTask);
-}
-
-// Used when layout.frame_rate is -1. Needs to be kept in sync with
-// DEFAULT_FRAME_RATE in nsRefreshDriver.cpp.
-static const int32_t kDefaultFrameRate = 60;
-
+#ifdef COMPOSITOR_PERFORMANCE_WARNING
 static int32_t
 CalculateCompositionFrameRate()
 {
+  // Used when layout.frame_rate is -1. Needs to be kept in sync with
+  // DEFAULT_FRAME_RATE in nsRefreshDriver.cpp.
+  // TODO: This should actually return the vsync rate.
+  const int32_t defaultFrameRate = 60;
   int32_t compositionFrameRatePref = gfxPrefs::LayersCompositionFrameRate();
   if (compositionFrameRatePref < 0) {
     // Use the same frame rate for composition as for layout.
@@ -306,68 +239,13 @@ CalculateCompositionFrameRate()
     if (layoutFrameRatePref < 0) {
       // TODO: The main thread frame scheduling code consults the actual
       // monitor refresh rate in this case. We should do the same.
-      return kDefaultFrameRate;
+      return defaultFrameRate;
     }
     return layoutFrameRatePref;
   }
   return compositionFrameRatePref;
 }
-
-void
-CompositorSoftwareTimerScheduler::ScheduleComposition()
-{
-  if (mCurrentCompositeTask) {
-    return;
-  }
-
-  bool initialComposition = mLastCompose.IsNull();
-  TimeDuration delta;
-  if (!initialComposition) {
-    delta = TimeStamp::Now() - mLastCompose;
-  }
-
-  int32_t rate = CalculateCompositionFrameRate();
-
-  // If rate == 0 (ASAP mode), minFrameDelta must be 0 so there's no delay.
-  TimeDuration minFrameDelta = TimeDuration::FromMilliseconds(
-    rate == 0 ? 0.0 : std::max(0.0, 1000.0 / rate));
-
-  mCurrentCompositeTask = NewRunnableMethod(this,
-                                            &CompositorSoftwareTimerScheduler::CallComposite);
-
-  if (!initialComposition && delta < minFrameDelta) {
-    TimeDuration delay = minFrameDelta - delta;
-#ifdef COMPOSITOR_PERFORMANCE_WARNING
-    mExpectedComposeStartTime = TimeStamp::Now() + delay;
-#endif
-    ScheduleTask(mCurrentCompositeTask, delay.ToMilliseconds());
-  } else {
-#ifdef COMPOSITOR_PERFORMANCE_WARNING
-    mExpectedComposeStartTime = TimeStamp::Now();
 #endif
-    ScheduleTask(mCurrentCompositeTask, 0);
-  }
-}
-
-bool
-CompositorSoftwareTimerScheduler::NeedsComposite()
-{
-  return mCurrentCompositeTask ? true : false;
-}
-
-void
-CompositorSoftwareTimerScheduler::CallComposite()
-{
-  Composite(TimeStamp::Now());
-}
-
-void
-CompositorSoftwareTimerScheduler::Composite(TimeStamp aTimestamp)
-{
-  mCurrentCompositeTask = nullptr;
-  mLastCompose = aTimestamp;
-  ComposeToTarget(nullptr);
-}
 
 CompositorVsyncScheduler::Observer::Observer(CompositorVsyncScheduler* aOwner)
   : mMutex("CompositorVsyncScheduler.Observer.Mutex")
@@ -398,11 +276,12 @@ CompositorVsyncScheduler::Observer::Destroy()
 }
 
 CompositorVsyncScheduler::CompositorVsyncScheduler(CompositorParent* aCompositorParent, nsIWidget* aWidget)
-  : CompositorScheduler(aCompositorParent)
+  : mCompositorParent(aCompositorParent)
+  , mLastCompose(TimeStamp::Now())
+  , mCurrentCompositeTask(nullptr)
   , mNeedsComposite(false)
   , mIsObservingVsync(false)
   , mVsyncNotificationsSkipped(0)
-  , mCompositorParent(aCompositorParent)
   , mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor")
   , mSetNeedsCompositeMonitor("SetNeedsCompositeMonitor")
   , mSetNeedsCompositeTask(nullptr)
@@ -414,6 +293,11 @@ CompositorVsyncScheduler::CompositorVsyncScheduler(CompositorParent* aCompositor
 #ifdef MOZ_WIDGET_GONK
   GeckoTouchDispatcher::GetInstance()->SetCompositorVsyncScheduler(this);
 #endif
+
+  // mAsapScheduling is set on the main thread during init,
+  // but is only accessed after on the compositor thread.
+  mAsapScheduling = gfxPrefs::LayersCompositionFrameRate() == 0 ||
+                    gfxPlatform::IsInLayoutAsapMode();
 }
 
 CompositorVsyncScheduler::~CompositorVsyncScheduler()
@@ -433,13 +317,32 @@ CompositorVsyncScheduler::Destroy()
   mVsyncObserver->Destroy();
   mVsyncObserver = nullptr;
   CancelCurrentSetNeedsCompositeTask();
-  CompositorScheduler::Destroy();
+  CancelCurrentCompositeTask();
+}
+
+void
+CompositorVsyncScheduler::PostCompositeTask(TimeStamp aCompositeTimestamp)
+{
+  // can be called from the compositor or vsync thread
+  MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
+  if (mCurrentCompositeTask == nullptr) {
+    mCurrentCompositeTask = NewRunnableMethod(this,
+                                              &CompositorVsyncScheduler::Composite,
+                                              aCompositeTimestamp);
+    ScheduleTask(mCurrentCompositeTask, 0);
+  }
 }
 
 void
 CompositorVsyncScheduler::ScheduleComposition()
 {
-  SetNeedsComposite(true);
+  MOZ_ASSERT(CompositorParent::IsInCompositorThread());
+  if (mAsapScheduling) {
+    // Used only for performance testing purposes
+    PostCompositeTask(TimeStamp::Now());
+  } else {
+    SetNeedsComposite(true);
+  }
 }
 
 void
@@ -488,14 +391,7 @@ CompositorVsyncScheduler::NotifyVsync(TimeStamp aVsyncTimestamp)
   // Called from the vsync dispatch thread
   MOZ_ASSERT(!CompositorParent::IsInCompositorThread());
   MOZ_ASSERT(!NS_IsMainThread());
-
-  MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
-  if (mCurrentCompositeTask == nullptr) {
-    mCurrentCompositeTask = NewRunnableMethod(this,
-                                              &CompositorVsyncScheduler::Composite,
-                                              aVsyncTimestamp);
-    ScheduleTask(mCurrentCompositeTask, 0);
-  }
+  PostCompositeTask(aVsyncTimestamp);
   return true;
 }
 
@@ -504,7 +400,10 @@ CompositorVsyncScheduler::CancelCurrentCompositeTask()
 {
   MOZ_ASSERT(CompositorParent::IsInCompositorThread() || NS_IsMainThread());
   MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
-  CompositorScheduler::CancelCurrentCompositeTask();
+  if (mCurrentCompositeTask) {
+    mCurrentCompositeTask->Cancel();
+    mCurrentCompositeTask = nullptr;
+  }
 }
 
 void
@@ -518,7 +417,7 @@ CompositorVsyncScheduler::Composite(TimeStamp aVsyncTimestamp)
 
   DispatchTouchEvents(aVsyncTimestamp);
 
-  if (mNeedsComposite) {
+  if (mNeedsComposite || mAsapScheduling) {
     mNeedsComposite = false;
     mLastCompose = aVsyncTimestamp;
     ComposeToTarget(nullptr);
@@ -549,7 +448,8 @@ void
 CompositorVsyncScheduler::ForceComposeToTarget(gfx::DrawTarget* aTarget, const IntRect* aRect)
 {
   OnForceComposeToTarget();
-  CompositorScheduler::ForceComposeToTarget(aTarget, aRect);
+  mLastCompose = TimeStamp::Now();
+  ComposeToTarget(aTarget, aRect);
 }
 
 bool
@@ -613,22 +513,27 @@ MessageLoop* CompositorParent::CompositorLoop()
   return CompositorThread() ? CompositorThread()->message_loop() : nullptr;
 }
 
-static bool
-IsInCompositorAsapMode()
+void
+CompositorVsyncScheduler::ScheduleTask(CancelableTask* aTask, int aTime)
+{
+  MOZ_ASSERT(CompositorParent::CompositorLoop());
+  MOZ_ASSERT(aTime >= 0);
+  CompositorParent::CompositorLoop()->PostDelayedTask(FROM_HERE, aTask, aTime);
+}
+
+void
+CompositorVsyncScheduler::ResumeComposition()
 {
-  // Returns true if the compositor is allowed to be in ASAP mode
-  // and layout is not in ASAP mode
-  return gfxPrefs::LayersCompositionFrameRate() == 0 &&
-            !gfxPlatform::IsInLayoutAsapMode();
+  mLastCompose = TimeStamp::Now();
+  ComposeToTarget(nullptr);
 }
 
-static bool
-UseVsyncComposition()
+void
+CompositorVsyncScheduler::ComposeToTarget(gfx::DrawTarget* aTarget, const IntRect* aRect)
 {
-  return gfxPrefs::VsyncAlignedCompositor()
-          && gfxPrefs::HardwareVsyncEnabled()
-          && !IsInCompositorAsapMode()
-          && !gfxPlatform::IsInLayoutAsapMode();
+  MOZ_ASSERT(CompositorParent::IsInCompositorThread());
+  MOZ_ASSERT(mCompositorParent);
+  mCompositorParent->CompositeToTarget(aTarget, aRect);
 }
 
 CompositorParent::CompositorParent(nsIWidget* aWidget,
@@ -675,12 +580,8 @@ CompositorParent::CompositorParent(nsIWidget* aWidget,
     mApzcTreeManager = new APZCTreeManager();
   }
 
-  if (UseVsyncComposition()) {
-    gfxDebugOnce() << "Enabling vsync compositor";
-    mCompositorScheduler = new CompositorVsyncScheduler(this, aWidget);
-  } else {
-    mCompositorScheduler = new CompositorSoftwareTimerScheduler(this);
-  }
+  gfxDebugOnce() << "Enabling vsync compositor";
+  mCompositorScheduler = new CompositorVsyncScheduler(this, aWidget);
 
   LayerScope::SetPixelScale(mWidget->GetDefaultScale().scale);
 }
diff --git a/gfx/layers/ipc/CompositorParent.h b/gfx/layers/ipc/CompositorParent.h
index 5e67d6138996..95a05d264f79 100644
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -88,21 +88,31 @@ class CompositorThreadHolder final
   friend class CompositorParent;
 };
 
-class CompositorScheduler
+/**
+ * Manages the vsync (de)registration and tracking on behalf of the
+ * compositor when it need to paint.
+ * Turns vsync notifications into scheduled composites.
+ **/
+class CompositorVsyncScheduler
 {
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorVsyncScheduler)
+
 public:
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorScheduler)
-  explicit CompositorScheduler(CompositorParent* aCompositorParent);
+  explicit CompositorVsyncScheduler(CompositorParent* aCompositorParent, nsIWidget* aWidget);
+  bool NotifyVsync(TimeStamp aVsyncTimestamp);
+  void SetNeedsComposite(bool aSchedule);
+  void OnForceComposeToTarget();
 
-  virtual void ScheduleComposition() = 0;
-  virtual void CancelCurrentCompositeTask();
-  virtual bool NeedsComposite() = 0;
-  virtual void Composite(TimeStamp aTimestamp) = 0;
-  virtual void ScheduleTask(CancelableTask*, int);
-  virtual void ResumeComposition();
-  virtual void ForceComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect);
-  virtual void ComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr);
-  virtual void Destroy();
+  void ScheduleTask(CancelableTask*, int);
+  void ResumeComposition();
+  void ComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr);
+  void PostCompositeTask(TimeStamp aCompositeTimestamp);
+  void Destroy();
+  void ScheduleComposition();
+  void CancelCurrentCompositeTask();
+  bool NeedsComposite();
+  void Composite(TimeStamp aVsyncTimestamp);
+  void ForceComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect);
 
   const TimeStamp& GetLastComposeTime()
   {
@@ -115,56 +125,7 @@ class CompositorScheduler
     return mExpectedComposeStartTime;
   }
 #endif
-
-protected:
-  virtual ~CompositorScheduler();
-
-  CompositorParent* mCompositorParent;
-  TimeStamp mLastCompose;
-  CancelableTask* mCurrentCompositeTask;
-
-#ifdef COMPOSITOR_PERFORMANCE_WARNING
-  TimeStamp mExpectedComposeStartTime;
-#endif
-};
-
-class CompositorSoftwareTimerScheduler final : public CompositorScheduler
-{
-public:
-  explicit CompositorSoftwareTimerScheduler(CompositorParent* aCompositorParent);
-
-  // from CompositorScheduler
-  virtual void ScheduleComposition() override;
-  virtual bool NeedsComposite() override;
-  virtual void Composite(TimeStamp aTimestamp) override;
-
-  void CallComposite();
-private:
-  virtual ~CompositorSoftwareTimerScheduler();
-};
-
-/**
- * Manages the vsync (de)registration and tracking on behalf of the
- * compositor when it need to paint.
- * Turns vsync notifications into scheduled composites.
- **/
-
-class CompositorVsyncScheduler final : public CompositorScheduler
-{
-public:
-  explicit CompositorVsyncScheduler(CompositorParent* aCompositorParent, nsIWidget* aWidget);
-  bool NotifyVsync(TimeStamp aVsyncTimestamp);
-  void SetNeedsComposite(bool aSchedule);
-  void OnForceComposeToTarget();
-
-  // from CompositorScheduler
-  virtual void ScheduleComposition() override;
-  virtual void CancelCurrentCompositeTask() override;
-  virtual bool NeedsComposite() override;
-  virtual void Composite(TimeStamp aVsyncTimestamp) override;
-  virtual void ForceComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect) override;
-  virtual void Destroy() override;
-
+ 
 private:
   virtual ~CompositorVsyncScheduler();
 
@@ -188,10 +149,18 @@ class CompositorVsyncScheduler final : public CompositorScheduler
     CompositorVsyncScheduler* mOwner;
   };
 
+  CompositorParent* mCompositorParent;
+  TimeStamp mLastCompose;
+  CancelableTask* mCurrentCompositeTask;
+
+#ifdef COMPOSITOR_PERFORMANCE_WARNING
+  TimeStamp mExpectedComposeStartTime;
+#endif
+
+  bool mAsapScheduling;
   bool mNeedsComposite;
   bool mIsObservingVsync;
   int32_t mVsyncNotificationsSkipped;
-  CompositorParent* mCompositorParent;
   nsRefPtr mCompositorVsyncDispatcher;
   nsRefPtr mVsyncObserver;
 
@@ -216,7 +185,7 @@ class CompositorParent final : public PCompositorParent,
                                public ShadowLayersManager
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(CompositorParent)
-  friend class CompositorScheduler;
+  friend class CompositorVsyncScheduler;
 
 public:
   explicit CompositorParent(nsIWidget* aWidget,
@@ -498,7 +467,7 @@ class CompositorParent final : public PCompositorParent,
   nsRefPtr mApzcTreeManager;
 
   nsRefPtr mCompositorThreadHolder;
-  nsRefPtr mCompositorScheduler;
+  nsRefPtr mCompositorScheduler;
 
   DISALLOW_EVIL_CONSTRUCTORS(CompositorParent);
 };
diff --git a/gfx/src/gfxCrashReporterUtils.cpp b/gfx/src/gfxCrashReporterUtils.cpp
index 79f013861888..69f9cf5074bc 100644
--- a/gfx/src/gfxCrashReporterUtils.cpp
+++ b/gfx/src/gfxCrashReporterUtils.cpp
@@ -98,7 +98,7 @@ ScopedGfxFeatureReporter::WriteAppNote(char statusChar)
   nsAutoCString featureString;
   featureString.AppendPrintf("%s%c ",
                              mFeature,
-                             mStatusChar);
+                             statusChar);
 
   if (!gFeaturesAlreadyReported->Contains(featureString)) {
     gFeaturesAlreadyReported->AppendElement(featureString);
diff --git a/gfx/src/nsThemeConstants.h b/gfx/src/nsThemeConstants.h
index 07e7073eebed..2552e75f6b3b 100644
--- a/gfx/src/nsThemeConstants.h
+++ b/gfx/src/nsThemeConstants.h
@@ -286,3 +286,5 @@
 #define NS_THEME_MAC_VIBRANCY_DARK                         244
 #define NS_THEME_MAC_DISCLOSURE_BUTTON_OPEN                245
 #define NS_THEME_MAC_DISCLOSURE_BUTTON_CLOSED              246
+
+#define NS_THEME_GTK_INFO_BAR                              247
diff --git a/gfx/thebes/gfxAndroidPlatform.cpp b/gfx/thebes/gfxAndroidPlatform.cpp
index a8df49057031..92d9148e7ee8 100644
--- a/gfx/thebes/gfxAndroidPlatform.cpp
+++ b/gfx/thebes/gfxAndroidPlatform.cpp
@@ -181,13 +181,30 @@ gfxAndroidPlatform::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
 {
     static const char kDroidSansJapanese[] = "Droid Sans Japanese";
     static const char kMotoyaLMaru[] = "MotoyaLMaru";
+    static const char kNotoColorEmoji[] = "Noto Color Emoji";
+#ifdef MOZ_WIDGET_GONK
+    static const char kFirefoxEmoji[] = "Firefox Emoji";
+#endif
 
     if (aNextCh == 0xfe0fu) {
         // if char is followed by VS16, try for a color emoji glyph
-        aFontList.AppendElement("Noto Color Emoji");
+#ifdef MOZ_WIDGET_GONK
+        aFontList.AppendElement(kFirefoxEmoji);
+#endif
+        aFontList.AppendElement(kNotoColorEmoji);
     }
 
-    if (IS_IN_BMP(aCh)) {
+    if (!IS_IN_BMP(aCh)) {
+        uint32_t p = aCh >> 16;
+        if (p == 1) { // try color emoji font, unless VS15 (text style) present
+            if (aNextCh != 0xfe0fu && aNextCh != 0xfe0eu) {
+#ifdef MOZ_WIDGET_GONK
+                aFontList.AppendElement(kFirefoxEmoji);
+#endif
+                aFontList.AppendElement(kNotoColorEmoji);
+            }
+        }
+    } else {
         // try language-specific "Droid Sans *" and "Noto Sans *" fonts for
         // certain blocks, as most devices probably have these
         uint8_t block = (aCh >> 8) & 0xff;
diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp
index b3b2726ea90a..d983924dbee0 100644
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -534,9 +534,9 @@ gfxFontShaper::MergeFontFeatures(
 }
 
 void
-gfxShapedText::SetupClusterBoundaries(uint32_t         aOffset,
+gfxShapedText::SetupClusterBoundaries(uint32_t        aOffset,
                                       const char16_t *aString,
-                                      uint32_t         aLength)
+                                      uint32_t        aLength)
 {
     CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
 
@@ -547,8 +547,15 @@ gfxShapedText::SetupClusterBoundaries(uint32_t         aOffset,
 
     // the ClusterIterator won't be able to tell us if the string
     // _begins_ with a cluster-extender, so we handle that here
-    if (aLength && IsClusterExtender(*aString)) {
-        *glyphs = extendCluster;
+    if (aLength) {
+        uint32_t ch = *aString;
+        if (aLength > 1 && NS_IS_HIGH_SURROGATE(ch) &&
+            NS_IS_LOW_SURROGATE(aString[1])) {
+            ch = SURROGATE_TO_UCS4(ch, aString[1]);
+        }
+        if (IsClusterExtender(ch)) {
+            *glyphs = extendCluster;
+        }
     }
 
     while (!iter.AtEnd()) {
diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h
index 2497cf29a70c..d99bcec0f815 100644
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -160,6 +160,7 @@ class gfxPrefs final
   DECL_GFX_PREF(Live, "apz.fling_repaint_interval",            APZFlingRepaintInterval, int32_t, 75);
   DECL_GFX_PREF(Once, "apz.fling_stop_on_tap_threshold",       APZFlingStopOnTapThreshold, float, 0.05f);
   DECL_GFX_PREF(Once, "apz.fling_stopped_threshold",           APZFlingStoppedThreshold, float, 0.01f);
+  DECL_GFX_PREF(Once, "apz.highlight_checkerboarded_areas",    APZHighlightCheckerboardedAreas, bool, false);
   DECL_GFX_PREF(Once, "apz.max_velocity_inches_per_ms",        APZMaxVelocity, float, -1.0f);
   DECL_GFX_PREF(Once, "apz.max_velocity_queue_size",           APZMaxVelocityQueueSize, uint32_t, 5);
   DECL_GFX_PREF(Live, "apz.min_skate_speed",                   APZMinSkateSpeed, float, 1.0f);
@@ -239,6 +240,7 @@ class gfxPrefs final
   // Note that        "gfx.logging.level" is defined in Logging.h
   DECL_GFX_PREF(Once, "gfx.logging.crash.length",              GfxLoggingCrashLength, uint32_t, 6);
   DECL_GFX_PREF(Live, "gfx.perf-warnings.enabled",             PerfWarnings, bool, false);
+  DECL_GFX_PREF(Live, "gfx.SurfaceTexture.detach.enabled",     SurfaceTextureDetachEnabled, bool, true);
   DECL_GFX_PREF(Live, "gfx.testing.device-reset",              DeviceResetForTesting, int32_t, 0);
   DECL_GFX_PREF(Live, "gfx.testing.device-fail",               DeviceFailForTesting, bool, false);
 
diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp
index 2a25068c6f17..e645035984e8 100755
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -1668,19 +1668,29 @@ bool CouldD3D11DeviceWork()
     return true;
   }
 
-  if (GetModuleHandleW(L"dlumd32.dll") && GetModuleHandleW(L"igd10umd32.dll")) {
-    nsString displayLinkModuleVersionString;
-    gfxWindowsPlatform::GetDLLVersion(L"dlumd32.dll", displayLinkModuleVersionString);
-    uint64_t displayLinkModuleVersion;
-    if (!ParseDriverVersion(displayLinkModuleVersionString, &displayLinkModuleVersion)) {
-      gfxCriticalError() << "DisplayLink: could not parse version";
-      gANGLESupportsD3D11 = false;
-      return false;
-    }
-    if (displayLinkModuleVersion <= V(8,6,1,36484)) {
-      gfxCriticalError(CriticalLog::DefaultOptions(false)) << "DisplayLink: too old version";
-      gANGLESupportsD3D11 = false;
-      return false;
+  if (GetModuleHandleW(L"igd10umd32.dll")) {
+    const wchar_t* checkModules[] = {L"dlumd32.dll",
+                                     L"dlumd11.dll",
+                                     L"dlumd10.dll"};
+    for (int i=0; i= (png_uint_32) decoder->mFrameRect.height) {
+  if (row_num >= static_cast(decoder->mFrameRect.height)) {
     return;
   }
 
@@ -772,11 +772,14 @@ nsPNGDecoder::row_callback(png_structp png_ptr, png_bytep new_row,
         png_longjmp(decoder->mPNG, 1);
     }
 
-    if (decoder->mNumFrames <= 1) {
-      // Only do incremental image display for the first frame
-      // XXXbholley - this check should be handled in the superclass
-      nsIntRect r(0, row_num, width, 1);
-      decoder->PostInvalidation(r);
+    if (!decoder->interlacebuf) {
+      // Do line-by-line partial invalidations for non-interlaced images
+      decoder->PostInvalidation(IntRect(0, row_num, width, 1));
+    } else if (row_num ==
+               static_cast(decoder->mFrameRect.height - 1)) {
+      // Do only one full image invalidation for each pass (Bug 1187569)
+      decoder->PostInvalidation(IntRect(0, 0, width,
+                                decoder->mFrameRect.height));
     }
   }
 }
diff --git a/ipc/chromium/src/base/command_line.h b/ipc/chromium/src/base/command_line.h
index 24a106bbc2c1..8ae11dabc920 100644
--- a/ipc/chromium/src/base/command_line.h
+++ b/ipc/chromium/src/base/command_line.h
@@ -119,6 +119,12 @@ class CommandLine {
   // Append a loose value to the command line.
   void AppendLooseValue(const std::wstring& value);
 
+#if defined(OS_WIN)
+  void AppendLooseValue(const wchar_t* value) {
+    AppendLooseValue(std::wstring(value));
+  }
+#endif
+
   // Append the arguments from another command line to this one.
   // If |include_program| is true, include |other|'s program as well.
   void AppendArguments(const CommandLine& other,
diff --git a/ipc/chromium/src/base/file_path.h b/ipc/chromium/src/base/file_path.h
index 73aa6830f8c4..a8317cd4e4a8 100644
--- a/ipc/chromium/src/base/file_path.h
+++ b/ipc/chromium/src/base/file_path.h
@@ -116,6 +116,10 @@ class FilePath {
   FilePath(const FilePath& that) : path_(that.path_) {}
   explicit FilePath(const StringType& path) : path_(path) {}
 
+#if defined(OS_WIN)
+  explicit FilePath(const wchar_t* path) : path_(path) {}
+#endif
+
   FilePath& operator=(const FilePath& that) {
     path_ = that.path_;
     return *this;
diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp
index 1441cc07a660..76c059e2c70a 100644
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -593,7 +593,7 @@ MaybeAddNsprLogFileAccess(std::vector& aAllowedFilesReadWrite)
   // Chromium sandbox can only allow access to fully qualified file paths. This
   // only affects the environment for the child process we're about to create,
   // because this will get reset to the original value in PerformAsyncLaunch.
-  aAllowedFilesReadWrite.push_back(resolvedFilePath.get());
+  aAllowedFilesReadWrite.push_back(std::wstring(resolvedFilePath.get()));
   nsAutoCString resolvedEnvVar("NSPR_LOG_FILE=");
   AppendUTF16toUTF8(resolvedFilePath, resolvedEnvVar);
   PR_SetEnv(resolvedEnvVar.get());
diff --git a/js/public/RootingAPI.h b/js/public/RootingAPI.h
index 4740d328e02a..8995115d8737 100644
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -1157,6 +1157,30 @@ CallTraceCallbackOnNonHeap(T* v, const TraceCallbacks& aCallbacks, const char* a
 } /* namespace gc */
 } /* namespace js */
 
+// mozilla::Swap uses a stack temporary, which prevents classes like Heap
+// from being declared MOZ_HEAP_CLASS.
+namespace mozilla {
+
+template 
+inline void
+Swap(JS::Heap& aX, JS::Heap& aY)
+{
+    T tmp = aX;
+    aX = aY;
+    aY = tmp;
+}
+
+template 
+inline void
+Swap(JS::TenuredHeap& aX, JS::TenuredHeap& aY)
+{
+    T tmp = aX;
+    aX = aY;
+    aY = tmp;
+}
+
+} /* namespace mozilla */
+
 #undef DELETE_ASSIGNMENT_OPS
 
 #endif  /* js_RootingAPI_h */
diff --git a/js/public/TraceableHashTable.h b/js/public/TraceableHashTable.h
index 256e1f98f77e..83f29dffc514 100644
--- a/js/public/TraceableHashTable.h
+++ b/js/public/TraceableHashTable.h
@@ -76,7 +76,7 @@ class TraceableHashMapOperations
     using Range = typename Map::Range;
     using Enum = typename Map::Enum;
 
-    const Map& map() const { return static_cast(this)->extract(); }
+    const Map& map() const { return static_cast(this)->get(); }
 
   public:
     bool initialized() const                   { return map().initialized(); }
@@ -101,7 +101,7 @@ class MutableTraceableHashMapOperations
     using Range = typename Map::Range;
     using Enum = typename Map::Enum;
 
-    Map& map() { return static_cast(this)->extract(); }
+    Map& map() { return static_cast(this)->get(); }
 
   public:
     bool init(uint32_t len = 16) { return map().init(len); }
@@ -140,40 +140,18 @@ class MutableTraceableHashMapOperations
 template 
 class RootedBase>
   : public MutableTraceableHashMapOperations>, A,B,C,D,E,F>
-{
-    using Map = TraceableHashMap;
-
-    friend class TraceableHashMapOperations, A,B,C,D,E,F>;
-    const Map& extract() const { return *static_cast*>(this)->address(); }
-
-    friend class MutableTraceableHashMapOperations, A,B,C,D,E,F>;
-    Map& extract() { return *static_cast*>(this)->address(); }
-};
+{};
 
 template 
 class MutableHandleBase>
   : public MutableTraceableHashMapOperations>,
                                              A,B,C,D,E,F>
-{
-    using Map = TraceableHashMap;
-
-    friend class TraceableHashMapOperations, A,B,C,D,E,F>;
-    const Map& extract() const {
-        return *static_cast*>(this)->address();
-    }
-
-    friend class MutableTraceableHashMapOperations, A,B,C,D,E,F>;
-    Map& extract() { return *static_cast*>(this)->address(); }
-};
+{};
 
 template 
 class HandleBase>
   : public TraceableHashMapOperations>, A,B,C,D,E,F>
-{
-    using Map = TraceableHashMap;
-    friend class TraceableHashMapOperations, A,B,C,D,E,F>;
-    const Map& extract() const { return *static_cast*>(this)->address(); }
-};
+{};
 
 } /* namespace js */
 
diff --git a/js/public/TraceableVector.h b/js/public/TraceableVector.h
index a86f653ed883..53c66961ba17 100644
--- a/js/public/TraceableVector.h
+++ b/js/public/TraceableVector.h
@@ -59,7 +59,7 @@ template ;
-    const Vec& vec() const { return static_cast(this)->extract(); }
+    const Vec& vec() const { return static_cast(this)->get(); }
 
   public:
     const AllocPolicy& allocPolicy() const { return vec().allocPolicy(); }
@@ -81,8 +81,8 @@ class MutableTraceableVectorOperations
   : public TraceableVectorOperations
 {
     using Vec = TraceableVector;
-    const Vec& vec() const { return static_cast(this)->extract(); }
-    Vec& vec() { return static_cast(this)->extract(); }
+    const Vec& vec() const { return static_cast(this)->get(); }
+    Vec& vec() { return static_cast(this)->get(); }
 
   public:
     const AllocPolicy& allocPolicy() const { return vec().allocPolicy(); }
@@ -144,59 +144,24 @@ class MutableTraceableVectorOperations
 template 
 class RootedBase>
   : public MutableTraceableVectorOperations>, T,N,AP,TP>
-{
-    using Vec = TraceableVector;
-
-    friend class TraceableVectorOperations, T,N,AP,TP>;
-    const Vec& extract() const { return *static_cast*>(this)->address(); }
-
-    friend class MutableTraceableVectorOperations, T,N,AP,TP>;
-    Vec& extract() { return *static_cast*>(this)->address(); }
-};
+{};
 
 template 
 class MutableHandleBase>
   : public MutableTraceableVectorOperations>,
                                             T,N,AP,TP>
-{
-    using Vec = TraceableVector;
-
-    friend class TraceableVectorOperations, T,N,AP,TP>;
-    const Vec& extract() const {
-        return *static_cast*>(this)->address();
-    }
-
-    friend class MutableTraceableVectorOperations, T,N,AP,TP>;
-    Vec& extract() { return *static_cast*>(this)->address(); }
-};
+{};
 
 template 
 class HandleBase>
   : public TraceableVectorOperations>, T,N,AP,TP>
-{
-    using Vec = TraceableVector;
-
-    friend class TraceableVectorOperations, T,N,AP,TP>;
-    const Vec& extract() const {
-        return *static_cast*>(this)->address();
-    }
-};
+{};
 
 template 
 class PersistentRootedBase>
   : public MutableTraceableVectorOperations>,
                                             T,N,AP,TP>
-{
-    using Vec = TraceableVector;
-
-    friend class TraceableVectorOperations, T,N,AP,TP>;
-    const Vec& extract() const {
-        return *static_cast*>(this)->address();
-    }
-
-    friend class MutableTraceableVectorOperations, T,N,AP,TP>;
-    Vec& extract() { return *static_cast*>(this)->address(); }
-};
+{};
 
 } // namespace js
 
diff --git a/js/public/Utility.h b/js/public/Utility.h
index 77566dd1ec05..d0e5c33a3402 100644
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -194,7 +194,7 @@ static inline char* js_strdup(const char* s)
  *   general SpiderMonkey idiom that a JSContext-taking function reports its
  *   own errors.)
  *
- * - Otherwise, use js_malloc/js_realloc/js_calloc/js_free/js_new
+ * - Otherwise, use js_malloc/js_realloc/js_calloc/js_new
  *
  * Deallocation:
  *
@@ -248,22 +248,22 @@ JS_DECLARE_NEW_METHODS(js_new, js_malloc, static MOZ_ALWAYS_INLINE)
 
 template 
 static MOZ_ALWAYS_INLINE void
-js_delete(T* p)
+js_delete(const T* p)
 {
     if (p) {
         p->~T();
-        js_free(p);
+        js_free(const_cast(p));
     }
 }
 
 template
 static MOZ_ALWAYS_INLINE void
-js_delete_poison(T* p)
+js_delete_poison(const T* p)
 {
     if (p) {
         p->~T();
-        memset(p, 0x3B, sizeof(T));
-        js_free(p);
+        memset(const_cast(p), 0x3B, sizeof(T));
+        js_free(const_cast(p));
     }
 }
 
diff --git a/js/public/Value.h b/js/public/Value.h
index 9f7c44369c32..fefc7dbc3ced 100644
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -1676,82 +1676,82 @@ template  class MutableValueOperations;
 /*
  * A class designed for CRTP use in implementing the non-mutating parts of the
  * Value interface in Value-like classes.  Outer must be a class inheriting
- * ValueOperations with a visible extract() method returning the
- * const Value* abstracted by Outer.
+ * ValueOperations with a visible get() method returning a const
+ * reference to the Value abstracted by Outer.
  */
 template 
 class ValueOperations
 {
     friend class MutableValueOperations;
 
-    const JS::Value * value() const { return static_cast(this)->extract(); }
+    const JS::Value& value() const { return static_cast(this)->get(); }
 
   public:
-    bool isUndefined() const { return value()->isUndefined(); }
-    bool isNull() const { return value()->isNull(); }
-    bool isBoolean() const { return value()->isBoolean(); }
-    bool isTrue() const { return value()->isTrue(); }
-    bool isFalse() const { return value()->isFalse(); }
-    bool isNumber() const { return value()->isNumber(); }
-    bool isInt32() const { return value()->isInt32(); }
-    bool isInt32(int32_t i32) const { return value()->isInt32(i32); }
-    bool isDouble() const { return value()->isDouble(); }
-    bool isString() const { return value()->isString(); }
-    bool isSymbol() const { return value()->isSymbol(); }
-    bool isObject() const { return value()->isObject(); }
-    bool isMagic() const { return value()->isMagic(); }
-    bool isMagic(JSWhyMagic why) const { return value()->isMagic(why); }
-    bool isMarkable() const { return value()->isMarkable(); }
-    bool isPrimitive() const { return value()->isPrimitive(); }
-    bool isGCThing() const { return value()->isGCThing(); }
-
-    bool isNullOrUndefined() const { return value()->isNullOrUndefined(); }
-    bool isObjectOrNull() const { return value()->isObjectOrNull(); }
-
-    bool toBoolean() const { return value()->toBoolean(); }
-    double toNumber() const { return value()->toNumber(); }
-    int32_t toInt32() const { return value()->toInt32(); }
-    double toDouble() const { return value()->toDouble(); }
-    JSString* toString() const { return value()->toString(); }
-    JS::Symbol* toSymbol() const { return value()->toSymbol(); }
-    JSObject& toObject() const { return value()->toObject(); }
-    JSObject* toObjectOrNull() const { return value()->toObjectOrNull(); }
-    gc::Cell* toGCThing() const { return value()->toGCThing(); }
-    JS::TraceKind traceKind() const { return value()->traceKind(); }
-    uint64_t asRawBits() const { return value()->asRawBits(); }
-
-    JSValueType extractNonDoubleType() const { return value()->extractNonDoubleType(); }
-    uint32_t toPrivateUint32() const { return value()->toPrivateUint32(); }
-
-    JSWhyMagic whyMagic() const { return value()->whyMagic(); }
-    uint32_t magicUint32() const { return value()->magicUint32(); }
+    bool isUndefined() const { return value().isUndefined(); }
+    bool isNull() const { return value().isNull(); }
+    bool isBoolean() const { return value().isBoolean(); }
+    bool isTrue() const { return value().isTrue(); }
+    bool isFalse() const { return value().isFalse(); }
+    bool isNumber() const { return value().isNumber(); }
+    bool isInt32() const { return value().isInt32(); }
+    bool isInt32(int32_t i32) const { return value().isInt32(i32); }
+    bool isDouble() const { return value().isDouble(); }
+    bool isString() const { return value().isString(); }
+    bool isSymbol() const { return value().isSymbol(); }
+    bool isObject() const { return value().isObject(); }
+    bool isMagic() const { return value().isMagic(); }
+    bool isMagic(JSWhyMagic why) const { return value().isMagic(why); }
+    bool isMarkable() const { return value().isMarkable(); }
+    bool isPrimitive() const { return value().isPrimitive(); }
+    bool isGCThing() const { return value().isGCThing(); }
+
+    bool isNullOrUndefined() const { return value().isNullOrUndefined(); }
+    bool isObjectOrNull() const { return value().isObjectOrNull(); }
+
+    bool toBoolean() const { return value().toBoolean(); }
+    double toNumber() const { return value().toNumber(); }
+    int32_t toInt32() const { return value().toInt32(); }
+    double toDouble() const { return value().toDouble(); }
+    JSString* toString() const { return value().toString(); }
+    JS::Symbol* toSymbol() const { return value().toSymbol(); }
+    JSObject& toObject() const { return value().toObject(); }
+    JSObject* toObjectOrNull() const { return value().toObjectOrNull(); }
+    gc::Cell* toGCThing() const { return value().toGCThing(); }
+    JS::TraceKind traceKind() const { return value().traceKind(); }
+    uint64_t asRawBits() const { return value().asRawBits(); }
+
+    JSValueType extractNonDoubleType() const { return value().extractNonDoubleType(); }
+    uint32_t toPrivateUint32() const { return value().toPrivateUint32(); }
+
+    JSWhyMagic whyMagic() const { return value().whyMagic(); }
+    uint32_t magicUint32() const { return value().magicUint32(); }
 };
 
 /*
  * A class designed for CRTP use in implementing all the mutating parts of the
  * Value interface in Value-like classes.  Outer must be a class inheriting
- * MutableValueOperations with visible extractMutable() and extract()
- * methods returning the const Value* and Value* abstracted by Outer.
+ * MutableValueOperations with visible get() methods returning const and
+ * non-const references to the Value abstracted by Outer.
  */
 template 
 class MutableValueOperations : public ValueOperations
 {
-    JS::Value * value() { return static_cast(this)->extractMutable(); }
+    JS::Value& value() { return static_cast(this)->get(); }
 
   public:
-    void setNull() { value()->setNull(); }
-    void setUndefined() { value()->setUndefined(); }
-    void setInt32(int32_t i) { value()->setInt32(i); }
-    void setDouble(double d) { value()->setDouble(d); }
+    void setNull() { value().setNull(); }
+    void setUndefined() { value().setUndefined(); }
+    void setInt32(int32_t i) { value().setInt32(i); }
+    void setDouble(double d) { value().setDouble(d); }
     void setNaN() { setDouble(JS::GenericNaN()); }
-    void setBoolean(bool b) { value()->setBoolean(b); }
-    void setMagic(JSWhyMagic why) { value()->setMagic(why); }
-    bool setNumber(uint32_t ui) { return value()->setNumber(ui); }
-    bool setNumber(double d) { return value()->setNumber(d); }
-    void setString(JSString* str) { this->value()->setString(str); }
-    void setSymbol(JS::Symbol* sym) { this->value()->setSymbol(sym); }
-    void setObject(JSObject& obj) { this->value()->setObject(obj); }
-    void setObjectOrNull(JSObject* arg) { this->value()->setObjectOrNull(arg); }
+    void setBoolean(bool b) { value().setBoolean(b); }
+    void setMagic(JSWhyMagic why) { value().setMagic(why); }
+    bool setNumber(uint32_t ui) { return value().setNumber(ui); }
+    bool setNumber(double d) { return value().setNumber(d); }
+    void setString(JSString* str) { this->value().setString(str); }
+    void setSymbol(JS::Symbol* sym) { this->value().setSymbol(sym); }
+    void setObject(JSObject& obj) { this->value().setObject(obj); }
+    void setObjectOrNull(JSObject* arg) { this->value().setObjectOrNull(arg); }
 };
 
 /*
@@ -1765,8 +1765,6 @@ class HeapBase : public ValueOperations >
 
     friend class ValueOperations;
 
-    const JS::Value * extract() const { return static_cast(this)->address(); }
-
     void setBarriered(const JS::Value& v) {
         *static_cast*>(this) = v;
     }
@@ -1812,72 +1810,21 @@ class HeapBase : public ValueOperations >
     }
 };
 
-/*
- * Augment the generic Handle interface when T = Value with type-querying
- * and value-extracting operations.
- */
 template <>
 class HandleBase : public ValueOperations >
-{
-    friend class ValueOperations >;
-    const JS::Value * extract() const {
-        return static_cast*>(this)->address();
-    }
-};
+{};
 
-/*
- * Augment the generic MutableHandle interface when T = Value with
- * type-querying, value-extracting, and mutating operations.
- */
 template <>
 class MutableHandleBase : public MutableValueOperations >
-{
-    friend class ValueOperations >;
-    const JS::Value * extract() const {
-        return static_cast*>(this)->address();
-    }
-
-    friend class MutableValueOperations >;
-    JS::Value * extractMutable() {
-        return static_cast*>(this)->address();
-    }
-};
+{};
 
-/*
- * Augment the generic Rooted interface when T = Value with type-querying,
- * value-extracting, and mutating operations.
- */
 template <>
 class RootedBase : public MutableValueOperations >
-{
-    friend class ValueOperations >;
-    const JS::Value * extract() const {
-        return static_cast*>(this)->address();
-    }
-
-    friend class MutableValueOperations >;
-    JS::Value * extractMutable() {
-        return static_cast*>(this)->address();
-    }
-};
+{};
 
-/*
- * Augment the generic PersistentRooted interface when T = Value with type-querying,
- * value-extracting, and mutating operations.
- */
 template <>
 class PersistentRootedBase : public MutableValueOperations>
-{
-    friend class ValueOperations>;
-    const JS::Value * extract() const {
-        return static_cast*>(this)->address();
-    }
-
-    friend class MutableValueOperations>;
-    JS::Value * extractMutable() {
-        return static_cast*>(this)->address();
-    }
-};
+{};
 
 /*
  * If the Value is a GC pointer type, convert to that type and call |f| with
diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js
index e64cd5806b18..f019d35e1f7f 100644
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -251,23 +251,8 @@ function ignoreGCFunction(mangled)
 
 function stripUCSAndNamespace(name)
 {
-    if (name.startsWith('struct '))
-        name = name.substr(7);
-    if (name.startsWith('class '))
-        name = name.substr(6);
-    if (name.startsWith('const '))
-        name = name.substr(6);
-    if (name.startsWith('js::ctypes::'))
-        name = name.substr(12);
-    if (name.startsWith('js::'))
-        name = name.substr(4);
-    if (name.startsWith('JS::'))
-        name = name.substr(4);
-    if (name.startsWith('mozilla::dom::'))
-        name = name.substr(14);
-    if (name.startsWith('mozilla::'))
-        name = name.substr(9);
-
+    name = name.replace(/(struct|class|union|const) /g, "");
+    name = name.replace(/(js::ctypes::|js::|JS::|mozilla::dom::|mozilla::)/g, "");
     return name;
 }
 
diff --git a/js/src/devtools/rootAnalysis/loadCallgraph.js b/js/src/devtools/rootAnalysis/loadCallgraph.js
index 022fcdeb8a84..934635fe17e9 100644
--- a/js/src/devtools/rootAnalysis/loadCallgraph.js
+++ b/js/src/devtools/rootAnalysis/loadCallgraph.js
@@ -74,8 +74,9 @@ function loadCallgraph(file)
     var suppressedFieldCalls = {};
     var resolvedFunctions = {};
 
-    var textLines = snarf(file).split('\n');
-    for (var line of textLines) {
+    for (var line of readFileLines_gen(file)) {
+        line = line.replace(/\n/, "");
+
         var match;
         if (match = line.charAt(0) == "#" && /^\#(\d+) (.*)/.exec(line)) {
             assert(functionNames.length == match[1]);
diff --git a/js/src/devtools/rootAnalysis/utility.js b/js/src/devtools/rootAnalysis/utility.js
index a460117e207c..b958d226c751 100644
--- a/js/src/devtools/rootAnalysis/utility.js
+++ b/js/src/devtools/rootAnalysis/utility.js
@@ -145,3 +145,32 @@ function xdbLibrary()
         free_string: lib.declare("xdb_free", ctypes.default_abi, ctypes.void_t, ctypes.char.ptr)
     };
 }
+
+function cLibrary()
+{
+    var lib;
+    try {
+        lib = ctypes.open("libc.so.6");
+    } catch(e) {
+        lib = ctypes.open("libc.so");
+    }
+
+    return {
+        fopen: lib.declare("fopen", ctypes.default_abi, ctypes.void_t.ptr, ctypes.char.ptr, ctypes.char.ptr),
+        getline: lib.declare("getline", ctypes.default_abi, ctypes.ssize_t, ctypes.char.ptr.ptr, ctypes.size_t.ptr, ctypes.void_t.ptr),
+        fclose: lib.declare("fopen", ctypes.default_abi, ctypes.int, ctypes.void_t.ptr),
+        setvbuf: lib.declare("setvbuf", ctypes.default_abi, ctypes.int, ctypes.void_t.ptr, ctypes.char.ptr, ctypes.int, ctypes.size_t),
+    };
+}
+
+function* readFileLines_gen(filename)
+{
+  var libc = cLibrary();
+  var linebuf = ctypes.char.array(4096)();
+  var bufsize = ctypes.size_t(4096);
+  var fp = libc.fopen(filename, "r");
+  var bufp = ctypes.char.ptr(linebuf.addressOfElement(0));
+  while (libc.getline(bufp.address(), bufsize.address(), fp) > 0)
+    yield bufp.readString();
+  libc.fclose(fp);
+}
diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
index de87a3d8a7ed..ae6a5748b8f2 100644
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -4574,7 +4574,7 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs)
 
 bool
 ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObjects, MutableHandleValue vp,
-                            NewObjectKind newKind)
+                            Value* compare, size_t ncompare, NewObjectKind newKind)
 {
     MOZ_ASSERT(newKind == TenuredObject || newKind == SingletonObject);
 
@@ -4625,7 +4625,7 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje
             return false;
         size_t idx;
         for (idx = 0; pn; idx++, pn = pn->pn_next) {
-            if (!pn->getConstantValue(cx, allowObjects, values[idx]))
+            if (!pn->getConstantValue(cx, allowObjects, values[idx], values.begin(), idx))
                 return false;
             if (values[idx].isMagic(JS_GENERIC_MAGIC)) {
                 vp.setMagic(JS_GENERIC_MAGIC);
@@ -4639,6 +4639,9 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje
         if (!obj)
             return false;
 
+        if (!CombineArrayElementTypes(cx, obj, compare, ncompare))
+            return false;
+
         vp.setObject(*obj);
         return true;
       }
@@ -4685,6 +4688,9 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje
         if (!obj)
             return false;
 
+        if (!CombinePlainObjectPropertyTypes(cx, obj, compare, ncompare))
+            return false;
+
         vp.setObject(*obj);
         return true;
       }
@@ -4700,7 +4706,7 @@ BytecodeEmitter::emitSingletonInitialiser(ParseNode* pn)
     NewObjectKind newKind = (pn->getKind() == PNK_OBJECT) ? SingletonObject : TenuredObject;
 
     RootedValue value(cx);
-    if (!pn->getConstantValue(cx, ParseNode::AllowObjects, &value, newKind))
+    if (!pn->getConstantValue(cx, ParseNode::AllowObjects, &value, nullptr, 0, newKind))
         return false;
 
     MOZ_ASSERT_IF(newKind == SingletonObject, value.toObject().isSingleton());
diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h
index 46c1a2677ca1..cead3676fe49 100644
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -916,6 +916,7 @@ class ParseNode
     };
 
     bool getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObjects, MutableHandleValue vp,
+                          Value* compare = nullptr, size_t ncompare = 0,
                           NewObjectKind newKind = TenuredObject);
     inline bool isConstant();
 
diff --git a/js/src/gc/Barrier.h b/js/src/gc/Barrier.h
index be47844b3485..7be7bfcc1a95 100644
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -342,12 +342,7 @@ class BarrieredBase : public BarrieredBaseMixins
 
 template <>
 class BarrieredBaseMixins : public ValueOperations >
-{
-    friend class ValueOperations >;
-    const JS::Value * extract() const {
-        return static_cast*>(this)->unsafeGet();
-    }
-};
+{};
 
 /*
  * PreBarriered only automatically handles pre-barriers. Post-barriers must
diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp
index 7f48bfda44aa..49d83ddca4c3 100644
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1900,7 +1900,7 @@ js::gc::StoreBuffer::MonoTypeBuffer::trace(StoreBuffer* owner, TenuringTracer
     mozilla::ReentrancyGuard g(*owner);
     MOZ_ASSERT(owner->isEnabled());
     MOZ_ASSERT(stores_.initialized());
-    sinkStores(owner);
+    sinkStore(owner);
     for (typename StoreSet::Range r = stores_.all(); !r.empty(); r.popFront())
         r.front().trace(mover);
 }
diff --git a/js/src/gc/StoreBuffer.h b/js/src/gc/StoreBuffer.h
index 2b1243e065cd..d0484524d18a 100644
--- a/js/src/gc/StoreBuffer.h
+++ b/js/src/gc/StoreBuffer.h
@@ -62,17 +62,15 @@ class StoreBuffer
         StoreSet stores_;
 
         /*
-         * A small, fixed-size buffer in front of the canonical set to simplify
-         * insertion via jit code.
+         * A one element cache in front of the canonical set to speed up
+         * temporary instances of RelocatablePtr.
          */
-        const static size_t NumBufferEntries = 4096 / sizeof(T);
-        T buffer_[NumBufferEntries];
-        T* insert_;
+        T last_;
 
         /* Maximum number of entries before we request a minor GC. */
         const static size_t MaxEntries = 48 * 1024 / sizeof(T);
 
-        explicit MonoTypeBuffer() { clearBuffer(); }
+        explicit MonoTypeBuffer() : last_(T()) {}
         ~MonoTypeBuffer() { stores_.finish(); }
 
         bool init() {
@@ -82,13 +80,8 @@ class StoreBuffer
             return true;
         }
 
-        void clearBuffer() {
-            JS_POISON(buffer_, JS_EMPTY_STOREBUFFER_PATTERN, NumBufferEntries * sizeof(T));
-            insert_ = buffer_;
-        }
-
         void clear() {
-            clearBuffer();
+            last_ = T();
             if (stores_.initialized())
                 stores_.clear();
         }
@@ -96,33 +89,35 @@ class StoreBuffer
         /* Add one item to the buffer. */
         void put(StoreBuffer* owner, const T& t) {
             MOZ_ASSERT(stores_.initialized());
-            *insert_++ = t;
-            if (MOZ_UNLIKELY(insert_ == buffer_ + NumBufferEntries))
-                sinkStores(owner);
+            sinkStore(owner);
+            last_ = t;
+        }
+
+        /* Remove an item from the store buffer. */
+        void unput(StoreBuffer* owner, const T& v) {
+            // Fast, hashless remove of last put.
+            if (last_ == v) {
+                last_ = T();
+                return;
+            }
+            stores_.remove(v);
         }
 
         /* Move any buffered stores to the canonical store set. */
-        void sinkStores(StoreBuffer* owner) {
+        void sinkStore(StoreBuffer* owner) {
             MOZ_ASSERT(stores_.initialized());
-
-            for (T* p = buffer_; p < insert_; ++p) {
-                if (!stores_.put(*p))
-                    CrashAtUnhandlableOOM("Failed to allocate for MonoTypeBuffer::sinkStores.");
+            if (last_) {
+                if (!stores_.put(last_))
+                    CrashAtUnhandlableOOM("Failed to allocate for MonoTypeBuffer::put.");
             }
-            clearBuffer();
+            last_ = T();
 
             if (MOZ_UNLIKELY(stores_.count() > MaxEntries))
                 owner->setAboutToOverflow();
         }
 
-        /* Remove an item from the store buffer. */
-        void unput(StoreBuffer* owner, const T& v) {
-            sinkStores(owner);
-            stores_.remove(v);
-        }
-
         bool has(StoreBuffer* owner, const T& v) {
-            sinkStores(owner);
+            sinkStore(owner);
             return stores_.has(v);
         }
 
@@ -226,6 +221,8 @@ class StoreBuffer
         CellPtrEdge untagged() const { return CellPtrEdge((Cell**)(uintptr_t(edge) & ~1)); }
         bool isTagged() const { return bool(uintptr_t(edge) & 1); }
 
+        explicit operator bool() const { return edge != nullptr; }
+
         typedef PointerEdgeHasher Hasher;
     };
 
@@ -251,6 +248,8 @@ class StoreBuffer
         ValueEdge untagged() const { return ValueEdge((JS::Value*)(uintptr_t(edge) & ~1)); }
         bool isTagged() const { return bool(uintptr_t(edge) & 1); }
 
+        explicit operator bool() const { return edge != nullptr; }
+
         typedef PointerEdgeHasher Hasher;
     };
 
@@ -293,6 +292,8 @@ class StoreBuffer
 
         void trace(TenuringTracer& mover) const;
 
+        explicit operator bool() const { return objectAndKind_ != 0; }
+
         typedef struct {
             typedef SlotsEdge Lookup;
             static HashNumber hash(const Lookup& l) { return l.objectAndKind_ ^ l.start_ ^ l.count_; }
@@ -319,6 +320,8 @@ class StoreBuffer
 
         void trace(TenuringTracer& mover) const;
 
+        explicit operator bool() const { return edge != nullptr; }
+
         typedef PointerEdgeHasher Hasher;
     };
 
diff --git a/js/src/jit-test/tests/basic/bug1190733.js b/js/src/jit-test/tests/basic/bug1190733.js
new file mode 100644
index 000000000000..2b3f4a419df8
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1190733.js
@@ -0,0 +1,7 @@
+
+x = [];
+Array.prototype.push.call(x, Uint8ClampedArray);
+(function() {
+    x.length = 9;
+})();
+Array.prototype.reverse.call(x);
diff --git a/js/src/jit-test/tests/basic/homogenous-literals.js b/js/src/jit-test/tests/basic/homogenous-literals.js
new file mode 100644
index 000000000000..d9e3e7761054
--- /dev/null
+++ b/js/src/jit-test/tests/basic/homogenous-literals.js
@@ -0,0 +1,50 @@
+
+function processNoProperty(a) {
+    var total = 0;
+    for (var i = 0; i < a.length; i++) {
+        var sa = a[i];
+        for (var j = 0; j < sa.length; j++)
+            total += sa[j];
+    }
+    assertEq(total, 22);
+}
+
+var literalArray = [
+    [1,2,3,4],
+    [1.5,2.5,3.5,4.5]
+];
+
+var jsonArray = JSON.parse(`[
+    [1,2,3,4],
+    [1.5,2.5,3.5,4.5]
+]`);
+
+for (var i = 0; i < 1000; i++) {
+    processNoProperty(literalArray);
+    processNoProperty(jsonArray);
+}
+
+function processWithProperty(a) {
+    var total = 0;
+    for (var i = 0; i < a.length; i++) {
+        var sa = a[i].p;
+        for (var j = 0; j < sa.length; j++)
+            total += sa[j];
+    }
+    assertEq(total, 22);
+}
+
+var literalPropertyArray = [
+    {p:[1,2,3,4]},
+    {p:[1.5,2.5,3.5,4.5]}
+];
+
+var jsonPropertyArray = JSON.parse(`[
+    {"p":[1,2,3,4]},
+    {"p":[1.5,2.5,3.5,4.5]}
+]`);
+
+for (var i = 0; i < 1000; i++) {
+    processWithProperty(literalPropertyArray);
+    processWithProperty(jsonPropertyArray);
+}
diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp
index 9eeaffb75598..db1e31ddbfdb 100644
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1628,7 +1628,7 @@ BaselineCompiler::emitUnaryArith()
     frame.popRegsAndSync(1);
 
     // Call IC
-    ICUnaryArith_Fallback::Compiler stubCompiler(cx);
+    ICUnaryArith_Fallback::Compiler stubCompiler(cx, ICStubCompiler::Engine::Baseline);
     if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
         return false;
 
diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp
index ae9ad0652d65..78e1a1d7a292 100644
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -6,7 +6,6 @@
 
 #include "jit/BaselineIC.h"
 
-#include "mozilla/Casting.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/SizePrintfMacros.h"
 #include "mozilla/TemplateLib.h"
@@ -42,7 +41,6 @@
 #include "vm/StringObject-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
-using mozilla::BitwiseCast;
 using mozilla::DebugOnly;
 
 namespace js {
@@ -1861,143 +1859,6 @@ ICToNumber_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
     return tailCallVM(DoToNumberFallbackInfo, masm);
 }
 
-//
-// UnaryArith_Fallback
-//
-
-static bool
-DoUnaryArithFallback(JSContext* cx, BaselineFrame* frame, ICUnaryArith_Fallback* stub_,
-                     HandleValue val, MutableHandleValue res)
-{
-    // This fallback stub may trigger debug mode toggling.
-    DebugModeOSRVolatileStub stub(frame, stub_);
-
-    RootedScript script(cx, frame->script());
-    jsbytecode* pc = stub->icEntry()->pc(script);
-    JSOp op = JSOp(*pc);
-    FallbackICSpew(cx, stub, "UnaryArith(%s)", js_CodeName[op]);
-
-    switch (op) {
-      case JSOP_BITNOT: {
-        int32_t result;
-        if (!BitNot(cx, val, &result))
-            return false;
-        res.setInt32(result);
-        break;
-      }
-      case JSOP_NEG:
-        if (!NegOperation(cx, script, pc, val, res))
-            return false;
-        break;
-      default:
-        MOZ_CRASH("Unexpected op");
-    }
-
-    // Check if debug mode toggling made the stub invalid.
-    if (stub.invalid())
-        return true;
-
-    if (res.isDouble())
-        stub->setSawDoubleResult();
-
-    if (stub->numOptimizedStubs() >= ICUnaryArith_Fallback::MAX_OPTIMIZED_STUBS) {
-        // TODO: Discard/replace stubs.
-        return true;
-    }
-
-    if (val.isInt32() && res.isInt32()) {
-        JitSpew(JitSpew_BaselineIC, "  Generating %s(Int32 => Int32) stub", js_CodeName[op]);
-        ICUnaryArith_Int32::Compiler compiler(cx, op);
-        ICStub* int32Stub = compiler.getStub(compiler.getStubSpace(script));
-        if (!int32Stub)
-            return false;
-        stub->addNewStub(int32Stub);
-        return true;
-    }
-
-    if (val.isNumber() && res.isNumber() && cx->runtime()->jitSupportsFloatingPoint) {
-        JitSpew(JitSpew_BaselineIC, "  Generating %s(Number => Number) stub", js_CodeName[op]);
-
-        // Unlink int32 stubs, the double stub handles both cases and TI specializes for both.
-        stub->unlinkStubsWithKind(cx, ICStub::UnaryArith_Int32);
-
-        ICUnaryArith_Double::Compiler compiler(cx, op);
-        ICStub* doubleStub = compiler.getStub(compiler.getStubSpace(script));
-        if (!doubleStub)
-            return false;
-        stub->addNewStub(doubleStub);
-        return true;
-    }
-
-    return true;
-}
-
-typedef bool (*DoUnaryArithFallbackFn)(JSContext*, BaselineFrame*, ICUnaryArith_Fallback*,
-                                       HandleValue, MutableHandleValue);
-static const VMFunction DoUnaryArithFallbackInfo =
-    FunctionInfo(DoUnaryArithFallback, TailCall, PopValues(1));
-
-bool
-ICUnaryArith_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    MOZ_ASSERT(engine_ == Engine::Baseline);
-    MOZ_ASSERT(R0 == JSReturnOperand);
-
-    // Restore the tail call register.
-    EmitRestoreTailCallReg(masm);
-
-    // Ensure stack is fully synced for the expression decompiler.
-    masm.pushValue(R0);
-
-    // Push arguments.
-    masm.pushValue(R0);
-    masm.push(ICStubReg);
-    pushFramePtr(masm, R0.scratchReg());
-
-    return tailCallVM(DoUnaryArithFallbackInfo, masm);
-}
-
-bool
-ICUnaryArith_Double::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    MOZ_ASSERT(engine_ == Engine::Baseline);
-
-    Label failure;
-    masm.ensureDouble(R0, FloatReg0, &failure);
-
-    MOZ_ASSERT(op == JSOP_NEG || op == JSOP_BITNOT);
-
-    if (op == JSOP_NEG) {
-        masm.negateDouble(FloatReg0);
-        masm.boxDouble(FloatReg0, R0);
-    } else {
-        // Truncate the double to an int32.
-        Register scratchReg = R1.scratchReg();
-
-        Label doneTruncate;
-        Label truncateABICall;
-        masm.branchTruncateDouble(FloatReg0, scratchReg, &truncateABICall);
-        masm.jump(&doneTruncate);
-
-        masm.bind(&truncateABICall);
-        masm.setupUnalignedABICall(scratchReg);
-        masm.passABIArg(FloatReg0, MoveOp::DOUBLE);
-        masm.callWithABI(BitwiseCast(JS::ToInt32));
-        masm.storeCallResult(scratchReg);
-
-        masm.bind(&doneTruncate);
-        masm.not32(scratchReg);
-        masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
-    }
-
-    EmitReturnFromIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
 //
 // GetElem_Fallback
 //
@@ -3293,7 +3154,7 @@ ICGetElemNativeCompiler::emitCallScripted(MacroAssembler& masm, Register objR
     // Push argc, callee, and descriptor.
     {
         Register callScratch = regs.takeAny();
-        EmitCreateStubFrameDescriptor(masm, callScratch);
+        EmitBaselineCreateStubFrameDescriptor(masm, callScratch);
         masm.Push(Imm32(0));  // ActualArgc is 0
         masm.Push(callee);
         masm.Push(callScratch);
@@ -7215,7 +7076,7 @@ ICGetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm)
     // Note that we use Push, not push, so that callJit will align the stack
     // properly on ARM.
     masm.Push(R0);
-    EmitCreateStubFrameDescriptor(masm, scratch);
+    EmitBaselineCreateStubFrameDescriptor(masm, scratch);
     masm.Push(Imm32(0));  // ActualArgc is 0
     masm.Push(callee);
     masm.Push(scratch);
@@ -8747,7 +8608,7 @@ ICSetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm)
     // Stack: [ ..., R0, R1, ..STUBFRAME-HEADER.., padding? ]
     masm.PushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE));
     masm.Push(R0);
-    EmitCreateStubFrameDescriptor(masm, scratch);
+    EmitBaselineCreateStubFrameDescriptor(masm, scratch);
     masm.Push(Imm32(1));  // ActualArgc is 1
     masm.Push(callee);
     masm.Push(scratch);
@@ -8968,7 +8829,7 @@ TryAttachFunCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script,
 
 static bool
 GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args,
-                           MutableHandleObject res)
+                           MutableHandleObject res, bool* skipAttach)
 {
     // Check for natives to which template objects can be attached. This is
     // done to provide templates to Ion for inlining these natives later on.
@@ -8984,10 +8845,17 @@ GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args,
             count = args[0].toInt32();
 
         if (count <= ArrayObject::EagerAllocationMaxLength) {
+            ObjectGroup* group = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array);
+            if (!group)
+                return false;
+            if (group->maybePreliminaryObjects()) {
+                *skipAttach = true;
+                return true;
+            }
+
             // With this and other array templates, set forceAnalyze so that we
             // don't end up with a template whose structure might change later.
-            res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, count, TenuredObject,
-                                                                   /* forceAnalyze = */ true));
+            res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, count, TenuredObject));
             if (!res)
                 return false;
             return true;
@@ -8995,17 +8863,30 @@ GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args,
     }
 
     if (native == js::array_concat || native == js::array_slice) {
-        if (args.thisv().isObject() && !args.thisv().toObject().isSingleton()) {
-            res.set(NewFullyAllocatedArrayTryReuseGroup(cx, &args.thisv().toObject(), 0,
-                                                        TenuredObject, /* forceAnalyze = */ true));
-            if (!res)
-                return false;
+        if (args.thisv().isObject()) {
+            JSObject* obj = &args.thisv().toObject();
+            if (!obj->isSingleton()) {
+                if (obj->group()->maybePreliminaryObjects()) {
+                    *skipAttach = true;
+                    return true;
+                }
+                res.set(NewFullyAllocatedArrayTryReuseGroup(cx, &args.thisv().toObject(), 0,
+                                                            TenuredObject));
+                return !!res;
+            }
         }
     }
 
     if (native == js::str_split && args.length() == 1 && args[0].isString()) {
-        res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, 0, TenuredObject,
-                                                               /* forceAnalyze = */ true));
+        ObjectGroup* group = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array);
+        if (!group)
+            return false;
+        if (group->maybePreliminaryObjects()) {
+            *skipAttach = true;
+            return true;
+        }
+
+        res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, 0, TenuredObject));
         if (!res)
             return false;
         return true;
@@ -9304,9 +9185,15 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
 
         RootedObject templateObject(cx);
         if (MOZ_LIKELY(!isSpread)) {
+            bool skipAttach = false;
             CallArgs args = CallArgsFromVp(argc, vp);
-            if (!GetTemplateObjectForNative(cx, fun->native(), args, &templateObject))
+            if (!GetTemplateObjectForNative(cx, fun->native(), args, &templateObject, &skipAttach))
                 return false;
+            if (skipAttach) {
+                *handled = true;
+                return true;
+            }
+            MOZ_ASSERT_IF(templateObject, !templateObject->group()->maybePreliminaryObjects());
         }
 
         JitSpew(JitSpew_BaselineIC, "  Generating Call_Native stub (fun=%p, cons=%s, spread=%s)",
@@ -10171,7 +10058,7 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm)
     masm.popValue(val);
     callee = masm.extractObject(val, ExtractTemp0);
 
-    EmitCreateStubFrameDescriptor(masm, scratch);
+    EmitBaselineCreateStubFrameDescriptor(masm, scratch);
 
     // Note that we use Push, not push, so that callJit will align the stack
     // properly on ARM.
@@ -10474,7 +10361,7 @@ ICCall_Native::Compiler::generateStubCode(MacroAssembler& masm)
     masm.push(argcReg);
 
     Register scratch = regs.takeAny();
-    EmitCreateStubFrameDescriptor(masm, scratch);
+    EmitBaselineCreateStubFrameDescriptor(masm, scratch);
     masm.push(scratch);
     masm.push(ICTailCallReg);
     masm.enterFakeExitFrame(NativeExitFrameLayout::Token());
@@ -10572,7 +10459,7 @@ ICCall_ClassHook::Compiler::generateStubCode(MacroAssembler& masm)
     // Construct a native exit frame.
     masm.push(argcReg);
 
-    EmitCreateStubFrameDescriptor(masm, scratch);
+    EmitBaselineCreateStubFrameDescriptor(masm, scratch);
     masm.push(scratch);
     masm.push(ICTailCallReg);
     masm.enterFakeExitFrame(NativeExitFrameLayout::Token());
@@ -10659,7 +10546,7 @@ ICCall_ScriptedApplyArray::Compiler::generateStubCode(MacroAssembler& masm)
     // All pushes after this use Push instead of push to make sure ARM can align
     // stack properly for call.
     Register scratch = regs.takeAny();
-    EmitCreateStubFrameDescriptor(masm, scratch);
+    EmitBaselineCreateStubFrameDescriptor(masm, scratch);
 
     // Reload argc from length of array.
     masm.extractObject(arrayVal, argcReg);
@@ -10760,7 +10647,7 @@ ICCall_ScriptedApplyArguments::Compiler::generateStubCode(MacroAssembler& masm)
     // All pushes after this use Push instead of push to make sure ARM can align
     // stack properly for call.
     Register scratch = regs.takeAny();
-    EmitCreateStubFrameDescriptor(masm, scratch);
+    EmitBaselineCreateStubFrameDescriptor(masm, scratch);
 
     masm.loadPtr(Address(BaselineFrameReg, 0), argcReg);
     masm.loadPtr(Address(argcReg, BaselineFrame::offsetOfNumActualArgs()), argcReg);
@@ -10893,7 +10780,7 @@ ICCall_ScriptedFunCall::Compiler::generateStubCode(MacroAssembler& masm)
     callee = masm.extractObject(val, ExtractTemp0);
 
     Register scratch = regs.takeAny();
-    EmitCreateStubFrameDescriptor(masm, scratch);
+    EmitBaselineCreateStubFrameDescriptor(masm, scratch);
 
     // Note that we use Push, not push, so that callJit will align the stack
     // properly on ARM.
diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h
index 04f7b9e51dbb..e9bd1b7f6bf5 100644
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -1104,94 +1104,6 @@ class ICToNumber_Fallback : public ICFallbackStub
     };
 };
 
-// UnaryArith
-//     JSOP_BITNOT
-//     JSOP_NEG
-
-class ICUnaryArith_Fallback : public ICFallbackStub
-{
-    friend class ICStubSpace;
-
-    explicit ICUnaryArith_Fallback(JitCode* stubCode)
-      : ICFallbackStub(UnaryArith_Fallback, stubCode)
-    {
-        extra_ = 0;
-    }
-
-  public:
-    static const uint32_t MAX_OPTIMIZED_STUBS = 8;
-
-    bool sawDoubleResult() {
-        return extra_;
-    }
-    void setSawDoubleResult() {
-        extra_ = 1;
-    }
-
-    // Compiler for this stub kind.
-    class Compiler : public ICStubCompiler {
-      protected:
-        bool generateStubCode(MacroAssembler& masm);
-
-      public:
-        explicit Compiler(JSContext* cx)
-          : ICStubCompiler(cx, ICStub::UnaryArith_Fallback, Engine::Baseline)
-        {}
-
-        ICStub* getStub(ICStubSpace* space) {
-            return newStub(space, getStubCode());
-        }
-    };
-};
-
-class ICUnaryArith_Int32 : public ICStub
-{
-    friend class ICStubSpace;
-
-    explicit ICUnaryArith_Int32(JitCode* stubCode)
-      : ICStub(UnaryArith_Int32, stubCode)
-    {}
-
-  public:
-    class Compiler : public ICMultiStubCompiler {
-      protected:
-        bool generateStubCode(MacroAssembler& masm);
-
-      public:
-        Compiler(JSContext* cx, JSOp op)
-          : ICMultiStubCompiler(cx, ICStub::UnaryArith_Int32, op, Engine::Baseline)
-        {}
-
-        ICStub* getStub(ICStubSpace* space) {
-            return newStub(space, getStubCode());
-        }
-    };
-};
-
-class ICUnaryArith_Double : public ICStub
-{
-    friend class ICStubSpace;
-
-    explicit ICUnaryArith_Double(JitCode* stubCode)
-      : ICStub(UnaryArith_Double, stubCode)
-    {}
-
-  public:
-    class Compiler : public ICMultiStubCompiler {
-      protected:
-        bool generateStubCode(MacroAssembler& masm);
-
-      public:
-        Compiler(JSContext* cx, JSOp op)
-          : ICMultiStubCompiler(cx, ICStub::UnaryArith_Double, op, Engine::Baseline)
-        {}
-
-        ICStub* getStub(ICStubSpace* space) {
-            return newStub(space, getStubCode());
-        }
-    };
-};
-
 // GetElem
 //      JSOP_GETELEM
 
diff --git a/js/src/jit/BaselineICList.h b/js/src/jit/BaselineICList.h
index f66d164429c4..76c0cb6d85af 100644
--- a/js/src/jit/BaselineICList.h
+++ b/js/src/jit/BaselineICList.h
@@ -49,10 +49,6 @@ namespace jit {
                                                  \
     _(ToNumber_Fallback)                         \
                                                  \
-    _(UnaryArith_Fallback)                       \
-    _(UnaryArith_Int32)                          \
-    _(UnaryArith_Double)                         \
-                                                 \
     _(Call_Fallback)                             \
     _(Call_Scripted)                             \
     _(Call_AnyScripted)                          \
diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp
index 2fe896777186..87bbe8bc4251 100644
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1729,6 +1729,10 @@ CodeGenerator::visitUnarySharedStub(LUnarySharedStub* lir)
 {
     JSOp jsop = JSOp(*lir->mir()->resumePoint()->pc());
     switch (jsop) {
+      case JSOP_BITNOT:
+      case JSOP_NEG:
+        emitSharedStub(ICStub::Kind::UnaryArith_Fallback, lir);
+        break;
       default:
         MOZ_CRASH("Unsupported jsop in shared stubs.");
     }
@@ -7238,22 +7242,23 @@ CodeGenerator::visitArrayConcat(LArrayConcat* lir)
     // inline and pass it to the stub. Else, we just pass nullptr and the stub falls
     // back to a slow path.
     Label fail, call;
-    if (lir->mir()->unboxedType() == JSVAL_TYPE_MAGIC) {
-        masm.loadPtr(Address(lhs, NativeObject::offsetOfElements()), temp1);
-        masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
-        masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
-
-        masm.loadPtr(Address(rhs, NativeObject::offsetOfElements()), temp1);
-        masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
-        masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
-    } else {
+    if (lir->mir()->unboxedThis()) {
         masm.load32(Address(lhs, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), temp1);
         masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp1);
         masm.branch32(Assembler::NotEqual, Address(lhs, UnboxedArrayObject::offsetOfLength()), temp1, &fail);
-
+    } else {
+        masm.loadPtr(Address(lhs, NativeObject::offsetOfElements()), temp1);
+        masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
+        masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
+    }
+    if (lir->mir()->unboxedArg()) {
         masm.load32(Address(rhs, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), temp1);
         masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp1);
         masm.branch32(Assembler::NotEqual, Address(rhs, UnboxedArrayObject::offsetOfLength()), temp1, &fail);
+    } else {
+        masm.loadPtr(Address(rhs, NativeObject::offsetOfElements()), temp1);
+        masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
+        masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
     }
 
     // Try to allocate an object.
@@ -7869,6 +7874,11 @@ CodeGenerator::linkSharedStubs(JSContext* cx)
             stub = stubCompiler.getStub(&stubSpace_);
             break;
           }
+          case ICStub::Kind::UnaryArith_Fallback: {
+            ICUnaryArith_Fallback::Compiler stubCompiler(cx, ICStubCompiler::Engine::IonMonkey);
+            stub = stubCompiler.getStub(&stubSpace_);
+            break;
+          }
           default:
             MOZ_CRASH("Unsupported shared stub.");
         }
diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
index b98daf72ae28..8474f57a7b4a 100644
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -4484,19 +4484,49 @@ IonBuilder::pushConstant(const Value& v)
     return true;
 }
 
+bool
+IonBuilder::bitnotTrySpecialized(bool* emitted, MDefinition* input)
+{
+    MOZ_ASSERT(*emitted == false);
+
+    // Try to emit a specialized bitnot instruction based on the input type
+    // of the operand.
+
+    if (input->mightBeType(MIRType_Object) || input->mightBeType(MIRType_Symbol))
+        return true;
+
+    MBitNot* ins = MBitNot::New(alloc(), input);
+    ins->setSpecialization(MIRType_Int32);
+
+    current->add(ins);
+    current->push(ins);
+
+    *emitted = true;
+    return true;
+}
+
 bool
 IonBuilder::jsop_bitnot()
 {
+    bool emitted = false;
+
     MDefinition* input = current->pop();
+
+    if (!forceInlineCaches()) {
+        if (!bitnotTrySpecialized(&emitted, input) || emitted)
+            return emitted;
+    }
+
+    if (!arithTrySharedStub(&emitted, JSOP_BITNOT, nullptr, input) || emitted)
+        return emitted;
+
+    // Not possible to optimize. Do a slow vm call.
     MBitNot* ins = MBitNot::New(alloc(), input);
 
     current->add(ins);
-    ins->infer();
-
     current->push(ins);
-    if (ins->isEffectful() && !resumeAfter(ins))
-        return false;
-    return true;
+    MOZ_ASSERT(ins->isEffectful());
+    return resumeAfter(ins);
 }
 bool
 IonBuilder::jsop_bitop(JSOp op)
@@ -4672,21 +4702,42 @@ IonBuilder::binaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
 }
 
 bool
-IonBuilder::binaryArithTrySharedStub(bool* emitted, JSOp op,
-                                     MDefinition* left, MDefinition* right)
+IonBuilder::arithTrySharedStub(bool* emitted, JSOp op,
+                               MDefinition* left, MDefinition* right)
 {
     MOZ_ASSERT(*emitted == false);
+    JSOp actualOp = JSOp(*pc);
 
     // Try to emit a shared stub cache.
 
     if (js_JitOptions.disableSharedStubs)
         return true;
 
-    // It is not possible for shared stubs to impersonate another op.
-    if (JSOp(*pc) != op)
+    // The actual jsop 'jsop_pos' is not supported yet.
+    if (actualOp == JSOP_POS)
         return true;
 
-    MBinarySharedStub *stub = MBinarySharedStub::New(alloc(), left, right);
+    MInstruction* stub = nullptr;
+    switch (actualOp) {
+      case JSOP_NEG:
+      case JSOP_BITNOT:
+        MOZ_ASSERT_IF(op == JSOP_MUL, left->isConstantValue() &&
+                                      left->constantValue().toInt32() == -1);
+        MOZ_ASSERT_IF(op != JSOP_MUL, !left);
+
+        stub = MUnarySharedStub::New(alloc(), right);
+        break;
+      case JSOP_ADD:
+      case JSOP_SUB:
+      case JSOP_MUL:
+      case JSOP_DIV:
+      case JSOP_MOD:
+        stub = MBinarySharedStub::New(alloc(), left, right);
+        break;
+      default:
+        MOZ_CRASH("unsupported arith");
+    }
+
     current->add(stub);
     current->push(stub);
 
@@ -4717,7 +4768,7 @@ IonBuilder::jsop_binary_arith(JSOp op, MDefinition* left, MDefinition* right)
             return emitted;
     }
 
-    if (!binaryArithTrySharedStub(&emitted, op, left, right) || emitted)
+    if (!arithTrySharedStub(&emitted, op, left, right) || emitted)
         return emitted;
 
     // Not possible to optimize. Do a slow vm call.
@@ -4791,9 +4842,7 @@ IonBuilder::jsop_neg()
 
     MDefinition* right = current->pop();
 
-    if (!jsop_binary_arith(JSOP_MUL, negator, right))
-        return false;
-    return true;
+    return jsop_binary_arith(JSOP_MUL, negator, right);
 }
 
 class AutoAccumulateReturns
diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h
index 800425c0d6c2..62d9cf5cb5d6 100644
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -489,7 +489,10 @@ class IonBuilder
     bool binaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
     bool binaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op, MDefinition* left,
                                                       MDefinition* right);
-    bool binaryArithTrySharedStub(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
+    bool arithTrySharedStub(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
+
+    // jsop_bitnot helpers.
+    bool bitnotTrySpecialized(bool* emitted, MDefinition* input);
 
     // binary data lookup helpers.
     TypedObjectPrediction typedObjectPrediction(MDefinition* typedObj);
diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp
index 807180dd2deb..370145c2bee3 100644
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -862,9 +862,10 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo)
     if (!thisTypes || !argTypes)
         return InliningStatus_NotInlined;
 
-    const Class* clasp = thisTypes->getKnownClass(constraints());
-    if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
+    const Class* thisClasp = thisTypes->getKnownClass(constraints());
+    if (thisClasp != &ArrayObject::class_ && thisClasp != &UnboxedArrayObject::class_)
         return InliningStatus_NotInlined;
+    bool unboxedThis = (thisClasp == &UnboxedArrayObject::class_);
     if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
                                   OBJECT_FLAG_LENGTH_OVERFLOW))
     {
@@ -872,8 +873,10 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo)
         return InliningStatus_NotInlined;
     }
 
-    if (argTypes->getKnownClass(constraints()) != clasp)
+    const Class* argClasp = argTypes->getKnownClass(constraints());
+    if (argClasp != &ArrayObject::class_ && argClasp != &UnboxedArrayObject::class_)
         return InliningStatus_NotInlined;
+    bool unboxedArg = (argClasp == &UnboxedArrayObject::class_);
     if (argTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
                                  OBJECT_FLAG_LENGTH_OVERFLOW))
     {
@@ -881,15 +884,6 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo)
         return InliningStatus_NotInlined;
     }
 
-    JSValueType unboxedType = JSVAL_TYPE_MAGIC;
-    if (clasp == &UnboxedArrayObject::class_) {
-        unboxedType = UnboxedArrayElementType(constraints(), thisArg, nullptr);
-        if (unboxedType == JSVAL_TYPE_MAGIC)
-            return InliningStatus_NotInlined;
-        if (unboxedType != UnboxedArrayElementType(constraints(), objArg, nullptr))
-            return InliningStatus_NotInlined;
-    }
-
     // Watch out for indexed properties on the prototype.
     if (ArrayPrototypeHasIndexedProperty(this, script())) {
         trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
@@ -956,7 +950,7 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo)
     MArrayConcat* ins = MArrayConcat::New(alloc(), constraints(), thisArg, objArg,
                                           templateObj,
                                           templateObj->group()->initialHeap(constraints()),
-                                          unboxedType);
+                                          unboxedThis, unboxedArg);
     current->add(ins);
     current->push(ins);
 
diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp
index f16cb85b3570..e74e0bd1747d 100644
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1998,15 +1998,6 @@ MCall::addArg(size_t argnum, MDefinition* arg)
     initOperand(argnum + NumNonArgumentOperands, arg);
 }
 
-void
-MBitNot::infer()
-{
-    if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(0)->mightBeType(MIRType_Symbol))
-        specialization_ = MIRType_None;
-    else
-        specialization_ = MIRType_Int32;
-}
-
 static inline bool
 IsConstant(MDefinition* def, double v)
 {
diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h
index 55ede231673d..0284e018440a 100644
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -5215,6 +5215,7 @@ class MBitNot
     explicit MBitNot(MDefinition* input)
       : MUnaryInstruction(input)
     {
+        specialization_ = MIRType_None;
         setResultType(MIRType_Int32);
         setMovable();
     }
@@ -5225,7 +5226,10 @@ class MBitNot
     static MBitNot* NewAsmJS(TempAllocator& alloc, MDefinition* input);
 
     MDefinition* foldsTo(TempAllocator& alloc) override;
-    void infer();
+    void setSpecialization(MIRType type) {
+        specialization_ = type;
+        setResultType(type);
+    }
 
     bool congruentTo(const MDefinition* ins) const override {
         return congruentIfOperandsEqual(ins);
@@ -9213,14 +9217,16 @@ class MArrayConcat
 {
     CompilerObject templateObj_;
     gc::InitialHeap initialHeap_;
-    JSValueType unboxedType_;
+    bool unboxedThis_, unboxedArg_;
 
     MArrayConcat(CompilerConstraintList* constraints, MDefinition* lhs, MDefinition* rhs,
-                 JSObject* templateObj, gc::InitialHeap initialHeap, JSValueType unboxedType)
+                 JSObject* templateObj, gc::InitialHeap initialHeap,
+                 bool unboxedThis, bool unboxedArg)
       : MBinaryInstruction(lhs, rhs),
         templateObj_(templateObj),
         initialHeap_(initialHeap),
-        unboxedType_(unboxedType)
+        unboxedThis_(unboxedThis),
+        unboxedArg_(unboxedArg)
     {
         setResultType(MIRType_Object);
         setResultTypeSet(MakeSingletonTypeSet(constraints, templateObj));
@@ -9232,10 +9238,10 @@ class MArrayConcat
     static MArrayConcat* New(TempAllocator& alloc, CompilerConstraintList* constraints,
                              MDefinition* lhs, MDefinition* rhs,
                              JSObject* templateObj, gc::InitialHeap initialHeap,
-                             JSValueType unboxedType)
+                             bool unboxedThis, bool unboxedArg)
     {
         return new(alloc) MArrayConcat(constraints, lhs, rhs, templateObj,
-                                       initialHeap, unboxedType);
+                                       initialHeap, unboxedThis, unboxedArg);
     }
 
     JSObject* templateObj() const {
@@ -9246,12 +9252,17 @@ class MArrayConcat
         return initialHeap_;
     }
 
-    JSValueType unboxedType() const {
-        return unboxedType_;
+    bool unboxedThis() const {
+        return unboxedThis_;
+    }
+
+    bool unboxedArg() const {
+        return unboxedArg_;
     }
 
     AliasSet getAliasSet() const override {
-        return AliasSet::Store(AliasSet::BoxedOrUnboxedElements(unboxedType()) |
+        return AliasSet::Store(AliasSet::BoxedOrUnboxedElements(unboxedThis() ? JSVAL_TYPE_INT32 : JSVAL_TYPE_MAGIC) |
+                               AliasSet::BoxedOrUnboxedElements(unboxedArg() ? JSVAL_TYPE_INT32 : JSVAL_TYPE_MAGIC) |
                                AliasSet::ObjectFields);
     }
     bool possiblyCalls() const override {
diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp
index 5539e92f3269..2f9f6faa58af 100644
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -5,6 +5,8 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/SharedIC.h"
+
+#include "mozilla/Casting.h"
 #include "mozilla/SizePrintfMacros.h"
 
 #include "jslibmath.h"
@@ -24,6 +26,8 @@
 #include "jit/MacroAssembler-inl.h"
 #include "vm/Interpreter-inl.h"
 
+using mozilla::BitwiseCast;
+
 namespace js {
 namespace jit {
 
@@ -746,7 +750,12 @@ ICStubCompiler::tailCallVM(const VMFunction& fun, MacroAssembler& masm)
 
     MOZ_ASSERT(fun.expectTailCall == TailCall);
     uint32_t argSize = fun.explicitStackSlots() * sizeof(void*);
-    EmitTailCallVM(code, masm, argSize);
+    if (engine_ == Engine::Baseline) {
+        EmitBaselineTailCallVM(code, masm, argSize);
+    } else {
+        uint32_t stackSize = argSize + fun.extraValuesToPop * sizeof(Value);
+        EmitIonTailCallVM(code, masm, stackSize);
+    }
     return true;
 }
 
@@ -760,7 +769,10 @@ ICStubCompiler::callVM(const VMFunction& fun, MacroAssembler& masm)
         return false;
 
     MOZ_ASSERT(fun.expectTailCall == NonTailCall);
-    EmitCallVM(code, masm);
+    if (engine_ == Engine::Baseline)
+        EmitBaselineCallVM(code, masm);
+    else
+        EmitIonCallVM(code, fun.explicitStackSlots(), masm);
     return true;
 }
 
@@ -778,7 +790,10 @@ ICStubCompiler::callTypeUpdateIC(MacroAssembler& masm, uint32_t objectOffset)
 void
 ICStubCompiler::enterStubFrame(MacroAssembler& masm, Register scratch)
 {
-    EmitEnterStubFrame(masm, scratch);
+    if (engine_ == Engine::Baseline)
+        EmitBaselineEnterStubFrame(masm, scratch);
+    else
+        EmitIonEnterStubFrame(masm, scratch);
 
     MOZ_ASSERT(!inStubFrame_);
     inStubFrame_ = true;
@@ -793,12 +808,21 @@ ICStubCompiler::leaveStubFrame(MacroAssembler& masm, bool calledIntoIon)
 {
     MOZ_ASSERT(entersStubFrame_ && inStubFrame_);
     inStubFrame_ = false;
-    EmitLeaveStubFrame(masm, calledIntoIon);
+
+    if (engine_ == Engine::Baseline)
+        EmitBaselineLeaveStubFrame(masm, calledIntoIon);
+    else
+        EmitIonLeaveStubFrame(masm);
 }
 
 void
 ICStubCompiler::pushFramePtr(MacroAssembler& masm, Register scratch)
 {
+    if (engine_ == Engine::IonMonkey) {
+        masm.push(Imm32(0));
+        return;
+    }
+
     if (inStubFrame_) {
         masm.loadPtr(Address(BaselineFrameReg, 0), scratch);
         masm.pushBaselineFramePtr(scratch, scratch);
@@ -1407,7 +1431,141 @@ ICBinaryArith_DoubleWithInt32::Compiler::generateStubCode(MacroAssembler& masm)
     return true;
 }
 
+//
+// UnaryArith_Fallback
+//
+
+static bool
+DoUnaryArithFallback(JSContext* cx, BaselineFrame* frame, ICUnaryArith_Fallback* stub_,
+                     HandleValue val, MutableHandleValue res)
+{
+    ICStubCompiler::Engine engine = SharedStubEngine(frame);
+    RootedScript script(cx, SharedStubScript(frame, stub_));
+
+    // This fallback stub may trigger debug mode toggling.
+    DebugModeOSRVolatileStub stub(engine, frame, stub_);
+
+    jsbytecode* pc = stub->icEntry()->pc(script);
+    JSOp op = JSOp(*pc);
+    FallbackICSpew(cx, stub, "UnaryArith(%s)", js_CodeName[op]);
+
+    switch (op) {
+      case JSOP_BITNOT: {
+        int32_t result;
+        if (!BitNot(cx, val, &result))
+            return false;
+        res.setInt32(result);
+        break;
+      }
+      case JSOP_NEG:
+        if (!NegOperation(cx, script, pc, val, res))
+            return false;
+        break;
+      default:
+        MOZ_CRASH("Unexpected op");
+    }
+
+    // Check if debug mode toggling made the stub invalid.
+    if (stub.invalid())
+        return true;
+
+    if (res.isDouble())
+        stub->setSawDoubleResult();
+
+    if (stub->numOptimizedStubs() >= ICUnaryArith_Fallback::MAX_OPTIMIZED_STUBS) {
+        // TODO: Discard/replace stubs.
+        return true;
+    }
+
+    if (val.isInt32() && res.isInt32()) {
+        JitSpew(JitSpew_BaselineIC, "  Generating %s(Int32 => Int32) stub", js_CodeName[op]);
+        ICUnaryArith_Int32::Compiler compiler(cx, op, engine);
+        ICStub* int32Stub = compiler.getStub(compiler.getStubSpace(script));
+        if (!int32Stub)
+            return false;
+        stub->addNewStub(int32Stub);
+        return true;
+    }
+
+    if (val.isNumber() && res.isNumber() && cx->runtime()->jitSupportsFloatingPoint) {
+        JitSpew(JitSpew_BaselineIC, "  Generating %s(Number => Number) stub", js_CodeName[op]);
+
+        // Unlink int32 stubs, the double stub handles both cases and TI specializes for both.
+        stub->unlinkStubsWithKind(cx, ICStub::UnaryArith_Int32);
+
+        ICUnaryArith_Double::Compiler compiler(cx, op, engine);
+        ICStub* doubleStub = compiler.getStub(compiler.getStubSpace(script));
+        if (!doubleStub)
+            return false;
+        stub->addNewStub(doubleStub);
+        return true;
+    }
+
+    return true;
+}
+
+typedef bool (*DoUnaryArithFallbackFn)(JSContext*, BaselineFrame*, ICUnaryArith_Fallback*,
+                                       HandleValue, MutableHandleValue);
+static const VMFunction DoUnaryArithFallbackInfo =
+    FunctionInfo(DoUnaryArithFallback, TailCall, PopValues(1));
+
+bool
+ICUnaryArith_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+    MOZ_ASSERT(R0 == JSReturnOperand);
+
+    // Restore the tail call register.
+    EmitRestoreTailCallReg(masm);
+
+    // Ensure stack is fully synced for the expression decompiler.
+    masm.pushValue(R0);
+
+    // Push arguments.
+    masm.pushValue(R0);
+    masm.push(ICStubReg);
+    pushFramePtr(masm, R0.scratchReg());
+
+    return tailCallVM(DoUnaryArithFallbackInfo, masm);
+}
+
+bool
+ICUnaryArith_Double::Compiler::generateStubCode(MacroAssembler& masm)
+{
+    Label failure;
+    masm.ensureDouble(R0, FloatReg0, &failure);
+
+    MOZ_ASSERT(op == JSOP_NEG || op == JSOP_BITNOT);
+
+    if (op == JSOP_NEG) {
+        masm.negateDouble(FloatReg0);
+        masm.boxDouble(FloatReg0, R0);
+    } else {
+        // Truncate the double to an int32.
+        Register scratchReg = R1.scratchReg();
+
+        Label doneTruncate;
+        Label truncateABICall;
+        masm.branchTruncateDouble(FloatReg0, scratchReg, &truncateABICall);
+        masm.jump(&doneTruncate);
+
+        masm.bind(&truncateABICall);
+        masm.setupUnalignedABICall(scratchReg);
+        masm.passABIArg(FloatReg0, MoveOp::DOUBLE);
+        masm.callWithABI(BitwiseCast(JS::ToInt32));
+        masm.storeCallResult(scratchReg);
+
+        masm.bind(&doneTruncate);
+        masm.not32(scratchReg);
+        masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
+    }
 
+    EmitReturnFromIC(masm);
+
+    // Failure case - jump to next stub
+    masm.bind(&failure);
+    EmitStubGuardFailure(masm);
+    return true;
+}
 
 } // namespace jit
 } // namespace js
diff --git a/js/src/jit/SharedIC.h b/js/src/jit/SharedIC.h
index d8054dc6943d..111b00ab5717 100644
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -1389,6 +1389,95 @@ class ICBinaryArith_DoubleWithInt32 : public ICStub
     };
 };
 
+// UnaryArith
+//     JSOP_BITNOT
+//     JSOP_NEG
+
+class ICUnaryArith_Fallback : public ICFallbackStub
+{
+    friend class ICStubSpace;
+
+    explicit ICUnaryArith_Fallback(JitCode* stubCode)
+      : ICFallbackStub(UnaryArith_Fallback, stubCode)
+    {
+        extra_ = 0;
+    }
+
+  public:
+    static const uint32_t MAX_OPTIMIZED_STUBS = 8;
+
+    bool sawDoubleResult() {
+        return extra_;
+    }
+    void setSawDoubleResult() {
+        extra_ = 1;
+    }
+
+    // Compiler for this stub kind.
+    class Compiler : public ICStubCompiler {
+      protected:
+        bool generateStubCode(MacroAssembler& masm);
+
+      public:
+        explicit Compiler(JSContext* cx, Engine engine)
+          : ICStubCompiler(cx, ICStub::UnaryArith_Fallback, engine)
+        {}
+
+        ICStub* getStub(ICStubSpace* space) {
+            return newStub(space, getStubCode());
+        }
+    };
+};
+
+class ICUnaryArith_Int32 : public ICStub
+{
+    friend class ICStubSpace;
+
+    explicit ICUnaryArith_Int32(JitCode* stubCode)
+      : ICStub(UnaryArith_Int32, stubCode)
+    {}
+
+  public:
+    class Compiler : public ICMultiStubCompiler {
+      protected:
+        bool generateStubCode(MacroAssembler& masm);
+
+      public:
+        Compiler(JSContext* cx, JSOp op, Engine engine)
+          : ICMultiStubCompiler(cx, ICStub::UnaryArith_Int32, op, engine)
+        {}
+
+        ICStub* getStub(ICStubSpace* space) {
+            return newStub(space, getStubCode());
+        }
+    };
+};
+
+class ICUnaryArith_Double : public ICStub
+{
+    friend class ICStubSpace;
+
+    explicit ICUnaryArith_Double(JitCode* stubCode)
+      : ICStub(UnaryArith_Double, stubCode)
+    {}
+
+  public:
+    class Compiler : public ICMultiStubCompiler {
+      protected:
+        bool generateStubCode(MacroAssembler& masm);
+
+      public:
+        Compiler(JSContext* cx, JSOp op, Engine engine)
+          : ICMultiStubCompiler(cx, ICStub::UnaryArith_Double, op, engine)
+        {}
+
+        ICStub* getStub(ICStubSpace* space) {
+            return newStub(space, getStubCode());
+        }
+    };
+};
+
+
 } // namespace jit
 } // namespace js
 
diff --git a/js/src/jit/SharedICList.h b/js/src/jit/SharedICList.h
index fb69f10fe40b..85e6936ba7f8 100644
--- a/js/src/jit/SharedICList.h
+++ b/js/src/jit/SharedICList.h
@@ -20,6 +20,9 @@ namespace jit {
     _(BinaryArith_BooleanWithInt32)              \
     _(BinaryArith_DoubleWithInt32)               \
                                                  \
+    _(UnaryArith_Fallback)                       \
+    _(UnaryArith_Int32)                          \
+    _(UnaryArith_Double)                         \
 
 } // namespace jit
 } // namespace js
diff --git a/js/src/jit/arm/SharedICHelpers-arm.h b/js/src/jit/arm/SharedICHelpers-arm.h
index 9f1419a01054..8e9a9cda842a 100644
--- a/js/src/jit/arm/SharedICHelpers-arm.h
+++ b/js/src/jit/arm/SharedICHelpers-arm.h
@@ -79,7 +79,7 @@ EmitChangeICReturnAddress(MacroAssembler& masm, Register reg)
 }
 
 inline void
-EmitTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t argSize)
+EmitBaselineTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t argSize)
 {
     // We assume during this that R0 and R1 have been pushed, and that R2 is
     // unused.
@@ -106,7 +106,13 @@ EmitTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t argSize)
 }
 
 inline void
-EmitCreateStubFrameDescriptor(MacroAssembler& masm, Register reg)
+EmitIonTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t stackSize)
+{
+    MOZ_CRASH("Not implemented yet.");
+}
+
+inline void
+EmitBaselineCreateStubFrameDescriptor(MacroAssembler& masm, Register reg)
 {
     // Compute stub frame size. We have to add two pointers: the stub reg and
     // previous frame pointer pushed by EmitEnterStubFrame.
@@ -118,19 +124,25 @@ EmitCreateStubFrameDescriptor(MacroAssembler& masm, Register reg)
 }
 
 inline void
-EmitCallVM(JitCode* target, MacroAssembler& masm)
+EmitBaselineCallVM(JitCode* target, MacroAssembler& masm)
 {
-    EmitCreateStubFrameDescriptor(masm, r0);
+    EmitBaselineCreateStubFrameDescriptor(masm, r0);
     masm.push(r0);
     masm.call(target);
 }
 
+inline void
+EmitIonCallVM(JitCode* target, size_t stackSlots, MacroAssembler& masm)
+{
+    MOZ_CRASH("Not implemented yet.");
+}
+
 // Size of vales pushed by EmitEnterStubFrame.
 static const uint32_t STUB_FRAME_SIZE = 4 * sizeof(void*);
 static const uint32_t STUB_FRAME_SAVED_STUB_OFFSET = sizeof(void*);
 
 inline void
-EmitEnterStubFrame(MacroAssembler& masm, Register scratch)
+EmitBaselineEnterStubFrame(MacroAssembler& masm, Register scratch)
 {
     MOZ_ASSERT(scratch != ICTailCallReg);
 
@@ -159,7 +171,13 @@ EmitEnterStubFrame(MacroAssembler& masm, Register scratch)
 }
 
 inline void
-EmitLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
+EmitIonEnterStubFrame(MacroAssembler& masm, Register scratch)
+{
+    MOZ_CRASH("Not implemented yet.");
+}
+
+inline void
+EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
 {
     // Ion frames do not save and restore the frame pointer. If we called into
     // Ion, we have to restore the stack pointer from the frame descriptor. If
@@ -183,6 +201,12 @@ EmitLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
     masm.pop(ScratchRegister);
 }
 
+inline void
+EmitIonLeaveStubFrame(MacroAssembler& masm)
+{
+    MOZ_CRASH("Not implemented yet.");
+}
+
 inline void
 EmitStowICValues(MacroAssembler& masm, int values)
 {
@@ -262,7 +286,7 @@ EmitCallTypeUpdateIC(MacroAssembler& masm, JitCode* code, uint32_t objectOffset)
     masm.j(Assembler::Equal, &success);
 
     // If the IC failed, then call the update fallback function.
-    EmitEnterStubFrame(masm, R1.scratchReg());
+    EmitBaselineEnterStubFrame(masm, R1.scratchReg());
 
     masm.loadValue(Address(BaselineStackReg, STUB_FRAME_SIZE + objectOffset), R1);
 
@@ -274,8 +298,8 @@ EmitCallTypeUpdateIC(MacroAssembler& masm, JitCode* code, uint32_t objectOffset)
     masm.loadPtr(Address(BaselineFrameReg, 0), R0.scratchReg());
     masm.pushBaselineFramePtr(R0.scratchReg(), R0.scratchReg());
 
-    EmitCallVM(code, masm);
-    EmitLeaveStubFrame(masm);
+    EmitBaselineCallVM(code, masm);
+    EmitBaselineLeaveStubFrame(masm);
 
     // Success at end.
     masm.bind(&success);
diff --git a/js/src/jit/arm/Trampoline-arm.cpp b/js/src/jit/arm/Trampoline-arm.cpp
index 9baeb12259b4..7eca4cdaee5e 100644
--- a/js/src/jit/arm/Trampoline-arm.cpp
+++ b/js/src/jit/arm/Trampoline-arm.cpp
@@ -980,7 +980,7 @@ JitRuntime::generateDebugTrapHandler(JSContext* cx)
     // stub frame has a nullptr ICStub pointer, since this pointer is marked
     // during GC.
     masm.movePtr(ImmPtr(nullptr), ICStubReg);
-    EmitEnterStubFrame(masm, scratch2);
+    EmitBaselineEnterStubFrame(masm, scratch2);
 
     JitCode* code = cx->runtime()->jitRuntime()->getVMWrapper(HandleDebugTrapInfo);
     if (!code)
@@ -988,9 +988,9 @@ JitRuntime::generateDebugTrapHandler(JSContext* cx)
 
     masm.push(lr);
     masm.push(scratch1);
-    EmitCallVM(code, masm);
+    EmitBaselineCallVM(code, masm);
 
-    EmitLeaveStubFrame(masm);
+    EmitBaselineLeaveStubFrame(masm);
 
     // If the stub returns |true|, we have to perform a forced return (return
     // from the JS frame). If the stub returns |false|, just return from the
diff --git a/js/src/jit/mips32/MacroAssembler-mips32.cpp b/js/src/jit/mips32/MacroAssembler-mips32.cpp
index 245fc96e1770..fc59e9fe8a3c 100644
--- a/js/src/jit/mips32/MacroAssembler-mips32.cpp
+++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp
@@ -2613,7 +2613,7 @@ MacroAssemblerMIPSCompat::boxNonDouble(JSValueType type, Register src,
 void
 MacroAssemblerMIPSCompat::boolValueToDouble(const ValueOperand& operand, FloatRegister dest)
 {
-    convertBoolToInt32(ScratchRegister, operand.payloadReg());
+    convertBoolToInt32(operand.payloadReg(), ScratchRegister);
     convertInt32ToDouble(ScratchRegister, dest);
 }
 
@@ -2629,7 +2629,7 @@ MacroAssemblerMIPSCompat::boolValueToFloat32(const ValueOperand& operand,
                                              FloatRegister dest)
 {
 
-    convertBoolToInt32(ScratchRegister, operand.payloadReg());
+    convertBoolToInt32(operand.payloadReg(), ScratchRegister);
     convertInt32ToFloat32(ScratchRegister, dest);
 }
 
diff --git a/js/src/jit/mips32/SharedICHelpers-mips32.h b/js/src/jit/mips32/SharedICHelpers-mips32.h
index e0269ad7e3d6..68a398a67911 100644
--- a/js/src/jit/mips32/SharedICHelpers-mips32.h
+++ b/js/src/jit/mips32/SharedICHelpers-mips32.h
@@ -78,7 +78,7 @@ EmitChangeICReturnAddress(MacroAssembler& masm, Register reg)
 }
 
 inline void
-EmitTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t argSize)
+EmitBaselineTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t argSize)
 {
     // We assume during this that R0 and R1 have been pushed, and that R2 is
     // unused.
@@ -107,7 +107,13 @@ EmitTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t argSize)
 }
 
 inline void
-EmitCreateStubFrameDescriptor(MacroAssembler& masm, Register reg)
+EmitIonTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t stackSize)
+{
+    MOZ_CRASH("Not implemented yet.");
+}
+
+inline void
+EmitBaselineCreateStubFrameDescriptor(MacroAssembler& masm, Register reg)
 {
     // Compute stub frame size. We have to add two pointers: the stub reg and
     // previous frame pointer pushed by EmitEnterStubFrame.
@@ -119,13 +125,19 @@ EmitCreateStubFrameDescriptor(MacroAssembler& masm, Register reg)
 }
 
 inline void
-EmitCallVM(JitCode* target, MacroAssembler& masm)
+EmitBaselineCallVM(JitCode* target, MacroAssembler& masm)
 {
-    EmitCreateStubFrameDescriptor(masm, t6);
+    EmitBaselineCreateStubFrameDescriptor(masm, t6);
     masm.push(t6);
     masm.call(target);
 }
 
+inline void
+EmitIonCallVM(JitCode* target, size_t stackSlots, MacroAssembler& masm)
+{
+    MOZ_CRASH("Not implemented yet.");
+}
+
 struct BaselineStubFrame {
     uintptr_t savedFrame;
     uintptr_t savedStub;
@@ -137,7 +149,7 @@ static const uint32_t STUB_FRAME_SIZE = sizeof(BaselineStubFrame);
 static const uint32_t STUB_FRAME_SAVED_STUB_OFFSET = offsetof(BaselineStubFrame, savedStub);
 
 inline void
-EmitEnterStubFrame(MacroAssembler& masm, Register scratch)
+mitBaselineEnterStubFrame(MacroAssembler& masm, Register scratch)
 {
     MOZ_ASSERT(scratch != ICTailCallReg);
 
@@ -170,7 +182,13 @@ EmitEnterStubFrame(MacroAssembler& masm, Register scratch)
 }
 
 inline void
-EmitLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
+EmitIonEnterStubFrame(MacroAssembler& masm, Register scratch)
+{
+    MOZ_CRASH("Not implemented yet.");
+}
+
+inline void
+EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
 {
     // Ion frames do not save and restore the frame pointer. If we called
     // into Ion, we have to restore the stack pointer from the frame descriptor.
@@ -198,6 +216,12 @@ EmitLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
     masm.addPtr(Imm32(STUB_FRAME_SIZE), StackPointer);
 }
 
+inline void
+EmitIonLeaveStubFrame(MacroAssembler& masm)
+{
+    MOZ_CRASH("Not implemented yet.");
+}
+
 inline void
 EmitStowICValues(MacroAssembler& masm, int values)
 {
@@ -274,7 +298,7 @@ EmitCallTypeUpdateIC(MacroAssembler& masm, JitCode* code, uint32_t objectOffset)
     masm.ma_b(R1.scratchReg(), Imm32(1), &success, Assembler::Equal, ShortJump);
 
     // If the IC failed, then call the update fallback function.
-    EmitEnterStubFrame(masm, R1.scratchReg());
+    EmitBaselineEnterStubFrame(masm, R1.scratchReg());
 
     masm.loadValue(Address(BaselineStackReg, STUB_FRAME_SIZE + objectOffset), R1);
 
@@ -286,8 +310,8 @@ EmitCallTypeUpdateIC(MacroAssembler& masm, JitCode* code, uint32_t objectOffset)
     masm.loadPtr(Address(BaselineFrameReg, 0), R0.scratchReg());
     masm.pushBaselineFramePtr(R0.scratchReg(), R0.scratchReg());
 
-    EmitCallVM(code, masm);
-    EmitLeaveStubFrame(masm);
+    EmitBaselineCallVM(code, masm);
+    EmitBaselineLeaveStubFrame(masm);
 
     // Success at end.
     masm.bind(&success);
diff --git a/js/src/jit/mips32/Trampoline-mips32.cpp b/js/src/jit/mips32/Trampoline-mips32.cpp
index e899a2b3babf..d57865c0a08d 100644
--- a/js/src/jit/mips32/Trampoline-mips32.cpp
+++ b/js/src/jit/mips32/Trampoline-mips32.cpp
@@ -948,7 +948,7 @@ JitRuntime::generateDebugTrapHandler(JSContext* cx)
     // the stub frame has a nullptr ICStub pointer, since this pointer is
     // marked during GC.
     masm.movePtr(ImmPtr(nullptr), ICStubReg);
-    EmitEnterStubFrame(masm, scratch2);
+    EmitBaselineEnterStubFrame(masm, scratch2);
 
     JitCode* code = cx->runtime()->jitRuntime()->getVMWrapper(HandleDebugTrapInfo);
     if (!code)
@@ -958,9 +958,9 @@ JitRuntime::generateDebugTrapHandler(JSContext* cx)
     masm.storePtr(ra, Address(StackPointer, sizeof(uintptr_t)));
     masm.storePtr(scratch1, Address(StackPointer, 0));
 
-    EmitCallVM(code, masm);
+    EmitBaselineCallVM(code, masm);
 
-    EmitLeaveStubFrame(masm);
+    EmitBaselineLeaveStubFrame(masm);
 
     // If the stub returns |true|, we have to perform a forced return
     // (return from the JS frame). If the stub returns |false|, just return
diff --git a/js/src/jit/none/SharedICHelpers-none.h b/js/src/jit/none/SharedICHelpers-none.h
index a35d6e0196bd..1cb33aaad097 100644
--- a/js/src/jit/none/SharedICHelpers-none.h
+++ b/js/src/jit/none/SharedICHelpers-none.h
@@ -20,11 +20,15 @@ inline void EmitCallIC(CodeOffsetLabel*, MacroAssembler&) { MOZ_CRASH(); }
 inline void EmitEnterTypeMonitorIC(MacroAssembler&, size_t v = 0) { MOZ_CRASH(); }
 inline void EmitReturnFromIC(MacroAssembler&) { MOZ_CRASH(); }
 inline void EmitChangeICReturnAddress(MacroAssembler&, Register) { MOZ_CRASH(); }
-inline void EmitTailCallVM(JitCode*, MacroAssembler&, uint32_t) { MOZ_CRASH(); }
-inline void EmitCreateStubFrameDescriptor(MacroAssembler&, Register) { MOZ_CRASH(); }
-inline void EmitCallVM(JitCode*, MacroAssembler&) { MOZ_CRASH(); }
-inline void EmitEnterStubFrame(MacroAssembler&, Register) { MOZ_CRASH(); }
-inline void EmitLeaveStubFrame(MacroAssembler&, bool v = false) { MOZ_CRASH(); }
+inline void EmitBaselineTailCallVM(JitCode*, MacroAssembler&, uint32_t) { MOZ_CRASH(); }
+inline void EmitIonTailCallVM(JitCode*, MacroAssembler&, uint32_t) { MOZ_CRASH(); }
+inline void EmitBaselineCreateStubFrameDescriptor(MacroAssembler&, Register) { MOZ_CRASH(); }
+inline void EmitBaselineCallVM(JitCode*, MacroAssembler&) { MOZ_CRASH(); }
+inline void EmitIonCallVM(JitCode*, size_t, MacroAssembler&) { MOZ_CRASH(); }
+inline void EmitBaselineEnterStubFrame(MacroAssembler&, Register) { MOZ_CRASH(); }
+inline void EmitIonEnterStubFrame(MacroAssembler&, Register) { MOZ_CRASH(); }
+inline void EmitBaselineLeaveStubFrame(MacroAssembler&, bool v = false) { MOZ_CRASH(); }
+inline void EmitIonLeaveStubFrame(MacroAssembler&) { MOZ_CRASH(); }
 inline void EmitStowICValues(MacroAssembler&, int) { MOZ_CRASH(); }
 inline void EmitUnstowICValues(MacroAssembler&, int, bool v = false) { MOZ_CRASH(); }
 inline void EmitCallTypeUpdateIC(MacroAssembler&, JitCode*, uint32_t) { MOZ_CRASH(); }
diff --git a/js/src/jit/x64/SharedICHelpers-x64.h b/js/src/jit/x64/SharedICHelpers-x64.h
index f00d2569914d..82760eaa03ea 100644
--- a/js/src/jit/x64/SharedICHelpers-x64.h
+++ b/js/src/jit/x64/SharedICHelpers-x64.h
@@ -70,7 +70,7 @@ EmitChangeICReturnAddress(MacroAssembler& masm, Register reg)
 }
 
 inline void
-EmitTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t argSize)
+EmitBaselineTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t argSize)
 {
     // We an assume during this that R0 and R1 have been pushed.
     masm.movq(BaselineFrameReg, ScratchReg);
@@ -90,7 +90,21 @@ EmitTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t argSize)
 }
 
 inline void
-EmitCreateStubFrameDescriptor(MacroAssembler& masm, Register reg)
+EmitIonTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t stackSize)
+{
+    masm.movq(Operand(esp, stackSize), ScratchReg);
+    masm.shrq(Imm32(FRAMESIZE_SHIFT), ScratchReg);
+    masm.addq(Imm32(stackSize + JitStubFrameLayout::Size() - sizeof(intptr_t)), ScratchReg);
+
+    // Push frame descriptor and perform the tail call.
+    masm.makeFrameDescriptor(ScratchReg, JitFrame_IonJS);
+    masm.push(ScratchReg);
+    masm.push(ICTailCallReg);
+    masm.jmp(target);
+}
+
+inline void
+EmitBaselineCreateStubFrameDescriptor(MacroAssembler& masm, Register reg)
 {
     // Compute stub frame size. We have to add two pointers: the stub reg and previous
     // frame pointer pushed by EmitEnterStubFrame.
@@ -102,19 +116,34 @@ EmitCreateStubFrameDescriptor(MacroAssembler& masm, Register reg)
 }
 
 inline void
-EmitCallVM(JitCode* target, MacroAssembler& masm)
+EmitBaselineCallVM(JitCode* target, MacroAssembler& masm)
 {
-    EmitCreateStubFrameDescriptor(masm, ScratchReg);
+    EmitBaselineCreateStubFrameDescriptor(masm, ScratchReg);
     masm.push(ScratchReg);
     masm.call(target);
 }
 
+inline void
+EmitIonCallVM(JitCode* target, size_t stackSlots, MacroAssembler& masm)
+{
+    uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonStub);
+    masm.Push(Imm32(descriptor));
+    masm.call(target);
+
+    // Remove rest of the frame left on the stack. We remove the return address
+    // which is implicitly poped when returning.
+    size_t framePop = sizeof(ExitFrameLayout) - sizeof(void*);
+
+    // Pop arguments from framePushed.
+    masm.implicitPop(stackSlots * sizeof(void*) + framePop);
+}
+
 // Size of vales pushed by EmitEnterStubFrame.
 static const uint32_t STUB_FRAME_SIZE = 4 * sizeof(void*);
 static const uint32_t STUB_FRAME_SAVED_STUB_OFFSET = sizeof(void*);
 
 inline void
-EmitEnterStubFrame(MacroAssembler& masm, Register)
+EmitBaselineEnterStubFrame(MacroAssembler& masm, Register)
 {
     EmitRestoreTailCallReg(masm);
 
@@ -140,7 +169,15 @@ EmitEnterStubFrame(MacroAssembler& masm, Register)
 }
 
 inline void
-EmitLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
+EmitIonEnterStubFrame(MacroAssembler& masm, Register)
+{
+    masm.pop(ICTailCallReg);
+    masm.push(ICTailCallReg);
+    masm.push(ICStubReg);
+}
+
+inline void
+EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
 {
     // Ion frames do not save and restore the frame pointer. If we called
     // into Ion, we have to restore the stack pointer from the frame descriptor.
@@ -165,6 +202,13 @@ EmitLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
     masm.storePtr(ICTailCallReg, Address(BaselineStackReg, 0));
 }
 
+inline void
+EmitIonLeaveStubFrame(MacroAssembler& masm)
+{
+    masm.pop(ICStubReg);
+    masm.pop(ICTailCallReg);
+}
+
 inline void
 EmitStowICValues(MacroAssembler& masm, int values)
 {
@@ -242,7 +286,7 @@ EmitCallTypeUpdateIC(MacroAssembler& masm, JitCode* code, uint32_t objectOffset)
     masm.j(Assembler::Equal, &success);
 
     // If the IC failed, then call the update fallback function.
-    EmitEnterStubFrame(masm, R1.scratchReg());
+    EmitBaselineEnterStubFrame(masm, R1.scratchReg());
 
     masm.loadValue(Address(BaselineStackReg, STUB_FRAME_SIZE + objectOffset), R1);
 
@@ -254,8 +298,8 @@ EmitCallTypeUpdateIC(MacroAssembler& masm, JitCode* code, uint32_t objectOffset)
     masm.loadPtr(Address(BaselineFrameReg, 0), R0.scratchReg());
     masm.pushBaselineFramePtr(R0.scratchReg(), R0.scratchReg());
 
-    EmitCallVM(code, masm);
-    EmitLeaveStubFrame(masm);
+    EmitBaselineCallVM(code, masm);
+    EmitBaselineLeaveStubFrame(masm);
 
     // Success at end.
     masm.bind(&success);
diff --git a/js/src/jit/x64/Trampoline-x64.cpp b/js/src/jit/x64/Trampoline-x64.cpp
index 48afad42d99d..9a7893045016 100644
--- a/js/src/jit/x64/Trampoline-x64.cpp
+++ b/js/src/jit/x64/Trampoline-x64.cpp
@@ -872,7 +872,7 @@ JitRuntime::generateDebugTrapHandler(JSContext* cx)
     // the stub frame has a nullptr ICStub pointer, since this pointer is marked
     // during GC.
     masm.movePtr(ImmPtr(nullptr), ICStubReg);
-    EmitEnterStubFrame(masm, scratch3);
+    EmitBaselineEnterStubFrame(masm, scratch3);
 
     JitCode* code = cx->runtime()->jitRuntime()->getVMWrapper(HandleDebugTrapInfo);
     if (!code)
@@ -880,9 +880,9 @@ JitRuntime::generateDebugTrapHandler(JSContext* cx)
 
     masm.push(scratch1);
     masm.push(scratch2);
-    EmitCallVM(code, masm);
+    EmitBaselineCallVM(code, masm);
 
-    EmitLeaveStubFrame(masm);
+    EmitBaselineLeaveStubFrame(masm);
 
     // If the stub returns |true|, we have to perform a forced return
     // (return from the JS frame). If the stub returns |false|, just return
diff --git a/js/src/jit/x86/SharedICHelpers-x86.h b/js/src/jit/x86/SharedICHelpers-x86.h
index d8d6135e92a8..8ad8a22b315d 100644
--- a/js/src/jit/x86/SharedICHelpers-x86.h
+++ b/js/src/jit/x86/SharedICHelpers-x86.h
@@ -71,7 +71,7 @@ EmitChangeICReturnAddress(MacroAssembler& masm, Register reg)
 }
 
 inline void
-EmitTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t argSize)
+EmitBaselineTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t argSize)
 {
     // We assume during this that R0 and R1 have been pushed.
 
@@ -93,7 +93,21 @@ EmitTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t argSize)
 }
 
 inline void
-EmitCreateStubFrameDescriptor(MacroAssembler& masm, Register reg)
+EmitIonTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t stackSize)
+{
+    masm.movl(Operand(esp, stackSize), eax);
+    masm.shrl(Imm32(FRAMESIZE_SHIFT), eax);
+    masm.addl(Imm32(stackSize + JitStubFrameLayout::Size() - sizeof(intptr_t)), eax);
+
+    masm.makeFrameDescriptor(eax, JitFrame_IonJS);
+    masm.push(eax);
+    masm.push(ICTailCallReg);
+
+    masm.jmp(target);
+}
+
+inline void
+EmitBaselineCreateStubFrameDescriptor(MacroAssembler& masm, Register reg)
 {
     // Compute stub frame size. We have to add two pointers: the stub reg and previous
     // frame pointer pushed by EmitEnterStubFrame.
@@ -105,19 +119,34 @@ EmitCreateStubFrameDescriptor(MacroAssembler& masm, Register reg)
 }
 
 inline void
-EmitCallVM(JitCode* target, MacroAssembler& masm)
+EmitBaselineCallVM(JitCode* target, MacroAssembler& masm)
 {
-    EmitCreateStubFrameDescriptor(masm, eax);
+    EmitBaselineCreateStubFrameDescriptor(masm, eax);
     masm.push(eax);
     masm.call(target);
 }
 
+inline void
+EmitIonCallVM(JitCode* target, size_t stackSlots, MacroAssembler& masm)
+{
+    uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonStub);
+    masm.Push(Imm32(descriptor));
+    masm.call(target);
+
+    // Remove rest of the frame left on the stack. We remove the return address
+    // which is implicitly poped when returning.
+    size_t framePop = sizeof(ExitFrameLayout) - sizeof(void*);
+
+    // Pop arguments from framePushed.
+    masm.implicitPop(stackSlots * sizeof(void*) + framePop);
+}
+
 // Size of vales pushed by EmitEnterStubFrame.
 static const uint32_t STUB_FRAME_SIZE = 4 * sizeof(void*);
 static const uint32_t STUB_FRAME_SAVED_STUB_OFFSET = sizeof(void*);
 
 inline void
-EmitEnterStubFrame(MacroAssembler& masm, Register scratch)
+EmitBaselineEnterStubFrame(MacroAssembler& masm, Register scratch)
 {
     MOZ_ASSERT(scratch != ICTailCallReg);
 
@@ -145,7 +174,17 @@ EmitEnterStubFrame(MacroAssembler& masm, Register scratch)
 }
 
 inline void
-EmitLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
+EmitIonEnterStubFrame(MacroAssembler& masm, Register scratch)
+{
+    MOZ_ASSERT(scratch != ICTailCallReg);
+
+    masm.pop(ICTailCallReg);
+    masm.push(ICTailCallReg);
+    masm.push(ICStubReg);
+}
+
+inline void
+EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
 {
     // Ion frames do not save and restore the frame pointer. If we called
     // into Ion, we have to restore the stack pointer from the frame descriptor.
@@ -171,6 +210,13 @@ EmitLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
     masm.storePtr(ICTailCallReg, Address(BaselineStackReg, 0));
 }
 
+inline void
+EmitIonLeaveStubFrame(MacroAssembler& masm)
+{
+    masm.pop(ICStubReg);
+    masm.pop(ICTailCallReg);
+}
+
 inline void
 EmitStowICValues(MacroAssembler& masm, int values)
 {
@@ -248,7 +294,7 @@ EmitCallTypeUpdateIC(MacroAssembler& masm, JitCode* code, uint32_t objectOffset)
     masm.j(Assembler::Equal, &success);
 
     // If the IC failed, then call the update fallback function.
-    EmitEnterStubFrame(masm, R1.scratchReg());
+    EmitBaselineEnterStubFrame(masm, R1.scratchReg());
 
     masm.loadValue(Address(BaselineStackReg, STUB_FRAME_SIZE + objectOffset), R1);
 
@@ -260,8 +306,8 @@ EmitCallTypeUpdateIC(MacroAssembler& masm, JitCode* code, uint32_t objectOffset)
     masm.loadPtr(Address(BaselineFrameReg, 0), R0.scratchReg());
     masm.pushBaselineFramePtr(R0.scratchReg(), R0.scratchReg());
 
-    EmitCallVM(code, masm);
-    EmitLeaveStubFrame(masm);
+    EmitBaselineCallVM(code, masm);
+    EmitBaselineLeaveStubFrame(masm);
 
     // Success at end.
     masm.bind(&success);
diff --git a/js/src/jit/x86/Trampoline-x86.cpp b/js/src/jit/x86/Trampoline-x86.cpp
index 7194759ef59a..f5f87edd10c8 100644
--- a/js/src/jit/x86/Trampoline-x86.cpp
+++ b/js/src/jit/x86/Trampoline-x86.cpp
@@ -900,7 +900,7 @@ JitRuntime::generateDebugTrapHandler(JSContext* cx)
     // the stub frame has a nullptr ICStub pointer, since this pointer is
     // marked during GC.
     masm.movePtr(ImmPtr(nullptr), ICStubReg);
-    EmitEnterStubFrame(masm, scratch3);
+    EmitBaselineEnterStubFrame(masm, scratch3);
 
     JitCode* code = cx->runtime()->jitRuntime()->getVMWrapper(HandleDebugTrapInfo);
     if (!code)
@@ -908,9 +908,9 @@ JitRuntime::generateDebugTrapHandler(JSContext* cx)
 
     masm.push(scratch1);
     masm.push(scratch2);
-    EmitCallVM(code, masm);
+    EmitBaselineCallVM(code, masm);
 
-    EmitLeaveStubFrame(masm);
+    EmitBaselineLeaveStubFrame(masm);
 
     // If the stub returns |true|, we have to perform a forced return
     // (return from the JS frame). If the stub returns |false|, just return
diff --git a/js/src/jsapi-tests/testArrayBufferView.cpp b/js/src/jsapi-tests/testArrayBufferView.cpp
index 34afbf7b33dd..853d55780d02 100644
--- a/js/src/jsapi-tests/testArrayBufferView.cpp
+++ b/js/src/jsapi-tests/testArrayBufferView.cpp
@@ -86,23 +86,10 @@ BEGIN_TEST(testArrayBufferView_type)
 static JSObject*
 CreateDataView(JSContext* cx)
 {
-    JS::Rooted global(cx, JS::CurrentGlobalOrNull(cx));
-    if (!global)
+    JS::Rooted buffer(cx, JS_NewArrayBuffer(cx, 8));
+    if (!buffer)
         return nullptr;
-
-    static const char code[] = "new DataView(new ArrayBuffer(8))";
-
-    JS::Rooted val(cx);
-    JS::CompileOptions opts(cx);
-    if (!JS::Evaluate(cx, opts.setFileAndLine(__FILE__, __LINE__),
-                      code, strlen(code), &val))
-        return nullptr;
-
-    JS::Rooted dv(cx, &val.toObject());
-    if (!JS_IsDataViewObject(dv))
-        return nullptr;
-
-    return dv;
+    return JS_NewDataView(cx, buffer, 0, 8);
 }
 
 template buffer(cx);
+    {
+        AutoCompartment ac(cx, otherGlobal);
+        buffer = JS_NewArrayBuffer(cx, 8);
+        CHECK(buffer);
+        CHECK(buffer->as().byteLength() == 8);
+    }
+    CHECK(buffer->compartment() == otherGlobal->compartment());
+    CHECK(JS_WrapObject(cx, &buffer));
+    CHECK(buffer->compartment() == global->compartment());
+
+    JS::Rooted dataview(cx, JS_NewDataView(cx, buffer, 4, 4));
+    CHECK(dataview);
+    CHECK(dataview->is());
+
+    JS::Rooted val(cx);
+
+    val = ObjectValue(*dataview);
+    CHECK(JS_SetProperty(cx, global, "view", val));
+
+    EVAL("view.buffer", &val);
+    CHECK(val.toObject().is());
+
+    CHECK(dataview->compartment() == global->compartment());
+    JS::Rooted otherView(cx, js::UncheckedUnwrap(dataview));
+    CHECK(otherView->compartment() == otherGlobal->compartment());
+    JS::Rooted otherBuffer(cx, js::UncheckedUnwrap(&val.toObject()));
+    CHECK(otherBuffer->compartment() == otherGlobal->compartment());
+
+    EVAL("Object.getPrototypeOf(view) === DataView.prototype", &val);
+    CHECK(val.toBoolean() == true);
+
     return true;
 }
 
diff --git a/js/src/jsapi.h b/js/src/jsapi.h
index 89294e736b81..81b9f7ad5d4b 100644
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2437,20 +2437,20 @@ namespace JS {
 template 
 class PropertyDescriptorOperations
 {
-    const JSPropertyDescriptor* desc() const { return static_cast(this)->extract(); }
+    const JSPropertyDescriptor& desc() const { return static_cast(this)->get(); }
 
     bool has(unsigned bit) const {
         MOZ_ASSERT(bit != 0);
         MOZ_ASSERT((bit & (bit - 1)) == 0);  // only a single bit
-        return (desc()->attrs & bit) != 0;
+        return (desc().attrs & bit) != 0;
     }
 
     bool hasAny(unsigned bits) const {
-        return (desc()->attrs & bits) != 0;
+        return (desc().attrs & bits) != 0;
     }
 
     bool hasAll(unsigned bits) const {
-        return (desc()->attrs & bits) == bits;
+        return (desc().attrs & bits) == bits;
     }
 
     // Non-API attributes bit used internally for arguments objects.
@@ -2461,7 +2461,7 @@ class PropertyDescriptorOperations
     // descriptors. It's complicated.
     bool isAccessorDescriptor() const { return hasAny(JSPROP_GETTER | JSPROP_SETTER); }
     bool isGenericDescriptor() const {
-        return (desc()->attrs&
+        return (desc().attrs&
                 (JSPROP_GETTER | JSPROP_SETTER | JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE)) ==
                (JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE);
     }
@@ -2475,7 +2475,7 @@ class PropertyDescriptorOperations
 
     bool hasValue() const { return !isAccessorDescriptor() && !has(JSPROP_IGNORE_VALUE); }
     JS::HandleValue value() const {
-        return JS::HandleValue::fromMarkedLocation(&desc()->value);
+        return JS::HandleValue::fromMarkedLocation(&desc().value);
     }
 
     bool hasWritable() const { return !isAccessorDescriptor() && !has(JSPROP_IGNORE_READONLY); }
@@ -2485,24 +2485,24 @@ class PropertyDescriptorOperations
     JS::HandleObject getterObject() const {
         MOZ_ASSERT(hasGetterObject());
         return JS::HandleObject::fromMarkedLocation(
-                reinterpret_cast(&desc()->getter));
+                reinterpret_cast(&desc().getter));
     }
     bool hasSetterObject() const { return has(JSPROP_SETTER); }
     JS::HandleObject setterObject() const {
         MOZ_ASSERT(hasSetterObject());
         return JS::HandleObject::fromMarkedLocation(
-                reinterpret_cast(&desc()->setter));
+                reinterpret_cast(&desc().setter));
     }
 
-    bool hasGetterOrSetter() const { return desc()->getter || desc()->setter; }
+    bool hasGetterOrSetter() const { return desc().getter || desc().setter; }
     bool isShared() const { return has(JSPROP_SHARED); }
 
     JS::HandleObject object() const {
-        return JS::HandleObject::fromMarkedLocation(&desc()->obj);
+        return JS::HandleObject::fromMarkedLocation(&desc().obj);
     }
-    unsigned attributes() const { return desc()->attrs; }
-    JSGetterOp getter() const { return desc()->getter; }
-    JSSetterOp setter() const { return desc()->setter; }
+    unsigned attributes() const { return desc().attrs; }
+    JSGetterOp getter() const { return desc().getter; }
+    JSSetterOp setter() const { return desc().setter; }
 
     void assertValid() const {
 #ifdef DEBUG
@@ -2569,7 +2569,7 @@ class PropertyDescriptorOperations
 template 
 class MutablePropertyDescriptorOperations : public PropertyDescriptorOperations
 {
-    JSPropertyDescriptor * desc() { return static_cast(this)->extractMutable(); }
+    JSPropertyDescriptor& desc() { return static_cast(this)->get(); }
 
   public:
     void clear() {
@@ -2615,63 +2615,63 @@ class MutablePropertyDescriptorOperations : public PropertyDescriptorOperations<
     }
 
     JS::MutableHandleObject object() {
-        return JS::MutableHandleObject::fromMarkedLocation(&desc()->obj);
+        return JS::MutableHandleObject::fromMarkedLocation(&desc().obj);
     }
-    unsigned& attributesRef() { return desc()->attrs; }
-    JSGetterOp& getter() { return desc()->getter; }
-    JSSetterOp& setter() { return desc()->setter; }
+    unsigned& attributesRef() { return desc().attrs; }
+    JSGetterOp& getter() { return desc().getter; }
+    JSSetterOp& setter() { return desc().setter; }
     JS::MutableHandleValue value() {
-        return JS::MutableHandleValue::fromMarkedLocation(&desc()->value);
+        return JS::MutableHandleValue::fromMarkedLocation(&desc().value);
     }
     void setValue(JS::HandleValue v) {
-        MOZ_ASSERT(!(desc()->attrs & (JSPROP_GETTER | JSPROP_SETTER)));
+        MOZ_ASSERT(!(desc().attrs & (JSPROP_GETTER | JSPROP_SETTER)));
         attributesRef() &= ~JSPROP_IGNORE_VALUE;
         value().set(v);
     }
 
     void setConfigurable(bool configurable) {
-        setAttributes((desc()->attrs & ~(JSPROP_IGNORE_PERMANENT | JSPROP_PERMANENT)) |
+        setAttributes((desc().attrs & ~(JSPROP_IGNORE_PERMANENT | JSPROP_PERMANENT)) |
                       (configurable ? 0 : JSPROP_PERMANENT));
     }
     void setEnumerable(bool enumerable) {
-        setAttributes((desc()->attrs & ~(JSPROP_IGNORE_ENUMERATE | JSPROP_ENUMERATE)) |
+        setAttributes((desc().attrs & ~(JSPROP_IGNORE_ENUMERATE | JSPROP_ENUMERATE)) |
                       (enumerable ? JSPROP_ENUMERATE : 0));
     }
     void setWritable(bool writable) {
-        MOZ_ASSERT(!(desc()->attrs & (JSPROP_GETTER | JSPROP_SETTER)));
-        setAttributes((desc()->attrs & ~(JSPROP_IGNORE_READONLY | JSPROP_READONLY)) |
+        MOZ_ASSERT(!(desc().attrs & (JSPROP_GETTER | JSPROP_SETTER)));
+        setAttributes((desc().attrs & ~(JSPROP_IGNORE_READONLY | JSPROP_READONLY)) |
                       (writable ? 0 : JSPROP_READONLY));
     }
-    void setAttributes(unsigned attrs) { desc()->attrs = attrs; }
+    void setAttributes(unsigned attrs) { desc().attrs = attrs; }
 
     void setGetter(JSGetterOp op) {
         MOZ_ASSERT(op != JS_PropertyStub);
-        desc()->getter = op;
+        desc().getter = op;
     }
     void setSetter(JSSetterOp op) {
         MOZ_ASSERT(op != JS_StrictPropertyStub);
-        desc()->setter = op;
+        desc().setter = op;
     }
     void setGetterObject(JSObject* obj) {
-        desc()->getter = reinterpret_cast(obj);
-        desc()->attrs &= ~(JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY | JSPROP_READONLY);
-        desc()->attrs |= JSPROP_GETTER | JSPROP_SHARED;
+        desc().getter = reinterpret_cast(obj);
+        desc().attrs &= ~(JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY | JSPROP_READONLY);
+        desc().attrs |= JSPROP_GETTER | JSPROP_SHARED;
     }
     void setSetterObject(JSObject* obj) {
-        desc()->setter = reinterpret_cast(obj);
-        desc()->attrs &= ~(JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY | JSPROP_READONLY);
-        desc()->attrs |= JSPROP_SETTER | JSPROP_SHARED;
+        desc().setter = reinterpret_cast(obj);
+        desc().attrs &= ~(JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY | JSPROP_READONLY);
+        desc().attrs |= JSPROP_SETTER | JSPROP_SHARED;
     }
 
     JS::MutableHandleObject getterObject() {
         MOZ_ASSERT(this->hasGetterObject());
         return JS::MutableHandleObject::fromMarkedLocation(
-                reinterpret_cast(&desc()->getter));
+                reinterpret_cast(&desc().getter));
     }
     JS::MutableHandleObject setterObject() {
         MOZ_ASSERT(this->hasSetterObject());
         return JS::MutableHandleObject::fromMarkedLocation(
-                reinterpret_cast(&desc()->setter));
+                reinterpret_cast(&desc().setter));
     }
 };
 
@@ -2682,40 +2682,17 @@ namespace js {
 template <>
 class RootedBase
   : public JS::MutablePropertyDescriptorOperations>
-{
-    friend class JS::PropertyDescriptorOperations>;
-    friend class JS::MutablePropertyDescriptorOperations>;
-    const JSPropertyDescriptor* extract() const {
-        return static_cast*>(this)->address();
-    }
-    JSPropertyDescriptor* extractMutable() {
-        return static_cast*>(this)->address();
-    }
-};
+{};
 
 template <>
 class HandleBase
   : public JS::PropertyDescriptorOperations>
-{
-    friend class JS::PropertyDescriptorOperations>;
-    const JSPropertyDescriptor* extract() const {
-        return static_cast*>(this)->address();
-    }
-};
+{};
 
 template <>
 class MutableHandleBase
   : public JS::MutablePropertyDescriptorOperations>
-{
-    friend class JS::PropertyDescriptorOperations>;
-    friend class JS::MutablePropertyDescriptorOperations>;
-    const JSPropertyDescriptor* extract() const {
-        return static_cast*>(this)->address();
-    }
-    JSPropertyDescriptor* extractMutable() {
-        return static_cast*>(this)->address();
-    }
-};
+{};
 
 } /* namespace js */
 
diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp
index 94f74281e1fb..15564ccc2985 100644
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1250,10 +1250,10 @@ ArrayReverseDenseKernel(JSContext* cx, HandleObject obj, uint32_t length)
         /* Fill out the array's initialized length to its proper length. */
         obj->as().ensureDenseInitializedLength(cx, length, 0);
     } else {
-        // Unboxed arrays can only be reversed if their initialized length
+        // Unboxed arrays can only be reversed here if their initialized length
         // matches their actual length. Otherwise the reversal will place holes
         // at the beginning of the array, which we don't support.
-        if (length != obj->as().length())
+        if (length != obj->as().initializedLength())
             return DenseElementResult::Incomplete;
     }
 
@@ -2532,40 +2532,41 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI
     return true;
 }
 
-template 
+template 
 DenseElementResult
 ArrayConcatDenseKernel(JSContext* cx, JSObject* obj1, JSObject* obj2, JSObject* result)
 {
-    uint32_t initlen1 = GetBoxedOrUnboxedInitializedLength(obj1);
+    uint32_t initlen1 = GetBoxedOrUnboxedInitializedLength(obj1);
     MOZ_ASSERT(initlen1 == GetAnyBoxedOrUnboxedArrayLength(obj1));
 
-    uint32_t initlen2 = GetBoxedOrUnboxedInitializedLength(obj2);
+    uint32_t initlen2 = GetBoxedOrUnboxedInitializedLength(obj2);
     MOZ_ASSERT(initlen2 == GetAnyBoxedOrUnboxedArrayLength(obj2));
 
     /* No overflow here due to nelements limit. */
     uint32_t len = initlen1 + initlen2;
 
-    MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength(result) == 0);
+    MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength(result) == 0);
 
-    if (!EnsureBoxedOrUnboxedDenseElements(cx, result, len))
-        return DenseElementResult::Failure;
+    DenseElementResult rv = EnsureBoxedOrUnboxedDenseElements(cx, result, len);
+    if (rv != DenseElementResult::Success)
+        return rv;
 
-    CopyBoxedOrUnboxedDenseElements(cx, result, obj1, 0, 0, initlen1);
-    CopyBoxedOrUnboxedDenseElements(cx, result, obj2, initlen1, 0, initlen2);
+    CopyBoxedOrUnboxedDenseElements(cx, result, obj1, 0, 0, initlen1);
+    CopyBoxedOrUnboxedDenseElements(cx, result, obj2, initlen1, 0, initlen2);
 
     SetAnyBoxedOrUnboxedArrayLength(cx, result, len);
     return DenseElementResult::Success;
 }
 
-DefineBoxedOrUnboxedFunctor4(ArrayConcatDenseKernel,
-                             JSContext*, JSObject*, JSObject*, JSObject*);
+DefineBoxedOrUnboxedFunctorPair4(ArrayConcatDenseKernel,
+                                 JSContext*, JSObject*, JSObject*, JSObject*);
 
 bool
 js::array_concat_dense(JSContext* cx, HandleObject obj1, HandleObject obj2,
                        HandleObject result)
 {
     ArrayConcatDenseKernelFunctor functor(cx, obj1, obj2, result);
-    DenseElementResult rv = CallBoxedOrUnboxedSpecialization(functor, result);
+    DenseElementResult rv = CallBoxedOrUnboxedSpecialization(functor, obj1, obj2);
     MOZ_ASSERT(rv != DenseElementResult::Incomplete);
     return rv == DenseElementResult::Success;
 }
@@ -2596,17 +2597,63 @@ js::array_concat(JSContext* cx, unsigned argc, Value* vp)
         narr = NewFullyAllocatedArrayTryReuseGroup(cx, aobj, initlen);
         if (!narr)
             return false;
+        CopyAnyBoxedOrUnboxedDenseElements(cx, narr, aobj, 0, 0, initlen);
         SetAnyBoxedOrUnboxedArrayLength(cx, narr, length);
 
-        DebugOnly result =
-            CopyAnyBoxedOrUnboxedDenseElements(cx, narr, aobj, 0, 0, initlen);
-        MOZ_ASSERT(result.value == DenseElementResult::Success);
-
         args.rval().setObject(*narr);
         if (argc == 0)
             return true;
         argc--;
         p++;
+
+        if (length == initlen) {
+            while (argc) {
+                HandleValue v = HandleValue::fromMarkedLocation(p);
+                if (!v.isObject())
+                    break;
+                RootedObject obj(cx, &v.toObject());
+
+                // This should be IsConcatSpreadable
+                if (!IsArray(obj, cx) || ObjectMayHaveExtraIndexedProperties(obj))
+                    break;
+
+                uint32_t argLength;
+                if (!GetLengthProperty(cx, obj, &argLength))
+                    return false;
+
+                initlen = GetAnyBoxedOrUnboxedInitializedLength(obj);
+                if (argLength != initlen)
+                    break;
+
+                DenseElementResult result =
+                    EnsureAnyBoxedOrUnboxedDenseElements(cx, narr, length + argLength);
+                if (result == DenseElementResult::Failure)
+                    return false;
+                if (result == DenseElementResult::Incomplete)
+                    break;
+
+                SetAnyBoxedOrUnboxedInitializedLength(cx, narr, length + argLength);
+
+                bool success = true;
+                for (size_t i = 0; i < initlen; i++) {
+                    Value v = GetAnyBoxedOrUnboxedDenseElement(obj, i);
+                    if (!InitAnyBoxedOrUnboxedDenseElement(cx, narr, length + i, v)) {
+                        success = false;
+                        break;
+                    }
+                }
+                if (!success) {
+                    SetAnyBoxedOrUnboxedInitializedLength(cx, narr, length);
+                    break;
+                }
+
+                length += argLength;
+                SetAnyBoxedOrUnboxedArrayLength(cx, narr, length);
+
+                argc--;
+                p++;
+            }
+        }
     } else {
         narr = NewDenseEmptyArray(cx);
         if (!narr)
@@ -2903,9 +2950,10 @@ ArraySliceDenseKernel(JSContext* cx, JSObject* obj, int32_t beginArg, int32_t en
     if (initlen > begin) {
         size_t count = Min(initlen - begin, end - begin);
         if (count) {
-            if (!EnsureBoxedOrUnboxedDenseElements(cx, result, count))
-                return DenseElementResult::Failure;
-            CopyBoxedOrUnboxedDenseElements(cx, result, obj, 0, begin, count);
+            DenseElementResult rv = EnsureBoxedOrUnboxedDenseElements(cx, result, count);
+            if (rv != DenseElementResult::Success)
+                return rv;
+            CopyBoxedOrUnboxedDenseElements(cx, result, obj, 0, begin, count);
         }
     }
 
diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h
index 5bd2c786ea25..c14cd030ca15 100644
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -2127,6 +2127,16 @@ JS_IsNeuteredArrayBufferObject(JSObject* obj);
 JS_FRIEND_API(bool)
 JS_IsDataViewObject(JSObject* obj);
 
+/*
+ * Create a new DataView using the given ArrayBuffer for storage. The given
+ * buffer must be an ArrayBuffer (or a cross-compartment wrapper of an
+ * ArrayBuffer), and the offset and length must fit within the bounds of the
+ * arrayBuffer. Currently, nullptr will be returned and an exception will be
+ * thrown if these conditions do not hold, but do not depend on that behavior.
+ */
+JS_FRIEND_API(JSObject*)
+JS_NewDataView(JSContext* cx, JS::HandleObject arrayBuffer, uint32_t byteOffset, int32_t byteLength);
+
 /*
  * Return the byte offset of a data view into its array buffer. |obj| must be a
  * DataView.
diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp
index a0f0fe6134aa..1b4769cd81e1 100644
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -4034,7 +4034,7 @@ GCRuntime::markWeakReferences(gcstats::Phase phase)
         for (CompartmentIterT c(rt); !c.done(); c.next()) {
             if (c->watchpointMap)
                 markedAny |= c->watchpointMap->markIteratively(&marker);
-            if (marker.weakMapAction() != ExpandWeakMaps)
+            if (!marker.isWeakMarkingTracer())
                 markedAny |= WeakMapBase::markCompartmentIteratively(c, &marker);
         }
         markedAny |= Debugger::markAllIteratively(&marker);
diff --git a/js/src/jsscript.h b/js/src/jsscript.h
index ab58d35d6aa8..187163363607 100644
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -349,7 +349,7 @@ class Bindings : public JS::Traceable
 template 
 class BindingsOperations
 {
-    const Bindings& bindings() const { return static_cast(this)->extract(); }
+    const Bindings& bindings() const { return static_cast(this)->get(); }
 
   public:
     // Direct data access to the underlying bindings.
@@ -413,7 +413,7 @@ class BindingsOperations
 template 
 class MutableBindingsOperations : public BindingsOperations
 {
-    Bindings& bindings() { return static_cast(this)->extractMutable(); }
+    Bindings& bindings() { return static_cast(this)->get(); }
 
   public:
     void setCallObjShape(HandleShape shape) { bindings().callObjShape_ = shape; }
@@ -438,27 +438,12 @@ class MutableBindingsOperations : public BindingsOperations
 
 template <>
 class HandleBase : public BindingsOperations>
-{
-    friend class BindingsOperations>;
-    const Bindings& extract() const {
-        return static_cast*>(this)->get();
-    }
-};
+{};
 
 template <>
 class MutableHandleBase
   : public MutableBindingsOperations>
-{
-    friend class BindingsOperations>;
-    const Bindings& extract() const {
-        return static_cast*>(this)->get();
-    }
-
-    friend class MutableBindingsOperations>;
-    Bindings& extractMutable() {
-        return static_cast*>(this)->get();
-    }
-};
+{};
 
 class ScriptCounts
 {
diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
index db74598a9929..8762f4018c65 100644
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -227,7 +227,7 @@ class ShellPrincipals: public JSPrincipals {
     static void destroy(JSPrincipals* principals) {
         MOZ_ASSERT(principals != &fullyTrusted);
         MOZ_ASSERT(principals->refcount == 0);
-        js_free(static_cast(principals));
+        js_delete(static_cast(principals));
     }
 
     static bool subsumes(JSPrincipals* first, JSPrincipals* second) {
diff --git a/js/src/tests/js1_8_5/extensions/clone-transferables.js b/js/src/tests/js1_8_5/extensions/clone-transferables.js
index 86e8e93ae99e..13657e5513d7 100644
--- a/js/src/tests/js1_8_5/extensions/clone-transferables.js
+++ b/js/src/tests/js1_8_5/extensions/clone-transferables.js
@@ -23,20 +23,26 @@ function test() {
                              Uint32Array,
                              Float32Array,
                              Float64Array,
-                             Uint8ClampedArray ];
+                             Uint8ClampedArray,
+                             DataView ];
 
         for (var ctor of constructors) {
+            var dataview = (ctor === DataView);
+
             var buf = new buffer_ctor(size);
             var old_arr = new ctor(buf);
             assertEq(buf.byteLength, size);
             assertEq(buf, old_arr.buffer);
-            assertEq(old_arr.length, size / old_arr.BYTES_PER_ELEMENT);
+            if (!dataview)
+                assertEq(old_arr.length, size / old_arr.BYTES_PER_ELEMENT);
 
             var copy_arr = deserialize(serialize(old_arr, [ buf ]));
             assertEq(buf.byteLength, 0, "donor array buffer should be neutered");
-            assertEq(old_arr.length, 0, "donor typed array should be neutered");
+            if (!dataview)
+                assertEq(old_arr.length, 0, "donor typed array should be neutered");
             assertEq(copy_arr.buffer.byteLength == size, true);
-            assertEq(copy_arr.length, size / old_arr.BYTES_PER_ELEMENT);
+            if (!dataview)
+                assertEq(copy_arr.length, size / old_arr.BYTES_PER_ELEMENT);
 
             buf = null;
             old_arr = null;
@@ -44,12 +50,16 @@ function test() {
         }
 
         for (var ctor of constructors) {
+            var dataview = (ctor === DataView);
+
             var buf = new buffer_ctor(size);
             var old_arr = new ctor(buf);
             var dv = new DataView(buf); // Second view
             var copy_arr = deserialize(serialize(old_arr, [ buf ]));
             assertEq(buf.byteLength, 0, "donor array buffer should be neutered");
-            assertEq(old_arr.length, 0, "donor typed array should be neutered");
+            assertEq(old_arr.byteLength, 0, "donor typed array should be neutered");
+            if (!dataview)
+                assertEq(old_arr.length, 0, "donor typed array should be neutered");
             assertEq(dv.byteLength, 0, "all views of donor array buffer should be neutered");
 
             buf = null;
diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp
index dae69d741d7c..eb34f0860dd4 100644
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -1056,8 +1056,10 @@ InnerViewTable::addView(JSContext* cx, ArrayBufferObject* obj, ArrayBufferViewOb
             }
         }
 
-        if (!views.append(view))
+        if (!views.append(view)) {
+            ReportOutOfMemory(cx);
             return false;
+        }
     } else {
         if (!map.add(p, obj, ViewVector()))
             return false;
@@ -1128,7 +1130,7 @@ InnerViewTable::sweep(JSRuntime* rt)
 void
 InnerViewTable::sweepAfterMinorGC(JSRuntime* rt)
 {
-    MOZ_ASSERT(!nurseryKeys.empty());
+    MOZ_ASSERT(needsSweepAfterMinorGC());
 
     if (nurseryKeysValid) {
         for (size_t i = 0; i < nurseryKeys.length(); i++) {
diff --git a/js/src/vm/ArrayBufferObject.h b/js/src/vm/ArrayBufferObject.h
index 03b25b75de21..5d807a27a223 100644
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -21,26 +21,25 @@ class ArrayBufferViewObject;
 // The inheritance hierarchy for the various classes relating to typed arrays
 // is as follows.
 //
-// - JSObject
+// - NativeObject
 //   - ArrayBufferObjectMaybeShared
 //     - ArrayBufferObject
 //     - SharedArrayBufferObject
-//   - ArrayBufferViewObject
-//     - DataViewObject
-//     - TypedArrayObject (declared in vm/TypedArrayObject.h)
-//       - TypedArrayObjectTemplate
-//         - Int8ArrayObject
-//         - Uint8ArrayObject
-//         - ...
-//     - TypedObject (declared in builtin/TypedObject.h)
+//   - DataViewObject
+//   - TypedArrayObject (declared in vm/TypedArrayObject.h)
+//     - TypedArrayObjectTemplate
+//       - Int8ArrayObject
+//       - Uint8ArrayObject
+//       - ...
 //   - SharedTypedArrayObject (declared in vm/SharedTypedArrayObject.h)
 //     - SharedTypedArrayObjectTemplate
 //       - SharedInt8ArrayObject
 //       - SharedUint8ArrayObject
 //       - ...
+// - JSObject
+//   - ArrayBufferViewObject
+//   - TypedObject (declared in builtin/TypedObject.h)
 //
-// Note that |TypedArrayObjectTemplate| is just an implementation
-// detail that makes implementing its various subclasses easier.
 // Note that |TypedArrayObjectTemplate| and |SharedTypedArrayObjectTemplate| are
 // just implementation details that make implementing their various subclasses easier.
 //
@@ -94,11 +93,11 @@ class ArrayBufferObjectMaybeShared : public NativeObject
 /*
  * ArrayBufferObject
  *
- * This class holds the underlying raw buffer that the various
- * ArrayBufferViewObject subclasses (DataViewObject and the TypedArrays)
- * access. It can be created explicitly and passed to an ArrayBufferViewObject
- * subclass, or can be created lazily when it is first accessed for a
- * TypedArrayObject or TypedObject that doesn't have an explicit buffer.
+ * This class holds the underlying raw buffer that the various ArrayBufferViews
+ * (eg DataViewObject, the TypedArrays, TypedObjects) access. It can be created
+ * explicitly and used to construct an ArrayBufferView, or can be created
+ * lazily when it is first accessed for a TypedArrayObject or TypedObject that
+ * doesn't have an explicit buffer.
  *
  * ArrayBufferObject (or really the underlying memory) /is not racy/: the
  * memory is private to a single worker.
diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp
index c82bbd1ba298..c63a5d959217 100644
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1770,12 +1770,7 @@ class ReservedRooted : public ReservedRootedBase
 
 template <>
 class ReservedRootedBase : public ValueOperations>
-{
-    friend class ValueOperations>;
-    const Value* extract() const {
-        return static_cast*>(this)->address();
-    }
-};
+{};
 
 static MOZ_NEVER_INLINE bool
 Interpret(JSContext* cx, RunState& state)
diff --git a/js/src/vm/JSONParser.cpp b/js/src/vm/JSONParser.cpp
index d4ba7bfe3d35..4ac4d254557e 100644
--- a/js/src/vm/JSONParser.cpp
+++ b/js/src/vm/JSONParser.cpp
@@ -590,6 +590,13 @@ JSONParserBase::finishObject(MutableHandleValue vp, PropertyVector& properties)
     if (!freeProperties.append(&properties))
         return false;
     stack.popBack();
+
+    if (!stack.empty() && stack.back().state == FinishArrayElement) {
+        const ElementVector& elements = stack.back().elements();
+        if (!CombinePlainObjectPropertyTypes(cx, obj, elements.begin(), elements.length()))
+            return false;
+    }
+
     return true;
 }
 
@@ -607,6 +614,13 @@ JSONParserBase::finishArray(MutableHandleValue vp, ElementVector& elements)
     if (!freeElements.append(&elements))
         return false;
     stack.popBack();
+
+    if (!stack.empty() && stack.back().state == FinishArrayElement) {
+        const ElementVector& elements = stack.back().elements();
+        if (!CombineArrayElementTypes(cx, obj, elements.begin(), elements.length()))
+            return false;
+    }
+
     return true;
 }
 
diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp
index 70b6a4478442..780069c33f2a 100644
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -868,6 +868,206 @@ ObjectGroup::newArrayObject(ExclusiveContext* cx,
                                      ShouldUpdateTypes::DontUpdate);
 }
 
+static bool
+GiveObjectGroup(ExclusiveContext* cx, JSObject* source, JSObject* target)
+{
+    MOZ_ASSERT(source->group() != target->group());
+
+    if (!target->is() && !target->is())
+        return true;
+
+    if (target->group()->maybePreliminaryObjects()) {
+        bool force = IsInsideNursery(source);
+        target->group()->maybePreliminaryObjects()->maybeAnalyze(cx, target->group(), force);
+    }
+
+    if (target->is()) {
+        ObjectGroup* sourceGroup = source->group();
+
+        if (source->is()) {
+            Shape* shape = target->as().lastProperty();
+            if (!UnboxedArrayObject::convertToNativeWithGroup(cx, source, target->group(), shape))
+                return false;
+        } else if (source->is()) {
+            source->setGroup(target->group());
+        } else {
+            return true;
+        }
+
+        if (sourceGroup->maybePreliminaryObjects())
+            sourceGroup->maybePreliminaryObjects()->unregisterObject(source);
+        if (target->group()->maybePreliminaryObjects())
+            target->group()->maybePreliminaryObjects()->registerNewObject(source);
+
+        for (size_t i = 0; i < source->as().getDenseInitializedLength(); i++) {
+            Value v = source->as().getDenseElement(i);
+            AddTypePropertyId(cx, source->group(), source, JSID_VOID, v);
+        }
+
+        return true;
+    }
+
+    if (target->is()) {
+        if (!source->is())
+            return true;
+        if (source->as().elementType() != JSVAL_TYPE_INT32)
+            return true;
+        if (target->as().elementType() != JSVAL_TYPE_DOUBLE)
+            return true;
+
+        return source->as().convertInt32ToDouble(cx, target->group());
+    }
+
+    return true;
+}
+
+static bool
+SameGroup(JSObject* first, JSObject* second)
+{
+    return first->group() == second->group();
+}
+
+// When generating a multidimensional array of literals, such as
+// [[1,2],[3,4],[5.5,6.5]], try to ensure that each element of the array has
+// the same group. This is mainly important when the elements might have
+// different native vs. unboxed layouts, or different unboxed layouts, and
+// accessing the heterogenous layouts from JIT code will be much slower than
+// if they were homogenous.
+//
+// To do this, with each new array element we compare it with one of the
+// previous ones, and try to mutate the group of the new element to fit that
+// of the old element. If this isn't possible, the groups for all old elements
+// are mutated to fit that of the new element.
+bool
+js::CombineArrayElementTypes(ExclusiveContext* cx, JSObject* newObj,
+                             const Value* compare, size_t ncompare)
+{
+    if (!ncompare || !compare[0].isObject())
+        return true;
+
+    JSObject* oldObj = &compare[0].toObject();
+    if (SameGroup(oldObj, newObj))
+        return true;
+
+    if (!GiveObjectGroup(cx, oldObj, newObj))
+        return false;
+
+    if (SameGroup(oldObj, newObj))
+        return true;
+
+    if (!GiveObjectGroup(cx, newObj, oldObj))
+        return false;
+
+    if (SameGroup(oldObj, newObj)) {
+        for (size_t i = 1; i < ncompare; i++) {
+            if (compare[i].isObject() && !SameGroup(&compare[i].toObject(), newObj)) {
+                if (!GiveObjectGroup(cx, newObj, &compare[i].toObject()))
+                    return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+// Similarly to CombineArrayElementTypes, if we are generating an array of
+// plain objects with a consistent property layout, such as
+// [{p:[1,2]},{p:[3,4]},{p:[5.5,6.5]}], where those plain objects in
+// turn have arrays as their own properties, try to ensure that a consistent
+// group is given to each array held by the same property of the plain objects.
+bool
+js::CombinePlainObjectPropertyTypes(ExclusiveContext* cx, JSObject* newObj,
+                                    const Value* compare, size_t ncompare)
+{
+    if (!ncompare || !compare[0].isObject())
+        return true;
+
+    JSObject* oldObj = &compare[0].toObject();
+    if (!SameGroup(oldObj, newObj))
+        return true;
+
+    if (newObj->is()) {
+        MOZ_ASSERT(newObj->as().lastProperty() == oldObj->as().lastProperty());
+
+        for (size_t slot = 0; slot < newObj->as().slotSpan(); slot++) {
+            Value newValue = newObj->as().getSlot(slot);
+            Value oldValue = oldObj->as().getSlot(slot);
+
+            if (!newValue.isObject() || !oldValue.isObject())
+                continue;
+
+            JSObject* newInnerObj = &newValue.toObject();
+            JSObject* oldInnerObj = &oldValue.toObject();
+
+            if (SameGroup(oldInnerObj, newInnerObj))
+                continue;
+
+            if (!GiveObjectGroup(cx, oldInnerObj, newInnerObj))
+                return false;
+
+            if (SameGroup(oldInnerObj, newInnerObj))
+                continue;
+
+            if (!GiveObjectGroup(cx, newInnerObj, oldInnerObj))
+                return false;
+
+            if (SameGroup(oldInnerObj, newInnerObj)) {
+                for (size_t i = 1; i < ncompare; i++) {
+                    if (compare[i].isObject() && SameGroup(&compare[i].toObject(), newObj)) {
+                        Value otherValue = compare[i].toObject().as().getSlot(slot);
+                        if (otherValue.isObject() && !SameGroup(&otherValue.toObject(), newInnerObj)) {
+                            if (!GiveObjectGroup(cx, newInnerObj, &otherValue.toObject()))
+                                return false;
+                        }
+                    }
+                }
+            }
+        }
+    } else if (newObj->is()) {
+        const UnboxedLayout& layout = newObj->as().layout();
+        const int32_t* traceList = layout.traceList();
+        if (!traceList)
+            return true;
+
+        uint8_t* newData = newObj->as().data();
+        uint8_t* oldData = oldObj->as().data();
+
+        for (; *traceList != -1; traceList++) {}
+        traceList++;
+        for (; *traceList != -1; traceList++) {
+            JSObject* newInnerObj = *reinterpret_cast(newData + *traceList);
+            JSObject* oldInnerObj = *reinterpret_cast(oldData + *traceList);
+
+            if (!newInnerObj || !oldInnerObj || SameGroup(oldInnerObj, newInnerObj))
+                continue;
+
+            if (!GiveObjectGroup(cx, oldInnerObj, newInnerObj))
+                return false;
+
+            if (SameGroup(oldInnerObj, newInnerObj))
+                continue;
+
+            if (!GiveObjectGroup(cx, newInnerObj, oldInnerObj))
+                return false;
+
+            if (SameGroup(oldInnerObj, newInnerObj)) {
+                for (size_t i = 1; i < ncompare; i++) {
+                    if (compare[i].isObject() && SameGroup(&compare[i].toObject(), newObj)) {
+                        uint8_t* otherData = compare[i].toObject().as().data();
+                        JSObject* otherInnerObj = *reinterpret_cast(otherData + *traceList);
+                        if (otherInnerObj && !SameGroup(otherInnerObj, newInnerObj)) {
+                            if (!GiveObjectGroup(cx, newInnerObj, otherInnerObj))
+                                return false;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    return true;
+}
+
 /////////////////////////////////////////////////////////////////////
 // ObjectGroupCompartment PlainObjectTable
 /////////////////////////////////////////////////////////////////////
diff --git a/js/src/vm/ObjectGroup.h b/js/src/vm/ObjectGroup.h
index cc5d3c1978e4..cb46b1edd654 100644
--- a/js/src/vm/ObjectGroup.h
+++ b/js/src/vm/ObjectGroup.h
@@ -81,36 +81,26 @@ template <> struct GCMethods
 template
 class TaggedProtoOperations
 {
-    const TaggedProto* value() const {
-        return static_cast(this)->extract();
+    const TaggedProto& value() const {
+        return static_cast(this)->get();
     }
 
   public:
-    uintptr_t toWord() const { return value()->toWord(); }
-    inline bool isLazy() const { return value()->isLazy(); }
-    inline bool isObject() const { return value()->isObject(); }
-    inline JSObject* toObject() const { return value()->toObject(); }
-    inline JSObject* toObjectOrNull() const { return value()->toObjectOrNull(); }
-    JSObject* raw() const { return value()->raw(); }
+    uintptr_t toWord() const { return value().toWord(); }
+    inline bool isLazy() const { return value().isLazy(); }
+    inline bool isObject() const { return value().isObject(); }
+    inline JSObject* toObject() const { return value().toObject(); }
+    inline JSObject* toObjectOrNull() const { return value().toObjectOrNull(); }
+    JSObject* raw() const { return value().raw(); }
 };
 
 template <>
 class HandleBase : public TaggedProtoOperations >
-{
-    friend class TaggedProtoOperations >;
-    const TaggedProto * extract() const {
-        return static_cast*>(this)->address();
-    }
-};
+{};
 
 template <>
 class RootedBase : public TaggedProtoOperations >
-{
-    friend class TaggedProtoOperations >;
-    const TaggedProto* extract() const {
-        return static_cast*>(this)->address();
-    }
-};
+{};
 
 // Since JSObject pointers are either nullptr or a valid object and since the
 // object layout of TaggedProto is identical to a bare object pointer, we can
@@ -730,6 +720,14 @@ PlainObject*
 NewPlainObjectWithProperties(ExclusiveContext* cx, IdValuePair* properties, size_t nproperties,
                              NewObjectKind newKind);
 
+bool
+CombineArrayElementTypes(ExclusiveContext* cx, JSObject* newObj,
+                         const Value* compare, size_t ncompare);
+
+bool
+CombinePlainObjectPropertyTypes(ExclusiveContext* cx, JSObject* newObj,
+                                const Value* compare, size_t ncompare);
+
 } // namespace js
 
 #endif /* vm_ObjectGroup_h */
diff --git a/js/src/vm/StructuredClone.cpp b/js/src/vm/StructuredClone.cpp
index 268dd02f571f..c86f218e2954 100644
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -91,6 +91,7 @@ enum StructuredDataType : uint32_t {
     SCTAG_SET_OBJECT,
     SCTAG_END_OF_KEYS,
     SCTAG_SHARED_TYPED_ARRAY_OBJECT,
+    SCTAG_DATA_VIEW_OBJECT,
 
     SCTAG_TYPED_ARRAY_V1_MIN = 0xFFFF0100,
     SCTAG_TYPED_ARRAY_V1_INT8 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Int8,
@@ -238,6 +239,7 @@ struct JSStructuredCloneReader {
     bool checkDouble(double d);
     bool readTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp,
                         bool v1Read = false);
+    bool readDataView(uint32_t byteLength, MutableHandleValue vp);
     bool readSharedTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp);
     bool readArrayBuffer(uint32_t nbytes, MutableHandleValue vp);
     bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp);
@@ -295,6 +297,7 @@ struct JSStructuredCloneWriter {
     bool writeString(uint32_t tag, JSString* str);
     bool writeArrayBuffer(HandleObject obj);
     bool writeTypedArray(HandleObject obj);
+    bool writeDataView(HandleObject obj);
     bool writeSharedArrayBuffer(HandleObject obj);
     bool writeSharedTypedArray(HandleObject obj);
     bool startObject(HandleObject obj, bool* backref);
@@ -863,6 +866,23 @@ JSStructuredCloneWriter::writeTypedArray(HandleObject obj)
     return out.write(tarr->byteOffset());
 }
 
+bool
+JSStructuredCloneWriter::writeDataView(HandleObject obj)
+{
+    Rooted view(context(), &CheckedUnwrap(obj)->as());
+    JSAutoCompartment ac(context(), view);
+
+    if (!out.writePair(SCTAG_DATA_VIEW_OBJECT, view->byteLength()))
+        return false;
+
+    // Write out the ArrayBuffer tag and contents
+    RootedValue val(context(), DataViewObject::bufferValue(view));
+    if (!startWrite(val))
+        return false;
+
+    return out.write(view->byteOffset());
+}
+
 bool
 JSStructuredCloneWriter::writeArrayBuffer(HandleObject obj)
 {
@@ -1046,6 +1066,8 @@ JSStructuredCloneWriter::startWrite(HandleValue v)
             return out.writePair(SCTAG_DATE_OBJECT, 0) && out.writeDouble(unboxed.toNumber());
         } else if (JS_IsTypedArrayObject(obj)) {
             return writeTypedArray(obj);
+        } else if (JS_IsDataViewObject(obj)) {
+            return writeDataView(obj);
         } else if (JS_IsArrayBufferObject(obj) && JS_ArrayBufferHasData(obj)) {
             return writeArrayBuffer(obj);
         } else if (JS_IsSharedTypedArrayObject(obj)) {
@@ -1410,6 +1432,37 @@ JSStructuredCloneReader::readTypedArray(uint32_t arrayType, uint32_t nelems, Mut
     return true;
 }
 
+bool
+JSStructuredCloneReader::readDataView(uint32_t byteLength, MutableHandleValue vp)
+{
+    // Push a placeholder onto the allObjs list to stand in for the DataView.
+    uint32_t placeholderIndex = allObjs.length();
+    Value dummy = UndefinedValue();
+    if (!allObjs.append(dummy))
+        return false;
+
+    // Read the ArrayBuffer object and its contents (but no properties).
+    RootedValue v(context());
+    if (!startRead(&v))
+        return false;
+
+    // Read byteOffset.
+    uint64_t n;
+    if (!in.read(&n))
+        return false;
+    uint32_t byteOffset = n;
+
+    RootedObject buffer(context(), &v.toObject());
+    RootedObject obj(context(), JS_NewDataView(context(), buffer, byteOffset, byteLength));
+    if (!obj)
+        return false;
+    vp.setObject(*obj);
+
+    allObjs[placeholderIndex].set(vp);
+
+    return true;
+}
+
 bool
 JSStructuredCloneReader::readSharedTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp)
 {
@@ -1673,6 +1726,11 @@ JSStructuredCloneReader::startRead(MutableHandleValue vp)
         return readTypedArray(arrayType, data, vp);
       }
 
+      case SCTAG_DATA_VIEW_OBJECT: {
+        // readDataView adds the array to allObjs.
+        return readDataView(data, vp);
+      }
+
       case SCTAG_SHARED_TYPED_ARRAY_OBJECT: {
         // readSharedTypedArray adds the array to allObjs.
         uint64_t arrayType;
diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp
index c1b223618f37..755616340392 100644
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -3312,6 +3312,19 @@ PreliminaryObjectArray::registerNewObject(JSObject* res)
     MOZ_CRASH("There should be room for registering the new object");
 }
 
+void
+PreliminaryObjectArray::unregisterObject(JSObject* obj)
+{
+    for (size_t i = 0; i < COUNT; i++) {
+        if (objects[i] == obj) {
+            objects[i] = nullptr;
+            return;
+        }
+    }
+
+    MOZ_CRASH("The object should be in the array");
+}
+
 bool
 PreliminaryObjectArray::full() const
 {
diff --git a/js/src/vm/TypeInference.h b/js/src/vm/TypeInference.h
index 443f1d713e64..2e15d39d41de 100644
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -794,6 +794,7 @@ class PreliminaryObjectArray
     }
 
     void registerNewObject(JSObject* res);
+    void unregisterObject(JSObject* obj);
 
     JSObject* get(size_t i) const {
         MOZ_ASSERT(i < COUNT);
diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp
index c5a8a2e205e4..2e1fdf05fa77 100644
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -481,8 +481,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
   public:
     static JSObject*
     fromBuffer(JSContext* cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt) {
-        RootedObject proto(cx, nullptr);
-        return fromBufferWithProto(cx, bufobj, byteOffset, lengthInt, proto);
+        return fromBufferWithProto(cx, bufobj, byteOffset, lengthInt, nullptr);
     }
 
     static JSObject*
@@ -969,17 +968,11 @@ DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
 {
     MOZ_ASSERT(byteOffset <= INT32_MAX);
     MOZ_ASSERT(byteLength <= INT32_MAX);
+    MOZ_ASSERT(byteOffset + byteLength < UINT32_MAX);
 
     RootedObject proto(cx, protoArg);
     RootedObject obj(cx);
 
-    // This is overflow-safe: 2 * INT32_MAX is still a valid uint32_t.
-    if (byteOffset + byteLength > arrayBuffer->byteLength()) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1");
-        return nullptr;
-
-    }
-
     NewObjectKind newKind = DataViewNewObjectKind(cx, byteLength, proto);
     obj = NewBuiltinClassInstance(cx, &class_, newKind);
     if (!obj)
@@ -1002,12 +995,17 @@ DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
         }
     }
 
+    // Caller should have established these preconditions, and no
+    // (non-self-hosted) JS code has had an opportunity to run so nothing can
+    // have invalidated them.
+    MOZ_ASSERT(byteOffset <= arrayBuffer->byteLength());
+    MOZ_ASSERT(byteOffset + byteLength <= arrayBuffer->byteLength());
+
     DataViewObject& dvobj = obj->as();
     dvobj.setFixedSlot(TypedArrayLayout::BYTEOFFSET_SLOT, Int32Value(byteOffset));
     dvobj.setFixedSlot(TypedArrayLayout::LENGTH_SLOT, Int32Value(byteLength));
     dvobj.setFixedSlot(TypedArrayLayout::BUFFER_SLOT, ObjectValue(*arrayBuffer));
     dvobj.initPrivate(arrayBuffer->dataPointer() + byteOffset);
-    MOZ_ASSERT(byteOffset + byteLength <= arrayBuffer->byteLength());
 
     // Include a barrier if the data view's data pointer is in the nursery, as
     // is done for typed arrays.
@@ -1033,9 +1031,8 @@ DataViewObject::construct(JSContext* cx, JSObject* bufobj, const CallArgs& args,
     }
 
     Rooted buffer(cx, &AsArrayBuffer(bufobj));
-    uint32_t bufferLength = buffer->byteLength();
     uint32_t byteOffset = 0;
-    uint32_t byteLength = bufferLength;
+    uint32_t byteLength = buffer->byteLength();
 
     if (args.length() > 1) {
         if (!ToUint32(cx, args[1], &byteOffset))
@@ -1055,6 +1052,8 @@ DataViewObject::construct(JSContext* cx, JSObject* bufobj, const CallArgs& args,
                 return false;
             }
         } else {
+            uint32_t bufferLength = buffer->byteLength();
+
             if (byteOffset > bufferLength) {
                 JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
                                      JSMSG_ARG_INDEX_OUT_OF_RANGE, "1");
@@ -1069,7 +1068,7 @@ DataViewObject::construct(JSContext* cx, JSObject* bufobj, const CallArgs& args,
     MOZ_ASSERT(byteOffset <= INT32_MAX);
     MOZ_ASSERT(byteLength <= INT32_MAX);
 
-    if (byteOffset + byteLength > bufferLength) {
+    if (byteOffset + byteLength > buffer->byteLength()) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1");
         return false;
     }
@@ -2348,3 +2347,27 @@ JS_GetDataViewByteLength(JSObject* obj)
         return 0;
     return obj->as().byteLength();
 }
+
+JS_FRIEND_API(JSObject*)
+JS_NewDataView(JSContext* cx, HandleObject arrayBuffer, uint32_t byteOffset, int32_t byteLength)
+{
+    ConstructArgs cargs(cx);
+    if (!cargs.init(3))
+        return nullptr;
+
+    RootedObject constructor(cx);
+    JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(&DataViewObject::class_);
+    if (!GetBuiltinConstructor(cx, key, &constructor))
+        return nullptr;
+
+    cargs[0].setObject(*arrayBuffer);
+    cargs[1].setNumber(byteOffset);
+    cargs[2].setInt32(byteLength);
+
+    RootedValue fun(cx, ObjectValue(*constructor));
+    RootedValue rval(cx);
+    if (!Construct(cx, fun, cargs, fun, &rval))
+        return nullptr;
+    MOZ_ASSERT(rval.isObject());
+    return &rval.toObject();
+}
diff --git a/js/src/vm/UnboxedObject-inl.h b/js/src/vm/UnboxedObject-inl.h
index c02440c46622..abd824d18c26 100644
--- a/js/src/vm/UnboxedObject-inl.h
+++ b/js/src/vm/UnboxedObject-inl.h
@@ -272,6 +272,12 @@ UnboxedArrayObject::triggerPreBarrier(size_t index)
 // Combined methods for NativeObject and UnboxedArrayObject accesses.
 /////////////////////////////////////////////////////////////////////
 
+static inline bool
+HasAnyBoxedOrUnboxedDenseElements(JSObject* obj)
+{
+    return obj->isNative() || obj->is();
+}
+
 static inline size_t
 GetAnyBoxedOrUnboxedInitializedLength(JSObject* obj)
 {
@@ -330,6 +336,16 @@ SetAnyBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, con
     return obj->as().setElement(cx, index, value);
 }
 
+static inline bool
+InitAnyBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value)
+{
+    if (obj->isNative()) {
+        obj->as().initDenseElementWithType(cx, index, value);
+        return true;
+    }
+    return obj->as().initElement(cx, index, value);
+}
+
 /////////////////////////////////////////////////////////////////////
 // Template methods for NativeObject and UnboxedArrayObject accesses.
 /////////////////////////////////////////////////////////////////////
@@ -417,19 +433,19 @@ SetBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const
 }
 
 template 
-static inline bool
+static inline DenseElementResult
 EnsureBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count)
 {
     if (Type == JSVAL_TYPE_MAGIC) {
         if (!obj->as().ensureElements(cx, count))
-            return false;
+            return DenseElementResult::Failure;
     } else {
         if (obj->as().capacity() < count) {
             if (!obj->as().growElements(cx, count))
-                return false;
+                return DenseElementResult::Failure;
         }
     }
-    return true;
+    return DenseElementResult::Success;
 }
 
 template 
@@ -547,33 +563,54 @@ MoveBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, uint32_t dstStart,
     return DenseElementResult::Success;
 }
 
-template 
+template 
 static inline DenseElementResult
 CopyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
                                 uint32_t dstStart, uint32_t srcStart, uint32_t length)
 {
-    MOZ_ASSERT(HasBoxedOrUnboxedDenseElements(src));
-    MOZ_ASSERT(HasBoxedOrUnboxedDenseElements(dst));
-    MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength(dst) == dstStart);
-    MOZ_ASSERT(GetBoxedOrUnboxedCapacity(dst) >= length);
-
-    SetBoxedOrUnboxedInitializedLength(cx, dst, dstStart + length);
-
-    if (Type == JSVAL_TYPE_MAGIC) {
-        const Value* vp = src->as().getDenseElements() + srcStart;
-        dst->as().initDenseElements(dstStart, vp, length);
-    } else {
+    MOZ_ASSERT(HasBoxedOrUnboxedDenseElements(src));
+    MOZ_ASSERT(HasBoxedOrUnboxedDenseElements(dst));
+    MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength(dst) == dstStart);
+    MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength(src) >= srcStart + length);
+    MOZ_ASSERT(GetBoxedOrUnboxedCapacity(dst) >= dstStart + length);
+
+    SetBoxedOrUnboxedInitializedLength(cx, dst, dstStart + length);
+
+    if (DstType == JSVAL_TYPE_MAGIC) {
+        if (SrcType == JSVAL_TYPE_MAGIC) {
+            const Value* vp = src->as().getDenseElements() + srcStart;
+            dst->as().initDenseElements(dstStart, vp, length);
+        } else {
+            for (size_t i = 0; i < length; i++) {
+                Value v = GetBoxedOrUnboxedDenseElement(src, srcStart + i);
+                dst->as().initDenseElement(dstStart + i, v);
+            }
+        }
+    } else if (DstType == SrcType) {
         uint8_t* dstData = dst->as().elements();
         uint8_t* srcData = src->as().elements();
-        size_t elementSize = UnboxedTypeSize(Type);
+        size_t elementSize = UnboxedTypeSize(DstType);
 
         memcpy(dstData + dstStart * elementSize,
                srcData + srcStart * elementSize,
                length * elementSize);
 
         // Add a store buffer entry if we might have copied a nursery pointer to dst.
-        if (UnboxedTypeNeedsPostBarrier(Type) && !IsInsideNursery(dst))
+        if (UnboxedTypeNeedsPostBarrier(DstType) && !IsInsideNursery(dst))
             dst->runtimeFromMainThread()->gc.storeBuffer.putWholeCellFromMainThread(dst);
+    } else if (DstType == JSVAL_TYPE_DOUBLE && SrcType == JSVAL_TYPE_INT32) {
+        uint8_t* dstData = dst->as().elements();
+        uint8_t* srcData = src->as().elements();
+
+        for (size_t i = 0; i < length; i++) {
+            int32_t v = *reinterpret_cast(srcData + (srcStart + i) * sizeof(int32_t));
+            *reinterpret_cast(dstData + (dstStart + i) * sizeof(double)) = v;
+        }
+    } else {
+        for (size_t i = 0; i < length; i++) {
+            Value v = GetBoxedOrUnboxedDenseElement(src, srcStart + i);
+            dst->as().initElementNoTypeChangeSpecific(dstStart + i, v);
+        }
     }
 
     return DenseElementResult::Success;
@@ -598,19 +635,70 @@ template 
 DenseElementResult
 CallBoxedOrUnboxedSpecialization(F f, JSObject* obj)
 {
-    if (HasBoxedOrUnboxedDenseElements(obj))
+    if (!HasAnyBoxedOrUnboxedDenseElements(obj))
+        return DenseElementResult::Incomplete;
+    switch (GetBoxedOrUnboxedType(obj)) {
+      case JSVAL_TYPE_MAGIC:
         return f. DEPENDENT_TEMPLATE_HINT operator()();
-    if (HasBoxedOrUnboxedDenseElements(obj))
+      case JSVAL_TYPE_BOOLEAN:
         return f. DEPENDENT_TEMPLATE_HINT operator()();
-    if (HasBoxedOrUnboxedDenseElements(obj))
+      case JSVAL_TYPE_INT32:
         return f. DEPENDENT_TEMPLATE_HINT operator()();
-    if (HasBoxedOrUnboxedDenseElements(obj))
+      case JSVAL_TYPE_DOUBLE:
         return f. DEPENDENT_TEMPLATE_HINT operator()();
-    if (HasBoxedOrUnboxedDenseElements(obj))
+      case JSVAL_TYPE_STRING:
         return f. DEPENDENT_TEMPLATE_HINT operator()();
-    if (HasBoxedOrUnboxedDenseElements(obj))
+      case JSVAL_TYPE_OBJECT:
         return f. DEPENDENT_TEMPLATE_HINT operator()();
-    return DenseElementResult::Incomplete;
+      default:
+        MOZ_CRASH();
+    }
+}
+
+// As above, except the specialization can reflect the unboxed type of two objects.
+template 
+DenseElementResult
+CallBoxedOrUnboxedSpecialization(F f, JSObject* obj1, JSObject* obj2)
+{
+    if (!HasAnyBoxedOrUnboxedDenseElements(obj1) || !HasAnyBoxedOrUnboxedDenseElements(obj2))
+        return DenseElementResult::Incomplete;
+
+#define SPECIALIZE_OBJ2(TYPE)                                                     \
+    switch (GetBoxedOrUnboxedType(obj2)) {                                        \
+      case JSVAL_TYPE_MAGIC:                                                      \
+        return f. DEPENDENT_TEMPLATE_HINT operator()();   \
+      case JSVAL_TYPE_BOOLEAN:                                                    \
+        return f. DEPENDENT_TEMPLATE_HINT operator()(); \
+      case JSVAL_TYPE_INT32:                                                      \
+        return f. DEPENDENT_TEMPLATE_HINT operator()();   \
+      case JSVAL_TYPE_DOUBLE:                                                     \
+        return f. DEPENDENT_TEMPLATE_HINT operator()();  \
+      case JSVAL_TYPE_STRING:                                                     \
+        return f. DEPENDENT_TEMPLATE_HINT operator()();  \
+      case JSVAL_TYPE_OBJECT:                                                     \
+        return f. DEPENDENT_TEMPLATE_HINT operator()();  \
+      default:                                                                    \
+        MOZ_CRASH();                                                              \
+    }
+
+    switch (GetBoxedOrUnboxedType(obj1)) {
+      case JSVAL_TYPE_MAGIC:
+        SPECIALIZE_OBJ2(JSVAL_TYPE_MAGIC)
+      case JSVAL_TYPE_BOOLEAN:
+        SPECIALIZE_OBJ2(JSVAL_TYPE_BOOLEAN)
+      case JSVAL_TYPE_INT32:
+        SPECIALIZE_OBJ2(JSVAL_TYPE_INT32)
+      case JSVAL_TYPE_DOUBLE:
+        SPECIALIZE_OBJ2(JSVAL_TYPE_DOUBLE)
+      case JSVAL_TYPE_STRING:
+        SPECIALIZE_OBJ2(JSVAL_TYPE_STRING)
+      case JSVAL_TYPE_OBJECT:
+        SPECIALIZE_OBJ2(JSVAL_TYPE_OBJECT)
+      default:
+        MOZ_CRASH();
+    }
+
+#undef SPECIALIZE_OBJ2
 }
 
 #undef DEPENDENT_TEMPLATE_HINT
@@ -651,6 +739,18 @@ struct Signature ## Functor {                                           \
     }                                                                   \
 }
 
+#define DefineBoxedOrUnboxedFunctorPair4(Signature, A, B, C, D)         \
+struct Signature ## Functor {                                           \
+    A a; B b; C c; D d;                                                 \
+    Signature ## Functor(A a, B b, C c, D d)                            \
+      : a(a), b(b), c(c), d(d)                                          \
+    {}                                                                  \
+    template                  \
+    DenseElementResult operator()() {                                   \
+        return Signature(a, b, c, d);                 \
+    }                                                                   \
+}
+
 #define DefineBoxedOrUnboxedFunctor5(Signature, A, B, C, D, E)          \
 struct Signature ## Functor {                                           \
     A a; B b; C c; D d; E e;                                            \
@@ -675,6 +775,18 @@ struct Signature ## Functor {                                           \
     }                                                                   \
 }
 
+#define DefineBoxedOrUnboxedFunctorPair6(Signature, A, B, C, D, E, F)   \
+struct Signature ## Functor {                                           \
+    A a; B b; C c; D d; E e; F f;                                       \
+    Signature ## Functor(A a, B b, C c, D d, E e, F f)                  \
+      : a(a), b(b), c(c), d(d), e(e), f(f)                              \
+    {}                                                                  \
+    template                  \
+    DenseElementResult operator()() {                                   \
+        return Signature(a, b, c, d, e, f);           \
+    }                                                                   \
+}
+
 DenseElementResult
 SetOrExtendAnyBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj,
                                           uint32_t start, const Value* vp, uint32_t count,
@@ -691,7 +803,7 @@ CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
 void
 SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen);
 
-bool
+DenseElementResult
 EnsureAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count);
 
 } // namespace js
diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp
index 860291ea1e1f..760dd0112354 100644
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -979,15 +979,9 @@ DefineBoxedOrUnboxedFunctor3(AppendUnboxedDenseElements,
                              UnboxedArrayObject*, uint32_t, AutoValueVector*);
 
 /* static */ bool
-UnboxedArrayObject::convertToNative(JSContext* cx, JSObject* obj)
+UnboxedArrayObject::convertToNativeWithGroup(ExclusiveContext* cx, JSObject* obj,
+                                             ObjectGroup* group, Shape* shape)
 {
-    const UnboxedLayout& layout = obj->as().layout();
-
-    if (!layout.nativeGroup()) {
-        if (!UnboxedLayout::makeNativeGroup(cx, obj->group()))
-            return false;
-    }
-
     size_t length = obj->as().length();
     size_t initlen = obj->as().initializedLength();
 
@@ -999,10 +993,10 @@ UnboxedArrayObject::convertToNative(JSContext* cx, JSObject* obj)
     DebugOnly result = CallBoxedOrUnboxedSpecialization(functor, obj);
     MOZ_ASSERT(result.value == DenseElementResult::Success);
 
-    obj->setGroup(layout.nativeGroup());
+    obj->setGroup(group);
 
     ArrayObject* aobj = &obj->as();
-    aobj->setLastPropertyMakeNative(cx, layout.nativeShape());
+    aobj->setLastPropertyMakeNative(cx, shape);
 
     // Make sure there is at least one element, so that this array does not
     // use emptyObjectElements.
@@ -1017,6 +1011,46 @@ UnboxedArrayObject::convertToNative(JSContext* cx, JSObject* obj)
     return true;
 }
 
+/* static */ bool
+UnboxedArrayObject::convertToNative(JSContext* cx, JSObject* obj)
+{
+    const UnboxedLayout& layout = obj->as().layout();
+
+    if (!layout.nativeGroup()) {
+        if (!UnboxedLayout::makeNativeGroup(cx, obj->group()))
+            return false;
+    }
+
+    return convertToNativeWithGroup(cx, obj, layout.nativeGroup(), layout.nativeShape());
+}
+
+bool
+UnboxedArrayObject::convertInt32ToDouble(ExclusiveContext* cx, ObjectGroup* group)
+{
+    MOZ_ASSERT(elementType() == JSVAL_TYPE_INT32);
+    MOZ_ASSERT(group->unboxedLayout().elementType() == JSVAL_TYPE_DOUBLE);
+
+    Vector values(cx);
+    if (!values.reserve(initializedLength()))
+        return false;
+    for (size_t i = 0; i < initializedLength(); i++)
+        values.infallibleAppend(getElementSpecific(i).toInt32());
+
+    uint8_t* newElements = ReallocateObjectBuffer(cx, this, elements(),
+                                                           capacity() * sizeof(int32_t),
+                                                           capacity() * sizeof(double));
+    if (!newElements)
+        return false;
+
+    setGroup(group);
+    elements_ = newElements;
+
+    for (size_t i = 0; i < initializedLength(); i++)
+        setElementNoTypeChangeSpecific(i, DoubleValue(values[i]));
+
+    return true;
+}
+
 /* static */ UnboxedArrayObject*
 UnboxedArrayObject::create(ExclusiveContext* cx, HandleObjectGroup group, uint32_t length,
                            NewObjectKind newKind, uint32_t maxLength)
@@ -1679,11 +1713,9 @@ CombineArrayObjectElements(ExclusiveContext* cx, ArrayObject* obj, JSValueType*
 {
     if (obj->inDictionaryMode() ||
         obj->lastProperty()->propid() != AtomToId(cx->names().length) ||
-        !obj->lastProperty()->previous()->isEmptyShape() ||
-        !obj->getDenseInitializedLength())
+        !obj->lastProperty()->previous()->isEmptyShape())
     {
-        // Only use an unboxed representation if the object has at
-        // least one element, and no properties.
+        // Only use an unboxed representation if the object has no properties.
         return false;
     }
 
@@ -1838,6 +1870,8 @@ UnboxedArrayObject::fillAfterConvert(ExclusiveContext* cx,
     setLength(cx, NextValue(values, valueCursor).toInt32());
 
     int32_t initlen = NextValue(values, valueCursor).toInt32();
+    if (!initlen)
+        return;
 
     if (!growElements(cx, initlen))
         CrashAtUnhandlableOOM("UnboxedArrayObject::fillAfterConvert");
@@ -2063,15 +2097,15 @@ js::MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
     return CallBoxedOrUnboxedSpecialization(functor, obj);
 }
 
-DefineBoxedOrUnboxedFunctor6(CopyBoxedOrUnboxedDenseElements,
-                             JSContext*, JSObject*, JSObject*, uint32_t, uint32_t, uint32_t);
+DefineBoxedOrUnboxedFunctorPair6(CopyBoxedOrUnboxedDenseElements,
+                                 JSContext*, JSObject*, JSObject*, uint32_t, uint32_t, uint32_t);
 
 DenseElementResult
 js::CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
                                        uint32_t dstStart, uint32_t srcStart, uint32_t length)
 {
     CopyBoxedOrUnboxedDenseElementsFunctor functor(cx, dst, src, dstStart, srcStart, length);
-    return CallBoxedOrUnboxedSpecialization(functor, dst);
+    return CallBoxedOrUnboxedSpecialization(functor, dst, src);
 }
 
 DefineBoxedOrUnboxedFunctor3(SetBoxedOrUnboxedInitializedLength,
@@ -2083,3 +2117,13 @@ js::SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t i
     SetBoxedOrUnboxedInitializedLengthFunctor functor(cx, obj, initlen);
     JS_ALWAYS_TRUE(CallBoxedOrUnboxedSpecialization(functor, obj) == DenseElementResult::Success);
 }
+
+DefineBoxedOrUnboxedFunctor3(EnsureBoxedOrUnboxedDenseElements,
+                             JSContext*, JSObject*, size_t);
+
+DenseElementResult
+js::EnsureAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t initlen)
+{
+    EnsureBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, initlen);
+    return CallBoxedOrUnboxedSpecialization(functor, obj);
+}
diff --git a/js/src/vm/UnboxedObject.h b/js/src/vm/UnboxedObject.h
index 04be43646f09..313bb58cb48d 100644
--- a/js/src/vm/UnboxedObject.h
+++ b/js/src/vm/UnboxedObject.h
@@ -421,6 +421,10 @@ class UnboxedArrayObject : public JSObject
                                       uint32_t length, NewObjectKind newKind,
                                       uint32_t maxLength = MaximumCapacity);
 
+    static bool convertToNativeWithGroup(ExclusiveContext* cx, JSObject* obj,
+                                         ObjectGroup* group, Shape* shape);
+    bool convertInt32ToDouble(ExclusiveContext* cx, ObjectGroup* group);
+
     void fillAfterConvert(ExclusiveContext* cx,
                           const AutoValueVector& values, size_t* valueCursor);
 
diff --git a/layout/base/AccessibleCaretEventHub.cpp b/layout/base/AccessibleCaretEventHub.cpp
index eb0cd4d78660..490943895821 100644
--- a/layout/base/AccessibleCaretEventHub.cpp
+++ b/layout/base/AccessibleCaretEventHub.cpp
@@ -500,7 +500,7 @@ AccessibleCaretEventHub::HandleMouseEvent(WidgetMouseEvent* aEvent)
                 kDefaultTouchId : mActiveTouchId);
   nsPoint point = GetMouseEventPosition(aEvent);
 
-  switch (aEvent->message) {
+  switch (aEvent->mMessage) {
   case NS_MOUSE_BUTTON_DOWN:
     AC_LOGV("Before NS_MOUSE_BUTTON_DOWN, state: %s", mState->Name());
     rv = mState->OnPress(this, point, id);
@@ -538,7 +538,7 @@ AccessibleCaretEventHub::HandleMouseEvent(WidgetMouseEvent* aEvent)
 nsEventStatus
 AccessibleCaretEventHub::HandleWheelEvent(WidgetWheelEvent* aEvent)
 {
-  switch (aEvent->message) {
+  switch (aEvent->mMessage) {
   case NS_WHEEL_WHEEL:
     AC_LOGV("NS_WHEEL_WHEEL, isMomentum %d, state: %s", aEvent->isMomentum,
             mState->Name());
@@ -573,7 +573,7 @@ AccessibleCaretEventHub::HandleTouchEvent(WidgetTouchEvent* aEvent)
                 aEvent->touches[0]->Identifier() : mActiveTouchId);
   nsPoint point = GetTouchEventPosition(aEvent, id);
 
-  switch (aEvent->message) {
+  switch (aEvent->mMessage) {
   case NS_TOUCH_START:
     AC_LOGV("Before NS_TOUCH_START, state: %s", mState->Name());
     rv = mState->OnPress(this, point, id);
@@ -609,7 +609,7 @@ AccessibleCaretEventHub::HandleTouchEvent(WidgetTouchEvent* aEvent)
 nsEventStatus
 AccessibleCaretEventHub::HandleKeyboardEvent(WidgetKeyboardEvent* aEvent)
 {
-  switch (aEvent->message) {
+  switch (aEvent->mMessage) {
   case NS_KEY_UP:
   case NS_KEY_DOWN:
   case NS_KEY_PRESS:
diff --git a/layout/base/AccessibleCaretManager.cpp b/layout/base/AccessibleCaretManager.cpp
index c522fb2a233d..94c4a83af462 100644
--- a/layout/base/AccessibleCaretManager.cpp
+++ b/layout/base/AccessibleCaretManager.cpp
@@ -328,33 +328,38 @@ AccessibleCaretManager::SelectWordOrShortcut(const nsPoint& aPoint)
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  // Find content offsets for mouse down point
+  // Find the frame under point.
   nsIFrame* ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, aPoint,
     nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC);
   if (!ptFrame) {
     return NS_ERROR_FAILURE;
   }
 
-  bool selectable;
-  ptFrame->IsSelectable(&selectable, nullptr);
-  if (!selectable) {
-    return NS_ERROR_FAILURE;
-  }
+  nsIFrame* focusedFrame = ChangeFocus(ptFrame);
 
-  nsPoint ptInFrame = aPoint;
-  nsLayoutUtils::TransformPoint(rootFrame, ptFrame, ptInFrame);
+#ifdef DEBUG_FRAME_DUMP
+  AC_LOG("%s: Found %s under (%d, %d)", __FUNCTION__, ptFrame->ListTag().get(),
+         aPoint.x, aPoint.y);
+  AC_LOG("%s: Focused on %s", __FUNCTION__,
+         focusedFrame ? focusedFrame->ListTag().get() : "no frame");
+#endif
 
-  nsIContent* editingHost = ptFrame->GetContent()->GetEditingHost();
-  if (ChangeFocus(ptFrame) &&
-      (editingHost && !nsContentUtils::HasNonEmptyTextContent(
-                         editingHost, nsContentUtils::eRecurseIntoChildren))) {
-    // Content is empty. No need to select word.
-    AC_LOG("%s, Cannot select word bacause content is empty", __FUNCTION__);
-    DispatchCaretStateChangedEvent(CaretChangedReason::Longpressonemptycontent);
+  // Firstly check long press on an empty editable content.
+  Element* newFocusEditingHost = ptFrame->GetContent()->GetEditingHost();
+  if (focusedFrame && newFocusEditingHost &&
+      !nsContentUtils::HasNonEmptyTextContent(
+        newFocusEditingHost, nsContentUtils::eRecurseIntoChildren)) {
+    // We need to update carets to get correct information before dispatching
+    // CaretStateChangedEvent.
     UpdateCarets();
+    DispatchCaretStateChangedEvent(CaretChangedReason::Longpressonemptycontent);
     return NS_OK;
   }
 
+  // Then try select a word under point.
+  nsPoint ptInFrame = aPoint;
+  nsLayoutUtils::TransformPoint(rootFrame, ptFrame, ptInFrame);
+
   nsresult rv = SelectWord(ptFrame, ptInFrame);
   UpdateCarets();
   return rv;
@@ -502,62 +507,54 @@ AccessibleCaretManager::GetCaretMode() const
   return CaretMode::Selection;
 }
 
-bool
+nsIFrame*
 AccessibleCaretManager::ChangeFocus(nsIFrame* aFrame) const
 {
-  nsIFrame* currFrame = aFrame;
-  nsIContent* newFocusContent = nullptr;
-  while (currFrame) {
-    int32_t tabIndexUnused = 0;
-    if (currFrame->IsFocusable(&tabIndexUnused, true)) {
-      newFocusContent = currFrame->GetContent();
-      nsCOMPtr domElement(do_QueryInterface(newFocusContent));
-      if (domElement)
-        break;
+  // This implementation is similar to EventStateManager::PostHandleEvent().
+  // Look for the nearest enclosing focusable frame.
+  nsIFrame* focusableFrame = aFrame;
+  while (focusableFrame) {
+    if (focusableFrame->IsFocusable(nullptr, true)) {
+      break;
     }
-    currFrame = currFrame->GetParent();
+    focusableFrame = focusableFrame->GetParent();
   }
 
-  // If target frame is focusable, we should move focus to it. If target frame
-  // isn't focusable, and our previous focused content is editable, we should
-  // clear focus.
+  // If a focusable frame is found, move focus to it. Otherwise, clear the old
+  // focus then re-focus the window.
   nsFocusManager* fm = nsFocusManager::GetFocusManager();
-  if (newFocusContent && currFrame) {
-    nsCOMPtr domElement(do_QueryInterface(newFocusContent));
-    fm->SetFocus(domElement, 0);
+  MOZ_ASSERT(fm);
+
+  if (focusableFrame) {
+    nsIContent* focusableContent = focusableFrame->GetContent();
+    MOZ_ASSERT(focusableContent, "Focusable frame must have content!");
+    nsCOMPtr focusableElement = do_QueryInterface(focusableContent);
+    fm->SetFocus(focusableElement, nsIFocusManager::FLAG_BYMOUSE);
   } else {
-    nsIContent* focusedContent = GetFocusedContent();
-    if (focusedContent) {
-      // Clear focus if content was editable element, or contentEditable.
-      nsGenericHTMLElement* focusedGeneric =
-        nsGenericHTMLElement::FromContent(focusedContent);
-      if (focusedContent->GetTextEditorRootContent() ||
-          (focusedGeneric && focusedGeneric->IsContentEditable())) {
-        nsIDOMWindow* win = mPresShell->GetDocument()->GetWindow();
-        if (win) {
-          fm->ClearFocus(win);
-        }
-      }
+    nsIDOMWindow* win = mPresShell->GetDocument()->GetWindow();
+    if (win) {
+      fm->ClearFocus(win);
+      fm->SetFocusedWindow(win);
     }
   }
 
-  return (newFocusContent && currFrame);
+  return focusableFrame;
 }
 
 nsresult
 AccessibleCaretManager::SelectWord(nsIFrame* aFrame, const nsPoint& aPoint) const
 {
+  bool selectable;
+  aFrame->IsSelectable(&selectable, nullptr);
+  if (!selectable) {
+    return NS_ERROR_FAILURE;
+  }
+
   SetSelectionDragState(true);
   nsFrame* frame = static_cast(aFrame);
   nsresult rs = frame->SelectByTypeAtPoint(mPresShell->GetPresContext(), aPoint,
                                            eSelectWord, eSelectWord, 0);
 
-#ifdef DEBUG_FRAME_DUMP
-  nsCString frameTag;
-  frame->ListTag(frameTag);
-  AC_LOG("Frame=%s, ptInFrame=(%d, %d)", frameTag.get(), aPoint.x, aPoint.y);
-#endif
-
   SetSelectionDragState(false);
   ClearMaintainedSelection();
 
@@ -957,6 +954,10 @@ AccessibleCaretManager::DispatchCaretStateChangedEvent(CaretChangedReason aReaso
 
   event->SetTrusted(true);
   event->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true;
+
+  AC_LOG("%s: reason %d, collapsed %d, caretVisible %d", __FUNCTION__,
+         init.mReason, init.mCollapsed, init.mCaretVisible);
+
   (new AsyncEventDispatcher(doc, event))->RunDOMEventWhenSafe();
 }
 
diff --git a/layout/base/AccessibleCaretManager.h b/layout/base/AccessibleCaretManager.h
index 4cae49fa8b18..55c661a86c27 100644
--- a/layout/base/AccessibleCaretManager.h
+++ b/layout/base/AccessibleCaretManager.h
@@ -115,7 +115,10 @@ class AccessibleCaretManager
   void UpdateCaretsForSelectionMode();
   void UpdateCaretsForTilt();
 
-  bool ChangeFocus(nsIFrame* aFrame) const;
+  // Change focus to the nearest enclosing focusable frame of aFrame.
+  // @return focusable frame if there is any; nullptr otherwise.
+  nsIFrame* ChangeFocus(nsIFrame* aFrame) const;
+
   nsresult SelectWord(nsIFrame* aFrame, const nsPoint& aPoint) const;
   void SetSelectionDragState(bool aState) const;
   void SetSelectionDirection(nsDirection aDir) const;
diff --git a/layout/base/SelectionCarets.cpp b/layout/base/SelectionCarets.cpp
index def5269122aa..cf7177696ded 100644
--- a/layout/base/SelectionCarets.cpp
+++ b/layout/base/SelectionCarets.cpp
@@ -204,11 +204,11 @@ SelectionCarets::HandleEvent(WidgetEvent* aEvent)
   nsPoint ptInRoot =
     nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, movePoint, rootFrame);
 
-  if (aEvent->message == NS_TOUCH_START ||
-      (aEvent->message == NS_MOUSE_BUTTON_DOWN &&
+  if (aEvent->mMessage == NS_TOUCH_START ||
+      (aEvent->mMessage == NS_MOUSE_BUTTON_DOWN &&
        mouseEvent->button == WidgetMouseEvent::eLeftButton)) {
     // If having a active touch, ignore other touch down event
-    if (aEvent->message == NS_TOUCH_START && mActiveTouchId >= 0) {
+    if (aEvent->mMessage == NS_TOUCH_START && mActiveTouchId >= 0) {
       return nsEventStatus_eConsumeNoDefault;
     }
 
@@ -231,9 +231,9 @@ SelectionCarets::HandleEvent(WidgetEvent* aEvent)
       mActiveTouchId = -1;
       LaunchLongTapDetector();
     }
-  } else if (aEvent->message == NS_TOUCH_END ||
-             aEvent->message == NS_TOUCH_CANCEL ||
-             aEvent->message == NS_MOUSE_BUTTON_UP) {
+  } else if (aEvent->mMessage == NS_TOUCH_END ||
+             aEvent->mMessage == NS_TOUCH_CANCEL ||
+             aEvent->mMessage == NS_MOUSE_BUTTON_UP) {
     CancelLongTapDetector();
     if (mDragMode != NONE) {
       // Only care about same id
@@ -244,8 +244,8 @@ SelectionCarets::HandleEvent(WidgetEvent* aEvent)
       }
       return nsEventStatus_eConsumeNoDefault;
     }
-  } else if (aEvent->message == NS_TOUCH_MOVE ||
-             aEvent->message == NS_MOUSE_MOVE) {
+  } else if (aEvent->mMessage == NS_TOUCH_MOVE ||
+             aEvent->mMessage == NS_MOUSE_MOVE) {
     if (mDragMode == START_FRAME || mDragMode == END_FRAME) {
       if (mActiveTouchId == nowTouchId) {
         ptInRoot.y += mCaretCenterToDownPointOffsetY;
@@ -271,7 +271,7 @@ SelectionCarets::HandleEvent(WidgetEvent* aEvent)
       CancelLongTapDetector();
     }
 
-  } else if (aEvent->message == NS_MOUSE_MOZLONGTAP) {
+  } else if (aEvent->mMessage == NS_MOUSE_MOZLONGTAP) {
     if (!mVisible || !sSelectionCaretDetectsLongTap) {
       SELECTIONCARETS_LOG("SelectWord from NS_MOUSE_MOZLONGTAP");
 
diff --git a/layout/base/TouchCaret.cpp b/layout/base/TouchCaret.cpp
index 83a517edad37..0775f0c4e920 100644
--- a/layout/base/TouchCaret.cpp
+++ b/layout/base/TouchCaret.cpp
@@ -759,7 +759,7 @@ TouchCaret::HandleEvent(WidgetEvent* aEvent)
 
   nsEventStatus status = nsEventStatus_eIgnore;
 
-  switch (aEvent->message) {
+  switch (aEvent->mMessage) {
     case NS_TOUCH_START:
       status = HandleTouchDownEvent(aEvent->AsTouchEvent());
       break;
@@ -790,7 +790,7 @@ TouchCaret::HandleEvent(WidgetEvent* aEvent)
     case NS_WHEEL_START:
     case NS_WHEEL_STOP:
       // Disable touch caret while key/wheel event is received.
-      TOUCHCARET_LOG("Receive key/wheel event %d", aEvent->message);
+      TOUCHCARET_LOG("Receive key/wheel event %d", aEvent->mMessage);
       SetVisibility(false);
       break;
     case NS_MOUSE_MOZLONGTAP:
diff --git a/layout/base/TouchManager.cpp b/layout/base/TouchManager.cpp
index 329507b69fae..6175a958c0af 100644
--- a/layout/base/TouchManager.cpp
+++ b/layout/base/TouchManager.cpp
@@ -101,7 +101,7 @@ TouchManager::PreHandleEvent(WidgetEvent* aEvent,
                              bool& aIsHandlingUserInput,
                              nsCOMPtr& aCurrentEventContent)
 {
-  switch (aEvent->message) {
+  switch (aEvent->mMessage) {
     case NS_TOUCH_START: {
       aIsHandlingUserInput = true;
       WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
@@ -123,7 +123,7 @@ TouchManager::PreHandleEvent(WidgetEvent* aEvent,
           // If it is not already in the queue, it is a new touch
           touch->mChanged = true;
         }
-        touch->mMessage = aEvent->message;
+        touch->mMessage = aEvent->mMessage;
         gCaptureTouchList->Put(id, touch);
       }
       break;
@@ -140,7 +140,7 @@ TouchManager::PreHandleEvent(WidgetEvent* aEvent,
           continue;
         }
         int32_t id = touch->Identifier();
-        touch->mMessage = aEvent->message;
+        touch->mMessage = aEvent->mMessage;
 
         nsRefPtr oldTouch = gCaptureTouchList->GetWeak(id);
         if (!oldTouch) {
@@ -203,7 +203,7 @@ TouchManager::PreHandleEvent(WidgetEvent* aEvent,
         if (!touch) {
           continue;
         }
-        touch->mMessage = aEvent->message;
+        touch->mMessage = aEvent->mMessage;
         touch->mChanged = true;
 
         int32_t id = touch->Identifier();
diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp
index 612115e3e3c2..ae9aa41e5142 100644
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -62,6 +62,7 @@
 #include "mozilla/dom/HTMLImageElement.h"
 #include "mozilla/dom/DOMRect.h"
 #include "mozilla/dom/KeyframeEffect.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
 #include "imgIRequest.h"
 #include "nsIImageLoadingContent.h"
 #include "nsCOMPtr.h"
@@ -1038,7 +1039,10 @@ GetDisplayPortImpl(nsIContent* aContent, nsRect *aResult, float aMultiplier)
                "Only one of rectData or marginsData should be set!");
 
   nsRect result;
-  if (rectData) {
+  if (APZCCallbackHelper::IsDisplayportSuppressed()) {
+    DisplayPortMarginsPropertyData noMargins(ScreenMargin(), 1);
+    result = GetDisplayPortFromMarginsData(aContent, &noMargins, aMultiplier);
+  } else if (rectData) {
     result = GetDisplayPortFromRectData(aContent, rectData, aMultiplier);
   } else {
     result = GetDisplayPortFromMarginsData(aContent, marginsData, aMultiplier);
diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp
index a75a550646ba..1d094ec1c8d1 100644
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -467,15 +467,15 @@ class MOZ_STACK_CLASS nsPresShellEventCB : public EventDispatchingCallback
   virtual void HandleEvent(EventChainPostVisitor& aVisitor) override
   {
     if (aVisitor.mPresContext && aVisitor.mEvent->mClass != eBasicEventClass) {
-      if (aVisitor.mEvent->message == NS_MOUSE_BUTTON_DOWN ||
-          aVisitor.mEvent->message == NS_MOUSE_BUTTON_UP) {
+      if (aVisitor.mEvent->mMessage == NS_MOUSE_BUTTON_DOWN ||
+          aVisitor.mEvent->mMessage == NS_MOUSE_BUTTON_UP) {
         // Mouse-up and mouse-down events call nsFrame::HandlePress/Release
         // which call GetContentOffsetsFromPoint which requires up-to-date layout.
         // Bring layout up-to-date now so that GetCurrentEventFrame() below
         // will return a real frame and we don't have to worry about
         // destroying it by flushing later.
         mPresShell->FlushPendingNotifications(Flush_Layout);
-      } else if (aVisitor.mEvent->message == NS_WHEEL_WHEEL &&
+      } else if (aVisitor.mEvent->mMessage == NS_WHEEL_WHEEL &&
                  aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault) {
         nsIFrame* frame = mPresShell->GetCurrentEventFrame();
         if (frame) {
@@ -492,8 +492,8 @@ class MOZ_STACK_CLASS nsPresShellEventCB : public EventDispatchingCallback
       }
       nsIFrame* frame = mPresShell->GetCurrentEventFrame();
       if (!frame &&
-          (aVisitor.mEvent->message == NS_MOUSE_BUTTON_UP ||
-           aVisitor.mEvent->message == NS_TOUCH_END)) {
+          (aVisitor.mEvent->mMessage == NS_MOUSE_BUTTON_UP ||
+           aVisitor.mEvent->mMessage == NS_TOUCH_END)) {
         // Redirect BUTTON_UP and TOUCH_END events to the root frame to ensure
         // that capturing is released.
         frame = mPresShell->GetRootFrame();
@@ -6342,7 +6342,7 @@ nsIPresShell::GetPointerInfo(uint32_t aPointerId, bool& aActiveState)
 void
 PresShell::UpdateActivePointerState(WidgetGUIEvent* aEvent)
 {
-  switch (aEvent->message) {
+  switch (aEvent->mMessage) {
   case NS_MOUSE_ENTER_WIDGET:
     // In this case we have to know information about available mouse pointers
     if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
@@ -6562,11 +6562,11 @@ PresShell::RecordMouseLocation(WidgetGUIEvent* aEvent)
     return;
   }
 
-  if ((aEvent->message == NS_MOUSE_MOVE &&
+  if ((aEvent->mMessage == NS_MOUSE_MOVE &&
        aEvent->AsMouseEvent()->reason == WidgetMouseEvent::eReal) ||
-      aEvent->message == NS_MOUSE_ENTER_WIDGET ||
-      aEvent->message == NS_MOUSE_BUTTON_DOWN ||
-      aEvent->message == NS_MOUSE_BUTTON_UP) {
+      aEvent->mMessage == NS_MOUSE_ENTER_WIDGET ||
+      aEvent->mMessage == NS_MOUSE_BUTTON_DOWN ||
+      aEvent->mMessage == NS_MOUSE_BUTTON_UP) {
     nsIFrame* rootFrame = GetRootFrame();
     if (!rootFrame) {
       nsView* rootView = mViewManager->GetRootView();
@@ -6577,15 +6577,17 @@ PresShell::RecordMouseLocation(WidgetGUIEvent* aEvent)
         nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, rootFrame);
     }
 #ifdef DEBUG_MOUSE_LOCATION
-    if (aEvent->message == NS_MOUSE_ENTER_WIDGET)
+    if (aEvent->mMessage == NS_MOUSE_ENTER_WIDGET) {
       printf("[ps=%p]got mouse enter for %p\n",
              this, aEvent->widget);
+    }
     printf("[ps=%p]setting mouse location to (%d,%d)\n",
            this, mMouseLocation.x, mMouseLocation.y);
 #endif
-    if (aEvent->message == NS_MOUSE_ENTER_WIDGET)
+    if (aEvent->mMessage == NS_MOUSE_ENTER_WIDGET) {
       SynthesizeMouseMove(false);
-  } else if (aEvent->message == NS_MOUSE_EXIT_WIDGET) {
+    }
+  } else if (aEvent->mMessage == NS_MOUSE_EXIT_WIDGET) {
     // Although we only care about the mouse moving into an area for which this
     // pres shell doesn't receive mouse move events, we don't check which widget
     // the mouse exit was for since this seems to vary by platform.  Hopefully
@@ -6662,7 +6664,7 @@ DispatchPointerFromMouseOrTouch(PresShell* aShell,
       return NS_OK;
     }
     int16_t button = mouseEvent->button;
-    switch (mouseEvent->message) {
+    switch (mouseEvent->mMessage) {
     case NS_MOUSE_MOVE:
       if (mouseEvent->buttons == 0) {
         button = -1;
@@ -6680,7 +6682,7 @@ DispatchPointerFromMouseOrTouch(PresShell* aShell,
     }
 
     WidgetPointerEvent event(*mouseEvent);
-    event.message = pointerMessage;
+    event.mMessage = pointerMessage;
     event.button = button;
     event.pressure = event.buttons ?
                      mouseEvent->pressure ? mouseEvent->pressure : 0.5f :
@@ -6691,7 +6693,7 @@ DispatchPointerFromMouseOrTouch(PresShell* aShell,
     WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
     // loop over all touches and dispatch pointer events on each touch
     // copy the event
-    switch (touchEvent->message) {
+    switch (touchEvent->mMessage) {
     case NS_TOUCH_MOVE:
       pointerMessage = NS_POINTER_MOVE;
       break;
@@ -6840,7 +6842,7 @@ PresShell::DispatchBeforeKeyboardEventInternal(const nsTArray
   }
 
   uint32_t message =
-    (aEvent.message == NS_KEY_DOWN) ? NS_KEY_BEFORE_DOWN : NS_KEY_BEFORE_UP;
+    (aEvent.mMessage == NS_KEY_DOWN) ? NS_KEY_BEFORE_DOWN : NS_KEY_BEFORE_UP;
   nsCOMPtr eventTarget;
   // Dispatch before events from the outermost element.
   for (int32_t i = length - 1; i >= 0; i--) {
@@ -6874,7 +6876,7 @@ PresShell::DispatchAfterKeyboardEventInternal(const nsTArray >
   }
 
   uint32_t message =
-    (aEvent.message == NS_KEY_DOWN) ? NS_KEY_AFTER_DOWN : NS_KEY_AFTER_UP;
+    (aEvent.mMessage == NS_KEY_DOWN) ? NS_KEY_AFTER_DOWN : NS_KEY_AFTER_UP;
   bool embeddedCancelled = aEmbeddedCancelled;
   nsCOMPtr eventTarget;
   // Dispatch after events from the innermost element.
@@ -6901,8 +6903,8 @@ PresShell::DispatchAfterKeyboardEvent(nsINode* aTarget,
   MOZ_ASSERT(aTarget);
   MOZ_ASSERT(BeforeAfterKeyboardEventEnabled());
 
-  if (NS_WARN_IF(aEvent.message != NS_KEY_DOWN &&
-                 aEvent.message != NS_KEY_UP)) {
+  if (NS_WARN_IF(aEvent.mMessage != NS_KEY_DOWN &&
+                 aEvent.mMessage != NS_KEY_UP)) {
     return;
   }
 
@@ -6931,7 +6933,7 @@ PresShell::HandleKeyboardEvent(nsINode* aTarget,
                                nsEventStatus* aStatus,
                                EventDispatchingCallback* aEventCB)
 {
-  if (aEvent.message == NS_KEY_PRESS ||
+  if (aEvent.mMessage == NS_KEY_PRESS ||
       !BeforeAfterKeyboardEventEnabled()) {
     EventDispatcher::Dispatch(aTarget, mPresContext,
                               &aEvent, nullptr, aStatus, aEventCB);
@@ -6939,7 +6941,7 @@ PresShell::HandleKeyboardEvent(nsINode* aTarget,
   }
 
   MOZ_ASSERT(aTarget);
-  MOZ_ASSERT(aEvent.message == NS_KEY_DOWN || aEvent.message == NS_KEY_UP);
+  MOZ_ASSERT(aEvent.mMessage == NS_KEY_DOWN || aEvent.mMessage == NS_KEY_UP);
 
   // Build up a target chain. Each item in the chain will receive a before event.
   nsAutoTArray, 5> chain;
@@ -7120,7 +7122,7 @@ PresShell::HandleEvent(nsIFrame* aFrame,
     }
 #ifdef DEBUG
     if (aEvent->IsIMERelatedEvent()) {
-      nsPrintfCString warning("%d event is discarded", aEvent->message);
+      nsPrintfCString warning("%d event is discarded", aEvent->mMessage);
       NS_WARNING(warning.get());
     }
 #endif
@@ -7175,7 +7177,7 @@ PresShell::HandleEvent(nsIFrame* aFrame,
       if (presShell != this) {
         nsIFrame* frame = presShell->GetRootFrame();
         if (!frame) {
-          if (aEvent->message == NS_QUERY_TEXT_CONTENT ||
+          if (aEvent->mMessage == NS_QUERY_TEXT_CONTENT ||
               aEvent->IsContentCommandEvent()) {
             return NS_OK;
           }
@@ -7194,7 +7196,7 @@ PresShell::HandleEvent(nsIFrame* aFrame,
 
   if (aEvent->mClass == eKeyboardEventClass &&
       mDocument && mDocument->EventHandlingSuppressed()) {
-    if (aEvent->message == NS_KEY_DOWN) {
+    if (aEvent->mMessage == NS_KEY_DOWN) {
       mNoDelayedKeyEvents = true;
     } else if (!mNoDelayedKeyEvents) {
       DelayedEvent* event = new DelayedKeyEvent(aEvent->AsKeyboardEvent());
@@ -7302,12 +7304,12 @@ PresShell::HandleEvent(nsIFrame* aFrame,
 
     // all touch events except for touchstart use a captured target
     if (aEvent->mClass == eTouchEventClass &&
-        aEvent->message != NS_TOUCH_START) {
+        aEvent->mMessage != NS_TOUCH_START) {
       captureRetarget = true;
     }
 
     WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
-    bool isWindowLevelMouseExit = (aEvent->message == NS_MOUSE_EXIT_WIDGET) &&
+    bool isWindowLevelMouseExit = (aEvent->mMessage == NS_MOUSE_EXIT_WIDGET) &&
       (mouseEvent && mouseEvent->exit == WidgetMouseEvent::eTopLevel);
 
     // Get the frame at the event point. However, don't do this if we're
@@ -7318,7 +7320,7 @@ PresShell::HandleEvent(nsIFrame* aFrame,
     if (!captureRetarget && !isWindowLevelMouseExit) {
       nsPoint eventPoint;
       uint32_t flags = 0;
-      if (aEvent->message == NS_TOUCH_START) {
+      if (aEvent->mMessage == NS_TOUCH_START) {
         flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
         WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
         // if this is a continuing session, ensure that all these events are
@@ -7446,7 +7448,7 @@ PresShell::HandleEvent(nsIFrame* aFrame,
     }
 
     if (aEvent->mClass == ePointerEventClass &&
-        aEvent->message != NS_POINTER_DOWN) {
+        aEvent->mMessage != NS_POINTER_DOWN) {
       if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
         uint32_t pointerId = pointerEvent->pointerId;
         nsIContent* pointerCapturingContent = GetPointerCapturingContent(pointerId);
@@ -7461,8 +7463,8 @@ PresShell::HandleEvent(nsIFrame* aFrame,
             frame = capturingFrame;
           }
 
-          if (pointerEvent->message == NS_POINTER_UP ||
-              pointerEvent->message == NS_POINTER_CANCEL) {
+          if (pointerEvent->mMessage == NS_POINTER_UP ||
+              pointerEvent->mMessage == NS_POINTER_CANCEL) {
             // Implicitly releasing capture for given pointer.
             // LOST_POINTER_CAPTURE should be send after NS_POINTER_UP or NS_POINTER_CANCEL.
             releasePointerCaptureCaller.SetTarget(pointerId, pointerCapturingContent);
@@ -7475,9 +7477,10 @@ PresShell::HandleEvent(nsIFrame* aFrame,
     // a document which needs events suppressed
     if (aEvent->mClass == eMouseEventClass &&
         frame->PresContext()->Document()->EventHandlingSuppressed()) {
-      if (aEvent->message == NS_MOUSE_BUTTON_DOWN) {
+      if (aEvent->mMessage == NS_MOUSE_BUTTON_DOWN) {
         mNoDelayedMouseEvents = true;
-      } else if (!mNoDelayedMouseEvents && aEvent->message == NS_MOUSE_BUTTON_UP) {
+      } else if (!mNoDelayedMouseEvents &&
+                 aEvent->mMessage == NS_MOUSE_BUTTON_UP) {
         DelayedEvent* event = new DelayedMouseEvent(aEvent->AsMouseEvent());
         if (!mDelayedEvents.AppendElement(event)) {
           delete event;
@@ -7493,7 +7496,7 @@ PresShell::HandleEvent(nsIFrame* aFrame,
 
     PresShell* shell =
         static_cast(frame->PresContext()->PresShell());
-    switch (aEvent->message) {
+    switch (aEvent->mMessage) {
       case NS_TOUCH_MOVE:
       case NS_TOUCH_CANCEL:
       case NS_TOUCH_END: {
@@ -7623,11 +7626,12 @@ PresShell::HandleEvent(nsIFrame* aFrame,
         }
       }
 
-      if (aEvent->message == NS_KEY_DOWN) {
+      if (aEvent->mMessage == NS_KEY_DOWN) {
         NS_IF_RELEASE(gKeyDownTarget);
         NS_IF_ADDREF(gKeyDownTarget = eventTarget);
       }
-      else if ((aEvent->message == NS_KEY_PRESS || aEvent->message == NS_KEY_UP) &&
+      else if ((aEvent->mMessage == NS_KEY_PRESS ||
+                aEvent->mMessage == NS_KEY_UP) &&
                gKeyDownTarget) {
         // If a different element is now focused for the keypress/keyup event
         // than what was focused during the keydown event, check if the new
@@ -7643,7 +7647,7 @@ PresShell::HandleEvent(nsIFrame* aFrame,
           }
         }
 
-        if (aEvent->message == NS_KEY_UP) {
+        if (aEvent->mMessage == NS_KEY_UP) {
           NS_RELEASE(gKeyDownTarget);
         }
       }
@@ -7831,7 +7835,7 @@ PresShell::HandleEventInternal(WidgetEvent* aEvent, nsEventStatus* aStatus)
 
     // XXX How about IME events and input events for plugins?
     if (aEvent->mFlags.mIsTrusted) {
-      switch (aEvent->message) {
+      switch (aEvent->mMessage) {
       case NS_KEY_PRESS:
       case NS_KEY_DOWN:
       case NS_KEY_UP: {
@@ -7851,7 +7855,7 @@ PresShell::HandleEventInternal(WidgetEvent* aEvent, nsEventStatus* aStatus)
             // The event listeners in chrome can prevent this ESC behavior by
             // calling prevent default on the preceding keydown/press events.
             if (!mIsLastChromeOnlyEscapeKeyConsumed &&
-                aEvent->message == NS_KEY_UP) {
+                aEvent->mMessage == NS_KEY_UP) {
               // ESC key released while in DOM fullscreen mode.
               // Fully exit all browser windows and documents from
               // fullscreen mode.
@@ -7863,7 +7867,7 @@ PresShell::HandleEventInternal(WidgetEvent* aEvent, nsEventStatus* aStatus)
           if (!mIsLastChromeOnlyEscapeKeyConsumed && pointerLockedDoc) {
             aEvent->mFlags.mDefaultPrevented = true;
             aEvent->mFlags.mOnlyChromeDispatch = true;
-            if (aEvent->message == NS_KEY_UP) {
+            if (aEvent->mMessage == NS_KEY_UP) {
               nsIDocument::UnlockPointer();
             }
           }
@@ -7902,7 +7906,7 @@ PresShell::HandleEventInternal(WidgetEvent* aEvent, nsEventStatus* aStatus)
       }
     }
 
-    if (aEvent->message == NS_CONTEXTMENU) {
+    if (aEvent->mMessage == NS_CONTEXTMENU) {
       WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
       if (mouseEvent->context == WidgetMouseEvent::eContextMenuKey &&
           !AdjustContextMenuKeyEvent(mouseEvent)) {
@@ -7917,7 +7921,7 @@ PresShell::HandleEventInternal(WidgetEvent* aEvent, nsEventStatus* aStatus)
     AutoHandlingUserInputStatePusher userInpStatePusher(isHandlingUserInput,
                                                         aEvent, mDocument);
 
-    if (aEvent->mFlags.mIsTrusted && aEvent->message == NS_MOUSE_MOVE) {
+    if (aEvent->mFlags.mIsTrusted && aEvent->mMessage == NS_MOUSE_MOVE) {
       nsIPresShell::AllowMouseCapture(
         EventStateManager::GetActiveEventStateManager() == manager);
     }
@@ -7962,12 +7966,12 @@ PresShell::HandleEventInternal(WidgetEvent* aEvent, nsEventStatus* aStatus)
       }
     }
 
-    switch (aEvent->message) {
+    switch (aEvent->mMessage) {
     case NS_KEY_PRESS:
     case NS_KEY_DOWN:
     case NS_KEY_UP: {
       if (aEvent->AsKeyboardEvent()->keyCode == NS_VK_ESCAPE) {
-        if (aEvent->message == NS_KEY_UP) {
+        if (aEvent->mMessage == NS_KEY_UP) {
           // Reset this flag after key up is handled.
           mIsLastChromeOnlyEscapeKeyConsumed = false;
         } else {
@@ -8069,9 +8073,9 @@ PresShell::DispatchTouchEventToDOM(WidgetEvent* aEvent,
   // calling preventDefault on touchstart or the first touchmove for a
   // point prevents mouse events. calling it on the touchend should
   // prevent click dispatching.
-  bool canPrevent = (aEvent->message == NS_TOUCH_START) ||
-              (aEvent->message == NS_TOUCH_MOVE && aTouchIsNew) ||
-              (aEvent->message == NS_TOUCH_END);
+  bool canPrevent = (aEvent->mMessage == NS_TOUCH_START) ||
+                    (aEvent->mMessage == NS_TOUCH_MOVE && aTouchIsNew) ||
+                    (aEvent->mMessage == NS_TOUCH_END);
   bool preventDefault = false;
   nsEventStatus tmpStatus = nsEventStatus_eIgnore;
   WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
@@ -8099,7 +8103,7 @@ PresShell::DispatchTouchEventToDOM(WidgetEvent* aEvent,
     }
     // copy the event
     WidgetTouchEvent newEvent(touchEvent->mFlags.mIsTrusted,
-                              touchEvent->message, touchEvent->widget);
+                              touchEvent->mMessage, touchEvent->widget);
     newEvent.AssignTouchEventData(*touchEvent, false);
     newEvent.target = targetPtr;
 
@@ -9536,7 +9540,7 @@ PresShell::DelayedMouseEvent::DelayedMouseEvent(WidgetMouseEvent* aEvent) :
 {
   WidgetMouseEvent* mouseEvent =
     new WidgetMouseEvent(aEvent->mFlags.mIsTrusted,
-                         aEvent->message,
+                         aEvent->mMessage,
                          aEvent->widget,
                          aEvent->reason,
                          aEvent->context);
@@ -9549,7 +9553,7 @@ PresShell::DelayedKeyEvent::DelayedKeyEvent(WidgetKeyboardEvent* aEvent) :
 {
   WidgetKeyboardEvent* keyEvent =
     new WidgetKeyboardEvent(aEvent->mFlags.mIsTrusted,
-                            aEvent->message,
+                            aEvent->mMessage,
                             aEvent->widget);
   keyEvent->AssignKeyEventData(*aEvent, false);
   keyEvent->mFlags.mIsSynthesizedForTests = aEvent->mFlags.mIsSynthesizedForTests;
diff --git a/layout/base/tests/chrome/test_bug370436.html b/layout/base/tests/chrome/test_bug370436.html
index 49e0934b26d0..9ad8511aba29 100644
--- a/layout/base/tests/chrome/test_bug370436.html
+++ b/layout/base/tests/chrome/test_bug370436.html
@@ -42,27 +42,32 @@
 	ta.focus();
 	ta.selectionStart = ta.selectionEnd = ta.value.length;
 
+// Note: This test, intentionally or by accident, relies on sending button '0'
+// with contextMenu, which triggers some key-equiv stuff in
+// PresShell::AdjustContextMenuKeyEvent.
+var mouseParams = { type: 'contextmenu', button: 0 };
+
 	/* Put cursor at start and middle of "sheep" */
 	synthesizeKey("VK_UP", {})
-	synthesizeMouse(ta, 0, 0, { type : "contextmenu" });
+	synthesizeMouse(ta, 0, 0, mouseParams);
 	synthesizeKey("VK_RIGHT", {})
-	synthesizeMouse(ta, 0, 0, { type : "contextmenu" });
+	synthesizeMouse(ta, 0, 0, mouseParams);
 	synthesizeKey("VK_RIGHT", {})
-	synthesizeMouse(ta, 0, 0, { type : "contextmenu" });
+	synthesizeMouse(ta, 0, 0, mouseParams);
 
 	/* Put cursor at the end of "hello" */
 	synthesizeKey("VK_UP", {})
-	synthesizeMouse(ta, 0, 0, { type : "contextmenu" });
+	synthesizeMouse(ta, 0, 0, mouseParams);
 	synthesizeKey("VK_RIGHT", {})
 	synthesizeKey("VK_RIGHT", {})
 	synthesizeKey("VK_RIGHT", {})
-	synthesizeMouse(ta, 0, 0, { type : "contextmenu" });
+	synthesizeMouse(ta, 0, 0, mouseParams);
 	synthesizeKey("VK_RIGHT", {})
-	synthesizeMouse(ta, 0, 0, { type : "contextmenu" });
+	synthesizeMouse(ta, 0, 0, mouseParams);
 
 	/* Put cursor on "welcome" */
 	synthesizeKey("VK_UP", {})
-	synthesizeMouse(ta, 0, 0, { type : "contextmenu" });
+	synthesizeMouse(ta, 0, 0, mouseParams);
 
 	is(words.pop(), "welcome", "Word 1 selected correctly");	
 	is(words.pop(), "world"  , "Word 2 selected correctly");	
diff --git a/layout/forms/nsComboboxControlFrame.cpp b/layout/forms/nsComboboxControlFrame.cpp
index df96fa565285..59b8705b27a7 100644
--- a/layout/forms/nsComboboxControlFrame.cpp
+++ b/layout/forms/nsComboboxControlFrame.cpp
@@ -1133,7 +1133,7 @@ nsComboboxControlFrame::HandleEvent(nsPresContext* aPresContext,
   }
 
 #if COMBOBOX_ROLLUP_CONSUME_EVENT == 0
-  if (aEvent->message == NS_MOUSE_BUTTON_DOWN) {
+  if (aEvent->mMessage == NS_MOUSE_BUTTON_DOWN) {
     nsIWidget* widget = GetNearestWidget();
     if (widget && GetContent() == widget->GetLastRollup()) {
       // This event did a Rollup on this control - prevent it from opening
diff --git a/layout/forms/nsImageControlFrame.cpp b/layout/forms/nsImageControlFrame.cpp
index bfecbbcebd8c..13bdf23a4eb6 100644
--- a/layout/forms/nsImageControlFrame.cpp
+++ b/layout/forms/nsImageControlFrame.cpp
@@ -162,7 +162,7 @@ nsImageControlFrame::HandleEvent(nsPresContext* aPresContext,
 
   *aEventStatus = nsEventStatus_eIgnore;
 
-  if (aEvent->message == NS_MOUSE_BUTTON_UP &&
+  if (aEvent->mMessage == NS_MOUSE_BUTTON_UP &&
       aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
     // Store click point for HTMLInputElement::SubmitNamesValues
     // Do this on MouseUp because the specs don't say and that's what IE does
diff --git a/layout/forms/nsListControlFrame.cpp b/layout/forms/nsListControlFrame.cpp
index af16d76c5003..dbc4a631a339 100644
--- a/layout/forms/nsListControlFrame.cpp
+++ b/layout/forms/nsListControlFrame.cpp
@@ -898,11 +898,11 @@ nsListControlFrame::HandleEvent(nsPresContext* aPresContext,
                           "NS_MOUSE_LEFT_CLICK",
                           "NS_MOUSE_MIDDLE_CLICK",
                           "NS_MOUSE_RIGHT_CLICK"};
-  int inx = aEvent->message-NS_MOUSE_MESSAGE_START;
+  int inx = aEvent->mMessage-NS_MOUSE_MESSAGE_START;
   if (inx >= 0 && inx <= (NS_MOUSE_RIGHT_CLICK-NS_MOUSE_MESSAGE_START)) {
-    printf("Mouse in ListFrame %s [%d]\n", desc[inx], aEvent->message);
+    printf("Mouse in ListFrame %s [%d]\n", desc[inx], aEvent->mMessage);
   } else {
-    printf("Mouse in ListFrame  [%d]\n", aEvent->message);
+    printf("Mouse in ListFrame  [%d]\n", aEvent->mMessage);
   }*/
 
   if (nsEventStatus_eConsumeNoDefault == *aEventStatus)
@@ -2461,11 +2461,8 @@ nsListEventListener::HandleEvent(nsIDOMEvent* aEvent)
     return mFrame->nsListControlFrame::MouseDown(aEvent);
   }
   if (eventType.EqualsLiteral("mouseup")) {
-    bool defaultPrevented = false;
-    aEvent->GetDefaultPrevented(&defaultPrevented);
-    if (defaultPrevented) {
-      return NS_OK;
-    }
+    // Don't try to honor defaultPrevented here - it's not web compatible.
+    // (bug 1194733)
     return mFrame->nsListControlFrame::MouseUp(aEvent);
   }
   if (eventType.EqualsLiteral("mousemove")) {
diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp
index a9a8d901c7bb..2a5d90a252de 100644
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2558,7 +2558,7 @@ nsFrame::HandleEvent(nsPresContext* aPresContext,
                      nsEventStatus* aEventStatus)
 {
 
-  if (aEvent->message == NS_MOUSE_MOVE) {
+  if (aEvent->mMessage == NS_MOUSE_MOVE) {
     // XXX If the second argument of HandleDrag() is WidgetMouseEvent,
     //     the implementation becomes simpler.
     return HandleDrag(aPresContext, aEvent, aEventStatus);
@@ -2567,9 +2567,11 @@ nsFrame::HandleEvent(nsPresContext* aPresContext,
   if ((aEvent->mClass == eMouseEventClass &&
        aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) ||
       aEvent->mClass == eTouchEventClass) {
-    if (aEvent->message == NS_MOUSE_BUTTON_DOWN || aEvent->message == NS_TOUCH_START) {
+    if (aEvent->mMessage == NS_MOUSE_BUTTON_DOWN ||
+        aEvent->mMessage == NS_TOUCH_START) {
       HandlePress(aPresContext, aEvent, aEventStatus);
-    } else if (aEvent->message == NS_MOUSE_BUTTON_UP || aEvent->message == NS_TOUCH_END) {
+    } else if (aEvent->mMessage == NS_MOUSE_BUTTON_UP ||
+               aEvent->mMessage == NS_TOUCH_END) {
       HandleRelease(aPresContext, aEvent, aEventStatus);
     }
   }
@@ -2602,8 +2604,8 @@ nsFrame::GetDataForTableSelection(const nsFrameSelection* aFrameSelection,
   //  (Mouse down does normal selection unless Ctrl/Cmd is pressed)
   bool doTableSelection =
      displaySelection == nsISelectionDisplay::DISPLAY_ALL && selectingTableCells &&
-     (aMouseEvent->message == NS_MOUSE_MOVE ||
-      (aMouseEvent->message == NS_MOUSE_BUTTON_UP &&
+     (aMouseEvent->mMessage == NS_MOUSE_MOVE ||
+      (aMouseEvent->mMessage == NS_MOUSE_BUTTON_UP &&
        aMouseEvent->button == WidgetMouseEvent::eLeftButton) ||
       aMouseEvent->IsShift());
 
diff --git a/layout/generic/nsFrameSetFrame.cpp b/layout/generic/nsFrameSetFrame.cpp
index bd4c1839135d..b064376048a8 100644
--- a/layout/generic/nsFrameSetFrame.cpp
+++ b/layout/generic/nsFrameSetFrame.cpp
@@ -651,7 +651,7 @@ nsresult nsHTMLFramesetFrame::HandleEvent(nsPresContext* aPresContext,
   NS_ENSURE_ARG_POINTER(aEventStatus);
   if (mDragger) {
     // the nsFramesetBorderFrame has captured NS_MOUSE_DOWN
-    switch (aEvent->message) {
+    switch (aEvent->mMessage) {
       case NS_MOUSE_MOVE:
         MouseDrag(aPresContext, aEvent);
 	      break;
@@ -1537,7 +1537,7 @@ nsHTMLFramesetBorderFrame::HandleEvent(nsPresContext* aPresContext,
     return NS_OK;
   }
 
-  if (aEvent->message == NS_MOUSE_BUTTON_DOWN &&
+  if (aEvent->mMessage == NS_MOUSE_BUTTON_DOWN &&
       aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
     nsHTMLFramesetFrame* parentFrame = do_QueryFrame(GetParent());
     if (parentFrame) {
diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h
index 8f34fab12bcb..65d84ea1594f 100644
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -34,6 +34,7 @@
 #include "nsITheme.h"
 #include "nsLayoutUtils.h"
 #include "nsQueryFrame.h"
+#include "nsStringGlue.h"
 #include "nsStyleContext.h"
 #include "nsStyleStruct.h"
 
@@ -3225,6 +3226,11 @@ NS_PTR_TO_INT32(frame->Properties().Get(nsIFrame::ParagraphDepthProperty()))
     fputs(t.get(), out);
   }
   void ListTag(nsACString& aTo) const;
+  nsAutoCString ListTag() const {
+    nsAutoCString tag;
+    ListTag(tag);
+    return tag;
+  }
   static void ListTag(nsACString& aTo, const nsIFrame* aFrame);
   void ListGeneric(nsACString& aTo, const char* aPrefix = "", uint32_t aFlags = 0) const;
   enum {
diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp
index a720d422888d..bc32f79154ad 100644
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -2010,9 +2010,9 @@ nsImageFrame::HandleEvent(nsPresContext* aPresContext,
 {
   NS_ENSURE_ARG_POINTER(aEventStatus);
 
-  if ((aEvent->message == NS_MOUSE_BUTTON_UP && 
+  if ((aEvent->mMessage == NS_MOUSE_BUTTON_UP && 
        aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) ||
-      aEvent->message == NS_MOUSE_MOVE) {
+      aEvent->mMessage == NS_MOUSE_MOVE) {
     nsImageMap* map = GetImageMap();
     bool isServerMap = IsServerImageMap();
     if ((nullptr != map) || isServerMap) {
@@ -2050,7 +2050,7 @@ nsImageFrame::HandleEvent(nsPresContext* aPresContext,
           uri->SetSpec(spec);                
           
           bool clicked = false;
-          if (aEvent->message == NS_MOUSE_BUTTON_UP) {
+          if (aEvent->mMessage == NS_MOUSE_BUTTON_UP) {
             *aEventStatus = nsEventStatus_eConsumeDoDefault; 
             clicked = true;
           }
diff --git a/layout/generic/nsPluginFrame.cpp b/layout/generic/nsPluginFrame.cpp
index 490bf22d0539..ff9702cc813c 100644
--- a/layout/generic/nsPluginFrame.cpp
+++ b/layout/generic/nsPluginFrame.cpp
@@ -1761,13 +1761,13 @@ nsPluginFrame::HandleEvent(nsPresContext* aPresContext,
 
   mInstanceOwner->ConsiderNewEventloopNestingLevel();
 
-  if (anEvent->message == NS_PLUGIN_ACTIVATE) {
+  if (anEvent->mMessage == NS_PLUGIN_ACTIVATE) {
     nsIFocusManager* fm = nsFocusManager::GetFocusManager();
     nsCOMPtr elem = do_QueryInterface(GetContent());
     if (fm && elem)
       return fm->SetFocus(elem, 0);
   }
-  else if (anEvent->message == NS_PLUGIN_FOCUS) {
+  else if (anEvent->mMessage == NS_PLUGIN_FOCUS) {
     nsIFocusManager* fm = nsFocusManager::GetFocusManager();
     if (fm)
       return fm->FocusPlugin(GetContent());
@@ -1788,8 +1788,8 @@ nsPluginFrame::HandleEvent(nsPresContext* aPresContext,
 
 #ifdef XP_MACOSX
   // we want to process some native mouse events in the cocoa event model
-  if ((anEvent->message == NS_MOUSE_ENTER_WIDGET ||
-       anEvent->message == NS_WHEEL_WHEEL) &&
+  if ((anEvent->mMessage == NS_MOUSE_ENTER_WIDGET ||
+       anEvent->mMessage == NS_WHEEL_WHEEL) &&
       mInstanceOwner->GetEventModel() == NPEventModelCocoa) {
     *anEventStatus = mInstanceOwner->ProcessEvent(*anEvent);
     // Due to plugin code reentering Gecko, this frame may be dead at this
@@ -1801,7 +1801,7 @@ nsPluginFrame::HandleEvent(nsPresContext* aPresContext,
   // and mouse-up) are needed to make the routing of mouse events while
   // dragging conform to standard OS X practice, and to the Cocoa NPAPI spec.
   // See bug 525078 and bug 909678.
-  if (anEvent->message == NS_MOUSE_BUTTON_DOWN) {
+  if (anEvent->mMessage == NS_MOUSE_BUTTON_DOWN) {
     nsIPresShell::SetCapturingContent(GetContent(), CAPTURE_IGNOREALLOWED);
   }
 #endif
@@ -1812,7 +1812,7 @@ nsPluginFrame::HandleEvent(nsPresContext* aPresContext,
   // nsPluginFrameSuper::HandleEvent() might have killed us.
 
 #ifdef XP_MACOSX
-  if (anEvent->message == NS_MOUSE_BUTTON_UP) {
+  if (anEvent->mMessage == NS_MOUSE_BUTTON_UP) {
     nsIPresShell::SetCapturingContent(nullptr, 0);
   }
 #endif
diff --git a/layout/printing/nsPrintPreviewListener.cpp b/layout/printing/nsPrintPreviewListener.cpp
index 5a4e7dae45e5..dca52906c906 100644
--- a/layout/printing/nsPrintPreviewListener.cpp
+++ b/layout/printing/nsPrintPreviewListener.cpp
@@ -118,7 +118,7 @@ GetActionForEvent(nsIDOMEvent* aEvent)
   }
 
   if (keyEvent->mFlags.mInSystemGroup) {
-    NS_ASSERTION(keyEvent->message == NS_KEY_DOWN,
+    NS_ASSERTION(keyEvent->mMessage == NS_KEY_DOWN,
       "Assuming we're listening only keydown event in system group");
     return eEventAction_StopPropagation;
   }
@@ -126,8 +126,8 @@ GetActionForEvent(nsIDOMEvent* aEvent)
   if (keyEvent->IsAlt() || keyEvent->IsControl() || keyEvent->IsMeta()) {
     // Don't consume keydown event because following keypress event may be
     // handled as access key or shortcut key.
-    return (keyEvent->message == NS_KEY_DOWN) ? eEventAction_StopPropagation :
-                                                eEventAction_Suppress;
+    return (keyEvent->mMessage == NS_KEY_DOWN) ? eEventAction_StopPropagation :
+                                                 eEventAction_Suppress;
   }
 
   static const uint32_t kOKKeyCodes[] = {
diff --git a/layout/reftests/font-features/fwid-spaces-ref.html b/layout/reftests/font-features/fwid-spaces-ref.html
index eab0f670a646..5183bf1d4209 100644
--- a/layout/reftests/font-features/fwid-spaces-ref.html
+++ b/layout/reftests/font-features/fwid-spaces-ref.html
@@ -11,7 +11,7 @@
 }
 
 #test, #test pre {
-  font: 150% Hiragino Kaku Gothic ProN, Meiryo, sans-serif;
+  font: 150% Hiragino Kaku Gothic ProN, Meiryo, Yu Gothic, sans-serif;
 }
 
 #test pre span {
diff --git a/layout/reftests/font-features/fwid-spaces.html b/layout/reftests/font-features/fwid-spaces.html
index 5715886d2585..7c8c55fe0300 100644
--- a/layout/reftests/font-features/fwid-spaces.html
+++ b/layout/reftests/font-features/fwid-spaces.html
@@ -11,7 +11,7 @@
 }
 
 #test, #test pre {
-  font: 150% Hiragino Kaku Gothic ProN, Meiryo, sans-serif;
+  font: 150% Hiragino Kaku Gothic ProN, Meiryo, Yu Gothic, sans-serif;
   -webkit-font-feature-settings: "fwid" on;
   font-feature-settings: "fwid" on;
 }
diff --git a/layout/reftests/writing-mode/1196887-1-computed-display-inline-block-ref.html b/layout/reftests/writing-mode/1196887-1-computed-display-inline-block-ref.html
new file mode 100644
index 000000000000..502e5a964550
--- /dev/null
+++ b/layout/reftests/writing-mode/1196887-1-computed-display-inline-block-ref.html
@@ -0,0 +1,3 @@
+
+

There should be no red:

+
diff --git a/layout/reftests/writing-mode/1196887-1-computed-display-inline-block.html b/layout/reftests/writing-mode/1196887-1-computed-display-inline-block.html new file mode 100644 index 000000000000..676cd3d22135 --- /dev/null +++ b/layout/reftests/writing-mode/1196887-1-computed-display-inline-block.html @@ -0,0 +1,5 @@ + +

There should be no red:

+
+
+ diff --git a/layout/reftests/writing-mode/reftest.list b/layout/reftests/writing-mode/reftest.list index 2e49fbc02523..071e59b5f1c5 100644 --- a/layout/reftests/writing-mode/reftest.list +++ b/layout/reftests/writing-mode/reftest.list @@ -150,6 +150,7 @@ test-pref(dom.meta-viewport.enabled,true) test-pref(font.size.inflation.emPerLin == 1172774-percent-padding-4.html 1172774-percent-vertical-ref.html == 1174450-intrinsic-sizing.html 1174450-intrinsic-sizing-ref.html == 1175789-underline-overline-1.html 1175789-underline-overline-1-ref.html +== 1196887-1-computed-display-inline-block.html 1196887-1-computed-display-inline-block-ref.html # Suite of tests from Gérard Talbot in bug 1079151 include abspos/reftest.list diff --git a/layout/style/nsAnimationManager.h b/layout/style/nsAnimationManager.h index 1dcd35eb5496..12db1d7ea442 100644 --- a/layout/style/nsAnimationManager.h +++ b/layout/style/nsAnimationManager.h @@ -44,7 +44,8 @@ struct AnimationEventInfo { // InternalAnimationEvent doesn't support copy-construction, so we need // to ourselves in order to work with nsTArray AnimationEventInfo(const AnimationEventInfo &aOther) - : mElement(aOther.mElement), mEvent(true, aOther.mEvent.message) + : mElement(aOther.mElement) + , mEvent(true, aOther.mEvent.mMessage) { mEvent.AssignAnimationEventData(aOther.mEvent, false); } diff --git a/layout/style/nsCSSKeywordList.h b/layout/style/nsCSSKeywordList.h index 44509b40bb4f..e66d3abc4014 100644 --- a/layout/style/nsCSSKeywordList.h +++ b/layout/style/nsCSSKeywordList.h @@ -71,6 +71,8 @@ CSS_KEY(-moz-grid-group, _moz_grid_group) CSS_KEY(-moz-grid-line, _moz_grid_line) CSS_KEY(-moz-grid, _moz_grid) CSS_KEY(-moz-groupbox, _moz_groupbox) +CSS_KEY(-moz-gtk-info-bar, _moz_gtk_info_bar) +CSS_KEY(-moz-gtk-info-bar-text, _moz_gtk_info_bar_text) CSS_KEY(-moz-hidden-unscrollable, _moz_hidden_unscrollable) CSS_KEY(-moz-hyperlinktext, _moz_hyperlinktext) CSS_KEY(-moz-html-cellhighlight, _moz_html_cellhighlight) diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index 96d6ad2d9b8d..bf0008250bef 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -745,6 +745,7 @@ const KTableValue nsCSSProps::kAppearanceKTable[] = { eCSSKeyword__moz_mac_vibrancy_dark, NS_THEME_MAC_VIBRANCY_DARK, eCSSKeyword__moz_mac_disclosure_button_open, NS_THEME_MAC_DISCLOSURE_BUTTON_OPEN, eCSSKeyword__moz_mac_disclosure_button_closed, NS_THEME_MAC_DISCLOSURE_BUTTON_CLOSED, + eCSSKeyword__moz_gtk_info_bar, NS_THEME_GTK_INFO_BAR, eCSSKeyword_UNKNOWN,-1 }; @@ -960,6 +961,7 @@ const KTableValue nsCSSProps::kColorKTable[] = { eCSSKeyword__moz_dialog, LookAndFeel::eColorID__moz_dialog, eCSSKeyword__moz_dialogtext, LookAndFeel::eColorID__moz_dialogtext, eCSSKeyword__moz_dragtargetzone, LookAndFeel::eColorID__moz_dragtargetzone, + eCSSKeyword__moz_gtk_info_bar_text, LookAndFeel::eColorID__moz_gtk_info_bar_text, eCSSKeyword__moz_hyperlinktext, NS_COLOR_MOZ_HYPERLINKTEXT, eCSSKeyword__moz_html_cellhighlight, LookAndFeel::eColorID__moz_html_cellhighlight, eCSSKeyword__moz_html_cellhighlighttext, LookAndFeel::eColorID__moz_html_cellhighlighttext, diff --git a/layout/style/nsStyleContext.cpp b/layout/style/nsStyleContext.cpp index d8969cffcf9e..cbbe83b95317 100644 --- a/layout/style/nsStyleContext.cpp +++ b/layout/style/nsStyleContext.cpp @@ -759,16 +759,19 @@ nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup) } } - // Elements with display:inline whose writing-mode is orthogonal to their - // parent's mode will be converted to display:inline-block. + /* + * According to https://drafts.csswg.org/css-writing-modes-3/#block-flow: + * + * If a box has a different block flow direction than its containing block: + * * If the box has a specified display of inline, its display computes + * to inline-block. [CSS21] + * ...etc. + */ if (disp->mDisplay == NS_STYLE_DISPLAY_INLINE && mParent) { - // We don't need the full mozilla::WritingMode value (incorporating dir and - // text-orientation) here, all we care about is vertical vs horizontal. - bool thisHorizontal = - StyleVisibility()->mWritingMode == NS_STYLE_WRITING_MODE_HORIZONTAL_TB; - bool parentHorizontal = mParent->StyleVisibility()->mWritingMode == - NS_STYLE_WRITING_MODE_HORIZONTAL_TB; - if (thisHorizontal != parentHorizontal) { + // We don't need the full mozilla::WritingMode value (incorporating dir + // and text-orientation) here; just the writing-mode property is enough. + if (StyleVisibility()->mWritingMode != + mParent->StyleVisibility()->mWritingMode) { nsStyleDisplay *mutable_display = static_cast(GetUniqueStyleData(eStyleStruct_Display)); mutable_display->mOriginalDisplay = mutable_display->mDisplay = diff --git a/layout/xul/nsButtonBoxFrame.cpp b/layout/xul/nsButtonBoxFrame.cpp index 3259e597f57e..bda9e52d7d47 100644 --- a/layout/xul/nsButtonBoxFrame.cpp +++ b/layout/xul/nsButtonBoxFrame.cpp @@ -110,7 +110,7 @@ nsButtonBoxFrame::HandleEvent(nsPresContext* aPresContext, return NS_OK; } - switch (aEvent->message) { + switch (aEvent->mMessage) { case NS_KEY_DOWN: { WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent(); if (!keyEvent) { diff --git a/layout/xul/nsMenuFrame.cpp b/layout/xul/nsMenuFrame.cpp index d498c66324df..22a363bcb981 100644 --- a/layout/xul/nsMenuFrame.cpp +++ b/layout/xul/nsMenuFrame.cpp @@ -394,7 +394,7 @@ nsMenuFrame::HandleEvent(nsPresContext* aPresContext, bool onmenu = IsOnMenu(); - if (aEvent->message == NS_KEY_PRESS && !IsDisabled()) { + if (aEvent->mMessage == NS_KEY_PRESS && !IsDisabled()) { WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent(); uint32_t keyCode = keyEvent->keyCode; #ifdef XP_MACOSX @@ -413,7 +413,7 @@ nsMenuFrame::HandleEvent(nsPresContext* aPresContext, } #endif } - else if (aEvent->message == NS_MOUSE_BUTTON_DOWN && + else if (aEvent->mMessage == NS_MOUSE_BUTTON_DOWN && aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton && !IsDisabled() && IsMenu()) { // The menu item was selected. Bring up the menu. @@ -431,10 +431,10 @@ nsMenuFrame::HandleEvent(nsPresContext* aPresContext, } else if ( #ifndef NSCONTEXTMENUISMOUSEUP - (aEvent->message == NS_MOUSE_BUTTON_UP && + (aEvent->mMessage == NS_MOUSE_BUTTON_UP && aEvent->AsMouseEvent()->button == WidgetMouseEvent::eRightButton) && #else - aEvent->message == NS_CONTEXTMENU && + aEvent->mMessage == NS_CONTEXTMENU && #endif onmenu && !IsMenu() && !IsDisabled()) { // if this menu is a context menu it accepts right-clicks...fire away! @@ -452,14 +452,14 @@ nsMenuFrame::HandleEvent(nsPresContext* aPresContext, Execute(aEvent); } } - else if (aEvent->message == NS_MOUSE_BUTTON_UP && + else if (aEvent->mMessage == NS_MOUSE_BUTTON_UP && aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton && !IsMenu() && !IsDisabled()) { // Execute the execute event handler. *aEventStatus = nsEventStatus_eConsumeNoDefault; Execute(aEvent); } - else if (aEvent->message == NS_MOUSE_OUT) { + else if (aEvent->mMessage == NS_MOUSE_OUT) { // Kill our timer if one is active. if (mOpenTimer) { mOpenTimer->Cancel(); @@ -479,7 +479,7 @@ nsMenuFrame::HandleEvent(nsPresContext* aPresContext, } } } - else if (aEvent->message == NS_MOUSE_MOVE && + else if (aEvent->mMessage == NS_MOUSE_MOVE && (onmenu || (menuParent && menuParent->IsMenuBar()))) { if (gEatMouseMove) { gEatMouseMove = false; diff --git a/layout/xul/nsResizerFrame.cpp b/layout/xul/nsResizerFrame.cpp index ea353a7be0f0..6e8f23c8fb65 100644 --- a/layout/xul/nsResizerFrame.cpp +++ b/layout/xul/nsResizerFrame.cpp @@ -61,7 +61,7 @@ nsResizerFrame::HandleEvent(nsPresContext* aPresContext, nsWeakFrame weakFrame(this); bool doDefault = true; - switch (aEvent->message) { + switch (aEvent->mMessage) { case NS_TOUCH_START: case NS_MOUSE_BUTTON_DOWN: { if (aEvent->mClass == eTouchEventClass || diff --git a/layout/xul/nsRootBoxFrame.cpp b/layout/xul/nsRootBoxFrame.cpp index 32ef238a7972..945c03f0bb4e 100644 --- a/layout/xul/nsRootBoxFrame.cpp +++ b/layout/xul/nsRootBoxFrame.cpp @@ -205,7 +205,7 @@ nsRootBoxFrame::HandleEvent(nsPresContext* aPresContext, return NS_OK; } - if (aEvent->message == NS_MOUSE_BUTTON_UP) { + if (aEvent->mMessage == NS_MOUSE_BUTTON_UP) { nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus); } diff --git a/layout/xul/nsScrollBoxFrame.cpp b/layout/xul/nsScrollBoxFrame.cpp index 8a9779a064b7..87f63a277def 100644 --- a/layout/xul/nsScrollBoxFrame.cpp +++ b/layout/xul/nsScrollBoxFrame.cpp @@ -83,8 +83,7 @@ nsAutoRepeatBoxFrame::HandleEvent(nsPresContext* aPresContext, return NS_OK; } - switch(aEvent->message) - { + switch(aEvent->mMessage) { // repeat mode may be "hover" for repeating while the mouse is hovering // over the element, otherwise repetition is done while the element is // active (pressed). diff --git a/layout/xul/nsScrollbarButtonFrame.cpp b/layout/xul/nsScrollbarButtonFrame.cpp index e135eccd01bb..1a40d84bb567 100644 --- a/layout/xul/nsScrollbarButtonFrame.cpp +++ b/layout/xul/nsScrollbarButtonFrame.cpp @@ -52,7 +52,7 @@ nsScrollbarButtonFrame::HandleEvent(nsPresContext* aPresContext, return NS_OK; } - switch (aEvent->message) { + switch (aEvent->mMessage) { case NS_MOUSE_BUTTON_DOWN: mCursorOnThis = true; // if we didn't handle the press ourselves, pass it on to the superclass diff --git a/layout/xul/nsSliderFrame.cpp b/layout/xul/nsSliderFrame.cpp index d774b693140b..c24a418b6268 100644 --- a/layout/xul/nsSliderFrame.cpp +++ b/layout/xul/nsSliderFrame.cpp @@ -477,7 +477,7 @@ nsSliderFrame::HandleEvent(nsPresContext* aPresContext, if (isDraggingThumb()) { - switch (aEvent->message) { + switch (aEvent->mMessage) { case NS_TOUCH_MOVE: case NS_MOUSE_MOVE: { nsPoint eventPoint; @@ -596,9 +596,9 @@ nsSliderFrame::HandleEvent(nsPresContext* aPresContext, aEvent->AsMouseEvent()->button == WidgetMouseEvent::eRightButton) { // HandlePress and HandleRelease are usually called via // nsFrame::HandleEvent, but only for the left mouse button. - if (aEvent->message == NS_MOUSE_BUTTON_DOWN) { + if (aEvent->mMessage == NS_MOUSE_BUTTON_DOWN) { HandlePress(aPresContext, aEvent, aEventStatus); - } else if (aEvent->message == NS_MOUSE_BUTTON_UP) { + } else if (aEvent->mMessage == NS_MOUSE_BUTTON_UP) { HandleRelease(aPresContext, aEvent, aEventStatus); } @@ -607,10 +607,13 @@ nsSliderFrame::HandleEvent(nsPresContext* aPresContext, #endif // XXX hack until handle release is actually called in nsframe. -// if (aEvent->message == NS_MOUSE_OUT || aEvent->message == NS_MOUSE_RIGHT_BUTTON_UP || aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) - // HandleRelease(aPresContext, aEvent, aEventStatus); + // if (aEvent->mMessage == NS_MOUSE_OUT || + // aEvent->mMessage == NS_MOUSE_RIGHT_BUTTON_UP || + // aEvent->mMessage == NS_MOUSE_LEFT_BUTTON_UP) { + // HandleRelease(aPresContext, aEvent, aEventStatus); + // } - if (aEvent->message == NS_MOUSE_OUT && mChange) + if (aEvent->mMessage == NS_MOUSE_OUT && mChange) HandleRelease(aPresContext, aEvent, aEventStatus); return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus); @@ -1042,7 +1045,7 @@ nsSliderFrame::RemoveListener() bool nsSliderFrame::ShouldScrollForEvent(WidgetGUIEvent* aEvent) { - switch (aEvent->message) { + switch (aEvent->mMessage) { case NS_TOUCH_START: case NS_TOUCH_END: return true; @@ -1070,11 +1073,11 @@ nsSliderFrame::ShouldScrollToClickForEvent(WidgetGUIEvent* aEvent) return false; } - if (aEvent->message == NS_TOUCH_START) { + if (aEvent->mMessage == NS_TOUCH_START) { return GetScrollToClick(); } - if (aEvent->message != NS_MOUSE_BUTTON_DOWN) { + if (aEvent->mMessage != NS_MOUSE_BUTTON_DOWN) { return false; } diff --git a/layout/xul/nsSplitterFrame.cpp b/layout/xul/nsSplitterFrame.cpp index 20b44c0a49b9..c0b10cc9c429 100644 --- a/layout/xul/nsSplitterFrame.cpp +++ b/layout/xul/nsSplitterFrame.cpp @@ -388,7 +388,7 @@ nsSplitterFrame::HandleEvent(nsPresContext* aPresContext, nsWeakFrame weakFrame(this); nsRefPtr kungFuDeathGrip(mInner); - switch (aEvent->message) { + switch (aEvent->mMessage) { case NS_MOUSE_MOVE: mInner->MouseDrag(aPresContext, aEvent); break; diff --git a/layout/xul/nsTitleBarFrame.cpp b/layout/xul/nsTitleBarFrame.cpp index fea19176e150..f6e302e9dd7a 100644 --- a/layout/xul/nsTitleBarFrame.cpp +++ b/layout/xul/nsTitleBarFrame.cpp @@ -66,7 +66,7 @@ nsTitleBarFrame::HandleEvent(nsPresContext* aPresContext, bool doDefault = true; - switch (aEvent->message) { + switch (aEvent->mMessage) { case NS_MOUSE_BUTTON_DOWN: { if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) { diff --git a/layout/xul/tree/nsTreeBodyFrame.cpp b/layout/xul/tree/nsTreeBodyFrame.cpp index 556bd70a343c..76ebbe9bd5fa 100644 --- a/layout/xul/tree/nsTreeBodyFrame.cpp +++ b/layout/xul/tree/nsTreeBodyFrame.cpp @@ -2563,7 +2563,7 @@ nsTreeBodyFrame::HandleEvent(nsPresContext* aPresContext, WidgetGUIEvent* aEvent, nsEventStatus* aEventStatus) { - if (aEvent->message == NS_MOUSE_OVER || aEvent->message == NS_MOUSE_MOVE) { + if (aEvent->mMessage == NS_MOUSE_OVER || aEvent->mMessage == NS_MOUSE_MOVE) { nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this); int32_t xTwips = pt.x - mInnerBox.x; int32_t yTwips = pt.y - mInnerBox.y; @@ -2576,14 +2576,12 @@ nsTreeBodyFrame::HandleEvent(nsPresContext* aPresContext, if (mMouseOverRow != -1) InvalidateRow(mMouseOverRow); } - } - else if (aEvent->message == NS_MOUSE_OUT) { + } else if (aEvent->mMessage == NS_MOUSE_OUT) { if (mMouseOverRow != -1) { InvalidateRow(mMouseOverRow); mMouseOverRow = -1; } - } - else if (aEvent->message == NS_DRAGDROP_ENTER) { + } else if (aEvent->mMessage == NS_DRAGDROP_ENTER) { if (!mSlots) mSlots = new Slots(); @@ -2600,8 +2598,7 @@ nsTreeBodyFrame::HandleEvent(nsPresContext* aPresContext, mSlots->mDropRow = -1; mSlots->mDropOrient = -1; mSlots->mDragAction = GetDropEffect(aEvent); - } - else if (aEvent->message == NS_DRAGDROP_OVER) { + } else if (aEvent->mMessage == NS_DRAGDROP_OVER) { // The mouse is hovering over this tree. If we determine things are // different from the last time, invalidate the drop feedback at the old // position, query the view to see if the current location is droppable, @@ -2710,8 +2707,7 @@ nsTreeBodyFrame::HandleEvent(nsPresContext* aPresContext, // Indicate that the drop is allowed by preventing the default behaviour. if (mSlots->mDropAllowed) *aEventStatus = nsEventStatus_eConsumeNoDefault; - } - else if (aEvent->message == NS_DRAGDROP_DROP) { + } else if (aEvent->mMessage == NS_DRAGDROP_DROP) { // this event was meant for another frame, so ignore it if (!mSlots) return NS_OK; @@ -2735,8 +2731,7 @@ nsTreeBodyFrame::HandleEvent(nsPresContext* aPresContext, mSlots->mDropOrient = -1; mSlots->mIsDragging = false; *aEventStatus = nsEventStatus_eConsumeNoDefault; // already handled the drop - } - else if (aEvent->message == NS_DRAGDROP_EXIT) { + } else if (aEvent->mMessage == NS_DRAGDROP_EXIT) { // this event was meant for another frame, so ignore it if (!mSlots) return NS_OK; diff --git a/media/libstagefright/binding/MP4Metadata.cpp b/media/libstagefright/binding/MP4Metadata.cpp index 5effe9f44998..6782b34e9428 100644 --- a/media/libstagefright/binding/MP4Metadata.cpp +++ b/media/libstagefright/binding/MP4Metadata.cpp @@ -287,17 +287,14 @@ MP4Metadata::HasCompleteMetadata(Stream* aSource) return parser->HasMetadata(); } -/*static*/ mozilla::MediaByteRange -MP4Metadata::MetadataRange(Stream* aSource) +/*static*/ already_AddRefed +MP4Metadata::Metadata(Stream* aSource) { // The MoofParser requires a monitor, but we don't need one here. mozilla::Monitor monitor("MP4Metadata::HasCompleteMetadata"); mozilla::MonitorAutoLock mon(monitor); auto parser = mozilla::MakeUnique(aSource, 0, false, &monitor); - if (parser->HasMetadata()) { - return parser->mInitRange; - } - return mozilla::MediaByteRange(); + return parser->Metadata(); } } // namespace mp4_demuxer diff --git a/media/libstagefright/binding/MoofParser.cpp b/media/libstagefright/binding/MoofParser.cpp index 7bb7d090de03..79fa3ff7741a 100644 --- a/media/libstagefright/binding/MoofParser.cpp +++ b/media/libstagefright/binding/MoofParser.cpp @@ -148,8 +148,9 @@ MoofParser::BlockingReadNextMoof() return false; } -bool -MoofParser::HasMetadata() +void +MoofParser::ScanForMetadata(mozilla::MediaByteRange& aFtyp, + mozilla::MediaByteRange& aMoov) { int64_t length = std::numeric_limits::max(); mSource->Length(&length); @@ -157,24 +158,57 @@ MoofParser::HasMetadata() byteRanges.AppendElement(MediaByteRange(0, length)); nsRefPtr stream = new BlockingStream(mSource); - MediaByteRange ftyp; - MediaByteRange moov; BoxContext context(stream, byteRanges); for (Box box(&context, mOffset); box.IsAvailable(); box = box.Next()) { if (box.IsType("ftyp")) { - ftyp = box.Range(); + aFtyp = box.Range(); continue; } if (box.IsType("moov")) { - moov = box.Range(); + aMoov = box.Range(); break; } } + mInitRange = aFtyp.Extents(aMoov); +} + +bool +MoofParser::HasMetadata() +{ + MediaByteRange ftyp; + MediaByteRange moov; + ScanForMetadata(ftyp, moov); + return !!ftyp.Length() && !!moov.Length(); +} + +already_AddRefed +MoofParser::Metadata() +{ + MediaByteRange ftyp; + MediaByteRange moov; + ScanForMetadata(ftyp, moov); if (!ftyp.Length() || !moov.Length()) { - return false; + return nullptr; } - mInitRange = ftyp.Extents(moov); - return true; + nsRefPtr metadata = new MediaByteBuffer(); + if (!metadata->SetLength(ftyp.Length() + moov.Length(), fallible)) { + // OOM + return nullptr; + } + + nsRefPtr stream = new BlockingStream(mSource); + size_t read; + bool rv = + stream->ReadAt(ftyp.mStart, metadata->Elements(), ftyp.Length(), &read); + if (!rv || read != ftyp.Length()) { + return nullptr; + } + rv = + stream->ReadAt(moov.mStart, metadata->Elements() + ftyp.Length(), moov.Length(), &read); + if (!rv || read != moov.Length()) { + return nullptr; + } + return metadata.forget(); } Interval diff --git a/media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h b/media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h index de9273c2725d..45def45af7a0 100644 --- a/media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h +++ b/media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h @@ -30,7 +30,7 @@ class MP4Metadata ~MP4Metadata(); static bool HasCompleteMetadata(Stream* aSource); - static mozilla::MediaByteRange MetadataRange(Stream* aSource); + static already_AddRefed Metadata(Stream* aSource); uint32_t GetNumberTracks(mozilla::TrackInfo::TrackType aType) const; mozilla::UniquePtr GetTrackInfo(mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const; diff --git a/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h b/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h index 86daead28cd4..4b1740b1e234 100644 --- a/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h +++ b/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h @@ -228,6 +228,7 @@ class MoofParser bool BlockingReadNextMoof(); bool HasMetadata(); + already_AddRefed Metadata(); MediaByteRange FirstCompleteMediaSegment(); MediaByteRange FirstCompleteMediaHeader(); @@ -244,6 +245,8 @@ class MoofParser Monitor* mMonitor; nsTArray& Moofs() { mMonitor->AssertCurrentThreadOwns(); return mMoofs; } private: + void ScanForMetadata(mozilla::MediaByteRange& aFtyp, + mozilla::MediaByteRange& aMoov); nsTArray mMoofs; nsTArray mMediaRanges; bool mIsAudio; diff --git a/media/libvpx/update.py b/media/libvpx/update.py index 9fbc6d17ee57..917cfb788ec7 100755 --- a/media/libvpx/update.py +++ b/media/libvpx/update.py @@ -398,6 +398,7 @@ def prepare_upstream(prefix, commit=None): configure = ['../../configure', '--target=%s' % target, '--disable-examples', '--disable-install-docs', '--enable-multi-res-encoding', + '--size-limit=4000x3000' ] if 'darwin9' in target: diff --git a/media/libvpx/vpx_config_armv7-android-gcc.asm b/media/libvpx/vpx_config_armv7-android-gcc.asm index 6a21c78ecdd6..2816b335b1ac 100644 --- a/media/libvpx/vpx_config_armv7-android-gcc.asm +++ b/media/libvpx/vpx_config_armv7-android-gcc.asm @@ -79,7 +79,7 @@ .equ CONFIG_COEFFICIENT_RANGE_CHECKING , 0 .equ CONFIG_VP9_HIGHBITDEPTH , 0 .equ CONFIG_EXPERIMENTAL , 0 -.equ CONFIG_SIZE_LIMIT , 0 +.equ CONFIG_SIZE_LIMIT , 1 .equ CONFIG_SPATIAL_SVC , 0 .equ CONFIG_FP_MB_STATS , 0 .equ CONFIG_EMULATE_HARDWARE , 0 diff --git a/media/libvpx/vpx_config_armv7-android-gcc.h b/media/libvpx/vpx_config_armv7-android-gcc.h index 468099c3a90f..ff7e42f715c2 100644 --- a/media/libvpx/vpx_config_armv7-android-gcc.h +++ b/media/libvpx/vpx_config_armv7-android-gcc.h @@ -88,8 +88,10 @@ #define CONFIG_COEFFICIENT_RANGE_CHECKING 0 #define CONFIG_VP9_HIGHBITDEPTH 0 #define CONFIG_EXPERIMENTAL 0 -#define CONFIG_SIZE_LIMIT 0 +#define CONFIG_SIZE_LIMIT 1 #define CONFIG_SPATIAL_SVC 0 #define CONFIG_FP_MB_STATS 0 #define CONFIG_EMULATE_HARDWARE 0 +#define DECODE_WIDTH_LIMIT 4000 +#define DECODE_HEIGHT_LIMIT 3000 #endif /* VPX_CONFIG_H */ diff --git a/media/libvpx/vpx_config_generic-gnu.asm b/media/libvpx/vpx_config_generic-gnu.asm index ac108f9eb0f3..0b3ddc569c4e 100644 --- a/media/libvpx/vpx_config_generic-gnu.asm +++ b/media/libvpx/vpx_config_generic-gnu.asm @@ -79,7 +79,7 @@ .equ CONFIG_COEFFICIENT_RANGE_CHECKING , 0 .equ CONFIG_VP9_HIGHBITDEPTH , 0 .equ CONFIG_EXPERIMENTAL , 0 -.equ CONFIG_SIZE_LIMIT , 0 +.equ CONFIG_SIZE_LIMIT , 1 .equ CONFIG_SPATIAL_SVC , 0 .equ CONFIG_FP_MB_STATS , 0 .equ CONFIG_EMULATE_HARDWARE , 0 diff --git a/media/libvpx/vpx_config_generic-gnu.h b/media/libvpx/vpx_config_generic-gnu.h index db883cf060f1..724edd6ede8f 100644 --- a/media/libvpx/vpx_config_generic-gnu.h +++ b/media/libvpx/vpx_config_generic-gnu.h @@ -88,8 +88,10 @@ #define CONFIG_COEFFICIENT_RANGE_CHECKING 0 #define CONFIG_VP9_HIGHBITDEPTH 0 #define CONFIG_EXPERIMENTAL 0 -#define CONFIG_SIZE_LIMIT 0 +#define CONFIG_SIZE_LIMIT 1 #define CONFIG_SPATIAL_SVC 0 #define CONFIG_FP_MB_STATS 0 #define CONFIG_EMULATE_HARDWARE 0 +#define DECODE_WIDTH_LIMIT 4000 +#define DECODE_HEIGHT_LIMIT 3000 #endif /* VPX_CONFIG_H */ diff --git a/media/libvpx/vpx_config_x86-darwin9-gcc.asm b/media/libvpx/vpx_config_x86-darwin9-gcc.asm index 9b6b8509b0f0..09e26a70ce16 100644 --- a/media/libvpx/vpx_config_x86-darwin9-gcc.asm +++ b/media/libvpx/vpx_config_x86-darwin9-gcc.asm @@ -76,7 +76,7 @@ CONFIG_VP9_TEMPORAL_DENOISING equ 0 CONFIG_COEFFICIENT_RANGE_CHECKING equ 0 CONFIG_VP9_HIGHBITDEPTH equ 0 CONFIG_EXPERIMENTAL equ 0 -CONFIG_SIZE_LIMIT equ 0 +CONFIG_SIZE_LIMIT equ 1 CONFIG_SPATIAL_SVC equ 0 CONFIG_FP_MB_STATS equ 0 CONFIG_EMULATE_HARDWARE equ 0 diff --git a/media/libvpx/vpx_config_x86-darwin9-gcc.h b/media/libvpx/vpx_config_x86-darwin9-gcc.h index 40502b410daa..7cca4b699d2a 100644 --- a/media/libvpx/vpx_config_x86-darwin9-gcc.h +++ b/media/libvpx/vpx_config_x86-darwin9-gcc.h @@ -88,8 +88,10 @@ #define CONFIG_COEFFICIENT_RANGE_CHECKING 0 #define CONFIG_VP9_HIGHBITDEPTH 0 #define CONFIG_EXPERIMENTAL 0 -#define CONFIG_SIZE_LIMIT 0 +#define CONFIG_SIZE_LIMIT 1 #define CONFIG_SPATIAL_SVC 0 #define CONFIG_FP_MB_STATS 0 #define CONFIG_EMULATE_HARDWARE 0 +#define DECODE_WIDTH_LIMIT 4000 +#define DECODE_HEIGHT_LIMIT 3000 #endif /* VPX_CONFIG_H */ diff --git a/media/libvpx/vpx_config_x86-linux-gcc.asm b/media/libvpx/vpx_config_x86-linux-gcc.asm index 5400f57d5a5f..63b48bb176c4 100644 --- a/media/libvpx/vpx_config_x86-linux-gcc.asm +++ b/media/libvpx/vpx_config_x86-linux-gcc.asm @@ -76,7 +76,7 @@ CONFIG_VP9_TEMPORAL_DENOISING equ 0 CONFIG_COEFFICIENT_RANGE_CHECKING equ 0 CONFIG_VP9_HIGHBITDEPTH equ 0 CONFIG_EXPERIMENTAL equ 0 -CONFIG_SIZE_LIMIT equ 0 +CONFIG_SIZE_LIMIT equ 1 CONFIG_SPATIAL_SVC equ 0 CONFIG_FP_MB_STATS equ 0 CONFIG_EMULATE_HARDWARE equ 0 diff --git a/media/libvpx/vpx_config_x86-linux-gcc.h b/media/libvpx/vpx_config_x86-linux-gcc.h index e7b472e84fea..edcf0ee9fc69 100644 --- a/media/libvpx/vpx_config_x86-linux-gcc.h +++ b/media/libvpx/vpx_config_x86-linux-gcc.h @@ -88,8 +88,10 @@ #define CONFIG_COEFFICIENT_RANGE_CHECKING 0 #define CONFIG_VP9_HIGHBITDEPTH 0 #define CONFIG_EXPERIMENTAL 0 -#define CONFIG_SIZE_LIMIT 0 +#define CONFIG_SIZE_LIMIT 1 #define CONFIG_SPATIAL_SVC 0 #define CONFIG_FP_MB_STATS 0 #define CONFIG_EMULATE_HARDWARE 0 +#define DECODE_WIDTH_LIMIT 4000 +#define DECODE_HEIGHT_LIMIT 3000 #endif /* VPX_CONFIG_H */ diff --git a/media/libvpx/vpx_config_x86-win32-gcc.asm b/media/libvpx/vpx_config_x86-win32-gcc.asm index 29c89a5186b1..86f39772f923 100644 --- a/media/libvpx/vpx_config_x86-win32-gcc.asm +++ b/media/libvpx/vpx_config_x86-win32-gcc.asm @@ -76,7 +76,7 @@ CONFIG_VP9_TEMPORAL_DENOISING equ 0 CONFIG_COEFFICIENT_RANGE_CHECKING equ 0 CONFIG_VP9_HIGHBITDEPTH equ 0 CONFIG_EXPERIMENTAL equ 0 -CONFIG_SIZE_LIMIT equ 0 +CONFIG_SIZE_LIMIT equ 1 CONFIG_SPATIAL_SVC equ 0 CONFIG_FP_MB_STATS equ 0 CONFIG_EMULATE_HARDWARE equ 0 diff --git a/media/libvpx/vpx_config_x86-win32-gcc.h b/media/libvpx/vpx_config_x86-win32-gcc.h index e60f84d8c171..abc34be9d6dd 100644 --- a/media/libvpx/vpx_config_x86-win32-gcc.h +++ b/media/libvpx/vpx_config_x86-win32-gcc.h @@ -89,8 +89,10 @@ #define CONFIG_COEFFICIENT_RANGE_CHECKING 0 #define CONFIG_VP9_HIGHBITDEPTH 0 #define CONFIG_EXPERIMENTAL 0 -#define CONFIG_SIZE_LIMIT 0 +#define CONFIG_SIZE_LIMIT 1 #define CONFIG_SPATIAL_SVC 0 #define CONFIG_FP_MB_STATS 0 #define CONFIG_EMULATE_HARDWARE 0 +#define DECODE_WIDTH_LIMIT 4000 +#define DECODE_HEIGHT_LIMIT 3000 #endif /* VPX_CONFIG_H */ diff --git a/media/libvpx/vpx_config_x86-win32-vs12.asm b/media/libvpx/vpx_config_x86-win32-vs12.asm index da0e226da7de..d290abc6072a 100644 --- a/media/libvpx/vpx_config_x86-win32-vs12.asm +++ b/media/libvpx/vpx_config_x86-win32-vs12.asm @@ -76,7 +76,7 @@ CONFIG_VP9_TEMPORAL_DENOISING equ 0 CONFIG_COEFFICIENT_RANGE_CHECKING equ 0 CONFIG_VP9_HIGHBITDEPTH equ 0 CONFIG_EXPERIMENTAL equ 0 -CONFIG_SIZE_LIMIT equ 0 +CONFIG_SIZE_LIMIT equ 1 CONFIG_SPATIAL_SVC equ 0 CONFIG_FP_MB_STATS equ 0 CONFIG_EMULATE_HARDWARE equ 0 diff --git a/media/libvpx/vpx_config_x86-win32-vs12.h b/media/libvpx/vpx_config_x86-win32-vs12.h index a91bb8396eac..9c1d36df8405 100644 --- a/media/libvpx/vpx_config_x86-win32-vs12.h +++ b/media/libvpx/vpx_config_x86-win32-vs12.h @@ -88,8 +88,10 @@ #define CONFIG_COEFFICIENT_RANGE_CHECKING 0 #define CONFIG_VP9_HIGHBITDEPTH 0 #define CONFIG_EXPERIMENTAL 0 -#define CONFIG_SIZE_LIMIT 0 +#define CONFIG_SIZE_LIMIT 1 #define CONFIG_SPATIAL_SVC 0 #define CONFIG_FP_MB_STATS 0 #define CONFIG_EMULATE_HARDWARE 0 +#define DECODE_WIDTH_LIMIT 4000 +#define DECODE_HEIGHT_LIMIT 3000 #endif /* VPX_CONFIG_H */ diff --git a/media/libvpx/vpx_config_x86_64-darwin9-gcc.asm b/media/libvpx/vpx_config_x86_64-darwin9-gcc.asm index 3dbb38fc2e61..7c808c83f37c 100644 --- a/media/libvpx/vpx_config_x86_64-darwin9-gcc.asm +++ b/media/libvpx/vpx_config_x86_64-darwin9-gcc.asm @@ -76,7 +76,7 @@ CONFIG_VP9_TEMPORAL_DENOISING equ 0 CONFIG_COEFFICIENT_RANGE_CHECKING equ 0 CONFIG_VP9_HIGHBITDEPTH equ 0 CONFIG_EXPERIMENTAL equ 0 -CONFIG_SIZE_LIMIT equ 0 +CONFIG_SIZE_LIMIT equ 1 CONFIG_SPATIAL_SVC equ 0 CONFIG_FP_MB_STATS equ 0 CONFIG_EMULATE_HARDWARE equ 0 diff --git a/media/libvpx/vpx_config_x86_64-darwin9-gcc.h b/media/libvpx/vpx_config_x86_64-darwin9-gcc.h index 0d0477ceeca1..d04556d4585f 100644 --- a/media/libvpx/vpx_config_x86_64-darwin9-gcc.h +++ b/media/libvpx/vpx_config_x86_64-darwin9-gcc.h @@ -88,8 +88,10 @@ #define CONFIG_COEFFICIENT_RANGE_CHECKING 0 #define CONFIG_VP9_HIGHBITDEPTH 0 #define CONFIG_EXPERIMENTAL 0 -#define CONFIG_SIZE_LIMIT 0 +#define CONFIG_SIZE_LIMIT 1 #define CONFIG_SPATIAL_SVC 0 #define CONFIG_FP_MB_STATS 0 #define CONFIG_EMULATE_HARDWARE 0 +#define DECODE_WIDTH_LIMIT 4000 +#define DECODE_HEIGHT_LIMIT 3000 #endif /* VPX_CONFIG_H */ diff --git a/media/libvpx/vpx_config_x86_64-linux-gcc.asm b/media/libvpx/vpx_config_x86_64-linux-gcc.asm index 6da93489bfb4..481d00df05c5 100644 --- a/media/libvpx/vpx_config_x86_64-linux-gcc.asm +++ b/media/libvpx/vpx_config_x86_64-linux-gcc.asm @@ -76,7 +76,7 @@ CONFIG_VP9_TEMPORAL_DENOISING equ 0 CONFIG_COEFFICIENT_RANGE_CHECKING equ 0 CONFIG_VP9_HIGHBITDEPTH equ 0 CONFIG_EXPERIMENTAL equ 0 -CONFIG_SIZE_LIMIT equ 0 +CONFIG_SIZE_LIMIT equ 1 CONFIG_SPATIAL_SVC equ 0 CONFIG_FP_MB_STATS equ 0 CONFIG_EMULATE_HARDWARE equ 0 diff --git a/media/libvpx/vpx_config_x86_64-linux-gcc.h b/media/libvpx/vpx_config_x86_64-linux-gcc.h index 21228cd1dd09..dcd1ad82a9f5 100644 --- a/media/libvpx/vpx_config_x86_64-linux-gcc.h +++ b/media/libvpx/vpx_config_x86_64-linux-gcc.h @@ -88,8 +88,10 @@ #define CONFIG_COEFFICIENT_RANGE_CHECKING 0 #define CONFIG_VP9_HIGHBITDEPTH 0 #define CONFIG_EXPERIMENTAL 0 -#define CONFIG_SIZE_LIMIT 0 +#define CONFIG_SIZE_LIMIT 1 #define CONFIG_SPATIAL_SVC 0 #define CONFIG_FP_MB_STATS 0 #define CONFIG_EMULATE_HARDWARE 0 +#define DECODE_WIDTH_LIMIT 4000 +#define DECODE_HEIGHT_LIMIT 3000 #endif /* VPX_CONFIG_H */ diff --git a/media/libvpx/vpx_config_x86_64-win64-gcc.asm b/media/libvpx/vpx_config_x86_64-win64-gcc.asm index 20ba15baee06..6d2ded296425 100644 --- a/media/libvpx/vpx_config_x86_64-win64-gcc.asm +++ b/media/libvpx/vpx_config_x86_64-win64-gcc.asm @@ -76,7 +76,7 @@ CONFIG_VP9_TEMPORAL_DENOISING equ 0 CONFIG_COEFFICIENT_RANGE_CHECKING equ 0 CONFIG_VP9_HIGHBITDEPTH equ 0 CONFIG_EXPERIMENTAL equ 0 -CONFIG_SIZE_LIMIT equ 0 +CONFIG_SIZE_LIMIT equ 1 CONFIG_SPATIAL_SVC equ 0 CONFIG_FP_MB_STATS equ 0 CONFIG_EMULATE_HARDWARE equ 0 diff --git a/media/libvpx/vpx_config_x86_64-win64-gcc.h b/media/libvpx/vpx_config_x86_64-win64-gcc.h index b056a0ea3850..cf8a66268eb1 100644 --- a/media/libvpx/vpx_config_x86_64-win64-gcc.h +++ b/media/libvpx/vpx_config_x86_64-win64-gcc.h @@ -89,8 +89,10 @@ #define CONFIG_COEFFICIENT_RANGE_CHECKING 0 #define CONFIG_VP9_HIGHBITDEPTH 0 #define CONFIG_EXPERIMENTAL 0 -#define CONFIG_SIZE_LIMIT 0 +#define CONFIG_SIZE_LIMIT 1 #define CONFIG_SPATIAL_SVC 0 #define CONFIG_FP_MB_STATS 0 #define CONFIG_EMULATE_HARDWARE 0 +#define DECODE_WIDTH_LIMIT 4000 +#define DECODE_HEIGHT_LIMIT 3000 #endif /* VPX_CONFIG_H */ diff --git a/media/libvpx/vpx_config_x86_64-win64-vs12.asm b/media/libvpx/vpx_config_x86_64-win64-vs12.asm index 39c17a07c3ee..a3d10b8d814a 100644 --- a/media/libvpx/vpx_config_x86_64-win64-vs12.asm +++ b/media/libvpx/vpx_config_x86_64-win64-vs12.asm @@ -76,7 +76,7 @@ CONFIG_VP9_TEMPORAL_DENOISING equ 0 CONFIG_COEFFICIENT_RANGE_CHECKING equ 0 CONFIG_VP9_HIGHBITDEPTH equ 0 CONFIG_EXPERIMENTAL equ 0 -CONFIG_SIZE_LIMIT equ 0 +CONFIG_SIZE_LIMIT equ 1 CONFIG_SPATIAL_SVC equ 0 CONFIG_FP_MB_STATS equ 0 CONFIG_EMULATE_HARDWARE equ 0 diff --git a/media/libvpx/vpx_config_x86_64-win64-vs12.h b/media/libvpx/vpx_config_x86_64-win64-vs12.h index 4ad69bdb8445..17cd9421ed24 100644 --- a/media/libvpx/vpx_config_x86_64-win64-vs12.h +++ b/media/libvpx/vpx_config_x86_64-win64-vs12.h @@ -88,8 +88,10 @@ #define CONFIG_COEFFICIENT_RANGE_CHECKING 0 #define CONFIG_VP9_HIGHBITDEPTH 0 #define CONFIG_EXPERIMENTAL 0 -#define CONFIG_SIZE_LIMIT 0 +#define CONFIG_SIZE_LIMIT 1 #define CONFIG_SPATIAL_SVC 0 #define CONFIG_FP_MB_STATS 0 #define CONFIG_EMULATE_HARDWARE 0 +#define DECODE_WIDTH_LIMIT 4000 +#define DECODE_HEIGHT_LIMIT 3000 #endif /* VPX_CONFIG_H */ diff --git a/media/mtransport/test/ice_unittest.cpp b/media/mtransport/test/ice_unittest.cpp index a3db872dd3fe..615e29c444a2 100644 --- a/media/mtransport/test/ice_unittest.cpp +++ b/media/mtransport/test/ice_unittest.cpp @@ -87,6 +87,13 @@ namespace { enum TrickleMode { TRICKLE_NONE, TRICKLE_SIMULATE, TRICKLE_REAL }; +const unsigned int ICE_TEST_PEER_OFFERER = (1 << 0); +const unsigned int ICE_TEST_PEER_SET_PRIORITIES = (1 << 1); +const unsigned int ICE_TEST_PEER_ALLOW_LOOPBACK = (1 << 2); +const unsigned int ICE_TEST_PEER_ENABLED_TCP = (1 << 3); +const unsigned int ICE_TEST_PEER_ALLOW_LINK_LOCAL = (1 << 4); +const unsigned int ICE_TEST_PEER_HIDE_NON_DEFAULT = (1 << 5); + typedef std::string (*CandidateFilter)(const std::string& candidate); static std::string IsRelayCandidate(const std::string& candidate) { @@ -802,7 +809,8 @@ class IceTestPeer : public sigslot::has_slots<> { if (candidate.empty()) { return; } - std::cerr << "Candidate initialized: " << candidate << std::endl; + std::cerr << "Candidate for stream " << stream->name() << " initialized: " + << candidate << std::endl; candidates_[stream->name()].push_back(candidate); // If we are connected, then try to trickle to the @@ -1148,9 +1156,15 @@ class IceGatherTest : public ::testing::Test { NrIceCtx::internal_DeinitializeGlobal(); } - void EnsurePeer() { + void EnsurePeer(const unsigned int flags = ICE_TEST_PEER_OFFERER) { if (!peer_) { - peer_ = new IceTestPeer("P1", true, false); + peer_ = new IceTestPeer("P1", + flags & ICE_TEST_PEER_OFFERER, + flags & ICE_TEST_PEER_SET_PRIORITIES, + flags & ICE_TEST_PEER_ALLOW_LOOPBACK, + flags & ICE_TEST_PEER_ENABLED_TCP, + flags & ICE_TEST_PEER_ALLOW_LINK_LOCAL, + flags & ICE_TEST_PEER_HIDE_NON_DEFAULT); peer_->AddStream(1); } } @@ -1221,7 +1235,7 @@ class IceGatherTest : public ::testing::Test { const std::string& fake_addr, uint16_t fake_port, const std::string& fqdn = std::string()) { - EnsurePeer(); + EnsurePeer(ICE_TEST_PEER_OFFERER | ICE_TEST_PEER_ENABLED_TCP); std::vector stun_servers; AddStunServerWithResponse(fake_addr, fake_port, fqdn, "tcp", &stun_servers); peer_->SetStunServers(stun_servers); @@ -1232,7 +1246,7 @@ class IceGatherTest : public ::testing::Test { uint16_t fake_udp_port, const std::string& fake_tcp_addr, uint16_t fake_tcp_port) { - EnsurePeer(); + EnsurePeer(ICE_TEST_PEER_OFFERER | ICE_TEST_PEER_ENABLED_TCP); std::vector stun_servers; AddStunServerWithResponse(fake_udp_addr, fake_udp_port, @@ -1669,10 +1683,11 @@ TEST_F(IceGatherTest, TestGatherFakeStunServerTcpHostnameNoResolver) { return; } - EnsurePeer(); + EnsurePeer(ICE_TEST_PEER_OFFERER | ICE_TEST_PEER_ENABLED_TCP); peer_->SetStunServer(g_stun_server_hostname, kDefaultStunServerPort, kNrIceTransportTcp); Gather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " TCP ")); } TEST_F(IceGatherTest, TestGatherFakeStunServerIpAddress) { @@ -1735,7 +1750,7 @@ TEST_F(IceGatherTest, TestGatherDNSStunServerIpAddressTcp) { return; } - EnsurePeer(); + EnsurePeer(ICE_TEST_PEER_OFFERER | ICE_TEST_PEER_ENABLED_TCP); peer_->SetStunServer(g_stun_server_address, kDefaultStunServerPort, kNrIceTransportTcp); peer_->SetDNSResolver(); @@ -1761,7 +1776,7 @@ TEST_F(IceGatherTest, TestGatherDNSStunServerHostname) { } TEST_F(IceGatherTest, TestGatherDNSStunServerHostnameTcp) { - EnsurePeer(); + EnsurePeer(ICE_TEST_PEER_OFFERER | ICE_TEST_PEER_ENABLED_TCP); peer_->SetStunServer(g_stun_server_hostname, kDefaultStunServerPort, kNrIceTransportTcp); peer_->SetDNSResolver(); @@ -1780,7 +1795,7 @@ TEST_F(IceGatherTest, TestGatherDNSStunServerHostnameBothUdpTcp) { std::vector stun_servers; - EnsurePeer(); + EnsurePeer(ICE_TEST_PEER_OFFERER | ICE_TEST_PEER_ENABLED_TCP); stun_servers.push_back(*NrIceStunServer::Create(g_stun_server_hostname, kDefaultStunServerPort, kNrIceTransportUdp)); stun_servers.push_back(*NrIceStunServer::Create(g_stun_server_hostname, @@ -1788,6 +1803,8 @@ TEST_F(IceGatherTest, TestGatherDNSStunServerHostnameBothUdpTcp) { peer_->SetStunServers(stun_servers); peer_->SetDNSResolver(); Gather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " UDP ")); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " TCP ")); } TEST_F(IceGatherTest, TestGatherDNSStunServerIpAddressBothUdpTcp) { @@ -1797,7 +1814,7 @@ TEST_F(IceGatherTest, TestGatherDNSStunServerIpAddressBothUdpTcp) { std::vector stun_servers; - EnsurePeer(); + EnsurePeer(ICE_TEST_PEER_OFFERER | ICE_TEST_PEER_ENABLED_TCP); stun_servers.push_back(*NrIceStunServer::Create(g_stun_server_address, kDefaultStunServerPort, kNrIceTransportUdp)); stun_servers.push_back(*NrIceStunServer::Create(g_stun_server_address, @@ -1805,6 +1822,8 @@ TEST_F(IceGatherTest, TestGatherDNSStunServerIpAddressBothUdpTcp) { peer_->SetStunServers(stun_servers); peer_->SetDNSResolver(); Gather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " UDP ")); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " TCP ")); } TEST_F(IceGatherTest, TestGatherDNSStunBogusHostname) { @@ -1812,14 +1831,16 @@ TEST_F(IceGatherTest, TestGatherDNSStunBogusHostname) { peer_->SetStunServer(kBogusStunServerHostname, kDefaultStunServerPort); peer_->SetDNSResolver(); Gather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " UDP ")); } TEST_F(IceGatherTest, TestGatherDNSStunBogusHostnameTcp) { - EnsurePeer(); + EnsurePeer(ICE_TEST_PEER_OFFERER | ICE_TEST_PEER_ENABLED_TCP); peer_->SetStunServer(kBogusStunServerHostname, kDefaultStunServerPort, kNrIceTransportTcp); peer_->SetDNSResolver(); Gather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " TCP ")); } TEST_F(IceGatherTest, TestDefaultCandidate) { diff --git a/media/mtransport/test/multi_tcp_socket_unittest.cpp b/media/mtransport/test/multi_tcp_socket_unittest.cpp index bc6535ab3d64..a390058496c8 100644 --- a/media/mtransport/test/multi_tcp_socket_unittest.cpp +++ b/media/mtransport/test/multi_tcp_socket_unittest.cpp @@ -155,6 +155,18 @@ class MultiTcpSocketTest : public ::testing::Test { NS_DISPATCH_SYNC); } + void Destroy_s(nr_socket *sock) { + int r = nr_socket_destroy(&sock); + ASSERT_EQ(0, r); + } + + void Destroy(nr_socket *sock) { + test_utils->sts_target()->Dispatch( + WrapRunnable( + this, &MultiTcpSocketTest::Destroy_s, sock), + NS_DISPATCH_SYNC); + } + void Connect_s(nr_socket *from, nr_socket *to) { nr_transport_addr addr_to; nr_transport_addr addr_from; @@ -251,6 +263,31 @@ class MultiTcpSocketTest : public ::testing::Test { NS_DISPATCH_SYNC); } + void RecvDataFailed_s(nr_socket *sent_to, size_t expected_len, int expected_err) { + SetReadable(false); + char received_data[expected_len+1]; + nr_transport_addr addr_to; + nr_transport_addr retaddr; + size_t retlen; + + int r=nr_socket_getaddr(sent_to, &addr_to); + ASSERT_EQ(0, r); + r=nr_socket_recvfrom(sent_to, received_data, expected_len+1, + &retlen, 0, &retaddr); + ASSERT_EQ(expected_err, r) << "Expecting receive failure " << expected_err + << " on " << addr_to.as_string; + } + + void RecvDataFailed(nr_socket *sent_to, size_t expected_len, + int expected_err) { + ASSERT_TRUE_WAIT(IsReadable(), 1000); + test_utils->sts_target()->Dispatch( + WrapRunnable( + this, &MultiTcpSocketTest::RecvDataFailed_s, sent_to, expected_len, + expected_err), + NS_DISPATCH_SYNC); + } + void TransferData(nr_socket *from, nr_socket *to, const char *data, size_t len) { SendData(from, to, data, len); @@ -295,6 +332,44 @@ TEST_F(MultiTcpSocketTest, TestTransmit) { TransferData(socks[1], socks[0], data, sizeof(data)); } +TEST_F(MultiTcpSocketTest, TestClosePassive) { + const char data[] = "TestClosePassive"; + socks[0] = Create(TCP_TYPE_ACTIVE); + socks[1] = Create(TCP_TYPE_PASSIVE); + Listen(socks[1]); + Connect(socks[0], socks[1]); + + TransferData(socks[0], socks[1], data, sizeof(data)); + TransferData(socks[1], socks[0], data, sizeof(data)); + + /* We have to destroy as only that calls PR_Close() */ + std::cerr << "Destructing socket" << std::endl; + Destroy(socks[1]); + + RecvDataFailed(socks[0], sizeof(data), R_EOD); + + socks[1] = nullptr; +} + +TEST_F(MultiTcpSocketTest, TestCloseActive) { + const char data[] = "TestCloseActive"; + socks[0] = Create(TCP_TYPE_ACTIVE); + socks[1] = Create(TCP_TYPE_PASSIVE); + Listen(socks[1]); + Connect(socks[0], socks[1]); + + TransferData(socks[0], socks[1], data, sizeof(data)); + TransferData(socks[1], socks[0], data, sizeof(data)); + + /* We have to destroy as only that calls PR_Close() */ + std::cerr << "Destructing socket" << std::endl; + Destroy(socks[0]); + + RecvDataFailed(socks[1], sizeof(data), R_EOD); + + socks[0] = nullptr; +} + TEST_F(MultiTcpSocketTest, TestTwoSendsBeforeReceives) { const char data1[] = "TestTwoSendsBeforeReceives"; const char data2[] = "2nd data"; diff --git a/media/pocketsphinx/moz.build b/media/pocketsphinx/moz.build index 3888baaecd5b..94173802a75c 100644 --- a/media/pocketsphinx/moz.build +++ b/media/pocketsphinx/moz.build @@ -12,9 +12,8 @@ EXPORTS.pocketsphinx += [ 'pocketsphinx.h', ] -SOURCES += [ +UNIFIED_SOURCES += [ 'src/acmod.c', - 'src/allphone_search.c', 'src/bin_mdef.c', 'src/blkarray_list.c', 'src/dict.c', @@ -29,7 +28,6 @@ SOURCES += [ 'src/ms_gauden.c', 'src/ms_mgau.c', 'src/ms_senone.c', - 'src/ngram_search.c', 'src/ngram_search_fwdflat.c', 'src/ngram_search_fwdtree.c', 'src/phone_loop_search.c', @@ -38,12 +36,17 @@ SOURCES += [ 'src/ps_lattice.c', 'src/ps_mllr.c', 'src/ptm_mgau.c', - 'src/s2_semi_mgau.c', - 'src/state_align_search.c', 'src/tmat.c', 'src/vector.c', ] +SOURCES += [ + 'src/allphone_search.c', + 'src/ngram_search.c', + 'src/s2_semi_mgau.c', + 'src/state_align_search.c', +] + # Suppress warnings in third-party code. if CONFIG['GNU_CC']: CFLAGS += [ diff --git a/memory/mozalloc/moz.build b/memory/mozalloc/moz.build index 443a7f0efe9c..6bfe4bd139ca 100644 --- a/memory/mozalloc/moz.build +++ b/memory/mozalloc/moz.build @@ -11,25 +11,18 @@ EXPORTS.mozilla += [ 'mozalloc_oom.h', ] -if CONFIG['MOZ_MSVC_STL_WRAP_RAISE'] or CONFIG['MOZ_MSVC_STL_WRAP_Throw']: - build_msvc_wrappers = 1 -else: - build_msvc_wrappers = 0 - if CONFIG['WRAP_STL_INCLUDES']: if CONFIG['GNU_CXX']: EXPORTS.mozilla += ['throw_gcc.h'] elif CONFIG['_MSC_VER']: DEFINES['_HAS_EXCEPTIONS'] = 0 - if build_msvc_wrappers: + if CONFIG['MOZ_MSVC_STL_WRAP_RAISE']: EXPORTS.mozilla += [ 'msvc_raise_wrappers.h', - 'msvc_throw_wrapper.h', 'throw_msvc.h', ] SOURCES += [ 'msvc_raise_wrappers.cpp', - 'msvc_throw_wrapper.cpp', ] UNIFIED_SOURCES += [ diff --git a/memory/mozalloc/msvc_raise_wrappers.cpp b/memory/mozalloc/msvc_raise_wrappers.cpp index 768efdc12f23..8099e8a681b5 100644 --- a/memory/mozalloc/msvc_raise_wrappers.cpp +++ b/memory/mozalloc/msvc_raise_wrappers.cpp @@ -9,9 +9,6 @@ #include "mozalloc_abort.h" -#define MOZALLOC_DONT_WRAP_RAISE_FUNCTIONS -#include "mozilla/throw_msvc.h" - __declspec(noreturn) static void abort_from_exception(const char* const which, const char* const what); static void @@ -27,31 +24,31 @@ namespace std { // doing this after careful review because we want to define our own // exception throwing semantics. Don't try this at home! -void +MFBT_API __declspec(noreturn) void moz_Xinvalid_argument(const char* what) { abort_from_exception("invalid_argument", what); } -void +MFBT_API __declspec(noreturn) void moz_Xlength_error(const char* what) { abort_from_exception("length_error", what); } -void +MFBT_API __declspec(noreturn) void moz_Xout_of_range(const char* what) { abort_from_exception("out_of_range", what); } -void +MFBT_API __declspec(noreturn) void moz_Xoverflow_error(const char* what) { abort_from_exception("overflow_error", what); } -void +MFBT_API __declspec(noreturn) void moz_Xruntime_error(const char* what) { abort_from_exception("runtime_error", what); diff --git a/memory/mozalloc/msvc_raise_wrappers.h b/memory/mozalloc/msvc_raise_wrappers.h index 557a481ea4e1..914bc629290f 100644 --- a/memory/mozalloc/msvc_raise_wrappers.h +++ b/memory/mozalloc/msvc_raise_wrappers.h @@ -17,22 +17,10 @@ #include "mozilla/mozalloc_abort.h" -namespace std { - -// NB: user code is not supposed to touch the std:: namespace. We're -// doing this after careful review because we want to define our own -// exception throwing semantics. Don't try this at home! - -MFBT_API __declspec(noreturn) void moz_Xinvalid_argument(const char*); -MFBT_API __declspec(noreturn) void moz_Xlength_error(const char*); -MFBT_API __declspec(noreturn) void moz_Xout_of_range(const char*); -MFBT_API __declspec(noreturn) void moz_Xoverflow_error(const char*); -MFBT_API __declspec(noreturn) void moz_Xruntime_error(const char*); - -} // namespace std - -#ifndef MOZALLOC_DONT_WRAP_RAISE_FUNCTIONS - +// xutility will declare the following functions in the std namespace. +// We #define them to be named differently so we can ensure the exception +// throwing semantics of these functions work exactly the way we want, by +// defining our own versions in msvc_raise_wrappers.cpp. # define _Xinvalid_argument moz_Xinvalid_argument # define _Xlength_error moz_Xlength_error # define _Xout_of_range moz_Xout_of_range @@ -45,6 +33,4 @@ MFBT_API __declspec(noreturn) void moz_Xruntime_error(const char*); # undef _RAISE # define _RAISE(x) mozalloc_abort((x).what()) -#endif // ifndef MOZALLOC_DONT_WRAP_RAISE_FUNCTIONS - #endif // ifndef mozilla_msvc_raise_wrappers_h diff --git a/memory/mozalloc/msvc_throw_wrapper.cpp b/memory/mozalloc/msvc_throw_wrapper.cpp deleted file mode 100644 index e93fd8a50ce6..000000000000 --- a/memory/mozalloc/msvc_throw_wrapper.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: sw=4 ts=4 et : - */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include - -#include "mozilla/mozalloc_abort.h" - -namespace std { - -// NB: user code is not supposed to touch the std:: namespace. We're -// doing this after careful review because we want to define our own -// exception throwing semantics. Don't try this at home! - -__declspec(dllexport) void mozilla_Throw(const exception& e); - -void -mozilla_Throw(const exception& e) -{ - mozalloc_abort(e.what()); -} - -} // namespace std diff --git a/memory/mozalloc/msvc_throw_wrapper.h b/memory/mozalloc/msvc_throw_wrapper.h deleted file mode 100644 index 3bdfba40381a..000000000000 --- a/memory/mozalloc/msvc_throw_wrapper.h +++ /dev/null @@ -1,18 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: sw=4 ts=4 et : - */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_msvc_throw_wrapper_h -#define mozilla_msvc_throw_wrapper_h - -// Define our own _Throw because the Win2k CRT doesn't export it. -# ifdef _EXCEPTION_ -# error "Unable to wrap _Throw(); CRT _Throw() already declared" -# endif -# define _Throw mozilla_Throw -# include - -#endif // ifndef mozilla_msvc_throw_wrapper_h diff --git a/memory/mozalloc/staticruntime/moz.build b/memory/mozalloc/staticruntime/moz.build index 56de6a066c0b..f29a9f8f5fea 100644 --- a/memory/mozalloc/staticruntime/moz.build +++ b/memory/mozalloc/staticruntime/moz.build @@ -6,17 +6,11 @@ NO_VISIBILITY_FLAGS = True -if CONFIG['MOZ_MSVC_STL_WRAP_RAISE'] or CONFIG['MOZ_MSVC_STL_WRAP_Throw']: - build_msvc_wrappers = 1 -else: - build_msvc_wrappers = 0 - if CONFIG['WRAP_STL_INCLUDES']: DEFINES['_HAS_EXCEPTIONS'] = 0 - if build_msvc_wrappers: + if CONFIG['MOZ_MSVC_STL_WRAP_RAISE']: SOURCES += [ '../msvc_raise_wrappers.cpp', - '../msvc_throw_wrapper.cpp', ] UNIFIED_SOURCES += [ diff --git a/memory/mozalloc/throw_msvc.h b/memory/mozalloc/throw_msvc.h index 3169e75f3fa6..e6ebf46dc4c0 100644 --- a/memory/mozalloc/throw_msvc.h +++ b/memory/mozalloc/throw_msvc.h @@ -10,8 +10,6 @@ #if defined(MOZ_MSVC_STL_WRAP_RAISE) # include "msvc_raise_wrappers.h" -#elif defined(MOZ_MSVC_STL_WRAP_Throw) -# include "msvc_throw_wrapper.h" #else # error "Unknown STL wrapper tactic" #endif diff --git a/mfbt/Attributes.h b/mfbt/Attributes.h index aa31d1729106..252346519a02 100644 --- a/mfbt/Attributes.h +++ b/mfbt/Attributes.h @@ -53,6 +53,7 @@ # define MOZ_HAVE_NEVER_INLINE __declspec(noinline) # define MOZ_HAVE_NORETURN __declspec(noreturn) # if _MSC_VER >= 1900 +# define MOZ_HAVE_CXX11_CONSTEXPR # define MOZ_HAVE_EXPLICIT_CONVERSION # endif # ifdef __clang__ diff --git a/mfbt/Char16.h b/mfbt/Char16.h index 1d7434c1e017..91cbc2878169 100644 --- a/mfbt/Char16.h +++ b/mfbt/Char16.h @@ -49,7 +49,7 @@ typedef unsigned int char32_t; #endif #ifdef MOZ_USE_CHAR16_WRAPPER -# include +# include /** * Win32 API extensively uses wchar_t, which is represented by a separated * builtin type than char16_t per spec. It's not the case for MSVC prior to @@ -93,10 +93,6 @@ class char16ptr_t { return mPtr != nullptr; } - operator std::wstring() const - { - return std::wstring(static_cast(*this)); - } /* Explicit cast operators to allow things like (char16_t*)str. */ explicit operator char16_t*() const diff --git a/mfbt/Compression.cpp b/mfbt/Compression.cpp index 154cef1031a6..c114c6c0fc8d 100644 --- a/mfbt/Compression.cpp +++ b/mfbt/Compression.cpp @@ -7,6 +7,11 @@ #include "mozilla/Compression.h" #include "mozilla/CheckedInt.h" +// Without including , MSVC 2015 complains about e.g. the impossibility +// to convert `const void* const` to `void*` when calling memchr from +// corecrt_memory.h. +#include + using namespace mozilla::Compression; namespace { diff --git a/mfbt/nsRefPtr.h b/mfbt/nsRefPtr.h index 6b70cc97aa07..c3440b289c81 100644 --- a/mfbt/nsRefPtr.h +++ b/mfbt/nsRefPtr.h @@ -348,12 +348,14 @@ class nsRefPtr private: // This helper class makes |nsRefPtr| possible by casting away // the constness from the pointer when calling AddRef() and Release(). + // // This is necessary because AddRef() and Release() implementations can't // generally expected to be const themselves (without heavy use of |mutable| // and |const_cast| in their own implementations). - // This should be sound because while |nsRefPtr| provides a const - // view of an object, the object itself should be const (it would have to be - // allocated as |new const T| or similar to itself be const). + // + // This should be sound because while |nsRefPtr| provides a + // const view of an object, the object itself should not be const (it + // would have to be allocated as |new const T| or similar to be const). template struct AddRefTraits { diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js index ec62eab3f9d9..7dcd3076c7ce 100644 --- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -525,7 +525,7 @@ pref("app.update.timerMinimumDelay", 30); // seconds // used by update service to decide whether or not to // automatically download an update pref("app.update.autodownload", "wifi"); -pref("app.update.url.android", "https://aus4.mozilla.org/update/4/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%MOZ_VERSION%/update.xml"); +pref("app.update.url.android", "https://aus5.mozilla.org/update/4/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%MOZ_VERSION%/update.xml"); #ifdef MOZ_UPDATER /* prefs used specifically for updating the app */ diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index b6532fab3d18..f6c23d0b7d8d 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -74,6 +74,7 @@ import org.mozilla.gecko.util.ActivityUtils; import org.mozilla.gecko.util.Clipboard; import org.mozilla.gecko.util.EventCallback; +import org.mozilla.gecko.util.FloatUtils; import org.mozilla.gecko.util.GamepadUtils; import org.mozilla.gecko.util.GeckoEventListener; import org.mozilla.gecko.util.HardwareUtils; @@ -1582,16 +1583,13 @@ public void onPanZoomStopped() { float toolbarTranslation = mLayerView.getDynamicToolbarAnimator().getToolbarTranslation(); boolean shortPage = metrics.getPageHeight() < metrics.getHeight(); - boolean toolbarMostlyVisible = toolbarTranslation < (mToolbarHeight / 2); - boolean atBottomOfLongPage = (metrics.pageRectBottom == metrics.viewportRectBottom()) + boolean atBottomOfLongPage = + FloatUtils.fuzzyEquals(metrics.pageRectBottom, metrics.viewportRectBottom()) && (metrics.pageRectBottom > 2 * metrics.getHeight()); Log.v(LOGTAG, "On pan/zoom stopped, short page: " + shortPage - + "; toolbarMostlyVisible: " + toolbarMostlyVisible + "; atBottomOfLongPage: " + atBottomOfLongPage); - if (shortPage || toolbarMostlyVisible || atBottomOfLongPage) { + if (shortPage || atBottomOfLongPage) { mDynamicToolbar.setVisible(true, VisibilityTransition.ANIMATE); - } else { - mDynamicToolbar.setVisible(false, VisibilityTransition.ANIMATE); } } diff --git a/mobile/android/base/GeckoAccessibility.java b/mobile/android/base/GeckoAccessibility.java index 5212d4ee89cb..1443dc558b8a 100644 --- a/mobile/android/base/GeckoAccessibility.java +++ b/mobile/android/base/GeckoAccessibility.java @@ -348,6 +348,8 @@ public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualDescendantId info.addAction(AccessibilityNodeInfo.ACTION_LONG_CLICK); info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY); info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY); + info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD); + info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH); @@ -388,6 +390,14 @@ public boolean performAction (int virtualViewId, int action, Bundle arguments) { GeckoAppShell. sendEventToGecko(GeckoEvent.createBroadcastEvent("Accessibility:LongPress", null)); return true; + } else if (action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD && virtualViewId == VIRTUAL_CURSOR_POSITION) { + GeckoAppShell. + sendEventToGecko(GeckoEvent.createBroadcastEvent("Accessibility:ScrollForward", null)); + return true; + } else if (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD && virtualViewId == VIRTUAL_CURSOR_POSITION) { + GeckoAppShell. + sendEventToGecko(GeckoEvent.createBroadcastEvent("Accessibility:ScrollBackward", null)); + return true; } else if (action == AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY && virtualViewId == VIRTUAL_CURSOR_POSITION) { // XXX: Self brailling gives this action with a bogus argument instead of an actual click action; diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index b68c0ba6de29..8e2f69dd5740 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -2391,12 +2391,20 @@ public static boolean isTablet() { return HardwareUtils.isTablet(); } + private static boolean sImeWasEnabledOnLastResize = false; public static void viewSizeChanged() { LayerView v = getLayerView(); - if (v != null && v.isIMEEnabled()) { + if (v == null) { + return; + } + boolean imeIsEnabled = v.isIMEEnabled(); + if (imeIsEnabled && !sImeWasEnabledOnLastResize) { + // The IME just came up after not being up, so let's scroll + // to the focused input. sendEventToGecko(GeckoEvent.createBroadcastEvent( "ScrollTo:FocusedInput", "")); } + sImeWasEnabledOnLastResize = imeIsEnabled; } @WrapForJNI(stubName = "GetCurrentNetworkInformationWrapper") diff --git a/mobile/android/base/gfx/DynamicToolbarAnimator.java b/mobile/android/base/gfx/DynamicToolbarAnimator.java index feb0e8848f94..e9c6ba883b6a 100644 --- a/mobile/android/base/gfx/DynamicToolbarAnimator.java +++ b/mobile/android/base/gfx/DynamicToolbarAnimator.java @@ -235,9 +235,17 @@ public void run() { } IntSize getViewportSize() { + ThreadUtils.assertOnUiThread(); + int viewWidth = mTarget.getView().getWidth(); int viewHeight = mTarget.getView().getHeight(); - int viewHeightVisible = viewHeight - Math.round(mMaxTranslation - mToolbarTranslation); + float toolbarTranslation = mToolbarTranslation; + if (mAnimationTask != null) { + // If we have an animation going, mToolbarTranslation may be in flux + // and we should use the final value it will settle on. + toolbarTranslation = mAnimationTask.getFinalToolbarTranslation(); + } + int viewHeightVisible = viewHeight - Math.round(mMaxTranslation - toolbarTranslation); return new IntSize(viewWidth, viewHeightVisible); } @@ -359,6 +367,15 @@ boolean onInterceptTouchEvent(MotionEvent event) { Log.v(LOGTAG, "Resetting touch sequence due to non-move"); mTouchStart = null; } + + if (event.getActionMasked() == MotionEvent.ACTION_UP) { + // We need to do this even if the toolbar is already fully + // visible or fully hidden, because this is what triggers the + // viewport resize in content and updates the viewport metrics. + boolean toolbarMostlyVisible = mToolbarTranslation < (mMaxTranslation / 2); + Log.v(LOGTAG, "All fingers lifted, completing " + (toolbarMostlyVisible ? "show" : "hide")); + animateToolbar(toolbarMostlyVisible, false); + } return false; } @@ -473,6 +490,10 @@ public DynamicToolbarAnimationTask(float aTranslation, boolean aImmediate, boole mShiftLayerView = aShiftLayerView; } + float getFinalToolbarTranslation() { + return mEndTranslation; + } + @Override public boolean internalRun(long timeDelta, long currentFrameStartTime) { if (!mContinueAnimation) { diff --git a/mobile/android/base/gfx/GLController.java b/mobile/android/base/gfx/GLController.java index 0c8f20264952..518320185331 100644 --- a/mobile/android/base/gfx/GLController.java +++ b/mobile/android/base/gfx/GLController.java @@ -272,6 +272,7 @@ void resumeCompositor(int width, int height) { if (mCompositorCreated) { GeckoAppShell.scheduleResumeComposition(width, height); GeckoAppShell.sendEventToGecko(GeckoEvent.createCompositorResumeEvent()); + mView.requestRender(); } } diff --git a/mobile/android/base/gfx/GeckoLayerClient.java b/mobile/android/base/gfx/GeckoLayerClient.java index effedc50b9e8..01083459f580 100644 --- a/mobile/android/base/gfx/GeckoLayerClient.java +++ b/mobile/android/base/gfx/GeckoLayerClient.java @@ -221,7 +221,7 @@ boolean setViewportSize(int width, int height, PointF scrollChange) { } mViewportMetrics = mViewportMetrics.setViewportSize(width, height); if (scrollChange != null) { - mViewportMetrics = mViewportMetrics.offsetViewportBy(scrollChange.x, scrollChange.y); + mViewportMetrics = mViewportMetrics.offsetViewportByAndClamp(scrollChange.x, scrollChange.y); } if (mGeckoIsReady) { diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 3eaa77bdd8b9..6992ca0e7dc9 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -552,6 +552,7 @@ pref("apz.fling_curve_threshold_inches_per_ms", "-1.0"); pref("apz.fling_friction", "0.002"); pref("apz.fling_stop_on_tap_threshold", "0.05"); pref("apz.fling_stopped_threshold", "0.01"); +pref("apz.highlight_checkerboarded_areas", false); pref("apz.max_velocity_inches_per_ms", "-1.0"); pref("apz.max_velocity_queue_size", 5); pref("apz.min_skate_speed", "1.0"); @@ -4524,12 +4525,13 @@ pref("dom.w3c_touch_events.enabled", 2); // W3C draft pointer events pref("dom.w3c_pointer_events.enabled", false); -// W3C touch-action css property (related to touch and pointer events) -pref("layout.css.touch_action.enabled", false); // W3C draft ImageCapture API pref("dom.imagecapture.enabled", false); +// W3C touch-action css property (related to touch and pointer events) +pref("layout.css.touch_action.enabled", false); + // Enables some assertions in nsStyleContext that are too expensive // for general use, but might be useful to enable for specific tests. // This only has an effect in DEBUG-builds. @@ -4967,7 +4969,7 @@ pref("browser.search.official", true); //pref("media.gmp-manager.url.override", ""); // Update service URL for GMP install/updates: -pref("media.gmp-manager.url", "https://aus4.mozilla.org/update/3/GMP/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml"); +pref("media.gmp-manager.url", "https://aus5.mozilla.org/update/3/GMP/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml"); // When |media.gmp-manager.cert.requireBuiltIn| is true or not specified the // final certificate and all certificates the connection is redirected to before @@ -4992,10 +4994,10 @@ pref("media.gmp-manager.cert.requireBuiltIn", true); // IMPORTANT! app.update.certs.* prefs should also be updated if these // are updated. pref("media.gmp-manager.cert.checkAttributes", true); -pref("media.gmp-manager.certs.1.issuerName", "CN=DigiCert Secure Server CA,O=DigiCert Inc,C=US"); -pref("media.gmp-manager.certs.1.commonName", "aus4.mozilla.org"); -pref("media.gmp-manager.certs.2.issuerName", "CN=Thawte SSL CA,O=\"Thawte, Inc.\",C=US"); -pref("media.gmp-manager.certs.2.commonName", "aus4.mozilla.org"); +pref("media.gmp-manager.certs.1.issuerName", "CN=DigiCert SHA2 Secure Server CA,O=DigiCert Inc,C=US"); +pref("media.gmp-manager.certs.1.commonName", "aus5.mozilla.org"); +pref("media.gmp-manager.certs.2.issuerName", "CN=thawte SSL CA - G2,O=thawte, Inc.,C=US"); +pref("media.gmp-manager.certs.2.commonName", "aus5.mozilla.org"); #endif // Whether or not to perform reader mode article parsing on page load. diff --git a/netwerk/base/Predictor.cpp b/netwerk/base/Predictor.cpp index 10efa21fedad..956de2aadea5 100644 --- a/netwerk/base/Predictor.cpp +++ b/netwerk/base/Predictor.cpp @@ -1515,7 +1515,7 @@ Predictor::SpaceCleaner::OnMetaDataElement(const char *key, const char *value) nsCString uri; nsresult rv = parsedURI->GetAsciiSpec(uri); uint32_t uriLength = uri.Length(); - if (ok && NS_SUCCEEDED(rv) && + if (NS_SUCCEEDED(rv) && uriLength > mPredictor->mMaxURILength) { // Default to getting rid of URIs that are too long and were put in before // we had our limit on URI length, in order to free up some space. @@ -1525,9 +1525,9 @@ Predictor::SpaceCleaner::OnMetaDataElement(const char *key, const char *value) return NS_OK; } - if (!ok || !mLRUKeyToDelete || lastHit < mLRUStamp) { + if (!mLRUKeyToDelete || lastHit < mLRUStamp) { mLRUKeyToDelete = key; - mLRUStamp = ok ? lastHit : 0; + mLRUStamp = lastHit; } return NS_OK; diff --git a/netwerk/base/nsChannelClassifier.cpp b/netwerk/base/nsChannelClassifier.cpp index cafe1805ef26..57dac1098d8d 100644 --- a/netwerk/base/nsChannelClassifier.cpp +++ b/netwerk/base/nsChannelClassifier.cpp @@ -620,8 +620,9 @@ nsChannelClassifier::OnClassifyComplete(nsresult aErrorCode) aErrorCode = NS_OK; } - LOG(("nsChannelClassifier[%p]:OnClassifyComplete %d", this, aErrorCode)); if (mSuspendedChannel) { + LOG(("nsChannelClassifier[%p]:OnClassifyComplete %d (suspended channel)", + this, aErrorCode)); MarkEntryClassified(aErrorCode); if (NS_FAILED(aErrorCode)) { diff --git a/netwerk/dns/nsHostResolver.cpp b/netwerk/dns/nsHostResolver.cpp index d06858b19c73..3689d4cc536a 100644 --- a/netwerk/dns/nsHostResolver.cpp +++ b/netwerk/dns/nsHostResolver.cpp @@ -522,13 +522,13 @@ nsHostResolver::nsHostResolver(uint32_t maxCacheEntries, , mDefaultGracePeriod(defaultGracePeriod) , mLock("nsHostResolver.mLock") , mIdleThreadCV(mLock, "nsHostResolver.mIdleThreadCV") + , mDB(&gHostDB_ops, sizeof(nsHostDBEnt), 0) + , mEvictionQSize(0) + , mShutdown(true) , mNumIdleThreads(0) , mThreadCount(0) , mActiveAnyThreadCount(0) - , mDB(&gHostDB_ops, sizeof(nsHostDBEnt), 0) - , mEvictionQSize(0) , mPendingCount(0) - , mShutdown(true) { mCreationTime = PR_Now(); PR_INIT_CLIST(&mHighQ); @@ -1070,10 +1070,10 @@ nsHostResolver::IssueLookup(nsHostRecord *rec) rv = ConditionallyCreateThread(rec); LOG ((" DNS thread counters: total=%d any-live=%d idle=%d pending=%d\n", - mThreadCount, - mActiveAnyThreadCount, - mNumIdleThreads, - mPendingCount)); + static_cast(mThreadCount), + static_cast(mActiveAnyThreadCount), + static_cast(mNumIdleThreads), + static_cast(mPendingCount))); return rv; } @@ -1111,7 +1111,7 @@ nsHostResolver::GetHostToLookup(nsHostRecord **result) { bool timedOut = false; PRIntervalTime epoch, now, timeout; - + MutexAutoLock lock(mLock); timeout = (mNumIdleThreads >= HighThreadThreshold) ? mShortIdleTimeout : mLongIdleTimeout; @@ -1179,7 +1179,6 @@ nsHostResolver::GetHostToLookup(nsHostRecord **result) } // tell thread to exit... - mThreadCount--; return false; } @@ -1411,24 +1410,31 @@ nsHostResolver::ThreadFunc(void *arg) getTtl); } #endif - TimeDuration elapsed = TimeStamp::Now() - startTime; - uint32_t millis = static_cast(elapsed.ToMilliseconds()); - - if (NS_SUCCEEDED(status)) { - Telemetry::ID histogramID; - if (!rec->addr_info_gencnt) { - // Time for initial lookup. - histogramID = Telemetry::DNS_LOOKUP_TIME; - } else if (!getTtl) { - // Time for renewal; categorized by expiration strategy. - histogramID = Telemetry::DNS_RENEWAL_TIME; - } else { - // Time to get TTL; categorized by expiration strategy. - histogramID = Telemetry::DNS_RENEWAL_TIME_FOR_TTL; + + { // obtain lock to check shutdown and manage inter-module telemetry + MutexAutoLock lock(resolver->mLock); + + if (!resolver->mShutdown) { + TimeDuration elapsed = TimeStamp::Now() - startTime; + uint32_t millis = static_cast(elapsed.ToMilliseconds()); + + if (NS_SUCCEEDED(status)) { + Telemetry::ID histogramID; + if (!rec->addr_info_gencnt) { + // Time for initial lookup. + histogramID = Telemetry::DNS_LOOKUP_TIME; + } else if (!getTtl) { + // Time for renewal; categorized by expiration strategy. + histogramID = Telemetry::DNS_RENEWAL_TIME; + } else { + // Time to get TTL; categorized by expiration strategy. + histogramID = Telemetry::DNS_RENEWAL_TIME_FOR_TTL; + } + Telemetry::Accumulate(histogramID, millis); + } else { + Telemetry::Accumulate(Telemetry::DNS_FAILED_LOOKUP_TIME, millis); + } } - Telemetry::Accumulate(histogramID, millis); - } else { - Telemetry::Accumulate(Telemetry::DNS_FAILED_LOOKUP_TIME, millis); } // OnLookupComplete may release "rec", long before we lose it. @@ -1444,6 +1450,7 @@ nsHostResolver::ThreadFunc(void *arg) rec = nullptr; } } + resolver->mThreadCount--; NS_RELEASE(resolver); LOG(("DNS lookup thread - queue empty, thread finished.\n")); } diff --git a/netwerk/dns/nsHostResolver.h b/netwerk/dns/nsHostResolver.h index 88b4d618fd6a..62ddbe473ce0 100644 --- a/netwerk/dns/nsHostResolver.h +++ b/netwerk/dns/nsHostResolver.h @@ -342,21 +342,22 @@ class nsHostResolver uint32_t mDefaultGracePeriod; // granularity seconds mutable Mutex mLock; // mutable so SizeOfIncludingThis can be const CondVar mIdleThreadCV; - uint32_t mNumIdleThreads; - uint32_t mThreadCount; - uint32_t mActiveAnyThreadCount; PLDHashTable mDB; PRCList mHighQ; PRCList mMediumQ; PRCList mLowQ; PRCList mEvictionQ; uint32_t mEvictionQSize; - uint32_t mPendingCount; PRTime mCreationTime; - bool mShutdown; PRIntervalTime mLongIdleTimeout; PRIntervalTime mShortIdleTimeout; + mozilla::Atomic mShutdown; + mozilla::Atomic mNumIdleThreads; + mozilla::Atomic mThreadCount; + mozilla::Atomic mActiveAnyThreadCount; + mozilla::Atomic mPendingCount; + // Set the expiration time stamps appropriately. void PrepareRecordExpiration(nsHostRecord* rec) const; diff --git a/netwerk/protocol/data/DataChannelChild.cpp b/netwerk/protocol/data/DataChannelChild.cpp index 3eea542900ab..ffb31b7ebd7b 100644 --- a/netwerk/protocol/data/DataChannelChild.cpp +++ b/netwerk/protocol/data/DataChannelChild.cpp @@ -40,7 +40,14 @@ NS_IMETHODIMP DataChannelChild::CompleteRedirectSetup(nsIStreamListener *aListener, nsISupports *aContext) { - nsresult rv = AsyncOpen(aListener, aContext); + nsresult rv; + if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) { + MOZ_ASSERT(!aContext, "aContext should be null!"); + rv = AsyncOpen2(aListener); + } + else { + rv = AsyncOpen(aListener, aContext); + } if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } diff --git a/netwerk/protocol/ftp/FTPChannelParent.cpp b/netwerk/protocol/ftp/FTPChannelParent.cpp index 45422dfce417..365bdc9130bb 100644 --- a/netwerk/protocol/ftp/FTPChannelParent.cpp +++ b/netwerk/protocol/ftp/FTPChannelParent.cpp @@ -188,10 +188,16 @@ FTPChannelParent::DoAsyncOpen(const URIParams& aURI, if (NS_FAILED(rv)) return SendFailedAsyncOpen(rv); - rv = ftpChan->AsyncOpen(this, nullptr); + if (loadInfo && loadInfo->GetEnforceSecurity()) { + rv = ftpChan->AsyncOpen2(this); + } + else { + rv = ftpChan->AsyncOpen(this, nullptr); + } + if (NS_FAILED(rv)) return SendFailedAsyncOpen(rv); - + return true; } diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index 29120bff15b8..d81cbf031efa 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -1307,6 +1307,10 @@ HttpChannelChild::CompleteRedirectSetup(nsIStreamListener *listener, // fresh - we will intercept the child channel this time, before creating a new // parent channel unnecessarily. PHttpChannelChild::Send__delete__(this); + if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) { + MOZ_ASSERT(!aContext, "aContext should be null!"); + return AsyncOpen2(listener); + } return AsyncOpen(listener, aContext); } diff --git a/netwerk/protocol/http/HttpChannelParent.cpp b/netwerk/protocol/http/HttpChannelParent.cpp index 739abd54a99f..fd6b5e58aa03 100644 --- a/netwerk/protocol/http/HttpChannelParent.cpp +++ b/netwerk/protocol/http/HttpChannelParent.cpp @@ -476,7 +476,12 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI, schedulingContextID.Parse(aSchedulingContextID.BeginReading()); mChannel->SetSchedulingContextID(schedulingContextID); - rv = mChannel->AsyncOpen(mParentListener, nullptr); + if (loadInfo && loadInfo->GetEnforceSecurity()) { + rv = mChannel->AsyncOpen2(mParentListener); + } + else { + rv = mChannel->AsyncOpen(mParentListener, nullptr); + } if (NS_FAILED(rv)) return SendFailedAsyncOpen(rv); diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 8e2bbb23dc63..81d612840dfd 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -1224,6 +1224,9 @@ GetPKPConsoleErrorTag(uint32_t failureResult, nsAString& consoleErrorTag) case nsISiteSecurityService::ERROR_COULD_NOT_SAVE_STATE: consoleErrorTag = NS_LITERAL_STRING("PKPCouldNotSaveState"); break; + case nsISiteSecurityService::ERROR_ROOT_NOT_BUILT_IN: + consoleErrorTag = NS_LITERAL_STRING("PKPRootNotBuiltIn"); + break; default: consoleErrorTag = NS_LITERAL_STRING("PKPUnknownError"); break; diff --git a/netwerk/protocol/rtsp/RtspChannelChild.cpp b/netwerk/protocol/rtsp/RtspChannelChild.cpp index f6c8f7904efa..c38ee5f95f8e 100644 --- a/netwerk/protocol/rtsp/RtspChannelChild.cpp +++ b/netwerk/protocol/rtsp/RtspChannelChild.cpp @@ -280,6 +280,10 @@ NS_IMETHODIMP RtspChannelChild::CompleteRedirectSetup(nsIStreamListener *aListener, nsISupports *aContext) { + if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) { + MOZ_ASSERT(!aContext, "aContext should be null!"); + return AsyncOpen2(aListener); + } return AsyncOpen(aListener, aContext); } diff --git a/netwerk/protocol/wyciwyg/WyciwygChannelParent.cpp b/netwerk/protocol/wyciwyg/WyciwygChannelParent.cpp index 1e5fb6f527e3..3cc8af4f8c5d 100644 --- a/netwerk/protocol/wyciwyg/WyciwygChannelParent.cpp +++ b/netwerk/protocol/wyciwyg/WyciwygChannelParent.cpp @@ -186,7 +186,14 @@ WyciwygChannelParent::RecvAsyncOpen(const URIParams& aOriginal, if (NS_FAILED(rv)) return SendCancelEarly(rv); - rv = mChannel->AsyncOpen(this, nullptr); + nsCOMPtr loadInfo = mChannel->GetLoadInfo(); + if (loadInfo && loadInfo->GetEnforceSecurity()) { + rv = mChannel->AsyncOpen2(this); + } + else { + rv = mChannel->AsyncOpen(this, nullptr); + } + if (NS_FAILED(rv)) return SendCancelEarly(rv); diff --git a/netwerk/sctp/datachannel/DataChannel.cpp b/netwerk/sctp/datachannel/DataChannel.cpp index 5a649381383e..7b23114ae12c 100644 --- a/netwerk/sctp/datachannel/DataChannel.cpp +++ b/netwerk/sctp/datachannel/DataChannel.cpp @@ -599,6 +599,32 @@ DataChannelConnection::CompleteConnect(TransportFlow *flow, TransportLayer::Stat LOG(("Calling usrsctp_connect")); r = usrsctp_connect(mMasterSocket, reinterpret_cast(&addr), sizeof(addr)); + if (r >= 0 || errno == EINPROGRESS) { + struct sctp_paddrparams paddrparams; + socklen_t opt_len; + + memset(&paddrparams, 0, sizeof(struct sctp_paddrparams)); + memcpy(&paddrparams.spp_address, &addr, sizeof(struct sockaddr_conn)); + opt_len = (socklen_t)sizeof(struct sctp_paddrparams); + r = usrsctp_getsockopt(mMasterSocket, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, + &paddrparams, &opt_len); + if (r < 0) { + LOG(("usrsctp_getsockopt failed: %d", r)); + } else { + // draft-ietf-rtcweb-data-channel-13 section 5: max initial MTU IPV4 1200, IPV6 1280 + paddrparams.spp_pathmtu = 1200; // safe for either + paddrparams.spp_flags &= !SPP_PMTUD_ENABLE; + paddrparams.spp_flags |= SPP_PMTUD_DISABLE; + opt_len = (socklen_t)sizeof(struct sctp_paddrparams); + r = usrsctp_setsockopt(mMasterSocket, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, + &paddrparams, opt_len); + if (r < 0) { + LOG(("usrsctp_getsockopt failed: %d", r)); + } else { + LOG(("usrsctp: PMTUD disabled, MTU set to %u", paddrparams.spp_pathmtu)); + } + } + } if (r < 0) { if (errno == EINPROGRESS) { // non-blocking diff --git a/python/mozbuild/mozbuild/mozinfo.py b/python/mozbuild/mozbuild/mozinfo.py index 0e59e8ffa495..7854b7726c9f 100755 --- a/python/mozbuild/mozbuild/mozinfo.py +++ b/python/mozbuild/mozbuild/mozinfo.py @@ -90,6 +90,7 @@ def build_dict(config, env=os.environ): d['telemetry'] = substs.get('MOZ_TELEMETRY_REPORTING') == '1' d['tests_enabled'] = substs.get('ENABLE_TESTS') == "1" d['bin_suffix'] = substs.get('BIN_SUFFIX', '') + d['addon_signing'] = substs.get('MOZ_ADDON_SIGNING') == '1' d['webm'] = bool(substs.get('MOZ_WEBM')) d['wave'] = bool(substs.get('MOZ_WAVE')) diff --git a/python/psutil/CREDITS b/python/psutil/CREDITS index 29027150b126..170751b0a3b0 100644 --- a/python/psutil/CREDITS +++ b/python/psutil/CREDITS @@ -255,9 +255,56 @@ I: 492 N: Jeff Tang W: https://github.com/mrjefftang -I: 340, 529 +I: 340, 529, 616, 653, 654 N: Yaolong Huang E: airekans@gmail.com W: http://airekans.github.io/ I: 530 + +N: Anders Chrigström +W: https://github.com/anders-chrigstrom +I: 496 + +N: spacewander +E: spacewanderlzx@gmail.com +I: 561 + +N: Sylvain Mouquet +E: sylvain.mouquet@gmail.com +I: 565 + +N: karthikrev +I: 568 + +N: Bruno Binet +E: bruno.binet@gmail.com +I: 572 + +N: Gabi Davar +C: Israel +W: https://github.com/mindw +I: 578, 581, 587 + +N: spacewanderlzx +C: Guangzhou,China +E: spacewanderlzx@gmail.com +I: 555 + +N: Fabian Groffen +I: 611, 618 + +N: desbma +W: https://github.com/desbma +C: France +I: 628 + +N: John Burnett +W: http://www.johnburnett.com/ +C: Irvine, CA, US +I: 614 + +N: Árni Már Jónsson +E: Reykjavik, Iceland +E: https://github.com/arnimarj +I: 634 diff --git a/python/psutil/HISTORY.rst b/python/psutil/HISTORY.rst index 65309c1ba197..12b985d1e93f 100644 --- a/python/psutil/HISTORY.rst +++ b/python/psutil/HISTORY.rst @@ -1,10 +1,143 @@ Bug tracker at https://github.com/giampaolo/psutil/issues -2.1.3 2014-09-26 -================ +3.1.1 - 2015-07-15 +================== + +**Bug fixes** + +- #645: [Linux] psutil.cpu_times_percent() may produce negative results. +- #656: 'from psutil import *' does not work. + + +3.1.0 - 2015-07-15 +================== + +**Enhancements** + +- #534: [Linux] disk_partitions() added support for ZFS filesystems. +- #646: continuous tests integration for Windows with + https://ci.appveyor.com/project/giampaolo/psutil. +- #647: new dev guide: + https://github.com/giampaolo/psutil/blob/master/DEVGUIDE.rst +- #651: continuous code quality test integration with + https://scrutinizer-ci.com/g/giampaolo/psutil/ + +**Bug fixes** + +- #340: [Windows] Process.open_files() no longer hangs. Instead it uses a + thred which times out and skips the file handle in case it's taking too long + to be retrieved. (patch by Jeff Tang, PR #597) +- #627: [Windows] Process.name() no longer raises AccessDenied for pids owned + by another user. +- #636: [Windows] Process.memory_info() raise AccessDenied. +- #637: [UNIX] raise exception if trying to send signal to Process PID 0 as it + will affect os.getpid()'s process group instead of PID 0. +- #639: [Linux] Process.cmdline() can be truncated. +- #640: [Linux] *connections functions may swallow errors and return an + incomplete list of connnections. +- #642: repr() of exceptions is incorrect. +- #653: [Windows] Add inet_ntop function for Windows XP to support IPv6. +- #641: [Windows] Replace deprecated string functions with safe equivalents. + + +3.0.1 - 2015-06-18 +================== + +**Bug fixes** + +- #632: [Linux] better error message if cannot parse process UNIX connections. +- #634: [Linux] Proces.cmdline() does not include empty string arguments. +- #635: [UNIX] crash on module import if 'enum' package is installed on python + < 3.4. + + +3.0.0 - 2015-06-13 +================== + +**Enhancements** + +- #250: new psutil.net_if_stats() returning NIC statistics (isup, duplex, + speed, MTU). +- #376: new psutil.net_if_addrs() returning all NIC addresses a-la ifconfig. +- #469: on Python >= 3.4 ``IOPRIO_CLASS_*`` and ``*_PRIORITY_CLASS`` constants + returned by psutil.Process' ionice() and nice() methods are enums instead of + plain integers. +- #581: add .gitignore. (patch by Gabi Davar) +- #582: connection constants returned by psutil.net_connections() and + psutil.Process.connections() were turned from int to enums on Python > 3.4. +- #587: Move native extension into the package. +- #589: Process.cpu_affinity() accepts any kind of iterable (set, tuple, ...), + not only lists. +- #594: all deprecated APIs were removed. +- #599: [Windows] process name() can now be determined for all processes even + when running as a limited user. +- #602: pre-commit GIT hook. +- #629: enhanced support for py.test and nose test discovery and tests run. +- #616: [Windows] Add inet_ntop function for Windows XP. + +**Bug fixes** + +- #428: [all UNIXes except Linux] correct handling of zombie processes; + introduced new ZombieProcess exception class. +- #512: [BSD] fix segfault in net_connections(). +- #555: [Linux] psutil.users() correctly handles ":0" as an alias for + "localhost" +- #579: [Windows] Fixed open_files() for PID>64K. +- #579: [Windows] fixed many compiler warnings. +- #585: [FreeBSD] net_connections() may raise KeyError. +- #586: [FreeBSD] cpu_affinity() segfaults on set in case an invalid CPU + number is provided. +- #593: [FreeBSD] Process().memory_maps() segfaults. +- #606: Process.parent() may swallow NoSuchProcess exceptions. +- #611: [SunOS] net_io_counters has send and received swapped +- #614: [Linux]: cpu_count(logical=False) return the number of physical CPUs + instead of physical cores. +- #618: [SunOS] swap tests fail on Solaris when run as normal user +- #628: [Linux] Process.name() truncates process name in case it contains + spaces or parentheses. + + +2.2.1 - 2015-02-02 +================== **Bug fixes** +- #496: [Linux] fix "ValueError: ambiguos inode with multiple PIDs references" + (patch by Bruno Binet) + + +2.2.0 - 2015-01-06 +================== + +**Enhancements** + +- #521: drop support for Python 2.4 and 2.5. +- #553: new examples/pstree.py script. +- #564: C extension version mismatch in case the user messed up with psutil + installation or with sys.path is now detected at import time. +- #568: New examples/pidof.py script. +- #569: [FreeBSD] add support for process CPU affinity. + +**Bug fixes** + +- #496: [Solaris] can't import psutil. +- #547: [UNIX] Process.username() may raise KeyError if UID can't be resolved. +- #551: [Windows] get rid of the unicode hack for net_io_counters() NIC names. +- #556: [Linux] lots of file handles were left open. +- #561: [Linux] net_connections() might skip some legitimate UNIX sockets. + (patch by spacewander) +- #565: [Windows] use proper encoding for psutil.Process.username() and + psutil.users(). (patch by Sylvain Mouquet) +- #567: [Linux] in the alternative implementation of CPU affinity PyList_Append + and Py_BuildValue return values are not checked. +- #569: [FreeBSD] fix memory leak in psutil.cpu_count(logical=False). +- #571: [Linux] Process.open_files() might swallow AccessDenied exceptions and + return an incomplete list of open files. + + +2.1.3 - 2014-09-26 +================== + - #536: [Linux]: fix "undefined symbol: CPU_ALLOC" compilation error. diff --git a/python/psutil/INSTALL.rst b/python/psutil/INSTALL.rst index 93ce46919746..e518c430ee4d 100644 --- a/python/psutil/INSTALL.rst +++ b/python/psutil/INSTALL.rst @@ -4,12 +4,12 @@ Installing using pip on UNIX The easiest way to install psutil on UNIX is by using pip (but first you might need to install python header files; see later). -First install pip: +First install pip:: - $ wget https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py - python ez_setup.py + $ wget https://bootstrap.pypa.io/get-pip.py + $ python get-pip.py -...then run: +...then run:: $ pip install psutil @@ -22,51 +22,27 @@ Installing on Windows Just get the right installer for your Python version and architecture from: https://pypi.python.org/pypi/psutil/#downloads - - -================================== -Compiling on Windows using mingw32 -================================== - -First install mingw (http://www.mingw.org/) then add mingw "bin" folder to -environment PATH (NOTE: this assumes MinGW is installed in C:\MinGW): - - SET PATH=C:\MinGW\bin;%PATH% - -You can then compile psutil by running: - - setup.py build -c mingw32 - -To compile and install: - - setup.py build -c mingw32 install - -You can also use make.bat which automatically sets the env variable for you: - - make.bat build - -FWIW I managed to compile psutil against all 32-bit Python versions but not -64 bit. +Since wheels installers are also available you may also use pip. ======================================== Compiling on Windows using Visual Studio ======================================== -To use Visual Studio to compile psutil you must have the same version of -Visual Studio used to compile your installation of Python which is:: +In order to compile psutil on Windows you'll need Visual Studio (Mingw32 is +no longer supported). You must have the same version of Visual Studio used to compile +your installation of Python, that is:: - Python 2.4: VS 2003 - Python 2.5: VS 2003 - Python 2.6: VS 2008 - Python 2.7: VS 2008 - Python 3.3+: VS 2010 +* Python 2.6: VS 2008 +* Python 2.7: VS 2008 +* Python 3.3, 3.4: VS 2010 (you can download it from `MS website `_) +* Python 3.5: `VS 2015 UP `_ -...then run: +...then run:: setup.py build -...or: +...or:: make.bat build @@ -75,24 +51,20 @@ Windows SDK and .NET Framework 3.5 SP1 to be installed first. Once you have those run vcvars64.bat, then compile: http://stackoverflow.com/questions/11072521/ -If you do not have the right version of Visual Studio available then try using -MinGW instead. - - =================== Installing on Linux =================== gcc is required and so the python headers. They can easily be installed by -using the distro package manager. For example, on Debian amd Ubuntu: +using the distro package manager. For example, on Debian and Ubuntu:: $ sudo apt-get install gcc python-dev -...on Redhat and CentOS: +...on Redhat and CentOS:: $ sudo yum install gcc python-devel -Once done, you can build/install psutil with: +Once done, you can build/install psutil with:: $ python setup.py install @@ -104,11 +76,11 @@ Installing on OS X OS X installation from source will require gcc which you can obtain as part of the 'XcodeTools' installer from Apple. Then you can run the standard distutils commands. -To build only: +To build only:: $ python setup.py build -To install and build: +To install and build:: $ python setup.py install @@ -121,11 +93,11 @@ The same compiler used to install Python must be present on the system in order to build modules using distutils. Assuming it is installed, you can build using the standard distutils commands. -Build only: +Build only:: $ python setup.py build -Install and build: +Install and build:: $ python setup.py install @@ -138,7 +110,7 @@ A makefile is available for both UNIX and Windows (make.bat). It provides some automations for the tasks described above and might be preferred over using setup.py. With it you can:: - $ make install # just install + $ make install # just install (in --user mode) $ make uninstall # uninstall (needs pip) $ make test # run tests $ make clean # remove installation files diff --git a/python/psutil/MANIFEST.in b/python/psutil/MANIFEST.in index b275f613b381..d807be289d54 100644 --- a/python/psutil/MANIFEST.in +++ b/python/psutil/MANIFEST.in @@ -1,3 +1,7 @@ +include .coveragerc +include .git-pre-commit +include .git-pre-commit +include .gitignore include .travis.yml include CREDITS include HISTORY.rst @@ -10,8 +14,9 @@ include README.rst include setup.py include TODO include tox.ini -recursive-include docs * recursive-exclude docs/_build * +recursive-include .appveyor/* +recursive-include docs * recursive-include examples *.py recursive-include psutil *.py *.c *.h -recursive-include test *.py README \ No newline at end of file +recursive-include test *.py README* diff --git a/python/psutil/Makefile b/python/psutil/Makefile index 9b7949535054..1e4eb4b01ac5 100644 --- a/python/psutil/Makefile +++ b/python/psutil/Makefile @@ -16,31 +16,49 @@ clean: rm -f `find . -type f -name \*.bak` rm -f `find . -type f -name \*.rej` rm -rf `find . -type d -name __pycache__` + rm -rf *.core rm -rf *.egg-info rm -rf *\$testfile* + rm -rf .coverage rm -rf .tox rm -rf build rm -rf dist rm -rf docs/_build + rm -rf htmlcov build: clean $(PYTHON) setup.py build + @# copies *.so files in ./psutil directory in order to allow + @# "import psutil" when using the interactive interpreter from within + @# this directory. + $(PYTHON) setup.py build_ext -i + +# useful deps which are nice to have while developing / testing +setup-dev-env: + python -c "import urllib2; \ + r = urllib2.urlopen('https://bootstrap.pypa.io/get-pip.py'); \ + open('/tmp/get-pip.py', 'w').write(r.read());" + $(PYTHON) /tmp/get-pip.py --user + rm /tmp/get-pip.py + $(PYTHON) -m pip install --user --upgrade pip + $(PYTHON) -m pip install --user --upgrade \ + coverage \ + flake8 \ + ipaddress \ + ipdb \ + mock==1.0.1 \ + nose \ + pep8 \ + pyflakes \ + sphinx \ + sphinx-pypi-upload \ + unittest2 \ install: build - if test $(PYTHON) = python2.4; then \ - $(PYTHON) setup.py install; \ - elif test $(PYTHON) = python2.5; then \ - $(PYTHON) setup.py install; \ - else \ - $(PYTHON) setup.py install --user; \ - fi + $(PYTHON) setup.py install --user uninstall: - if test $(PYTHON) = python2.4; then \ - pip-2.4 uninstall -y -v psutil; \ - else \ - cd ..; $(PYTHON) -m pip uninstall -y -v psutil; \ - fi + cd ..; $(PYTHON) -m pip uninstall -y -v psutil test: install $(PYTHON) $(TSCRIPT) @@ -52,27 +70,36 @@ test-system: install $(PYTHON) -m unittest -v test.test_psutil.TestSystemAPIs test-memleaks: install - $(PYTHON) -m unittest -v test.test_memory_leaks + $(PYTHON) test/test_memory_leaks.py # Run a specific test by name; e.g. "make test-by-name disk_" will run # all test methods containing "disk_" in their name. # Requires "pip install nose". -test-by-name: - @$(PYTHON) -m nose test/test_psutil.py --nocapture -v -m $(filter-out $@,$(MAKECMDGOALS)) +test-by-name: install + @$(PYTHON) -m nose test/test_psutil.py test/_* --nocapture -v -m $(filter-out $@,$(MAKECMDGOALS)) + +# Same as above but for test_memory_leaks.py script. +test-memleaks-by-name: install + @$(PYTHON) -m nose test/test_memory_leaks.py --nocapture -v -m $(filter-out $@,$(MAKECMDGOALS)) + +coverage: install + # Note: coverage options are controlled by .coveragerc file + rm -rf .coverage htmlcov + $(PYTHON) -m coverage run $(TSCRIPT) + $(PYTHON) -m coverage report + @echo "writing results to htmlcov/index.html" + $(PYTHON) -m coverage html + $(PYTHON) -m webbrowser -t htmlcov/index.html -# requires "pip install pep8" pep8: - @git ls-files | grep \\.py$ | xargs pep8 + @git ls-files | grep \\.py$ | xargs $(PYTHON) -m pep8 -# requires "pip install pyflakes" pyflakes: @export PYFLAKES_NODOCTEST=1 && \ - git ls-files | grep \\.py$ | xargs pyflakes + git ls-files | grep \\.py$ | xargs $(PYTHON) -m pyflakes -# requires "pip install flake8" flake8: - @git ls-files | grep \\.py$ | xargs flake8 - + @git ls-files | grep \\.py$ | xargs $(PYTHON) -m flake8 # Upload source tarball on https://pypi.python.org/pypi/psutil. upload-src: clean @@ -88,3 +115,8 @@ upload-doc: git-tag-release: git tag -a release-`python -c "import setup; print(setup.get_version())"` -m `git rev-list HEAD --count`:`git rev-parse --short HEAD` echo "done; now run 'git push --follow-tags' to push the new tag on the remote repo" + +# install GIT pre-commit hook +install-git-hooks: + ln -sf ../../.git-pre-commit .git/hooks/pre-commit + chmod +x .git/hooks/pre-commit diff --git a/python/psutil/PKG-INFO b/python/psutil/PKG-INFO index f505977ea812..e74d33f6587d 100644 --- a/python/psutil/PKG-INFO +++ b/python/psutil/PKG-INFO @@ -1,36 +1,54 @@ Metadata-Version: 1.1 Name: psutil -Version: 2.1.3 +Version: 3.1.1 Summary: psutil is a cross-platform library for retrieving information onrunning processes and system utilization (CPU, memory, disks, network)in Python. Home-page: https://github.com/giampaolo/psutil Author: Giampaolo Rodola Author-email: g.rodola gmail com License: BSD -Description: .. image:: https://pypip.in/d/psutil/badge.png - :target: https://crate.io/packages/psutil/ - :alt: Download this month +Description: .. image:: https://img.shields.io/pypi/dm/psutil.svg + :target: https://pypi.python.org/pypi/psutil#downloads + :alt: Downloads this month - .. image:: https://pypip.in/v/psutil/badge.png + .. image:: https://api.travis-ci.org/giampaolo/psutil.png?branch=master + :target: https://travis-ci.org/giampaolo/psutil + :alt: Linux tests (Travis) + + .. image:: https://ci.appveyor.com/api/projects/status/qdwvw7v1t915ywr5/branch/master?svg=true + :target: https://ci.appveyor.com/project/giampaolo/psutil + :alt: Windows tests (Appveyor) + + .. image:: https://coveralls.io/repos/giampaolo/psutil/badge.svg?branch=master&service=github + :target: https://coveralls.io/github/giampaolo/psutil?branch=master + :alt: Test coverage (coverall.io) + + .. image:: https://img.shields.io/pypi/v/psutil.svg :target: https://pypi.python.org/pypi/psutil/ :alt: Latest version - .. image:: https://pypip.in/license/psutil/badge.png + .. image:: https://img.shields.io/github/stars/giampaolo/psutil.svg + :target: https://github.com/giampaolo/psutil/ + :alt: Github stars + + .. image:: https://img.shields.io/scrutinizer/g/giampaolo/psutil.svg + :target: https://scrutinizer-ci.com/g/giampaolo/psutil/ + :alt: Code quality (scrutinizer-ci.com) + + .. image:: https://img.shields.io/pypi/l/psutil.svg :target: https://pypi.python.org/pypi/psutil/ :alt: License - .. image:: https://api.travis-ci.org/giampaolo/psutil.png?branch=master - :target: https://travis-ci.org/giampaolo/psutil - :alt: Travis - =========== Quick links =========== - `Home page `_ - `Documentation `_ + - `Installation `_ - `Download `_ - `Forum `_ - `Blog `_ + - `Development guide `_ - `What's new `_ ======= @@ -45,8 +63,9 @@ Description: .. image:: https://pypip.in/d/psutil/badge.png tools such as: ps, top, lsof, netstat, ifconfig, who, df, kill, free, nice, ionice, iostat, iotop, uptime, pidof, tty, taskset, pmap. It currently supports **Linux, Windows, OSX, FreeBSD** and **Sun Solaris**, both **32-bit** and - **64-bit** architectures, with Python versions from **2.4 to 3.4**. PyPy is - also known to work. + **64-bit** architectures, with Python versions from **2.6 to 3.5** (users of + Python 2.4 and 2.5 may use `2.1.3 `__ version). + `PyPy `__ is also known to work. ==================== Example applications @@ -67,8 +86,8 @@ Description: .. image:: https://pypip.in/d/psutil/badge.png See also: * https://github.com/nicolargo/glances + * https://github.com/google/grr * https://github.com/Jahaja/psdash - * https://code.google.com/p/grr/ ============== Example usages @@ -117,9 +136,9 @@ Description: .. image:: https://pypip.in/d/psutil/badge.png .. code-block:: python >>> psutil.virtual_memory() - svmem(total=8374149120L, available=2081050624L, percent=75.1, used=8074080256L, free=300068864L, active=3294920704, inactive=1361616896, buffers=529895424L, cached=1251086336) + svmem(total=8374149120, available=2081050624, percent=75.1, used=8074080256, free=300068864, active=3294920704, inactive=1361616896, buffers=529895424, cached=1251086336) >>> psutil.swap_memory() - sswap(total=2097147904L, used=296128512L, free=1801019392L, percent=14.1, sin=304193536, sout=677842944) + sswap(total=2097147904, used=296128512, free=1801019392, percent=14.1, sin=304193536, sout=677842944) >>> Disks @@ -148,11 +167,23 @@ Description: .. image:: https://pypip.in/d/psutil/badge.png 'lo': netio(bytes_sent=2838627, bytes_recv=2838627, packets_sent=30567, packets_recv=30567, errin=0, errout=0, dropin=0, dropout=0)} >>> >>> psutil.net_connections() - [pconn(fd=115, family=2, type=1, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED', pid=1254), - pconn(fd=117, family=2, type=1, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING', pid=2987), - pconn(fd=-1, family=2, type=1, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED', pid=None), - pconn(fd=-1, family=2, type=1, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT', pid=None) + [pconn(fd=115, family=, type=, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED', pid=1254), + pconn(fd=117, family=, type=, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING', pid=2987), + pconn(fd=-1, family=, type=, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED', pid=None), + pconn(fd=-1, family=, type=, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT', pid=None) ...] + >>> + >>> psutil.net_if_addrs() + {'lo': [snic(family=, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1'), + snic(family=, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None), + snic(family=, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00')], + 'wlan0': [snic(family=, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255'), + snic(family=, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None), + snic(family=, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff')]} + >>> + >>> psutil.net_if_stats() + {'eth0': snicstats(isup=True, duplex=, speed=100, mtu=1500), + 'lo': snicstats(isup=True, duplex=, speed=0, mtu=65536)} Other system info ================= @@ -218,7 +249,7 @@ Description: .. image:: https://pypip.in/d/psutil/badge.png >>> >>> p.memory_info() pmem(rss=7471104, vms=68513792) - >>> p.ext_memory_info() + >>> p.memory_info_ex() extmem(rss=9662464, vms=49192960, shared=3612672, text=2564096, lib=0, data=5754880, dirty=0) >>> p.memory_maps() [pmmap_grouped(path='/lib/x86_64-linux-gnu/libutil-2.15.so', rss=16384, anonymous=8192, swap=0), @@ -235,10 +266,10 @@ Description: .. image:: https://pypip.in/d/psutil/badge.png [popenfile(path='/home/giampaolo/svn/psutil/somefile', fd=3)] >>> >>> p.connections() - [pconn(fd=115, family=2, type=1, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED'), - pconn(fd=117, family=2, type=1, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING'), - pconn(fd=119, family=2, type=1, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED'), - pconn(fd=123, family=2, type=1, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT')] + [pconn(fd=115, family=, type=, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED'), + pconn(fd=117, family=, type=, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING'), + pconn(fd=119, family=, type=, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED'), + pconn(fd=123, family=, type=, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT')] >>> >>> p.num_threads() 4 @@ -259,7 +290,7 @@ Description: .. image:: https://pypip.in/d/psutil/badge.png >>> >>> p.ionice(psutil.IOPRIO_CLASS_IDLE) # IO priority (Win and Linux only) >>> p.ionice() - pionice(ioclass=3, value=0) + pionice(ioclass=, value=0) >>> >>> p.rlimit(psutil.RLIMIT_NOFILE, (5, 5)) # set resource limits (Linux only) >>> p.rlimit(psutil.RLIMIT_NOFILE) @@ -327,6 +358,12 @@ Description: .. image:: https://pypip.in/d/psutil/badge.png Timeline ======== + - 2015-07-15: `psutil-3.1.1.tar.gz `_ + - 2015-07-15: `psutil-3.1.0.tar.gz `_ + - 2015-06-18: `psutil-3.0.1.tar.gz `_ + - 2015-06-13: `psutil-3.0.0.tar.gz `_ + - 2015-02-02: `psutil-2.2.1.tar.gz `_ + - 2015-01-06: `psutil-2.2.0.tar.gz `_ - 2014-09-26: `psutil-2.1.3.tar.gz `_ - 2014-09-21: `psutil-2.1.2.tar.gz `_ - 2014-04-30: `psutil-2.1.1.tar.gz `_ @@ -356,7 +393,7 @@ Description: .. image:: https://pypip.in/d/psutil/badge.png - 2009-03-06: `psutil-0.1.1.tar.gz `_ - 2009-01-27: `psutil-0.1.0.tar.gz `_ -Keywords: ps,top,kill,free,lsof,netstat,nice,tty,ionice,uptime,taskmgr,process,df,iotop,iostat,ifconfig,taskset,who,pidof,pmap,smem,monitoring,ulimit,prlimit +Keywords: ps,top,kill,free,lsof,netstat,nice,tty,ionice,uptime,taskmgr,process,df,iotop,iostat,ifconfig,taskset,who,pidof,pmap,smem,pstree,monitoring,ulimit,prlimit Platform: Platform Independent Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console @@ -375,8 +412,6 @@ Classifier: Operating System :: POSIX :: SunOS/Solaris Classifier: Operating System :: POSIX Classifier: Programming Language :: C Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.4 -Classifier: Programming Language :: Python :: 2.5 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 diff --git a/python/psutil/README.rst b/python/psutil/README.rst index f0926851197d..564656146c42 100644 --- a/python/psutil/README.rst +++ b/python/psutil/README.rst @@ -1,28 +1,46 @@ -.. image:: https://pypip.in/d/psutil/badge.png - :target: https://crate.io/packages/psutil/ - :alt: Download this month +.. image:: https://img.shields.io/pypi/dm/psutil.svg + :target: https://pypi.python.org/pypi/psutil#downloads + :alt: Downloads this month -.. image:: https://pypip.in/v/psutil/badge.png +.. image:: https://api.travis-ci.org/giampaolo/psutil.png?branch=master + :target: https://travis-ci.org/giampaolo/psutil + :alt: Linux tests (Travis) + +.. image:: https://ci.appveyor.com/api/projects/status/qdwvw7v1t915ywr5/branch/master?svg=true + :target: https://ci.appveyor.com/project/giampaolo/psutil + :alt: Windows tests (Appveyor) + +.. image:: https://coveralls.io/repos/giampaolo/psutil/badge.svg?branch=master&service=github + :target: https://coveralls.io/github/giampaolo/psutil?branch=master + :alt: Test coverage (coverall.io) + +.. image:: https://img.shields.io/pypi/v/psutil.svg :target: https://pypi.python.org/pypi/psutil/ :alt: Latest version -.. image:: https://pypip.in/license/psutil/badge.png +.. image:: https://img.shields.io/github/stars/giampaolo/psutil.svg + :target: https://github.com/giampaolo/psutil/ + :alt: Github stars + +.. image:: https://img.shields.io/scrutinizer/g/giampaolo/psutil.svg + :target: https://scrutinizer-ci.com/g/giampaolo/psutil/ + :alt: Code quality (scrutinizer-ci.com) + +.. image:: https://img.shields.io/pypi/l/psutil.svg :target: https://pypi.python.org/pypi/psutil/ :alt: License -.. image:: https://api.travis-ci.org/giampaolo/psutil.png?branch=master - :target: https://travis-ci.org/giampaolo/psutil - :alt: Travis - =========== Quick links =========== - `Home page `_ - `Documentation `_ +- `Installation `_ - `Download `_ - `Forum `_ - `Blog `_ +- `Development guide `_ - `What's new `_ ======= @@ -37,8 +55,9 @@ running processes**. It implements many functionalities offered by command line tools such as: ps, top, lsof, netstat, ifconfig, who, df, kill, free, nice, ionice, iostat, iotop, uptime, pidof, tty, taskset, pmap. It currently supports **Linux, Windows, OSX, FreeBSD** and **Sun Solaris**, both **32-bit** and -**64-bit** architectures, with Python versions from **2.4 to 3.4**. PyPy is -also known to work. +**64-bit** architectures, with Python versions from **2.6 to 3.5** (users of +Python 2.4 and 2.5 may use `2.1.3 `__ version). +`PyPy `__ is also known to work. ==================== Example applications @@ -59,8 +78,8 @@ Example applications See also: * https://github.com/nicolargo/glances + * https://github.com/google/grr * https://github.com/Jahaja/psdash - * https://code.google.com/p/grr/ ============== Example usages @@ -109,9 +128,9 @@ Memory .. code-block:: python >>> psutil.virtual_memory() - svmem(total=8374149120L, available=2081050624L, percent=75.1, used=8074080256L, free=300068864L, active=3294920704, inactive=1361616896, buffers=529895424L, cached=1251086336) + svmem(total=8374149120, available=2081050624, percent=75.1, used=8074080256, free=300068864, active=3294920704, inactive=1361616896, buffers=529895424, cached=1251086336) >>> psutil.swap_memory() - sswap(total=2097147904L, used=296128512L, free=1801019392L, percent=14.1, sin=304193536, sout=677842944) + sswap(total=2097147904, used=296128512, free=1801019392, percent=14.1, sin=304193536, sout=677842944) >>> Disks @@ -140,11 +159,23 @@ Network 'lo': netio(bytes_sent=2838627, bytes_recv=2838627, packets_sent=30567, packets_recv=30567, errin=0, errout=0, dropin=0, dropout=0)} >>> >>> psutil.net_connections() - [pconn(fd=115, family=2, type=1, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED', pid=1254), - pconn(fd=117, family=2, type=1, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING', pid=2987), - pconn(fd=-1, family=2, type=1, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED', pid=None), - pconn(fd=-1, family=2, type=1, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT', pid=None) + [pconn(fd=115, family=, type=, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED', pid=1254), + pconn(fd=117, family=, type=, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING', pid=2987), + pconn(fd=-1, family=, type=, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED', pid=None), + pconn(fd=-1, family=, type=, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT', pid=None) ...] + >>> + >>> psutil.net_if_addrs() + {'lo': [snic(family=, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1'), + snic(family=, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None), + snic(family=, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00')], + 'wlan0': [snic(family=, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255'), + snic(family=, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None), + snic(family=, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff')]} + >>> + >>> psutil.net_if_stats() + {'eth0': snicstats(isup=True, duplex=, speed=100, mtu=1500), + 'lo': snicstats(isup=True, duplex=, speed=0, mtu=65536)} Other system info ================= @@ -210,7 +241,7 @@ Process management >>> >>> p.memory_info() pmem(rss=7471104, vms=68513792) - >>> p.ext_memory_info() + >>> p.memory_info_ex() extmem(rss=9662464, vms=49192960, shared=3612672, text=2564096, lib=0, data=5754880, dirty=0) >>> p.memory_maps() [pmmap_grouped(path='/lib/x86_64-linux-gnu/libutil-2.15.so', rss=16384, anonymous=8192, swap=0), @@ -227,10 +258,10 @@ Process management [popenfile(path='/home/giampaolo/svn/psutil/somefile', fd=3)] >>> >>> p.connections() - [pconn(fd=115, family=2, type=1, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED'), - pconn(fd=117, family=2, type=1, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING'), - pconn(fd=119, family=2, type=1, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED'), - pconn(fd=123, family=2, type=1, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT')] + [pconn(fd=115, family=, type=, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED'), + pconn(fd=117, family=, type=, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING'), + pconn(fd=119, family=, type=, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED'), + pconn(fd=123, family=, type=, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT')] >>> >>> p.num_threads() 4 @@ -251,7 +282,7 @@ Process management >>> >>> p.ionice(psutil.IOPRIO_CLASS_IDLE) # IO priority (Win and Linux only) >>> p.ionice() - pionice(ioclass=3, value=0) + pionice(ioclass=, value=0) >>> >>> p.rlimit(psutil.RLIMIT_NOFILE, (5, 5)) # set resource limits (Linux only) >>> p.rlimit(psutil.RLIMIT_NOFILE) @@ -319,6 +350,12 @@ http://groups.google.com/group/psutil/ Timeline ======== +- 2015-07-15: `psutil-3.1.1.tar.gz `_ +- 2015-07-15: `psutil-3.1.0.tar.gz `_ +- 2015-06-18: `psutil-3.0.1.tar.gz `_ +- 2015-06-13: `psutil-3.0.0.tar.gz `_ +- 2015-02-02: `psutil-2.2.1.tar.gz `_ +- 2015-01-06: `psutil-2.2.0.tar.gz `_ - 2014-09-26: `psutil-2.1.3.tar.gz `_ - 2014-09-21: `psutil-2.1.2.tar.gz `_ - 2014-04-30: `psutil-2.1.1.tar.gz `_ diff --git a/python/psutil/TODO b/python/psutil/TODO index a2bd9f53ab7e..a5df809d023a 100644 --- a/python/psutil/TODO +++ b/python/psutil/TODO @@ -9,22 +9,29 @@ https://github.com/giampaolo/psutil/issues HIGHER PRIORITY =============== - * #250: net ifaces speed. - - * #376: ifconfig functionalities aka psutil.net_ifaces (could be merged - with #250) - * OpenBSD support. * #371: CPU temperature (apparently OSX and Linux only; on Linux it requires lm-sensors lib). - * #269: expose network ifaces RX/TW queues. + * #269: expose network ifaces RX/TW queues. This should probably go into + net_if_stats(). Figure out on what platforms this is supported: + Linux: yes + Others: ? - * Process.threads(): thread names + * Process.threads(): thread names; patch for OSX available at: + https://code.google.com/p/plcrashreporter/issues/detail?id=65 * Asynchronous psutil.Popen (see http://bugs.python.org/issue1191964) + * (Windows) fall back on using WMIC for Process methods returning AccessDenied + + * #613: thread names. + + * #604: emulate os.getloadavg() on Windows + + * #269: NIC rx/tx queue. + LOWER PRIORITY ============== @@ -39,11 +46,6 @@ LOWER PRIORITY * AIX support? - * examples/pidof.py (same as 'pidof' cli tool) - - * examples/pstree.py (same as 'pstree' cli tool) - * threads() should also return thread names in order to implement it - * examples/taskmgr-gui.py (using tk). * system-wide number of open file descriptors: @@ -55,10 +57,7 @@ LOWER PRIORITY * #357: what CPU a process is on. - * thread names: - * https://code.google.com/p/plcrashreporter/issues/detail?id=65 - - * Doc / wiki which compares similarities between UNIX cli tools and psutil. + * Doc / wiki which compares similarities between UNIX cli tools and psutil. Example: df -a -> psutil.disk_partitions lsof -> psutil.Process.open_files() and psutil.Process.open_connections() @@ -70,7 +69,13 @@ LOWER PRIORITY DEBATABLE ========= - * support wheels? http://pythonwheels.com/ + * psutil.proc_tree() something which obtains a {pid:ppid, ...} dict for + all running processes in one shot. This can be factored out from + Process.children() and exposed as a first class function. + PROS: on Windows we can take advantage of _psutil_windows.ppid_map() + which is faster than iterating over all pids and calling ppid(). + CONS: examples/pstree.py shows this can be easily done in the user code + so maybe it's not worth the addition. * advanced cmdline interface exposing the whole API and providing different kind of outputs (e.g. pprinted, colorized, json). @@ -91,10 +96,18 @@ DEBATABLE Also, we can probably reimplement wait_pid() on POSIX which is currently implemented as a busy-loop. - * Certain systems (XXX figure out which ones exactly) provide CPU times about - process children. On those systems Process.cpu_times() might return - a (user, system, user_children, system_children) ntuple. - Also, os.times() provides 'elapsed' times as well. + * Certain systems provide CPU times about process children. On those systems + Process.cpu_times() might return a (user, system, user_children, + system_children) ntuple. + * Linux: /proc/{PID}/stat + * Solaris: pr_cutime and pr_cstime + * FreeBSD: none + * OSX: none + * Windows: none + + * ...also, os.times() provides 'elapsed' times as well. + + * ...also Linux provides guest_time and cguest_time. * Enrich exception classes hierarchy on Python >= 3.3 / post PEP-3151 so that: - NoSuchProcess inherits from ProcessLookupError @@ -130,6 +143,10 @@ DEBATABLE * round Process.memory_percent() result? + * #550: number of threads per core. + + * Have psutil.Process().cpu_affinity([]) be an alias for "all CPUs"? + COMPATIBILITY BREAKAGE ====================== @@ -147,4 +164,4 @@ Removals (will likely happen in 2.2): REJECTED IDEAS ============== -STUB \ No newline at end of file +STUB diff --git a/python/psutil/docs/conf.py b/python/psutil/docs/conf.py index 9ec91df70dda..9fa163b65eac 100644 --- a/python/psutil/docs/conf.py +++ b/python/psutil/docs/conf.py @@ -14,29 +14,17 @@ import datetime import os -import sys -if sys.version_info >= (3, ): - def u(s): - return s -else: - def u(s): - if not isinstance(s, unicode): # NOQA - s = unicode(s, "unicode_escape") # NOQA - return s - - -PROJECT_NAME = u("psutil") -AUTHOR = u("Giampaolo Rodola'") +PROJECT_NAME = "psutil" +AUTHOR = "Giampaolo Rodola'" THIS_YEAR = str(datetime.datetime.now().year) HERE = os.path.abspath(os.path.dirname(__file__)) def get_version(): INIT = os.path.abspath(os.path.join(HERE, '../psutil/__init__.py')) - f = open(INIT, 'r') - try: + with open(INIT, 'r') as f: for line in f: if line.startswith('__version__'): ret = eval(line.strip().split(' = ')[1]) @@ -46,8 +34,6 @@ def get_version(): return ret else: raise ValueError("couldn't find version string") - finally: - f.close() VERSION = get_version() @@ -77,7 +63,7 @@ def get_version(): # General information about the project. project = PROJECT_NAME -copyright = u('2009-%s, %s' % (THIS_YEAR, AUTHOR)) +copyright = '2009-%s, %s' % (THIS_YEAR, AUTHOR) # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -223,7 +209,7 @@ def get_version(): # [howto/manual]). latex_documents = [ ('index', '%s.tex' % PROJECT_NAME, - u('%s documentation') % PROJECT_NAME, AUTHOR), + '%s documentation' % PROJECT_NAME, AUTHOR), ] # The name of an image file (relative to this directory) to place at @@ -255,7 +241,7 @@ def get_version(): # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', PROJECT_NAME, u('%s documentation') % PROJECT_NAME, [AUTHOR], 1) + ('index', PROJECT_NAME, '%s documentation' % PROJECT_NAME, [AUTHOR], 1) ] # If true, show URL addresses after external links. diff --git a/python/psutil/docs/index.rst b/python/psutil/docs/index.rst index e053b9357534..44301922678d 100644 --- a/python/psutil/docs/index.rst +++ b/python/psutil/docs/index.rst @@ -18,8 +18,10 @@ Quick links * `Home page `__ * `Blog `__ -* `Download `__ * `Forum `__ +* `Download `__ +* `Installation `_ +* `Development guide `_ * `What's new `__ About @@ -38,8 +40,8 @@ From project's home page: ionice, iostat, iotop, uptime, pidof, tty, taskset, pmap*. It currently supports **Linux, Windows, OSX, FreeBSD** and **Sun Solaris**, both **32-bit** and **64-bit** architectures, with Python versions from - **2.4** to **3.4**. - `Pypy `__ is also known to work. + **2.6 to 3.4** (users of Python 2.4 and 2.5 may use `2.1.3 `__ version). + `PyPy `__ is also known to work. The psutil documentation you're reading is distributed as a single HTML page. @@ -66,7 +68,7 @@ CPU - **guest** *(Linux 2.6.24+)* - **guest_nice** *(Linux 3.2.0+)* - When *percpu* is ``True`` return a list of nameduples for each logical CPU + When *percpu* is ``True`` return a list of namedtuples for each logical CPU on the system. First element of the list refers to first CPU, second element to second CPU and so on. @@ -199,7 +201,7 @@ Memory * **total**: total swap memory in bytes * **used**: used swap memory in bytes * **free**: free swap memory in bytes - * **percent**: the percentage usage + * **percent**: the percentage usage calculated as ``(total - available) / total * 100`` * **sin**: the number of bytes the system has swapped in from disk (cumulative) * **sout**: the number of bytes the system has swapped out from disk @@ -274,7 +276,7 @@ Disks If *perdisk* is ``True`` return the same information for every physical disk installed on the system as a dictionary with partition names as the keys and - the namedutuple described above as the values. + the namedtuple described above as the values. See `examples/iotop.py `__ for an example application. @@ -321,7 +323,7 @@ Network .. function:: net_connections(kind='inet') - Return system-wide socket connections as a list of namedutples. + Return system-wide socket connections as a list of namedtuples. Every namedtuple provides 7 attributes: - **fd**: the socket file descriptor, if retrievable, else ``-1``. @@ -383,6 +385,7 @@ Network | "all" | the sum of all the possible families and protocols | +----------------+-----------------------------------------------------+ + On OSX this function requires root privileges. To get per-process connections use :meth:`Process.connections`. Also, see `netstat.py sample script `__. @@ -390,17 +393,85 @@ Network >>> import psutil >>> psutil.net_connections() - [pconn(fd=115, family=2, type=1, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED', pid=1254), - pconn(fd=117, family=2, type=1, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING', pid=2987), - pconn(fd=-1, family=2, type=1, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED', pid=None), - pconn(fd=-1, family=2, type=1, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT', pid=None) + [pconn(fd=115, family=, type=, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED', pid=1254), + pconn(fd=117, family=, type=, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING', pid=2987), + pconn(fd=-1, family=, type=, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED', pid=None), + pconn(fd=-1, family=, type=, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT', pid=None) ...] .. note:: (OSX) :class:`psutil.AccessDenied` is always raised unless running as root (lsof does the same). .. note:: (Solaris) UNIX sockets are not supported. - *New in 2.1.0* + .. versionadded:: 2.1.0 + +.. function:: net_if_addrs() + + Return the addresses associated to each NIC (network interface card) + installed on the system as a dictionary whose keys are the NIC names and + value is a list of namedtuples for each address assigned to the NIC. + Each namedtuple includes 4 fields: + + - **family** + - **address** + - **netmask** + - **broadcast** + + *family* can be either + `AF_INET `__, + `AF_INET6 `__ + or :const:`psutil.AF_LINK`, which refers to a MAC address. + *address* is the primary address, *netmask* and *broadcast* may be ``None``. + Example:: + + >>> import psutil + >>> psutil.net_if_addrs() + {'lo': [snic(family=, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1'), + snic(family=, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None), + snic(family=, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00')], + 'wlan0': [snic(family=, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255'), + snic(family=, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None), + snic(family=, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff')]} + >>> + + See also `examples/ifconfig.py `__ + for an example application. + + .. note:: if you're interested in others families (e.g. AF_BLUETOOTH) you can + use the more powerful `netifaces `__ + extension. + + .. note:: you can have more than one address of the same family associated + with each interface (that's why dict values are lists). + + *New in 3.0.0* + +.. function:: net_if_stats() + + Return information about each NIC (network interface card) installed on the + system as a dictionary whose keys are the NIC names and value is a namedtuple + with the following fields: + + - **isup** + - **duplex** + - **speed** + - **mtu** + + *isup* is a boolean indicating whether the NIC is up and running, *duplex* + can be either :const:`NIC_DUPLEX_FULL`, :const:`NIC_DUPLEX_HALF` or + :const:`NIC_DUPLEX_UNKNOWN`, *speed* is the NIC speed expressed in mega bits + (MB), if it can't be determined (e.g. 'localhost') it will be set to ``0``, + *mtu* is the maximum transmission unit expressed in bytes. + See also `examples/ifconfig.py `__ + for an example application. + Example: + + >>> import psutil + >>> psutil.net_if_stats() + {'eth0': snicstats(isup=True, duplex=, speed=100, mtu=1500), + 'lo': snicstats(isup=True, duplex=, speed=0, mtu=65536)} + + *New in 3.0.0* Other system info @@ -498,7 +569,7 @@ Functions import psutil def on_terminate(proc): - print("process {} terminated".format(proc)) + print("process {} terminated with exit code {}".format(proc, proc.returncode)) procs = [...] # a list of Process instances for p in procs: @@ -521,6 +592,18 @@ Exceptions exists. "name" is the name the process had before disappearing and gets set only if :meth:`Process.name()` was previosly called. +.. class:: ZombieProcess(pid, name=None, ppid=None, msg=None) + + This may be raised by :class:`Process` class methods when querying a zombie + process on UNIX (Windows doesn't have zombie processes). Depending on the + method called the OS may be able to succeed in retrieving the process + information or not. + Note: this is a subclass of :class:`NoSuchProcess` so if you're not + interested in retrieving zombies (e.g. when using :func:`process_iter()`) + you can ignore this exception and just catch :class:`NoSuchProcess`. + + *New in 3.0.0* + .. class:: AccessDenied(pid=None, name=None, msg=None) Raised by :class:`Process` class methods when permission to perform an @@ -609,20 +692,24 @@ Process class >>> datetime.datetime.fromtimestamp(p.create_time()).strftime("%Y-%m-%d %H:%M:%S") '2011-03-05 18:03:52' - .. method:: as_dict(attrs=[], ad_value=None) + .. method:: as_dict(attrs=None, ad_value=None) Utility method returning process information as a hashable dictionary. If *attrs* is specified it must be a list of strings reflecting available :class:`Process` class's attribute names (e.g. ``['cpu_times', 'name']``) else all public (read only) attributes are assumed. *ad_value* is the value which gets assigned to a dict key in case :class:`AccessDenied` - exception is raised when retrieving that particular process information. + or :class:`ZombieProcess` exception is raised when retrieving that + particular process information. >>> import psutil >>> p = psutil.Process() >>> p.as_dict(attrs=['pid', 'name', 'username']) {'username': 'giampaolo', 'pid': 12366, 'name': 'python'} + .. versionchanged:: 3.0.0 *ad_value* is used also when incurring into + :class:`ZombieProcess` exception, not only :class:`AccessDenied` + .. method:: parent() Utility method which returns the parent process as a :class:`Process` @@ -646,7 +733,7 @@ Process class .. method:: uids() The **real**, **effective** and **saved** user ids of this process as a - nameduple. This is the same as + namedtuple. This is the same as `os.getresuid() `__ but can be used for every process PID. @@ -655,7 +742,7 @@ Process class .. method:: gids() The **real**, **effective** and **saved** group ids of this process as a - nameduple. This is the same as + namedtuple. This is the same as `os.getresgid() `__ but can be used for every process PID. @@ -682,6 +769,13 @@ Process class 10 >>> + Starting from `Python 3.3 `__ this + functionality is also available as + `os.getpriority() `__ + and + `os.setpriority() `__ + (UNIX only). + On Windows this is available as well by using `GetPriorityClass `__ and `SetPriorityClass `__ @@ -692,12 +786,6 @@ Process class >>> p.nice(psutil.HIGH_PRIORITY_CLASS) - Starting from `Python 3.3 `__ this - same functionality is available as - `os.getpriority() `__ - and - `os.setpriority() `__. - .. method:: ionice(ioclass=None, value=None) Get or set @@ -714,7 +802,7 @@ Process class >>> p = psutil.Process() >>> p.ionice(psutil.IOPRIO_CLASS_IDLE) # set >>> p.ionice() # get - pionice(ioclass=3, value=0) + pionice(ioclass=, value=0) >>> On Windows only *ioclass* is used and it can be set to ``2`` (normal), @@ -722,6 +810,10 @@ Process class Availability: Linux and Windows > Vista + .. versionchanged:: 3.0.0 on >= Python 3.4 the returned ``ioclass`` + constant is an `enum `__ + instead of a plain integer. + .. method:: rlimit(resource, limits=None) Get or set process resource limits (see @@ -761,7 +853,7 @@ Process class >>> p.io_counters() pio(read_count=454556, write_count=3456, read_bytes=110592, write_bytes=0) - Availability: all platforms except OSX + Availability: all platforms except OSX and Solaris .. method:: num_ctx_switches() @@ -836,7 +928,8 @@ Process class `CPU affinity `__. CPU affinity consists in telling the OS to run a certain process on a limited set of CPUs only. The number of eligible CPUs can be obtained with - ``list(range(psutil.cpu_count()))``. + ``list(range(psutil.cpu_count()))``. On set raises ``ValueError`` in case + an invalid CPU number is specified. >>> import psutil >>> psutil.cpu_count() @@ -845,9 +938,17 @@ Process class >>> p.cpu_affinity() # get [0, 1, 2, 3] >>> p.cpu_affinity([0]) # set; from now on, process will run on CPU #0 only + >>> p.cpu_affinity() + [0] >>> + >>> # reset affinity against all CPUs + >>> all_cpus = list(range(psutil.cpu_count())) + >>> p.cpu_affinity(all_cpus) + >>> + + Availability: Linux, Windows, BSD - Availability: Linux, Windows + .. versionchanged:: 2.2.0 added support for FreeBSD .. method:: memory_info() @@ -903,7 +1004,7 @@ Process class .. method:: memory_maps(grouped=True) - Return process's mapped memory regions as a list of nameduples whose + Return process's mapped memory regions as a list of namedtuples whose fields are variable depending on the platform. As such, portable applications should rely on namedtuple's `path` and `rss` fields only. This method is useful to obtain a detailed representation of process @@ -958,15 +1059,31 @@ Process class the absolute file name and the file descriptor number (on Windows this is always ``-1``). Example: - >>> import psutil - >>> f = open('file.ext', 'w') - >>> p = psutil.Process() - >>> p.open_files() - [popenfile(path='/home/giampaolo/svn/psutil/file.ext', fd=3)] + >>> import psutil + >>> f = open('file.ext', 'w') + >>> p = psutil.Process() + >>> p.open_files() + [popenfile(path='/home/giampaolo/svn/psutil/file.ext', fd=3)] + + .. warning:: + on Windows this is not fully reliable as due to some limitations of the + Windows API the underlying implementation may hang when retrieving + certain file handles. + In order to work around that psutil on Windows Vista (and higher) spawns + a thread and kills it if it's not responding after 100ms. + That implies that on Windows this method is not guaranteed to enumerate + all regular file handles (see full discusion + `here `_). + + .. warning:: + on FreeBSD this method can return files with a 'null' path (see + `issue 595 `_). + + .. versionchanged:: 3.1.0 no longer hangs on Windows. .. method:: connections(kind="inet") - Return socket connections opened by process as a list of namedutples. + Return socket connections opened by process as a list of namedtuples. To get system-wide connections use :func:`psutil.net_connections()`. Every namedtuple provides 6 attributes: @@ -1032,10 +1149,10 @@ Process class >>> p.name() 'firefox' >>> p.connections() - [pconn(fd=115, family=2, type=1, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED'), - pconn(fd=117, family=2, type=1, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING'), - pconn(fd=119, family=2, type=1, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED'), - pconn(fd=123, family=2, type=1, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT')] + [pconn(fd=115, family=, type=, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED'), + pconn(fd=117, family=, type=, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING'), + pconn(fd=119, family=, type=, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED'), + pconn(fd=123, family=, type=, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT')] .. method:: is_running() @@ -1125,7 +1242,7 @@ Popen class :meth:`send_signal() `, :meth:`terminate() ` and :meth:`kill() ` - so that you don't accidentally terminate another process, fixing + so that you can't accidentally terminate another process, fixing http://bugs.python.org/issue6973. >>> import psutil @@ -1197,6 +1314,10 @@ Constants Availability: Windows + .. versionchanged:: 3.0.0 on Python >= 3.4 these constants are + `enums `__ + instead of a plain integer. + .. _const-ioprio: .. data:: IOPRIO_CLASS_NONE IOPRIO_CLASS_RT @@ -1220,6 +1341,10 @@ Constants Availability: Linux + .. versionchanged:: 3.0.0 on Python >= 3.4 thse constants are + `enums `__ + instead of a plain integer. + .. _const-rlimit: .. data:: RLIMIT_INFINITY RLIMIT_AS @@ -1245,3 +1370,31 @@ Constants `man prlimit `__ for futher information. Availability: Linux + +.. _const-aflink: +.. data:: AF_LINK + + Constant which identifies a MAC address associated with a network interface. + To be used in conjunction with :func:`psutil.net_if_addrs()`. + + *New in 3.0.0* + +.. _const-duplex: +.. data:: NIC_DUPLEX_FULL + NIC_DUPLEX_HALF + NIC_DUPLEX_UNKNOWN + + Constants which identifies whether a NIC (network interface card) has full or + half mode speed. NIC_DUPLEX_FULL means the NIC is able to send and receive + data (files) simultaneously, NIC_DUPLEX_FULL means the NIC can either send or + receive data at a time. + To be used in conjunction with :func:`psutil.net_if_stats()`. + + *New in 3.0.0* + +Development guide +================= + +If you plan on hacking on psutil (e.g. want to add a new feature or fix a bug) +take a look at the +`development guide `_. diff --git a/python/psutil/docs/xxx b/python/psutil/docs/xxx new file mode 100644 index 000000000000..b78d53f2d6bb --- /dev/null +++ b/python/psutil/docs/xxx @@ -0,0 +1,11 @@ +cpu 1974613 1749 485728 6305758 80280 15 5924 0 0 0 + +cpu0 519156 374 132999 5977865 72925 10 1458 0 0 0 + +cpu1 524667 401 125931 108960 2110 4 2214 0 0 0 + +cpu2 462286 520 117046 109514 2666 0 828 0 0 0 + +cpu3 468502 453 109750 109418 2578 0 1424 0 0 0 + + diff --git a/python/psutil/examples/disk_usage.py b/python/psutil/examples/disk_usage.py index 787cb0f5ac9a..d8600a8c4764 100755 --- a/python/psutil/examples/disk_usage.py +++ b/python/psutil/examples/disk_usage.py @@ -18,7 +18,6 @@ import sys import os import psutil -from psutil._compat import print_ def bytes2human(n): @@ -40,8 +39,8 @@ def bytes2human(n): def main(): templ = "%-17s %8s %8s %8s %5s%% %9s %s" - print_(templ % ("Device", "Total", "Used", "Free", "Use ", "Type", - "Mount")) + print(templ % ("Device", "Total", "Used", "Free", "Use ", "Type", + "Mount")) for part in psutil.disk_partitions(all=False): if os.name == 'nt': if 'cdrom' in part.opts or part.fstype == '': @@ -50,7 +49,7 @@ def main(): # partition or just hang. continue usage = psutil.disk_usage(part.mountpoint) - print_(templ % ( + print(templ % ( part.device, bytes2human(usage.total), bytes2human(usage.used), diff --git a/python/psutil/examples/free.py b/python/psutil/examples/free.py index 95e11fb6847d..913ca58a4cfa 100755 --- a/python/psutil/examples/free.py +++ b/python/psutil/examples/free.py @@ -14,15 +14,14 @@ """ import psutil -from psutil._compat import print_ def main(): virt = psutil.virtual_memory() swap = psutil.swap_memory() templ = "%-7s %10s %10s %10s %10s %10s %10s" - print_(templ % ('', 'total', 'used', 'free', 'shared', 'buffers', 'cache')) - print_(templ % ( + print(templ % ('', 'total', 'used', 'free', 'shared', 'buffers', 'cache')) + print(templ % ( 'Mem:', int(virt.total / 1024), int(virt.used / 1024), @@ -30,7 +29,7 @@ def main(): int(getattr(virt, 'shared', 0) / 1024), int(getattr(virt, 'buffers', 0) / 1024), int(getattr(virt, 'cached', 0) / 1024))) - print_(templ % ( + print(templ % ( 'Swap:', int(swap.total / 1024), int(swap.used / 1024), int(swap.free / 1024), diff --git a/python/psutil/examples/ifconfig.py b/python/psutil/examples/ifconfig.py new file mode 100644 index 000000000000..e7a436cc0a0a --- /dev/null +++ b/python/psutil/examples/ifconfig.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python + +# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +A clone of 'ifconfig' on UNIX. + +$ python examples/ifconfig.py +lo (speed=0MB, duplex=?, mtu=65536, up=yes): + IPv4 address : 127.0.0.1 + broadcast : 127.0.0.1 + netmask : 255.0.0.0 + IPv6 address : ::1 + netmask : ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff + MAC address : 00:00:00:00:00:00 + broadcast : 00:00:00:00:00:00 + +wlan0 (speed=0MB, duplex=?, mtu=1500, up=yes): + IPv4 address : 10.0.3.1 + broadcast : 10.0.3.255 + netmask : 255.255.255.0 + IPv6 address : fe80::3005:adff:fe31:8698 + netmask : ffff:ffff:ffff:ffff:: + MAC address : 32:05:ad:31:86:98 + broadcast : ff:ff:ff:ff:ff:ff + +eth0 (speed=100MB, duplex=full, mtu=1500, up=yes): + IPv4 address : 192.168.1.2 + broadcast : 192.168.1.255 + netmask : 255.255.255.0 + IPv6 address : fe80::c685:8ff:fe45:641 + netmask : ffff:ffff:ffff:ffff:: + MAC address : c4:85:08:45:06:41 + broadcast : ff:ff:ff:ff:ff:ff +""" + +from __future__ import print_function +import socket + +import psutil + + +af_map = { + socket.AF_INET: 'IPv4', + socket.AF_INET6: 'IPv6', + psutil.AF_LINK: 'MAC', +} + +duplex_map = { + psutil.NIC_DUPLEX_FULL: "full", + psutil.NIC_DUPLEX_HALF: "half", + psutil.NIC_DUPLEX_UNKNOWN: "?", +} + + +def main(): + stats = psutil.net_if_stats() + for nic, addrs in psutil.net_if_addrs().items(): + if nic in stats: + print("%s (speed=%sMB, duplex=%s, mtu=%s, up=%s):" % ( + nic, stats[nic].speed, duplex_map[stats[nic].duplex], + stats[nic].mtu, "yes" if stats[nic].isup else "no")) + else: + print("%s:" % (nic)) + for addr in addrs: + print(" %-8s" % af_map.get(addr.family, addr.family), end="") + print(" address : %s" % addr.address) + if addr.broadcast: + print(" broadcast : %s" % addr.broadcast) + if addr.netmask: + print(" netmask : %s" % addr.netmask) + print("") + + +if __name__ == '__main__': + main() diff --git a/python/psutil/examples/iotop.py b/python/psutil/examples/iotop.py index 47f1ca552634..16ac7fbf61b7 100755 --- a/python/psutil/examples/iotop.py +++ b/python/psutil/examples/iotop.py @@ -30,14 +30,15 @@ Author: Giampaolo Rodola' """ -import os +import atexit +import time import sys -import psutil -if not hasattr(psutil.Process, 'io_counters') or os.name != 'posix': +try: + import curses +except ImportError: sys.exit('platform not supported') -import time -import curses -import atexit + +import psutil # --- curses stuff @@ -116,7 +117,7 @@ def poll(interval): if not p._cmdline: p._cmdline = p.name() p._username = p.username() - except psutil.NoSuchProcess: + except (psutil.NoSuchProcess, psutil.ZombieProcess): procs.remove(p) disks_after = psutil.disk_io_counters() @@ -167,7 +168,7 @@ def refresh_window(procs, disks_read, disks_write): def main(): try: interval = 0 - while 1: + while True: args = poll(interval) refresh_window(*args) interval = 1 diff --git a/python/psutil/examples/meminfo.py b/python/psutil/examples/meminfo.py index 671f907caf55..c463a3de4b63 100755 --- a/python/psutil/examples/meminfo.py +++ b/python/psutil/examples/meminfo.py @@ -31,7 +31,6 @@ """ import psutil -from psutil._compat import print_ def bytes2human(n): @@ -56,13 +55,13 @@ def pprint_ntuple(nt): value = getattr(nt, name) if name != 'percent': value = bytes2human(value) - print_('%-10s : %7s' % (name.capitalize(), value)) + print('%-10s : %7s' % (name.capitalize(), value)) def main(): - print_('MEMORY\n------') + print('MEMORY\n------') pprint_ntuple(psutil.virtual_memory()) - print_('\nSWAP\n----') + print('\nSWAP\n----') pprint_ntuple(psutil.swap_memory()) if __name__ == '__main__': diff --git a/python/psutil/examples/netstat.py b/python/psutil/examples/netstat.py index 70bc23175493..884622e9e3a6 100755 --- a/python/psutil/examples/netstat.py +++ b/python/psutil/examples/netstat.py @@ -23,7 +23,6 @@ from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM import psutil -from psutil._compat import print_ AD = "-" @@ -38,7 +37,7 @@ def main(): templ = "%-5s %-30s %-30s %-13s %-6s %s" - print_(templ % ( + print(templ % ( "Proto", "Local address", "Remote address", "Status", "PID", "Program name")) proc_names = {} @@ -52,7 +51,7 @@ def main(): raddr = "" if c.raddr: raddr = "%s:%s" % (c.raddr) - print_(templ % ( + print(templ % ( proto_map[(c.family, c.type)], laddr, raddr or AD, diff --git a/python/psutil/examples/nettop.py b/python/psutil/examples/nettop.py index 857285cf885b..7a8343ee4cd9 100755 --- a/python/psutil/examples/nettop.py +++ b/python/psutil/examples/nettop.py @@ -31,13 +31,13 @@ pkts-recv 1214470 0 """ -import sys -import os -if os.name != 'posix': - sys.exit('platform not supported') import atexit -import curses import time +import sys +try: + import curses +except ImportError: + sys.exit('platform not supported') import psutil diff --git a/python/psutil/examples/pidof.py b/python/psutil/examples/pidof.py new file mode 100755 index 000000000000..8692a3152bc3 --- /dev/null +++ b/python/psutil/examples/pidof.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +# Copyright (c) 2009, Giampaolo Rodola', karthikrev. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + + +""" +A clone of 'pidof' cmdline utility. +$ pidof python +1140 1138 1136 1134 1133 1129 1127 1125 1121 1120 1119 +""" + +from __future__ import print_function +import psutil +import sys + + +def pidof(pgname): + pids = [] + for proc in psutil.process_iter(): + # search for matches in the process name and cmdline + try: + name = proc.name() + except psutil.Error: + pass + else: + if name == pgname: + pids.append(str(proc.pid)) + continue + + try: + cmdline = proc.cmdline() + except psutil.Error: + pass + else: + if cmdline and cmdline[0] == pgname: + pids.append(str(proc.pid)) + + return pids + + +def main(): + if len(sys.argv) != 2: + sys.exit('usage: %s pgname' % __file__) + else: + pgname = sys.argv[1] + pids = pidof(pgname) + if pids: + print(" ".join(pids)) + +if __name__ == '__main__': + main() diff --git a/python/psutil/examples/pmap.py b/python/psutil/examples/pmap.py index 1936c0b276f9..7593777aef22 100755 --- a/python/psutil/examples/pmap.py +++ b/python/psutil/examples/pmap.py @@ -33,26 +33,25 @@ import sys import psutil -from psutil._compat import print_ def main(): if len(sys.argv) != 2: sys.exit('usage: pmap ') p = psutil.Process(int(sys.argv[1])) - print_("pid=%s, name=%s" % (p.pid, p.name())) + print("pid=%s, name=%s" % (p.pid, p.name())) templ = "%-16s %10s %-7s %s" - print_(templ % ("Address", "RSS", "Mode", "Mapping")) + print(templ % ("Address", "RSS", "Mode", "Mapping")) total_rss = 0 for m in p.memory_maps(grouped=False): total_rss += m.rss - print_(templ % ( + print(templ % ( m.addr.split('-')[0].zfill(16), str(m.rss / 1024) + 'K', m.perms, m.path)) - print_("-" * 33) - print_(templ % ("Total", str(total_rss / 1024) + 'K', '', '')) + print("-" * 33) + print(templ % ("Total", str(total_rss / 1024) + 'K', '', '')) if __name__ == '__main__': main() diff --git a/python/psutil/examples/process_detail.py b/python/psutil/examples/process_detail.py index a07872865838..e20371aefe97 100755 --- a/python/psutil/examples/process_detail.py +++ b/python/psutil/examples/process_detail.py @@ -68,8 +68,8 @@ def run(pid): try: p = psutil.Process(pid) pinfo = p.as_dict(ad_value=ACCESS_DENIED) - except psutil.NoSuchProcess: - sys.exit(str(sys.exc_info()[1])) + except psutil.NoSuchProcess as err: + sys.exit(str(err)) try: parent = p.parent() @@ -79,13 +79,19 @@ def run(pid): parent = '' except psutil.Error: parent = '' - started = datetime.datetime.fromtimestamp( - pinfo['create_time']).strftime('%Y-%m-%d %H:%M') + if pinfo['create_time'] != ACCESS_DENIED: + started = datetime.datetime.fromtimestamp( + pinfo['create_time']).strftime('%Y-%m-%d %H:%M') + else: + started = ACCESS_DENIED io = pinfo.get('io_counters', ACCESS_DENIED) - mem = '%s%% (resident=%s, virtual=%s) ' % ( - round(pinfo['memory_percent'], 1), - convert_bytes(pinfo['memory_info'].rss), - convert_bytes(pinfo['memory_info'].vms)) + if pinfo['memory_info'] != ACCESS_DENIED: + mem = '%s%% (resident=%s, virtual=%s) ' % ( + round(pinfo['memory_percent'], 1), + convert_bytes(pinfo['memory_info'].rss), + convert_bytes(pinfo['memory_info'].vms)) + else: + mem = ACCESS_DENIED children = p.children() print_('pid', pinfo['pid']) @@ -101,8 +107,7 @@ def run(pid): print_('gids', 'real=%s, effective=%s, saved=%s' % pinfo['gids']) if POSIX: print_('terminal', pinfo['terminal'] or '') - if hasattr(p, 'getcwd'): - print_('cwd', pinfo['cwd']) + print_('cwd', pinfo['cwd']) print_('memory', mem) print_('cpu', '%s%% (user=%s, system=%s)' % ( pinfo['cpu_percent'], diff --git a/python/psutil/examples/ps.py b/python/psutil/examples/ps.py index 2ead74700735..2b67bd18fffb 100644 --- a/python/psutil/examples/ps.py +++ b/python/psutil/examples/ps.py @@ -16,7 +16,6 @@ import time import psutil -from psutil._compat import print_ def main(): @@ -27,8 +26,8 @@ def main(): if os.name == 'posix': attrs.append('uids') attrs.append('terminal') - print_(templ % ("USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY", - "START", "TIME", "COMMAND")) + print(templ % ("USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY", + "START", "TIME", "COMMAND")) for p in psutil.process_iter(): try: pinfo = p.as_dict(attrs, ad_value='') @@ -65,16 +64,17 @@ def main(): int(pinfo['memory_info'].rss / 1024) or '?' memp = pinfo['memory_percent'] and \ round(pinfo['memory_percent'], 1) or '?' - print_(templ % (user[:10], - pinfo['pid'], - pinfo['cpu_percent'], - memp, - vms, - rss, - pinfo.get('terminal', '') or '?', - ctime, - cputime, - pinfo['name'].strip() or '?')) + print(templ % ( + user[:10], + pinfo['pid'], + pinfo['cpu_percent'], + memp, + vms, + rss, + pinfo.get('terminal', '') or '?', + ctime, + cputime, + pinfo['name'].strip() or '?')) if __name__ == '__main__': diff --git a/python/psutil/examples/pstree.py b/python/psutil/examples/pstree.py new file mode 100644 index 000000000000..1bf8c9c0493d --- /dev/null +++ b/python/psutil/examples/pstree.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python + +# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Similar to 'ps aux --forest' on Linux, prints the process list +as a tree structure. + +$ python examples/pstree.py +0 ? +|- 1 init +| |- 289 cgmanager +| |- 616 upstart-socket-bridge +| |- 628 rpcbind +| |- 892 upstart-file-bridge +| |- 907 dbus-daemon +| |- 978 avahi-daemon +| | `_ 979 avahi-daemon +| |- 987 NetworkManager +| | |- 2242 dnsmasq +| | `_ 10699 dhclient +| |- 993 polkitd +| |- 1061 getty +| |- 1066 su +| | `_ 1190 salt-minion... +... +""" + +from __future__ import print_function +import collections +import sys + +import psutil + + +def print_tree(parent, tree, indent=''): + try: + name = psutil.Process(parent).name() + except psutil.Error: + name = "?" + print(parent, name) + if parent not in tree: + return + children = tree[parent][:-1] + for child in children: + sys.stdout.write(indent + "|- ") + print_tree(child, tree, indent + "| ") + child = tree[parent][-1] + sys.stdout.write(indent + "`_ ") + print_tree(child, tree, indent + " ") + + +def main(): + # construct a dict where 'values' are all the processes + # having 'key' as their parent + tree = collections.defaultdict(list) + for p in psutil.process_iter(): + try: + tree[p.ppid()].append(p.pid) + except (psutil.NoSuchProcess, psutil.ZombieProcess): + pass + # on systems supporting PID 0, PID 0's parent is usually 0 + if 0 in tree and 0 in tree[0]: + tree[0].remove(0) + print_tree(min(tree), tree) + + +if __name__ == '__main__': + main() diff --git a/python/psutil/examples/top.py b/python/psutil/examples/top.py index 479c797e22a5..7aebef1d42ab 100755 --- a/python/psutil/examples/top.py +++ b/python/psutil/examples/top.py @@ -34,14 +34,15 @@ ... """ +from datetime import datetime, timedelta +import atexit import os +import time import sys -if os.name != 'posix': +try: + import curses +except ImportError: sys.exit('platform not supported') -import atexit -import curses -import time -from datetime import datetime, timedelta import psutil @@ -221,7 +222,7 @@ def refresh_window(procs, procs_status): def main(): try: interval = 0 - while 1: + while True: args = poll(interval) refresh_window(*args) interval = 1 diff --git a/python/psutil/examples/who.py b/python/psutil/examples/who.py index 8ffbc81872da..b382bebfa338 100755 --- a/python/psutil/examples/who.py +++ b/python/psutil/examples/who.py @@ -18,13 +18,12 @@ from datetime import datetime import psutil -from psutil._compat import print_ def main(): users = psutil.users() for user in users: - print_("%-15s %-15s %s (%s)" % ( + print("%-15s %-15s %s (%s)" % ( user.name, user.terminal or '-', datetime.fromtimestamp(user.started).strftime("%Y-%m-%d %H:%M"), diff --git a/python/psutil/make.bat b/python/psutil/make.bat index 72ace94c2f8d..9c430101dd5e 100644 --- a/python/psutil/make.bat +++ b/python/psutil/make.bat @@ -7,18 +7,16 @@ rem psutil ("make.bat build", "make.bat install") and running tests rem ("make.bat test"). rem rem This script is modeled after my Windows installation which uses: -rem - mingw32 for Python 2.4 and 2.5 rem - Visual studio 2008 for Python 2.6, 2.7, 3.2 rem - Visual studio 2010 for Python 3.3+ rem ...therefore it might not work on your Windows installation. rem rem By default C:\Python27\python.exe is used. rem To compile for a specific Python version run: +rem set PYTHON=C:\Python34\python.exe & make.bat build rem -rem set PYTHON=C:\Python24\python.exe & make.bat build -rem -rem If you compile by using mingw on Python 2.4 and 2.5 you need to patch -rem distutils first: http://stackoverflow.com/questions/13592192 +rem To use a different test script: +rem set PYTHON=C:\Python34\python.exe & set TSCRIPT=foo.py & make.bat test rem ========================================================================== if "%PYTHON%" == "" ( @@ -28,8 +26,16 @@ if "%TSCRIPT%" == "" ( set TSCRIPT=test\test_psutil.py ) -rem Needed to compile using Mingw. -set PATH=C:\MinGW\bin;%PATH% +set PYTHON26=C:\Python26\python.exe +set PYTHON27=C:\Python27\python.exe +set PYTHON33=C:\Python33\python.exe +set PYTHON34=C:\Python34\python.exe +set PYTHON26-64=C:\Python26-64\python.exe +set PYTHON27-64=C:\Python27-64\python.exe +set PYTHON33-64=C:\Python33-64\python.exe +set PYTHON34-64=C:\Python34-64\python.exe + +set ALL_PYTHONS=%PYTHON26% %PYTHON27% %PYTHON33% %PYTHON34% %PYTHON26-64% %PYTHON27-64% %PYTHON33-64% %PYTHON34-64% rem Needed to locate the .pypirc file and upload exes on PYPI. set HOME=%USERPROFILE% @@ -40,23 +46,21 @@ if "%1" == "help" ( :help echo Run `make ^` where ^ is one of: echo build compile without installing - echo build-exes create exe installers in dist directory - echo build-wheels create wheel installers in dist directory + echo build-all build exes + wheels echo clean clean build files + echo flake8 run flake8 echo install compile and install - echo memtest run memory leak tests - echo setup-env install pip, unittest2, wheels for all python versions + echo setup-dev-env install pip, pywin32, wheels, etc. for all python versions echo test run tests + echo test-memleaks run memory leak tests echo test-process run process related tests echo test-system run system APIs related tests echo uninstall uninstall - echo upload-exes upload exe installers on pypi - echo upload-wheels upload wheel installers on pypi + echo upload-all upload exes + wheels goto :eof ) if "%1" == "clean" ( - :clean for /r %%R in (__pycache__) do if exist %%R (rmdir /S /Q %%R) for /r %%R in (*.pyc) do if exist %%R (del /s %%R) for /r %%R in (*.pyd) do if exist %%R (del /s %%R) @@ -71,31 +75,25 @@ if "%1" == "clean" ( if "%1" == "build" ( :build - if %PYTHON%==C:\Python24\python.exe ( - %PYTHON% setup.py build -c mingw32 - ) else if %PYTHON%==C:\Python25\python.exe ( - %PYTHON% setup.py build -c mingw32 - ) else ( - %PYTHON% setup.py build - ) + "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat" + %PYTHON% setup.py build + if %errorlevel% neq 0 goto :error + rem copies *.pyd files in ./psutil directory in order to allow + rem "import psutil" when using the interactive interpreter from + rem within this directory. + %PYTHON% setup.py build_ext -i if %errorlevel% neq 0 goto :error goto :eof ) if "%1" == "install" ( :install - if %PYTHON%==C:\Python24\python.exe ( - %PYTHON% setup.py build -c mingw32 install - ) else if %PYTHON%==C:\Python25\python.exe ( - %PYTHON% setup.py build -c mingw32 install - ) else ( - %PYTHON% setup.py build install - ) + call :build + %PYTHON% setup.py install goto :eof ) if "%1" == "uninstall" ( - :uninstall for %%A in ("%PYTHON%") do ( set folder=%%~dpA ) @@ -106,125 +104,98 @@ if "%1" == "uninstall" ( ) if "%1" == "test" ( - :test call :install %PYTHON% %TSCRIPT% goto :eof ) if "%1" == "test-process" ( - :test call :install %PYTHON% -m unittest -v test.test_psutil.TestProcess goto :eof ) if "%1" == "test-system" ( - :test call :install %PYTHON% -m unittest -v test.test_psutil.TestSystem goto :eof ) if "%1" == "test-memleaks" ( - :memtest call :install %PYTHON% test\test_memory_leaks.py goto :eof ) -if "%1" == "build-exes" ( - :build-exes - rem mingw 32 versions - C:\Python24\python.exe setup.py build -c mingw32 bdist_wininst || goto :error - C:\Python25\python.exe setup.py build -c mingw32 bdist_wininst || goto :error - rem "standard" 32 bit versions, using VS 2008 (2.6, 2.7) or VS 2010 (3.3+) - C:\Python26\python.exe setup.py build bdist_wininst || goto :error - C:\Python27\python.exe setup.py build bdist_wininst || goto :error - C:\Python33\python.exe setup.py build bdist_wininst || goto :error - C:\Python34\python.exe setup.py build bdist_wininst || goto :error - rem 64 bit versions - rem Python 2.7 + VS 2008 requires vcvars64.bat to be run first: - rem http://stackoverflow.com/questions/11072521/ - rem Windows SDK and .NET Framework 3.5 SP1 also need to be installed (sigh) +if "%1" == "build-all" ( + :build-all "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat" - C:\Python27-64\python.exe setup.py build bdist_wininst || goto :error - C:\Python33-64\python.exe setup.py build bdist_wininst || goto :error - C:\Python34-64\python.exe setup.py build bdist_wininst || goto :error + for %%P in (%ALL_PYTHONS%) do ( + @echo ------------------------------------------------ + @echo building exe for %%P + @echo ------------------------------------------------ + %%P setup.py build bdist_wininst || goto :error + @echo ------------------------------------------------ + @echo building wheel for %%P + @echo ------------------------------------------------ + %%P setup.py build bdist_wheel || goto :error + ) echo OK goto :eof ) -if "%1" == "upload-exes" ( +if "%1" == "upload-all" ( :upload-exes - rem mingw 32 versions - C:\Python25\python.exe setup.py build -c mingw32 bdist_wininst upload || goto :error - rem "standard" 32 bit versions, using VS 2008 (2.6, 2.7) or VS 2010 (3.3+) - C:\Python26\python.exe setup.py bdist_wininst upload || goto :error - C:\Python27\python.exe setup.py bdist_wininst upload || goto :error - C:\Python33\python.exe setup.py bdist_wininst upload || goto :error - C:\Python34\python.exe setup.py bdist_wininst upload || goto :error - rem 64 bit versions - C:\Python27-64\python.exe setup.py build bdist_wininst upload || goto :error - C:\Python33-64\python.exe setup.py build bdist_wininst upload || goto :error - C:\Python34-64\python.exe setup.py build bdist_wininst upload || goto :error + "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat" + for %%P in (%ALL_PYTHONS%) do ( + @echo ------------------------------------------------ + @echo uploading exe for %%P + @echo ------------------------------------------------ + %%P setup.py build bdist_wininst upload || goto :error + @echo ------------------------------------------------ + @echo uploading wheel for %%P + @echo ------------------------------------------------ + %%P setup.py build bdist_wheel upload || goto :error + ) echo OK goto :eof ) -if "%1" == "setup-env" ( +if "%1" == "setup-dev-env" ( :setup-env - C:\python27\python.exe -c "import urllib2; url = urllib2.urlopen('https://raw.github.com/pypa/pip/master/contrib/get-pip.py'); data = url.read(); f = open('get-pip.py', 'w'); f.write(data)" - C:\python26\python.exe get-pip.py & C:\python26\scripts\pip install unittest2 wheel --upgrade - C:\python27\python.exe get-pip.py & C:\python27\scripts\pip install wheel --upgrade - C:\python33\python.exe get-pip.py & C:\python33\scripts\pip install wheel --upgrade - C:\python34\scripts\easy_install.exe wheel - rem 64-bit versions - C:\python27-64\python.exe get-pip.py & C:\python27-64\scripts\pip install wheel --upgrade - C:\python33-64\python.exe get-pip.py & C:\python33-64\scripts\pip install wheel --upgrade - C:\python34-64\scripts\easy_install.exe wheel - goto :eof -) - -if "%1" == "build-wheels" ( - :build-wheels - C:\Python26\python.exe setup.py build bdist_wheel || goto :error - C:\Python27\python.exe setup.py build bdist_wheel || goto :error - C:\Python33\python.exe setup.py build bdist_wheel || goto :error - C:\Python34\python.exe setup.py build bdist_wheel || goto :error - rem 64 bit versions - rem Python 2.7 + VS 2008 requires vcvars64.bat to be run first: - rem http://stackoverflow.com/questions/11072521/ - rem Windows SDK and .NET Framework 3.5 SP1 also need to be installed (sigh) - "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat" - C:\Python27-64\python.exe setup.py build bdist_wheel || goto :error - C:\Python33-64\python.exe setup.py build bdist_wheel || goto :error - C:\Python34-64\python.exe setup.py build bdist_wheel || goto :error - echo OK + @echo ------------------------------------------------ + @echo downloading pip installer + @echo ------------------------------------------------ + C:\python27\python.exe -c "import urllib2; r = urllib2.urlopen('https://raw.github.com/pypa/pip/master/contrib/get-pip.py'); open('get-pip.py', 'wb').write(r.read())" + for %%P in (%ALL_PYTHONS%) do ( + @echo ------------------------------------------------ + @echo installing pip for %%P + @echo ------------------------------------------------ + %%P get-pip.py + ) + for %%P in (%ALL_PYTHONS%) do ( + @echo ------------------------------------------------ + @echo installing deps for %%P + @echo ------------------------------------------------ + rem mandatory / for unittests + %%P -m pip install unittest2 ipaddress mock wmi wheel pypiwin32 --upgrade + rem nice to have + %%P -m pip install ipdb pep8 pyflakes flake8 --upgrade + ) goto :eof ) -if "%1" == "upload-wheels" ( - :build-wheels - C:\Python26\python.exe setup.py build bdist_wheel upload || goto :error - C:\Python27\python.exe setup.py build bdist_wheel upload || goto :error - C:\Python33\python.exe setup.py build bdist_wheel upload || goto :error - C:\Python34\python.exe setup.py build bdist_wheel upload || goto :error - rem 64 bit versions - rem Python 2.7 + VS 2008 requires vcvars64.bat to be run first: - rem http://stackoverflow.com/questions/11072521/ - rem Windows SDK and .NET Framework 3.5 SP1 also need to be installed (sigh) - "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat" - C:\Python27-64\python.exe setup.py build bdist_wheel upload || goto :error - C:\Python33-64\python.exe setup.py build bdist_wheel upload || goto :error - C:\Python34-64\python.exe setup.py build bdist_wheel upload || goto :error - echo OK +if "%1" == "flake8" ( + :flake8 + %PYTHON% -c "from flake8.main import main; main()" goto :eof ) goto :help :error - echo last command exited with error code %errorlevel% - exit /b %errorlevel% + @echo ------------------------------------------------ + @echo last command exited with error code %errorlevel% + @echo ------------------------------------------------ + @exit /b %errorlevel% goto :eof diff --git a/python/psutil/psutil.egg-info/PKG-INFO b/python/psutil/psutil.egg-info/PKG-INFO new file mode 100644 index 000000000000..e74d33f6587d --- /dev/null +++ b/python/psutil/psutil.egg-info/PKG-INFO @@ -0,0 +1,434 @@ +Metadata-Version: 1.1 +Name: psutil +Version: 3.1.1 +Summary: psutil is a cross-platform library for retrieving information onrunning processes and system utilization (CPU, memory, disks, network)in Python. +Home-page: https://github.com/giampaolo/psutil +Author: Giampaolo Rodola +Author-email: g.rodola gmail com +License: BSD +Description: .. image:: https://img.shields.io/pypi/dm/psutil.svg + :target: https://pypi.python.org/pypi/psutil#downloads + :alt: Downloads this month + + .. image:: https://api.travis-ci.org/giampaolo/psutil.png?branch=master + :target: https://travis-ci.org/giampaolo/psutil + :alt: Linux tests (Travis) + + .. image:: https://ci.appveyor.com/api/projects/status/qdwvw7v1t915ywr5/branch/master?svg=true + :target: https://ci.appveyor.com/project/giampaolo/psutil + :alt: Windows tests (Appveyor) + + .. image:: https://coveralls.io/repos/giampaolo/psutil/badge.svg?branch=master&service=github + :target: https://coveralls.io/github/giampaolo/psutil?branch=master + :alt: Test coverage (coverall.io) + + .. image:: https://img.shields.io/pypi/v/psutil.svg + :target: https://pypi.python.org/pypi/psutil/ + :alt: Latest version + + .. image:: https://img.shields.io/github/stars/giampaolo/psutil.svg + :target: https://github.com/giampaolo/psutil/ + :alt: Github stars + + .. image:: https://img.shields.io/scrutinizer/g/giampaolo/psutil.svg + :target: https://scrutinizer-ci.com/g/giampaolo/psutil/ + :alt: Code quality (scrutinizer-ci.com) + + .. image:: https://img.shields.io/pypi/l/psutil.svg + :target: https://pypi.python.org/pypi/psutil/ + :alt: License + + =========== + Quick links + =========== + + - `Home page `_ + - `Documentation `_ + - `Installation `_ + - `Download `_ + - `Forum `_ + - `Blog `_ + - `Development guide `_ + - `What's new `_ + + ======= + Summary + ======= + + psutil (python system and process utilities) is a cross-platform library for + retrieving information on **running processes** and **system utilization** + (CPU, memory, disks, network) in Python. It is useful mainly for **system + monitoring**, **profiling and limiting process resources** and **management of + running processes**. It implements many functionalities offered by command line + tools such as: ps, top, lsof, netstat, ifconfig, who, df, kill, free, nice, + ionice, iostat, iotop, uptime, pidof, tty, taskset, pmap. It currently supports + **Linux, Windows, OSX, FreeBSD** and **Sun Solaris**, both **32-bit** and + **64-bit** architectures, with Python versions from **2.6 to 3.5** (users of + Python 2.4 and 2.5 may use `2.1.3 `__ version). + `PyPy `__ is also known to work. + + ==================== + Example applications + ==================== + + .. image:: http://psutil.googlecode.com/svn/wiki/images/top-thumb.png + :target: http://psutil.googlecode.com/svn/wiki/images/top.png + :alt: top + + .. image:: http://psutil.googlecode.com/svn/wiki/images/nettop-thumb.png + :target: http://psutil.googlecode.com/svn/wiki/images/nettop.png + :alt: nettop + + .. image:: http://psutil.googlecode.com/svn/wiki/images/iotop-thumb.png + :target: http://psutil.googlecode.com/svn/wiki/images/iotop.png + :alt: iotop + + See also: + + * https://github.com/nicolargo/glances + * https://github.com/google/grr + * https://github.com/Jahaja/psdash + + ============== + Example usages + ============== + + CPU + === + + .. code-block:: python + + >>> import psutil + >>> psutil.cpu_times() + scputimes(user=3961.46, nice=169.729, system=2150.659, idle=16900.540, iowait=629.59, irq=0.0, softirq=19.42, steal=0.0, guest=0, nice=0.0) + >>> + >>> for x in range(3): + ... psutil.cpu_percent(interval=1) + ... + 4.0 + 5.9 + 3.8 + >>> + >>> for x in range(3): + ... psutil.cpu_percent(interval=1, percpu=True) + ... + [4.0, 6.9, 3.7, 9.2] + [7.0, 8.5, 2.4, 2.1] + [1.2, 9.0, 9.9, 7.2] + >>> + >>> + >>> for x in range(3): + ... psutil.cpu_times_percent(interval=1, percpu=False) + ... + scputimes(user=1.5, nice=0.0, system=0.5, idle=96.5, iowait=1.5, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0) + scputimes(user=1.0, nice=0.0, system=0.0, idle=99.0, iowait=0.0, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0) + scputimes(user=2.0, nice=0.0, system=0.0, idle=98.0, iowait=0.0, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0) + >>> + >>> psutil.cpu_count() + 4 + >>> psutil.cpu_count(logical=False) + 2 + >>> + + Memory + ====== + + .. code-block:: python + + >>> psutil.virtual_memory() + svmem(total=8374149120, available=2081050624, percent=75.1, used=8074080256, free=300068864, active=3294920704, inactive=1361616896, buffers=529895424, cached=1251086336) + >>> psutil.swap_memory() + sswap(total=2097147904, used=296128512, free=1801019392, percent=14.1, sin=304193536, sout=677842944) + >>> + + Disks + ===== + + .. code-block:: python + + >>> psutil.disk_partitions() + [sdiskpart(device='/dev/sda1', mountpoint='/', fstype='ext4', opts='rw,nosuid'), + sdiskpart(device='/dev/sda2', mountpoint='/home', fstype='ext, opts='rw')] + >>> + >>> psutil.disk_usage('/') + sdiskusage(total=21378641920, used=4809781248, free=15482871808, percent=22.5) + >>> + >>> psutil.disk_io_counters(perdisk=False) + sdiskio(read_count=719566, write_count=1082197, read_bytes=18626220032, write_bytes=24081764352, read_time=5023392, write_time=63199568) + >>> + + Network + ======= + + .. code-block:: python + + >>> psutil.net_io_counters(pernic=True) + {'eth0': netio(bytes_sent=485291293, bytes_recv=6004858642, packets_sent=3251564, packets_recv=4787798, errin=0, errout=0, dropin=0, dropout=0), + 'lo': netio(bytes_sent=2838627, bytes_recv=2838627, packets_sent=30567, packets_recv=30567, errin=0, errout=0, dropin=0, dropout=0)} + >>> + >>> psutil.net_connections() + [pconn(fd=115, family=, type=, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED', pid=1254), + pconn(fd=117, family=, type=, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING', pid=2987), + pconn(fd=-1, family=, type=, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED', pid=None), + pconn(fd=-1, family=, type=, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT', pid=None) + ...] + >>> + >>> psutil.net_if_addrs() + {'lo': [snic(family=, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1'), + snic(family=, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None), + snic(family=, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00')], + 'wlan0': [snic(family=, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255'), + snic(family=, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None), + snic(family=, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff')]} + >>> + >>> psutil.net_if_stats() + {'eth0': snicstats(isup=True, duplex=, speed=100, mtu=1500), + 'lo': snicstats(isup=True, duplex=, speed=0, mtu=65536)} + + Other system info + ================= + + .. code-block:: python + + >>> psutil.users() + [user(name='giampaolo', terminal='pts/2', host='localhost', started=1340737536.0), + user(name='giampaolo', terminal='pts/3', host='localhost', started=1340737792.0)] + >>> + >>> psutil.boot_time() + 1365519115.0 + >>> + + Process management + ================== + + .. code-block:: python + + >>> import psutil + >>> psutil.pids() + [1, 2, 3, 4, 5, 6, 7, 46, 48, 50, 51, 178, 182, 222, 223, 224, + 268, 1215, 1216, 1220, 1221, 1243, 1244, 1301, 1601, 2237, 2355, + 2637, 2774, 3932, 4176, 4177, 4185, 4187, 4189, 4225, 4243, 4245, + 4263, 4282, 4306, 4311, 4312, 4313, 4314, 4337, 4339, 4357, 4358, + 4363, 4383, 4395, 4408, 4433, 4443, 4445, 4446, 5167, 5234, 5235, + 5252, 5318, 5424, 5644, 6987, 7054, 7055, 7071] + >>> + >>> p = psutil.Process(7055) + >>> p.name() + 'python' + >>> p.exe() + '/usr/bin/python' + >>> p.cwd() + '/home/giampaolo' + >>> p.cmdline() + ['/usr/bin/python', 'main.py'] + >>> + >>> p.status() + 'running' + >>> p.username() + 'giampaolo' + >>> p.create_time() + 1267551141.5019531 + >>> p.terminal() + '/dev/pts/0' + >>> + >>> p.uids() + puids(real=1000, effective=1000, saved=1000) + >>> p.gids() + pgids(real=1000, effective=1000, saved=1000) + >>> + >>> p.cpu_times() + pcputimes(user=1.02, system=0.31) + >>> p.cpu_percent(interval=1.0) + 12.1 + >>> p.cpu_affinity() + [0, 1, 2, 3] + >>> p.cpu_affinity([0]) # set + >>> + >>> p.memory_percent() + 0.63423 + >>> + >>> p.memory_info() + pmem(rss=7471104, vms=68513792) + >>> p.memory_info_ex() + extmem(rss=9662464, vms=49192960, shared=3612672, text=2564096, lib=0, data=5754880, dirty=0) + >>> p.memory_maps() + [pmmap_grouped(path='/lib/x86_64-linux-gnu/libutil-2.15.so', rss=16384, anonymous=8192, swap=0), + pmmap_grouped(path='/lib/x86_64-linux-gnu/libc-2.15.so', rss=6384, anonymous=15, swap=0), + pmmap_grouped(path='/lib/x86_64-linux-gnu/libcrypto.so.1.0.0', rss=34124, anonymous=1245, swap=0), + pmmap_grouped(path='[heap]', rss=54653, anonymous=8192, swap=0), + pmmap_grouped(path='[stack]', rss=1542, anonymous=166, swap=0), + ...] + >>> + >>> p.io_counters() + pio(read_count=478001, write_count=59371, read_bytes=700416, write_bytes=69632) + >>> + >>> p.open_files() + [popenfile(path='/home/giampaolo/svn/psutil/somefile', fd=3)] + >>> + >>> p.connections() + [pconn(fd=115, family=, type=, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED'), + pconn(fd=117, family=, type=, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING'), + pconn(fd=119, family=, type=, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED'), + pconn(fd=123, family=, type=, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT')] + >>> + >>> p.num_threads() + 4 + >>> p.num_fds() + 8 + >>> p.threads() + [pthread(id=5234, user_time=22.5, system_time=9.2891), + pthread(id=5235, user_time=0.0, system_time=0.0), + pthread(id=5236, user_time=0.0, system_time=0.0), + pthread(id=5237, user_time=0.0707, system_time=1.1)] + >>> + >>> p.num_ctx_switches() + pctxsw(voluntary=78, involuntary=19) + >>> + >>> p.nice() + 0 + >>> p.nice(10) # set + >>> + >>> p.ionice(psutil.IOPRIO_CLASS_IDLE) # IO priority (Win and Linux only) + >>> p.ionice() + pionice(ioclass=, value=0) + >>> + >>> p.rlimit(psutil.RLIMIT_NOFILE, (5, 5)) # set resource limits (Linux only) + >>> p.rlimit(psutil.RLIMIT_NOFILE) + (5, 5) + >>> + >>> p.suspend() + >>> p.resume() + >>> + >>> p.terminate() + >>> p.wait(timeout=3) + 0 + >>> + >>> psutil.test() + USER PID %CPU %MEM VSZ RSS TTY START TIME COMMAND + root 1 0.0 0.0 24584 2240 Jun17 00:00 init + root 2 0.0 0.0 0 0 Jun17 00:00 kthreadd + root 3 0.0 0.0 0 0 Jun17 00:05 ksoftirqd/0 + ... + giampaolo 31475 0.0 0.0 20760 3024 /dev/pts/0 Jun19 00:00 python2.4 + giampaolo 31721 0.0 2.2 773060 181896 00:04 10:30 chrome + root 31763 0.0 0.0 0 0 00:05 00:00 kworker/0:1 + >>> + + Further process APIs + ==================== + + .. code-block:: python + + >>> for p in psutil.process_iter(): + ... print(p) + ... + psutil.Process(pid=1, name='init') + psutil.Process(pid=2, name='kthreadd') + psutil.Process(pid=3, name='ksoftirqd/0') + ... + >>> + >>> def on_terminate(proc): + ... print("process {} terminated".format(proc)) + ... + >>> # waits for multiple processes to terminate + >>> gone, alive = psutil.wait_procs(procs_list, 3, callback=on_terminate) + >>> + + ====== + Donate + ====== + + A lot of time and effort went into making psutil as it is right now. + If you feel psutil is useful to you or your business and want to support its future development please consider donating me (`Giampaolo Rodola' `_) some money. + I only ask for a small donation, but of course I appreciate any amount. + + .. image:: http://www.paypal.com/en_US/i/btn/x-click-but04.gif + :target: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A9ZS7PKKRM3S8 + :alt: Donate via PayPal + + Don't want to donate money? Then maybe you could `write me a recommendation on Linkedin `_. + + ============ + Mailing list + ============ + + http://groups.google.com/group/psutil/ + + ======== + Timeline + ======== + + - 2015-07-15: `psutil-3.1.1.tar.gz `_ + - 2015-07-15: `psutil-3.1.0.tar.gz `_ + - 2015-06-18: `psutil-3.0.1.tar.gz `_ + - 2015-06-13: `psutil-3.0.0.tar.gz `_ + - 2015-02-02: `psutil-2.2.1.tar.gz `_ + - 2015-01-06: `psutil-2.2.0.tar.gz `_ + - 2014-09-26: `psutil-2.1.3.tar.gz `_ + - 2014-09-21: `psutil-2.1.2.tar.gz `_ + - 2014-04-30: `psutil-2.1.1.tar.gz `_ + - 2014-04-08: `psutil-2.1.0.tar.gz `_ + - 2014-03-10: `psutil-2.0.0.tar.gz `_ + - 2013-11-25: `psutil-1.2.1.tar.gz `_ + - 2013-11-20: `psutil-1.2.0.tar.gz `_ + - 2013-11-07: `psutil-1.1.3.tar.gz `_ + - 2013-10-22: `psutil-1.1.2.tar.gz `_ + - 2013-10-08: `psutil-1.1.1.tar.gz `_ + - 2013-09-28: `psutil-1.1.0.tar.gz `_ + - 2013-07-12: `psutil-1.0.1.tar.gz `_ + - 2013-07-10: `psutil-1.0.0.tar.gz `_ + - 2013-05-03: `psutil-0.7.1.tar.gz `_ + - 2013-04-12: `psutil-0.7.0.tar.gz `_ + - 2012-08-16: `psutil-0.6.1.tar.gz `_ + - 2012-08-13: `psutil-0.6.0.tar.gz `_ + - 2012-06-29: `psutil-0.5.1.tar.gz `_ + - 2012-06-27: `psutil-0.5.0.tar.gz `_ + - 2011-12-14: `psutil-0.4.1.tar.gz `_ + - 2011-10-29: `psutil-0.4.0.tar.gz `_ + - 2011-07-08: `psutil-0.3.0.tar.gz `_ + - 2011-03-20: `psutil-0.2.1.tar.gz `_ + - 2010-11-13: `psutil-0.2.0.tar.gz `_ + - 2010-03-02: `psutil-0.1.3.tar.gz `_ + - 2009-05-06: `psutil-0.1.2.tar.gz `_ + - 2009-03-06: `psutil-0.1.1.tar.gz `_ + - 2009-01-27: `psutil-0.1.0.tar.gz `_ + +Keywords: ps,top,kill,free,lsof,netstat,nice,tty,ionice,uptime,taskmgr,process,df,iotop,iostat,ifconfig,taskset,who,pidof,pmap,smem,pstree,monitoring,ulimit,prlimit +Platform: Platform Independent +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Environment :: Win32 (MS Windows) +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Information Technology +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows :: Windows NT/2000 +Classifier: Operating System :: Microsoft +Classifier: Operating System :: OS Independent +Classifier: Operating System :: POSIX :: BSD :: FreeBSD +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: POSIX :: SunOS/Solaris +Classifier: Operating System :: POSIX +Classifier: Programming Language :: C +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.0 +Classifier: Programming Language :: Python :: 3.1 +Classifier: Programming Language :: Python :: 3.2 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Programming Language :: Python +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: System :: Benchmark +Classifier: Topic :: System :: Hardware +Classifier: Topic :: System :: Monitoring +Classifier: Topic :: System :: Networking :: Monitoring +Classifier: Topic :: System :: Networking +Classifier: Topic :: System :: Systems Administration +Classifier: Topic :: Utilities diff --git a/python/psutil/psutil.egg-info/SOURCES.txt b/python/psutil/psutil.egg-info/SOURCES.txt new file mode 100644 index 000000000000..990b53e75f1c --- /dev/null +++ b/python/psutil/psutil.egg-info/SOURCES.txt @@ -0,0 +1,96 @@ +.coveragerc +.git-pre-commit +.gitignore +.travis.yml +CREDITS +HISTORY.rst +INSTALL.rst +LICENSE +MANIFEST.in +Makefile +README.rst +TODO +make.bat +setup.py +tox.ini +docs/Makefile +docs/README +docs/conf.py +docs/index.rst +docs/make.bat +docs/xxx +docs/_static/copybutton.js +docs/_static/favicon.ico +docs/_static/logo.png +docs/_static/sidebar.js +docs/_template/globaltoc.html +docs/_template/indexcontent.html +docs/_template/indexsidebar.html +docs/_template/page.html +docs/_themes/pydoctheme/theme.conf +docs/_themes/pydoctheme/static/pydoctheme.css +examples/disk_usage.py +examples/free.py +examples/ifconfig.py +examples/iotop.py +examples/killall.py +examples/meminfo.py +examples/netstat.py +examples/nettop.py +examples/pidof.py +examples/pmap.py +examples/process_detail.py +examples/ps.py +examples/pstree.py +examples/top.py +examples/who.py +psutil/__init__.py +psutil/_common.py +psutil/_compat.py +psutil/_psbsd.py +psutil/_pslinux.py +psutil/_psosx.py +psutil/_psposix.py +psutil/_pssunos.py +psutil/_psutil_bsd.c +psutil/_psutil_bsd.h +psutil/_psutil_common.c +psutil/_psutil_common.h +psutil/_psutil_linux.c +psutil/_psutil_linux.h +psutil/_psutil_osx.c +psutil/_psutil_osx.h +psutil/_psutil_posix.c +psutil/_psutil_posix.h +psutil/_psutil_sunos.c +psutil/_psutil_sunos.h +psutil/_psutil_windows.c +psutil/_psutil_windows.h +psutil/_pswindows.py +psutil.egg-info/PKG-INFO +psutil.egg-info/SOURCES.txt +psutil.egg-info/dependency_links.txt +psutil.egg-info/top_level.txt +psutil/arch/bsd/process_info.c +psutil/arch/bsd/process_info.h +psutil/arch/osx/process_info.c +psutil/arch/osx/process_info.h +psutil/arch/windows/glpi.h +psutil/arch/windows/inet_ntop.c +psutil/arch/windows/inet_ntop.h +psutil/arch/windows/ntextapi.h +psutil/arch/windows/process_handles.c +psutil/arch/windows/process_handles.h +psutil/arch/windows/process_info.c +psutil/arch/windows/process_info.h +psutil/arch/windows/security.c +psutil/arch/windows/security.h +test/README.rst +test/_bsd.py +test/_linux.py +test/_osx.py +test/_posix.py +test/_sunos.py +test/_windows.py +test/test_memory_leaks.py +test/test_psutil.py \ No newline at end of file diff --git a/python/psutil/psutil.egg-info/dependency_links.txt b/python/psutil/psutil.egg-info/dependency_links.txt new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/python/psutil/psutil.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/python/psutil/psutil.egg-info/top_level.txt b/python/psutil/psutil.egg-info/top_level.txt new file mode 100644 index 000000000000..a4d92cc08db6 --- /dev/null +++ b/python/psutil/psutil.egg-info/top_level.txt @@ -0,0 +1 @@ +psutil diff --git a/python/psutil/psutil/__init__.py b/python/psutil/psutil/__init__.py index 8cb551874d31..1444425b8d2d 100644 --- a/python/psutil/psutil/__init__.py +++ b/python/psutil/psutil/__init__.py @@ -12,106 +12,78 @@ from __future__ import division -__author__ = "Giampaolo Rodola'" -__version__ = "2.1.3" -version_info = tuple([int(num) for num in __version__.split('.')]) - -__all__ = [ - # exceptions - "Error", "NoSuchProcess", "AccessDenied", "TimeoutExpired", - # constants - "version_info", "__version__", - "STATUS_RUNNING", "STATUS_IDLE", "STATUS_SLEEPING", "STATUS_DISK_SLEEP", - "STATUS_STOPPED", "STATUS_TRACING_STOP", "STATUS_ZOMBIE", "STATUS_DEAD", - "STATUS_WAKING", "STATUS_LOCKED", "STATUS_WAITING", "STATUS_LOCKED", - "CONN_ESTABLISHED", "CONN_SYN_SENT", "CONN_SYN_RECV", "CONN_FIN_WAIT1", - "CONN_FIN_WAIT2", "CONN_TIME_WAIT", "CONN_CLOSE", "CONN_CLOSE_WAIT", - "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING", "CONN_NONE", - # classes - "Process", "Popen", - # functions - "pid_exists", "pids", "process_iter", "wait_procs", # proc - "virtual_memory", "swap_memory", # memory - "cpu_times", "cpu_percent", "cpu_times_percent", "cpu_count", # cpu - "net_io_counters", "net_connections", # network - "disk_io_counters", "disk_partitions", "disk_usage", # disk - "users", "boot_time", # others -] - -import sys +import collections +import errno +import functools import os -import time import signal -import warnings -import errno import subprocess +import sys +import time try: import pwd except ImportError: pwd = None -from psutil._common import memoize -from psutil._compat import property, callable, long, defaultdict -from psutil._compat import (wraps as _wraps, - PY3 as _PY3) -from psutil._common import (deprecated_method as _deprecated_method, - deprecated as _deprecated, - sdiskio as _nt_sys_diskio, - snetio as _nt_sys_netio) - -from psutil._common import (STATUS_RUNNING, # NOQA - STATUS_SLEEPING, - STATUS_DISK_SLEEP, - STATUS_STOPPED, - STATUS_TRACING_STOP, - STATUS_ZOMBIE, - STATUS_DEAD, - STATUS_WAKING, - STATUS_LOCKED, - STATUS_IDLE, # bsd - STATUS_WAITING, # bsd - STATUS_LOCKED) # bsd - -from psutil._common import (CONN_ESTABLISHED, - CONN_SYN_SENT, - CONN_SYN_RECV, - CONN_FIN_WAIT1, - CONN_FIN_WAIT2, - CONN_TIME_WAIT, - CONN_CLOSE, - CONN_CLOSE_WAIT, - CONN_LAST_ACK, - CONN_LISTEN, - CONN_CLOSING, - CONN_NONE) +from . import _common +from ._common import memoize +from ._compat import callable, long +from ._compat import PY3 as _PY3 + +from ._common import (STATUS_RUNNING, # NOQA + STATUS_SLEEPING, + STATUS_DISK_SLEEP, + STATUS_STOPPED, + STATUS_TRACING_STOP, + STATUS_ZOMBIE, + STATUS_DEAD, + STATUS_WAKING, + STATUS_LOCKED, + STATUS_IDLE, # bsd + STATUS_WAITING) # bsd + +from ._common import (CONN_ESTABLISHED, + CONN_SYN_SENT, + CONN_SYN_RECV, + CONN_FIN_WAIT1, + CONN_FIN_WAIT2, + CONN_TIME_WAIT, + CONN_CLOSE, + CONN_CLOSE_WAIT, + CONN_LAST_ACK, + CONN_LISTEN, + CONN_CLOSING, + CONN_NONE) + +from ._common import (NIC_DUPLEX_FULL, # NOQA + NIC_DUPLEX_HALF, + NIC_DUPLEX_UNKNOWN) if sys.platform.startswith("linux"): - import psutil._pslinux as _psplatform - from psutil._pslinux import (phymem_buffers, # NOQA - cached_phymem) - - from psutil._pslinux import (IOPRIO_CLASS_NONE, # NOQA - IOPRIO_CLASS_RT, - IOPRIO_CLASS_BE, - IOPRIO_CLASS_IDLE) + from . import _pslinux as _psplatform + + from ._pslinux import (IOPRIO_CLASS_NONE, # NOQA + IOPRIO_CLASS_RT, + IOPRIO_CLASS_BE, + IOPRIO_CLASS_IDLE) # Linux >= 2.6.36 if _psplatform.HAS_PRLIMIT: - from _psutil_linux import (RLIM_INFINITY, # NOQA - RLIMIT_AS, - RLIMIT_CORE, - RLIMIT_CPU, - RLIMIT_DATA, - RLIMIT_FSIZE, - RLIMIT_LOCKS, - RLIMIT_MEMLOCK, - RLIMIT_NOFILE, - RLIMIT_NPROC, - RLIMIT_RSS, - RLIMIT_STACK) + from ._psutil_linux import (RLIM_INFINITY, # NOQA + RLIMIT_AS, + RLIMIT_CORE, + RLIMIT_CPU, + RLIMIT_DATA, + RLIMIT_FSIZE, + RLIMIT_LOCKS, + RLIMIT_MEMLOCK, + RLIMIT_NOFILE, + RLIMIT_NPROC, + RLIMIT_RSS, + RLIMIT_STACK) # Kinda ugly but considerably faster than using hasattr() and # setattr() against the module object (we are at import time: # speed matters). - import _psutil_linux + from . import _psutil_linux try: RLIMIT_MSGQUEUE = _psutil_linux.RLIMIT_MSGQUEUE except AttributeError: @@ -135,38 +107,80 @@ del _psutil_linux elif sys.platform.startswith("win32"): - import psutil._pswindows as _psplatform - from _psutil_windows import (ABOVE_NORMAL_PRIORITY_CLASS, # NOQA - BELOW_NORMAL_PRIORITY_CLASS, - HIGH_PRIORITY_CLASS, - IDLE_PRIORITY_CLASS, - NORMAL_PRIORITY_CLASS, - REALTIME_PRIORITY_CLASS) - from psutil._pswindows import CONN_DELETE_TCB # NOQA + from . import _pswindows as _psplatform + from ._psutil_windows import (ABOVE_NORMAL_PRIORITY_CLASS, # NOQA + BELOW_NORMAL_PRIORITY_CLASS, + HIGH_PRIORITY_CLASS, + IDLE_PRIORITY_CLASS, + NORMAL_PRIORITY_CLASS, + REALTIME_PRIORITY_CLASS) + from ._pswindows import CONN_DELETE_TCB # NOQA elif sys.platform.startswith("darwin"): - import psutil._psosx as _psplatform + from . import _psosx as _psplatform elif sys.platform.startswith("freebsd"): - import psutil._psbsd as _psplatform + from . import _psbsd as _psplatform elif sys.platform.startswith("sunos"): - import psutil._pssunos as _psplatform - from psutil._pssunos import (CONN_IDLE, # NOQA - CONN_BOUND) + from . import _pssunos as _psplatform + from ._pssunos import (CONN_IDLE, # NOQA + CONN_BOUND) -else: +else: # pragma: no cover raise NotImplementedError('platform %s is not supported' % sys.platform) -__all__.extend(_psplatform.__extra__all__) - +__all__ = [ + # exceptions + "Error", "NoSuchProcess", "ZombieProcess", "AccessDenied", + "TimeoutExpired", + # constants + "version_info", "__version__", + "STATUS_RUNNING", "STATUS_IDLE", "STATUS_SLEEPING", "STATUS_DISK_SLEEP", + "STATUS_STOPPED", "STATUS_TRACING_STOP", "STATUS_ZOMBIE", "STATUS_DEAD", + "STATUS_WAKING", "STATUS_LOCKED", "STATUS_WAITING", "STATUS_LOCKED", + "CONN_ESTABLISHED", "CONN_SYN_SENT", "CONN_SYN_RECV", "CONN_FIN_WAIT1", + "CONN_FIN_WAIT2", "CONN_TIME_WAIT", "CONN_CLOSE", "CONN_CLOSE_WAIT", + "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING", "CONN_NONE", + "AF_LINK", + "NIC_DUPLEX_FULL", "NIC_DUPLEX_HALF", "NIC_DUPLEX_UNKNOWN", + # classes + "Process", "Popen", + # functions + "pid_exists", "pids", "process_iter", "wait_procs", # proc + "virtual_memory", "swap_memory", # memory + "cpu_times", "cpu_percent", "cpu_times_percent", "cpu_count", # cpu + "net_io_counters", "net_connections", "net_if_addrs", # network + "net_if_stats", + "disk_io_counters", "disk_partitions", "disk_usage", # disk + "users", "boot_time", # others +] +__all__.extend(_psplatform.__extra__all__) +__author__ = "Giampaolo Rodola'" +__version__ = "3.1.1" +version_info = tuple([int(num) for num in __version__.split('.')]) +AF_LINK = _psplatform.AF_LINK _TOTAL_PHYMEM = None _POSIX = os.name == 'posix' _WINDOWS = os.name == 'nt' _timer = getattr(time, 'monotonic', time.time) +# Sanity check in case the user messed up with psutil installation +# or did something weird with sys.path. In this case we might end +# up importing a python module using a C extension module which +# was compiled for a different version of psutil. +# We want to prevent that by failing sooner rather than later. +# See: https://github.com/giampaolo/psutil/issues/564 +if (int(__version__.replace('.', '')) != + getattr(_psplatform.cext, 'version', None)): + msg = "version conflict: %r C extension module was built for another " \ + "version of psutil (different than %s)" % (_psplatform.cext.__file__, + __version__) + raise ImportError(msg) + + # ===================================================================== # --- exceptions # ===================================================================== @@ -176,14 +190,24 @@ class Error(Exception): from this one. """ + def __init__(self, msg=""): + self.msg = msg + + def __repr__(self): + ret = "%s.%s %s" % (self.__class__.__module__, + self.__class__.__name__, self.msg) + return ret.strip() + + __str__ = __repr__ + class NoSuchProcess(Error): """Exception raised when a process with a certain PID doesn't - or no longer exists (zombie). + or no longer exists. """ def __init__(self, pid, name=None, msg=None): - Error.__init__(self) + Error.__init__(self, msg) self.pid = pid self.name = name self.msg = msg @@ -194,15 +218,37 @@ def __init__(self, pid, name=None, msg=None): details = "(pid=%s)" % self.pid self.msg = "process no longer exists " + details - def __str__(self): - return self.msg + +class ZombieProcess(NoSuchProcess): + """Exception raised when querying a zombie process. This is + raised on OSX, BSD and Solaris only, and not always: depending + on the query the OS may be able to succeed anyway. + On Linux all zombie processes are querable (hence this is never + raised). Windows doesn't have zombie processes. + """ + + def __init__(self, pid, name=None, ppid=None, msg=None): + Error.__init__(self, msg) + self.pid = pid + self.ppid = ppid + self.name = name + self.msg = msg + if msg is None: + if name and ppid: + details = "(pid=%s, name=%s, ppid=%s)" % ( + self.pid, repr(self.name), self.ppid) + elif name: + details = "(pid=%s, name=%s)" % (self.pid, repr(self.name)) + else: + details = "(pid=%s)" % self.pid + self.msg = "process still exists but it's a zombie " + details class AccessDenied(Error): """Exception raised when permission to perform an action is denied.""" def __init__(self, pid=None, name=None, msg=None): - Error.__init__(self) + Error.__init__(self, msg) self.pid = pid self.name = name self.msg = msg @@ -214,9 +260,6 @@ def __init__(self, pid=None, name=None, msg=None): else: self.msg = "" - def __str__(self): - return self.msg - class TimeoutExpired(Error): """Raised on Process.wait(timeout) if timeout expires and process @@ -224,21 +267,19 @@ class TimeoutExpired(Error): """ def __init__(self, seconds, pid=None, name=None): - Error.__init__(self) + Error.__init__(self, "timeout after %s seconds" % seconds) self.seconds = seconds self.pid = pid self.name = name - self.msg = "timeout after %s seconds" % seconds if (pid is not None) and (name is not None): self.msg += " (pid=%s, name=%s)" % (pid, repr(name)) elif (pid is not None): self.msg += " (pid=%s)" % self.pid - def __str__(self): - return self.msg # push exception classes into platform specific module namespace _psplatform.NoSuchProcess = NoSuchProcess +_psplatform.ZombieProcess = ZombieProcess _psplatform.AccessDenied = AccessDenied _psplatform.TimeoutExpired = TimeoutExpired @@ -247,11 +288,12 @@ def __str__(self): # --- Process class # ===================================================================== + def _assert_pid_not_reused(fun): """Decorator which raises NoSuchProcess in case a process is no longer running or its PID has been reused. """ - @_wraps(fun) + @functools.wraps(fun) def wrapper(self, *args, **kwargs): if not self.is_running(): raise NoSuchProcess(self.pid, self._name) @@ -325,6 +367,11 @@ def _init(self, pid, _ignore_nsp=False): # process creation time on all platforms even as a # limited user pass + except ZombieProcess: + # Let's consider a zombie process as legitimate as + # tehcnically it's still alive (it can be queried, + # although not always, and it's returned by pids()). + pass except NoSuchProcess: if not _ignore_nsp: msg = 'no process found with pid %s' % pid @@ -341,6 +388,8 @@ def __str__(self): try: pid = self.pid name = repr(self.name()) + except ZombieProcess: + details = "(pid=%s (zombie))" % self.pid except NoSuchProcess: details = "(pid=%s (terminated))" % self.pid except AccessDenied: @@ -370,7 +419,7 @@ def __hash__(self): # --- utility methods - def as_dict(self, attrs=[], ad_value=None): + def as_dict(self, attrs=None, ad_value=None): """Utility method returning process information as a hashable dictionary. @@ -380,32 +429,17 @@ def as_dict(self, attrs=[], ad_value=None): only) attributes are assumed. 'ad_value' is the value which gets assigned in case - AccessDenied exception is raised when retrieving that - particular process information. + AccessDenied or ZombieProcess exception is raised when + retrieving that particular process information. """ excluded_names = set( ['send_signal', 'suspend', 'resume', 'terminate', 'kill', 'wait', 'is_running', 'as_dict', 'parent', 'children', 'rlimit']) retdict = dict() - ls = set(attrs or [x for x in dir(self) if not x.startswith('get')]) + ls = set(attrs or [x for x in dir(self)]) for name in ls: if name.startswith('_'): continue - if name.startswith('set_'): - continue - if name.startswith('get_'): - msg = "%s() is deprecated; use %s() instead" % (name, name[4:]) - warnings.warn(msg, category=DeprecationWarning, stacklevel=2) - name = name[4:] - if name in ls: - continue - if name == 'getcwd': - msg = "getcwd() is deprecated; use cwd() instead" - warnings.warn(msg, category=DeprecationWarning, stacklevel=2) - name = 'cwd' - if name in ls: - continue - if name in excluded_names: continue try: @@ -414,7 +448,7 @@ def as_dict(self, attrs=[], ad_value=None): ret = attr() else: ret = attr - except AccessDenied: + except (AccessDenied, ZombieProcess): ret = ad_value except NotImplementedError: # in case of not implemented functionality (may happen @@ -433,9 +467,10 @@ def parent(self): """ ppid = self.ppid() if ppid is not None: + ctime = self.create_time() try: parent = Process(ppid) - if parent.create_time() <= self.create_time(): + if parent.create_time() <= ctime: return parent # ...else ppid has been reused by another process except NoSuchProcess: @@ -480,8 +515,7 @@ def ppid(self): if _POSIX: return self._proc.ppid() else: - if self._ppid is None: - self._ppid = self._proc.ppid() + self._ppid = self._ppid or self._proc.ppid() return self._ppid def name(self): @@ -520,9 +554,9 @@ def guess_it(fallback): # Attempt to guess only in case of an absolute path. # It is not safe otherwise as the process might have # changed cwd. - if (os.path.isabs(exe) - and os.path.isfile(exe) - and os.access(exe, os.X_OK)): + if (os.path.isabs(exe) and + os.path.isfile(exe) and + os.access(exe, os.X_OK)): return exe if isinstance(fallback, AccessDenied): raise fallback @@ -531,8 +565,7 @@ def guess_it(fallback): if self._exe is None: try: exe = self._proc.exe() - except AccessDenied: - err = sys.exc_info()[1] + except AccessDenied as err: return guess_it(fallback=err) else: if not exe: @@ -552,7 +585,10 @@ def cmdline(self): def status(self): """The process current status as a STATUS_* constant.""" - return self._proc.status() + try: + return self._proc.status() + except ZombieProcess: + return STATUS_ZOMBIE def username(self): """The name of the user that owns the process. @@ -563,7 +599,12 @@ def username(self): # might happen if python was installed from sources raise ImportError( "requires pwd module shipped with standard python") - return pwd.getpwuid(self.uids().real).pw_name + real_uid = self.uids().real + try: + return pwd.getpwuid(real_uid).pw_name + except KeyError: + # the uid can't be resolved by the system + return str(real_uid) else: return self._proc.username() @@ -644,7 +685,7 @@ def ionice(self, ioclass=None, value=None): """ if ioclass is None: if value is not None: - raise ValueError("'ioclass' must be specified") + raise ValueError("'ioclass' argument must be specified") return self._proc.ionice_get() else: return self._proc.ionice_set(ioclass, value) @@ -667,18 +708,22 @@ def rlimit(self, resource, limits=None): else: return self._proc.rlimit(resource, limits) - # Windows and Linux only + # Windows, Linux and BSD only if hasattr(_psplatform.Process, "cpu_affinity_get"): def cpu_affinity(self, cpus=None): """Get or set process CPU affinity. If specified 'cpus' must be a list of CPUs for which you want to set the affinity (e.g. [0, 1]). + (Windows, Linux and BSD only). """ + # Automatically remove duplicates both on get and + # set (for get it's not really necessary, it's + # just for extra safety). if cpus is None: - return self._proc.cpu_affinity_get() + return list(set(self._proc.cpu_affinity_get())) else: - self._proc.cpu_affinity_set(cpus) + self._proc.cpu_affinity_set(list(set(cpus))) if _WINDOWS: @@ -750,7 +795,7 @@ def children(self, recursive=False): # (self) it means child's PID has been reused if self.create_time() <= p.create_time(): ret.append(p) - except NoSuchProcess: + except (NoSuchProcess, ZombieProcess): pass else: # Windows only (faster) @@ -762,24 +807,24 @@ def children(self, recursive=False): # (self) it means child's PID has been reused if self.create_time() <= child.create_time(): ret.append(child) - except NoSuchProcess: + except (NoSuchProcess, ZombieProcess): pass else: # construct a dict where 'values' are all the processes # having 'key' as their parent - table = defaultdict(list) + table = collections.defaultdict(list) if ppid_map is None: for p in process_iter(): try: table[p.ppid()].append(p) - except NoSuchProcess: + except (NoSuchProcess, ZombieProcess): pass else: for pid, ppid in ppid_map.items(): try: p = Process(pid) table[ppid].append(p) - except NoSuchProcess: + except (NoSuchProcess, ZombieProcess): pass # At this point we have a mapping table where table[self.pid] # are the current process' children. @@ -792,7 +837,7 @@ def children(self, recursive=False): # if child happens to be older than its parent # (self) it means child's PID has been reused intime = self.create_time() <= child.create_time() - except NoSuchProcess: + except (NoSuchProcess, ZombieProcess): pass else: if intime: @@ -831,9 +876,11 @@ def cpu_percent(self, interval=None): blocking = interval is not None and interval > 0.0 num_cpus = cpu_count() if _POSIX: - timer = lambda: _timer() * num_cpus + def timer(): + return _timer() * num_cpus else: - timer = lambda: sum(cpu_times()) + def timer(): + return sum(cpu_times()) if blocking: st1 = timer() pt1 = self._proc.cpu_times() @@ -908,7 +955,7 @@ def memory_percent(self): return 0.0 def memory_maps(self, grouped=True): - """Return process' mapped memory regions as a list of nameduples + """Return process' mapped memory regions as a list of namedtuples whose fields are variable depending on the platform. If 'grouped' is True the mapped regions with the same 'path' @@ -964,10 +1011,15 @@ def connections(self, kind='inet'): if _POSIX: def _send_signal(self, sig): + if self.pid == 0: + # see "man 2 kill" + raise ValueError( + "preventing sending signal to process with PID 0 as it " + "would affect every process in the process group of the " + "calling process (os.getpid()) instead of PID 0") try: os.kill(self.pid, sig) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno == errno.ESRCH: self._gone = True raise NoSuchProcess(self.pid, self._name) @@ -1049,119 +1101,12 @@ def wait(self, timeout=None): raise ValueError("timeout must be a positive integer") return self._proc.wait(timeout) - # --- deprecated APIs - - _locals = set(locals()) - - @_deprecated_method(replacement='children') - def get_children(self): - pass - - @_deprecated_method(replacement='connections') - def get_connections(self): - pass - - if "cpu_affinity" in _locals: - @_deprecated_method(replacement='cpu_affinity') - def get_cpu_affinity(self): - pass - - @_deprecated_method(replacement='cpu_affinity') - def set_cpu_affinity(self, cpus): - pass - - @_deprecated_method(replacement='cpu_percent') - def get_cpu_percent(self): - pass - - @_deprecated_method(replacement='cpu_times') - def get_cpu_times(self): - pass - - @_deprecated_method(replacement='cwd') - def getcwd(self): - pass - - @_deprecated_method(replacement='memory_info_ex') - def get_ext_memory_info(self): - pass - - if "io_counters" in _locals: - @_deprecated_method(replacement='io_counters') - def get_io_counters(self): - pass - - if "ionice" in _locals: - @_deprecated_method(replacement='ionice') - def get_ionice(self): - pass - - @_deprecated_method(replacement='ionice') - def set_ionice(self, ioclass, value=None): - pass - - @_deprecated_method(replacement='memory_info') - def get_memory_info(self): - pass - - @_deprecated_method(replacement='memory_maps') - def get_memory_maps(self): - pass - - @_deprecated_method(replacement='memory_percent') - def get_memory_percent(self): - pass - - @_deprecated_method(replacement='nice') - def get_nice(self): - pass - - @_deprecated_method(replacement='num_ctx_switches') - def get_num_ctx_switches(self): - pass - - if 'num_fds' in _locals: - @_deprecated_method(replacement='num_fds') - def get_num_fds(self): - pass - - if 'num_handles' in _locals: - @_deprecated_method(replacement='num_handles') - def get_num_handles(self): - pass - - @_deprecated_method(replacement='num_threads') - def get_num_threads(self): - pass - - @_deprecated_method(replacement='open_files') - def get_open_files(self): - pass - - if "rlimit" in _locals: - @_deprecated_method(replacement='rlimit') - def get_rlimit(self): - pass - - @_deprecated_method(replacement='rlimit') - def set_rlimit(self, resource, limits): - pass - - @_deprecated_method(replacement='threads') - def get_threads(self): - pass - - @_deprecated_method(replacement='nice') - def set_nice(self, value): - pass - - del _locals - # ===================================================================== # --- Popen class # ===================================================================== + class Popen(Process): """A more convenient interface to stdlib subprocess module. It starts a sub process and deals with it exactly as when using @@ -1229,6 +1174,7 @@ def wait(self, timeout=None): # --- system processes related functions # ===================================================================== + def pids(): """Return a list of current running PIDs.""" return _psplatform.pids() @@ -1395,13 +1341,14 @@ def check_gone(proc, timeout): # --- CPU related functions # ===================================================================== + @memoize def cpu_count(logical=True): """Return the number of logical CPUs in the system (same as os.cpu_count() in Python 3.4). If logical is False return the number of physical cores only - (hyper thread CPUs are excluded). + (e.g. hyper thread CPUs are excluded). Return None if undetermined. @@ -1431,7 +1378,7 @@ def cpu_times(percpu=False): - guest (Linux >= 2.6.24) - guest_nice (Linux >= 3.2.0) - When percpu is True return a list of nameduples for each CPU. + When percpu is True return a list of namedtuples for each CPU. First element of the list refers to first CPU, second element to second CPU and so on. The order of the list is consistent across calls. @@ -1558,23 +1505,20 @@ def calculate(t1, t2): except ZeroDivisionError: field_perc = 0.0 field_perc = round(field_perc, 1) - if _WINDOWS: - # XXX - # Work around: - # https://github.com/giampaolo/psutil/issues/392 - # CPU times are always supposed to increase over time - # or at least remain the same and that's because time - # cannot go backwards. - # Surprisingly sometimes this might not be the case on - # Windows where 'system' CPU time can be smaller - # compared to the previous call, resulting in corrupted - # percentages (< 0 or > 100). - # I really don't know what to do about that except - # forcing the value to 0 or 100. - if field_perc > 100.0: - field_perc = 100.0 - elif field_perc < 0.0: - field_perc = 0.0 + # CPU times are always supposed to increase over time + # or at least remain the same and that's because time + # cannot go backwards. + # Surprisingly sometimes this might not be the case (at + # least on Windows and Linux), see: + # https://github.com/giampaolo/psutil/issues/392 + # https://github.com/giampaolo/psutil/issues/645 + # I really don't know what to do about that except + # forcing the value to 0 or 100. + if field_perc > 100.0: + field_perc = 100.0 + # `<=` because `-0.0 == 0.0` evaluates to True + elif field_perc <= 0.0: + field_perc = 0.0 nums.append(field_perc) return _psplatform.scputimes(*nums) @@ -1605,6 +1549,7 @@ def calculate(t1, t2): # --- system memory related functions # ===================================================================== + def virtual_memory(): """Return statistics about system memory usage as a namedtuple including the following fields, expressed in bytes: @@ -1685,6 +1630,7 @@ def swap_memory(): # --- disks/paritions related functions # ===================================================================== + def disk_usage(path): """Return disk usage statistics about the given path as a namedtuple including total, used and free space expressed in bytes plus the @@ -1718,7 +1664,7 @@ def disk_io_counters(perdisk=False): If perdisk is True return the same information for every physical disk installed on the system as a dictionary - with partition names as the keys and the namedutuple + with partition names as the keys and the namedtuple described above as the values. On recent Windows versions 'diskperf -y' command may need to be @@ -1729,16 +1675,17 @@ def disk_io_counters(perdisk=False): raise RuntimeError("couldn't find any physical disk") if perdisk: for disk, fields in rawdict.items(): - rawdict[disk] = _nt_sys_diskio(*fields) + rawdict[disk] = _common.sdiskio(*fields) return rawdict else: - return _nt_sys_diskio(*[sum(x) for x in zip(*rawdict.values())]) + return _common.sdiskio(*[sum(x) for x in zip(*rawdict.values())]) # ===================================================================== # --- network related functions # ===================================================================== + def net_io_counters(pernic=False): """Return network I/O statistics as a namedtuple including the following fields: @@ -1763,10 +1710,10 @@ def net_io_counters(pernic=False): raise RuntimeError("couldn't find any network interface") if pernic: for nic, fields in rawdict.items(): - rawdict[nic] = _nt_sys_netio(*fields) + rawdict[nic] = _common.snetio(*fields) return rawdict else: - return _nt_sys_netio(*[sum(x) for x in zip(*rawdict.values())]) + return _common.snetio(*[sum(x) for x in zip(*rawdict.values())]) def net_connections(kind='inet'): @@ -1789,19 +1736,75 @@ def net_connections(kind='inet'): udp6 UDP over IPv6 unix UNIX socket (both UDP and TCP protocols) all the sum of all the possible families and protocols + + On OSX this function requires root privileges. """ return _psplatform.net_connections(kind) +def net_if_addrs(): + """Return the addresses associated to each NIC (network interface + card) installed on the system as a dictionary whose keys are the + NIC names and value is a list of namedtuples for each address + assigned to the NIC. Each namedtuple includes 4 fields: + + - family + - address + - netmask + - broadcast + + 'family' can be either socket.AF_INET, socket.AF_INET6 or + psutil.AF_LINK, which refers to a MAC address. + 'address' is the primary address, 'netmask' and 'broadcast' + may be None. + Note: you can have more than one address of the same family + associated with each interface. + """ + has_enums = sys.version_info >= (3, 4) + if has_enums: + import socket + rawlist = _psplatform.net_if_addrs() + rawlist.sort(key=lambda x: x[1]) # sort by family + ret = collections.defaultdict(list) + for name, fam, addr, mask, broadcast in rawlist: + if has_enums: + try: + fam = socket.AddressFamily(fam) + except ValueError: + if os.name == 'nt' and fam == -1: + fam = _psplatform.AF_LINK + elif (hasattr(_psplatform, "AF_LINK") and + _psplatform.AF_LINK == fam): + # Linux defines AF_LINK as an alias for AF_PACKET. + # We re-set the family here so that repr(family) + # will show AF_LINK rather than AF_PACKET + fam = _psplatform.AF_LINK + ret[name].append(_common.snic(fam, addr, mask, broadcast)) + return dict(ret) + + +def net_if_stats(): + """Return information about each NIC (network interface card) + installed on the system as a dictionary whose keys are the + NIC names and value is a namedtuple with the following fields: + + - isup: whether the interface is up (bool) + - duplex: can be either NIC_DUPLEX_FULL, NIC_DUPLEX_HALF or + NIC_DUPLEX_UNKNOWN + - speed: the NIC speed expressed in mega bits (MB); if it can't + be determined (e.g. 'localhost') it will be set to 0. + - mtu: the maximum transmission unit expressed in bytes. + """ + return _psplatform.net_if_stats() + + # ===================================================================== # --- other system related functions # ===================================================================== def boot_time(): - """Return the system boot time expressed in seconds since the epoch. - This is also available as psutil.BOOT_TIME. - """ + """Return the system boot time expressed in seconds since the epoch.""" # Note: we are not caching this because it is subject to # system clock updates. return _psplatform.boot_time() @@ -1820,75 +1823,11 @@ def users(): return _psplatform.users() -# ===================================================================== -# --- deprecated functions -# ===================================================================== - -@_deprecated(replacement="psutil.pids()") -def get_pid_list(): - return pids() - - -@_deprecated(replacement="list(process_iter())") -def get_process_list(): - return list(process_iter()) - - -@_deprecated(replacement="psutil.users()") -def get_users(): - return users() - - -@_deprecated(replacement="psutil.virtual_memory()") -def phymem_usage(): - """Return the amount of total, used and free physical memory - on the system in bytes plus the percentage usage. - Deprecated; use psutil.virtual_memory() instead. - """ - return virtual_memory() - - -@_deprecated(replacement="psutil.swap_memory()") -def virtmem_usage(): - return swap_memory() - - -@_deprecated(replacement="psutil.phymem_usage().free") -def avail_phymem(): - return phymem_usage().free - - -@_deprecated(replacement="psutil.phymem_usage().used") -def used_phymem(): - return phymem_usage().used - - -@_deprecated(replacement="psutil.virtmem_usage().total") -def total_virtmem(): - return virtmem_usage().total - - -@_deprecated(replacement="psutil.virtmem_usage().used") -def used_virtmem(): - return virtmem_usage().used - - -@_deprecated(replacement="psutil.virtmem_usage().free") -def avail_virtmem(): - return virtmem_usage().free - - -@_deprecated(replacement="psutil.net_io_counters()") -def network_io_counters(pernic=False): - return net_io_counters(pernic) - - def test(): """List info of all currently running processes emulating ps aux output. """ import datetime - from psutil._compat import print_ today_day = datetime.date.today() templ = "%-10s %5s %4s %4s %7s %7s %-13s %5s %7s %s" @@ -1897,8 +1836,8 @@ def test(): if _POSIX: attrs.append('uids') attrs.append('terminal') - print_(templ % ("USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY", - "START", "TIME", "COMMAND")) + print(templ % ("USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY", + "START", "TIME", "COMMAND")) for p in process_iter(): try: pinfo = p.as_dict(attrs, ad_value='') @@ -1917,14 +1856,6 @@ def test(): time.localtime(sum(pinfo['cpu_times']))) try: user = p.username() - except KeyError: - if _POSIX: - if pinfo['uids']: - user = str(pinfo['uids'].real) - else: - user = '' - else: - raise except Error: user = '' if _WINDOWS and '\\' in user: @@ -1935,56 +1866,20 @@ def test(): int(pinfo['memory_info'].rss / 1024) or '?' memp = pinfo['memory_percent'] and \ round(pinfo['memory_percent'], 1) or '?' - print_(templ % (user[:10], - pinfo['pid'], - pinfo['cpu_percent'], - memp, - vms, - rss, - pinfo.get('terminal', '') or '?', - ctime, - cputime, - pinfo['name'].strip() or '?')) - - -def _replace_module(): - """Dirty hack to replace the module object in order to access - deprecated module constants, see: - http://www.dr-josiah.com/2013/12/properties-on-python-modules.html - """ - class ModuleWrapper(object): - - def __repr__(self): - return repr(self._module) - __str__ = __repr__ - - @property - def NUM_CPUS(self): - msg = "NUM_CPUS constant is deprecated; use cpu_count() instead" - warnings.warn(msg, category=DeprecationWarning, stacklevel=2) - return cpu_count() - - @property - def BOOT_TIME(self): - msg = "BOOT_TIME constant is deprecated; use boot_time() instead" - warnings.warn(msg, category=DeprecationWarning, stacklevel=2) - return boot_time() - - @property - def TOTAL_PHYMEM(self): - msg = "TOTAL_PHYMEM constant is deprecated; " \ - "use virtual_memory().total instead" - warnings.warn(msg, category=DeprecationWarning, stacklevel=2) - return virtual_memory().total - - mod = ModuleWrapper() - mod.__dict__ = globals() - mod._module = sys.modules[__name__] - sys.modules[__name__] = mod - - -_replace_module() -del property, memoize, division, _replace_module + print(templ % ( + user[:10], + pinfo['pid'], + pinfo['cpu_percent'], + memp, + vms, + rss, + pinfo.get('terminal', '') or '?', + ctime, + cputime, + pinfo['name'].strip() or '?')) + + +del memoize, division if sys.version_info < (3, 0): del num diff --git a/python/psutil/psutil/_common.py b/python/psutil/psutil/_common.py index 9db5ad37f306..e9acf595d764 100644 --- a/python/psutil/psutil/_common.py +++ b/python/psutil/psutil/_common.py @@ -8,19 +8,23 @@ from __future__ import division import errno +import functools import os import socket import stat import sys -import warnings +from collections import namedtuple +from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM try: import threading except ImportError: import dummy_threading as threading -from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM +if sys.version_info >= (3, 4): + import enum +else: + enum = None -from psutil._compat import namedtuple, wraps # --- constants @@ -53,6 +57,18 @@ CONN_CLOSING = "CLOSING" CONN_NONE = "NONE" +if enum is None: + NIC_DUPLEX_FULL = 2 + NIC_DUPLEX_HALF = 1 + NIC_DUPLEX_UNKNOWN = 0 +else: + class NicDuplex(enum.IntEnum): + NIC_DUPLEX_FULL = 2 + NIC_DUPLEX_HALF = 1 + NIC_DUPLEX_UNKNOWN = 0 + + globals().update(NicDuplex.__members__) + # --- functions @@ -82,7 +98,7 @@ def memoize(fun): >>> foo.cache_clear() >>> """ - @wraps(fun) + @functools.wraps(fun) def wrapper(*args, **kwargs): key = (args, frozenset(sorted(kwargs.items()))) lock.acquire() @@ -109,43 +125,6 @@ def cache_clear(): return wrapper -# http://code.activestate.com/recipes/577819-deprecated-decorator/ -def deprecated(replacement=None): - """A decorator which can be used to mark functions as deprecated.""" - def outer(fun): - msg = "psutil.%s is deprecated" % fun.__name__ - if replacement is not None: - msg += "; use %s instead" % replacement - if fun.__doc__ is None: - fun.__doc__ = msg - - @wraps(fun) - def inner(*args, **kwargs): - warnings.warn(msg, category=DeprecationWarning, stacklevel=2) - return fun(*args, **kwargs) - - return inner - return outer - - -def deprecated_method(replacement): - """A decorator which can be used to mark a method as deprecated - 'replcement' is the method name which will be called instead. - """ - def outer(fun): - msg = "%s() is deprecated; use %s() instead" % ( - fun.__name__, replacement) - if fun.__doc__ is None: - fun.__doc__ = msg - - @wraps(fun) - def inner(self, *args, **kwargs): - warnings.warn(msg, category=DeprecationWarning, stacklevel=2) - return getattr(self, replacement)(*args, **kwargs) - return inner - return outer - - def isfile_strict(path): """Same as os.path.isfile() but does not swallow EACCES / EPERM exceptions, see: @@ -153,8 +132,7 @@ def isfile_strict(path): """ try: st = os.stat(path) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno in (errno.EPERM, errno.EACCES): raise return False @@ -162,6 +140,30 @@ def isfile_strict(path): return stat.S_ISREG(st.st_mode) +def sockfam_to_enum(num): + """Convert a numeric socket family value to an IntEnum member. + If it's not a known member, return the numeric value itself. + """ + if enum is None: + return num + try: + return socket.AddressFamily(num) + except (ValueError, AttributeError): + return num + + +def socktype_to_enum(num): + """Convert a numeric socket type value to an IntEnum member. + If it's not a known member, return the numeric value itself. + """ + if enum is None: + return num + try: + return socket.AddressType(num) + except (ValueError, AttributeError): + return num + + # --- Process.connections() 'kind' parameter mapping conn_tmap = { @@ -186,7 +188,7 @@ def isfile_strict(path): "unix": ([AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]), }) -del AF_INET, AF_INET6, AF_UNIX, SOCK_STREAM, SOCK_DGRAM, socket +del AF_INET, AF_INET6, AF_UNIX, SOCK_STREAM, SOCK_DGRAM # --- namedtuples for psutil.* system-related functions @@ -212,6 +214,10 @@ def isfile_strict(path): # psutil.net_connections() sconn = namedtuple('sconn', ['fd', 'family', 'type', 'laddr', 'raddr', 'status', 'pid']) +# psutil.net_if_addrs() +snic = namedtuple('snic', ['family', 'address', 'netmask', 'broadcast']) +# psutil.net_if_stats() +snicstats = namedtuple('snicstats', ['isup', 'duplex', 'speed', 'mtu']) # --- namedtuples for psutil.Process methods @@ -235,24 +241,6 @@ def isfile_strict(path): pionice = namedtuple('pionice', ['ioclass', 'value']) # psutil.Process.ctx_switches() pctxsw = namedtuple('pctxsw', ['voluntary', 'involuntary']) - - -# --- misc - -# backward compatibility layer for Process.connections() ntuple -class pconn( - namedtuple('pconn', - ['fd', 'family', 'type', 'laddr', 'raddr', 'status'])): - __slots__ = () - - @property - def local_address(self): - warnings.warn("'local_address' field is deprecated; use 'laddr'" - "instead", category=DeprecationWarning, stacklevel=2) - return self.laddr - - @property - def remote_address(self): - warnings.warn("'remote_address' field is deprecated; use 'raddr'" - "instead", category=DeprecationWarning, stacklevel=2) - return self.raddr +# psutil.Process.connections() +pconn = namedtuple('pconn', ['fd', 'family', 'type', 'laddr', 'raddr', + 'status']) diff --git a/python/psutil/psutil/_compat.py b/python/psutil/psutil/_compat.py index ad3bccee0b03..38744a84aea1 100644 --- a/python/psutil/psutil/_compat.py +++ b/python/psutil/psutil/_compat.py @@ -6,59 +6,29 @@ """Module which provides compatibility with older Python versions.""" -__all__ = ["PY3", "int", "long", "xrange", "exec_", "callable", "namedtuple", - "property", "wraps", "defaultdict", "update_wrapper", "lru_cache"] - +import collections +import functools import sys -try: - import __builtin__ -except ImportError: - import builtins as __builtin__ # py3 + +__all__ = ["PY3", "long", "xrange", "unicode", "callable", "lru_cache"] PY3 = sys.version_info[0] == 3 if PY3: - int = int long = int xrange = range unicode = str - basestring = str - exec_ = getattr(__builtin__, "exec") - print_ = getattr(__builtin__, "print") def u(s): return s - - def b(s): - return s.encode("latin-1") else: - int = int long = long xrange = xrange unicode = unicode - basestring = basestring def u(s): return unicode(s, "unicode_escape") - def b(s): - return s - - def exec_(code, globs=None, locs=None): - if globs is None: - frame = _sys._getframe(1) - globs = frame.f_globals - if locs is None: - locs = frame.f_locals - del frame - elif locs is None: - locs = globs - exec("""exec code in globs, locs""") - - def print_(s): - sys.stdout.write(s + '\n') - sys.stdout.flush() - # removed in 3.0, reintroduced in 3.2 try: @@ -70,222 +40,6 @@ def callable(obj): # --- stdlib additions -# py 2.6 collections.namedtuple -# Taken from: http://code.activestate.com/recipes/500261/ -# Credits: Raymond Hettinger -try: - from collections import namedtuple -except ImportError: - from operator import itemgetter as _itemgetter - from keyword import iskeyword as _iskeyword - import sys as _sys - - def namedtuple(typename, field_names, verbose=False, rename=False): - """A collections.namedtuple implementation, see: - http://docs.python.org/library/collections.html#namedtuple - """ - if isinstance(field_names, basestring): - field_names = field_names.replace(',', ' ').split() - field_names = tuple(map(str, field_names)) - if rename: - names = list(field_names) - seen = set() - for i, name in enumerate(names): - if ((not min(c.isalnum() or c == '_' for c in name) - or _iskeyword(name) - or not name or name[0].isdigit() - or name.startswith('_') - or name in seen)): - names[i] = '_%d' % i - seen.add(name) - field_names = tuple(names) - for name in (typename,) + field_names: - if not min(c.isalnum() or c == '_' for c in name): - raise ValueError('Type names and field names can only contain ' - 'alphanumeric characters and underscores: %r' - % name) - if _iskeyword(name): - raise ValueError('Type names and field names cannot be a ' - 'keyword: %r' % name) - if name[0].isdigit(): - raise ValueError('Type names and field names cannot start ' - 'with a number: %r' % name) - seen_names = set() - for name in field_names: - if name.startswith('_') and not rename: - raise ValueError( - 'Field names cannot start with an underscore: %r' % name) - if name in seen_names: - raise ValueError('Encountered duplicate field name: %r' % name) - seen_names.add(name) - - numfields = len(field_names) - argtxt = repr(field_names).replace("'", "")[1:-1] - reprtxt = ', '.join('%s=%%r' % name for name in field_names) - template = '''class %(typename)s(tuple): - '%(typename)s(%(argtxt)s)' \n - __slots__ = () \n - _fields = %(field_names)r \n - def __new__(_cls, %(argtxt)s): - return _tuple.__new__(_cls, (%(argtxt)s)) \n - @classmethod - def _make(cls, iterable, new=tuple.__new__, len=len): - 'Make a new %(typename)s object from a sequence or iterable' - result = new(cls, iterable) - if len(result) != %(numfields)d: - raise TypeError( - 'Expected %(numfields)d arguments, got %%d' %% len(result)) - return result \n - def __repr__(self): - return '%(typename)s(%(reprtxt)s)' %% self \n - def _asdict(self): - 'Return a new dict which maps field names to their values' - return dict(zip(self._fields, self)) \n - def _replace(_self, **kwds): - result = _self._make(map(kwds.pop, %(field_names)r, _self)) - if kwds: - raise ValueError( - 'Got unexpected field names: %%r' %% kwds.keys()) - return result \n - def __getnewargs__(self): - return tuple(self) \n\n''' % locals() - for i, name in enumerate(field_names): - template += ' %s = _property(_itemgetter(%d))\n' % (name, i) - if verbose: - sys.stdout.write(template + '\n') - sys.stdout.flush() - - namespace = dict( - _itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, - _property=property, _tuple=tuple) - try: - exec_(template, namespace) - except SyntaxError: - e = sys.exc_info()[1] - raise SyntaxError(e.message + ':\n' + template) - result = namespace[typename] - try: - result.__module__ = _sys._getframe( - 1).f_globals.get('__name__', '__main__') - except (AttributeError, ValueError): - pass - - return result - - -# hack to support property getter/setter/deleter on python < 2.6 -# http://docs.python.org/library/functions.html?highlight=property#property -if hasattr(property, 'setter'): - property = property -else: - class property(__builtin__.property): - __metaclass__ = type - - def __init__(self, fget, *args, **kwargs): - super(property, self).__init__(fget, *args, **kwargs) - self.__doc__ = fget.__doc__ - - def getter(self, method): - return property(method, self.fset, self.fdel) - - def setter(self, method): - return property(self.fget, method, self.fdel) - - def deleter(self, method): - return property(self.fget, self.fset, method) - - -# py 2.5 collections.defauldict -# Taken from: -# http://code.activestate.com/recipes/523034-emulate-collectionsdefaultdict/ -# Credits: Jason Kirtland -try: - from collections import defaultdict -except ImportError: - class defaultdict(dict): - """Dict subclass that calls a factory function to supply - missing values: - http://docs.python.org/library/collections.html#collections.defaultdict - """ - - def __init__(self, default_factory=None, *a, **kw): - if ((default_factory is not None and - not hasattr(default_factory, '__call__'))): - raise TypeError('first argument must be callable') - dict.__init__(self, *a, **kw) - self.default_factory = default_factory - - def __getitem__(self, key): - try: - return dict.__getitem__(self, key) - except KeyError: - return self.__missing__(key) - - def __missing__(self, key): - if self.default_factory is None: - raise KeyError(key) - self[key] = value = self.default_factory() - return value - - def __reduce__(self): - if self.default_factory is None: - args = tuple() - else: - args = self.default_factory, - return type(self), args, None, None, self.items() - - def copy(self): - return self.__copy__() - - def __copy__(self): - return type(self)(self.default_factory, self) - - def __deepcopy__(self, memo): - import copy - return type(self)(self.default_factory, - copy.deepcopy(self.items())) - - def __repr__(self): - return 'defaultdict(%s, %s)' % (self.default_factory, - dict.__repr__(self)) - - -# py 2.5 functools.wraps -try: - from functools import wraps -except ImportError: - def wraps(original): - def inner(fn): - for attribute in ['__module__', '__name__', '__doc__']: - setattr(fn, attribute, getattr(original, attribute)) - for attribute in ['__dict__']: - if hasattr(fn, attribute): - getattr(fn, attribute).update(getattr(original, attribute)) - else: - setattr(fn, attribute, - getattr(original, attribute).copy()) - return fn - return inner - - -# py 2.5 functools.update_wrapper -try: - from functools import update_wrapper -except ImportError: - WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__') - WRAPPER_UPDATES = ('__dict__',) - - def update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, - updated=WRAPPER_UPDATES): - """Update a wrapper function to look like the wrapped function, see: - http://docs.python.org/library/functools.html#functools.update_wrapper - """ - for attr in assigned: - setattr(wrapper, attr, getattr(wrapped, attr)) - for attr in updated: - getattr(wrapper, attr).update(getattr(wrapped, attr, {})) - return wrapper - # py 3.2 functools.lru_cache # Taken from: http://code.activestate.com/recipes/578078 @@ -298,8 +52,8 @@ def update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, except ImportError: from dummy_threading import RLock - _CacheInfo = namedtuple("CacheInfo", - ["hits", "misses", "maxsize", "currsize"]) + _CacheInfo = collections.namedtuple( + "CacheInfo", ["hits", "misses", "maxsize", "currsize"]) class _HashedSeq(list): __slots__ = 'hashvalue' @@ -430,6 +184,6 @@ def cache_clear(): wrapper.__wrapped__ = user_function wrapper.cache_info = cache_info wrapper.cache_clear = cache_clear - return update_wrapper(wrapper, user_function) + return functools.update_wrapper(wrapper, user_function) return decorating_function diff --git a/python/psutil/psutil/_psbsd.py b/python/psutil/psutil/_psbsd.py index 9dcdfc21c944..db54a02e1360 100644 --- a/python/psutil/psutil/_psbsd.py +++ b/python/psutil/psutil/_psbsd.py @@ -7,15 +7,17 @@ """FreeBSD platform implementation.""" import errno +import functools import os -import sys +import xml.etree.ElementTree as ET +from collections import namedtuple -from psutil import _common -from psutil import _psposix -from psutil._common import conn_tmap, usage_percent -from psutil._compat import namedtuple, wraps -import _psutil_bsd as cext -import _psutil_posix +from . import _common +from . import _psposix +from . import _psutil_bsd as cext +from . import _psutil_posix as cext_posix +from ._common import conn_tmap, usage_percent, sockfam_to_enum +from ._common import socktype_to_enum __extra__all__ = [] @@ -48,6 +50,7 @@ } PAGESIZE = os.sysconf("SC_PAGE_SIZE") +AF_LINK = cext_posix.AF_LINK # extend base mem ntuple with BSD-specific memory metrics svmem = namedtuple( @@ -63,12 +66,13 @@ # set later from __init__.py NoSuchProcess = None +ZombieProcess = None AccessDenied = None TimeoutExpired = None def virtual_memory(): - """System virtual memory as a namedutple.""" + """System virtual memory as a namedtuple.""" mem = cext.virtual_mem() total, free, active, inactive, wired, cached, buffers, shared = mem avail = inactive + cached + free @@ -86,14 +90,14 @@ def swap_memory(): def cpu_times(): - """Return system per-CPU times as a named tuple""" + """Return system per-CPU times as a namedtuple""" user, nice, system, idle, irq = cext.cpu_times() return scputimes(user, nice, system, idle, irq) if hasattr(cext, "per_cpu_times"): def per_cpu_times(): - """Return system CPU times as a named tuple""" + """Return system CPU times as a namedtuple""" ret = [] for cpu_t in cext.per_cpu_times(): user, nice, system, idle, irq = cpu_t @@ -131,19 +135,25 @@ def cpu_count_physical(): # We may get None in case "sysctl kern.sched.topology_spec" # is not supported on this BSD version, in which case we'll mimic # os.cpu_count() and return None. + ret = None s = cext.cpu_count_phys() if s is not None: # get rid of padding chars appended at the end of the string index = s.rfind("") if index != -1: s = s[:index + 9] - if sys.version_info >= (2, 5): - import xml.etree.ElementTree as ET - root = ET.fromstring(s) - return len(root.findall('group/children/group/cpu')) or None - else: - s = s[s.find(''):] - return s.count("> + if err.errno in (errno.EINVAL, errno.EDEADLK): + for cpu in cpus: + if cpu not in allcpus: + raise ValueError("invalid CPU #%i (choose between %s)" + % (cpu, allcpus)) + raise diff --git a/python/psutil/psutil/_pslinux.py b/python/psutil/psutil/_pslinux.py index 2abd59c3ce9e..7eb25f519c29 100644 --- a/python/psutil/psutil/_pslinux.py +++ b/python/psutil/psutil/_pslinux.py @@ -10,19 +10,27 @@ import base64 import errno +import functools import os import re import socket import struct import sys import warnings +from collections import namedtuple, defaultdict -from psutil import _common -from psutil import _psposix -from psutil._common import (isfile_strict, usage_percent, deprecated) -from psutil._compat import PY3, namedtuple, wraps, b, defaultdict -import _psutil_linux as cext -import _psutil_posix +from . import _common +from . import _psposix +from . import _psutil_linux as cext +from . import _psutil_posix as cext_posix +from ._common import isfile_strict, usage_percent +from ._common import NIC_DUPLEX_FULL, NIC_DUPLEX_HALF, NIC_DUPLEX_UNKNOWN +from ._compat import PY3, long + +if sys.version_info >= (3, 4): + import enum +else: + enum = None __extra__all__ = [ @@ -32,10 +40,7 @@ # connection status constants "CONN_ESTABLISHED", "CONN_SYN_SENT", "CONN_SYN_RECV", "CONN_FIN_WAIT1", "CONN_FIN_WAIT2", "CONN_TIME_WAIT", "CONN_CLOSE", "CONN_CLOSE_WAIT", - "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING", - # other - "phymem_buffers", "cached_phymem"] - + "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING", ] # --- constants @@ -52,12 +57,27 @@ PAGESIZE = os.sysconf("SC_PAGE_SIZE") BOOT_TIME = None # set later DEFAULT_ENCODING = sys.getdefaultencoding() +if enum is None: + AF_LINK = socket.AF_PACKET +else: + AddressFamily = enum.IntEnum('AddressFamily', + {'AF_LINK': socket.AF_PACKET}) + AF_LINK = AddressFamily.AF_LINK # ioprio_* constants http://linux.die.net/man/2/ioprio_get -IOPRIO_CLASS_NONE = 0 -IOPRIO_CLASS_RT = 1 -IOPRIO_CLASS_BE = 2 -IOPRIO_CLASS_IDLE = 3 +if enum is None: + IOPRIO_CLASS_NONE = 0 + IOPRIO_CLASS_RT = 1 + IOPRIO_CLASS_BE = 2 + IOPRIO_CLASS_IDLE = 3 +else: + class IOPriority(enum.IntEnum): + IOPRIO_CLASS_NONE = 0 + IOPRIO_CLASS_RT = 1 + IOPRIO_CLASS_BE = 2 + IOPRIO_CLASS_IDLE = 3 + + globals().update(IOPriority.__members__) # taken from /fs/proc/array.c PROC_STATUSES = { @@ -90,6 +110,7 @@ # set later from __init__.py NoSuchProcess = None +ZombieProcess = None AccessDenied = None TimeoutExpired = None @@ -102,11 +123,8 @@ def _get_cputimes_fields(): (user, nice, system, idle, iowait, irq, softirq, [steal, [guest, [guest_nice]]]) """ - f = open('/proc/stat', 'rb') - try: + with open('/proc/stat', 'rb') as f: values = f.readline().split()[1:] - finally: - f.close() fields = ['user', 'nice', 'system', 'idle', 'iowait', 'irq', 'softirq'] vlen = len(values) if vlen >= 8: @@ -143,19 +161,17 @@ def _get_cputimes_fields(): def virtual_memory(): total, free, buffers, shared, _, _ = cext.linux_sysinfo() cached = active = inactive = None - f = open('/proc/meminfo', 'rb') - CACHED, ACTIVE, INACTIVE = b("Cached:"), b("Active:"), b("Inactive:") - try: + with open('/proc/meminfo', 'rb') as f: for line in f: - if line.startswith(CACHED): + if line.startswith(b"Cached:"): cached = int(line.split()[1]) * 1024 - elif line.startswith(ACTIVE): + elif line.startswith(b"Active:"): active = int(line.split()[1]) * 1024 - elif line.startswith(INACTIVE): + elif line.startswith(b"Inactive:"): inactive = int(line.split()[1]) * 1024 - if (cached is not None - and active is not None - and inactive is not None): + if (cached is not None and + active is not None and + inactive is not None): break else: # we might get here when dealing with exotic Linux flavors, see: @@ -164,8 +180,6 @@ def virtual_memory(): "be determined and were set to 0" warnings.warn(msg, RuntimeWarning) cached = active = inactive = 0 - finally: - f.close() avail = free + buffers + cached used = total - free percent = usage_percent((total - avail), total, _round=1) @@ -178,16 +192,14 @@ def swap_memory(): used = total - free percent = usage_percent(used, total, _round=1) # get pgin/pgouts - f = open("/proc/vmstat", "rb") - SIN, SOUT = b('pswpin'), b('pswpout') - sin = sout = None - try: + with open("/proc/vmstat", "rb") as f: + sin = sout = None for line in f: # values are expressed in 4 kilo bytes, we want bytes instead - if line.startswith(SIN): - sin = int(line.split(b(' '))[1]) * 4 * 1024 - elif line.startswith(SOUT): - sout = int(line.split(b(' '))[1]) * 4 * 1024 + if line.startswith(b'pswpin'): + sin = int(line.split(b' ')[1]) * 4 * 1024 + elif line.startswith(b'pswpout'): + sout = int(line.split(b' ')[1]) * 4 * 1024 if sin is not None and sout is not None: break else: @@ -197,21 +209,9 @@ def swap_memory(): "be determined and were set to 0" warnings.warn(msg, RuntimeWarning) sin = sout = 0 - finally: - f.close() return _common.sswap(total, used, free, percent, sin, sout) -@deprecated(replacement='psutil.virtual_memory().cached') -def cached_phymem(): - return virtual_memory().cached - - -@deprecated(replacement='psutil.virtual_memory().buffers') -def phymem_buffers(): - return virtual_memory().buffers - - # --- CPUs def cpu_times(): @@ -221,11 +221,8 @@ def cpu_times(): [guest_nice]]]) Last 3 fields may not be available on all Linux kernel versions. """ - f = open('/proc/stat', 'rb') - try: + with open('/proc/stat', 'rb') as f: values = f.readline().split() - finally: - f.close() fields = values[1:len(scputimes._fields) + 1] fields = [float(x) / CLOCK_TICKS for x in fields] return scputimes(*fields) @@ -236,21 +233,17 @@ def per_cpu_times(): for every CPU available on the system. """ cpus = [] - f = open('/proc/stat', 'rb') - try: + with open('/proc/stat', 'rb') as f: # get rid of the first line which refers to system wide CPU stats f.readline() - CPU = b('cpu') for line in f: - if line.startswith(CPU): + if line.startswith(b'cpu'): values = line.split() fields = values[1:len(scputimes._fields) + 1] fields = [float(x) / CLOCK_TICKS for x in fields] entry = scputimes(*fields) cpus.append(entry) return cpus - finally: - f.close() def cpu_count_logical(): @@ -260,53 +253,51 @@ def cpu_count_logical(): except ValueError: # as a second fallback we try to parse /proc/cpuinfo num = 0 - f = open('/proc/cpuinfo', 'rb') - try: - lines = f.readlines() - finally: - f.close() - PROCESSOR = b('processor') - for line in lines: - if line.lower().startswith(PROCESSOR): - num += 1 - - # unknown format (e.g. amrel/sparc architectures), see: - # https://github.com/giampaolo/psutil/issues/200 - # try to parse /proc/stat as a last resort - if num == 0: - f = open('/proc/stat', 'rt') - try: - lines = f.readlines() - finally: - f.close() - search = re.compile('cpu\d') - for line in lines: - line = line.split(' ')[0] - if search.match(line): - num += 1 + with open('/proc/cpuinfo', 'rb') as f: + for line in f: + if line.lower().startswith(b'processor'): + num += 1 + + # unknown format (e.g. amrel/sparc architectures), see: + # https://github.com/giampaolo/psutil/issues/200 + # try to parse /proc/stat as a last resort + if num == 0: + search = re.compile('cpu\d') + with open('/proc/stat', 'rt') as f: + for line in f: + line = line.split(' ')[0] + if search.match(line): + num += 1 - if num == 0: - # mimic os.cpu_count() - return None - return num + if num == 0: + # mimic os.cpu_count() + return None + return num def cpu_count_physical(): - """Return the number of physical CPUs in the system.""" - f = open('/proc/cpuinfo', 'rb') - try: - lines = f.readlines() - finally: - f.close() - found = set() - PHYSICAL_ID = b('physical id') - for line in lines: - if line.lower().startswith(PHYSICAL_ID): - found.add(line.strip()) - if found: - return len(found) - else: - return None # mimic os.cpu_count() + """Return the number of physical cores in the system.""" + mapping = {} + current_info = {} + with open('/proc/cpuinfo', 'rb') as f: + for line in f: + line = line.strip().lower() + if not line: + # new section + if (b'physical id' in current_info and + b'cpu cores' in current_info): + mapping[current_info[b'physical id']] = \ + current_info[b'cpu cores'] + current_info = {} + else: + # ongoing section + if (line.startswith(b'physical id') or + line.startswith(b'cpu cores')): + key, value = line.split(b'\t:', 1) + current_info[key] = int(value) + + # mimic os.cpu_count() + return sum(mapping.values()) or None # --- other system functions @@ -322,7 +313,7 @@ def users(): # to use them in the future. if not user_process: continue - if hostname == ':0.0': + if hostname == ':0.0' or hostname == ':0': hostname = 'localhost' nt = _common.suser(user, tty or None, hostname, tstamp) retlist.append(nt) @@ -332,24 +323,20 @@ def users(): def boot_time(): """Return the system boot time expressed in seconds since the epoch.""" global BOOT_TIME - f = open('/proc/stat', 'rb') - try: - BTIME = b('btime') + with open('/proc/stat', 'rb') as f: for line in f: - if line.startswith(BTIME): + if line.startswith(b'btime'): ret = float(line.strip().split()[1]) BOOT_TIME = ret return ret - raise RuntimeError("line 'btime' not found") - finally: - f.close() + raise RuntimeError("line 'btime' not found in /proc/stat") # --- processes def pids(): """Returns a list of PIDs currently running on the system.""" - return [int(x) for x in os.listdir(b('/proc')) if x.isdigit()] + return [int(x) for x in os.listdir(b'/proc') if x.isdigit()] def pid_exists(pid): @@ -396,9 +383,17 @@ def get_proc_inodes(self, pid): for fd in os.listdir("/proc/%s/fd" % pid): try: inode = os.readlink("/proc/%s/fd/%s" % (pid, fd)) - except OSError: - # TODO: need comment here - continue + except OSError as err: + # ENOENT == file which is gone in the meantime; + # os.stat('/proc/%s' % self.pid) will be done later + # to force NSP (if it's the case) + if err.errno in (errno.ENOENT, errno.ESRCH): + continue + elif err.errno == errno.EINVAL: + # not a link + continue + else: + raise else: if inode.startswith('socket:['): # the process is using a socket @@ -411,7 +406,7 @@ def get_all_inodes(self): for pid in pids(): try: inodes.update(self.get_proc_inodes(pid)) - except OSError: + except OSError as err: # os.listdir() is gonna raise a lot of access denied # exceptions in case of unprivileged user; that's fine # as we'll just end up returning a connection with PID @@ -419,7 +414,6 @@ def get_all_inodes(self): # Both netstat -an and lsof does the same so it's # unlikely we can do any better. # ENOENT just means a PID disappeared on us. - err = sys.exc_info()[1] if err.errno not in ( errno.ENOENT, errno.ESRCH, errno.EPERM, errno.EACCES): raise @@ -477,59 +471,67 @@ def process_inet(self, file, family, type_, inodes, filter_pid=None): if file.endswith('6') and not os.path.exists(file): # IPv6 not supported return - f = open(file, 'rt') - f.readline() # skip the first line - for line in f: - _, laddr, raddr, status, _, _, _, _, _, inode = \ - line.split()[:10] - if inode in inodes: - # We assume inet sockets are unique, so we error - # out if there are multiple references to the - # same inode. We won't do this for UNIX sockets. - if len(inodes[inode]) > 1 and type_ != socket.AF_UNIX: - raise ValueError("ambiguos inode with multiple " - "PIDs references") - pid, fd = inodes[inode][0] - else: - pid, fd = None, -1 - if filter_pid is not None and filter_pid != pid: - continue - else: - if type_ == socket.SOCK_STREAM: - status = TCP_STATUSES[status] + with open(file, 'rt') as f: + f.readline() # skip the first line + for line in f: + try: + _, laddr, raddr, status, _, _, _, _, _, inode = \ + line.split()[:10] + except ValueError: + raise RuntimeError( + "error while parsing %s; malformed line %r" % ( + file, line)) + if inode in inodes: + # # We assume inet sockets are unique, so we error + # # out if there are multiple references to the + # # same inode. We won't do this for UNIX sockets. + # if len(inodes[inode]) > 1 and family != socket.AF_UNIX: + # raise ValueError("ambiguos inode with multiple " + # "PIDs references") + pid, fd = inodes[inode][0] else: - status = _common.CONN_NONE - laddr = self.decode_address(laddr, family) - raddr = self.decode_address(raddr, family) - yield (fd, family, type_, laddr, raddr, status, pid) - f.close() + pid, fd = None, -1 + if filter_pid is not None and filter_pid != pid: + continue + else: + if type_ == socket.SOCK_STREAM: + status = TCP_STATUSES[status] + else: + status = _common.CONN_NONE + laddr = self.decode_address(laddr, family) + raddr = self.decode_address(raddr, family) + yield (fd, family, type_, laddr, raddr, status, pid) def process_unix(self, file, family, inodes, filter_pid=None): """Parse /proc/net/unix files.""" - f = open(file, 'rt') - f.readline() # skip the first line - for line in f: - tokens = line.split() - _, _, _, _, type_, _, inode = tokens[0:7] - if inode in inodes: - # With UNIX sockets we can have a single inode - # referencing many file descriptors. - pairs = inodes[inode] - else: - pairs = [(None, -1)] - for pid, fd in pairs: - if filter_pid is not None and filter_pid != pid: - continue + with open(file, 'rt') as f: + f.readline() # skip the first line + for line in f: + tokens = line.split() + try: + _, _, _, _, type_, _, inode = tokens[0:7] + except ValueError: + raise RuntimeError( + "error while parsing %s; malformed line %r" % ( + file, line)) + if inode in inodes: + # With UNIX sockets we can have a single inode + # referencing many file descriptors. + pairs = inodes[inode] else: - if len(tokens) == 8: - path = tokens[-1] + pairs = [(None, -1)] + for pid, fd in pairs: + if filter_pid is not None and filter_pid != pid: + continue else: - path = "" - type_ = int(type_) - raddr = None - status = _common.CONN_NONE - yield (fd, family, type_, path, raddr, status, pid) - f.close() + if len(tokens) == 8: + path = tokens[-1] + else: + path = "" + type_ = int(type_) + raddr = None + status = _common.CONN_NONE + yield (fd, family, type_, path, raddr, status, pid) def retrieve(self, kind, pid=None): if kind not in self.tmap: @@ -542,7 +544,7 @@ def retrieve(self, kind, pid=None): return [] else: inodes = self.get_all_inodes() - ret = [] + ret = set() for f, family, type_ in self.tmap[kind]: if family in (socket.AF_INET, socket.AF_INET6): ls = self.process_inet( @@ -557,8 +559,8 @@ def retrieve(self, kind, pid=None): else: conn = _common.sconn(fd, family, type_, laddr, raddr, status, bound_pid) - ret.append(conn) - return ret + ret.add(conn) + return list(ret) _connections = Connections() @@ -573,12 +575,8 @@ def net_io_counters(): """Return network I/O statistics for every network interface installed on the system as a dict of raw tuples. """ - f = open("/proc/net/dev", "rt") - try: + with open("/proc/net/dev", "rt") as f: lines = f.readlines() - finally: - f.close() - retdict = {} for line in lines[2:]: colon = line.rfind(':') @@ -598,6 +596,23 @@ def net_io_counters(): return retdict +def net_if_stats(): + """Get NIC stats (isup, duplex, speed, mtu).""" + duplex_map = {cext.DUPLEX_FULL: NIC_DUPLEX_FULL, + cext.DUPLEX_HALF: NIC_DUPLEX_HALF, + cext.DUPLEX_UNKNOWN: NIC_DUPLEX_UNKNOWN} + names = net_io_counters().keys() + ret = {} + for name in names: + isup, duplex, speed, mtu = cext.net_if_stats(name) + duplex = duplex_map[duplex] + ret[name] = _common.snicstats(isup, duplex, speed, mtu) + return ret + + +net_if_addrs = cext_posix.net_if_addrs + + # --- disks def disk_io_counters(): @@ -611,11 +626,8 @@ def disk_io_counters(): # determine partitions we want to look for partitions = [] - f = open("/proc/partitions", "rt") - try: + with open("/proc/partitions", "rt") as f: lines = f.readlines()[2:] - finally: - f.close() for line in reversed(lines): _, _, _, name = line.split() if name[-1].isdigit(): @@ -631,11 +643,8 @@ def disk_io_counters(): partitions.append(name) # retdict = {} - f = open("/proc/diskstats", "rt") - try: + with open("/proc/diskstats", "rt") as f: lines = f.readlines() - finally: - f.close() for line in lines: # http://www.mjmwired.net/kernel/Documentation/iostats.txt fields = line.split() @@ -658,15 +667,18 @@ def disk_io_counters(): def disk_partitions(all=False): - """Return mounted disk partitions as a list of nameduples""" - phydevs = [] - f = open("/proc/filesystems", "r") - try: + """Return mounted disk partitions as a list of namedtuples""" + fstypes = set() + with open("/proc/filesystems", "r") as f: for line in f: + line = line.strip() if not line.startswith("nodev"): - phydevs.append(line.strip()) - finally: - f.close() + fstypes.add(line.strip()) + else: + # ignore all lines starting with "nodev" except "nodev zfs" + fstype = line.split("\t")[1] + if fstype == "zfs": + fstypes.add("zfs") retlist = [] partitions = cext.disk_partitions() @@ -675,7 +687,7 @@ def disk_partitions(all=False): if device == 'none': device = '' if not all: - if device == '' or fstype not in phydevs: + if device == '' or fstype not in fstypes: continue ntuple = _common.sdiskpart(device, mountpoint, fstype, opts) retlist.append(ntuple) @@ -691,18 +703,17 @@ def wrap_exceptions(fun): """Decorator which translates bare OSError and IOError exceptions into NoSuchProcess and AccessDenied. """ - @wraps(fun) + @functools.wraps(fun) def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except EnvironmentError: + except EnvironmentError as err: # support for private module import if NoSuchProcess is None or AccessDenied is None: raise # ENOENT (no such file or directory) gets raised on open(). # ESRCH (no such process) can get raised on read() if # process is gone in meantime. - err = sys.exc_info()[1] if err.errno in (errno.ENOENT, errno.ESRCH): raise NoSuchProcess(self.pid, self._name) if err.errno in (errno.EPERM, errno.EACCES): @@ -711,34 +722,43 @@ def wrapper(self, *args, **kwargs): return wrapper +def wrap_exceptions_w_zombie(fun): + """Same as above but also handles zombies.""" + @functools.wraps(fun) + def wrapper(self, *args, **kwargs): + try: + return wrap_exceptions(fun)(self) + except NoSuchProcess: + if not pid_exists(self.pid): + raise + else: + raise ZombieProcess(self.pid, self._name, self._ppid) + return wrapper + + class Process(object): """Linux process implementation.""" - __slots__ = ["pid", "_name"] + __slots__ = ["pid", "_name", "_ppid"] def __init__(self, pid): self.pid = pid self._name = None + self._ppid = None @wrap_exceptions def name(self): fname = "/proc/%s/stat" % self.pid - if PY3: - f = open(fname, "rt", encoding=DEFAULT_ENCODING) - else: - f = open(fname, "rt") - try: - name = f.read().split(' ')[1].replace('(', '').replace(')', '') - finally: - f.close() + kw = dict(encoding=DEFAULT_ENCODING) if PY3 else dict() + with open(fname, "rt", **kw) as f: + data = f.read() # XXX - gets changed later and probably needs refactoring - return name + return data[data.find('(') + 1:data.rfind(')')] def exe(self): try: exe = os.readlink("/proc/%s/exe" % self.pid) - except (OSError, IOError): - err = sys.exc_info()[1] + except OSError as err: if err.errno in (errno.ENOENT, errno.ESRCH): # no such file error; might be raised also if the # path actually exists for system processes with @@ -746,8 +766,10 @@ def exe(self): if os.path.lexists("/proc/%s" % self.pid): return "" else: - # ok, it is a process which has gone away - raise NoSuchProcess(self.pid, self._name) + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._name) + else: + raise ZombieProcess(self.pid, self._name, self._ppid) if err.errno in (errno.EPERM, errno.EACCES): raise AccessDenied(self.pid, self._name) raise @@ -765,24 +787,18 @@ def exe(self): @wrap_exceptions def cmdline(self): fname = "/proc/%s/cmdline" % self.pid - if PY3: - f = open(fname, "rt", encoding=DEFAULT_ENCODING) - else: - f = open(fname, "rt") - try: - # return the args as a list - return [x for x in f.read().split('\x00') if x] - finally: - f.close() + kw = dict(encoding=DEFAULT_ENCODING) if PY3 else dict() + with open(fname, "rt", **kw) as f: + data = f.read() + if data.endswith('\x00'): + data = data[:-1] + return [x for x in data.split('\x00')] @wrap_exceptions def terminal(self): tmap = _psposix._get_terminal_map() - f = open("/proc/%s/stat" % self.pid, 'rb') - try: - tty_nr = int(f.read().split(b(' '))[6]) - finally: - f.close() + with open("/proc/%s/stat" % self.pid, 'rb') as f: + tty_nr = int(f.read().split(b' ')[6]) try: return tmap[tty_nr] except KeyError: @@ -792,27 +808,22 @@ def terminal(self): @wrap_exceptions def io_counters(self): fname = "/proc/%s/io" % self.pid - f = open(fname, 'rb') - SYSCR, SYSCW = b("syscr"), b("syscw") - READ_BYTES, WRITE_BYTES = b("read_bytes"), b("write_bytes") - try: + with open(fname, 'rb') as f: rcount = wcount = rbytes = wbytes = None for line in f: - if rcount is None and line.startswith(SYSCR): + if rcount is None and line.startswith(b"syscr"): rcount = int(line.split()[1]) - elif wcount is None and line.startswith(SYSCW): + elif wcount is None and line.startswith(b"syscw"): wcount = int(line.split()[1]) - elif rbytes is None and line.startswith(READ_BYTES): + elif rbytes is None and line.startswith(b"read_bytes"): rbytes = int(line.split()[1]) - elif wbytes is None and line.startswith(WRITE_BYTES): + elif wbytes is None and line.startswith(b"write_bytes"): wbytes = int(line.split()[1]) for x in (rcount, wcount, rbytes, wbytes): if x is None: raise NotImplementedError( "couldn't read all necessary info from %r" % fname) return _common.pio(rcount, wcount, rbytes, wbytes) - finally: - f.close() else: def io_counters(self): raise NotImplementedError("couldn't find /proc/%s/io (kernel " @@ -820,14 +831,11 @@ def io_counters(self): @wrap_exceptions def cpu_times(self): - f = open("/proc/%s/stat" % self.pid, 'rb') - try: + with open("/proc/%s/stat" % self.pid, 'rb') as f: st = f.read().strip() - finally: - f.close() # ignore the first two values ("pid (exe)") - st = st[st.find(b(')')) + 2:] - values = st.split(b(' ')) + st = st[st.find(b')') + 2:] + values = st.split(b' ') utime = float(values[11]) / CLOCK_TICKS stime = float(values[12]) / CLOCK_TICKS return _common.pcputimes(utime, stime) @@ -844,14 +852,11 @@ def wait(self, timeout=None): @wrap_exceptions def create_time(self): - f = open("/proc/%s/stat" % self.pid, 'rb') - try: + with open("/proc/%s/stat" % self.pid, 'rb') as f: st = f.read().strip() - finally: - f.close() # ignore the first two values ("pid (exe)") - st = st[st.rfind(b(')')) + 2:] - values = st.split(b(' ')) + st = st[st.rfind(b')') + 2:] + values = st.split(b' ') # According to documentation, starttime is in field 21 and the # unit is jiffies (clock ticks). # We first divide it for clock ticks and then add uptime returning @@ -862,13 +867,10 @@ def create_time(self): @wrap_exceptions def memory_info(self): - f = open("/proc/%s/statm" % self.pid, 'rb') - try: + with open("/proc/%s/statm" % self.pid, 'rb') as f: vms, rss = f.readline().split()[:2] return _common.pmem(int(rss) * PAGESIZE, int(vms) * PAGESIZE) - finally: - f.close() @wrap_exceptions def memory_info_ex(self): @@ -883,23 +885,20 @@ def memory_info_ex(self): # | data | data + stack | drs | DATA | # | dirty | dirty pages (unused in Linux 2.6) | dt | | # ============================================================ - f = open("/proc/%s/statm" % self.pid, "rb") - try: + with open("/proc/%s/statm" % self.pid, "rb") as f: vms, rss, shared, text, lib, data, dirty = \ [int(x) * PAGESIZE for x in f.readline().split()[:7]] - finally: - f.close() return pextmem(rss, vms, shared, text, lib, data, dirty) if os.path.exists('/proc/%s/smaps' % os.getpid()): + + @wrap_exceptions def memory_maps(self): - """Return process's mapped memory regions as a list of nameduples. + """Return process's mapped memory regions as a list of named tuples. Fields are explained in 'man proc'; here is an updated (Apr 2012) version: http://goo.gl/fmebo """ - f = None - try: - f = open("/proc/%s/smaps" % self.pid, "rt") + with open("/proc/%s/smaps" % self.pid, "rt") as f: first_line = f.readline() current_block = [first_line] @@ -923,6 +922,7 @@ def get_blocks(): "rpret line %r" % line) yield (current_block.pop(), data) + ls = [] if first_line: # smaps file can be empty for header, data in get_blocks(): hfields = header.split(None, 5) @@ -935,35 +935,20 @@ def get_blocks(): path = '[anon]' else: path = path.strip() - yield (addr, perms, path, - data['Rss:'], - data.get('Size:', 0), - data.get('Pss:', 0), - data.get('Shared_Clean:', 0), - data.get('Shared_Dirty:', 0), - data.get('Private_Clean:', 0), - data.get('Private_Dirty:', 0), - data.get('Referenced:', 0), - data.get('Anonymous:', 0), - data.get('Swap:', 0)) - f.close() - except EnvironmentError: - # XXX - Can't use wrap_exceptions decorator as we're - # returning a generator; this probably needs some - # refactoring in order to avoid this code duplication. - if f is not None: - f.close() - err = sys.exc_info()[1] - if err.errno in (errno.ENOENT, errno.ESRCH): - raise NoSuchProcess(self.pid, self._name) - if err.errno in (errno.EPERM, errno.EACCES): - raise AccessDenied(self.pid, self._name) - raise - except: - if f is not None: - f.close() - raise - f.close() + ls.append(( + addr, perms, path, + data['Rss:'], + data.get('Size:', 0), + data.get('Pss:', 0), + data.get('Shared_Clean:', 0), + data.get('Shared_Dirty:', 0), + data.get('Private_Clean:', 0), + data.get('Private_Dirty:', 0), + data.get('Referenced:', 0), + data.get('Anonymous:', 0), + data.get('Swap:', 0) + )) + return ls else: def memory_maps(self): @@ -972,7 +957,7 @@ def memory_maps(self): % self.pid raise NotImplementedError(msg) - @wrap_exceptions + @wrap_exceptions_w_zombie def cwd(self): # readlink() might return paths containing null bytes causing # problems when used with other fs-related functions (os.*, @@ -983,14 +968,11 @@ def cwd(self): @wrap_exceptions def num_ctx_switches(self): vol = unvol = None - f = open("/proc/%s/status" % self.pid, "rb") - VOLUNTARY = b("voluntary_ctxt_switches") - NON_VOLUNTARY = b("nonvoluntary_ctxt_switches") - try: + with open("/proc/%s/status" % self.pid, "rb") as f: for line in f: - if line.startswith(VOLUNTARY): + if line.startswith(b"voluntary_ctxt_switches"): vol = int(line.split()[1]) - elif line.startswith(NON_VOLUNTARY): + elif line.startswith(b"nonvoluntary_ctxt_switches"): unvol = int(line.split()[1]) if vol is not None and unvol is not None: return _common.pctxsw(vol, unvol) @@ -998,20 +980,14 @@ def num_ctx_switches(self): "'voluntary_ctxt_switches' and 'nonvoluntary_ctxt_switches'" "fields were not found in /proc/%s/status; the kernel is " "probably older than 2.6.23" % self.pid) - finally: - f.close() @wrap_exceptions def num_threads(self): - f = open("/proc/%s/status" % self.pid, "rb") - try: - THREADS = b("Threads:") + with open("/proc/%s/status" % self.pid, "rb") as f: for line in f: - if line.startswith(THREADS): + if line.startswith(b"Threads:"): return int(line.split()[1]) raise NotImplementedError("line not found") - finally: - f.close() @wrap_exceptions def threads(self): @@ -1020,23 +996,20 @@ def threads(self): retlist = [] hit_enoent = False for thread_id in thread_ids: + fname = "/proc/%s/task/%s/stat" % (self.pid, thread_id) try: - f = open("/proc/%s/task/%s/stat" % (self.pid, thread_id), 'rb') - except EnvironmentError: - err = sys.exc_info()[1] + with open(fname, 'rb') as f: + st = f.read().strip() + except IOError as err: if err.errno == errno.ENOENT: # no such file or directory; it means thread # disappeared on us hit_enoent = True continue raise - try: - st = f.read().strip() - finally: - f.close() # ignore the first two values ("pid (exe)") - st = st[st.find(b(')')) + 2:] - values = st.split(b(' ')) + st = st[st.find(b')') + 2:] + values = st.split(b' ') utime = float(values[11]) / CLOCK_TICKS stime = float(values[12]) / CLOCK_TICKS ntuple = _common.pthread(int(thread_id), utime, stime) @@ -1048,19 +1021,16 @@ def threads(self): @wrap_exceptions def nice_get(self): - # f = open('/proc/%s/stat' % self.pid, 'r') - # try: + # with open('/proc/%s/stat' % self.pid, 'r') as f: # data = f.read() # return int(data.split()[18]) - # finally: - # f.close() # Use C implementation - return _psutil_posix.getpriority(self.pid) + return cext_posix.getpriority(self.pid) @wrap_exceptions def nice_set(self, value): - return _psutil_posix.setpriority(self.pid, value) + return cext_posix.setpriority(self.pid, value) @wrap_exceptions def cpu_affinity_get(self): @@ -1070,8 +1040,7 @@ def cpu_affinity_get(self): def cpu_affinity_set(self, cpus): try: cext.proc_cpu_affinity_set(self.pid, cpus) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno == errno.EINVAL: allcpus = tuple(range(len(per_cpu_times()))) for cpu in cpus: @@ -1086,64 +1055,82 @@ def cpu_affinity_set(self, cpus): @wrap_exceptions def ionice_get(self): ioclass, value = cext.proc_ioprio_get(self.pid) + if enum is not None: + ioclass = IOPriority(ioclass) return _common.pionice(ioclass, value) @wrap_exceptions def ionice_set(self, ioclass, value): + if value is not None: + if not PY3 and not isinstance(value, (int, long)): + msg = "value argument is not an integer (gor %r)" % value + raise TypeError(msg) + if not 0 <= value <= 8: + raise ValueError( + "value argument range expected is between 0 and 8") + if ioclass in (IOPRIO_CLASS_NONE, None): if value: - msg = "can't specify value with IOPRIO_CLASS_NONE" + msg = "can't specify value with IOPRIO_CLASS_NONE " \ + "(got %r)" % value raise ValueError(msg) ioclass = IOPRIO_CLASS_NONE value = 0 - if ioclass in (IOPRIO_CLASS_RT, IOPRIO_CLASS_BE): - if value is None: - value = 4 elif ioclass == IOPRIO_CLASS_IDLE: if value: - msg = "can't specify value with IOPRIO_CLASS_IDLE" + msg = "can't specify value with IOPRIO_CLASS_IDLE " \ + "(got %r)" % value raise ValueError(msg) value = 0 + elif ioclass in (IOPRIO_CLASS_RT, IOPRIO_CLASS_BE): + if value is None: + # TODO: add comment explaining why this is 4 (?) + value = 4 else: - value = 0 - if not 0 <= value <= 8: - raise ValueError( - "value argument range expected is between 0 and 8") + # otherwise we would get OSError(EVINAL) + raise ValueError("invalid ioclass argument %r" % ioclass) + return cext.proc_ioprio_set(self.pid, ioclass, value) if HAS_PRLIMIT: @wrap_exceptions def rlimit(self, resource, limits=None): - # if pid is 0 prlimit() applies to the calling process and - # we don't want that + # If pid is 0 prlimit() applies to the calling process and + # we don't want that. We should never get here though as + # PID 0 is not supported on Linux. if self.pid == 0: raise ValueError("can't use prlimit() against PID 0 process") - if limits is None: - # get - return cext.linux_prlimit(self.pid, resource) - else: - # set - if len(limits) != 2: - raise ValueError( - "second argument must be a (soft, hard) tuple") - soft, hard = limits - cext.linux_prlimit(self.pid, resource, soft, hard) + try: + if limits is None: + # get + return cext.linux_prlimit(self.pid, resource) + else: + # set + if len(limits) != 2: + raise ValueError( + "second argument must be a (soft, hard) tuple, " + "got %s" % repr(limits)) + soft, hard = limits + cext.linux_prlimit(self.pid, resource, soft, hard) + except OSError as err: + if err.errno == errno.ENOSYS and pid_exists(self.pid): + # I saw this happening on Travis: + # https://travis-ci.org/giampaolo/psutil/jobs/51368273 + raise ZombieProcess(self.pid, self._name, self._ppid) + else: + raise @wrap_exceptions def status(self): - f = open("/proc/%s/status" % self.pid, 'rb') - try: - STATE = b("State:") + with open("/proc/%s/status" % self.pid, 'rb') as f: for line in f: - if line.startswith(STATE): + if line.startswith(b"State:"): letter = line.split()[1] if PY3: letter = letter.decode() # XXX is '?' legit? (we're not supposed to return # it anyway) return PROC_STATUSES.get(letter, '?') - finally: - f.close() @wrap_exceptions def open_files(self): @@ -1152,24 +1139,26 @@ def open_files(self): hit_enoent = False for fd in files: file = "/proc/%s/fd/%s" % (self.pid, fd) - if os.path.islink(file): - try: - file = os.readlink(file) - except OSError: - # ENOENT == file which is gone in the meantime - err = sys.exc_info()[1] - if err.errno in (errno.ENOENT, errno.ESRCH): - hit_enoent = True - continue - raise + try: + file = os.readlink(file) + except OSError as err: + # ENOENT == file which is gone in the meantime + if err.errno in (errno.ENOENT, errno.ESRCH): + hit_enoent = True + continue + elif err.errno == errno.EINVAL: + # not a link + continue else: - # If file is not an absolute path there's no way - # to tell whether it's a regular file or not, - # so we skip it. A regular file is always supposed - # to be absolutized though. - if file.startswith('/') and isfile_strict(file): - ntuple = _common.popenfile(file, int(fd)) - retlist.append(ntuple) + raise + else: + # If file is not an absolute path there's no way + # to tell whether it's a regular file or not, + # so we skip it. A regular file is always supposed + # to be absolutized though. + if file.startswith('/') and isfile_strict(file): + ntuple = _common.popenfile(file, int(fd)) + retlist.append(ntuple) if hit_enoent: # raise NSP if the process disappeared on us os.stat('/proc/%s' % self.pid) @@ -1188,39 +1177,30 @@ def num_fds(self): @wrap_exceptions def ppid(self): - f = open("/proc/%s/status" % self.pid, 'rb') - try: - PPID = b("PPid:") + fpath = "/proc/%s/status" % self.pid + with open(fpath, 'rb') as f: for line in f: - if line.startswith(PPID): + if line.startswith(b"PPid:"): # PPid: nnnn return int(line.split()[1]) - raise NotImplementedError("line not found") - finally: - f.close() + raise NotImplementedError("line 'PPid' not found in %s" % fpath) @wrap_exceptions def uids(self): - f = open("/proc/%s/status" % self.pid, 'rb') - try: - UID = b('Uid:') + fpath = "/proc/%s/status" % self.pid + with open(fpath, 'rb') as f: for line in f: - if line.startswith(UID): + if line.startswith(b'Uid:'): _, real, effective, saved, fs = line.split() return _common.puids(int(real), int(effective), int(saved)) - raise NotImplementedError("line not found") - finally: - f.close() + raise NotImplementedError("line 'Uid' not found in %s" % fpath) @wrap_exceptions def gids(self): - f = open("/proc/%s/status" % self.pid, 'rb') - try: - GID = b('Gid:') + fpath = "/proc/%s/status" % self.pid + with open(fpath, 'rb') as f: for line in f: - if line.startswith(GID): + if line.startswith(b'Gid:'): _, real, effective, saved, fs = line.split() return _common.pgids(int(real), int(effective), int(saved)) - raise NotImplementedError("line not found") - finally: - f.close() + raise NotImplementedError("line 'Gid' not found in %s" % fpath) diff --git a/python/psutil/psutil/_psosx.py b/python/psutil/psutil/_psosx.py index 8953867da7a0..41875fe4045e 100644 --- a/python/psutil/psutil/_psosx.py +++ b/python/psutil/psutil/_psosx.py @@ -7,15 +7,16 @@ """OSX platform implementation.""" import errno +import functools import os -import sys +from collections import namedtuple -from psutil import _common -from psutil import _psposix -from psutil._common import conn_tmap, usage_percent, isfile_strict -from psutil._compat import namedtuple, wraps -import _psutil_osx as cext -import _psutil_posix +from . import _common +from . import _psposix +from . import _psutil_osx as cext +from . import _psutil_posix as cext_posix +from ._common import conn_tmap, usage_percent, isfile_strict +from ._common import sockfam_to_enum, socktype_to_enum __extra__all__ = [] @@ -23,6 +24,7 @@ # --- constants PAGESIZE = os.sysconf("SC_PAGE_SIZE") +AF_LINK = cext_posix.AF_LINK # http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h TCP_STATUSES = { @@ -65,6 +67,7 @@ # set later from __init__.py NoSuchProcess = None +ZombieProcess = None AccessDenied = None TimeoutExpired = None @@ -165,28 +168,44 @@ def net_connections(kind='inet'): return ret +def net_if_stats(): + """Get NIC stats (isup, duplex, speed, mtu).""" + names = net_io_counters().keys() + ret = {} + for name in names: + isup, duplex, speed, mtu = cext_posix.net_if_stats(name) + if hasattr(_common, 'NicDuplex'): + duplex = _common.NicDuplex(duplex) + ret[name] = _common.snicstats(isup, duplex, speed, mtu) + return ret + + pids = cext.pids pid_exists = _psposix.pid_exists disk_usage = _psposix.disk_usage net_io_counters = cext.net_io_counters disk_io_counters = cext.disk_io_counters +net_if_addrs = cext_posix.net_if_addrs def wrap_exceptions(fun): """Decorator which translates bare OSError exceptions into NoSuchProcess and AccessDenied. """ - @wraps(fun) + @functools.wraps(fun) def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except OSError: + except OSError as err: # support for private module import - if NoSuchProcess is None or AccessDenied is None: + if (NoSuchProcess is None or AccessDenied is None or + ZombieProcess is None): raise - err = sys.exc_info()[1] if err.errno == errno.ESRCH: - raise NoSuchProcess(self.pid, self._name) + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._name) + else: + raise ZombieProcess(self.pid, self._name, self._ppid) if err.errno in (errno.EPERM, errno.EACCES): raise AccessDenied(self.pid, self._name) raise @@ -196,11 +215,12 @@ def wrapper(self, *args, **kwargs): class Process(object): """Wrapper class around underlying C implementation.""" - __slots__ = ["pid", "_name"] + __slots__ = ["pid", "_name", "_ppid"] def __init__(self, pid): self.pid = pid self._name = None + self._ppid = None @wrap_exceptions def name(self): @@ -293,6 +313,8 @@ def connections(self, kind='inet'): for item in rawlist: fd, fam, type, laddr, raddr, status = item status = TCP_STATUSES[status] + fam = sockfam_to_enum(fam) + type = socktype_to_enum(type) nt = _common.pconn(fd, fam, type, laddr, raddr, status) ret.append(nt) return ret @@ -315,11 +337,11 @@ def wait(self, timeout=None): @wrap_exceptions def nice_get(self): - return _psutil_posix.getpriority(self.pid) + return cext_posix.getpriority(self.pid) @wrap_exceptions def nice_set(self, value): - return _psutil_posix.setpriority(self.pid, value) + return cext_posix.setpriority(self.pid, value) @wrap_exceptions def status(self): diff --git a/python/psutil/psutil/_psposix.py b/python/psutil/psutil/_psposix.py index 810760439c1c..5bb16a386d14 100644 --- a/python/psutil/psutil/_psposix.py +++ b/python/psutil/psutil/_psposix.py @@ -12,8 +12,8 @@ import sys import time -from psutil._common import sdiskusage, usage_percent, memoize -from psutil._compat import PY3, unicode +from ._common import sdiskusage, usage_percent, memoize +from ._compat import PY3, unicode class TimeoutExpired(Exception): @@ -31,8 +31,7 @@ def pid_exists(pid): return True try: os.kill(pid, 0) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno == errno.ESRCH: # ESRCH == No such process return False @@ -69,17 +68,18 @@ def check_timeout(delay): timer = getattr(time, 'monotonic', time.time) if timeout is not None: - waitcall = lambda: os.waitpid(pid, os.WNOHANG) + def waitcall(): + return os.waitpid(pid, os.WNOHANG) stop_at = timer() + timeout else: - waitcall = lambda: os.waitpid(pid, 0) + def waitcall(): + return os.waitpid(pid, 0) delay = 0.0001 - while 1: + while True: try: retpid, status = waitcall() - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno == errno.EINTR: delay = check_timeout(delay) continue @@ -90,7 +90,7 @@ def check_timeout(delay): # - pid never existed in the first place # In both cases we'll eventually return None as we # can't determine its exit status code. - while 1: + while True: if pid_exists(pid): delay = check_timeout(delay) else: @@ -150,8 +150,7 @@ def _get_terminal_map(): assert name not in ret try: ret[os.stat(name).st_rdev] = name - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno != errno.ENOENT: raise return ret diff --git a/python/psutil/psutil/_pssunos.py b/python/psutil/psutil/_pssunos.py index 2fc1ec4522b9..bc35a718c36e 100644 --- a/python/psutil/psutil/_pssunos.py +++ b/python/psutil/psutil/_pssunos.py @@ -11,18 +11,21 @@ import socket import subprocess import sys +from collections import namedtuple -from psutil import _common -from psutil import _psposix -from psutil._common import usage_percent, isfile_strict -from psutil._compat import namedtuple, PY3 -import _psutil_posix -import _psutil_sunos as cext +from . import _common +from . import _psposix +from . import _psutil_posix as cext_posix +from . import _psutil_sunos as cext +from ._common import isfile_strict, socktype_to_enum, sockfam_to_enum +from ._common import usage_percent +from ._compat import PY3 __extra__all__ = ["CONN_IDLE", "CONN_BOUND"] PAGE_SIZE = os.sysconf('SC_PAGE_SIZE') +AF_LINK = cext_posix.AF_LINK CONN_IDLE = "IDLE" CONN_BOUND = "BOUND" @@ -63,6 +66,7 @@ # set later from __init__.py NoSuchProcess = None +ZombieProcess = None AccessDenied = None TimeoutExpired = None @@ -71,6 +75,7 @@ disk_io_counters = cext.disk_io_counters net_io_counters = cext.net_io_counters disk_usage = _psposix.disk_usage +net_if_addrs = cext_posix.net_if_addrs def virtual_memory(): @@ -91,7 +96,9 @@ def swap_memory(): # usr/src/cmd/swap/swap.c # ...nevertheless I can't manage to obtain the same numbers as 'swap' # cmdline utility, so let's parse its output (sigh!) - p = subprocess.Popen(['swap', '-l', '-k'], stdout=subprocess.PIPE) + p = subprocess.Popen(['/usr/bin/env', 'PATH=/usr/sbin:/sbin:%s' % + os.environ['PATH'], 'swap', '-l', '-k'], + stdout=subprocess.PIPE) stdout, stderr = p.communicate() if PY3: stdout = stdout.decode(sys.stdout.encoding) @@ -209,7 +216,7 @@ def net_connections(kind, _pid=-1): % (kind, ', '.join([repr(x) for x in cmap]))) families, types = _common.conn_tmap[kind] rawlist = cext.net_connections(_pid, families, types) - ret = [] + ret = set() for item in rawlist: fd, fam, type_, laddr, raddr, status, pid = item if fam not in families: @@ -217,11 +224,24 @@ def net_connections(kind, _pid=-1): if type_ not in types: continue status = TCP_STATUSES[status] + fam = sockfam_to_enum(fam) + type_ = socktype_to_enum(type_) if _pid == -1: nt = _common.sconn(fd, fam, type_, laddr, raddr, status, pid) else: nt = _common.pconn(fd, fam, type_, laddr, raddr, status) - ret.append(nt) + ret.add(nt) + return list(ret) + + +def net_if_stats(): + """Get NIC stats (isup, duplex, speed, mtu).""" + ret = cext.net_if_stats() + for name, items in ret.items(): + isup, duplex, speed, mtu = items + if hasattr(_common, 'NicDuplex'): + duplex = _common.NicDuplex(duplex) + ret[name] = _common.snicstats(isup, duplex, speed, mtu) return ret @@ -232,16 +252,19 @@ def wrap_exceptions(fun): def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except EnvironmentError: + except EnvironmentError as err: # support for private module import - if NoSuchProcess is None or AccessDenied is None: + if (NoSuchProcess is None or AccessDenied is None or + ZombieProcess is None): raise # ENOENT (no such file or directory) gets raised on open(). # ESRCH (no such process) can get raised on read() if # process is gone in meantime. - err = sys.exc_info()[1] if err.errno in (errno.ENOENT, errno.ESRCH): - raise NoSuchProcess(self.pid, self._name) + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._name) + else: + raise ZombieProcess(self.pid, self._name, self._ppid) if err.errno in (errno.EPERM, errno.EACCES): raise AccessDenied(self.pid, self._name) raise @@ -251,11 +274,12 @@ def wrapper(self, *args, **kwargs): class Process(object): """Wrapper class around underlying C implementation.""" - __slots__ = ["pid", "_name"] + __slots__ = ["pid", "_name", "_ppid"] def __init__(self, pid): self.pid = pid self._name = None + self._ppid = None @wrap_exceptions def name(self): @@ -292,10 +316,11 @@ def nice_get(self): # Note: tested on Solaris 11; on Open Solaris 5 everything is # fine. try: - return _psutil_posix.getpriority(self.pid) - except EnvironmentError: - err = sys.exc_info()[1] - if err.errno in (errno.ENOENT, errno.ESRCH): + return cext_posix.getpriority(self.pid) + except EnvironmentError as err: + # 48 is 'operation not supported' but errno does not expose + # it. It occurs for low system pids. + if err.errno in (errno.ENOENT, errno.ESRCH, 48): if pid_exists(self.pid): raise AccessDenied(self.pid, self._name) raise @@ -308,7 +333,7 @@ def nice_set(self, value): # The process actually exists though, as it has a name, # creation time, etc. raise AccessDenied(self.pid, self._name) - return _psutil_posix.setpriority(self.pid, value) + return cext_posix.setpriority(self.pid, value) @wrap_exceptions def ppid(self): @@ -338,8 +363,7 @@ def terminal(self): for x in (0, 1, 2, 255): try: return os.readlink('/proc/%d/path/%d' % (self.pid, x)) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno == errno.ENOENT: hit_enoent = True continue @@ -356,8 +380,7 @@ def cwd(self): # Reference: http://goo.gl/55XgO try: return os.readlink("/proc/%s/path/cwd" % self.pid) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno == errno.ENOENT: os.stat("/proc/%s" % self.pid) return None @@ -388,9 +411,8 @@ def threads(self): try: utime, stime = cext.query_process_thread( self.pid, tid) - except EnvironmentError: + except EnvironmentError as err: # ENOENT == thread gone in meantime - err = sys.exc_info()[1] if err.errno == errno.ENOENT: hit_enoent = True continue @@ -413,9 +435,8 @@ def open_files(self): if os.path.islink(path): try: file = os.readlink(path) - except OSError: + except OSError as err: # ENOENT == file which is gone in the meantime - err = sys.exc_info()[1] if err.errno == errno.ENOENT: hit_enoent = True continue @@ -495,8 +516,7 @@ def toaddr(start, end): if not name.startswith('['): try: name = os.readlink('/proc/%s/path/%s' % (self.pid, name)) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno == errno.ENOENT: # sometimes the link may not be resolved by # readlink() even if it exists (ls shows it). diff --git a/python/psutil/psutil/_psutil_bsd.c b/python/psutil/psutil/_psutil_bsd.c index 5a9f9c08b70c..7b6e56173d2d 100644 --- a/python/psutil/psutil/_psutil_bsd.c +++ b/python/psutil/psutil/_psutil_bsd.c @@ -21,12 +21,14 @@ #include #include #include +#include #include #include #include // for struct xsocket #include #include +#include // for xinpcb struct #include #include @@ -49,6 +51,7 @@ #include // net io counters #include #include +#include #include // process open files/connections #include @@ -91,6 +94,18 @@ psutil_kinfo_proc(const pid_t pid, struct kinfo_proc *proc) } +/* + * Set exception to AccessDenied if pid exists else NoSuchProcess. + */ +void +psutil_raise_ad_or_nsp(long pid) { + if (psutil_pid_exists(pid) == 0) + NoSuchProcess(); + else + AccessDenied(); +} + + /* * Return a Python list of all the PIDs running on the system. */ @@ -104,9 +119,8 @@ psutil_pids(PyObject *self, PyObject *args) PyObject *retlist = PyList_New(0); PyObject *pid = NULL; - if (retlist == NULL) { + if (retlist == NULL) return NULL; - } if (psutil_get_proc_list(&proclist, &num_processes) != 0) { PyErr_SetString(PyExc_RuntimeError, "failed to retrieve process list."); @@ -132,9 +146,8 @@ psutil_pids(PyObject *self, PyObject *args) error: Py_XDECREF(pid); Py_DECREF(retlist); - if (orig_address != NULL) { + if (orig_address != NULL) free(orig_address); - } return NULL; } @@ -167,12 +180,10 @@ psutil_proc_name(PyObject *self, PyObject *args) { long pid; struct kinfo_proc kp; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (psutil_kinfo_proc(pid, &kp) == -1) { + if (psutil_kinfo_proc(pid, &kp) == -1) return NULL; - } return Py_BuildValue("s", kp.ki_comm); } @@ -191,9 +202,8 @@ psutil_proc_exe(PyObject *self, PyObject *args) int mib[4]; size_t size; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } mib[0] = CTL_KERN; mib[1] = KERN_PROC; @@ -207,12 +217,10 @@ psutil_proc_exe(PyObject *self, PyObject *args) return NULL; } if (size == 0 || strlen(pathname) == 0) { - if (psutil_pid_exists(pid) == 0) { + if (psutil_pid_exists(pid) == 0) return NoSuchProcess(); - } - else { + else strcpy(pathname, ""); - } } return Py_BuildValue("s", pathname); } @@ -227,18 +235,16 @@ psutil_proc_cmdline(PyObject *self, PyObject *args) long pid; PyObject *arglist = NULL; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } // get the commandline, defined in arch/bsd/process_info.c arglist = psutil_get_arg_list(pid); // psutil_get_arg_list() returns NULL only if psutil_cmd_args // failed with ESRCH (no process with that PID) - if (NULL == arglist) { + if (NULL == arglist) return PyErr_SetFromErrno(PyExc_OSError); - } return Py_BuildValue("N", arglist); } @@ -251,12 +257,10 @@ psutil_proc_ppid(PyObject *self, PyObject *args) { long pid; struct kinfo_proc kp; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (psutil_kinfo_proc(pid, &kp) == -1) { + if (psutil_kinfo_proc(pid, &kp) == -1) return NULL; - } return Py_BuildValue("l", (long)kp.ki_ppid); } @@ -269,12 +273,10 @@ psutil_proc_status(PyObject *self, PyObject *args) { long pid; struct kinfo_proc kp; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (psutil_kinfo_proc(pid, &kp) == -1) { + if (psutil_kinfo_proc(pid, &kp) == -1) return NULL; - } return Py_BuildValue("i", (int)kp.ki_stat); } @@ -288,12 +290,10 @@ psutil_proc_uids(PyObject *self, PyObject *args) { long pid; struct kinfo_proc kp; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (psutil_kinfo_proc(pid, &kp) == -1) { + if (psutil_kinfo_proc(pid, &kp) == -1) return NULL; - } return Py_BuildValue("lll", (long)kp.ki_ruid, (long)kp.ki_uid, @@ -310,12 +310,10 @@ psutil_proc_gids(PyObject *self, PyObject *args) { long pid; struct kinfo_proc kp; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (psutil_kinfo_proc(pid, &kp) == -1) { + if (psutil_kinfo_proc(pid, &kp) == -1) return NULL; - } return Py_BuildValue("lll", (long)kp.ki_rgid, (long)kp.ki_groups[0], @@ -332,12 +330,10 @@ psutil_proc_tty_nr(PyObject *self, PyObject *args) { long pid; struct kinfo_proc kp; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (psutil_kinfo_proc(pid, &kp) == -1) { + if (psutil_kinfo_proc(pid, &kp) == -1) return NULL; - } return Py_BuildValue("i", kp.ki_tdev); } @@ -350,12 +346,10 @@ psutil_proc_num_ctx_switches(PyObject *self, PyObject *args) { long pid; struct kinfo_proc kp; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (psutil_kinfo_proc(pid, &kp) == -1) { + if (psutil_kinfo_proc(pid, &kp) == -1) return NULL; - } return Py_BuildValue("(ll)", kp.ki_rusage.ru_nvcsw, kp.ki_rusage.ru_nivcsw); @@ -370,12 +364,10 @@ psutil_proc_num_threads(PyObject *self, PyObject *args) { long pid; struct kinfo_proc kp; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (psutil_kinfo_proc(pid, &kp) == -1) { + if (psutil_kinfo_proc(pid, &kp) == -1) return NULL; - } return Py_BuildValue("l", (long)kp.ki_numthreads); } @@ -456,9 +448,8 @@ psutil_proc_threads(PyObject *self, PyObject *args) error: Py_XDECREF(pyTuple); Py_DECREF(retList); - if (kip != NULL) { + if (kip != NULL) free(kip); - } return NULL; } @@ -472,12 +463,10 @@ psutil_proc_cpu_times(PyObject *self, PyObject *args) long pid; double user_t, sys_t; struct kinfo_proc kp; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (psutil_kinfo_proc(pid, &kp) == -1) { + if (psutil_kinfo_proc(pid, &kp) == -1) return NULL; - } // convert from microseconds to seconds user_t = TV2DOUBLE(kp.ki_rusage.ru_utime); sys_t = TV2DOUBLE(kp.ki_rusage.ru_stime); @@ -500,14 +489,10 @@ psutil_cpu_count_logical(PyObject *self, PyObject *args) mib[1] = HW_NCPU; len = sizeof(ncpu); - if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) { - // mimic os.cpu_count() - Py_INCREF(Py_None); - return Py_None; - } - else { + if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) + Py_RETURN_NONE; // mimic os.cpu_count() + else return Py_BuildValue("i", ncpu); - } } @@ -520,6 +505,7 @@ psutil_cpu_count_phys(PyObject *self, PyObject *args) { void *topology = NULL; size_t size = 0; + PyObject *py_str; if (sysctlbyname("kern.sched.topology_spec", NULL, &size, NULL, 0)) goto error; @@ -533,11 +519,14 @@ psutil_cpu_count_phys(PyObject *self, PyObject *args) if (sysctlbyname("kern.sched.topology_spec", topology, &size, NULL, 0)) goto error; - return Py_BuildValue("s", topology); + py_str = Py_BuildValue("s", topology); + free(topology); + return py_str; error: - Py_INCREF(Py_None); - return Py_None; + if (topology != NULL) + free(topology); + Py_RETURN_NONE; } @@ -550,12 +539,10 @@ psutil_proc_create_time(PyObject *self, PyObject *args) { long pid; struct kinfo_proc kp; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (psutil_kinfo_proc(pid, &kp) == -1) { + if (psutil_kinfo_proc(pid, &kp) == -1) return NULL; - } return Py_BuildValue("d", TV2DOUBLE(kp.ki_start)); } @@ -569,12 +556,10 @@ psutil_proc_io_counters(PyObject *self, PyObject *args) { long pid; struct kinfo_proc kp; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (psutil_kinfo_proc(pid, &kp) == -1) { + if (psutil_kinfo_proc(pid, &kp) == -1) return NULL; - } // there's apparently no way to determine bytes count, hence return -1. return Py_BuildValue("(llll)", kp.ki_rusage.ru_inblock, @@ -592,12 +577,10 @@ psutil_proc_memory_info(PyObject *self, PyObject *args) { long pid; struct kinfo_proc kp; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (psutil_kinfo_proc(pid, &kp) == -1) { + if (psutil_kinfo_proc(pid, &kp) == -1) return NULL; - } return Py_BuildValue("(lllll)", ptoa(kp.ki_rssize), // rss (long)kp.ki_size, // vms @@ -749,7 +732,10 @@ psutil_cpu_times(PyObject *self, PyObject *args) #if defined(__FreeBSD_version) && __FreeBSD_version >= 800000 /* - * Return files opened by process as a list of (path, fd) tuples + * Return files opened by process as a list of (path, fd) tuples. + * TODO: this is broken as it may report empty paths. 'procstat' + * utility has the same problem see: + * https://github.com/giampaolo/psutil/issues/595 */ static PyObject * psutil_proc_open_files(PyObject *self, PyObject *args) @@ -867,9 +853,8 @@ psutil_proc_cwd(PyObject *self, PyObject *args) * (lsof can't do that it either). Since this happens even * as root we return an empty string instead of AccessDenied. */ - if (path == NULL) { + if (path == NULL) path = Py_BuildValue("s", ""); - } free(freep); return path; @@ -887,7 +872,6 @@ psutil_fetch_tcplist(void) { char *buf; size_t len; - int error; for (;;) { if (sysctlbyname("net.inet.tcp.pcblist", NULL, &len, NULL, 0) < 0) { @@ -917,7 +901,8 @@ psutil_sockaddr_port(int family, struct sockaddr_storage *ss) if (family == AF_INET) { sin = (struct sockaddr_in *)ss; return (sin->sin_port); - } else { + } + else { sin6 = (struct sockaddr_in6 *)ss; return (sin6->sin6_port); } @@ -932,7 +917,8 @@ psutil_sockaddr_addr(int family, struct sockaddr_storage *ss) if (family == AF_INET) { sin = (struct sockaddr_in *)ss; return (&sin->sin_addr); - } else { + } + else { sin6 = (struct sockaddr_in6 *)ss; return (&sin6->sin6_addr); } @@ -1030,12 +1016,10 @@ psutil_proc_connections(PyObject *self, PyObject *args) PyObject *_family = NULL; PyObject *_type = NULL; - if (retList == NULL) { + if (retList == NULL) return NULL; - } - if (! PyArg_ParseTuple(args, "lOO", &pid, &af_filter, &type_filter)) { + if (! PyArg_ParseTuple(args, "lOO", &pid, &af_filter, &type_filter)) goto error; - } if (!PySequence_Check(af_filter) || !PySequence_Check(type_filter)) { PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence"); goto error; @@ -1063,22 +1047,18 @@ psutil_proc_connections(PyObject *self, PyObject *args) raddr = NULL; kif = &freep[i]; - if (kif->kf_type == KF_TYPE_SOCKET) - { + if (kif->kf_type == KF_TYPE_SOCKET) { // apply filters _family = PyLong_FromLong((long)kif->kf_sock_domain); inseq = PySequence_Contains(af_filter, _family); Py_DECREF(_family); - if (inseq == 0) { + if (inseq == 0) continue; - } _type = PyLong_FromLong((long)kif->kf_sock_type); inseq = PySequence_Contains(type_filter, _type); Py_DECREF(_type); - if (inseq == 0) { + if (inseq == 0) continue; - } - // IPv4 / IPv6 socket if ((kif->kf_sock_domain == AF_INET) || (kif->kf_sock_domain == AF_INET6)) { @@ -1112,12 +1092,10 @@ psutil_proc_connections(PyObject *self, PyObject *args) laddr = Py_BuildValue("(si)", lip, lport); if (!laddr) goto error; - if (rport != 0) { + if (rport != 0) raddr = Py_BuildValue("(si)", rip, rport); - } - else { + else raddr = Py_BuildValue("()"); - } if (!raddr) goto error; tuple = Py_BuildValue("(iiiNNi)", @@ -1140,7 +1118,7 @@ psutil_proc_connections(PyObject *self, PyObject *args) sun = (struct sockaddr_un *)&kif->kf_sa_local; snprintf( path, sizeof(path), "%.*s", - (sun->sun_len - (sizeof(*sun) - sizeof(sun->sun_path))), + (int)(sun->sun_len - (sizeof(*sun) - sizeof(sun->sun_path))), sun->sun_path); tuple = Py_BuildValue("(iiisOi)", @@ -1250,7 +1228,7 @@ void remove_spaces(char *str) { do while (*p2 == ' ') p2++; - while (*p1++ = *p2++); + while ((*p1++ = *p2++)); } @@ -1264,7 +1242,7 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) long pid; int ptrwidth; int i, cnt; - char addr[30]; + char addr[1000]; char perms[4]; const char *path; struct kinfo_proc kp; @@ -1274,15 +1252,12 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) PyObject *pytuple = NULL; PyObject *retlist = PyList_New(0); - if (retlist == NULL) { + if (retlist == NULL) return NULL; - } - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) goto error; - } - if (psutil_kinfo_proc(pid, &kp) == -1) { + if (psutil_kinfo_proc(pid, &kp) == -1) goto error; - } freep = kinfo_getvmmap(pid, &cnt); if (freep == NULL) { @@ -1306,36 +1281,36 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) if (strlen(kve->kve_path) == 0) { switch (kve->kve_type) { - case KVME_TYPE_NONE: - path = "[none]"; - break; - case KVME_TYPE_DEFAULT: - path = "[default]"; - break; - case KVME_TYPE_VNODE: - path = "[vnode]"; - break; - case KVME_TYPE_SWAP: - path = "[swap]"; - break; - case KVME_TYPE_DEVICE: - path = "[device]"; - break; - case KVME_TYPE_PHYS: - path = "[phys]"; - break; - case KVME_TYPE_DEAD: - path = "[dead]"; - break; - case KVME_TYPE_SG: - path = "[sg]"; - break; - case KVME_TYPE_UNKNOWN: - path = "[unknown]"; - break; - default: - path = "[?]"; - break; + case KVME_TYPE_NONE: + path = "[none]"; + break; + case KVME_TYPE_DEFAULT: + path = "[default]"; + break; + case KVME_TYPE_VNODE: + path = "[vnode]"; + break; + case KVME_TYPE_SWAP: + path = "[swap]"; + break; + case KVME_TYPE_DEVICE: + path = "[device]"; + break; + case KVME_TYPE_PHYS: + path = "[phys]"; + break; + case KVME_TYPE_DEAD: + path = "[dead]"; + break; + case KVME_TYPE_SG: + path = "[sg]"; + break; + case KVME_TYPE_UNKNOWN: + path = "[unknown]"; + break; + default: + path = "[?]"; + break; } } else { @@ -1489,9 +1464,9 @@ psutil_net_io_counters(PyObject *self, PyObject *args) size_t len; PyObject *py_retdict = PyDict_New(); PyObject *py_ifc_info = NULL; + if (py_retdict == NULL) return NULL; - mib[0] = CTL_NET; // networking subsystem mib[1] = PF_ROUTE; // type of information mib[2] = 0; // protocol (IPPROTO_xxx) @@ -1533,9 +1508,8 @@ psutil_net_io_counters(PyObject *self, PyObject *args) // http://lists.freebsd.org/pipermail/freebsd-current/ // 2011-October/028752.html // 'ifconfig -a' doesn't show them, nor do we. - if (strncmp(ifc_name, "usbus", 5) == 0) { + if (strncmp(ifc_name, "usbus", 5) == 0) continue; - } py_ifc_info = Py_BuildValue("(kkkkkkki)", if2m->ifm_data.ifi_obytes, @@ -1580,9 +1554,9 @@ psutil_disk_io_counters(PyObject *self, PyObject *args) PyObject *py_retdict = PyDict_New(); PyObject *py_disk_info = NULL; + if (py_retdict == NULL) return NULL; - if (devstat_checkversion(NULL) < 0) { PyErr_Format(PyExc_RuntimeError, "devstat_checkversion() failed"); goto error; @@ -1626,9 +1600,8 @@ psutil_disk_io_counters(PyObject *self, PyObject *args) Py_DECREF(py_disk_info); } - if (stats.dinfo->mem_ptr) { + if (stats.dinfo->mem_ptr) free(stats.dinfo->mem_ptr); - } free(stats.dinfo); return py_retdict; @@ -1766,14 +1739,16 @@ psutil_get_pid_from_sock(int sock_hash) if (xf->xf_data == NULL) continue; hash = (int)((uintptr_t)xf->xf_data % HASHSIZE); - if (sock_hash == hash) { + if (sock_hash == hash) return xf->xf_pid; - } } return -1; } +// Reference: +// https://gitorious.org/freebsd/freebsd/source/ +// f1d6f4778d2044502209708bc167c05f9aa48615:usr.bin/sockstat/sockstat.c int psutil_gather_inet(int proto, PyObject *py_retlist) { struct xinpgen *xig, *exig; @@ -1781,25 +1756,26 @@ int psutil_gather_inet(int proto, PyObject *py_retlist) struct xtcpcb *xtp; struct inpcb *inp; struct xsocket *so; - struct sock *sock; - const char *varname; + const char *varname = NULL; size_t len, bufsize; void *buf; - int hash, retry, vflag, type; + int hash; + int retry; + int type; PyObject *tuple = NULL; PyObject *laddr = NULL; PyObject *raddr = NULL; switch (proto) { - case IPPROTO_TCP: - varname = "net.inet.tcp.pcblist"; - type = SOCK_STREAM; - break; - case IPPROTO_UDP: - varname = "net.inet.udp.pcblist"; - type = SOCK_DGRAM; - break; + case IPPROTO_TCP: + varname = "net.inet.tcp.pcblist"; + type = SOCK_STREAM; + break; + case IPPROTO_UDP: + varname = "net.inet.udp.pcblist"; + type = SOCK_DGRAM; + break; } buf = NULL; @@ -1808,10 +1784,8 @@ int psutil_gather_inet(int proto, PyObject *py_retlist) do { for (;;) { buf = realloc(buf, bufsize); - if (buf == NULL) { - // XXX - continue; - } + if (buf == NULL) + continue; // XXX len = bufsize; if (sysctlbyname(varname, buf, &len, NULL, 0) == 0) break; @@ -1831,33 +1805,41 @@ int psutil_gather_inet(int proto, PyObject *py_retlist) for (;;) { + int lport, rport, pid, status, family; + xig = (struct xinpgen *)(void *)((char *)xig + xig->xig_len); if (xig >= exig) break; switch (proto) { - case IPPROTO_TCP: - xtp = (struct xtcpcb *)xig; - if (xtp->xt_len != sizeof *xtp) { - PyErr_Format(PyExc_RuntimeError, "struct xtcpcb size mismatch"); - goto error; - } - break; - case IPPROTO_UDP: - xip = (struct xinpcb *)xig; - if (xip->xi_len != sizeof *xip) { - PyErr_Format(PyExc_RuntimeError, "struct xinpcb size mismatch"); + case IPPROTO_TCP: + xtp = (struct xtcpcb *)xig; + if (xtp->xt_len != sizeof *xtp) { + PyErr_Format(PyExc_RuntimeError, + "struct xtcpcb size mismatch"); + goto error; + } + inp = &xtp->xt_inp; + so = &xtp->xt_socket; + status = xtp->xt_tp.t_state; + break; + case IPPROTO_UDP: + xip = (struct xinpcb *)xig; + if (xip->xi_len != sizeof *xip) { + PyErr_Format(PyExc_RuntimeError, + "struct xinpcb size mismatch"); + goto error; + } + inp = &xip->xi_inp; + so = &xip->xi_socket; + status = PSUTIL_CONN_NONE; + break; + default: + PyErr_Format(PyExc_RuntimeError, "invalid proto"); goto error; - } - inp = &xip->xi_inp; - so = &xip->xi_socket; - break; } - inp = &xtp->xt_inp; - so = &xtp->xt_socket; char lip[200], rip[200]; - int family, lport, rport, pid, status; hash = (int)((uintptr_t)so->xso_so % HASHSIZE); pid = psutil_get_pid_from_sock(hash); @@ -1865,7 +1847,6 @@ int psutil_gather_inet(int proto, PyObject *py_retlist) continue; lport = ntohs(inp->inp_lport); rport = ntohs(inp->inp_fport); - status = xtp->xt_tp.t_state; if (inp->inp_vflag & INP_IPV4) { family = AF_INET; @@ -1882,12 +1863,10 @@ int psutil_gather_inet(int proto, PyObject *py_retlist) laddr = Py_BuildValue("(si)", lip, lport); if (!laddr) goto error; - if (rport != 0) { + if (rport != 0) raddr = Py_BuildValue("(si)", rip, rport); - } - else { + else raddr = Py_BuildValue("()"); - } if (!raddr) goto error; tuple = Py_BuildValue("(iiiNNii)", -1, family, type, laddr, raddr, @@ -1897,7 +1876,7 @@ int psutil_gather_inet(int proto, PyObject *py_retlist) if (PyList_Append(py_retlist, tuple)) goto error; Py_DECREF(tuple); - } + } free(buf); return 1; @@ -1915,12 +1894,14 @@ int psutil_gather_unix(int proto, PyObject *py_retlist) { struct xunpgen *xug, *exug; struct xunpcb *xup; - struct sock *sock; - const char *varname, *protoname; - size_t len, bufsize; + const char *varname = NULL; + const char *protoname = NULL; + size_t len; + size_t bufsize; void *buf; - int hash, retry; - int family, lport, rport, pid; + int hash; + int retry; + int pid; struct sockaddr_un *sun; char path[PATH_MAX]; @@ -1929,14 +1910,14 @@ int psutil_gather_unix(int proto, PyObject *py_retlist) PyObject *raddr = NULL; switch (proto) { - case SOCK_STREAM: - varname = "net.local.stream.pcblist"; - protoname = "stream"; - break; - case SOCK_DGRAM: - varname = "net.local.dgram.pcblist"; - protoname = "dgram"; - break; + case SOCK_STREAM: + varname = "net.local.stream.pcblist"; + protoname = "stream"; + break; + case SOCK_DGRAM: + varname = "net.local.dgram.pcblist"; + protoname = "dgram"; + break; } buf = NULL; @@ -1973,10 +1954,8 @@ int psutil_gather_unix(int proto, PyObject *py_retlist) if (xug >= exug) break; xup = (struct xunpcb *)xug; - if (xup->xu_len != sizeof *xup) { - warnx("struct xunpgen size mismatch"); + if (xup->xu_len != sizeof *xup) goto error; - } hash = (int)((uintptr_t) xup->xu_socket.xso_so % HASHSIZE); pid = psutil_get_pid_from_sock(hash); @@ -1985,7 +1964,7 @@ int psutil_gather_unix(int proto, PyObject *py_retlist) sun = (struct sockaddr_un *)&xup->xu_addr; snprintf(path, sizeof(path), "%.*s", - (sun->sun_len - (sizeof(*sun) - sizeof(sun->sun_path))), + (int)(sun->sun_len - (sizeof(*sun) - sizeof(sun->sun_path))), sun->sun_path); tuple = Py_BuildValue("(iiisOii)", -1, AF_UNIX, proto, path, Py_None, @@ -2016,13 +1995,12 @@ int psutil_gather_unix(int proto, PyObject *py_retlist) static PyObject* psutil_net_connections(PyObject* self, PyObject* args) { - PyObject *af_filter = NULL; - PyObject *type_filter = NULL; PyObject *py_retlist = PyList_New(0); + if (py_retlist == NULL) + return NULL; if (psutil_populate_xfiles() != 1) goto error; - if (psutil_gather_inet(IPPROTO_TCP, py_retlist) == 0) goto error; if (psutil_gather_inet(IPPROTO_UDP, py_retlist) == 0) @@ -2042,6 +2020,107 @@ psutil_net_connections(PyObject* self, PyObject* args) } +/* + * Get process CPU affinity. + * Reference: http://sources.freebsd.org/RELENG_9/src/usr.bin/cpuset/cpuset.c + */ +static PyObject* +psutil_proc_cpu_affinity_get(PyObject* self, PyObject* args) +{ + long pid; + int ret; + int i; + cpuset_t mask; + PyObject* py_retlist; + PyObject* py_cpu_num; + + if (!PyArg_ParseTuple(args, "i", &pid)) + return NULL; + ret = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, pid, + sizeof(mask), &mask); + if (ret != 0) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + py_retlist = PyList_New(0); + if (py_retlist == NULL) + return NULL; + + for (i = 0; i < CPU_SETSIZE; i++) { + if (CPU_ISSET(i, &mask)) { + py_cpu_num = Py_BuildValue("i", i); + if (py_cpu_num == NULL) + goto error; + if (PyList_Append(py_retlist, py_cpu_num)) + goto error; + } + } + + return py_retlist; + +error: + Py_XDECREF(py_cpu_num); + Py_DECREF(py_retlist); + return NULL; +} + + +/* + * Set process CPU affinity. + * Reference: http://sources.freebsd.org/RELENG_9/src/usr.bin/cpuset/cpuset.c + */ +static PyObject * +psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) +{ + long pid; + int i; + int seq_len; + int ret; + cpuset_t cpu_set; + PyObject *py_cpu_set; + PyObject *py_cpu_seq = NULL; + + if (!PyArg_ParseTuple(args, "lO", &pid, &py_cpu_set)) + return NULL; + + py_cpu_seq = PySequence_Fast(py_cpu_set, "expected a sequence or integer"); + if (!py_cpu_seq) + return NULL; + seq_len = PySequence_Fast_GET_SIZE(py_cpu_seq); + + // calculate the mask + CPU_ZERO(&cpu_set); + for (i = 0; i < seq_len; i++) { + PyObject *item = PySequence_Fast_GET_ITEM(py_cpu_seq, i); +#if PY_MAJOR_VERSION >= 3 + long value = PyLong_AsLong(item); +#else + long value = PyInt_AsLong(item); +#endif + if (value == -1 && PyErr_Occurred()) + goto error; + CPU_SET(value, &cpu_set); + } + + // set affinity + ret = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, pid, + sizeof(cpu_set), &cpu_set); + if (ret != 0) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + + Py_DECREF(py_cpu_seq); + Py_RETURN_NONE; + +error: + if (py_cpu_seq != NULL) + Py_DECREF(py_cpu_seq); + return NULL; +} + + /* * define the psutil C module methods and initialize the module. */ @@ -2083,6 +2162,10 @@ PsutilMethods[] = "Return process IO counters"}, {"proc_tty_nr", psutil_proc_tty_nr, METH_VARARGS, "Return process tty (terminal) number"}, + {"proc_cpu_affinity_get", psutil_proc_cpu_affinity_get, METH_VARARGS, + "Return process CPU affinity."}, + {"proc_cpu_affinity_set", psutil_proc_cpu_affinity_set, METH_VARARGS, + "Set process CPU affinity."}, #if defined(__FreeBSD_version) && __FreeBSD_version >= 800000 {"proc_open_files", psutil_proc_open_files, METH_VARARGS, "Return files opened by process as a list of (path, fd) tuples"}, @@ -2181,6 +2264,8 @@ void init_psutil_bsd(void) #else PyObject *module = Py_InitModule("_psutil_bsd", PsutilMethods); #endif + PyModule_AddIntConstant(module, "version", PSUTIL_VERSION); + // process status constants PyModule_AddIntConstant(module, "SSTOP", SSTOP); PyModule_AddIntConstant(module, "SSLEEP", SSLEEP); @@ -2203,9 +2288,8 @@ void init_psutil_bsd(void) PyModule_AddIntConstant(module, "TCPS_TIME_WAIT", TCPS_TIME_WAIT); PyModule_AddIntConstant(module, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE); - if (module == NULL) { + if (module == NULL) INITERROR; - } #if PY_MAJOR_VERSION >= 3 return module; #endif diff --git a/python/psutil/psutil/_psutil_bsd.h b/python/psutil/psutil/_psutil_bsd.h index 2bc7c7018a84..803957dac78a 100644 --- a/python/psutil/psutil/_psutil_bsd.h +++ b/python/psutil/psutil/_psutil_bsd.h @@ -26,6 +26,8 @@ static PyObject* psutil_proc_status(PyObject* self, PyObject* args); static PyObject* psutil_proc_threads(PyObject* self, PyObject* args); static PyObject* psutil_proc_tty_nr(PyObject* self, PyObject* args); static PyObject* psutil_proc_uids(PyObject* self, PyObject* args); +static PyObject* psutil_proc_cpu_affinity_get(PyObject* self, PyObject* args); +static PyObject* psutil_proc_cpu_affinity_set(PyObject* self, PyObject* args); #if defined(__FreeBSD_version) && __FreeBSD_version >= 800000 static PyObject* psutil_proc_open_files(PyObject* self, PyObject* args); @@ -48,4 +50,4 @@ static PyObject* psutil_virtual_mem(PyObject* self, PyObject* args); #if defined(__FreeBSD_version) && __FreeBSD_version >= 800000 static PyObject* psutil_per_cpu_times(PyObject* self, PyObject* args); -#endif \ No newline at end of file +#endif diff --git a/python/psutil/psutil/_psutil_linux.c b/python/psutil/psutil/_psutil_linux.c index 4602178e4e50..a3bf5643ccc0 100644 --- a/python/psutil/psutil/_psutil_linux.c +++ b/python/psutil/psutil/_psutil_linux.c @@ -19,6 +19,11 @@ #include #include #include +#include +#include +#include +#include +#include #include "_psutil_linux.h" @@ -46,6 +51,12 @@ enum { IOPRIO_WHO_PROCESS = 1, }; +// May happen on old RedHat versions, see: +// https://github.com/giampaolo/psutil/issues/607 +#ifndef DUPLEX_UNKNOWN + #define DUPLEX_UNKNOWN 0xff +#endif + static inline int ioprio_get(int which, int who) { @@ -74,13 +85,11 @@ psutil_proc_ioprio_get(PyObject *self, PyObject *args) { long pid; int ioprio, ioclass, iodata; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid); - if (ioprio == -1) { + if (ioprio == -1) return PyErr_SetFromErrno(PyExc_OSError); - } ioclass = IOPRIO_PRIO_CLASS(ioprio); iodata = IOPRIO_PRIO_DATA(ioprio); return Py_BuildValue("ii", ioclass, iodata); @@ -99,16 +108,13 @@ psutil_proc_ioprio_set(PyObject *self, PyObject *args) int ioprio, ioclass, iodata; int retval; - if (! PyArg_ParseTuple(args, "lii", &pid, &ioclass, &iodata)) { + if (! PyArg_ParseTuple(args, "lii", &pid, &ioclass, &iodata)) return NULL; - } ioprio = IOPRIO_PRIO_VALUE(ioclass, iodata); retval = ioprio_set(IOPRIO_WHO_PROCESS, pid, ioprio); - if (retval == -1) { + if (retval == -1) return PyErr_SetFromErrno(PyExc_OSError); - } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } #endif @@ -129,9 +135,8 @@ psutil_linux_prlimit(PyObject *self, PyObject *args) PyObject *soft = NULL; PyObject *hard = NULL; - if (! PyArg_ParseTuple(args, "li|OO", &pid, &resource, &soft, &hard)) { + if (! PyArg_ParseTuple(args, "li|OO", &pid, &resource, &soft, &hard)) return NULL; - } // get if (soft == NULL && hard == NULL) { @@ -169,8 +174,7 @@ psutil_linux_prlimit(PyObject *self, PyObject *args) ret = prlimit(pid, resource, newp, &old); if (ret == -1) return PyErr_SetFromErrno(PyExc_OSError); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } } #endif @@ -235,10 +239,9 @@ static PyObject * psutil_linux_sysinfo(PyObject *self, PyObject *args) { struct sysinfo info; - if (sysinfo(&info) != 0) { - return PyErr_SetFromErrno(PyExc_OSError); - } + if (sysinfo(&info) != 0) + return PyErr_SetFromErrno(PyExc_OSError); // note: boot time might also be determined from here return Py_BuildValue( "(KKKKKK)", @@ -268,10 +271,8 @@ psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) cpu_set_t *mask = NULL; PyObject *res = NULL; - if (!PyArg_ParseTuple(args, "i", &pid)) { + if (!PyArg_ParseTuple(args, "i", &pid)) return NULL; - } - ncpus = NCPUS_START; while (1) { setsize = CPU_ALLOC_SIZE(ncpus); @@ -303,7 +304,6 @@ psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) #else PyObject *cpu_num = PyInt_FromLong(cpu); #endif - --count; if (cpu_num == NULL) goto error; if (PyList_Append(res, cpu_num)) { @@ -311,6 +311,7 @@ psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) goto error; } Py_DECREF(cpu_num); + --count; } } CPU_FREE(mask); @@ -325,33 +326,45 @@ psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) #else +/* + * Alternative implementation in case CPU_ALLOC is not defined. + */ static PyObject * psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) { cpu_set_t cpuset; unsigned int len = sizeof(cpu_set_t); long pid; - int i; - PyObject* ret_list; + int i; + PyObject* py_retlist = NULL; + PyObject *py_cpu_num = NULL; - if (!PyArg_ParseTuple(args, "i", &pid)) { + if (!PyArg_ParseTuple(args, "i", &pid)) return NULL; - } - CPU_ZERO(&cpuset); - if (sched_getaffinity(pid, len, &cpuset) < 0) { + if (sched_getaffinity(pid, len, &cpuset) < 0) return PyErr_SetFromErrno(PyExc_OSError); - } - - ret_list = PyList_New(0); + py_retlist = PyList_New(0); + if (py_retlist == NULL) + goto error; for (i = 0; i < CPU_SETSIZE; ++i) { if (CPU_ISSET(i, &cpuset)) { - PyList_Append(ret_list, Py_BuildValue("i", i)); + py_cpu_num = Py_BuildValue("i", i); + if (py_cpu_num == NULL) + goto error; + if (PyList_Append(py_retlist, py_cpu_num)) + goto error; + Py_DECREF(py_cpu_num); } } - return ret_list; + return py_retlist; + +error: + Py_XDECREF(py_cpu_num); + Py_DECREF(py_retlist); + return NULL; } #endif @@ -368,22 +381,18 @@ psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) PyObject *py_cpu_set; PyObject *py_cpu_seq = NULL; - if (!PyArg_ParseTuple(args, "lO", &pid, &py_cpu_set)) { - goto error; - } + if (!PyArg_ParseTuple(args, "lO", &pid, &py_cpu_set)) + return NULL; if (!PySequence_Check(py_cpu_set)) { - // does not work on Python 2.4 - // PyErr_Format(PyExc_TypeError, "sequence argument expected, got %s", - // Py_TYPE(py_cpu_set)->tp_name); - PyErr_Format(PyExc_TypeError, "sequence argument expected"); + PyErr_Format(PyExc_TypeError, "sequence argument expected, got %s", + Py_TYPE(py_cpu_set)->tp_name); goto error; } py_cpu_seq = PySequence_Fast(py_cpu_set, "expected a sequence or integer"); - if (!py_cpu_seq) { + if (!py_cpu_seq) goto error; - } seq_len = PySequence_Fast_GET_SIZE(py_cpu_seq); CPU_ZERO(&cpu_set); for (i = 0; i < seq_len; i++) { @@ -393,9 +402,8 @@ psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) #else long value = PyInt_AsLong(item); #endif - if (value == -1 && PyErr_Occurred()) { + if (value == -1 && PyErr_Occurred()) goto error; - } CPU_SET(value, &cpu_set); } @@ -406,14 +414,11 @@ psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) } Py_DECREF(py_cpu_seq); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; error: - if (py_cpu_seq != NULL) { + if (py_cpu_seq != NULL) Py_DECREF(py_cpu_seq); - } - return NULL; } @@ -465,6 +470,87 @@ psutil_users(PyObject *self, PyObject *args) } +/* + * Return stats about a particular network + * interface. References: + * https://github.com/dpaleino/wicd/blob/master/wicd/backends/be-ioctl.py + * http://www.i-scream.org/libstatgrab/ + */ +static PyObject* +psutil_net_if_stats(PyObject* self, PyObject* args) +{ + char *nic_name; + int sock = 0; + int ret; + int duplex; + int speed; + int mtu; + struct ifreq ifr; + struct ethtool_cmd ethcmd; + PyObject *py_is_up = NULL; + PyObject *py_ret = NULL; + + if (! PyArg_ParseTuple(args, "s", &nic_name)) + return NULL; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == -1) + goto error; + strncpy(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name)); + + // is up? + ret = ioctl(sock, SIOCGIFFLAGS, &ifr); + if (ret == -1) + goto error; + if ((ifr.ifr_flags & IFF_UP) != 0) + py_is_up = Py_True; + else + py_is_up = Py_False; + Py_INCREF(py_is_up); + + // MTU + ret = ioctl(sock, SIOCGIFMTU, &ifr); + if (ret == -1) + goto error; + mtu = ifr.ifr_mtu; + + // duplex and speed + memset(ðcmd, 0, sizeof ethcmd); + ethcmd.cmd = ETHTOOL_GSET; + ifr.ifr_data = (caddr_t)ðcmd; + ret = ioctl(sock, SIOCETHTOOL, &ifr); + + if (ret != -1) { + duplex = ethcmd.duplex; + speed = ethcmd.speed; + } + else { + if (errno == EOPNOTSUPP) { + // we typically get here in case of wi-fi cards + duplex = DUPLEX_UNKNOWN; + speed = 0; + } + else { + goto error; + } + } + + close(sock); + py_ret = Py_BuildValue("[Oiii]", py_is_up, duplex, speed, mtu); + if (!py_ret) + goto error; + Py_DECREF(py_is_up); + return py_ret; + +error: + Py_XDECREF(py_is_up); + if (sock != 0) + close(sock); + PyErr_SetFromErrno(PyExc_OSError); + return NULL; +} + + /* * Define the psutil C module methods and initialize the module. */ @@ -491,6 +577,8 @@ PsutilMethods[] = "device, mount point and filesystem type"}, {"users", psutil_users, METH_VARARGS, "Return currently connected users as a list of tuples"}, + {"net_if_stats", psutil_net_if_stats, METH_VARARGS, + "Return NIC stats (isup, duplex, speed, mtu)"}, // --- linux specific @@ -559,6 +647,7 @@ void init_psutil_linux(void) #endif + PyModule_AddIntConstant(module, "version", PSUTIL_VERSION); #if PSUTIL_HAVE_PRLIMIT PyModule_AddIntConstant(module, "RLIM_INFINITY", RLIM_INFINITY); PyModule_AddIntConstant(module, "RLIMIT_AS", RLIMIT_AS); @@ -588,10 +677,12 @@ void init_psutil_linux(void) PyModule_AddIntConstant(module, "RLIMIT_SIGPENDING", RLIMIT_SIGPENDING); #endif #endif + PyModule_AddIntConstant(module, "DUPLEX_HALF", DUPLEX_HALF); + PyModule_AddIntConstant(module, "DUPLEX_FULL", DUPLEX_FULL); + PyModule_AddIntConstant(module, "DUPLEX_UNKNOWN", DUPLEX_UNKNOWN); - if (module == NULL) { + if (module == NULL) INITERROR; - } #if PY_MAJOR_VERSION >= 3 return module; #endif diff --git a/python/psutil/psutil/_psutil_linux.h b/python/psutil/psutil/_psutil_linux.h index 04ffec3da1f7..ec6a338719a2 100644 --- a/python/psutil/psutil/_psutil_linux.h +++ b/python/psutil/psutil/_psutil_linux.h @@ -18,3 +18,4 @@ static PyObject* psutil_proc_ioprio_get(PyObject* self, PyObject* args); static PyObject* psutil_disk_partitions(PyObject* self, PyObject* args); static PyObject* psutil_linux_sysinfo(PyObject* self, PyObject* args); static PyObject* psutil_users(PyObject* self, PyObject* args); +static PyObject* psutil_net_if_stats(PyObject* self, PyObject* args); diff --git a/python/psutil/psutil/_psutil_osx.c b/python/psutil/psutil/_psutil_osx.c index 0c8334577590..3ebf8ff27219 100644 --- a/python/psutil/psutil/_psutil_osx.c +++ b/python/psutil/psutil/_psutil_osx.c @@ -120,12 +120,10 @@ psutil_proc_name(PyObject *self, PyObject *args) { long pid; struct kinfo_proc kp; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (psutil_get_kinfo_proc(pid, &kp) == -1) { + if (psutil_get_kinfo_proc(pid, &kp) == -1) return NULL; - } return Py_BuildValue("s", kp.kp_proc.p_comm); } @@ -139,9 +137,8 @@ psutil_proc_cwd(PyObject *self, PyObject *args) long pid; struct proc_vnodepathinfo pathinfo; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } if (! psutil_proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, &pathinfo, sizeof(pathinfo))) @@ -162,17 +159,14 @@ psutil_proc_exe(PyObject *self, PyObject *args) char buf[PATH_MAX]; int ret; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } ret = proc_pidpath(pid, &buf, sizeof(buf)); if (ret == 0) { - if (! psutil_pid_exists(pid)) { + if (! psutil_pid_exists(pid)) return NoSuchProcess(); - } - else { + else return AccessDenied(); - } } return Py_BuildValue("s", buf); } @@ -187,9 +181,8 @@ psutil_proc_cmdline(PyObject *self, PyObject *args) long pid; PyObject *arglist = NULL; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } // get the commandline, defined in arch/osx/process_info.c arglist = psutil_get_arg_list(pid); @@ -205,12 +198,10 @@ psutil_proc_ppid(PyObject *self, PyObject *args) { long pid; struct kinfo_proc kp; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (psutil_get_kinfo_proc(pid, &kp) == -1) { + if (psutil_get_kinfo_proc(pid, &kp) == -1) return NULL; - } return Py_BuildValue("l", (long)kp.kp_eproc.e_ppid); } @@ -223,12 +214,10 @@ psutil_proc_uids(PyObject *self, PyObject *args) { long pid; struct kinfo_proc kp; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (psutil_get_kinfo_proc(pid, &kp) == -1) { + if (psutil_get_kinfo_proc(pid, &kp) == -1) return NULL; - } return Py_BuildValue("lll", (long)kp.kp_eproc.e_pcred.p_ruid, (long)kp.kp_eproc.e_ucred.cr_uid, @@ -244,12 +233,10 @@ psutil_proc_gids(PyObject *self, PyObject *args) { long pid; struct kinfo_proc kp; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (psutil_get_kinfo_proc(pid, &kp) == -1) { + if (psutil_get_kinfo_proc(pid, &kp) == -1) return NULL; - } return Py_BuildValue("lll", (long)kp.kp_eproc.e_pcred.p_rgid, (long)kp.kp_eproc.e_ucred.cr_groups[0], @@ -265,12 +252,10 @@ psutil_proc_tty_nr(PyObject *self, PyObject *args) { long pid; struct kinfo_proc kp; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (psutil_get_kinfo_proc(pid, &kp) == -1) { + if (psutil_get_kinfo_proc(pid, &kp) == -1) return NULL; - } return Py_BuildValue("i", kp.kp_eproc.e_tdev); } @@ -299,9 +284,8 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) if (py_list == NULL) return NULL; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) goto error; - } err = task_for_pid(mach_task_self(), pid, &task); @@ -324,11 +308,8 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) err = vm_region_recurse_64(task, &address, &size, &depth, (vm_region_info_64_t)&info, &count); - - if (err == KERN_INVALID_ADDRESS) { + if (err == KERN_INVALID_ADDRESS) break; - } - if (info.is_submap) { depth++; } @@ -356,32 +337,30 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) if (strlen(buf) == 0) { switch (info.share_mode) { - /* - case SM_LARGE_PAGE: - // Treat SM_LARGE_PAGE the same as SM_PRIVATE - // since they are not shareable and are wired. - */ - case SM_COW: - strcpy(buf, "[cow]"); - break; - case SM_PRIVATE: - strcpy(buf, "[prv]"); - break; - case SM_EMPTY: - strcpy(buf, "[nul]"); - break; - case SM_SHARED: - case SM_TRUESHARED: - strcpy(buf, "[shm]"); - break; - case SM_PRIVATE_ALIASED: - strcpy(buf, "[ali]"); - break; - case SM_SHARED_ALIASED: - strcpy(buf, "[s/a]"); - break; - default: - strcpy(buf, "[???]"); + // case SM_LARGE_PAGE: + // Treat SM_LARGE_PAGE the same as SM_PRIVATE + // since they are not shareable and are wired. + case SM_COW: + strcpy(buf, "[cow]"); + break; + case SM_PRIVATE: + strcpy(buf, "[prv]"); + break; + case SM_EMPTY: + strcpy(buf, "[nul]"); + break; + case SM_SHARED: + case SM_TRUESHARED: + strcpy(buf, "[shm]"); + break; + case SM_PRIVATE_ALIASED: + strcpy(buf, "[ali]"); + break; + case SM_SHARED_ALIASED: + strcpy(buf, "[s/a]"); + break; + default: + strcpy(buf, "[???]"); } } @@ -432,19 +411,14 @@ psutil_cpu_count_logical(PyObject *self, PyObject *args) int mib[2]; int ncpu; size_t len; - mib[0] = CTL_HW; mib[1] = HW_NCPU; len = sizeof(ncpu); - if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) { - // mimic os.cpu_count() - Py_INCREF(Py_None); - return Py_None; - } - else { + if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) + Py_RETURN_NONE; // mimic os.cpu_count() + else return Py_BuildValue("i", ncpu); - } } @@ -456,12 +430,11 @@ psutil_cpu_count_phys(PyObject *self, PyObject *args) { int num; size_t size = sizeof(int); - if (sysctlbyname("hw.physicalcpu", &num, &size, NULL, 0)) { - // mimic os.cpu_count() - Py_INCREF(Py_None); - return Py_None; - } - return Py_BuildValue("i", num); + + if (sysctlbyname("hw.physicalcpu", &num, &size, NULL, 0)) + Py_RETURN_NONE; // mimic os.cpu_count() + else + return Py_BuildValue("i", num); } @@ -475,12 +448,11 @@ psutil_proc_cpu_times(PyObject *self, PyObject *args) { long pid; struct proc_taskinfo pti; - if (! PyArg_ParseTuple(args, "l", &pid)) { + + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (! psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, &pti, sizeof(pti))) { + if (! psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, &pti, sizeof(pti))) return NULL; - } return Py_BuildValue("(dd)", (float)pti.pti_total_user / 1000000000.0, (float)pti.pti_total_system / 1000000000.0); @@ -496,12 +468,10 @@ psutil_proc_create_time(PyObject *self, PyObject *args) { long pid; struct kinfo_proc kp; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (psutil_get_kinfo_proc(pid, &kp) == -1) { + if (psutil_get_kinfo_proc(pid, &kp) == -1) return NULL; - } return Py_BuildValue("d", TV2DOUBLE(kp.kp_proc.p_starttime)); } @@ -514,13 +484,11 @@ psutil_proc_memory_info(PyObject *self, PyObject *args) { long pid; struct proc_taskinfo pti; - if (! PyArg_ParseTuple(args, "l", &pid)) { + + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (! psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, &pti, sizeof(pti))) { + if (! psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, &pti, sizeof(pti))) return NULL; - } - // Note: determining other memory stats on OSX is a mess: // http://www.opensource.apple.com/source/top/top-67/libtop.c?txt // I just give up... @@ -544,12 +512,11 @@ psutil_proc_num_threads(PyObject *self, PyObject *args) { long pid; struct proc_taskinfo pti; - if (! PyArg_ParseTuple(args, "l", &pid)) { + + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (! psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, &pti, sizeof(pti))) { + if (! psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, &pti, sizeof(pti))) return NULL; - } return Py_BuildValue("k", pti.pti_threadnum); } @@ -562,12 +529,11 @@ psutil_proc_num_ctx_switches(PyObject *self, PyObject *args) { long pid; struct proc_taskinfo pti; - if (! PyArg_ParseTuple(args, "l", &pid)) { + + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (! psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, &pti, sizeof(pti))) { + if (! psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, &pti, sizeof(pti))) return NULL; - } // unvoluntary value seems not to be available; // pti.pti_csw probably refers to the sum of the two (getrusage() // numbers seems to confirm this theory). @@ -587,10 +553,10 @@ psutil_virtual_mem(PyObject *self, PyObject *args) size_t len = sizeof(total); vm_statistics_data_t vm; int pagesize = getpagesize(); - // physical mem mib[0] = CTL_HW; mib[1] = HW_MEMSIZE; + if (sysctl(mib, 2, &total, &len, NULL, 0)) { if (errno != 0) PyErr_SetFromErrno(PyExc_OSError); @@ -600,9 +566,8 @@ psutil_virtual_mem(PyObject *self, PyObject *args) } // vm - if (!psutil_sys_vminfo(&vm)) { + if (!psutil_sys_vminfo(&vm)) return NULL; - } return Py_BuildValue( "KKKKK", @@ -637,9 +602,8 @@ psutil_swap_mem(PyObject *self, PyObject *args) PyErr_Format(PyExc_RuntimeError, "sysctl(VM_SWAPUSAGE) failed"); return NULL; } - if (!psutil_sys_vminfo(&vmstat)) { + if (!psutil_sys_vminfo(&vmstat)) return NULL; - } return Py_BuildValue( "LLLKK", @@ -664,11 +628,10 @@ psutil_cpu_times(PyObject *self, PyObject *args) mach_port_t host_port = mach_host_self(); error = host_statistics(host_port, HOST_CPU_LOAD_INFO, (host_info_t)&r_load, &count); - if (error != KERN_SUCCESS) { + if (error != KERN_SUCCESS) return PyErr_Format(PyExc_RuntimeError, "Error in host_statistics(): %s", mach_error_string(error)); - } mach_port_deallocate(mach_task_self(), host_port); return Py_BuildValue( @@ -728,9 +691,8 @@ psutil_per_cpu_times(PyObject *self, PyObject *args) ret = vm_deallocate(mach_task_self(), (vm_address_t)info_array, info_count * sizeof(int)); - if (ret != KERN_SUCCESS) { + if (ret != KERN_SUCCESS) PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2); - } return py_retlist; error: @@ -739,9 +701,8 @@ psutil_per_cpu_times(PyObject *self, PyObject *args) if (cpu_load_info != NULL) { ret = vm_deallocate(mach_task_self(), (vm_address_t)info_array, info_count * sizeof(int)); - if (ret != KERN_SUCCESS) { + if (ret != KERN_SUCCESS) PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2); - } } return NULL; } @@ -900,12 +861,10 @@ psutil_proc_status(PyObject *self, PyObject *args) { long pid; struct kinfo_proc kp; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (psutil_get_kinfo_proc(pid, &kp) == -1) { + if (psutil_get_kinfo_proc(pid, &kp) == -1) return NULL; - } return Py_BuildValue("i", (int)kp.kp_proc.p_stat); } @@ -934,19 +893,16 @@ psutil_proc_threads(PyObject *self, PyObject *args) return NULL; // the argument passed should be a process id - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) goto error; - } // task_for_pid() requires special privileges err = task_for_pid(mach_task_self(), pid, &task); if (err != KERN_SUCCESS) { - if (! psutil_pid_exists(pid)) { + if (! psutil_pid_exists(pid)) NoSuchProcess(); - } - else { + else AccessDenied(); - } goto error; } @@ -999,9 +955,8 @@ psutil_proc_threads(PyObject *self, PyObject *args) ret = vm_deallocate(task, (vm_address_t)thread_list, thread_count * sizeof(int)); - if (ret != KERN_SUCCESS) { + if (ret != KERN_SUCCESS) PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2); - } mach_port_deallocate(mach_task_self(), task); @@ -1015,9 +970,8 @@ psutil_proc_threads(PyObject *self, PyObject *args) if (thread_list != NULL) { ret = vm_deallocate(task, (vm_address_t)thread_list, thread_count * sizeof(int)); - if (ret != KERN_SUCCESS) { + if (ret != KERN_SUCCESS) PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2); - } } return NULL; } @@ -1048,9 +1002,8 @@ psutil_proc_open_files(PyObject *self, PyObject *args) if (retList == NULL) return NULL; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) goto error; - } pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0); if (pidinfo_result <= 0) { @@ -1127,19 +1080,14 @@ psutil_proc_open_files(PyObject *self, PyObject *args) error: Py_XDECREF(tuple); Py_DECREF(retList); - if (fds_pointer != NULL) { + if (fds_pointer != NULL) free(fds_pointer); - } - if (errno != 0) { + if (errno != 0) return PyErr_SetFromErrno(PyExc_OSError); - } - else if (! psutil_pid_exists(pid)) { + else if (! psutil_pid_exists(pid)) return NoSuchProcess(); - } - else { - // exception has already been set earlier - return NULL; - } + else + return NULL; // exception has already been set earlier } @@ -1175,23 +1123,19 @@ psutil_proc_connections(PyObject *self, PyObject *args) if (retList == NULL) return NULL; - if (! PyArg_ParseTuple(args, "lOO", &pid, &af_filter, &type_filter)) { + if (! PyArg_ParseTuple(args, "lOO", &pid, &af_filter, &type_filter)) goto error; - } if (!PySequence_Check(af_filter) || !PySequence_Check(type_filter)) { PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence"); goto error; } - if (pid == 0) { + if (pid == 0) return retList; - } - pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0); - if (pidinfo_result <= 0) { + if (pidinfo_result <= 0) goto error; - } fds_pointer = malloc(pidinfo_result); if (fds_pointer == NULL) { @@ -1201,10 +1145,8 @@ psutil_proc_connections(PyObject *self, PyObject *args) pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer, pidinfo_result); - if (pidinfo_result <= 0) { + if (pidinfo_result <= 0) goto error; - } - iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE); for (i = 0; i < iterations; i++) { @@ -1225,14 +1167,12 @@ psutil_proc_connections(PyObject *self, PyObject *args) // let's assume socket has been closed continue; } - if (errno != 0) { + if (errno != 0) PyErr_SetFromErrno(PyExc_OSError); - } - else { + else PyErr_Format( PyExc_RuntimeError, "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed"); - } goto error; } if (nb < sizeof(si)) { @@ -1258,15 +1198,13 @@ psutil_proc_connections(PyObject *self, PyObject *args) _family = PyLong_FromLong((long)family); inseq = PySequence_Contains(af_filter, _family); Py_DECREF(_family); - if (inseq == 0) { + if (inseq == 0) continue; - } _type = PyLong_FromLong((long)type); inseq = PySequence_Contains(type_filter, _type); Py_DECREF(_type); - if (inseq == 0) { + if (inseq == 0) continue; - } if (errno != 0) { PyErr_SetFromErrno(PyExc_OSError); @@ -1305,22 +1243,18 @@ psutil_proc_connections(PyObject *self, PyObject *args) lport = ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_lport); rport = ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_fport); - if (type == SOCK_STREAM) { + if (type == SOCK_STREAM) state = (int)si.psi.soi_proto.pri_tcp.tcpsi_state; - } - else { + else state = PSUTIL_CONN_NONE; - } laddr = Py_BuildValue("(si)", lip, lport); if (!laddr) goto error; - if (rport != 0) { + if (rport != 0) raddr = Py_BuildValue("(si)", rip, rport); - } - else { + else raddr = Py_BuildValue("()"); - } if (!raddr) goto error; @@ -1359,19 +1293,15 @@ psutil_proc_connections(PyObject *self, PyObject *args) Py_XDECREF(raddr); Py_DECREF(retList); - if (fds_pointer != NULL) { + if (fds_pointer != NULL) free(fds_pointer); - } - if (errno != 0) { + if (errno != 0) return PyErr_SetFromErrno(PyExc_OSError); - } - else if (! psutil_pid_exists(pid) ) { + else if (! psutil_pid_exists(pid)) return NoSuchProcess(); - } - else { + else return PyErr_Format(PyExc_RuntimeError, "proc_pidinfo(PROC_PIDLISTFDS) failed"); - } } @@ -1386,19 +1316,16 @@ psutil_proc_num_fds(PyObject *self, PyObject *args) int num; struct proc_fdinfo *fds_pointer; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0); - if (pidinfo_result <= 0) { + if (pidinfo_result <= 0) return PyErr_SetFromErrno(PyExc_OSError); - } fds_pointer = malloc(pidinfo_result); - if (fds_pointer == NULL) { + if (fds_pointer == NULL) return PyErr_NoMemory(); - } pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer, pidinfo_result); if (pidinfo_result <= 0) { @@ -1851,6 +1778,7 @@ init_psutil_osx(void) #else PyObject *module = Py_InitModule("_psutil_osx", PsutilMethods); #endif + PyModule_AddIntConstant(module, "version", PSUTIL_VERSION); // process status constants, defined in: // http://fxr.watson.org/fxr/source/bsd/sys/proc.h?v=xnu-792.6.70#L149 PyModule_AddIntConstant(module, "SIDL", SIDL); @@ -1872,9 +1800,8 @@ init_psutil_osx(void) PyModule_AddIntConstant(module, "TCPS_TIME_WAIT", TCPS_TIME_WAIT); PyModule_AddIntConstant(module, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE); - if (module == NULL) { + if (module == NULL) INITERROR; - } #if PY_MAJOR_VERSION >= 3 return module; #endif diff --git a/python/psutil/psutil/_psutil_posix.c b/python/psutil/psutil/_psutil_posix.c index ad2e59dbb880..183dab0e12ee 100644 --- a/python/psutil/psutil/_psutil_posix.c +++ b/python/psutil/psutil/_psutil_posix.c @@ -10,6 +10,24 @@ #include #include #include +#include +#include +#include + +#ifdef __linux +#include +#include +#endif // end linux + +#if defined(__FreeBSD__) || defined(__APPLE__) +#include +#include +#include +#endif + +#if defined(__sun) +#include +#endif #include "_psutil_posix.h" @@ -23,13 +41,12 @@ psutil_posix_getpriority(PyObject *self, PyObject *args) long pid; int priority; errno = 0; - if (! PyArg_ParseTuple(args, "l", &pid)) { + + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } priority = getpriority(PRIO_PROCESS, pid); - if (errno != 0) { + if (errno != 0) return PyErr_SetFromErrno(PyExc_OSError); - } return Py_BuildValue("i", priority); } @@ -43,18 +60,394 @@ psutil_posix_setpriority(PyObject *self, PyObject *args) long pid; int priority; int retval; - if (! PyArg_ParseTuple(args, "li", &pid, &priority)) { + + if (! PyArg_ParseTuple(args, "li", &pid, &priority)) return NULL; - } retval = setpriority(PRIO_PROCESS, pid, priority); - if (retval == -1) { + if (retval == -1) return PyErr_SetFromErrno(PyExc_OSError); + Py_RETURN_NONE; +} + + +/* + * Translate a sockaddr struct into a Python string. + * Return None if address family is not AF_INET* or AF_PACKET. + */ +static PyObject * +psutil_convert_ipaddr(struct sockaddr *addr, int family) +{ + char buf[NI_MAXHOST]; + int err; + int addrlen; + int n; + size_t len; + const char *data; + char *ptr; + + if (addr == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + else if (family == AF_INET || family == AF_INET6) { + if (family == AF_INET) + addrlen = sizeof(struct sockaddr_in); + else + addrlen = sizeof(struct sockaddr_in6); + err = getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0, + NI_NUMERICHOST); + if (err != 0) { + // XXX we get here on FreeBSD when processing 'lo' / AF_INET6 + // broadcast. Not sure what to do other than returning None. + // ifconfig does not show anything BTW. + //PyErr_Format(PyExc_RuntimeError, gai_strerror(err)); + //return NULL; + Py_INCREF(Py_None); + return Py_None; + } + else { + return Py_BuildValue("s", buf); + } + } +#ifdef __linux + else if (family == AF_PACKET) { + struct sockaddr_ll *lladdr = (struct sockaddr_ll *)addr; + len = lladdr->sll_halen; + data = (const char *)lladdr->sll_addr; + } +#endif +#if defined(__FreeBSD__) || defined(__APPLE__) + else if (addr->sa_family == AF_LINK) { + // Note: prior to Python 3.4 socket module does not expose + // AF_LINK so we'll do. + struct sockaddr_dl *dladdr = (struct sockaddr_dl *)addr; + len = dladdr->sdl_alen; + data = LLADDR(dladdr); + } +#endif + else { + // unknown family + Py_INCREF(Py_None); + return Py_None; + } + + // AF_PACKET or AF_LINK + if (len > 0) { + ptr = buf; + for (n = 0; n < len; ++n) { + sprintf(ptr, "%02x:", data[n] & 0xff); + ptr += 3; + } + *--ptr = '\0'; + return Py_BuildValue("s", buf); + } + else { + Py_INCREF(Py_None); + return Py_None; + } +} + + +/* + * Return NICs information a-la ifconfig as a list of tuples. + * TODO: on Solaris we won't get any MAC address. + */ +static PyObject* +psutil_net_if_addrs(PyObject* self, PyObject* args) +{ + struct ifaddrs *ifaddr, *ifa; + int family; + + PyObject *py_retlist = PyList_New(0); + PyObject *py_tuple = NULL; + PyObject *py_address = NULL; + PyObject *py_netmask = NULL; + PyObject *py_broadcast = NULL; + + if (py_retlist == NULL) + return NULL; + if (getifaddrs(&ifaddr) == -1) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) + continue; + family = ifa->ifa_addr->sa_family; + py_address = psutil_convert_ipaddr(ifa->ifa_addr, family); + // If the primary address can't be determined just skip it. + // I've never seen this happen on Linux but I did on FreeBSD. + if (py_address == Py_None) + continue; + if (py_address == NULL) + goto error; + py_netmask = psutil_convert_ipaddr(ifa->ifa_netmask, family); + if (py_netmask == NULL) + goto error; +#ifdef __linux + py_broadcast = psutil_convert_ipaddr(ifa->ifa_ifu.ifu_broadaddr, family); +#else + py_broadcast = psutil_convert_ipaddr(ifa->ifa_broadaddr, family); +#endif + if (py_broadcast == NULL) + goto error; + py_tuple = Py_BuildValue( + "(siOOO)", + ifa->ifa_name, + family, + py_address, + py_netmask, + py_broadcast + ); + + if (! py_tuple) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_DECREF(py_tuple); + Py_DECREF(py_address); + Py_DECREF(py_netmask); + Py_DECREF(py_broadcast); + } + + freeifaddrs(ifaddr); + return py_retlist; + +error: + if (ifaddr != NULL) + freeifaddrs(ifaddr); + Py_DECREF(py_retlist); + Py_XDECREF(py_tuple); + Py_XDECREF(py_address); + Py_XDECREF(py_netmask); + Py_XDECREF(py_broadcast); + return NULL; +} + + +/* + * net_if_stats() implementation. This is here because it is common + * to both OSX and FreeBSD and I didn't know where else to put it. + */ +#if defined(__FreeBSD__) || defined(__APPLE__) + +#include +#include +#include + +int psutil_get_nic_speed(int ifm_active) { + // Determine NIC speed. Taken from: + // http://www.i-scream.org/libstatgrab/ + // Assuming only ETHER devices + switch(IFM_TYPE(ifm_active)) { + case IFM_ETHER: + switch(IFM_SUBTYPE(ifm_active)) { +#if defined(IFM_HPNA_1) && ((!defined(IFM_10G_LR)) \ + || (IFM_10G_LR != IFM_HPNA_1)) + // HomePNA 1.0 (1Mb/s) + case(IFM_HPNA_1): + return 1; +#endif + // 10 Mbit + case(IFM_10_T): // 10BaseT - RJ45 + case(IFM_10_2): // 10Base2 - Thinnet + case(IFM_10_5): // 10Base5 - AUI + case(IFM_10_STP): // 10BaseT over shielded TP + case(IFM_10_FL): // 10baseFL - Fiber + return 10; + // 100 Mbit + case(IFM_100_TX): // 100BaseTX - RJ45 + case(IFM_100_FX): // 100BaseFX - Fiber + case(IFM_100_T4): // 100BaseT4 - 4 pair cat 3 + case(IFM_100_VG): // 100VG-AnyLAN + case(IFM_100_T2): // 100BaseT2 + return 100; + // 1000 Mbit + case(IFM_1000_SX): // 1000BaseSX - multi-mode fiber + case(IFM_1000_LX): // 1000baseLX - single-mode fiber + case(IFM_1000_CX): // 1000baseCX - 150ohm STP +#if defined(IFM_1000_TX) && !defined(OPENBSD) + // FreeBSD 4 and others (but NOT OpenBSD)? + case(IFM_1000_TX): +#endif +#ifdef IFM_1000_FX + case(IFM_1000_FX): +#endif +#ifdef IFM_1000_T + case(IFM_1000_T): +#endif + return 1000; +#if defined(IFM_10G_SR) || defined(IFM_10G_LR) || defined(IFM_10G_CX4) \ + || defined(IFM_10G_T) +#ifdef IFM_10G_SR + case(IFM_10G_SR): +#endif +#ifdef IFM_10G_LR + case(IFM_10G_LR): +#endif +#ifdef IFM_10G_CX4 + case(IFM_10G_CX4): +#endif +#ifdef IFM_10G_TWINAX + case(IFM_10G_TWINAX): +#endif +#ifdef IFM_10G_TWINAX_LONG + case(IFM_10G_TWINAX_LONG): +#endif +#ifdef IFM_10G_T + case(IFM_10G_T): +#endif + return 10000; +#endif +#if defined(IFM_2500_SX) +#ifdef IFM_2500_SX + case(IFM_2500_SX): +#endif + return 2500; +#endif // any 2.5GBit stuff... + // We don't know what it is + default: + return 0; + } + break; + +#ifdef IFM_TOKEN + case IFM_TOKEN: + switch(IFM_SUBTYPE(ifm_active)) { + case IFM_TOK_STP4: // Shielded twisted pair 4m - DB9 + case IFM_TOK_UTP4: // Unshielded twisted pair 4m - RJ45 + return 4; + case IFM_TOK_STP16: // Shielded twisted pair 16m - DB9 + case IFM_TOK_UTP16: // Unshielded twisted pair 16m - RJ45 + return 16; +#if defined(IFM_TOK_STP100) || defined(IFM_TOK_UTP100) +#ifdef IFM_TOK_STP100 + case IFM_TOK_STP100: // Shielded twisted pair 100m - DB9 +#endif +#ifdef IFM_TOK_UTP100 + case IFM_TOK_UTP100: // Unshielded twisted pair 100m - RJ45 +#endif + return 100; +#endif + // We don't know what it is + default: + return 0; + } + break; +#endif + +#ifdef IFM_FDDI + case IFM_FDDI: + switch(IFM_SUBTYPE(ifm_active)) { + // We don't know what it is + default: + return 0; + } + break; +#endif + case IFM_IEEE80211: + switch(IFM_SUBTYPE(ifm_active)) { + case IFM_IEEE80211_FH1: // Frequency Hopping 1Mbps + case IFM_IEEE80211_DS1: // Direct Sequence 1Mbps + return 1; + case IFM_IEEE80211_FH2: // Frequency Hopping 2Mbps + case IFM_IEEE80211_DS2: // Direct Sequence 2Mbps + return 2; + case IFM_IEEE80211_DS5: // Direct Sequence 5Mbps + return 5; + case IFM_IEEE80211_DS11: // Direct Sequence 11Mbps + return 11; + case IFM_IEEE80211_DS22: // Direct Sequence 22Mbps + return 22; + // We don't know what it is + default: + return 0; + } + break; + + default: + return 0; } - Py_INCREF(Py_None); - return Py_None; } +/* + * Return stats about a particular network interface. + * References: + * http://www.i-scream.org/libstatgrab/ + */ +static PyObject * +psutil_net_if_stats(PyObject *self, PyObject *args) +{ + char *nic_name; + int sock = 0; + int ret; + int duplex; + int speed; + int mtu; + struct ifreq ifr; + struct ifmediareq ifmed; + + PyObject *py_is_up = NULL; + + if (! PyArg_ParseTuple(args, "s", &nic_name)) + return NULL; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == -1) + goto error; + strncpy(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name)); + + // is up? + ret = ioctl(sock, SIOCGIFFLAGS, &ifr); + if (ret == -1) + goto error; + if ((ifr.ifr_flags & IFF_UP) != 0) + py_is_up = Py_True; + else + py_is_up = Py_False; + Py_INCREF(py_is_up); + + // MTU + ret = ioctl(sock, SIOCGIFMTU, &ifr); + if (ret == -1) + goto error; + mtu = ifr.ifr_mtu; + + // speed / duplex + memset(&ifmed, 0, sizeof(struct ifmediareq)); + strlcpy(ifmed.ifm_name, nic_name, sizeof(ifmed.ifm_name)); + ret = ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmed); + if (ret == -1) { + speed = 0; + duplex = 0; + } + else { + speed = psutil_get_nic_speed(ifmed.ifm_active); + if ((ifmed.ifm_active | IFM_FDX) == ifmed.ifm_active) + duplex = 2; + else if ((ifmed.ifm_active | IFM_HDX) == ifmed.ifm_active) + duplex = 1; + else + duplex = 0; + } + + close(sock); + Py_DECREF(py_is_up); + + return Py_BuildValue("[Oiii]", py_is_up, duplex, speed, mtu); + +error: + Py_XDECREF(py_is_up); + if (sock != 0) + close(sock); + PyErr_SetFromErrno(PyExc_OSError); + return NULL; +} +#endif // net_if_stats() implementation + + /* * define the psutil C module methods and initialize the module. */ @@ -65,6 +458,12 @@ PsutilMethods[] = "Return process priority"}, {"setpriority", psutil_posix_setpriority, METH_VARARGS, "Set process priority"}, + {"net_if_addrs", psutil_net_if_addrs, METH_VARARGS, + "Retrieve NICs information"}, +#if defined(__FreeBSD__) || defined(__APPLE__) + {"net_if_stats", psutil_net_if_stats, METH_VARARGS, + "Return NIC stats."}, +#endif {NULL, NULL, 0, NULL} }; @@ -119,9 +518,13 @@ void init_psutil_posix(void) #else PyObject *module = Py_InitModule("_psutil_posix", PsutilMethods); #endif - if (module == NULL) { + +#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__sun) + PyModule_AddIntConstant(module, "AF_LINK", AF_LINK); +#endif + + if (module == NULL) INITERROR; - } #if PY_MAJOR_VERSION >= 3 return module; #endif diff --git a/python/psutil/psutil/_psutil_posix.h b/python/psutil/psutil/_psutil_posix.h index 5a4681d18876..bbe6fc5ad2d3 100644 --- a/python/psutil/psutil/_psutil_posix.h +++ b/python/psutil/psutil/_psutil_posix.h @@ -6,5 +6,10 @@ #include +static PyObject* psutil_net_if_addrs(PyObject* self, PyObject* args); static PyObject* psutil_posix_getpriority(PyObject* self, PyObject* args); static PyObject* psutil_posix_setpriority(PyObject* self, PyObject* args); + +#if defined(__FreeBSD__) || defined(__APPLE__) +static PyObject* psutil_net_if_stats(PyObject* self, PyObject* args); +#endif diff --git a/python/psutil/psutil/_psutil_sunos.c b/python/psutil/psutil/_psutil_sunos.c index 031aacb001f8..0cb6978f27f3 100644 --- a/python/psutil/psutil/_psutil_sunos.c +++ b/python/psutil/psutil/_psutil_sunos.c @@ -30,6 +30,8 @@ #include // for MNTTAB #include #include +#include +#include #include #include #include @@ -38,6 +40,7 @@ #include #include #include +#include #include "_psutil_sunos.h" @@ -201,13 +204,11 @@ proc_io_counters(PyObject* self, PyObject* args) char path[100]; prusage_t info; - if (! PyArg_ParseTuple(args, "i", &pid)) { + if (! PyArg_ParseTuple(args, "i", &pid)) return NULL; - } sprintf(path, "/proc/%i/usage", pid); - if (! psutil_file_to_struct(path, (void *)&info, sizeof(info))) { + if (! psutil_file_to_struct(path, (void *)&info, sizeof(info))) return NULL; - } // On Solaris we only have 'pr_ioch' which accounts for bytes read // *and* written. @@ -315,9 +316,8 @@ psutil_swap_mem(PyObject *self, PyObject *args) uint_t sout = 0; kc = kstat_open(); - if (kc == NULL) { + if (kc == NULL) return PyErr_SetFromErrno(PyExc_OSError);; - } k = kc->kc_chain; while (k != NULL) { @@ -442,8 +442,6 @@ psutil_per_cpu_times(PyObject *self, PyObject *args) kstat_ctl_t *kc; kstat_t *ksp; cpu_stat_t cs; - int numcpus; - int i; PyObject *py_retlist = PyList_New(0); PyObject *py_cputime = NULL; @@ -456,29 +454,24 @@ psutil_per_cpu_times(PyObject *self, PyObject *args) goto error; } - numcpus = sysconf(_SC_NPROCESSORS_ONLN) - 1; - for (i = 0; i <= numcpus; i++) { - ksp = kstat_lookup(kc, "cpu_stat", i, NULL); - if (ksp == NULL) { - PyErr_SetFromErrno(PyExc_OSError); - goto error; - } - if (kstat_read(kc, ksp, &cs) == -1) { - PyErr_SetFromErrno(PyExc_OSError); - goto error; + for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) { + if (strcmp(ksp->ks_module, "cpu_stat") == 0) { + if (kstat_read(kc, ksp, &cs) == -1) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + py_cputime = Py_BuildValue("ffff", + (float)cs.cpu_sysinfo.cpu[CPU_USER], + (float)cs.cpu_sysinfo.cpu[CPU_KERNEL], + (float)cs.cpu_sysinfo.cpu[CPU_IDLE], + (float)cs.cpu_sysinfo.cpu[CPU_WAIT]); + if (py_cputime == NULL) + goto error; + if (PyList_Append(py_retlist, py_cputime)) + goto error; + Py_DECREF(py_cputime); + py_cputime = NULL; } - - py_cputime = Py_BuildValue("ffff", - (float)cs.cpu_sysinfo.cpu[CPU_USER], - (float)cs.cpu_sysinfo.cpu[CPU_KERNEL], - (float)cs.cpu_sysinfo.cpu[CPU_IDLE], - (float)cs.cpu_sysinfo.cpu[CPU_WAIT]); - if (py_cputime == NULL) - goto error; - if (PyList_Append(py_retlist, py_cputime)) - goto error; - Py_DECREF(py_cputime); - } kstat_close(kc); @@ -576,17 +569,14 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) PyObject *pytuple = NULL; PyObject *py_retlist = PyList_New(0); - if (py_retlist == NULL) { + if (py_retlist == NULL) return NULL; - } - if (! PyArg_ParseTuple(args, "i", &pid)) { + if (! PyArg_ParseTuple(args, "i", &pid)) goto error; - } sprintf(path, "/proc/%i/status", pid); - if (! psutil_file_to_struct(path, (void *)&status, sizeof(status))) { + if (! psutil_file_to_struct(path, (void *)&status, sizeof(status))) goto error; - } sprintf(path, "/proc/%i/xmap", pid); if (stat(path, &st) == -1) { @@ -722,9 +712,8 @@ psutil_net_io_counters(PyObject *self, PyObject *args) (strcmp(ksp->ks_module, "lo") != 0)) { goto skip; */ - if ((strcmp(ksp->ks_module, "link") != 0)) { + if ((strcmp(ksp->ks_module, "link") != 0)) goto next; - } if (kstat_read(kc, ksp, NULL) == -1) { errno = 0; @@ -747,18 +736,18 @@ psutil_net_io_counters(PyObject *self, PyObject *args) #if defined(_INT64_TYPE) py_ifc_info = Py_BuildValue("(KKKKkkii)", - rbytes->value.ui64, wbytes->value.ui64, - rpkts->value.ui64, + rbytes->value.ui64, wpkts->value.ui64, + rpkts->value.ui64, ierrs->value.ui32, oerrs->value.ui32, #else py_ifc_info = Py_BuildValue("(kkkkkkii)", - rbytes->value.ui32, wbytes->value.ui32, - rpkts->value.ui32, + rbytes->value.ui32, wpkts->value.ui32, + rpkts->value.ui32, ierrs->value.ui32, oerrs->value.ui32, #endif @@ -810,7 +799,7 @@ static PyObject * psutil_net_connections(PyObject *self, PyObject *args) { long pid; - int sd = NULL; + int sd = 0; mib2_tcpConnEntry_t *tp = NULL; mib2_udpEntry_t *ude; #if defined(AF_INET6) @@ -822,6 +811,7 @@ psutil_net_connections(PyObject *self, PyObject *args) char lip[200], rip[200]; int lport, rport; int processed_pid; + int databuf_init = 0; struct strbuf ctlbuf, databuf; struct T_optmgmt_req *tor = (struct T_optmgmt_req *)buf; struct T_optmgmt_ack *toa = (struct T_optmgmt_ack *)buf; @@ -922,6 +912,7 @@ psutil_net_connections(PyObject *self, PyObject *args) PyErr_NoMemory(); goto error; } + databuf_init = 1; flags = 0; getcode = getmsg(sd, (struct strbuf *)0, &databuf, &flags); @@ -948,9 +939,8 @@ psutil_net_connections(PyObject *self, PyObject *args) py_laddr = Py_BuildValue("(si)", lip, lport); if (!py_laddr) goto error; - if (rport != 0) { + if (rport != 0) py_raddr = Py_BuildValue("(si)", rip, rport); - } else { py_raddr = Py_BuildValue("()"); } @@ -962,9 +952,8 @@ psutil_net_connections(PyObject *self, PyObject *args) py_tuple = Py_BuildValue("(iiiNNiI)", -1, AF_INET, SOCK_STREAM, py_laddr, py_raddr, state, processed_pid); - if (!py_tuple) { + if (!py_tuple) goto error; - } if (PyList_Append(py_retlist, py_tuple)) goto error; Py_DECREF(py_tuple); @@ -991,12 +980,10 @@ psutil_net_connections(PyObject *self, PyObject *args) py_laddr = Py_BuildValue("(si)", lip, lport); if (!py_laddr) goto error; - if (rport != 0) { + if (rport != 0) py_raddr = Py_BuildValue("(si)", rip, rport); - } - else { + else py_raddr = Py_BuildValue("()"); - } if (!py_raddr) goto error; state = tp6->tcp6ConnEntryInfo.ce_state; @@ -1004,9 +991,8 @@ psutil_net_connections(PyObject *self, PyObject *args) // add item py_tuple = Py_BuildValue("(iiiNNiI)", -1, AF_INET6, SOCK_STREAM, py_laddr, py_raddr, state, processed_pid); - if (!py_tuple) { + if (!py_tuple) goto error; - } if (PyList_Append(py_retlist, py_tuple)) goto error; Py_DECREF(py_tuple); @@ -1039,9 +1025,8 @@ psutil_net_connections(PyObject *self, PyObject *args) py_tuple = Py_BuildValue("(iiiNNiI)", -1, AF_INET, SOCK_DGRAM, py_laddr, py_raddr, PSUTIL_CONN_NONE, processed_pid); - if (!py_tuple) { + if (!py_tuple) goto error; - } if (PyList_Append(py_retlist, py_tuple)) goto error; Py_DECREF(py_tuple); @@ -1049,7 +1034,9 @@ psutil_net_connections(PyObject *self, PyObject *args) } #if defined(AF_INET6) // UDPv6 - else if (mibhdr->level == MIB2_UDP6 || mibhdr->level == MIB2_UDP6_ENTRY) { + else if (mibhdr->level == MIB2_UDP6 || + mibhdr->level == MIB2_UDP6_ENTRY) + { ude6 = (mib2_udp6Entry_t *)databuf.buf; num_ent = mibhdr->len / sizeof(mib2_udp6Entry_t); for (i = 0; i < num_ent; i++, ude6++) { @@ -1067,9 +1054,8 @@ psutil_net_connections(PyObject *self, PyObject *args) py_tuple = Py_BuildValue("(iiiNNiI)", -1, AF_INET6, SOCK_DGRAM, py_laddr, py_raddr, PSUTIL_CONN_NONE, processed_pid); - if (!py_tuple) { + if (!py_tuple) goto error; - } if (PyList_Append(py_retlist, py_tuple)) goto error; Py_DECREF(py_tuple); @@ -1087,8 +1073,9 @@ psutil_net_connections(PyObject *self, PyObject *args) Py_XDECREF(py_laddr); Py_XDECREF(py_raddr); Py_DECREF(py_retlist); - // TODO : free databuf - if (sd != NULL) + if (databuf_init == 1) + free(databuf.buf); + if (sd != 0) close(sd); return NULL; } @@ -1137,7 +1124,7 @@ psutil_cpu_count_phys(PyObject *self, PyObject *args) for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { if (strcmp(ksp->ks_module, "cpu_info") != 0) continue; - if (kstat_read(kc, ksp, NULL) == NULL) + if (kstat_read(kc, ksp, NULL) == -1) goto error; ncpus += 1; } @@ -1152,8 +1139,117 @@ psutil_cpu_count_phys(PyObject *self, PyObject *args) // mimic os.cpu_count() if (kc != NULL) kstat_close(kc); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; +} + + +/* + * Return stats about a particular network + * interface. References: + * https://github.com/dpaleino/wicd/blob/master/wicd/backends/be-ioctl.py + * http://www.i-scream.org/libstatgrab/ + */ +static PyObject* +psutil_net_if_stats(PyObject* self, PyObject* args) +{ + kstat_ctl_t *kc = NULL; + kstat_t *ksp; + kstat_named_t *knp; + int ret; + int sock = 0; + int duplex; + int speed; + + PyObject *py_retdict = PyDict_New(); + PyObject *py_ifc_info = NULL; + PyObject *py_is_up = NULL; + + if (py_retdict == NULL) + return NULL; + kc = kstat_open(); + if (kc == NULL) + goto error; + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == -1) + goto error; + + for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { + if (strcmp(ksp->ks_class, "net") == 0) { + struct ifreq ifr; + + kstat_read(kc, ksp, NULL); + if (ksp->ks_type != KSTAT_TYPE_NAMED) + continue; + if (strcmp(ksp->ks_class, "net") != 0) + continue; + + strncpy(ifr.ifr_name, ksp->ks_name, sizeof(ifr.ifr_name)); + ret = ioctl(sock, SIOCGIFFLAGS, &ifr); + if (ret == -1) + continue; // not a network interface + + // is up? + if ((ifr.ifr_flags & IFF_UP) != 0) { + if ((knp = kstat_data_lookup(ksp, "link_up")) != NULL) { + if (knp->value.ui32 != 0u) + py_is_up = Py_True; + else + py_is_up = Py_False; + } + else { + py_is_up = Py_True; + } + } + else { + py_is_up = Py_False; + } + Py_INCREF(py_is_up); + + // duplex + duplex = 0; // unknown + if ((knp = kstat_data_lookup(ksp, "link_duplex")) != NULL) { + if (knp->value.ui32 == 1) + duplex = 1; // half + else if (knp->value.ui32 == 2) + duplex = 2; // full + } + + // speed + if ((knp = kstat_data_lookup(ksp, "ifspeed")) != NULL) + // expressed in bits per sec, we want mega bits per sec + speed = (int)knp->value.ui64 / 1000000; + else + speed = 0; + + // mtu + ret = ioctl(sock, SIOCGIFMTU, &ifr); + if (ret == -1) + goto error; + + py_ifc_info = Py_BuildValue("(Oiii)", py_is_up, duplex, speed, + ifr.ifr_mtu); + if (!py_ifc_info) + goto error; + if (PyDict_SetItemString(py_retdict, ksp->ks_name, py_ifc_info)) + goto error; + Py_DECREF(py_ifc_info); + } + } + + close(sock); + kstat_close(kc); + return py_retdict; + +error: + Py_XDECREF(py_is_up); + Py_XDECREF(py_ifc_info); + Py_DECREF(py_retdict); + if (sock != 0) + close(sock); + if (kc != NULL) + kstat_close(kc); + PyErr_SetFromErrno(PyExc_OSError); + return NULL; } @@ -1198,6 +1294,8 @@ PsutilMethods[] = "Return the number of physical CPUs on the system."}, {"net_connections", psutil_net_connections, METH_VARARGS, "Return TCP and UDP syste-wide open connections."}, + {"net_if_stats", psutil_net_if_stats, METH_VARARGS, + "Return NIC stats (isup, duplex, speed, mtu)"}, {NULL, NULL, 0, NULL} }; @@ -1254,6 +1352,8 @@ void init_psutil_sunos(void) #else PyObject *module = Py_InitModule("_psutil_sunos", PsutilMethods); #endif + PyModule_AddIntConstant(module, "version", PSUTIL_VERSION); + PyModule_AddIntConstant(module, "SSLEEP", SSLEEP); PyModule_AddIntConstant(module, "SRUN", SRUN); PyModule_AddIntConstant(module, "SZOMB", SZOMB); @@ -1281,9 +1381,8 @@ void init_psutil_sunos(void) PyModule_AddIntConstant(module, "TCPS_BOUND", TCPS_BOUND); PyModule_AddIntConstant(module, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE); - if (module == NULL) { + if (module == NULL) INITERROR; - } #if PY_MAJOR_VERSION >= 3 return module; #endif diff --git a/python/psutil/psutil/_psutil_sunos.h b/python/psutil/psutil/_psutil_sunos.h index 414a7d80e624..f93dbfe0fd38 100644 --- a/python/psutil/psutil/_psutil_sunos.h +++ b/python/psutil/psutil/_psutil_sunos.h @@ -25,3 +25,4 @@ static PyObject* psutil_per_cpu_times(PyObject* self, PyObject* args); static PyObject* psutil_swap_mem(PyObject* self, PyObject* args); static PyObject* psutil_users(PyObject* self, PyObject* args); static PyObject* psutil_net_connections(PyObject* self, PyObject* args); +static PyObject* psutil_net_if_stats(PyObject* self, PyObject* args); diff --git a/python/psutil/psutil/_psutil_windows.c b/python/psutil/psutil/_psutil_windows.c index b536afec608e..3e0f7a7cdc8e 100644 --- a/python/psutil/psutil/_psutil_windows.c +++ b/python/psutil/psutil/_psutil_windows.c @@ -20,6 +20,7 @@ #include #include #include +#include // Link with Iphlpapi.lib #pragma comment(lib, "IPHLPAPI.lib") @@ -30,11 +31,158 @@ #include "arch/windows/process_info.h" #include "arch/windows/process_handles.h" #include "arch/windows/ntextapi.h" +#include "arch/windows/inet_ntop.h" #ifdef __MINGW32__ #include "arch/windows/glpi.h" #endif + +/* + * ============================================================================ + * Utilities + * ============================================================================ + */ + + // a flag for connections without an actual status +static int PSUTIL_CONN_NONE = 128; + +#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) +#define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) +#define LO_T ((float)1e-7) +#define HI_T (LO_T*4294967296.0) +#define BYTESWAP_USHORT(x) ((((USHORT)(x) << 8) | ((USHORT)(x) >> 8)) & 0xffff) +#ifndef AF_INET6 +#define AF_INET6 23 +#endif +#define _psutil_conn_decref_objs() \ + Py_DECREF(_AF_INET); \ + Py_DECREF(_AF_INET6);\ + Py_DECREF(_SOCK_STREAM);\ + Py_DECREF(_SOCK_DGRAM); + +typedef BOOL (WINAPI *LPFN_GLPI) + (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD); + +// fix for mingw32, see +// https://github.com/giampaolo/psutil/issues/351#c2 +typedef struct _DISK_PERFORMANCE_WIN_2008 { + LARGE_INTEGER BytesRead; + LARGE_INTEGER BytesWritten; + LARGE_INTEGER ReadTime; + LARGE_INTEGER WriteTime; + LARGE_INTEGER IdleTime; + DWORD ReadCount; + DWORD WriteCount; + DWORD QueueDepth; + DWORD SplitCount; + LARGE_INTEGER QueryTime; + DWORD StorageDeviceNumber; + WCHAR StorageManagerName[8]; +} DISK_PERFORMANCE_WIN_2008; + +// --- network connections mingw32 support +#ifndef _IPRTRMIB_H +typedef struct _MIB_TCP6ROW_OWNER_PID { + UCHAR ucLocalAddr[16]; + DWORD dwLocalScopeId; + DWORD dwLocalPort; + UCHAR ucRemoteAddr[16]; + DWORD dwRemoteScopeId; + DWORD dwRemotePort; + DWORD dwState; + DWORD dwOwningPid; +} MIB_TCP6ROW_OWNER_PID, *PMIB_TCP6ROW_OWNER_PID; + +typedef struct _MIB_TCP6TABLE_OWNER_PID { + DWORD dwNumEntries; + MIB_TCP6ROW_OWNER_PID table[ANY_SIZE]; +} MIB_TCP6TABLE_OWNER_PID, *PMIB_TCP6TABLE_OWNER_PID; +#endif + +#ifndef __IPHLPAPI_H__ +typedef struct in6_addr { + union { + UCHAR Byte[16]; + USHORT Word[8]; + } u; +} IN6_ADDR, *PIN6_ADDR, FAR *LPIN6_ADDR; + +typedef enum _UDP_TABLE_CLASS { + UDP_TABLE_BASIC, + UDP_TABLE_OWNER_PID, + UDP_TABLE_OWNER_MODULE +} UDP_TABLE_CLASS, *PUDP_TABLE_CLASS; + +typedef struct _MIB_UDPROW_OWNER_PID { + DWORD dwLocalAddr; + DWORD dwLocalPort; + DWORD dwOwningPid; +} MIB_UDPROW_OWNER_PID, *PMIB_UDPROW_OWNER_PID; + +typedef struct _MIB_UDPTABLE_OWNER_PID { + DWORD dwNumEntries; + MIB_UDPROW_OWNER_PID table[ANY_SIZE]; +} MIB_UDPTABLE_OWNER_PID, *PMIB_UDPTABLE_OWNER_PID; +#endif + +typedef struct _MIB_UDP6ROW_OWNER_PID { + UCHAR ucLocalAddr[16]; + DWORD dwLocalScopeId; + DWORD dwLocalPort; + DWORD dwOwningPid; +} MIB_UDP6ROW_OWNER_PID, *PMIB_UDP6ROW_OWNER_PID; + +typedef struct _MIB_UDP6TABLE_OWNER_PID { + DWORD dwNumEntries; + MIB_UDP6ROW_OWNER_PID table[ANY_SIZE]; +} MIB_UDP6TABLE_OWNER_PID, *PMIB_UDP6TABLE_OWNER_PID; + + +PIP_ADAPTER_ADDRESSES +psutil_get_nic_addresses() { + // allocate a 15 KB buffer to start with + int outBufLen = 15000; + DWORD dwRetVal = 0; + ULONG attempts = 0; + PIP_ADAPTER_ADDRESSES pAddresses = NULL; + + do { + pAddresses = (IP_ADAPTER_ADDRESSES *) malloc(outBufLen); + if (pAddresses == NULL) { + PyErr_NoMemory(); + return NULL; + } + + dwRetVal = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, pAddresses, + &outBufLen); + if (dwRetVal == ERROR_BUFFER_OVERFLOW) { + free(pAddresses); + pAddresses = NULL; + } + else { + break; + } + + attempts++; + } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (attempts < 3)); + + if (dwRetVal != NO_ERROR) { + PyErr_SetString(PyExc_RuntimeError, "GetAdaptersAddresses() failed."); + return NULL; + } + + return pAddresses; +} + + +/* + * ============================================================================ + * Public Python API + * ============================================================================ + */ + + /* * Return a Python float representing the system uptime expressed in seconds * since the epoch. @@ -82,14 +230,12 @@ psutil_pid_exists(PyObject *self, PyObject *args) long pid; int status; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } status = psutil_pid_is_running(pid); - if (-1 == status) { + if (-1 == status) return NULL; // exception raised in psutil_pid_is_running() - } return PyBool_FromLong(status); } @@ -106,13 +252,11 @@ psutil_pids(PyObject *self, PyObject *args) PyObject *pid = NULL; PyObject *retlist = PyList_New(0); - if (retlist == NULL) { + if (retlist == NULL) return NULL; - } proclist = psutil_get_pids(&numberOfReturnedPIDs); - if (NULL == proclist) { + if (proclist == NULL) goto error; - } for (i = 0; i < numberOfReturnedPIDs; i++) { pid = Py_BuildValue("I", proclist[i]); @@ -145,12 +289,10 @@ psutil_proc_kill(PyObject *self, PyObject *args) HANDLE hProcess; long pid; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (pid == 0) { + if (pid == 0) return AccessDenied(); - } hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); if (hProcess == NULL) { @@ -172,8 +314,7 @@ psutil_proc_kill(PyObject *self, PyObject *args) } CloseHandle(hProcess); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } @@ -189,12 +330,10 @@ psutil_proc_wait(PyObject *self, PyObject *args) long pid; long timeout; - if (! PyArg_ParseTuple(args, "ll", &pid, &timeout)) { + if (! PyArg_ParseTuple(args, "ll", &pid, &timeout)) return NULL; - } - if (pid == 0) { + if (pid == 0) return AccessDenied(); - } hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, pid); @@ -202,8 +341,7 @@ psutil_proc_wait(PyObject *self, PyObject *args) if (GetLastError() == ERROR_INVALID_PARAMETER) { // no such process; we do not want to raise NSP but // return None instead. - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } else { PyErr_SetFromWindowsErr(0); @@ -250,15 +388,12 @@ psutil_proc_cpu_times(PyObject *self, PyObject *args) HANDLE hProcess; FILETIME ftCreate, ftExit, ftKernel, ftUser; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } hProcess = psutil_handle_from_pid(pid); - if (hProcess == NULL) { + if (hProcess == NULL) return NULL; - } - if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) { CloseHandle(hProcess); if (GetLastError() == ERROR_ACCESS_DENIED) { @@ -293,32 +428,6 @@ psutil_proc_cpu_times(PyObject *self, PyObject *args) } -/* - * Alternative implementation of the one above but bypasses ACCESS DENIED. - */ -static PyObject * -psutil_proc_cpu_times_2(PyObject *self, PyObject *args) -{ - DWORD pid; - PSYSTEM_PROCESS_INFORMATION process; - PVOID buffer; - double user, kernel; - - if (! PyArg_ParseTuple(args, "l", &pid)) { - return NULL; - } - if (! psutil_get_proc_info(pid, &process, &buffer)) { - return NULL; - } - user = (double)process->UserTime.HighPart * 429.4967296 + \ - (double)process->UserTime.LowPart * 1e-7; - kernel = (double)process->KernelTime.HighPart * 429.4967296 + \ - (double)process->KernelTime.LowPart * 1e-7; - free(buffer); - return Py_BuildValue("(dd)", user, kernel); -} - - /* * Return a Python float indicating the process create time expressed in * seconds since the epoch. @@ -333,20 +442,16 @@ psutil_proc_create_time(PyObject *self, PyObject *args) BOOL ret; FILETIME ftCreate, ftExit, ftKernel, ftUser; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } // special case for PIDs 0 and 4, return system boot time - if (0 == pid || 4 == pid) { + if (0 == pid || 4 == pid) return psutil_boot_time(NULL, NULL); - } hProcess = psutil_handle_from_pid(pid); - if (hProcess == NULL) { + if (hProcess == NULL) return NULL; - } - if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) { CloseHandle(hProcess); if (GetLastError() == ERROR_ACCESS_DENIED) { @@ -368,9 +473,8 @@ psutil_proc_create_time(PyObject *self, PyObject *args) ret = GetExitCodeProcess(hProcess, &exitCode); CloseHandle(hProcess); if (ret != 0) { - if (exitCode != STILL_ACTIVE) { + if (exitCode != STILL_ACTIVE) return NoSuchProcess(); - } } else { // Ignore access denied as it means the process is still alive. @@ -393,39 +497,6 @@ psutil_proc_create_time(PyObject *self, PyObject *args) } -/* - * Alternative implementation of the one above but bypasses ACCESS DENIED. - */ -static PyObject * -psutil_proc_create_time_2(PyObject *self, PyObject *args) -{ - DWORD pid; - PSYSTEM_PROCESS_INFORMATION process; - PVOID buffer; - long long unix_time; - - if (! PyArg_ParseTuple(args, "l", &pid)) { - return NULL; - } - if (! psutil_get_proc_info(pid, &process, &buffer)) { - return NULL; - } - // special case for PIDs 0 and 4, return system boot time - if (0 == pid || 4 == pid) { - return psutil_boot_time(NULL, NULL); - } - /* - Convert the LARGE_INTEGER union to a Unix time. - It's the best I could find by googling and borrowing code here and there. - The time returned has a precision of 1 second. - */ - unix_time = ((LONGLONG)process->CreateTime.HighPart) << 32; - unix_time += process->CreateTime.LowPart - 116444736000000000LL; - unix_time /= 10000000; - free(buffer); - return Py_BuildValue("d", (double)unix_time); -} - /* * Return the number of logical CPUs. @@ -437,20 +508,13 @@ psutil_cpu_count_logical(PyObject *self, PyObject *args) system_info.dwNumberOfProcessors = 0; GetSystemInfo(&system_info); - if (system_info.dwNumberOfProcessors == 0) { - // mimic os.cpu_count() - Py_INCREF(Py_None); - return Py_None; - } - else { + if (system_info.dwNumberOfProcessors == 0) + Py_RETURN_NONE; // mimic os.cpu_count() + else return Py_BuildValue("I", system_info.dwNumberOfProcessors); - } } -typedef BOOL (WINAPI *LPFN_GLPI) (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, - PDWORD); - /* * Return the number of physical CPU cores. */ @@ -510,8 +574,7 @@ psutil_cpu_count_phys(PyObject *self, PyObject *args) // mimic os.cpu_count() if (buffer != NULL) free(buffer); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } @@ -524,20 +587,16 @@ psutil_proc_cmdline(PyObject *self, PyObject *args) { int pid_return; PyObject *arglist; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if ((pid == 0) || (pid == 4)) { + if ((pid == 0) || (pid == 4)) return Py_BuildValue("[]"); - } pid_return = psutil_pid_is_running(pid); - if (pid_return == 0) { + if (pid_return == 0) return NoSuchProcess(); - } - if (pid_return == -1) { + if (pid_return == -1) return NULL; - } // XXX the assumptio below probably needs to go away @@ -563,15 +622,13 @@ psutil_proc_exe(PyObject *self, PyObject *args) { long pid; HANDLE hProcess; wchar_t exe[MAX_PATH]; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } hProcess = psutil_handle_from_pid_waccess(pid, PROCESS_QUERY_INFORMATION); - if (NULL == hProcess) { + if (NULL == hProcess) return NULL; - } - if (GetProcessImageFileNameW(hProcess, &exe, MAX_PATH) == 0) { + if (GetProcessImageFileNameW(hProcess, exe, MAX_PATH) == 0) { CloseHandle(hProcess); PyErr_SetFromWindowsErr(0); return NULL; @@ -581,6 +638,47 @@ psutil_proc_exe(PyObject *self, PyObject *args) { } +/* + * Return process base name. + * Note: psutil_proc_exe() is attempted first because it's faster + * but it raise AccessDenied for processes owned by other users + * in which case we fall back on using this. + */ +static PyObject * +psutil_proc_name(PyObject *self, PyObject *args) { + long pid; + int ok; + PROCESSENTRY32 pentry; + HANDLE hSnapShot; + + if (! PyArg_ParseTuple(args, "l", &pid)) + return NULL; + hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, pid); + if (hSnapShot == INVALID_HANDLE_VALUE) { + PyErr_SetFromWindowsErr(0); + return NULL; + } + pentry.dwSize = sizeof(PROCESSENTRY32); + ok = Process32First(hSnapShot, &pentry); + if (! ok) { + CloseHandle(hSnapShot); + PyErr_SetFromWindowsErr(0); + return NULL; + } + while (ok) { + if (pentry.th32ProcessID == pid) { + CloseHandle(hSnapShot); + return Py_BuildValue("s", pentry.szExeFile); + } + ok = Process32Next(hSnapShot, &pentry); + } + + CloseHandle(hSnapShot); + NoSuchProcess(); + return NULL; +} + + /* * Return process memory information as a Python tuple. */ @@ -596,16 +694,15 @@ psutil_proc_memory_info(PyObject *self, PyObject *args) #endif SIZE_T private = 0; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } hProcess = psutil_handle_from_pid(pid); - if (NULL == hProcess) { + if (NULL == hProcess) return NULL; - } - if (! GetProcessMemoryInfo(hProcess, &cnt, sizeof(cnt)) ) { + if (! GetProcessMemoryInfo(hProcess, (PPROCESS_MEMORY_COUNTERS)&cnt, + sizeof(cnt))) { CloseHandle(hProcess); return PyErr_SetFromWindowsErr(0); } @@ -668,12 +765,10 @@ psutil_proc_memory_info_2(PyObject *self, PyObject *args) unsigned int m1, m2, m3, m4, m5, m6, m7, m8; #endif - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (! psutil_get_proc_info(pid, &process, &buffer)) { + if (! psutil_get_proc_info(pid, &process, &buffer)) return NULL; - } #if (_WIN32_WINNT >= 0x0501) // Windows XP with SP2 private = process->PrivatePageCount; @@ -716,10 +811,8 @@ psutil_virtual_mem(PyObject *self, PyObject *args) MEMORYSTATUSEX memInfo; memInfo.dwLength = sizeof(MEMORYSTATUSEX); - if (! GlobalMemoryStatusEx(&memInfo) ) { + if (! GlobalMemoryStatusEx(&memInfo)) return PyErr_SetFromWindowsErr(0); - } - return Py_BuildValue("(LLLLLL)", memInfo.ullTotalPhys, // total memInfo.ullAvailPhys, // avail @@ -730,10 +823,6 @@ psutil_virtual_mem(PyObject *self, PyObject *args) } -#define LO_T ((float)1e-7) -#define HI_T (LO_T*4294967296.0) - - /* * Retrieves system CPU timing information as a (user, system, idle) * tuple. On a multiprocessor system, the values returned are the @@ -745,9 +834,8 @@ psutil_cpu_times(PyObject *self, PyObject *args) float idle, kernel, user, system; FILETIME idle_time, kernel_time, user_time; - if (!GetSystemTimes(&idle_time, &kernel_time, &user_time)) { + if (!GetSystemTimes(&idle_time, &kernel_time, &user_time)) return PyErr_SetFromWindowsErr(0); - } idle = (float)((HI_T * idle_time.dwHighDateTime) + \ (LO_T * idle_time.dwLowDateTime)); @@ -848,12 +936,10 @@ psutil_per_cpu_times(PyObject *self, PyObject *args) error: Py_XDECREF(arg); Py_DECREF(retlist); - if (sppi) { + if (sppi) free(sppi); - } - if (hNtDll) { + if (hNtDll) FreeLibrary(hNtDll); - } PyErr_SetFromWindowsErr(0); return NULL; } @@ -876,14 +962,12 @@ psutil_proc_cwd(PyObject *self, PyObject *args) PyObject *cwd_from_wchar = NULL; PyObject *cwd = NULL; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } processHandle = psutil_handle_from_pid(pid); - if (processHandle == NULL) { + if (processHandle == NULL) return NULL; - } pebAddress = psutil_get_peb_address(processHandle); @@ -1072,15 +1156,12 @@ psutil_proc_suspend(PyObject *self, PyObject *args) { long pid; int suspend = 1; - if (! PyArg_ParseTuple(args, "l", &pid)) { - return NULL; - } - if (! psutil_proc_suspend_or_resume(pid, suspend)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - Py_INCREF(Py_None); - return Py_None; + if (! psutil_proc_suspend_or_resume(pid, suspend)) + return NULL; + Py_RETURN_NONE; } @@ -1089,35 +1170,12 @@ psutil_proc_resume(PyObject *self, PyObject *args) { long pid; int suspend = 0; - if (! PyArg_ParseTuple(args, "l", &pid)) { - return NULL; - } - if (! psutil_proc_suspend_or_resume(pid, suspend)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - Py_INCREF(Py_None); - return Py_None; -} - - -static PyObject * -psutil_proc_num_threads(PyObject *self, PyObject *args) -{ - DWORD pid; - PSYSTEM_PROCESS_INFORMATION process; - PVOID buffer; - int num; - - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! psutil_proc_suspend_or_resume(pid, suspend)) return NULL; - } - if (! psutil_get_proc_info(pid, &process, &buffer)) { - return NULL; - } - num = (int)process->NumberOfThreads; - free(buffer); - return Py_BuildValue("i", num); + Py_RETURN_NONE; } @@ -1134,12 +1192,10 @@ psutil_proc_threads(PyObject *self, PyObject *args) PyObject *pyTuple = NULL; HANDLE hThreadSnap = NULL; - if (retList == NULL) { + if (retList == NULL) return NULL; - } - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) goto error; - } if (pid == 0) { // raise AD instead of returning 0 as procexp is able to // retrieve useful information somehow @@ -1152,9 +1208,8 @@ psutil_proc_threads(PyObject *self, PyObject *args) NoSuchProcess(); goto error; } - if (pid_return == -1) { + if (pid_return == -1) goto error; - } hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (hThreadSnap == INVALID_HANDLE_VALUE) { @@ -1172,10 +1227,8 @@ psutil_proc_threads(PyObject *self, PyObject *args) // Walk the thread snapshot to find all threads of the process. // If the thread belongs to the process, increase the counter. - do - { - if (te32.th32OwnerProcessID == pid) - { + do { + if (te32.th32OwnerProcessID == pid) { pyTuple = NULL; hThread = NULL; hThread = OpenThread(THREAD_QUERY_INFORMATION, @@ -1226,9 +1279,8 @@ psutil_proc_threads(PyObject *self, PyObject *args) Py_DECREF(retList); if (hThread != NULL) CloseHandle(hThread); - if (hThreadSnap != NULL) { + if (hThreadSnap != NULL) CloseHandle(hThreadSnap); - } return NULL; } @@ -1241,20 +1293,16 @@ psutil_proc_open_files(PyObject *self, PyObject *args) DWORD access = PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION; PyObject *filesList; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } processHandle = psutil_handle_from_pid_waccess(pid, access); - if (processHandle == NULL) { + if (processHandle == NULL) return NULL; - } - filesList = psutil_get_open_files(pid, processHandle); CloseHandle(processHandle); - if (filesList == NULL) { + if (filesList == NULL) return PyErr_SetFromWindowsErr(0); - } return filesList; } @@ -1271,17 +1319,15 @@ psutil_win32_QueryDosDevice(PyObject *self, PyObject *args) TCHAR d = TEXT('A'); TCHAR szBuff[5]; - if (!PyArg_ParseTuple(args, "s", &lpDevicePath)) { + if (!PyArg_ParseTuple(args, "s", &lpDevicePath)) return NULL; - } - while (d <= TEXT('Z')) - { + while (d <= TEXT('Z')) { TCHAR szDeviceName[3] = {d, TEXT(':'), TEXT('\0')}; TCHAR szTarget[512] = {0}; if (QueryDosDevice(szDeviceName, szTarget, 511) != 0) { if (_tcscmp(lpDevicePath, szTarget) == 0) { - _stprintf(szBuff, TEXT("%c:"), d); + _stprintf_s(szBuff, _countof(szBuff), TEXT("%c:"), d); return Py_BuildValue("s", szBuff); } } @@ -1310,15 +1356,13 @@ psutil_proc_username(PyObject *self, PyObject *args) PTSTR fullName; PyObject *returnObject; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } processHandle = psutil_handle_from_pid_waccess( pid, PROCESS_QUERY_INFORMATION); - if (processHandle == NULL) { + if (processHandle == NULL) return NULL; - } if (!OpenProcessToken(processHandle, TOKEN_QUERY, &tokenHandle)) { CloseHandle(processHandle); @@ -1331,9 +1375,8 @@ psutil_proc_username(PyObject *self, PyObject *args) bufferSize = 0x100; user = malloc(bufferSize); - if (user == NULL) { + if (user == NULL) return PyErr_NoMemory(); - } if (!GetTokenInformation(tokenHandle, TokenUser, user, bufferSize, &bufferSize)) @@ -1404,90 +1447,16 @@ psutil_proc_username(PyObject *self, PyObject *args) memcpy(&fullName[domainNameSize + 1], name, nameSize); fullName[domainNameSize + 1 + nameSize] = '\0'; - returnObject = Py_BuildValue("s", fullName); - - free(fullName); - free(name); - free(domainName); - free(user); - - return returnObject; -} - - -// --- network connections mingw32 support - -#ifndef _IPRTRMIB_H -typedef struct _MIB_TCP6ROW_OWNER_PID { - UCHAR ucLocalAddr[16]; - DWORD dwLocalScopeId; - DWORD dwLocalPort; - UCHAR ucRemoteAddr[16]; - DWORD dwRemoteScopeId; - DWORD dwRemotePort; - DWORD dwState; - DWORD dwOwningPid; -} MIB_TCP6ROW_OWNER_PID, *PMIB_TCP6ROW_OWNER_PID; - -typedef struct _MIB_TCP6TABLE_OWNER_PID { - DWORD dwNumEntries; - MIB_TCP6ROW_OWNER_PID table[ANY_SIZE]; -} MIB_TCP6TABLE_OWNER_PID, *PMIB_TCP6TABLE_OWNER_PID; -#endif - -#ifndef __IPHLPAPI_H__ -typedef struct in6_addr { - union { - UCHAR Byte[16]; - USHORT Word[8]; - } u; -} IN6_ADDR, *PIN6_ADDR, FAR *LPIN6_ADDR; - -typedef enum _UDP_TABLE_CLASS { - UDP_TABLE_BASIC, - UDP_TABLE_OWNER_PID, - UDP_TABLE_OWNER_MODULE -} UDP_TABLE_CLASS, *PUDP_TABLE_CLASS; - -typedef struct _MIB_UDPROW_OWNER_PID { - DWORD dwLocalAddr; - DWORD dwLocalPort; - DWORD dwOwningPid; -} MIB_UDPROW_OWNER_PID, *PMIB_UDPROW_OWNER_PID; - -typedef struct _MIB_UDPTABLE_OWNER_PID { - DWORD dwNumEntries; - MIB_UDPROW_OWNER_PID table[ANY_SIZE]; -} MIB_UDPTABLE_OWNER_PID, *PMIB_UDPTABLE_OWNER_PID; -#endif - -typedef struct _MIB_UDP6ROW_OWNER_PID { - UCHAR ucLocalAddr[16]; - DWORD dwLocalScopeId; - DWORD dwLocalPort; - DWORD dwOwningPid; -} MIB_UDP6ROW_OWNER_PID, *PMIB_UDP6ROW_OWNER_PID; - -typedef struct _MIB_UDP6TABLE_OWNER_PID { - DWORD dwNumEntries; - MIB_UDP6ROW_OWNER_PID table[ANY_SIZE]; -} MIB_UDP6TABLE_OWNER_PID, *PMIB_UDP6TABLE_OWNER_PID; - - -#define BYTESWAP_USHORT(x) ((((USHORT)(x) << 8) | ((USHORT)(x) >> 8)) & 0xffff) - -#ifndef AF_INET6 -#define AF_INET6 23 -#endif + returnObject = PyUnicode_Decode( + fullName, _tcslen(fullName), Py_FileSystemDefaultEncoding, "replace"); -#define _psutil_conn_decref_objs() \ - Py_DECREF(_AF_INET); \ - Py_DECREF(_AF_INET6);\ - Py_DECREF(_SOCK_STREAM);\ - Py_DECREF(_SOCK_DGRAM); + free(fullName); + free(name); + free(domainName); + free(user); -// a signaler for connections without an actual status -static int PSUTIL_CONN_NONE = 128; + return returnObject; +} /* @@ -1939,9 +1908,8 @@ psutil_proc_priority_get(PyObject *self, PyObject *args) long pid; DWORD priority; HANDLE hProcess; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } hProcess = psutil_handle_from_pid(pid); if (hProcess == NULL) { @@ -1985,8 +1953,7 @@ psutil_proc_priority_set(PyObject *self, PyObject *args) PyErr_SetFromWindowsErr(0); return NULL; } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } @@ -2005,9 +1972,8 @@ psutil_proc_io_priority_get(PyObject *self, PyObject *args) (_NtQueryInformationProcess)GetProcAddress( GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess"); - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } hProcess = psutil_handle_from_pid(pid); if (hProcess == NULL) { return NULL; @@ -2061,8 +2027,7 @@ psutil_proc_io_priority_set(PyObject *self, PyObject *args) ); CloseHandle(hProcess); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } #endif @@ -2077,9 +2042,8 @@ psutil_proc_io_counters(PyObject *self, PyObject *args) HANDLE hProcess; IO_COUNTERS IoCounters; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } hProcess = psutil_handle_from_pid(pid); if (NULL == hProcess) { return NULL; @@ -2097,32 +2061,6 @@ psutil_proc_io_counters(PyObject *self, PyObject *args) } -/* - * Alternative implementation of the one above but bypasses ACCESS DENIED. - */ -static PyObject * -psutil_proc_io_counters_2(PyObject *self, PyObject *args) -{ - DWORD pid; - PSYSTEM_PROCESS_INFORMATION process; - PVOID buffer; - LONGLONG rcount, wcount, rbytes, wbytes; - - if (! PyArg_ParseTuple(args, "l", &pid)) { - return NULL; - } - if (! psutil_get_proc_info(pid, &process, &buffer)) { - return NULL; - } - rcount = process->ReadOperationCount.QuadPart; - wcount = process->WriteOperationCount.QuadPart; - rbytes = process->ReadTransferCount.QuadPart; - wbytes = process->WriteTransferCount.QuadPart; - free(buffer); - return Py_BuildValue("KKKK", rcount, wcount, rbytes, wbytes); -} - - /* * Return process CPU affinity as a bitmask */ @@ -2131,12 +2069,11 @@ psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) { DWORD pid; HANDLE hProcess; - PDWORD_PTR proc_mask; - PDWORD_PTR system_mask; + DWORD_PTR proc_mask; + DWORD_PTR system_mask; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } hProcess = psutil_handle_from_pid(pid); if (hProcess == NULL) { return NULL; @@ -2186,8 +2123,7 @@ psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) } CloseHandle(hProcess); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } @@ -2203,9 +2139,8 @@ psutil_proc_is_suspended(PyObject *self, PyObject *args) PSYSTEM_PROCESS_INFORMATION process; PVOID buffer; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } if (! psutil_get_proc_info(pid, &process, &buffer)) { return NULL; } @@ -2267,14 +2202,9 @@ psutil_disk_usage(PyObject *self, PyObject *args) static PyObject * psutil_net_io_counters(PyObject *self, PyObject *args) { - int attempts = 0; - int i; - int outBufLen = 15000; char ifname[MAX_PATH]; DWORD dwRetVal = 0; MIB_IFROW *pIfRow = NULL; - ULONG flags = 0; - ULONG family = AF_UNSPEC; PIP_ADAPTER_ADDRESSES pAddresses = NULL; PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; @@ -2282,35 +2212,13 @@ psutil_net_io_counters(PyObject *self, PyObject *args) PyObject *py_nic_info = NULL; PyObject *py_nic_name = NULL; - if (py_retdict == NULL) { + if (py_retdict == NULL) return NULL; - } - do { - pAddresses = (IP_ADAPTER_ADDRESSES *) malloc(outBufLen); - if (pAddresses == NULL) { - PyErr_NoMemory(); - goto error; - } - - dwRetVal = GetAdaptersAddresses(family, flags, NULL, pAddresses, - &outBufLen); - if (dwRetVal == ERROR_BUFFER_OVERFLOW) { - free(pAddresses); - pAddresses = NULL; - } - else { - break; - } - - attempts++; - } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (attempts < 3)); - - if (dwRetVal != NO_ERROR) { - PyErr_SetString(PyExc_RuntimeError, "GetAdaptersAddresses() failed."); + pAddresses = psutil_get_nic_addresses(); + if (pAddresses == NULL) goto error; - } - pCurrAddresses = pAddresses; + while (pCurrAddresses) { py_nic_name = NULL; py_nic_info = NULL; @@ -2340,22 +2248,10 @@ psutil_net_io_counters(PyObject *self, PyObject *args) if (!py_nic_info) goto error; - sprintf(ifname, "%wS", pCurrAddresses->FriendlyName); + sprintf_s(ifname, MAX_PATH, "%wS", pCurrAddresses->FriendlyName); + py_nic_name = PyUnicode_Decode( + ifname, _tcslen(ifname), Py_FileSystemDefaultEncoding, "replace"); -#if PY_MAJOR_VERSION >= 3 - // XXX - Dirty hack to avoid encoding errors on Python 3, see: - // https://github.com/giampaolo/psutil/issues/446#c9 - for (i = 0; i < MAX_PATH; i++) { - if (*(ifname+i) < 0 || *(ifname+i) > 256) { - // replace the non unicode character - *(ifname+i) = '?'; - } - else if (*(ifname+i) == '\0') { - break; - } - } -#endif - py_nic_name = Py_BuildValue("s", ifname); if (py_nic_name == NULL) goto error; if (PyDict_SetItem(py_retdict, py_nic_name, py_nic_info)) @@ -2381,22 +2277,6 @@ psutil_net_io_counters(PyObject *self, PyObject *args) return NULL; } -// fix for mingw32, see -// https://github.com/giampaolo/psutil/issues/351#c2 -typedef struct _DISK_PERFORMANCE_WIN_2008 { - LARGE_INTEGER BytesRead; - LARGE_INTEGER BytesWritten; - LARGE_INTEGER ReadTime; - LARGE_INTEGER WriteTime; - LARGE_INTEGER IdleTime; - DWORD ReadCount; - DWORD WriteCount; - DWORD QueueDepth; - DWORD SplitCount; - LARGE_INTEGER QueryTime; - DWORD StorageDeviceNumber; - WCHAR StorageManagerName[8]; -} DISK_PERFORMANCE_WIN_2008; /* * Return a Python dict of tuples for disk I/O information @@ -2422,7 +2302,7 @@ psutil_disk_io_counters(PyObject *self, PyObject *args) // in the alphabet (from A:\ to Z:\). for (devNum = 0; devNum <= 32; ++devNum) { py_disk_info = NULL; - sprintf(szDevice, "\\\\.\\PhysicalDrive%d", devNum); + sprintf_s(szDevice, MAX_PATH, "\\\\.\\PhysicalDrive%d", devNum); hDevice = CreateFile(szDevice, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); @@ -2433,7 +2313,7 @@ psutil_disk_io_counters(PyObject *self, PyObject *args) &diskPerformance, sizeof(diskPerformance), &dwSize, NULL)) { - sprintf(szDeviceDisplay, "PhysicalDrive%d", devNum); + sprintf_s(szDeviceDisplay, MAX_PATH, "PhysicalDrive%d", devNum); py_disk_info = Py_BuildValue( "(IILLKK)", diskPerformance.ReadCount, @@ -2475,22 +2355,22 @@ psutil_disk_io_counters(PyObject *self, PyObject *args) static char *psutil_get_drive_type(int type) { switch (type) { - case DRIVE_FIXED: - return "fixed"; - case DRIVE_CDROM: - return "cdrom"; - case DRIVE_REMOVABLE: - return "removable"; - case DRIVE_UNKNOWN: - return "unknown"; - case DRIVE_NO_ROOT_DIR: - return "unmounted"; - case DRIVE_REMOTE: - return "remote"; - case DRIVE_RAMDISK: - return "ramdisk"; - default: - return "?"; + case DRIVE_FIXED: + return "fixed"; + case DRIVE_CDROM: + return "cdrom"; + case DRIVE_REMOVABLE: + return "removable"; + case DRIVE_UNKNOWN: + return "unknown"; + case DRIVE_NO_ROOT_DIR: + return "unmounted"; + case DRIVE_REMOTE: + return "remote"; + case DRIVE_RAMDISK: + return "ramdisk"; + default: + return "?"; } } @@ -2574,25 +2454,25 @@ psutil_disk_partitions(PyObject *self, PyObject *args) // which case the error is (21, "device not ready"). // Let's pretend it didn't happen as we already have // the drive name and type ('removable'). - strcat(opts, ""); + strcat_s(opts, _countof(opts), ""); SetLastError(0); } else { if (pflags & FILE_READ_ONLY_VOLUME) { - strcat(opts, "ro"); + strcat_s(opts, _countof(opts), "ro"); } else { - strcat(opts, "rw"); + strcat_s(opts, _countof(opts), "rw"); } if (pflags & FILE_VOLUME_IS_COMPRESSED) { - strcat(opts, ",compressed"); + strcat_s(opts, _countof(opts), ",compressed"); } } if (strlen(opts) > 0) { - strcat(opts, ","); + strcat_s(opts, _countof(opts), ","); } - strcat(opts, psutil_get_drive_type(type)); + strcat_s(opts, _countof(opts), psutil_get_drive_type(type)); py_tuple = Py_BuildValue( "(ssss)", @@ -2621,14 +2501,6 @@ psutil_disk_partitions(PyObject *self, PyObject *args) return NULL; } - -#ifdef UNICODE -#define WTSOpenServer WTSOpenServerW -#else -#define WTSOpenServer WTSOpenServerA -#endif - - /* * Return a Python dict of tuples for disk I/O information */ @@ -2655,6 +2527,8 @@ psutil_users(PyObject *self, PyObject *args) PyObject *py_retlist = PyList_New(0); PyObject *py_tuple = NULL; PyObject *py_address = NULL; + PyObject *py_buffer_user_encoded = NULL; + if (py_retlist == NULL) { return NULL; } @@ -2709,12 +2583,13 @@ psutil_users(PyObject *self, PyObject *args) address = (PWTS_CLIENT_ADDRESS)buffer_addr; if (address->AddressFamily == 0) { // AF_INET - sprintf(address_str, - "%u.%u.%u.%u", - address->Address[0], - address->Address[1], - address->Address[2], - address->Address[3]); + sprintf_s(address_str, + _countof(address_str), + "%u.%u.%u.%u", + address->Address[0], + address->Address[1], + address->Address[2], + address->Address[3]); py_address = Py_BuildValue("s", address_str); if (!py_address) goto error; @@ -2739,12 +2614,16 @@ psutil_users(PyObject *self, PyObject *args) station_info.ConnectTime.dwLowDateTime - 116444736000000000LL; unix_time /= 10000000; - py_tuple = Py_BuildValue("sOd", buffer_user, py_address, + py_buffer_user_encoded = PyUnicode_Decode( + buffer_user, _tcslen(buffer_user), Py_FileSystemDefaultEncoding, + "replace"); + py_tuple = Py_BuildValue("OOd", py_buffer_user_encoded, py_address, (double)unix_time); if (!py_tuple) goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; + Py_XDECREF(py_buffer_user_encoded); Py_XDECREF(py_address); Py_XDECREF(py_tuple); } @@ -2757,6 +2636,7 @@ psutil_users(PyObject *self, PyObject *args) return py_retlist; error: + Py_XDECREF(py_buffer_user_encoded); Py_XDECREF(py_tuple); Py_XDECREF(py_address); Py_DECREF(py_retlist); @@ -2790,9 +2670,8 @@ psutil_proc_num_handles(PyObject *self, PyObject *args) HANDLE hProcess; DWORD handleCount; - if (! PyArg_ParseTuple(args, "l", &pid)) { + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } hProcess = psutil_handle_from_pid(pid); if (NULL == hProcess) { return NULL; @@ -2807,75 +2686,102 @@ psutil_proc_num_handles(PyObject *self, PyObject *args) /* - * Alternative implementation of the one above but bypasses ACCESS DENIED. - */ -static PyObject * -psutil_proc_num_handles_2(PyObject *self, PyObject *args) -{ - DWORD pid; - PSYSTEM_PROCESS_INFORMATION process; - PVOID buffer; - ULONG count; - - if (! PyArg_ParseTuple(args, "l", &pid)) { - return NULL; - } - if (! psutil_get_proc_info(pid, &process, &buffer)) { - return NULL; - } - count = process->HandleCount; - free(buffer); - return Py_BuildValue("k", count); -} - - -/* - * Return the number of context switches executed by process. + * Get various process information by using NtQuerySystemInformation. + * We use this as a fallback when faster functions fail with access + * denied. This is slower because it iterates over all processes. + * Returned tuple includes the following process info: + * + * - num_threads + * - ctx_switches + * - num_handles (fallback) + * - user/kernel times (fallback) + * - create time (fallback) + * - io counters (fallback) */ static PyObject * -psutil_proc_num_ctx_switches(PyObject *self, PyObject *args) +psutil_proc_info(PyObject *self, PyObject *args) { DWORD pid; PSYSTEM_PROCESS_INFORMATION process; PVOID buffer; + ULONG num_handles; ULONG i; - ULONG total = 0; + ULONG ctx_switches = 0; + double user_time; + double kernel_time; + long long create_time; + int num_threads; + LONGLONG io_rcount, io_wcount, io_rbytes, io_wbytes; - if (! PyArg_ParseTuple(args, "l", &pid)) { + + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } - if (! psutil_get_proc_info(pid, &process, &buffer)) { + if (! psutil_get_proc_info(pid, &process, &buffer)) return NULL; + + num_handles = process->HandleCount; + for (i = 0; i < process->NumberOfThreads; i++) + ctx_switches += process->Threads[i].ContextSwitches; + user_time = (double)process->UserTime.HighPart * 429.4967296 + \ + (double)process->UserTime.LowPart * 1e-7; + kernel_time = (double)process->KernelTime.HighPart * 429.4967296 + \ + (double)process->KernelTime.LowPart * 1e-7; + // Convert the LARGE_INTEGER union to a Unix time. + // It's the best I could find by googling and borrowing code here + // and there. The time returned has a precision of 1 second. + if (0 == pid || 4 == pid) { + // the python module will translate this into BOOT_TIME later + create_time = 0; } - for (i = 0; i < process->NumberOfThreads; i++) { - total += process->Threads[i].ContextSwitches; - } + else { + create_time = ((LONGLONG)process->CreateTime.HighPart) << 32; + create_time += process->CreateTime.LowPart - 116444736000000000LL; + create_time /= 10000000; + } + num_threads = (int)process->NumberOfThreads; + io_rcount = process->ReadOperationCount.QuadPart; + io_wcount = process->WriteOperationCount.QuadPart; + io_rbytes = process->ReadTransferCount.QuadPart; + io_wbytes = process->WriteTransferCount.QuadPart; free(buffer); - return Py_BuildValue("ki", total, 0); + + return Py_BuildValue( + "kkdddiKKKK", + num_handles, + ctx_switches, + user_time, + kernel_time, + (double)create_time, + num_threads, + io_rcount, + io_wcount, + io_rbytes, + io_wbytes + ); } static char *get_region_protection_string(ULONG protection) { switch (protection & 0xff) { - case PAGE_NOACCESS: - return ""; - case PAGE_READONLY: - return "r"; - case PAGE_READWRITE: - return "rw"; - case PAGE_WRITECOPY: - return "wc"; - case PAGE_EXECUTE: - return "x"; - case PAGE_EXECUTE_READ: - return "xr"; - case PAGE_EXECUTE_READWRITE: - return "xrw"; - case PAGE_EXECUTE_WRITECOPY: - return "xwc"; - default: - return "?"; + case PAGE_NOACCESS: + return ""; + case PAGE_READONLY: + return "r"; + case PAGE_READWRITE: + return "rw"; + case PAGE_WRITECOPY: + return "wc"; + case PAGE_EXECUTE: + return "x"; + case PAGE_EXECUTE_READ: + return "xr"; + case PAGE_EXECUTE_READWRITE: + return "xrw"; + case PAGE_EXECUTE_WRITECOPY: + return "xwc"; + default: + return "?"; } } @@ -3000,6 +2906,273 @@ psutil_ppid_map(PyObject *self, PyObject *args) } +/* + * Return NICs addresses. + */ + +static PyObject * +psutil_net_if_addrs(PyObject *self, PyObject *args) +{ + unsigned int i = 0; + ULONG family; + PCTSTR intRet; + char *ptr; + char buff[100]; + char ifname[MAX_PATH]; + DWORD bufflen = 100; + PIP_ADAPTER_ADDRESSES pAddresses = NULL; + PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; + PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL; + + PyObject *py_retlist = PyList_New(0); + PyObject *py_tuple = NULL; + PyObject *py_address = NULL; + PyObject *py_mac_address = NULL; + + if (py_retlist == NULL) + return NULL; + + pAddresses = psutil_get_nic_addresses(); + if (pAddresses == NULL) + goto error; + pCurrAddresses = pAddresses; + + while (pCurrAddresses) { + pUnicast = pCurrAddresses->FirstUnicastAddress; + sprintf_s(ifname, MAX_PATH, "%wS", pCurrAddresses->FriendlyName); + + // MAC address + if (pCurrAddresses->PhysicalAddressLength != 0) { + ptr = buff; + *ptr = '\0'; + for (i = 0; i < (int) pCurrAddresses->PhysicalAddressLength; i++) { + if (i == (pCurrAddresses->PhysicalAddressLength - 1)) { + sprintf_s(ptr, _countof(buff), "%.2X\n", + (int)pCurrAddresses->PhysicalAddress[i]); + } + else { + sprintf_s(ptr, _countof(buff), "%.2X-", + (int)pCurrAddresses->PhysicalAddress[i]); + } + ptr += 3; + } + *--ptr = '\0'; + +#if PY_MAJOR_VERSION >= 3 + py_mac_address = PyUnicode_FromString(buff); +#else + py_mac_address = PyString_FromString(buff); +#endif + if (py_mac_address == NULL) + goto error; + + Py_INCREF(Py_None); + Py_INCREF(Py_None); + py_tuple = Py_BuildValue( + "(siOOO)", + ifname, + -1, // this will be converted later to AF_LINK + py_mac_address, + Py_None, + Py_None + ); + if (! py_tuple) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_DECREF(py_tuple); + Py_DECREF(py_mac_address); + } + + // find out the IP address associated with the NIC + if (pUnicast != NULL) { + for (i = 0; pUnicast != NULL; i++) { + family = pUnicast->Address.lpSockaddr->sa_family; + if (family == AF_INET) { + struct sockaddr_in *sa_in = (struct sockaddr_in *) + pUnicast->Address.lpSockaddr; + intRet = inet_ntop(AF_INET, &(sa_in->sin_addr), buff, + bufflen); + } + else if (family == AF_INET6) { + struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *) + pUnicast->Address.lpSockaddr; + intRet = inet_ntop(AF_INET6, &(sa_in6->sin6_addr), + buff, bufflen); + } + else { + // we should never get here + pUnicast = pUnicast->Next; + continue; + } + + if (intRet == NULL) { + PyErr_SetFromWindowsErr(GetLastError()); + goto error; + } +#if PY_MAJOR_VERSION >= 3 + py_address = PyUnicode_FromString(buff); +#else + py_address = PyString_FromString(buff); +#endif + if (py_address == NULL) + goto error; + + Py_INCREF(Py_None); + Py_INCREF(Py_None); + py_tuple = Py_BuildValue( + "(siOOO)", + ifname, + family, + py_address, + Py_None, + Py_None + ); + + if (! py_tuple) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_DECREF(py_tuple); + Py_DECREF(py_address); + + pUnicast = pUnicast->Next; + } + } + + pCurrAddresses = pCurrAddresses->Next; + } + + free(pAddresses); + return py_retlist; + +error: + if (pAddresses) + free(pAddresses); + Py_DECREF(py_retlist); + Py_XDECREF(py_tuple); + Py_XDECREF(py_address); + return NULL; +} + + +/* + * Provides stats about NIC interfaces installed on the system. + * TODO: get 'duplex' (currently it's hard coded to '2', aka + 'full duplex') + */ +static PyObject * +psutil_net_if_stats(PyObject *self, PyObject *args) +{ + int i; + DWORD dwSize = 0; + DWORD dwRetVal = 0; + MIB_IFTABLE *pIfTable; + MIB_IFROW *pIfRow; + PIP_ADAPTER_ADDRESSES pAddresses = NULL; + PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; + char friendly_name[MAX_PATH]; + char descr[MAX_PATH]; + int ifname_found; + + PyObject *py_retdict = PyDict_New(); + PyObject *py_ifc_info = NULL; + PyObject *py_is_up = NULL; + + if (py_retdict == NULL) + return NULL; + + pAddresses = psutil_get_nic_addresses(); + if (pAddresses == NULL) + goto error; + + pIfTable = (MIB_IFTABLE *) malloc(sizeof (MIB_IFTABLE)); + if (pIfTable == NULL) { + PyErr_NoMemory(); + goto error; + } + dwSize = sizeof(MIB_IFTABLE); + if (GetIfTable(pIfTable, &dwSize, FALSE) == ERROR_INSUFFICIENT_BUFFER) { + free(pIfTable); + pIfTable = (MIB_IFTABLE *) malloc(dwSize); + if (pIfTable == NULL) { + PyErr_NoMemory(); + goto error; + } + } + // Make a second call to GetIfTable to get the actual + // data we want. + if ((dwRetVal = GetIfTable(pIfTable, &dwSize, FALSE)) != NO_ERROR) { + PyErr_SetString(PyExc_RuntimeError, "GetIfTable() failed"); + goto error; + } + + for (i = 0; i < (int) pIfTable->dwNumEntries; i++) { + pIfRow = (MIB_IFROW *) & pIfTable->table[i]; + + // GetIfTable is not able to give us NIC with "friendly names" + // so we determine them via GetAdapterAddresses() which + // provides friendly names *and* descriptions and find the + // ones that match. + ifname_found = 0; + pCurrAddresses = pAddresses; + while (pCurrAddresses) { + sprintf_s(descr, MAX_PATH, "%wS", pCurrAddresses->Description); + if (lstrcmp(descr, pIfRow->bDescr) == 0) { + sprintf_s(friendly_name, MAX_PATH, "%wS", pCurrAddresses->FriendlyName); + ifname_found = 1; + break; + } + pCurrAddresses = pCurrAddresses->Next; + } + if (ifname_found == 0) { + // Name not found means GetAdapterAddresses() doesn't list + // this NIC, only GetIfTable, meaning it's not really a NIC + // interface so we skip it. + continue; + } + + // is up? + if((pIfRow->dwOperStatus == MIB_IF_OPER_STATUS_CONNECTED || + pIfRow->dwOperStatus == MIB_IF_OPER_STATUS_OPERATIONAL) && + pIfRow->dwAdminStatus == 1 ) { + py_is_up = Py_True; + } + else { + py_is_up = Py_False; + } + Py_INCREF(py_is_up); + + py_ifc_info = Py_BuildValue( + "(Oikk)", + py_is_up, + 2, // there's no way to know duplex so let's assume 'full' + pIfRow->dwSpeed / 1000000, // expressed in bytes, we want Mb + pIfRow->dwMtu + ); + if (!py_ifc_info) + goto error; + if (PyDict_SetItemString(py_retdict, friendly_name, py_ifc_info)) + goto error; + Py_DECREF(py_ifc_info); + } + + free(pIfTable); + free(pAddresses); + return py_retdict; + +error: + Py_XDECREF(py_is_up); + Py_XDECREF(py_ifc_info); + Py_DECREF(py_retdict); + if (pIfTable != NULL) + free(pIfTable); + if (pAddresses != NULL) + free(pAddresses); + return NULL; +} + + // ------------------------ Python init --------------------------- static PyMethodDef @@ -3011,6 +3184,8 @@ PsutilMethods[] = "Return process cmdline as a list of cmdline arguments"}, {"proc_exe", psutil_proc_exe, METH_VARARGS, "Return path of the process executable"}, + {"proc_name", psutil_proc_name, METH_VARARGS, + "Return process name"}, {"proc_kill", psutil_proc_kill, METH_VARARGS, "Kill the process identified by the given PID"}, {"proc_cpu_times", psutil_proc_cpu_times, METH_VARARGS, @@ -3020,6 +3195,8 @@ PsutilMethods[] = "seconds since the epoch"}, {"proc_memory_info", psutil_proc_memory_info, METH_VARARGS, "Return a tuple of process memory information"}, + {"proc_memory_info_2", psutil_proc_memory_info_2, METH_VARARGS, + "Alternate implementation"}, {"proc_cwd", psutil_proc_cwd, METH_VARARGS, "Return process current working directory"}, {"proc_suspend", psutil_proc_suspend, METH_VARARGS, @@ -3030,8 +3207,6 @@ PsutilMethods[] = "Return files opened by process"}, {"proc_username", psutil_proc_username, METH_VARARGS, "Return the username of a process"}, - {"proc_num_threads", psutil_proc_num_threads, METH_VARARGS, - "Return the network connections of a process"}, {"proc_threads", psutil_proc_threads, METH_VARARGS, "Return process threads information as a list of tuple"}, {"proc_wait", psutil_proc_wait, METH_VARARGS, @@ -3056,22 +3231,12 @@ PsutilMethods[] = "Return True if one of the process threads is in a suspended state"}, {"proc_num_handles", psutil_proc_num_handles, METH_VARARGS, "Return the number of handles opened by process."}, - {"proc_num_ctx_switches", psutil_proc_num_ctx_switches, METH_VARARGS, - "Return the number of context switches performed by process."}, {"proc_memory_maps", psutil_proc_memory_maps, METH_VARARGS, "Return a list of process's memory mappings"}, // --- alternative pinfo interface - {"proc_cpu_times_2", psutil_proc_cpu_times_2, METH_VARARGS, - "Alternative implementation"}, - {"proc_create_time_2", psutil_proc_create_time_2, METH_VARARGS, - "Alternative implementation"}, - {"proc_num_handles_2", psutil_proc_num_handles_2, METH_VARARGS, - "Alternative implementation"}, - {"proc_io_counters_2", psutil_proc_io_counters_2, METH_VARARGS, - "Alternative implementation"}, - {"proc_memory_info_2", psutil_proc_memory_info_2, METH_VARARGS, - "Alternative implementation"}, + {"proc_info", psutil_proc_info, METH_VARARGS, + "Various process information"}, // --- system-related functions {"pids", psutil_pids, METH_VARARGS, @@ -3104,7 +3269,10 @@ PsutilMethods[] = "Return disk partitions."}, {"net_connections", psutil_net_connections, METH_VARARGS, "Return system-wide connections"}, - + {"net_if_addrs", psutil_net_if_addrs, METH_VARARGS, + "Return NICs addresses."}, + {"net_if_stats", psutil_net_if_stats, METH_VARARGS, + "Return NICs stats."}, // --- windows API bindings {"win32_QueryDosDevice", psutil_win32_QueryDosDevice, METH_VARARGS, @@ -3176,6 +3344,8 @@ void init_psutil_windows(void) INITERROR; } + PyModule_AddIntConstant(module, "version", PSUTIL_VERSION); + // process status constants // http://msdn.microsoft.com/en-us/library/ms683211(v=vs.85).aspx PyModule_AddIntConstant( diff --git a/python/psutil/psutil/_psutil_windows.h b/python/psutil/psutil/_psutil_windows.h index 546704e3cc9d..c77f64e9c330 100644 --- a/python/psutil/psutil/_psutil_windows.h +++ b/python/psutil/psutil/_psutil_windows.h @@ -13,22 +13,18 @@ static PyObject* psutil_proc_cmdline(PyObject* self, PyObject* args); static PyObject* psutil_proc_cpu_affinity_get(PyObject* self, PyObject* args); static PyObject* psutil_proc_cpu_affinity_set(PyObject* self, PyObject* args); static PyObject* psutil_proc_cpu_times(PyObject* self, PyObject* args); -static PyObject* psutil_proc_cpu_times_2(PyObject* self, PyObject* args); static PyObject* psutil_proc_create_time(PyObject* self, PyObject* args); -static PyObject* psutil_proc_create_time_2(PyObject* self, PyObject* args); static PyObject* psutil_proc_cwd(PyObject* self, PyObject* args); static PyObject* psutil_proc_exe(PyObject* self, PyObject* args); +static PyObject* psutil_proc_info(PyObject* self, PyObject* args); static PyObject* psutil_proc_io_counters(PyObject* self, PyObject* args); -static PyObject* psutil_proc_io_counters_2(PyObject* self, PyObject* args); static PyObject* psutil_proc_is_suspended(PyObject* self, PyObject* args); static PyObject* psutil_proc_kill(PyObject* self, PyObject* args); static PyObject* psutil_proc_memory_info(PyObject* self, PyObject* args); static PyObject* psutil_proc_memory_info_2(PyObject* self, PyObject* args); static PyObject* psutil_proc_memory_maps(PyObject* self, PyObject* args); -static PyObject* psutil_proc_num_ctx_switches(PyObject* self, PyObject* args); +static PyObject* psutil_proc_name(PyObject* self, PyObject* args); static PyObject* psutil_proc_num_handles(PyObject* self, PyObject* args); -static PyObject* psutil_proc_num_handles_2(PyObject* self, PyObject* args); -static PyObject* psutil_proc_num_threads(PyObject* self, PyObject* args); static PyObject* psutil_proc_open_files(PyObject* self, PyObject* args); static PyObject* psutil_proc_priority_get(PyObject* self, PyObject* args); static PyObject* psutil_proc_priority_set(PyObject* self, PyObject* args); @@ -60,6 +56,8 @@ static PyObject* psutil_pids(PyObject* self, PyObject* args); static PyObject* psutil_ppid_map(PyObject* self, PyObject* args); static PyObject* psutil_users(PyObject* self, PyObject* args); static PyObject* psutil_virtual_mem(PyObject* self, PyObject* args); +static PyObject* psutil_net_if_addrs(PyObject* self, PyObject* args); +static PyObject* psutil_net_if_stats(PyObject* self, PyObject* args); // --- windows API bindings diff --git a/python/psutil/psutil/_pswindows.py b/python/psutil/psutil/_pswindows.py index b540bdac2b2a..2d8babb19cfe 100644 --- a/python/psutil/psutil/_pswindows.py +++ b/python/psutil/psutil/_pswindows.py @@ -7,21 +7,35 @@ """Windows platform implementation.""" import errno +import functools import os import sys - -from psutil import _common -from psutil._common import conn_tmap, usage_percent, isfile_strict -from psutil._compat import PY3, xrange, wraps, lru_cache, namedtuple -import _psutil_windows as cext +from collections import namedtuple + +from . import _common +from . import _psutil_windows as cext +from ._common import conn_tmap, usage_percent, isfile_strict +from ._common import sockfam_to_enum, socktype_to_enum +from ._compat import PY3, xrange, lru_cache, long +from ._psutil_windows import (ABOVE_NORMAL_PRIORITY_CLASS, + BELOW_NORMAL_PRIORITY_CLASS, + HIGH_PRIORITY_CLASS, + IDLE_PRIORITY_CLASS, + NORMAL_PRIORITY_CLASS, + REALTIME_PRIORITY_CLASS) + +if sys.version_info >= (3, 4): + import enum +else: + enum = None # process priority constants, import from __init__.py: # http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx __extra__all__ = ["ABOVE_NORMAL_PRIORITY_CLASS", "BELOW_NORMAL_PRIORITY_CLASS", "HIGH_PRIORITY_CLASS", "IDLE_PRIORITY_CLASS", "NORMAL_PRIORITY_CLASS", "REALTIME_PRIORITY_CLASS", - # "CONN_DELETE_TCB", + "AF_LINK", ] # --- module level constants (gets pushed up to psutil module) @@ -30,6 +44,11 @@ WAIT_TIMEOUT = 0x00000102 # 258 in decimal ACCESS_DENIED_SET = frozenset([errno.EPERM, errno.EACCES, cext.ERROR_ACCESS_DENIED]) +if enum is None: + AF_LINK = -1 +else: + AddressFamily = enum.IntEnum('AddressFamily', {'AF_LINK': -1}) + AF_LINK = AddressFamily.AF_LINK TCP_STATUSES = { cext.MIB_TCP_STATE_ESTAB: _common.CONN_ESTABLISHED, @@ -47,6 +66,16 @@ cext.PSUTIL_CONN_NONE: _common.CONN_NONE, } +if enum is not None: + class Priority(enum.IntEnum): + ABOVE_NORMAL_PRIORITY_CLASS = ABOVE_NORMAL_PRIORITY_CLASS + BELOW_NORMAL_PRIORITY_CLASS = BELOW_NORMAL_PRIORITY_CLASS + HIGH_PRIORITY_CLASS = HIGH_PRIORITY_CLASS + IDLE_PRIORITY_CLASS = IDLE_PRIORITY_CLASS + NORMAL_PRIORITY_CLASS = NORMAL_PRIORITY_CLASS + REALTIME_PRIORITY_CLASS = REALTIME_PRIORITY_CLASS + + globals().update(Priority.__members__) scputimes = namedtuple('scputimes', ['user', 'system', 'idle']) svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free']) @@ -57,6 +86,10 @@ pmmap_grouped = namedtuple('pmmap_grouped', ['path', 'rss']) pmmap_ext = namedtuple( 'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields)) +ntpinfo = namedtuple( + 'ntpinfo', ['num_handles', 'ctx_switches', 'user_time', 'kernel_time', + 'create_time', 'num_threads', 'io_rcount', 'io_wcount', + 'io_rbytes', 'io_wbytes']) # set later from __init__.py NoSuchProcess = None @@ -166,15 +199,27 @@ def net_connections(kind, _pid=-1): % (kind, ', '.join([repr(x) for x in conn_tmap]))) families, types = conn_tmap[kind] rawlist = cext.net_connections(_pid, families, types) - ret = [] + ret = set() for item in rawlist: fd, fam, type, laddr, raddr, status, pid = item status = TCP_STATUSES[status] + fam = sockfam_to_enum(fam) + type = socktype_to_enum(type) if _pid == -1: nt = _common.sconn(fd, fam, type, laddr, raddr, status, pid) else: nt = _common.pconn(fd, fam, type, laddr, raddr, status) - ret.append(nt) + ret.add(nt) + return list(ret) + + +def net_if_stats(): + ret = cext.net_if_stats() + for name, items in ret.items(): + isup, duplex, speed, mtu = items + if hasattr(_common, 'NicDuplex'): + duplex = _common.NicDuplex(duplex) + ret[name] = _common.snicstats(isup, duplex, speed, mtu) return ret @@ -194,21 +239,21 @@ def users(): net_io_counters = cext.net_io_counters disk_io_counters = cext.disk_io_counters ppid_map = cext.ppid_map # not meant to be public +net_if_addrs = cext.net_if_addrs def wrap_exceptions(fun): """Decorator which translates bare OSError and WindowsError exceptions into NoSuchProcess and AccessDenied. """ - @wraps(fun) + @functools.wraps(fun) def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except OSError: + except OSError as err: # support for private module import if NoSuchProcess is None or AccessDenied is None: raise - err = sys.exc_info()[1] if err.errno in ACCESS_DENIED_SET: raise AccessDenied(self.pid, self._name) if err.errno == errno.ESRCH: @@ -220,11 +265,12 @@ def wrapper(self, *args, **kwargs): class Process(object): """Wrapper class around underlying C implementation.""" - __slots__ = ["pid", "_name"] + __slots__ = ["pid", "_name", "_ppid"] def __init__(self, pid): self.pid = pid self._name = None + self._ppid = None @wrap_exceptions def name(self): @@ -238,7 +284,12 @@ def name(self): elif self.pid == 4: return "System" else: - return os.path.basename(self.exe()) + try: + # Note: this will fail with AD for most PIDs owned + # by another user but it's faster. + return os.path.basename(self.exe()) + except AccessDenied: + return cext.proc_name(self.pid) @wrap_exceptions def exe(self): @@ -265,9 +316,10 @@ def ppid(self): def _get_raw_meminfo(self): try: return cext.proc_memory_info(self.pid) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno in ACCESS_DENIED_SET: + # TODO: the C ext can probably be refactored in order + # to get this from cext.proc_info() return cext.proc_memory_info_2(self.pid) raise @@ -287,10 +339,9 @@ def memory_info_ex(self): def memory_maps(self): try: raw = cext.proc_memory_maps(self.pid) - except OSError: + except OSError as err: # XXX - can't use wrap_exceptions decorator as we're # returning a generator; probably needs refactoring. - err = sys.exc_info()[1] if err.errno in ACCESS_DENIED_SET: raise AccessDenied(self.pid, self._name) if err.errno == errno.ESRCH: @@ -334,15 +385,14 @@ def create_time(self): return boot_time() try: return cext.proc_create_time(self.pid) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno in ACCESS_DENIED_SET: - return cext.proc_create_time_2(self.pid) + return ntpinfo(*cext.proc_info(self.pid)).create_time raise @wrap_exceptions def num_threads(self): - return cext.proc_num_threads(self.pid) + return ntpinfo(*cext.proc_info(self.pid)).num_threads @wrap_exceptions def threads(self): @@ -357,10 +407,10 @@ def threads(self): def cpu_times(self): try: ret = cext.proc_cpu_times(self.pid) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno in ACCESS_DENIED_SET: - ret = cext.proc_cpu_times_2(self.pid) + nt = ntpinfo(*cext.proc_info(self.pid)) + ret = (nt.user_time, nt.kernel_time) else: raise return _common.pcputimes(*ret) @@ -392,10 +442,10 @@ def open_files(self): # Convert the first part in the corresponding drive letter # (e.g. "C:\") by using Windows's QueryDosDevice() raw_file_names = cext.proc_open_files(self.pid) - for file in raw_file_names: - file = _convert_raw_path(file) - if isfile_strict(file) and file not in retlist: - ntuple = _common.popenfile(file, -1) + for _file in raw_file_names: + _file = _convert_raw_path(_file) + if isfile_strict(_file) and _file not in retlist: + ntuple = _common.popenfile(_file, -1) retlist.append(ntuple) return retlist @@ -405,7 +455,10 @@ def connections(self, kind='inet'): @wrap_exceptions def nice_get(self): - return cext.proc_priority_get(self.pid) + value = cext.proc_priority_get(self.pid) + if enum is not None: + value = Priority(value) + return value @wrap_exceptions def nice_set(self, value): @@ -431,10 +484,10 @@ def ionice_set(self, value, _): def io_counters(self): try: ret = cext.proc_io_counters(self.pid) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno in ACCESS_DENIED_SET: - ret = cext.proc_io_counters_2(self.pid) + nt = ntpinfo(*cext.proc_info(self.pid)) + ret = (nt.io_rcount, nt.io_wcount, nt.io_rbytes, nt.io_wbytes) else: raise return _common.pio(*ret) @@ -449,7 +502,8 @@ def status(self): @wrap_exceptions def cpu_affinity_get(self): - from_bitmask = lambda x: [i for i in xrange(64) if (1 << i) & x] + def from_bitmask(x): + return [i for i in xrange(64) if (1 << i) & x] bitmask = cext.proc_cpu_affinity_get(self.pid) return from_bitmask(bitmask) @@ -469,7 +523,11 @@ def to_bitmask(l): allcpus = list(range(len(per_cpu_times()))) for cpu in value: if cpu not in allcpus: - raise ValueError("invalid CPU %r" % cpu) + if not isinstance(cpu, (int, long)): + raise TypeError( + "invalid CPU %r; an integer is required" % cpu) + else: + raise ValueError("invalid CPU %r" % cpu) bitmask = to_bitmask(value) cext.proc_cpu_affinity_set(self.pid, bitmask) @@ -478,13 +536,13 @@ def to_bitmask(l): def num_handles(self): try: return cext.proc_num_handles(self.pid) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno in ACCESS_DENIED_SET: - return cext.proc_num_handles_2(self.pid) + return ntpinfo(*cext.proc_info(self.pid)).num_handles raise @wrap_exceptions def num_ctx_switches(self): - tupl = cext.proc_num_ctx_switches(self.pid) - return _common.pctxsw(*tupl) + ctx_switches = ntpinfo(*cext.proc_info(self.pid)).ctx_switches + # only voluntary ctx switches are supported + return _common.pctxsw(ctx_switches, 0) diff --git a/python/psutil/psutil/arch/bsd/process_info.c b/python/psutil/psutil/arch/bsd/process_info.c index 1c19556633a1..4d739240617f 100644 --- a/python/psutil/psutil/arch/bsd/process_info.c +++ b/python/psutil/psutil/arch/bsd/process_info.c @@ -128,9 +128,8 @@ char mib[3] = pid; // call with a null buffer first to determine if we need a buffer - if (sysctl(mib, 4, NULL, &size, NULL, 0) == -1) { + if (sysctl(mib, 4, NULL, &size, NULL, 0) == -1) return NULL; - } path = malloc(size); if (path == NULL) { @@ -213,14 +212,11 @@ psutil_get_arg_list(long pid) PyObject *retlist = Py_BuildValue("[]"); PyObject *item = NULL; - if (pid < 0) { + if (pid < 0) return retlist; - } - argstr = psutil_get_cmd_args(pid, &argsize); - if (argstr == NULL) { + if (argstr == NULL) goto error; - } // args are returned as a flattened string with \0 separators between // arguments add each string to the list then step forward to the next @@ -256,30 +252,14 @@ int psutil_pid_exists(long pid) { int kill_ret; - if (pid < 0) { - return 0; - } + if (pid < 0) + return 0; // if kill returns success of permission denied we know it's a valid PID kill_ret = kill(pid , 0); - if ((0 == kill_ret) || (EPERM == errno)) { + if ((0 == kill_ret) || (EPERM == errno)) return 1; - } - // otherwise return 0 for PID not found return 0; } - -/* - * Set exception to AccessDenied if pid exists else NoSuchProcess. - */ -int -psutil_raise_ad_or_nsp(pid) { - if (psutil_pid_exists(pid) == 0) { - NoSuchProcess(); - } - else { - AccessDenied(); - } -} diff --git a/python/psutil/psutil/arch/osx/process_info.c b/python/psutil/psutil/arch/osx/process_info.c index be8092efe78b..b6dd5bb93860 100644 --- a/python/psutil/psutil/arch/osx/process_info.c +++ b/python/psutil/psutil/arch/osx/process_info.c @@ -32,15 +32,12 @@ psutil_pid_exists(long pid) int kill_ret; // save some time if it's an invalid PID - if (pid < 0) { + if (pid < 0) return 0; - } - // if kill returns success of permission denied we know it's a valid PID kill_ret = kill(pid , 0); - if ( (0 == kill_ret) || (EPERM == errno) ) { + if ( (0 == kill_ret) || (EPERM == errno)) return 1; - } // otherwise return 0 for PID not found return 0; @@ -85,34 +82,29 @@ psutil_get_proc_list(kinfo_proc **procList, size_t *procCount) */ while (lim-- > 0) { size = 0; - if (sysctl((int *)mib3, 3, NULL, &size, NULL, 0) == -1) { + if (sysctl((int *)mib3, 3, NULL, &size, NULL, 0) == -1) return errno; - } - size2 = size + (size >> 3); // add some if (size2 > size) { ptr = malloc(size2); - if (ptr == NULL) { + if (ptr == NULL) ptr = malloc(size); - } else { + else size = size2; - } } else { ptr = malloc(size); } - if (ptr == NULL) { + if (ptr == NULL) return ENOMEM; - } if (sysctl((int *)mib3, 3, ptr, &size, NULL, 0) == -1) { err = errno; free(ptr); - if (err != ENOMEM) { + if (err != ENOMEM) return err; - } - - } else { + } + else { *procList = (kinfo_proc *)ptr; *procCount = size / sizeof(kinfo_proc); return 0; @@ -130,9 +122,8 @@ psutil_get_argmax() int mib[] = { CTL_KERN, KERN_ARGMAX }; size_t size = sizeof(argmax); - if (sysctl(mib, 2, &argmax, &size, NULL, 0) == 0) { + if (sysctl(mib, 2, &argmax, &size, NULL, 0) == 0) return argmax; - } return 0; } @@ -153,9 +144,8 @@ psutil_get_arg_list(long pid) PyObject *arglist = NULL; // special case for PID 0 (kernel_task) where cmdline cannot be fetched - if (pid == 0) { + if (pid == 0) return Py_BuildValue("[]"); - } // read argmax and allocate memory for argument space. argmax = psutil_get_argmax(); @@ -177,11 +167,10 @@ psutil_get_arg_list(long pid) if (sysctl(mib, 3, procargs, &argmax, NULL, 0) < 0) { if (EINVAL == errno) { // EINVAL == access denied OR nonexistent PID - if ( psutil_pid_exists(pid) ) { + if (psutil_pid_exists(pid)) AccessDenied(); - } else { + else NoSuchProcess(); - } } goto error; } @@ -201,9 +190,8 @@ psutil_get_arg_list(long pid) // skip ahead to the first argument for (; arg_ptr < arg_end; arg_ptr++) { - if (*arg_ptr != '\0') { + if (*arg_ptr != '\0') break; - } } // iterate through arguments diff --git a/python/psutil/psutil/arch/windows/inet_ntop.c b/python/psutil/psutil/arch/windows/inet_ntop.c new file mode 100644 index 000000000000..b9fffd1c1636 --- /dev/null +++ b/python/psutil/psutil/arch/windows/inet_ntop.c @@ -0,0 +1,41 @@ +#include "inet_ntop.h" + +// From: https://memset.wordpress.com/2010/10/09/inet_ntop-for-win32/ +PCSTR +WSAAPI +inet_ntop( + __in INT Family, + __in PVOID pAddr, + __out_ecount(StringBufSize) PSTR pStringBuf, + __in size_t StringBufSize + ) +{ + DWORD dwAddressLength = 0; + struct sockaddr_storage srcaddr; + struct sockaddr_in *srcaddr4 = (struct sockaddr_in*) &srcaddr; + struct sockaddr_in6 *srcaddr6 = (struct sockaddr_in6*) &srcaddr; + + memset(&srcaddr, 0, sizeof(struct sockaddr_storage)); + srcaddr.ss_family = Family; + + if (Family == AF_INET) + { + dwAddressLength = sizeof(struct sockaddr_in); + memcpy(&(srcaddr4->sin_addr), pAddr, sizeof(struct in_addr)); + } else if (Family == AF_INET6) + { + dwAddressLength = sizeof(struct sockaddr_in6); + memcpy(&(srcaddr6->sin6_addr), pAddr, sizeof(struct in6_addr)); + } else { + return NULL; + } + + if (WSAAddressToString((LPSOCKADDR) &srcaddr, + dwAddressLength, + 0, + pStringBuf, + (LPDWORD) &StringBufSize) != 0) { + return NULL; + } + return pStringBuf; +} \ No newline at end of file diff --git a/python/psutil/psutil/arch/windows/inet_ntop.h b/python/psutil/psutil/arch/windows/inet_ntop.h new file mode 100644 index 000000000000..0d97e28c88bf --- /dev/null +++ b/python/psutil/psutil/arch/windows/inet_ntop.h @@ -0,0 +1,10 @@ +#include + +PCSTR +WSAAPI +inet_ntop( + __in INT Family, + __in PVOID pAddr, + __out_ecount(StringBufSize) PSTR pStringBuf, + __in size_t StringBufSize +); \ No newline at end of file diff --git a/python/psutil/psutil/arch/windows/ntextapi.h b/python/psutil/psutil/arch/windows/ntextapi.h index 298c078004f7..d10432a3e3bf 100644 --- a/python/psutil/psutil/arch/windows/ntextapi.h +++ b/python/psutil/psutil/arch/windows/ntextapi.h @@ -3,6 +3,9 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ +#if !defined(__NTEXTAPI_H__) +#define __NTEXTAPI_H__ +#include typedef enum _KTHREAD_STATE { Initialized, @@ -63,23 +66,6 @@ typedef struct _CLIENT_ID { HANDLE UniqueThread; } CLIENT_ID, *PCLIENT_ID; - -typedef struct _UNICODE_STRING { - USHORT Length; - USHORT MaximumLength; - PWSTR Buffer; -} UNICODE_STRING, *PUNICODE_STRING; - -typedef struct _SYSTEM_TIMEOFDAY_INFORMATION { - LARGE_INTEGER BootTime; - LARGE_INTEGER CurrentTime; - LARGE_INTEGER TimeZoneBias; - ULONG TimeZoneId; - ULONG Reserved; - ULONGLONG BootTimeBias; - ULONGLONG SleepTimeBias; -} SYSTEM_TIMEOFDAY_INFORMATION, *PSYSTEM_TIMEOFDAY_INFORMATION; - typedef struct _SYSTEM_THREAD_INFORMATION { LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; @@ -108,7 +94,7 @@ typedef struct _SYSTEM_EXTENDED_THREAD_INFORMATION { ULONG_PTR Reserved4; } SYSTEM_EXTENDED_THREAD_INFORMATION, *PSYSTEM_EXTENDED_THREAD_INFORMATION; -typedef struct _SYSTEM_PROCESS_INFORMATION { +typedef struct _SYSTEM_PROCESS_INFORMATION2 { ULONG NextEntryOffset; ULONG NumberOfThreads; LARGE_INTEGER SpareLi1; @@ -143,31 +129,10 @@ typedef struct _SYSTEM_PROCESS_INFORMATION { LARGE_INTEGER WriteTransferCount; LARGE_INTEGER OtherTransferCount; SYSTEM_THREAD_INFORMATION Threads[1]; -} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION; - - -// structures and enums from winternl.h (not available under mingw) -typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION { - LARGE_INTEGER IdleTime; - LARGE_INTEGER KernelTime; - LARGE_INTEGER UserTime; - LARGE_INTEGER Reserved1[2]; - ULONG Reserved2; -} SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION, - *PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; - +} SYSTEM_PROCESS_INFORMATION2, *PSYSTEM_PROCESS_INFORMATION2; -typedef enum _SYSTEM_INFORMATION_CLASS { - SystemBasicInformation = 0, - SystemPerformanceInformation = 2, - SystemTimeOfDayInformation = 3, - SystemProcessInformation = 5, - SystemProcessorPerformanceInformation = 8, - SystemInterruptInformation = 23, - SystemExceptionInformation = 33, - SystemRegistryQuotaInformation = 37, - SystemLookasideInformation = 45 -} SYSTEM_INFORMATION_CLASS; +#define SYSTEM_PROCESS_INFORMATION SYSTEM_PROCESS_INFORMATION2 +#define PSYSTEM_PROCESS_INFORMATION PSYSTEM_PROCESS_INFORMATION2 // ================================================ @@ -186,32 +151,9 @@ typedef struct _WINSTATION_INFO { FILETIME CurrentTime; } WINSTATION_INFO, *PWINSTATION_INFO; -typedef enum _WINSTATIONINFOCLASS { - WinStationInformation = 8 -} WINSTATIONINFOCLASS; - typedef BOOLEAN (WINAPI * PWINSTATIONQUERYINFORMATIONW) (HANDLE,ULONG,WINSTATIONINFOCLASS,PVOID,ULONG,PULONG); -typedef struct _WINSTATIONINFORMATIONW { - BYTE Reserved2[70]; - ULONG LogonId; - BYTE Reserved3[1140]; -} WINSTATIONINFORMATIONW, *PWINSTATIONINFORMATIONW; - -// mingw support: -// http://www.koders.com/c/fid7C02CAE627C526914CDEB427405B51DF393A5EFA.aspx -#ifndef _INC_WTSAPI -typedef struct _WTS_CLIENT_ADDRESS { - DWORD AddressFamily; // AF_INET, AF_IPX, AF_NETBIOS, AF_UNSPEC - BYTE Address[20]; // client network address -} WTS_CLIENT_ADDRESS, * PWTS_CLIENT_ADDRESS; - -HANDLE WINAPI WTSOpenServerA(IN LPSTR pServerName); - -VOID WINAPI WTSCloseServer(IN HANDLE hServer); -#endif - /* * NtQueryInformationProcess code taken from @@ -235,16 +177,9 @@ typedef NTSTATUS (NTAPI *_NtSetInformationProcess)( DWORD ProcessInformationLength ); -typedef struct _PROCESS_BASIC_INFORMATION { - PVOID Reserved1; - PVOID PebBaseAddress; - PVOID Reserved2[2]; - ULONG_PTR UniqueProcessId; - PVOID Reserved3; -} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION; -typedef enum _PROCESSINFOCLASS { - ProcessBasicInformation, +typedef enum _PROCESSINFOCLASS2 { + _ProcessBasicInformation, ProcessQuotaLimits, ProcessIoCounters, ProcessVmCounters, @@ -270,7 +205,7 @@ typedef enum _PROCESSINFOCLASS { ProcessDeviceMap, ProcessSessionInformation, ProcessForegroundInformation, - ProcessWow64Information, + _ProcessWow64Information, /* added after XP+ */ ProcessImageFileName, ProcessLUIDDeviceMapsEnabled, @@ -284,4 +219,10 @@ typedef enum _PROCESSINFOCLASS { ProcessCookie, ProcessImageInformation, MaxProcessInfoClass -} PROCESSINFOCLASS; +} PROCESSINFOCLASS2; + +#define PROCESSINFOCLASS PROCESSINFOCLASS2 +#define ProcessBasicInformation _ProcessBasicInformation +#define ProcessWow64Information _ProcessWow64Information + +#endif // __NTEXTAPI_H__ diff --git a/python/psutil/psutil/arch/windows/process_handles.c b/python/psutil/psutil/arch/windows/process_handles.c index f7479e994a33..b3f480af54da 100644 --- a/python/psutil/psutil/arch/windows/process_handles.c +++ b/python/psutil/psutil/arch/windows/process_handles.c @@ -4,108 +4,22 @@ * found in the LICENSE file. * */ - -#ifndef UNICODE -#define UNICODE -#endif - -#include -#include -#include #include "process_handles.h" -#ifndef NT_SUCCESS -#define NT_SUCCESS(x) ((x) >= 0) -#endif -#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004 - -#define SystemHandleInformation 16 -#define ObjectBasicInformation 0 -#define ObjectNameInformation 1 -#define ObjectTypeInformation 2 - - -typedef LONG NTSTATUS; - -typedef struct _UNICODE_STRING { - USHORT Length; - USHORT MaximumLength; - PWSTR Buffer; -} UNICODE_STRING, *PUNICODE_STRING; - -typedef NTSTATUS (NTAPI *_NtQuerySystemInformation)( - ULONG SystemInformationClass, - PVOID SystemInformation, - ULONG SystemInformationLength, - PULONG ReturnLength -); - -typedef NTSTATUS (NTAPI *_NtDuplicateObject)( - HANDLE SourceProcessHandle, - HANDLE SourceHandle, - HANDLE TargetProcessHandle, - PHANDLE TargetHandle, - ACCESS_MASK DesiredAccess, - ULONG Attributes, - ULONG Options -); - -typedef NTSTATUS (NTAPI *_NtQueryObject)( - HANDLE ObjectHandle, - ULONG ObjectInformationClass, - PVOID ObjectInformation, - ULONG ObjectInformationLength, - PULONG ReturnLength -); - -typedef struct _SYSTEM_HANDLE { - ULONG ProcessId; - BYTE ObjectTypeNumber; - BYTE Flags; - USHORT Handle; - PVOID Object; - ACCESS_MASK GrantedAccess; -} SYSTEM_HANDLE, *PSYSTEM_HANDLE; - -typedef struct _SYSTEM_HANDLE_INFORMATION { - ULONG HandleCount; - SYSTEM_HANDLE Handles[1]; -} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; - -typedef enum _POOL_TYPE { - NonPagedPool, - PagedPool, - NonPagedPoolMustSucceed, - DontUseThisType, - NonPagedPoolCacheAligned, - PagedPoolCacheAligned, - NonPagedPoolCacheAlignedMustS -} POOL_TYPE, *PPOOL_TYPE; - -typedef struct _OBJECT_TYPE_INFORMATION { - UNICODE_STRING Name; - ULONG TotalNumberOfObjects; - ULONG TotalNumberOfHandles; - ULONG TotalPagedPoolUsage; - ULONG TotalNonPagedPoolUsage; - ULONG TotalNamePoolUsage; - ULONG TotalHandleTableUsage; - ULONG HighWaterNumberOfObjects; - ULONG HighWaterNumberOfHandles; - ULONG HighWaterPagedPoolUsage; - ULONG HighWaterNonPagedPoolUsage; - ULONG HighWaterNamePoolUsage; - ULONG HighWaterHandleTableUsage; - ULONG InvalidAttributes; - GENERIC_MAPPING GenericMapping; - ULONG ValidAccess; - BOOLEAN SecurityRequired; - BOOLEAN MaintainHandleCount; - USHORT MaintainTypeList; - POOL_TYPE PoolType; - ULONG PagedPoolUsage; - ULONG NonPagedPoolUsage; -} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; +static _NtQuerySystemInformation __NtQuerySystemInformation = NULL; +static _NtQueryObject __NtQueryObject = NULL; + +CRITICAL_SECTION g_cs; +BOOL g_initialized = FALSE; +NTSTATUS g_status; +HANDLE g_hFile = NULL; +HANDLE g_hEvtStart = NULL; +HANDLE g_hEvtFinish = NULL; +HANDLE g_hThread = NULL; +PUNICODE_STRING g_pNameBuffer = NULL; +ULONG g_dwSize = 0; +ULONG g_dwLength = 0; +PVOID g_fiber = NULL; PVOID @@ -114,224 +28,506 @@ GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName) return GetProcAddress(GetModuleHandleA(LibraryName), ProcName); } - PyObject * -psutil_get_open_files(long pid, HANDLE processHandle) +psutil_get_open_files(long dwPid, HANDLE hProcess) { - _NtQuerySystemInformation NtQuerySystemInformation = + OSVERSIONINFO osvi; + + ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&osvi); + + // Threaded version only works for Vista+ + if (osvi.dwMajorVersion >= 6) + return psutil_get_open_files_ntqueryobject(dwPid, hProcess); + else + return psutil_get_open_files_getmappedfilename(dwPid, hProcess); +} + +VOID +psutil_get_open_files_init(BOOL threaded) +{ + if (g_initialized == TRUE) + return; + + // Resolve the Windows API calls + __NtQuerySystemInformation = GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation"); - _NtQueryObject NtQueryObject = - GetLibraryProcAddress("ntdll.dll", "NtQueryObject"); - - NTSTATUS status; - PSYSTEM_HANDLE_INFORMATION handleInfo; - ULONG handleInfoSize = 0x10000; - ULONG i; - ULONG fileNameLength; - PyObject *filesList = Py_BuildValue("[]"); - PyObject *arg = NULL; - PyObject *fileFromWchar = NULL; - - if (filesList == NULL) - return NULL; - - handleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(handleInfoSize); - if (handleInfo == NULL) { - Py_DECREF(filesList); - PyErr_NoMemory(); - return NULL; + __NtQueryObject = GetLibraryProcAddress("ntdll.dll", "NtQueryObject"); + + // Create events for signalling work between threads + if (threaded == TRUE) { + g_hEvtStart = CreateEvent(NULL, FALSE, FALSE, NULL); + g_hEvtFinish = CreateEvent(NULL, FALSE, FALSE, NULL); + InitializeCriticalSection(&g_cs); } - // NtQuerySystemInformation won't give us the correct buffer size, - // so we guess by doubling the buffer size. - while ((status = NtQuerySystemInformation( - SystemHandleInformation, - handleInfo, - handleInfoSize, - NULL - )) == STATUS_INFO_LENGTH_MISMATCH) + g_initialized = TRUE; +} + +PyObject * +psutil_get_open_files_ntqueryobject(long dwPid, HANDLE hProcess) +{ + NTSTATUS status; + PSYSTEM_HANDLE_INFORMATION_EX pHandleInfo = NULL; + DWORD dwInfoSize = 0x10000; + DWORD dwRet = 0; + PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX hHandle = NULL; + DWORD i = 0; + BOOLEAN error = FALSE; + PyObject* pyListFiles = NULL; + PyObject* pyFilePath = NULL; + DWORD dwWait = 0; + + if (g_initialized == FALSE) + psutil_get_open_files_init(TRUE); + + // Due to the use of global variables, ensure only 1 call + // to psutil_get_open_files() is running + EnterCriticalSection(&g_cs); + + if (__NtQuerySystemInformation == NULL || + __NtQueryObject == NULL || + g_hEvtStart == NULL || + g_hEvtFinish == NULL) + { - handleInfo = (PSYSTEM_HANDLE_INFORMATION) \ - realloc(handleInfo, handleInfoSize *= 2); + PyErr_SetFromWindowsErr(0); + error = TRUE; + goto cleanup; + } + + // Py_BuildValue raises an exception if NULL is returned + pyListFiles = PyList_New(0); + if (pyListFiles == NULL) { + error = TRUE; + goto cleanup; } + do { + if (pHandleInfo != NULL) { + HeapFree(GetProcessHeap(), 0, pHandleInfo); + pHandleInfo = NULL; + } + + // NtQuerySystemInformation won't give us the correct buffer size, + // so we guess by doubling the buffer size. + dwInfoSize *= 2; + pHandleInfo = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + dwInfoSize); + + if (pHandleInfo == NULL) { + PyErr_NoMemory(); + error = TRUE; + goto cleanup; + } + } while ((status = __NtQuerySystemInformation( + SystemExtendedHandleInformation, + pHandleInfo, + dwInfoSize, + &dwRet)) == STATUS_INFO_LENGTH_MISMATCH); + // NtQuerySystemInformation stopped giving us STATUS_INFO_LENGTH_MISMATCH if (!NT_SUCCESS(status)) { - Py_DECREF(filesList); - free(handleInfo); - return NULL; + PyErr_SetFromWindowsErr(HRESULT_FROM_NT(status)); + error = TRUE; + goto cleanup; } - for (i = 0; i < handleInfo->HandleCount; i++) { - SYSTEM_HANDLE handle = handleInfo->Handles[i]; - HANDLE dupHandle = NULL; - HANDLE mapHandle = NULL; - POBJECT_TYPE_INFORMATION objectTypeInfo = NULL; - PVOID objectNameInfo; - UNICODE_STRING objectName; - ULONG returnLength; - DWORD error = 0; - fileFromWchar = NULL; - arg = NULL; - - // Check if this handle belongs to the PID the user specified. - if (handle.ProcessId != pid) - continue; - - // Skip handles with the following access codes as the next call - // to NtDuplicateObject() or NtQueryObject() might hang forever. - if ((handle.GrantedAccess == 0x0012019f) - || (handle.GrantedAccess == 0x001a019f) - || (handle.GrantedAccess == 0x00120189) - || (handle.GrantedAccess == 0x00100000)) { - continue; - } + for (i = 0; i < pHandleInfo->NumberOfHandles; i++) { + hHandle = &pHandleInfo->Handles[i]; - if (!DuplicateHandle(processHandle, - handle.Handle, + // Check if this hHandle belongs to the PID the user specified. + if (hHandle->UniqueProcessId != (HANDLE)dwPid || + hHandle->ObjectTypeIndex != HANDLE_TYPE_FILE) + goto loop_cleanup; + + if (!DuplicateHandle(hProcess, + hHandle->HandleValue, GetCurrentProcess(), - &dupHandle, + &g_hFile, 0, TRUE, DUPLICATE_SAME_ACCESS)) - { - //printf("[%#x] Error: %d \n", handle.Handle, GetLastError()); - continue; - } - - - mapHandle = CreateFileMapping(dupHandle, - NULL, - PAGE_READONLY, - 0, - 0, - NULL); - if (mapHandle == NULL) { - error = GetLastError(); - if (error == ERROR_INVALID_HANDLE || error == ERROR_BAD_EXE_FORMAT) { - CloseHandle(dupHandle); - //printf("CreateFileMapping Error: %d\n", error); - continue; - } - } - CloseHandle(mapHandle); - - // Query the object type. - objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000); - if (!NT_SUCCESS(NtQueryObject( - dupHandle, - ObjectTypeInformation, - objectTypeInfo, - 0x1000, - NULL - ))) { - free(objectTypeInfo); - CloseHandle(dupHandle); - continue; + /* + printf("[%d] DuplicateHandle (%#x): %#x \n", + dwPid, + hHandle->HandleValue, + GetLastError()); + */ + goto loop_cleanup; } - objectNameInfo = malloc(0x1000); - if (!NT_SUCCESS(NtQueryObject( - dupHandle, - ObjectNameInformation, - objectNameInfo, - 0x1000, - &returnLength - ))) - { - // Reallocate the buffer and try again. - objectNameInfo = realloc(objectNameInfo, returnLength); - if (!NT_SUCCESS(NtQueryObject( - dupHandle, - ObjectNameInformation, - objectNameInfo, - returnLength, - NULL - ))) - { - // We have the type name, so just display that. + // Guess buffer size is MAX_PATH + 1 + g_dwLength = (MAX_PATH+1) * sizeof(WCHAR); + + do { + // Release any previously allocated buffer + if (g_pNameBuffer != NULL) { + HeapFree(GetProcessHeap(), 0, g_pNameBuffer); + g_pNameBuffer = NULL; + g_dwSize = 0; + } + + // NtQueryObject puts the required buffer size in g_dwLength + // WinXP edge case puts g_dwLength == 0, just skip this handle + if (g_dwLength == 0) + goto loop_cleanup; + + g_dwSize = g_dwLength; + if (g_dwSize > 0) { + g_pNameBuffer = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + g_dwSize); + + if (g_pNameBuffer == NULL) + goto loop_cleanup; + } + + dwWait = psutil_NtQueryObject(); + + // If the call does not return, skip this handle + if (dwWait != WAIT_OBJECT_0) + goto loop_cleanup; + + } while (g_status == STATUS_INFO_LENGTH_MISMATCH); + + // NtQueryObject stopped returning STATUS_INFO_LENGTH_MISMATCH + if (!NT_SUCCESS(g_status)) + goto loop_cleanup; + + // Convert to PyUnicode and append it to the return list + if (g_pNameBuffer->Length > 0) { + /* + printf("[%d] Filename (%#x) %#d bytes: %S\n", + dwPid, + hHandle->HandleValue, + g_pNameBuffer->Length, + g_pNameBuffer->Buffer); + */ + + pyFilePath = PyUnicode_FromWideChar(g_pNameBuffer->Buffer, + g_pNameBuffer->Length/2); + if (pyFilePath == NULL) { /* - printf( - "[%#x] %.*S: (could not get name)\n", - handle.Handle, - objectTypeInfo->Name.Length / 2, - objectTypeInfo->Name.Buffer - ); + printf("[%d] PyUnicode_FromWideChar (%#x): %#x \n", + dwPid, + hHandle->HandleValue, + GetLastError()); */ - free(objectTypeInfo); - free(objectNameInfo); - CloseHandle(dupHandle); - continue; + error = TRUE; + goto loop_cleanup; + } + if (PyList_Append(pyListFiles, pyFilePath)) { + /* + printf("[%d] PyList_Append (%#x): %#x \n", + dwPid, + hHandle->HandleValue, + GetLastError()); + */ + error = TRUE; + goto loop_cleanup; } } - // Cast our buffer into an UNICODE_STRING. - objectName = *(PUNICODE_STRING)objectNameInfo; +loop_cleanup: + Py_XDECREF(pyFilePath); + pyFilePath = NULL; + + if (g_pNameBuffer != NULL) + HeapFree(GetProcessHeap(), 0, g_pNameBuffer); + g_pNameBuffer = NULL; + g_dwSize = 0; + g_dwLength = 0; + + if (g_hFile != NULL) + CloseHandle(g_hFile); + g_hFile = NULL; + } + +cleanup: + if (g_pNameBuffer != NULL) + HeapFree(GetProcessHeap(), 0, g_pNameBuffer); + g_pNameBuffer = NULL; + g_dwSize = 0; + g_dwLength = 0; + + if (g_hFile != NULL) + CloseHandle(g_hFile); + g_hFile = NULL; + + if (pHandleInfo != NULL) + HeapFree(GetProcessHeap(), 0, pHandleInfo); + pHandleInfo = NULL; + + if (error) { + Py_XDECREF(pyListFiles); + pyListFiles = NULL; + } + + LeaveCriticalSection(&g_cs); + + return pyListFiles; +} + +DWORD +psutil_NtQueryObject() +{ + DWORD dwWait = 0; + + if (g_hThread == NULL) + g_hThread = CreateThread(NULL, + 0, + (LPTHREAD_START_ROUTINE)psutil_NtQueryObjectThread, + NULL, + 0, + NULL); + if (g_hThread == NULL) + return GetLastError(); + + // Signal the worker thread to start + SetEvent(g_hEvtStart); + + // Wait for the worker thread to finish + dwWait = WaitForSingleObject(g_hEvtFinish, NTQO_TIMEOUT); + + // If the thread hangs, kill it and cleanup + if (dwWait == WAIT_TIMEOUT) { + SuspendThread(g_hThread); + TerminateThread(g_hThread, 1); + WaitForSingleObject(g_hThread, INFINITE); + CloseHandle(g_hThread); + + // Cleanup Fiber + if (g_fiber != NULL) + DeleteFiber(g_fiber); + g_fiber = NULL; + + g_hThread = NULL; + } + + return dwWait; +} + +void +psutil_NtQueryObjectThread() +{ + // Prevent the thread stack from leaking when this + // thread gets terminated due to NTQueryObject hanging + g_fiber = ConvertThreadToFiber(NULL); + + // Loop infinitely waiting for work + while (TRUE) { + WaitForSingleObject(g_hEvtStart, INFINITE); + + g_status = __NtQueryObject(g_hFile, + ObjectNameInformation, + g_pNameBuffer, + g_dwSize, + &g_dwLength); + SetEvent(g_hEvtFinish); + } +} + +PyObject * +psutil_get_open_files_getmappedfilename(long dwPid, HANDLE hProcess) +{ + NTSTATUS status; + PSYSTEM_HANDLE_INFORMATION_EX pHandleInfo = NULL; + DWORD dwInfoSize = 0x10000; + DWORD dwRet = 0; + PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX hHandle = NULL; + HANDLE hFile = NULL; + HANDLE hMap = NULL; + DWORD i = 0; + BOOLEAN error = FALSE; + PyObject* pyListFiles = NULL; + PyObject* pyFilePath = NULL; + ULONG dwSize = 0; + LPVOID pMem = NULL; + TCHAR pszFilename[MAX_PATH+1]; + + if (g_initialized == FALSE) + psutil_get_open_files_init(FALSE); + + if (__NtQuerySystemInformation == NULL || __NtQueryObject == NULL) { + PyErr_SetFromWindowsErr(0); + error = TRUE; + goto cleanup; + } + + // Py_BuildValue raises an exception if NULL is returned + pyListFiles = PyList_New(0); + if (pyListFiles == NULL) { + error = TRUE; + goto cleanup; + } + + do { + if (pHandleInfo != NULL) { + HeapFree(GetProcessHeap(), 0, pHandleInfo); + pHandleInfo = NULL; + } + + // NtQuerySystemInformation won't give us the correct buffer size, + // so we guess by doubling the buffer size. + dwInfoSize *= 2; + pHandleInfo = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + dwInfoSize); + + if (pHandleInfo == NULL) { + PyErr_NoMemory(); + error = TRUE; + goto cleanup; + } + } while ((status = __NtQuerySystemInformation( + SystemExtendedHandleInformation, + pHandleInfo, + dwInfoSize, + &dwRet)) == STATUS_INFO_LENGTH_MISMATCH); + + // NtQuerySystemInformation stopped giving us STATUS_INFO_LENGTH_MISMATCH + if (!NT_SUCCESS(status)) { + PyErr_SetFromWindowsErr(HRESULT_FROM_NT(status)); + error = TRUE; + goto cleanup; + } + + for (i = 0; i < pHandleInfo->NumberOfHandles; i++) { + hHandle = &pHandleInfo->Handles[i]; + + // Check if this hHandle belongs to the PID the user specified. + if (hHandle->UniqueProcessId != (HANDLE)dwPid || + hHandle->ObjectTypeIndex != HANDLE_TYPE_FILE) + goto loop_cleanup; - // Print the information! - if (objectName.Length) + if (!DuplicateHandle(hProcess, + hHandle->HandleValue, + GetCurrentProcess(), + &hFile, + 0, + TRUE, + DUPLICATE_SAME_ACCESS)) { - // The object has a name. Make sure it is a file otherwise - // ignore it - fileNameLength = objectName.Length / 2; - if (wcscmp(objectTypeInfo->Name.Buffer, L"File") == 0) { - // printf("%.*S\n", objectName.Length / 2, objectName.Buffer); - fileFromWchar = PyUnicode_FromWideChar(objectName.Buffer, - fileNameLength); - if (fileFromWchar == NULL) - goto error_py_fun; -#if PY_MAJOR_VERSION >= 3 - arg = Py_BuildValue("N", - PyUnicode_AsUTF8String(fileFromWchar)); -#else - arg = Py_BuildValue("N", - PyUnicode_FromObject(fileFromWchar)); -#endif - if (!arg) - goto error_py_fun; - Py_XDECREF(fileFromWchar); - fileFromWchar = NULL; - if (PyList_Append(filesList, arg)) - goto error_py_fun; - Py_XDECREF(arg); - } /* - printf( - "[%#x] %.*S: %.*S\n", - handle.Handle, - objectTypeInfo->Name.Length / 2, - objectTypeInfo->Name.Buffer, - objectName.Length / 2, - objectName.Buffer - ); + printf("[%d] DuplicateHandle (%#x): %#x \n", + dwPid, + hHandle->HandleValue, + GetLastError()); */ + goto loop_cleanup; } - else - { - // Print something else. + + hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + if (hMap == NULL) { /* - printf( - "[%#x] %.*S: (unnamed)\n", - handle.Handle, - objectTypeInfo->Name.Length / 2, - objectTypeInfo->Name.Buffer - ); + printf("[%d] CreateFileMapping (%#x): %#x \n", + dwPid, + hHandle->HandleValue, + GetLastError()); */ - ;; + goto loop_cleanup; } - free(objectTypeInfo); - free(objectNameInfo); - CloseHandle(dupHandle); + + pMem = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 1); + + if (pMem == NULL) { + /* + printf("[%d] MapViewOfFile (%#x): %#x \n", + dwPid, + hHandle->HandleValue, + GetLastError()); + */ + goto loop_cleanup; + } + + dwSize = GetMappedFileName(GetCurrentProcess(), pMem, pszFilename, MAX_PATH); + if (dwSize == 0) { + /* + printf("[%d] GetMappedFileName (%#x): %#x \n", + dwPid, + hHandle->HandleValue, + GetLastError()); + */ + goto loop_cleanup; + } + + pszFilename[dwSize] = '\0'; + /* + printf("[%d] Filename (%#x) %#d bytes: %S\n", + dwPid, + hHandle->HandleValue, + dwSize, + pszFilename); + */ + + pyFilePath = PyUnicode_FromWideChar(pszFilename, dwSize); + if (pyFilePath == NULL) { + /* + printf("[%d] PyUnicode_FromStringAndSize (%#x): %#x \n", + dwPid, + hHandle->HandleValue, + GetLastError()); + */ + error = TRUE; + goto loop_cleanup; + } + + if (PyList_Append(pyListFiles, pyFilePath)) { + /* + printf("[%d] PyList_Append (%#x): %#x \n", + dwPid, + hHandle->HandleValue, + GetLastError()); + */ + error = TRUE; + goto loop_cleanup; + } + +loop_cleanup: + Py_XDECREF(pyFilePath); + pyFilePath = NULL; + + if (pMem != NULL) + UnmapViewOfFile(pMem); + pMem = NULL; + + if (hMap != NULL) + CloseHandle(hMap); + hMap = NULL; + + if (hFile != NULL) + CloseHandle(hFile); + hFile = NULL; + + dwSize = 0; } - free(handleInfo); - CloseHandle(processHandle); - return filesList; - -error_py_fun: - Py_XDECREF(arg); - Py_XDECREF(fileFromWchar); - Py_DECREF(filesList); - return NULL; + +cleanup: + if (pMem != NULL) + UnmapViewOfFile(pMem); + pMem = NULL; + + if (hMap != NULL) + CloseHandle(hMap); + hMap = NULL; + + if (hFile != NULL) + CloseHandle(hFile); + hFile = NULL; + + if (pHandleInfo != NULL) + HeapFree(GetProcessHeap(), 0, pHandleInfo); + pHandleInfo = NULL; + + if (error) { + Py_XDECREF(pyListFiles); + pyListFiles = NULL; + } + + return pyListFiles; } diff --git a/python/psutil/psutil/arch/windows/process_handles.h b/python/psutil/psutil/arch/windows/process_handles.h index 342ce8fd26ad..4cf4023eccbf 100644 --- a/python/psutil/psutil/arch/windows/process_handles.h +++ b/python/psutil/psutil/arch/windows/process_handles.h @@ -4,7 +4,110 @@ * found in the LICENSE file. */ +#ifndef __PROCESS_HANDLES_H__ +#define __PROCESS_HANDLES_H__ + +#ifndef UNICODE +#define UNICODE +#endif + #include +#include #include +#include +#include +#include + + +#ifndef NT_SUCCESS +#define NT_SUCCESS(x) ((x) >= 0) +#endif + +#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004 +#define ObjectBasicInformation 0 +#define ObjectNameInformation 1 +#define ObjectTypeInformation 2 +#define HANDLE_TYPE_FILE 28 +#define NTQO_TIMEOUT 100 + +typedef NTSTATUS (NTAPI *_NtQuerySystemInformation)( + ULONG SystemInformationClass, + PVOID SystemInformation, + ULONG SystemInformationLength, + PULONG ReturnLength +); + +typedef NTSTATUS (NTAPI *_NtQueryObject)( + HANDLE ObjectHandle, + ULONG ObjectInformationClass, + PVOID ObjectInformation, + ULONG ObjectInformationLength, + PULONG ReturnLength +); +// Undocumented FILE_INFORMATION_CLASS: FileNameInformation +static const SYSTEM_INFORMATION_CLASS SystemExtendedHandleInformation = (SYSTEM_INFORMATION_CLASS)64; + +typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX +{ + PVOID Object; + HANDLE UniqueProcessId; + HANDLE HandleValue; + ULONG GrantedAccess; + USHORT CreatorBackTraceIndex; + USHORT ObjectTypeIndex; + ULONG HandleAttributes; + ULONG Reserved; +} SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX; + +typedef struct _SYSTEM_HANDLE_INFORMATION_EX +{ + ULONG_PTR NumberOfHandles; + ULONG_PTR Reserved; + SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1]; +} SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX; + +typedef enum _POOL_TYPE { + NonPagedPool, + PagedPool, + NonPagedPoolMustSucceed, + DontUseThisType, + NonPagedPoolCacheAligned, + PagedPoolCacheAligned, + NonPagedPoolCacheAlignedMustS +} POOL_TYPE, *PPOOL_TYPE; + +typedef struct _OBJECT_TYPE_INFORMATION { + UNICODE_STRING Name; + ULONG TotalNumberOfObjects; + ULONG TotalNumberOfHandles; + ULONG TotalPagedPoolUsage; + ULONG TotalNonPagedPoolUsage; + ULONG TotalNamePoolUsage; + ULONG TotalHandleTableUsage; + ULONG HighWaterNumberOfObjects; + ULONG HighWaterNumberOfHandles; + ULONG HighWaterPagedPoolUsage; + ULONG HighWaterNonPagedPoolUsage; + ULONG HighWaterNamePoolUsage; + ULONG HighWaterHandleTableUsage; + ULONG InvalidAttributes; + GENERIC_MAPPING GenericMapping; + ULONG ValidAccess; + BOOLEAN SecurityRequired; + BOOLEAN MaintainHandleCount; + USHORT MaintainTypeList; + POOL_TYPE PoolType; + ULONG PagedPoolUsage; + ULONG NonPagedPoolUsage; +} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; + +PVOID GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName); +VOID psutil_get_open_files_init(BOOL threaded); PyObject* psutil_get_open_files(long pid, HANDLE processHandle); +PyObject* psutil_get_open_files_ntqueryobject(long dwPid, HANDLE hProcess); +PyObject* psutil_get_open_files_getmappedfilename(long dwPid, HANDLE hProcess); +DWORD psutil_NtQueryObject(void); +void psutil_NtQueryObjectThread(void); + +#endif // __PROCESS_HANDLES_H__ diff --git a/python/psutil/psutil/arch/windows/process_info.c b/python/psutil/psutil/arch/windows/process_info.c index 8298b16c5af4..a59cce47a279 100644 --- a/python/psutil/psutil/arch/windows/process_info.c +++ b/python/psutil/psutil/arch/windows/process_info.c @@ -38,12 +38,10 @@ psutil_handle_from_pid_waccess(DWORD pid, DWORD dwDesiredAccess) hProcess = OpenProcess(dwDesiredAccess, FALSE, pid); if (hProcess == NULL) { - if (GetLastError() == ERROR_INVALID_PARAMETER) { + if (GetLastError() == ERROR_INVALID_PARAMETER) NoSuchProcess(); - } - else { + else PyErr_SetFromWindowsErr(0); - } return NULL; } @@ -129,13 +127,10 @@ psutil_pid_is_running(DWORD pid) DWORD exitCode; // Special case for PID 0 System Idle Process - if (pid == 0) { + if (pid == 0) return 1; - } - - if (pid < 0) { + if (pid < 0) return 0; - } hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); @@ -183,10 +178,8 @@ psutil_pid_in_proclist(DWORD pid) DWORD i; proclist = psutil_get_pids(&numberOfReturnedPIDs); - if (NULL == proclist) { + if (proclist == NULL) return -1; - } - for (i = 0; i < numberOfReturnedPIDs; i++) { if (pid == proclist[i]) { free(proclist); @@ -205,13 +198,12 @@ int handlep_is_running(HANDLE hProcess) { DWORD dwCode; - if (NULL == hProcess) { + + if (NULL == hProcess) return 0; - } if (GetExitCodeProcess(hProcess, &dwCode)) { - if (dwCode == STILL_ACTIVE) { + if (dwCode == STILL_ACTIVE) return 1; - } } return 0; } @@ -236,10 +228,8 @@ psutil_get_arg_list(long pid) PyObject *argList = NULL; hProcess = psutil_handle_from_pid(pid); - if (hProcess == NULL) { + if (hProcess == NULL) return NULL; - } - pebAddress = psutil_get_peb_address(hProcess); // get the address of ProcessParameters @@ -367,7 +357,10 @@ const int STATUS_BUFFER_TOO_SMALL = 0xC0000023L; /* * Given a process PID and a PSYSTEM_PROCESS_INFORMATION structure - * fills the structure with process information. + * fills the structure with various process information by using + * NtQuerySystemInformation. + * We use this as a fallback when faster functions fail with access + * denied. This is slower because it iterates over all processes. * On success return 1, else 0 with Python exception already set. */ int @@ -419,9 +412,8 @@ psutil_get_proc_info(DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess, goto error; } - if (bufferSize <= 0x20000) { + if (bufferSize <= 0x20000) initialBufferSize = bufferSize; - } process = PH_FIRST_PROCESS(buffer); do { diff --git a/python/psutil/psutil/arch/windows/process_info.h b/python/psutil/psutil/arch/windows/process_info.h index 9544f5d6676b..a44c4aced0ab 100644 --- a/python/psutil/psutil/arch/windows/process_info.h +++ b/python/psutil/psutil/arch/windows/process_info.h @@ -4,8 +4,13 @@ * found in the LICENSE file. */ +#if !defined(__PROCESS_INFO_H) +#define __PROCESS_INFO_H + #include #include +#include "security.h" +#include "ntextapi.h" DWORD* psutil_get_pids(DWORD *numberOfReturnedPIDs); HANDLE psutil_handle_from_pid(DWORD pid); @@ -15,3 +20,7 @@ int psutil_pid_in_proclist(DWORD pid); int psutil_pid_is_running(DWORD pid); PVOID psutil_get_peb_address(HANDLE ProcessHandle); PyObject* psutil_get_arg_list(long pid); +int psutil_get_proc_info(DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess, + PVOID *retBuffer); + +#endif diff --git a/python/psutil/psutil/arch/windows/security.c b/python/psutil/psutil/arch/windows/security.c index a837dfe4e29b..3aabffd0cb01 100644 --- a/python/psutil/psutil/arch/windows/security.c +++ b/python/psutil/psutil/arch/windows/security.c @@ -18,9 +18,8 @@ HANDLE psutil_token_from_handle(HANDLE hProcess) { HANDLE hToken = NULL; - if (! OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) { + if (! OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) return PyErr_SetFromWindowsErr(0); - } return hToken; } @@ -50,10 +49,8 @@ psutil_has_system_privilege(HANDLE hProcess) { TOKEN_PRIVILEGES *tp = NULL; HANDLE hToken = psutil_token_from_handle(hProcess); - if (NULL == hToken) { + if (NULL == hToken) return -1; - } - // call GetTokenInformation first to get the buffer size if (! GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwSize)) { dwRetval = GetLastError(); @@ -140,14 +137,11 @@ psutil_set_privilege(HANDLE hToken, LPCTSTR Privilege, BOOL bEnablePrivilege) tpPrevious.PrivilegeCount = 1; tpPrevious.Privileges[0].Luid = luid; - if (bEnablePrivilege) { + if (bEnablePrivilege) tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED); - } - - else { + else tpPrevious.Privileges[0].Attributes ^= (SE_PRIVILEGE_ENABLED & tpPrevious.Privileges[0].Attributes); - } AdjustTokenPrivileges( hToken, @@ -213,15 +207,12 @@ psutil_unset_se_debug() &hToken) ) { if (GetLastError() == ERROR_NO_TOKEN) { - if (! ImpersonateSelf(SecurityImpersonation)) { + if (! ImpersonateSelf(SecurityImpersonation)) return 0; - } - if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, - &hToken) - ) + &hToken)) { return 0; } @@ -229,9 +220,8 @@ psutil_unset_se_debug() } // now disable SeDebug - if (! psutil_set_privilege(hToken, SE_DEBUG_NAME, FALSE)) { + if (! psutil_set_privilege(hToken, SE_DEBUG_NAME, FALSE)) return 0; - } CloseHandle(hToken); return 1; diff --git a/python/psutil/setup.py b/python/psutil/setup.py index 277ef75b0b8b..4c42548ef3f2 100644 --- a/python/psutil/setup.py +++ b/python/psutil/setup.py @@ -22,8 +22,7 @@ def get_version(): INIT = os.path.join(HERE, 'psutil/__init__.py') - f = open(INIT, 'r') - try: + with open(INIT, 'r') as f: for line in f: if line.startswith('__version__'): ret = eval(line.strip().split(' = ')[1]) @@ -33,24 +32,28 @@ def get_version(): return ret else: raise ValueError("couldn't find version string") - finally: - f.close() def get_description(): README = os.path.join(HERE, 'README.rst') - f = open(README, 'r') - try: + with open(README, 'r') as f: return f.read() - finally: - f.close() + + +VERSION = get_version() +VERSION_MACRO = ('PSUTIL_VERSION', int(VERSION.replace('.', ''))) # POSIX if os.name == 'posix': + libraries = [] + if sys.platform.startswith("sunos"): + libraries.append('socket') + posix_extension = Extension( - '_psutil_posix', + 'psutil._psutil_posix', sources=['psutil/_psutil_posix.c'], + libraries=libraries, ) # Windows if sys.platform.startswith("win32"): @@ -60,25 +63,28 @@ def get_winver(): return '0x0%s' % ((maj * 100) + min) extensions = [Extension( - '_psutil_windows', + 'psutil._psutil_windows', sources=[ 'psutil/_psutil_windows.c', 'psutil/_psutil_common.c', 'psutil/arch/windows/process_info.c', 'psutil/arch/windows/process_handles.c', 'psutil/arch/windows/security.c', + 'psutil/arch/windows/inet_ntop.c', ], define_macros=[ + VERSION_MACRO, # be nice to mingw, see: # http://www.mingw.org/wiki/Use_more_recent_defined_functions ('_WIN32_WINNT', get_winver()), ('_AVAIL_WINVER_', get_winver()), + ('_CRT_SECURE_NO_WARNINGS', None), # see: https://github.com/giampaolo/psutil/issues/348 ('PSAPI_VERSION', 1), ], libraries=[ "psapi", "kernel32", "advapi32", "shell32", "netapi32", "iphlpapi", - "wtsapi32", + "wtsapi32", "ws2_32", ], # extra_compile_args=["/Z7"], # extra_link_args=["/DEBUG"] @@ -86,12 +92,13 @@ def get_winver(): # OS X elif sys.platform.startswith("darwin"): extensions = [Extension( - '_psutil_osx', + 'psutil._psutil_osx', sources=[ 'psutil/_psutil_osx.c', 'psutil/_psutil_common.c', 'psutil/arch/osx/process_info.c' ], + define_macros=[VERSION_MACRO], extra_link_args=[ '-framework', 'CoreFoundation', '-framework', 'IOKit' ], @@ -101,28 +108,31 @@ def get_winver(): # FreeBSD elif sys.platform.startswith("freebsd"): extensions = [Extension( - '_psutil_bsd', + 'psutil._psutil_bsd', sources=[ 'psutil/_psutil_bsd.c', 'psutil/_psutil_common.c', 'psutil/arch/bsd/process_info.c' ], + define_macros=[VERSION_MACRO], libraries=["devstat"]), posix_extension, ] # Linux elif sys.platform.startswith("linux"): extensions = [Extension( - '_psutil_linux', - sources=['psutil/_psutil_linux.c']), + 'psutil._psutil_linux', + sources=['psutil/_psutil_linux.c'], + define_macros=[VERSION_MACRO]), posix_extension, ] # Solaris elif sys.platform.lower().startswith('sunos'): extensions = [Extension( - '_psutil_sunos', + 'psutil._psutil_sunos', sources=['psutil/_psutil_sunos.c'], - libraries=['kstat', 'nsl'],), + define_macros=[VERSION_MACRO], + libraries=['kstat', 'nsl', 'socket']), posix_extension, ] else: @@ -132,14 +142,14 @@ def get_winver(): def main(): setup_args = dict( name='psutil', - version=get_version(), + version=VERSION, description=__doc__.replace('\n', '').strip(), long_description=get_description(), keywords=[ - 'ps', 'top', 'kill', 'free', 'lsof', 'netstat', 'nice', - 'tty', 'ionice', 'uptime', 'taskmgr', 'process', 'df', - 'iotop', 'iostat', 'ifconfig', 'taskset', 'who', 'pidof', - 'pmap', 'smem', 'monitoring', 'ulimit', 'prlimit', + 'ps', 'top', 'kill', 'free', 'lsof', 'netstat', 'nice', 'tty', + 'ionice', 'uptime', 'taskmgr', 'process', 'df', 'iotop', 'iostat', + 'ifconfig', 'taskset', 'who', 'pidof', 'pmap', 'smem', 'pstree', + 'monitoring', 'ulimit', 'prlimit', ], author='Giampaolo Rodola', author_email='g.rodola gmail com', @@ -166,8 +176,6 @@ def main(): 'Operating System :: POSIX', 'Programming Language :: C', 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.4', - 'Programming Language :: Python :: 2.5', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', diff --git a/python/psutil/test/README b/python/psutil/test/README deleted file mode 100644 index 801f93d1dee5..000000000000 --- a/python/psutil/test/README +++ /dev/null @@ -1,15 +0,0 @@ -- The recommended way to run tests (also on Windows) is to cd into parent - directory and run: - - make test - -- If you're on Python < 2.7 unittest2 module must be installed first: - https://pypi.python.org/pypi/unittest2 - -- The main test script is test_psutil.py, which also imports platform-specific - _*.py scripts (which should be ignored). - -- test_memory_leaks.py looks for memory leaks into C extension modules and must - be run separately with: - - make memtest diff --git a/python/psutil/test/README.rst b/python/psutil/test/README.rst new file mode 100644 index 000000000000..3f2a468efd80 --- /dev/null +++ b/python/psutil/test/README.rst @@ -0,0 +1,21 @@ +- The recommended way to run tests (also on Windows) is to cd into parent + directory and run ``make test`` + +- Dependencies for running tests: + - python 2.6: ipaddress, mock, unittest2 + - python 2.7: ipaddress, mock + - python 3.2: ipaddress, mock + - python 3.3: ipaddress + - python >= 3.4: no deps required + +- The main test script is ``test_psutil.py``, which also imports platform-specific + ``_*.py`` scripts (which should be ignored). + +- ``test_memory_leaks.py`` looks for memory leaks into C extension modules and must + be run separately with ``make test-memleaks``. + +- To run tests on all supported Python version install tox (pip install tox) + then run ``tox``. + +- Every time a commit is pushed tests are automatically run on Travis: + https://travis-ci.org/giampaolo/psutil/ diff --git a/python/psutil/test/__init__.py b/python/psutil/test/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/python/psutil/test/_bsd.py b/python/psutil/test/_bsd.py index 59be49bf0f24..e4a3225d2083 100644 --- a/python/psutil/test/_bsd.py +++ b/python/psutil/test/_bsd.py @@ -8,15 +8,15 @@ """BSD specific tests. These are implicitly run by test_psutil.py.""" +import os import subprocess -import time import sys -import os +import time import psutil from psutil._compat import PY3 -from test_psutil import (TOLERANCE, sh, get_test_subprocess, which, +from test_psutil import (TOLERANCE, BSD, sh, get_test_subprocess, which, retry_before_failing, reap_children, unittest) @@ -50,6 +50,7 @@ def muse(field): return int(line.split()[1]) +@unittest.skipUnless(BSD, "not a BSD system") class BSDSpecificTestCase(unittest.TestCase): @classmethod @@ -106,6 +107,7 @@ def df(path): if abs(usage.used - used) > 10 * 1024 * 1024: self.fail("psutil=%s, df=%s" % (usage.used, used)) + @retry_before_failing() def test_memory_maps(self): out = sh('procstat -v %s' % self.pid) maps = psutil.Process(self.pid).memory_maps(grouped=False) @@ -120,6 +122,29 @@ def test_memory_maps(self): if not map.path.startswith('['): self.assertEqual(fields[10], map.path) + def test_exe(self): + out = sh('procstat -b %s' % self.pid) + self.assertEqual(psutil.Process(self.pid).exe(), + out.split('\n')[1].split()[-1]) + + def test_cmdline(self): + out = sh('procstat -c %s' % self.pid) + self.assertEqual(' '.join(psutil.Process(self.pid).cmdline()), + ' '.join(out.split('\n')[1].split()[2:])) + + def test_uids_gids(self): + out = sh('procstat -s %s' % self.pid) + euid, ruid, suid, egid, rgid, sgid = out.split('\n')[1].split()[2:8] + p = psutil.Process(self.pid) + uids = p.uids() + gids = p.gids() + self.assertEqual(uids.real, int(ruid)) + self.assertEqual(uids.effective, int(euid)) + self.assertEqual(uids.saved, int(suid)) + self.assertEqual(gids.real, int(rgid)) + self.assertEqual(gids.effective, int(egid)) + self.assertEqual(gids.saved, int(sgid)) + # --- virtual_memory(); tests against sysctl def test_vmem_total(self): @@ -162,6 +187,10 @@ def test_vmem_buffers(self): self.assertAlmostEqual(psutil.virtual_memory().buffers, syst, delta=TOLERANCE) + def test_cpu_count_logical(self): + syst = sysctl("hw.ncpu") + self.assertEqual(psutil.cpu_count(logical=True), syst) + # --- virtual_memory(); tests against muse @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") @@ -212,12 +241,12 @@ def test_buffers(self): delta=TOLERANCE) -def test_main(): +def main(): test_suite = unittest.TestSuite() test_suite.addTest(unittest.makeSuite(BSDSpecificTestCase)) result = unittest.TextTestRunner(verbosity=2).run(test_suite) return result.wasSuccessful() if __name__ == '__main__': - if not test_main(): + if not main(): sys.exit(1) diff --git a/python/psutil/test/_linux.py b/python/psutil/test/_linux.py index 77f6da273280..c1927ea8b268 100644 --- a/python/psutil/test/_linux.py +++ b/python/psutil/test/_linux.py @@ -7,18 +7,70 @@ """Linux specific tests. These are implicitly run by test_psutil.py.""" from __future__ import division +import contextlib +import errno +import fcntl +import io import os +import pprint import re +import socket +import struct import sys +import tempfile import time +import warnings -from test_psutil import POSIX, TOLERANCE, TRAVIS +try: + from unittest import mock # py3 +except ImportError: + import mock # requires "pip install mock" + +from test_psutil import POSIX, TOLERANCE, TRAVIS, LINUX from test_psutil import (skip_on_not_implemented, sh, get_test_subprocess, - retry_before_failing, get_kernel_version, unittest) + retry_before_failing, get_kernel_version, unittest, + which, call_until) import psutil +import psutil._pslinux +from psutil._compat import PY3, u + + +SIOCGIFADDR = 0x8915 +SIOCGIFCONF = 0x8912 +SIOCGIFHWADDR = 0x8927 + + +def get_ipv4_address(ifname): + ifname = ifname[:15] + if PY3: + ifname = bytes(ifname, 'ascii') + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + with contextlib.closing(s): + return socket.inet_ntoa( + fcntl.ioctl(s.fileno(), + SIOCGIFADDR, + struct.pack('256s', ifname))[20:24]) + + +def get_mac_address(ifname): + ifname = ifname[:15] + if PY3: + ifname = bytes(ifname, 'ascii') + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + with contextlib.closing(s): + info = fcntl.ioctl( + s.fileno(), SIOCGIFHWADDR, struct.pack('256s', ifname)) + if PY3: + def ord(x): + return x + else: + import __builtin__ + ord = __builtin__.ord + return ''.join(['%02x:' % ord(char) for char in info[18:24]])[:-1] +@unittest.skipUnless(LINUX, "not a Linux system") class LinuxSpecificTestCase(unittest.TestCase): @unittest.skipIf( @@ -139,6 +191,241 @@ def test_cpu_times(self): else: self.assertNotIn('guest_nice', fields) + def test_net_if_addrs_ips(self): + for name, addrs in psutil.net_if_addrs().items(): + for addr in addrs: + if addr.family == psutil.AF_LINK: + self.assertEqual(addr.address, get_mac_address(name)) + elif addr.family == socket.AF_INET: + self.assertEqual(addr.address, get_ipv4_address(name)) + # TODO: test for AF_INET6 family + + @unittest.skipUnless(which('ip'), "'ip' utility not available") + @unittest.skipIf(TRAVIS, "skipped on Travis") + def test_net_if_names(self): + out = sh("ip addr").strip() + nics = psutil.net_if_addrs() + found = 0 + for line in out.split('\n'): + line = line.strip() + if re.search("^\d+:", line): + found += 1 + name = line.split(':')[1].strip() + self.assertIn(name, nics.keys()) + self.assertEqual(len(nics), found, msg="%s\n---\n%s" % ( + pprint.pformat(nics), out)) + + @unittest.skipUnless(which("nproc"), "nproc utility not available") + def test_cpu_count_logical_w_nproc(self): + num = int(sh("nproc --all")) + self.assertEqual(psutil.cpu_count(logical=True), num) + + @unittest.skipUnless(which("lscpu"), "lscpu utility not available") + def test_cpu_count_logical_w_lscpu(self): + out = sh("lscpu -p") + num = len([x for x in out.split('\n') if not x.startswith('#')]) + self.assertEqual(psutil.cpu_count(logical=True), num) + + # --- mocked tests + + def test_virtual_memory_mocked_warnings(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + with warnings.catch_warnings(record=True) as ws: + warnings.simplefilter("always") + ret = psutil._pslinux.virtual_memory() + assert m.called + self.assertEqual(len(ws), 1) + w = ws[0] + self.assertTrue(w.filename.endswith('psutil/_pslinux.py')) + self.assertIn( + "'cached', 'active' and 'inactive' memory stats couldn't " + "be determined", str(w.message)) + self.assertEqual(ret.cached, 0) + self.assertEqual(ret.active, 0) + self.assertEqual(ret.inactive, 0) + + def test_swap_memory_mocked_warnings(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + with warnings.catch_warnings(record=True) as ws: + warnings.simplefilter("always") + ret = psutil._pslinux.swap_memory() + assert m.called + self.assertEqual(len(ws), 1) + w = ws[0] + self.assertTrue(w.filename.endswith('psutil/_pslinux.py')) + self.assertIn( + "'sin' and 'sout' swap memory stats couldn't " + "be determined", str(w.message)) + self.assertEqual(ret.sin, 0) + self.assertEqual(ret.sout, 0) + + def test_cpu_count_logical_mocked(self): + import psutil._pslinux + original = psutil._pslinux.cpu_count_logical() + # Here we want to mock os.sysconf("SC_NPROCESSORS_ONLN") in + # order to cause the parsing of /proc/cpuinfo and /proc/stat. + with mock.patch( + 'psutil._pslinux.os.sysconf', side_effect=ValueError) as m: + self.assertEqual(psutil._pslinux.cpu_count_logical(), original) + assert m.called + + # Let's have open() return emtpy data and make sure None is + # returned ('cause we mimick os.cpu_count()). + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertIsNone(psutil._pslinux.cpu_count_logical()) + self.assertEqual(m.call_count, 2) + # /proc/stat should be the last one + self.assertEqual(m.call_args[0][0], '/proc/stat') + + # Let's push this a bit further and make sure /proc/cpuinfo + # parsing works as expected. + with open('/proc/cpuinfo', 'rb') as f: + cpuinfo_data = f.read() + fake_file = io.BytesIO(cpuinfo_data) + with mock.patch('psutil._pslinux.open', + return_value=fake_file, create=True) as m: + self.assertEqual(psutil._pslinux.cpu_count_logical(), original) + + def test_cpu_count_physical_mocked(self): + # Have open() return emtpy data and make sure None is returned + # ('cause we want to mimick os.cpu_count()) + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertIsNone(psutil._pslinux.cpu_count_physical()) + assert m.called + + def test_proc_open_files_file_gone(self): + # simulates a file which gets deleted during open_files() + # execution + p = psutil.Process() + files = p.open_files() + with tempfile.NamedTemporaryFile(): + # give the kernel some time to see the new file + call_until(p.open_files, "len(ret) != %i" % len(files)) + with mock.patch('psutil._pslinux.os.readlink', + side_effect=OSError(errno.ENOENT, "")) as m: + files = p.open_files() + assert not files + assert m.called + # also simulate the case where os.readlink() returns EINVAL + # in which case psutil is supposed to 'continue' + with mock.patch('psutil._pslinux.os.readlink', + side_effect=OSError(errno.EINVAL, "")) as m: + self.assertEqual(p.open_files(), []) + assert m.called + + def test_proc_terminal_mocked(self): + with mock.patch('psutil._pslinux._psposix._get_terminal_map', + return_value={}) as m: + self.assertIsNone(psutil._pslinux.Process(os.getpid()).terminal()) + assert m.called + + def test_proc_num_ctx_switches_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + NotImplementedError, + psutil._pslinux.Process(os.getpid()).num_ctx_switches) + assert m.called + + def test_proc_num_threads_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + NotImplementedError, + psutil._pslinux.Process(os.getpid()).num_threads) + assert m.called + + def test_proc_ppid_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + NotImplementedError, + psutil._pslinux.Process(os.getpid()).ppid) + assert m.called + + def test_proc_uids_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + NotImplementedError, + psutil._pslinux.Process(os.getpid()).uids) + assert m.called + + def test_proc_gids_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + NotImplementedError, + psutil._pslinux.Process(os.getpid()).gids) + assert m.called + + def test_proc_cmdline_mocked(self): + # see: https://github.com/giampaolo/psutil/issues/639 + p = psutil.Process() + fake_file = io.StringIO(u('foo\x00bar\x00')) + with mock.patch('psutil._pslinux.open', + return_value=fake_file, create=True) as m: + p.cmdline() == ['foo', 'bar'] + assert m.called + fake_file = io.StringIO(u('foo\x00bar\x00\x00')) + with mock.patch('psutil._pslinux.open', + return_value=fake_file, create=True) as m: + p.cmdline() == ['foo', 'bar', ''] + assert m.called + + def test_proc_io_counters_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + NotImplementedError, + psutil._pslinux.Process(os.getpid()).io_counters) + assert m.called + + def test_boot_time_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + RuntimeError, + psutil._pslinux.boot_time) + assert m.called + + def test_users_mocked(self): + # Make sure ':0' and ':0.0' (returned by C ext) are converted + # to 'localhost'. + with mock.patch('psutil._pslinux.cext.users', + return_value=[('giampaolo', 'pts/2', ':0', + 1436573184.0, True)]) as m: + self.assertEqual(psutil.users()[0].host, 'localhost') + assert m.called + with mock.patch('psutil._pslinux.cext.users', + return_value=[('giampaolo', 'pts/2', ':0.0', + 1436573184.0, True)]) as m: + self.assertEqual(psutil.users()[0].host, 'localhost') + assert m.called + # ...otherwise it should be returned as-is + with mock.patch('psutil._pslinux.cext.users', + return_value=[('giampaolo', 'pts/2', 'foo', + 1436573184.0, True)]) as m: + self.assertEqual(psutil.users()[0].host, 'foo') + assert m.called + + def test_disk_partitions_mocked(self): + # Test that ZFS partitions are returned. + with open("/proc/filesystems", "r") as f: + data = f.read() + if 'zfs' in data: + for part in psutil.disk_partitions(): + if part.fstype == 'zfs': + break + else: + self.fail("couldn't find any ZFS partition") + else: + # No ZFS partitions on this system. Let's fake one. + fake_file = io.StringIO(u("nodev\tzfs\n")) + with mock.patch('psutil._pslinux.open', + return_value=fake_file, create=True) as m1: + with mock.patch( + 'psutil._pslinux.cext.disk_partitions', + return_value=[('/dev/sdb3', '/', 'zfs', 'rw')]) as m2: + ret = psutil.disk_partitions() + assert m1.called + assert m2.called + assert ret + self.assertEqual(ret[0].fstype, 'zfs') + # --- tests for specific kernel versions @unittest.skipUnless( @@ -175,12 +462,12 @@ def test_resource_consts_kernel_v(self): self.assertTrue(hasattr(psutil, "RLIMIT_SIGPENDING")) -def test_main(): +def main(): test_suite = unittest.TestSuite() test_suite.addTest(unittest.makeSuite(LinuxSpecificTestCase)) result = unittest.TextTestRunner(verbosity=2).run(test_suite) return result.wasSuccessful() if __name__ == '__main__': - if not test_main(): + if not main(): sys.exit(1) diff --git a/python/psutil/test/_osx.py b/python/psutil/test/_osx.py index 784f00e07354..6e6e4380ef03 100644 --- a/python/psutil/test/_osx.py +++ b/python/psutil/test/_osx.py @@ -15,8 +15,8 @@ import psutil from psutil._compat import PY3 -from test_psutil import (TOLERANCE, sh, get_test_subprocess, reap_children, - retry_before_failing, unittest) +from test_psutil import (TOLERANCE, OSX, sh, get_test_subprocess, + reap_children, retry_before_failing, unittest) PAGESIZE = os.sysconf("SC_PAGE_SIZE") @@ -47,6 +47,7 @@ def vm_stat(field): return int(re.search('\d+', line).group(0)) * PAGESIZE +@unittest.skipUnless(OSX, "not an OSX system") class OSXSpecificTestCase(unittest.TestCase): @classmethod @@ -148,12 +149,12 @@ def test_swapmem_total(self): self.assertEqual(tot1, tot2) -def test_main(): +def main(): test_suite = unittest.TestSuite() test_suite.addTest(unittest.makeSuite(OSXSpecificTestCase)) result = unittest.TextTestRunner(verbosity=2).run(test_suite) return result.wasSuccessful() if __name__ == '__main__': - if not test_main(): + if not main(): sys.exit(1) diff --git a/python/psutil/test/_posix.py b/python/psutil/test/_posix.py index 8cbd629a5cac..e6c56aac3e60 100644 --- a/python/psutil/test/_posix.py +++ b/python/psutil/test/_posix.py @@ -14,8 +14,8 @@ import psutil -from psutil._compat import PY3 -from test_psutil import LINUX, SUNOS, OSX, BSD, PYTHON +from psutil._compat import PY3, callable +from test_psutil import LINUX, SUNOS, OSX, BSD, PYTHON, POSIX, TRAVIS from test_psutil import (get_test_subprocess, skip_on_access_denied, retry_before_failing, reap_children, sh, unittest, get_kernel_version, wait_for_pid) @@ -42,6 +42,7 @@ def ps(cmd): return output +@unittest.skipUnless(POSIX, "not a POSIX system") class PosixSpecificTestCase(unittest.TestCase): """Compare psutil results against 'ps' command line utility.""" @@ -111,11 +112,14 @@ def test_process_name(self): def test_process_create_time(self): time_ps = ps("ps --no-headers -o start -p %s" % self.pid).split(' ')[0] time_psutil = psutil.Process(self.pid).create_time() - if SUNOS: - time_psutil = round(time_psutil) time_psutil_tstamp = datetime.datetime.fromtimestamp( time_psutil).strftime("%H:%M:%S") - self.assertEqual(time_ps, time_psutil_tstamp) + # sometimes ps shows the time rounded up instead of down, so we check + # for both possible values + round_time_psutil = round(time_psutil) + round_time_psutil_tstamp = datetime.datetime.fromtimestamp( + round_time_psutil).strftime("%H:%M:%S") + self.assertIn(time_ps, [time_psutil_tstamp, round_time_psutil_tstamp]) def test_process_exe(self): ps_pathname = ps("ps --no-headers -o command -p %s" % @@ -173,9 +177,10 @@ def test_pids(self): [x for x in pids_ps if x not in pids_psutil] self.fail("difference: " + str(difference)) - # for some reason ifconfig -a does not report differente interfaces - # psutil does + # for some reason ifconfig -a does not report all interfaces + # returned by psutil @unittest.skipIf(SUNOS, "test not reliable on SUNOS") + @unittest.skipIf(TRAVIS, "test not reliable on Travis") def test_nic_names(self): p = subprocess.Popen("ifconfig -a", shell=1, stdout=subprocess.PIPE) output = p.communicate()[0].strip() @@ -186,7 +191,9 @@ def test_nic_names(self): if line.startswith(nic): break else: - self.fail("couldn't find %s nic in 'ifconfig -a' output" % nic) + self.fail( + "couldn't find %s nic in 'ifconfig -a' output\n%s" % ( + nic, output)) @retry_before_failing() def test_users(self): @@ -208,8 +215,6 @@ def call(p, attr): if attr is not None and callable(attr): if name == 'rlimit': args = (psutil.RLIMIT_NOFILE,) - elif name == 'set_rlimit': - args = (psutil.RLIMIT_NOFILE, (5, 5)) attr(*args) else: attr @@ -220,11 +225,10 @@ def call(p, attr): 'send_signal', 'wait', 'children', 'as_dict'] if LINUX and get_kernel_version() < (2, 6, 36): ignored_names.append('rlimit') + if LINUX and get_kernel_version() < (2, 6, 23): + ignored_names.append('num_ctx_switches') for name in dir(psutil.Process): - if (name.startswith('_') - or name.startswith('set_') - or name.startswith('get') # deprecated APIs - or name in ignored_names): + if (name.startswith('_') or name in ignored_names): continue else: try: @@ -243,12 +247,12 @@ def call(p, attr): self.fail('\n' + '\n'.join(failures)) -def test_main(): +def main(): test_suite = unittest.TestSuite() test_suite.addTest(unittest.makeSuite(PosixSpecificTestCase)) result = unittest.TextTestRunner(verbosity=2).run(test_suite) return result.wasSuccessful() if __name__ == '__main__': - if not test_main(): + if not main(): sys.exit(1) diff --git a/python/psutil/test/_sunos.py b/python/psutil/test/_sunos.py index 7fdc50b6c0b4..3d54ccd8cfb5 100644 --- a/python/psutil/test/_sunos.py +++ b/python/psutil/test/_sunos.py @@ -7,15 +7,17 @@ """Sun OS specific tests. These are implicitly run by test_psutil.py.""" import sys +import os -from test_psutil import sh, unittest +from test_psutil import SUNOS, sh, unittest import psutil +@unittest.skipUnless(SUNOS, "not a SunOS system") class SunOSSpecificTestCase(unittest.TestCase): def test_swap_memory(self): - out = sh('swap -l -k') + out = sh('env PATH=/usr/sbin:/sbin:%s swap -l -k' % os.environ['PATH']) lines = out.strip().split('\n')[1:] if not lines: raise ValueError('no swap device(s) configured') @@ -35,12 +37,12 @@ def test_swap_memory(self): self.assertEqual(psutil_swap.free, free) -def test_main(): +def main(): test_suite = unittest.TestSuite() test_suite.addTest(unittest.makeSuite(SunOSSpecificTestCase)) result = unittest.TextTestRunner(verbosity=2).run(test_suite) return result.wasSuccessful() if __name__ == '__main__': - if not test_main(): + if not main(): sys.exit(1) diff --git a/python/psutil/test/_windows.py b/python/psutil/test/_windows.py index e1b27cc0a1c6..b7477bfeb499 100644 --- a/python/psutil/test/_windows.py +++ b/python/psutil/test/_windows.py @@ -15,8 +15,10 @@ import time import traceback -from test_psutil import (get_test_subprocess, reap_children, unittest) +from test_psutil import APPVEYOR, WINDOWS +from test_psutil import get_test_subprocess, reap_children, unittest +import mock try: import wmi except ImportError: @@ -28,17 +30,18 @@ win32api = win32con = None from psutil._compat import PY3, callable, long -from psutil._pswindows import ACCESS_DENIED_SET -import _psutil_windows import psutil +cext = psutil._psplatform.cext + + def wrap_exceptions(fun): def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except OSError: - err = sys.exc_info()[1] + except OSError as err: + from psutil._pswindows import ACCESS_DENIED_SET if err.errno in ACCESS_DENIED_SET: raise psutil.AccessDenied(None, None) if err.errno == errno.ESRCH: @@ -47,6 +50,7 @@ def wrapper(self, *args, **kwargs): return wrapper +@unittest.skipUnless(WINDOWS, "not a Windows system") class WindowsSpecificTestCase(unittest.TestCase): @classmethod @@ -114,7 +118,9 @@ def test_process_name(self): def test_process_exe(self): w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] p = psutil.Process(self.pid) - self.assertEqual(p.exe(), w.ExecutablePath) + # Note: wmi reports the exe as a lower case string. + # Being Windows paths case-insensitive we ignore that. + self.assertEqual(p.exe().lower(), w.ExecutablePath.lower()) @unittest.skipIf(wmi is None, "wmi module is not installed") def test_process_cmdline(self): @@ -164,7 +170,7 @@ def test_process_create_time(self): # --- psutil namespace functions and constants tests - @unittest.skipUnless(hasattr(os, 'NUMBER_OF_PROCESSORS'), + @unittest.skipUnless('NUMBER_OF_PROCESSORS' in os.environ, 'NUMBER_OF_PROCESSORS env var is not available') def test_cpu_count(self): num_cpus = int(os.environ['NUMBER_OF_PROCESSORS']) @@ -188,20 +194,16 @@ def test_total_phymem(self): # time.localtime(p.create_time())) # + # Note: this test is not very reliable @unittest.skipIf(wmi is None, "wmi module is not installed") + @unittest.skipIf(APPVEYOR, "test not relieable on appveyor") def test_pids(self): # Note: this test might fail if the OS is starting/killing # other processes in the meantime w = wmi.WMI().Win32_Process() - wmi_pids = [x.ProcessId for x in w] - wmi_pids.sort() - psutil_pids = psutil.pids() - psutil_pids.sort() - if wmi_pids != psutil_pids: - difference = \ - filter(lambda x: x not in wmi_pids, psutil_pids) + \ - filter(lambda x: x not in psutil_pids, wmi_pids) - self.fail("difference: " + str(difference)) + wmi_pids = set([x.ProcessId for x in w]) + psutil_pids = set(psutil.pids()) + self.assertEqual(wmi_pids, psutil_pids) @unittest.skipIf(wmi is None, "wmi module is not installed") def test_disks(self): @@ -215,8 +217,7 @@ def test_disks(self): break try: usage = psutil.disk_usage(ps_part.mountpoint) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno == errno.ENOENT: # usually this is the floppy break @@ -259,8 +260,6 @@ def call(p, attr): failures = [] for name in dir(psutil.Process): if name.startswith('_') \ - or name.startswith('set_') \ - or name.startswith('get') \ or name in ('terminate', 'kill', 'suspend', 'resume', 'nice', 'send_signal', 'wait', 'children', 'as_dict'): @@ -282,29 +281,42 @@ def call(p, attr): if failures: self.fail('\n' + '\n'.join(failures)) + def test_name_always_available(self): + # On Windows name() is never supposed to raise AccessDenied, + # see https://github.com/giampaolo/psutil/issues/627 + for p in psutil.process_iter(): + try: + p.name() + except psutil.NoSuchProcess(): + pass + +@unittest.skipUnless(WINDOWS, "not a Windows system") class TestDualProcessImplementation(unittest.TestCase): + """ + Certain APIs on Windows have 2 internal implementations, one + based on documented Windows APIs, another one based + NtQuerySystemInformation() which gets called as fallback in + case the first fails because of limited permission error. + Here we test that the two methods return the exact same value, + see: + https://github.com/giampaolo/psutil/issues/304 + """ + fun_names = [ # function name, tolerance ('proc_cpu_times', 0.2), ('proc_create_time', 0.5), ('proc_num_handles', 1), # 1 because impl #1 opens a handle - ('proc_io_counters', 0), ('proc_memory_info', 1024), # KB + ('proc_io_counters', 0), ] def test_compare_values(self): - # Certain APIs on Windows have 2 internal implementations, one - # based on documented Windows APIs, another one based - # NtQuerySystemInformation() which gets called as fallback in - # case the first fails because of limited permission error. - # Here we test that the two methods return the exact same value, - # see: - # https://github.com/giampaolo/psutil/issues/304 def assert_ge_0(obj): if isinstance(obj, tuple): for value in obj: - self.assertGreaterEqual(value, 0) + self.assertGreaterEqual(value, 0, msg=obj) elif isinstance(obj, (int, long, float)): self.assertGreaterEqual(obj, 0) else: @@ -322,55 +334,125 @@ def compare_with_tolerance(ret1, ret2, tolerance): diff = abs(a - b) self.assertLessEqual(diff, tolerance) + from psutil._pswindows import ntpinfo failures = [] - for name, tolerance in self.fun_names: - meth1 = wrap_exceptions(getattr(_psutil_windows, name)) - meth2 = wrap_exceptions(getattr(_psutil_windows, name + '_2')) - for p in psutil.process_iter(): + for p in psutil.process_iter(): + try: + nt = ntpinfo(*cext.proc_info(p.pid)) + except psutil.NoSuchProcess: + continue + assert_ge_0(nt) + + for name, tolerance in self.fun_names: if name == 'proc_memory_info' and p.pid == os.getpid(): continue - # - try: - ret1 = meth1(p.pid) - except psutil.NoSuchProcess: + if name == 'proc_create_time' and p.pid in (0, 4): continue - except psutil.AccessDenied: - ret1 = None - # + meth = wrap_exceptions(getattr(cext, name)) try: - ret2 = meth2(p.pid) - except psutil.NoSuchProcess: - # this is supposed to fail only in case of zombie process - # never for permission error + ret = meth(p.pid) + except (psutil.NoSuchProcess, psutil.AccessDenied): continue - # compare values try: - if ret1 is None: - assert_ge_0(ret2) - else: - compare_with_tolerance(ret1, ret2, tolerance) - assert_ge_0(ret1) - assert_ge_0(ret2) + if name == 'proc_cpu_times': + compare_with_tolerance(ret[0], nt.user_time, tolerance) + compare_with_tolerance(ret[1], + nt.kernel_time, tolerance) + elif name == 'proc_create_time': + compare_with_tolerance(ret, nt.create_time, tolerance) + elif name == 'proc_num_handles': + compare_with_tolerance(ret, nt.num_handles, tolerance) + elif name == 'proc_io_counters': + compare_with_tolerance(ret[0], nt.io_rcount, tolerance) + compare_with_tolerance(ret[1], nt.io_wcount, tolerance) + compare_with_tolerance(ret[2], nt.io_rbytes, tolerance) + compare_with_tolerance(ret[3], nt.io_wbytes, tolerance) + elif name == 'proc_memory_info': + try: + rawtupl = cext.proc_memory_info_2(p.pid) + except psutil.NoSuchProcess: + continue + compare_with_tolerance(ret, rawtupl, tolerance) except AssertionError: trace = traceback.format_exc() msg = '%s\npid=%s, method=%r, ret_1=%r, ret_2=%r' % ( - trace, p.pid, name, ret1, ret2) + trace, p.pid, name, ret, nt) failures.append(msg) break + if failures: self.fail('\n\n'.join(failures)) + # --- + # same tests as above but mimicks the AccessDenied failure of + # the first (fast) method failing with AD. + # TODO: currently does not take tolerance into account. + + def test_name(self): + name = psutil.Process().name() + with mock.patch("psutil._psplatform.cext.proc_exe", + side_effect=psutil.AccessDenied(os.getpid())) as fun: + psutil.Process().name() == name + assert fun.called + + def test_memory_info(self): + mem = psutil.Process().memory_info() + with mock.patch("psutil._psplatform.cext.proc_memory_info", + side_effect=OSError(errno.EPERM, "msg")) as fun: + psutil.Process().memory_info() == mem + assert fun.called + + def test_create_time(self): + ctime = psutil.Process().create_time() + with mock.patch("psutil._psplatform.cext.proc_create_time", + side_effect=OSError(errno.EPERM, "msg")) as fun: + psutil.Process().create_time() == ctime + assert fun.called + + def test_cpu_times(self): + cpu_times = psutil.Process().cpu_times() + with mock.patch("psutil._psplatform.cext.proc_cpu_times", + side_effect=OSError(errno.EPERM, "msg")) as fun: + psutil.Process().cpu_times() == cpu_times + assert fun.called + + def test_io_counters(self): + io_counters = psutil.Process().io_counters() + with mock.patch("psutil._psplatform.cext.proc_io_counters", + side_effect=OSError(errno.EPERM, "msg")) as fun: + psutil.Process().io_counters() == io_counters + assert fun.called + + def test_num_handles(self): + io_counters = psutil.Process().io_counters() + with mock.patch("psutil._psplatform.cext.proc_io_counters", + side_effect=OSError(errno.EPERM, "msg")) as fun: + psutil.Process().io_counters() == io_counters + assert fun.called + + # --- other tests + + def test_compare_name_exe(self): + for p in psutil.process_iter(): + try: + a = os.path.basename(p.exe()) + b = p.name() + except (psutil.NoSuchProcess, psutil.AccessDenied): + pass + else: + self.assertEqual(a, b) + def test_zombies(self): # test that NPS is raised by the 2nd implementation in case a # process no longer exists ZOMBIE_PID = max(psutil.pids()) + 5000 for name, _ in self.fun_names: - meth = wrap_exceptions(getattr(_psutil_windows, name)) + meth = wrap_exceptions(getattr(cext, name)) self.assertRaises(psutil.NoSuchProcess, meth, ZOMBIE_PID) -def test_main(): +def main(): test_suite = unittest.TestSuite() test_suite.addTest(unittest.makeSuite(WindowsSpecificTestCase)) test_suite.addTest(unittest.makeSuite(TestDualProcessImplementation)) @@ -378,5 +460,5 @@ def test_main(): return result.wasSuccessful() if __name__ == '__main__': - if not test_main(): + if not main(): sys.exit(1) diff --git a/python/psutil/test/test_memory_leaks.py b/python/psutil/test/test_memory_leaks.py index c33d37f049d7..6f02dc0acf38 100644 --- a/python/psutil/test/test_memory_leaks.py +++ b/python/psutil/test/test_memory_leaks.py @@ -10,6 +10,7 @@ after the calls. It might produce false positives. """ +import functools import gc import os import socket @@ -17,20 +18,20 @@ import threading import time -if sys.version_info < (2, 7): - import unittest2 as unittest # https://pypi.python.org/pypi/unittest2 -else: - import unittest - import psutil import psutil._common -from psutil._compat import callable, xrange -from test_psutil import (WINDOWS, POSIX, OSX, LINUX, SUNOS, TESTFN, - RLIMIT_SUPPORT) +from psutil._compat import xrange, callable +from test_psutil import (WINDOWS, POSIX, OSX, LINUX, SUNOS, BSD, TESTFN, + RLIMIT_SUPPORT, TRAVIS) from test_psutil import (reap_children, supports_ipv6, safe_remove, get_test_subprocess) +if sys.version_info < (2, 7): + import unittest2 as unittest # https://pypi.python.org/pypi/unittest2 +else: + import unittest + LOOPS = 1000 TOLERANCE = 4096 @@ -43,7 +44,7 @@ def skip_if_linux(): class Base(unittest.TestCase): - proc = psutil.Process(os.getpid()) + proc = psutil.Process() def execute(self, function, *args, **kwargs): def call_many_times(): @@ -73,7 +74,7 @@ def call_many_times(): # Let's keep calling fun for 3 more seconds and fail if # we notice any difference. stop_at = time.time() + 3 - while 1: + while True: self.call(function, *args, **kwargs) if time.time() >= stop_at: break @@ -85,10 +86,14 @@ def call_many_times(): self.fail("rss2=%s, rss3=%s, difference=%s" % (rss2, rss3, difference)) + def execute_w_exc(self, exc, function, *args, **kwargs): + kwargs['_exc'] = exc + self.execute(function, *args, **kwargs) + def get_mem(self): - return psutil.Process(os.getpid()).memory_info()[0] + return psutil.Process().memory_info()[0] - def call(self, *args, **kwargs): + def call(self, function, *args, **kwargs): raise NotImplementedError("must be implemented in subclass") @@ -102,12 +107,25 @@ def tearDown(self): reap_children() def call(self, function, *args, **kwargs): - try: - obj = getattr(self.proc, function) - if callable(obj): - obj(*args, **kwargs) - except psutil.Error: - pass + if callable(function): + if '_exc' in kwargs: + exc = kwargs.pop('_exc') + self.assertRaises(exc, function, *args, **kwargs) + else: + try: + function(*args, **kwargs) + except psutil.Error: + pass + else: + meth = getattr(self.proc, function) + if '_exc' in kwargs: + exc = kwargs.pop('_exc') + self.assertRaises(exc, meth, *args, **kwargs) + else: + try: + meth(*args, **kwargs) + except psutil.Error: + pass @skip_if_linux() def test_name(self): @@ -143,7 +161,7 @@ def test_nice_get(self): self.execute('nice') def test_nice_set(self): - niceness = psutil.Process(os.getpid()).nice() + niceness = psutil.Process().nice() self.execute('nice', niceness) @unittest.skipUnless(hasattr(psutil.Process, 'ionice'), @@ -155,16 +173,20 @@ def test_ionice_get(self): "Linux and Windows Vista only") def test_ionice_set(self): if WINDOWS: - value = psutil.Process(os.getpid()).ionice() + value = psutil.Process().ionice() self.execute('ionice', value) else: + from psutil._pslinux import cext self.execute('ionice', psutil.IOPRIO_CLASS_NONE) + fun = functools.partial(cext.proc_ioprio_set, os.getpid(), -1, 0) + self.execute_w_exc(OSError, fun) - @unittest.skipIf(OSX, "feature not supported on this platform") + @unittest.skipIf(OSX or SUNOS, "feature not supported on this platform") @skip_if_linux() def test_io_counters(self): self.execute('io_counters') + @unittest.skipUnless(WINDOWS, "not worth being tested on posix") def test_username(self): self.execute('username') @@ -215,23 +237,24 @@ def test_resume(self): def test_cwd(self): self.execute('cwd') - @unittest.skipUnless(WINDOWS or LINUX, "Windows or Linux only") + @unittest.skipUnless(WINDOWS or LINUX or BSD, + "Windows or Linux or BSD only") def test_cpu_affinity_get(self): self.execute('cpu_affinity') - @unittest.skipUnless(WINDOWS or LINUX, "Windows or Linux only") + @unittest.skipUnless(WINDOWS or LINUX or BSD, + "Windows or Linux or BSD only") def test_cpu_affinity_set(self): - affinity = psutil.Process(os.getpid()).cpu_affinity() + affinity = psutil.Process().cpu_affinity() self.execute('cpu_affinity', affinity) + if not TRAVIS: + self.execute_w_exc(ValueError, 'cpu_affinity', [-1]) @skip_if_linux() def test_open_files(self): safe_remove(TESTFN) # needed after UNIX socket test has run - f = open(TESTFN, 'w') - try: + with open(TESTFN, 'w'): self.execute('open_files') - finally: - f.close() # OSX implementation is unbelievably slow @unittest.skipIf(OSX, "OSX implementation is too slow") @@ -251,6 +274,7 @@ def test_rlimit_get(self): def test_rlimit_set(self): limit = psutil.Process().rlimit(psutil.RLIMIT_NOFILE) self.execute('rlimit', psutil.RLIMIT_NOFILE, limit) + self.execute_w_exc(OSError, 'rlimit', -1) @skip_if_linux() # Windows implementation is based on a single system-wide function @@ -301,6 +325,12 @@ class TestProcessObjectLeaksZombie(TestProcessObjectLeaks): """ proc = DEAD_PROC + def call(self, *args, **kwargs): + try: + TestProcessObjectLeaks.call(self, *args, **kwargs) + except psutil.NoSuchProcess: + pass + if not POSIX: def test_kill(self): self.execute('kill') @@ -325,9 +355,8 @@ def setUp(self): gc.collect() def call(self, function, *args, **kwargs): - obj = getattr(psutil, function) - if callable(obj): - obj(*args, **kwargs) + fun = getattr(psutil, function) + fun(*args, **kwargs) @skip_if_linux() def test_cpu_count_logical(self): @@ -393,8 +422,15 @@ def test_users(self): def test_net_connections(self): self.execute('net_connections') + def test_net_if_addrs(self): + self.execute('net_if_addrs') + + @unittest.skipIf(TRAVIS, "EPERM on travis") + def test_net_if_stats(self): + self.execute('net_if_stats') + -def test_main(): +def main(): test_suite = unittest.TestSuite() tests = [TestProcessObjectLeaksZombie, TestProcessObjectLeaks, @@ -405,5 +441,5 @@ def test_main(): return result.wasSuccessful() if __name__ == '__main__': - if not test_main(): + if not main(): sys.exit(1) diff --git a/python/psutil/test/test_psutil.py b/python/psutil/test/test_psutil.py index 2aec16899652..3b2e3587ae9d 100644 --- a/python/psutil/test/test_psutil.py +++ b/python/psutil/test/test_psutil.py @@ -15,11 +15,18 @@ from __future__ import division +import ast import atexit +import collections +import contextlib import datetime import errno +import functools +import imp +import json import os import pickle +import pprint import re import select import shutil @@ -37,21 +44,25 @@ import warnings from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM try: - import ast # python >= 2.6 + import ipaddress # python >= 3.3 except ImportError: - ast = None + ipaddress = None try: - import json # python >= 2.6 + from unittest import mock # py3 except ImportError: - json = None + import mock # requires "pip install mock" + +import psutil +from psutil._compat import PY3, callable, long, unicode if sys.version_info < (2, 7): import unittest2 as unittest # https://pypi.python.org/pypi/unittest2 else: import unittest - -import psutil -from psutil._compat import PY3, callable, long, wraps, unicode +if sys.version_info >= (3, 4): + import enum +else: + enum = None # =================================================================== @@ -93,6 +104,14 @@ if x.startswith('STATUS_')] # whether we're running this test suite on Travis (https://travis-ci.org/) TRAVIS = bool(os.environ.get('TRAVIS')) +# whether we're running this test suite on Appveyor for Windows +# (http://www.appveyor.com/) +APPVEYOR = bool(os.environ.get('APPVEYOR')) + +if TRAVIS or 'tox' in sys.argv[0]: + import ipaddress +if TRAVIS or APPVEYOR: + GLOBAL_TIMEOUT = GLOBAL_TIMEOUT * 4 # =================================================================== @@ -128,7 +147,7 @@ def get_test_subprocess(cmd=None, stdout=DEVNULL, stderr=DEVNULL, pyline = "" if wait: pyline += "open(r'%s', 'w'); " % TESTFN - pyline += "import time; time.sleep(2);" + pyline += "import time; time.sleep(60);" cmd_ = [PYTHON, "-c", pyline] else: cmd_ = cmd @@ -157,20 +176,15 @@ def pyrun(src): """ if PY3: src = bytes(src, 'ascii') - # could have used NamedTemporaryFile(delete=False) but it's - # >= 2.6 only - fd, path = tempfile.mkstemp(prefix=TESTFILE_PREFIX) - _testfiles.append(path) - f = open(path, 'wb') - try: + with tempfile.NamedTemporaryFile( + prefix=TESTFILE_PREFIX, delete=False) as f: + _testfiles.append(f.name) f.write(src) f.flush() - subp = get_test_subprocess([PYTHON, f.name], stdout=None, stderr=None) + subp = get_test_subprocess([PYTHON, f.name], stdout=None, + stderr=None) wait_for_pid(subp.pid) return subp - finally: - os.close(fd) - f.close() def warn(msg): @@ -244,7 +258,7 @@ def wait_for_pid(pid, timeout=GLOBAL_TIMEOUT): Used in the test suite to give time the sub process to initialize. """ raise_at = time.time() + timeout - while 1: + while True: if pid in psutil.pids(): # give it one more iteration to allow full initialization time.sleep(0.01) @@ -254,6 +268,23 @@ def wait_for_pid(pid, timeout=GLOBAL_TIMEOUT): raise RuntimeError("Timed out") +def wait_for_file(fname, timeout=GLOBAL_TIMEOUT, delete_file=True): + """Wait for a file to be written on disk.""" + stop_at = time.time() + 3 + while time.time() < stop_at: + try: + with open(fname, "r") as f: + data = f.read() + if not data: + continue + if delete_file: + os.remove(fname) + return data + except IOError: + time.sleep(0.001) + raise RuntimeError("timed out (couldn't read file)") + + def reap_children(search_all=False): """Kill any subprocess started by this test suite and ensure that no zombies stick around to hog resources and create problems when @@ -285,34 +316,57 @@ def reap_children(search_all=False): def check_ip_address(addr, family): """Attempts to check IP address's validity.""" - if not addr: - return - if family in (AF_INET, AF_INET6): - assert isinstance(addr, tuple), addr - ip, port = addr - assert isinstance(port, int), port - if family == AF_INET: - ip = list(map(int, ip.split('.'))) - assert len(ip) == 4, ip - for num in ip: - assert 0 <= num <= 255, ip - assert 0 <= port <= 65535, port - elif family == AF_UNIX: - assert isinstance(addr, (str, None)), addr + if enum and PY3: + assert isinstance(family, enum.IntEnum), family + if family == AF_INET: + octs = [int(x) for x in addr.split('.')] + assert len(octs) == 4, addr + for num in octs: + assert 0 <= num <= 255, addr + if ipaddress: + if not PY3: + addr = unicode(addr) + ipaddress.IPv4Address(addr) + elif family == AF_INET6: + assert isinstance(addr, str), addr + if ipaddress: + if not PY3: + addr = unicode(addr) + ipaddress.IPv6Address(addr) + elif family == psutil.AF_LINK: + assert re.match('([a-fA-F0-9]{2}[:|\-]?){6}', addr) is not None, addr else: raise ValueError("unknown family %r", family) -def check_connection(conn): +def check_connection_ntuple(conn): """Check validity of a connection namedtuple.""" valid_conn_states = [getattr(psutil, x) for x in dir(psutil) if x.startswith('CONN_')] - + assert conn[0] == conn.fd + assert conn[1] == conn.family + assert conn[2] == conn.type + assert conn[3] == conn.laddr + assert conn[4] == conn.raddr + assert conn[5] == conn.status assert conn.type in (SOCK_STREAM, SOCK_DGRAM), repr(conn.type) assert conn.family in (AF_INET, AF_INET6, AF_UNIX), repr(conn.family) assert conn.status in valid_conn_states, conn.status - check_ip_address(conn.laddr, conn.family) - check_ip_address(conn.raddr, conn.family) + + # check IP address and port sanity + for addr in (conn.laddr, conn.raddr): + if not addr: + continue + if conn.family in (AF_INET, AF_INET6): + assert isinstance(addr, tuple), addr + ip, port = addr + assert isinstance(port, int), port + assert 0 <= port <= 65535, port + check_ip_address(ip, conn.family) + elif conn.family == AF_UNIX: + assert isinstance(addr, (str, None)), addr + else: + raise ValueError("unknown family %r", conn.family) if conn.family in (AF_INET, AF_INET6): # actually try to bind the local socket; ignore IPv6 @@ -321,13 +375,12 @@ def check_connection(conn): # and that's rejected by bind() if conn.family == AF_INET: s = socket.socket(conn.family, conn.type) - try: - s.bind((conn.laddr[0], 0)) - except socket.error: - err = sys.exc_info()[1] - if err.errno != errno.EADDRNOTAVAIL: - raise - s.close() + with contextlib.closing(s): + try: + s.bind((conn.laddr[0], 0)) + except socket.error as err: + if err.errno != errno.EADDRNOTAVAIL: + raise elif conn.family == AF_UNIX: assert not conn.raddr, repr(conn.raddr) assert conn.status == psutil.CONN_NONE, conn.status @@ -335,30 +388,22 @@ def check_connection(conn): if getattr(conn, 'fd', -1) != -1: assert conn.fd > 0, conn if hasattr(socket, 'fromfd') and not WINDOWS: - dupsock = None try: - try: - dupsock = socket.fromfd(conn.fd, conn.family, conn.type) - except (socket.error, OSError): - err = sys.exc_info()[1] - if err.args[0] != errno.EBADF: - raise - else: - # python >= 2.5 - if hasattr(dupsock, "family"): - assert dupsock.family == conn.family - assert dupsock.type == conn.type - finally: - if dupsock is not None: - dupsock.close() + dupsock = socket.fromfd(conn.fd, conn.family, conn.type) + except (socket.error, OSError) as err: + if err.args[0] != errno.EBADF: + raise + else: + with contextlib.closing(dupsock): + assert dupsock.family == conn.family + assert dupsock.type == conn.type def safe_remove(file): "Convenience function for removing temporary test files" try: os.remove(file) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno != errno.ENOENT: # file is being used by another process if WINDOWS and isinstance(err, WindowsError) and err.errno == 13: @@ -370,8 +415,7 @@ def safe_rmdir(dir): "Convenience function for removing temporary test directories" try: os.rmdir(dir) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno != errno.ENOENT: raise @@ -394,7 +438,7 @@ def retry_before_failing(ntimes=None): actually failing. """ def decorator(fun): - @wraps(fun) + @functools.wraps(fun) def wrapper(*args, **kwargs): for x in range(ntimes or NO_RETRIES): try: @@ -409,7 +453,7 @@ def wrapper(*args, **kwargs): def skip_on_access_denied(only_if=None): """Decorator to Ignore AccessDenied exceptions.""" def decorator(fun): - @wraps(fun) + @functools.wraps(fun) def wrapper(*args, **kwargs): try: return fun(*args, **kwargs) @@ -419,8 +463,7 @@ def wrapper(*args, **kwargs): raise msg = "%r was skipped because it raised AccessDenied" \ % fun.__name__ - self = args[0] - self.skip(msg) + raise unittest.SkipTest(msg) return wrapper return decorator @@ -428,7 +471,7 @@ def wrapper(*args, **kwargs): def skip_on_not_implemented(only_if=None): """Decorator to Ignore NotImplementedError exceptions.""" def decorator(fun): - @wraps(fun) + @functools.wraps(fun) def wrapper(*args, **kwargs): try: return fun(*args, **kwargs) @@ -438,8 +481,7 @@ def wrapper(*args, **kwargs): raise msg = "%r was skipped because it raised NotImplementedError" \ % fun.__name__ - self = args[0] - self.skip(msg) + raise unittest.SkipTest(msg) return wrapper return decorator @@ -450,13 +492,12 @@ def supports_ipv6(): return False sock = None try: - try: - sock = socket.socket(AF_INET6, SOCK_STREAM) - sock.bind(("::1", 0)) - except (socket.error, socket.gaierror): - return False - else: - return True + sock = socket.socket(AF_INET6, SOCK_STREAM) + sock.bind(("::1", 0)) + except (socket.error, socket.gaierror): + return False + else: + return True finally: if sock is not None: sock.close() @@ -513,12 +554,6 @@ def stop(self): self.join() -# python 2.4 -if not hasattr(subprocess.Popen, 'terminate'): - subprocess.Popen.terminate = \ - lambda self: psutil.Process(self.pid).terminate() - - # =================================================================== # --- System-related API tests # =================================================================== @@ -542,14 +577,16 @@ def test_process_iter(self): self.assertNotIn(sproc.pid, [x.pid for x in psutil.process_iter()]) def test_wait_procs(self): - l = [] - callback = lambda p: l.append(p.pid) + def callback(p): + l.append(p.pid) + l = [] sproc1 = get_test_subprocess() sproc2 = get_test_subprocess() sproc3 = get_test_subprocess() procs = [psutil.Process(x.pid) for x in (sproc1, sproc2, sproc3)] self.assertRaises(ValueError, psutil.wait_procs, procs, timeout=-1) + self.assertRaises(TypeError, psutil.wait_procs, procs, callback=1) t = time.time() gone, alive = psutil.wait_procs(procs, timeout=0.01, callback=callback) @@ -560,10 +597,16 @@ def test_wait_procs(self): for p in alive: self.assertFalse(hasattr(p, 'returncode')) + @retry_before_failing(30) + def test(procs, callback): + gone, alive = psutil.wait_procs(procs, timeout=0.03, + callback=callback) + self.assertEqual(len(gone), 1) + self.assertEqual(len(alive), 2) + return gone, alive + sproc3.terminate() - gone, alive = psutil.wait_procs(procs, timeout=0.03, callback=callback) - self.assertEqual(len(gone), 1) - self.assertEqual(len(alive), 2) + gone, alive = test(procs, callback) self.assertIn(sproc3.pid, [x.pid for x in gone]) if POSIX: self.assertEqual(gone.pop().returncode, signal.SIGTERM) @@ -573,11 +616,17 @@ def test_wait_procs(self): for p in alive: self.assertFalse(hasattr(p, 'returncode')) + @retry_before_failing(30) + def test(procs, callback): + gone, alive = psutil.wait_procs(procs, timeout=0.03, + callback=callback) + self.assertEqual(len(gone), 3) + self.assertEqual(len(alive), 0) + return gone, alive + sproc1.terminate() sproc2.terminate() - gone, alive = psutil.wait_procs(procs, timeout=0.03, callback=callback) - self.assertEqual(len(gone), 3) - self.assertEqual(len(alive), 0) + gone, alive = test(procs, callback) self.assertEqual(set(l), set([sproc1.pid, sproc2.pid, sproc3.pid])) for p in gone: self.assertTrue(hasattr(p, 'returncode')) @@ -605,99 +654,6 @@ def test_PAGESIZE(self): import resource self.assertEqual(os.sysconf("SC_PAGE_SIZE"), resource.getpagesize()) - def test_deprecated_apis(self): - s = socket.socket() - s.bind(('localhost', 0)) - s.listen(1) - warnings.filterwarnings("error") - p = psutil.Process() - try: - # system APIs - self.assertRaises(DeprecationWarning, getattr, psutil, 'NUM_CPUS') - self.assertRaises(DeprecationWarning, getattr, psutil, 'BOOT_TIME') - self.assertRaises(DeprecationWarning, getattr, psutil, - 'TOTAL_PHYMEM') - self.assertRaises(DeprecationWarning, psutil.get_pid_list) - self.assertRaises(DeprecationWarning, psutil.get_users) - self.assertRaises(DeprecationWarning, psutil.virtmem_usage) - self.assertRaises(DeprecationWarning, psutil.used_phymem) - self.assertRaises(DeprecationWarning, psutil.avail_phymem) - self.assertRaises(DeprecationWarning, psutil.total_virtmem) - self.assertRaises(DeprecationWarning, psutil.used_virtmem) - self.assertRaises(DeprecationWarning, psutil.avail_virtmem) - self.assertRaises(DeprecationWarning, psutil.phymem_usage) - self.assertRaises(DeprecationWarning, psutil.get_process_list) - self.assertRaises(DeprecationWarning, psutil.network_io_counters) - if LINUX: - self.assertRaises(DeprecationWarning, psutil.phymem_buffers) - self.assertRaises(DeprecationWarning, psutil.cached_phymem) - - # Process class - names = dir(psutil.Process) - self.assertRaises(DeprecationWarning, p.get_children) - self.assertRaises(DeprecationWarning, p.get_connections) - if 'cpu_affinity' in names: - self.assertRaises(DeprecationWarning, p.get_cpu_affinity) - self.assertRaises(DeprecationWarning, p.get_cpu_percent) - self.assertRaises(DeprecationWarning, p.get_cpu_times) - self.assertRaises(DeprecationWarning, p.getcwd) - self.assertRaises(DeprecationWarning, p.get_ext_memory_info) - if 'io_counters' in names: - self.assertRaises(DeprecationWarning, p.get_io_counters) - if 'ionice' in names: - self.assertRaises(DeprecationWarning, p.get_ionice) - self.assertRaises(DeprecationWarning, p.get_memory_info) - self.assertRaises(DeprecationWarning, p.get_memory_maps) - self.assertRaises(DeprecationWarning, p.get_memory_percent) - self.assertRaises(DeprecationWarning, p.get_nice) - self.assertRaises(DeprecationWarning, p.get_num_ctx_switches) - if 'num_fds' in names: - self.assertRaises(DeprecationWarning, p.get_num_fds) - if 'num_handles' in names: - self.assertRaises(DeprecationWarning, p.get_num_handles) - self.assertRaises(DeprecationWarning, p.get_num_threads) - self.assertRaises(DeprecationWarning, p.get_open_files) - if 'rlimit' in names: - self.assertRaises(DeprecationWarning, p.get_rlimit) - self.assertRaises(DeprecationWarning, p.get_threads) - # ...just to be extra sure: - for name in names: - if name.startswith('get'): - meth = getattr(p, name) - try: - self.assertRaises(DeprecationWarning, meth) - except AssertionError: - self.fail("%s did not raise DeprecationWarning" % name) - - # named tuples - ret = call_until(p.connections, "len(ret) != 0") - self.assertRaises(DeprecationWarning, - getattr, ret[0], 'local_address') - self.assertRaises(DeprecationWarning, - getattr, ret[0], 'remote_address') - finally: - s.close() - warnings.resetwarnings() - - # check value against new APIs - warnings.filterwarnings("ignore") - try: - self.assertEqual(psutil.get_pid_list(), psutil.pids()) - self.assertEqual(psutil.NUM_CPUS, psutil.cpu_count()) - self.assertEqual(psutil.BOOT_TIME, psutil.boot_time()) - self.assertEqual(psutil.TOTAL_PHYMEM, - psutil.virtual_memory().total) - finally: - warnings.resetwarnings() - - def test_deprecated_apis_retval(self): - warnings.filterwarnings("ignore") - try: - self.assertEqual(psutil.total_virtmem(), - psutil.swap_memory().total) - finally: - warnings.resetwarnings() - def test_virtual_memory(self): mem = psutil.virtual_memory() assert mem.total > 0, mem @@ -738,6 +694,8 @@ def test_pid_exists(self): self.assertFalse(psutil.pid_exists(sproc.pid)) self.assertFalse(psutil.pid_exists(-1)) self.assertEqual(psutil.pid_exists(0), 0 in psutil.pids()) + # pid 0 + psutil.pid_exists(0) == 0 in psutil.pids() def test_pid_exists_2(self): reap_children() @@ -753,7 +711,7 @@ def test_pid_exists_2(self): self.fail(pid) pids = range(max(pids) + 5000, max(pids) + 6000) for pid in pids: - self.assertFalse(psutil.pid_exists(pid)) + self.assertFalse(psutil.pid_exists(pid), msg=pid) def test_pids(self): plist = [x.pid for x in psutil.process_iter()] @@ -776,6 +734,11 @@ def test_cpu_count(self): self.assertEqual(logical, len(psutil.cpu_times(percpu=True))) self.assertGreaterEqual(logical, 1) # + if LINUX: + with open("/proc/cpuinfo") as fd: + cpuinfo_data = fd.read() + if "physical id" not in cpuinfo_data: + raise unittest.SkipTest("cpuinfo doesn't include physical id") physical = psutil.cpu_count(logical=False) self.assertGreaterEqual(physical, 1) self.assertGreaterEqual(logical, physical) @@ -824,27 +787,30 @@ def test_sys_per_cpu_times(self): str(times) self.assertEqual(len(psutil.cpu_times(percpu=True)[0]), len(psutil.cpu_times(percpu=False))) - if not WINDOWS: - # CPU times are always supposed to increase over time or - # remain the same but never go backwards, see: - # https://github.com/giampaolo/psutil/issues/392 - last = psutil.cpu_times(percpu=True) - for x in range(100): - new = psutil.cpu_times(percpu=True) - for index in range(len(new)): - newcpu = new[index] - lastcpu = last[index] - for field in newcpu._fields: - new_t = getattr(newcpu, field) - last_t = getattr(lastcpu, field) - self.assertGreaterEqual( - new_t, last_t, msg="%s %s" % (lastcpu, newcpu)) - last = new - def test_sys_per_cpu_times2(self): + # Note: in theory CPU times are always supposed to increase over + # time or remain the same but never go backwards. In practice + # sometimes this is not the case. + # This issue seemd to be afflict Windows: + # https://github.com/giampaolo/psutil/issues/392 + # ...but it turns out also Linux (rarely) behaves the same. + # last = psutil.cpu_times(percpu=True) + # for x in range(100): + # new = psutil.cpu_times(percpu=True) + # for index in range(len(new)): + # newcpu = new[index] + # lastcpu = last[index] + # for field in newcpu._fields: + # new_t = getattr(newcpu, field) + # last_t = getattr(lastcpu, field) + # self.assertGreaterEqual( + # new_t, last_t, msg="%s %s" % (lastcpu, newcpu)) + # last = new + + def test_sys_per_cpu_times_2(self): tot1 = psutil.cpu_times(percpu=True) stop_at = time.time() + 0.1 - while 1: + while True: if time.time() >= stop_at: break tot2 = psutil.cpu_times(percpu=True) @@ -855,42 +821,61 @@ def test_sys_per_cpu_times2(self): return self.fail() - def _test_cpu_percent(self, percent): - self.assertIsInstance(percent, float) - self.assertGreaterEqual(percent, 0.0) - self.assertLessEqual(percent, 100.0 * psutil.cpu_count()) + def _test_cpu_percent(self, percent, last_ret, new_ret): + try: + self.assertIsInstance(percent, float) + self.assertGreaterEqual(percent, 0.0) + self.assertIsNot(percent, -0.0) + self.assertLessEqual(percent, 100.0 * psutil.cpu_count()) + except AssertionError as err: + raise AssertionError("\n%s\nlast=%s\nnew=%s" % ( + err, pprint.pformat(last_ret), pprint.pformat(new_ret))) def test_sys_cpu_percent(self): - psutil.cpu_percent(interval=0.001) - for x in range(1000): - self._test_cpu_percent(psutil.cpu_percent(interval=None)) + last = psutil.cpu_percent(interval=0.001) + for x in range(100): + new = psutil.cpu_percent(interval=None) + self._test_cpu_percent(new, last, new) + last = new def test_sys_per_cpu_percent(self): - self.assertEqual(len(psutil.cpu_percent(interval=0.001, percpu=True)), - psutil.cpu_count()) - for x in range(1000): - percents = psutil.cpu_percent(interval=None, percpu=True) - for percent in percents: - self._test_cpu_percent(percent) + last = psutil.cpu_percent(interval=0.001, percpu=True) + self.assertEqual(len(last), psutil.cpu_count()) + for x in range(100): + new = psutil.cpu_percent(interval=None, percpu=True) + for percent in new: + self._test_cpu_percent(percent, last, new) + last = new def test_sys_cpu_times_percent(self): - psutil.cpu_times_percent(interval=0.001) - for x in range(1000): - cpu = psutil.cpu_times_percent(interval=None) - for percent in cpu: - self._test_cpu_percent(percent) - self._test_cpu_percent(sum(cpu)) + last = psutil.cpu_times_percent(interval=0.001) + for x in range(100): + new = psutil.cpu_times_percent(interval=None) + for percent in new: + self._test_cpu_percent(percent, last, new) + self._test_cpu_percent(sum(new), last, new) + last = new def test_sys_per_cpu_times_percent(self): - self.assertEqual(len(psutil.cpu_times_percent(interval=0.001, - percpu=True)), - psutil.cpu_count()) - for x in range(1000): - cpus = psutil.cpu_times_percent(interval=None, percpu=True) - for cpu in cpus: + last = psutil.cpu_times_percent(interval=0.001, percpu=True) + self.assertEqual(len(last), psutil.cpu_count()) + for x in range(100): + new = psutil.cpu_times_percent(interval=None, percpu=True) + for cpu in new: + for percent in cpu: + self._test_cpu_percent(percent, last, new) + self._test_cpu_percent(sum(cpu), last, new) + last = new + + def test_sys_per_cpu_times_percent_negative(self): + # see: https://github.com/giampaolo/psutil/issues/645 + psutil.cpu_times_percent(percpu=True) + zero_times = [x._make([0 for x in range(len(x._fields))]) + for x in psutil.cpu_times(percpu=True)] + with mock.patch('psutil.cpu_times', return_value=zero_times): + for cpu in psutil.cpu_times_percent(percpu=True): for percent in cpu: - self._test_cpu_percent(percent) - self._test_cpu_percent(sum(cpu)) + self._test_cpu_percent(percent, None, None) @unittest.skipIf(POSIX and not hasattr(os, 'statvfs'), "os.statvfs() function not available on this platform") @@ -917,8 +902,7 @@ def test_disk_usage(self): fname = tempfile.mktemp() try: psutil.disk_usage(fname) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.args[0] != errno.ENOENT: raise else: @@ -972,10 +956,9 @@ def test_disk_partitions(self): if not WINDOWS: try: os.stat(disk.mountpoint) - except OSError: + except OSError as err: # http://mail.python.org/pipermail/python-dev/ # 2012-June/120787.html - err = sys.exc_info()[1] if err.errno not in (errno.EPERM, errno.EACCES): raise else: @@ -998,6 +981,7 @@ def find_mount_point(path): self.assertIn(mount, mounts) psutil.disk_usage(mount) + @skip_on_access_denied() def test_net_connections(self): def check(cons, families, types_): for conn in cons: @@ -1011,6 +995,7 @@ def check(cons, families, types_): continue families, types_ = groups cons = psutil.net_connections(kind) + self.assertEqual(len(cons), len(set(cons))) check(cons, families, types_) def test_net_io_counters(self): @@ -1040,8 +1025,73 @@ def check_ntuple(nt): self.assertTrue(key) check_ntuple(ret[key]) + def test_net_if_addrs(self): + nics = psutil.net_if_addrs() + assert nics, nics + + # Not reliable on all platforms (net_if_addrs() reports more + # interfaces). + # self.assertEqual(sorted(nics.keys()), + # sorted(psutil.net_io_counters(pernic=True).keys())) + + families = set([socket.AF_INET, AF_INET6, psutil.AF_LINK]) + for nic, addrs in nics.items(): + self.assertEqual(len(set(addrs)), len(addrs)) + for addr in addrs: + self.assertIsInstance(addr.family, int) + self.assertIsInstance(addr.address, str) + self.assertIsInstance(addr.netmask, (str, type(None))) + self.assertIsInstance(addr.broadcast, (str, type(None))) + self.assertIn(addr.family, families) + if sys.version_info >= (3, 4): + self.assertIsInstance(addr.family, enum.IntEnum) + if addr.family == socket.AF_INET: + s = socket.socket(addr.family) + with contextlib.closing(s): + s.bind((addr.address, 0)) + elif addr.family == socket.AF_INET6: + info = socket.getaddrinfo( + addr.address, 0, socket.AF_INET6, socket.SOCK_STREAM, + 0, socket.AI_PASSIVE)[0] + af, socktype, proto, canonname, sa = info + s = socket.socket(af, socktype, proto) + with contextlib.closing(s): + s.bind(sa) + for ip in (addr.address, addr.netmask, addr.broadcast): + if ip is not None: + # TODO: skip AF_INET6 for now because I get: + # AddressValueError: Only hex digits permitted in + # u'c6f3%lxcbr0' in u'fe80::c8e0:fff:fe54:c6f3%lxcbr0' + if addr.family != AF_INET6: + check_ip_address(ip, addr.family) + + if BSD or OSX or SUNOS: + if hasattr(socket, "AF_LINK"): + self.assertEqual(psutil.AF_LINK, socket.AF_LINK) + elif LINUX: + self.assertEqual(psutil.AF_LINK, socket.AF_PACKET) + elif WINDOWS: + self.assertEqual(psutil.AF_LINK, -1) + + @unittest.skipIf(TRAVIS, "EPERM on travis") + def test_net_if_stats(self): + nics = psutil.net_if_stats() + assert nics, nics + all_duplexes = (psutil.NIC_DUPLEX_FULL, + psutil.NIC_DUPLEX_HALF, + psutil.NIC_DUPLEX_UNKNOWN) + for nic, stats in nics.items(): + isup, duplex, speed, mtu = stats + self.assertIsInstance(isup, bool) + self.assertIn(duplex, all_duplexes) + self.assertIn(duplex, all_duplexes) + self.assertGreaterEqual(speed, 0) + self.assertGreaterEqual(mtu, 0) + @unittest.skipIf(LINUX and not os.path.exists('/proc/diskstats'), '/proc/diskstats not available on this linux version') + @unittest.skipIf(APPVEYOR, + "can't find any physical disk on Appveyor") def test_disk_io_counters(self): def check_ntuple(nt): self.assertEqual(nt[0], nt.read_count) @@ -1074,7 +1124,8 @@ def check_ntuple(nt): def test_users(self): users = psutil.users() - self.assertNotEqual(users, []) + if not APPVEYOR: + self.assertNotEqual(users, []) for user in users: assert user.name, user user.terminal @@ -1106,28 +1157,48 @@ def test_kill(self): test_pid = sproc.pid p = psutil.Process(test_pid) p.kill() - p.wait() + sig = p.wait() self.assertFalse(psutil.pid_exists(test_pid)) + if POSIX: + self.assertEqual(sig, signal.SIGKILL) def test_terminate(self): sproc = get_test_subprocess(wait=True) test_pid = sproc.pid p = psutil.Process(test_pid) p.terminate() - p.wait() + sig = p.wait() self.assertFalse(psutil.pid_exists(test_pid)) + if POSIX: + self.assertEqual(sig, signal.SIGTERM) def test_send_signal(self): - if POSIX: - sig = signal.SIGKILL - else: - sig = signal.SIGTERM + sig = signal.SIGKILL if POSIX else signal.SIGTERM sproc = get_test_subprocess() - test_pid = sproc.pid - p = psutil.Process(test_pid) + p = psutil.Process(sproc.pid) p.send_signal(sig) - p.wait() - self.assertFalse(psutil.pid_exists(test_pid)) + exit_sig = p.wait() + self.assertFalse(psutil.pid_exists(p.pid)) + if POSIX: + self.assertEqual(exit_sig, sig) + # + sproc = get_test_subprocess() + p = psutil.Process(sproc.pid) + p.send_signal(sig) + with mock.patch('psutil.os.kill', + side_effect=OSError(errno.ESRCH, "")) as fun: + with self.assertRaises(psutil.NoSuchProcess): + p.send_signal(sig) + assert fun.called + # + sproc = get_test_subprocess() + p = psutil.Process(sproc.pid) + p.send_signal(sig) + with mock.patch('psutil.os.kill', + side_effect=OSError(errno.EPERM, "")) as fun: + with self.assertRaises(psutil.AccessDenied): + p.send_signal(sig) + assert fun.called def test_wait(self): # check exit code signal @@ -1182,7 +1253,7 @@ def test_wait_non_children(self): # test wait() against processes which are not our children code = "import sys;" code += "from subprocess import Popen, PIPE;" - code += "cmd = ['%s', '-c', 'import time; time.sleep(2)'];" % PYTHON + code += "cmd = ['%s', '-c', 'import time; time.sleep(60)'];" % PYTHON code += "sp = Popen(cmd, stdout=PIPE);" code += "sys.stdout.write(str(sp.pid));" sproc = get_test_subprocess([PYTHON, "-c", code], @@ -1205,7 +1276,7 @@ def test_wait_timeout_0(self): self.assertRaises(psutil.TimeoutExpired, p.wait, 0) p.kill() stop_at = time.time() + 2 - while 1: + while True: try: code = p.wait(0) except psutil.TimeoutExpired: @@ -1292,9 +1363,8 @@ def test_io_counters(self): p = psutil.Process() # test reads io1 = p.io_counters() - f = open(PYTHON, 'rb') - f.read() - f.close() + with open(PYTHON, 'rb') as f: + f.read() io2 = p.io_counters() if not BSD: assert io2.read_count > io1.read_count, (io1, io2) @@ -1303,12 +1373,11 @@ def test_io_counters(self): assert io2.write_bytes >= io1.write_bytes, (io1, io2) # test writes io1 = p.io_counters() - f = tempfile.TemporaryFile(prefix=TESTFILE_PREFIX) - if PY3: - f.write(bytes("x" * 1000000, 'ascii')) - else: - f.write("x" * 1000000) - f.close() + with tempfile.TemporaryFile(prefix=TESTFILE_PREFIX) as f: + if PY3: + f.write(bytes("x" * 1000000, 'ascii')) + else: + f.write("x" * 1000000) io2 = p.io_counters() assert io2.write_count >= io1.write_count, (io1, io2) assert io2.write_bytes >= io1.write_bytes, (io1, io2) @@ -1330,6 +1399,8 @@ def test_ionice(self): try: p.ionice(2) ioclass, value = p.ionice() + if enum is not None: + self.assertIsInstance(ioclass, enum.IntEnum) self.assertEqual(ioclass, 2) self.assertEqual(value, 4) # @@ -1346,12 +1417,26 @@ def test_ionice(self): ioclass, value = p.ionice() self.assertEqual(ioclass, 2) self.assertEqual(value, 7) + # self.assertRaises(ValueError, p.ionice, 2, 10) + self.assertRaises(ValueError, p.ionice, 2, -1) + self.assertRaises(ValueError, p.ionice, 4) + self.assertRaises(TypeError, p.ionice, 2, "foo") + self.assertRaisesRegexp( + ValueError, "can't specify value with IOPRIO_CLASS_NONE", + p.ionice, psutil.IOPRIO_CLASS_NONE, 1) + self.assertRaisesRegexp( + ValueError, "can't specify value with IOPRIO_CLASS_IDLE", + p.ionice, psutil.IOPRIO_CLASS_IDLE, 1) + self.assertRaisesRegexp( + ValueError, "'ioclass' argument must be specified", + p.ionice, value=1) finally: p.ionice(IOPRIO_CLASS_NONE) else: p = psutil.Process() original = p.ionice() + self.assertIsInstance(original, int) try: value = 0 # very low if original == value: @@ -1370,14 +1455,13 @@ def test_rlimit_get(self): import resource p = psutil.Process(os.getpid()) names = [x for x in dir(psutil) if x.startswith('RLIMIT')] - assert names + assert names, names for name in names: value = getattr(psutil, name) self.assertGreaterEqual(value, 0) if name in dir(resource): self.assertEqual(value, getattr(resource, name)) - self.assertEqual(p.rlimit(value), - resource.getrlimit(value)) + self.assertEqual(p.rlimit(value), resource.getrlimit(value)) else: ret = p.rlimit(value) self.assertEqual(len(ret), 2) @@ -1391,6 +1475,12 @@ def test_rlimit_set(self): p = psutil.Process(sproc.pid) p.rlimit(psutil.RLIMIT_NOFILE, (5, 5)) self.assertEqual(p.rlimit(psutil.RLIMIT_NOFILE), (5, 5)) + # If pid is 0 prlimit() applies to the calling process and + # we don't want that. + with self.assertRaises(ValueError): + psutil._psplatform.Process(0).rlimit(0) + with self.assertRaises(ValueError): + p.rlimit(psutil.RLIMIT_NOFILE, (5, 5, 5)) def test_num_threads(self): # on certain platforms such as Linux we might test for exact @@ -1525,7 +1615,7 @@ def test_exe(self): self.assertEqual(exe.replace(ver, ''), PYTHON.replace(ver, '')) def test_cmdline(self): - cmdline = [PYTHON, "-c", "import time; time.sleep(2)"] + cmdline = [PYTHON, "-c", "import time; time.sleep(60)"] sproc = get_test_subprocess(cmdline, wait=True) self.assertEqual(' '.join(psutil.Process(sproc.pid).cmdline()), ' '.join(cmdline)) @@ -1536,6 +1626,30 @@ def test_name(self): pyexe = os.path.basename(os.path.realpath(sys.executable)).lower() assert pyexe.startswith(name), (pyexe, name) + @unittest.skipUnless(POSIX, "posix only") + # TODO: add support for other compilers + @unittest.skipUnless(which("gcc"), "gcc not available") + def test_prog_w_funky_name(self): + # Test that name(), exe() and cmdline() correctly handle programs + # with funky chars such as spaces and ")", see: + # https://github.com/giampaolo/psutil/issues/628 + funky_name = "/tmp/foo bar )" + _, c_file = tempfile.mkstemp(prefix='psutil-', suffix='.c', dir="/tmp") + self.addCleanup(lambda: safe_remove(c_file)) + self.addCleanup(lambda: safe_remove(funky_name)) + with open(c_file, "w") as f: + f.write("void main() { pause(); }") + subprocess.check_call(["gcc", c_file, "-o", funky_name]) + sproc = get_test_subprocess( + [funky_name, "arg1", "arg2", "", "arg3", ""]) + p = psutil.Process(sproc.pid) + # ...in order to try to prevent occasional failures on travis + wait_for_pid(p.pid) + self.assertEqual(p.name(), "foo bar )") + self.assertEqual(p.exe(), "/tmp/foo bar )") + self.assertEqual( + p.cmdline(), ["/tmp/foo bar )", "arg1", "arg2", "", "arg3", ""]) + @unittest.skipUnless(POSIX, 'posix only') def test_uids(self): p = psutil.Process() @@ -1567,7 +1681,12 @@ def test_nice(self): self.assertRaises(TypeError, p.nice, "str") if WINDOWS: try: - self.assertEqual(p.nice(), psutil.NORMAL_PRIORITY_CLASS) + init = p.nice() + if sys.version_info > (3, 4): + self.assertIsInstance(init, enum.IntEnum) + else: + self.assertIsInstance(init, int) + self.assertEqual(init, psutil.NORMAL_PRIORITY_CLASS) p.nice(psutil.HIGH_PRIORITY_CLASS) self.assertEqual(p.nice(), psutil.HIGH_PRIORITY_CLASS) p.nice(psutil.NORMAL_PRIORITY_CLASS) @@ -1576,17 +1695,16 @@ def test_nice(self): p.nice(psutil.NORMAL_PRIORITY_CLASS) else: try: - try: - first_nice = p.nice() - p.nice(1) - self.assertEqual(p.nice(), 1) - # going back to previous nice value raises - # AccessDenied on OSX - if not OSX: - p.nice(0) - self.assertEqual(p.nice(), 0) - except psutil.AccessDenied: - pass + first_nice = p.nice() + p.nice(1) + self.assertEqual(p.nice(), 1) + # going back to previous nice value raises + # AccessDenied on OSX + if not OSX: + p.nice(0) + self.assertEqual(p.nice(), 0) + except psutil.AccessDenied: + pass finally: try: p.nice(first_nice) @@ -1603,6 +1721,11 @@ def test_username(self): if POSIX: import pwd self.assertEqual(p.username(), pwd.getpwuid(os.getuid()).pw_name) + with mock.patch("psutil.pwd.getpwuid", + side_effect=KeyError) as fun: + p.username() == str(p.uids().real) + assert fun.called + elif WINDOWS and 'USERNAME' in os.environ: expected_username = os.environ['USERNAME'] expected_domain = os.environ['USERDOMAIN'] @@ -1618,18 +1741,20 @@ def test_cwd(self): self.assertEqual(p.cwd(), os.getcwd()) def test_cwd_2(self): - cmd = [PYTHON, "-c", "import os, time; os.chdir('..'); time.sleep(2)"] + cmd = [PYTHON, "-c", "import os, time; os.chdir('..'); time.sleep(60)"] sproc = get_test_subprocess(cmd, wait=True) p = psutil.Process(sproc.pid) call_until(p.cwd, "ret == os.path.dirname(os.getcwd())") - @unittest.skipUnless(WINDOWS or LINUX, 'not available on this platform') + @unittest.skipUnless(WINDOWS or LINUX or BSD, + 'not available on this platform') @unittest.skipIf(LINUX and TRAVIS, "unknown failure on travis") def test_cpu_affinity(self): p = psutil.Process() initial = p.cpu_affinity() if hasattr(os, "sched_getaffinity"): self.assertEqual(initial, list(os.sched_getaffinity(p.pid))) + self.assertEqual(len(initial), len(set(initial))) all_cpus = list(range(len(psutil.cpu_percent(percpu=True)))) # setting on travis doesn't seem to work (always return all # CPUs on get): @@ -1649,25 +1774,33 @@ def test_cpu_affinity(self): # self.assertRaises(TypeError, p.cpu_affinity, 1) p.cpu_affinity(initial) + # it should work with all iterables, not only lists + p.cpu_affinity(set(all_cpus)) + p.cpu_affinity(tuple(all_cpus)) invalid_cpu = [len(psutil.cpu_times(percpu=True)) + 10] self.assertRaises(ValueError, p.cpu_affinity, invalid_cpu) self.assertRaises(ValueError, p.cpu_affinity, range(10000, 11000)) + self.assertRaises(TypeError, p.cpu_affinity, [0, "1"]) + # TODO + @unittest.skipIf(BSD, "broken on BSD, see #595") + @unittest.skipIf(APPVEYOR, + "can't find any process file on Appveyor") def test_open_files(self): # current process p = psutil.Process() files = p.open_files() self.assertFalse(TESTFN in files) - f = open(TESTFN, 'w') - call_until(p.open_files, "len(ret) != %i" % len(files)) - filenames = [x.path for x in p.open_files()] - self.assertIn(TESTFN, filenames) - f.close() + with open(TESTFN, 'w'): + # give the kernel some time to see the new file + call_until(p.open_files, "len(ret) != %i" % len(files)) + filenames = [x.path for x in p.open_files()] + self.assertIn(TESTFN, filenames) for file in filenames: assert os.path.isfile(file), file # another process - cmdline = "import time; f = open(r'%s', 'r'); time.sleep(2);" % TESTFN + cmdline = "import time; f = open(r'%s', 'r'); time.sleep(60);" % TESTFN sproc = get_test_subprocess([PYTHON, "-c", cmdline], wait=True) p = psutil.Process(sproc.pid) @@ -1681,27 +1814,30 @@ def test_open_files(self): for file in filenames: assert os.path.isfile(file), file + # TODO + @unittest.skipIf(BSD, "broken on BSD, see #595") + @unittest.skipIf(APPVEYOR, + "can't find any process file on Appveyor") def test_open_files2(self): # test fd and path fields - fileobj = open(TESTFN, 'w') - p = psutil.Process() - for path, fd in p.open_files(): - if path == fileobj.name or fd == fileobj.fileno(): - break - else: - self.fail("no file found; files=%s" % repr(p.open_files())) - self.assertEqual(path, fileobj.name) - if WINDOWS: - self.assertEqual(fd, -1) - else: - self.assertEqual(fd, fileobj.fileno()) - # test positions - ntuple = p.open_files()[0] - self.assertEqual(ntuple[0], ntuple.path) - self.assertEqual(ntuple[1], ntuple.fd) - # test file is gone - fileobj.close() - self.assertTrue(fileobj.name not in p.open_files()) + with open(TESTFN, 'w') as fileobj: + p = psutil.Process() + for path, fd in p.open_files(): + if path == fileobj.name or fd == fileobj.fileno(): + break + else: + self.fail("no file found; files=%s" % repr(p.open_files())) + self.assertEqual(path, fileobj.name) + if WINDOWS: + self.assertEqual(fd, -1) + else: + self.assertEqual(fd, fileobj.fileno()) + # test positions + ntuple = p.open_files()[0] + self.assertEqual(ntuple[0], ntuple.path) + self.assertEqual(ntuple[1], ntuple.fd) + # test file is gone + self.assertTrue(fileobj.name not in p.open_files()) def compare_proc_sys_cons(self, pid, proc_cons): from psutil._common import pconn @@ -1714,199 +1850,172 @@ def compare_proc_sys_cons(self, pid, proc_cons): proc_cons = [pconn(*[-1] + list(x[1:])) for x in proc_cons] self.assertEqual(sorted(proc_cons), sorted(sys_cons)) - def test_connection_constants(self): - ints = [] - strs = [] - for name in dir(psutil): - if name.startswith('CONN_'): - num = getattr(psutil, name) - str_ = str(num) - assert str_.isupper(), str_ - assert str_ not in strs, str_ - assert num not in ints, num - ints.append(num) - strs.append(str_) - if SUNOS: - psutil.CONN_IDLE - psutil.CONN_BOUND - if WINDOWS: - psutil.CONN_DELETE_TCB - + @skip_on_access_denied(only_if=OSX) def test_connections(self): - arg = "import socket, time;" \ - "s = socket.socket();" \ - "s.bind(('127.0.0.1', 0));" \ - "s.listen(1);" \ - "conn, addr = s.accept();" \ - "time.sleep(2);" - sproc = get_test_subprocess([PYTHON, "-c", arg]) - p = psutil.Process(sproc.pid) - cons = call_until(p.connections, "len(ret) != 0") - self.assertEqual(len(cons), 1) - con = cons[0] - check_connection(con) - self.assertEqual(con.family, AF_INET) - self.assertEqual(con.type, SOCK_STREAM) - self.assertEqual(con.status, psutil.CONN_LISTEN, con.status) - self.assertEqual(con.laddr[0], '127.0.0.1') - self.assertEqual(con.raddr, ()) - # test positions - self.assertEqual(con[0], con.fd) - self.assertEqual(con[1], con.family) - self.assertEqual(con[2], con.type) - self.assertEqual(con[3], con.laddr) - self.assertEqual(con[4], con.raddr) - self.assertEqual(con[5], con.status) - # test kind arg - self.assertRaises(ValueError, p.connections, 'foo') - # compare against system-wide connections - self.compare_proc_sys_cons(p.pid, cons) - - @unittest.skipUnless(supports_ipv6(), 'IPv6 is not supported') - def test_connections_ipv6(self): - s = socket.socket(AF_INET6, SOCK_STREAM) - self.addCleanup(s.close) - s.bind(('::1', 0)) - s.listen(1) - cons = psutil.Process().connections() - self.assertEqual(len(cons), 1) - self.assertEqual(cons[0].laddr[0], '::1') - self.compare_proc_sys_cons(os.getpid(), cons) - - @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), - 'AF_UNIX is not supported') - def test_connections_unix(self): - def check(type): - safe_remove(TESTFN) - sock = socket.socket(AF_UNIX, type) - try: - sock.bind(TESTFN) - cons = psutil.Process().connections(kind='unix') - conn = cons[0] - check_connection(conn) - if conn.fd != -1: # != sunos and windows - self.assertEqual(conn.fd, sock.fileno()) - self.assertEqual(conn.family, AF_UNIX) - self.assertEqual(conn.type, type) - self.assertEqual(conn.laddr, TESTFN) - if not SUNOS: - # XXX Solaris can't retrieve system-wide UNIX - # sockets. - self.compare_proc_sys_cons(os.getpid(), cons) - finally: - sock.close() - - check(SOCK_STREAM) - check(SOCK_DGRAM) - - @unittest.skipUnless(hasattr(socket, "fromfd"), - 'socket.fromfd() is not availble') - @unittest.skipIf(WINDOWS or SUNOS, - 'connection fd not available on this platform') - def test_connection_fromfd(self): - sock = socket.socket() - sock.bind(('localhost', 0)) - sock.listen(1) - p = psutil.Process() - for conn in p.connections(): - if conn.fd == sock.fileno(): - break - else: - sock.close() - self.fail("couldn't find socket fd") - dupsock = socket.fromfd(conn.fd, conn.family, conn.type) - try: - self.assertEqual(dupsock.getsockname(), conn.laddr) - self.assertNotEqual(sock.fileno(), dupsock.fileno()) - finally: - sock.close() - dupsock.close() + def check_conn(proc, conn, family, type, laddr, raddr, status, kinds): + all_kinds = ("all", "inet", "inet4", "inet6", "tcp", "tcp4", + "tcp6", "udp", "udp4", "udp6") + check_connection_ntuple(conn) + self.assertEqual(conn.family, family) + self.assertEqual(conn.type, type) + self.assertEqual(conn.laddr, laddr) + self.assertEqual(conn.raddr, raddr) + self.assertEqual(conn.status, status) + for kind in all_kinds: + cons = proc.connections(kind=kind) + if kind in kinds: + self.assertNotEqual(cons, []) + else: + self.assertEqual(cons, []) + # compare against system-wide connections + # XXX Solaris can't retrieve system-wide UNIX + # sockets. + if not SUNOS: + self.compare_proc_sys_cons(proc.pid, [conn]) - def test_connections_all(self): tcp_template = textwrap.dedent(""" - import socket + import socket, time s = socket.socket($family, socket.SOCK_STREAM) s.bind(('$addr', 0)) s.listen(1) - conn, addr = s.accept() + with open('$testfn', 'w') as f: + f.write(str(s.getsockname()[:2])) + time.sleep(60) """) udp_template = textwrap.dedent(""" import socket, time s = socket.socket($family, socket.SOCK_DGRAM) s.bind(('$addr', 0)) - time.sleep(2) + with open('$testfn', 'w') as f: + f.write(str(s.getsockname()[:2])) + time.sleep(60) """) from string import Template + testfile = os.path.basename(TESTFN) tcp4_template = Template(tcp_template).substitute( - family=int(AF_INET), addr="127.0.0.1") + family=int(AF_INET), addr="127.0.0.1", testfn=testfile) udp4_template = Template(udp_template).substitute( - family=int(AF_INET), addr="127.0.0.1") + family=int(AF_INET), addr="127.0.0.1", testfn=testfile) tcp6_template = Template(tcp_template).substitute( - family=int(AF_INET6), addr="::1") + family=int(AF_INET6), addr="::1", testfn=testfile) udp6_template = Template(udp_template).substitute( - family=int(AF_INET6), addr="::1") + family=int(AF_INET6), addr="::1", testfn=testfile) # launch various subprocess instantiating a socket of various # families and types to enrich psutil results tcp4_proc = pyrun(tcp4_template) + tcp4_addr = eval(wait_for_file(testfile)) udp4_proc = pyrun(udp4_template) + udp4_addr = eval(wait_for_file(testfile)) if supports_ipv6(): tcp6_proc = pyrun(tcp6_template) + tcp6_addr = eval(wait_for_file(testfile)) udp6_proc = pyrun(udp6_template) + udp6_addr = eval(wait_for_file(testfile)) else: tcp6_proc = None udp6_proc = None - - # check matches against subprocesses just created - all_kinds = ("all", "inet", "inet4", "inet6", "tcp", "tcp4", "tcp6", - "udp", "udp4", "udp6") - - def check_conn(proc, conn, family, type, laddr, raddr, status, kinds): - self.assertEqual(conn.family, family) - self.assertEqual(conn.type, type) - self.assertIn(conn.laddr[0], laddr) - self.assertEqual(conn.raddr, raddr) - self.assertEqual(conn.status, status) - for kind in all_kinds: - cons = proc.connections(kind=kind) - if kind in kinds: - self.assertNotEqual(cons, []) - else: - self.assertEqual(cons, []) + tcp6_addr = None + udp6_addr = None for p in psutil.Process().children(): cons = p.connections() + self.assertEqual(len(cons), 1) for conn in cons: # TCP v4 if p.pid == tcp4_proc.pid: - check_conn(p, conn, AF_INET, SOCK_STREAM, "127.0.0.1", (), + check_conn(p, conn, AF_INET, SOCK_STREAM, tcp4_addr, (), psutil.CONN_LISTEN, ("all", "inet", "inet4", "tcp", "tcp4")) # UDP v4 elif p.pid == udp4_proc.pid: - check_conn(p, conn, AF_INET, SOCK_DGRAM, "127.0.0.1", (), + check_conn(p, conn, AF_INET, SOCK_DGRAM, udp4_addr, (), psutil.CONN_NONE, ("all", "inet", "inet4", "udp", "udp4")) # TCP v6 elif p.pid == getattr(tcp6_proc, "pid", None): - check_conn(p, conn, AF_INET6, SOCK_STREAM, ("::", "::1"), - (), psutil.CONN_LISTEN, + check_conn(p, conn, AF_INET6, SOCK_STREAM, tcp6_addr, (), + psutil.CONN_LISTEN, ("all", "inet", "inet6", "tcp", "tcp6")) # UDP v6 elif p.pid == getattr(udp6_proc, "pid", None): - check_conn(p, conn, AF_INET6, SOCK_DGRAM, ("::", "::1"), - (), psutil.CONN_NONE, + check_conn(p, conn, AF_INET6, SOCK_DGRAM, udp6_addr, (), + psutil.CONN_NONE, ("all", "inet", "inet6", "udp", "udp6")) + @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), + 'AF_UNIX is not supported') + @skip_on_access_denied(only_if=OSX) + def test_connections_unix(self): + def check(type): + safe_remove(TESTFN) + sock = socket.socket(AF_UNIX, type) + with contextlib.closing(sock): + sock.bind(TESTFN) + cons = psutil.Process().connections(kind='unix') + conn = cons[0] + check_connection_ntuple(conn) + if conn.fd != -1: # != sunos and windows + self.assertEqual(conn.fd, sock.fileno()) + self.assertEqual(conn.family, AF_UNIX) + self.assertEqual(conn.type, type) + self.assertEqual(conn.laddr, TESTFN) + if not SUNOS: + # XXX Solaris can't retrieve system-wide UNIX + # sockets. + self.compare_proc_sys_cons(os.getpid(), cons) + + check(SOCK_STREAM) + check(SOCK_DGRAM) + + @unittest.skipUnless(hasattr(socket, "fromfd"), + 'socket.fromfd() is not availble') + @unittest.skipIf(WINDOWS or SUNOS, + 'connection fd not available on this platform') + def test_connection_fromfd(self): + with contextlib.closing(socket.socket()) as sock: + sock.bind(('localhost', 0)) + sock.listen(1) + p = psutil.Process() + for conn in p.connections(): + if conn.fd == sock.fileno(): + break + else: + self.fail("couldn't find socket fd") + dupsock = socket.fromfd(conn.fd, conn.family, conn.type) + with contextlib.closing(dupsock): + self.assertEqual(dupsock.getsockname(), conn.laddr) + self.assertNotEqual(sock.fileno(), dupsock.fileno()) + + def test_connection_constants(self): + ints = [] + strs = [] + for name in dir(psutil): + if name.startswith('CONN_'): + num = getattr(psutil, name) + str_ = str(num) + assert str_.isupper(), str_ + assert str_ not in strs, str_ + assert num not in ints, num + ints.append(num) + strs.append(str_) + if SUNOS: + psutil.CONN_IDLE + psutil.CONN_BOUND + if WINDOWS: + psutil.CONN_DELETE_TCB + @unittest.skipUnless(POSIX, 'posix only') def test_num_fds(self): p = psutil.Process() start = p.num_fds() file = open(TESTFN, 'w') + self.addCleanup(file.close) self.assertEqual(p.num_fds(), start + 1) sock = socket.socket() + self.addCleanup(sock.close) self.assertEqual(p.num_fds(), start + 2) file.close() sock.close() @@ -1951,14 +2060,14 @@ def test_children_recursive(self): # A (parent) -> B (child) -> C (grandchild) s = "import subprocess, os, sys, time;" s += "PYTHON = os.path.realpath(sys.executable);" - s += "cmd = [PYTHON, '-c', 'import time; time.sleep(2);'];" + s += "cmd = [PYTHON, '-c', 'import time; time.sleep(60);'];" s += "subprocess.Popen(cmd);" - s += "time.sleep(2);" + s += "time.sleep(60);" get_test_subprocess(cmd=[PYTHON, "-c", s]) p = psutil.Process() self.assertEqual(len(p.children(recursive=False)), 1) # give the grandchild some time to start - stop_at = time.time() + 1.5 + stop_at = time.time() + GLOBAL_TIMEOUT while time.time() < stop_at: children = p.children(recursive=True) if len(children) > 1: @@ -1969,8 +2078,7 @@ def test_children_recursive(self): def test_children_duplicates(self): # find the process which has the highest number of children - from psutil._compat import defaultdict - table = defaultdict(int) + table = collections.defaultdict(int) for p in psutil.process_iter(): try: table[p.ppid()] += 1 @@ -2021,95 +2129,140 @@ def test_halfway_terminated_process(self): # Refers to Issue #15 sproc = get_test_subprocess() p = psutil.Process(sproc.pid) - p.kill() + p.terminate() p.wait() + if WINDOWS: + wait_for_pid(p.pid) + self.assertFalse(p.is_running()) + self.assertFalse(p.pid in psutil.pids()) excluded_names = ['pid', 'is_running', 'wait', 'create_time'] if LINUX and not RLIMIT_SUPPORT: excluded_names.append('rlimit') for name in dir(p): - if (name.startswith('_') - or name.startswith('get') # deprecated APIs - or name.startswith('set') # deprecated APIs - or name in excluded_names): + if (name.startswith('_') or + name in excluded_names): continue try: meth = getattr(p, name) # get/set methods if name == 'nice': if POSIX: - meth(1) + ret = meth(1) else: - meth(psutil.NORMAL_PRIORITY_CLASS) + ret = meth(psutil.NORMAL_PRIORITY_CLASS) elif name == 'ionice': - meth() - meth(2) + ret = meth() + ret = meth(2) elif name == 'rlimit': - meth(psutil.RLIMIT_NOFILE) - meth(psutil.RLIMIT_NOFILE, (5, 5)) + ret = meth(psutil.RLIMIT_NOFILE) + ret = meth(psutil.RLIMIT_NOFILE, (5, 5)) elif name == 'cpu_affinity': - meth() - meth([0]) + ret = meth() + ret = meth([0]) elif name == 'send_signal': - meth(signal.SIGTERM) + ret = meth(signal.SIGTERM) else: - meth() + ret = meth() + except psutil.ZombieProcess: + self.fail("ZombieProcess for %r was not supposed to happen" % + name) except psutil.NoSuchProcess: pass except NotImplementedError: pass else: - self.fail("NoSuchProcess exception not raised for %r" % name) - - self.assertFalse(p.is_running()) + self.fail( + "NoSuchProcess exception not raised for %r, retval=%s" % ( + name, ret)) @unittest.skipUnless(POSIX, 'posix only') def test_zombie_process(self): + def succeed_or_zombie_p_exc(fun, *args, **kwargs): + try: + fun(*args, **kwargs) + except (psutil.ZombieProcess, psutil.AccessDenied): + pass + # Note: in this test we'll be creating two sub processes. # Both of them are supposed to be freed / killed by # reap_children() as they are attributable to 'us' # (os.getpid()) via children(recursive=True). src = textwrap.dedent("""\ - import os, sys, time, socket + import os, sys, time, socket, contextlib child_pid = os.fork() if child_pid > 0: time.sleep(3000) else: # this is the zombie process s = socket.socket(socket.AF_UNIX) - s.connect('%s') - if sys.version_info < (3, ): - pid = str(os.getpid()) - else: - pid = bytes(str(os.getpid()), 'ascii') - s.sendall(pid) - s.close() + with contextlib.closing(s): + s.connect('%s') + if sys.version_info < (3, ): + pid = str(os.getpid()) + else: + pid = bytes(str(os.getpid()), 'ascii') + s.sendall(pid) """ % TESTFN) - sock = None - try: - sock = socket.socket(socket.AF_UNIX) - sock.settimeout(GLOBAL_TIMEOUT) - sock.bind(TESTFN) - sock.listen(1) - pyrun(src) - conn, _ = sock.accept() - select.select([conn.fileno()], [], [], GLOBAL_TIMEOUT) - zpid = int(conn.recv(1024)) - zproc = psutil.Process(zpid) - # Make sure we can re-instantiate the process after its - # status changed to zombie and at least be able to - # query its status. - # XXX should we also assume ppid should be querable? - call_until(lambda: zproc.status(), "ret == psutil.STATUS_ZOMBIE") - self.assertTrue(psutil.pid_exists(zpid)) - zproc = psutil.Process(zpid) - descendants = [x.pid for x in psutil.Process().children( - recursive=True)] - self.assertIn(zpid, descendants) - finally: - if sock is not None: - sock.close() - reap_children(search_all=True) + with contextlib.closing(socket.socket(socket.AF_UNIX)) as sock: + try: + sock.settimeout(GLOBAL_TIMEOUT) + sock.bind(TESTFN) + sock.listen(1) + pyrun(src) + conn, _ = sock.accept() + select.select([conn.fileno()], [], [], GLOBAL_TIMEOUT) + zpid = int(conn.recv(1024)) + zproc = psutil.Process(zpid) + call_until(lambda: zproc.status(), + "ret == psutil.STATUS_ZOMBIE") + # A zombie process should always be instantiable + zproc = psutil.Process(zpid) + # ...and at least its status always be querable + self.assertEqual(zproc.status(), psutil.STATUS_ZOMBIE) + # ...and it should be considered 'running' + self.assertTrue(zproc.is_running()) + # ...and as_dict() shouldn't crash + zproc.as_dict() + if hasattr(zproc, "rlimit"): + succeed_or_zombie_p_exc(zproc.rlimit, psutil.RLIMIT_NOFILE) + succeed_or_zombie_p_exc(zproc.rlimit, psutil.RLIMIT_NOFILE, + (5, 5)) + # set methods + succeed_or_zombie_p_exc(zproc.parent) + if hasattr(zproc, 'cpu_affinity'): + succeed_or_zombie_p_exc(zproc.cpu_affinity, [0]) + succeed_or_zombie_p_exc(zproc.nice, 0) + if hasattr(zproc, 'ionice'): + if LINUX: + succeed_or_zombie_p_exc(zproc.ionice, 2, 0) + else: + succeed_or_zombie_p_exc(zproc.ionice, 0) # Windows + if hasattr(zproc, 'rlimit'): + succeed_or_zombie_p_exc(zproc.rlimit, + psutil.RLIMIT_NOFILE, (5, 5)) + succeed_or_zombie_p_exc(zproc.suspend) + succeed_or_zombie_p_exc(zproc.resume) + succeed_or_zombie_p_exc(zproc.terminate) + succeed_or_zombie_p_exc(zproc.kill) + + # ...its parent should 'see' it + # edit: not true on BSD and OSX + # descendants = [x.pid for x in psutil.Process().children( + # recursive=True)] + # self.assertIn(zpid, descendants) + # XXX should we also assume ppid be usable? Note: this + # would be an important use case as the only way to get + # rid of a zombie is to kill its parent. + # self.assertEqual(zpid.ppid(), os.getpid()) + # ...and all other APIs should be able to deal with it + self.assertTrue(psutil.pid_exists(zpid)) + self.assertIn(zpid, psutil.pids()) + self.assertIn(zpid, [x.pid for x in psutil.process_iter()]) + psutil._pmap = {} + self.assertIn(zpid, [x.pid for x in psutil.process_iter()]) + finally: + reap_children(search_all=True) def test_pid_0(self): # Process(0) is supposed to work on all platforms except Linux @@ -2127,6 +2280,10 @@ def test_pid_0(self): except psutil.AccessDenied: pass + self.assertRaisesRegexp( + ValueError, "preventing sending signal to process with PID 0", + p.send_signal, signal.SIGTERM) + self.assertIn(p.ppid(), (0, 1)) # self.assertEqual(p.exe(), "") p.cmdline() @@ -2140,7 +2297,6 @@ def test_pid_0(self): except psutil.AccessDenied: pass - # username property try: if POSIX: self.assertEqual(p.username(), 'root') @@ -2159,7 +2315,7 @@ def test_Popen(self): # XXX this test causes a ResourceWarning on Python 3 because # psutil.__subproc instance doesn't get propertly freed. # Not sure what to do though. - cmd = [PYTHON, "-c", "import time; time.sleep(2);"] + cmd = [PYTHON, "-c", "import time; time.sleep(60);"] proc = psutil.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: @@ -2167,6 +2323,7 @@ def test_Popen(self): proc.stdin self.assertTrue(hasattr(proc, 'name')) self.assertTrue(hasattr(proc, 'stdin')) + self.assertTrue(dir(proc)) self.assertRaises(AttributeError, getattr, proc, 'foo') finally: proc.kill() @@ -2201,11 +2358,6 @@ def test_fetch_all(self): for name in dir(psutil.Process): if name.startswith("_"): continue - if name.startswith("get"): - # deprecated APIs - continue - if name.startswith("set_"): - continue if name in excluded_names: continue attrs.append(name) @@ -2230,8 +2382,7 @@ def test_fetch_all(self): msg = "%r was skipped because not implemented" % ( self.__class__.__name__ + '.test_' + name) warn(msg) - except (psutil.NoSuchProcess, psutil.AccessDenied): - err = sys.exc_info()[1] + except (psutil.NoSuchProcess, psutil.AccessDenied) as err: self.assertEqual(err.pid, p.pid) if err.name: # make sure exception's name attr is set @@ -2244,8 +2395,7 @@ def test_fetch_all(self): assert ret, ret meth = getattr(self, name) meth(ret) - except Exception: - err = sys.exc_info()[1] + except Exception as err: s = '\n' + '=' * 70 + '\n' s += "FAIL: test_%s (proc=%s" % (name, p) if ret != default: @@ -2375,16 +2525,16 @@ def num_fds(self, ret): self.assertTrue(ret >= 0) def connections(self, ret): + self.assertEqual(len(ret), len(set(ret))) for conn in ret: - check_connection(conn) + check_connection_ntuple(conn) def cwd(self, ret): if ret is not None: # BSD may return None assert os.path.isabs(ret), ret try: st = os.stat(ret) - except OSError: - err = sys.exc_info()[1] + except OSError as err: # directory has been removed in mean time if err.errno != errno.ENOENT: raise @@ -2449,58 +2599,55 @@ def rlimit(self, ret): # --- Limited user tests # =================================================================== -if hasattr(os, 'getuid') and os.getuid() == 0: - - class LimitedUserTestCase(TestProcess): - """Repeat the previous tests by using a limited user. - Executed only on UNIX and only if the user who run the test script - is root. - """ - # the uid/gid the test suite runs under +@unittest.skipUnless(POSIX, "UNIX only") +@unittest.skipUnless(hasattr(os, 'getuid') and os.getuid() == 0, + "super user privileges are required") +class LimitedUserTestCase(TestProcess): + """Repeat the previous tests by using a limited user. + Executed only on UNIX and only if the user who run the test script + is root. + """ + # the uid/gid the test suite runs under + if hasattr(os, 'getuid'): PROCESS_UID = os.getuid() PROCESS_GID = os.getgid() - def __init__(self, *args, **kwargs): - TestProcess.__init__(self, *args, **kwargs) - # re-define all existent test methods in order to - # ignore AccessDenied exceptions - for attr in [x for x in dir(self) if x.startswith('test')]: - meth = getattr(self, attr) - - def test_(self): - try: - meth() - except psutil.AccessDenied: - pass - setattr(self, attr, types.MethodType(test_, self)) + def __init__(self, *args, **kwargs): + TestProcess.__init__(self, *args, **kwargs) + # re-define all existent test methods in order to + # ignore AccessDenied exceptions + for attr in [x for x in dir(self) if x.startswith('test')]: + meth = getattr(self, attr) - def setUp(self): - safe_remove(TESTFN) - os.setegid(1000) - os.seteuid(1000) - TestProcess.setUp(self) + def test_(self): + try: + meth() + except psutil.AccessDenied: + pass + setattr(self, attr, types.MethodType(test_, self)) - def tearDown(self): - os.setegid(self.PROCESS_UID) - os.seteuid(self.PROCESS_GID) - TestProcess.tearDown(self) + def setUp(self): + safe_remove(TESTFN) + TestProcess.setUp(self) + os.setegid(1000) + os.seteuid(1000) - def test_nice(self): - try: - psutil.Process().nice(-1) - except psutil.AccessDenied: - pass - else: - self.fail("exception not raised") + def tearDown(self): + os.setegid(self.PROCESS_UID) + os.seteuid(self.PROCESS_GID) + TestProcess.tearDown(self) - def test_zombie_process(self): - # causes problems if test test suite is run as root + def test_nice(self): + try: + psutil.Process().nice(-1) + except psutil.AccessDenied: pass -else: + else: + self.fail("exception not raised") - class LimitedUserTestCase(unittest.TestCase): - def test_it(self): - unittest.skip("super user privileges are required") + def test_zombie_process(self): + # causes problems if test test suite is run as root + pass # =================================================================== @@ -2510,22 +2657,84 @@ def test_it(self): class TestMisc(unittest.TestCase): """Misc / generic tests.""" - def test__str__(self): - sproc = get_test_subprocess() - p = psutil.Process(sproc.pid) - self.assertIn(str(sproc.pid), str(p)) - # python shows up as 'Python' in cmdline on OS X so - # test fails on OS X - if not OSX: - self.assertIn(os.path.basename(PYTHON), str(p)) - sproc = get_test_subprocess() - p = psutil.Process(sproc.pid) - p.kill() - p.wait() - self.assertIn(str(sproc.pid), str(p)) - self.assertIn("terminated", str(p)) - - def test__eq__(self): + def test_process__repr__(self, func=repr): + p = psutil.Process() + r = func(p) + self.assertIn("psutil.Process", r) + self.assertIn("pid=%s" % p.pid, r) + self.assertIn("name=", r) + self.assertIn(p.name(), r) + with mock.patch.object(psutil.Process, "name", + side_effect=psutil.ZombieProcess(os.getpid())): + p = psutil.Process() + r = func(p) + self.assertIn("pid=%s" % p.pid, r) + self.assertIn("zombie", r) + self.assertNotIn("name=", r) + with mock.patch.object(psutil.Process, "name", + side_effect=psutil.NoSuchProcess(os.getpid())): + p = psutil.Process() + r = func(p) + self.assertIn("pid=%s" % p.pid, r) + self.assertIn("terminated", r) + self.assertNotIn("name=", r) + + def test_process__str__(self): + self.test_process__repr__(func=str) + + def test_no_such_process__repr__(self, func=repr): + self.assertEqual( + repr(psutil.NoSuchProcess(321)), + "psutil.NoSuchProcess process no longer exists (pid=321)") + self.assertEqual( + repr(psutil.NoSuchProcess(321, name='foo')), + "psutil.NoSuchProcess process no longer exists (pid=321, " + "name='foo')") + self.assertEqual( + repr(psutil.NoSuchProcess(321, msg='foo')), + "psutil.NoSuchProcess foo") + + def test_zombie_process__repr__(self, func=repr): + self.assertEqual( + repr(psutil.ZombieProcess(321)), + "psutil.ZombieProcess process still exists but it's a zombie " + "(pid=321)") + self.assertEqual( + repr(psutil.ZombieProcess(321, name='foo')), + "psutil.ZombieProcess process still exists but it's a zombie " + "(pid=321, name='foo')") + self.assertEqual( + repr(psutil.ZombieProcess(321, name='foo', ppid=1)), + "psutil.ZombieProcess process still exists but it's a zombie " + "(pid=321, name='foo', ppid=1)") + self.assertEqual( + repr(psutil.ZombieProcess(321, msg='foo')), + "psutil.ZombieProcess foo") + + def test_access_denied__repr__(self, func=repr): + self.assertEqual( + repr(psutil.AccessDenied(321)), + "psutil.AccessDenied (pid=321)") + self.assertEqual( + repr(psutil.AccessDenied(321, name='foo')), + "psutil.AccessDenied (pid=321, name='foo')") + self.assertEqual( + repr(psutil.AccessDenied(321, msg='foo')), + "psutil.AccessDenied foo") + + def test_timeout_expired__repr__(self, func=repr): + self.assertEqual( + repr(psutil.TimeoutExpired(321)), + "psutil.TimeoutExpired timeout after 321 seconds") + self.assertEqual( + repr(psutil.TimeoutExpired(321, pid=111)), + "psutil.TimeoutExpired timeout after 321 seconds (pid=111)") + self.assertEqual( + repr(psutil.TimeoutExpired(321, pid=111, name='foo')), + "psutil.TimeoutExpired timeout after 321 seconds " + "(pid=111, name='foo')") + + def test_process__eq__(self): p1 = psutil.Process() p2 = psutil.Process() self.assertEqual(p1, p2) @@ -2533,13 +2742,14 @@ def test__eq__(self): self.assertNotEqual(p1, p2) self.assertNotEqual(p1, 'foo') - def test__hash__(self): + def test_process__hash__(self): s = set([psutil.Process(), psutil.Process()]) self.assertEqual(len(s), 1) def test__all__(self): - for name in dir(psutil): - if name in ('callable', 'defaultdict', 'error', 'namedtuple', + dir_psutil = dir(psutil) + for name in dir_psutil: + if name in ('callable', 'error', 'namedtuple', 'long', 'test', 'NUM_CPUS', 'BOOT_TIME', 'TOTAL_PHYMEM'): continue @@ -2555,6 +2765,17 @@ def test__all__(self): 'deprecated' not in fun.__doc__.lower()): self.fail('%r not in psutil.__all__' % name) + # Import 'star' will break if __all__ is inconsistent, see: + # https://github.com/giampaolo/psutil/issues/656 + # Can't do `from psutil import *` as it won't work on python 3 + # so we simply iterate over __all__. + for name in psutil.__all__: + self.assertIn(name, dir_psutil) + + def test_version(self): + self.assertEqual('.'.join([str(x) for x in psutil.version_info]), + psutil.__version__) + def test_memoize(self): from psutil._common import memoize @@ -2592,6 +2813,23 @@ def foo(*args, **kwargs): # docstring self.assertEqual(foo.__doc__, "foo docstring") + def test_isfile_strict(self): + from psutil._common import isfile_strict + this_file = os.path.abspath(__file__) + assert isfile_strict(this_file) + assert not isfile_strict(os.path.dirname(this_file)) + with mock.patch('psutil._common.os.stat', + side_effect=OSError(errno.EPERM, "foo")): + self.assertRaises(OSError, isfile_strict, this_file) + with mock.patch('psutil._common.os.stat', + side_effect=OSError(errno.EACCES, "foo")): + self.assertRaises(OSError, isfile_strict, this_file) + with mock.patch('psutil._common.os.stat', + side_effect=OSError(errno.EINVAL, "foo")): + assert not isfile_strict(this_file) + with mock.patch('psutil._common.stat.S_ISREG', return_value=False): + assert not isfile_strict(this_file) + def test_serialization(self): def check(ret): if json is not None: @@ -2609,11 +2847,36 @@ def check(ret): if LINUX and not os.path.exists('/proc/diskstats'): pass else: - check(psutil.disk_io_counters()) + if not APPVEYOR: + check(psutil.disk_io_counters()) check(psutil.disk_partitions()) check(psutil.disk_usage(os.getcwd())) check(psutil.users()) + def test_setup_script(self): + here = os.path.abspath(os.path.dirname(__file__)) + setup_py = os.path.realpath(os.path.join(here, '..', 'setup.py')) + module = imp.load_source('setup', setup_py) + self.assertRaises(SystemExit, module.setup) + self.assertEqual(module.get_version(), psutil.__version__) + + def test_ad_on_process_creation(self): + # We are supposed to be able to instantiate Process also in case + # of zombie processes or access denied. + with mock.patch.object(psutil.Process, 'create_time', + side_effect=psutil.AccessDenied) as meth: + psutil.Process() + assert meth.called + with mock.patch.object(psutil.Process, 'create_time', + side_effect=psutil.ZombieProcess(1)) as meth: + psutil.Process() + assert meth.called + with mock.patch.object(psutil.Process, 'create_time', + side_effect=ValueError) as meth: + with self.assertRaises(ValueError): + psutil.Process() + assert meth.called + # =================================================================== # --- Example script tests @@ -2628,8 +2891,7 @@ def assert_stdout(self, exe, args=None): exe = exe + ' ' + args try: out = sh(sys.executable + ' ' + exe).strip() - except RuntimeError: - err = sys.exc_info()[1] + except RuntimeError as err: if 'AccessDenied' in str(err): return str(err) else: @@ -2639,11 +2901,8 @@ def assert_stdout(self, exe, args=None): def assert_syntax(self, exe, args=None): exe = os.path.join(EXAMPLES_DIR, exe) - f = open(exe, 'r') - try: + with open(exe, 'r') as f: src = f.read() - finally: - f.close() ast.parse(src) def test_check_presence(self): @@ -2668,15 +2927,23 @@ def test_meminfo(self): def test_process_detail(self): self.assert_stdout('process_detail.py') + @unittest.skipIf(APPVEYOR, "can't find users on Appveyor") def test_who(self): self.assert_stdout('who.py') def test_ps(self): self.assert_stdout('ps.py') + def test_pstree(self): + self.assert_stdout('pstree.py') + def test_netstat(self): self.assert_stdout('netstat.py') + @unittest.skipIf(TRAVIS, "permission denied on travis") + def test_ifconfig(self): + self.assert_stdout('ifconfig.py') + def test_pmap(self): self.assert_stdout('pmap.py', args=str(os.getpid())) @@ -2700,8 +2967,12 @@ def test_top(self): def test_iotop(self): self.assert_syntax('iotop.py') + def test_pidof(self): + output = self.assert_stdout('pidof.py %s' % psutil.Process().name()) + self.assertIn(str(os.getpid()), output) + -def test_main(): +def main(): tests = [] test_suite = unittest.TestSuite() tests.append(TestSystemAPIs) @@ -2738,5 +3009,5 @@ def test_main(): return result.wasSuccessful() if __name__ == '__main__': - if not test_main(): + if not main(): sys.exit(1) diff --git a/python/psutil/tox.ini b/python/psutil/tox.ini index 8f6d7f9415cc..d80dd174b8c3 100644 --- a/python/psutil/tox.ini +++ b/python/psutil/tox.ini @@ -9,16 +9,24 @@ envlist = py26, py27, py32, py33, py34 [testenv] deps = - pytest flake8 + pytest + py26: ipaddress + py26: mock==1.0.1 + py26: unittest2 + py27: ipaddress + py27: mock + py32: ipaddress + py32: mock + py33: ipaddress + setenv = PYTHONPATH = {toxinidir}/test + commands = py.test {posargs} - flake8 --exclude=build,.tox,.git + git ls-files | grep \\.py$ | xargs flake8 -[testenv:py26] -deps = - flake8 - pytest - unittest2 +# suppress "WARNING: 'git' command found but not installed in testenv +whitelist_externals = git +usedevelop = True diff --git a/security/certverifier/CertVerifier.cpp b/security/certverifier/CertVerifier.cpp index c0263181faf1..3d0470e7cd5a 100644 --- a/security/certverifier/CertVerifier.cpp +++ b/security/certverifier/CertVerifier.cpp @@ -124,7 +124,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage, /*optional out*/ SECOidTag* evOidPolicy, /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus, /*optional out*/ KeySizeStatus* keySizeStatus, - /*optional out*/ SignatureDigestStatus* sigDigestStatus) + /*optional out*/ SignatureDigestStatus* sigDigestStatus, + /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo) { MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("Top of VerifyCert\n")); @@ -212,7 +213,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage, mCertShortLifetimeInDays, pinningDisabled, MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff, - AcceptAllAlgorithms, nullptr, + AcceptAllAlgorithms, nullptr, nullptr, builtChain); rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity, @@ -262,12 +263,18 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage, for (size_t i=0; i < digestAlgorithmOptionsCount && rv != Success && srv == SECSuccess; i++) { + // Because of the try-strict and fallback approach, we have to clear any + // previously noted telemetry information + if (pinningTelemetryInfo) { + pinningTelemetryInfo->Reset(); + } NSSCertDBTrustDomain trustDomain(trustSSL, evOCSPFetching, mOCSPCache, pinArg, ocspGETConfig, mCertShortLifetimeInDays, mPinningMode, MIN_RSA_BITS, ValidityCheckingMode::CheckForEV, - digestAlgorithmOptions[i], hostname, builtChain); + digestAlgorithmOptions[i], pinningTelemetryInfo, hostname, + builtChain); rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time, KeyUsage::digitalSignature,// (EC)DHE KeyUsage::keyEncipherment, // RSA @@ -315,12 +322,19 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage, for (size_t i=0; iReset(); + } + NSSCertDBTrustDomain trustDomain(trustSSL, defaultOCSPFetching, mOCSPCache, pinArg, ocspGETConfig, mCertShortLifetimeInDays, mPinningMode, keySizeOptions[i], ValidityCheckingMode::CheckingOff, digestAlgorithmOptions[j], + pinningTelemetryInfo, hostname, builtChain); rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time, KeyUsage::digitalSignature,//(EC)DHE @@ -361,7 +375,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage, mCertShortLifetimeInDays, pinningDisabled, MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff, - AcceptAllAlgorithms, nullptr, + AcceptAllAlgorithms, nullptr, nullptr, builtChain); rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeCA, KeyUsage::keyCertSign, @@ -376,7 +390,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage, mCertShortLifetimeInDays, pinningDisabled, MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff, - AcceptAllAlgorithms, nullptr, + AcceptAllAlgorithms, nullptr, nullptr, builtChain); rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity, @@ -402,7 +416,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage, mCertShortLifetimeInDays, pinningDisabled, MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff, - AcceptAllAlgorithms, nullptr, + AcceptAllAlgorithms, nullptr, nullptr, builtChain); rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity, @@ -425,7 +439,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage, mCertShortLifetimeInDays, pinningDisabled, MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff, - AcceptAllAlgorithms, nullptr, + AcceptAllAlgorithms, nullptr, nullptr, builtChain); rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity, @@ -457,7 +471,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage, pinArg, ocspGETConfig, mCertShortLifetimeInDays, pinningDisabled, MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff, - AcceptAllAlgorithms, nullptr, builtChain); + AcceptAllAlgorithms, nullptr, nullptr, + builtChain); rv = BuildCertChain(sslTrust, certDER, time, endEntityOrCA, keyUsage, eku, CertPolicyId::anyPolicy, stapledOCSPResponse); @@ -467,7 +482,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage, mCertShortLifetimeInDays, pinningDisabled, MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff, - AcceptAllAlgorithms, nullptr, + AcceptAllAlgorithms, nullptr, nullptr, builtChain); rv = BuildCertChain(emailTrust, certDER, time, endEntityOrCA, keyUsage, eku, CertPolicyId::anyPolicy, @@ -481,7 +496,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage, MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff, AcceptAllAlgorithms, - nullptr, builtChain); + nullptr, nullptr, builtChain); rv = BuildCertChain(objectSigningTrust, certDER, time, endEntityOrCA, keyUsage, eku, CertPolicyId::anyPolicy, stapledOCSPResponse); @@ -515,7 +530,8 @@ CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert, /*optional out*/ SECOidTag* evOidPolicy, /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus, /*optional out*/ KeySizeStatus* keySizeStatus, - /*optional out*/ SignatureDigestStatus* sigDigestStatus) + /*optional out*/ SignatureDigestStatus* sigDigestStatus, + /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo) { PR_ASSERT(peerCert); // XXX: PR_ASSERT(pinarg) @@ -540,7 +556,8 @@ CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert, SECStatus rv = VerifyCert(peerCert, certificateUsageSSLServer, time, pinarg, hostname, flags, stapledOCSPResponse, &builtChainTemp, evOidPolicy, ocspStaplingStatus, - keySizeStatus, sigDigestStatus); + keySizeStatus, sigDigestStatus, + pinningTelemetryInfo); if (rv != SECSuccess) { return rv; } diff --git a/security/certverifier/CertVerifier.h b/security/certverifier/CertVerifier.h index f282d01c4af1..dfd3cf81ae59 100644 --- a/security/certverifier/CertVerifier.h +++ b/security/certverifier/CertVerifier.h @@ -7,6 +7,7 @@ #ifndef mozilla_psm__CertVerifier_h #define mozilla_psm__CertVerifier_h +#include "mozilla/Telemetry.h" #include "pkix/pkixtypes.h" #include "OCSPCache.h" #include "ScopedNSSTypes.h" @@ -31,6 +32,20 @@ enum class SignatureDigestStatus { AlreadyBad = 5, }; +class PinningTelemetryInfo +{ +public: + // Should we accumulate pinning telemetry for the result? + bool accumulateResult; + Telemetry::ID certPinningResultHistogram; + int32_t certPinningResultBucket; + // Should we accumulate telemetry for the root? + bool accumulateForRoot; + int32_t rootBucket; + + void Reset() { accumulateForRoot = false; accumulateResult = false; } +}; + class CertVerifier { public: @@ -62,7 +77,8 @@ class CertVerifier /*optional out*/ SECOidTag* evOidPolicy = nullptr, /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus = nullptr, /*optional out*/ KeySizeStatus* keySizeStatus = nullptr, - /*optional out*/ SignatureDigestStatus* sigDigestStatus = nullptr); + /*optional out*/ SignatureDigestStatus* sigDigestStatus = nullptr, + /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr); SECStatus VerifySSLServerCert( CERTCertificate* peerCert, @@ -76,7 +92,8 @@ class CertVerifier /*optional out*/ SECOidTag* evOidPolicy = nullptr, /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus = nullptr, /*optional out*/ KeySizeStatus* keySizeStatus = nullptr, - /*optional out*/ SignatureDigestStatus* sigDigestStatus = nullptr); + /*optional out*/ SignatureDigestStatus* sigDigestStatus = nullptr, + /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr); enum PinningMode { pinningDisabled = 0, diff --git a/security/certverifier/NSSCertDBTrustDomain.cpp b/security/certverifier/NSSCertDBTrustDomain.cpp index 30508ff84876..1c7807fffc2a 100644 --- a/security/certverifier/NSSCertDBTrustDomain.cpp +++ b/security/certverifier/NSSCertDBTrustDomain.cpp @@ -51,6 +51,7 @@ NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType, unsigned int minRSABits, ValidityCheckingMode validityCheckingMode, SignatureDigestOption signatureDigestOption, + /*optional*/ PinningTelemetryInfo* pinningTelemetryInfo, /*optional*/ const char* hostname, /*optional*/ ScopedCERTCertList* builtChain) : mCertDBTrustType(certDBTrustType) @@ -63,6 +64,7 @@ NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType, , mMinRSABits(minRSABits) , mValidityCheckingMode(validityCheckingMode) , mSignatureDigestOption(signatureDigestOption) + , mPinningTelemetryInfo(pinningTelemetryInfo) , mHostname(hostname) , mBuiltChain(builtChain) , mCertBlocklist(do_GetService(NS_CERTBLOCKLIST_CONTRACTID)) @@ -792,7 +794,8 @@ NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray, Time time) (mPinningMode == CertVerifier::pinningEnforceTestMode); bool chainHasValidPins; nsresult nsrv = PublicKeyPinningService::ChainHasValidPins( - certList, mHostname, time, enforceTestMode, chainHasValidPins); + certList, mHostname, time, enforceTestMode, chainHasValidPins, + mPinningTelemetryInfo); if (NS_FAILED(nsrv)) { return Result::FATAL_ERROR_LIBRARY_FAILURE; } diff --git a/security/certverifier/NSSCertDBTrustDomain.h b/security/certverifier/NSSCertDBTrustDomain.h index fa7534534cc5..7ab032248d7d 100644 --- a/security/certverifier/NSSCertDBTrustDomain.h +++ b/security/certverifier/NSSCertDBTrustDomain.h @@ -70,6 +70,7 @@ class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain unsigned int minRSABits, ValidityCheckingMode validityCheckingMode, SignatureDigestOption, + /*optional*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr, /*optional*/ const char* hostname = nullptr, /*optional out*/ ScopedCERTCertList* builtChain = nullptr); @@ -154,6 +155,7 @@ class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain const unsigned int mMinRSABits; ValidityCheckingMode mValidityCheckingMode; SignatureDigestOption mSignatureDigestOption; + PinningTelemetryInfo* mPinningTelemetryInfo; const char* mHostname; // non-owning - only used for pinning checks ScopedCERTCertList* mBuiltChain; // non-owning nsCOMPtr mCertBlocklist; diff --git a/security/manager/ssl/PublicKeyPinningService.cpp b/security/manager/ssl/PublicKeyPinningService.cpp index 262bd4d61ce3..073b3db9db0e 100644 --- a/security/manager/ssl/PublicKeyPinningService.cpp +++ b/security/manager/ssl/PublicKeyPinningService.cpp @@ -271,7 +271,8 @@ FindPinningInformation(const char* hostname, mozilla::pkix::Time time, static nsresult CheckPinsForHostname(const CERTCertList* certList, const char* hostname, bool enforceTestMode, mozilla::pkix::Time time, - /*out*/ bool& chainHasValidPins) + /*out*/ bool& chainHasValidPins, + /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo) { chainHasValidPins = false; if (!certList) { @@ -316,22 +317,32 @@ CheckPinsForHostname(const CERTCertList* certList, const char* hostname, } // We can collect per-host pinning violations for this host because it is // operationally critical to Firefox. - if (staticFingerprints->mId != kUnknownId) { - int32_t bucket = staticFingerprints->mId * 2 + (enforceTestModeResult ? 1 : 0); - histogram = staticFingerprints->mTestMode - ? Telemetry::CERT_PINNING_MOZ_TEST_RESULTS_BY_HOST - : Telemetry::CERT_PINNING_MOZ_RESULTS_BY_HOST; - Telemetry::Accumulate(histogram, bucket); - } else { - Telemetry::Accumulate(histogram, enforceTestModeResult ? 1 : 0); + if (pinningTelemetryInfo) { + if (staticFingerprints->mId != kUnknownId) { + int32_t bucket = staticFingerprints->mId * 2 + + (enforceTestModeResult ? 1 : 0); + histogram = staticFingerprints->mTestMode + ? Telemetry::CERT_PINNING_MOZ_TEST_RESULTS_BY_HOST + : Telemetry::CERT_PINNING_MOZ_RESULTS_BY_HOST; + pinningTelemetryInfo->certPinningResultBucket = bucket; + } else { + pinningTelemetryInfo->certPinningResultBucket = + enforceTestModeResult ? 1 : 0; + } + pinningTelemetryInfo->accumulateResult = true; + pinningTelemetryInfo->certPinningResultHistogram = histogram; } // We only collect per-CA pinning statistics upon failures. CERTCertListNode* rootNode = CERT_LIST_TAIL(certList); // Only log telemetry if the certificate list is non-empty. if (!CERT_LIST_END(rootNode, certList)) { - if (!enforceTestModeResult) { - AccumulateTelemetryForRootCA(Telemetry::CERT_PINNING_FAILURES_BY_CA, rootNode->cert); + if (!enforceTestModeResult && pinningTelemetryInfo) { + int32_t binNumber = RootCABinNumber(&rootNode->cert->derCert); + if (binNumber != ROOT_CERTIFICATE_UNKNOWN ) { + pinningTelemetryInfo->accumulateForRoot = true; + pinningTelemetryInfo->rootBucket = binNumber; + } } } @@ -350,7 +361,8 @@ PublicKeyPinningService::ChainHasValidPins(const CERTCertList* certList, const char* hostname, mozilla::pkix::Time time, bool enforceTestMode, - /*out*/ bool& chainHasValidPins) + /*out*/ bool& chainHasValidPins, + /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo) { chainHasValidPins = false; if (!certList) { @@ -361,7 +373,8 @@ PublicKeyPinningService::ChainHasValidPins(const CERTCertList* certList, } nsAutoCString canonicalizedHostname(CanonicalizeHostname(hostname)); return CheckPinsForHostname(certList, canonicalizedHostname.get(), - enforceTestMode, time, chainHasValidPins); + enforceTestMode, time, chainHasValidPins, + pinningTelemetryInfo); } nsresult diff --git a/security/manager/ssl/PublicKeyPinningService.h b/security/manager/ssl/PublicKeyPinningService.h index bb6021b237fd..9a02ab912056 100644 --- a/security/manager/ssl/PublicKeyPinningService.h +++ b/security/manager/ssl/PublicKeyPinningService.h @@ -6,6 +6,7 @@ #define PublicKeyPinningService_h #include "cert.h" +#include "CertVerifier.h" #include "nsString.h" #include "nsTArray.h" #include "pkix/Time.h" @@ -31,7 +32,8 @@ class PublicKeyPinningService const char* hostname, mozilla::pkix::Time time, bool enforceTestMode, - /*out*/ bool& chainHasValidPins); + /*out*/ bool& chainHasValidPins, + /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo); /** * Returns true if there is any intersection between the certificate list * and the pins specified in the aSHA256key array. Values passed in are diff --git a/security/manager/ssl/RootCertificateTelemetryUtils.cpp b/security/manager/ssl/RootCertificateTelemetryUtils.cpp index 2ca71627dd0a..6070bc89dba3 100644 --- a/security/manager/ssl/RootCertificateTelemetryUtils.cpp +++ b/security/manager/ssl/RootCertificateTelemetryUtils.cpp @@ -11,13 +11,7 @@ #include "ScopedNSSTypes.h" #include "mozilla/ArrayUtils.h" -// Note: New CAs will show up as UNKNOWN_ROOT until -// RootHashes.inc is updated to include them. 0 is reserved by -// genRootCAHashes.js for the unknowns. -#define UNKNOWN_ROOT 0 -#define HASH_FAILURE -1 - -namespace mozilla { namespace psm { +namespace mozilla { namespace psm { PRLogModuleInfo* gPublicKeyPinningTelemetryLog = PR_NewLogModule("PublicKeyPinningTelemetryService"); @@ -54,7 +48,7 @@ RootCABinNumber(const SECItem* cert) // Compute SHA256 hash of the certificate nsresult rv = digest.DigestBuf(SEC_OID_SHA256, cert->data, cert->len); if (NS_WARN_IF(NS_FAILED(rv))) { - return HASH_FAILURE; + return ROOT_CERTIFICATE_HASH_FAILURE; } // Compare against list of stored hashes @@ -76,7 +70,7 @@ RootCABinNumber(const SECItem* cert) } // Didn't match. - return UNKNOWN_ROOT; + return ROOT_CERTIFICATE_UNKNOWN; } @@ -88,7 +82,7 @@ AccumulateTelemetryForRootCA(mozilla::Telemetry::ID probe, { int32_t binId = RootCABinNumber(&cert->derCert); - if (binId != HASH_FAILURE) { + if (binId != ROOT_CERTIFICATE_HASH_FAILURE) { Accumulate(probe, binId); } } diff --git a/security/manager/ssl/RootCertificateTelemetryUtils.h b/security/manager/ssl/RootCertificateTelemetryUtils.h index 7ed3e54f4a60..05dbb4e44612 100644 --- a/security/manager/ssl/RootCertificateTelemetryUtils.h +++ b/security/manager/ssl/RootCertificateTelemetryUtils.h @@ -12,6 +12,15 @@ namespace mozilla { namespace psm { +// Note: New CAs will show up as UNKNOWN_ROOT until +// RootHashes.inc is updated to include them. 0 is reserved by +// genRootCAHashes.js for the unknowns. +#define ROOT_CERTIFICATE_UNKNOWN 0 +#define ROOT_CERTIFICATE_HASH_FAILURE -1 + +int32_t +RootCABinNumber(const SECItem* cert); + void AccumulateTelemetryForRootCA(mozilla::Telemetry::ID probe, const CERTCertificate* cert); diff --git a/security/manager/ssl/SSLServerCertVerification.cpp b/security/manager/ssl/SSLServerCertVerification.cpp index 91728bccc862..d75b9cf70d75 100644 --- a/security/manager/ssl/SSLServerCertVerification.cpp +++ b/security/manager/ssl/SSLServerCertVerification.cpp @@ -1178,13 +1178,15 @@ AuthCertificate(CertVerifier& certVerifier, CertVerifier::OCSP_STAPLING_NEVER_CHECKED; KeySizeStatus keySizeStatus = KeySizeStatus::NeverChecked; SignatureDigestStatus sigDigestStatus = SignatureDigestStatus::NeverChecked; + PinningTelemetryInfo pinningTelemetryInfo; rv = certVerifier.VerifySSLServerCert(cert, stapledOCSPResponse, time, infoObject, infoObject->GetHostNameRaw(), saveIntermediates, 0, &certList, &evOidPolicy, &ocspStaplingStatus, - &keySizeStatus, &sigDigestStatus); + &keySizeStatus, &sigDigestStatus, + &pinningTelemetryInfo); PRErrorCode savedErrorCode; if (rv != SECSuccess) { savedErrorCode = PR_GetError(); @@ -1202,6 +1204,16 @@ AuthCertificate(CertVerifier& certVerifier, static_cast(sigDigestStatus)); } + if (pinningTelemetryInfo.accumulateForRoot) { + Telemetry::Accumulate(Telemetry::CERT_PINNING_FAILURES_BY_CA, + pinningTelemetryInfo.rootBucket); + } + + if (pinningTelemetryInfo.accumulateResult) { + Telemetry::Accumulate(pinningTelemetryInfo.certPinningResultHistogram, + pinningTelemetryInfo.certPinningResultBucket); + } + // We want to remember the CA certs in the temp db, so that the application can find the // complete chain at any time it might need it. // But we keep only those CA certs in the temp db, that we didn't already know. diff --git a/security/manager/ssl/nsISiteSecurityService.idl b/security/manager/ssl/nsISiteSecurityService.idl index a90fd8199bf0..8f3df1ac9de5 100644 --- a/security/manager/ssl/nsISiteSecurityService.idl +++ b/security/manager/ssl/nsISiteSecurityService.idl @@ -23,7 +23,7 @@ namespace mozilla [ref] native nsCStringTArrayRef(nsTArray); [ref] native mozillaPkixTime(mozilla::pkix::Time); -[scriptable, uuid(e6cac961-9f03-4cc3-ad00-a829ae7304dc)] +[scriptable, uuid(275127f8-dbd7-4681-afbf-6df0c6587a01)] interface nsISiteSecurityService : nsISupports { const uint32_t HEADER_HSTS = 0; @@ -44,6 +44,7 @@ interface nsISiteSecurityService : nsISupports const uint32_t ERROR_PINSET_DOES_NOT_MATCH_CHAIN = 11; const uint32_t ERROR_NO_BACKUP_PIN = 12; const uint32_t ERROR_COULD_NOT_SAVE_STATE = 13; + const uint32_t ERROR_ROOT_NOT_BUILT_IN = 14; /** * Parses a given HTTP header and records the results internally. diff --git a/security/manager/ssl/nsSiteSecurityService.cpp b/security/manager/ssl/nsSiteSecurityService.cpp index 49a1f517db11..407cb2677900 100644 --- a/security/manager/ssl/nsSiteSecurityService.cpp +++ b/security/manager/ssl/nsSiteSecurityService.cpp @@ -718,7 +718,10 @@ nsSiteSecurityService::ProcessPKPHeader(nsIURI* aSourceURI, } if (!isBuiltIn && !mProcessPKPHeadersFromNonBuiltInRoots) { - return NS_OK; + if (aFailureResult) { + *aFailureResult = nsISiteSecurityService::ERROR_ROOT_NOT_BUILT_IN; + } + return NS_ERROR_FAILURE; } // if maxAge == 0 we must delete all state, for now no hole-punching diff --git a/security/manager/ssl/tests/unit/test_pinning.js b/security/manager/ssl/tests/unit/test_pinning.js index 8e32951e4d05..b771be2b0467 100644 --- a/security/manager/ssl/tests/unit/test_pinning.js +++ b/security/manager/ssl/tests/unit/test_pinning.js @@ -214,11 +214,11 @@ function check_pinning_telemetry() { .snapshot(); // Because all of our test domains are pinned to user-specified trust // anchors, effectively only strict mode and enforce test-mode get evaluated - equal(prod_histogram.counts[0], 16, + equal(prod_histogram.counts[0], 4, "Actual and expected prod (non-Mozilla) failure count should match"); equal(prod_histogram.counts[1], 4, "Actual and expected prod (non-Mozilla) success count should match"); - equal(test_histogram.counts[0], 5, + equal(test_histogram.counts[0], 2, "Actual and expected test (non-Mozilla) failure count should match"); equal(test_histogram.counts[1], 0, "Actual and expected test (non-Mozilla) success count should match"); diff --git a/security/manager/tools/PreloadedHPKPins.json b/security/manager/tools/PreloadedHPKPins.json index b05ae57814d7..50d24fa19919 100644 --- a/security/manager/tools/PreloadedHPKPins.json +++ b/security/manager/tools/PreloadedHPKPins.json @@ -227,7 +227,9 @@ // twitterCDN. More specific rules take precedence because we search for // exact domain name first. { "name": "twitter.com", "include_subdomains": true, - "pins": "twitterCDN", "test_mode": false } + "pins": "twitterCDN", "test_mode": false }, + { "name": "aus5.mozilla.org", "include_subdomains": true, + "pins": "mozilla", "test_mode": true, "id": 7 } ], "extra_certificates": [] diff --git a/security/pkix/include/pkix/Input.h b/security/pkix/include/pkix/Input.h index 8ff9bb09243a..e09526fb490a 100644 --- a/security/pkix/include/pkix/Input.h +++ b/security/pkix/include/pkix/Input.h @@ -25,7 +25,7 @@ #ifndef mozilla_pkix_Input_h #define mozilla_pkix_Input_h -#include +#include #include "pkix/Result.h" #include "stdint.h" @@ -133,7 +133,7 @@ inline bool InputsAreEqual(const Input& a, const Input& b) { return a.GetLength() == b.GetLength() && - !std::memcmp(a.UnsafeGetData(), b.UnsafeGetData(), a.GetLength()); + std::equal(a.UnsafeGetData(), a.UnsafeGetData() + a.GetLength(), b.UnsafeGetData()); } // An Reader is a cursor/iterator through the contents of an Input, designed to @@ -205,7 +205,7 @@ class Reader final if (static_cast(end - input) != N) { return false; } - if (memcmp(input, toMatch, N)) { + if (!std::equal(input, end, toMatch)) { return false; } input = end; @@ -221,7 +221,7 @@ class Reader final if (toMatch.GetLength() != remaining) { return false; } - if (std::memcmp(input, toMatch.UnsafeGetData(), remaining)) { + if (!std::equal(input, end, toMatch.UnsafeGetData())) { return false; } input = end; diff --git a/security/pkix/test/lib/pkixtestutil.h b/security/pkix/test/lib/pkixtestutil.h index 896ab5eb9bfa..5a445cc17758 100644 --- a/security/pkix/test/lib/pkixtestutil.h +++ b/security/pkix/test/lib/pkixtestutil.h @@ -28,6 +28,7 @@ #include #include // Some Mozilla-supported compilers lack #include +#include #include "pkix/pkixtypes.h" #include "../../lib/ScopedPtr.h" diff --git a/testing/docker/desktop-build/Dockerfile b/testing/docker/desktop-build/Dockerfile index 327878d74cd6..30bb3ff0fb56 100644 --- a/testing/docker/desktop-build/Dockerfile +++ b/testing/docker/desktop-build/Dockerfile @@ -6,9 +6,6 @@ MAINTAINER Morgan Reece Phillips ADD bin /home/worker/bin RUN chmod +x /home/worker/bin/* -# Add custom mozharness configs -ADD configs /home/worker/configs - ## # COMPILER HACKS ## diff --git a/testing/docker/desktop-build/bin/checkout-sources.sh b/testing/docker/desktop-build/bin/checkout-sources.sh index caedd192789e..93ebd2ce39c8 100644 --- a/testing/docker/desktop-build/bin/checkout-sources.sh +++ b/testing/docker/desktop-build/bin/checkout-sources.sh @@ -10,7 +10,7 @@ set -x -e # the canonical repo to clone and *_HEAD_REPO as the repo containing the # desired revision. For Mercurial clones, only *_HEAD_REV is required; for Git # clones, specify the branch name to fetch as *_HEAD_REF and the desired sha1 -# as *_HEAD_REV. For compatibility, we also accept MOZHARNESS_{REV,REF} +# as *_HEAD_REV. : GECKO_REPOSITORY ${GECKO_REPOSITORY:=https://hg.mozilla.org/mozilla-central} : GECKO_BASE_REPOSITORY ${GECKO_BASE_REPOSITORY:=${GECKO_REPOSITORY}} @@ -18,15 +18,6 @@ set -x -e : GECKO_HEAD_REV ${GECKO_HEAD_REV:=default} : GECKO_HEAD_REF ${GECKO_HEAD_REF:=${GECKO_HEAD_REV}} -: MOZHARNESS_REPOSITORY ${MOZHARNESS_REPOSITORY:=https://hg.mozilla.org/build/mozharness} -: MOZHARNESS_BASE_REPOSITORY ${MOZHARNESS_BASE_REPOSITORY:=${MOZHARNESS_REPOSITORY}} -: MOZHARNESS_HEAD_REPOSITORY ${MOZHARNESS_HEAD_REPOSITORY:=${MOZHARNESS_REPOSITORY}} -: MOZHARNESS_REV ${MOZHARNESS_REV:=production} -: MOZHARNESS_REF ${MOZHARNESS_REF:=${MOZHARNESS_REV}} -: MOZHARNESS_HEAD_REV ${MOZHARNESS_HEAD_REV:=${MOZHARNESS_REV}} -: MOZHARNESS_HEAD_REF ${MOZHARNESS_HEAD_REF:=${MOZHARNESS_REF}} -: MOZHARNESS_DISABLE ${MOZHARNESS_DISABLE:=false} - : TOOLS_REPOSITORY ${TOOLS_REPOSITORY:=https://hg.mozilla.org/build/tools} : TOOLS_BASE_REPOSITORY ${TOOLS_BASE_REPOSITORY:=${TOOLS_REPOSITORY}} : TOOLS_HEAD_REPOSITORY ${TOOLS_HEAD_REPOSITORY:=${TOOLS_REPOSITORY}} @@ -42,12 +33,6 @@ set -x -e set -v -# check out mozharness -if [ ! "$MOZHARNESS_DISABLE" = "true" ] -then - tc-vcs checkout mozharness $MOZHARNESS_BASE_REPOSITORY $MOZHARNESS_HEAD_REPOSITORY $MOZHARNESS_HEAD_REV $MOZHARNESS_HEAD_REF -fi - # check out tools where mozharness expects it to be ($PWD/build/tools and $WORKSPACE/build/tools) if [ ! "$TOOLS_DISABLE" = true ] then @@ -59,5 +44,4 @@ then fi fi -# and check out mozilla-central where mozharness will use it as a cache (/builds/hg-shared) tc-vcs checkout $WORKSPACE/build/src $GECKO_BASE_REPOSITORY $GECKO_HEAD_REPOSITORY $GECKO_HEAD_REV $GECKO_HEAD_REF diff --git a/testing/docker/desktop32-build/REGISTRY b/testing/docker/desktop32-build/REGISTRY index cb1e1bb482a2..8d8449526f96 100644 --- a/testing/docker/desktop32-build/REGISTRY +++ b/testing/docker/desktop32-build/REGISTRY @@ -1 +1 @@ -taskcluster +quay.io/djmitche diff --git a/testing/docker/desktop32-build/bin/checkout-sources.sh b/testing/docker/desktop32-build/bin/checkout-sources.sh index 6cc2acfb759c..082729b3709a 100644 --- a/testing/docker/desktop32-build/bin/checkout-sources.sh +++ b/testing/docker/desktop32-build/bin/checkout-sources.sh @@ -10,7 +10,7 @@ set -x -e # the canonical repo to clone and *_HEAD_REPO as the repo containing the # desired revision. For Mercurial clones, only *_HEAD_REV is required; for Git # clones, specify the branch name to fetch as *_HEAD_REF and the desired sha1 -# as *_HEAD_REV. For compatibility, we also accept MOZHARNESS_{REV,REF} +# as *_HEAD_REV. : GECKO_REPOSITORY ${GECKO_REPOSITORY:=https://hg.mozilla.org/mozilla-central} : GECKO_BASE_REPOSITORY ${GECKO_BASE_REPOSITORY:=${GECKO_REPOSITORY}} @@ -18,31 +18,16 @@ set -x -e : GECKO_HEAD_REV ${GECKO_HEAD_REV:=default} : GECKO_HEAD_REF ${GECKO_HEAD_REF:=${GECKO_HEAD_REV}} -: MOZHARNESS_REPOSITORY ${MOZHARNESS_REPOSITORY:=https://hg.mozilla.org/build/mozharness} -: MOZHARNESS_BASE_REPOSITORY ${MOZHARNESS_BASE_REPOSITORY:=${MOZHARNESS_REPOSITORY}} -: MOZHARNESS_HEAD_REPOSITORY ${MOZHARNESS_HEAD_REPOSITORY:=${MOZHARNESS_REPOSITORY}} -: MOZHARNESS_REV ${MOZHARNESS_REV:=production} -: MOZHARNESS_REF ${MOZHARNESS_REF:=${MOZHARNESS_REV}} -: MOZHARNESS_HEAD_REV ${MOZHARNESS_HEAD_REV:=${MOZHARNESS_REV}} -: MOZHARNESS_HEAD_REF ${MOZHARNESS_HEAD_REF:=${MOZHARNESS_REF}} - : TOOLS_REPOSITORY ${TOOLS_REPOSITORY:=https://hg.mozilla.org/build/tools} : TOOLS_BASE_REPOSITORY ${TOOLS_BASE_REPOSITORY:=${TOOLS_REPOSITORY}} : TOOLS_HEAD_REPOSITORY ${TOOLS_HEAD_REPOSITORY:=${TOOLS_REPOSITORY}} : TOOLS_HEAD_REV ${TOOLS_HEAD_REV:=default} : TOOLS_HEAD_REF ${TOOLS_HEAD_REF:=${TOOLS_HEAD_REV}} -: MH_CUSTOM_BUILD_VARIANT_CFG ${MH_CUSTOM_BUILD_VARIANT_CFG} -: MH_BRANCH ${MH_BRANCH:=mozilla-central} -: MH_BUILD_POOL ${MH_BUILD_POOL:=staging} - : WORKSPACE ${WORKSPACE:=/home/worker/workspace} set -v -# check out mozharness -tc-vcs checkout mozharness $MOZHARNESS_BASE_REPOSITORY $MOZHARNESS_HEAD_REPOSITORY $MOZHARNESS_HEAD_REV $MOZHARNESS_HEAD_REF - # check out tools where mozharness expects it to be ($PWD/build/tools and $WORKSPACE/build/tools) tc-vcs checkout $WORKSPACE/build/tools $TOOLS_BASE_REPOSITORY $TOOLS_HEAD_REPOSITORY $TOOLS_HEAD_REV $TOOLS_HEAD_REF if [ ! -d build ]; then @@ -50,5 +35,4 @@ if [ ! -d build ]; then ln -s $WORKSPACE/build/tools build/tools fi -# and check out mozilla-central where mozharness will use it as a cache (/builds/hg-shared) tc-vcs checkout $WORKSPACE/build/src $GECKO_BASE_REPOSITORY $GECKO_HEAD_REPOSITORY $GECKO_HEAD_REV $GECKO_HEAD_REF diff --git a/testing/marionette/client/marionette/tests/unit/test_accessibility.py b/testing/marionette/client/marionette/tests/unit/test_accessibility.py index 0863aeacf739..ebd5c154418d 100644 --- a/testing/marionette/client/marionette/tests/unit/test_accessibility.py +++ b/testing/marionette/client/marionette/tests/unit/test_accessibility.py @@ -4,7 +4,7 @@ from marionette import MarionetteTestCase from marionette_driver.errors import (ElementNotAccessibleException, - ElementNotVisibleException) + ElementNotVisibleException) class TestAccessibility(MarionetteTestCase): @@ -16,7 +16,13 @@ class TestAccessibility(MarionetteTestCase): "button1", # Button2 is an accessible button with a valid accessible name # computed from aria-label - "button2" + "button2", + # Button13 is an accessible button that is implemented via role="button" + # and is explorable using tabindex="0" + "button13", + # button17 is an accessible button that overrides parent's + # pointer-events:none; property with its own pointer-events:all; + "button17" ] # Elements that are not accessible with the accessibility API @@ -35,7 +41,10 @@ class TestAccessibility(MarionetteTestCase): "button7", # Button8 is not currently visible via the accessibility API and may # not be manipulated by it (in hidden subtree) - "button8" + "button8", + # Button14 is accessible button but is not explorable because of lack + # of tabindex that would make it focusable. + "button14" ] # Elements that are either accessible to accessibility API or not accessible @@ -57,6 +66,12 @@ class TestAccessibility(MarionetteTestCase): disabled_elementIDs = ["button11", "no_accessible_but_disabled"] + # Elements that are enabled but otherwise disabled or not explorable via the accessibility API + disabled_accessibility_elementIDs = ["button12", "button15", "button16"] + + # Elements that are reporting selected state + valid_option_elementIDs = ["option1", "option2"] + def run_element_test(self, ids, testFn): for id in ids: element = self.marionette.find_element("id", id) @@ -129,13 +144,50 @@ def test_element_is_visible_to_accessibility(self): # No exception should be raised self.run_element_test(self.displayed_elementIDs, lambda element: element.is_displayed()) - def test_is_element_is_not_enabled_to_accessbility(self): + def test_element_is_not_enabled_to_accessbility(self): self.setup_accessibility() - # Button is enabled but disabled via the accessibility API - self.assertRaises(ElementNotAccessibleException, - self.marionette.find_element("id", "button12").is_enabled) + # Buttons are enabled but disabled/not-explorable via the accessibility API + self.run_element_test(self.disabled_accessibility_elementIDs, + lambda element: self.assertRaises(ElementNotAccessibleException, + element.is_enabled)) + + # Buttons are enabled but disabled/not-explorable via the accessibility API and thus are not + # clickable via the accessibility API + self.run_element_test(self.disabled_accessibility_elementIDs, + lambda element: self.assertRaises(ElementNotAccessibleException, + element.click)) + + self.setup_accessibility(False, False) + self.run_element_test(self.disabled_accessibility_elementIDs, + lambda element: element.is_enabled()) + self.run_element_test(self.disabled_accessibility_elementIDs, + lambda element: element.click()) def test_element_is_enabled_to_accessibility(self): self.setup_accessibility() # No exception should be raised self.run_element_test(self.disabled_elementIDs, lambda element: element.is_enabled()) + + def test_send_keys_raises_no_exception(self): + self.setup_accessibility() + # Sending keys to valid input should not raise any exceptions + self.run_element_test(['input1'], lambda element: element.send_keys("a")) + + self.setup_accessibility(False, False) + # Sending keys to invalid element should not raise any exceptions when raising accessibility + # exceptions is disabled + self.run_element_test(['button5'], lambda element: element.send_keys("abc")) + + def test_send_keys_raises_element_not_accessible(self): + self.setup_accessibility() + # Sending keys to invalid element should raise an exception + self.run_element_test(['button5'], + lambda element: self.assertRaises(ElementNotAccessibleException, + element.send_keys)) + + def test_is_selected_raises_no_exception(self): + self.setup_accessibility() + # No exception should be raised for valid options + self.run_element_test(self.valid_option_elementIDs, lambda element: element.is_selected()) + # No exception should be raised for non-selectable elements + self.run_element_test(self.valid_elementIDs, lambda element: element.is_selected()) diff --git a/testing/marionette/client/marionette/tests/unit/test_capabilities.py b/testing/marionette/client/marionette/tests/unit/test_capabilities.py index baa74c0f3a4f..ddc6b4ccb4ca 100644 --- a/testing/marionette/client/marionette/tests/unit/test_capabilities.py +++ b/testing/marionette/client/marionette/tests/unit/test_capabilities.py @@ -18,6 +18,7 @@ def test_mandates_capabilities(self): self.assertIn("browserVersion", self.caps) self.assertIn("platformName", self.caps) self.assertIn("platformVersion", self.caps) + self.assertIn("specificationLevel", self.caps) self.assertEqual(self.caps["browserName"], self.appinfo["name"]) self.assertEqual(self.caps["browserVersion"], self.appinfo["version"]) diff --git a/testing/marionette/client/marionette/tests/unit/test_errors.py b/testing/marionette/client/marionette/tests/unit/test_errors.py index 3f3feae44623..fe410f82e0f1 100644 --- a/testing/marionette/client/marionette/tests/unit/test_errors.py +++ b/testing/marionette/client/marionette/tests/unit/test_errors.py @@ -17,7 +17,7 @@ def fake_cause(): cause = fake_cause() stacktrace = "first\nsecond" -class TestExceptionType(marionette_test.MarionetteTestCase): +class TestErrors(marionette_test.MarionetteTestCase): def test_defaults(self): exc = errors.MarionetteException() self.assertIsNone(exc.msg) @@ -72,7 +72,7 @@ def test_by_known_unicode_string(self): errors.lookup(u"no such element")) -class TestAllExceptions(marionette_test.MarionetteTestCase): +class TestAllErrors(marionette_test.MarionetteTestCase): def test_properties(self): for exc in errors.es_: self.assertTrue(hasattr(exc, "code"), diff --git a/testing/marionette/client/marionette/www/test_accessibility.html b/testing/marionette/client/marionette/www/test_accessibility.html index 8838ce869974..8cc9fd64930c 100644 --- a/testing/marionette/client/marionette/www/test_accessibility.html +++ b/testing/marionette/client/marionette/www/test_accessibility.html @@ -32,7 +32,22 @@

I have no accessible object + Span button + Unexplorable Span button + +
+ +
+
+ +
+ +