author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Tue, 02 Aug 2016 17:13:42 +0200 | |
changeset 307835 | cbdc4af95b6ad32fb32f776e2b3c0673e0461e3b |
parent 307834 | 2565f0eeecf9ee9359e893ce35fc92ad1fb01317 (current diff) |
parent 307759 | f299890191b297427f73dce94e3094d7e24a4552 (diff) |
child 307836 | 51347417809ee6c2324e48c7deed3cfae245f6a0 |
push id | 31034 |
push user | cbook@mozilla.com |
push date | Wed, 03 Aug 2016 15:09:36 +0000 |
treeherder | autoland@8ceb6981305f [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 51.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/.taskcluster.yml +++ b/.taskcluster.yml @@ -31,17 +31,17 @@ scopes: # - as_slugid: convert a label into a slugId # - from_now: generate a timestamp at a fixed offset from now tasks: - taskId: '{{#as_slugid}}decision task{{/as_slugid}}' task: created: '{{now}}' deadline: '{{#from_now}}1 day{{/from_now}}' - expires: '{{#from_now}}14 day{{/from_now}}' + expires: '{{#from_now}}365 day{{/from_now}}' metadata: owner: mozilla-taskcluster-maintenance@mozilla.com source: {{{source}}} name: "Gecko Decision Task" description: | The task that creates all of the other tasks in the task graph workerType: "gecko-decision" @@ -65,48 +65,58 @@ tasks: payload: env: # checkout-gecko uses these to check out the source; the inputs # to `mach taskgraph decision` are all on the command line. GECKO_BASE_REPOSITORY: 'https://hg.mozilla.org/mozilla-unified' GECKO_HEAD_REPOSITORY: '{{{url}}}' GECKO_HEAD_REF: '{{revision}}' GECKO_HEAD_REV: '{{revision}}' - # Arguments passed into `mach taskgraph decision` - # TODO use mozilla-unified for the base repository once the tc-vcs - # tar.gz archives are created or tc-vcs isn't being used. - DECISION_ARGS: > - --pushlog-id='{{pushlog_id}}' - --project='{{project}}' - --message='{{comment}}' - --owner='{{owner}}' - --level='{{level}}' - --base-repository='https://hg.mozilla.org/mozilla-central' - --head-repository='{{{url}}}' - --head-ref='{{revision}}' - --head-rev='{{revision}}' - --revision-hash='{{revision_hash}}' cache: level-{{level}}-hg-shared: /home/worker/hg-shared - level-{{level}}-gecko-decision: /home/worker/workspace + level-{{level}}-checkouts: /home/worker/checkouts features: taskclusterProxy: true # Note: This task is built server side without the context or tooling that # exist in tree so we must hard code the version - image: 'taskcluster/decision:0.1.2' + image: 'taskcluster/decision:0.1.3' maxRunTime: 1800 + # TODO use mozilla-unified for the base repository once the tc-vcs + # tar.gz archives are created or tc-vcs isn't being used. command: - - /home/worker/bin/run-decision + - /home/worker/bin/run-task + - '--vcs-checkout=/home/worker/checkouts/gecko' + - '--' + - bash + - -cx + - > + cd /home/worker/checkouts/gecko && + ln -s /home/worker/artifacts artifacts && + ./mach --log-no-times taskgraph decision + --pushlog-id='{{pushlog_id}}' + --project='{{project}}' + --message='{{comment}}' + --owner='{{owner}}' + --level='{{level}}' + --base-repository='https://hg.mozilla.org/mozilla-central' + --head-repository='{{{url}}}' + --head-ref='{{revision}}' + --head-rev='{{revision}}' + --revision-hash='{{revision_hash}}' artifacts: 'public': type: 'directory' path: '/home/worker/artifacts' + expires: '{{#from_now}}364 days{{/from_now}}' + 'public/docker_image_contexts': + type: 'directory' + path: '/home/worker/docker_image_contexts' expires: '{{#from_now}}7 days{{/from_now}}' extra: treeherder: symbol: D
--- a/browser/base/content/test/general/browser_addKeywordSearch.js +++ b/browser/base/content/test/general/browser_addKeywordSearch.js @@ -5,29 +5,29 @@ var testData = [ /* baseURI, field name, expected */ [ 'http://example.com/', 'q', 'http://example.com/?q=%s' ], [ 'http://example.com/new-path-here/', 'q', 'http://example.com/new-path-here/?q=%s' ], [ '', 'q', 'http://example.org/browser/browser/base/content/test/general/dummy_page.html?q=%s' ], // Tests for proper behaviour when called on a form whose action contains a question mark. [ 'http://example.com/search?oe=utf-8', 'q', 'http://example.com/search?oe=utf-8&q=%s' ], ]; -var mm = gBrowser.selectedBrowser.messageManager; - add_task(function*() { yield BrowserTestUtils.withNewTab({ gBrowser, url: "http://example.org/browser/browser/base/content/test/general/dummy_page.html", }, function* (browser) { yield ContentTask.spawn(browser, null, function* () { let doc = content.document; let base = doc.createElement("base"); doc.head.appendChild(base); }); + var mm = browser.messageManager; + for (let [baseURI, fieldName, expected] of testData) { let popupShownPromise = BrowserTestUtils.waitForEvent(document.getElementById("contentAreaContextMenu"), "popupshown"); yield ContentTask.spawn(browser, { baseURI, fieldName }, function* (args) { let doc = content.document; let base = doc.querySelector('head > base');
--- a/browser/base/content/test/general/browser_tabfocus.js +++ b/browser/base/content/test/general/browser_tabfocus.js @@ -3,24 +3,42 @@ */ var testPage1 = "<html id='html1'><body id='body1'><button id='button1'>Tab 1</button></body></html>"; var testPage2 = "<html id='html2'><body id='body2'><button id='button2'>Tab 2</button></body></html>"; var testPage3 = "<html id='html3'><body id='body3'><button id='button3'>Tab 3</button></body></html>"; const fm = Services.focus; +function EventStore() { + this["main-window"] = []; + this["window1"] = []; + this["window2"] = []; +}; + +EventStore.prototype = { + "push": function (event) { + if (event.indexOf("1") > -1) { + this["window1"].push(event); + } else if (event.indexOf("2") > -1) { + this["window2"].push(event); + } else { + this["main-window"].push(event); + } + } +} + var tab1 = null; var tab2 = null; var browser1 = null; var browser2 = null; -var _browser_tabfocus_test_lastfocus; -var _browser_tabfocus_test_lastfocuswindow = null; -var actualEvents = []; -var expectedEvents = []; +var _lastfocus; +var _lastfocuswindow = null; +var actualEvents = new EventStore(); +var expectedEvents = new EventStore(); var currentTestName = ""; var _expectedElement = null; var _expectedWindow = null; var currentPromiseResolver = null; function* getFocusedElementForBrowser(browser, dontCheckExtraFocus = false) { @@ -133,18 +151,18 @@ add_task(function*() { var messages = ""; if (gMultiProcessBrowser) { messageManager.addMessageListener("Browser:FocusChanged", message => { actualEvents.push(message.data.details); compareFocusResults(); }); } - _browser_tabfocus_test_lastfocus = "urlbar"; - _browser_tabfocus_test_lastfocuswindow = "main-window"; + _lastfocus = "urlbar"; + _lastfocuswindow = "main-window"; window.addEventListener("focus", _browser_tabfocus_test_eventOccured, true); window.addEventListener("blur", _browser_tabfocus_test_eventOccured, true); // make sure that the focus initially starts out blank var fm = Services.focus; var focusedWindow = {}; @@ -324,19 +342,19 @@ add_task(function*() { document.getElementById('Browser:Back').doCommand(); }); is(window.document.activeElement, gURLBar.inputField, "urlbar still focused after navigating back"); // Document navigation with F6 does not yet work in mutli-process browsers. if (!gMultiProcessBrowser) { gURLBar.focus(); - actualEvents = []; - _browser_tabfocus_test_lastfocus = "urlbar"; - _browser_tabfocus_test_lastfocuswindow = "main-window"; + actualEvents = new EventStore(); + _lastfocus = "urlbar"; + _lastfocuswindow = "main-window"; yield expectFocusShift(() => EventUtils.synthesizeKey("VK_F6", { }), "window1", "html1", true, "switch document forward with f6"); EventUtils.synthesizeKey("VK_F6", { }); is(fm.focusedWindow, window, "switch document forward again with f6"); @@ -411,36 +429,29 @@ function getId(element) return (element.localName == "input") ? "urlbar" : element.id; } function compareFocusResults() { if (!currentPromiseResolver) return; - if (actualEvents.length < expectedEvents.length) - return; + let winIds = ["main-window", "window1", "window2"]; - for (let e = 0; e < expectedEvents.length; e++) { - // When remote browsers are used, the child process events can occur before or after the - // browser focusing. Allow either order. - if (gMultiProcessBrowser && actualEvents[e] != expectedEvents[e] && - actualEvents[e].startsWith("focus: browser")) { + for (let winId of winIds) { + if (actualEvents[winId].length < expectedEvents[winId].length) + return; + } - // Ensure that this event is expected later, and remove the later expected event and - // reinsert it at the current index. - let foundLaterIndex = expectedEvents.indexOf(actualEvents[e], e + 1); - if (foundLaterIndex > e) { - expectedEvents.splice(e, 0, expectedEvents.splice(foundLaterIndex, 1)[0]); - } + for (let winId of winIds) { + for (let e = 0; e < expectedEvents.length; e++) { + is(actualEvents[winId][e], expectedEvents[winId][e], currentTestName + " events [event " + e + "]"); } - - is(actualEvents[e], expectedEvents[e], currentTestName + " events [event " + e + "]"); + actualEvents[winId] = []; } - actualEvents = []; // Use executeSoon as this will be called during a focus/blur event handler executeSoon(() => { let matchWindow = window; if (gMultiProcessBrowser) { is(_expectedWindow, "main-window", "main-window is always expected"); } else if (_expectedWindow != "main-window") { @@ -475,17 +486,17 @@ function* expectFocusShiftAfterTabSwitch yield tabSwitchPromise; } function* expectFocusShift(callback, expectedWindow, expectedElement, focusChanged, testid) { currentPromiseResolver = null; currentTestName = testid; - expectedEvents = []; + expectedEvents = new EventStore(); if (focusChanged) { _expectedElement = expectedElement; _expectedWindow = expectedWindow; // When the content is in a child process, the expected element in the chrome window // will always be the urlbar or a browser element. if (gMultiProcessBrowser) { @@ -493,67 +504,67 @@ function* expectFocusShift(callback, exp _expectedElement = "browser1"; } else if (_expectedWindow == "window2") { _expectedElement = "browser2"; } _expectedWindow = "main-window"; } - if (gMultiProcessBrowser && _browser_tabfocus_test_lastfocuswindow != "main-window" && - _browser_tabfocus_test_lastfocuswindow != expectedWindow) { - let browserid = _browser_tabfocus_test_lastfocuswindow == "window1" ? "browser1" : "browser2"; + if (gMultiProcessBrowser && _lastfocuswindow != "main-window" && + _lastfocuswindow != expectedWindow) { + let browserid = _lastfocuswindow == "window1" ? "browser1" : "browser2"; expectedEvents.push("blur: " + browserid); } var newElementIsFocused = (expectedElement && !expectedElement.startsWith("html")); if (newElementIsFocused && gMultiProcessBrowser && - _browser_tabfocus_test_lastfocuswindow != "main-window" && + _lastfocuswindow != "main-window" && expectedWindow == "main-window") { // When switching from a child to a chrome element, the focus on the element will arrive first. expectedEvents.push("focus: " + expectedElement); newElementIsFocused = false; } - if (_browser_tabfocus_test_lastfocus && _browser_tabfocus_test_lastfocus != _expectedElement) - expectedEvents.push("blur: " + _browser_tabfocus_test_lastfocus); + if (_lastfocus && _lastfocus != _expectedElement) + expectedEvents.push("blur: " + _lastfocus); - if (_browser_tabfocus_test_lastfocuswindow && - _browser_tabfocus_test_lastfocuswindow != expectedWindow) { + if (_lastfocuswindow && + _lastfocuswindow != expectedWindow) { - if (!gMultiProcessBrowser || _browser_tabfocus_test_lastfocuswindow != "main-window") { - expectedEvents.push("blur: " + _browser_tabfocus_test_lastfocuswindow + "-document"); - expectedEvents.push("blur: " + _browser_tabfocus_test_lastfocuswindow + "-window"); + if (!gMultiProcessBrowser || _lastfocuswindow != "main-window") { + expectedEvents.push("blur: " + _lastfocuswindow + "-document"); + expectedEvents.push("blur: " + _lastfocuswindow + "-window"); } } - if (expectedWindow && _browser_tabfocus_test_lastfocuswindow != expectedWindow) { + if (expectedWindow && _lastfocuswindow != expectedWindow) { if (gMultiProcessBrowser && expectedWindow != "main-window") { let browserid = expectedWindow == "window1" ? "browser1" : "browser2"; expectedEvents.push("focus: " + browserid); } if (!gMultiProcessBrowser || expectedWindow != "main-window") { expectedEvents.push("focus: " + expectedWindow + "-document"); expectedEvents.push("focus: " + expectedWindow + "-window"); } } if (newElementIsFocused) { expectedEvents.push("focus: " + expectedElement); } - _browser_tabfocus_test_lastfocus = expectedElement; - _browser_tabfocus_test_lastfocuswindow = expectedWindow; + _lastfocus = expectedElement; + _lastfocuswindow = expectedWindow; } return new Promise((resolve, reject) => { currentPromiseResolver = resolve; callback(); // No events are expected, so resolve the promise immediately. - if (!expectedEvents.length) { + if (expectedEvents["main-window"].length + expectedEvents["window1"].length + expectedEvents["window2"].length == 0) { currentPromiseResolver(); currentPromiseResolver = null; } }); }
--- a/browser/components/extensions/test/browser/browser_ext_optionsPage_privileges.js +++ b/browser/components/extensions/test/browser/browser_ext_optionsPage_privileges.js @@ -25,17 +25,17 @@ add_task(function* test_tab_options_priv browser.runtime.sendMessage({msgName: "removeTabId", tabId: tab.id}); }).catch(error => { browser.test.log(`Error: ${error} :: ${error.stack}`); browser.test.notifyFail("options-ui-privileges"); }); } let extension = ExtensionTestUtils.loadExtension({ - useAddonManager: true, + useAddonManager: "temporary", manifest: { "permissions": ["tabs"], "options_ui": { "page": "options.html", }, }, files: {
--- a/browser/components/extensions/test/browser/browser_ext_runtime_openOptionsPage.js +++ b/browser/components/extensions/test/browser/browser_ext_runtime_openOptionsPage.js @@ -1,15 +1,15 @@ /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; function* loadExtension(options) { let extension = ExtensionTestUtils.loadExtension({ - useAddonManager: true, + useAddonManager: "temporary", manifest: Object.assign({ "permissions": ["tabs"], }, options.manifest), files: { "options.html": `<!DOCTYPE html> <html>
--- a/browser/components/sessionstore/test/browser_crashedTabs.js +++ b/browser/components/sessionstore/test/browser_crashedTabs.js @@ -3,20 +3,24 @@ "use strict"; requestLongerTimeout(10); const PAGE_1 = "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page."; const PAGE_2 = "data:text/html,<html><body>Another%20regular,%20everyday,%20normal%20page."; -// Turn off tab animations for testing -Services.prefs.setBoolPref("browser.tabs.animate", false); -registerCleanupFunction(() => { - Services.prefs.clearUserPref("browser.tabs.animate"); +// Turn off tab animations for testing and use a single content process +// for these tests since we want to test tabs within the crashing process here. +add_task(function* test_initialize() { + yield SpecialPowers.pushPrefEnv({ + set: [ + [ "dom.ipc.processCount", 1 ], + [ "browser.tabs.animate", false] + ] }); }); // Allow tabs to restore on demand so we can test pending states Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand"); function clickButton(browser, id) { info("Clicking " + id);
--- a/browser/config/mozconfigs/linux32/release +++ b/browser/config/mozconfigs/linux32/release @@ -1,14 +1,14 @@ # This make file should be identical to the beta mozconfig, apart from the # safeguard below MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1} if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then - MOZ_AUTOMATION_UPLOAD_SYMBOLS=1 + MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS-1} MOZ_AUTOMATION_UPDATE_PACKAGING=1 fi . "$topsrcdir/browser/config/mozconfigs/linux32/common-opt" ac_add_options --enable-official-branding ac_add_options --enable-verify-mar
--- a/browser/config/mozconfigs/linux64/release +++ b/browser/config/mozconfigs/linux64/release @@ -1,14 +1,14 @@ # This make file should be identical to the beta mozconfig, apart from the # safeguard below MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1} if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then - MOZ_AUTOMATION_UPLOAD_SYMBOLS=1 + MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS-1} MOZ_AUTOMATION_UPDATE_PACKAGING=1 fi . "$topsrcdir/browser/config/mozconfigs/linux64/common-opt" ac_add_options --enable-official-branding ac_add_options --enable-verify-mar
--- a/browser/config/tooltool-manifests/win32/releng.manifest +++ b/browser/config/tooltool-manifests/win32/releng.manifest @@ -25,15 +25,15 @@ "size": 167175, "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831", "algorithm": "sha512", "filename": "sccache.tar.bz2", "unpack": true }, { "version": "Visual Studio 2015 Update 2 / SDK 10.0.10586.0/212", -"size": 332343834, -"digest": "55814aaabcd4aa51fe85918ec02a8c29bc067d41ee79ddcfd628daaba5a06d4241a73a51bf5a8bc69cc762b52551009f44b05e65682c45b4684c17fb2d017c2c", +"size": 332442800, +"digest": "995394a4a515c7cb0f8595f26f5395361a638870dd0bbfcc22193fe1d98a0c47126057d5999cc494f3f3eac5cb49160e79757c468f83ee5797298e286ef6252c", "algorithm": "sha512", "filename": "vs2015u2.zip", "unpack": true } ]
--- a/browser/config/tooltool-manifests/win64/releng.manifest +++ b/browser/config/tooltool-manifests/win64/releng.manifest @@ -26,15 +26,15 @@ "size": 167175, "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831", "algorithm": "sha512", "filename": "sccache.tar.bz2", "unpack": true }, { "version": "Visual Studio 2015 Update 2 / SDK 10.0.10586.0/212", -"size": 332343834, -"digest": "55814aaabcd4aa51fe85918ec02a8c29bc067d41ee79ddcfd628daaba5a06d4241a73a51bf5a8bc69cc762b52551009f44b05e65682c45b4684c17fb2d017c2c", +"size": 332442800, +"digest": "995394a4a515c7cb0f8595f26f5395361a638870dd0bbfcc22193fe1d98a0c47126057d5999cc494f3f3eac5cb49160e79757c468f83ee5797298e286ef6252c", "algorithm": "sha512", "filename": "vs2015u2.zip", "unpack": true } ]
--- a/build/autoconf/compiler-opts.m4 +++ b/build/autoconf/compiler-opts.m4 @@ -20,17 +20,16 @@ dnl Default to MSVC for win32 and gcc-4. dnl ============================================================== if test -z "$CROSS_COMPILE"; then case "$target" in *-mingw*) if test -z "$CC"; then CC=cl; fi if test -z "$CXX"; then CXX=cl; fi if test -z "$CPP"; then CPP="$CC -E -nologo"; fi if test -z "$CXXCPP"; then CXXCPP="$CXX -TP -E -nologo"; ac_cv_prog_CXXCPP="$CXXCPP"; fi - if test -z "$LD"; then LD=link; fi if test -z "$AS"; then case "${target_cpu}" in i*86) AS=ml; ;; x86_64) AS=ml64; ;;
--- a/build/moz.configure/util.configure +++ b/build/moz.configure/util.configure @@ -56,36 +56,79 @@ def is_absolute_or_relative(path): return True return os.sep in path @imports(_import='mozpack.path', _as='mozpath') def normsep(path): return mozpath.normsep(path) + +@imports('ctypes') +@imports(_from='ctypes', _import='wintypes') +def get_GetShortPathNameW(): + GetShortPathNameW = ctypes.windll.kernel32.GetShortPathNameW + GetShortPathNameW.argtypes = [wintypes.LPCWSTR, wintypes.LPWSTR, + wintypes.DWORD] + GetShortPathNameW.restype = wintypes.DWORD + return GetShortPathNameW + + +@template +@imports('ctypes') +@imports('platform') +@imports(_from='mozbuild.shellutil', _import='quote') +def normalize_path(): + # Until the build system can properly handle programs that need quoting, + # transform those paths into their short version on Windows (e.g. + # c:\PROGRA~1...). + if platform.system() == 'Windows': + GetShortPathNameW = get_GetShortPathNameW() + + def normalize_path(path): + path = normsep(path) + if quote(path) == path: + return path + size = 0 + while True: + out = ctypes.create_unicode_buffer(size) + needed = GetShortPathNameW(path, out, size) + if size >= needed: + return normsep(out.value) + size = needed + + else: + def normalize_path(path): + return normsep(path) + + return normalize_path + +normalize_path = normalize_path() + + # Locates the given program using which, or returns the given path if it # exists. # The `paths` parameter may be passed to search the given paths instead of # $PATH. @imports(_from='which', _import='which') @imports(_from='which', _import='WhichError') @imports('itertools') @imports(_from='os', _import='pathsep') def find_program(file, paths=None): try: if is_absolute_or_relative(file): - return normsep(which(os.path.basename(file), - [os.path.dirname(file)])) + return normalize_path(which(os.path.basename(file), + [os.path.dirname(file)])) if paths: if not isinstance(paths, (list, tuple)): die("Paths provided to find_program must be a list of strings, " "not %r", paths) paths = list(itertools.chain( *(p.split(pathsep) for p in paths if p))) - return normsep(which(file, path=paths)) + return normalize_path(which(file, path=paths)) except WhichError: return None @imports('os') @imports('subprocess') @imports(_from='mozbuild.configure.util', _import='LineIO') @imports(_from='tempfile', _import='mkstemp')
--- a/build/moz.configure/windows.configure +++ b/build/moz.configure/windows.configure @@ -45,41 +45,58 @@ def windows_sdk_dir(value, host): return tuple(x[1] for x in get_registry_values( r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots' r'\KitsRoot*')) # The Windows SDK 8.1 and 10 have different layouts. The former has # $SDK/include/$subdir, while the latter has $SDK/include/$version/$subdir. # The vcvars* scripts don't actually care about the version, they just take -# the last. +# the last alphanumerically. +# The $SDK/lib directories always have version subdirectories, but while the +# versions match the one in $SDK/include for SDK 10, it's "winv6.3" for SDK +# 8.1. @imports('os') @imports('re') @imports(_from='__builtin__', _import='sorted') @imports(_from='__builtin__', _import='WindowsError') -def get_include_dir(sdk, subdir): - base = os.path.join(sdk, 'include') - try: - subdirs = [d for d in os.listdir(base) - if os.path.isdir(os.path.join(base, d))] - except WindowsError: - subdirs = [] - if not subdirs: - return None - if subdir in subdirs: - return os.path.join(base, subdir) - # At this point, either we have an incomplete or invalid SDK directory, - # or we exclusively have version numbers in subdirs. - versions = sorted((Version(d) for d in subdirs), reverse=True) - # Version('non-number').major is 0, so if the biggest version we have is - # 0, we have a problem. - if versions[0].major == 0: - return None - path = os.path.join(base, str(versions[0]), subdir) - return path if os.path.isdir(path) else None +def get_sdk_dirs(sdk, subdir): + def get_dirs_containing(sdk, stem, subdir): + base = os.path.join(sdk, stem) + try: + subdirs = [d for d in os.listdir(base) + if os.path.isdir(os.path.join(base, d))] + except WindowsError: + subdirs = [] + if not subdirs: + return () + if subdir in subdirs: + return (base,) + # At this point, either we have an incomplete or invalid SDK directory, + # or we exclusively have version numbers in subdirs. + return tuple(os.path.join(base, s) for s in subdirs + if os.path.isdir(os.path.join(base, s, subdir))) + + def categorize(dirs): + return {os.path.basename(d): d for d in dirs} + + include_dirs = categorize(get_dirs_containing(sdk, 'include', subdir)) + lib_dirs = categorize(get_dirs_containing(sdk, 'lib', subdir)) + + if 'include' in include_dirs: + include_dirs['winv6.3'] = include_dirs['include'] + del include_dirs['include'] + + valid_versions = sorted(set(include_dirs) & set(lib_dirs), reverse=True) + if valid_versions: + return namespace( + path=sdk, + lib=lib_dirs[valid_versions[0]], + include=include_dirs[valid_versions[0]], + ) @imports(_from='mozbuild.shellutil', _import='quote') def valid_windows_sdk_dir_result(value): if value: return '0x%04x in %s' % (value.version, quote(value.path)) @depends_win(c_compiler, windows_sdk_dir, valid_windows_version, @@ -88,74 +105,237 @@ def valid_windows_sdk_dir_result(value): @imports(_from='__builtin__', _import='sorted') @imports(_from='textwrap', _import='dedent') def valid_windows_sdk_dir(compiler, windows_sdk_dir, target_version, windows_sdk_dir_env): if windows_sdk_dir_env: windows_sdk_dir_env = windows_sdk_dir_env[0] sdks = {} for d in windows_sdk_dir: - um_dir = get_include_dir(d, 'um') - shared_dir = get_include_dir(d, 'shared') - if um_dir and shared_dir: + sdk = get_sdk_dirs(d, 'um') + if sdk: check = dedent('''\ #include <winsdkver.h> WINVER_MAXVER ''') + um_dir = os.path.join(sdk.include, 'um') + shared_dir = os.path.join(sdk.include, 'shared') result = try_preprocess(compiler.wrapper + [compiler.compiler] + compiler.flags + ['-I', um_dir, '-I', shared_dir], 'C', check) if result: maxver = result.splitlines()[-1] try: maxver = int(maxver, 0) except: pass else: - sdks[d] = maxver + sdks[d] = maxver, sdk continue if d == windows_sdk_dir_env: raise FatalCheckError( 'Error while checking the version of the SDK in ' 'WINDOWSSDKDIR (%s). Please verify it contains a valid and ' 'complete SDK installation.' % windows_sdk_dir_env) - valid_sdks = sorted(sdks, key=lambda x: sdks[x], reverse=True) + valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True) if valid_sdks: - biggest_version = sdks[valid_sdks[0]] + biggest_version, sdk = sdks[valid_sdks[0]] if not valid_sdks or biggest_version < target_version: if windows_sdk_dir_env: raise FatalCheckError( 'You are targeting Windows version 0x%04x, but your SDK only ' 'supports up to version 0x%04x. Install and use an updated SDK, ' 'or target a lower version using --with-windows-version. ' 'Alternatively, try running the Windows SDK Configuration Tool ' 'and selecting a newer SDK. See ' 'https://developer.mozilla.org/En/Windows_SDK_versions for ' 'details on fixing this.' % (target_version, biggest_version)) raise FatalCheckError( 'Cannot find a Windows SDK for version >= 0x%04x.' % target_version) return namespace( - path=valid_sdks[0], + path=sdk.path, + include=sdk.include, + lib=sdk.lib, version=biggest_version, ) add_old_configure_assignment( 'WINDOWSSDKDIR', delayed_getattr(valid_windows_sdk_dir, 'path')) add_old_configure_assignment( 'MOZ_WINSDK_MAXVER', depends(valid_windows_sdk_dir)( lambda x: '0x%04X0000' % x.version if x else None)) +@imports(_from='mozbuild.shellutil', _import='quote') +def valid_ucrt_sdk_dir_result(value): + if value: + return '%s in %s' % (value.version, quote(value.path)) + +@depends_win(windows_sdk_dir, 'WINDOWSSDKDIR') +@checking('for Universal CRT SDK', valid_ucrt_sdk_dir_result) +@imports(_from='__builtin__', _import='sorted') +def valid_ucrt_sdk_dir(windows_sdk_dir, windows_sdk_dir_env): + if windows_sdk_dir_env: + windows_sdk_dir_env = windows_sdk_dir_env[0] + sdks = {} + for d in windows_sdk_dir: + sdk = get_sdk_dirs(d, 'ucrt') + if sdk: + version = os.path.basename(sdk.include) + # We're supposed to always find a version in the directory, because + # the 8.1 SDK, which doesn't have a version in the directory, doesn't + # contain the Universal CRT SDK. When the main SDK is 8.1, there + # is, however, supposed to be a reduced install of the SDK 10 + # with the UCRT. + if version != 'include': + sdks[d] = Version(version), sdk + continue + if d == windows_sdk_dir_env: + raise FatalCheckError( + 'The SDK in WINDOWSSDKDIR (%s) does not contain the Universal ' + 'CRT.' % windows_sdk_dir_env) + + valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True) + if not valid_sdks: + raise FatalCheckError('Cannot find the Universal CRT SDK. ' + 'Please install it.') + + version, sdk = sdks[valid_sdks[0]] + + return namespace( + path=sdk.path, + include=sdk.include, + lib=sdk.lib, + version=version, + ) + + +@depends_win(c_compiler) +@imports('os') +def vc_path(c_compiler): + if c_compiler.type != 'msvc': + return + # Normally, we'd start from c_compiler.compiler, but for now, it's not the + # ideal full path to the compiler. At least, we're guaranteed find_program + # will get us the one we found in toolchain.configure. + cl = find_program(c_compiler.compiler) + result = os.path.dirname(cl) + while True: + next, p = os.path.split(result) + if next == result: + die('Cannot determine the Visual C++ directory the compiler (%s) ' + 'is in' % cl) + result = next + if p.lower() == 'bin': + break + return result + + +@depends_win(vc_path) +@checking('for the Debug Interface Access SDK', lambda x: x or 'not found') +def dia_sdk_dir(vc_path): + if vc_path: + path = os.path.join(os.path.dirname(vc_path), 'DIA SDK') + if os.path.isdir(path): + return path + + +@depends_win(vc_path, valid_windows_sdk_dir, valid_ucrt_sdk_dir, dia_sdk_dir) +@imports('os') +def include_path(vc_path, windows_sdk_dir, ucrt_sdk_dir, dia_sdk_dir): + if not vc_path: + return + atlmfc_dir = os.path.join(vc_path, 'atlmfc', 'include') + if not os.path.isdir(atlmfc_dir): + die('Cannot find the ATL/MFC headers in the Visual C++ directory (%s). ' + 'Please install them.' % vc_path) + + winrt_dir = os.path.join(windows_sdk_dir.include, 'winrt') + if not os.path.isdir(winrt_dir): + die('Cannot find the WinRT headers in the Windows SDK directory (%s). ' + 'Please install them.' % windows_sdk_dir.path) + + includes = [] + include_env = os.environ.get('INCLUDE') + if include_env: + includes.append(include_env) + includes.extend(( + os.path.join(vc_path, 'include'), + atlmfc_dir, + os.path.join(windows_sdk_dir.include, 'shared'), + os.path.join(windows_sdk_dir.include, 'um'), + winrt_dir, + os.path.join(ucrt_sdk_dir.include, 'ucrt'), + )) + if dia_sdk_dir: + includes.append(os.path.join(dia_sdk_dir, 'include')) + # Set in the environment for old-configure + includes = os.pathsep.join(includes) + os.environ['INCLUDE'] = includes + return includes + +set_config('INCLUDE', include_path) + + +@depends_win(target, vc_path, valid_windows_sdk_dir, valid_ucrt_sdk_dir, dia_sdk_dir) +@imports('os') +def lib_path(target, vc_path, windows_sdk_dir, ucrt_sdk_dir, dia_sdk_dir): + if not vc_path: + return + vc_target = { + 'x86': '', + 'x86_64': 'amd64', + 'arm': 'arm', + }.get(target.cpu) + if vc_target is None: + return + # As vc_target can be '', and os.path.join will happily use the empty + # string, leading to a string ending with a backslash, that Make will + # interpret as a "string continues on next line" indicator, use variable + # args. + vc_target = (vc_target,) if vc_target else () + sdk_target = { + 'x86': 'x86', + 'x86_64': 'x64', + 'arm': 'arm', + }.get(target.cpu) + + atlmfc_dir = os.path.join(vc_path, 'atlmfc', 'lib', *vc_target) + if not os.path.isdir(atlmfc_dir): + die('Cannot find the ATL/MFC libraries in the Visual C++ directory (%s). ' + 'Please install them.' % vc_path) + + + libs = [] + lib_env = os.environ.get('LIB') + if lib_env: + libs.append(lib_env) + libs.extend(( + os.path.join(vc_path, 'lib', *vc_target), + atlmfc_dir, + os.path.join(windows_sdk_dir.lib, 'um', sdk_target), + os.path.join(ucrt_sdk_dir.lib, 'ucrt', sdk_target), + )) + if dia_sdk_dir: + libs.append(os.path.join(dia_sdk_dir, 'lib', *vc_target)) + # Set in the environment for old-configure + libs = os.pathsep.join(libs) + os.environ['LIB'] = libs + return libs + +set_config('LIB', lib_path) + + option(env='MT', nargs=1, help='Path to the Microsoft Manifest Tool') @depends_win(valid_windows_sdk_dir) @imports(_from='os', _import='environ') @imports('platform') def sdk_bin_path(valid_windows_sdk_dir): if not valid_windows_sdk_dir: return @@ -170,20 +350,18 @@ def sdk_bin_path(valid_windows_sdk_dir): os.path.join(valid_windows_sdk_dir.path, 'bin', vc_host) ] if vc_host == 'x64': result.append( os.path.join(valid_windows_sdk_dir.path, 'bin', 'x86')) return result -# Normally, we'd use `MT` instead of `_MT`, but for now, we want MT to only contain -# mt.exe. -mt = check_prog('_MT', depends_win()(lambda: ('mt.exe',)), what='mt', - input='MT', paths=sdk_bin_path) +mt = check_prog('MT', depends_win()(lambda: ('mt.exe',)), input='MT', + paths=sdk_bin_path) # Check that MT is not something unexpected like "magnetic tape manipulation # utility". @depends_win(mt) @checking('whether MT is really Microsoft Manifest Tool', lambda x: bool(x)) @imports('subprocess') def valid_mt(path): @@ -193,20 +371,29 @@ def valid_mt(path): if 'Microsoft (R) Manifest Tool' in l) if out: return path except subprocess.CalledProcessError: pass raise FatalCheckError('%s is not Microsoft Manifest Tool') -set_config('MT', depends_if(valid_mt)(lambda x: os.path.basename(x))) set_config('MSMANIFEST_TOOL', depends(valid_mt)(lambda x: bool(x))) +# Ultimately, this will move to toolchain.configure and be turned into a +# cross-platform check. +option(env='LD', nargs=1, help='Path to the linker') + +link = check_prog('LINK', depends_win()(lambda: ('link.exe',)), input='LD', + paths=vc_compiler_path) + +add_old_configure_assignment('LD', depends_win(link)(lambda x: x)) + + # Normally, we'd just have CC, etc. set to absolute paths, but the build system # doesn't currently handle properly the case where the paths contain spaces. # Additionally, there's the issue described in toolchain.configure, in # valid_compiler(). @depends_win(sdk_bin_path) @imports('os') def alter_path(sdk_bin_path): path = os.pathsep.join(sdk_bin_path)
--- a/build/win32/mozconfig.vs2015-win64 +++ b/build/win32/mozconfig.vs2015-win64 @@ -1,23 +1,25 @@ if [ -z "${VSPATH}" ]; then TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir} VSPATH="$(cd ${TOOLTOOL_DIR} && pwd)/vs2015u2" - VSWINPATH="$(cd ${TOOLTOOL_DIR} && pwd -W)/vs2015u2" fi +VSWINPATH="$(cd ${VSPATH} && pwd -W)" + export WINDOWSSDKDIR="${VSWINPATH}/SDK" export WIN32_REDIST_DIR="${VSPATH}/VC/redist/x86/Microsoft.VC140.CRT" export WIN_UCRT_REDIST_DIR="${VSPATH}/SDK/Redist/ucrt/DLLs/x86" -export PATH="${VSPATH}/VC/bin/amd64_x86:${VSPATH}/VC/bin/amd64:${VSPATH}/VC/bin:${VSPATH}/SDK/bin/x86:${VSPATH}/SDK/bin/x64:${VSPATH}/DIASDK/bin:${PATH}" +export PATH="${VSPATH}/VC/bin/amd64_x86:${VSPATH}/VC/bin/amd64:${VSPATH}/VC/bin:${VSPATH}/SDK/bin/x86:${VSPATH}/SDK/bin/x64:${VSPATH}/DIA SDK/bin:${PATH}" export PATH="${VSPATH}/VC/redist/x86/Microsoft.VC140.CRT:${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT:${VSPATH}/SDK/Redist/ucrt/DLLs/x86:${VSPATH}/SDK/Redist/ucrt/DLLs/x64:${PATH}" -export INCLUDE="${VSPATH}/VC/include:${VSPATH}/VC/atlmfc/include:${VSPATH}/SDK/Include/ucrt:${VSPATH}/SDK/Include/shared:${VSPATH}/SDK/Include/um:${VSPATH}/SDK/Include/winrt:${VSPATH}/DIASDK/include" -export LIB="${VSPATH}/VC/lib:${VSPATH}/VC/atlmfc/lib:${VSPATH}/SDK/lib/ucrt/x86:${VSPATH}/SDK/lib/um/x86:${VSPATH}/DIASDK/lib" +export INCLUDE="${VSPATH}/VC/include:${VSPATH}/VC/atlmfc/include:${VSPATH}/SDK/Include/10.0.10586.0/ucrt:${VSPATH}/SDK/Include/10.0.10586.0/shared:${VSPATH}/SDK/Include/10.0.10586.0/um:${VSPATH}/SDK/Include/10.0.10586.0/winrt:${VSPATH}/DIA SDK/include" +export LIB="${VSPATH}/VC/lib:${VSPATH}/VC/atlmfc/lib:${VSPATH}/SDK/lib/10.0.10586.0/ucrt/x86:${VSPATH}/SDK/lib/10.0.10586.0/um/x86:${VSPATH}/DIA SDK/lib" . $topsrcdir/build/mozconfig.vs-common +mk_export_correct_style WINDOWSSDKDIR mk_export_correct_style INCLUDE mk_export_correct_style LIB mk_export_correct_style PATH mk_export_correct_style WIN32_REDIST_DIR mk_export_correct_style WIN_UCRT_REDIST_DIR
--- a/build/win64/mozconfig.vs2015 +++ b/build/win64/mozconfig.vs2015 @@ -1,22 +1,24 @@ if [ -z "${VSPATH}" ]; then TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir} VSPATH="$(cd ${TOOLTOOL_DIR} && pwd)/vs2015u2" - VSWINPATH="$(cd ${TOOLTOOL_DIR} && pwd -W)/vs2015u2" fi +VSWINPATH="$(cd ${VSPATH} && pwd -W)" + export WINDOWSSDKDIR="${VSWINPATH}/SDK" export WIN32_REDIST_DIR=${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT export WIN_UCRT_REDIST_DIR="${VSPATH}/SDK/Redist/ucrt/DLLs/x64" -export PATH="${VSPATH}/VC/bin/amd64:${VSPATH}/VC/bin:${VSPATH}/SDK/bin/x64:${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT:${VSPATH}/SDK/Redist/ucrt/DLLs/x64:${VSPATH}/DIASDK/bin/amd64:${PATH}" +export PATH="${VSPATH}/VC/bin/amd64:${VSPATH}/VC/bin:${VSPATH}/SDK/bin/x64:${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT:${VSPATH}/SDK/Redist/ucrt/DLLs/x64:${VSPATH}/DIA SDK/bin/amd64:${PATH}" -export INCLUDE="${VSPATH}/VC/include:${VSPATH}/VC/atlmfc/include:${VSPATH}/SDK/Include/ucrt:${VSPATH}/SDK/Include/shared:${VSPATH}/SDK/Include/um:${VSPATH}/SDK/Include/winrt:${VSPATH}/DIASDK/include" -export LIB="${VSPATH}/VC/lib/amd64:${VSPATH}/VC/atlmfc/lib/amd64:${VSPATH}/SDK/lib/ucrt/x64:${VSPATH}/SDK/lib/um/x64:${VSPATH}/DIASDK/lib/amd64" +export INCLUDE="${VSPATH}/VC/include:${VSPATH}/VC/atlmfc/include:${VSPATH}/SDK/Include/10.0.10586.0/ucrt:${VSPATH}/SDK/Include/10.0.10586.0/shared:${VSPATH}/SDK/Include/10.0.10586.0/um:${VSPATH}/SDK/Include/10.0.10586.0/winrt:${VSPATH}/DIA SDK/include" +export LIB="${VSPATH}/VC/lib/amd64:${VSPATH}/VC/atlmfc/lib/amd64:${VSPATH}/SDK/lib/10.0.10586.0/ucrt/x64:${VSPATH}/SDK/lib/10.0.10586.0/um/x64:${VSPATH}/DIA SDK/lib/amd64" . $topsrcdir/build/mozconfig.vs-common +mk_export_correct_style WINDOWSSDKDIR mk_export_correct_style INCLUDE mk_export_correct_style LIB mk_export_correct_style PATH mk_export_correct_style WIN32_REDIST_DIR mk_export_correct_style WIN_UCRT_REDIST_DIR
--- a/build/windows_toolchain.py +++ b/build/windows_toolchain.py @@ -24,33 +24,28 @@ from mozpack.mozjar import ( ) import mozpack.path as mozpath # mozpack.match patterns for files under "Microsoft Visual Studio 14.0". VS_PATTERNS = [ { 'pattern': 'DIA SDK/bin/**', - # Various tools don't like spaces in filenames. So remove it. - 'rewrite': [('DIA SDK/', 'DIASDK/')], 'ignore': ( 'DIA SDK/bin/arm/**', ), }, { 'pattern': 'DIA SDK/idl/**', - 'rewrite': [('DIA SDK/', 'DIASDK/')], }, { 'pattern': 'DIA SDK/include/**', - 'rewrite': [('DIA SDK/', 'DIASDK/')], }, { 'pattern': 'DIA SDK/lib/**', - 'rewrite': [('DIA SDK/', 'DIASDK/')], 'ignore': ( 'DIA SDK/lib/arm/**', ), }, # ATL is needed by Breakpad. { 'pattern': 'VC/atlmfc/include/**', }, @@ -155,28 +150,22 @@ def resolve_files(): vs_path, sdk_path = find_vs_paths() for entry in VS_PATTERNS: finder = FileFinder(vs_path, find_executables=False, ignore=entry.get('ignore', [])) for p, f in finder.find(entry['pattern']): assert p.startswith(('VC/', 'DIA SDK/')) - for source, dest in entry.get('rewrite', []): - p = p.replace(source, dest) - yield p.encode('utf-8'), f for entry in SDK_PATTERNS: finder = FileFinder(sdk_path, find_executables=False, ignore=entry.get('ignore', [])) for p, f in finder.find(entry['pattern']): - # We remove the SDK version from the path so we don't have - # to update other configs when we change the SDK version. - p = p.replace('/%s/' % SDK_RELEASE, '/') relpath = 'SDK/%s' % p yield relpath.encode('utf-8'), f def resolve_files_and_hash(manifest): """Resolve files and hash their data.
--- a/config/config.mk +++ b/config/config.mk @@ -353,16 +353,28 @@ SDK_LIB_DIR = $(DIST)/sdk/lib SDK_BIN_DIR = $(DIST)/sdk/bin DEPENDENCIES = .md ifdef MACOSX_DEPLOYMENT_TARGET export MACOSX_DEPLOYMENT_TARGET endif # MACOSX_DEPLOYMENT_TARGET +# Export to propagate to cl and submake for third-party code. +# Eventually, we'll want to just use -I. +ifdef INCLUDE +export INCLUDE +endif + +# Export to propagate to link.exe and submake for third-party code. +# Eventually, we'll want to just use -LIBPATH. +ifdef LIB +export LIB +endif + ifdef MOZ_USING_CCACHE ifdef CLANG_CXX export CCACHE_CPP2=1 endif endif # Set link flags according to whether we want a console. ifeq ($(OS_ARCH),WINNT)
--- a/devtools/client/themes/toolbars.css +++ b/devtools/client/themes/toolbars.css @@ -243,16 +243,17 @@ .devtools-button:disabled, .devtools-button[disabled], .devtools-toolbarbutton[disabled] { opacity: 0.5 !important; } .devtools-button[checked]:empty::before, .devtools-button[open]:empty::before, +.devtools-button.checked::before, .devtools-toolbarbutton:not([label])[checked=true] > image, .devtools-toolbarbutton:not([label])[open=true] > image { filter: var(--checked-icon-filter); } /* Icon-and-text buttons */ .devtools-toolbarbutton.icon-and-text .toolbarbutton-text { margin-inline-start: .5em !important; @@ -303,16 +304,20 @@ :root { --clear-icon-url: url("chrome://devtools/skin/images/clear.svg"); } .devtools-button.devtools-clear-icon::before { background-image: var(--clear-icon-url); } +.devtools-button.devtools-filter-icon::before { + background-image: var(--filter-image); +} + .devtools-toolbarbutton.devtools-clear-icon { list-style-image: var(--clear-icon-url); } .devtools-option-toolbarbutton { list-style-image: var(--tool-options-image); } @@ -379,16 +384,25 @@ visibility: hidden; } .devtools-searchinput .textbox-input::-moz-placeholder, .devtools-filterinput .textbox-input::-moz-placeholder { font-style: normal; } +.devtools-plaininput { + border-color: transparent; + background-color: transparent; +} + +.theme-dark .devtools-plaininput { + color: var(--theme-highlight-gray); +} + /* Searchbox is a div container element for a search input element */ .devtools-searchbox { display: flex; flex: 1; height: 23px; position: relative; padding: 0 3px; } @@ -644,8 +658,36 @@ @keyframes throbber-spin { from { transform: none; } to { transform: rotate(360deg); } } + +/* + * Filter buttons + * @TODO : Fix when https://bugzilla.mozilla.org/show_bug.cgi?id=1255116 lands + */ +.menu-filter-button { + -moz-appearance: none; + background: rgba(128,128,128,0.1); + border: none; + border-radius: 2px; + min-width: 0; + padding: 0 5px; + margin: 2px; + color: var(--theme-body-color); +} + +.menu-filter-button:hover { + background: rgba(128,128,128,0.2); +} + +.menu-filter-button:hover:active { + background-color: var(--theme-selection-background-semitransparent); +} + +.menu-filter-button:not(:active).checked { + background-color: var(--theme-selection-background); + color: var(--theme-selection-color); +}
--- a/devtools/client/themes/webconsole.css +++ b/devtools/client/themes/webconsole.css @@ -146,16 +146,17 @@ a { display: block; flex: auto; } #output-wrapper { direction: ltr; overflow: auto; -moz-user-select: text; + position: relative; } /* The width on #output-container is set to a hardcoded px in webconsole.js since it's way faster than using 100% with -moz-box-flex (see Bug 1237368) */ #output-container.hideTimestamps > .message { padding-inline-start: 0; margin-inline-start: 7px; @@ -619,8 +620,46 @@ a.learn-more-link.webconsole-learn-more- .theme-firebug .consoletable .theme-body { width: 100%; border-top: 1px solid #D7D7D7; border-bottom: 2px solid #D7D7D7; border-left: 1px solid #D7D7D7; border-right: 1px solid #D7D7D7; } + + +/* NEW CONSOLE STYLES */ + +#output-wrapper > div { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; +} + +#output-container { + height: 100%; +} + +.webconsole-output-wrapper { + display: flex; + flex-direction: column; + height: 100%; +} + +.webconsole-filterbar-wrapper { + flex-grow: 0; +} + +.webconsole-output { + flex: 1; + overflow: auto; +} + +.webconsole-filterbar-primary { + display: flex; +} + +.webconsole-filterbar-primary .devtools-plaininput { + flex: 1 1 100%; +}
--- a/devtools/client/webconsole/new-console-output/actions/messages.js +++ b/devtools/client/webconsole/new-console-output/actions/messages.js @@ -8,26 +8,53 @@ const { prepareMessage } = require("devtools/client/webconsole/new-console-output/utils/messages"); const { MESSAGE_ADD, MESSAGES_CLEAR, + SEVERITY_FILTER, + MESSAGES_SEARCH, + FILTERS_CLEAR, } = require("../constants"); function messageAdd(packet) { let message = prepareMessage(packet); return { type: MESSAGE_ADD, message }; } function messagesClear() { return { type: MESSAGES_CLEAR }; } +function severityFilter(filter, toggled) { + return { + type: SEVERITY_FILTER, + filter, + toggled + }; +} + +function filtersClear() { + return { + type: FILTERS_CLEAR + }; +} + +function messagesSearch(searchText) { + return { + type: MESSAGES_SEARCH, + searchText + }; +} + exports.messageAdd = messageAdd; exports.messagesClear = messagesClear; +exports.severityFilter = severityFilter; +exports.filtersClear = filtersClear; +exports.messagesSearch = messagesSearch;
--- a/devtools/client/webconsole/new-console-output/actions/moz.build +++ b/devtools/client/webconsole/new-console-output/actions/moz.build @@ -1,8 +1,9 @@ # vim: set filetype=python: # 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/. DevToolsModules( 'messages.js', + 'ui.js', )
new file mode 100644 --- /dev/null +++ b/devtools/client/webconsole/new-console-output/actions/ui.js @@ -0,0 +1,19 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* 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/. */ + +"use strict"; + +const { + FILTERBAR_TOGGLE, +} = require("../constants"); + +function filterBarToggle(show) { + return { + type: FILTERBAR_TOGGLE + }; +} + +exports.filterBarToggle = filterBarToggle;
--- a/devtools/client/webconsole/new-console-output/components/console-output.js +++ b/devtools/client/webconsole/new-console-output/components/console-output.js @@ -22,38 +22,37 @@ const ConsoleOutput = createClass({ // This function is created in mergeProps openVariablesView: PropTypes.func.isRequired, messages: PropTypes.array.isRequired }, displayName: "ConsoleOutput", componentWillUpdate() { - // @TODO Move this to a parent component. - let node = ReactDOM.findDOMNode(this).parentNode.parentNode.parentNode; + let node = ReactDOM.findDOMNode(this); if (node.lastChild) { this.shouldScrollBottom = isScrolledToBottom(node.lastChild, node); } }, componentDidUpdate() { if (this.shouldScrollBottom) { - let node = ReactDOM.findDOMNode(this).parentNode.parentNode.parentNode; + let node = ReactDOM.findDOMNode(this); node.scrollTop = node.scrollHeight; } }, render() { let messageNodes = this.props.messages.map(function (message) { return ( MessageContainer({ message }) ); }); return ( - dom.div({}, messageNodes) + dom.div({className: "webconsole-output"}, messageNodes) ); } }); function isScrolledToBottom(outputNode, scrollNode) { let lastNodeHeight = outputNode.lastChild ? outputNode.lastChild.clientHeight : 0; return scrollNode.scrollTop + scrollNode.clientHeight >=
new file mode 100644 --- /dev/null +++ b/devtools/client/webconsole/new-console-output/components/filter-bar.js @@ -0,0 +1,133 @@ +/* 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/. */ +"use strict"; + +const { + createFactory, + createClass, + DOM: dom, + PropTypes +} = require("devtools/client/shared/vendor/react"); +const { connect } = require("devtools/client/shared/vendor/react-redux"); +const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters"); +const { getAllUi } = require("devtools/client/webconsole/new-console-output/selectors/ui"); +const messagesActions = require("devtools/client/webconsole/new-console-output/actions/messages"); +const uiActions = require("devtools/client/webconsole/new-console-output/actions/ui"); +const { store } = require("devtools/client/webconsole/new-console-output/store"); +const { + SEVERITY_FILTER +} = require("../constants"); +const FilterToggleButton = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-toggle-button").FilterToggleButton); + +const FilterBar = createClass({ + + displayName: "FilterBar", + + propTypes: { + filter: PropTypes.object.isRequired, + ui: PropTypes.object.isRequired + }, + + onClearOutputButtonClick: function () { + store.dispatch(messagesActions.messagesClear()); + }, + + onToggleFilterConfigBarButtonClick: function () { + store.dispatch(uiActions.filterBarToggle()); + }, + + onClearFiltersButtonClick: function () { + store.dispatch(messagesActions.filtersClear()); + }, + + onSearchInput: function (e) { + store.dispatch(messagesActions.messagesSearch(e.target.value)); + }, + + render() { + const {filter, ui} = this.props; + let configFilterBarVisible = ui.configFilterBarVisible; + let children = []; + + children.push(dom.div({className: "devtools-toolbar webconsole-filterbar-primary"}, + dom.button({ + className: "devtools-button devtools-clear-icon", + title: "Clear output", + onClick: this.onClearOutputButtonClick + }), + dom.button({ + className: "devtools-button devtools-filter-icon" + ( + configFilterBarVisible ? " checked" : ""), + title: "Toggle filter bar", + onClick: this.onToggleFilterConfigBarButtonClick + }), + dom.input({ + className: "devtools-plaininput", + type: "search", + value: filter.searchText, + placeholder: "Filter output", + onInput: this.onSearchInput + }) + )); + + if (configFilterBarVisible) { + children.push( + dom.div({className: "devtools-toolbar"}, + FilterToggleButton({ + active: filter.error, + label: "Errors", + filterType: SEVERITY_FILTER, + filterKey: "error"}), + FilterToggleButton({ + active: filter.warn, + label: "Warnings", + filterType: SEVERITY_FILTER, + filterKey: "warn"}), + FilterToggleButton({ + active: filter.log, + label: "Logs", + filterType: SEVERITY_FILTER, + filterKey: "log"}), + FilterToggleButton({ + active: filter.info, + label: "Info", + filterType: SEVERITY_FILTER, + filterKey: "info"}) + ) + ); + } + + if (ui.filteredMessageVisible) { + children.push( + dom.div({className: "devtools-toolbar"}, + dom.span({ + className: "clear"}, + "You have filters set that may hide some results. " + + "Learn more about our filtering syntax ", + dom.a({}, "here"), + "."), + dom.button({ + className: "menu-filter-button", + onClick: this.onClearFiltersButtonClick + }, "Remove filters") + ) + ); + } + + return ( + dom.div({className: "webconsole-filteringbar-wrapper"}, + children + ) + ); + } +}); + +function mapStateToProps(state) { + return { + filter: getAllFilters(state), + ui: getAllUi(state) + }; +} + +module.exports = connect(mapStateToProps)(FilterBar);
new file mode 100644 --- /dev/null +++ b/devtools/client/webconsole/new-console-output/components/filter-toggle-button.js @@ -0,0 +1,50 @@ +/* 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/. */ +"use strict"; + +const { + createClass, + DOM: dom, + PropTypes +} = require("devtools/client/shared/vendor/react"); +const { store } = require("devtools/client/webconsole/new-console-output/store"); +const actions = require("devtools/client/webconsole/new-console-output/actions/messages"); +const { + SEVERITY_FILTER +} = require("../constants"); + +const FilterToggleButton = createClass({ + + displayName: "FilterToggleButton", + + propTypes: { + label: PropTypes.string.isRequired, + filterType: PropTypes.string.isRequired, + filterKey: PropTypes.string.isRequired, + active: PropTypes.bool.isRequired, + }, + + onClick: function () { + if (this.props.filterType === SEVERITY_FILTER) { + store.dispatch(actions.severityFilter( + this.props.filterKey, !this.props.active)); + } + }, + + render() { + const {label, active} = this.props; + + let classList = ["menu-filter-button"]; + if (active) { + classList.push("checked"); + } + + return dom.button({ + className: classList.join(" "), + onClick: this.onClick + }, label); + } +}); + +exports.FilterToggleButton = FilterToggleButton;
--- a/devtools/client/webconsole/new-console-output/components/message-container.js +++ b/devtools/client/webconsole/new-console-output/components/message-container.js @@ -28,16 +28,20 @@ const componentMap = new Map([ const MessageContainer = createClass({ displayName: "MessageContainer", propTypes: { message: PropTypes.object.isRequired }, + shouldComponentUpdate(nextProps, nextState) { + return this.props.message.repeat !== nextProps.message.repeat; + }, + render() { const { message } = this.props; let MessageComponent = createFactory(getMessageComponent(message)); return MessageComponent({ message }); } }); function getMessageComponent(message) {
--- a/devtools/client/webconsole/new-console-output/components/moz.build +++ b/devtools/client/webconsole/new-console-output/components/moz.build @@ -4,14 +4,16 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. DIRS += [ 'message-types' ] DevToolsModules( 'console-output.js', + 'filter-bar.js', + 'filter-toggle-button.js', 'grip-message-body.js', 'message-container.js', 'message-icon.js', 'message-repeat.js', 'variables-view-link.js' )
--- a/devtools/client/webconsole/new-console-output/constants.js +++ b/devtools/client/webconsole/new-console-output/constants.js @@ -3,16 +3,20 @@ /* 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/. */ "use strict"; const actionTypes = { MESSAGE_ADD: "MESSAGE_ADD", MESSAGES_CLEAR: "MESSAGES_CLEAR", + SEVERITY_FILTER: "SEVERITY_FILTER", + MESSAGES_SEARCH: "MESSAGES_SEARCH", + FILTERS_CLEAR: "FILTERS_CLEAR", + FILTERBAR_TOGGLE: "FILTERBAR_TOGGLE", }; const categories = { CATEGORY_NETWORK: "network", CATEGORY_CSS: "cssparser", CATEGORY_JS: "exception", CATEGORY_WEBDEV: "console", CATEGORY_INPUT: "input", @@ -87,11 +91,15 @@ const chromeRDPEnums = { LOG: "log", ERROR: "error", WARN: "warn", DEBUG: "debug", INFO: "info" } }; +const filterTypes = { + SEVERITY_FILTER: "SEVERITY_FILTER" +}; + // Combine into a single constants object module.exports = Object.assign({}, actionTypes, categories, severities, levels, - chromeRDPEnums); + chromeRDPEnums, filterTypes);
--- a/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js +++ b/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js @@ -7,21 +7,29 @@ const React = require("devtools/client/shared/vendor/react"); const ReactDOM = require("devtools/client/shared/vendor/react-dom"); const { Provider } = require("devtools/client/shared/vendor/react-redux"); const actions = require("devtools/client/webconsole/new-console-output/actions/messages"); const { store } = require("devtools/client/webconsole/new-console-output/store"); const ConsoleOutput = React.createFactory(require("devtools/client/webconsole/new-console-output/components/console-output")); +const FilterBar = React.createFactory(require("devtools/client/webconsole/new-console-output/components/filter-bar")); function NewConsoleOutputWrapper(parentNode, jsterm) { let childComponent = ConsoleOutput({ jsterm }); + let filterBar = FilterBar({}); let provider = React.createElement( - Provider, { store: store }, childComponent); + Provider, + { store: store }, + React.DOM.div( + {className: "webconsole-output-wrapper"}, + filterBar, + childComponent + )); this.body = ReactDOM.render(provider, parentNode); } NewConsoleOutputWrapper.prototype = { dispatchMessageAdd: (message) => { store.dispatch(actions.messageAdd(message)); }, dispatchMessagesClear: () => {
new file mode 100644 --- /dev/null +++ b/devtools/client/webconsole/new-console-output/reducers/filters.js @@ -0,0 +1,35 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* 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/. */ +"use strict"; + +const Immutable = require("devtools/client/shared/vendor/immutable"); +const constants = require("devtools/client/webconsole/new-console-output/constants"); + +const FilterState = Immutable.Record({ + error: true, + warn: true, + info: true, + log: true, + searchText: "" +}); + +function filters(state = new FilterState(), action) { + switch (action.type) { + case constants.SEVERITY_FILTER: + let {filter, toggled} = action; + return state.set(filter, toggled); + case constants.FILTERS_CLEAR: + return new FilterState(); + case constants.MESSAGES_SEARCH: + let {searchText} = action; + return state.set("searchText", searchText); + } + + return state; +} + +exports.FilterState = FilterState; +exports.filters = filters;
--- a/devtools/client/webconsole/new-console-output/reducers/index.js +++ b/devtools/client/webconsole/new-console-output/reducers/index.js @@ -1,14 +1,18 @@ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* 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/. */ "use strict"; +const { filters } = require("./filters"); const { messages } = require("./messages"); const { prefs } = require("./prefs"); +const { ui } = require("./ui"); exports.reducers = { + filters, messages, prefs, + ui, };
--- a/devtools/client/webconsole/new-console-output/reducers/moz.build +++ b/devtools/client/webconsole/new-console-output/reducers/moz.build @@ -1,10 +1,12 @@ # vim: set filetype=python: # 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/. DevToolsModules( + 'filters.js', 'index.js', 'messages.js', 'prefs.js', + 'ui.js', )
--- a/devtools/client/webconsole/new-console-output/reducers/prefs.js +++ b/devtools/client/webconsole/new-console-output/reducers/prefs.js @@ -1,12 +1,18 @@ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* 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/. */ "use strict"; -function prefs(state = {}, action) { +const Immutable = require("devtools/client/shared/vendor/immutable"); +const PrefState = Immutable.Record({ + logLimit: 1000 +}); + +function prefs(state = new PrefState(), action) { return state; } +exports.PrefState = PrefState; exports.prefs = prefs;
new file mode 100644 --- /dev/null +++ b/devtools/client/webconsole/new-console-output/reducers/ui.js @@ -0,0 +1,25 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* 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/. */ +"use strict"; + +const constants = require("devtools/client/webconsole/new-console-output/constants"); +const Immutable = require("devtools/client/shared/vendor/immutable"); + +const Ui = Immutable.Record({ + configFilterBarVisible: false, + filteredMessageVisible: false +}); + +function ui(state = new Ui(), action) { + switch (action.type) { + case constants.FILTERBAR_TOGGLE: + return state.set("configFilterBarVisible", !state.configFilterBarVisible); + } + + return state; +} + +exports.ui = ui;
new file mode 100644 --- /dev/null +++ b/devtools/client/webconsole/new-console-output/selectors/filters.js @@ -0,0 +1,12 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* 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/. */ +"use strict"; + +function getAllFilters(state) { + return state.filters; +} + +exports.getAllFilters = getAllFilters;
--- a/devtools/client/webconsole/new-console-output/selectors/messages.js +++ b/devtools/client/webconsole/new-console-output/selectors/messages.js @@ -1,22 +1,55 @@ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* 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/. */ "use strict"; +const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters"); const { getLogLimit } = require("devtools/client/webconsole/new-console-output/selectors/prefs"); function getAllMessages(state) { let messages = state.messages; + let logLimit = getLogLimit(state); + let filters = getAllFilters(state); + + return prune( + search( + filterSeverity(messages, filters), + filters.searchText + ), + logLimit + ); +} + +function filterSeverity(messages, filters) { + return messages.filter((message) => filters[message.severity] === true); +} + +function search(messages, searchText = "") { + if (searchText === "") { + return messages; + } + + return messages.filter(function (message) { + // @TODO: message.parameters can be a grip, see how we can handle that + if (!Array.isArray(message.parameters)) { + return true; + } + return message + .parameters.join("") + .toLocaleLowerCase() + .includes(searchText.toLocaleLowerCase()); + }); +} + +function prune(messages, logLimit) { let messageCount = messages.count(); - let logLimit = getLogLimit(state); - if (messageCount > logLimit) { return messages.splice(0, messageCount - logLimit); } return messages; } exports.getAllMessages = getAllMessages;
--- a/devtools/client/webconsole/new-console-output/selectors/moz.build +++ b/devtools/client/webconsole/new-console-output/selectors/moz.build @@ -1,9 +1,11 @@ # vim: set filetype=python: # 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/. DevToolsModules( + 'filters.js', 'messages.js', 'prefs.js', + 'ui.js', )
new file mode 100644 --- /dev/null +++ b/devtools/client/webconsole/new-console-output/selectors/ui.js @@ -0,0 +1,12 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* 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/. */ +"use strict"; + +function getAllUi(state) { + return state.ui; +} + +exports.getAllUi = getAllUi;
--- a/devtools/client/webconsole/new-console-output/store.js +++ b/devtools/client/webconsole/new-console-output/store.js @@ -1,24 +1,31 @@ /* 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/. */ "use strict"; +const Services = require("Services"); +const {FilterState} = require("devtools/client/webconsole/new-console-output/reducers/filters"); +const {PrefState} = require("devtools/client/webconsole/new-console-output/reducers/prefs"); const { combineReducers, createStore } = require("devtools/client/shared/vendor/redux"); -const Immutable = require("devtools/client/shared/vendor/immutable"); const { reducers } = require("./reducers/index"); -const Services = require("Services"); function storeFactory() { const initialState = { - messages: Immutable.List(), - prefs: { - logLimit: Math.max(Services.prefs.getIntPref("devtools.hud.loglimit"), 1) - } + prefs: new PrefState({ + logLimit: Math.max(Services.prefs.getIntPref("devtools.hud.loglimit"), 1), + }), + filters: new FilterState({ + error: Services.prefs.getBoolPref("devtools.webconsole.filter.error"), + warn: Services.prefs.getBoolPref("devtools.webconsole.filter.warn"), + info: Services.prefs.getBoolPref("devtools.webconsole.filter.info"), + log: Services.prefs.getBoolPref("devtools.webconsole.filter.log"), + searchText: "" + }) }; return createStore(combineReducers(reducers), initialState); } // Provide the single store instance for app code. module.exports.store = storeFactory(); // Provide the store factory for test code so that each test is working with
--- a/devtools/client/webconsole/new-console-output/test/components/test_console-api-call.html +++ b/devtools/client/webconsole/new-console-output/test/components/test_console-api-call.html @@ -39,18 +39,26 @@ function testConsoleCount() { for (let i = 0; i < 3; i++) { const packet = yield getPacket("console.count('bar')", "consoleAPICall"); const message = prepareMessage(packet); const rendered = renderComponent(ConsoleApiCall, {message: message}); const messageBody = getMessageBody(rendered); const expected = `bar: ${i + 1}`; is(messageBody.textContent, expected, - "console.count has the expected text content: ${expected}"); + `console.count has the expected text content: "${expected}"`); } + + const packet = yield getPacket("console.count()", "consoleAPICall") + const message = prepareMessage(packet); + const rendered = renderComponent(ConsoleApiCall, {message: message}); + const messageBody = getMessageBody(rendered); + const expected = "<no label>: 1"; + is(messageBody.textContent, expected, + `console.count without label has the expected text content: "${expected}"`); } function getMessageBody(renderedComponent) { const queryPath = "div.message.cm-s-mozilla span span.message-flex-body span.message-body.devtools-monospace"; return renderedComponent.querySelector(queryPath); } </script>
--- a/devtools/client/webconsole/new-console-output/test/utils/chrome.ini +++ b/devtools/client/webconsole/new-console-output/test/utils/chrome.ini @@ -1,6 +1,8 @@ [DEFAULT] support-files = ../components/head.js [test_getRepeatId.html] +[test_render_perf.html] +skip-if = debug
new file mode 100644 --- /dev/null +++ b/devtools/client/webconsole/new-console-output/test/utils/test_render_perf.html @@ -0,0 +1,59 @@ +<!DOCTYPE HTML> +<html lang="en"> +<head> + <meta charset="utf8"> + <title>Test for getRepeatId()</title> + <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript;version=1.8" src="head.js"></script> + <!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +</head> +<body> +<p>Test for render perf</p> +<div id="output"></div> + +<script type="text/javascript;version=1.8"> +const testPackets = []; +const numMessages = 1000; +for (let id = 0; id < numMessages; id++) { + testPackets.push({ + "from": "server1.conn4.child1/consoleActor2", + "type": "consoleAPICall", + "message": { + "arguments": [ + "foobar", + "test", + id + ], + "columnNumber": 1, + "counter": null, + "filename": "file:///test.html", + "functionName": "", + "groupName": "", + "level": "log", + "lineNumber": 1, + "private": false, + "styles": [], + "timeStamp": 1455064271115 + id, + "timer": null, + "workerType": "none", + "category": "webdev" + } + }); +} +window.onload = Task.async(function* () { + const NewConsoleOutputWrapper = browserRequire("devtools/client/webconsole/new-console-output/new-console-output-wrapper"); + const wrapper = new NewConsoleOutputWrapper(document.querySelector("#output"), {}); + let start = performance.now(); + testPackets.forEach((message) => { + wrapper.dispatchMessageAdd(message); + }); + let elapsed = performance.now() - start; + info("took " + elapsed / 1000 + " seconds"); + ok(true, "Yay, it didn't time out!"); + + SimpleTest.finish(); +}); +</script> +</body> +</html>
--- a/devtools/client/webconsole/new-console-output/utils/messages.js +++ b/devtools/client/webconsole/new-console-output/utils/messages.js @@ -53,17 +53,19 @@ function transformPacket(packet) { case "clear": // We show a message to users when calls console.clear() is called. parameters = [l10n.getStr("consoleCleared")]; break; case "count": // Chrome RDP doesn't have a special type for count. type = MESSAGE_TYPE.LOG; level = MESSAGE_LEVEL.DEBUG; - messageText = `${message.counter.label}: ${message.counter.count}`; + let {counter} = message; + let label = counter.label ? counter.label : l10n.getStr("noCounterLabel"); + messageText = `${label}: ${counter.count}`; parameters = null; break; } return new ConsoleMessage({ source: MESSAGE_SOURCE.CONSOLE_API, type, level,
--- a/devtools/client/webconsole/webconsole.js +++ b/devtools/client/webconsole/webconsole.js @@ -548,16 +548,19 @@ WebConsoleFrame.prototype = { // panel, but for now let's just hide it. this.experimentalOutputNode = this.outputNode.cloneNode(); this.outputNode.hidden = true; this.outputNode.parentNode.appendChild(this.experimentalOutputNode); // @TODO Once the toolbox has been converted to React, see if passing // in JSTerm is still necessary. this.newConsoleOutput = new this.window.NewConsoleOutput(this.experimentalOutputNode, this.jsterm); console.log("Created newConsoleOutput", this.newConsoleOutput); + + let filterToolbar = doc.querySelector(".hud-console-filter-toolbar"); + filterToolbar.hidden = true; } this.resize(); this.window.addEventListener("resize", this.resize, true); this.jsterm.on("sidebar-opened", this.resize); this.jsterm.on("sidebar-closed", this.resize); let toolbox = gDevTools.getToolbox(this.owner.target); @@ -581,16 +584,23 @@ WebConsoleFrame.prototype = { } // Do not focus if a link was clicked if (event.target.nodeName.toLowerCase() === "a" || event.target.parentNode.nodeName.toLowerCase() === "a") { return; } + // Do not focus if a search input was clicked on the new frontend + if (this.NEW_CONSOLE_OUTPUT_ENABLED && + event.target.nodeName.toLowerCase() === "input" && + event.target.getAttribute("type").toLowerCase() === "search") { + return; + } + this.jsterm.focus(); }); // Toggle the timestamp on preference change gDevTools.on("pref-changed", this._onToolboxPrefChanged); this._onToolboxPrefChanged("pref-changed", { pref: PREF_MESSAGE_TIMESTAMP, newValue: Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP),
--- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -8084,17 +8084,18 @@ nsContentUtils::SendMouseEvent(nsCOMPtr< int32_t aButton, int32_t aClickCount, int32_t aModifiers, bool aIgnoreRootScrollFrame, float aPressure, unsigned short aInputSourceArg, bool aToWindow, bool *aPreventDefault, - bool aIsSynthesized) + bool aIsDOMEventSynthesized, + bool aIsWidgetEventSynthesized) { nsPoint offset; nsCOMPtr<nsIWidget> widget = GetWidget(aPresShell, &offset); if (!widget) return NS_ERROR_FAILURE; EventMessage msg; bool contextMenuKey = false; @@ -8118,27 +8119,30 @@ nsContentUtils::SendMouseEvent(nsCOMPtr< } else { return NS_ERROR_FAILURE; } if (aInputSourceArg == nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN) { aInputSourceArg = nsIDOMMouseEvent::MOZ_SOURCE_MOUSE; } - WidgetMouseEvent event(true, msg, widget, WidgetMouseEvent::eReal, + WidgetMouseEvent event(true, msg, widget, + aIsWidgetEventSynthesized ? + WidgetMouseEvent::eSynthesized : + WidgetMouseEvent::eReal, contextMenuKey ? WidgetMouseEvent::eContextMenuKey : WidgetMouseEvent::eNormal); event.mModifiers = GetWidgetModifiers(aModifiers); event.button = aButton; event.buttons = GetButtonsFlagForButton(aButton); event.pressure = aPressure; event.inputSource = aInputSourceArg; event.mClickCount = aClickCount; event.mTime = PR_IntervalNow(); - event.mFlags.mIsSynthesizedForTests = aIsSynthesized; + event.mFlags.mIsSynthesizedForTests = aIsDOMEventSynthesized; nsPresContext* presContext = aPresShell->GetPresContext(); if (!presContext) return NS_ERROR_FAILURE; event.mRefPoint = ToWidgetPoint(CSSPoint(aX, aY), offset, presContext); event.mIgnoreRootScrollFrame = aIgnoreRootScrollFrame;
--- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -2547,17 +2547,18 @@ public: int32_t aButton, int32_t aClickCount, int32_t aModifiers, bool aIgnoreRootScrollFrame, float aPressure, unsigned short aInputSourceArg, bool aToWindow, bool *aPreventDefault, - bool aIsSynthesized); + bool aIsDOMEventSynthesized, + bool aIsWidgetEventSynthesized); static void FirePageShowEvent(nsIDocShellTreeItem* aItem, mozilla::dom::EventTarget* aChromeEventHandler, bool aFireIfShowing); static void FirePageHideEvent(nsIDocShellTreeItem* aItem, mozilla::dom::EventTarget* aChromeEventHandler);
--- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -643,66 +643,76 @@ nsDOMWindowUtils::SendMouseEvent(const n float aX, float aY, int32_t aButton, int32_t aClickCount, int32_t aModifiers, bool aIgnoreRootScrollFrame, float aPressure, unsigned short aInputSourceArg, - bool aIsSynthesized, + bool aIsDOMEventSynthesized, + bool aIsWidgetEventSynthesized, uint8_t aOptionalArgCount, bool *aPreventDefault) { return SendMouseEventCommon(aType, aX, aY, aButton, aClickCount, aModifiers, aIgnoreRootScrollFrame, aPressure, aInputSourceArg, false, aPreventDefault, - aOptionalArgCount >= 4 ? aIsSynthesized : true); + aOptionalArgCount >= 4 ? + aIsDOMEventSynthesized : true, + aOptionalArgCount >= 5 ? + aIsWidgetEventSynthesized : false); } NS_IMETHODIMP nsDOMWindowUtils::SendMouseEventToWindow(const nsAString& aType, float aX, float aY, int32_t aButton, int32_t aClickCount, int32_t aModifiers, bool aIgnoreRootScrollFrame, float aPressure, unsigned short aInputSourceArg, - bool aIsSynthesized, + bool aIsDOMEventSynthesized, + bool aIsWidgetEventSynthesized, uint8_t aOptionalArgCount) { PROFILER_LABEL("nsDOMWindowUtils", "SendMouseEventToWindow", js::ProfileEntry::Category::EVENTS); return SendMouseEventCommon(aType, aX, aY, aButton, aClickCount, aModifiers, aIgnoreRootScrollFrame, aPressure, aInputSourceArg, true, nullptr, - aOptionalArgCount >= 4 ? aIsSynthesized : true); + aOptionalArgCount >= 4 ? + aIsDOMEventSynthesized : true, + aOptionalArgCount >= 5 ? + aIsWidgetEventSynthesized : false); } NS_IMETHODIMP nsDOMWindowUtils::SendMouseEventCommon(const nsAString& aType, float aX, float aY, int32_t aButton, int32_t aClickCount, int32_t aModifiers, bool aIgnoreRootScrollFrame, float aPressure, unsigned short aInputSourceArg, bool aToWindow, bool *aPreventDefault, - bool aIsSynthesized) + bool aIsDOMEventSynthesized, + bool aIsWidgetEventSynthesized) { nsCOMPtr<nsIPresShell> presShell = GetPresShell(); return nsContentUtils::SendMouseEvent(presShell, aType, aX, aY, aButton, aClickCount, aModifiers, aIgnoreRootScrollFrame, aPressure, - aInputSourceArg, aToWindow, aPreventDefault, aIsSynthesized); + aInputSourceArg, aToWindow, aPreventDefault, aIsDOMEventSynthesized, + aIsWidgetEventSynthesized); } NS_IMETHODIMP nsDOMWindowUtils::SendPointerEventCommon(const nsAString& aType, float aX, float aY, int32_t aButton, int32_t aClickCount,
--- a/dom/base/nsDOMWindowUtils.h +++ b/dom/base/nsDOMWindowUtils.h @@ -88,17 +88,18 @@ protected: int32_t aButton, int32_t aClickCount, int32_t aModifiers, bool aIgnoreRootScrollFrame, float aPressure, unsigned short aInputSourceArg, bool aToWindow, bool *aPreventDefault, - bool aIsSynthesized); + bool aIsDOMEventSynthesized, + bool aIsWidgetEventSynthesized); NS_IMETHOD SendPointerEventCommon(const nsAString& aType, float aX, float aY, int32_t aButton, int32_t aClickCount, int32_t aModifiers, bool aIgnoreRootScrollFrame,
--- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -3699,16 +3699,48 @@ nsPIDOMWindow<T>::MaybeCreateDoc() if (nsIDocShell* docShell = GetDocShell()) { // Note that |document| here is the same thing as our mDoc, but we // don't have to explicitly set the member variable because the docshell // has already called SetNewDocument(). nsCOMPtr<nsIDocument> document = docShell->GetDocument(); } } +void +nsPIDOMWindowOuter::SetInitialKeyboardIndicators( + UIStateChangeType aShowAccelerators, UIStateChangeType aShowFocusRings) +{ + MOZ_ASSERT(IsOuterWindow()); + MOZ_ASSERT(!GetCurrentInnerWindow()); + + nsPIDOMWindowOuter* piWin = GetPrivateRoot(); + if (!piWin) { + return; + } + + MOZ_ASSERT(piWin == AsOuter()); + + // only change the flags that have been modified + nsCOMPtr<nsPIWindowRoot> windowRoot = do_QueryInterface(mChromeEventHandler); + if (!windowRoot) { + return; + } + + if (aShowAccelerators != UIStateChangeType_NoChange) { + windowRoot->SetShowAccelerators(aShowAccelerators == UIStateChangeType_Set); + } + if (aShowFocusRings != UIStateChangeType_NoChange) { + windowRoot->SetShowFocusRings(aShowFocusRings == UIStateChangeType_Set); + } + + nsContentUtils::SetKeyboardIndicatorsOnRemoteChildren(GetOuterWindow(), + aShowAccelerators, + aShowFocusRings); +} + Element* nsPIDOMWindowOuter::GetFrameElementInternal() const { MOZ_ASSERT(IsOuterWindow()); return mFrameElement; } void
--- a/dom/base/nsPIDOMWindow.h +++ b/dom/base/nsPIDOMWindow.h @@ -843,16 +843,22 @@ public: nsPIDOMWindowInner* EnsureInnerWindow() { MOZ_ASSERT(IsOuterWindow()); // GetDoc forces inner window creation if there isn't one already GetDoc(); return GetCurrentInnerWindow(); } + /** + * Set initial keyboard indicator state for accelerators and focus rings. + */ + void SetInitialKeyboardIndicators(UIStateChangeType aShowAccelerators, + UIStateChangeType aShowFocusRings); + // Internal getter/setter for the frame element, this version of the // getter crosses chrome boundaries whereas the public scriptable // one doesn't for security reasons. mozilla::dom::Element* GetFrameElementInternal() const; void SetFrameElementInternal(mozilla::dom::Element* aFrameElement); bool IsActive() {
--- a/dom/base/test/chrome/cpows_child.js +++ b/dom/base/test/chrome/cpows_child.js @@ -1,11 +1,12 @@ dump('loaded child cpow test\n'); var Cu = Components.utils; +var Ci = Components.interfaces; (function start() { [is_remote] = sendRpcMessage("cpows:is_remote"); var tests = [ parent_test, error_reporting_test, dom_test, @@ -15,16 +16,17 @@ var Cu = Components.utils; regexp_test, postmessage_test, sync_test, async_test, rpc_test, lifetime_test, cancel_test, cancel_test2, + dead_test, unsafe_test, ]; function go() { if (tests.length == 0) { sendRpcMessage("cpows:done", {}); return; } @@ -350,8 +352,28 @@ function unsafe_test(finish) function f() {} sendAsyncMessage("cpows:unsafe", null, {f}); addMessageListener("cpows:unsafe_done", msg => { sendRpcMessage("cpows:safe", null, {f}); addMessageListener("cpows:safe_done", finish); }); } + +function dead_test(finish) +{ + if (!is_remote) { + // Only run this test when running out-of-process. + finish(); + return; + } + + { + let thing = { value: "Gonna croak" }; + sendAsyncMessage("cpows:dead", null, { thing }); + } + // Force the GC to dead-ify the thing. + content.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils) + .garbageCollect(); + + addMessageListener("cpows:dead_done", finish); +}
--- a/dom/base/test/chrome/cpows_parent.xul +++ b/dom/base/test/chrome/cpows_parent.xul @@ -412,16 +412,27 @@ msg.objects.f(); } catch (e if /unsafe CPOW usage forbidden/.test(String(e))) { ok(false, "cpow failed"); } opener.wrappedJSObject.SpecialPowers.clearUserPref(PREF_UNSAFE_FORBIDDEN); msg.target.messageManager.sendAsyncMessage("cpows:safe_done"); } + function recvDead(msg) { + try { + msg.objects.thing.value; + ok(false, "Should have been a dead CPOW"); + } catch(e if /dead CPOW/.test(String(e))) { + ok(true, "Got the expected dead CPOW"); + ok(e.stack, "The exception has a stack"); + } + msg.target.messageManager.sendAsyncMessage("cpows:dead_done"); + } + function run_tests(type) { info("Running tests: " + type); var node = document.getElementById('cpowbrowser_' + type); test_state = type; test_node = node; function recvIsRemote(message) { @@ -453,16 +464,17 @@ mm.addMessageListener("cpows:postmessage_test", recvPostMessageTest); mm.addMessageListener("cpows:lifetime_test_1", recvLifetimeTest1); mm.addMessageListener("cpows:lifetime_test_2", recvLifetimeTest2); mm.addMessageListener("cpows:cancel_test", recvCancelTest); mm.addMessageListener("cpows:cancel_sync_message", recvCancelSyncMessage); mm.addMessageListener("cpows:cancel_test2", recvCancelTest2); mm.addMessageListener("cpows:unsafe", recvUnsafe); mm.addMessageListener("cpows:safe", recvSafe); + mm.addMessageListener("cpows:dead", recvDead); mm.loadFrameScript("chrome://mochitests/content/chrome/dom/base/test/chrome/cpows_child.js", true); } function start() { run_tests('remote'); } function finish() {
--- a/dom/canvas/WebGL2Context.cpp +++ b/dom/canvas/WebGL2Context.cpp @@ -96,44 +96,53 @@ static const gl::GLFeature kRequiredFeat gl::GLFeature::vertex_array_object }; bool WebGLContext::InitWebGL2(FailureReason* const out_failReason) { MOZ_ASSERT(IsWebGL2(), "WebGLContext is not a WebGL 2 context!"); - // check OpenGL features - if (!gl->IsSupported(gl::GLFeature::occlusion_query) && - !gl->IsSupported(gl::GLFeature::occlusion_query_boolean)) - { - // On desktop, we fake occlusion_query_boolean with occlusion_query if - // necessary. (See WebGL2ContextQueries.cpp) - *out_failReason = FailureReason("FEATURE_FAILURE_WEBGL2_OCCL", - "WebGL 2 requires occlusion query support."); - return false; + std::vector<gl::GLFeature> missingList; + + const auto fnGatherMissing = [&](gl::GLFeature cur) { + if (!gl->IsSupported(cur)) { + missingList.push_back(cur); + } + }; + + const auto fnGatherMissing2 = [&](gl::GLFeature main, gl::GLFeature alt) { + if (!gl->IsSupported(main) && !gl->IsSupported(alt)) { + missingList.push_back(main); + } + }; + + //// + + for (const auto& cur : kRequiredFeatures) { + fnGatherMissing(cur); } - std::vector<gl::GLFeature> missingList; - - for (size_t i = 0; i < ArrayLength(kRequiredFeatures); i++) { - if (!gl->IsSupported(kRequiredFeatures[i])) { - missingList.push_back(kRequiredFeatures[i]); - } - } + // On desktop, we fake occlusion_query_boolean with occlusion_query if + // necessary. (See WebGL2ContextQueries.cpp) + fnGatherMissing2(gl::GLFeature::occlusion_query_boolean, + gl::GLFeature::occlusion_query); #ifdef XP_MACOSX // On OSX, GL core profile is used. This requires texture swizzle // support to emulate legacy texture formats: ALPHA, LUMINANCE, // and LUMINANCE_ALPHA. - if (!gl->IsSupported(gl::GLFeature::texture_swizzle)) { - missingList.push_back(gl::GLFeature::texture_swizzle); - } + fnGatherMissing(gl::GLFeature::texture_swizzle); #endif + fnGatherMissing2(gl::GLFeature::prim_restart_fixed, + gl::GLFeature::prim_restart); + + //// + if (missingList.size()) { nsAutoCString exts; for (auto itr = missingList.begin(); itr != missingList.end(); ++itr) { exts.AppendLiteral("\n "); exts.Append(gl::GLContext::GetFeatureName(*itr)); } const nsPrintfCString reason("WebGL 2 requires support for the following" @@ -150,23 +159,31 @@ WebGLContext::InitWebGL2(FailureReason* &mGLMaxUniformBufferBindings); mBoundTransformFeedbackBuffers.SetLength(mGLMaxTransformFeedbackSeparateAttribs); mBoundUniformBuffers.SetLength(mGLMaxUniformBufferBindings); mDefaultTransformFeedback = new WebGLTransformFeedback(this, 0); mBoundTransformFeedback = mDefaultTransformFeedback; + //// + if (!gl->IsGLES()) { // Desktop OpenGL requires the following to be enabled in order to // support sRGB operations on framebuffers. - gl->MakeCurrent(); gl->fEnable(LOCAL_GL_FRAMEBUFFER_SRGB_EXT); } + if (gl->IsSupported(gl::GLFeature::prim_restart_fixed)) { + gl->fEnable(LOCAL_GL_PRIMITIVE_RESTART_FIXED_INDEX); + } else { + MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart)); + gl->fEnable(LOCAL_GL_PRIMITIVE_RESTART); + } + ////// static const GLenum kWebGL2_CompressedFormats[] = { LOCAL_GL_COMPRESSED_R11_EAC, LOCAL_GL_COMPRESSED_SIGNED_R11_EAC, LOCAL_GL_COMPRESSED_RG11_EAC, LOCAL_GL_COMPRESSED_SIGNED_RG11_EAC, LOCAL_GL_COMPRESSED_RGB8_ETC2,
--- a/dom/canvas/WebGLContext.h +++ b/dom/canvas/WebGLContext.h @@ -914,16 +914,18 @@ protected: WebGLTexture** const out_texture, WebGLTexture::ImageInfo** const out_imageInfo); bool ValidateUnpackInfo(const char* funcName, bool usePBOs, GLenum format, GLenum type, webgl::PackingInfo* const out); // ----------------------------------------------------------------------------- // Vertices Feature (WebGLContextVertices.cpp) + GLenum mPrimRestartTypeBytes; + public: void DrawArrays(GLenum mode, GLint first, GLsizei count); void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount); void DrawElements(GLenum mode, GLsizei count, GLenum type, WebGLintptr byteOffset); void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, WebGLintptr byteOffset, GLsizei primcount);
--- a/dom/canvas/WebGLContextDraw.cpp +++ b/dom/canvas/WebGLContextDraw.cpp @@ -253,16 +253,26 @@ WebGLContext::DrawArrays_check(GLint fir ErrorInvalidValue("%s: negative primcount", info); return false; } if (!ValidateStencilParamsForDrawCall()) { return false; } + if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) { + MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart)); + if (mPrimRestartTypeBytes != 4) { + mPrimRestartTypeBytes = 4; + + // OSX has issues leaving this as 0. + gl->fPrimitiveRestartIndex(UINT32_MAX); + } + } + // If count is 0, there's nothing to do. if (count == 0 || primcount == 0) { return false; } if (!ValidateBufferFetching(info)) { return false; } @@ -409,16 +419,30 @@ WebGLContext::DrawElements_check(GLsizei } if (byteOffset % bytesPerElem != 0) { ErrorInvalidOperation("%s: `byteOffset` must be a multiple of the size of `type`", info); return false; } + //// + + if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) { + MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart)); + if (mPrimRestartTypeBytes != bytesPerElem) { + mPrimRestartTypeBytes = bytesPerElem; + + const uint32_t ones = UINT32_MAX >> (4 - mPrimRestartTypeBytes); + gl->fPrimitiveRestartIndex(ones); + } + } + + //// + const GLsizei first = byteOffset / bytesPerElem; const CheckedUint32 checked_byteCount = bytesPerElem * CheckedUint32(count); if (!checked_byteCount.isValid()) { ErrorInvalidValue("%s: overflow in byteCount", info); return false; }
--- a/dom/canvas/WebGLContextValidate.cpp +++ b/dom/canvas/WebGLContextValidate.cpp @@ -996,16 +996,18 @@ WebGLContext::InitAndValidateGL(FailureR mPixelStore_UnpackSkipRows = 0; mPixelStore_UnpackSkipPixels = 0; mPixelStore_UnpackAlignment = 4; mPixelStore_PackRowLength = 0; mPixelStore_PackSkipRows = 0; mPixelStore_PackSkipPixels = 0; mPixelStore_PackAlignment = 4; + mPrimRestartTypeBytes = 0; + return true; } bool WebGLContext::ValidateFramebufferTarget(GLenum target, const char* const info) { bool isValid = true;
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini +++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini @@ -5773,17 +5773,17 @@ skip-if = (os == 'android' || os == 'lin skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2')) [generated/test_2_conformance__rendering__many-draw-calls.html] skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2')) [generated/test_2_conformance__rendering__more-than-65536-indices.html] skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2')) [generated/test_2_conformance__rendering__multisample-corruption.html] skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2')) [generated/test_2_conformance__rendering__negative-one-index.html] -fail-if = (os == 'mac') || (os == 'win') +fail-if = (os == 'mac') skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2')) [generated/test_2_conformance__rendering__point-no-attributes.html] skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2')) [generated/test_2_conformance__rendering__point-size.html] fail-if = (os == 'mac') skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2')) [generated/test_2_conformance__rendering__point-specific-shader-variables.html] skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini +++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini @@ -146,17 +146,17 @@ fail-if = (os == 'mac') || (os == 'win') fail-if = (os == 'mac') [generated/test_2_conformance__attribs__gl-vertexattribpointer.html] fail-if = (os == 'mac') || (os == 'win') [generated/test_2_conformance2__glsl3__forbidden-operators.html] fail-if = (os == 'mac') || (os == 'win') [generated/test_2_conformance2__vertex_arrays__vertex-array-object.html] fail-if = (os == 'mac') || (os == 'win') [generated/test_2_conformance__rendering__negative-one-index.html] -fail-if = (os == 'mac') || (os == 'win') +fail-if = (os == 'mac') [generated/test_conformance__extensions__oes-texture-half-float.html] fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux') [generated/test_2_conformance2__reading__read-pixels-pack-parameters.html] fail-if = (os == 'mac') || (os == 'win') [generated/test_conformance__attribs__gl-vertexattribpointer.html] fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux') [generated/test_conformance__ogles__GL__biuDepthRange__biuDepthRange_001_to_002.html] fail-if = (os == 'android') || (os == 'linux')
--- a/dom/canvas/test/webgl-mochitest/test_fuzzing_bugs.html +++ b/dom/canvas/test/webgl-mochitest/test_fuzzing_bugs.html @@ -142,17 +142,17 @@ function run() { SimpleTest.finish(); } SimpleTest.waitForExplicitFinish(); try { var prefArrArr = [ ['webgl.force-enabled', true], - ['webgl.enable-prototype-webgl2', true], + ['webgl.enable-webgl2', true], ]; var prefEnv = {'set': prefArrArr}; SpecialPowers.pushPrefEnv(prefEnv, run); } catch (e) { warning('No SpecialPowers, but trying WebGL2 anyway...'); run(); } </script>
--- a/dom/canvas/test/webgl-mochitest/test_webgl2_alpha_luminance.html +++ b/dom/canvas/test/webgl-mochitest/test_webgl2_alpha_luminance.html @@ -1,29 +1,29 @@ <!DOCTYPE HTML> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <title>WebGL2 test: Alpha and Luminance Textures</title> <script src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" href="/tests/SimpleTest/test.css"> <script src="driver-info.js"></script> <script src="webgl-util.js"></script> -<script id="vs" type="x-shader/x-vertex"> -#version 300 es +<script id="vs" type="x-shader/x-vertex" +>#version 300 es in vec2 aTexCoord; out vec2 vTexCoord; void main() { vec2 pos = vec2(2.0)*aTexCoord - vec2(1.0); gl_Position = vec4(pos, 0.0, 1.0); vTexCoord = aTexCoord; } </script> -<script id="fs" type="x-shader/x-fragment"> -#version 300 es +<script id="fs" type="x-shader/x-fragment" +>#version 300 es precision mediump float; in vec2 vTexCoord; uniform sampler2D uTex; out vec4 oFragColor; void main() { oFragColor = texture(uTex, vTexCoord);
--- a/dom/canvas/test/webgl-mochitest/test_webgl2_not_exposed.html +++ b/dom/canvas/test/webgl-mochitest/test_webgl2_not_exposed.html @@ -5,17 +5,17 @@ <script src='/tests/SimpleTest/SimpleTest.js'></script> <link rel='stylesheet' href='/tests/SimpleTest/test.css'> </head> <body> <script> function ShouldExpose() { try { - return SpecialPowers.getBoolPref('webgl.enable-prototype-webgl2'); + return SpecialPowers.getBoolPref('webgl.enable-webgl2'); } catch (e) {} return false; } function DoesExpose() { try { null instanceof WebGL2RenderingContext;
--- a/dom/canvas/test/webgl-mochitest/webgl-util.js +++ b/dom/canvas/test/webgl-mochitest/webgl-util.js @@ -81,19 +81,17 @@ WebGLUtil = (function() { callback(gl); onFinished(); }; try { var prefArrArr = [ ['webgl.force-enabled', true], - ['webgl.disable-angle', true], - ['webgl.bypass-shader-validation', true], - ['webgl.enable-prototype-webgl2', true], + ['webgl.enable-webgl2', true], ]; var prefEnv = {'set': prefArrArr}; SpecialPowers.pushPrefEnv(prefEnv, run); } catch (e) { warning('No SpecialPowers, but trying WebGL2 anyway...'); run(); } }
--- a/dom/events/test/pointerevents/mochitest.ini +++ b/dom/events/test/pointerevents/mochitest.ini @@ -119,11 +119,11 @@ support-files = pointerevent_touch-action-none-css_touch-manual.html pointerevent_touch-action-pan-x-css_touch-manual.html pointerevent_touch-action-pan-x-pan-y-pan-y_touch-manual.html pointerevent_touch-action-pan-x-pan-y_touch-manual.html pointerevent_touch-action-pan-y-css_touch-manual.html pointerevent_touch-action-span-test_touch-manual.html pointerevent_touch-action-svg-test_touch-manual.html pointerevent_touch-action-table-test_touch-manual.html - +[test_bug1285128.html] [test_empty_file.html] disabled = disabled # Bug 1150091 - Issue with support-files
new file mode 100644 --- /dev/null +++ b/dom/events/test/pointerevents/test_bug1285128.html @@ -0,0 +1,48 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1285128 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1285128</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1285128">Mozilla Bug 1285128</a> +<p id="display"></p> +<div id="target0" style="width: 200px; height: 200px; background: green"></div> +<script type="text/javascript"> + +/** Test for Bug 1285128 **/ +SimpleTest.waitForExplicitFinish(); + +function runTests() { + let target0 = window.document.getElementById("target0"); + let pointerEventsList = ["pointerover", "pointerenter", "pointerdown", + "pointerup", "pointerleave", "pointerout"]; + let receivedPointerEvents = false; + pointerEventsList.forEach((elem, index, arr) => { + target0.addEventListener(elem, (event) => { + ok(false, "receiving event " + event.type); + receivedPointerEvents = true; + }, false); + }); + + target0.addEventListener("mouseenter", () => { + ok(!receivedPointerEvents, "synthesized mousemove should not trigger any pointer events"); + SimpleTest.finish(); + }); + + synthesizeMouseAtCenter(target0, { type: "mousemove", + inputSource: SpecialPowers.Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE, + isWidgetEventSynthesized: true }); +} + +SpecialPowers.pushPrefEnv({"set": [["dom.w3c_pointer_events.enabled", true]]}, runTests); + +</script> +</body> +</html>
--- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -792,16 +792,27 @@ HTMLMediaElement::GetMozDebugReaderData( void HTMLMediaElement::MozDumpDebugInfo() { if (mDecoder) { mDecoder->DumpDebugInfo(); } } +void +HTMLMediaElement::SetVisible(bool aVisible) +{ + if (!mDecoder) { + return; + } + + mDecoder->NotifyOwnerActivityChanged(aVisible); + +} + already_AddRefed<DOMMediaStream> HTMLMediaElement::GetSrcObject() const { NS_ASSERTION(!mSrcAttrStream || mSrcAttrStream->GetPlaybackStream(), "MediaStream should have been set up properly"); RefPtr<DOMMediaStream> stream = mSrcAttrStream; return stream.forget(); }
--- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -601,16 +601,18 @@ public: already_AddRefed<MediaSource> GetMozMediaSourceObject() const; // Returns a string describing the state of the media player internal // data. Used for debugging purposes. void GetMozDebugReaderData(nsAString& aString); void MozDumpDebugInfo(); + void SetVisible(bool aVisible); + already_AddRefed<DOMMediaStream> GetSrcObject() const; void SetSrcObject(DOMMediaStream& aValue); void SetSrcObject(DOMMediaStream* aValue); // TODO: remove prefixed versions soon (1183495). already_AddRefed<DOMMediaStream> GetMozSrcObject() const; void SetMozSrcObject(DOMMediaStream& aValue); void SetMozSrcObject(DOMMediaStream* aValue);
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -307,33 +307,36 @@ interface nsIDOMWindowUtils : nsISupport * @param aButton button to synthesize * @param aClickCount number of clicks that have been performed * @param aModifiers modifiers pressed, using constants defined as MODIFIER_* * @param aIgnoreRootScrollFrame whether the event should ignore viewport bounds * during dispatch * @param aPressure touch input pressure: 0.0 -> 1.0 * @param aInputSourceArg input source, see nsIDOMMouseEvent for values, * defaults to mouse input. - * @param aIsSynthesized controls nsIDOMEvent.isSynthesized value - * that helps identifying test related events, - * defaults to true + * @param aIsDOMEventSynthesized controls nsIDOMEvent.isSynthesized value + * that helps identifying test related events, + * defaults to true + * @param aIsWidgetEventSynthesized controls WidgetMouseEvent.mReason value + * defaults to false (WidgetMouseEvent::eReal) * * returns true if the page called prevent default on this event */ [optional_argc] boolean sendMouseEvent(in AString aType, in float aX, in float aY, in long aButton, in long aClickCount, in long aModifiers, [optional] in boolean aIgnoreRootScrollFrame, [optional] in float aPressure, [optional] in unsigned short aInputSourceArg, - [optional] in boolean aIsSynthesized); + [optional] in boolean aIsDOMEventSynthesized, + [optional] in boolean aIsWidgetEventSynthesized); /** Synthesize a pointer event. The event types supported are: * pointerdown, pointerup, pointermove, pointerover, pointerout * * Events are sent in coordinates offset by aX and aY from the window. * * Note that additional events may be fired as a result of this call. For @@ -442,17 +445,18 @@ interface nsIDOMWindowUtils : nsISupport in float aX, in float aY, in long aButton, in long aClickCount, in long aModifiers, [optional] in boolean aIgnoreRootScrollFrame, [optional] in float aPressure, [optional] in unsigned short aInputSourceArg, - [optional] in boolean aIsSynthesized); + [optional] in boolean aIsDOMEventSynthesized, + [optional] in boolean aIsWidgetEventSynthesized); /** The same as sendPointerEvent but ensures that the event * is dispatched to this DOM window or one of its children. */ [optional_argc] void sendPointerEventToWindow(in AString aType, in float aX, in float aY,
--- a/dom/interfaces/base/nsITabParent.idl +++ b/dom/interfaces/base/nsITabParent.idl @@ -50,16 +50,21 @@ interface nsITabParent : nsISupports * suppress the displayport. Each enable called must be matched * with a disable call. */ void suppressDisplayport(in bool aEnabled); readonly attribute uint64_t tabId; /** + * The OS level process Id of the related child process. + */ + readonly attribute int32_t osPid; + + /** * Navigate by key. If aForDocumentNavigation is true, navigate by document. * If aForDocumentNavigation is false, navigate by element. * * If aForward is true, navigate to the first focusable element or document. * If aForward is false, navigate to the last focusable element or document. */ void navigateByKey(in bool aForward, in bool aForDocumentNavigation);
--- a/dom/ipc/ContentBridgeParent.h +++ b/dom/ipc/ContentBridgeParent.h @@ -57,16 +57,21 @@ public: virtual bool IsForApp() const override { return mIsForApp; } virtual bool IsForBrowser() const override { return mIsForBrowser; } + virtual int32_t Pid() const override + { + // XXX: do we need this for ContentBridgeParent? + return -1; + } protected: virtual ~ContentBridgeParent(); void SetChildID(ContentParentId aId) { mChildID = aId; }
--- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -381,18 +381,16 @@ public: #endif } GeckoChildProcessHost* Process() const { return mSubprocess; } - int32_t Pid() const; - ContentParent* Opener() const { return mOpener; } bool NeedsPermissionsUpdate() const { return mSendPermissionUpdates; @@ -586,16 +584,18 @@ public: virtual bool RecvStoreAndBroadcastBlobURLRegistration(const nsCString& aURI, PBlobParent* aBlobParent, const Principal& aPrincipal) override; virtual bool RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aURI) override; + virtual int32_t Pid() const override; + protected: void OnChannelConnected(int32_t pid) override; virtual void ActorDestroy(ActorDestroyReason why) override; void OnNuwaForkTimeout(); bool ShouldContinueFromReplyTimeout() override;
--- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -837,17 +837,22 @@ TabChild::Init() // XXX: ideally, we would set a chrome event handler earlier, // and all windows, even the root one, will use the docshell one. nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation()); NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); nsCOMPtr<EventTarget> chromeHandler = do_QueryInterface(window->GetChromeEventHandler()); docShell->SetChromeEventHandler(chromeHandler); - window->SetKeyboardIndicators(ShowAccelerators(), ShowFocusRings()); + if (window->GetCurrentInnerWindow()) { + window->SetKeyboardIndicators(ShowAccelerators(), ShowFocusRings()); + } else { + // Skip ShouldShowFocusRing check if no inner window is available + window->SetInitialKeyboardIndicators(ShowAccelerators(), ShowFocusRings()); + } // Set prerender flag if necessary. if (mIsPrerendered) { docShell->SetIsPrerendered(); } nsContentUtils::SetScrollbarsVisibility(window->GetDocShell(), !!(mChromeFlags & nsIWebBrowserChrome::CHROME_SCROLLBARS));
--- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -2939,16 +2939,23 @@ TabParent::SuppressDisplayport(bool aEna NS_IMETHODIMP TabParent::GetTabId(uint64_t* aId) { *aId = GetTabId(); return NS_OK; } NS_IMETHODIMP +TabParent::GetOsPid(int32_t* aId) +{ + *aId = Manager()->Pid(); + return NS_OK; +} + +NS_IMETHODIMP TabParent::GetHasContentOpener(bool* aResult) { *aResult = mHasContentOpener; return NS_OK; } void TabParent::SetHasContentOpener(bool aHasContentOpener)
--- a/dom/ipc/nsIContentParent.h +++ b/dom/ipc/nsIContentParent.h @@ -77,16 +77,18 @@ public: virtual bool IsContentParent() const { return false; } ContentParent* AsContentParent(); virtual bool IsContentBridgeParent() const { return false; } ContentBridgeParent* AsContentBridgeParent(); + virtual int32_t Pid() const = 0; + protected: // methods bool CanOpenBrowser(const IPCTabContext& aContext); protected: // IPDL methods virtual mozilla::jsipc::PJavaScriptParent* AllocPJavaScriptParent(); virtual bool DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*); virtual PBrowserParent* AllocPBrowserParent(const TabId& aTabId,
--- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -287,20 +287,17 @@ MediaDecoder::ResourceCallback::NotifyBy }); AbstractThread::MainThread()->Dispatch(r.forget()); } void MediaDecoder::NotifyOwnerActivityChanged(bool aIsVisible) { MOZ_ASSERT(NS_IsMainThread()); - - if (IsShutdown()) { - return; - } + MOZ_ASSERT(!IsShutdown()); SetElementVisibility(aIsVisible); UpdateDormantState(false /* aDormantTimeout */, false /* aActivity */); // Start dormant timer if necessary StartDormantTimer(); } @@ -317,19 +314,19 @@ MediaDecoder::IsHeuristicDormantSupporte #endif mIsHeuristicDormantSupported; } void MediaDecoder::UpdateDormantState(bool aDormantTimeout, bool aActivity) { MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!IsShutdown()); - if (IsShutdown() || - !mDecoderStateMachine || + if (!mDecoderStateMachine || !mOwner->GetVideoFrameContainer() || (mOwner->GetMediaElement() && mOwner->GetMediaElement()->IsBeingDestroyed()) || !mDormantSupported) { return; } DECODER_LOG("UpdateDormantState aTimeout=%d aActivity=%d mIsDormant=%d " @@ -384,26 +381,21 @@ MediaDecoder::DormantTimerExpired(nsITim decoder->UpdateDormantState(true /* aDormantTimeout */, false /* aActivity */); } void MediaDecoder::StartDormantTimer() { MOZ_ASSERT(NS_IsMainThread()); - if (!IsHeuristicDormantSupported()) { - return; - } - if (mIsHeuristicDormant || - IsShutdown() || + if (!IsHeuristicDormantSupported() || + mIsHeuristicDormant || mIsVisible || - (mPlayState != PLAY_STATE_PAUSED && - !IsEnded())) - { + (mPlayState != PLAY_STATE_PAUSED && !IsEnded())) { return; } if (!mDormantTimer) { mDormantTimer = do_CreateInstance("@mozilla.org/timer;1"); } mDormantTimer->InitWithFuncCallback(&MediaDecoder::DormantTimerExpired, this, @@ -419,19 +411,17 @@ MediaDecoder::CancelDormantTimer() mDormantTimer->Cancel(); } } void MediaDecoder::Pause() { MOZ_ASSERT(NS_IsMainThread()); - if (IsShutdown()) { - return; - } + MOZ_ASSERT(!IsShutdown()); if (mPlayState == PLAY_STATE_LOADING || IsEnded()) { mNextState = PLAY_STATE_PAUSED; return; } ChangeState(PLAY_STATE_PAUSED); } void @@ -614,16 +604,18 @@ MediaDecoder::Shutdown() mWatchManager.Shutdown(); mResourceCallback->Disconnect(); #ifdef MOZ_EME mCDMProxyPromiseHolder.RejectIfExists(true, __func__); #endif + DiscardOngoingSeekIfExists(); + // This changes the decoder state to SHUTDOWN and does other things // necessary to unblock the state machine thread if it's blocked, so // the asynchronous shutdown in nsDestroyStateMachine won't deadlock. if (mDecoderStateMachine) { mTimedMetadataListener.Disconnect(); mMetadataLoadedListener.Disconnect(); mFirstFrameLoadedListener.Disconnect(); mOnPlaybackEvent.Disconnect(); @@ -799,17 +791,17 @@ MediaDecoder::Play() ChangeState(PLAY_STATE_PLAYING); return NS_OK; } nsresult MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType, dom::Promise* aPromise /*=nullptr*/) { MOZ_ASSERT(NS_IsMainThread()); - NS_ENSURE_TRUE(!IsShutdown(), NS_ERROR_FAILURE); + MOZ_ASSERT(!IsShutdown()); UpdateDormantState(false /* aDormantTimeout */, true /* aActivity */); MOZ_ASSERT(!mIsDormant, "should be out of dormant by now"); MOZ_ASSERT(aTime >= 0.0, "Cannot seek to a negative value."); int64_t timeUsecs = TimeUnit::FromSeconds(aTime).ToMicroseconds(); @@ -1670,19 +1662,17 @@ MediaDecoder::GetStateMachine() const { MOZ_ASSERT(NS_IsMainThread()); return mDecoderStateMachine; } void MediaDecoder::FireTimeUpdate() { MOZ_ASSERT(NS_IsMainThread()); - if (IsShutdown()) { - return; - } + MOZ_ASSERT(!IsShutdown()); mOwner->FireTimeUpdate(true); } void MediaDecoder::PinForSeek() { MOZ_ASSERT(NS_IsMainThread()); MediaResource* resource = GetResource(); @@ -1857,18 +1847,19 @@ MediaDecoder::GetOwner() // mOwner is valid until shutdown. return !IsShutdown() ? mOwner : nullptr; } void MediaDecoder::ConstructMediaTracks() { MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!IsShutdown()); - if (IsShutdown() || mMediaTracksConstructed || !mInfo) { + if (mMediaTracksConstructed || !mInfo) { return; } HTMLMediaElement* element = mOwner->GetMediaElement(); if (!element) { return; } @@ -1893,20 +1884,17 @@ MediaDecoder::ConstructMediaTracks() track->SetEnabledInternal(info.mEnabled, MediaTrack::FIRE_NO_EVENTS); } } void MediaDecoder::RemoveMediaTracks() { MOZ_ASSERT(NS_IsMainThread()); - - if (IsShutdown()) { - return; - } + MOZ_ASSERT(!IsShutdown()); HTMLMediaElement* element = mOwner->GetMediaElement(); if (!element) { return; } AudioTrackList* audioList = element->AudioTracks(); if (audioList) { @@ -1934,27 +1922,28 @@ MediaDecoder::NextFrameBufferedStatus() return GetBuffered().Contains(interval) ? MediaDecoderOwner::NEXT_FRAME_AVAILABLE : MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE; } void MediaDecoder::DumpDebugInfo() { + MOZ_ASSERT(!IsShutdown()); DUMP_LOG("metadata: channels=%u rate=%u hasAudio=%d hasVideo=%d, " - "state: mPlayState=%s mIsDormant=%d, IsShutdown()=%d", + "state: mPlayState=%s mIsDormant=%d", mInfo ? mInfo->mAudio.mChannels : 0, mInfo ? mInfo->mAudio.mRate : 0, mInfo ? mInfo->HasAudio() : 0, mInfo ? mInfo->HasVideo() : 0, - PlayStateStr(), mIsDormant, IsShutdown()); + PlayStateStr(), mIsDormant); nsString str; GetMozDebugReaderData(str); DUMP_LOG("reader data:\n%s", NS_ConvertUTF16toUTF8(str).get()); - if (!IsShutdown() && GetStateMachine()) { + if (GetStateMachine()) { GetStateMachine()->DumpDebugInfo(); } } void MediaDecoder::NotifyAudibleStateChanged() { MOZ_ASSERT(!IsShutdown());
--- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -486,19 +486,18 @@ private: virtual void NotifyDecodedFrames(const FrameStatisticsData& aStats) override { GetFrameStatistics().NotifyDecodedFrames(aStats); } void UpdateReadyState() { MOZ_ASSERT(NS_IsMainThread()); - if (!IsShutdown()) { - mOwner->UpdateReadyState(); - } + MOZ_ASSERT(!IsShutdown()); + mOwner->UpdateReadyState(); } virtual MediaDecoderOwner::NextFrameStatus NextFrameStatus() { return mNextFrameStatus; } virtual MediaDecoderOwner::NextFrameStatus NextFrameBufferedStatus(); // Returns a string describing the state of the media player internal // data. Used for debugging purposes. virtual void GetMozDebugReaderData(nsAString& aString) {}
--- a/dom/media/MediaDecoderReader.h +++ b/dom/media/MediaDecoderReader.h @@ -312,16 +312,21 @@ public: MOZ_ASSERT(OnTaskQueue()); mIsSuspended = aState; } AbstractCanonical<bool>* CanonicalIsSuspended() { return &mIsSuspended; } + // Switch the video decoder to BlankDecoderModule. It might takes effective + // since a few samples later depends on how much demuxed samples are already + // queued in the original video decoder. + virtual void SetVideoBlankDecode(bool aIsBlankDecode) {} + protected: virtual ~MediaDecoderReader(); // Populates aBuffered with the time ranges which are buffered. This may only // be called on the decode task queue, and should only be used internally by // UpdateBuffered - mBuffered (or mirrors of it) should be used for everything // else. //
--- a/dom/media/MediaDecoderReaderWrapper.cpp +++ b/dom/media/MediaDecoderReaderWrapper.cpp @@ -405,9 +405,19 @@ MediaDecoderReaderWrapper::OnMetadataRea self->mReader->DispatchSetStartTime(self->StartTime().ToMicroseconds()); }, [] () { NS_WARNING("Setting start time on reader failed"); }); } } +void +MediaDecoderReaderWrapper::SetVideoBlankDecode(bool aIsBlankDecode) +{ + MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); + nsCOMPtr<nsIRunnable> r = + NewRunnableMethod<bool>(mReader, &MediaDecoderReader::SetVideoBlankDecode, + aIsBlankDecode); + mReader->OwnerThread()->Dispatch(r.forget()); +} + } // namespace mozilla
--- a/dom/media/MediaDecoderReaderWrapper.h +++ b/dom/media/MediaDecoderReaderWrapper.h @@ -116,16 +116,18 @@ public: AbstractCanonical<bool>* CanonicalIsSuspended() { return mReader->CanonicalIsSuspended(); } #ifdef MOZ_EME void SetCDMProxy(CDMProxy* aProxy) { mReader->SetCDMProxy(aProxy); } #endif + void SetVideoBlankDecode(bool aIsBlankDecode); + private: ~MediaDecoderReaderWrapper(); void OnMetadataRead(MetadataHolder* aMetadata); void OnMetadataNotRead() {} MediaCallbackExc<WaitCallbackData>& WaitCallbackRef(MediaData::Type aType); MozPromiseRequestHolder<WaitForDataPromise>& WaitRequestRef(MediaData::Type aType);
--- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -1357,16 +1357,17 @@ void MediaDecoderStateMachine::Visibilit // Resuming from suspended decoding // If suspend timer exists, destroy it. mVideoDecodeSuspendTimer.Reset(); if (mVideoDecodeSuspended) { mVideoDecodeSuspended = false; + mReader->SetVideoBlankDecode(false); if (mIsReaderSuspended) { return; } // If an existing seek is in flight don't bother creating a new // one to catch up. if (mSeekTask || mQueuedSeek.Exists()) { @@ -2635,17 +2636,17 @@ bool MediaDecoderStateMachine::IsStateMa { MOZ_ASSERT(OnTaskQueue()); return mDispatchedStateMachine || mDelayedScheduler.IsScheduled(); } bool MediaDecoderStateMachine::IsVideoDecodeSuspended() const { MOZ_ASSERT(OnTaskQueue()); - return mVideoDecodeSuspended || mIsReaderSuspended; + return mIsReaderSuspended; } void MediaDecoderStateMachine::LogicalPlaybackRateChanged() { MOZ_ASSERT(OnTaskQueue()); if (mLogicalPlaybackRate == 0) { @@ -2927,16 +2928,17 @@ MediaDecoderStateMachine::VideoRequestSt } void MediaDecoderStateMachine::OnSuspendTimerResolved() { DECODER_LOG("OnSuspendTimerResolved"); mVideoDecodeSuspendTimer.CompleteRequest(); mVideoDecodeSuspended = true; + mReader->SetVideoBlankDecode(true); } void MediaDecoderStateMachine::OnSuspendTimerRejected() { DECODER_LOG("OnSuspendTimerRejected"); MOZ_ASSERT(OnTaskQueue()); MOZ_ASSERT(!mVideoDecodeSuspended);
--- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -413,31 +413,33 @@ MediaFormatReader::EnsureDecoderCreated( MonitorAutoLock mon(decoder.mMonitor); switch (aTrack) { case TrackType::kAudioTrack: { decoder.mDecoder = mPlatform->CreateDecoder({ decoder.mInfo ? *decoder.mInfo->GetAsAudioInfo() : mInfo.mAudio, decoder.mTaskQueue, decoder.mCallback.get(), - mCrashHelper + mCrashHelper, + decoder.mIsBlankDecode }); break; } case TrackType::kVideoTrack: { // Decoders use the layers backend to decide if they can use hardware decoding, // so specify LAYERS_NONE if we want to forcibly disable it. decoder.mDecoder = mPlatform->CreateDecoder({ mVideo.mInfo ? *mVideo.mInfo->GetAsVideoInfo() : mInfo.mVideo, decoder.mTaskQueue, decoder.mCallback.get(), mLayersBackendType, GetImageContainer(), - mCrashHelper + mCrashHelper, + decoder.mIsBlankDecode }); break; } default: break; } if (decoder.mDecoder ) { decoder.mDescription = decoder.mDecoder->GetDescriptionName(); @@ -2051,9 +2053,37 @@ MediaFormatReader::GetMozDebugReaderData mVideo.mNumSamplesInput, mVideo.mNumSamplesOutput, unsigned(size_t(mVideo.mSizeOfQueue)), unsigned(mVideo.mOutput.Length()), mVideo.mWaitingForData, mVideo.mLastStreamSourceID); } aString += NS_ConvertUTF8toUTF16(result); } +void +MediaFormatReader::SetVideoBlankDecode(bool aIsBlankDecode) +{ + MOZ_ASSERT(OnTaskQueue()); + return SetBlankDecode(TrackType::kVideoTrack, aIsBlankDecode); +} + +void +MediaFormatReader::SetBlankDecode(TrackType aTrack, bool aIsBlankDecode) +{ + MOZ_ASSERT(OnTaskQueue()); + auto& decoder = GetDecoderData(aTrack); + + LOG("%s, decoder.mIsBlankDecode = %d => aIsBlankDecode = %d", + TrackTypeToStr(aTrack), decoder.mIsBlankDecode, aIsBlankDecode); + + if (decoder.mIsBlankDecode == aIsBlankDecode) { + return; + } + + decoder.mIsBlankDecode = aIsBlankDecode; + decoder.Flush(); + decoder.ShutdownDecoder(); + NotifyDecodingRequested(TrackInfo::kVideoTrack); // Calls ScheduleUpdate(). + + return; +} + } // namespace mozilla
--- a/dom/media/MediaFormatReader.h +++ b/dom/media/MediaFormatReader.h @@ -96,16 +96,18 @@ public: #ifdef MOZ_EME void SetCDMProxy(CDMProxy* aProxy) override; #endif // Returns a string describing the state of the decoder data. // Used for debugging purposes. void GetMozDebugReaderData(nsAString& aString); + void SetVideoBlankDecode(bool aIsBlankDecode) override; + private: bool HasVideo() { return mVideo.mTrackDemuxer; } bool HasAudio() { return mAudio.mTrackDemuxer; } bool IsWaitingOnCDMResource(); bool InitDemuxer(); @@ -248,16 +250,17 @@ private: , mMaxConsecutiveError(aNumOfMaxError) , mNumSamplesInput(0) , mNumSamplesOutput(0) , mNumSamplesOutputTotal(0) , mNumSamplesSkippedTotal(0) , mSizeOfQueue(0) , mIsHardwareAccelerated(false) , mLastStreamSourceID(UINT32_MAX) + , mIsBlankDecode(false) {} MediaFormatReader* mOwner; // Disambiguate Audio vs Video. MediaData::Type mType; RefPtr<MediaTrackDemuxer> mTrackDemuxer; // TaskQueue on which decoder can choose to decode. // Only non-null up until the decoder is created. @@ -422,16 +425,19 @@ private: Atomic<bool> mIsHardwareAccelerated; // Sample format monitoring. uint32_t mLastStreamSourceID; Maybe<uint32_t> mNextStreamSourceID; media::TimeIntervals mTimeRanges; Maybe<media::TimeUnit> mLastTimeRangesEnd; RefPtr<SharedTrackInfo> mInfo; Maybe<media::TimeUnit> mFirstDemuxedSampleTime; + // Use BlankDecoderModule or not. + bool mIsBlankDecode; + }; class DecoderDataWithPromise : public DecoderData { public: DecoderDataWithPromise(MediaFormatReader* aOwner, MediaData::Type aType, uint32_t aDecodeAhead, uint32_t aNumOfMaxError) @@ -566,13 +572,15 @@ private: RefPtr<VideoFrameContainer> mVideoFrameContainer; layers::ImageContainer* GetImageContainer(); #ifdef MOZ_EME RefPtr<CDMProxy> mCDMProxy; #endif RefPtr<GMPCrashHelper> mCrashHelper; + + void SetBlankDecode(TrackType aTrack, bool aIsBlankDecode); }; } // namespace mozilla #endif
--- a/dom/media/eme/MediaKeyStatusMap.cpp +++ b/dom/media/eme/MediaKeyStatusMap.cpp @@ -37,31 +37,37 @@ MediaKeyStatusMap::WrapObject(JSContext* } nsPIDOMWindowInner* MediaKeyStatusMap::GetParentObject() const { return mParent; } -MediaKeyStatus -MediaKeyStatusMap::Get(const ArrayBufferViewOrArrayBuffer& aKey) const +void +MediaKeyStatusMap::Get(JSContext* aCx, + const ArrayBufferViewOrArrayBuffer& aKey, + JS::MutableHandle<JS::Value> aOutValue, + ErrorResult& aOutRv) const { ArrayData keyId = GetArrayBufferViewOrArrayBufferData(aKey); if (!keyId.IsValid()) { - return MediaKeyStatus::Internal_error; + aOutValue.setUndefined(); + return; } - for (const KeyStatus& status : mStatuses) { if (keyId == status.mKeyId) { - return status.mStatus; + bool ok = ToJSValue(aCx, status.mStatus, aOutValue); + if (!ok) { + aOutRv.NoteJSContextException(aCx); + } + return; } } - - return MediaKeyStatus::Internal_error; + aOutValue.setUndefined(); } bool MediaKeyStatusMap::Has(const ArrayBufferViewOrArrayBuffer& aKey) const { ArrayData keyId = GetArrayBufferViewOrArrayBufferData(aKey); if (!keyId.IsValid()) { return false;
--- a/dom/media/eme/MediaKeyStatusMap.h +++ b/dom/media/eme/MediaKeyStatusMap.h @@ -39,17 +39,20 @@ public: protected: ~MediaKeyStatusMap(); public: nsPIDOMWindowInner* GetParentObject() const; JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; - MediaKeyStatus Get(const ArrayBufferViewOrArrayBuffer& aKey) const; + void Get(JSContext* aCx, + const ArrayBufferViewOrArrayBuffer& aKey, + JS::MutableHandle<JS::Value> aOutValue, + ErrorResult& aOutRv) const; bool Has(const ArrayBufferViewOrArrayBuffer& aKey) const; uint32_t Size() const; uint32_t GetIterableLength() const; TypedArrayCreator<ArrayBuffer> GetKeyAtIndex(uint32_t aIndex) const; MediaKeyStatus GetValueAtIndex(uint32_t aIndex) const; void Update(const nsTArray<CDMCaps::KeyStatus>& keys);
--- a/dom/media/encoder/fmp4_muxer/ISOMediaBoxes.cpp +++ b/dom/media/encoder/fmp4_muxer/ISOMediaBoxes.cpp @@ -1396,18 +1396,17 @@ Box::Box(const nsACString& aType, ISOCon aControl->GetAudioMetadata(mAudioMeta); aControl->GetVideoMetadata(mVideoMeta); } FullBox::FullBox(const nsACString& aType, uint8_t aVersion, uint32_t aFlags, ISOControl* aControl) : Box(aType, aControl) { - // Cast to uint64_t due to VC2010 bug. - std::bitset<24> tmp_flags((uint64_t)aFlags); + std::bitset<24> tmp_flags(aFlags); version = aVersion; flags = tmp_flags; size += sizeof(version) + flags.size() / CHAR_BIT; } nsresult FullBox::Write() {
--- a/dom/media/fmp4/MP4Decoder.cpp +++ b/dom/media/fmp4/MP4Decoder.cpp @@ -168,16 +168,24 @@ MP4Decoder::CanHandleMediaType(const nsA return CanHandleMediaType(NS_ConvertUTF16toUTF8(mimeType), codecs, aDiagnostics); } /* static */ bool +MP4Decoder::IsH264(const nsACString& aMimeType) +{ + return aMimeType.EqualsLiteral("video/mp4") || + aMimeType.EqualsLiteral("video/avc"); +} + +/* static */ +bool MP4Decoder::IsEnabled() { return Preferences::GetBool("media.mp4.enabled", true); } // sTestH264ExtraData represents the content of the avcC atom found in // an AVC1 h264 video. It contains the H264 SPS and PPS NAL. // the structure of the avcC atom is as follow:
--- a/dom/media/fmp4/MP4Decoder.h +++ b/dom/media/fmp4/MP4Decoder.h @@ -33,16 +33,21 @@ public: // out params wether the codecs string contains AAC or H.264. static bool CanHandleMediaType(const nsACString& aMIMETypeExcludingCodecs, const nsAString& aCodecs, DecoderDoctorDiagnostics* aDiagnostics); static bool CanHandleMediaType(const nsAString& aMIMEType, DecoderDoctorDiagnostics* aDiagnostics); + // Return true if aMimeType is a one of the strings used by our demuxers to + // identify H264. Does not parse general content type strings, i.e. white + // space matters. + static bool IsH264(const nsACString& aMimeType); + // Returns true if the MP4 backend is preffed on. static bool IsEnabled(); static already_AddRefed<dom::Promise> IsVideoAccelerated(layers::LayersBackend aBackend, nsIGlobalObject* aParent); void GetMozDebugReaderData(nsAString& aString) override;
--- a/dom/media/omx/MediaOmxCommonDecoder.cpp +++ b/dom/media/omx/MediaOmxCommonDecoder.cpp @@ -226,18 +226,18 @@ MediaOmxCommonDecoder::ChangeState(PlayS void MediaOmxCommonDecoder::CallSeek(const SeekTarget& aTarget, dom::Promise* aPromise) { if (!mAudioOffloadPlayer) { MediaDecoder::CallSeek(aTarget, aPromise); return; } + DiscardOngoingSeekIfExists(); mSeekDOMPromise = aPromise; - mSeekRequest.DisconnectIfExists(); mSeekRequest.Begin(mAudioOffloadPlayer->Seek(aTarget) ->Then(AbstractThread::MainThread(), __func__, static_cast<MediaDecoder*>(this), &MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected)); } int64_t MediaOmxCommonDecoder::CurrentPosition() {
--- a/dom/media/platforms/PDMFactory.cpp +++ b/dom/media/platforms/PDMFactory.cpp @@ -41,16 +41,18 @@ #ifdef MOZ_EME #include "EMEDecoderModule.h" #include "mozilla/CDMProxy.h" #endif #include "DecoderDoctorDiagnostics.h" +#include "MP4Decoder.h" + namespace mozilla { extern already_AddRefed<PlatformDecoderModule> CreateAgnosticDecoderModule(); extern already_AddRefed<PlatformDecoderModule> CreateBlankDecoderModule(); class PDMFactoryImpl final { public: @@ -74,16 +76,17 @@ public: StaticAutoPtr<PDMFactoryImpl> PDMFactory::sInstance; StaticMutex PDMFactory::sMonitor; PDMFactory::PDMFactory() { EnsureInit(); CreatePDMs(); + CreateBlankPDM(); } PDMFactory::~PDMFactory() { } void PDMFactory::EnsureInit() const @@ -113,16 +116,21 @@ PDMFactory::EnsureInit() const } }); SyncRunnable::DispatchToThread(mainThread, runnable); } already_AddRefed<MediaDataDecoder> PDMFactory::CreateDecoder(const CreateDecoderParams& aParams) { + if (aParams.mUseBlankDecoder) { + MOZ_ASSERT(mBlankPDM); + return CreateDecoderWithPDM(mBlankPDM, aParams); + } + const TrackInfo& config = aParams.mConfig; bool isEncrypted = mEMEPDM && config.mCrypto.mValid; if (isEncrypted) { return CreateDecoderWithPDM(mEMEPDM, aParams); } DecoderDoctorDiagnostics* diagnostics = aParams.mDiagnostics; @@ -178,17 +186,17 @@ PDMFactory::CreateDecoderWithPDM(Platfor TimeDuration::FromMilliseconds(MediaPrefs::PDMFuzzingInterval())); callbackWrapper->SetDontDelayInputExhausted(!MediaPrefs::PDMFuzzingDelayInputExhausted()); callback = callbackWrapper.get(); } CreateDecoderParams params = aParams; params.mCallback = callback; - if (H264Converter::IsH264(config)) { + if (MP4Decoder::IsH264(config.mMimeType)) { RefPtr<H264Converter> h = new H264Converter(aPDM, params); const nsresult rv = h->GetLastError(); if (NS_SUCCEEDED(rv) || rv == NS_ERROR_NOT_INITIALIZED) { // The H264Converter either successfully created the wrapped decoder, // or there wasn't enough AVCC data to do so. Otherwise, there was some // problem, for example WMF DLLs were missing. m = h.forget(); } @@ -280,16 +288,23 @@ PDMFactory::CreatePDMs() if (MediaPrefs::PDMGMPEnabled()) { m = new GMPDecoderModule(); mGMPPDMFailedToStartup = !StartupPDM(m); } else { mGMPPDMFailedToStartup = false; } } +void +PDMFactory::CreateBlankPDM() +{ + mBlankPDM = CreateBlankDecoderModule(); + MOZ_ASSERT(mBlankPDM && NS_SUCCEEDED(mBlankPDM->Startup())); +} + bool PDMFactory::StartupPDM(PlatformDecoderModule* aPDM) { if (aPDM && NS_SUCCEEDED(aPDM->Startup())) { mCurrentPDMs.AppendElement(aPDM); return true; } return false;
--- a/dom/media/platforms/PDMFactory.h +++ b/dom/media/platforms/PDMFactory.h @@ -42,29 +42,31 @@ public: // that we use on on aTaskQueue to decode the decrypted stream. // This is called on the decode task queue. void SetCDMProxy(CDMProxy* aProxy); #endif private: virtual ~PDMFactory(); void CreatePDMs(); + void CreateBlankPDM(); // Startup the provided PDM and add it to our list if successful. bool StartupPDM(PlatformDecoderModule* aPDM); // Returns the first PDM in our list supporting the mimetype. already_AddRefed<PlatformDecoderModule> GetDecoder(const nsACString& aMimeType, DecoderDoctorDiagnostics* aDiagnostics) const; already_AddRefed<MediaDataDecoder> CreateDecoderWithPDM(PlatformDecoderModule* aPDM, const CreateDecoderParams& aParams); nsTArray<RefPtr<PlatformDecoderModule>> mCurrentPDMs; RefPtr<PlatformDecoderModule> mEMEPDM; + RefPtr<PlatformDecoderModule> mBlankPDM; bool mWMFFailedToLoad = false; bool mFFmpegFailedToLoad = false; bool mGMPPDMFailedToStartup = false; void EnsureInit() const; template<class T> friend class StaticAutoPtr; static StaticAutoPtr<PDMFactoryImpl> sInstance;
--- a/dom/media/platforms/PlatformDecoderModule.h +++ b/dom/media/platforms/PlatformDecoderModule.h @@ -59,24 +59,26 @@ struct CreateDecoderParams { const TrackInfo& mConfig; TaskQueue* mTaskQueue = nullptr; MediaDataDecoderCallback* mCallback = nullptr; DecoderDoctorDiagnostics* mDiagnostics = nullptr; layers::ImageContainer* mImageContainer = nullptr; layers::LayersBackend mLayersBackend = layers::LayersBackend::LAYERS_NONE; RefPtr<GMPCrashHelper> mCrashHelper; + bool mUseBlankDecoder = false; private: void Set(TaskQueue* aTaskQueue) { mTaskQueue = aTaskQueue; } void Set(MediaDataDecoderCallback* aCallback) { mCallback = aCallback; } void Set(DecoderDoctorDiagnostics* aDiagnostics) { mDiagnostics = aDiagnostics; } void Set(layers::ImageContainer* aImageContainer) { mImageContainer = aImageContainer; } void Set(layers::LayersBackend aLayersBackend) { mLayersBackend = aLayersBackend; } void Set(GMPCrashHelper* aCrashHelper) { mCrashHelper = aCrashHelper; } + void Set(bool aUseBlankDecoder) { mUseBlankDecoder = aUseBlankDecoder; } template <typename T1, typename T2, typename... Ts> void Set(T1&& a1, T2&& a2, Ts&&... args) { Set(mozilla::Forward<T1>(a1)); Set(mozilla::Forward<T2>(a2), mozilla::Forward<Ts>(args)...); } };
--- a/dom/media/platforms/agnostic/BlankDecoderModule.cpp +++ b/dom/media/platforms/agnostic/BlankDecoderModule.cpp @@ -1,39 +1,46 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* 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 "H264Converter.h" #include "ImageContainer.h" #include "MediaDecoderReader.h" #include "MediaInfo.h" #include "mozilla/CheckedInt.h" #include "mozilla/mozalloc.h" // for operator new, and new (fallible) #include "mozilla/RefPtr.h" #include "mozilla/TaskQueue.h" +#include "mp4_demuxer/H264.h" #include "nsAutoPtr.h" #include "nsRect.h" #include "PlatformDecoderModule.h" +#include "ReorderQueue.h" #include "TimeUnits.h" #include "VideoUtils.h" namespace mozilla { // Decoder that uses a passed in object's Create function to create blank // MediaData objects. template<class BlankMediaDataCreator> class BlankMediaDataDecoder : public MediaDataDecoder { public: BlankMediaDataDecoder(BlankMediaDataCreator* aCreator, const CreateDecoderParams& aParams) : mCreator(aCreator) , mCallback(aParams.mCallback) + , mMaxRefFrames(aParams.mConfig.GetType() == TrackInfo::kVideoTrack && + H264Converter::IsH264(aParams.mConfig) + ? mp4_demuxer::H264::ComputeMaxRefFrames(aParams.VideoConfig().mExtraData) + : 0) , mType(aParams.mConfig.GetType()) { } RefPtr<InitPromise> Init() override { return InitPromise::CreateAndResolve(mType, __func__); } @@ -42,41 +49,70 @@ public: } nsresult Input(MediaRawData* aSample) override { RefPtr<MediaData> data = mCreator->Create(media::TimeUnit::FromMicroseconds(aSample->mTime), media::TimeUnit::FromMicroseconds(aSample->mDuration), aSample->mOffset); - if (!data) { - mCallback->Error(MediaDataDecoderError::FATAL_ERROR); - } else { - mCallback->Output(data); - } + + OutputFrame(data); + return NS_OK; } - nsresult Flush() override { + nsresult Flush() override + { + mReorderQueue.Clear(); + return NS_OK; } - nsresult Drain() override { + nsresult Drain() override + { + while (!mReorderQueue.IsEmpty()) { + mCallback->Output(mReorderQueue.Pop().get()); + } + mCallback->DrainComplete(); return NS_OK; } const char* GetDescriptionName() const override { return "blank media data decoder"; } private: + void OutputFrame(MediaData* aData) + { + if (!aData) { + mCallback->Error(MediaDataDecoderError::FATAL_ERROR); + return; + } + + // Frames come out in DTS order but we need to output them in PTS order. + mReorderQueue.Push(aData); + + while (mReorderQueue.Length() > mMaxRefFrames) { + mCallback->Output(mReorderQueue.Pop().get()); + } + + if (mReorderQueue.Length() <= mMaxRefFrames) { + mCallback->InputExhausted(); + } + + } + +private: nsAutoPtr<BlankMediaDataCreator> mCreator; MediaDataDecoderCallback* mCallback; + const uint32_t mMaxRefFrames; + ReorderQueue mReorderQueue; TrackInfo::TrackType mType; }; class BlankVideoDataCreator { public: BlankVideoDataCreator(uint32_t aFrameWidth, uint32_t aFrameHeight, layers::ImageContainer* aImageContainer) @@ -88,66 +124,70 @@ public: mPicture = gfx::IntRect(0, 0, mFrameWidth, mFrameHeight); } already_AddRefed<MediaData> Create(const media::TimeUnit& aDTS, const media::TimeUnit& aDuration, int64_t aOffsetInStream) { // Create a fake YUV buffer in a 420 format. That is, an 8bpp Y plane, // with a U and V plane that are half the size of the Y plane, i.e 8 bit, - // 2x2 subsampled. Have the data pointers of each frame point to the - // first plane, they'll always be zero'd memory anyway. - auto frame = MakeUnique<uint8_t[]>(mFrameWidth * mFrameHeight); - memset(frame.get(), 0, mFrameWidth * mFrameHeight); + // 2x2 subsampled. + const int sizeY = mFrameWidth * mFrameHeight; + const int sizeCbCr = ((mFrameWidth + 1) / 2) * ((mFrameHeight + 1) / 2); + auto frame = MakeUnique<uint8_t[]>(sizeY + sizeCbCr); VideoData::YCbCrBuffer buffer; // Y plane. buffer.mPlanes[0].mData = frame.get(); buffer.mPlanes[0].mStride = mFrameWidth; buffer.mPlanes[0].mHeight = mFrameHeight; buffer.mPlanes[0].mWidth = mFrameWidth; buffer.mPlanes[0].mOffset = 0; buffer.mPlanes[0].mSkip = 0; // Cb plane. - buffer.mPlanes[1].mData = frame.get(); + buffer.mPlanes[1].mData = frame.get() + sizeY; buffer.mPlanes[1].mStride = mFrameWidth / 2; buffer.mPlanes[1].mHeight = mFrameHeight / 2; buffer.mPlanes[1].mWidth = mFrameWidth / 2; buffer.mPlanes[1].mOffset = 0; buffer.mPlanes[1].mSkip = 0; // Cr plane. - buffer.mPlanes[2].mData = frame.get(); + buffer.mPlanes[2].mData = frame.get() + sizeY; buffer.mPlanes[2].mStride = mFrameWidth / 2; buffer.mPlanes[2].mHeight = mFrameHeight / 2; buffer.mPlanes[2].mWidth = mFrameWidth / 2; buffer.mPlanes[2].mOffset = 0; buffer.mPlanes[2].mSkip = 0; + // Set to color white. + memset(buffer.mPlanes[0].mData, 255, sizeY); + memset(buffer.mPlanes[1].mData, 128, sizeCbCr); + return VideoData::Create(mInfo, mImageContainer, nullptr, aOffsetInStream, aDTS.ToMicroseconds(), aDuration.ToMicroseconds(), buffer, true, aDTS.ToMicroseconds(), mPicture); } + private: VideoInfo mInfo; gfx::IntRect mPicture; uint32_t mFrameWidth; uint32_t mFrameHeight; RefPtr<layers::ImageContainer> mImageContainer; }; - class BlankAudioDataCreator { public: BlankAudioDataCreator(uint32_t aChannelCount, uint32_t aSampleRate) : mFrameSum(0), mChannelCount(aChannelCount), mSampleRate(aSampleRate) { } MediaData* Create(const media::TimeUnit& aDTS, @@ -224,17 +264,21 @@ public: DecoderDoctorDiagnostics* aDiagnostics) const override { return true; } ConversionRequired DecoderNeedsConversion(const TrackInfo& aConfig) const override { - return kNeedNone; + if (aConfig.IsVideo() && H264Converter::IsH264(aConfig)) { + return kNeedAVCC; + } else { + return kNeedNone; + } } }; already_AddRefed<PlatformDecoderModule> CreateBlankDecoderModule() { RefPtr<PlatformDecoderModule> pdm = new BlankDecoderModule(); return pdm.forget();
--- a/dom/media/platforms/agnostic/VPXDecoder.h +++ b/dom/media/platforms/agnostic/VPXDecoder.h @@ -34,17 +34,19 @@ public: return "libvpx video decoder"; } enum Codec: uint8_t { VP8 = 1 << 0, VP9 = 1 << 1 }; - // Return true if mimetype is a VPX codec of given types. + // Return true if aMimeType is a one of the strings used by our demuxers to + // identify VPX of the specified type. Does not parse general content type + // strings, i.e. white space matters. static bool IsVPX(const nsACString& aMimeType, uint8_t aCodecMask=VP8|VP9); static bool IsVP8(const nsACString& aMimeType); static bool IsVP9(const nsACString& aMimeType); private: void ProcessDecode(MediaRawData* aSample); int DoDecode(MediaRawData* aSample); void ProcessDrain();
--- a/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp +++ b/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp @@ -11,16 +11,17 @@ #include "mozIGeckoMediaPluginService.h" #include "mozilla/CDMProxy.h" #include "mozilla/unused.h" #include "nsAutoPtr.h" #include "nsServiceManagerUtils.h" #include "MediaInfo.h" #include "nsClassHashtable.h" #include "GMPDecoderModule.h" +#include "MP4Decoder.h" namespace mozilla { typedef MozPromiseRequestHolder<CDMProxy::DecryptPromise> DecryptPromiseRequestHolder; class EMEDecryptor : public MediaDataDecoder { public: @@ -287,19 +288,17 @@ EMEDecoderModule::CreateAudioDecoder(con mProxy, AbstractThread::GetCurrent()->AsTaskQueue())); return emeDecoder.forget(); } PlatformDecoderModule::ConversionRequired EMEDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const { - if (aConfig.IsVideo() && - (aConfig.mMimeType.EqualsLiteral("video/avc") || - aConfig.mMimeType.EqualsLiteral("video/mp4"))) { +