diff --git a/accessible/src/html/nsHTMLTableAccessible.cpp b/accessible/src/html/nsHTMLTableAccessible.cpp index 88010995909..b8a62a6a234 100644 --- a/accessible/src/html/nsHTMLTableAccessible.cpp +++ b/accessible/src/html/nsHTMLTableAccessible.cpp @@ -1494,28 +1494,24 @@ nsHTMLTableAccessible::IsProbablyForLayout(bool *aIsProbablyForLayout) * Rules for non-bordered tables with 2-4 columns and 2+ rows from here on forward */ - // Check for styled background color across the row - // Alternating background color is a common way - nsCOMPtr nodeList; - nsCOMPtr tableElt(do_QueryInterface(mContent)); - tableElt->GetElementsByTagName(NS_LITERAL_STRING("tr"), getter_AddRefs(nodeList)); - NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE); - PRUint32 length; - nodeList->GetLength(&length); - nsAutoString color, lastRowColor; - for (PRUint32 rowCount = 0; rowCount < length; rowCount ++) { - nsCOMPtr rowNode; - nodeList->Item(rowCount, getter_AddRefs(rowNode)); - nsCOMPtr rowContent(do_QueryInterface(rowNode)); - - nsCOMPtr styleDecl = - nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), rowContent); - NS_ENSURE_TRUE(styleDecl, NS_ERROR_FAILURE); - - lastRowColor = color; - styleDecl->GetPropertyValue(NS_LITERAL_STRING("background-color"), color); - if (rowCount > 0 && false == lastRowColor.Equals(color)) { - RETURN_LAYOUT_ANSWER(false, "2 styles of row background color, non-bordered"); + // Check for styled background color across rows (alternating background + // color is a common feature for data tables). + PRUint32 childCount = GetChildCount(); + nsAutoString rowColor, prevRowColor; + for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) { + nsAccessible* child = GetChildAt(childIdx); + if (child->Role() == roles::ROW) { + nsCOMPtr styleDecl = + nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), + child->GetContent()); + if (styleDecl) { + prevRowColor = rowColor; + styleDecl->GetPropertyValue(NS_LITERAL_STRING("background-color"), + rowColor); + if (childIdx > 0 && !prevRowColor.Equals(rowColor)) { + RETURN_LAYOUT_ANSWER(false, "2 styles of row background color, non-bordered"); + } + } } } diff --git a/accessible/tests/mochitest/treeupdate/Makefile.in b/accessible/tests/mochitest/treeupdate/Makefile.in index ca73261c52b..053201134d6 100644 --- a/accessible/tests/mochitest/treeupdate/Makefile.in +++ b/accessible/tests/mochitest/treeupdate/Makefile.in @@ -48,6 +48,7 @@ include $(topsrcdir)/config/rules.mk _TEST_FILES =\ test_ariadialog.html \ test_colorpicker.xul \ + test_cssoverflow.html \ test_contextmenu.xul \ test_doc.html \ test_gencontent.html \ diff --git a/accessible/tests/mochitest/treeupdate/test_cssoverflow.html b/accessible/tests/mochitest/treeupdate/test_cssoverflow.html new file mode 100644 index 00000000000..102fc976f78 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_cssoverflow.html @@ -0,0 +1,141 @@ + + + + Testing HTML scrollable frames (css overflow style) + + + + + + + + + + + + + + + Mozilla Bug 677154 + +

+ +
+  
+
+ +
+
+ + diff --git a/aclocal.m4 b/aclocal.m4 index fc58446c9dc..77f9452fb6c 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -17,6 +17,7 @@ builtin(include, build/autoconf/acwinpaths.m4)dnl builtin(include, build/autoconf/lto.m4)dnl builtin(include, build/autoconf/gcc-pr49911.m4)dnl builtin(include, build/autoconf/frameptr.m4)dnl +builtin(include, build/autoconf/compiler-opts.m4)dnl MOZ_PROG_CHECKMSYS() diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index eadcb278ee6..a166ce649b4 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -426,7 +426,13 @@ pref("media.realtime_decoder.enabled", true); // by bug 710563. pref("layout.frame_rate.precise", true); +// Temporary remote js console hack +pref("b2g.remote-js.enabled", true); +pref("b2g.remote-js.port", 9999); + // Screen timeout in minutes pref("power.screen.timeout", 60); pref("full-screen-api.enabled", true); + +pref("media.volume.steps", 10); diff --git a/b2g/chrome/content/shell.js b/b2g/chrome/content/shell.js index 4facc01217b..1ddb0a18811 100644 --- a/b2g/chrome/content/shell.js +++ b/b2g/chrome/content/shell.js @@ -8,6 +8,7 @@ const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; const CC = Components.Constructor; +const Cr = Components.results; const LocalFile = CC('@mozilla.org/file/local;1', 'nsILocalFile', @@ -25,11 +26,18 @@ XPCOMUtils.defineLazyGetter(Services, 'ss', function() { return Cc['@mozilla.org/content/style-sheet-service;1'] .getService(Ci.nsIStyleSheetService); }); + XPCOMUtils.defineLazyGetter(Services, 'idle', function() { return Cc['@mozilla.org/widget/idleservice;1'] .getService(Ci.nsIIdleService); }); +XPCOMUtils.defineLazyServiceGetter(Services, 'fm', function(){ + return Cc['@mozilla.org/focus-managr;1'] + .getService(Ci.nsFocusManager); +}); + + // In order to use http:// scheme instead of file:// scheme // (that is much more restricted) the following code kick-off // a local http server listening on http://127.0.0.1:7777 and @@ -105,6 +113,7 @@ var shell = { window.addEventListener('keypress', this); window.addEventListener('MozApplicationManifest', this); window.addEventListener("AppCommand", this); + window.addEventListener('mozfullscreenchange', this); this.contentBrowser.addEventListener('load', this, true); try { @@ -186,6 +195,24 @@ var shell = { Services.prefs.setBoolPref("nglayout.debug.paint_flashing", false); } }, + + changeVolume: function shell_changeVolume(aDelta) { + let audioManager = Cc["@mozilla.org/telephony/audiomanager;1"].getService(Ci.nsIAudioManager); + + let steps = 10; + try { + steps = Services.prefs.getIntPref("media.volume.steps"); + if (steps <= 0) + steps = 1; + } catch(e) {} + + let volume = audioManager.masterVolume + aDelta / steps; + if (volume > 1) + volume = 1; + if (volume < 0) + volume = 0; + audioManager.masterVolume = volume; + }, handleEvent: function shell_handleEvent(evt) { switch (evt.type) { @@ -217,8 +244,22 @@ var shell = { case 'Search': this.toggleDebug(); break; + case 'VolumeUp': + this.changeVolume(1); + break; + case 'VolumeDown': + this.changeVolume(-1); + break; } break; + + case 'mozfullscreenchange': + // When the screen goes fullscreen make sure to set the focus to the + // main window so noboby can prevent the ESC key to get out fullscreen + // mode + if (document.mozFullScreen) + Services.fm.focusedWindow = window; + break; case 'load': this.contentBrowser.removeEventListener('load', this, true); this.turnScreenOn(); @@ -339,3 +380,50 @@ Services.obs.addObserver(function onConsoleAPILogEvent(subject, topic, data) { " in " + (message.functionName || "anonymous") + ": "; Services.console.logStringMessage(prefix + Array.join(message.arguments, " ")); }, "console-api-log-event", false); + +(function Repl() { + if (!Services.prefs.getBoolPref('b2g.remote-js.enabled')) { + return; + } + const prompt = 'JS> '; + let output; + let reader = { + onInputStreamReady : function repl_readInput(input) { + let sin = Cc['@mozilla.org/scriptableinputstream;1'] + .createInstance(Ci.nsIScriptableInputStream); + sin.init(input); + try { + let val = eval(sin.read(sin.available())); + let ret = (typeof val === 'undefined') ? 'undefined\n' : val + '\n'; + output.write(ret, ret.length); + // TODO: check if socket has been closed + } catch (e) { + if (e.result === Cr.NS_BASE_STREAM_CLOSED || + (typeof e === 'object' && e.result === Cr.NS_BASE_STREAM_CLOSED)) { + return; + } + let message = (typeof e === 'object') ? e.message + '\n' : e + '\n'; + output.write(message, message.length); + } + output.write(prompt, prompt.length); + input.asyncWait(reader, 0, 0, Services.tm.mainThread); + } + } + let listener = { + onSocketAccepted: function repl_acceptConnection(serverSocket, clientSocket) { + dump('Accepted connection on ' + clientSocket.host + '\n'); + let input = clientSocket.openInputStream(Ci.nsITransport.OPEN_BLOCKING, 0, 0) + .QueryInterface(Ci.nsIAsyncInputStream); + output = clientSocket.openOutputStream(Ci.nsITransport.OPEN_BLOCKING, 0, 0); + output.write(prompt, prompt.length); + input.asyncWait(reader, 0, 0, Services.tm.mainThread); + } + } + let serverPort = Services.prefs.getIntPref('b2g.remote-js.port'); + let serverSocket = Cc['@mozilla.org/network/server-socket;1'] + .createInstance(Ci.nsIServerSocket); + serverSocket.init(serverPort, true, -1); + dump('Opened socket on ' + serverSocket.port + '\n'); + serverSocket.asyncListen(listener); +})(); + diff --git a/b2g/chrome/content/webapi.js b/b2g/chrome/content/webapi.js index 346378a507d..d4c405c0ced 100644 --- a/b2g/chrome/content/webapi.js +++ b/b2g/chrome/content/webapi.js @@ -177,19 +177,22 @@ const ContentPanning = { } }, - position: { - origin: new Point(0, 0), - current: new Point(0 , 0) - }, + position: new Point(0 , 0), onTouchStart: function cp_onTouchStart(evt) { this.dragging = true; - KineticPanning.stop(); + + // If there is a pan animation running (from a previous pan gesture) and + // the user touch back the screen, stop this animation immediatly and + // prevent the possible click action. + if (KineticPanning.active) { + KineticPanning.stop(); + this.preventNextClick = true; + } this.scrollCallback = this.getPannable(evt.originalTarget); - this.position.origin.set(evt.screenX, evt.screenY); - this.position.current.set(evt.screenX, evt.screenY); - KineticPanning.record(new Point(0, 0)); + this.position.set(evt.screenX, evt.screenY); + KineticPanning.record(new Point(0, 0), evt.timeStamp); }, onTouchEnd: function cp_onTouchEnd(evt) { @@ -197,26 +200,29 @@ const ContentPanning = { return; this.dragging = false; - if (this.isPan()) { - if (evt.detail) // The event will generate a click - evt.target.addEventListener('click', this, true); + this.onTouchMove(evt); + let pan = KineticPanning.isPan(); + let click = evt.detail; + if (click && (pan || this.preventNextClick)) + evt.target.addEventListener('click', this, true); + + this.preventNextClick = false; + + if (pan) KineticPanning.start(this); - } }, onTouchMove: function cp_onTouchMove(evt) { if (!this.dragging || !this.scrollCallback) return; - let current = this.position.current; + let current = this.position; let delta = new Point(evt.screenX - current.x, evt.screenY - current.y); current.set(evt.screenX, evt.screenY); - if (this.isPan()) { - KineticPanning.record(delta); - this.scrollCallback(delta.scale(-1)); - } + KineticPanning.record(delta, evt.timeStamp); + this.scrollCallback(delta.scale(-1)); }, @@ -232,24 +238,11 @@ const ContentPanning = { this.scrollCallback = null; }, - isPan: function cp_isPan() { - let dpi = content.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils) - .displayDPI; - - let threshold = Services.prefs.getIntPref('ui.dragThresholdX') / 240 * dpi; - - let deltaX = this.position.origin.x - this.position.current.x; - let deltaY = this.position.origin.y - this.position.current.y; - return (Math.abs(deltaX) > threshold || Math.abs(deltaY) > threshold); - }, - getPannable: function cp_getPannable(node) { if (!(node instanceof Ci.nsIDOMHTMLElement) || node.tagName == 'HTML') return null; let content = node.ownerDocument.defaultView; - while (!(node instanceof Ci.nsIDOMHTMLBodyElement)) { let style = content.getComputedStyle(node, null); @@ -299,7 +292,7 @@ const kMinVelocity = 0.4; const kMaxVelocity = 6; // Constants that affect the "friction" of the scroll pane. -const kExponentialC = 1400; +const kExponentialC = 1000; const kPolynomialC = 100 / 1000000; // How often do we change the position of the scroll pane? @@ -307,17 +300,25 @@ const kPolynomialC = 100 / 1000000; // Too little and panning will be choppy. In milliseconds. const kUpdateInterval = 16; +// The numbers of momentums to use for calculating the velocity of the pan. +// Those are taken from the end of the action +const kSamples = 5; + const KineticPanning = { _position: new Point(0, 0), _velocity: new Point(0, 0), _acceleration: new Point(0, 0), + get active() { + return this.target !== null; + }, + _target: null, start: function kp_start(target) { this.target = target; // Calculate the initial velocity of the movement based on user input - let momentums = this.momentums; + let momentums = this.momentums.slice(-kSamples); let distance = new Point(0, 0); momentums.forEach(function(momentum) { @@ -338,6 +339,7 @@ const KineticPanning = { let velocity = this._velocity; velocity.set(Math.abs(velocityX) < kMinVelocity ? 0 : velocityX, Math.abs(velocityY) < kMinVelocity ? 0 : velocityY); + this.momentums = []; // Set acceleration vector to opposite signs of velocity function sign(x) { @@ -358,20 +360,32 @@ const KineticPanning = { if (!this.target) return; - this.momentums.splice(0); + this.momentums = []; this.target.onKineticEnd(); this.target = null; }, momentums: [], - record: function kp_record(delta) { - // If the panning direction has changed, stop the current activity. - if (this.target && ((delta.x * this._velocity.x < 0) || - (delta.y * this._velocity.y < 0))) - this.stop(); + record: function kp_record(delta, timestamp) { + this.momentums.push({ 'time': timestamp, 'dx' : delta.x, 'dy' : delta.y }); + }, - this.momentums.push({ 'time': Date.now(), 'dx' : delta.x, 'dy' : delta.y }); + isPan: function cp_isPan() { + let dpi = content.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils) + .displayDPI; + + let threshold = Services.prefs.getIntPref('ui.dragThresholdX') / 240 * dpi; + + let deltaX = 0; + let deltaY = 0; + let start = this.momentums[0].time; + return this.momentums.slice(1).some(function(momentum) { + deltaX += momentum.dx; + deltaY += momentum.dy; + return (Math.abs(deltaX) > threshold) || (Math.abs(deltaY) > threshold); + }); }, _startAnimation: function kp_startAnimation() { diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in index 5e3d84ae0f6..ff427492356 100644 --- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -151,9 +151,6 @@ @BINPATH@/components/dom_system_b2g.xpt #endif @BINPATH@/components/dom_battery.xpt -#ifdef MOZ_B2G_BT -@BINPATH@/components/dom_bluetooth.xpt -#endif @BINPATH@/components/dom_canvas.xpt @BINPATH@/components/dom_core.xpt @BINPATH@/components/dom_css.xpt diff --git a/browser/app/blocklist.xml b/browser/app/blocklist.xml index c9bf104c2b9..8a969db7358 100644 --- a/browser/app/blocklist.xml +++ b/browser/app/blocklist.xml @@ -1,5 +1,5 @@ - + @@ -27,8 +27,12 @@ - - + + + + + + @@ -122,8 +126,11 @@ - - + + + + + @@ -178,11 +185,8 @@ - - - - - + + @@ -192,6 +196,10 @@ + + + + diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index f89a8abda58..49946f5f9e3 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -282,6 +282,7 @@ pref("browser.urlbar.doubleClickSelectsAll", true); pref("browser.urlbar.doubleClickSelectsAll", false); #endif pref("browser.urlbar.autoFill", false); +pref("browser.urlbar.autoFill.typed", true); // 0: Match anywhere (e.g., middle of words) // 1: Match on word boundaries and then try matching anywhere // 2: Match only on word boundaries (e.g., after / or .) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 3adbc57d129..f9040ea410d 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -9053,7 +9053,14 @@ XPCOMUtils.defineLazyGetter(Scratchpad, "ScratchpadManager", function() { var StyleEditor = { prefEnabledName: "devtools.styleeditor.enabled", - openChrome: function SE_openChrome() + /** + * Opens the style editor. If the UI is already open, it will be focused. + * + * @param {CSSStyleSheet} [aSelectedStyleSheet] default Stylesheet. + * @param {Number} [aLine] Line to which the caret should be moved (one-indexed). + * @param {Number} [aCol] Column to which the caret should be moved (one-indexed). + */ + openChrome: function SE_openChrome(aSelectedStyleSheet, aLine, aCol) { const CHROME_URL = "chrome://browser/content/styleeditor.xul"; const CHROME_WINDOW_TYPE = "Tools:StyleEditor"; @@ -9067,14 +9074,23 @@ var StyleEditor = { while (enumerator.hasMoreElements()) { var win = enumerator.getNext(); if (win.styleEditorChrome.contentWindowID == contentWindowID) { + if (aSelectedStyleSheet) { + win.styleEditorChrome.selectStyleSheet(aSelectedStyleSheet, aLine, aCol); + } win.focus(); return win; } } + let args = { + contentWindow: contentWindow, + selectedStyleSheet: aSelectedStyleSheet, + line: aLine, + col: aCol + }; + args.wrappedJSObject = args; let chromeWindow = Services.ww.openWindow(null, CHROME_URL, "_blank", - CHROME_WINDOW_FLAGS, - contentWindow); + CHROME_WINDOW_FLAGS, args); chromeWindow.focus(); return chromeWindow; } diff --git a/browser/base/content/browser.xul b/browser/base/content/browser.xul index 64c99679b3f..3ac55cb125d 100644 --- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -90,6 +90,7 @@ lightweightthemes="true" lightweightthemesfooter="browser-bottombox" windowtype="navigator:browser" + macanimationtype="document" screenX="4" screenY="4" browsingmode="normal" persist="screenX screenY width height sizemode"> diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 90aa31dba5d..0eeb0276abd 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -1266,6 +1266,12 @@ else { t._animStartTime = Date.now(); t.setAttribute("fadein", "true"); + + // This call to adjustTabstrip is redundant but needed so that + // when opening a second tab, the first tab's close buttons + // appears immediately rather than when the transition ends. + if (tabContainer.childNodes.length == 2) + tabContainer.adjustTabstrip(); } }, 0, this.tabContainer); } @@ -1365,7 +1371,8 @@ // pretend the user typed this so it'll be available till // the document successfully loads - b.userTypedValue = aURI; + if (!isBlankPageURL(aURI)) + b.userTypedValue = aURI; let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE; if (aAllowThirdPartyFixup) @@ -1557,15 +1564,26 @@ diff --git a/browser/devtools/sourceeditor/source-editor-orion.jsm b/browser/devtools/sourceeditor/source-editor-orion.jsm index fb6d485072f..5cb4b0693df 100644 --- a/browser/devtools/sourceeditor/source-editor-orion.jsm +++ b/browser/devtools/sourceeditor/source-editor-orion.jsm @@ -78,6 +78,9 @@ const ORION_EVENTS = { Selection: "Selection", Focus: "Focus", Blur: "Blur", + MouseOver: "MouseOver", + MouseOut: "MouseOut", + MouseMove: "MouseMove", }; /** diff --git a/browser/devtools/sourceeditor/source-editor.jsm b/browser/devtools/sourceeditor/source-editor.jsm index e38ac0e2ef2..85d07c85955 100644 --- a/browser/devtools/sourceeditor/source-editor.jsm +++ b/browser/devtools/sourceeditor/source-editor.jsm @@ -161,6 +161,30 @@ SourceEditor.EVENTS = { * The blur event is fired when the editor goes out of focus. */ BLUR: "Blur", + + /** + * The MouseMove event is sent when the user moves the mouse over a line + * annotation. The event object properties: + * - event - the DOM mousemove event object. + * - x and y - the mouse coordinates relative to the document being edited. + */ + MOUSE_MOVE: "MouseMove", + + /** + * The MouseOver event is sent when the mouse pointer enters a line + * annotation. The event object properties: + * - event - the DOM mouseover event object. + * - x and y - the mouse coordinates relative to the document being edited. + */ + MOUSE_OVER: "MouseOver", + + /** + * This MouseOut event is sent when the mouse pointer exits a line + * annotation. The event object properties: + * - event - the DOM mouseout event object. + * - x and y - the mouse coordinates relative to the document being edited. + */ + MOUSE_OUT: "MouseOut", }; /** diff --git a/browser/devtools/sourceeditor/test/Makefile.in b/browser/devtools/sourceeditor/test/Makefile.in index f8f390b0bee..081dc57dcbf 100644 --- a/browser/devtools/sourceeditor/test/Makefile.in +++ b/browser/devtools/sourceeditor/test/Makefile.in @@ -55,6 +55,7 @@ _BROWSER_TEST_FILES = \ browser_bug687160_line_api.js \ browser_bug650345_find.js \ browser_bug703692_focus_blur.js \ + browser_bug725388_mouse_events.js \ head.js \ libs:: $(_BROWSER_TEST_FILES) diff --git a/browser/devtools/sourceeditor/test/browser_bug725388_mouse_events.js b/browser/devtools/sourceeditor/test/browser_bug725388_mouse_events.js new file mode 100644 index 00000000000..07959127161 --- /dev/null +++ b/browser/devtools/sourceeditor/test/browser_bug725388_mouse_events.js @@ -0,0 +1,97 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +let tempScope = {}; +Cu.import("resource:///modules/source-editor.jsm", tempScope); +let SourceEditor = tempScope.SourceEditor; + +let testWin; +let editor; + +function test() +{ + waitForExplicitFinish(); + + const windowUrl = "data:text/xml," + + ""; + const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no"; + + testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null); + testWin.addEventListener("load", function onWindowLoad() { + testWin.removeEventListener("load", onWindowLoad, false); + waitForFocus(initEditor, testWin); + }, false); +} + +function initEditor() +{ + let hbox = testWin.document.querySelector("hbox"); + + editor = new SourceEditor(); + editor.init(hbox, {}, editorLoaded); +} + +function editorLoaded() +{ + let text = "BrowserBug - 725388"; + editor.setText(text); + + let target = editor.editorElement; + let targetWin = target.ownerDocument.defaultView; + + let mMoveHandler = function(aEvent) { + editor.removeEventListener(SourceEditor.EVENTS.MOUSE_MOVE, mMoveHandler); + + is(aEvent.event.type, "mousemove", "MouseMove event fired."); + + editor.addEventListener(SourceEditor.EVENTS.MOUSE_OVER, mOverHandler); + waitForFocus(function() { + EventUtils.synthesizeMouse(target, 10, 10, {type: "mouseover"}, + targetWin); + }); + }; + + let mOverHandler = function(aEvent) { + editor.removeEventListener(SourceEditor.EVENTS.MOUSE_OVER, mOverHandler); + + is(aEvent.event.type, "mouseover", "MouseOver event fired."); + + editor.addEventListener(SourceEditor.EVENTS.MOUSE_OUT, mOutHandler); + waitForFocus(function() { + EventUtils.synthesizeMouse(target, -10, -10, {type: "mouseout"}, + targetWin); + }, targetWin); + }; + + let mOutHandler = function(aEvent) { + editor.removeEventListener(SourceEditor.EVENTS.MOUSE_OUT, mOutHandler); + + is(aEvent.event.type, "mouseout", "MouseOut event fired."); + executeSoon(testEnd); + }; + + editor.addEventListener(SourceEditor.EVENTS.MOUSE_MOVE, mMoveHandler); + + editor.focus(); + waitForFocus(function() { + EventUtils.synthesizeMouse(target, 1, 1, {type: "mousemove"}, + targetWin); + }, targetWin); +} + +function testEnd() +{ + if (editor) { + editor.destroy(); + } + if (testWin) { + testWin.close(); + } + testWin = editor = null; + + waitForFocus(finish, window); +} diff --git a/browser/devtools/styleeditor/StyleEditor.jsm b/browser/devtools/styleeditor/StyleEditor.jsm index 4eb8241eee2..ff492cb056f 100644 --- a/browser/devtools/styleeditor/StyleEditor.jsm +++ b/browser/devtools/styleeditor/StyleEditor.jsm @@ -146,7 +146,7 @@ StyleEditor.prototype = { */ get styleSheet() { - assert(this._styleSheet, "StyleSheet must be loaded first.") + assert(this._styleSheet, "StyleSheet must be loaded first."); return this._styleSheet; }, @@ -921,9 +921,11 @@ StyleEditor.prototype = { aArgs.unshift(this); } + // copy the list of listeners to allow adding/removing listeners in handlers + let listeners = this._actionListeners.concat(); // trigger all listeners that have this action handler - for (let i = 0; i < this._actionListeners.length; ++i) { - let listener = this._actionListeners[i]; + for (let i = 0; i < listeners.length; ++i) { + let listener = listeners[i]; let actionHandler = listener["on" + aName]; if (actionHandler) { actionHandler.apply(listener, aArgs); diff --git a/browser/devtools/styleeditor/StyleEditorChrome.jsm b/browser/devtools/styleeditor/StyleEditorChrome.jsm index ca57f0208ce..0873941cb44 100644 --- a/browser/devtools/styleeditor/StyleEditorChrome.jsm +++ b/browser/devtools/styleeditor/StyleEditorChrome.jsm @@ -270,9 +270,11 @@ StyleEditorChrome.prototype = { aArgs.unshift(this); } - // trigger all listeners that have this named handler - for (let i = 0; i < this._listeners.length; ++i) { - let listener = this._listeners[i]; + // copy the list of listeners to allow adding/removing listeners in handlers + let listeners = this._listeners.concat(); + // trigger all listeners that have this named handler. + for (let i = 0; i < listeners.length; i++) { + let listener = listeners[i]; let handler = listener["on" + aName]; if (handler) { handler.apply(listener, aArgs); @@ -329,10 +331,10 @@ StyleEditorChrome.prototype = { { this._resetChrome(); - this._document.title = _("chromeWindowTitle", - this.contentDocument.title || this.contentDocument.location.href); - let document = this.contentDocument; + this._document.title = _("chromeWindowTitle", + document.title || document.location.href); + for (let i = 0; i < document.styleSheets.length; ++i) { let styleSheet = document.styleSheets[i]; @@ -352,6 +354,79 @@ StyleEditorChrome.prototype = { }, this); }, + /** + * selects a stylesheet and optionally moves the cursor to a selected line + * + * @param {CSSStyleSheet} [aSheet] + * Stylesheet that should be selected. If a stylesheet is not passed + * and the editor is not initialized we focus the first stylesheet. If + * a stylesheet is not passed and the editor is initialized we ignore + * the call. + * @param {Number} [aLine] + * Line to which the caret should be moved (one-indexed). + * @param {Number} [aCol] + * Column to which the caret should be moved (one-indexed). + */ + selectStyleSheet: function SEC_selectSheet(aSheet, aLine, aCol) + { + let select = function DEC_select(aEditor) { + let summary = aSheet ? this.getSummaryElementForEditor(aEditor) + : this._view.getSummaryElementByOrdinal(0); + let setCaret = false; + + if (aLine || aCol) { + aLine = aLine || 1; + aCol = aCol || 1; + setCaret = true; + } + if (!aEditor.sourceEditor) { + // If a line or column was specified we move the caret appropriately. + if (setCaret) { + aEditor.addActionListener({ + onAttach: function SEC_selectSheet_onAttach() + { + aEditor.removeActionListener(this); + aEditor.sourceEditor.setCaretPosition(aLine - 1, aCol - 1); + } + }); + } + this._view.activeSummary = summary; + } else { + this._view.activeSummary = summary; + + // If a line or column was specified we move the caret appropriately. + if (setCaret) { + aEditor.sourceEditor.setCaretPosition(aLine - 1, aCol - 1); + } + } + }.bind(this); + + if (!this.editors.length) { + // We are in the main initialization phase so we wait for the editor + // containing the target stylesheet to be added and select the target + // stylesheet, optionally moving the cursor to a selected line. + this.addChromeListener({ + onEditorAdded: function SEC_selectSheet_onEditorAdded(aChrome, aEditor) { + if ((!aSheet && aEditor.styleSheetIndex == 0) || + aEditor.styleSheet == aSheet) { + aChrome.removeChromeListener(this); + select(aEditor); + } + } + }); + } else if (aSheet) { + // We are already initialized and a stylesheet has been specified. Here + // we iterate through the editors and select the one containing the target + // stylesheet, optionally moving the cursor to a selected line. + for each (let editor in this.editors) { + if (editor.styleSheet == aSheet) { + select(editor); + break; + } + } + } + }, + /** * Disable all UI, effectively making editors read-only. * This is automatically called when no content window is attached. @@ -455,9 +530,8 @@ StyleEditorChrome.prototype = { } }, false); - // autofocus the first or new stylesheet - if (editor.styleSheetIndex == 0 || - editor.hasFlag(StyleEditorFlags.NEW)) { + // autofocus new stylesheets + if (editor.hasFlag(StyleEditorFlags.NEW)) { this._view.activeSummary = aSummary; } diff --git a/browser/devtools/styleeditor/styleeditor.xul b/browser/devtools/styleeditor/styleeditor.xul index f10ae6deda2..e84df003c0f 100644 --- a/browser/devtools/styleeditor/styleeditor.xul +++ b/browser/devtools/styleeditor/styleeditor.xul @@ -132,8 +132,10 @@ diff --git a/browser/devtools/styleeditor/test/Makefile.in b/browser/devtools/styleeditor/test/Makefile.in index f8dcecf7e8f..0b411622473 100644 --- a/browser/devtools/styleeditor/test/Makefile.in +++ b/browser/devtools/styleeditor/test/Makefile.in @@ -51,6 +51,7 @@ _BROWSER_TEST_FILES = \ browser_styleeditor_init.js \ browser_styleeditor_loading.js \ browser_styleeditor_new.js \ + browser_styleeditor_passedinsheet.js \ browser_styleeditor_pretty.js \ browser_styleeditor_readonly.js \ browser_styleeditor_reopen.js \ diff --git a/browser/devtools/styleeditor/test/browser_styleeditor_passedinsheet.js b/browser/devtools/styleeditor/test/browser_styleeditor_passedinsheet.js new file mode 100644 index 00000000000..05b37019e49 --- /dev/null +++ b/browser/devtools/styleeditor/test/browser_styleeditor_passedinsheet.js @@ -0,0 +1,61 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const TESTCASE_URI = TEST_BASE + "simple.html"; +const LINE = 6; +const COL = 2; + +let editor = null; +let sheet = null; + +function test() +{ + waitForExplicitFinish(); + gBrowser.selectedBrowser.addEventListener("load", function () { + gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); + run(); + }, true); + content.location = TESTCASE_URI; +} + +function run() +{ + sheet = content.document.styleSheets[1]; + launchStyleEditorChrome(function attachListeners(aChrome) { + aChrome.addChromeListener({ + onEditorAdded: checkSourceEditor + }); + }, sheet, LINE, COL); +} + +function checkSourceEditor(aChrome, aEditor) +{ + if (!aEditor.sourceEditor) { + aEditor.addActionListener({ + onAttach: function (aEditor) { + aEditor.removeActionListener(this); + validate(aEditor); + } + }); + } else { + validate(aEditor); + } +} + +function validate(aEditor) +{ + info("validating style editor"); + let sourceEditor = aEditor.sourceEditor; + let caretPosition = sourceEditor.getCaretPosition(); + is(caretPosition.line, LINE - 1, "caret row is correct"); // index based + is(caretPosition.col, COL - 1, "caret column is correct"); + is(aEditor.styleSheet, sheet, "loaded stylesheet matches document stylesheet"); + finishUp(); +} + +function finishUp() +{ + editor = sheet = null; + finish(); +} diff --git a/browser/devtools/styleeditor/test/head.js b/browser/devtools/styleeditor/test/head.js index f1bfbfbacd9..bc4267a6962 100644 --- a/browser/devtools/styleeditor/test/head.js +++ b/browser/devtools/styleeditor/test/head.js @@ -19,9 +19,9 @@ function cleanup() } } -function launchStyleEditorChrome(aCallback) +function launchStyleEditorChrome(aCallback, aSheet, aLine, aCol) { - gChromeWindow = StyleEditor.openChrome(); + gChromeWindow = StyleEditor.openChrome(aSheet, aLine, aCol); if (gChromeWindow.document.readyState != "complete") { gChromeWindow.addEventListener("load", function onChromeLoad() { gChromeWindow.removeEventListener("load", onChromeLoad, true); @@ -34,12 +34,12 @@ function launchStyleEditorChrome(aCallback) } } -function addTabAndLaunchStyleEditorChromeWhenLoaded(aCallback) +function addTabAndLaunchStyleEditorChromeWhenLoaded(aCallback, aSheet, aLine, aCol) { gBrowser.selectedTab = gBrowser.addTab(); gBrowser.selectedBrowser.addEventListener("load", function onLoad() { gBrowser.selectedBrowser.removeEventListener("load", onLoad, true); - launchStyleEditorChrome(aCallback); + launchStyleEditorChrome(aCallback, aSheet, aLine, aCol); }, true); } diff --git a/browser/devtools/styleinspector/CssHtmlTree.jsm b/browser/devtools/styleinspector/CssHtmlTree.jsm index 7e121e1fac4..4915b3f2794 100644 --- a/browser/devtools/styleinspector/CssHtmlTree.jsm +++ b/browser/devtools/styleinspector/CssHtmlTree.jsm @@ -273,6 +273,7 @@ CssHtmlTree.prototype = { this._matchedProperties = null; if (this.htmlComplete) { + this.refreshSourceFilter(); this.refreshPanel(); } else { if (this._refreshProcess) { @@ -281,6 +282,9 @@ CssHtmlTree.prototype = { CssHtmlTree.processTemplate(this.templateRoot, this.root, this); + // Refresh source filter ... this must be done after templateRoot has been + // processed. + this.refreshSourceFilter(); this.numVisibleProperties = 0; let fragment = this.doc.createDocumentFragment(); this._refreshProcess = new UpdateProcess(this.win, CssHtmlTree.propertyNames, { @@ -362,21 +366,28 @@ CssHtmlTree.prototype = { }, /** - * The change event handler for the onlyUserStyles checkbox. When - * onlyUserStyles.checked is true we do not display properties that have no - * matched selectors, and we do not display UA styles. If .checked is false we - * do display even properties with no matched selectors, and we include the UA - * styles. + * The change event handler for the onlyUserStyles checkbox. * * @param {Event} aEvent the DOM Event object. */ onlyUserStylesChanged: function CssHtmltree_onlyUserStylesChanged(aEvent) + { + this.refreshSourceFilter(); + this.refreshPanel(); + }, + + /** + * When onlyUserStyles.checked is true we only display properties that have + * matched selectors and have been included by the document or one of the + * document's stylesheets. If .checked is false we display all properties + * including those that come from UA stylesheets. + */ + refreshSourceFilter: function CssHtmlTree_setSourceFilter() { this._matchedProperties = null; this.cssLogic.sourceFilter = this.showOnlyUserStyles ? CssLogic.FILTER.ALL : CssLogic.FILTER.UA; - this.refreshPanel(); }, /** @@ -974,4 +985,24 @@ SelectorView.prototype = { return result; }, + + /** + * When a css link is clicked this method is called in order to either: + * 1. Open the link in view source (for element style attributes). + * 2. Open the link in the style editor. + * + * @param aEvent The click event + */ + openStyleEditor: function(aEvent) + { + if (this.selectorInfo.selector._cssRule._cssSheet) { + let styleSheet = this.selectorInfo.selector._cssRule._cssSheet.domSheet; + let line = this.selectorInfo.ruleLine; + + this.tree.win.StyleEditor.openChrome(styleSheet, line); + } else { + let href = this.selectorInfo.sourceElement.ownerDocument.location.href; + this.tree.win.openUILinkIn("view-source:" + href, "window"); + } + }, }; diff --git a/browser/devtools/styleinspector/CssLogic.jsm b/browser/devtools/styleinspector/CssLogic.jsm index d4b03d1c2a4..67e37810071 100644 --- a/browser/devtools/styleinspector/CssLogic.jsm +++ b/browser/devtools/styleinspector/CssLogic.jsm @@ -232,7 +232,7 @@ CssLogic.prototype = { // Update the CssSheet objects. this.forEachSheet(function(aSheet) { aSheet._sheetAllowed = -1; - if (!aSheet.systemSheet && aSheet.sheetAllowed) { + if (aSheet.contentSheet && aSheet.sheetAllowed) { ruleCount += aSheet.ruleCount; } }, this); @@ -345,7 +345,7 @@ CssLogic.prototype = { let sheets = []; this.forEachSheet(function (aSheet) { - if (!aSheet.systemSheet) { + if (aSheet.contentSheet) { sheets.push(aSheet); } }, this); @@ -395,7 +395,7 @@ CssLogic.prototype = { } sheet = new CssSheet(this, aDomSheet, aIndex); - if (sheet.sheetAllowed && !sheet.systemSheet) { + if (sheet.sheetAllowed && sheet.contentSheet) { this._ruleCount += sheet.ruleCount; } @@ -569,7 +569,7 @@ CssLogic.prototype = { this.forEachSheet(function (aSheet) { // We do not show unmatched selectors from system stylesheets - if (aSheet.systemSheet || aSheet.disabled || !aSheet.mediaMatches) { + if (!aSheet.contentSheet || aSheet.disabled || !aSheet.mediaMatches) { return; } @@ -664,7 +664,7 @@ CssLogic.prototype = { sheet._passId = this._passId; } - if (filter !== CssLogic.FILTER.UA && sheet.systemSheet) { + if (filter === CssLogic.FILTER.ALL && !sheet.contentSheet) { continue; } @@ -710,7 +710,7 @@ CssLogic.prototype = { let result = {}; this.forSomeSheets(function (aSheet) { - if (aSheet.systemSheet || aSheet.disabled || !aSheet.mediaMatches) { + if (!aSheet.contentSheet || aSheet.disabled || !aSheet.mediaMatches) { return false; } @@ -865,29 +865,23 @@ XPCOMUtils.defineLazyGetter(CssLogic, "_strings", function() Services.strings .createBundle("chrome://browser/locale/devtools/styleinspector.properties")); /** - * Is the given property sheet a system (user agent) stylesheet? + * Is the given property sheet a content stylesheet? * * @param {CSSStyleSheet} aSheet a stylesheet - * @return {boolean} true if the given stylesheet is a system stylesheet or + * @return {boolean} true if the given stylesheet is a content stylesheet, * false otherwise. */ -CssLogic.isSystemStyleSheet = function CssLogic_isSystemStyleSheet(aSheet) +CssLogic.isContentStylesheet = function CssLogic_isContentStylesheet(aSheet) { - if (!aSheet) { + // All sheets with owner nodes have been included by content. + if (aSheet.ownerNode) { return true; } - let url = aSheet.href; - - if (!url) return false; - if (url.length === 0) return true; - - // Check for http[s] - if (url[0] === 'h') return false; - if (url.substr(0, 9) === "resource:") return true; - if (url.substr(0, 7) === "chrome:") return true; - if (url === "XPCSafeJSObjectWrapper.cpp") return true; - if (url.substr(0, 6) === "about:") return true; + // If the sheet has a CSSImportRule we need to check the parent stylesheet. + if (aSheet.ownerRule instanceof Ci.nsIDOMCSSImportRule) { + return CssLogic.isContentStylesheet(aSheet.parentStyleSheet); + } return false; }; @@ -942,7 +936,7 @@ function CssSheet(aCssLogic, aDomSheet, aIndex) { this._cssLogic = aCssLogic; this.domSheet = aDomSheet; - this.index = this.systemSheet ? -100 * aIndex : aIndex; + this.index = this.contentSheet ? aIndex : -100 * aIndex; // Cache of the sheets href. Cached by the getter. this._href = null; @@ -960,21 +954,21 @@ function CssSheet(aCssLogic, aDomSheet, aIndex) CssSheet.prototype = { _passId: null, - _systemSheet: null, + _contentSheet: null, _mediaMatches: null, /** * Tells if the stylesheet is provided by the browser or not. * - * @return {boolean} true if this is a browser-provided stylesheet, or false + * @return {boolean} false if this is a browser-provided stylesheet, or true * otherwise. */ - get systemSheet() + get contentSheet() { - if (this._systemSheet === null) { - this._systemSheet = CssLogic.isSystemStyleSheet(this.domSheet); + if (this._contentSheet === null) { + this._contentSheet = CssLogic.isContentStylesheet(this.domSheet); } - return this._systemSheet; + return this._contentSheet; }, /** @@ -1048,7 +1042,7 @@ CssSheet.prototype = { this._sheetAllowed = true; let filter = this._cssLogic.sourceFilter; - if (filter === CssLogic.FILTER.ALL && this.systemSheet) { + if (filter === CssLogic.FILTER.ALL && !this.contentSheet) { this._sheetAllowed = false; } if (filter !== CssLogic.FILTER.ALL && filter !== CssLogic.FILTER.UA) { @@ -1202,13 +1196,13 @@ function CssRule(aCssSheet, aDomRule, aElement) this.line = this._cssSheet._cssLogic.domUtils.getRuleLine(this._domRule); this.source = this._cssSheet.shortSource + ":" + this.line; this.href = this._cssSheet.href; - this.systemRule = this._cssSheet.systemSheet; + this.contentRule = this._cssSheet.contentSheet; } else if (aElement) { this._selectors = [ new CssSelector(this, "@element.style") ]; this.line = -1; this.source = CssLogic.l10n("rule.sourceElement"); this.href = "#"; - this.systemRule = false; + this.contentRule = true; this.sourceElement = aElement; } } @@ -1396,12 +1390,12 @@ CssSelector.prototype = { /** * Check if the selector comes from a browser-provided stylesheet. * - * @return {boolean} true if the selector comes from a browser-provided + * @return {boolean} true if the selector comes from a content-provided * stylesheet, or false otherwise. */ - get systemRule() + get contentRule() { - return this._cssRule.systemRule; + return this._cssRule.contentRule; }, /** @@ -1794,12 +1788,12 @@ function CssSelectorInfo(aSelector, aProperty, aValue, aStatus) 4 important 5 inline important */ - let scorePrefix = this.systemRule ? 0 : 2; + let scorePrefix = this.contentRule ? 2 : 0; if (this.elementStyle) { scorePrefix++; } if (this.important) { - scorePrefix += this.systemRule ? 1 : 2; + scorePrefix += this.contentRule ? 2 : 1; } this.specificityScore = "" + scorePrefix + this.specificity.ids + @@ -1902,9 +1896,9 @@ CssSelectorInfo.prototype = { * @return {boolean} true if the selector comes from a browser-provided * stylesheet, or false otherwise. */ - get systemRule() + get contentRule() { - return this.selector.systemRule; + return this.selector.contentRule; }, /** @@ -1916,8 +1910,8 @@ CssSelectorInfo.prototype = { */ compareTo: function CssSelectorInfo_compareTo(aThat) { - if (this.systemRule && !aThat.systemRule) return 1; - if (!this.systemRule && aThat.systemRule) return -1; + if (!this.contentRule && aThat.contentRule) return 1; + if (this.contentRule && !aThat.contentRule) return -1; if (this.elementStyle && !aThat.elementStyle) { if (!this.important && aThat.important) return 1; diff --git a/browser/devtools/styleinspector/CssRuleView.jsm b/browser/devtools/styleinspector/CssRuleView.jsm index c12a160e216..f001b49f9d6 100644 --- a/browser/devtools/styleinspector/CssRuleView.jsm +++ b/browser/devtools/styleinspector/CssRuleView.jsm @@ -38,7 +38,7 @@ * * ***** END LICENSE BLOCK ***** */ -"use strict" +"use strict"; const Cc = Components.classes; const Ci = Components.interfaces; @@ -181,8 +181,8 @@ ElementStyle.prototype = { let domRule = domRules.GetElementAt(i); // XXX: Optionally provide access to system sheets. - let systemSheet = CssLogic.isSystemStyleSheet(domRule.parentStyleSheet); - if (systemSheet) { + let contentSheet = CssLogic.isContentStylesheet(domRule.parentStyleSheet); + if (!contentSheet) { continue; } @@ -324,7 +324,7 @@ ElementStyle.prototype = { aProp.overridden = overridden; return dirty; } -} +}; /** * A single style rule or declaration. @@ -358,11 +358,9 @@ Rule.prototype = { if (this._title) { return this._title; } - let sheet = this.domRule ? this.domRule.parentStyleSheet : null; - this._title = CssLogic.shortSource(sheet); + this._title = CssLogic.shortSource(this.sheet); if (this.domRule) { - let line = this.elementStyle.domUtils.getRuleLine(this.domRule); - this._title += ":" + line; + this._title += ":" + this.ruleLine; } if (this.inherited) { @@ -378,6 +376,26 @@ Rule.prototype = { return this._title; }, + /** + * The rule's stylesheet. + */ + get sheet() + { + return this.domRule ? this.domRule.parentStyleSheet : null; + }, + + /** + * The rule's line within a stylesheet + */ + get ruleLine() + { + if (!this.sheet) { + // No stylesheet, no ruleLine + return null; + } + return this.elementStyle.domUtils.getRuleLine(this.domRule); + }, + /** * Create a new TextProperty to include in the rule. * @@ -530,7 +548,7 @@ Rule.prototype = { this.textProps.push(textProp); } }, -} +}; /** * A single property in a rule's cssText. @@ -618,7 +636,7 @@ TextProperty.prototype = { { this.rule.removeProperty(this); } -} +}; /** @@ -643,7 +661,7 @@ TextProperty.prototype = { * apply to a given element. After construction, the 'element' * property will be available with the user interface. * - * @param Document aDocument + * @param Document aDoc * The document that will contain the rule view. * @param object aStore * The CSS rule view can use this object to store metadata @@ -655,7 +673,6 @@ function CssRuleView(aDoc, aStore) { this.doc = aDoc; this.store = aStore; - this.element = this.doc.createElementNS(XUL_NS, "vbox"); this.element.setAttribute("tabindex", "0"); this.element.classList.add("ruleview"); @@ -768,6 +785,14 @@ RuleEditor.prototype = { class: "ruleview-rule-source", textContent: this.rule.title }); + source.addEventListener("click", function() { + let rule = this.rule; + let evt = this.doc.createEvent("CustomEvent"); + evt.initCustomEvent("CssRuleViewCSSLinkClicked", true, false, { + rule: rule, + }); + this.element.dispatchEvent(evt); + }.bind(this)); let code = createChild(this.element, "div", { class: "ruleview-code" @@ -1094,8 +1119,6 @@ TextPropertyEditor.prototype = { _parseValue: function TextPropertyEditor_parseValue(aValue) { let pieces = aValue.split("!", 2); - let value = pieces[0]; - let priority = pieces.length > 1 ? pieces[1] : ""; return { value: pieces[0].trim(), priority: (pieces.length > 1 ? pieces[1].trim() : "") diff --git a/browser/devtools/styleinspector/csshtmltree.xul b/browser/devtools/styleinspector/csshtmltree.xul index e5c3bcc5a4c..1e4eee123ad 100644 --- a/browser/devtools/styleinspector/csshtmltree.xul +++ b/browser/devtools/styleinspector/csshtmltree.xul @@ -114,7 +114,7 @@ To visually debug the templates without running firefox, alter the display:none ${selector.humanReadableText(__element)} - ${selector.selectorInfo.source} diff --git a/browser/devtools/styleinspector/test/Makefile.in b/browser/devtools/styleinspector/test/Makefile.in index a4665bddc3d..e77fb1a6e2b 100644 --- a/browser/devtools/styleinspector/test/Makefile.in +++ b/browser/devtools/styleinspector/test/Makefile.in @@ -59,11 +59,19 @@ _BROWSER_TEST_FILES = \ browser_ruleview_manipulation.js \ browser_ruleview_override.js \ browser_ruleview_ui.js \ + browser_bug705707_is_content_stylesheet.js \ head.js \ $(NULL) _BROWSER_TEST_PAGES = \ browser_bug683672.html \ + browser_bug705707_is_content_stylesheet.html \ + browser_bug705707_is_content_stylesheet_imported.css \ + browser_bug705707_is_content_stylesheet_imported2.css \ + browser_bug705707_is_content_stylesheet_linked.css \ + browser_bug705707_is_content_stylesheet_script.css \ + browser_bug705707_is_content_stylesheet.xul \ + browser_bug705707_is_content_stylesheet_xul.css \ $(NULL) libs:: $(_BROWSER_TEST_FILES) diff --git a/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet.html b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet.html new file mode 100644 index 00000000000..96ef5b6e9e9 --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet.html @@ -0,0 +1,33 @@ + + + test + + + + + + + + + + + + +
+

Simple test

+
+ + diff --git a/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet.js b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet.js new file mode 100644 index 00000000000..fec06cf5620 --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet.js @@ -0,0 +1,100 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the correct stylesheets origins are identified in HTML & XUL +// stylesheets + +let doc; + +const TEST_URI = "http://example.com/browser/browser/devtools/styleinspector/" + + "test/browser_bug705707_is_content_stylesheet.html"; +const TEST_URI2 = "http://example.com/browser/browser/devtools/styleinspector/" + + "test/browser_bug705707_is_content_stylesheet.xul"; +const XUL_URI = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService) + .newURI(TEST_URI2, null, null); + +let tempScope = {}; +Cu.import("resource:///modules/devtools/CssLogic.jsm", tempScope); +let CssLogic = tempScope.CssLogic; + +function test() +{ + waitForExplicitFinish(); + addTab(TEST_URI); + browser.addEventListener("load", htmlLoaded, true); +} + +function htmlLoaded() +{ + browser.removeEventListener("load", htmlLoaded, true); + doc = content.document; + testFromHTML() +} + +function testFromHTML() +{ + let target = doc.querySelector("#target"); + + executeSoon(function() { + checkSheets(target); + gBrowser.removeCurrentTab(); + openXUL(); + }); +} + +function openXUL() +{ + Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager) + .add(XUL_URI, 'allowXULXBL', Ci.nsIPermissionManager.ALLOW_ACTION); + addTab(TEST_URI2); + browser.addEventListener("load", xulLoaded, true); +} + +function xulLoaded() +{ + browser.removeEventListener("load", xulLoaded, true); + doc = content.document; + testFromXUL() +} + +function testFromXUL() +{ + let target = doc.querySelector("#target"); + + executeSoon(function() { + checkSheets(target); + finishUp(); + }); +} + +function checkSheets(aTarget) +{ + let domUtils = Cc["@mozilla.org/inspector/dom-utils;1"] + .getService(Ci.inIDOMUtils); + let domRules = domUtils.getCSSStyleRules(aTarget); + + for (let i = 0, n = domRules.Count(); i < n; i++) { + let domRule = domRules.GetElementAt(i); + let sheet = domRule.parentStyleSheet; + let isContentSheet = CssLogic.isContentStylesheet(sheet); + + if (!sheet.href || + /browser_bug705707_is_content_stylesheet_/.test(sheet.href)) { + ok(isContentSheet, sheet.href + " identified as content stylesheet"); + } else { + ok(!isContentSheet, sheet.href + " identified as non-content stylesheet"); + } + } +} + +function finishUp() +{ + info("finishing up"); + Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager) + .add(XUL_URI, 'allowXULXBL', Ci.nsIPermissionManager.DENY_ACTION); + doc = null; + gBrowser.removeCurrentTab(); + finish(); +} diff --git a/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet.xul b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet.xul new file mode 100644 index 00000000000..abbd03030e8 --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet.xul @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_imported.css b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_imported.css new file mode 100644 index 00000000000..4092afeea39 --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_imported.css @@ -0,0 +1,5 @@ +@import url("./browser_bug705707_is_content_stylesheet_imported2.css"); + +#target { + text-decoration: underline; +} diff --git a/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_imported2.css b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_imported2.css new file mode 100644 index 00000000000..77c73299ea9 --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_imported2.css @@ -0,0 +1,3 @@ +#target { + text-decoration: underline; +} diff --git a/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_linked.css b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_linked.css new file mode 100644 index 00000000000..712ba78fb6f --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_linked.css @@ -0,0 +1,3 @@ +table { + border-collapse: collapse; +} diff --git a/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_script.css b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_script.css new file mode 100644 index 00000000000..bf4fc8ddcb8 --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_script.css @@ -0,0 +1,5 @@ +@import url("./browser_bug705707_is_content_stylesheet_imported.css"); + +table { + opacity: 1; +} diff --git a/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_xul.css b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_xul.css new file mode 100644 index 00000000000..a14ae7f6fdb --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_xul.css @@ -0,0 +1,3 @@ +#target { + font-size: 200px; +} diff --git a/browser/devtools/webconsole/GcliCommands.jsm b/browser/devtools/webconsole/GcliCommands.jsm index 31532b9e0ae..07dbf13d82d 100644 --- a/browser/devtools/webconsole/GcliCommands.jsm +++ b/browser/devtools/webconsole/GcliCommands.jsm @@ -126,6 +126,40 @@ gcli.addCommand({ } }); +/** + * 'edit' command + */ +gcli.addCommand({ + name: "edit", + description: gcli.lookup("editDesc"), + manual: gcli.lookup("editManual"), + params: [ + { + name: 'resource', + type: { + name: 'resource', + include: 'text/css' + }, + description: gcli.lookup("editResourceDesc") + }, + { + name: "line", + defaultValue: 1, + type: { + name: "number", + min: 1, + step: 10 + }, + description: gcli.lookup("editLineToJumpToDesc") + } + ], + exec: function(args, context) { + let hud = HUDService.getHudReferenceById(context.environment.hudId); + let StyleEditor = hud.gcliterm.document.defaultView.StyleEditor; + StyleEditor.openChrome(args.resource.element, args.line); + } +}); + let breakpoints = []; /** diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 98f687c2dac..2512344b9f7 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -150,9 +150,6 @@ @BINPATH@/components/dom_system_b2g.xpt #endif @BINPATH@/components/dom_battery.xpt -#ifdef MOZ_B2G_BT -@BINPATH@/components/dom_bluetooth.xpt -#endif @BINPATH@/components/dom_canvas.xpt @BINPATH@/components/dom_core.xpt @BINPATH@/components/dom_css.xpt diff --git a/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties index bfa6a92669b..f4a1ce01820 100644 --- a/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties +++ b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties @@ -129,3 +129,23 @@ breakdelRemoved=Breakpoint removed # 'console close' command. This string is designed to be shown in a menu # alongside the command name, which is why it should be as short as possible. consolecloseDesc=Close the console + +# LOCALIZATION NOTE (editDesc) A very short description of the 'edit' +# command. See editManual for a fuller description of what it does. This +# string is designed to be shown in a menu alongside the command name, which +# is why it should be as short as possible. +editDesc=Tweak a page resource + +# LOCALIZATION NOTE (editManual) A fuller description of the 'edit' command, +# displayed when the user asks for help on what it does. +editManual=Edit one of the resources that is part of this page (or maybe any generic web resource?) + +# LOCALIZATION NOTE (editResourceDesc) A very short string to describe the +# 'resource' parameter to the 'edit' command, which is displayed in a dialog +# when the user is using this command. +editResourceDesc=URL to edit + +# LOCALIZATION NOTE (editLineToJumpToDesc) A very short string to describe the +# 'line' parameter to the 'edit' command, which is displayed in a dialog +# when the user is using this command. +editLineToJumpToDesc=Line to jump to diff --git a/browser/makefiles.sh b/browser/makefiles.sh index e5bc351d0ba..d76045f8cfa 100644 --- a/browser/makefiles.sh +++ b/browser/makefiles.sh @@ -88,6 +88,12 @@ browser/themes/Makefile $MOZ_BRANDING_DIRECTORY/Makefile $MOZ_BRANDING_DIRECTORY/content/Makefile $MOZ_BRANDING_DIRECTORY/locales/Makefile +toolkit/locales/Makefile +extensions/spellcheck/locales/Makefile +intl/locales/Makefile +netwerk/locales/Makefile +dom/locales/Makefile +security/manager/locales/Makefile " if [ "$MOZ_SAFE_BROWSING" ]; then diff --git a/browser/themes/gnomestripe/devtools/csshtmltree.css b/browser/themes/gnomestripe/devtools/csshtmltree.css index d15f6268508..8e19dcf2920 100644 --- a/browser/themes/gnomestripe/devtools/csshtmltree.css +++ b/browser/themes/gnomestripe/devtools/csshtmltree.css @@ -68,6 +68,9 @@ .helplink:visited { text-decoration: none; } +.link:hover { + text-decoration: underline; +} .helplink { display: block; @@ -135,6 +138,7 @@ .rule-link { text-align: end; -moz-padding-start: 10px; + cursor: pointer; } /* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */ @@ -200,7 +204,13 @@ .ruleview-rule-source { background-color: -moz-dialog; + color: #0091ff; padding: 2px 5px; + cursor: pointer; +} + +.ruleview-rule-source:hover { + text-decoration: underline; } .ruleview-code { diff --git a/browser/themes/pinstripe/devtools/csshtmltree.css b/browser/themes/pinstripe/devtools/csshtmltree.css index d5b4c32b5ae..316b6d0e530 100644 --- a/browser/themes/pinstripe/devtools/csshtmltree.css +++ b/browser/themes/pinstripe/devtools/csshtmltree.css @@ -68,6 +68,9 @@ .helplink:visited { text-decoration: none; } +.link:hover { + text-decoration: underline; +} .helplink { display: block; @@ -137,6 +140,7 @@ .rule-link { text-align: end; -moz-padding-start: 10px; + cursor: pointer; } /* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */ @@ -202,7 +206,13 @@ .ruleview-rule-source { background-color: -moz-dialog; + color: #0091ff; padding: 2px 5px; + cursor: pointer; +} + +.ruleview-rule-source:hover { + text-decoration: underline; } .ruleview-code { diff --git a/browser/themes/winstripe/devtools/csshtmltree.css b/browser/themes/winstripe/devtools/csshtmltree.css index 1d2b8ea5dae..459a11af4c9 100644 --- a/browser/themes/winstripe/devtools/csshtmltree.css +++ b/browser/themes/winstripe/devtools/csshtmltree.css @@ -67,6 +67,9 @@ .helplink:visited { text-decoration: none; } +.link:hover { + text-decoration: underline; +} .helplink { display: block; @@ -135,6 +138,7 @@ .rule-link { text-align: end; -moz-padding-start: 10px; + cursor: pointer; } /* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */ @@ -200,7 +204,13 @@ .ruleview-rule-source { background-color: -moz-dialog; + color: #0091ff; padding: 2px 5px; + cursor: pointer; +} + +.ruleview-rule-source:hover { + text-decoration: underline; } .ruleview-code { diff --git a/build/Makefile.in b/build/Makefile.in index 0b592470628..f50833dd8d3 100644 --- a/build/Makefile.in +++ b/build/Makefile.in @@ -84,7 +84,7 @@ DEFINES += -DGRE_MILESTONE=$(GRE_MILESTONE) -DAPP_BUILDID=$(APP_BUILDID) DEFINES += -DMOZ_APP_VERSION="$(MOZ_APP_VERSION)" APP_INI_DEPS += $(DEPTH)/config/autoconf.mk -MOZ_SOURCE_STAMP ?= $(firstword $(shell hg -R $(topsrcdir)/$(MOZ_BUILD_APP)/.. parent --template="{node|short}\n" 2>/dev/null)) +MOZ_SOURCE_STAMP := $(firstword $(shell cd $(topsrcdir)/$(MOZ_BUILD_APP)/.. && hg parent --template="{node|short}\n" 2>/dev/null)) ifdef MOZ_SOURCE_STAMP DEFINES += -DMOZ_SOURCE_STAMP="$(MOZ_SOURCE_STAMP)" endif diff --git a/build/autoconf/compiler-opts.m4 b/build/autoconf/compiler-opts.m4 new file mode 100644 index 00000000000..0c2fd2a0b1a --- /dev/null +++ b/build/autoconf/compiler-opts.m4 @@ -0,0 +1,13 @@ +dnl Add compiler specific options + +AC_DEFUN([MOZ_COMPILER_OPTS], +[ +if test "$CLANG_CXX"; then + ## We disable return-type-c-linkage because jsval is defined as a C++ type but is + ## returned by C functions. This is possible because we use knowledge about the ABI + ## to typedef it to a C type with the same layout when the headers are included + ## from C. + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-return-type-c-linkage" +fi +]) + diff --git a/build/unix/build-toolchain/build-gcc.py b/build/unix/build-toolchain/build-gcc.py index 3b46b6742e3..496e6bce939 100755 --- a/build/unix/build-toolchain/build-gcc.py +++ b/build/unix/build-toolchain/build-gcc.py @@ -1,5 +1,18 @@ #!/usr/bin/python +# The directories end up in the debug info, so the easy way of getting +# a reproducible build is to run it in a know absolute directory. +# We use a directory in /builds/slave because the mozilla infrastructure +# cleans it up automatically. +base_dir = "/builds/slave/moz-toolschain" + +source_dir = base_dir + "/src" +build_dir = base_dir + "/build" +aux_inst_dir = build_dir + '/aux_inst' +old_make = aux_inst_dir + '/bin/make' + +############################################## + import urllib import os import os.path @@ -33,17 +46,21 @@ def patch(patch, plevel, srcdir): check_run(['patch', '-d', srcdir, '-p%s' % plevel, '-i', patch, '--fuzz=0', '-s']) -def build_package(package_source_dir, package_build_dir, configure_args): +def build_package(package_source_dir, package_build_dir, configure_args, + make = old_make): os.mkdir(package_build_dir) run_in(package_build_dir, ["%s/configure" % package_source_dir] + configure_args) - run_in(package_build_dir, ["make", "-j8"]) - run_in(package_build_dir, ["make", "install"]) + run_in(package_build_dir, [make, "-j8"]) + run_in(package_build_dir, [make, "install"]) -def build_tar(base_dir, tar_inst_dir): +def build_aux_tools(base_dir): + make_build_dir = base_dir + '/make_build' + build_package(make_source_dir, make_build_dir, + ["--prefix=%s" % aux_inst_dir], "make") tar_build_dir = base_dir + '/tar_build' build_package(tar_source_dir, tar_build_dir, - ["--prefix=%s" % tar_inst_dir]) + ["--prefix=%s" % aux_inst_dir]) def with_env(env, f): old_env = os.environ.copy() @@ -133,21 +150,13 @@ def build_tar_package(tar, name, base, directory): ############################################## -# The directories end up in the debug info, so the easy way of getting -# a reproducible build is to run it in a know absolute directory. -# We use a directory in /builds/slave because the mozilla infrastructure -# cleans it up automatically. -base_dir = "/builds/slave/moz-toolschain" - -source_dir = base_dir + "/src" -build_dir = base_dir + "/build" - def build_source_dir(prefix, version): return source_dir + '/' + prefix + version binutils_version = "2.21.1" -glibc_version = "2.12.2" #FIXME: should probably use 2.5.1 +glibc_version = "2.5.1" tar_version = "1.26" +make_version = "3.81" gcc_version = "4.5.2" mpfr_version = "2.4.2" gmp_version = "5.0.1" @@ -159,6 +168,8 @@ glibc_source_uri = "http://ftp.gnu.org/gnu/glibc/glibc-%s.tar.bz2" % \ glibc_version tar_source_uri = "http://ftp.gnu.org/gnu/tar/tar-%s.tar.bz2" % \ tar_version +make_source_uri = "http://ftp.gnu.org/gnu/make/make-%s.tar.bz2" % \ + make_version gcc_source_uri = "http://ftp.gnu.org/gnu/gcc/gcc-%s/gcc-%s.tar.bz2" % \ (gcc_version, gcc_version) mpfr_source_uri = "http://www.mpfr.org/mpfr-%s/mpfr-%s.tar.bz2" % \ @@ -170,6 +181,7 @@ mpc_source_uri = "http://www.multiprecision.org/mpc/download/mpc-%s.tar.gz" % \ binutils_source_tar = download_uri(binutils_source_uri) glibc_source_tar = download_uri(glibc_source_uri) tar_source_tar = download_uri(tar_source_uri) +make_source_tar = download_uri(make_source_uri) mpc_source_tar = download_uri(mpc_source_uri) mpfr_source_tar = download_uri(mpfr_source_uri) gmp_source_tar = download_uri(gmp_source_uri) @@ -178,6 +190,7 @@ gcc_source_tar = download_uri(gcc_source_uri) binutils_source_dir = build_source_dir('binutils-', binutils_version) glibc_source_dir = build_source_dir('glibc-', glibc_version) tar_source_dir = build_source_dir('tar-', tar_version) +make_source_dir = build_source_dir('make-', make_version) mpc_source_dir = build_source_dir('mpc-', mpc_version) mpfr_source_dir = build_source_dir('mpfr-', mpfr_version) gmp_source_dir = build_source_dir('gmp-', gmp_version) @@ -191,6 +204,7 @@ if not os.path.exists(source_dir): patch('glibc-deterministic.patch', 1, glibc_source_dir) run_in(glibc_source_dir, ["autoconf"]) extract(tar_source_tar, source_dir) + extract(make_source_tar, source_dir) extract(mpc_source_tar, source_dir) extract(mpfr_source_tar, source_dir) extract(gmp_source_tar, source_dir) @@ -203,19 +217,18 @@ if os.path.exists(build_dir): shutil.rmtree(build_dir) os.makedirs(build_dir) -tar_inst_dir = build_dir + '/tar_inst' -build_tar(build_dir, tar_inst_dir) +build_aux_tools(build_dir) stage1_dir = build_dir + '/stage1' build_one_stage({"CC": "gcc", "CXX" : "g++"}, stage1_dir, True) stage1_tool_inst_dir = stage1_dir + '/inst' stage2_dir = build_dir + '/stage2' -build_one_stage({"CC" : stage1_tool_inst_dir + "/bin/gcc", +build_one_stage({"CC" : stage1_tool_inst_dir + "/bin/gcc -fgnu89-inline", "CXX" : stage1_tool_inst_dir + "/bin/g++", "AR" : stage1_tool_inst_dir + "/bin/ar", "RANLIB" : "true" }, stage2_dir, False) -build_tar_package(tar_inst_dir + "/bin/tar", +build_tar_package(aux_inst_dir + "/bin/tar", "toolchain.tar", stage2_dir, "inst") diff --git a/build/unix/build-toolchain/glibc-deterministic.patch b/build/unix/build-toolchain/glibc-deterministic.patch index 016fbf1ea72..bc5a11f0920 100644 --- a/build/unix/build-toolchain/glibc-deterministic.patch +++ b/build/unix/build-toolchain/glibc-deterministic.patch @@ -1,7 +1,22 @@ diff -ru a/configure.in b/configure.in --- a/configure.in 2011-01-17 23:34:07.000000000 -0500 +++ b/configure.in 2012-01-25 20:40:27.919485606 -0500 -@@ -2230,6 +2230,7 @@ +@@ -841,14 +841,6 @@ + LIBC_PROG_BINUTILS + AC_SUBST(MIG)dnl Needed by sysdeps/mach/configure.in + +-# Accept binutils 2.13 or newer. +-AC_CHECK_PROG_VER(AS, $AS, --version, +- [GNU assembler.* \([0-9]*\.[0-9.]*\)], +- [2.1[3-9]*], AS=: critic_missing="$critic_missing as") +-AC_CHECK_PROG_VER(LD, $LD, --version, +- [GNU ld.* \([0-9][0-9]*\.[0-9.]*\)], +- [2.1[3-9]*], LD=: critic_missing="$critic_missing ld") +- + # We need the physical current working directory. We cannot use the + # "pwd -P" shell builtin since that's not portable. Instead we try to + # find a pwd binary. Note that assigning to the PWD environment +@@ -2175,6 +2167,7 @@ fi AC_SUBST(old_glibc_headers) @@ -12,7 +27,7 @@ diff -ru a/configure.in b/configure.in diff -ru a/csu/Makefile b/csu/Makefile --- a/csu/Makefile 2011-01-17 23:34:07.000000000 -0500 +++ b/csu/Makefile 2012-01-23 13:58:28.957792633 -0500 -@@ -234,8 +234,7 @@ +@@ -223,8 +223,7 @@ if [ -z "$$os" ]; then \ os=Linux; \ fi; \ @@ -22,10 +37,58 @@ diff -ru a/csu/Makefile b/csu/Makefile *) ;; \ esac; \ files="$(all-Banner-files)"; \ +diff -ru a/elf/Makefile b/elf/Makefile +--- a/elf/Makefile 2008-10-31 16:35:11.000000000 -0400 ++++ b/elf/Makefile 2012-02-16 12:20:00.038593752 -0500 +@@ -295,18 +295,11 @@ + z-now-yes = -Wl,-z,now + + $(objpfx)ld.so: $(objpfx)librtld.os $(ld-map) +- @rm -f $@.lds +- $(LINK.o) -nostdlib -nostartfiles -shared $(z-now-$(bind-now)) \ +- $(LDFLAGS-rtld) -Wl,-z,defs -Wl,--verbose 2>&1 | \ +- LC_ALL=C \ +- sed -e '/^=========/,/^=========/!d;/^=========/d' \ +- -e 's/\. = 0 + SIZEOF_HEADERS;/& _begin = . - SIZEOF_HEADERS;/' \ +- > $@.lds + $(LINK.o) -nostdlib -nostartfiles -shared -o $@ \ + $(LDFLAGS-rtld) -Wl,-z,defs $(z-now-$(bind-now)) \ + $(filter-out $(map-file),$^) $(load-map-file) \ +- -Wl,-soname=$(rtld-installed-name) -T $@.lds +- rm -f $@.lds ++ -Wl,-soname=$(rtld-installed-name) \ ++ -Wl,-defsym=_begin=0 + + # interp.c exists just to get this string into the libraries. + CFLAGS-interp.c = -D'RUNTIME_LINKER="$(slibdir)/$(rtld-installed-name)"' \ +diff -ru a/localedata/Makefile b/localedata/Makefile +--- a/localedata/Makefile 2006-04-26 01:14:03.000000000 -0400 ++++ b/localedata/Makefile 2012-02-17 10:31:24.592345047 -0500 +@@ -113,7 +113,7 @@ + $(make-target-directory) + rm -f $(@:.gz=) $@ + $(INSTALL_DATA) $< $(@:.gz=) +- gzip -9 $(@:.gz=) ++ gzip -9n $(@:.gz=) + + # Install the locale source files in the appropriate directory. + $(inst_i18ndir)/locales/%: locales/% $(+force); $(do-install) +diff -ru a/Makeconfig b/Makeconfig +--- a/Makeconfig 2006-07-10 17:42:27.000000000 -0400 ++++ b/Makeconfig 2012-02-17 08:28:31.859584817 -0500 +@@ -674,7 +674,7 @@ + $(foreach lib,$(libof-$(basename $(@F))) \ + $(libof-$(= r6b +dnl creates some (http://code.google.com/p/android/issues/detail?id=23203) +dnl We however want to avoid these text relocations, and this can be done +dnl by making gcc not link crtbegin and crtend. In the broken NDKs, crtend +dnl doesn't contain anything at all, beside placeholders for some sections, +dnl and crtbegin only contains a finalizer function that calls +dnl __cxa_finalize. The custom linker actually takes care of calling +dnl __cxa_finalize when the library doesn't call it itself, which makes it +dnl safe not to link crtbegin. Besides, previous versions of the NDK didn't +dnl link crtbegin and crtend at all. +if test -n "$MOZ_LINKER" -a -z "$MOZ_OLD_LINKER" -a "$OS_TARGET" = "Android"; then + AC_CACHE_CHECK([whether the CRT objects have text relocations], + ac_cv_crt_has_text_relocations, + [echo 'int foo() { return 0; }' > conftest.cpp + if AC_TRY_COMMAND(${CXX-g++} -o conftest${DLL_SUFFIX} $CXXFLAGS $DSO_LDOPTS $LDFLAGS conftest.cpp $LIBS 1>&5) && + test -s conftest${DLL_SUFFIX}; then + if readelf -d conftest${DLL_SUFFIX} | grep TEXTREL > /dev/null; then + ac_cv_crt_has_text_relocations=yes + else + ac_cv_crt_has_text_relocations=no + fi + else + AC_ERROR([couldn't compile a simple C file]) + fi + rm -rf conftest*]) + if test "$ac_cv_crt_has_text_relocations" = yes; then + dnl While we want libraries to skip the CRT files, we don't want + dnl executables to be treated the same way. We thus set the flag + dnl in DSO_LDOPTS and not LDFLAGS. However, to pass it to nspr, + dnl we need to use LDFLAGS because nspr doesn't inherit DSO_LDOPTS. + dnl Using LDFLAGS in nspr is safe, since we only really build + dnl libraries there. + DSO_LDOPTS="$DSO_LDOPTS -nostartfiles" + NSPR_LDFLAGS=-nostartfiles + fi +fi + dnl Check for the existence of various allocation headers/functions MALLOC_H= @@ -4612,6 +4663,7 @@ LIBJPEG_TURBO_AS= LIBJPEG_TURBO_ASFLAGS= LIBJPEG_TURBO_X86_ASM= LIBJPEG_TURBO_X64_ASM= +LIBJPEG_TURBO_ARM_ASM= MOZ_PANGO=1 MOZ_PERMISSIONS=1 MOZ_PLACES=1 @@ -4927,7 +4979,6 @@ cairo-gonk) MOZ_PDF_PRINTING=1 MOZ_B2G_RIL=1 MOZ_TOUCH=1 - MOZ_B2G_BT=1 ;; esac @@ -6171,38 +6222,51 @@ if test -n "$MOZ_LIBJPEG_TURBO"; then LIBJPEG_TURBO_ASFLAGS="-f win64 -rnasm -pnasm -D__x86_64__ -DPIC -DWIN64 -DMSVC" LIBJPEG_TURBO_X64_ASM=1 ;; + *:arm*) + LIBJPEG_TURBO_ASFLAGS="-march=armv7-a -mfpu=neon" + LIBJPEG_TURBO_ARM_ASM=1 + ;; esac fi -dnl If we're on a system which supports libjpeg-turbo's asm routines and -dnl --disable-libjpeg-turbo wasn't passed, check for yasm, and error out if it -dnl doesn't exist or we have too old of a version. +dnl If we're on an x86 or x64 system which supports libjpeg-turbo's asm routines +dnl and --disable-libjpeg-turbo wasn't passed, check for Yasm, and error out if +dnl it doesn't exist or we have too old of a version. if test -n "$LIBJPEG_TURBO_X86_ASM" -o -n "$LIBJPEG_TURBO_X64_ASM" ; then - AC_MSG_CHECKING([for YASM assembler]) + AC_MSG_CHECKING([for Yasm assembler]) AC_CHECK_PROGS(LIBJPEG_TURBO_AS, yasm, "") if test -z "$LIBJPEG_TURBO_AS" ; then - AC_MSG_ERROR([yasm is required to build with libjpeg-turbo's optimized JPEG decoding routines, but you do not appear to have yasm installed. Either install it or configure with --disable-libjpeg-turbo to use the pure C JPEG decoder. See https://developer.mozilla.org/en/YASM for more details.]) + AC_MSG_ERROR([Yasm is required to build with libjpeg-turbo's optimized JPEG decoding routines, but you do not appear to have Yasm installed. Either install it or configure with --disable-libjpeg-turbo to use the pure C JPEG decoder. See https://developer.mozilla.org/en/YASM for more details.]) fi dnl Check that we have the right yasm version. We require 1.0.1 or newer dnl on Linux and 1.1 or newer everywhere else. if test "$OS_ARCH" = "Linux" ; then if test "$_YASM_MAJOR_VERSION" -lt "1" -o \( "$_YASM_MAJOR_VERSION" -eq "1" -a "$_YASM_MINOR_VERSION" -eq "0" -a "$_YASM_RELEASE" -lt "1" \) ; then - AC_MSG_ERROR([yasm 1.0.1 or greater is required to build with libjpeg-turbo's optimized JPEG decoding routines, but you appear to have version $_YASM_MAJOR_VERSION.$_YASM_MINOR_VERSION.$_YASM_RELEASE. Upgrade to the newest version or configure with --disable-libjpeg-turbo to use the pure C JPEG decoder. See https://developer.mozilla.org/en/YASM for more details.]) + AC_MSG_ERROR([Yasm 1.0.1 or greater is required to build with libjpeg-turbo's optimized JPEG decoding routines, but you appear to have version $_YASM_MAJOR_VERSION.$_YASM_MINOR_VERSION.$_YASM_RELEASE. Upgrade to the newest version or configure with --disable-libjpeg-turbo to use the pure C JPEG decoder. See https://developer.mozilla.org/en/YASM for more details.]) fi else if test "$_YASM_MAJOR_VERSION" -lt "1" -o \( "$_YASM_MAJOR_VERSION" -eq "1" -a "$_YASM_MINOR_VERSION" -lt "1" \) ; then - AC_MSG_ERROR([yasm 1.1 or greater is required to build with libjpeg-turbo's optimized JPEG decoding routines, but you appear to have version $_YASM_MAJOR_VERSION.$_YASM_MINOR_VERSION. Upgrade to the newest version or configure with --disable-libjpeg-turbo to use the pure C JPEG decoder. See https://developer.mozilla.org/en/YASM for more details.]) + AC_MSG_ERROR([Yasm 1.1 or greater is required to build with libjpeg-turbo's optimized JPEG decoding routines, but you appear to have version $_YASM_MAJOR_VERSION.$_YASM_MINOR_VERSION. Upgrade to the newest version or configure with --disable-libjpeg-turbo to use the pure C JPEG decoder. See https://developer.mozilla.org/en/YASM for more details.]) fi fi fi +dnl If we're on an ARM system which supports libjpeg-turbo's asm routines and +dnl --disable-libjpeg-turbo wasn't passed, use the C compiler as the assembler. +if test -n "$LIBJPEG_TURBO_ARM_ASM" ; then + echo "Using $AS as the assembler for ARM code." + LIBJPEG_TURBO_AS=$AS +fi + if test -n "$LIBJPEG_TURBO_X86_ASM"; then AC_DEFINE(LIBJPEG_TURBO_X86_ASM) elif test -n "$LIBJPEG_TURBO_X64_ASM"; then AC_DEFINE(LIBJPEG_TURBO_X64_ASM) +elif test -n "$LIBJPEG_TURBO_ARM_ASM"; then + AC_DEFINE(LIBJPEG_TURBO_ARM_ASM) elif test -n "$MOZ_LIBJPEG_TURBO"; then dnl Warn if we're not building the optimized routines, even though the user dnl didn't specify --disable-libjpeg-turbo. @@ -7598,18 +7662,6 @@ if test -n "$MOZ_B2G_RIL"; then fi AC_SUBST(MOZ_B2G_RIL) -dnl ======================================================== -dnl = Enable Bluetooth Interface for B2G (Gonk usually) -dnl ======================================================== -MOZ_ARG_ENABLE_BOOL(b2g-bt, -[ --enable-b2g-bt Set compile flags necessary for compiling Bluetooth API for B2G ], - MOZ_B2G_BT=1, - MOZ_B2G_BT= ) -if test -n "$MOZ_B2G_BT"; then - AC_DEFINE(MOZ_B2G_BT) -fi -AC_SUBST(MOZ_B2G_BT) - dnl ======================================================== dnl = Support for demangling undefined symbols dnl ======================================================== @@ -8585,8 +8637,6 @@ AC_SUBST(MOZ_APP_VERSION) AC_DEFINE_UNQUOTED(MOZ_UA_FIREFOX_VERSION, "$FIREFOX_VERSION") AC_DEFINE_UNQUOTED(FIREFOX_VERSION,$FIREFOX_VERSION) AC_SUBST(FIREFOX_VERSION) -AC_DEFINE_UNQUOTED(MOZ_UA_BUILDID, "$MOZ_UA_BUILDID") -AC_SUBST(MOZ_UA_BUILDID) # We can't use the static application.ini data when building against # a libxul SDK. @@ -8756,6 +8806,7 @@ AC_SUBST(LIBJPEG_TURBO_AS) AC_SUBST(LIBJPEG_TURBO_ASFLAGS) AC_SUBST(LIBJPEG_TURBO_X86_ASM) AC_SUBST(LIBJPEG_TURBO_X64_ASM) +AC_SUBST(LIBJPEG_TURBO_ARM_ASM) AC_MSG_CHECKING([for posix_fallocate]) AC_TRY_LINK([#define _XOPEN_SOURCE 600 @@ -9090,7 +9141,11 @@ if test -z "$MOZ_NATIVE_NSPR"; then _SAVE_CPPFLAGS="$CPPFLAGS" export CPPFLAGS="-include $_topsrcdir/mozglue/linker/dladdr.h $CPPFLAGS" fi + _SAVE_LDFLAGS="$LDFLAGS" + export LDFLAGS="$LDFLAGS $NSPR_LDFLAGS" AC_OUTPUT_SUBDIRS(nsprpub) + unset LDFLAGS + LDFLAGS="$_SAVE_LDFLAGS" if test -n "$MOZ_LINKER" -a -z "$MOZ_OLD_LINKER" -a "$ac_cv_func_dladdr" = no; then unset CPPFLAGS CPPFLAGS="$_SAVE_CFLAGS" diff --git a/content/base/crashtests/700512-worker.js b/content/base/crashtests/700512-worker.js index 83b732ab570..fcb558fcf45 100644 --- a/content/base/crashtests/700512-worker.js +++ b/content/base/crashtests/700512-worker.js @@ -1,7 +1,7 @@ onmessage = function(event) { var blob = event.data; - blob.mozSlice(1, 5); + blob.slice(1, 5); postMessage("done"); } diff --git a/content/base/public/nsIDOMFile.idl b/content/base/public/nsIDOMFile.idl index 6d47a5fc5b8..7c9913fe203 100644 --- a/content/base/public/nsIDOMFile.idl +++ b/content/base/public/nsIDOMFile.idl @@ -71,9 +71,9 @@ interface nsIDOMBlob : nsISupports // blob: protocol handler [noscript] DOMString getInternalUrl(in nsIPrincipal principal); - [optional_argc] nsIDOMBlob mozSlice([optional] in long long start, - [optional] in long long end, - [optional] in DOMString contentType); + [optional_argc] nsIDOMBlob slice([optional] in long long start, + [optional] in long long end, + [optional] in DOMString contentType); // Get internal id of stored file. Returns -1 if it is not a stored file. // Intended only for testing. It can be called on any thread. diff --git a/content/base/src/nsContentAreaDragDrop.cpp b/content/base/src/nsContentAreaDragDrop.cpp index 7190bb4fd37..ec8b803137b 100644 --- a/content/base/src/nsContentAreaDragDrop.cpp +++ b/content/base/src/nsContentAreaDragDrop.cpp @@ -48,6 +48,7 @@ #include "nsCopySupport.h" #include "nsIDOMUIEvent.h" #include "nsISelection.h" +#include "nsISelectionController.h" #include "nsIDOMNode.h" #include "nsIDOMNodeList.h" #include "nsIDOMEvent.h" @@ -70,6 +71,7 @@ #include "nsIDocShell.h" #include "nsIContent.h" #include "nsIImageLoadingContent.h" +#include "nsITextControlElement.h" #include "nsUnicharUtils.h" #include "nsIURL.h" #include "nsIDocument.h" @@ -84,37 +86,6 @@ #include "imgIRequest.h" #include "nsDOMDataTransfer.h" -// private clipboard data flavors for html copy, used by editor when pasting -#define kHTMLContext "text/_moz_htmlcontext" -#define kHTMLInfo "text/_moz_htmlinfo" - -// if aNode is null, use the selection from the window -static nsresult -GetTransferableForNodeOrSelection(nsIDOMWindow* aWindow, - nsIContent* aNode, - nsITransferable** aTransferable) -{ - NS_ENSURE_ARG_POINTER(aWindow); - - nsCOMPtr domDoc; - aWindow->GetDocument(getter_AddRefs(domDoc)); - NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE); - nsCOMPtr doc = do_QueryInterface(domDoc); - - nsresult rv; - if (aNode) { - rv = nsCopySupport::GetTransferableForNode(aNode, doc, aTransferable); - } else { - nsCOMPtr selection; - aWindow->GetSelection(getter_AddRefs(selection)); - rv = nsCopySupport::GetTransferableForSelection(selection, doc, - aTransferable); - } - - NS_ENSURE_SUCCESS(rv, rv); - return rv; -} - class NS_STACK_CLASS DragDataProducer { public: @@ -124,7 +95,7 @@ public: bool aIsAltKeyPressed); nsresult Produce(nsDOMDataTransfer* aDataTransfer, bool* aCanDrag, - bool* aDragSelection, + nsISelection** aSelection, nsIContent** aDragNode); private: @@ -172,7 +143,7 @@ nsContentAreaDragDrop::GetDragData(nsIDOMWindow* aWindow, bool aIsAltKeyPressed, nsDOMDataTransfer* aDataTransfer, bool* aCanDrag, - bool* aDragSelection, + nsISelection** aSelection, nsIContent** aDragNode) { NS_ENSURE_TRUE(aSelectionTargetNode, NS_ERROR_INVALID_ARG); @@ -181,7 +152,7 @@ nsContentAreaDragDrop::GetDragData(nsIDOMWindow* aWindow, DragDataProducer provider(aWindow, aTarget, aSelectionTargetNode, aIsAltKeyPressed); - return provider.Produce(aDataTransfer, aCanDrag, aDragSelection, aDragNode); + return provider.Produce(aDataTransfer, aCanDrag, aSelection, aDragNode); } @@ -412,10 +383,10 @@ DragDataProducer::GetNodeString(nsIContent* inNode, nsresult DragDataProducer::Produce(nsDOMDataTransfer* aDataTransfer, bool* aCanDrag, - bool* aDragSelection, + nsISelection** aSelection, nsIContent** aDragNode) { - NS_PRECONDITION(aCanDrag && aDragSelection && aDataTransfer && aDragNode, + NS_PRECONDITION(aCanDrag && aSelection && aDataTransfer && aDragNode, "null pointer passed to Produce"); NS_ASSERTION(mWindow, "window not set"); NS_ASSERTION(mSelectionTargetNode, "selection target node should have been set"); @@ -424,33 +395,72 @@ DragDataProducer::Produce(nsDOMDataTransfer* aDataTransfer, nsresult rv; nsIContent* dragNode = nsnull; + *aSelection = nsnull; - // find the selection to see what we could be dragging and if - // what we're dragging is in what is selected. + // Find the selection to see what we could be dragging and if what we're + // dragging is in what is selected. If this is an editable textbox, use + // the textbox's selection, otherwise use the window's selection. nsCOMPtr selection; - mWindow->GetSelection(getter_AddRefs(selection)); - if (!selection) { - return NS_OK; - } - - // check if the node is inside a form control. If so, dragging will be - // handled in editor code (nsPlaintextDataTransfer::DoDrag). Don't set - // aCanDrag to false however, as we still want to allow the drag. - nsCOMPtr findFormNode = mSelectionTargetNode; - nsIContent* findFormParent = findFormNode->GetParent(); - while (findFormParent) { - nsCOMPtr form(do_QueryInterface(findFormParent)); - if (form && !form->AllowDraggableChildren()) { - return NS_OK; + nsIContent* editingElement = mSelectionTargetNode->IsEditable() ? + mSelectionTargetNode->GetEditingHost() : nsnull; + nsCOMPtr textControl(do_QueryInterface(editingElement)); + if (textControl) { + nsISelectionController* selcon = textControl->GetSelectionController(); + if (selcon) { + selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection)); + if (!selection) + return NS_OK; + } + } + else { + mWindow->GetSelection(getter_AddRefs(selection)); + if (!selection) + return NS_OK; + + // Check if the node is inside a form control. Don't set aCanDrag to false + //however, as we still want to allow the drag. + nsCOMPtr findFormNode = mSelectionTargetNode; + nsIContent* findFormParent = findFormNode->GetParent(); + while (findFormParent) { + nsCOMPtr form(do_QueryInterface(findFormParent)); + if (form && !form->AllowDraggableChildren()) { + return NS_OK; + } + findFormParent = findFormParent->GetParent(); } - findFormParent = findFormParent->GetParent(); } // if set, serialize the content under this node nsCOMPtr nodeToSerialize; - *aDragSelection = false; - { + bool isChromeShell = false; + nsCOMPtr webnav = do_GetInterface(mWindow); + nsCOMPtr dsti = do_QueryInterface(webnav); + if (dsti) { + PRInt32 type = -1; + if (NS_SUCCEEDED(dsti->GetItemType(&type)) && + type == nsIDocShellTreeItem::typeChrome) { + isChromeShell = true; + } + } + + // In chrome shells, only allow dragging inside editable areas. + if (isChromeShell && !editingElement) + return NS_OK; + + if (isChromeShell && textControl) { + // Only use the selection if it isn't collapsed. + bool isCollapsed = false; + selection->GetIsCollapsed(&isCollapsed); + if (!isCollapsed) + selection.swap(*aSelection); + } + else { + // In content shells, a number of checks are made below to determine + // whether an image or a link is being dragged. If so, add additional + // data to the data transfer. This is also done for chrome shells, but + // only when in a non-textbox editor. + bool haveSelectedContent = false; // possible parent link node @@ -490,7 +500,7 @@ DragDataProducer::Produce(nsDOMDataTransfer* aDataTransfer, return NS_OK; } - *aDragSelection = true; + selection.swap(*aSelection); } else if (selectedImageOrLinkNode) { // an image is selected image = do_QueryInterface(selectedImageOrLinkNode); @@ -660,20 +670,28 @@ DragDataProducer::Produce(nsDOMDataTransfer* aDataTransfer, } } - if (nodeToSerialize || *aDragSelection) { - // if we have selected text, use it in preference to the node - if (*aDragSelection) { - nodeToSerialize = nsnull; - } - + if (nodeToSerialize || *aSelection) { mHtmlString.Truncate(); mContextString.Truncate(); mInfoString.Truncate(); mTitleString.Truncate(); + + nsCOMPtr domDoc; + mWindow->GetDocument(getter_AddRefs(domDoc)); + nsCOMPtr doc = do_QueryInterface(domDoc); + NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); + + // if we have selected text, use it in preference to the node nsCOMPtr transferable; - rv = ::GetTransferableForNodeOrSelection(mWindow, nodeToSerialize, - getter_AddRefs(transferable)); - NS_ENSURE_SUCCESS(rv, rv); + if (*aSelection) { + rv = nsCopySupport::GetTransferableForSelection(*aSelection, doc, + getter_AddRefs(transferable)); + } + else { + rv = nsCopySupport::GetTransferableForNode(nodeToSerialize, doc, + getter_AddRefs(transferable)); + } + nsCOMPtr data; PRUint32 dataSize; rv = transferable->GetTransferData(kHTMLMime, getter_AddRefs(data), &dataSize); @@ -747,15 +765,17 @@ DragDataProducer::AddStringsToDataTransfer(nsIContent* aDragNode, AddString(aDataTransfer, NS_LITERAL_STRING("text/uri-list"), mUrlString, principal); } - // add a special flavor, even if we don't have html context data - AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLContext), mContextString, principal); + // add a special flavor for the html context data + if (!mContextString.IsEmpty()) + AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLContext), mContextString, principal); // add a special flavor if we have html info data if (!mInfoString.IsEmpty()) AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLInfo), mInfoString, principal); // add the full html - AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLMime), mHtmlString, principal); + if (!mHtmlString.IsEmpty()) + AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLMime), mHtmlString, principal); // add the plain text. we use the url for text/plain data if an anchor is // being dragged, rather than the title text of the link or the alt text for diff --git a/content/base/src/nsContentAreaDragDrop.h b/content/base/src/nsContentAreaDragDrop.h index 64a1ad4ba80..06482876dcf 100644 --- a/content/base/src/nsContentAreaDragDrop.h +++ b/content/base/src/nsContentAreaDragDrop.h @@ -78,8 +78,8 @@ public: * aDataTransfer - the dataTransfer for the drag event. * aCanDrag - [out] set to true if the drag may proceed, false to stop the * drag entirely - * aDragSelection - [out] set to true to indicate that a selection is being - * dragged, rather than a specific node + * aSelection - [out] set to the selection being dragged, or null if no + * selection is being dragged. * aDragNode - [out] the link, image or area being dragged, or null if the * drag occurred on another element. */ @@ -89,7 +89,7 @@ public: bool aIsAltKeyPressed, nsDOMDataTransfer* aDataTransfer, bool* aCanDrag, - bool* aDragSelection, + nsISelection** aSelection, nsIContent** aDragNode); }; diff --git a/content/base/src/nsCopySupport.cpp b/content/base/src/nsCopySupport.cpp index 6054a56603e..f6c32d25dce 100644 --- a/content/base/src/nsCopySupport.cpp +++ b/content/base/src/nsCopySupport.cpp @@ -91,10 +91,6 @@ static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID); static NS_DEFINE_CID(kHTMLConverterCID, NS_HTMLFORMATCONVERTER_CID); -// private clipboard data flavors for html copy, used by editor when pasting -#define kHTMLContext "text/_moz_htmlcontext" -#define kHTMLInfo "text/_moz_htmlinfo" - // copy string data onto the transferable static nsresult AppendString(nsITransferable *aTransferable, const nsAString& aString, diff --git a/content/base/src/nsDOMBlobBuilder.cpp b/content/base/src/nsDOMBlobBuilder.cpp index b1dab01e18f..402edddd103 100644 --- a/content/base/src/nsDOMBlobBuilder.cpp +++ b/content/base/src/nsDOMBlobBuilder.cpp @@ -122,9 +122,9 @@ nsDOMMultipartFile::CreateSlice(PRUint64 aStart, PRUint64 aLength, PRUint64 upperBound = NS_MIN(l - skipStart, length); nsCOMPtr firstBlob; - rv = blob->MozSlice(skipStart, skipStart + upperBound, - aContentType, 3, - getter_AddRefs(firstBlob)); + rv = blob->Slice(skipStart, skipStart + upperBound, + aContentType, 3, + getter_AddRefs(firstBlob)); NS_ENSURE_SUCCESS(rv, nsnull); // Avoid wrapping a single blob inside an nsDOMMultipartFile @@ -150,8 +150,8 @@ nsDOMMultipartFile::CreateSlice(PRUint64 aStart, PRUint64 aLength, if (length < l) { nsCOMPtr lastBlob; - rv = blob->MozSlice(0, length, aContentType, 3, - getter_AddRefs(lastBlob)); + rv = blob->Slice(0, length, aContentType, 3, + getter_AddRefs(lastBlob)); NS_ENSURE_SUCCESS(rv, nsnull); blobs.AppendElement(lastBlob); diff --git a/content/base/src/nsDOMFile.cpp b/content/base/src/nsDOMFile.cpp index 9c1d56d08d6..1df7d3863e0 100644 --- a/content/base/src/nsDOMFile.cpp +++ b/content/base/src/nsDOMFile.cpp @@ -238,9 +238,9 @@ ParseSize(PRInt64 aSize, PRInt64& aStart, PRInt64& aEnd) } NS_IMETHODIMP -nsDOMFileBase::MozSlice(PRInt64 aStart, PRInt64 aEnd, - const nsAString& aContentType, PRUint8 optional_argc, - nsIDOMBlob **aBlob) +nsDOMFileBase::Slice(PRInt64 aStart, PRInt64 aEnd, + const nsAString& aContentType, PRUint8 optional_argc, + nsIDOMBlob **aBlob) { *aBlob = nsnull; diff --git a/content/base/src/nsGenericElement.cpp b/content/base/src/nsGenericElement.cpp index 7c7f74107b2..e26bf228eae 100644 --- a/content/base/src/nsGenericElement.cpp +++ b/content/base/src/nsGenericElement.cpp @@ -1700,9 +1700,29 @@ nsINode::SetExplicitBaseURI(nsIURI* aURI) //---------------------------------------------------------------------- +static JSObject* +GetJSObjectChild(nsWrapperCache* aCache) +{ + if (aCache->PreservingWrapper()) { + return aCache->GetWrapperPreserveColor(); + } + return aCache->GetExpandoObjectPreserveColor(); +} + +static bool +NeedsScriptTraverse(nsWrapperCache* aCache) +{ + JSObject* o = GetJSObjectChild(aCache); + return o && xpc_IsGrayGCThing(o); +} + +//---------------------------------------------------------------------- + NS_IMPL_CYCLE_COLLECTING_ADDREF(nsChildContentList) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsChildContentList) +// If nsChildContentList is changed so that any additional fields are +// traversed by the cycle collector, then CAN_SKIP must be updated. NS_IMPL_CYCLE_COLLECTION_CLASS(nsChildContentList) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsChildContentList) NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER @@ -1714,6 +1734,20 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsChildContentList) NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_TRACE_END +// nsChildContentList only ever has a single child, its wrapper, so if +// the wrapper is black, the list can't be part of a garbage cycle. +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsChildContentList) + return !NeedsScriptTraverse(tmp); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsChildContentList) + return !NeedsScriptTraverse(tmp); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END + +// CanSkipThis returns false to avoid problems with incomplete unlinking. +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsChildContentList) +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END + NS_INTERFACE_TABLE_HEAD(nsChildContentList) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_NODELIST_OFFSET_AND_INTERFACE_TABLE_BEGIN(nsChildContentList) @@ -4405,22 +4439,6 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGenericElement) nsINode::Trace(tmp, aCallback, aClosure); NS_IMPL_CYCLE_COLLECTION_TRACE_END -static JSObject* -GetJSObjectChild(nsINode* aNode) -{ - if (aNode->PreservingWrapper()) { - return aNode->GetWrapperPreserveColor(); - } - return aNode->GetExpandoObjectPreserveColor(); -} - -static bool -NeedsScriptTraverse(nsINode* aNode) -{ - JSObject* o = GetJSObjectChild(aNode); - return o && xpc_IsGrayGCThing(o); -} - void nsGenericElement::MarkUserData(void* aObject, nsIAtom* aKey, void* aChild, void* aData) diff --git a/content/base/src/nsGenericElement.h b/content/base/src/nsGenericElement.h index bfe8869d236..1417a46ce64 100644 --- a/content/base/src/nsGenericElement.h +++ b/content/base/src/nsGenericElement.h @@ -102,7 +102,7 @@ public: } NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsChildContentList) + NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(nsChildContentList) // nsWrapperCache virtual JSObject* WrapObject(JSContext *cx, XPCWrappedNativeScope *scope, diff --git a/content/base/test/fileutils.js b/content/base/test/fileutils.js index fbe6cbaa1c3..67ba18989b4 100644 --- a/content/base/test/fileutils.js +++ b/content/base/test/fileutils.js @@ -201,24 +201,24 @@ function testSlice(file, size, type, contents, fileType) { ok(file instanceof File, fileType + " file is a File"); ok(file instanceof Blob, fileType + " file is also a Blob"); - var slice = file.mozSlice(0, size); + var slice = file.slice(0, size); ok(slice instanceof Blob, fileType + " fullsize slice is a Blob"); ok(!(slice instanceof File), fileType + " fullsize slice is not a File"); - slice = file.mozSlice(0, 1234); + slice = file.slice(0, 1234); ok(slice instanceof Blob, fileType + " sized slice is a Blob"); ok(!(slice instanceof File), fileType + " sized slice is not a File"); - slice = file.mozSlice(0, size, "foo/bar"); + slice = file.slice(0, size, "foo/bar"); is(slice.type, "foo/bar", fileType + " fullsize slice foo/bar type"); - slice = file.mozSlice(0, 5432, "foo/bar"); + slice = file.slice(0, 5432, "foo/bar"); is(slice.type, "foo/bar", fileType + " sized slice foo/bar type"); - is(slice.mozSlice(0, 10).type, "", fileType + " slice-slice type"); - is(slice.mozSlice(0, 10).size, 10, fileType + " slice-slice size"); - is(slice.mozSlice(0, 10, "hello/world").type, "hello/world", fileType + " slice-slice hello/world type"); - is(slice.mozSlice(0, 10, "hello/world").size, 10, fileType + " slice-slice hello/world size"); + is(slice.slice(0, 10).type, "", fileType + " slice-slice type"); + is(slice.slice(0, 10).size, 10, fileType + " slice-slice size"); + is(slice.slice(0, 10, "hello/world").type, "hello/world", fileType + " slice-slice hello/world type"); + is(slice.slice(0, 10, "hello/world").size, 10, fileType + " slice-slice hello/world size"); // Start, end, expected size var indexes = [[0, size, size], @@ -247,17 +247,17 @@ function testSlice(file, size, type, contents, fileType) { var sliceContents; var testName; if (indexes[i][0] == undefined) { - slice = file.mozSlice(); + slice = file.slice(); sliceContents = contents.slice(); testName = fileType + " slice()"; } else if (indexes[i][1] == undefined) { - slice = file.mozSlice(indexes[i][0]); + slice = file.slice(indexes[i][0]); sliceContents = contents.slice(indexes[i][0]); testName = fileType + " slice(" + indexes[i][0] + ")"; } else { - slice = file.mozSlice(indexes[i][0], indexes[i][1]); + slice = file.slice(indexes[i][0], indexes[i][1]); sliceContents = contents.slice(indexes[i][0], indexes[i][1]); testName = fileType + " slice(" + indexes[i][0] + ", " + indexes[i][1] + ")"; } @@ -268,11 +268,11 @@ function testSlice(file, size, type, contents, fileType) { } // Slice of slice - var slice = file.mozSlice(0, 40000); - testFile(slice.mozSlice(5000, 42000), contents.slice(5000, 40000), "file slice slice"); + var slice = file.slice(0, 40000); + testFile(slice.slice(5000, 42000), contents.slice(5000, 40000), "file slice slice"); // ...of slice of slice - slice = slice.mozSlice(5000, 42000).mozSlice(400, 700); + slice = slice.slice(5000, 42000).slice(400, 700); SpecialPowers.gc(); testFile(slice, contents.slice(5400, 5700), "file slice slice slice"); } diff --git a/content/base/test/test_blobbuilder.html b/content/base/test/test_blobbuilder.html index 7b767c78245..6addb2f75f5 100644 --- a/content/base/test/test_blobbuilder.html +++ b/content/base/test/test_blobbuilder.html @@ -151,7 +151,7 @@ function doTest(data) { ok(blob instanceof Blob, "Test " + testCounter + " blob is a Blob"); ok(!(blob instanceof File), "Test " + testCounter + " blob is not a File"); - let slice = blob.mozSlice(test.start, test.start + test.length); + let slice = blob.slice(test.start, test.start + test.length); ok(slice, "Test " + testCounter + " got slice"); ok(slice instanceof Blob, "Test " + testCounter + " slice is a Blob"); ok(!(slice instanceof File), "Test " + testCounter + " slice is not a File"); diff --git a/content/base/test/test_fileapi_slice.html b/content/base/test/test_fileapi_slice.html index 2b5594ce93e..9bfc6e0315c 100644 --- a/content/base/test/test_fileapi_slice.html +++ b/content/base/test/test_fileapi_slice.html @@ -104,7 +104,7 @@ function imageLoadHandler(event) { var imgfile = createFileWithData(testBinaryData + fileData + testBinaryData); is(imgfile.size, size + testBinaryData.length * 2, "correct file size (middle)"); var img = new Image; -img.src = URL.createObjectURL(imgfile.mozSlice(testBinaryData.length, testBinaryData.length + size)); +img.src = URL.createObjectURL(imgfile.slice(testBinaryData.length, testBinaryData.length + size)); img.onload = imageLoadHandler; expectedTestCount++; @@ -112,7 +112,7 @@ expectedTestCount++; var imgfile = createFileWithData(fileData + testBinaryData); is(imgfile.size, size + testBinaryData.length, "correct file size (start)"); var img = new Image; -img.src = URL.createObjectURL(imgfile.mozSlice(0, size)); +img.src = URL.createObjectURL(imgfile.slice(0, size)); img.onload = imageLoadHandler; expectedTestCount++; @@ -120,7 +120,7 @@ expectedTestCount++; var imgfile = createFileWithData(testBinaryData + fileData); is(imgfile.size, size + testBinaryData.length, "correct file size (end)"); var img = new Image; -img.src = URL.createObjectURL(imgfile.mozSlice(testBinaryData.length, testBinaryData.length + size)); +img.src = URL.createObjectURL(imgfile.slice(testBinaryData.length, testBinaryData.length + size)); img.onload = imageLoadHandler; expectedTestCount++; @@ -128,7 +128,7 @@ expectedTestCount++; var imgfile = createFileWithData(testBinaryData + fileData); is(imgfile.size, size + testBinaryData.length, "correct file size (past end)"); var img = new Image; -img.src = URL.createObjectURL(imgfile.mozSlice(testBinaryData.length, testBinaryData.length + size + 1000)); +img.src = URL.createObjectURL(imgfile.slice(testBinaryData.length, testBinaryData.length + size + 1000)); img.onload = imageLoadHandler; expectedTestCount++; diff --git a/content/canvas/src/WebGLContext.h b/content/canvas/src/WebGLContext.h index 5a709d0c1ea..1b99c250bf5 100644 --- a/content/canvas/src/WebGLContext.h +++ b/content/canvas/src/WebGLContext.h @@ -1024,7 +1024,7 @@ struct WebGLVertexAttribData { } }; -class WebGLBuffer +class WebGLBuffer MOZ_FINAL : public nsIWebGLBuffer , public WebGLRefCountedObject , public WebGLContextBoundObject @@ -1158,7 +1158,7 @@ protected: void* mData; // in the case of an Element Array Buffer, we keep a copy. }; -class WebGLTexture +class WebGLTexture MOZ_FINAL : public nsIWebGLTexture , public WebGLRefCountedObject , public WebGLContextBoundObject @@ -1608,7 +1608,7 @@ public: } }; -class WebGLShader +class WebGLShader MOZ_FINAL : public nsIWebGLShader , public WebGLRefCountedObject , public WebGLContextBoundObject @@ -1673,7 +1673,7 @@ protected: WebGLMonotonicHandle mMonotonicHandle; }; -class WebGLProgram +class WebGLProgram MOZ_FINAL : public nsIWebGLProgram , public WebGLRefCountedObject , public WebGLContextBoundObject @@ -1795,7 +1795,7 @@ protected: WebGLMonotonicHandle mMonotonicHandle; }; -class WebGLRenderbuffer +class WebGLRenderbuffer MOZ_FINAL : public nsIWebGLRenderbuffer , public WebGLRefCountedObject , public WebGLRectangleObject @@ -2001,7 +2001,7 @@ public: } }; -class WebGLFramebuffer +class WebGLFramebuffer MOZ_FINAL : public nsIWebGLFramebuffer , public WebGLRefCountedObject , public WebGLContextBoundObject @@ -2296,7 +2296,7 @@ public: WebGLMonotonicHandle mMonotonicHandle; }; -class WebGLUniformLocation +class WebGLUniformLocation MOZ_FINAL : public nsIWebGLUniformLocation , public WebGLContextBoundObject , public WebGLRefCountedObject @@ -2337,7 +2337,7 @@ protected: friend class WebGLProgram; }; -class WebGLActiveInfo +class WebGLActiveInfo MOZ_FINAL : public nsIWebGLActiveInfo { public: @@ -2356,7 +2356,7 @@ protected: nsString mName; }; -class WebGLShaderPrecisionFormat +class WebGLShaderPrecisionFormat MOZ_FINAL : public nsIWebGLShaderPrecisionFormat { public: diff --git a/content/canvas/src/WebGLContextGL.cpp b/content/canvas/src/WebGLContextGL.cpp index c0a8fed9c8e..fd5b6fce070 100644 --- a/content/canvas/src/WebGLContextGL.cpp +++ b/content/canvas/src/WebGLContextGL.cpp @@ -4377,7 +4377,7 @@ WebGLContext::CompileShader(nsIWebGLShader *sobj) // ESSL backend compiler = ShConstructCompiler((ShShaderType) shader->ShaderType(), SH_WEBGL_SPEC, -#ifdef MOZ_WIDGET_ANDROID +#ifdef ANDROID SH_GLSL_OUTPUT, #else gl->IsGLES2() ? SH_ESSL_OUTPUT : SH_GLSL_OUTPUT, diff --git a/content/canvas/src/nsCanvasRenderingContext2D.cpp b/content/canvas/src/nsCanvasRenderingContext2D.cpp index b159d7efbf7..7b4fa8b7f3a 100644 --- a/content/canvas/src/nsCanvasRenderingContext2D.cpp +++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp @@ -180,7 +180,7 @@ CopyContext(gfxContext* dest, gfxContext* src) **/ #define NS_CANVASGRADIENT_PRIVATE_IID \ { 0x491d39d8, 0x4058, 0x42bd, { 0xac, 0x76, 0x70, 0xd5, 0x62, 0x7f, 0x02, 0x10 } } -class nsCanvasGradient : public nsIDOMCanvasGradient +class nsCanvasGradient MOZ_FINAL : public nsIDOMCanvasGradient { public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASGRADIENT_PRIVATE_IID) @@ -238,7 +238,7 @@ NS_INTERFACE_MAP_END **/ #define NS_CANVASPATTERN_PRIVATE_IID \ { 0xb85c6c8a, 0x0624, 0x4530, { 0xb8, 0xee, 0xff, 0xdf, 0x42, 0xe8, 0x21, 0x6d } } -class nsCanvasPattern : public nsIDOMCanvasPattern +class nsCanvasPattern MOZ_FINAL : public nsIDOMCanvasPattern { public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASPATTERN_PRIVATE_IID) diff --git a/content/canvas/test/webgl/failing_tests_mac.txt b/content/canvas/test/webgl/failing_tests_mac.txt index 5bc2bc7c82d..eaabd7f6f1e 100644 --- a/content/canvas/test/webgl/failing_tests_mac.txt +++ b/content/canvas/test/webgl/failing_tests_mac.txt @@ -7,3 +7,5 @@ conformance/more/conformance/quickCheckAPI-S_V.html conformance/glsl/misc/attrib-location-length-limits.html conformance/glsl/misc/uniform-location-length-limits.html conformance/programs/program-test.html +conformance/textures/texture-mips.html +conformance/textures/texture-npot.html diff --git a/content/events/src/nsDOMEvent.cpp b/content/events/src/nsDOMEvent.cpp index 1e36b45f0b6..a80cd3159f6 100644 --- a/content/events/src/nsDOMEvent.cpp +++ b/content/events/src/nsDOMEvent.cpp @@ -180,8 +180,7 @@ nsDOMEvent::InitPresContextData(nsPresContext* aPresContext) // Get the explicit original target (if it's anonymous make it null) { nsCOMPtr content = GetTargetFromFrame(); - mTmpRealOriginalTarget = do_QueryInterface(content); - mExplicitOriginalTarget = mTmpRealOriginalTarget; + mExplicitOriginalTarget = do_QueryInterface(content); if (content && content->IsInAnonymousSubtree()) { mExplicitOriginalTarget = nsnull; } @@ -237,10 +236,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMEvent) } } NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPresContext); - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTmpRealOriginalTarget) - // Always set mExplicitOriginalTarget to null, when - // mTmpRealOriginalTarget doesn't point to any object! - tmp->mExplicitOriginalTarget = nsnull; + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mExplicitOriginalTarget); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMEvent) @@ -275,7 +271,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMEvent) } } NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mPresContext.get(), nsPresContext) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mTmpRealOriginalTarget) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mExplicitOriginalTarget) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END // nsIDOMEventInterface @@ -355,18 +351,6 @@ nsDOMEvent::GetExplicitOriginalTarget(nsIDOMEventTarget** aRealEventTarget) return GetTarget(aRealEventTarget); } -NS_IMETHODIMP -nsDOMEvent::GetTmpRealOriginalTarget(nsIDOMEventTarget** aRealEventTarget) -{ - if (mTmpRealOriginalTarget) { - *aRealEventTarget = mTmpRealOriginalTarget; - NS_ADDREF(*aRealEventTarget); - return NS_OK; - } - - return GetOriginalTarget(aRealEventTarget); -} - NS_IMETHODIMP nsDOMEvent::GetOriginalTarget(nsIDOMEventTarget** aOriginalTarget) { diff --git a/content/events/src/nsDOMEvent.h b/content/events/src/nsDOMEvent.h index f3b53c04890..e2c2017cd58 100644 --- a/content/events/src/nsDOMEvent.h +++ b/content/events/src/nsDOMEvent.h @@ -267,8 +267,7 @@ protected: nsEvent* mEvent; nsRefPtr mPresContext; - nsCOMPtr mTmpRealOriginalTarget; - nsIDOMEventTarget* mExplicitOriginalTarget; + nsCOMPtr mExplicitOriginalTarget; nsString mCachedType; bool mEventIsInternal; bool mPrivateDataDuplicated; diff --git a/content/events/src/nsEventStateManager.cpp b/content/events/src/nsEventStateManager.cpp index 7978989e7d2..2143c8d266b 100644 --- a/content/events/src/nsEventStateManager.cpp +++ b/content/events/src/nsEventStateManager.cpp @@ -2081,14 +2081,12 @@ nsEventStateManager::GenerateDragGesture(nsPresContext* aPresContext, if (!dataTransfer) return; - bool isInEditor = false; - bool isSelection = false; + nsCOMPtr selection; nsCOMPtr eventContent, targetContent; mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(eventContent)); if (eventContent) DetermineDragTarget(aPresContext, eventContent, dataTransfer, - &isSelection, &isInEditor, - getter_AddRefs(targetContent)); + getter_AddRefs(selection), getter_AddRefs(targetContent)); // Stop tracking the drag gesture now. This should stop us from // reentering GenerateDragGesture inside DOM event processing. @@ -2129,9 +2127,8 @@ nsEventStateManager::GenerateDragGesture(nsPresContext* aPresContext, // elements in an editor, only fire the draggesture event so that the // editor code can handle it but content doesn't see a dragstart. nsEventStatus status = nsEventStatus_eIgnore; - if (!isInEditor) - nsEventDispatcher::Dispatch(targetContent, aPresContext, &startEvent, nsnull, - &status); + nsEventDispatcher::Dispatch(targetContent, aPresContext, &startEvent, nsnull, + &status); nsDragEvent* event = &startEvent; if (status != nsEventStatus_eConsumeNoDefault) { @@ -2148,7 +2145,7 @@ nsEventStateManager::GenerateDragGesture(nsPresContext* aPresContext, if (status != nsEventStatus_eConsumeNoDefault) { bool dragStarted = DoDefaultDragStart(aPresContext, event, dataTransfer, - targetContent, isSelection); + targetContent, selection); if (dragStarted) { sActiveESM = nsnull; aEvent->flags |= NS_EVENT_FLAG_STOP_DISPATCH; @@ -2173,37 +2170,27 @@ void nsEventStateManager::DetermineDragTarget(nsPresContext* aPresContext, nsIContent* aSelectionTarget, nsDOMDataTransfer* aDataTransfer, - bool* aIsSelection, - bool* aIsInEditor, + nsISelection** aSelection, nsIContent** aTargetNode) { *aTargetNode = nsnull; - *aIsInEditor = false; nsCOMPtr container = aPresContext->GetContainer(); nsCOMPtr window = do_GetInterface(container); // GetDragData determines if a selection, link or image in the content // should be dragged, and places the data associated with the drag in the - // data transfer. Skip this check for chrome shells. + // data transfer. + // mGestureDownContent is the node where the mousedown event for the drag + // occurred, and aSelectionTarget is the node to use when a selection is used bool canDrag; nsCOMPtr dragDataNode; - nsCOMPtr dsti = do_QueryInterface(container); - if (dsti) { - PRInt32 type = -1; - if (NS_SUCCEEDED(dsti->GetItemType(&type)) && - type != nsIDocShellTreeItem::typeChrome) { - // mGestureDownContent is the node where the mousedown event for the drag - // occurred, and aSelectionTarget is the node to use when a selection is used - nsresult rv = - nsContentAreaDragDrop::GetDragData(window, mGestureDownContent, - aSelectionTarget, mGestureDownAlt, - aDataTransfer, &canDrag, aIsSelection, - getter_AddRefs(dragDataNode)); - if (NS_FAILED(rv) || !canDrag) - return; - } - } + nsresult rv = nsContentAreaDragDrop::GetDragData(window, mGestureDownContent, + aSelectionTarget, mGestureDownAlt, + aDataTransfer, &canDrag, aSelection, + getter_AddRefs(dragDataNode)); + if (NS_FAILED(rv) || !canDrag) + return; // if GetDragData returned a node, use that as the node being dragged. // Otherwise, if a selection is being dragged, use the node within the @@ -2211,7 +2198,7 @@ nsEventStateManager::DetermineDragTarget(nsPresContext* aPresContext, nsIContent* dragContent = mGestureDownContent; if (dragDataNode) dragContent = dragDataNode; - else if (*aIsSelection) + else if (*aSelection) dragContent = aSelectionTarget; nsIContent* originalDragContent = dragContent; @@ -2220,7 +2207,7 @@ nsEventStateManager::DetermineDragTarget(nsPresContext* aPresContext, // draggable property set. If one is found, use that as the target of the // drag instead of the node that was clicked on. If a draggable node wasn't // found, just use the clicked node. - if (!*aIsSelection) { + if (!*aSelection) { while (dragContent) { nsCOMPtr htmlElement = do_QueryInterface(dragContent); if (htmlElement) { @@ -2245,17 +2232,6 @@ nsEventStateManager::DetermineDragTarget(nsPresContext* aPresContext, // otherwise, it's not an HTML or XUL element, so just keep looking } dragContent = dragContent->GetParent(); - - // if an editable parent is encountered, then we don't look at any - // ancestors. This is used because the editor attaches a draggesture - // listener to the editable element and we want to call it without - // making the editable element draggable. This should be removed once - // the editor is switched over to using the proper drag and drop api. - nsCOMPtr editableElement = do_QueryInterface(dragContent); - if (editableElement) { - *aIsInEditor = true; - break; - } } } @@ -2279,7 +2255,7 @@ nsEventStateManager::DoDefaultDragStart(nsPresContext* aPresContext, nsDragEvent* aDragEvent, nsDOMDataTransfer* aDataTransfer, nsIContent* aDragTarget, - bool aIsSelection) + nsISelection* aSelection) { nsCOMPtr dragService = do_GetService("@mozilla.org/widget/dragservice;1"); @@ -2333,22 +2309,6 @@ nsEventStateManager::DoDefaultDragStart(nsPresContext* aPresContext, PRInt32 imageX, imageY; nsIDOMElement* dragImage = aDataTransfer->GetDragImage(&imageX, &imageY); - // If a selection is being dragged, and no custom drag image was - // set, get the selection so that the drag region can be created - // from the selection area. If a custom image was set, it doesn't - // matter what the selection is since the image will be used instead. - nsISelection* selection = nsnull; - if (aIsSelection && !dragImage) { - nsIDocument* doc = aDragTarget->GetCurrentDoc(); - if (doc) { - nsIPresShell* presShell = doc->GetShell(); - if (presShell) { - selection = presShell->GetCurrentSelection( - nsISelectionController::SELECTION_NORMAL); - } - } - } - nsCOMPtr transArray; aDataTransfer->GetTransferables(getter_AddRefs(transArray)); if (!transArray) @@ -2363,8 +2323,13 @@ nsEventStateManager::DoDefaultDragStart(nsPresContext* aPresContext, nsCOMPtr domDragEvent = do_QueryInterface(domEvent); // if creating a drag event failed, starting a drag session will // just fail. - if (selection) { - dragService->InvokeDragSessionWithSelection(selection, transArray, + + // Use InvokeDragSessionWithSelection if a selection is being dragged, + // such that the image can be generated from the selected text. However, + // use InvokeDragSessionWithImage if a custom image was set or something + // other than a selection is being dragged. + if (!dragImage && aSelection) { + dragService->InvokeDragSessionWithSelection(aSelection, transArray, action, domDragEvent, aDataTransfer); } diff --git a/content/events/src/nsEventStateManager.h b/content/events/src/nsEventStateManager.h index b6839292d8e..f831f587205 100644 --- a/content/events/src/nsEventStateManager.h +++ b/content/events/src/nsEventStateManager.h @@ -417,15 +417,13 @@ protected: * * aSelectionTarget - target to check for selection * aDataTransfer - data transfer object that will contain the data to drag - * aIsSelection - [out] set to true if a selection is being dragged - * aIsInEditor - [out] set to true if the content is in an editor field + * aSelection - [out] set to the selection to be dragged * aTargetNode - [out] the draggable node, or null if there isn't one */ void DetermineDragTarget(nsPresContext* aPresContext, nsIContent* aSelectionTarget, nsDOMDataTransfer* aDataTransfer, - bool* aIsSelection, - bool* aIsInEditor, + nsISelection** aSelection, nsIContent** aTargetNode); /* @@ -436,13 +434,13 @@ protected: * aDragEvent - the dragstart/draggesture event * aDataTransfer - the data transfer that holds the data to be dragged * aDragTarget - the target of the drag - * aIsSelection - true if a selection is being dragged + * aSelection - the selection to be dragged */ bool DoDefaultDragStart(nsPresContext* aPresContext, nsDragEvent* aDragEvent, nsDOMDataTransfer* aDataTransfer, nsIContent* aDragTarget, - bool aIsSelection); + nsISelection* aSelection); bool IsTrackingDragGesture ( ) const { return mGestureDownContent != nsnull; } /** diff --git a/content/media/test/Makefile.in b/content/media/test/Makefile.in index 3abd920a883..c45e30c6481 100644 --- a/content/media/test/Makefile.in +++ b/content/media/test/Makefile.in @@ -109,7 +109,6 @@ _TEST_FILES = \ test_bug495300.html \ test_bug686942.html \ test_can_play_type.html \ - test_closing_connections.html \ test_constants.html \ test_controls.html \ test_currentTime.html \ @@ -176,6 +175,8 @@ endif # test_mixed_principals.html # Disabled since we don't play Wave files standalone, for now # test_audioDocumentTitle.html +# Bug 634564: +# test_closing_connections.html \ # sample files _TEST_FILES += \ diff --git a/content/svg/content/src/nsSVGElement.cpp b/content/svg/content/src/nsSVGElement.cpp index 1b8d052dc59..2e6ef2b09ab 100644 --- a/content/svg/content/src/nsSVGElement.cpp +++ b/content/svg/content/src/nsSVGElement.cpp @@ -1486,7 +1486,8 @@ nsSVGElement::GetCtx() const } /* virtual */ gfxMatrix -nsSVGElement::PrependLocalTransformTo(const gfxMatrix &aMatrix) const +nsSVGElement::PrependLocalTransformsTo(const gfxMatrix &aMatrix, + TransformTypes aWhich) const { return aMatrix; } diff --git a/content/svg/content/src/nsSVGElement.h b/content/svg/content/src/nsSVGElement.h index 92e1a451274..44a63c87abd 100644 --- a/content/svg/content/src/nsSVGElement.h +++ b/content/svg/content/src/nsSVGElement.h @@ -151,11 +151,36 @@ public: // nsnull for outer or SVG without an parent (invalid SVG). nsSVGSVGElement* GetCtx() const; + enum TransformTypes { + eAllTransforms + ,eUserSpaceToParent + ,eChildToUserSpace + }; /** - * Returns aMatrix post-multiplied by the transform from the userspace - * established by this element to the userspace established by its parent. + * Returns aMatrix pre-multiplied by (explicit or implicit) transforms that + * are introduced by attributes on this element. + * + * If aWhich is eAllTransforms, then all the transforms from the coordinate + * space established by this element for its children to the coordinate + * space established by this element's parent element for this element, are + * included. + * + * If aWhich is eUserSpaceToParent, then only the transforms from this + * element's userspace to the coordinate space established by its parent is + * included. This includes any transforms introduced by the 'transform' + * attribute, transform animations and animateMotion, but not any offsets + * due to e.g. 'x'/'y' attributes, or any transform due to a 'viewBox' + * attribute. (SVG userspace is defined to be the coordinate space in which + * coordinates on an element apply.) + * + * If aWhich is eChildToUserSpace, then only the transforms from the + * coordinate space established by this element for its childre to this + * elements userspace are included. This includes any offsets due to e.g. + * 'x'/'y' attributes, and any transform due to a 'viewBox' attribute, but + * does not include any transforms due to the 'transform' attribute. */ - virtual gfxMatrix PrependLocalTransformTo(const gfxMatrix &aMatrix) const; + virtual gfxMatrix PrependLocalTransformsTo(const gfxMatrix &aMatrix, + TransformTypes aWhich = eAllTransforms) const; // Setter for to set the current transformation // Only visible for nsSVGGraphicElement, so it's a no-op here, and that diff --git a/content/svg/content/src/nsSVGForeignObjectElement.cpp b/content/svg/content/src/nsSVGForeignObjectElement.cpp index 054e2b7ee7b..4385a4a2830 100644 --- a/content/svg/content/src/nsSVGForeignObjectElement.cpp +++ b/content/svg/content/src/nsSVGForeignObjectElement.cpp @@ -112,16 +112,28 @@ NS_IMETHODIMP nsSVGForeignObjectElement::GetHeight(nsIDOMSVGAnimatedLength * *aH // nsSVGElement methods /* virtual */ gfxMatrix -nsSVGForeignObjectElement::PrependLocalTransformTo(const gfxMatrix &aMatrix) const +nsSVGForeignObjectElement::PrependLocalTransformsTo(const gfxMatrix &aMatrix, + TransformTypes aWhich) const { + NS_ABORT_IF_FALSE(aWhich != eChildToUserSpace || aMatrix.IsIdentity(), + "Skipping eUserSpaceToParent transforms makes no sense"); + // 'transform' attribute: - gfxMatrix matrix = nsSVGForeignObjectElementBase::PrependLocalTransformTo(aMatrix); - - // now translate by our 'x' and 'y': + gfxMatrix fromUserSpace = + nsSVGForeignObjectElementBase::PrependLocalTransformsTo(aMatrix, aWhich); + if (aWhich == eUserSpaceToParent) { + return fromUserSpace; + } + // our 'x' and 'y' attributes: float x, y; const_cast(this)-> GetAnimatedLengthValues(&x, &y, nsnull); - return gfxMatrix().Translate(gfxPoint(x, y)) * matrix; + gfxMatrix toUserSpace = gfxMatrix().Translate(gfxPoint(x, y)); + if (aWhich == eChildToUserSpace) { + return toUserSpace; + } + NS_ABORT_IF_FALSE(aWhich == eAllTransforms, "Unknown TransformTypes"); + return toUserSpace * fromUserSpace; } //---------------------------------------------------------------------- diff --git a/content/svg/content/src/nsSVGForeignObjectElement.h b/content/svg/content/src/nsSVGForeignObjectElement.h index 9d662bb9daf..d9794dbeffe 100644 --- a/content/svg/content/src/nsSVGForeignObjectElement.h +++ b/content/svg/content/src/nsSVGForeignObjectElement.h @@ -69,7 +69,8 @@ public: NS_FORWARD_NSIDOMSVGELEMENT(nsSVGForeignObjectElementBase::) // nsSVGElement specializations: - virtual gfxMatrix PrependLocalTransformTo(const gfxMatrix &aMatrix) const; + virtual gfxMatrix PrependLocalTransformsTo(const gfxMatrix &aMatrix, + TransformTypes aWhich = eAllTransforms) const; // nsIContent interface NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* name) const; diff --git a/content/svg/content/src/nsSVGGraphicElement.cpp b/content/svg/content/src/nsSVGGraphicElement.cpp index 517168b7759..814d095029b 100644 --- a/content/svg/content/src/nsSVGGraphicElement.cpp +++ b/content/svg/content/src/nsSVGGraphicElement.cpp @@ -189,10 +189,25 @@ nsSVGGraphicElement::IsEventName(nsIAtom* aName) } gfxMatrix -nsSVGGraphicElement::PrependLocalTransformTo(const gfxMatrix &aMatrix) const +nsSVGGraphicElement::PrependLocalTransformsTo(const gfxMatrix &aMatrix, + TransformTypes aWhich) const { + NS_ABORT_IF_FALSE(aWhich != eChildToUserSpace || aMatrix.IsIdentity(), + "Skipping eUserSpaceToParent transforms makes no sense"); + gfxMatrix result(aMatrix); + if (aWhich == eChildToUserSpace) { + // We don't have anything to prepend. + // eChildToUserSpace is not the common case, which is why we return + // 'result' to benefit from NRVO rather than returning aMatrix before + // creating 'result'. + return result; + } + + NS_ABORT_IF_FALSE(aWhich == eAllTransforms || aWhich == eUserSpaceToParent, + "Unknown TransformTypes"); + // animateMotion's resulting transform is supposed to apply *on top of* // any transformations from the |transform| attribute. So since we're // PRE-multiplying, we need to apply the animateMotion transform *first*. diff --git a/content/svg/content/src/nsSVGGraphicElement.h b/content/svg/content/src/nsSVGGraphicElement.h index 923e8b56bc7..84f7f02289e 100644 --- a/content/svg/content/src/nsSVGGraphicElement.h +++ b/content/svg/content/src/nsSVGGraphicElement.h @@ -62,7 +62,8 @@ public: // nsIContent interface NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const; - virtual gfxMatrix PrependLocalTransformTo(const gfxMatrix &aMatrix) const; + virtual gfxMatrix PrependLocalTransformsTo(const gfxMatrix &aMatrix, + TransformTypes aWhich = eAllTransforms) const; virtual void SetAnimateMotionTransform(const gfxMatrix* aMatrix); virtual mozilla::SVGAnimatedTransformList* GetAnimatedTransformList(); diff --git a/content/svg/content/src/nsSVGPathElement.cpp b/content/svg/content/src/nsSVGPathElement.cpp index b9c1477e53e..c43da6ebec1 100644 --- a/content/svg/content/src/nsSVGPathElement.cpp +++ b/content/svg/content/src/nsSVGPathElement.cpp @@ -440,7 +440,7 @@ nsSVGPathElement::GetPathLengthScale(PathLengthScaleForType aFor) // For textPath, a transform on the referenced path affects the // textPath layout, so when calculating the actual path length // we need to take that into account. - matrix = PrependLocalTransformTo(matrix); + matrix = PrependLocalTransformsTo(matrix); } nsRefPtr path = GetFlattenedPath(matrix); if (path) { diff --git a/content/svg/content/src/nsSVGSVGElement.cpp b/content/svg/content/src/nsSVGSVGElement.cpp index 9533de44d72..9a199cb999a 100644 --- a/content/svg/content/src/nsSVGSVGElement.cpp +++ b/content/svg/content/src/nsSVGSVGElement.cpp @@ -750,7 +750,16 @@ nsSVGSVGElement::GetCTM(nsIDOMSVGMatrix * *aCTM) NS_IMETHODIMP nsSVGSVGElement::GetScreenCTM(nsIDOMSVGMatrix **aCTM) { - gfxMatrix m = nsSVGUtils::GetCTM(this, true); + gfxMatrix m; + if (IsRoot()) { + // Consistency with other elements would have us return only the + // eFromUserSpace transforms, but this is what we've been doing for + // a while, and it keeps us consistent with WebKit and Opera (if not + // really with the ambiguous spec). + m = PrependLocalTransformsTo(m); + } else { + m = nsSVGUtils::GetCTM(this, true); + } *aCTM = m.IsSingular() ? nsnull : new DOMSVGMatrix(m); NS_IF_ADDREF(*aCTM); return NS_OK; @@ -1180,12 +1189,29 @@ nsSVGSVGElement::GetLength(PRUint8 aCtxType) // nsSVGElement methods /* virtual */ gfxMatrix -nsSVGSVGElement::PrependLocalTransformTo(const gfxMatrix &aMatrix) const +nsSVGSVGElement::PrependLocalTransformsTo(const gfxMatrix &aMatrix, + TransformTypes aWhich) const { + NS_ABORT_IF_FALSE(aWhich != eChildToUserSpace || aMatrix.IsIdentity(), + "Skipping eUserSpaceToParent transforms makes no sense"); + if (IsInner()) { float x, y; const_cast(this)->GetAnimatedLengthValues(&x, &y, nsnull); - return GetViewBoxTransform() * gfxMatrix().Translate(gfxPoint(x, y)) * aMatrix; + if (aWhich == eAllTransforms) { + // the common case + return GetViewBoxTransform() * gfxMatrix().Translate(gfxPoint(x, y)) * aMatrix; + } + if (aWhich == eUserSpaceToParent) { + return gfxMatrix().Translate(gfxPoint(x, y)) * aMatrix; + } + NS_ABORT_IF_FALSE(aWhich == eChildToUserSpace, "Unknown TransformTypes"); + return GetViewBoxTransform(); // no need to multiply identity aMatrix + } + + if (aWhich == eUserSpaceToParent) { + // only inner- has eUserSpaceToParent transforms + return aMatrix; } if (IsRoot()) { diff --git a/content/svg/content/src/nsSVGSVGElement.h b/content/svg/content/src/nsSVGSVGElement.h index b4589b74fd3..668de43e533 100644 --- a/content/svg/content/src/nsSVGSVGElement.h +++ b/content/svg/content/src/nsSVGSVGElement.h @@ -186,8 +186,9 @@ public: virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor); // nsSVGElement specializations: - virtual gfxMatrix PrependLocalTransformTo(const gfxMatrix &aMatrix) const; - + virtual gfxMatrix PrependLocalTransformsTo(const gfxMatrix &aMatrix, + TransformTypes aWhich = eAllTransforms) const; + // nsSVGSVGElement methods: float GetLength(PRUint8 mCtxType); diff --git a/content/svg/content/src/nsSVGUseElement.cpp b/content/svg/content/src/nsSVGUseElement.cpp index ce13b776c96..458b8cc6124 100644 --- a/content/svg/content/src/nsSVGUseElement.cpp +++ b/content/svg/content/src/nsSVGUseElement.cpp @@ -488,15 +488,27 @@ nsSVGUseElement::UnlinkSource() // nsSVGElement methods /* virtual */ gfxMatrix -nsSVGUseElement::PrependLocalTransformTo(const gfxMatrix &aMatrix) const +nsSVGUseElement::PrependLocalTransformsTo(const gfxMatrix &aMatrix, + TransformTypes aWhich) const { - // 'transform' attribute: - gfxMatrix matrix = nsSVGUseElementBase::PrependLocalTransformTo(aMatrix); + NS_ABORT_IF_FALSE(aWhich != eChildToUserSpace || aMatrix.IsIdentity(), + "Skipping eUserSpaceToParent transforms makes no sense"); - // now translate by our 'x' and 'y': + // 'transform' attribute: + gfxMatrix fromUserSpace = + nsSVGUseElementBase::PrependLocalTransformsTo(aMatrix, aWhich); + if (aWhich == eUserSpaceToParent) { + return fromUserSpace; + } + // our 'x' and 'y' attributes: float x, y; const_cast(this)->GetAnimatedLengthValues(&x, &y, nsnull); - return matrix.PreMultiply(gfxMatrix().Translate(gfxPoint(x, y))); + gfxMatrix toUserSpace = gfxMatrix().Translate(gfxPoint(x, y)); + if (aWhich == eChildToUserSpace) { + return toUserSpace; + } + NS_ABORT_IF_FALSE(aWhich == eAllTransforms, "Unknown TransformTypes"); + return toUserSpace * fromUserSpace; } nsSVGElement::LengthAttributesInfo diff --git a/content/svg/content/src/nsSVGUseElement.h b/content/svg/content/src/nsSVGUseElement.h index 0a47f814e34..f9f4d43df13 100644 --- a/content/svg/content/src/nsSVGUseElement.h +++ b/content/svg/content/src/nsSVGUseElement.h @@ -103,7 +103,8 @@ public: void DestroyAnonymousContent(); // nsSVGElement specializations: - virtual gfxMatrix PrependLocalTransformTo(const gfxMatrix &aMatrix) const; + virtual gfxMatrix PrependLocalTransformsTo(const gfxMatrix &aMatrix, + TransformTypes aWhich = eAllTransforms) const; // nsIContent interface virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const; diff --git a/content/svg/content/test/bbox-helper.svg b/content/svg/content/test/bbox-helper.svg index 8c27c8ab1c1..9c2d2b428eb 100644 --- a/content/svg/content/test/bbox-helper.svg +++ b/content/svg/content/test/bbox-helper.svg @@ -1,7 +1,7 @@ - + b a diff --git a/content/svg/content/test/getCTM-helper.svg b/content/svg/content/test/getCTM-helper.svg index f1ba9f48081..2bf32de73bd 100644 --- a/content/svg/content/test/getCTM-helper.svg +++ b/content/svg/content/test/getCTM-helper.svg @@ -17,7 +17,7 @@ - + diff --git a/content/svg/content/test/test_bbox.xhtml b/content/svg/content/test/test_bbox.xhtml index aaeee14816f..c51084ed835 100644 --- a/content/svg/content/test/test_bbox.xhtml +++ b/content/svg/content/test/test_bbox.xhtml @@ -39,7 +39,7 @@ function run() is(bbox1.height, bbox2.height, id1 + ".getBBox().height"); } - checkBBox("f", 0, 0, 100, 100); + checkBBox("fO", 10, 10, 100, 100); checkBBoxHeight("a", "b"); checkBBoxHeight("a", "y"); checkBBox("v", 95, 45, 10, 155); diff --git a/content/svg/content/test/test_getCTM.html b/content/svg/content/test/test_getCTM.html index 1307da3c06b..57e798c3cbb 100644 --- a/content/svg/content/test/test_getCTM.html +++ b/content/svg/content/test/test_getCTM.html @@ -37,6 +37,7 @@ function runTest() var g4 = doc.getElementById("g4"); var sym = doc.getElementById("sym"); var symbolRect = doc.getElementById("symbolRect"); + var fO = doc.getElementById("fO"); /* Tests the consistency with nearestViewportElement (code is from test_viewport.html) */ // root.nearestViewportElement == null @@ -60,6 +61,9 @@ function runTest() // symbolRect.nearestViewportElement == sym is((function(){try{return symbolRect.getCTM().e}catch(e){return e}})(), 70, "symbolRect.getCTM().e"); is((function(){try{return symbolRect.getCTM().f}catch(e){return e}})(), 80, "symbolRect.getCTM().f"); + // fO.nearestViewportElement = with no 'id' + is((function(){try{return fO.getCTM().e}catch(e){return e}})(), 2, "fO.getCTM().e"); + is((function(){try{return fO.getCTM().f}catch(e){return e}})(), 3, "fO.getCTM().f"); /* Tests the consistency with farthestViewportElement (code is from test_viewport.html) */ @@ -83,6 +87,9 @@ function runTest() // symbolRect.farthestViewportElement == root is((function(){try{return symbolRect.getScreenCTM().e}catch(e){return e}})(), 85, "symbolRect.getScreenCTM().e"); is((function(){try{return symbolRect.getScreenCTM().f}catch(e){return e}})(), 108, "symbolRect.getScreenCTM().f"); + // fO.farthestViewportElement == root + is((function(){try{return fO.getScreenCTM().e}catch(e){return e}})(), 16, "symbolRect.getScreenCTM().e"); + is((function(){try{return fO.getScreenCTM().f}catch(e){return e}})(), 29, "symbolRect.getScreenCTM().f"); SimpleTest.finish(); } diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 6fd6c8cf816..8385cffa807 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -4119,9 +4119,7 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI, // Display the error as a page or an alert prompt NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE); - // Note: For now, display an alert instead of an error page if we have no - // URI object. Missing URI objects are handled badly by session history. - if (mUseErrorPages && aURI) { + if (mUseErrorPages) { // Display an error page LoadErrorPage(aURI, aURL, errorPage.get(), error.get(), messageStr.get(), cssClass.get(), aFailedChannel); @@ -4190,6 +4188,10 @@ nsDocShell::LoadErrorPage(nsIURI *aURI, const PRUnichar *aURL, } else if (aURL) { + // We need a URI object to store a session history entry, so make up a URI + nsresult rv = NS_NewURI(getter_AddRefs(mFailedURI), "about:blank"); + NS_ENSURE_SUCCESS(rv, rv); + CopyUTF16toUTF8(aURL, url); } else diff --git a/dom/Makefile.in b/dom/Makefile.in index 91a0480f65e..d082f8d5175 100644 --- a/dom/Makefile.in +++ b/dom/Makefile.in @@ -97,11 +97,6 @@ DIRS += \ $(NULL) endif -ifdef MOZ_B2G_BT -DIRS += \ - bluetooth \ - $(NULL) -endif TEST_DIRS += tests ifneq (,$(filter gtk2 cocoa windows android qt os2,$(MOZ_WIDGET_TOOLKIT))) TEST_DIRS += plugins/test diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index d032c2986fe..e9890da74f0 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -80,10 +80,6 @@ #ifdef MOZ_B2G_RIL #include "TelephonyFactory.h" #endif -#ifdef MOZ_B2G_BT -#include "nsIDOMBluetoothAdapter.h" -#include "BluetoothAdapter.h" -#endif // This should not be in the namespace. DOMCI_DATA(Navigator, mozilla::dom::Navigator) @@ -137,9 +133,6 @@ NS_INTERFACE_MAP_BEGIN(Navigator) NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorTelephony) #endif NS_INTERFACE_MAP_ENTRY(nsIDOMMozNavigatorNetwork) -#ifdef MOZ_B2G_BT - NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorBluetooth) -#endif NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Navigator) NS_INTERFACE_MAP_END @@ -189,12 +182,6 @@ Navigator::Invalidate() mConnection->Shutdown(); mConnection = nsnull; } - -#ifdef MOZ_B2G_BT - if (mBluetooth) { - mBluetooth = nsnull; - } -#endif } nsPIDOMWindow * @@ -1125,30 +1112,6 @@ Navigator::GetMozConnection(nsIDOMMozConnection** aConnection) return NS_OK; } -#ifdef MOZ_B2G_BT -//***************************************************************************** -// nsNavigator::nsIDOMNavigatorBluetooth -//***************************************************************************** - -NS_IMETHODIMP -Navigator::GetMozBluetooth(nsIDOMBluetoothAdapter** aBluetooth) -{ - nsCOMPtr bluetooth = mBluetooth; - - if (!bluetooth) { - nsCOMPtr window = do_QueryReferent(mWindow); - NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); - - mBluetooth = new bluetooth::BluetoothAdapter(); - - bluetooth = mBluetooth; - } - - bluetooth.forget(aBluetooth); - return NS_OK; -} -#endif //MOZ_B2G_BT - PRInt64 Navigator::SizeOf() const { diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h index 3ba648c7a1a..35dc43dfa05 100644 --- a/dom/base/Navigator.h +++ b/dom/base/Navigator.h @@ -65,11 +65,6 @@ class nsIDOMMozConnection; class nsIDOMTelephony; #endif -#ifdef MOZ_B2G_BT -#include "nsIDOMNavigatorBluetooth.h" -#endif - -class nsIDOMAdapter; //***************************************************************************** // Navigator: Script "navigator" object //***************************************************************************** @@ -103,9 +98,6 @@ class Navigator : public nsIDOMNavigator , public nsIDOMNavigatorTelephony #endif , public nsIDOMMozNavigatorNetwork -#ifdef MOZ_B2G_BT - , public nsIDOMNavigatorBluetooth -#endif { public: Navigator(nsPIDOMWindow *aInnerWindow); @@ -123,10 +115,6 @@ public: #endif NS_DECL_NSIDOMMOZNAVIGATORNETWORK -#ifdef MOZ_B2G_BT - NS_DECL_NSIDOMNAVIGATORBLUETOOTH -#endif - static void Init(); void Invalidate(); @@ -158,9 +146,6 @@ private: nsCOMPtr mTelephony; #endif nsRefPtr mConnection; -#ifdef MOZ_B2G_BT - nsCOMPtr mBluetooth; -#endif nsWeakPtr mWindow; }; diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index efe13341ef7..2ef36d34bd8 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -532,10 +532,6 @@ using mozilla::dom::indexedDB::IDBWrapperCache; #include "CallEvent.h" #endif -#ifdef MOZ_B2G_BT -#include "BluetoothAdapter.h" -#endif - #include "DOMError.h" using namespace mozilla; @@ -1631,11 +1627,6 @@ static nsDOMClassInfoData sClassInfoData[] = { DOM_DEFAULT_SCRIPTABLE_FLAGS) #endif -#ifdef MOZ_B2G_BT - NS_DEFINE_CLASSINFO_DATA(BluetoothAdapter, nsDOMGenericSH, - DOM_DEFAULT_SCRIPTABLE_FLAGS) -#endif - NS_DEFINE_CLASSINFO_DATA(DOMError, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) }; @@ -2432,9 +2423,6 @@ nsDOMClassInfo::Init() #endif DOM_CLASSINFO_MAP_CONDITIONAL_ENTRY(nsIDOMMozNavigatorNetwork, network::IsAPIEnabled()) -#ifdef MOZ_B2G_BT - DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorBluetooth) -#endif DOM_CLASSINFO_MAP_END DOM_CLASSINFO_MAP_BEGIN(Plugin, nsIDOMPlugin) @@ -4377,12 +4365,6 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_MAP_END #endif -#ifdef MOZ_B2G_BT - DOM_CLASSINFO_MAP_BEGIN(BluetoothAdapter, nsIDOMBluetoothAdapter) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMBluetoothAdapter) - DOM_CLASSINFO_MAP_END -#endif - DOM_CLASSINFO_MAP_BEGIN(DOMError, nsIDOMDOMError) DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMError) DOM_CLASSINFO_MAP_END diff --git a/dom/base/nsDOMClassInfoClasses.h b/dom/base/nsDOMClassInfoClasses.h index 2c3aeed980c..8a945a897d1 100644 --- a/dom/base/nsDOMClassInfoClasses.h +++ b/dom/base/nsDOMClassInfoClasses.h @@ -540,8 +540,4 @@ DOMCI_CLASS(TelephonyCall) DOMCI_CLASS(CallEvent) #endif -#ifdef MOZ_B2G_BT -DOMCI_CLASS(BluetoothAdapter) -#endif - DOMCI_CLASS(DOMError) diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 36d1a3bf32e..805def0a7ca 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -2091,6 +2091,13 @@ nsDOMWindowUtils::GetFileReferences(const nsAString& aDatabaseName, return NS_OK; } +NS_IMETHODIMP +nsDOMWindowUtils::IsIncrementalGCEnabled(JSContext* cx, bool* aResult) +{ + *aResult = js::IsIncrementalGCEnabled(JS_GetRuntime(cx)); + return NS_OK; +} + NS_IMETHODIMP nsDOMWindowUtils::StartPCCountProfiling(JSContext* cx) { diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 4988e92e12a..7e8fa870fcc 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -135,6 +135,9 @@ static PRLogModuleInfo* gJSDiagnostics; // doing the first GC. #define NS_FIRST_GC_DELAY 10000 // ms +// Maximum amount of time that should elapse between incremental GC slices +#define NS_INTERSLICE_GC_DELAY 100 // ms + // The amount of time we wait between a request to CC (after GC ran) // and doing the actual CC. #define NS_CC_DELAY 5000 // ms @@ -154,6 +157,9 @@ static nsITimer *sCCTimer; static PRTime sLastCCEndTime; static bool sGCHasRun; +static bool sCCLockedOut; + +static js::GCSliceCallback sPrevGCSliceCallback; // The number of currently pending document loads. This count isn't // guaranteed to always reflect reality and can't easily as we don't @@ -3274,6 +3280,11 @@ nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener, return; } + if (sCCLockedOut) { + // We're in the middle of an incremental GC; finish it first + nsJSContext::GarbageCollectNow(js::gcreason::CC_FORCED, nsGCNormal); + } + SAMPLE_LABEL("GC", "CycleCollectNow"); NS_TIME_FUNCTION_MIN(1.0); @@ -3357,7 +3368,7 @@ GCTimerFired(nsITimer *aTimer, void *aClosure) NS_RELEASE(sGCTimer); uintptr_t reason = reinterpret_cast(aClosure); - nsJSContext::GarbageCollectNow(static_cast(reason), nsGCNormal); + nsJSContext::GarbageCollectNow(static_cast(reason), nsGCIncremental); } void @@ -3375,6 +3386,9 @@ CCTimerFired(nsITimer *aTimer, void *aClosure) if (sDidShutdown) { return; } + if (sCCLockedOut) { + return; + } ++sCCTimerFireCount; if (sCCTimerFireCount < (NS_CC_DELAY / NS_CC_SKIPPABLE_DELAY)) { PRUint32 suspected = nsCycleCollector_suspectedCount(); @@ -3443,7 +3457,7 @@ nsJSContext::LoadEnd() // static void -nsJSContext::PokeGC(js::gcreason::Reason aReason) +nsJSContext::PokeGC(js::gcreason::Reason aReason, int aDelay) { if (sGCTimer) { // There's already a timer for GC'ing, just return @@ -3460,9 +3474,11 @@ nsJSContext::PokeGC(js::gcreason::Reason aReason) static bool first = true; sGCTimer->InitWithFuncCallback(GCTimerFired, reinterpret_cast(aReason), - first - ? NS_FIRST_GC_DELAY - : NS_GC_DELAY, + aDelay + ? aDelay + : (first + ? NS_FIRST_GC_DELAY + : NS_GC_DELAY), nsITimer::TYPE_ONE_SHOT); first = false; @@ -3549,11 +3565,11 @@ nsJSContext::GC(js::gcreason::Reason aReason) } static void -DOMGCFinishedCallback(JSRuntime *rt, JSCompartment *comp, const char *status) +DOMGCSliceCallback(JSRuntime *aRt, js::GCProgress aProgress, const js::GCDescription &aDesc) { NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread"); - if (sPostGCEventsToConsole) { + if (aDesc.logMessage && sPostGCEventsToConsole) { PRTime now = PR_Now(); PRTime delta = 0; if (sFirstCollectionTime) { @@ -3565,45 +3581,66 @@ DOMGCFinishedCallback(JSRuntime *rt, JSCompartment *comp, const char *status) NS_NAMED_LITERAL_STRING(kFmt, "GC(T+%.1f) %s"); nsString msg; msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), - double(delta) / PR_USEC_PER_SEC, status)); + double(delta) / PR_USEC_PER_SEC, + aDesc.logMessage)); nsCOMPtr cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID); if (cs) { cs->LogStringMessage(msg.get()); } } - sCCollectedWaitingForGC = 0; - sCleanupSinceLastGC = false; + // Prevent cycle collections during incremental GC. + if (aProgress == js::GC_CYCLE_BEGIN) { + sCCLockedOut = true; + } else if (aProgress == js::GC_CYCLE_END) { + sCCLockedOut = false; + } - if (sGCTimer) { - // If we were waiting for a GC to happen, kill the timer. + // The GC has more work to do, so schedule another GC slice. + if (aProgress == js::GC_SLICE_END) { nsJSContext::KillGCTimer(); + nsJSContext::KillCCTimer(); - // If this is a compartment GC, restart it. We still want - // a full GC to happen. Compartment GCs usually happen as a - // result of last-ditch or MaybeGC. In both cases its - // probably a time of heavy activity and we want to delay - // the full GC, but we do want it to happen eventually. - if (comp) { - nsJSContext::PokeGC(js::gcreason::POST_COMPARTMENT); + nsJSContext::PokeGC(js::gcreason::INTER_SLICE_GC, NS_INTERSLICE_GC_DELAY); + } - // We poked the GC, so we can kill any pending CC here. - nsJSContext::KillCCTimer(); + if (aProgress == js::GC_CYCLE_END) { + sCCollectedWaitingForGC = 0; + sCleanupSinceLastGC = false; + + if (sGCTimer) { + // If we were waiting for a GC to happen, kill the timer. + nsJSContext::KillGCTimer(); + + // If this is a compartment GC, restart it. We still want + // a full GC to happen. Compartment GCs usually happen as a + // result of last-ditch or MaybeGC. In both cases its + // probably a time of heavy activity and we want to delay + // the full GC, but we do want it to happen eventually. + if (aDesc.isCompartment) { + nsJSContext::PokeGC(js::gcreason::POST_COMPARTMENT); + + // We poked the GC, so we can kill any pending CC here. + nsJSContext::KillCCTimer(); + } + } else { + // If this was a full GC, poke the CC to run soon. + if (!aDesc.isCompartment) { + sGCHasRun = true; + nsJSContext::MaybePokeCC(); + } } - } else { - // If this was a full GC, poke the CC to run soon. - if (!comp) { - sGCHasRun = true; - nsJSContext::MaybePokeCC(); + + // If we didn't end up scheduling a GC, make sure that we release GC buffers + // soon after canceling previous shrinking attempt. + nsJSContext::KillShrinkGCBuffersTimer(); + if (!sGCTimer) { + nsJSContext::PokeShrinkGCBuffers(); } } - // If we didn't end up scheduling a GC, make sure that we release GC buffers - // soon after canceling previous shrinking attempt - nsJSContext::KillShrinkGCBuffersTimer(); - if (!sGCTimer) { - nsJSContext::PokeShrinkGCBuffers(); - } + if (sPrevGCSliceCallback) + (*sPrevGCSliceCallback)(aRt, aProgress, aDesc); } // Script object mananagement - note duplicate implementation @@ -3697,6 +3734,7 @@ nsJSRuntime::Startup() // initialize all our statics, so that we can restart XPCOM sGCTimer = sCCTimer = nsnull; sGCHasRun = false; + sCCLockedOut = false; sLastCCEndTime = 0; sPendingLoadCount = 0; sLoadingInProgress = false; @@ -3768,10 +3806,27 @@ SetMemoryMaxPrefChangedCallback(const char* aPrefName, void* aClosure) static int SetMemoryGCModePrefChangedCallback(const char* aPrefName, void* aClosure) { - bool enableCompartmentGC = Preferences::GetBool(aPrefName); - JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MODE, enableCompartmentGC - ? JSGC_MODE_COMPARTMENT - : JSGC_MODE_GLOBAL); + PRBool enableCompartmentGC = Preferences::GetBool("javascript.options.mem.gc_per_compartment"); + PRBool enableIncrementalGC = Preferences::GetBool("javascript.options.mem.gc_incremental"); + JSGCMode mode; + if (enableIncrementalGC) { + mode = JSGC_MODE_INCREMENTAL; + } else if (enableCompartmentGC) { + mode = JSGC_MODE_COMPARTMENT; + } else { + mode = JSGC_MODE_GLOBAL; + } + JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MODE, mode); + return 0; +} + +static int +SetMemoryGCSliceTimePrefChangedCallback(const char* aPrefName, void* aClosure) +{ + PRInt32 pref = Preferences::GetInt(aPrefName, -1); + // handle overflow and negative pref values + if (pref > 0 && pref < 100000) + JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_SLICE_TIME_BUDGET, pref); return 0; } @@ -3858,7 +3913,7 @@ nsJSRuntime::Init() // Let's make sure that our main thread is the same as the xpcom main thread. NS_ASSERTION(NS_IsMainThread(), "bad"); - ::JS_SetGCFinishedCallback(sRuntime, DOMGCFinishedCallback); + sPrevGCSliceCallback = js::SetGCSliceCallback(sRuntime, DOMGCSliceCallback); JSSecurityCallbacks *callbacks = JS_GetRuntimeSecurityCallbacks(sRuntime); NS_ASSERTION(callbacks, "SecMan should have set security callbacks!"); @@ -3903,6 +3958,16 @@ nsJSRuntime::Init() SetMemoryGCModePrefChangedCallback("javascript.options.mem.gc_per_compartment", nsnull); + Preferences::RegisterCallback(SetMemoryGCModePrefChangedCallback, + "javascript.options.mem.gc_incremental"); + SetMemoryGCModePrefChangedCallback("javascript.options.mem.gc_incremental", + nsnull); + + Preferences::RegisterCallback(SetMemoryGCSliceTimePrefChangedCallback, + "javascript.options.mem.gc_incremental_slice_ms"); + SetMemoryGCSliceTimePrefChangedCallback("javascript.options.mem.gc_incremental_slice_ms", + nsnull); + nsCOMPtr obs = mozilla::services::GetObserverService(); if (!obs) return NS_ERROR_FAILURE; diff --git a/dom/base/nsJSEnvironment.h b/dom/base/nsJSEnvironment.h index 43b503e976b..fca6c2a918c 100644 --- a/dom/base/nsJSEnvironment.h +++ b/dom/base/nsJSEnvironment.h @@ -188,7 +188,7 @@ public: static void CycleCollectNow(nsICycleCollectorListener *aListener = nsnull, PRInt32 aExtraForgetSkippableCalls = 0); - static void PokeGC(js::gcreason::Reason aReason); + static void PokeGC(js::gcreason::Reason aReason, int aDelay = 0); static void KillGCTimer(); static void PokeShrinkGCBuffers(); diff --git a/dom/bluetooth/BluetoothAdapter.cpp b/dom/bluetooth/BluetoothAdapter.cpp deleted file mode 100644 index 80faefc2a4a..00000000000 --- a/dom/bluetooth/BluetoothAdapter.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* vim: set ts=2 et sw=2 tw=40: */ -/* 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 "BluetoothAdapter.h" -#include "nsDOMClassInfo.h" - -USING_BLUETOOTH_NAMESPACE - -BluetoothAdapter::BluetoothAdapter() : mPower(false) -{ -} - -NS_INTERFACE_MAP_BEGIN(BluetoothAdapter) - NS_INTERFACE_MAP_ENTRY(nsIDOMBluetoothAdapter) - NS_INTERFACE_MAP_ENTRY(nsISupports) - NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(BluetoothAdapter) -NS_INTERFACE_MAP_END - -NS_IMPL_ADDREF(BluetoothAdapter) -NS_IMPL_RELEASE(BluetoothAdapter) - -DOMCI_DATA(BluetoothAdapter, BluetoothAdapter) - -NS_IMETHODIMP -BluetoothAdapter::GetPower(bool* aPower) -{ - *aPower = mPower; - return NS_OK; -} - -NS_IMETHODIMP -BluetoothAdapter::SetPower(bool aPower) -{ - mPower = aPower; - return NS_OK; -} diff --git a/dom/bluetooth/BluetoothAdapter.h b/dom/bluetooth/BluetoothAdapter.h deleted file mode 100644 index 3db9a26f735..00000000000 --- a/dom/bluetooth/BluetoothAdapter.h +++ /dev/null @@ -1,28 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* vim: set ts=2 et sw=2 tw=40: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_bluetooth_bluetoothadapter_h__ -#define mozilla_dom_bluetooth_bluetoothadapter_h__ - -#include "BluetoothCommon.h" -#include "nsIDOMBluetoothAdapter.h" - -BEGIN_BLUETOOTH_NAMESPACE - -class BluetoothAdapter : public nsIDOMBluetoothAdapter -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIDOMBLUETOOTHADAPTER - - BluetoothAdapter(); - -protected: - bool mPower; -}; - -END_BLUETOOTH_NAMESPACE -#endif diff --git a/dom/bluetooth/BluetoothCommon.h b/dom/bluetooth/BluetoothCommon.h deleted file mode 100644 index e7443592652..00000000000 --- a/dom/bluetooth/BluetoothCommon.h +++ /dev/null @@ -1,19 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* vim: set ts=2 et sw=2 tw=40: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_bluetooth_bluetoothcommon_h__ -#define mozilla_dom_bluetooth_bluetoothcommon_h__ - -#define BEGIN_BLUETOOTH_NAMESPACE \ - namespace mozilla { namespace dom { namespace bluetooth { -#define END_BLUETOOTH_NAMESPACE \ - } /* namespace bluetooth */ } /* namespace dom */ } /* namespace mozilla */ -#define USING_BLUETOOTH_NAMESPACE \ - using namespace mozilla::dom::bluetooth; - -class nsIDOMBluetooth; - -#endif // mozilla_dom_bluetooth_bluetoothcommon_h__ diff --git a/dom/bluetooth/Makefile.in b/dom/bluetooth/Makefile.in deleted file mode 100644 index 7d782e5356c..00000000000 --- a/dom/bluetooth/Makefile.in +++ /dev/null @@ -1,30 +0,0 @@ -# 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/. - -DEPTH = ../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -MODULE = dom -LIBRARY_NAME = dombluetooth_s -XPIDL_MODULE = dom_bluetooth -LIBXUL_LIBRARY = 1 -FORCE_STATIC_LIB = 1 - -include $(topsrcdir)/dom/dom-config.mk - -CPPSRCS = \ - BluetoothAdapter.cpp \ - $(NULL) - -XPIDLSRCS = \ - nsIDOMNavigatorBluetooth.idl \ - nsIDOMBluetoothAdapter.idl \ - $(NULL) - -include $(topsrcdir)/config/rules.mk - diff --git a/dom/bluetooth/nsIDOMBluetoothAdapter.idl b/dom/bluetooth/nsIDOMBluetoothAdapter.idl deleted file mode 100644 index 1dc2e1265fd..00000000000 --- a/dom/bluetooth/nsIDOMBluetoothAdapter.idl +++ /dev/null @@ -1,13 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* vim: set ts=2 et sw=2 tw=40: */ -/* 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 "nsISupports.idl" - -[scriptable, builtinclass, uuid(29689a22-45ff-4ccf-b552-5364ce3a3642)] -interface nsIDOMBluetoothAdapter : nsISupports -{ - attribute boolean power; -}; diff --git a/dom/bluetooth/nsIDOMNavigatorBluetooth.idl b/dom/bluetooth/nsIDOMNavigatorBluetooth.idl deleted file mode 100644 index 18f28de44f1..00000000000 --- a/dom/bluetooth/nsIDOMNavigatorBluetooth.idl +++ /dev/null @@ -1,15 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* vim: set ts=2 et sw=2 tw=40: */ -/* 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 "nsISupports.idl" - -interface nsIDOMBluetoothAdapter; - -[scriptable, uuid(677f2c2d-c4d1-41ea-addc-21d30d0d3858)] -interface nsIDOMNavigatorBluetooth : nsISupports -{ - readonly attribute nsIDOMBluetoothAdapter mozBluetooth; -}; diff --git a/dom/dom-config.mk b/dom/dom-config.mk index 69004508d42..ac61255020c 100644 --- a/dom/dom-config.mk +++ b/dom/dom-config.mk @@ -31,9 +31,5 @@ DOM_SRCDIRS += \ $(NULL) endif -ifdef MOZ_B2G_BT -DOM_SRCDIRS += dom/bluetooth -endif - LOCAL_INCLUDES += $(DOM_SRCDIRS:%=-I$(topsrcdir)/%) DEFINES += -D_IMPL_NS_LAYOUT diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index 2f070969e86..bf6b7c8d9a6 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -70,7 +70,7 @@ interface nsIDOMFile; interface nsIFile; interface nsIDOMTouch; -[scriptable, uuid(ab6e9c71-8aa1-40bb-8bf9-65e16429055f)] +[scriptable, uuid(73b48170-55d5-11e1-b86c-0800200c9a66)] interface nsIDOMWindowUtils : nsISupports { /** @@ -992,6 +992,12 @@ interface nsIDOMWindowUtils : nsISupports { [optional] out long aDBRefCnt, [optional] out long aSliceRefCnt); + /** + * Return whether incremental GC has been disabled due to a binary add-on. + */ + [implicit_jscontext] + boolean isIncrementalGCEnabled(); + /** * Begin opcode-level profiling of all JavaScript execution in the window's * runtime. diff --git a/dom/interfaces/css/nsIDOMCSSStyleDeclaration.idl b/dom/interfaces/css/nsIDOMCSSStyleDeclaration.idl index d50ddf86a49..83959e9f6ba 100644 --- a/dom/interfaces/css/nsIDOMCSSStyleDeclaration.idl +++ b/dom/interfaces/css/nsIDOMCSSStyleDeclaration.idl @@ -60,7 +60,7 @@ interface nsIDOMCSSStyleDeclaration : nsISupports DOMString getPropertyPriority(in DOMString propertyName); void setProperty(in DOMString propertyName, in DOMString value, - in DOMString priority) + [optional] in DOMString priority) raises(DOMException); readonly attribute unsigned long length; DOMString item(in unsigned long index); diff --git a/dom/interfaces/events/nsIDOMNSEvent.idl b/dom/interfaces/events/nsIDOMNSEvent.idl index d01fe33a4c4..b64c83955f7 100644 --- a/dom/interfaces/events/nsIDOMNSEvent.idl +++ b/dom/interfaces/events/nsIDOMNSEvent.idl @@ -44,7 +44,7 @@ #endif %} -[scriptable, uuid(9be8096b-f795-4045-9664-0c275f36fe5b)] +[scriptable, uuid(07F905C1-9170-4103-86CA-766374DA149A)] interface nsIDOMNSEvent : nsISupports { const long MOUSEDOWN = 0x00000001; @@ -97,12 +97,6 @@ interface nsIDOMNSEvent : nsISupports * contain anonymous content. */ readonly attribute nsIDOMEventTarget explicitOriginalTarget; - /* XXX This is TEMPORARY. - * The original target, without any retargeting (like textnode retargeting). - * This really needs to be in .originalTarget, but this is a less risky way of - * fixing it. - */ - [noscript] readonly attribute nsIDOMEventTarget tmpRealOriginalTarget; /** * @deprecated Use nsIDOMEvent::stopPropagation. diff --git a/dom/plugins/base/nsJSNPRuntime.cpp b/dom/plugins/base/nsJSNPRuntime.cpp index a05a68833be..4f81b25aec0 100644 --- a/dom/plugins/base/nsJSNPRuntime.cpp +++ b/dom/plugins/base/nsJSNPRuntime.cpp @@ -179,7 +179,7 @@ CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj, NPObject *npobj, static JSClass sNPObjectJSWrapperClass = { NPRUNTIME_JSCLASS_NAME, - JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE, + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE, NPObjWrapper_AddProperty, NPObjWrapper_DelProperty, NPObjWrapper_GetProperty, NPObjWrapper_SetProperty, (JSEnumerateOp)NPObjWrapper_newEnumerate, diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp index fa9cecdb5c8..bf0caf76302 100644 --- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -3115,8 +3115,13 @@ nsresult nsPluginHost::NewPluginURLStream(const nsString& aURL, // deal with headers and post data nsCOMPtr httpChannel(do_QueryInterface(channel)); if (httpChannel) { - rv = httpChannel->SetReferrer(doc->GetDocumentURI()); - NS_ENSURE_SUCCESS(rv,rv); + if (!aPostStream) { + // Only set the Referer header for GET requests because IIS throws + // errors about malformed requests if we include it in POSTs. See + // bug 724465. + rv = httpChannel->SetReferrer(doc->GetDocumentURI()); + NS_ENSURE_SUCCESS(rv,rv); + } if (aPostStream) { // XXX it's a bit of a hack to rewind the postdata stream diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp index b67de18fbcf..b64642c6f38 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -1390,7 +1390,19 @@ void nsPluginInstanceOwner::CARefresh(nsITimer *aTimer, void *aClosure) { } } -void nsPluginInstanceOwner::AddToCARefreshTimer(nsPluginInstanceOwner *aPluginInstance) { +void nsPluginInstanceOwner::AddToCARefreshTimer() { + if (!mInstance) { + return; + } + + // Flash invokes InvalidateRect for us. + const char* mime = nsnull; + if (NS_SUCCEEDED(mInstance->GetMIMEType(&mime)) && mime) { + if (strcmp(mime, "application/x-shockwave-flash") == 0) { + return; + } + } + if (!sCARefreshListeners) { sCARefreshListeners = new nsTArray(); if (!sCARefreshListeners) { @@ -1398,9 +1410,11 @@ void nsPluginInstanceOwner::AddToCARefreshTimer(nsPluginInstanceOwner *aPluginIn } } - NS_ASSERTION(!sCARefreshListeners->Contains(aPluginInstance), - "pluginInstanceOwner already registered as a listener"); - sCARefreshListeners->AppendElement(aPluginInstance); + if (sCARefreshListeners->Contains(this)) { + return; + } + + sCARefreshListeners->AppendElement(this); if (!sCATimer) { sCATimer = new nsCOMPtr(); @@ -1416,12 +1430,12 @@ void nsPluginInstanceOwner::AddToCARefreshTimer(nsPluginInstanceOwner *aPluginIn } } -void nsPluginInstanceOwner::RemoveFromCARefreshTimer(nsPluginInstanceOwner *aPluginInstance) { - if (!sCARefreshListeners || sCARefreshListeners->Contains(aPluginInstance) == false) { +void nsPluginInstanceOwner::RemoveFromCARefreshTimer() { + if (!sCARefreshListeners || sCARefreshListeners->Contains(this) == false) { return; } - sCARefreshListeners->RemoveElement(aPluginInstance); + sCARefreshListeners->RemoveElement(this); if (sCARefreshListeners->Length() == 0) { if (sCATimer) { @@ -1434,21 +1448,6 @@ void nsPluginInstanceOwner::RemoveFromCARefreshTimer(nsPluginInstanceOwner *aPlu } } -void nsPluginInstanceOwner::SetupCARefresh() -{ - if (!mInstance) { - return; - } - - const char* mime = nsnull; - if (NS_SUCCEEDED(mInstance->GetMIMEType(&mime)) && mime) { - // Flash invokes InvalidateRect for us. - if (strcmp(mime, "application/x-shockwave-flash") != 0) { - AddToCARefreshTimer(this); - } -} -} - void nsPluginInstanceOwner::RenderCoreAnimation(CGContextRef aCGContext, int aWidth, int aHeight) { @@ -2699,7 +2698,7 @@ nsPluginInstanceOwner::Destroy() CancelTimer(); #endif #ifdef XP_MACOSX - RemoveFromCARefreshTimer(this); + RemoveFromCARefreshTimer(); if (mColorProfile) ::CGColorSpaceRelease(mColorProfile); #endif @@ -3277,7 +3276,7 @@ NS_IMETHODIMP nsPluginInstanceOwner::CreateWidget(void) { NS_ENSURE_TRUE(mPluginWindow, NS_ERROR_NULL_POINTER); - nsresult rv = NS_ERROR_FAILURE; + nsresult rv = NS_ERROR_FAILURE; // Can't call this twice! if (mWidget) { @@ -3318,6 +3317,21 @@ NS_IMETHODIMP nsPluginInstanceOwner::CreateWidget(void) mWidget->EnableDragDrop(true); mWidget->Show(false); mWidget->Enable(false); + +#ifdef XP_MACOSX + // Now that we have a widget we want to set the event model before + // any events are processed. + nsCOMPtr pluginWidget = do_QueryInterface(mWidget); + if (!pluginWidget) { + return NS_ERROR_FAILURE; + } + pluginWidget->SetPluginEventModel(GetEventModel()); + pluginWidget->SetPluginDrawingModel(GetDrawingModel()); + + if (GetDrawingModel() == NPDrawingModelCoreAnimation) { + AddToCARefreshTimer(); + } +#endif } if (mObjectFrame) { @@ -3669,10 +3683,13 @@ void nsPluginInstanceOwner::SetFrame(nsObjectFrame *aFrame) container->SetCurrentImage(nsnull); } - // If we had an old frame and we're not going to have a new one then - // we should unregister for some things. +#if defined(XP_MACOSX) && !defined(NP_NO_QUICKDRAW) if (!aFrame) { - // Unregister scroll position listeners + // At this point we had a frame but it is going away and we're not getting a new one. + // Unregister for a scroll position listening, which is only required for Carbon + // event model plugins on Mac OS X. It's OK to unregister when we didn't register, + // so don't be strict about unregistering. Better to unregister when we didn't have to + // than to not unregister when we should. for (nsIFrame* f = mObjectFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) { nsIScrollableFrame* sf = do_QueryFrame(f); if (sf) { @@ -3680,21 +3697,28 @@ void nsPluginInstanceOwner::SetFrame(nsObjectFrame *aFrame) } } } +#endif // Make sure the old frame isn't holding a reference to us. mObjectFrame->SetInstanceOwner(nsnull); } else { + // Scroll position listening is only required for Carbon event model plugins on Mac OS X. + // Note that we probably have a crash bug in the way we register/unregister, bug 723190. + // Bug 723190 is mitigated by limiting registration to Carbon event model plugins. +#if defined(XP_MACOSX) && !defined(NP_NO_QUICKDRAW) if (aFrame) { - // We didn't have an object frame before but we do now! - // We need to register a scroll position listener on every scrollable - // frame up to the top - for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) { - nsIScrollableFrame* sf = do_QueryFrame(f); - if (sf) { - sf->AddScrollPositionListener(this); + // We didn't have an object frame before but we do now. We need to register a scroll + // position listener on every scrollable frame up to the top. + if (GetEventModel() == NPEventModelCarbon) { + for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) { + nsIScrollableFrame* sf = do_QueryFrame(f); + if (sf) { + sf->AddScrollPositionListener(this); + } } } } +#endif } // Swap in the new frame (or no frame) diff --git a/dom/plugins/base/nsPluginInstanceOwner.h b/dom/plugins/base/nsPluginInstanceOwner.h index 4e4a029c248..1eacec927a3 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.h +++ b/dom/plugins/base/nsPluginInstanceOwner.h @@ -182,9 +182,8 @@ public: bool IsRemoteDrawingCoreAnimation(); NPEventModel GetEventModel(); static void CARefresh(nsITimer *aTimer, void *aClosure); - static void AddToCARefreshTimer(nsPluginInstanceOwner *aPluginInstance); - static void RemoveFromCARefreshTimer(nsPluginInstanceOwner *aPluginInstance); - void SetupCARefresh(); + void AddToCARefreshTimer(); + void RemoveFromCARefreshTimer(); // This calls into the plugin (NPP_SetWindow) and can run script. void* FixUpPluginWindow(PRInt32 inPaintState); void HidePluginWindow(); diff --git a/dom/plugins/test/mochitest/Makefile.in b/dom/plugins/test/mochitest/Makefile.in index ca7423294ab..c19a56748c7 100644 --- a/dom/plugins/test/mochitest/Makefile.in +++ b/dom/plugins/test/mochitest/Makefile.in @@ -109,6 +109,8 @@ _MOCHITEST_FILES = \ test_instance_unparent1.html \ test_instance_unparent2.html \ test_instance_unparent3.html \ + test_pluginstream_referer.html \ + plugin-stream-referer.sjs \ $(NULL) # test_plugin_scroll_painting.html \ bug 596491 diff --git a/dom/plugins/test/mochitest/plugin-stream-referer.sjs b/dom/plugins/test/mochitest/plugin-stream-referer.sjs new file mode 100644 index 00000000000..cca3e14dee9 --- /dev/null +++ b/dom/plugins/test/mochitest/plugin-stream-referer.sjs @@ -0,0 +1,10 @@ +function handleRequest(request, response) +{ + response.setHeader('Content-Type', 'text/plain', false); + if (request.hasHeader('Referer')) { + response.write('Referer found: ' + request.getHeader('Referer')); + } + else { + response.write('No Referer found'); + } +} diff --git a/dom/plugins/test/mochitest/test_pluginstream_referer.html b/dom/plugins/test/mochitest/test_pluginstream_referer.html new file mode 100644 index 00000000000..d96aac76c61 --- /dev/null +++ b/dom/plugins/test/mochitest/test_pluginstream_referer.html @@ -0,0 +1,42 @@ + + Do plugin stream requests send the Referer header correctly? + + + + +

+ + + + + diff --git a/dom/src/events/nsJSEventListener.cpp b/dom/src/events/nsJSEventListener.cpp index cac6b24467e..b02dd3bf982 100644 --- a/dom/src/events/nsJSEventListener.cpp +++ b/dom/src/events/nsJSEventListener.cpp @@ -233,6 +233,8 @@ nsJSEventListener::HandleEvent(nsIDOMEvent* aEvent) "JSEventListener has wrong script context?"); #endif nsCOMPtr vrv; + xpc_UnmarkGrayObject(mScopeObject); + xpc_UnmarkGrayObject(mHandler); rv = mContext->CallEventHandler(mTarget, mScopeObject, mHandler, iargv, getter_AddRefs(vrv)); diff --git a/dom/wifi/nsIWifi.idl b/dom/wifi/nsIWifi.idl index 5cc39dbfea0..e89d6920a16 100644 --- a/dom/wifi/nsIWifi.idl +++ b/dom/wifi/nsIWifi.idl @@ -19,7 +19,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): - * Philipp von Weitershausen + * Blake Kaplan * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -37,6 +37,14 @@ #include "nsISupports.idl" +[scriptable, uuid(14c815f0-e9db-41d4-a15e-f3e69140f83b)] +interface nsIWifiNetwork : nsISupports { + readonly attribute DOMString ssid; // can be null + readonly attribute DOMString bssid; // can be null + readonly attribute DOMString flags; // TODO make this be real flags instead of a string + readonly attribute long signal; +}; + [scriptable, uuid(9DCE05BF-659C-4427-A050-0EAC3BB6C1C0)] interface nsIWifi : nsISupports { }; diff --git a/dom/wifi/nsWifiWorker.js b/dom/wifi/nsWifiWorker.js index 93eaea68406..913354e2d7b 100644 --- a/dom/wifi/nsWifiWorker.js +++ b/dom/wifi/nsWifiWorker.js @@ -795,6 +795,15 @@ var WifiManager = (function() { return manager; })(); +function WifiNetwork(ssid, bssid, flags, signal) { + this.ssid = ssid; + this.bssid = bssid; + this.flags = flags; + this.signal = Number(signal); +} + +WifiNetwork.prototype.QueryInterface = XPCOMUtils.generateQI([Ci.nsIWifiNetwork]); + function nsWifiWorker() { WifiManager.onsupplicantconnection = function() { debug("Connected to supplicant"); @@ -806,48 +815,61 @@ function nsWifiWorker() { debug("Couldn't connect to supplicant"); } - var state; + var self = this; + + this.state = null; + this.networks = Object.create(null); WifiManager.onstatechange = function() { - debug("State change: " + state + " -> " + this.state); - if (state === "SCANNING" && this.state === "INACTIVE") { - // We're not trying to connect so try to find an open Mozilla network. - // TODO Remove me in favor of UI and a way to select a network. + debug("State change: " + self.state + " -> " + this.state); + self.state = this.state; - debug("Haven't connected to a network, trying a default (for now)"); - var name = "Mozilla"; - var net = networks[name]; - if (net && (net[1] && net[1] !== "[IBSS]")) { - debug("Network Mozilla exists, but is encrypted"); - net = null; - } - if (!net) { - name = "Mozilla Guest"; - net = networks[name]; - if (!net || (net[1] && net[1] !== "[IBSS]")) { - debug("Network Mozilla Guest doesn't exist or is encrypted"); - return; - } - } - - var config = Object.create(null); - config["ssid"] = '"' + name + '"'; - config["key_mgmt"] = "NONE"; - WifiManager.addNetwork(config, function(ok) { - if (!ok) { - debug("Unable to add the network!"); - return; - } - - WifiManager.enableNetwork(config.netId, false, function(ok) { - debug((ok ? "Successfully enabled " : "Didn't enable ") + name); - }); - }); - } - - state = this.state; + // TODO Worth adding a more generic API for this? + if (self.state === "INACTIVE" && connectToMozilla.waiting) + connectToMozilla(); } - var networks = Object.create(null); + function connectToMozilla() { + if (self.state !== "INACTIVE") { + connectToMozilla.waiting = true; + return; + } + + // We're not trying to connect so try to find an open Mozilla network. + // TODO Remove me in favor of UI and a way to select a network. + + debug("Haven't connected to a network, trying a default (for now)"); + var name = "Mozilla"; + var net = self.networks[name]; + if (net && (net.flags && net.flags !== "[IBSS]")) { + debug("Network Mozilla exists, but is encrypted"); + net = null; + } + + var config = Object.create(null); + if (!net) { + name = "Mozilla Guest"; + net = self.networks[name]; + if (!net || (net.flags && net.flags !== "[IBSS]")) { + debug("Can't find either network, trying to force 'Mozilla Guest'"); + config.scan_ssid = 1; + } + } + + config.ssid = '"' + name + '"'; + config.key_mgmt = "NONE"; + WifiManager.addNetwork(config, function(ok) { + if (!ok) { + debug("Unable to add the network!"); + return; + } + + WifiManager.enableNetwork(config.netId, false, function(ok) { + debug((ok ? "Successfully enabled " : "Didn't enable ") + name); + }); + }); + } + this.waitForScan(connectToMozilla); + WifiManager.onscanresultsavailable = function() { debug("Scan results are available! Asking for them."); WifiManager.getScanResults(function(r) { @@ -855,12 +877,18 @@ function nsWifiWorker() { // NB: Skip the header line. for (let i = 1; i < lines.length; ++i) { // bssid / frequency / signal level / flags / ssid - var match = /([\S]+)\s+([\S]+)\s+([\S]+)\s+(\[[\S]+\])?\s+(.*)/.exec(lines[i]) - if (match) - networks[match[5]] = [match[1], match[4]]; - else + var match = /([\S]+)\s+([\S]+)\s+([\S]+)\s+(\[[\S]+\])?\s+(.*)/.exec(lines[i]); + + // TODO Choose bssid based on strength? + if (match && match[5]) + self.networks[match[5]] = new WifiNetwork(match[5], match[1], match[4], match[3]); + else if (!match) debug("Match didn't find anything for: " + lines[i]); } + + if (self.wantScanResults) { + self.wantScanResults(); + } }); } @@ -885,6 +913,18 @@ nsWifiWorker.prototype = { QueryInterface: XPCOMUtils.generateQI([Ci.nsIWorkerHolder, Ci.nsIWifi]), + // Internal methods. + waitForScan: function(callback) { + if (this.wantScanResults) { + var older = this.wantScanResults; + this.wantScanResults = function() { callback(); older(); }; + } else { + this.wantScanResults = callback; + } + }, + + // nsIWifi + setWifiEnabled: function(enable) { WifiManager.setWifiEnabled(enable, function (ok) { debug(ok); diff --git a/dom/workers/File.cpp b/dom/workers/File.cpp index 29975086f70..109ea97b258 100644 --- a/dom/workers/File.cpp +++ b/dom/workers/File.cpp @@ -171,14 +171,14 @@ private: } static JSBool - MozSlice(JSContext* aCx, uintN aArgc, jsval* aVp) + Slice(JSContext* aCx, uintN aArgc, jsval* aVp) { JSObject* obj = JS_THIS_OBJECT(aCx, aVp); if (!obj) { return false; } - nsIDOMBlob* blob = GetInstancePrivate(aCx, obj, "mozSlice"); + nsIDOMBlob* blob = GetInstancePrivate(aCx, obj, "slice"); if (!blob) { return false; } @@ -197,10 +197,10 @@ private: PRUint8 optionalArgc = aArgc; nsCOMPtr rtnBlob; - if (NS_FAILED(blob->MozSlice(static_cast(start), - static_cast(end), - contentType, optionalArgc, - getter_AddRefs(rtnBlob)))) { + if (NS_FAILED(blob->Slice(static_cast(start), + static_cast(end), + contentType, optionalArgc, + getter_AddRefs(rtnBlob)))) { ThrowFileExceptionForCode(aCx, FILE_NOT_READABLE_ERR); return false; } @@ -230,7 +230,7 @@ JSPropertySpec Blob::sProperties[] = { }; JSFunctionSpec Blob::sFunctions[] = { - JS_FN("mozSlice", MozSlice, 1, JSPROP_ENUMERATE), + JS_FN("slice", Slice, 1, JSPROP_ENUMERATE), JS_FS_END }; diff --git a/dom/workers/ListenerManager.cpp b/dom/workers/ListenerManager.cpp index 94f27947854..1cc7c930b11 100644 --- a/dom/workers/ListenerManager.cpp +++ b/dom/workers/ListenerManager.cpp @@ -107,6 +107,9 @@ struct Listener : PRCList static void Remove(JSContext* aCx, Listener* aListener) { + if (js::IsIncrementalBarrierNeeded(aCx)) + js::IncrementalValueBarrier(aListener->mListenerVal); + PR_REMOVE_LINK(aListener); JS_free(aCx, aListener); } diff --git a/dom/workers/Worker.cpp b/dom/workers/Worker.cpp index 9b589cf2cee..e90f99f1802 100644 --- a/dom/workers/Worker.cpp +++ b/dom/workers/Worker.cpp @@ -300,7 +300,7 @@ private: JSClass Worker::sClass = { "Worker", - JSCLASS_HAS_PRIVATE, + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize, NULL, NULL, NULL, NULL, NULL, NULL, Trace, NULL @@ -415,7 +415,7 @@ private: JSClass ChromeWorker::sClass = { "ChromeWorker", - JSCLASS_HAS_PRIVATE, + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize, NULL, NULL, NULL, NULL, NULL, NULL, Trace, NULL diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index 864521c0e83..828c1354b1b 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -799,7 +799,7 @@ private: JSClass DedicatedWorkerGlobalScope::sClass = { "DedicatedWorkerGlobalScope", - JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, + JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_NEW_RESOLVE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, reinterpret_cast(Resolve), JS_ConvertStub, Finalize, NULL, NULL, NULL, NULL, NULL, NULL, Trace, NULL diff --git a/dom/workers/XMLHttpRequest.cpp b/dom/workers/XMLHttpRequest.cpp index 4d1ed06f7fd..b30bfc92038 100644 --- a/dom/workers/XMLHttpRequest.cpp +++ b/dom/workers/XMLHttpRequest.cpp @@ -220,7 +220,7 @@ private: JSClass XMLHttpRequestUpload::sClass = { "XMLHttpRequestUpload", - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT), + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize, NULL, NULL, NULL, NULL, NULL, NULL, Trace, NULL @@ -769,7 +769,7 @@ private: JSClass XMLHttpRequest::sClass = { "XMLHttpRequest", - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT), + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize, NULL, NULL, NULL, NULL, NULL, NULL, Trace, NULL diff --git a/dom/workers/test/Makefile.in b/dom/workers/test/Makefile.in index e3c610caa70..27a7138b045 100644 --- a/dom/workers/test/Makefile.in +++ b/dom/workers/test/Makefile.in @@ -136,21 +136,21 @@ _CHROME_TEST_FILES = \ test_extension.xul \ test_extensionBootstrap.xul \ test_file.xul \ - test_fileMozSlice.xul \ + test_fileSlice.xul \ test_fileBlobPosting.xul \ test_filePosting.xul \ test_fileReaderSync.xul \ test_fileReaderSyncErrors.xul \ - test_fileReadMozSlice.xul \ + test_fileReadSlice.xul \ test_fileSubWorker.xul \ test_fileBlobSubWorker.xul \ file_worker.js \ fileBlob_worker.js \ - fileMozSlice_worker.js \ + fileSlice_worker.js \ filePosting_worker.js \ fileReaderSync_worker.js \ fileReaderSyncErrors_worker.js \ - fileReadMozSlice_worker.js \ + fileReadSlice_worker.js \ fileSubWorker_worker.js \ fileBlobSubWorker_worker.js \ WorkerTest.jsm \ diff --git a/dom/workers/test/fileReadMozSlice_worker.js b/dom/workers/test/fileReadSlice_worker.js similarity index 89% rename from dom/workers/test/fileReadMozSlice_worker.js rename to dom/workers/test/fileReadSlice_worker.js index 659eb1eca28..2c10b7d7633 100644 --- a/dom/workers/test/fileReadMozSlice_worker.js +++ b/dom/workers/test/fileReadSlice_worker.js @@ -7,7 +7,7 @@ onmessage = function(event) { var start = event.data.start; var end = event.data.end; - var slicedBlob = blob.mozSlice(start, end); + var slicedBlob = blob.slice(start, end); var fileReader = new FileReaderSync(); var text = fileReader.readAsText(slicedBlob); diff --git a/dom/workers/test/fileMozSlice_worker.js b/dom/workers/test/fileSlice_worker.js similarity index 81% rename from dom/workers/test/fileMozSlice_worker.js rename to dom/workers/test/fileSlice_worker.js index edeb82def54..d0c6364e2e2 100644 --- a/dom/workers/test/fileMozSlice_worker.js +++ b/dom/workers/test/fileSlice_worker.js @@ -11,11 +11,11 @@ onmessage = function(event) { var slicedBlob; if (contentType == undefined && end == undefined) { - slicedBlob = blob.mozSlice(start); + slicedBlob = blob.slice(start); } else if (contentType == undefined) { - slicedBlob = blob.mozSlice(start, end); + slicedBlob = blob.slice(start, end); } else { - slicedBlob = blob.mozSlice(start, end, contentType); + slicedBlob = blob.slice(start, end, contentType); } var rtnObj = new Object(); diff --git a/dom/workers/test/test_fileBlobPosting.xul b/dom/workers/test/test_fileBlobPosting.xul index c849f026159..7537e9332a4 100644 --- a/dom/workers/test/test_fileBlobPosting.xul +++ b/dom/workers/test/test_fileBlobPosting.xul @@ -70,7 +70,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=664783 finish(); }; - var blob = file.mozSlice(); + var blob = file.slice(); worker.postMessage(blob); waitForWorkerFinish(); } diff --git a/dom/workers/test/test_fileBlobSubWorker.xul b/dom/workers/test/test_fileBlobSubWorker.xul index d2cf4e113e5..eda8daac8cf 100644 --- a/dom/workers/test/test_fileBlobSubWorker.xul +++ b/dom/workers/test/test_fileBlobSubWorker.xul @@ -72,7 +72,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=664783 finish(); }; - var blob = file.mozSlice(); + var blob = file.slice(); worker.postMessage(blob); waitForWorkerFinish(); } diff --git a/dom/workers/test/test_fileReadMozSlice.xul b/dom/workers/test/test_fileReadSlice.xul similarity index 85% rename from dom/workers/test/test_fileReadMozSlice.xul rename to dom/workers/test/test_fileReadSlice.xul index c701585179f..6249613a943 100644 --- a/dom/workers/test/test_fileReadMozSlice.xul +++ b/dom/workers/test/test_fileReadSlice.xul @@ -36,7 +36,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=664783 var testFile = Components.classes["@mozilla.org/file/directory_service;1"] .getService(Components.interfaces.nsIProperties) .get("ProfD", Components.interfaces.nsIFile); - testFile.append("workerReadMozSlice" + fileNum++); + testFile.append("workerReadSlice" + fileNum++); var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"] .createInstance(Components.interfaces.nsIFileOutputStream); @@ -55,8 +55,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=664783 * Creates a worker which slices a blob to the given start and end offset and * reads the content as text. */ - function readMozSlice(blob, start, end, expectedText) { - var worker = new Worker("fileReadMozSlice_worker.js"); + function readSlice(blob, start, end, expectedText) { + var worker = new Worker("fileReadSlice_worker.js"); worker.onerror = function(event) { ok(false, "Worker had an error: " + event.data); @@ -74,16 +74,16 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=664783 } // Empty file. - readMozSlice(createFileWithData(""), 0, 0, ""); + readSlice(createFileWithData(""), 0, 0, ""); // Typical use case. - readMozSlice(createFileWithData("HelloBye"), 5, 8, "Bye"); + readSlice(createFileWithData("HelloBye"), 5, 8, "Bye"); // End offset too large. - readMozSlice(createFileWithData("HelloBye"), 5, 9, "Bye"); + readSlice(createFileWithData("HelloBye"), 5, 9, "Bye"); // Start of file. - readMozSlice(createFileWithData("HelloBye"), 0, 5, "Hello"); + readSlice(createFileWithData("HelloBye"), 0, 5, "Hello"); ]]> diff --git a/dom/workers/test/test_fileMozSlice.xul b/dom/workers/test/test_fileSlice.xul similarity index 80% rename from dom/workers/test/test_fileMozSlice.xul rename to dom/workers/test/test_fileSlice.xul index 05240b03d83..7756acd87b6 100644 --- a/dom/workers/test/test_fileMozSlice.xul +++ b/dom/workers/test/test_fileSlice.xul @@ -37,7 +37,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=664783 .getService(Components.interfaces.nsIProperties) .get("ProfD", Components.interfaces.nsIFile); var fileExtension = (extension == undefined) ? "" : "." + extension; - testFile.append("workerMozSlice" + fileNum++ + fileExtension); + testFile.append("workerSlice" + fileNum++ + fileExtension); var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"] .createInstance(Components.interfaces.nsIFileOutputStream); @@ -56,8 +56,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=664783 * Starts a worker which slices the blob to the given start offset and optional end offset and * content type. It then verifies that the size and type of the sliced blob is correct. */ - function createMozSlice(blob, start, expectedLength, /** optional */ end, /** optional */ contentType) { - var worker = new Worker("fileMozSlice_worker.js"); + function createSlice(blob, start, expectedLength, /** optional */ end, /** optional */ contentType) { + var worker = new Worker("fileSlice_worker.js"); worker.onerror = function(event) { ok(false, "Worker had an error: " + event.data); @@ -76,30 +76,30 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=664783 } // Empty file. - createMozSlice(createFileWithData(""), 0, 0, 0); + createSlice(createFileWithData(""), 0, 0, 0); // Typical use case. - createMozSlice(createFileWithData("Hello"), 1, 1, 2); + createSlice(createFileWithData("Hello"), 1, 1, 2); // Longish file. var text = ""; for (var i = 0; i < 10000; ++i) { text += "long"; } - createMozSlice(createFileWithData(text), 2000, 2000, 4000); + createSlice(createFileWithData(text), 2000, 2000, 4000); // Slice to different type. - createMozSlice(createFileWithData("text", "txt"), 0, 2, 2, "image/png"); + createSlice(createFileWithData("text", "txt"), 0, 2, 2, "image/png"); // Length longer than blob. - createMozSlice(createFileWithData("text"), 0, 4, 20); + createSlice(createFileWithData("text"), 0, 4, 20); // Start longer than blob. - createMozSlice(createFileWithData("text"), 20, 0, 4); + createSlice(createFileWithData("text"), 20, 0, 4); // No optional arguments - createMozSlice(createFileWithData("text"), 0, 4); - createMozSlice(createFileWithData("text"), 2, 2); + createSlice(createFileWithData("text"), 0, 4); + createSlice(createFileWithData("text"), 2, 2); ]]> diff --git a/editor/composer/src/nsEditorSpellCheck.cpp b/editor/composer/src/nsEditorSpellCheck.cpp index 3778f8f0a31..5eed950c5e4 100644 --- a/editor/composer/src/nsEditorSpellCheck.cpp +++ b/editor/composer/src/nsEditorSpellCheck.cpp @@ -83,7 +83,7 @@ class UpdateDictionnaryHolder { #define CPS_PREF_NAME NS_LITERAL_STRING("spellcheck.lang") -class LastDictionary { +class LastDictionary MOZ_FINAL { public: /** * Store current dictionary for editor document url. Use content pref diff --git a/editor/idl/nsIEditor.idl b/editor/idl/nsIEditor.idl index bd489a1d6d5..052592a7f74 100644 --- a/editor/idl/nsIEditor.idl +++ b/editor/idl/nsIEditor.idl @@ -55,7 +55,7 @@ interface nsIEditActionListener; interface nsIInlineSpellChecker; interface nsITransferable; -[scriptable, uuid(20ee0b70-c528-11e0-9572-0800200c9a66)] +[scriptable, uuid(94479B76-7FD7-47D3-BB1E-5B77846339D2)] interface nsIEditor : nsISupports { @@ -360,26 +360,6 @@ interface nsIEditor : nsISupports /** sets the document selection to the end of the document */ void endOfDocument(); - /* ------------ Drag/Drop methods -------------- */ - - /** - * canDrag decides if a drag should be started - * (for example, based on the current selection and mousepoint). - */ - boolean canDrag(in nsIDOMEvent aEvent); - - /** - * doDrag transfers the relevant data (as appropriate) - * to a transferable so it can later be dropped. - */ - void doDrag(in nsIDOMEvent aEvent); - - /** - * insertFromDrop looks for a dragsession and inserts the - * relevant data in response to a drop. - */ - void insertFromDrop(in nsIDOMEvent aEvent); - /* ------------ Node manipulation methods -------------- */ /** diff --git a/editor/idl/nsIHTMLEditor.idl b/editor/idl/nsIHTMLEditor.idl index f43529dc383..6bc187a8355 100644 --- a/editor/idl/nsIHTMLEditor.idl +++ b/editor/idl/nsIHTMLEditor.idl @@ -52,7 +52,7 @@ interface nsIContentFilter; NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_EDITOR, 1) %} -[scriptable, uuid(d58f35a7-c269-4292-b9aa-a79e200a7c99)] +[scriptable, uuid(FF67AD39-ED58-4CD1-A1A3-DCD988390A97)] interface nsIHTMLEditor : nsISupports { @@ -199,26 +199,6 @@ interface nsIHTMLEditor : nsISupports */ void decreaseFontSize(); - /* ------------ Drag/Drop methods -------------- */ - - /** - * canDrag decides if a drag should be started - * (for example, based on the current selection and mousepoint). - */ - boolean canDrag(in nsIDOMEvent aEvent); - - /** - * doDrag transfers the relevant data (as appropriate) - * to a transferable so it can later be dropped. - */ - void doDrag(in nsIDOMEvent aEvent); - - /** - * insertFromDrop looks for a dragsession and inserts the - * relevant data in response to a drop. - */ - void insertFromDrop(in nsIDOMEvent aEvent); - /* ------------ HTML content methods -------------- */ /** @@ -534,12 +514,6 @@ interface nsIHTMLEditor : nsISupports */ void setBodyAttribute(in AString aAttr, in AString aValue); - /** - * XXX Used to suppress spurious drag/drop events to workaround bug 50703 - * Don't use this method! It will go away after first release! - */ - void ignoreSpuriousDragEvent(in boolean aIgnoreSpuriousDragEvent); - /** * Find all the nodes in the document which contain references * to outside URIs (e.g. a href, img src, script src, etc.) diff --git a/editor/libeditor/base/nsEditor.cpp b/editor/libeditor/base/nsEditor.cpp index 9010eee2de1..197c9f2b000 100644 --- a/editor/libeditor/base/nsEditor.cpp +++ b/editor/libeditor/base/nsEditor.cpp @@ -1191,25 +1191,6 @@ nsEditor::CanPasteTransferable(nsITransferable *aTransferable, bool *aCanPaste) return NS_ERROR_NOT_IMPLEMENTED; } -NS_IMETHODIMP -nsEditor::CanDrag(nsIDOMEvent *aEvent, bool *aCanDrag) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -nsEditor::DoDrag(nsIDOMEvent *aEvent) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -nsEditor::InsertFromDrop(nsIDOMEvent *aEvent) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - - NS_IMETHODIMP nsEditor::SetAttribute(nsIDOMElement *aElement, const nsAString & aAttribute, const nsAString & aValue) { diff --git a/editor/libeditor/base/nsEditor.h b/editor/libeditor/base/nsEditor.h index 81af35574e3..349d52f278e 100644 --- a/editor/libeditor/base/nsEditor.h +++ b/editor/libeditor/base/nsEditor.h @@ -757,6 +757,20 @@ public: // added here. void OnFocus(nsIDOMEventTarget* aFocusEventTarget); + // Used to insert content from a data transfer into the editable area. + // This is called for each item in the data transfer, with the index of + // each item passed as aIndex. + virtual nsresult InsertFromDataTransfer(nsIDOMDataTransfer *aDataTransfer, + PRInt32 aIndex, + nsIDOMDocument *aSourceDoc, + nsIDOMNode *aDestinationNode, + PRInt32 aDestOffset, + bool aDoDeleteSelection) = 0; + + virtual nsresult InsertFromDrop(nsIDOMEvent* aDropEvent) = 0; + + virtual already_AddRefed FindUserSelectAllNode(nsIDOMNode* aNode) { return nsnull; } + protected: PRUint32 mModCount; // number of modifications (for undo/redo stack) diff --git a/editor/libeditor/base/nsEditorEventListener.cpp b/editor/libeditor/base/nsEditorEventListener.cpp index 6948a30dbfc..50aa05206c5 100644 --- a/editor/libeditor/base/nsEditorEventListener.cpp +++ b/editor/libeditor/base/nsEditorEventListener.cpp @@ -61,8 +61,6 @@ // Drag & Drop, Clipboard #include "nsIServiceManager.h" #include "nsIClipboard.h" -#include "nsIDragService.h" -#include "nsIDragSession.h" #include "nsIContent.h" #include "nsISupportsPrimitives.h" #include "nsIDOMRange.h" @@ -158,11 +156,6 @@ nsEditorEventListener::InstallToEditor() NS_LITERAL_STRING("keypress"), NS_EVENT_FLAG_BUBBLE | NS_EVENT_FLAG_SYSTEM_EVENT); - // See bug 455215, we cannot use the standard dragstart event yet - elmP->AddEventListenerByType(this, - NS_LITERAL_STRING("draggesture"), - NS_EVENT_FLAG_BUBBLE | - NS_EVENT_FLAG_SYSTEM_EVENT); elmP->AddEventListenerByType(this, NS_LITERAL_STRING("dragenter"), NS_EVENT_FLAG_BUBBLE | @@ -255,10 +248,6 @@ nsEditorEventListener::UninstallFromEditor() NS_LITERAL_STRING("keypress"), NS_EVENT_FLAG_BUBBLE | NS_EVENT_FLAG_SYSTEM_EVENT); - elmP->RemoveEventListenerByType(this, - NS_LITERAL_STRING("draggesture"), - NS_EVENT_FLAG_BUBBLE | - NS_EVENT_FLAG_SYSTEM_EVENT); elmP->RemoveEventListenerByType(this, NS_LITERAL_STRING("dragenter"), NS_EVENT_FLAG_BUBBLE | @@ -332,8 +321,6 @@ nsEditorEventListener::HandleEvent(nsIDOMEvent* aEvent) nsCOMPtr dragEvent = do_QueryInterface(aEvent); if (dragEvent) { - if (eventType.EqualsLiteral("draggesture")) - return DragGesture(dragEvent); if (eventType.EqualsLiteral("dragenter")) return DragEnter(dragEvent); if (eventType.EqualsLiteral("dragover")) @@ -658,18 +645,6 @@ nsEditorEventListener::HandleText(nsIDOMEvent* aTextEvent) * Drag event implementation */ -nsresult -nsEditorEventListener::DragGesture(nsIDOMDragEvent* aDragEvent) -{ - // ...figure out if a drag should be started... - bool canDrag; - nsresult rv = mEditor->CanDrag(aDragEvent, &canDrag); - if ( NS_SUCCEEDED(rv) && canDrag ) - rv = mEditor->DoDrag(aDragEvent); - - return rv; -} - nsresult nsEditorEventListener::DragEnter(nsIDOMDragEvent* aDragEvent) { @@ -723,6 +698,10 @@ nsEditorEventListener::DragOver(nsIDOMDragEvent* aDragEvent) } else { + // This is needed when dropping on an input, to prevent the editor for + // the editable parent from receiving the event. + aDragEvent->StopPropagation(); + if (mCaret) { mCaret->EraseCaret(); @@ -794,8 +773,6 @@ nsEditorEventListener::Drop(nsIDOMDragEvent* aMouseEvent) aMouseEvent->StopPropagation(); aMouseEvent->PreventDefault(); - // Beware! This may flush notifications via synchronous - // ScrollSelectionIntoView. return mEditor->InsertFromDrop(aMouseEvent); } diff --git a/editor/libeditor/base/nsEditorUtils.h b/editor/libeditor/base/nsEditorUtils.h index 1bdab22e5b7..debe2dc05fa 100644 --- a/editor/libeditor/base/nsEditorUtils.h +++ b/editor/libeditor/base/nsEditorUtils.h @@ -257,7 +257,6 @@ class nsEditorUtils }; -class nsIDragSession; class nsITransferable; class nsIDOMEvent; class nsISimpleEnumerator; diff --git a/editor/libeditor/base/tests/Makefile.in b/editor/libeditor/base/tests/Makefile.in index b697234bbbb..df93aa38c99 100644 --- a/editor/libeditor/base/tests/Makefile.in +++ b/editor/libeditor/base/tests/Makefile.in @@ -56,6 +56,7 @@ _CHROME_TEST_FILES = \ test_selection_move_commands.xul \ test_bug46555.html \ test_bug646194.xul \ + test_dragdrop.html \ $(NULL) libs:: $(_TEST_FILES) diff --git a/editor/libeditor/base/tests/test_dragdrop.html b/editor/libeditor/base/tests/test_dragdrop.html new file mode 100644 index 00000000000..b5eb855df54 --- /dev/null +++ b/editor/libeditor/base/tests/test_dragdrop.html @@ -0,0 +1,163 @@ + + + + + + + + + + + + Some Text + + + +

This is some editable text.

+ + + + diff --git a/editor/libeditor/html/nsHTMLAnonymousUtils.cpp b/editor/libeditor/html/nsHTMLAnonymousUtils.cpp index b84725db881..923bfbd2fe9 100644 --- a/editor/libeditor/html/nsHTMLAnonymousUtils.cpp +++ b/editor/libeditor/html/nsHTMLAnonymousUtils.cpp @@ -102,7 +102,7 @@ static PRInt32 GetCSSFloatValue(nsIDOMCSSStyleDeclaration * aDecl, return (PRInt32) f; } -class nsElementDeletionObserver : public nsIMutationObserver +class nsElementDeletionObserver MOZ_FINAL : public nsIMutationObserver { public: nsElementDeletionObserver(nsINode* aNativeAnonNode, nsINode* aObservedNode) diff --git a/editor/libeditor/html/nsHTMLDataTransfer.cpp b/editor/libeditor/html/nsHTMLDataTransfer.cpp index 9a0e513ab01..ca2dbf9807f 100644 --- a/editor/libeditor/html/nsHTMLDataTransfer.cpp +++ b/editor/libeditor/html/nsHTMLDataTransfer.cpp @@ -64,6 +64,8 @@ #include "nsIContent.h" #include "nsIContentIterator.h" #include "nsIDOMRange.h" +#include "nsIDOMDOMStringList.h" +#include "nsIDOMDragEvent.h" #include "nsCOMArray.h" #include "nsIFile.h" #include "nsIURL.h" @@ -131,9 +133,6 @@ const PRUnichar nbsp = 160; static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID); -// private clipboard data flavors for html copy/paste -#define kHTMLContext "text/_moz_htmlcontext" -#define kHTMLInfo "text/_moz_htmlinfo" #define kInsertCookie "_moz_Insert Here_moz_" #define NS_FOUND_TARGET NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_EDITOR, 3) @@ -1204,6 +1203,116 @@ nsHTMLEditor::ParseCFHTML(nsCString & aCfhtml, PRUnichar **aStuffToPaste, PRUnic return NS_OK; } +bool nsHTMLEditor::IsSafeToInsertData(nsIDOMDocument* aSourceDoc) +{ + // Try to determine whether we should use a sanitizing fragment sink + bool isSafe = false; + nsCOMPtr destdomdoc; + GetDocument(getter_AddRefs(destdomdoc)); + + nsCOMPtr destdoc = do_QueryInterface(destdomdoc); + NS_ASSERTION(destdoc, "Where is our destination doc?"); + nsCOMPtr container = destdoc->GetContainer(); + nsCOMPtr dsti(do_QueryInterface(container)); + nsCOMPtr root; + if (dsti) + dsti->GetRootTreeItem(getter_AddRefs(root)); + nsCOMPtr docShell(do_QueryInterface(root)); + PRUint32 appType; + if (docShell && NS_SUCCEEDED(docShell->GetAppType(&appType))) + isSafe = appType == nsIDocShell::APP_TYPE_EDITOR; + if (!isSafe && aSourceDoc) { + nsCOMPtr srcdoc = do_QueryInterface(aSourceDoc); + NS_ASSERTION(srcdoc, "Where is our source doc?"); + + nsIPrincipal* srcPrincipal = srcdoc->NodePrincipal(); + nsIPrincipal* destPrincipal = destdoc->NodePrincipal(); + NS_ASSERTION(srcPrincipal && destPrincipal, "How come we don't have a principal?"); + srcPrincipal->Subsumes(destPrincipal, &isSafe); + } + + return isSafe; +} + +nsresult nsHTMLEditor::InsertObject(const char* aType, nsISupports* aObject, bool aIsSafe, + nsIDOMDocument *aSourceDoc, + nsIDOMNode *aDestinationNode, + PRInt32 aDestOffset, + bool aDoDeleteSelection) +{ + nsresult rv; + + const char* type = aType; + + // Check to see if we can insert an image file + bool insertAsImage = false; + nsCOMPtr fileURI; + if (0 == nsCRT::strcmp(type, kFileMime)) + { + nsCOMPtr fileObj(do_QueryInterface(aObject)); + if (fileObj) + { + rv = NS_NewFileURI(getter_AddRefs(fileURI), fileObj); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr mime = do_GetService("@mozilla.org/mime;1"); + NS_ENSURE_TRUE(mime, NS_ERROR_FAILURE); + nsCAutoString contentType; + rv = mime->GetTypeFromFile(fileObj, contentType); + NS_ENSURE_SUCCESS(rv, rv); + + // Accept any image type fed to us + if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("image/"))) { + insertAsImage = true; + type = contentType.get(); + } + } + } + + if (0 == nsCRT::strcmp(type, kJPEGImageMime) || + 0 == nsCRT::strcmp(type, kPNGImageMime) || + 0 == nsCRT::strcmp(type, kGIFImageMime) || + insertAsImage) + { + nsCOMPtr imageStream; + if (insertAsImage) { + NS_ASSERTION(fileURI, "The file URI should be retrieved earlier"); + rv = NS_OpenURI(getter_AddRefs(imageStream), fileURI); + NS_ENSURE_SUCCESS(rv, rv); + } else { + imageStream = do_QueryInterface(aObject); + NS_ENSURE_TRUE(imageStream, NS_ERROR_FAILURE); + } + + nsCString imageData; + rv = NS_ConsumeStream(imageStream, PR_UINT32_MAX, imageData); + NS_ENSURE_SUCCESS(rv, rv); + + rv = imageStream->Close(); + NS_ENSURE_SUCCESS(rv, rv); + + char * base64 = PL_Base64Encode(imageData.get(), imageData.Length(), nsnull); + NS_ENSURE_TRUE(base64, NS_ERROR_OUT_OF_MEMORY); + + nsAutoString stuffToPaste; + stuffToPaste.AssignLiteral("\"\""); + nsAutoEditBatch beginBatching(this); + rv = DoInsertHTMLWithContext(stuffToPaste, EmptyString(), EmptyString(), + NS_LITERAL_STRING(kFileMime), + aSourceDoc, + aDestinationNode, aDestOffset, + aDoDeleteSelection, + aIsSafe); + PR_Free(base64); + } + + return NS_OK; +} + NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable, nsIDOMDocument *aSourceDoc, const nsAString & aContextStr, @@ -1226,59 +1335,16 @@ NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable printf("Got flavor [%s]\n", bestFlavor.get()); #endif - // Try to determine whether we should use a sanitizing fragment sink - bool isSafe = false; - nsCOMPtr destdomdoc; - rv = GetDocument(getter_AddRefs(destdomdoc)); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr destdoc = do_QueryInterface(destdomdoc); - NS_ASSERTION(destdoc, "Where is our destination doc?"); - nsCOMPtr container = destdoc->GetContainer(); - nsCOMPtr dsti(do_QueryInterface(container)); - nsCOMPtr root; - if (dsti) - dsti->GetRootTreeItem(getter_AddRefs(root)); - nsCOMPtr docShell(do_QueryInterface(root)); - PRUint32 appType; - if (docShell && NS_SUCCEEDED(docShell->GetAppType(&appType))) - isSafe = appType == nsIDocShell::APP_TYPE_EDITOR; - if (!isSafe && aSourceDoc) { - nsCOMPtr srcdoc = do_QueryInterface(aSourceDoc); - NS_ASSERTION(srcdoc, "Where is our source doc?"); + bool isSafe = IsSafeToInsertData(aSourceDoc); - nsIPrincipal* srcPrincipal = srcdoc->NodePrincipal(); - nsIPrincipal* destPrincipal = destdoc->NodePrincipal(); - NS_ASSERTION(srcPrincipal && destPrincipal, "How come we don't have a principal?"); - rv = srcPrincipal->Subsumes(destPrincipal, &isSafe); - NS_ENSURE_SUCCESS(rv, rv); + if (0 == nsCRT::strcmp(bestFlavor, kFileMime) || + 0 == nsCRT::strcmp(bestFlavor, kJPEGImageMime) || + 0 == nsCRT::strcmp(bestFlavor, kPNGImageMime) || + 0 == nsCRT::strcmp(bestFlavor, kGIFImageMime)) { + rv = InsertObject(bestFlavor, genericDataObj, isSafe, + aSourceDoc, aDestinationNode, aDestOffset, aDoDeleteSelection); } - - // Check to see if we can insert an image file - bool insertAsImage = false; - nsCOMPtr fileURI; - if (0 == nsCRT::strcmp(bestFlavor, kFileMime)) - { - nsCOMPtr fileObj(do_QueryInterface(genericDataObj)); - if (fileObj && len > 0) - { - rv = NS_NewFileURI(getter_AddRefs(fileURI), fileObj); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr mime = do_GetService("@mozilla.org/mime;1"); - NS_ENSURE_TRUE(mime, NS_ERROR_FAILURE); - nsCAutoString contentType; - rv = mime->GetTypeFromFile(fileObj, contentType); - NS_ENSURE_SUCCESS(rv, rv); - - // Accept any image type fed to us - if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("image/"))) { - insertAsImage = true; - bestFlavor = contentType; - } - } - } - - if (0 == nsCRT::strcmp(bestFlavor, kNativeHTMLMime)) + else if (0 == nsCRT::strcmp(bestFlavor, kNativeHTMLMime)) { // note cf_html uses utf8, hence use length = len, not len/2 as in flavors below nsCOMPtr textDataObj(do_QueryInterface(genericDataObj)); @@ -1302,8 +1368,9 @@ NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable } } } - else if (0 == nsCRT::strcmp(bestFlavor, kHTMLMime)) - { + else if (0 == nsCRT::strcmp(bestFlavor, kHTMLMime) || + 0 == nsCRT::strcmp(bestFlavor, kUnicodeMime) || + 0 == nsCRT::strcmp(bestFlavor, kMozTextInternal)) { nsCOMPtr textDataObj(do_QueryInterface(genericDataObj)); if (textDataObj && len > 0) { @@ -1311,419 +1378,135 @@ NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable textDataObj->GetData(text); NS_ASSERTION(text.Length() <= (len/2), "Invalid length!"); stuffToPaste.Assign(text.get(), len / 2); + nsAutoEditBatch beginBatching(this); - rv = DoInsertHTMLWithContext(stuffToPaste, - aContextStr, aInfoStr, flavor, - aSourceDoc, - aDestinationNode, aDestOffset, - aDoDeleteSelection, - isSafe); + if (0 == nsCRT::strcmp(bestFlavor, kHTMLMime)) { + rv = DoInsertHTMLWithContext(stuffToPaste, + aContextStr, aInfoStr, flavor, + aSourceDoc, + aDestinationNode, aDestOffset, + aDoDeleteSelection, + isSafe); + } else { + rv = InsertTextAt(stuffToPaste, aDestinationNode, aDestOffset, aDoDeleteSelection); + } } } - else if (0 == nsCRT::strcmp(bestFlavor, kUnicodeMime) || - 0 == nsCRT::strcmp(bestFlavor, kMozTextInternal)) - { - nsCOMPtr textDataObj(do_QueryInterface(genericDataObj)); - if (textDataObj && len > 0) - { - nsAutoString text; - textDataObj->GetData(text); - NS_ASSERTION(text.Length() <= (len/2), "Invalid length!"); - stuffToPaste.Assign(text.get(), len / 2); - nsAutoEditBatch beginBatching(this); - // need to provide a hook from this point - rv = InsertTextAt(stuffToPaste, aDestinationNode, aDestOffset, aDoDeleteSelection); - } - } - else if (0 == nsCRT::strcmp(bestFlavor, kJPEGImageMime) || - 0 == nsCRT::strcmp(bestFlavor, kPNGImageMime) || - 0 == nsCRT::strcmp(bestFlavor, kGIFImageMime) || - insertAsImage) - { - nsCOMPtr imageStream; - if (insertAsImage) { - NS_ASSERTION(fileURI, "The file URI should be retrieved earlier"); - rv = NS_OpenURI(getter_AddRefs(imageStream), fileURI); - NS_ENSURE_SUCCESS(rv, rv); - } else { - imageStream = do_QueryInterface(genericDataObj); - NS_ENSURE_TRUE(imageStream, NS_ERROR_FAILURE); - } - - nsCString imageData; - rv = NS_ConsumeStream(imageStream, PR_UINT32_MAX, imageData); - NS_ENSURE_SUCCESS(rv, rv); - - rv = imageStream->Close(); - NS_ENSURE_SUCCESS(rv, rv); - - char * base64 = PL_Base64Encode(imageData.get(), imageData.Length(), nsnull); - NS_ENSURE_TRUE(base64, NS_ERROR_OUT_OF_MEMORY); - - stuffToPaste.AssignLiteral("\"\""); - nsAutoEditBatch beginBatching(this); - rv = DoInsertHTMLWithContext(stuffToPaste, EmptyString(), EmptyString(), - NS_LITERAL_STRING(kFileMime), - aSourceDoc, - aDestinationNode, aDestOffset, - aDoDeleteSelection, - isSafe); - PR_Free(base64); - } } - - // Try to scroll the selection into view if the paste/drop succeeded - // After ScrollSelectionIntoView(), the pending notifications might be - // flushed and PresShell/PresContext/Frames may be dead. See bug 418470. + // Try to scroll the selection into view if the paste succeeded if (NS_SUCCEEDED(rv)) ScrollSelectionIntoView(false); return rv; } -NS_IMETHODIMP nsHTMLEditor::InsertFromDrop(nsIDOMEvent* aDropEvent) +static void +GetStringFromDataTransfer(nsIDOMDataTransfer *aDataTransfer, const nsAString& aType, + PRInt32 aIndex, nsAString& aOutputString) { - ForceCompositionEnd(); + nsCOMPtr variant; + aDataTransfer->MozGetDataAt(aType, aIndex, getter_AddRefs(variant)); + if (variant) + variant->GetAsAString(aOutputString); +} + +nsresult nsHTMLEditor::InsertFromDataTransfer(nsIDOMDataTransfer *aDataTransfer, + PRInt32 aIndex, + nsIDOMDocument *aSourceDoc, + nsIDOMNode *aDestinationNode, + PRInt32 aDestOffset, + bool aDoDeleteSelection) +{ + nsresult rv = NS_OK; + + nsCOMPtr types; + aDataTransfer->MozTypesAt(aIndex, getter_AddRefs(types)); + + bool hasPrivateHTMLFlavor; + types->Contains(NS_LITERAL_STRING(kHTMLContext), &hasPrivateHTMLFlavor); + + bool isText = IsPlaintextEditor(); + bool isSafe = IsSafeToInsertData(aSourceDoc); - nsresult rv; - nsCOMPtr dragService = - do_GetService("@mozilla.org/widget/dragservice;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); + PRUint32 length; + types->GetLength(&length); + for (PRUint32 t = 0; t < length; t++) { + nsAutoString type; + types->Item(t, type); - nsCOMPtr dragSession; - dragService->GetCurrentSession(getter_AddRefs(dragSession)); - NS_ENSURE_TRUE(dragSession, NS_OK); - - // transferable hooks here - nsCOMPtr domdoc; - GetDocument(getter_AddRefs(domdoc)); - - // find out if we have our internal html flavor on the clipboard. We don't want to mess - // around with cfhtml if we do. - bool bHavePrivateHTMLFlavor = false; - rv = dragSession->IsDataFlavorSupported(kHTMLContext, &bHavePrivateHTMLFlavor); - NS_ENSURE_SUCCESS(rv, rv); - - // Get the nsITransferable interface for getting the data from the drop - nsCOMPtr trans; - rv = PrepareHTMLTransferable(getter_AddRefs(trans), bHavePrivateHTMLFlavor); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(trans, NS_OK); // NS_ERROR_FAILURE; SHOULD WE FAIL? - - PRUint32 numItems = 0; - rv = dragSession->GetNumDropItems(&numItems); - NS_ENSURE_SUCCESS(rv, rv); - - // Combine any deletion and drop insertion into one transaction - nsAutoEditBatch beginBatching(this); - - // We never have to delete if selection is already collapsed - bool deleteSelection = false; - nsCOMPtr newSelectionParent; - PRInt32 newSelectionOffset = 0; - - // Source doc is null if source is *not* the current editor document - nsCOMPtr srcdomdoc; - rv = dragSession->GetSourceDocument(getter_AddRefs(srcdomdoc)); - NS_ENSURE_SUCCESS(rv, rv); - - PRUint32 i; - bool doPlaceCaret = true; - for (i = 0; i < numItems; ++i) - { - rv = dragSession->GetData(trans, i); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(trans, NS_OK); // NS_ERROR_FAILURE; Should we fail? - - // get additional html copy hints, if present - nsAutoString contextStr, infoStr; - nsCOMPtr contextDataObj, infoDataObj; - PRUint32 contextLen, infoLen; - nsCOMPtr textDataObj; - - nsCOMPtr contextTrans = - do_CreateInstance("@mozilla.org/widget/transferable;1"); - NS_ENSURE_TRUE(contextTrans, NS_ERROR_NULL_POINTER); - contextTrans->AddDataFlavor(kHTMLContext); - dragSession->GetData(contextTrans, i); - contextTrans->GetTransferData(kHTMLContext, getter_AddRefs(contextDataObj), &contextLen); - - nsCOMPtr infoTrans = - do_CreateInstance("@mozilla.org/widget/transferable;1"); - NS_ENSURE_TRUE(infoTrans, NS_ERROR_NULL_POINTER); - infoTrans->AddDataFlavor(kHTMLInfo); - dragSession->GetData(infoTrans, i); - infoTrans->GetTransferData(kHTMLInfo, getter_AddRefs(infoDataObj), &infoLen); - - if (contextDataObj) - { - nsAutoString text; - textDataObj = do_QueryInterface(contextDataObj); - textDataObj->GetData(text); - NS_ASSERTION(text.Length() <= (contextLen/2), "Invalid length!"); - contextStr.Assign(text.get(), contextLen / 2); - } - - if (infoDataObj) - { - nsAutoString text; - textDataObj = do_QueryInterface(infoDataObj); - textDataObj->GetData(text); - NS_ASSERTION(text.Length() <= (infoLen/2), "Invalid length!"); - infoStr.Assign(text.get(), infoLen / 2); - } - - if (doPlaceCaret) - { - // check if the user pressed the key to force a copy rather than a move - // if we run into problems here, we'll just assume the user doesn't want a copy - bool userWantsCopy = false; - - nsCOMPtr uiEvent = do_QueryInterface(aDropEvent); - NS_ENSURE_TRUE(uiEvent, NS_ERROR_FAILURE); - - nsCOMPtr mouseEvent = do_QueryInterface(aDropEvent); - if (mouseEvent) { -#if defined(XP_MACOSX) - mouseEvent->GetAltKey(&userWantsCopy); -#else - mouseEvent->GetCtrlKey(&userWantsCopy); -#endif - } - - // Current doc is destination - nsCOMPtr destdomdoc; - rv = GetDocument(getter_AddRefs(destdomdoc)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr selection; - rv = GetSelection(getter_AddRefs(selection)); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); - - bool isCollapsed; - rv = selection->GetIsCollapsed(&isCollapsed); - NS_ENSURE_SUCCESS(rv, rv); - - // Parent and offset under the mouse cursor - rv = uiEvent->GetRangeParent(getter_AddRefs(newSelectionParent)); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(newSelectionParent, NS_ERROR_FAILURE); - - rv = uiEvent->GetRangeOffset(&newSelectionOffset); - NS_ENSURE_SUCCESS(rv, rv); - - // XXX: This userSelectNode code is a workaround for bug 195957. - // - // Check to see if newSelectionParent is part of a "-moz-user-select: all" - // subtree. If it is, we need to make sure we don't drop into it! - - nsCOMPtr userSelectNode = FindUserSelectAllNode(newSelectionParent); - - if (userSelectNode) - { - // The drop is happening over a "-moz-user-select: all" - // subtree so make sure the content we insert goes before - // the root of the subtree. - // - // XXX: Note that inserting before the subtree matches the - // current behavior when dropping on top of an image. - // The decision for dropping before or after the - // subtree should really be done based on coordinates. - - rv = GetNodeLocation(userSelectNode, address_of(newSelectionParent), - &newSelectionOffset); - - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(newSelectionParent, NS_ERROR_FAILURE); - } - - // We never have to delete if selection is already collapsed - bool cursorIsInSelection = false; - - // Check if mouse is in the selection - if (!isCollapsed) - { - PRInt32 rangeCount; - rv = selection->GetRangeCount(&rangeCount); - NS_ENSURE_SUCCESS(rv, rv); - - for (PRInt32 j = 0; j < rangeCount; j++) - { - nsCOMPtr range; - - rv = selection->GetRangeAt(j, getter_AddRefs(range)); - if (NS_FAILED(rv) || !range) - continue;//don't bail yet, iterate through them all - - rv = range->IsPointInRange(newSelectionParent, newSelectionOffset, &cursorIsInSelection); - if(cursorIsInSelection) - break; - } - if (cursorIsInSelection) - { - // Dragging within same doc can't drop on itself -- leave! - // (We shouldn't get here - drag event shouldn't have started if over selection) - if (srcdomdoc == destdomdoc) + if (!isText) { + if (type.EqualsLiteral(kFileMime) || + type.EqualsLiteral(kJPEGImageMime) || + type.EqualsLiteral(kPNGImageMime) || + type.EqualsLiteral(kGIFImageMime)) { + nsCOMPtr variant; + aDataTransfer->MozGetDataAt(type, aIndex, getter_AddRefs(variant)); + if (variant) { + nsCOMPtr object; + variant->GetAsISupports(getter_AddRefs(object)); + rv = InsertObject(NS_ConvertUTF16toUTF8(type).get(), object, isSafe, + aSourceDoc, aDestinationNode, aDestOffset, aDoDeleteSelection); + if (NS_SUCCEEDED(rv)) return NS_OK; - - // Dragging from another window onto a selection - // XXX Decision made to NOT do this, - // note that 4.x does replace if dropped on - //deleteSelection = true; - } - else - { - // We are NOT over the selection - if (srcdomdoc == destdomdoc) - { - // Within the same doc: delete if user doesn't want to copy - deleteSelection = !userWantsCopy; - } - else - { - // Different source doc: Don't delete - deleteSelection = false; - } } } + else if (!hasPrivateHTMLFlavor && type.EqualsLiteral(kNativeHTMLMime)) { + nsAutoString text; + GetStringFromDataTransfer(aDataTransfer, NS_LITERAL_STRING(kNativeHTMLMime), aIndex, text); + NS_ConvertUTF16toUTF8 cfhtml(text); - // We have to figure out whether to delete/relocate caret only once - doPlaceCaret = false; + nsXPIDLString cfcontext, cffragment, cfselection; // cfselection left emtpy for now + + rv = ParseCFHTML(cfhtml, getter_Copies(cffragment), getter_Copies(cfcontext)); + if (NS_SUCCEEDED(rv) && !cffragment.IsEmpty()) + { + nsAutoEditBatch beginBatching(this); + rv = DoInsertHTMLWithContext(cffragment, + cfcontext, cfselection, type, + aSourceDoc, + aDestinationNode, aDestOffset, + aDoDeleteSelection, + isSafe); + if (NS_SUCCEEDED(rv)) + return NS_OK; + } + } + else if (type.EqualsLiteral(kHTMLMime)) { + nsAutoString text, contextString, infoString; + GetStringFromDataTransfer(aDataTransfer, type, aIndex, text); + GetStringFromDataTransfer(aDataTransfer, NS_LITERAL_STRING(kHTMLContext), aIndex, contextString); + GetStringFromDataTransfer(aDataTransfer, NS_LITERAL_STRING(kHTMLInfo), aIndex, infoString); + + nsAutoEditBatch beginBatching(this); + if (type.EqualsLiteral(kHTMLMime)) { + rv = DoInsertHTMLWithContext(text, + contextString, infoString, type, + aSourceDoc, + aDestinationNode, aDestOffset, + aDoDeleteSelection, + isSafe); + if (NS_SUCCEEDED(rv)) + return NS_OK; + } + } } - - // handle transferable hooks - if (!nsEditorHookUtils::DoInsertionHook(domdoc, aDropEvent, trans)) - return NS_OK; - // Beware! This may flush notifications via synchronous - // ScrollSelectionIntoView. - rv = InsertFromTransferable(trans, srcdomdoc, contextStr, infoStr, - newSelectionParent, - newSelectionOffset, deleteSelection); + if (type.EqualsLiteral(kTextMime) || + type.EqualsLiteral(kMozTextInternal)) { + nsAutoString text; + GetStringFromDataTransfer(aDataTransfer, type, aIndex, text); + + nsAutoEditBatch beginBatching(this); + rv = InsertTextAt(text, aDestinationNode, aDestOffset, aDoDeleteSelection); + if (NS_SUCCEEDED(rv)) + return NS_OK; + } } return rv; } -NS_IMETHODIMP nsHTMLEditor::CanDrag(nsIDOMEvent *aDragEvent, bool *aCanDrag) -{ - return nsPlaintextEditor::CanDrag(aDragEvent, aCanDrag); -} - -nsresult -nsHTMLEditor::PutDragDataInTransferable(nsITransferable **aTransferable) -{ - NS_ENSURE_ARG_POINTER(aTransferable); - *aTransferable = nsnull; - nsCOMPtr docEncoder; - nsresult rv = SetupDocEncoder(getter_AddRefs(docEncoder)); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE); - - // grab a string - nsAutoString buffer, parents, info; - - // find out if we're a plaintext control or not - if (!IsPlaintextEditor()) - { - // encode the selection as html with contextual info - rv = docEncoder->EncodeToStringWithContext(parents, info, buffer); - NS_ENSURE_SUCCESS(rv, rv); - } - else - { - // encode the selection - rv = docEncoder->EncodeToString(buffer); - NS_ENSURE_SUCCESS(rv, rv); - } - - // if we have an empty string, we're done; otherwise continue - if ( buffer.IsEmpty() ) - return NS_OK; - - nsCOMPtr dataWrapper, contextWrapper, infoWrapper; - - dataWrapper = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - rv = dataWrapper->SetData(buffer); - NS_ENSURE_SUCCESS(rv, rv); - - /* create html flavor transferable */ - nsCOMPtr trans = do_CreateInstance("@mozilla.org/widget/transferable;1"); - NS_ENSURE_TRUE(trans, NS_ERROR_FAILURE); - - if (IsPlaintextEditor()) - { - // Add the unicode flavor to the transferable - rv = trans->AddDataFlavor(kUnicodeMime); - NS_ENSURE_SUCCESS(rv, rv); - - // QI the data object an |nsISupports| so that when the transferable holds - // onto it, it will addref the correct interface. - nsCOMPtr genericDataObj(do_QueryInterface(dataWrapper)); - rv = trans->SetTransferData(kUnicodeMime, genericDataObj, - buffer.Length() * sizeof(PRUnichar)); - NS_ENSURE_SUCCESS(rv, rv); - } - else - { - contextWrapper = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID); - NS_ENSURE_TRUE(contextWrapper, NS_ERROR_FAILURE); - infoWrapper = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID); - NS_ENSURE_TRUE(infoWrapper, NS_ERROR_FAILURE); - - contextWrapper->SetData(parents); - infoWrapper->SetData(info); - - rv = trans->AddDataFlavor(kHTMLMime); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr htmlConverter = - do_CreateInstance("@mozilla.org/widget/htmlformatconverter;1"); - NS_ENSURE_TRUE(htmlConverter, NS_ERROR_FAILURE); - - rv = trans->SetConverter(htmlConverter); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr genericDataObj(do_QueryInterface(dataWrapper)); - rv = trans->SetTransferData(kHTMLMime, genericDataObj, - buffer.Length() * sizeof(PRUnichar)); - NS_ENSURE_SUCCESS(rv, rv); - - if (!parents.IsEmpty()) - { - // Add the htmlcontext DataFlavor to the transferable - trans->AddDataFlavor(kHTMLContext); - genericDataObj = do_QueryInterface(contextWrapper); - trans->SetTransferData(kHTMLContext, genericDataObj, - parents.Length() * sizeof(PRUnichar)); - } - if (!info.IsEmpty()) - { - // Add the htmlinfo DataFlavor to the transferable - trans->AddDataFlavor(kHTMLInfo); - genericDataObj = do_QueryInterface(infoWrapper); - trans->SetTransferData(kHTMLInfo, genericDataObj, - info.Length() * sizeof(PRUnichar)); - } - } - - *aTransferable = trans; - NS_ADDREF(*aTransferable); - return NS_OK; -} - -NS_IMETHODIMP nsHTMLEditor::DoDrag(nsIDOMEvent *aDragEvent) -{ - return nsPlaintextEditor::DoDrag(aDragEvent); -} - bool nsHTMLEditor::HavePrivateHTMLFlavor(nsIClipboard *aClipboard) { // check the clipboard for our special kHTMLContext flavor. If that is there, we know @@ -1814,8 +1597,6 @@ NS_IMETHODIMP nsHTMLEditor::Paste(PRInt32 aSelectionType) if (!nsEditorHookUtils::DoInsertionHook(domdoc, nsnull, trans)) return NS_OK; - // Beware! This may flush notifications via synchronous - // ScrollSelectionIntoView. rv = InsertFromTransferable(trans, nsnull, contextStr, infoStr, nsnull, 0, true); } @@ -1835,8 +1616,6 @@ NS_IMETHODIMP nsHTMLEditor::PasteTransferable(nsITransferable *aTransferable) if (!nsEditorHookUtils::DoInsertionHook(domdoc, nsnull, aTransferable)) return NS_OK; - // Beware! This may flush notifications via synchronous - // ScrollSelectionIntoView. nsAutoString contextStr, infoStr; return InsertFromTransferable(aTransferable, nsnull, contextStr, infoStr, nsnull, 0, true); @@ -1864,8 +1643,6 @@ NS_IMETHODIMP nsHTMLEditor::PasteNoFormatting(PRInt32 aSelectionType) if (NS_SUCCEEDED(clipboard->GetData(trans, aSelectionType)) && IsModifiable()) { const nsAFlatString& empty = EmptyString(); - // Beware! This may flush notifications via synchronous - // ScrollSelectionIntoView. rv = InsertFromTransferable(trans, nsnull, empty, empty, nsnull, 0, true); } diff --git a/editor/libeditor/html/nsHTMLEditor.cpp b/editor/libeditor/html/nsHTMLEditor.cpp index 9034f4b6e74..483146817e4 100644 --- a/editor/libeditor/html/nsHTMLEditor.cpp +++ b/editor/libeditor/html/nsHTMLEditor.cpp @@ -113,7 +113,6 @@ static char namedanchorText[] = "namedanchor"; nsHTMLEditor::nsHTMLEditor() : nsPlaintextEditor() -, mIgnoreSpuriousDragEvent(false) , mCRInParagraphCreatesParagraph(false) , mSelectedCellIndex(0) , mIsObjectResizingEnabled(true) @@ -5311,13 +5310,6 @@ nsHTMLEditor::EndUpdateViewBatch() return res; } -NS_IMETHODIMP -nsHTMLEditor::IgnoreSpuriousDragEvent(bool aIgnoreSpuriousDragEvent) -{ - mIgnoreSpuriousDragEvent = aIgnoreSpuriousDragEvent; - return NS_OK; -} - NS_IMETHODIMP nsHTMLEditor::GetSelectionContainer(nsIDOMElement ** aReturn) { diff --git a/editor/libeditor/html/nsHTMLEditor.h b/editor/libeditor/html/nsHTMLEditor.h index c4b80887822..0143039bdf7 100644 --- a/editor/libeditor/html/nsHTMLEditor.h +++ b/editor/libeditor/html/nsHTMLEditor.h @@ -370,8 +370,7 @@ public: nsCOMPtr *ioParent, PRInt32 *ioOffset, bool aNoEmptyNodes); - already_AddRefed FindUserSelectAllNode(nsIDOMNode* aNode); - + virtual already_AddRefed FindUserSelectAllNode(nsIDOMNode* aNode); /** returns the absolute position of the end points of aSelection * in the document as a text stream. @@ -572,11 +571,20 @@ protected: NS_IMETHOD InsertAsPlaintextQuotation(const nsAString & aQuotedText, bool aAddCites, nsIDOMNode **aNodeInserted); + // Return true if the data is safe to insert as the source and destination + // principals match, or we are in a editor context where this doesn't matter. + // Otherwise, the data must be sanitized first. + bool IsSafeToInsertData(nsIDOMDocument* aSourceDoc); + + nsresult InsertObject(const char* aType, nsISupports* aObject, bool aIsSafe, + nsIDOMDocument *aSourceDoc, + nsIDOMNode *aDestinationNode, + PRInt32 aDestOffset, + bool aDoDeleteSelection); // factored methods for handling insertion of data from transferables (drag&drop or clipboard) NS_IMETHOD PrepareTransferable(nsITransferable **transferable); NS_IMETHOD PrepareHTMLTransferable(nsITransferable **transferable, bool havePrivFlavor); - nsresult PutDragDataInTransferable(nsITransferable **aTransferable); NS_IMETHOD InsertFromTransferable(nsITransferable *transferable, nsIDOMDocument *aSourceDoc, const nsAString & aContextStr, @@ -584,6 +592,12 @@ protected: nsIDOMNode *aDestinationNode, PRInt32 aDestinationOffset, bool aDoDeleteSelection); + nsresult InsertFromDataTransfer(nsIDOMDataTransfer *aDataTransfer, + PRInt32 aIndex, + nsIDOMDocument *aSourceDoc, + nsIDOMNode *aDestinationNode, + PRInt32 aDestOffset, + bool aDoDeleteSelection); bool HavePrivateHTMLFlavor( nsIClipboard *clipboard ); nsresult ParseCFHTML(nsCString & aCfhtml, PRUnichar **aStuffToPaste, PRUnichar **aCfcontext); nsresult DoContentFilterCallback(const nsAString &aFlavor, @@ -722,9 +736,6 @@ protected: nsresult GetFirstEditableLeaf( nsIDOMNode *aNode, nsCOMPtr *aOutFirstLeaf); nsresult GetLastEditableLeaf( nsIDOMNode *aNode, nsCOMPtr *aOutLastLeaf); - //XXX Kludge: Used to suppress spurious drag/drop events (bug 50703) - bool mIgnoreSpuriousDragEvent; - nsresult GetInlinePropertyBase(nsIAtom *aProperty, const nsAString *aAttribute, const nsAString *aValue, diff --git a/editor/libeditor/text/nsPlaintextDataTransfer.cpp b/editor/libeditor/text/nsPlaintextDataTransfer.cpp index abb48e48678..3b2683f3d3a 100644 --- a/editor/libeditor/text/nsPlaintextDataTransfer.cpp +++ b/editor/libeditor/text/nsPlaintextDataTransfer.cpp @@ -52,6 +52,7 @@ #include "nsServiceManagerUtils.h" #include "nsIDOMRange.h" +#include "nsIDOMDOMStringList.h" #include "nsIDocumentEncoder.h" #include "nsISupportsPrimitives.h" @@ -61,6 +62,7 @@ #include "nsIDragService.h" #include "nsIDOMUIEvent.h" #include "nsCopySupport.h" +#include "nsITransferable.h" // Misc #include "nsEditorUtils.h" @@ -151,40 +153,48 @@ NS_IMETHODIMP nsPlaintextEditor::InsertTextFromTransferable(nsITransferable *aTr // Try to scroll the selection into view if the paste/drop succeeded - // After ScrollSelectionIntoView(), the pending notifications might be flushed - // and PresShell/PresContext/Frames may be dead. See bug 418470. if (NS_SUCCEEDED(rv)) ScrollSelectionIntoView(false); return rv; } -NS_IMETHODIMP nsPlaintextEditor::InsertFromDrop(nsIDOMEvent* aDropEvent) +nsresult nsPlaintextEditor::InsertFromDataTransfer(nsIDOMDataTransfer *aDataTransfer, + PRInt32 aIndex, + nsIDOMDocument *aSourceDoc, + nsIDOMNode *aDestinationNode, + PRInt32 aDestOffset, + bool aDoDeleteSelection) +{ + nsCOMPtr data; + aDataTransfer->MozGetDataAt(NS_LITERAL_STRING("text/plain"), aIndex, + getter_AddRefs(data)); + nsAutoString insertText; + data->GetAsAString(insertText); + nsContentUtils::PlatformToDOMLineBreaks(insertText); + + nsAutoEditBatch beginBatching(this); + return InsertTextAt(insertText, aDestinationNode, aDestOffset, aDoDeleteSelection); +} + +nsresult nsPlaintextEditor::InsertFromDrop(nsIDOMEvent* aDropEvent) { ForceCompositionEnd(); - - nsresult rv; - nsCOMPtr dragService = - do_GetService("@mozilla.org/widget/dragservice;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr dragSession; - dragService->GetCurrentSession(getter_AddRefs(dragSession)); - NS_ENSURE_TRUE(dragSession, NS_OK); + nsCOMPtr dragEvent(do_QueryInterface(aDropEvent)); + NS_ENSURE_TRUE(dragEvent, NS_ERROR_FAILURE); + + nsCOMPtr dataTransfer; + nsresult rv = dragEvent->GetDataTransfer(getter_AddRefs(dataTransfer)); + NS_ENSURE_SUCCESS(rv, rv); // Current doc is destination nsCOMPtr destdomdoc; rv = GetDocument(getter_AddRefs(destdomdoc)); NS_ENSURE_SUCCESS(rv, rv); - // Get the nsITransferable interface for getting the data from the drop - nsCOMPtr trans; - rv = PrepareTransferable(getter_AddRefs(trans)); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(trans, NS_OK); // NS_ERROR_FAILURE; SHOULD WE FAIL? - - PRUint32 numItems = 0; - rv = dragSession->GetNumDropItems(&numItems); + PRUint32 numItems = 0; + rv = dataTransfer->GetMozItemCount(&numItems); NS_ENSURE_SUCCESS(rv, rv); if (numItems < 1) return NS_ERROR_FAILURE; // nothing to drop? @@ -216,6 +226,35 @@ NS_IMETHODIMP nsPlaintextEditor::InsertFromDrop(nsIDOMEvent* aDropEvent) rv = selection->GetIsCollapsed(&isCollapsed); NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr sourceNode; + dataTransfer->GetMozSourceNode(getter_AddRefs(sourceNode)); + + nsCOMPtr srcdomdoc; + if (sourceNode) { + sourceNode->GetOwnerDocument(getter_AddRefs(srcdomdoc)); + NS_ENSURE_TRUE(sourceNode, NS_ERROR_FAILURE); + } + + // Only the nsHTMLEditor::FindUserSelectAllNode returns a node. + nsCOMPtr userSelectNode = FindUserSelectAllNode(newSelectionParent); + if (userSelectNode) + { + // The drop is happening over a "-moz-user-select: all" + // subtree so make sure the content we insert goes before + // the root of the subtree. + // + // XXX: Note that inserting before the subtree matches the + // current behavior when dropping on top of an image. + // The decision for dropping before or after the + // subtree should really be done based on coordinates. + + rv = GetNodeLocation(userSelectNode, address_of(newSelectionParent), + &newSelectionOffset); + + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(newSelectionParent, NS_ERROR_FAILURE); + } + // Check if mouse is in the selection // if so, jump through some hoops to determine if mouse is over selection (bail) // and whether user wants to copy selection or delete it @@ -240,12 +279,6 @@ NS_IMETHODIMP nsPlaintextEditor::InsertFromDrop(nsIDOMEvent* aDropEvent) break; } - // Source doc is null if source is *not* the current editor document - // Current doc is destination (set earlier) - nsCOMPtr srcdomdoc; - rv = dragSession->GetSourceDocument(getter_AddRefs(srcdomdoc)); - NS_ENSURE_SUCCESS(rv, rv); - if (cursorIsInSelection) { // Dragging within same doc can't drop on itself -- leave! @@ -263,9 +296,9 @@ NS_IMETHODIMP nsPlaintextEditor::InsertFromDrop(nsIDOMEvent* aDropEvent) if (srcdomdoc == destdomdoc) { // Within the same doc: delete if user doesn't want to copy - PRUint32 action; - dragSession->GetDragAction(&action); - deleteSelection = !(action & nsIDragService::DRAGDROP_ACTION_COPY); + PRUint32 dropEffect; + dataTransfer->GetDropEffectInt(&dropEffect); + deleteSelection = !(dropEffect & nsIDragService::DRAGDROP_ACTION_COPY); } else { @@ -275,140 +308,26 @@ NS_IMETHODIMP nsPlaintextEditor::InsertFromDrop(nsIDOMEvent* aDropEvent) } } - nsCOMPtr newSelectionContent = - do_QueryInterface(newSelectionParent); - nsIContent *content = newSelectionContent; - - while (content) { - nsCOMPtr formControl(do_QueryInterface(content)); - - if (formControl && !formControl->AllowDrop()) { - // Don't allow dropping into a form control that doesn't allow being - // dropped into. - - return NS_OK; - } - - content = content->GetParent(); - } - - PRUint32 i; - for (i = 0; i < numItems; ++i) - { - rv = dragSession->GetData(trans, i); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(trans, NS_OK); // NS_ERROR_FAILURE; Should we fail? - - // Beware! This may flush notifications via synchronous - // ScrollSelectionIntoView. - rv = InsertTextFromTransferable(trans, newSelectionParent, newSelectionOffset, deleteSelection); - } - - return rv; -} - -NS_IMETHODIMP nsPlaintextEditor::CanDrag(nsIDOMEvent *aDragEvent, bool *aCanDrag) -{ - NS_ENSURE_TRUE(aCanDrag, NS_ERROR_NULL_POINTER); - /* we really should be checking the XY coordinates of the mouseevent and ensure that - * that particular point is actually within the selection (not just that there is a selection) - */ - *aCanDrag = false; - - // KLUDGE to work around bug 50703 - // After double click and object property editing, - // we get a spurious drag event - if (mIgnoreSpuriousDragEvent) - { - mIgnoreSpuriousDragEvent = false; - return NS_OK; - } - - nsCOMPtr selection; - nsresult res = GetSelection(getter_AddRefs(selection)); - NS_ENSURE_SUCCESS(res, res); - - bool isCollapsed; - res = selection->GetIsCollapsed(&isCollapsed); - NS_ENSURE_SUCCESS(res, res); - - // if we are collapsed, we have no selection so nothing to drag - if ( isCollapsed ) - return NS_OK; - - nsCOMPtr eventTarget; - - nsCOMPtr nsevent(do_QueryInterface(aDragEvent)); - if (nsevent) { - res = nsevent->GetTmpRealOriginalTarget(getter_AddRefs(eventTarget)); - NS_ENSURE_SUCCESS(res, res); - } - - if (eventTarget) - { - nsCOMPtr eventTargetDomNode = do_QueryInterface(eventTarget); - if ( eventTargetDomNode ) - { - bool isTargetedCorrectly = false; - res = selection->ContainsNode(eventTargetDomNode, false, &isTargetedCorrectly); - NS_ENSURE_SUCCESS(res, res); - - *aCanDrag = isTargetedCorrectly; + if (IsPlaintextEditor()) { + nsCOMPtr content = do_QueryInterface(newSelectionParent); + while (content) { + nsCOMPtr formControl(do_QueryInterface(content)); + if (formControl && !formControl->AllowDrop()) { + // Don't allow dropping into a form control that doesn't allow being + // dropped into. + return NS_OK; + } + content = content->GetParent(); } } - return res; -} + for (PRUint32 i = 0; i < numItems; ++i) { + InsertFromDataTransfer(dataTransfer, i, srcdomdoc, newSelectionParent, + newSelectionOffset, deleteSelection); + } -NS_IMETHODIMP nsPlaintextEditor::DoDrag(nsIDOMEvent *aDragEvent) -{ - nsresult rv; - - nsCOMPtr trans; - rv = PutDragDataInTransferable(getter_AddRefs(trans)); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(trans, NS_OK); // maybe there was nothing to copy? - - /* get the drag service */ - nsCOMPtr dragService = - do_GetService("@mozilla.org/widget/dragservice;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - - /* create an array of transferables */ - nsCOMPtr transferableArray; - NS_NewISupportsArray(getter_AddRefs(transferableArray)); - NS_ENSURE_TRUE(transferableArray, NS_ERROR_OUT_OF_MEMORY); - - /* add the transferable to the array */ - rv = transferableArray->AppendElement(trans); - NS_ENSURE_SUCCESS(rv, rv); - - // check our transferable hooks (if any) - nsCOMPtr domdoc; - GetDocument(getter_AddRefs(domdoc)); - - /* invoke drag */ - nsCOMPtr eventTarget; - rv = aDragEvent->GetTarget(getter_AddRefs(eventTarget)); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr domnode = do_QueryInterface(eventTarget); - - nsCOMPtr selRegion; - nsCOMPtr selection; - rv = GetSelection(getter_AddRefs(selection)); - NS_ENSURE_SUCCESS(rv, rv); - - unsigned int flags; - // in some cases we'll want to cut rather than copy... hmmmmm... - flags = nsIDragService::DRAGDROP_ACTION_COPY + nsIDragService::DRAGDROP_ACTION_MOVE; - - nsCOMPtr dragEvent(do_QueryInterface(aDragEvent)); - rv = dragService->InvokeDragSessionWithSelection(selection, transferableArray, - flags, dragEvent, nsnull); - NS_ENSURE_SUCCESS(rv, rv); - - aDragEvent->StopPropagation(); - aDragEvent->PreventDefault(); + if (NS_SUCCEEDED(rv)) + ScrollSelectionIntoView(false); return rv; } @@ -438,8 +357,6 @@ NS_IMETHODIMP nsPlaintextEditor::Paste(PRInt32 aSelectionType) if (!nsEditorHookUtils::DoInsertionHook(domdoc, nsnull, trans)) return NS_OK; - // Beware! This may flush notifications via synchronous - // ScrollSelectionIntoView. rv = InsertTextFromTransferable(trans, nsnull, nsnull, true); } } @@ -461,8 +378,6 @@ NS_IMETHODIMP nsPlaintextEditor::PasteTransferable(nsITransferable *aTransferabl if (!nsEditorHookUtils::DoInsertionHook(domdoc, nsnull, aTransferable)) return NS_OK; - // Beware! This may flush notifications via synchronous - // ScrollSelectionIntoView. return InsertTextFromTransferable(aTransferable, nsnull, nsnull, true); } @@ -521,102 +436,3 @@ NS_IMETHODIMP nsPlaintextEditor::CanPasteTransferable(nsITransferable *aTransfer return NS_OK; } - - -nsresult -nsPlaintextEditor::SetupDocEncoder(nsIDocumentEncoder **aDocEncoder) -{ - nsCOMPtr domDoc; - nsresult rv = GetDocument(getter_AddRefs(domDoc)); - NS_ENSURE_SUCCESS(rv, rv); - - // find out if we're a plaintext control or not - // get correct mimeType and document encoder flags set - nsAutoString mimeType; - PRUint32 docEncoderFlags = 0; - if (IsPlaintextEditor()) - { - docEncoderFlags |= nsIDocumentEncoder::OutputBodyOnly | nsIDocumentEncoder::OutputPreformatted; - mimeType.AssignLiteral(kUnicodeMime); - } - else - mimeType.AssignLiteral(kHTMLMime); - - // set up docEncoder - nsCOMPtr encoder = do_CreateInstance(NS_HTMLCOPY_ENCODER_CONTRACTID); - NS_ENSURE_TRUE(encoder, NS_ERROR_OUT_OF_MEMORY); - - rv = encoder->Init(domDoc, mimeType, docEncoderFlags); - NS_ENSURE_SUCCESS(rv, rv); - - /* get the selection to be dragged */ - nsCOMPtr selection; - rv = GetSelection(getter_AddRefs(selection)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = encoder->SetSelection(selection); - NS_ENSURE_SUCCESS(rv, rv); - - *aDocEncoder = encoder; - NS_ADDREF(*aDocEncoder); - return NS_OK; -} - -nsresult -nsPlaintextEditor::PutDragDataInTransferable(nsITransferable **aTransferable) -{ - *aTransferable = nsnull; - nsCOMPtr docEncoder; - nsresult rv = SetupDocEncoder(getter_AddRefs(docEncoder)); - NS_ENSURE_SUCCESS(rv, rv); - - // grab a string - nsAutoString buffer; - rv = docEncoder->EncodeToString(buffer); - NS_ENSURE_SUCCESS(rv, rv); - - // if we have an empty string, we're done; otherwise continue - if (buffer.IsEmpty()) - return NS_OK; - - nsCOMPtr dataWrapper = - do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - rv = dataWrapper->SetData(buffer); - NS_ENSURE_SUCCESS(rv, rv); - - /* create html flavor transferable */ - nsCOMPtr trans = do_CreateInstance("@mozilla.org/widget/transferable;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - - // find out if we're a plaintext control or not - if (IsPlaintextEditor()) - { - // Add the unicode flavor to the transferable - rv = trans->AddDataFlavor(kUnicodeMime); - NS_ENSURE_SUCCESS(rv, rv); - } - else - { - rv = trans->AddDataFlavor(kHTMLMime); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr htmlConverter = do_CreateInstance("@mozilla.org/widget/htmlformatconverter;1"); - NS_ENSURE_TRUE(htmlConverter, NS_ERROR_FAILURE); - - rv = trans->SetConverter(htmlConverter); - NS_ENSURE_SUCCESS(rv, rv); - } - - // QI the data object an |nsISupports| so that when the transferable holds - // onto it, it will addref the correct interface. - nsCOMPtr nsisupportsDataWrapper = do_QueryInterface(dataWrapper); - rv = trans->SetTransferData(IsPlaintextEditor() ? kUnicodeMime : kHTMLMime, - nsisupportsDataWrapper, buffer.Length() * sizeof(PRUnichar)); - NS_ENSURE_SUCCESS(rv, rv); - - *aTransferable = trans; - NS_ADDREF(*aTransferable); - return NS_OK; -} diff --git a/editor/libeditor/text/nsPlaintextEditor.cpp b/editor/libeditor/text/nsPlaintextEditor.cpp index a129c98791a..949454f2227 100644 --- a/editor/libeditor/text/nsPlaintextEditor.cpp +++ b/editor/libeditor/text/nsPlaintextEditor.cpp @@ -88,7 +88,6 @@ using namespace mozilla; nsPlaintextEditor::nsPlaintextEditor() : nsEditor() -, mIgnoreSpuriousDragEvent(false) , mRules(nsnull) , mWrapToWindow(false) , mWrapColumn(0) diff --git a/editor/libeditor/text/nsPlaintextEditor.h b/editor/libeditor/text/nsPlaintextEditor.h index 5f6082b0099..44079c050f8 100644 --- a/editor/libeditor/text/nsPlaintextEditor.h +++ b/editor/libeditor/text/nsPlaintextEditor.h @@ -118,10 +118,6 @@ public: NS_IMETHOD PasteTransferable(nsITransferable *aTransferable); NS_IMETHOD CanPasteTransferable(nsITransferable *aTransferable, bool *aCanPaste); - NS_IMETHOD CanDrag(nsIDOMEvent *aDragEvent, bool *aCanDrag); - NS_IMETHOD DoDrag(nsIDOMEvent *aDragEvent); - NS_IMETHOD InsertFromDrop(nsIDOMEvent* aDropEvent); - NS_IMETHOD OutputToString(const nsAString& aFormatType, PRUint32 aFlags, nsAString& aOutputString); @@ -167,6 +163,15 @@ public: PRInt32 aDestOffset, bool aDoDeleteSelection); + virtual nsresult InsertFromDataTransfer(nsIDOMDataTransfer *aDataTransfer, + PRInt32 aIndex, + nsIDOMDocument *aSourceDoc, + nsIDOMNode *aDestinationNode, + PRInt32 aDestOffset, + bool aDoDeleteSelection); + + virtual nsresult InsertFromDrop(nsIDOMEvent* aDropEvent); + /** * Extends the selection for given deletion operation * If done, also update aAction to what's actually left to do after the @@ -205,8 +210,6 @@ protected: nsIDOMNode *aDestinationNode, PRInt32 aDestOffset, bool aDoDeleteSelection); - virtual nsresult SetupDocEncoder(nsIDocumentEncoder **aDocEncoder); - virtual nsresult PutDragDataInTransferable(nsITransferable **aTransferable); /** shared outputstring; returns whether selection is collapsed and resulting string */ nsresult SharedOutputString(PRUint32 aFlags, bool* aIsCollapsed, nsAString& aResult); @@ -214,10 +217,6 @@ protected: /* small utility routine to test the eEditorReadonly bit */ bool IsModifiable(); - //XXX Kludge: Used to suppress spurious drag/drop events (bug 50703) - bool mIgnoreSpuriousDragEvent; - NS_IMETHOD IgnoreSpuriousDragEvent(bool aIgnoreSpuriousDragEvent) {mIgnoreSpuriousDragEvent = aIgnoreSpuriousDragEvent; return NS_OK;} - bool CanCutOrCopy(); bool FireClipboardEvent(PRInt32 aType); diff --git a/embedding/browser/webBrowser/nsIWebBrowserChrome.idl b/embedding/browser/webBrowser/nsIWebBrowserChrome.idl index ffef485bec7..58b03015c50 100644 --- a/embedding/browser/webBrowser/nsIWebBrowserChrome.idl +++ b/embedding/browser/webBrowser/nsIWebBrowserChrome.idl @@ -47,7 +47,7 @@ interface nsIDocShellTreeItem; * containing an embedded Gecko web browser. */ -[scriptable, uuid(BA434C60-9D52-11d3-AFB0-00A024FFC08C)] +[scriptable, uuid(E8C414C4-DC38-4BA3-AB4E-EC4CBBE22907)] interface nsIWebBrowserChrome : nsISupports { const unsigned long STATUS_SCRIPT = 0x00000001; @@ -75,43 +75,47 @@ interface nsIWebBrowserChrome : nsISupports /** * Definitions for the chrome flags */ - const unsigned long CHROME_DEFAULT = 0x00000001; - const unsigned long CHROME_WINDOW_BORDERS = 0x00000002; - const unsigned long CHROME_WINDOW_CLOSE = 0x00000004; - const unsigned long CHROME_WINDOW_RESIZE = 0x00000008; - const unsigned long CHROME_MENUBAR = 0x00000010; - const unsigned long CHROME_TOOLBAR = 0x00000020; - const unsigned long CHROME_LOCATIONBAR = 0x00000040; - const unsigned long CHROME_STATUSBAR = 0x00000080; - const unsigned long CHROME_PERSONAL_TOOLBAR = 0x00000100; - const unsigned long CHROME_SCROLLBARS = 0x00000200; - const unsigned long CHROME_TITLEBAR = 0x00000400; - const unsigned long CHROME_EXTRA = 0x00000800; + const unsigned long CHROME_DEFAULT = 0x00000001; + const unsigned long CHROME_WINDOW_BORDERS = 0x00000002; + const unsigned long CHROME_WINDOW_CLOSE = 0x00000004; + const unsigned long CHROME_WINDOW_RESIZE = 0x00000008; + const unsigned long CHROME_MENUBAR = 0x00000010; + const unsigned long CHROME_TOOLBAR = 0x00000020; + const unsigned long CHROME_LOCATIONBAR = 0x00000040; + const unsigned long CHROME_STATUSBAR = 0x00000080; + const unsigned long CHROME_PERSONAL_TOOLBAR = 0x00000100; + const unsigned long CHROME_SCROLLBARS = 0x00000200; + const unsigned long CHROME_TITLEBAR = 0x00000400; + const unsigned long CHROME_EXTRA = 0x00000800; // createBrowserWindow specific flags - const unsigned long CHROME_WITH_SIZE = 0x00001000; - const unsigned long CHROME_WITH_POSITION = 0x00002000; + const unsigned long CHROME_WITH_SIZE = 0x00001000; + const unsigned long CHROME_WITH_POSITION = 0x00002000; // special cases - const unsigned long CHROME_WINDOW_MIN = 0x00004000; - const unsigned long CHROME_WINDOW_POPUP = 0x00008000; + const unsigned long CHROME_WINDOW_MIN = 0x00004000; + const unsigned long CHROME_WINDOW_POPUP = 0x00008000; - const unsigned long CHROME_WINDOW_RAISED = 0x02000000; - const unsigned long CHROME_WINDOW_LOWERED = 0x04000000; - const unsigned long CHROME_CENTER_SCREEN = 0x08000000; + // Prevents new window animations on Mac OS X Lion. Ignored on other + // platforms. + const unsigned long CHROME_MAC_SUPPRESS_ANIMATION = 0x01000000; + + const unsigned long CHROME_WINDOW_RAISED = 0x02000000; + const unsigned long CHROME_WINDOW_LOWERED = 0x04000000; + const unsigned long CHROME_CENTER_SCREEN = 0x08000000; // Make the new window dependent on the parent. This flag is only // meaningful if CHROME_OPENAS_CHROME is set; content windows should not be // dependent. - const unsigned long CHROME_DEPENDENT = 0x10000000; + const unsigned long CHROME_DEPENDENT = 0x10000000; // Note: The modal style bit just affects the way the window looks and does // mean it's actually modal. - const unsigned long CHROME_MODAL = 0x20000000; - const unsigned long CHROME_OPENAS_DIALOG = 0x40000000; - const unsigned long CHROME_OPENAS_CHROME = 0x80000000; + const unsigned long CHROME_MODAL = 0x20000000; + const unsigned long CHROME_OPENAS_DIALOG = 0x40000000; + const unsigned long CHROME_OPENAS_CHROME = 0x80000000; - const unsigned long CHROME_ALL = 0x00000ffe; + const unsigned long CHROME_ALL = 0x00000ffe; /** * The chrome flags for this browser chrome. The implementation should diff --git a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp index 947681086c8..1ac0bc55c43 100644 --- a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp +++ b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp @@ -1527,6 +1527,9 @@ PRUint32 nsWindowWatcher::CalculateChromeFlags(const char *aFeatures, else if (WinHasOption(aFeatures, "alwaysRaised", 0, nsnull)) chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_RAISED; + chromeFlags |= WinHasOption(aFeatures, "macsuppressanimation", 0, nsnull) ? + nsIWebBrowserChrome::CHROME_MAC_SUPPRESS_ANIMATION : 0; + chromeFlags |= WinHasOption(aFeatures, "chrome", 0, nsnull) ? nsIWebBrowserChrome::CHROME_OPENAS_CHROME : 0; chromeFlags |= WinHasOption(aFeatures, "extrachrome", 0, nsnull) ? diff --git a/extensions/auth/nsHttpNegotiateAuth.cpp b/extensions/auth/nsHttpNegotiateAuth.cpp index 85629aac673..b3e1e4db7b5 100644 --- a/extensions/auth/nsHttpNegotiateAuth.cpp +++ b/extensions/auth/nsHttpNegotiateAuth.cpp @@ -70,6 +70,7 @@ #include "prprf.h" #include "prlog.h" #include "prmem.h" +#include "prnetdb.h" //----------------------------------------------------------------------------- @@ -77,6 +78,7 @@ static const char kNegotiate[] = "Negotiate"; static const char kNegotiateAuthTrustedURIs[] = "network.negotiate-auth.trusted-uris"; static const char kNegotiateAuthDelegationURIs[] = "network.negotiate-auth.delegation-uris"; static const char kNegotiateAuthAllowProxies[] = "network.negotiate-auth.allow-proxies"; +static const char kNegotiateAuthAllowNonFqdn[] = "network.negotiate-auth.allow-non-fqdn"; static const char kNegotiateAuthSSPI[] = "network.auth.use-sspi"; #define kNegotiateLen (sizeof(kNegotiate)-1) @@ -143,7 +145,8 @@ nsHttpNegotiateAuth::ChallengeReceived(nsIHttpAuthenticableChannel *authChannel, proxyInfo->GetHost(service); } else { - bool allowed = TestPref(uri, kNegotiateAuthTrustedURIs); + bool allowed = TestNonFqdn(uri) || + TestPref(uri, kNegotiateAuthTrustedURIs); if (!allowed) { LOG(("nsHttpNegotiateAuth::ChallengeReceived URI blocked\n")); return NS_ERROR_ABORT; @@ -331,6 +334,23 @@ nsHttpNegotiateAuth::TestBoolPref(const char *pref) return val; } +bool +nsHttpNegotiateAuth::TestNonFqdn(nsIURI *uri) +{ + nsCAutoString host; + PRNetAddr addr; + + if (!TestBoolPref(kNegotiateAuthAllowNonFqdn)) + return false; + + if (NS_FAILED(uri->GetAsciiHost(host))) + return false; + + // return true if host does not contain a dot and is not an ip address + return !host.IsEmpty() && host.FindChar('.') == kNotFound && + PR_StringToNetAddr(host.BeginReading(), &addr) != PR_SUCCESS; +} + bool nsHttpNegotiateAuth::TestPref(nsIURI *uri, const char *pref) { diff --git a/extensions/auth/nsHttpNegotiateAuth.h b/extensions/auth/nsHttpNegotiateAuth.h index 7b1583cee37..f7bd889db98 100644 --- a/extensions/auth/nsHttpNegotiateAuth.h +++ b/extensions/auth/nsHttpNegotiateAuth.h @@ -58,6 +58,9 @@ private: // returns the value of the given boolean pref bool TestBoolPref(const char *pref); + // tests if the host part of an uri is fully qualified + bool TestNonFqdn(nsIURI *uri); + // returns true if URI is accepted by the list of hosts in the pref bool TestPref(nsIURI *, const char *pref); diff --git a/gfx/2d/DrawTargetD2D.cpp b/gfx/2d/DrawTargetD2D.cpp index 13771aee764..f3cb54db98d 100644 --- a/gfx/2d/DrawTargetD2D.cpp +++ b/gfx/2d/DrawTargetD2D.cpp @@ -633,7 +633,7 @@ DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface, if (mPushedClips.size()) { mPrivateData->mEffect->GetVariableByName("mask")->AsShaderResource()->SetResource(maskSRView); mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()-> - SetFloatVector(ShaderConstantRectD3D10(shadowDest.x / mSize.width, shadowDest.y / mSize.width, + SetFloatVector(ShaderConstantRectD3D10(shadowDest.x / mSize.width, shadowDest.y / mSize.height, Float(aSurface->GetSize().width) / mSize.width, Float(aSurface->GetSize().height) / mSize.height)); mPrivateData->mEffect->GetTechniqueByName("SampleTextureWithShadow")-> @@ -658,7 +658,7 @@ DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface, if (mPushedClips.size()) { mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()-> - SetFloatVector(ShaderConstantRectD3D10(aDest.x / mSize.width, aDest.y / mSize.width, + SetFloatVector(ShaderConstantRectD3D10(aDest.x / mSize.width, aDest.y / mSize.height, Float(aSurface->GetSize().width) / mSize.width, Float(aSurface->GetSize().height) / mSize.height)); mPrivateData->mEffect->GetTechniqueByName("SampleMaskedTexture")-> diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c index c7d9aced602..e91dfff0d57 100644 --- a/gfx/cairo/cairo/src/cairo-quartz-surface.c +++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c @@ -135,6 +135,8 @@ static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL; static CGFloat (*CGContextGetAlphaPtr) (CGContextRef) = NULL; +static SInt32 _cairo_quartz_osx_version = 0x0; + static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE; /* @@ -170,6 +172,11 @@ static void quartz_ensure_symbols(void) CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); CGContextGetAlphaPtr = dlsym(RTLD_DEFAULT, "CGContextGetAlpha"); + if (Gestalt(gestaltSystemVersion, &_cairo_quartz_osx_version) != noErr) { + // assume 10.5 + _cairo_quartz_osx_version = 0x1050; + } + _cairo_quartz_symbol_lookup_done = TRUE; } @@ -3035,8 +3042,11 @@ _cairo_quartz_surface_mask_cg (void *abstract_surface, /* If we have CGContextClipToMask, we can do more complex masks */ if (CGContextClipToMaskPtr) { /* For these, we can skip creating a temporary surface, since we already have one */ - if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && mask->extend == CAIRO_EXTEND_NONE) + /* For some reason this doesn't work reliably on OS X 10.5. See bug 721663. */ + if (_cairo_quartz_osx_version >= 0x1060 && mask->type == CAIRO_PATTERN_TYPE_SURFACE && + mask->extend == CAIRO_EXTEND_NONE) { return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask, clip); + } return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask, clip); } diff --git a/gfx/cairo/quartz-surface-mask-patch b/gfx/cairo/quartz-surface-mask-patch new file mode 100644 index 00000000000..d5ee7d8be3c --- /dev/null +++ b/gfx/cairo/quartz-surface-mask-patch @@ -0,0 +1,79 @@ +diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c +--- a/gfx/cairo/cairo/src/cairo-quartz-surface.c ++++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c +@@ -128,20 +128,22 @@ CG_EXTERN CGImageRef CGBitmapContextCrea + */ + static void (*CGContextClipToMaskPtr) (CGContextRef, CGRect, CGImageRef) = NULL; + static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL; + static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL; + static void (*CGContextSetShouldAntialiasFontsPtr) (CGContextRef, bool) = NULL; + static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL; + static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; + static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL; + static CGFloat (*CGContextGetAlphaPtr) (CGContextRef) = NULL; + ++static SInt32 _cairo_quartz_osx_version = 0x0; ++ + static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE; + + /* + * Utility functions + */ + + #ifdef QUARTZ_DEBUG + static void quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest); + static void quartz_image_to_png (CGImageRef, char *dest); + #endif +@@ -163,20 +165,25 @@ static void quartz_ensure_symbols(void) + + CGContextClipToMaskPtr = dlsym(RTLD_DEFAULT, "CGContextClipToMask"); + CGContextDrawTiledImagePtr = dlsym(RTLD_DEFAULT, "CGContextDrawTiledImage"); + CGContextGetTypePtr = dlsym(RTLD_DEFAULT, "CGContextGetType"); + CGContextSetShouldAntialiasFontsPtr = dlsym(RTLD_DEFAULT, "CGContextSetShouldAntialiasFonts"); + CGContextCopyPathPtr = dlsym(RTLD_DEFAULT, "CGContextCopyPath"); + CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); + CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); + CGContextGetAlphaPtr = dlsym(RTLD_DEFAULT, "CGContextGetAlpha"); + ++ if (Gestalt(gestaltSystemVersion, &_cairo_quartz_osx_version) != noErr) { ++ // assume 10.5 ++ _cairo_quartz_osx_version = 0x1050; ++ } ++ + _cairo_quartz_symbol_lookup_done = TRUE; + } + + CGImageRef + _cairo_quartz_create_cgimage (cairo_format_t format, + unsigned int width, + unsigned int height, + unsigned int stride, + void *data, + cairo_bool_t interpolate, +@@ -3028,22 +3035,25 @@ static cairo_int_status_t + CGContextSetAlpha (surface->cgContext, solid_mask->color.alpha); + rv = _cairo_quartz_surface_paint_cg (surface, op, source, clip); + CGContextSetAlpha (surface->cgContext, 1.0); + + return rv; + } + + /* If we have CGContextClipToMask, we can do more complex masks */ + if (CGContextClipToMaskPtr) { + /* For these, we can skip creating a temporary surface, since we already have one */ +- if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && mask->extend == CAIRO_EXTEND_NONE) ++ /* For some reason this doesn't work reliably on OS X 10.5. See bug 721663. */ ++ if (_cairo_quartz_osx_version >= 0x1060 && mask->type == CAIRO_PATTERN_TYPE_SURFACE && ++ mask->extend == CAIRO_EXTEND_NONE) { + return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask, clip); ++ } + + return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask, clip); + } + + /* So, CGContextClipToMask is not present in 10.3.9, so we're + * doomed; if we have imageData, we can do fallback, otherwise + * just pretend success. + */ + if (surface->imageData) + return CAIRO_INT_STATUS_UNSUPPORTED; diff --git a/gfx/gl/GLContext.cpp b/gfx/gl/GLContext.cpp index 74da1f0a373..b61ebfd118c 100644 --- a/gfx/gl/GLContext.cpp +++ b/gfx/gl/GLContext.cpp @@ -1472,6 +1472,8 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize, const bool aUseReadFBO, c // We're good, and the framebuffer is already attached. // Now restore the GL state back to what it was before the resize took place. + // If the user was using fb 0, this will bind the offscreen framebuffer we + // just created. BindDrawFBO(curBoundFramebufferDraw); BindReadFBO(curBoundFramebufferRead); fBindTexture(LOCAL_GL_TEXTURE_2D, curBoundTexture); diff --git a/gfx/gl/GLContext.h b/gfx/gl/GLContext.h index 4c8924912cf..8c76b689c62 100644 --- a/gfx/gl/GLContext.h +++ b/gfx/gl/GLContext.h @@ -541,8 +541,10 @@ public: bool aIsOffscreen = false, GLContext *aSharedContext = nsnull) : mFlushGuaranteesResolve(false), - mBoundDrawFBO(0), - mBoundReadFBO(0), + mUserBoundDrawFBO(0), + mUserBoundReadFBO(0), + mInternalBoundDrawFBO(0), + mInternalBoundReadFBO(0), mOffscreenFBOsDirty(false), mInitialized(false), mIsOffscreen(aIsOffscreen), @@ -865,23 +867,67 @@ public: private: - GLuint mBoundDrawFBO; - GLuint mBoundReadFBO; + GLuint mUserBoundDrawFBO; + GLuint mUserBoundReadFBO; + GLuint mInternalBoundDrawFBO; + GLuint mInternalBoundReadFBO; public: void fBindFramebuffer(GLenum target, GLuint framebuffer) { switch (target) { - case LOCAL_GL_FRAMEBUFFER: - mBoundDrawFBO = mBoundReadFBO = framebuffer; - break; case LOCAL_GL_DRAW_FRAMEBUFFER_EXT: - mBoundDrawFBO = framebuffer; + mUserBoundDrawFBO = framebuffer; + + if (framebuffer == 0) { + mInternalBoundDrawFBO = mOffscreenDrawFBO; + } else { + mInternalBoundDrawFBO = mUserBoundDrawFBO; + } + + raw_fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, + mInternalBoundDrawFBO); break; + case LOCAL_GL_READ_FRAMEBUFFER_EXT: - mBoundReadFBO = framebuffer; + mUserBoundReadFBO = framebuffer; + + if (framebuffer == 0) { + mInternalBoundReadFBO = mOffscreenReadFBO; + } else { + mInternalBoundReadFBO = mUserBoundReadFBO; + } + + raw_fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, + mInternalBoundReadFBO); + break; + + case LOCAL_GL_FRAMEBUFFER: + mUserBoundDrawFBO = mUserBoundReadFBO = framebuffer; + + if (framebuffer == 0) { + mInternalBoundDrawFBO = mOffscreenDrawFBO; + mInternalBoundReadFBO = mOffscreenReadFBO; + } else { + mInternalBoundDrawFBO = mUserBoundDrawFBO; + mInternalBoundReadFBO = mUserBoundReadFBO; + } + + if (SupportsOffscreenSplit()) { + raw_fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, + mInternalBoundDrawFBO); + raw_fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, + mInternalBoundReadFBO); + } else { + raw_fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, + mInternalBoundDrawFBO); + } + + break; + + default: + raw_fBindFramebuffer(target, framebuffer); break; } - raw_fBindFramebuffer(target, framebuffer); } GLuint GetBoundDrawFBO() { @@ -889,32 +935,38 @@ public: GLint ret = 0; // Don't need a branch here, because: // LOCAL_GL_DRAW_FRAMEBUFFER_BINDING_EXT == LOCAL_GL_FRAMEBUFFER_BINDING == 0x8CA6 - fGetIntegerv(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING_EXT, &ret); + // We use raw_ here because this is debug code and we need to see what + // the driver thinks. + raw_fGetIntegerv(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING_EXT, &ret); - if (mBoundDrawFBO != (GLuint)ret) { - printf_stderr("!!! Draw FBO mismatch: Was: %d, Expected: %d\n", ret, mBoundDrawFBO); + if (mInternalBoundDrawFBO != (GLuint)ret) { + printf_stderr("!!! Draw FBO mismatch: Was: %d, Expected: %d\n", ret, mInternalBoundDrawFBO); NS_ABORT(); } #endif - return mBoundDrawFBO; + // We only ever expose the user's bound FBOs + return mUserBoundDrawFBO; } GLuint GetBoundReadFBO() { #ifdef DEBUG GLint ret = 0; + // We use raw_ here because this is debug code and we need to see what + // the driver thinks. if (SupportsOffscreenSplit()) - fGetIntegerv(LOCAL_GL_READ_FRAMEBUFFER_BINDING_EXT, &ret); + raw_fGetIntegerv(LOCAL_GL_READ_FRAMEBUFFER_BINDING_EXT, &ret); else - fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &ret); + raw_fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &ret); - if (mBoundReadFBO != (GLuint)ret) { - printf_stderr("!!! Read FBO mismatch: Was: %d, Expected: %d\n", ret, mBoundReadFBO); + if (mInternalBoundReadFBO != (GLuint)ret) { + printf_stderr("!!! Read FBO mismatch: Was: %d, Expected: %d\n", ret, mInternalBoundReadFBO); NS_ABORT(); } #endif - return mBoundReadFBO; + // We only ever expose the user's bound FBOs + return mUserBoundReadFBO; } void BindDrawFBO(GLuint name) { @@ -957,8 +1009,6 @@ public: } private: - GLuint mPrevDrawFBOBinding; - GLuint mPrevReadFBOBinding; bool mOffscreenFBOsDirty; void GetShaderPrecisionFormatNonES2(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) { @@ -983,40 +1033,29 @@ private: } } + // Do whatever setup is necessary to draw to our offscreen FBO, if it's + // bound. void BeforeGLDrawCall() { - // Record and rebind if necessary - mPrevDrawFBOBinding = GetBoundDrawFBO(); - if (mPrevDrawFBOBinding == 0) { - BindDrawFBO(mOffscreenDrawFBO); - } else if (mPrevDrawFBOBinding != mOffscreenDrawFBO) + if (mInternalBoundDrawFBO != mOffscreenDrawFBO) return; - // Must be after binding the proper FBO if (mOffscreenDrawFBO == mOffscreenReadFBO) return; - // If we're already dirty, no need to set it again - if (mOffscreenFBOsDirty) - return; - mOffscreenFBOsDirty = true; } + // Do whatever tear-down is necessary after drawing to our offscreen FBO, + // if it's bound. void AfterGLDrawCall() { - if (mPrevDrawFBOBinding == 0) { - BindDrawFBO(0); - } } + // Do whatever setup is necessary to read from our offscreen FBO, if it's + // bound. void BeforeGLReadCall() { - // Record and rebind if necessary - mPrevReadFBOBinding = GetBoundReadFBO(); - if (mPrevReadFBOBinding == 0) { - BindReadFBO(mOffscreenReadFBO); - } else if (mPrevReadFBOBinding != mOffscreenReadFBO) + if (mInternalBoundReadFBO != mOffscreenReadFBO) return; - // Must be after binding the proper FBO if (mOffscreenDrawFBO == mOffscreenReadFBO) return; @@ -1030,7 +1069,7 @@ private: // flip read/draw for blitting GLuint prevDraw = SwapBoundDrawFBO(mOffscreenReadFBO); - BindReadFBO(mOffscreenDrawFBO); // We know that Read must already be mOffscreenRead, so no need to write that down + GLuint prevRead = SwapBoundReadFBO(mOffscreenDrawFBO); GLint width = mOffscreenActualSize.width; GLint height = mOffscreenActualSize.height; @@ -1040,7 +1079,7 @@ private: LOCAL_GL_NEAREST); BindDrawFBO(prevDraw); - BindReadFBO(mOffscreenReadFBO); + BindReadFBO(prevRead); if (scissor) fEnable(LOCAL_GL_SCISSOR_TEST); @@ -1048,10 +1087,9 @@ private: mOffscreenFBOsDirty = false; } + // Do whatever tear-down is necessary after reading from our offscreen FBO, + // if it's bound. void AfterGLReadCall() { - if (mPrevReadFBOBinding == 0) { - BindReadFBO(0); - } } public: @@ -2000,12 +2038,34 @@ public: return retval; } - void fGetIntegerv(GLenum pname, GLint *params) { +private: + void raw_fGetIntegerv(GLenum pname, GLint *params) { BEFORE_GL_CALL; mSymbols.fGetIntegerv(pname, params); AFTER_GL_CALL; } +public: + void fGetIntegerv(GLenum pname, GLint *params) { + switch (pname) + { + // LOCAL_GL_FRAMEBUFFER_BINDING is equal to + // LOCAL_GL_DRAW_FRAMEBUFFER_BINDING_EXT, so we don't need two + // cases. + case LOCAL_GL_FRAMEBUFFER_BINDING: + *params = GetBoundDrawFBO(); + break; + + case LOCAL_GL_READ_FRAMEBUFFER_BINDING_EXT: + *params = GetBoundReadFBO(); + break; + + default: + raw_fGetIntegerv(pname, params); + break; + } + } + void fGetFloatv(GLenum pname, GLfloat *params) { BEFORE_GL_CALL; mSymbols.fGetFloatv(pname, params); diff --git a/gfx/harfbuzz/src/hb-ot-layout.cc b/gfx/harfbuzz/src/hb-ot-layout.cc index f3e07139adb..bfbe890b4ab 100644 --- a/gfx/harfbuzz/src/hb-ot-layout.cc +++ b/gfx/harfbuzz/src/hb-ot-layout.cc @@ -32,6 +32,7 @@ #include "hb-ot-layout-gsub-table.hh" #include "hb-ot-layout-gpos-table.hh" #include "hb-ot-maxp-table.hh" +#include "hb-ot-shape-private.hh" #include @@ -496,8 +497,46 @@ hb_ot_layout_position_lookup (hb_font_t *font, } void -hb_ot_layout_position_finish (hb_buffer_t *buffer) +hb_ot_layout_position_finish (hb_face_t *face, hb_buffer_t *buffer) { + /* force diacritics to have zero width */ + unsigned int count = buffer->len; + if (hb_ot_layout_has_glyph_classes (face)) { + const GDEF& gdef = _get_gdef (face); + if (buffer->props.direction == HB_DIRECTION_RTL) { + for (unsigned int i = 1; i < count; i++) { + if (gdef.get_glyph_class (buffer->info[i].codepoint) == GDEF::MarkGlyph) { + buffer->pos[i].x_advance = 0; + } + } + } else { + for (unsigned int i = 1; i < count; i++) { + if (gdef.get_glyph_class (buffer->info[i].codepoint) == GDEF::MarkGlyph) { + hb_glyph_position_t& pos = buffer->pos[i]; + pos.x_offset -= pos.x_advance; + pos.x_advance = 0; + } + } + } + } else { + /* no GDEF classes available, so use General Category as a fallback */ + if (buffer->props.direction == HB_DIRECTION_RTL) { + for (unsigned int i = 1; i < count; i++) { + if (buffer->info[i].general_category() == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) { + buffer->pos[i].x_advance = 0; + } + } + } else { + for (unsigned int i = 1; i < count; i++) { + if (buffer->info[i].general_category() == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) { + hb_glyph_position_t& pos = buffer->pos[i]; + pos.x_offset -= pos.x_advance; + pos.x_advance = 0; + } + } + } + } + GPOS::position_finish (buffer); } diff --git a/gfx/harfbuzz/src/hb-ot-layout.h b/gfx/harfbuzz/src/hb-ot-layout.h index 447e35dbfad..96f25722dea 100644 --- a/gfx/harfbuzz/src/hb-ot-layout.h +++ b/gfx/harfbuzz/src/hb-ot-layout.h @@ -199,7 +199,7 @@ hb_ot_layout_position_lookup (hb_font_t *font, /* Should be called after all the position_lookup's are done */ void -hb_ot_layout_position_finish (hb_buffer_t *buffer); +hb_ot_layout_position_finish (hb_face_t *face, hb_buffer_t *buffer); HB_END_DECLS diff --git a/gfx/harfbuzz/src/hb-ot-shape-private.hh b/gfx/harfbuzz/src/hb-ot-shape-private.hh index c49c2b0e36a..c48a4d6c6ea 100644 --- a/gfx/harfbuzz/src/hb-ot-shape-private.hh +++ b/gfx/harfbuzz/src/hb-ot-shape-private.hh @@ -104,12 +104,40 @@ _hb_unicode_modified_combining_class (hb_unicode_funcs_t *ufuncs, { int c = hb_unicode_combining_class (ufuncs, unicode); + /* For Hebrew, we permute the "fixed-position" classes 10-25 into the order + * described in the SBL Hebrew manual http://www.sbl-site.org/Fonts/SBLHebrewUserManual1.5x.pdf + * (as recommended by http://forum.fontlab.com/archive-old-microsoft-volt-group/vista-and-diacritic-ordering-t6751.0.html) + */ + static const int permuted_hebrew_classes[25 - 10 + 1] = { + /* 10 sheva */ 15, + /* 11 hataf segol */ 16, + /* 12 hataf patah */ 17, + /* 13 hataf qamats */ 18, + /* 14 hiriq */ 19, + /* 15 tsere */ 20, + /* 16 segol */ 21, + /* 17 patah */ 22, + /* 18 qamats */ 23, + /* 19 holam */ 14, + /* 20 qubuts */ 24, + /* 21 dagesh */ 12, + /* 22 meteg */ 25, + /* 23 rafe */ 13, + /* 24 shin dot */ 10, + /* 25 sin dot */ 11, + }; + /* Modify the combining-class to suit Arabic better. See: * http://unicode.org/faq/normalization.html#8 * http://unicode.org/faq/normalization.html#9 */ if (unlikely (hb_in_range (c, 27, 33))) c = c == 33 ? 27 : c + 1; + /* The equivalent fix for Hebrew is more complex, + * see the SBL Hebrew manual. + */ + else if (unlikely (hb_in_range (c, 10, 25))) + c = permuted_hebrew_classes[c - 10]; return c; } diff --git a/gfx/harfbuzz/src/hb-ot-shape.cc b/gfx/harfbuzz/src/hb-ot-shape.cc index 4275afc8251..09db9125cbe 100644 --- a/gfx/harfbuzz/src/hb-ot-shape.cc +++ b/gfx/harfbuzz/src/hb-ot-shape.cc @@ -299,15 +299,36 @@ hb_ot_position_complex (hb_ot_shape_context_t *c) c->applied_position_complex = TRUE; } - hb_ot_layout_position_finish (c->buffer); + hb_ot_layout_position_finish (c->face, c->buffer); return; } static void -hb_position_complex_fallback (hb_ot_shape_context_t *c HB_UNUSED) +hb_position_complex_fallback (hb_ot_shape_context_t *c) { - /* TODO Mark pos */ + unsigned int count = c->buffer->len; + if (c->buffer->props.direction == HB_DIRECTION_RTL) { + for (unsigned int i = 1; i < count; i++) { + unsigned int gen_cat = c->buffer->info[i].general_category(); + if ((1<buffer->pos[i].x_advance = 0; + } + } + } else { + for (unsigned int i = 1; i < count; i++) { + unsigned int gen_cat = c->buffer->info[i].general_category(); + if ((1<buffer->pos[i]; + pos.x_offset = -pos.x_advance; + pos.x_advance = 0; + } + } + } } static void diff --git a/gfx/thebes/gfx3DMatrix.cpp b/gfx/thebes/gfx3DMatrix.cpp index 5e5720cfc3b..95de00912c5 100644 --- a/gfx/thebes/gfx3DMatrix.cpp +++ b/gfx/thebes/gfx3DMatrix.cpp @@ -714,8 +714,9 @@ gfx3DMatrix::Is2D(gfxMatrix* aMatrix) const bool gfx3DMatrix::CanDraw2D(gfxMatrix* aMatrix) const { - if (_14 != 0.0f || _24 != 0.0f || - _34 != 0.0f || _44 != 1.0f) { + if (_14 != 0.0f || + _24 != 0.0f || + _44 != 1.0f) { return false; } if (aMatrix) { diff --git a/gfx/thebes/gfx3DMatrix.h b/gfx/thebes/gfx3DMatrix.h index 169328674f2..de8656283bf 100644 --- a/gfx/thebes/gfx3DMatrix.h +++ b/gfx/thebes/gfx3DMatrix.h @@ -114,7 +114,10 @@ public: * Returns true if the matrix can be reduced to a 2D affine transformation * (i.e. as obtained by From2D). If it is, optionally returns the 2D * matrix in aMatrix. This should only be used on matrices required for - * rendering, not for intermediate calculations. + * rendering, not for intermediate calculations. It is assumed that the 2D + * matrix will only be used for transforming objects on to the z=0 plane, + * therefore any z-component perspective is ignored. This means that if + * aMatrix is applied to objects with z != 0, the results may be incorrect. * * Since drawing is to a 2d plane, any 3d transform without perspective * can be reduced by dropping the z row and column. diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 7ec3599dd69..b87d239b8aa 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -1358,11 +1358,19 @@ gfxPlatform::FontsPrefsChanged(const char *aPref) #ifdef MOZ_GRAPHITE } else if (!strcmp(GFX_PREF_GRAPHITE_SHAPING, aPref)) { mGraphiteShapingEnabled = UNINITIALIZED_VALUE; - gfxFontCache::GetCache()->AgeAllGenerations(); + gfxFontCache *fontCache = gfxFontCache::GetCache(); + if (fontCache) { + fontCache->AgeAllGenerations(); + fontCache->FlushShapedWordCaches(); + } #endif } else if (!strcmp(GFX_PREF_HARFBUZZ_SCRIPTS, aPref)) { mUseHarfBuzzScripts = UNINITIALIZED_VALUE; - gfxFontCache::GetCache()->AgeAllGenerations(); + gfxFontCache *fontCache = gfxFontCache::GetCache(); + if (fontCache) { + fontCache->AgeAllGenerations(); + fontCache->FlushShapedWordCaches(); + } } else if (!strcmp(BIDI_NUMERAL_PREF, aPref)) { mBidiNumeralOption = UNINITIALIZED_VALUE; } diff --git a/gfx/thebes/nsIOSurface.h b/gfx/thebes/nsIOSurface.h index 621e0b1eda4..f71a75d534a 100644 --- a/gfx/thebes/nsIOSurface.h +++ b/gfx/thebes/nsIOSurface.h @@ -44,7 +44,7 @@ #import class gfxASurface; -class _CGLContextObject; +struct _CGLContextObject; typedef _CGLContextObject* CGLContextObj; typedef uint32_t IOSurfaceID; diff --git a/hal/gonk/GonkHal.cpp b/hal/gonk/GonkHal.cpp index 4c98315b003..a36e4984b14 100644 --- a/hal/gonk/GonkHal.cpp +++ b/hal/gonk/GonkHal.cpp @@ -268,10 +268,29 @@ GetCurrentBatteryInformation(hal::BatteryInformation *aBatteryInfo) } FILE *chargingFile = fopen("/sys/class/power_supply/battery/charging_source", "r"); - int chargingSrc = 1; + int chargingSrc = BATTERY_CHARGING_USB; + bool done = false; if (chargingFile) { fscanf(chargingFile, "%d", &chargingSrc); fclose(chargingFile); + done = true; + } + + if (!done) { + // toro devices support + chargingFile = fopen("/sys/class/power_supply/battery/status", "r"); + if (chargingFile) { + char status[16]; + fscanf(chargingFile, "%s", &status); + if (!strcmp(status, "Charging") || !strcmp(status, "Full")) { + // no way here to know if we're charging from USB or AC. + chargingSrc = BATTERY_CHARGING_USB; + } else { + chargingSrc = BATTERY_NOT_CHARGING; + } + fclose(chargingFile); + done = true; + } } #ifdef DEBUG diff --git a/js/jsd/jsd_xpc.cpp b/js/jsd/jsd_xpc.cpp index bdd63e0f2e7..f8698fbdc39 100644 --- a/js/jsd/jsd_xpc.cpp +++ b/js/jsd/jsd_xpc.cpp @@ -107,8 +107,8 @@ #define JSD_AUTOREG_ENTRY "JSDebugger Startup Observer" #define JSD_STARTUP_ENTRY "JSDebugger Startup Observer" -static JSBool -jsds_GCCallbackProc (JSContext *cx, JSGCStatus status); +static void +jsds_GCSliceCallbackProc (JSRuntime *rt, js::GCProgress progress, const js::GCDescription &desc); /******************************************************************************* * global vars @@ -128,9 +128,9 @@ PRUint32 gContextCount = 0; PRUint32 gFrameCount = 0; #endif -static jsdService *gJsds = 0; -static JSGCCallback gLastGCProc = jsds_GCCallbackProc; -static JSGCStatus gGCStatus = JSGC_END; +static jsdService *gJsds = 0; +static js::GCSliceCallback gPrevGCSliceCallback = jsds_GCSliceCallbackProc; +static bool gGCRunning = false; static struct DeadScript { PRCList links; @@ -460,11 +460,8 @@ jsds_FilterHook (JSDContext *jsdc, JSDThreadState *state) *******************************************************************************/ static void -jsds_NotifyPendingDeadScripts (JSContext *cx) +jsds_NotifyPendingDeadScripts (JSRuntime *rt) { -#ifdef CAUTIOUS_SCRIPTHOOK - JSRuntime *rt = JS_GetRuntime(cx); -#endif jsdService *jsds = gJsds; nsCOMPtr hook; @@ -511,31 +508,23 @@ jsds_NotifyPendingDeadScripts (JSContext *cx) } } -static JSBool -jsds_GCCallbackProc (JSContext *cx, JSGCStatus status) +static void +jsds_GCSliceCallbackProc (JSRuntime *rt, js::GCProgress progress, const js::GCDescription &desc) { -#ifdef DEBUG_verbose - printf ("new gc status is %i\n", status); -#endif - if (status == JSGC_END) { - /* just to guard against reentering. */ - gGCStatus = JSGC_BEGIN; + if (progress == js::GC_CYCLE_END || progress == js::GC_SLICE_END) { + NS_ASSERTION(gGCRunning, "GC slice callback was missed"); + while (gDeadScripts) - jsds_NotifyPendingDeadScripts (cx); + jsds_NotifyPendingDeadScripts (rt); + + gGCRunning = false; + } else { + NS_ASSERTION(!gGCRunning, "should not re-enter GC"); + gGCRunning = true; } - gGCStatus = status; - if (gLastGCProc && !gLastGCProc (cx, status)) { - /* - * If gLastGCProc returns false, then the GC will abort without making - * another callback with status=JSGC_END, so set the status to JSGC_END - * here. - */ - gGCStatus = JSGC_END; - return JS_FALSE; - } - - return JS_TRUE; + if (gPrevGCSliceCallback) + (*gPrevGCSliceCallback)(rt, progress, desc); } static uintN @@ -751,7 +740,7 @@ jsds_ScriptHookProc (JSDContext* jsdc, JSDScript* jsdscript, JSBool creating, jsdis->Invalidate(); - if (gGCStatus == JSGC_END) { + if (!gGCRunning) { nsCOMPtr hook; gJsds->GetScriptHook(getter_AddRefs(hook)); if (!hook) @@ -2580,9 +2569,9 @@ jsdService::ActivateDebugger (JSRuntime *rt) mRuntime = rt; - if (gLastGCProc == jsds_GCCallbackProc) + if (gPrevGCSliceCallback == jsds_GCSliceCallbackProc) /* condition indicates that the callback proc has not been set yet */ - gLastGCProc = JS_SetGCCallbackRT (rt, jsds_GCCallbackProc); + gPrevGCSliceCallback = js::SetGCSliceCallback (rt, jsds_GCSliceCallbackProc); mCx = JSD_DebuggerOnForUser (rt, NULL, NULL); if (!mCx) @@ -2652,19 +2641,14 @@ jsdService::Off (void) return NS_ERROR_NOT_INITIALIZED; if (gDeadScripts) { - if (gGCStatus != JSGC_END) + if (gGCRunning) return NS_ERROR_NOT_AVAILABLE; JSContext *cx = JSD_GetDefaultJSContext(mCx); while (gDeadScripts) - jsds_NotifyPendingDeadScripts (cx); + jsds_NotifyPendingDeadScripts (JS_GetRuntime(cx)); } - /* - if (gLastGCProc != jsds_GCCallbackProc) - JS_SetGCCallbackRT (mRuntime, gLastGCProc); - */ - DeactivateDebugger(); #ifdef DEBUG @@ -3374,7 +3358,7 @@ jsdService::~jsdService() mThrowHook = nsnull; mTopLevelHook = nsnull; mFunctionHook = nsnull; - gGCStatus = JSGC_END; + gGCRunning = false; Off(); gJsds = nsnull; } diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 1abd09f1621..86751a1131a 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -119,7 +119,6 @@ CPPSRCS = \ jsfun.cpp \ jsgc.cpp \ jsgcmark.cpp \ - jsgcstats.cpp \ jscrashreport.cpp \ jshash.cpp \ jsinfer.cpp \ @@ -193,7 +192,6 @@ INSTALLED_HEADERS = \ jsfriendapi.h \ jsgc.h \ jscell.h \ - jsgcstats.h \ jshash.h \ jslock.h \ json.h \ diff --git a/js/src/aclocal.m4 b/js/src/aclocal.m4 index b826fcbe471..cd5b6727cfa 100644 --- a/js/src/aclocal.m4 +++ b/js/src/aclocal.m4 @@ -14,5 +14,6 @@ builtin(include, build/autoconf/acwinpaths.m4)dnl builtin(include, build/autoconf/lto.m4)dnl builtin(include, build/autoconf/gcc-pr49911.m4)dnl builtin(include, build/autoconf/frameptr.m4)dnl +builtin(include, build/autoconf/compiler-opts.m4)dnl MOZ_PROG_CHECKMSYS() diff --git a/js/src/assembler/wtf/Assertions.h b/js/src/assembler/wtf/Assertions.h index 537737a5b82..80663ef6de7 100644 --- a/js/src/assembler/wtf/Assertions.h +++ b/js/src/assembler/wtf/Assertions.h @@ -29,8 +29,16 @@ #include "Platform.h" #include "mozilla/Assertions.h" +#ifndef DEBUG + /* + * Prevent unused-variable warnings by defining the macro WTF uses to test + * for assertions taking effect. + */ +# define ASSERT_DISABLED 1 +#endif + #define ASSERT(assertion) MOZ_ASSERT(assertion) -#define ASSERT_UNUSED(variable, assertion) ASSERT(assertion) +#define ASSERT_UNUSED(variable, assertion) (((void)variable), ASSERT(assertion)) #define ASSERT_NOT_REACHED() MOZ_NOT_REACHED("") #define CRASH() MOZ_Crash() #define COMPILE_ASSERT(exp, name) MOZ_STATIC_ASSERT(exp, #name) diff --git a/js/src/build/autoconf/compiler-opts.m4 b/js/src/build/autoconf/compiler-opts.m4 new file mode 100644 index 00000000000..0c2fd2a0b1a --- /dev/null +++ b/js/src/build/autoconf/compiler-opts.m4 @@ -0,0 +1,13 @@ +dnl Add compiler specific options + +AC_DEFUN([MOZ_COMPILER_OPTS], +[ +if test "$CLANG_CXX"; then + ## We disable return-type-c-linkage because jsval is defined as a C++ type but is + ## returned by C functions. This is possible because we use knowledge about the ABI + ## to typedef it to a C type with the same layout when the headers are included + ## from C. + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-return-type-c-linkage" +fi +]) + diff --git a/js/src/builtin/MapObject.cpp b/js/src/builtin/MapObject.cpp index c7cdd46e3b8..61832a61b63 100644 --- a/js/src/builtin/MapObject.cpp +++ b/js/src/builtin/MapObject.cpp @@ -146,7 +146,7 @@ HashableValue::equals(const HashableValue &other) const Class MapObject::class_ = { "Map", - JSCLASS_HAS_PRIVATE | + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_CACHED_PROTO(JSProto_Map), JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* delProperty */ @@ -185,8 +185,11 @@ MapObject::mark(JSTracer *trc, JSObject *obj) MapObject *mapobj = static_cast(obj); if (ValueMap *map = mapobj->getData()) { for (ValueMap::Range r = map->all(); !r.empty(); r.popFront()) { - gc::MarkValue(trc, r.front().key, "key"); - gc::MarkValue(trc, r.front().value, "value"); + const HeapValue &key = r.front().key; + HeapValue tmp(key); + gc::MarkValue(trc, &tmp, "key"); + JS_ASSERT(tmp.get() == key.get()); + gc::MarkValue(trc, &r.front().value, "value"); } } } @@ -294,7 +297,7 @@ js_InitMapClass(JSContext *cx, JSObject *obj) Class SetObject::class_ = { "Set", - JSCLASS_HAS_PRIVATE | + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_CACHED_PROTO(JSProto_Set), JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* delProperty */ @@ -331,8 +334,12 @@ SetObject::mark(JSTracer *trc, JSObject *obj) { SetObject *setobj = static_cast(obj); if (ValueSet *set = setobj->getData()) { - for (ValueSet::Range r = set->all(); !r.empty(); r.popFront()) - gc::MarkValue(trc, r.front(), "key"); + for (ValueSet::Range r = set->all(); !r.empty(); r.popFront()) { + const HeapValue &key = r.front(); + HeapValue tmp(key); + gc::MarkValue(trc, &tmp, "key"); + JS_ASSERT(tmp.get() == key.get()); + } } } diff --git a/js/src/configure.in b/js/src/configure.in index d663c46d846..adde2e68dd0 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -3133,6 +3133,7 @@ AC_SUBST(WRAP_SYSTEM_INCLUDES) AC_SUBST(VISIBILITY_FLAGS) MOZ_GCC_PR49911 +MOZ_COMPILER_OPTS dnl Check for __force_align_arg_pointer__ for SSE2 on gcc dnl ======================================================== diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index 4ef88ee463e..4ed0cd9ef51 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -255,7 +255,7 @@ static JSClass sCDataProtoClass = { static JSClass sCTypeClass = { "CType", - JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS), + JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CType::Finalize, NULL, NULL, CType::ConstructData, CType::ConstructData, NULL, @@ -272,7 +272,7 @@ static JSClass sCDataClass = { static JSClass sCClosureClass = { "CClosure", - JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS), + JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CClosure::Finalize, NULL, NULL, NULL, NULL, NULL, NULL, CClosure::Trace, NULL diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 1b4b4f81012..e032f6e03ad 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -257,7 +257,7 @@ Parser::trace(JSTracer *trc) { ObjectBox *objbox = traceListHead; while (objbox) { - MarkObjectRoot(trc, objbox->object, "parser.object"); + MarkObjectRoot(trc, &objbox->object, "parser.object"); if (objbox->isFunctionBox) static_cast(objbox)->bindings.trace(trc); objbox = objbox->traceLink; diff --git a/js/src/gc/Barrier-inl.h b/js/src/gc/Barrier-inl.h index b7310137494..e8ed435b71b 100644 --- a/js/src/gc/Barrier-inl.h +++ b/js/src/gc/Barrier-inl.h @@ -134,8 +134,11 @@ inline void HeapValue::writeBarrierPre(JSCompartment *comp, const Value &value) { #ifdef JSGC_INCREMENTAL - if (comp->needsBarrier()) - js::gc::MarkValueUnbarriered(comp->barrierTracer(), value, "write barrier"); + if (comp->needsBarrier()) { + Value tmp(value); + js::gc::MarkValueUnbarriered(comp->barrierTracer(), &tmp, "write barrier"); + JS_ASSERT(tmp == value); + } #endif } @@ -263,6 +266,31 @@ HeapId::operator=(const HeapId &v) return *this; } +inline const Value & +ReadBarrieredValue::get() const +{ + if (value.isObject()) + JSObject::readBarrier(&value.toObject()); + else if (value.isString()) + JSString::readBarrier(value.toString()); + else + JS_ASSERT(!value.isMarkable()); + + return value; +} + +inline +ReadBarrieredValue::operator const Value &() const +{ + return get(); +} + +inline JSObject & +ReadBarrieredValue::toObject() const +{ + return get().toObject(); +} + } /* namespace js */ #endif /* jsgc_barrier_inl_h___ */ diff --git a/js/src/gc/Barrier.h b/js/src/gc/Barrier.h index 78d5fa03f56..c82e509b4bd 100644 --- a/js/src/gc/Barrier.h +++ b/js/src/gc/Barrier.h @@ -319,6 +319,7 @@ class HeapValue inline void set(JSCompartment *comp, const Value &v); const Value &get() const { return value; } + Value *unsafeGet() { return &value; } operator const Value &() const { return value; } bool isUndefined() const { return value.isUndefined(); } @@ -405,6 +406,7 @@ class HeapId bool operator!=(jsid id) const { return value != id; } jsid get() const { return value; } + jsid *unsafeGet() { return &value; } operator jsid() const { return value; } private: @@ -455,6 +457,20 @@ class ReadBarriered operator MarkablePtr() const { return MarkablePtr(value); } }; +class ReadBarrieredValue +{ + Value value; + + public: + ReadBarrieredValue() : value(UndefinedValue()) {} + ReadBarrieredValue(const Value &value) : value(value) {} + + inline const Value &get() const; + inline operator const Value &() const; + + inline JSObject &toObject() const; +}; + } #endif /* jsgc_barrier_h___ */ diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index ba17a972246..32084e9c83c 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -38,9 +38,10 @@ * ***** END LICENSE BLOCK ***** */ #include -#include +#include #include "jscntxt.h" +#include "jscompartment.h" #include "jscrashformat.h" #include "jscrashreport.h" #include "jsprf.h" @@ -69,78 +70,114 @@ ExplainReason(gcreason::Reason reason) } } -Statistics::ColumnInfo::ColumnInfo(const char *title, double t, double total) - : title(title) +void +Statistics::fmt(const char *f, ...) { - JS_snprintf(str, sizeof(str), "%.1f", t); - JS_snprintf(totalStr, sizeof(totalStr), "%.1f", total); - width = 6; -} + va_list va; + size_t off = strlen(buffer); -Statistics::ColumnInfo::ColumnInfo(const char *title, double t) - : title(title) -{ - JS_snprintf(str, sizeof(str), "%.1f", t); - strcpy(totalStr, "n/a"); - width = 6; + va_start(va, f); + JS_vsnprintf(buffer + off, BUFFER_SIZE - off, f, va); + va_end(va); } -Statistics::ColumnInfo::ColumnInfo(const char *title, unsigned int data) - : title(title) -{ - JS_snprintf(str, sizeof(str), "%d", data); - strcpy(totalStr, "n/a"); - width = 4; -} - -Statistics::ColumnInfo::ColumnInfo(const char *title, const char *data) - : title(title) -{ - JS_ASSERT(strlen(data) < sizeof(str)); - strcpy(str, data); - strcpy(totalStr, "n/a "); - width = 0; -} - -static const int NUM_COLUMNS = 17; - void -Statistics::makeTable(ColumnInfo *cols) +Statistics::fmtIfNonzero(const char *name, double t) { - int i = 0; + if (t) { + if (needComma) + fmt(", "); + fmt("%s: %.1f", name, t); + needComma = true; + } +} - cols[i++] = ColumnInfo("Type", compartment ? "Comp" : "Glob"); +void +Statistics::formatPhases(int64_t *times) +{ + needComma = false; + fmtIfNonzero("mark", t(times[PHASE_MARK])); + fmtIfNonzero("mark-roots", t(times[PHASE_MARK_ROOTS])); + fmtIfNonzero("mark-delayed", t(times[PHASE_MARK_DELAYED])); + fmtIfNonzero("mark-other", t(times[PHASE_MARK_OTHER])); + fmtIfNonzero("sweep", t(times[PHASE_SWEEP])); + fmtIfNonzero("sweep-obj", t(times[PHASE_SWEEP_OBJECT])); + fmtIfNonzero("sweep-string", t(times[PHASE_SWEEP_STRING])); + fmtIfNonzero("sweep-script", t(times[PHASE_SWEEP_SCRIPT])); + fmtIfNonzero("sweep-shape", t(times[PHASE_SWEEP_SHAPE])); + fmtIfNonzero("discard-code", t(times[PHASE_DISCARD_CODE])); + fmtIfNonzero("discard-analysis", t(times[PHASE_DISCARD_ANALYSIS])); + fmtIfNonzero("xpconnect", t(times[PHASE_XPCONNECT])); + fmtIfNonzero("deallocate", t(times[PHASE_DESTROY])); +} - cols[i++] = ColumnInfo("Total", t(PHASE_GC), total(PHASE_GC)); - cols[i++] = ColumnInfo("Wait", beginDelay(PHASE_MARK, PHASE_GC)); - cols[i++] = ColumnInfo("Mark", t(PHASE_MARK), total(PHASE_MARK)); - cols[i++] = ColumnInfo("Sweep", t(PHASE_SWEEP), total(PHASE_SWEEP)); - cols[i++] = ColumnInfo("FinObj", t(PHASE_SWEEP_OBJECT), total(PHASE_SWEEP_OBJECT)); - cols[i++] = ColumnInfo("FinStr", t(PHASE_SWEEP_STRING), total(PHASE_SWEEP_STRING)); - cols[i++] = ColumnInfo("FinScr", t(PHASE_SWEEP_SCRIPT), total(PHASE_SWEEP_SCRIPT)); - cols[i++] = ColumnInfo("FinShp", t(PHASE_SWEEP_SHAPE), total(PHASE_SWEEP_SHAPE)); - cols[i++] = ColumnInfo("DisCod", t(PHASE_DISCARD_CODE), total(PHASE_DISCARD_CODE)); - cols[i++] = ColumnInfo("DisAnl", t(PHASE_DISCARD_ANALYSIS), total(PHASE_DISCARD_ANALYSIS)); - cols[i++] = ColumnInfo("XPCnct", t(PHASE_XPCONNECT), total(PHASE_XPCONNECT)); - cols[i++] = ColumnInfo("Destry", t(PHASE_DESTROY), total(PHASE_DESTROY)); - cols[i++] = ColumnInfo("End", endDelay(PHASE_GC, PHASE_DESTROY)); +/* Except for the first and last, slices of less than 12ms are not reported. */ +static const int64_t SLICE_MIN_REPORT_TIME = 12 * PRMJ_USEC_PER_MSEC; - cols[i++] = ColumnInfo("+Chu", counts[STAT_NEW_CHUNK]); - cols[i++] = ColumnInfo("-Chu", counts[STAT_DESTROY_CHUNK]); +const char * +Statistics::formatData() +{ + buffer[0] = 0x00; - cols[i++] = ColumnInfo("Reason", ExplainReason(triggerReason)); + int64_t total = 0, longest = 0; - JS_ASSERT(i == NUM_COLUMNS); + for (SliceData *slice = slices.begin(); slice != slices.end(); slice++) { + total += slice->duration(); + if (slice->duration() > longest) + longest = slice->duration(); + } + + double mmu20 = computeMMU(20 * PRMJ_USEC_PER_MSEC); + double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC); + + fmt("TotalTime: %.1fms, Type: %s", t(total), compartment ? "compartment" : "global"); + fmt(", MMU(20ms): %d%%, MMU(50ms): %d%%", int(mmu20 * 100), int(mmu50 * 100)); + + if (slices.length() > 1) + fmt(", MaxPause: %.1f", t(longest)); + else + fmt(", Reason: %s", ExplainReason(slices[0].reason)); + + if (wasReset) + fmt(", ***RESET***"); + + fmt(", +chunks: %d, -chunks: %d\n", counts[STAT_NEW_CHUNK], counts[STAT_DESTROY_CHUNK]); + + if (slices.length() > 1) { + for (size_t i = 0; i < slices.length(); i++) { + int64_t width = slices[i].duration(); + if (i != 0 && i != slices.length() - 1 && width < SLICE_MIN_REPORT_TIME) + continue; + + fmt(" Slice %d @ %.1fms (Pause: %.1f, Reason: %s): ", + i, + t(slices[i].end - slices[0].start), + t(width), + ExplainReason(slices[i].reason)); + formatPhases(slices[i].phaseTimes); + fmt("\n"); + } + + fmt(" Totals: "); + } + + formatPhases(phaseTimes); + fmt("\n"); + + return buffer; } Statistics::Statistics(JSRuntime *rt) : runtime(rt), - triggerReason(gcreason::NO_REASON) + startupTime(PRMJ_Now()), + fp(NULL), + fullFormat(false), + compartment(NULL), + wasReset(false), + needComma(false) { + PodArrayZero(phaseTotals); PodArrayZero(counts); - PodArrayZero(totals); - - startupTime = PRMJ_Now(); char *env = getenv("MOZ_GCTIMER"); if (!env || strcmp(env, "none") == 0) { @@ -159,14 +196,6 @@ Statistics::Statistics(JSRuntime *rt) fp = fopen(env, "a"); JS_ASSERT(fp); - - fprintf(fp, " AppTime"); - - ColumnInfo cols[NUM_COLUMNS]; - makeTable(cols); - for (int i = 0; i < NUM_COLUMNS; i++) - fprintf(fp, ", %*s", cols[i].width, cols[i].title); - fprintf(fp, "\n"); } } @@ -174,13 +203,9 @@ Statistics::~Statistics() { if (fp) { if (fullFormat) { - fprintf(fp, "------>TOTAL"); - - ColumnInfo cols[NUM_COLUMNS]; - makeTable(cols); - for (int i = 0; i < NUM_COLUMNS && cols[i].totalStr[0]; i++) - fprintf(fp, ", %*s", cols[i].width, cols[i].totalStr); - fprintf(fp, "\n"); + buffer[0] = 0x00; + formatPhases(phaseTotals); + fprintf(fp, "TOTALS\n%s\n\n-------\n", buffer); } if (fp != stdout && fp != stderr) @@ -188,120 +213,65 @@ Statistics::~Statistics() } } -struct GCCrashData -{ - int isRegen; - int isCompartment; -}; - -void -Statistics::beginGC(JSCompartment *comp, gcreason::Reason reason) -{ - compartment = comp; - - PodArrayZero(phaseStarts); - PodArrayZero(phaseEnds); - PodArrayZero(phaseTimes); - - triggerReason = reason; - - beginPhase(PHASE_GC); - Probes::GCStart(); - - GCCrashData crashData; - crashData.isCompartment = !!compartment; - crash::SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData)); -} - double -Statistics::t(Phase phase) +Statistics::t(int64_t t) { - return double(phaseTimes[phase]) / PRMJ_USEC_PER_MSEC; + return double(t) / PRMJ_USEC_PER_MSEC; } -double -Statistics::total(Phase phase) +int64_t +Statistics::gcDuration() { - return double(totals[phase]) / PRMJ_USEC_PER_MSEC; -} - -double -Statistics::beginDelay(Phase phase1, Phase phase2) -{ - return double(phaseStarts[phase1] - phaseStarts[phase2]) / PRMJ_USEC_PER_MSEC; -} - -double -Statistics::endDelay(Phase phase1, Phase phase2) -{ - return double(phaseEnds[phase1] - phaseEnds[phase2]) / PRMJ_USEC_PER_MSEC; -} - -void -Statistics::statsToString(char *buffer, size_t size) -{ - JS_ASSERT(size); - buffer[0] = 0x00; - - ColumnInfo cols[NUM_COLUMNS]; - makeTable(cols); - - size_t pos = 0; - for (int i = 0; i < NUM_COLUMNS; i++) { - int len = strlen(cols[i].title) + 1 + strlen(cols[i].str); - if (i > 0) - len += 2; - if (pos + len >= size) - break; - if (i > 0) - strcat(buffer, ", "); - strcat(buffer, cols[i].title); - strcat(buffer, ":"); - strcat(buffer, cols[i].str); - pos += len; - } + return slices.back().end - slices[0].start; } void Statistics::printStats() { if (fullFormat) { - fprintf(fp, "%12.0f", double(phaseStarts[PHASE_GC] - startupTime) / PRMJ_USEC_PER_MSEC); - - ColumnInfo cols[NUM_COLUMNS]; - makeTable(cols); - for (int i = 0; i < NUM_COLUMNS; i++) - fprintf(fp, ", %*s", cols[i].width, cols[i].str); - fprintf(fp, "\n"); + fprintf(fp, "GC(T+%.3fs) %s\n", + t(slices[0].start - startupTime) / 1000.0, + formatData()); } else { fprintf(fp, "%f %f %f\n", - t(PHASE_GC), t(PHASE_MARK), t(PHASE_SWEEP)); + t(gcDuration()), + t(phaseTimes[PHASE_MARK]), + t(phaseTimes[PHASE_SWEEP])); } fflush(fp); } +void +Statistics::beginGC() +{ + PodArrayZero(phaseStarts); + PodArrayZero(phaseTimes); + + slices.clearAndFree(); + wasReset = false; + + Probes::GCStart(); +} + void Statistics::endGC() { Probes::GCEnd(); - endPhase(PHASE_GC); crash::SnapshotGCStack(); for (int i = 0; i < PHASE_LIMIT; i++) - totals[i] += phaseTimes[i]; + phaseTotals[i] += phaseTimes[i]; if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) { - (*cb)(JS_TELEMETRY_GC_REASON, triggerReason); (*cb)(JS_TELEMETRY_GC_IS_COMPARTMENTAL, compartment ? 1 : 0); - (*cb)(JS_TELEMETRY_GC_MS, t(PHASE_GC)); - (*cb)(JS_TELEMETRY_GC_MARK_MS, t(PHASE_MARK)); - (*cb)(JS_TELEMETRY_GC_SWEEP_MS, t(PHASE_SWEEP)); - } + (*cb)(JS_TELEMETRY_GC_MS, t(gcDuration())); + (*cb)(JS_TELEMETRY_GC_MARK_MS, t(phaseTimes[PHASE_MARK])); + (*cb)(JS_TELEMETRY_GC_SWEEP_MS, t(phaseTimes[PHASE_SWEEP])); + (*cb)(JS_TELEMETRY_GC_RESET, wasReset); + (*cb)(JS_TELEMETRY_GC_INCREMENTAL_DISABLED, !runtime->gcIncrementalEnabled); - if (JSGCFinishedCallback cb = runtime->gcFinishedCallback) { - char buffer[1024]; - statsToString(buffer, sizeof(buffer)); - (*cb)(runtime, compartment, buffer); + double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC); + (*cb)(JS_TELEMETRY_GC_MMU_50, mmu50 * 100); } if (fp) @@ -310,6 +280,47 @@ Statistics::endGC() PodArrayZero(counts); } +void +Statistics::beginSlice(JSCompartment *comp, gcreason::Reason reason) +{ + compartment = comp; + + bool first = runtime->gcIncrementalState == gc::NO_INCREMENTAL; + if (first) + beginGC(); + + SliceData data(reason, PRMJ_Now()); + (void) slices.append(data); /* Ignore any OOMs here. */ + + if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) + (*cb)(JS_TELEMETRY_GC_REASON, reason); + + if (GCSliceCallback cb = runtime->gcSliceCallback) { + GCDescription desc(NULL, !!compartment); + (*cb)(runtime, first ? GC_CYCLE_BEGIN : GC_SLICE_BEGIN, desc); + } +} + +void +Statistics::endSlice() +{ + slices.back().end = PRMJ_Now(); + + if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) + (*cb)(JS_TELEMETRY_GC_SLICE_MS, t(slices.back().end - slices.back().start)); + + bool last = runtime->gcIncrementalState == gc::NO_INCREMENTAL; + if (last) + endGC(); + + if (GCSliceCallback cb = runtime->gcSliceCallback) { + if (last) + (*cb)(runtime, GC_CYCLE_END, GCDescription(formatData(), !!compartment)); + else + (*cb)(runtime, GC_SLICE_END, GCDescription(NULL, !!compartment)); + } +} + void Statistics::beginPhase(Phase phase) { @@ -324,8 +335,10 @@ Statistics::beginPhase(Phase phase) void Statistics::endPhase(Phase phase) { - phaseEnds[phase] = PRMJ_Now(); - phaseTimes[phase] += phaseEnds[phase] - phaseStarts[phase]; + int64_t now = PRMJ_Now(); + int64_t t = now - phaseStarts[phase]; + slices.back().phaseTimes[phase] += t; + phaseTimes[phase] += t; if (phase == gcstats::PHASE_MARK) Probes::GCEndMarkPhase(); @@ -333,5 +346,44 @@ Statistics::endPhase(Phase phase) Probes::GCEndSweepPhase(); } +/* + * MMU (minimum mutator utilization) is a measure of how much garbage collection + * is affecting the responsiveness of the system. MMU measurements are given + * with respect to a certain window size. If we report MMU(50ms) = 80%, then + * that means that, for any 50ms window of time, at least 80% of the window is + * devoted to the mutator. In other words, the GC is running for at most 20% of + * the window, or 10ms. The GC can run multiple slices during the 50ms window + * as long as the total time it spends is at most 10ms. + */ +double +Statistics::computeMMU(int64_t window) +{ + JS_ASSERT(!slices.empty()); + + int64_t gc = slices[0].end - slices[0].start; + int64_t gcMax = gc; + + if (gc >= window) + return 0.0; + + int startIndex = 0; + for (size_t endIndex = 1; endIndex < slices.length(); endIndex++) { + gc += slices[endIndex].end - slices[endIndex].start; + + while (slices[endIndex].end - slices[startIndex].end >= window) { + gc -= slices[startIndex].end - slices[startIndex].start; + startIndex++; + } + + int64_t cur = gc; + if (slices[endIndex].end - slices[startIndex].start > window) + cur -= (slices[endIndex].end - slices[startIndex].start - window); + if (cur > gcMax) + gcMax = cur; + } + + return double(window - gcMax) / window; +} + } /* namespace gcstats */ } /* namespace js */ diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index 497efaca607..97b8def045e 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -52,8 +52,10 @@ namespace js { namespace gcstats { enum Phase { - PHASE_GC, PHASE_MARK, + PHASE_MARK_ROOTS, + PHASE_MARK_DELAYED, + PHASE_MARK_OTHER, PHASE_SWEEP, PHASE_SWEEP_OBJECT, PHASE_SWEEP_STRING, @@ -74,16 +76,20 @@ enum Stat { STAT_LIMIT }; +static const size_t BUFFER_SIZE = 8192; + struct Statistics { Statistics(JSRuntime *rt); ~Statistics(); - void beginGC(JSCompartment *comp, gcreason::Reason reason); - void endGC(); - void beginPhase(Phase phase); void endPhase(Phase phase); + void beginSlice(JSCompartment *comp, gcreason::Reason reason); + void endSlice(); + + void reset() { wasReset = true; } + void count(Stat s) { JS_ASSERT(s < STAT_LIMIT); counts[s]++; @@ -92,48 +98,64 @@ struct Statistics { private: JSRuntime *runtime; - uint64_t startupTime; + int64_t startupTime; FILE *fp; bool fullFormat; - gcreason::Reason triggerReason; JSCompartment *compartment; + bool wasReset; - uint64_t phaseStarts[PHASE_LIMIT]; - uint64_t phaseEnds[PHASE_LIMIT]; - uint64_t phaseTimes[PHASE_LIMIT]; - uint64_t totals[PHASE_LIMIT]; - unsigned int counts[STAT_LIMIT]; + struct SliceData { + SliceData(gcreason::Reason reason, int64_t start) + : reason(reason), start(start) + { + PodArrayZero(phaseTimes); + } - double t(Phase phase); - double total(Phase phase); - double beginDelay(Phase phase1, Phase phase2); - double endDelay(Phase phase1, Phase phase2); - void printStats(); - void statsToString(char *buffer, size_t size); + gcreason::Reason reason; + int64_t start, end; + int64_t phaseTimes[PHASE_LIMIT]; - struct ColumnInfo { - const char *title; - char str[32]; - char totalStr[32]; - int width; - - ColumnInfo() {} - ColumnInfo(const char *title, double t, double total); - ColumnInfo(const char *title, double t); - ColumnInfo(const char *title, unsigned int data); - ColumnInfo(const char *title, const char *data); + int64_t duration() const { return end - start; } }; - void makeTable(ColumnInfo *cols); + Vector slices; + + /* Most recent time when the given phase started. */ + int64_t phaseStarts[PHASE_LIMIT]; + + /* Total time in a given phase for this GC. */ + int64_t phaseTimes[PHASE_LIMIT]; + + /* Total time in a given phase over all GCs. */ + int64_t phaseTotals[PHASE_LIMIT]; + + /* Number of events of this type for this GC. */ + unsigned int counts[STAT_LIMIT]; + + char buffer[BUFFER_SIZE]; + bool needComma; + + void beginGC(); + void endGC(); + + int64_t gcDuration(); + double t(int64_t t); + void printStats(); + void fmt(const char *f, ...); + void fmtIfNonzero(const char *name, double t); + void formatPhases(int64_t *times); + const char *formatData(); + + double computeMMU(int64_t resolution); }; -struct AutoGC { - AutoGC(Statistics &stats, JSCompartment *comp, gcreason::Reason reason - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : stats(stats) { JS_GUARD_OBJECT_NOTIFIER_INIT; stats.beginGC(comp, reason); } - ~AutoGC() { stats.endGC(); } +struct AutoGCSlice { + AutoGCSlice(Statistics &stats, JSCompartment *comp, gcreason::Reason reason + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : stats(stats) { JS_GUARD_OBJECT_NOTIFIER_INIT; stats.beginSlice(comp, reason); } + ~AutoGCSlice() { stats.endSlice(); } Statistics &stats; JS_DECL_USE_GUARD_OBJECT_NOTIFIER diff --git a/js/src/jit-test/lib/prolog.js b/js/src/jit-test/lib/prolog.js index 84ab993cb5d..7d597923917 100644 --- a/js/src/jit-test/lib/prolog.js +++ b/js/src/jit-test/lib/prolog.js @@ -8,3 +8,6 @@ if (!("gczeal" in this)) { gczeal = function() { } } +if (!("schedulegc" in this)) { + schedulegc = function() { } +} diff --git a/js/src/jit-test/tests/basic/bug727921.js b/js/src/jit-test/tests/basic/bug727921.js new file mode 100644 index 00000000000..88e6773a7df --- /dev/null +++ b/js/src/jit-test/tests/basic/bug727921.js @@ -0,0 +1,10 @@ +(function() { + let(d) { + yield + } +})() +eval("\ + (function(){\ + schedulegc(5), 'a'.replace(/a/,function(){yield})\ + })\ +")() diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index a214c1f6758..c9c38331879 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -723,8 +723,6 @@ JSRuntime::JSRuntime() gcMaxBytes(0), gcMaxMallocBytes(0), gcNumArenasFreeCommitted(0), - gcNumber(0), - gcIncrementalTracer(NULL), gcVerifyData(NULL), gcChunkAllocationSinceLastGC(false), gcNextFullGCTime(0), @@ -733,12 +731,20 @@ JSRuntime::JSRuntime() gcIsNeeded(0), gcWeakMapList(NULL), gcStats(thisFromCtor()), + gcNumber(0), + gcStartNumber(0), gcTriggerReason(gcreason::NO_REASON), gcTriggerCompartment(NULL), gcCurrentCompartment(NULL), gcCheckCompartment(NULL), + gcIncrementalState(gc::NO_INCREMENTAL), + gcCompartmentCreated(false), + gcLastMarkSlice(false), + gcInterFrameGC(0), + gcSliceBudget(SliceBudget::Unlimited), + gcIncrementalEnabled(true), + gcIncrementalCompartment(NULL), gcPoke(false), - gcMarkAndSweep(false), gcRunning(false), #ifdef JS_GC_ZEAL gcZeal_(0), @@ -747,7 +753,7 @@ JSRuntime::JSRuntime() gcDebugCompartmentGC(false), #endif gcCallback(NULL), - gcFinishedCallback(NULL), + gcSliceCallback(NULL), gcMallocBytes(0), gcBlackRootsTraceOp(NULL), gcBlackRootsData(NULL), @@ -814,6 +820,9 @@ JSRuntime::init(uint32_t maxbytes) if (!js_InitGC(this, maxbytes)) return false; + if (!gcMarker.init()) + return false; + if (!(atomsCompartment = this->new_(this)) || !atomsCompartment->init(NULL) || !compartments.append(atomsCompartment)) { @@ -2437,13 +2446,7 @@ JS_SetExtraGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data) JS_PUBLIC_API(void) JS_TracerInit(JSTracer *trc, JSContext *cx, JSTraceCallback callback) { - trc->runtime = cx->runtime; - trc->context = cx; - trc->callback = callback; - trc->debugPrinter = NULL; - trc->debugPrintArg = NULL; - trc->debugPrintIndex = size_t(-1); - trc->eagerlyTraceWeakMaps = true; + InitTracer(trc, cx->runtime, cx, callback); } JS_PUBLIC_API(void) @@ -2875,8 +2878,7 @@ JS_CompartmentGC(JSContext *cx, JSCompartment *comp) /* We cannot GC the atoms compartment alone; use a full GC instead. */ JS_ASSERT(comp != cx->runtime->atomsCompartment); - js::gc::VerifyBarriers(cx, true); - js_GC(cx, comp, GC_NORMAL, gcreason::API); + GC(cx, comp, GC_NORMAL, gcreason::API); } JS_PUBLIC_API(void) @@ -2914,7 +2916,6 @@ JS_PUBLIC_API(JSBool) JS_IsAboutToBeFinalized(void *thing) { gc::Cell *t = static_cast(thing); - JS_ASSERT(!t->compartment()->rt->gcIncrementalTracer); return IsAboutToBeFinalized(t); } @@ -2931,11 +2932,15 @@ JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32_t value) case JSGC_MAX_MALLOC_BYTES: rt->setGCMaxMallocBytes(value); break; + case JSGC_SLICE_TIME_BUDGET: + rt->gcSliceBudget = SliceBudget::TimeBudget(value); + break; default: JS_ASSERT(key == JSGC_MODE); rt->gcMode = JSGCMode(value); JS_ASSERT(rt->gcMode == JSGC_MODE_GLOBAL || - rt->gcMode == JSGC_MODE_COMPARTMENT); + rt->gcMode == JSGC_MODE_COMPARTMENT || + rt->gcMode == JSGC_MODE_INCREMENTAL); return; } } @@ -2956,9 +2961,11 @@ JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key) return uint32_t(rt->gcChunkPool.getEmptyCount()); case JSGC_TOTAL_CHUNKS: return uint32_t(rt->gcChunkSet.count() + rt->gcChunkPool.getEmptyCount()); + case JSGC_SLICE_TIME_BUDGET: + return uint32_t(rt->gcSliceBudget > 0 ? rt->gcSliceBudget / PRMJ_USEC_PER_MSEC : 0); default: JS_ASSERT(key == JSGC_NUMBER); - return rt->gcNumber; + return uint32_t(rt->gcNumber); } } @@ -4415,20 +4422,13 @@ JS_ElementIteratorStub(JSContext *cx, JSObject *obj, JSBool keysonly) JS_PUBLIC_API(jsval) JS_GetReservedSlot(JSObject *obj, uint32_t index) { - if (!obj->isNative()) - return UndefinedValue(); - - return GetReservedSlot(obj, index); + return obj->getReservedSlot(index); } JS_PUBLIC_API(void) JS_SetReservedSlot(JSObject *obj, uint32_t index, jsval v) { - if (!obj->isNative()) - return; - - SetReservedSlot(obj, index, v); - GCPoke(obj->compartment()->rt, NullValue()); + obj->setReservedSlot(index, v); } JS_PUBLIC_API(JSObject *) @@ -6616,7 +6616,16 @@ JS_AbortIfWrongThread(JSRuntime *rt) JS_PUBLIC_API(void) JS_SetGCZeal(JSContext *cx, uint8_t zeal, uint32_t frequency, JSBool compartment) { - bool schedule = zeal >= js::gc::ZealAllocThreshold && zeal < js::gc::ZealVerifierThreshold; +#ifdef JS_GC_ZEAL + const char *env = getenv("JS_GC_ZEAL"); + if (env) { + zeal = atoi(env); + frequency = 1; + compartment = false; + } +#endif + + bool schedule = zeal >= js::gc::ZealAllocValue; cx->runtime->gcZeal_ = zeal; cx->runtime->gcZealFrequency = frequency; cx->runtime->gcNextScheduled = schedule ? frequency : 0; diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 509c2e17c4b..63a952f442c 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1029,7 +1029,7 @@ class AutoEnumStateRooter : private AutoGCRooter protected: void trace(JSTracer *trc); - JSObject * const obj; + JSObject *obj; private: Value stateValue; @@ -1428,8 +1428,11 @@ typedef JSBool (* JSContextCallback)(JSContext *cx, uintN contextOp); typedef enum JSGCStatus { + /* These callbacks happen outside the GC lock. */ JSGC_BEGIN, JSGC_END, + + /* These callbacks happen within the GC lock. */ JSGC_MARK_END, JSGC_FINALIZE_END } JSGCStatus; @@ -1860,7 +1863,11 @@ INTERNED_STRING_TO_JSID(JSContext *cx, JSString *str) jsid id; JS_ASSERT(str); JS_ASSERT(((size_t)str & JSID_TYPE_MASK) == 0); +#ifdef DEBUG JS_ASSERT(JS_StringHasBeenInterned(cx, str)); +#else + (void)cx; +#endif JSID_BITS(id) = (size_t)str; return id; } @@ -3286,7 +3293,10 @@ typedef enum JSGCParamKey { JSGC_UNUSED_CHUNKS = 7, /* Total number of allocated GC chunks. */ - JSGC_TOTAL_CHUNKS = 8 + JSGC_TOTAL_CHUNKS = 8, + + /* Max milliseconds to spend in an incremental GC slice. */ + JSGC_SLICE_TIME_BUDGET = 9 } JSGCParamKey; typedef enum JSGCMode { @@ -3294,7 +3304,13 @@ typedef enum JSGCMode { JSGC_MODE_GLOBAL = 0, /* Perform per-compartment GCs until too much garbage has accumulated. */ - JSGC_MODE_COMPARTMENT = 1 + JSGC_MODE_COMPARTMENT = 1, + + /* + * Collect in short time slices rather than all at once. Implies + * JSGC_MODE_COMPARTMENT. + */ + JSGC_MODE_INCREMENTAL = 2 } JSGCMode; extern JS_PUBLIC_API(void) @@ -3389,6 +3405,8 @@ struct JSClass { object in prototype chain passed in via *objp in/out parameter */ +#define JSCLASS_IMPLEMENTS_BARRIERS (1<<5) /* Correctly implements GC read + and write barriers */ #define JSCLASS_DOCUMENT_OBSERVER (1<<6) /* DOM document observer */ /* @@ -5314,6 +5332,8 @@ JS_IsConstructing(JSContext *cx, const jsval *vp) } else { JS_ASSERT(JS_GetClass(callee)->construct != NULL); } +#else + (void)cx; #endif return JSVAL_IS_MAGIC_IMPL(JSVAL_TO_IMPL(vp[1])); diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index dc1e0bbbc03..80be1d59e57 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1434,23 +1434,22 @@ JSObject::makeDenseArraySlow(JSContext *cx) class ArraySharpDetector { JSContext *cx; - JSHashEntry *he; + bool success; bool alreadySeen; bool sharp; public: ArraySharpDetector(JSContext *cx) : cx(cx), - he(NULL), + success(false), alreadySeen(false), sharp(false) {} bool init(JSObject *obj) { - he = js_EnterSharpObject(cx, obj, NULL, &alreadySeen); - if (!he) + success = js_EnterSharpObject(cx, obj, NULL, &alreadySeen, &sharp); + if (!success) return false; - sharp = IS_SHARP(he); return true; } @@ -1460,7 +1459,7 @@ class ArraySharpDetector } ~ArraySharpDetector() { - if (he && !sharp) + if (success && !sharp) js_LeaveSharpObject(cx, NULL); } }; diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index e92801b3a09..84c3d35dc70 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -386,15 +386,20 @@ js_TraceAtomState(JSTracer *trc) JSAtomState *state = &rt->atomState; if (rt->gcKeepAtoms) { - for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) - MarkStringRoot(trc, r.front().asPtr(), "locked_atom"); + for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) { + JSAtom *tmp = r.front().asPtr(); + MarkStringRoot(trc, &tmp, "locked_atom"); + JS_ASSERT(tmp == r.front().asPtr()); + } } else { for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) { AtomStateEntry entry = r.front(); if (!entry.isTagged()) continue; - MarkStringRoot(trc, entry.asPtr(), "interned_atom"); + JSAtom *tmp = entry.asPtr(); + MarkStringRoot(trc, &tmp, "interned_atom"); + JS_ASSERT(tmp == entry.asPtr()); } } } diff --git a/js/src/jsbool.cpp b/js/src/jsbool.cpp index c1fdd201ea0..277617a853a 100644 --- a/js/src/jsbool.cpp +++ b/js/src/jsbool.cpp @@ -157,7 +157,7 @@ js_InitBooleanClass(JSContext *cx, JSObject *obj) JSObject *booleanProto = global->createBlankPrototype(cx, &BooleanClass); if (!booleanProto) return NULL; - booleanProto->setPrimitiveThis(BooleanValue(false)); + booleanProto->setFixedSlot(BooleanObject::PRIMITIVE_VALUE_SLOT, BooleanValue(false)); JSFunction *ctor = global->createConstructor(cx, Boolean, &BooleanClass, CLASS_ATOM(cx, Boolean), 1); diff --git a/js/src/jsboolinlines.h b/js/src/jsboolinlines.h index c9f17259346..573f3ed4502 100644 --- a/js/src/jsboolinlines.h +++ b/js/src/jsboolinlines.h @@ -42,13 +42,15 @@ #include "jsobjinlines.h" +#include "vm/BooleanObject-inl.h" + namespace js { inline bool BooleanGetPrimitiveValue(JSContext *cx, JSObject &obj, Value *vp) { if (obj.isBoolean()) { - *vp = obj.getPrimitiveThis(); + *vp = BooleanValue(obj.asBoolean().unbox()); return true; } diff --git a/js/src/jsclone.cpp b/js/src/jsclone.cpp index ef93f5088d7..496a0506821 100644 --- a/js/src/jsclone.cpp +++ b/js/src/jsclone.cpp @@ -42,7 +42,10 @@ #include "jstypedarrayinlines.h" +#include "vm/BooleanObject-inl.h" +#include "vm/NumberObject-inl.h" #include "vm/RegExpObject-inl.h" +#include "vm/StringObject-inl.h" using namespace js; @@ -536,12 +539,12 @@ JSStructuredCloneWriter::startWrite(const js::Value &v) } else if (js_IsArrayBuffer(obj)) { return writeArrayBuffer(obj); } else if (obj->isBoolean()) { - return out.writePair(SCTAG_BOOLEAN_OBJECT, obj->getPrimitiveThis().toBoolean()); + return out.writePair(SCTAG_BOOLEAN_OBJECT, obj->asBoolean().unbox()); } else if (obj->isNumber()) { return out.writePair(SCTAG_NUMBER_OBJECT, 0) && - out.writeDouble(obj->getPrimitiveThis().toNumber()); + out.writeDouble(obj->asNumber().unbox()); } else if (obj->isString()) { - return writeString(SCTAG_STRING_OBJECT, obj->getPrimitiveThis().toString()); + return writeString(SCTAG_STRING_OBJECT, obj->asString().unbox()); } if (callbacks && callbacks->write) diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index e9a0b8d60d8..cf781e63a2b 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -282,10 +282,10 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) c->clearTraps(cx); JS_ClearAllWatchPoints(cx); - js_GC(cx, NULL, GC_NORMAL, gcreason::LAST_CONTEXT); + GC(cx, NULL, GC_NORMAL, gcreason::LAST_CONTEXT); } else if (mode == JSDCM_FORCE_GC) { - js_GC(cx, NULL, GC_NORMAL, gcreason::DESTROY_CONTEXT); + GC(cx, NULL, GC_NORMAL, gcreason::DESTROY_CONTEXT); } else if (mode == JSDCM_MAYBE_GC) { JS_MaybeGC(cx); } @@ -875,7 +875,7 @@ js_InvokeOperationCallback(JSContext *cx) JS_ATOMIC_SET(&rt->interrupt, 0); if (rt->gcIsNeeded) - js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL, rt->gcTriggerReason); + GCSlice(cx, rt->gcTriggerCompartment, GC_NORMAL, rt->gcTriggerReason); #ifdef JS_THREADSAFE /* @@ -970,6 +970,7 @@ JSContext::JSContext(JSRuntime *rt) stack(thisDuringConstruction()), /* depends on cx->thread_ */ parseMapPool_(NULL), globalObject(NULL), + sharpObjectMap(this), argumentFormatMap(NULL), lastMessage(NULL), errorReporter(NULL), @@ -1270,6 +1271,26 @@ JSContext::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const return mallocSizeOf(this) + busyArrays.sizeOfExcludingThis(mallocSizeOf); } +void +JSContext::mark(JSTracer *trc) +{ + /* Stack frames and slots are traced by StackSpace::mark. */ + + /* Mark other roots-by-definition in the JSContext. */ + if (globalObject && !hasRunOption(JSOPTION_UNROOTED_GLOBAL)) + MarkObjectRoot(trc, &globalObject, "global object"); + if (isExceptionPending()) + MarkValueRoot(trc, &exception, "exception"); + + if (autoGCRooters) + autoGCRooters->traceAll(trc); + + if (sharpObjectMap.depth > 0) + js_TraceSharpMap(trc, &sharpObjectMap); + + MarkValueRoot(trc, &iterValue, "iterValue"); +} + namespace JS { #if defined JS_THREADSAFE && defined DEBUG diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 8e6cd4fe7d2..13847778516 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -76,12 +76,23 @@ JS_BEGIN_EXTERN_C struct DtoaState; JS_END_EXTERN_C -struct JSSharpObjectMap { - jsrefcount depth; - uint32_t sharpgen; - JSHashTable *table; +struct JSSharpInfo { + bool hasGen; + bool isSharp; - JSSharpObjectMap() : depth(0), sharpgen(0), table(NULL) {} + JSSharpInfo() : hasGen(false), isSharp(false) {} +}; + +typedef js::HashMap JSSharpTable; + +struct JSSharpObjectMap { + jsrefcount depth; + uint32_t sharpgen; + JSSharpTable table; + + JSSharpObjectMap(JSContext *cx) : depth(0), sharpgen(0), table(js::TempAllocPolicy(cx)) { + table.init(); + } }; namespace js { @@ -293,24 +304,25 @@ struct JSRuntime : js::RuntimeFriendFields * in MaybeGC. */ volatile uint32_t gcNumArenasFreeCommitted; - uint32_t gcNumber; - js::GCMarker *gcIncrementalTracer; + js::FullGCMarker gcMarker; void *gcVerifyData; bool gcChunkAllocationSinceLastGC; int64_t gcNextFullGCTime; int64_t gcJitReleaseTime; JSGCMode gcMode; - volatile uintptr_t gcBarrierFailed; volatile uintptr_t gcIsNeeded; js::WeakMapBase *gcWeakMapList; js::gcstats::Statistics gcStats; + /* Incremented on every GC slice. */ + uint64_t gcNumber; + + /* The gcNumber at the time of the most recent GC's first slice. */ + uint64_t gcStartNumber; + /* The reason that an interrupt-triggered GC should be called. */ js::gcreason::Reason gcTriggerReason; - /* Pre-allocated space for the GC mark stack. */ - uintptr_t gcMarkStackArray[js::MARK_STACK_LENGTH]; - /* * Compartment that triggered GC. If more than one Compatment need GC, * gcTriggerCompartment is reset to NULL and a global GC is performed. @@ -326,6 +338,53 @@ struct JSRuntime : js::RuntimeFriendFields */ JSCompartment *gcCheckCompartment; + /* + * The current incremental GC phase. During non-incremental GC, this is + * always NO_INCREMENTAL. + */ + js::gc::State gcIncrementalState; + + /* Indicates that a new compartment was created during incremental GC. */ + bool gcCompartmentCreated; + + /* Indicates that the last incremental slice exhausted the mark stack. */ + bool gcLastMarkSlice; + + /* + * Indicates that a GC slice has taken place in the middle of an animation + * frame, rather than at the beginning. In this case, the next slice will be + * delayed so that we don't get back-to-back slices. + */ + volatile uintptr_t gcInterFrameGC; + + /* Default budget for incremental GC slice. See SliceBudget in jsgc.h. */ + int64_t gcSliceBudget; + + /* + * We disable incremental GC if we encounter a js::Class with a trace hook + * that does not implement write barriers. + */ + bool gcIncrementalEnabled; + + /* Compartment that is undergoing an incremental GC. */ + JSCompartment *gcIncrementalCompartment; + + /* + * We save all conservative scanned roots in this vector so that + * conservative scanning can be "replayed" deterministically. In DEBUG mode, + * this allows us to run a non-incremental GC after every incremental GC to + * ensure that no objects were missed. + */ +#ifdef DEBUG + struct SavedGCRoot { + void *thing; + JSGCTraceKind kind; + + SavedGCRoot(void *thing, JSGCTraceKind kind) : thing(thing), kind(kind) {} + }; + js::Vector gcSavedRoots; +#endif + /* * We can pack these flags as only the GC thread writes to them. Atomic * updates to packed bytes are not guaranteed, so stores issued by one @@ -333,7 +392,6 @@ struct JSRuntime : js::RuntimeFriendFields * other threads. */ bool gcPoke; - bool gcMarkAndSweep; bool gcRunning; /* @@ -342,7 +400,7 @@ struct JSRuntime : js::RuntimeFriendFields * gcNextScheduled is decremented. When it reaches zero, we do either a * full or a compartmental GC, based on gcDebugCompartmentGC. * - * At this point, if gcZeal_ >= 2 then gcNextScheduled is reset to the + * At this point, if gcZeal_ == 2 then gcNextScheduled is reset to the * value of gcZealFrequency. Otherwise, no additional GCs take place. * * You can control these values in several ways: @@ -350,9 +408,8 @@ struct JSRuntime : js::RuntimeFriendFields * - Call gczeal() or schedulegc() from inside shell-executed JS code * (see the help for details) * - * Additionally, if gzZeal_ == 1 then we perform GCs in select places - * (during MaybeGC and whenever a GC poke happens). This option is mainly - * useful to embedders. + * If gzZeal_ == 1 then we perform GCs in select places (during MaybeGC and + * whenever a GC poke happens). This option is mainly useful to embedders. * * We use gcZeal_ == 4 to enable write barrier verification. See the comment * in jsgc.cpp for more information about this. @@ -367,7 +424,7 @@ struct JSRuntime : js::RuntimeFriendFields bool needZealousGC() { if (gcNextScheduled > 0 && --gcNextScheduled == 0) { - if (gcZeal() >= js::gc::ZealAllocThreshold && gcZeal() < js::gc::ZealVerifierThreshold) + if (gcZeal() == js::gc::ZealAllocValue) gcNextScheduled = gcZealFrequency; return true; } @@ -379,7 +436,7 @@ struct JSRuntime : js::RuntimeFriendFields #endif JSGCCallback gcCallback; - JSGCFinishedCallback gcFinishedCallback; + js::GCSliceCallback gcSliceCallback; private: /* @@ -1121,6 +1178,8 @@ struct JSContext : js::ContextFriendFields return reinterpret_cast(uintptr_t(link) - offsetof(JSContext, link)); } + void mark(JSTracer *trc); + private: /* * The allocation code calls the function to indicate either OOM failure @@ -1558,18 +1617,18 @@ class AutoShapeVector : public AutoVectorRooter class AutoValueArray : public AutoGCRooter { - const js::Value *start_; + js::Value *start_; unsigned length_; public: - AutoValueArray(JSContext *cx, const js::Value *start, unsigned length + AutoValueArray(JSContext *cx, js::Value *start, unsigned length JS_GUARD_OBJECT_NOTIFIER_PARAM) : AutoGCRooter(cx, VALARRAY), start_(start), length_(length) { JS_GUARD_OBJECT_NOTIFIER_INIT; } - const Value *start() const { return start_; } + Value *start() { return start_; } unsigned length() const { return length_; } JS_DECL_USE_GUARD_OBJECT_NOTIFIER diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index b87b948697b..d40a5513f11 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -73,7 +73,6 @@ JSCompartment::JSCompartment(JSRuntime *rt) : rt(rt), principals(NULL), needsBarrier_(false), - gcIncrementalTracer(NULL), gcBytes(0), gcTriggerBytes(0), gcLastBytes(0), @@ -128,6 +127,9 @@ JSCompartment::init(JSContext *cx) if (!scriptFilenameTable.init()) return false; + if (!barrierMarker_.init()) + return false; + return debuggees.init(); } @@ -416,8 +418,11 @@ JSCompartment::markCrossCompartmentWrappers(JSTracer *trc) { JS_ASSERT(trc->runtime->gcCurrentCompartment); - for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) - MarkValueRoot(trc, e.front().key, "cross-compartment wrapper"); + for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) { + Value tmp = e.front().key; + MarkValueRoot(trc, &tmp, "cross-compartment wrapper"); + JS_ASSERT(tmp == e.front().key); + } } void @@ -432,7 +437,8 @@ JSCompartment::markTypes(JSTracer *trc) for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { JSScript *script = i.get(); - MarkScriptRoot(trc, script, "mark_types_script"); + MarkScriptRoot(trc, &script, "mark_types_script"); + JS_ASSERT(script == i.get()); } for (size_t thingKind = FINALIZE_OBJECT0; @@ -440,13 +446,42 @@ JSCompartment::markTypes(JSTracer *trc) thingKind++) { for (CellIterUnderGC i(this, AllocKind(thingKind)); !i.done(); i.next()) { JSObject *object = i.get(); - if (object->hasSingletonType()) - MarkObjectRoot(trc, object, "mark_types_singleton"); + if (object->hasSingletonType()) { + MarkObjectRoot(trc, &object, "mark_types_singleton"); + JS_ASSERT(object == i.get()); + } } } - for (CellIterUnderGC i(this, FINALIZE_TYPE_OBJECT); !i.done(); i.next()) - MarkTypeObjectRoot(trc, i.get(), "mark_types_scan"); + for (CellIterUnderGC i(this, FINALIZE_TYPE_OBJECT); !i.done(); i.next()) { + types::TypeObject *type = i.get(); + MarkTypeObjectRoot(trc, &type, "mark_types_scan"); + JS_ASSERT(type == i.get()); + } +} + +void +JSCompartment::discardJitCode(JSContext *cx) +{ + /* + * Kick all frames on the stack into the interpreter, and release all JIT + * code in the compartment. + */ +#ifdef JS_METHODJIT + mjit::ClearAllFrames(this); + + for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { + JSScript *script = i.get(); + mjit::ReleaseScriptCode(cx, script); + + /* + * Use counts for scripts are reset on GC. After discarding code we + * need to let it warm back up to get information like which opcodes + * are setting array holes or accessing getter properties. + */ + script->resetUseCount(); + } +#endif } void @@ -465,6 +500,8 @@ JSCompartment::sweep(JSContext *cx, bool releaseTypes) /* Remove dead references held weakly by the compartment. */ + regExps.sweep(rt); + sweepBaseShapeTable(cx); sweepInitialShapeTable(cx); sweepNewTypeObjectTable(cx, newTypeObjects); @@ -479,26 +516,7 @@ JSCompartment::sweep(JSContext *cx, bool releaseTypes) { gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_CODE); - - /* - * Kick all frames on the stack into the interpreter, and release all JIT - * code in the compartment. - */ -#ifdef JS_METHODJIT - mjit::ClearAllFrames(this); - - for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { - JSScript *script = i.get(); - mjit::ReleaseScriptCode(cx, script); - - /* - * Use counts for scripts are reset on GC. After discarding code we - * need to let it warm back up to get information like which opcodes - * are setting array holes or accessing getter properties. - */ - script->resetUseCount(); - } -#endif + discardJitCode(cx); } if (!activeAnalysis) { @@ -552,8 +570,6 @@ JSCompartment::sweep(JSContext *cx, bool releaseTypes) void JSCompartment::purge(JSContext *cx) { - arenas.purge(); - regExps.purge(); dtoaCache.purge(); /* @@ -767,13 +783,6 @@ JSCompartment::sweepBreakpoints(JSContext *cx) } } -GCMarker * -JSCompartment::createBarrierTracer() -{ - JS_ASSERT(!gcIncrementalTracer); - return NULL; -} - size_t JSCompartment::sizeOfShapeTable(JSMallocSizeOfFun mallocSizeOf) { diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 5c0edb1f5e2..14c25b84a3d 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -46,7 +46,6 @@ #include "jscntxt.h" #include "jsfun.h" #include "jsgc.h" -#include "jsgcstats.h" #include "jsobj.h" #include "jsscope.h" #include "vm/GlobalObject.h" @@ -163,6 +162,23 @@ typedef HashSet ScriptFilenameTable; +/* If HashNumber grows, need to change WrapperHasher. */ +JS_STATIC_ASSERT(sizeof(HashNumber) == 4); + +struct WrapperHasher +{ + typedef Value Lookup; + + static HashNumber hash(Value key) { + uint64_t bits = JSVAL_TO_IMPL(key).asBits; + return uint32_t(bits) ^ uint32_t(bits >> 32); + } + + static bool match(const Value &l, const Value &k) { return l == k; } +}; + +typedef HashMap WrapperMap; + } /* namespace js */ namespace JS { @@ -177,7 +193,7 @@ struct JSCompartment js::gc::ArenaLists arenas; bool needsBarrier_; - js::GCMarker *gcIncrementalTracer; + js::BarrierGCMarker barrierMarker_; bool needsBarrier() { return needsBarrier_; @@ -185,9 +201,7 @@ struct JSCompartment js::GCMarker *barrierTracer() { JS_ASSERT(needsBarrier_); - if (gcIncrementalTracer) - return gcIncrementalTracer; - return createBarrierTracer(); + return &barrierMarker_; } size_t gcBytes; @@ -325,10 +339,11 @@ struct JSCompartment bool wrap(JSContext *cx, js::AutoIdVector &props); void markTypes(JSTracer *trc); + void discardJitCode(JSContext *cx); void sweep(JSContext *cx, bool releaseTypes); void purge(JSContext *cx); - void setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind); + void setGCLastBytes(size_t lastBytes, js::JSGCInvocationKind gckind); void reduceGCTriggerBytes(size_t amount); void resetGCMallocBytes(); @@ -397,8 +412,6 @@ struct JSCompartment private: void sweepBreakpoints(JSContext *cx); - js::GCMarker *createBarrierTracer(); - public: js::WatchpointMap *watchpointMap; }; diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index 5ee3aaf47a9..ff61005b467 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -94,7 +94,7 @@ exn_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, Class js::ErrorClass = { js_Error_str, - JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_CACHED_PROTO(JSProto_Error), JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* delProperty */ @@ -419,22 +419,21 @@ exn_trace(JSTracer *trc, JSObject *obj) priv = GetExnPrivate(obj); if (priv) { if (priv->message) - MarkString(trc, priv->message, "exception message"); + MarkString(trc, &priv->message, "exception message"); if (priv->filename) - MarkString(trc, priv->filename, "exception filename"); + MarkString(trc, &priv->filename, "exception filename"); elem = priv->stackElems; for (vcount = i = 0; i != priv->stackDepth; ++i, ++elem) { if (elem->funName) - MarkString(trc, elem->funName, "stack trace function name"); + MarkString(trc, &elem->funName, "stack trace function name"); if (IS_GC_MARKING_TRACER(trc) && elem->filename) js_MarkScriptFilename(elem->filename); vcount += elem->argc; } vp = GetStackTraceValueBuffer(priv); - for (i = 0; i != vcount; ++i, ++vp) { - MarkValue(trc, *vp, "stack trace argument"); - } + for (i = 0; i != vcount; ++i, ++vp) + MarkValue(trc, vp, "stack trace argument"); } } diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 93d98bc54ed..cdb3786bfdb 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -132,7 +132,7 @@ JS_NewObjectWithUniqueType(JSContext *cx, JSClass *clasp, JSObject *proto, JSObj JS_FRIEND_API(void) js::GCForReason(JSContext *cx, gcreason::Reason reason) { - js_GC(cx, NULL, GC_NORMAL, reason); + GC(cx, NULL, GC_NORMAL, reason); } JS_FRIEND_API(void) @@ -141,13 +141,19 @@ js::CompartmentGCForReason(JSContext *cx, JSCompartment *comp, gcreason::Reason /* We cannot GC the atoms compartment alone; use a full GC instead. */ JS_ASSERT(comp != cx->runtime->atomsCompartment); - js_GC(cx, comp, GC_NORMAL, reason); + GC(cx, comp, GC_NORMAL, reason); } JS_FRIEND_API(void) js::ShrinkingGC(JSContext *cx, gcreason::Reason reason) { - js_GC(cx, NULL, GC_SHRINK, reason); + GC(cx, NULL, GC_SHRINK, reason); +} + +JS_FRIEND_API(void) +js::IncrementalGC(JSContext *cx, gcreason::Reason reason) +{ + GCSlice(cx, NULL, GC_NORMAL, reason); } JS_FRIEND_API(void) @@ -171,7 +177,7 @@ JS_WrapPropertyDescriptor(JSContext *cx, js::PropertyDescriptor *desc) JS_FRIEND_API(void) JS_TraceShapeCycleCollectorChildren(JSTracer *trc, void *shape) { - MarkCycleCollectorChildren(trc, (const Shape *)shape); + MarkCycleCollectorChildren(trc, (Shape *)shape); } AutoPreserveCompartment::AutoPreserveCompartment(JSContext *cx @@ -401,12 +407,6 @@ JS_SetAccumulateTelemetryCallback(JSRuntime *rt, JSAccumulateTelemetryDataCallba rt->telemetryCallback = callback; } -JS_FRIEND_API(void) -JS_SetGCFinishedCallback(JSRuntime *rt, JSGCFinishedCallback callback) -{ - rt->gcFinishedCallback = callback; -} - #ifdef DEBUG JS_FRIEND_API(void) js_DumpString(JSString *str) @@ -551,39 +551,6 @@ js::DumpHeapComplete(JSContext *cx, FILE *fp) namespace js { -JS_FRIEND_API(bool) -IsIncrementalBarrierNeeded(JSRuntime *rt) -{ - return !!rt->gcIncrementalTracer && !rt->gcRunning; -} - -JS_FRIEND_API(bool) -IsIncrementalBarrierNeeded(JSContext *cx) -{ - return IsIncrementalBarrierNeeded(cx->runtime); -} - -extern JS_FRIEND_API(void) -IncrementalReferenceBarrier(void *ptr) -{ - if (!ptr) - return; - JS_ASSERT(!static_cast(ptr)->compartment()->rt->gcRunning); - uint32_t kind = gc::GetGCThingTraceKind(ptr); - if (kind == JSTRACE_OBJECT) - JSObject::writeBarrierPre((JSObject *) ptr); - else if (kind == JSTRACE_STRING) - JSString::writeBarrierPre((JSString *) ptr); - else - JS_NOT_REACHED("invalid trace kind"); -} - -extern JS_FRIEND_API(void) -IncrementalValueBarrier(const Value &v) -{ - HeapValue::writeBarrierPre(v); -} - /* static */ void AutoLockGC::LockGC(JSRuntime *rt) { @@ -719,4 +686,90 @@ SizeOfJSContext() return sizeof(JSContext); } +JS_FRIEND_API(GCSliceCallback) +SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback) +{ + GCSliceCallback old = rt->gcSliceCallback; + rt->gcSliceCallback = callback; + return old; +} + +JS_FRIEND_API(bool) +WantGCSlice(JSRuntime *rt) +{ + if (rt->gcZeal() == gc::ZealFrameVerifierValue || rt->gcZeal() == gc::ZealFrameGCValue) + return true; + + if (rt->gcIncrementalState != gc::NO_INCREMENTAL) + return true; + + return false; +} + +JS_FRIEND_API(void) +NotifyDidPaint(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + + if (rt->gcZeal() == gc::ZealFrameVerifierValue) { + gc::VerifyBarriers(cx); + return; + } + + if (rt->gcZeal() == gc::ZealFrameGCValue) { + GCSlice(cx, NULL, GC_NORMAL, gcreason::REFRESH_FRAME); + return; + } + + if (rt->gcIncrementalState != gc::NO_INCREMENTAL && !rt->gcInterFrameGC) + GCSlice(cx, rt->gcIncrementalCompartment, GC_NORMAL, gcreason::REFRESH_FRAME); + + rt->gcInterFrameGC = false; +} + +extern JS_FRIEND_API(bool) +IsIncrementalGCEnabled(JSRuntime *rt) +{ + return rt->gcIncrementalEnabled; +} + +JS_FRIEND_API(bool) +IsIncrementalBarrierNeeded(JSRuntime *rt) +{ + return (rt->gcIncrementalState == gc::MARK && !rt->gcRunning); +} + +JS_FRIEND_API(bool) +IsIncrementalBarrierNeeded(JSContext *cx) +{ + return IsIncrementalBarrierNeeded(cx->runtime); +} + +JS_FRIEND_API(bool) +IsIncrementalBarrierNeededOnObject(JSObject *obj) +{ + return obj->compartment()->needsBarrier(); +} + +extern JS_FRIEND_API(void) +IncrementalReferenceBarrier(void *ptr) +{ + if (!ptr) + return; + JS_ASSERT(!static_cast(ptr)->compartment()->rt->gcRunning); + uint32_t kind = gc::GetGCThingTraceKind(ptr); + if (kind == JSTRACE_OBJECT) + JSObject::writeBarrierPre((JSObject *) ptr); + else if (kind == JSTRACE_STRING) + JSString::writeBarrierPre((JSString *) ptr); + else + JS_NOT_REACHED("invalid trace kind"); +} + +extern JS_FRIEND_API(void) +IncrementalValueBarrier(const Value &v) +{ + HeapValue::writeBarrierPre(v); +} + } // namespace js diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 495b4006817..c81d036e8ae 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -100,7 +100,11 @@ enum { JS_TELEMETRY_GC_IS_COMPARTMENTAL, JS_TELEMETRY_GC_MS, JS_TELEMETRY_GC_MARK_MS, - JS_TELEMETRY_GC_SWEEP_MS + JS_TELEMETRY_GC_SWEEP_MS, + JS_TELEMETRY_GC_SLICE_MS, + JS_TELEMETRY_GC_MMU_50, + JS_TELEMETRY_GC_RESET, + JS_TELEMETRY_GC_INCREMENTAL_DISABLED }; typedef void @@ -109,12 +113,6 @@ typedef void extern JS_FRIEND_API(void) JS_SetAccumulateTelemetryCallback(JSRuntime *rt, JSAccumulateTelemetryDataCallback callback); -typedef void -(* JSGCFinishedCallback)(JSRuntime *rt, JSCompartment *comp, const char *description); - -extern JS_FRIEND_API(void) -JS_SetGCFinishedCallback(JSRuntime *rt, JSGCFinishedCallback callback); - extern JS_FRIEND_API(JSPrincipals *) JS_GetCompartmentPrincipals(JSCompartment *compartment); @@ -703,12 +701,65 @@ CompartmentGCForReason(JSContext *cx, JSCompartment *comp, gcreason::Reason reas extern JS_FRIEND_API(void) ShrinkingGC(JSContext *cx, gcreason::Reason reason); +extern JS_FRIEND_API(void) +IncrementalGC(JSContext *cx, gcreason::Reason reason); + +extern JS_FRIEND_API(void) +SetGCSliceTimeBudget(JSContext *cx, int64_t millis); + +enum GCProgress { + /* + * During non-incremental GC, the GC is bracketed by JSGC_CYCLE_BEGIN/END + * callbacks. During an incremental GC, the sequence of callbacks is as + * follows: + * JSGC_CYCLE_BEGIN, JSGC_SLICE_END (first slice) + * JSGC_SLICE_BEGIN, JSGC_SLICE_END (second slice) + * ... + * JSGC_SLICE_BEGIN, JSGC_CYCLE_END (last slice) + */ + + GC_CYCLE_BEGIN, + GC_SLICE_BEGIN, + GC_SLICE_END, + GC_CYCLE_END +}; + +struct GCDescription { + const char *logMessage; + bool isCompartment; + + GCDescription(const char *msg, bool isCompartment) + : logMessage(msg), isCompartment(isCompartment) {} +}; + +typedef void +(* GCSliceCallback)(JSRuntime *rt, GCProgress progress, const GCDescription &desc); + +extern JS_FRIEND_API(GCSliceCallback) +SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback); + +extern JS_FRIEND_API(bool) +WantGCSlice(JSRuntime *rt); + +/* + * Signals a good place to do an incremental slice, because the browser is + * drawing a frame. + */ +extern JS_FRIEND_API(void) +NotifyDidPaint(JSContext *cx); + +extern JS_FRIEND_API(bool) +IsIncrementalGCEnabled(JSRuntime *rt); + extern JS_FRIEND_API(bool) IsIncrementalBarrierNeeded(JSRuntime *rt); extern JS_FRIEND_API(bool) IsIncrementalBarrierNeeded(JSContext *cx); +extern JS_FRIEND_API(bool) +IsIncrementalBarrierNeededOnObject(JSObject *obj); + extern JS_FRIEND_API(void) IncrementalReferenceBarrier(void *ptr); diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 1c57cb9b42a..ceb96391c5c 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -100,12 +100,6 @@ using namespace js; using namespace js::gc; using namespace js::types; -inline JSObject * -JSObject::getThrowTypeError() const -{ - return global().getThrowTypeError(); -} - JSBool js_GetArgsValue(JSContext *cx, StackFrame *fp, Value *vp) { @@ -475,8 +469,8 @@ strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject } attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED; - getter = CastAsPropertyOp(argsobj.getThrowTypeError()); - setter = CastAsStrictPropertyOp(argsobj.getThrowTypeError()); + getter = CastAsPropertyOp(argsobj.global().getThrowTypeError()); + setter = CastAsStrictPropertyOp(argsobj.global().getThrowTypeError()); } Value undef = UndefinedValue(); @@ -530,7 +524,7 @@ args_trace(JSTracer *trc, JSObject *obj) { ArgumentsObject &argsobj = obj->asArguments(); ArgumentsData *data = argsobj.data(); - MarkValue(trc, data->callee, js_callee_str); + MarkValue(trc, &data->callee, js_callee_str); MarkValueRange(trc, argsobj.initialLength(), data->slots, js_arguments_str); /* @@ -545,7 +539,7 @@ args_trace(JSTracer *trc, JSObject *obj) #if JS_HAS_GENERATORS StackFrame *fp = argsobj.maybeStackFrame(); if (fp && fp->isFloatingGenerator()) - MarkObject(trc, js_FloatingFrameToGenerator(fp)->obj, "generator object"); + MarkObject(trc, &js_FloatingFrameToGenerator(fp)->obj, "generator object"); #endif } @@ -557,7 +551,7 @@ args_trace(JSTracer *trc, JSObject *obj) */ Class js::NormalArgumentsObjectClass = { "Arguments", - JSCLASS_NEW_RESOLVE | + JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(NormalArgumentsObject::RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | JSCLASS_FOR_OF_ITERATION, @@ -593,7 +587,7 @@ Class js::NormalArgumentsObjectClass = { */ Class js::StrictArgumentsObjectClass = { "Arguments", - JSCLASS_NEW_RESOLVE | + JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(StrictArgumentsObject::RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | JSCLASS_FOR_OF_ITERATION, @@ -942,13 +936,13 @@ call_trace(JSTracer *trc, JSObject *obj) #if JS_HAS_GENERATORS StackFrame *fp = (StackFrame *) obj->getPrivate(); if (fp && fp->isFloatingGenerator()) - MarkObject(trc, js_FloatingFrameToGenerator(fp)->obj, "generator object"); + MarkObject(trc, &js_FloatingFrameToGenerator(fp)->obj, "generator object"); #endif } JS_PUBLIC_DATA(Class) js::CallClass = { "Call", - JSCLASS_HAS_PRIVATE | + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CallObject::RESERVED_SLOTS) | JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS, JS_PropertyStub, /* addProperty */ @@ -1329,7 +1323,7 @@ fun_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, StrictPropertyOp setter; uintN attrs = JSPROP_PERMANENT; if (fun->isInterpreted() ? fun->inStrictMode() : fun->isBoundFunction()) { - JSObject *throwTypeError = fun->getThrowTypeError(); + JSObject *throwTypeError = fun->global().getThrowTypeError(); getter = CastAsPropertyOp(throwTypeError); setter = CastAsStrictPropertyOp(throwTypeError); @@ -1471,7 +1465,7 @@ JSFunction::trace(JSTracer *trc) if (isInterpreted()) { if (script()) - MarkScript(trc, script(), "script"); + MarkScript(trc, &script(), "script"); if (environment()) MarkObjectUnbarriered(trc, environment(), "fun_callscope"); } @@ -1505,7 +1499,7 @@ JSFunction::sizeOfMisc(JSMallocSizeOfFun mallocSizeOf) const */ JS_FRIEND_DATA(Class) js::FunctionClass = { js_Function_str, - JSCLASS_NEW_RESOLVE | + JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_CACHED_PROTO(JSProto_Function), JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* delProperty */ diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index f92f1e5bfc6..a7e42a43148 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -44,13 +44,39 @@ #include "mozilla/Util.h" /* - * This GC allocates fixed-sized things with sizes up to GC_NBYTES_MAX (see - * jsgc.h). It allocates from a special GC arena pool with each arena allocated - * using malloc. It uses an ideally parallel array of flag bytes to hold the - * mark bit, finalizer type index, etc. + * This code implements a mark-and-sweep garbage collector. The mark phase is + * incremental. Most sweeping is done on a background thread. A GC is divided + * into slices as follows: * - * XXX swizzle page to freelist for better locality of reference + * Slice 1: Roots pushed onto the mark stack. The mark stack is processed by + * popping an element, marking it, and pushing its children. + * ... JS code runs ... + * Slice 2: More mark stack processing. + * ... JS code runs ... + * Slice n-1: More mark stack processing. + * ... JS code runs ... + * Slice n: Mark stack is completely drained. Some sweeping is done. + * ... JS code runs, remaining sweeping done on background thread ... + * + * When background sweeping finishes the GC is complete. + * + * Incremental GC requires close collaboration with the mutator (i.e., JS code): + * + * 1. During an incremental GC, if a memory location (except a root) is written + * to, then the value it previously held must be marked. Write barriers ensure + * this. + * 2. Any object that is allocated during incremental GC must start out marked. + * 3. Roots are special memory locations that don't need write + * barriers. However, they must be marked in the first slice. Roots are things + * like the C stack and the VM stack, since it would be too expensive to put + * barriers on them. + * + * Write barriers are handled using the compartment's barrierMarker_ + * JSTracer. This includes a per-compartment stack of GC things that have been + * write-barriered. This stack is processed in each GC slice. The barrierMarker_ + * is also used during write barrier verification (VerifyBarriers below). */ + #include #include /* for memset used when DEBUG */ @@ -118,12 +144,31 @@ using namespace js::gc; namespace js { namespace gc { +/* + * Lower limit after which we limit the heap growth + */ +const size_t GC_ALLOCATION_THRESHOLD = 30 * 1024 * 1024; + +/* + * A GC is triggered once the number of newly allocated arenas is + * GC_HEAP_GROWTH_FACTOR times the number of live arenas after the last GC + * starting after the lower limit of GC_ALLOCATION_THRESHOLD. This number is + * used for non-incremental GCs. + */ +const float GC_HEAP_GROWTH_FACTOR = 3.0f; + +/* Perform a Full GC every 20 seconds if MaybeGC is called */ +static const uint64_t GC_IDLE_FULL_SPAN = 20 * 1000 * 1000; + #ifdef JS_GC_ZEAL static void StartVerifyBarriers(JSContext *cx); static void EndVerifyBarriers(JSContext *cx); + +void +FinishVerifier(JSRuntime *rt); #endif /* This array should be const, but that doesn't link right under GCC. */ @@ -275,6 +320,8 @@ Arena::finalize(JSContext *cx, AllocKind thingKind, size_t thingSize, bool backg JS_ASSERT(thingKind == aheader.getAllocKind()); JS_ASSERT(thingSize == aheader.getThingSize()); JS_ASSERT(!aheader.hasDelayedMarking); + JS_ASSERT(!aheader.markOverflow); + JS_ASSERT(!aheader.allocatedDuringIncremental); uintptr_t thing = thingsStart(thingKind); uintptr_t lastByte = thingsEnd() - 1; @@ -850,7 +897,6 @@ IsAboutToBeFinalized(const Cell *thing) JSRuntime *rt = thingCompartment->rt; if (rt->gcCurrentCompartment != NULL && rt->gcCurrentCompartment != thingCompartment) return false; - return !reinterpret_cast(thing)->isMarked(); } @@ -926,6 +972,18 @@ InFreeList(ArenaHeader *aheader, uintptr_t addr) } } +enum ConservativeGCTest +{ + CGCT_VALID, + CGCT_LOWBITSET, /* excluded because one of the low bits was set */ + CGCT_NOTARENA, /* not within arena range in a chunk */ + CGCT_OTHERCOMPARTMENT, /* in another compartment */ + CGCT_NOTCHUNK, /* not within a valid chunk */ + CGCT_FREEARENA, /* within arena containing only free things */ + CGCT_NOTLIVE, /* gcthing is not allocated */ + CGCT_END +}; + /* * Tests whether w is a (possibly dead) GC thing. Returns CGCT_VALID and * details about the thing if so. On failure, returns the reason for rejection. @@ -1024,22 +1082,18 @@ MarkIfGCThingWord(JSTracer *trc, uintptr_t w) if (InFreeList(aheader, uintptr_t(thing))) return CGCT_NOTLIVE; + JSGCTraceKind traceKind = MapAllocToTraceKind(thingKind); #ifdef DEBUG const char pattern[] = "machine_stack %p"; char nameBuf[sizeof(pattern) - 2 + sizeof(thing) * 2]; JS_snprintf(nameBuf, sizeof(nameBuf), pattern, thing); JS_SET_TRACING_NAME(trc, nameBuf); #endif - MarkKind(trc, thing, MapAllocToTraceKind(thingKind)); + MarkKind(trc, thing, traceKind); -#ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS - if (IS_GC_MARKING_TRACER(trc)) { - GCMarker *marker = static_cast(trc); - if (marker->conservativeDumpFileName) - marker->conservativeRoots.append(thing); - if (uintptr_t(thing) != w) - marker->conservativeStats.unaligned++; - } +#ifdef DEBUG + if (trc->runtime->gcIncrementalState == MARK_ROOTS) + trc->runtime->gcSavedRoots.append(JSRuntime::SavedGCRoot(thing, traceKind)); #endif return CGCT_VALID; @@ -1070,8 +1124,26 @@ MarkRangeConservatively(JSTracer *trc, const uintptr_t *begin, const uintptr_t * } static JS_NEVER_INLINE void -MarkConservativeStackRoots(JSTracer *trc, JSRuntime *rt) +MarkConservativeStackRoots(JSTracer *trc, bool useSavedRoots) { + JSRuntime *rt = trc->runtime; + +#ifdef DEBUG + if (useSavedRoots) { + for (JSRuntime::SavedGCRoot *root = rt->gcSavedRoots.begin(); + root != rt->gcSavedRoots.end(); + root++) + { + JS_SET_TRACING_NAME(trc, "cstack"); + MarkKind(trc, root->thing, root->kind); + } + return; + } + + if (rt->gcIncrementalState == MARK_ROOTS) + rt->gcSavedRoots.clearAndFree(); +#endif + ConservativeGCData *cgcd = &rt->conservativeGC; if (!cgcd->hasStackToScan()) { #ifdef JS_THREADSAFE @@ -1132,6 +1204,8 @@ MarkStackRangeConservatively(JSTracer *trc, Value *beginv, Value *endv) #endif } + + JS_NEVER_INLINE void ConservativeGCData::recordStackTop() { @@ -1191,6 +1265,11 @@ js_FinishGC(JSRuntime *rt) rt->gcHelperThread.finish(); #endif +#ifdef JS_GC_ZEAL + /* Free memory associated with GC verification. */ + FinishVerifier(rt); +#endif + /* Delete all remaining Compartments. */ for (CompartmentsIter c(rt); !c.done(); c.next()) Foreground::delete_(c.get()); @@ -1236,7 +1315,7 @@ js_AddRootRT(JSRuntime *rt, jsval *vp, const char *name) { /* * Due to the long-standing, but now removed, use of rt->gcLock across the - * bulk of js_GC, API users have come to depend on JS_AddRoot etc. locking + * bulk of js::GC, API users have come to depend on JS_AddRoot etc. locking * properly with a racing GC, without calling JS_AddRoot from a request. * We have to preserve API compatibility here, now that we avoid holding * rt->gcLock across the mark phase (including the root hashtable mark). @@ -1252,7 +1331,7 @@ js_AddGCThingRootRT(JSRuntime *rt, void **rp, const char *name) { /* * Due to the long-standing, but now removed, use of rt->gcLock across the - * bulk of js_GC, API users have come to depend on JS_AddRoot etc. locking + * bulk of js::GC, API users have come to depend on JS_AddRoot etc. locking * properly with a racing GC, without calling JS_AddRoot from a request. * We have to preserve API compatibility here, now that we avoid holding * rt->gcLock across the mark phase (including the root hashtable mark). @@ -1370,6 +1449,19 @@ JSCompartment::reduceGCTriggerBytes(size_t amount) namespace js { namespace gc { +inline void +ArenaLists::prepareForIncrementalGC(JSCompartment *comp) +{ + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { + FreeSpan *headSpan = &freeLists[i]; + if (!headSpan->isEmpty()) { + ArenaHeader *aheader = headSpan->arenaHeader(); + aheader->allocatedDuringIncremental = true; + comp->barrierMarker_.delayMarkingArena(aheader); + } + } +} + inline void * ArenaLists::allocateFromArena(JSCompartment *comp, AllocKind thingKind) { @@ -1423,6 +1515,10 @@ ArenaLists::allocateFromArena(JSCompartment *comp, AllocKind thingKind) */ freeLists[thingKind] = aheader->getFirstFreeSpan(); aheader->setAsFullyUsed(); + if (JS_UNLIKELY(comp->needsBarrier())) { + aheader->allocatedDuringIncremental = true; + comp->barrierMarker_.delayMarkingArena(aheader); + } return freeLists[thingKind].infallibleAllocate(Arena::thingSize(thingKind)); } @@ -1448,6 +1544,10 @@ ArenaLists::allocateFromArena(JSCompartment *comp, AllocKind thingKind) if (!aheader) return NULL; + if (JS_UNLIKELY(comp->needsBarrier())) { + aheader->allocatedDuringIncremental = true; + comp->barrierMarker_.delayMarkingArena(aheader); + } aheader->next = al->head; if (!al->head) { JS_ASSERT(al->cursor == &al->head); @@ -1619,7 +1719,7 @@ RunLastDitchGC(JSContext *cx) /* The last ditch GC preserves all atoms. */ AutoKeepAtoms keep(rt); - js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL, gcreason::LAST_DITCH); + GC(cx, rt->gcTriggerCompartment, GC_NORMAL, gcreason::LAST_DITCH); } /* static */ void * @@ -1631,7 +1731,7 @@ ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind) JSRuntime *rt = comp->rt; JS_ASSERT(!rt->gcRunning); - bool runGC = !!rt->gcIsNeeded; + bool runGC = rt->gcIncrementalState != NO_INCREMENTAL && comp->gcBytes > comp->gcTriggerBytes; for (;;) { if (JS_UNLIKELY(runGC)) { RunLastDitchGC(cx); @@ -1722,6 +1822,137 @@ js_UnlockGCThingRT(JSRuntime *rt, void *thing) namespace js { +void +InitTracer(JSTracer *trc, JSRuntime *rt, JSContext *cx, JSTraceCallback callback) +{ + trc->runtime = rt; + trc->context = cx; + trc->callback = callback; + trc->debugPrinter = NULL; + trc->debugPrintArg = NULL; + trc->debugPrintIndex = size_t(-1); + trc->eagerlyTraceWeakMaps = true; +} + +/* static */ int64_t +SliceBudget::TimeBudget(int64_t millis) +{ + return millis * PRMJ_USEC_PER_MSEC; +} + +/* static */ int64_t +SliceBudget::WorkBudget(int64_t work) +{ + return -work; +} + +SliceBudget::SliceBudget() + : deadline(INT64_MAX), + counter(INTPTR_MAX) +{ +} + +SliceBudget::SliceBudget(int64_t budget) +{ + if (budget == Unlimited) { + deadline = INT64_MAX; + counter = INTPTR_MAX; + } else if (budget > 0) { + deadline = PRMJ_Now() + budget; + counter = CounterReset; + } else { + deadline = 0; + counter = -budget; + } +} + +bool +SliceBudget::checkOverBudget() +{ + bool over = PRMJ_Now() > deadline; + if (!over) + counter = CounterReset; + return over; +} + +GCMarker::GCMarker() + : color(BLACK), + started(false), + unmarkedArenaStackTop(NULL), + markLaterArenas(0), + grayFailed(false) +{ +} + +bool +GCMarker::init(bool lazy) +{ + if (!stack.init(lazy ? 0 : MARK_STACK_LENGTH)) + return false; + return true; +} + +void +GCMarker::start(JSRuntime *rt, JSContext *cx) +{ + InitTracer(this, rt, cx, NULL); + JS_ASSERT(!started); + started = true; + color = BLACK; + + JS_ASSERT(!unmarkedArenaStackTop); + JS_ASSERT(markLaterArenas == 0); + + JS_ASSERT(grayRoots.empty()); + JS_ASSERT(!grayFailed); + + /* + * The GC is recomputing the liveness of WeakMap entries, so we delay + * visting entries. + */ + eagerlyTraceWeakMaps = JS_FALSE; +} + +void +GCMarker::stop() +{ + JS_ASSERT(isDrained()); + + JS_ASSERT(started); + started = false; + + JS_ASSERT(!unmarkedArenaStackTop); + JS_ASSERT(markLaterArenas == 0); + + JS_ASSERT(grayRoots.empty()); + grayFailed = false; +} + +void +GCMarker::reset() +{ + color = BLACK; + + stack.reset(); + JS_ASSERT(isMarkStackEmpty()); + + while (unmarkedArenaStackTop) { + ArenaHeader *aheader = unmarkedArenaStackTop; + JS_ASSERT(aheader->hasDelayedMarking); + JS_ASSERT(markLaterArenas); + unmarkedArenaStackTop = aheader->getNextDelayedMarking(); + aheader->hasDelayedMarking = 0; + aheader->markOverflow = 0; + aheader->allocatedDuringIncremental = 0; + markLaterArenas--; + } + JS_ASSERT(isDrained()); + JS_ASSERT(!markLaterArenas); + + grayRoots.clearAndFree(); + grayFailed = false; +} + /* * When the native stack is low, the GC does not call JS_TraceChildren to mark * the reachable "children" of the thing. Rather the thing is put aside and @@ -1736,63 +1967,52 @@ namespace js { * from the stack until it empties. */ -GCMarker::GCMarker(JSContext *cx) - : color(BLACK), - unmarkedArenaStackTop(NULL), - stack(cx->runtime->gcMarkStackArray) +inline void +GCMarker::delayMarkingArena(ArenaHeader *aheader) { - JS_TracerInit(this, cx, NULL); - markLaterArenas = 0; -#ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS - conservativeDumpFileName = getenv("JS_DUMP_CONSERVATIVE_GC_ROOTS"); - memset(&conservativeStats, 0, sizeof(conservativeStats)); -#endif - - /* - * The GC is recomputing the liveness of WeakMap entries, so we - * delay visting entries. - */ - eagerlyTraceWeakMaps = JS_FALSE; -} - -GCMarker::~GCMarker() -{ -#ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS - dumpConservativeRoots(); -#endif + if (aheader->hasDelayedMarking) { + /* Arena already scheduled to be marked later */ + return; + } + aheader->setNextDelayedMarking(unmarkedArenaStackTop); + unmarkedArenaStackTop = aheader; + markLaterArenas++; } void GCMarker::delayMarkingChildren(const void *thing) { const Cell *cell = reinterpret_cast(thing); - ArenaHeader *aheader = cell->arenaHeader(); - if (aheader->hasDelayedMarking) { - /* Arena already scheduled to be marked later */ - return; - } - aheader->setNextDelayedMarking(unmarkedArenaStackTop); - unmarkedArenaStackTop = aheader->getArena(); - markLaterArenas++; -} - -static void -MarkDelayedChildren(GCMarker *trc, Arena *a) -{ - AllocKind allocKind = a->aheader.getAllocKind(); - JSGCTraceKind traceKind = MapAllocToTraceKind(allocKind); - size_t thingSize = Arena::thingSize(allocKind); - uintptr_t end = a->thingsEnd(); - for (uintptr_t thing = a->thingsStart(allocKind); thing != end; thing += thingSize) { - Cell *t = reinterpret_cast(thing); - if (t->isMarked()) - JS_TraceChildren(trc, t, traceKind); - } + cell->arenaHeader()->markOverflow = 1; + delayMarkingArena(cell->arenaHeader()); } void -GCMarker::markDelayedChildren() +GCMarker::markDelayedChildren(ArenaHeader *aheader) { + if (aheader->markOverflow) { + bool always = aheader->allocatedDuringIncremental; + aheader->markOverflow = 0; + + for (CellIterUnderGC i(aheader); !i.done(); i.next()) { + Cell *t = i.getCell(); + if (always || t->isMarked()) { + t->markIfUnmarked(); + JS_TraceChildren(this, t, MapAllocToTraceKind(aheader->getAllocKind())); + } + } + } else { + JS_ASSERT(aheader->allocatedDuringIncremental); + PushArena(this, aheader); + } + aheader->allocatedDuringIncremental = 0; +} + +bool +GCMarker::markDelayedChildren(SliceBudget &budget) +{ + gcstats::AutoPhase ap(runtime->gcStats, gcstats::PHASE_MARK_DELAYED); + JS_ASSERT(unmarkedArenaStackTop); do { /* @@ -1800,15 +2020,101 @@ GCMarker::markDelayedChildren() * marking of its things. For that we pop arena from the stack and * clear its hasDelayedMarking flag before we begin the marking. */ - Arena *a = unmarkedArenaStackTop; - JS_ASSERT(a->aheader.hasDelayedMarking); + ArenaHeader *aheader = unmarkedArenaStackTop; + JS_ASSERT(aheader->hasDelayedMarking); JS_ASSERT(markLaterArenas); - unmarkedArenaStackTop = a->aheader.getNextDelayedMarking(); - a->aheader.hasDelayedMarking = 0; + unmarkedArenaStackTop = aheader->getNextDelayedMarking(); + aheader->hasDelayedMarking = 0; markLaterArenas--; - MarkDelayedChildren(this, a); + markDelayedChildren(aheader); + + if (budget.checkOverBudget()) + return false; } while (unmarkedArenaStackTop); JS_ASSERT(!markLaterArenas); + + return true; +} + +#ifdef DEBUG +void +GCMarker::checkCompartment(void *p) +{ + JS_ASSERT(started); + + Cell *cell = static_cast(p); + if (runtime->gcRunning && runtime->gcCurrentCompartment) + JS_ASSERT(cell->compartment() == runtime->gcCurrentCompartment); + else if (runtime->gcIncrementalCompartment) + JS_ASSERT(cell->compartment() == runtime->gcIncrementalCompartment); +} +#endif + +bool +GCMarker::hasBufferedGrayRoots() const +{ + return !grayFailed; +} + +void +GCMarker::startBufferingGrayRoots() +{ + JS_ASSERT(!callback); + callback = GrayCallback; + JS_ASSERT(IS_GC_MARKING_TRACER(this)); +} + +void +GCMarker::endBufferingGrayRoots() +{ + JS_ASSERT(callback == GrayCallback); + callback = NULL; + JS_ASSERT(IS_GC_MARKING_TRACER(this)); +} + +void +GCMarker::markBufferedGrayRoots() +{ + JS_ASSERT(!grayFailed); + + for (GrayRoot *elem = grayRoots.begin(); elem != grayRoots.end(); elem++) { +#ifdef DEBUG + debugPrinter = elem->debugPrinter; + debugPrintArg = elem->debugPrintArg; + debugPrintIndex = elem->debugPrintIndex; +#endif + MarkKind(this, elem->thing, elem->kind); + } + + grayRoots.clearAndFree(); +} + +void +GCMarker::appendGrayRoot(void *thing, JSGCTraceKind kind) +{ + JS_ASSERT(started); + + if (grayFailed) + return; + + GrayRoot root(thing, kind); +#ifdef DEBUG + root.debugPrinter = debugPrinter; + root.debugPrintArg = debugPrintArg; + root.debugPrintIndex = debugPrintIndex; +#endif + + if (!grayRoots.append(root)) { + grayRoots.clearAndFree(); + grayFailed = true; + } +} + +void +GCMarker::GrayCallback(JSTracer *trc, void **thingp, JSGCTraceKind kind) +{ + GCMarker *gcmarker = static_cast(trc); + gcmarker->appendGrayRoot(*thingp, kind); } } /* namespace js */ @@ -1855,7 +2161,7 @@ gc_root_traversal(JSTracer *trc, const RootEntry &entry) if (entry.value.type == JS_GC_ROOT_GCTHING_PTR) MarkGCThingRoot(trc, *reinterpret_cast(entry.key), name); else - MarkValueRoot(trc, *reinterpret_cast(entry.key), name); + MarkValueRoot(trc, reinterpret_cast(entry.key), name); } static void @@ -1865,6 +2171,17 @@ gc_lock_traversal(const GCLocks::Entry &entry, JSTracer *trc) MarkGCThingRoot(trc, entry.key, "locked object"); } +namespace js { + +void +MarkCompartmentActive(StackFrame *fp) +{ + if (fp->isScriptFrame()) + fp->script()->compartment()->active = true; +} + +} /* namespace js */ + void AutoIdArray::trace(JSTracer *trc) { @@ -1875,7 +2192,7 @@ AutoIdArray::trace(JSTracer *trc) void AutoEnumStateRooter::trace(JSTracer *trc) { - gc::MarkObjectRoot(trc, obj, "JS::AutoEnumStateRooter.obj"); + gc::MarkObjectRoot(trc, &obj, "JS::AutoEnumStateRooter.obj"); } inline void @@ -1883,7 +2200,7 @@ AutoGCRooter::trace(JSTracer *trc) { switch (tag) { case JSVAL: - MarkValueRoot(trc, static_cast(this)->val, "JS::AutoValueRooter.val"); + MarkValueRoot(trc, &static_cast(this)->val, "JS::AutoValueRooter.val"); return; case PARSER: @@ -1905,10 +2222,10 @@ AutoGCRooter::trace(JSTracer *trc) static_cast(this)->descriptors; for (size_t i = 0, len = descriptors.length(); i < len; i++) { PropDesc &desc = descriptors[i]; - MarkValueRoot(trc, desc.pd, "PropDesc::pd"); - MarkValueRoot(trc, desc.value, "PropDesc::value"); - MarkValueRoot(trc, desc.get, "PropDesc::get"); - MarkValueRoot(trc, desc.set, "PropDesc::set"); + MarkValueRoot(trc, &desc.pd, "PropDesc::pd"); + MarkValueRoot(trc, &desc.value, "PropDesc::value"); + MarkValueRoot(trc, &desc.get, "PropDesc::get"); + MarkValueRoot(trc, &desc.set, "PropDesc::set"); } return; } @@ -1916,12 +2233,18 @@ AutoGCRooter::trace(JSTracer *trc) case DESCRIPTOR : { PropertyDescriptor &desc = *static_cast(this); if (desc.obj) - MarkObjectRoot(trc, desc.obj, "Descriptor::obj"); - MarkValueRoot(trc, desc.value, "Descriptor::value"); - if ((desc.attrs & JSPROP_GETTER) && desc.getter) - MarkObjectRoot(trc, CastAsObject(desc.getter), "Descriptor::get"); - if (desc.attrs & JSPROP_SETTER && desc.setter) - MarkObjectRoot(trc, CastAsObject(desc.setter), "Descriptor::set"); + MarkObjectRoot(trc, &desc.obj, "Descriptor::obj"); + MarkValueRoot(trc, &desc.value, "Descriptor::value"); + if ((desc.attrs & JSPROP_GETTER) && desc.getter) { + JSObject *tmp = JS_FUNC_TO_DATA_PTR(JSObject *, desc.getter); + MarkObjectRoot(trc, &tmp, "Descriptor::get"); + desc.getter = JS_DATA_TO_FUNC_PTR(JSPropertyOp, tmp); + } + if (desc.attrs & JSPROP_SETTER && desc.setter) { + JSObject *tmp = JS_FUNC_TO_DATA_PTR(JSObject *, desc.setter); + MarkObjectRoot(trc, &tmp, "Descriptor::set"); + desc.setter = JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, tmp); + } return; } @@ -1937,12 +2260,13 @@ AutoGCRooter::trace(JSTracer *trc) return; case OBJECT: - if (JSObject *obj = static_cast(this)->obj) - MarkObjectRoot(trc, obj, "JS::AutoObjectRooter.obj"); + if (static_cast(this)->obj) + MarkObjectRoot(trc, &static_cast(this)->obj, + "JS::AutoObjectRooter.obj"); return; case ID: - MarkIdRoot(trc, static_cast(this)->id_, "JS::AutoIdRooter.id_"); + MarkIdRoot(trc, &static_cast(this)->id_, "JS::AutoIdRooter.id_"); return; case VALVECTOR: { @@ -1952,8 +2276,9 @@ AutoGCRooter::trace(JSTracer *trc) } case STRING: - if (JSString *str = static_cast(this)->str) - MarkStringRoot(trc, str, "JS::AutoStringRooter.str"); + if (static_cast(this)->str) + MarkStringRoot(trc, &static_cast(this)->str, + "JS::AutoStringRooter.str"); return; case IDVECTOR: { @@ -1996,45 +2321,19 @@ AutoGCRooter::traceAll(JSTracer *trc) namespace js { -JS_FRIEND_API(void) -MarkContext(JSTracer *trc, JSContext *acx) -{ - /* Stack frames and slots are traced by StackSpace::mark. */ - - /* Mark other roots-by-definition in acx. */ - if (acx->globalObject && !acx->hasRunOption(JSOPTION_UNROOTED_GLOBAL)) - MarkObjectRoot(trc, acx->globalObject, "global object"); - if (acx->isExceptionPending()) - MarkValueRoot(trc, acx->getPendingException(), "exception"); - - if (acx->autoGCRooters) - acx->autoGCRooters->traceAll(trc); - - if (acx->sharpObjectMap.depth > 0) - js_TraceSharpMap(trc, &acx->sharpObjectMap); - - MarkValueRoot(trc, acx->iterValue, "iterValue"); -} - -void -MarkWeakReferences(GCMarker *gcmarker) -{ - JS_ASSERT(gcmarker->isMarkStackEmpty()); - while (WatchpointMap::markAllIteratively(gcmarker) || - WeakMapBase::markAllIteratively(gcmarker) || - Debugger::markAllIteratively(gcmarker)) { - gcmarker->drainMarkStack(); - } - JS_ASSERT(gcmarker->isMarkStackEmpty()); -} - static void -MarkRuntime(JSTracer *trc) +MarkRuntime(JSTracer *trc, bool useSavedRoots = false) { JSRuntime *rt = trc->runtime; + JS_ASSERT(trc->callback != GCMarker::GrayCallback); + if (rt->gcCurrentCompartment) { + for (CompartmentsIter c(rt); !c.done(); c.next()) + c->markCrossCompartmentWrappers(trc); + Debugger::markCrossCompartmentDebuggerObjectReferents(trc); + } if (rt->hasContexts()) - MarkConservativeStackRoots(trc, rt); + MarkConservativeStackRoots(trc, useSavedRoots); for (RootRange r = rt->gcRootsHash.all(); !r.empty(); r.popFront()) gc_root_traversal(trc, r.front()); @@ -2043,9 +2342,9 @@ MarkRuntime(JSTracer *trc) gc_lock_traversal(r.front(), trc); if (rt->scriptPCCounters) { - const ScriptOpcodeCountsVector &vec = *rt->scriptPCCounters; + ScriptOpcodeCountsVector &vec = *rt->scriptPCCounters; for (size_t i = 0; i < vec.length(); i++) - MarkScriptRoot(trc, vec[i].script, "scriptPCCounters"); + MarkScriptRoot(trc, &vec[i].script, "scriptPCCounters"); } js_TraceAtomState(trc); @@ -2053,7 +2352,7 @@ MarkRuntime(JSTracer *trc) JSContext *iter = NULL; while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter)) - MarkContext(trc, acx); + acx->mark(trc); for (GCCompartmentsIter c(rt); !c.done(); c.next()) { if (c->activeAnalysis) @@ -2069,8 +2368,10 @@ MarkRuntime(JSTracer *trc) if (rt->profilingScripts) { for (CellIterUnderGC i(c, FINALIZE_SCRIPT); !i.done(); i.next()) { JSScript *script = i.get(); - if (script->pcCounters) - MarkScriptRoot(trc, script, "profilingScripts"); + if (script->pcCounters) { + MarkScriptRoot(trc, &script, "profilingScripts"); + JS_ASSERT(script == i.get()); + } } } } @@ -2087,13 +2388,18 @@ MarkRuntime(JSTracer *trc) if (JSTraceDataOp op = rt->gcBlackRootsTraceOp) (*op)(trc, rt->gcBlackRootsData); - if (!IS_GC_MARKING_TRACER(trc)) { - /* We don't want to miss these when called from TraceRuntime. */ - if (JSTraceDataOp op = rt->gcGrayRootsTraceOp) + /* During GC, this buffers up the gray roots and doesn't mark them. */ + if (JSTraceDataOp op = rt->gcGrayRootsTraceOp) { + if (IS_GC_MARKING_TRACER(trc)) { + GCMarker *gcmarker = static_cast(trc); + gcmarker->startBufferingGrayRoots(); (*op)(trc, rt->gcGrayRootsData); + gcmarker->endBufferingGrayRoots(); + } else { + (*op)(trc, rt->gcGrayRootsData); + } } } - void TriggerGC(JSRuntime *rt, gcreason::Reason reason) { @@ -2115,12 +2421,12 @@ TriggerCompartmentGC(JSCompartment *comp, gcreason::Reason reason) JSRuntime *rt = comp->rt; JS_ASSERT(!rt->gcRunning); - if (rt->gcZeal()) { + if (rt->gcZeal() == ZealAllocValue) { TriggerGC(rt, reason); return; } - if (rt->gcMode != JSGC_MODE_COMPARTMENT || comp == rt->atomsCompartment) { + if (rt->gcMode == JSGC_MODE_GLOBAL || comp == rt->atomsCompartment) { /* We can't do a compartmental GC of the default compartment. */ TriggerGC(rt, reason); return; @@ -2149,19 +2455,23 @@ MaybeGC(JSContext *cx) JSRuntime *rt = cx->runtime; JS_ASSERT(rt->onOwnerThread()); - if (rt->gcZeal()) { - js_GC(cx, NULL, GC_NORMAL, gcreason::MAYBEGC); + if (rt->gcZeal() == ZealAllocValue || rt->gcZeal() == ZealPokeValue) { + GC(cx, NULL, GC_NORMAL, gcreason::MAYBEGC); return; } JSCompartment *comp = cx->compartment; if (rt->gcIsNeeded) { - js_GC(cx, (comp == rt->gcTriggerCompartment) ? comp : NULL, GC_NORMAL, gcreason::MAYBEGC); + GCSlice(cx, (comp == rt->gcTriggerCompartment) ? comp : NULL, + GC_NORMAL, gcreason::MAYBEGC); return; } - if (comp->gcBytes > 8192 && comp->gcBytes >= 3 * (comp->gcTriggerBytes / 4)) { - js_GC(cx, (rt->gcMode == JSGC_MODE_COMPARTMENT) ? comp : NULL, GC_NORMAL, gcreason::MAYBEGC); + if (comp->gcBytes > 8192 && + comp->gcBytes >= 3 * (comp->gcTriggerBytes / 4) && + rt->gcIncrementalState == NO_INCREMENTAL) + { + GCSlice(cx, NULL, GC_NORMAL, gcreason::MAYBEGC); return; } @@ -2175,7 +2485,7 @@ MaybeGC(JSContext *cx) if (rt->gcChunkAllocationSinceLastGC || rt->gcNumArenasFreeCommitted > FreeCommittedArenasThreshold) { - js_GC(cx, NULL, GC_SHRINK, gcreason::MAYBEGC); + GCSlice(cx, NULL, GC_SHRINK, gcreason::MAYBEGC); } else { rt->gcNextFullGCTime = now + GC_IDLE_FULL_SPAN; } @@ -2622,7 +2932,7 @@ SweepCompartments(JSContext *cx, JSGCInvocationKind gckind) } static void -BeginMarkPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind) +PurgeRuntime(JSContext *cx) { JSRuntime *rt = cx->runtime; @@ -2636,43 +2946,100 @@ BeginMarkPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind) while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter)) acx->purge(); } +} + +static void +BeginMarkPhase(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + GCMarker *gcmarker = &rt->gcMarker; + + rt->gcStartNumber = rt->gcNumber; + + /* Reset weak map list. */ + WeakMapBase::resetWeakMapList(rt); + + /* + * We must purge the runtime at the beginning of an incremental GC. The + * danger if we purge later is that the snapshot invariant of incremental + * GC will be broken, as follows. If some object is reachable only through + * some cache (say the dtoaCache) then it will not be part of the snapshot. + * If we purge after root marking, then the mutator could obtain a pointer + * to the object and start using it. This object might never be marked, so + * a GC hazard would exist. + */ + PurgeRuntime(cx); /* * Mark phase. */ - rt->gcStats.beginPhase(gcstats::PHASE_MARK); + gcstats::AutoPhase ap1(rt->gcStats, gcstats::PHASE_MARK); + gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_MARK_ROOTS); for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront()) r.front()->bitmap.clear(); - if (rt->gcCurrentCompartment) { - for (CompartmentsIter c(rt); !c.done(); c.next()) - c->markCrossCompartmentWrappers(gcmarker); - Debugger::markCrossCompartmentDebuggerObjectReferents(gcmarker); - } - MarkRuntime(gcmarker); } +void +MarkWeakReferences(GCMarker *gcmarker) +{ + JS_ASSERT(gcmarker->isDrained()); + while (WatchpointMap::markAllIteratively(gcmarker) || + WeakMapBase::markAllIteratively(gcmarker) || + Debugger::markAllIteratively(gcmarker)) + { + SliceBudget budget; + gcmarker->drainMarkStack(budget); + } + JS_ASSERT(gcmarker->isDrained()); +} + static void -EndMarkPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind) +MarkGrayAndWeak(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + FullGCMarker *gcmarker = &rt->gcMarker; + + JS_ASSERT(gcmarker->isDrained()); + MarkWeakReferences(gcmarker); + + gcmarker->setMarkColorGray(); + if (gcmarker->hasBufferedGrayRoots()) { + gcmarker->markBufferedGrayRoots(); + } else { + if (JSTraceDataOp op = rt->gcGrayRootsTraceOp) + (*op)(gcmarker, rt->gcGrayRootsData); + } + SliceBudget budget; + gcmarker->drainMarkStack(budget); + MarkWeakReferences(gcmarker); + JS_ASSERT(gcmarker->isDrained()); +} + +#ifdef DEBUG +static void +ValidateIncrementalMarking(JSContext *cx); +#endif + +static void +EndMarkPhase(JSContext *cx) { JSRuntime *rt = cx->runtime; - JS_ASSERT(gcmarker->isMarkStackEmpty()); - MarkWeakReferences(gcmarker); - - if (JSTraceDataOp op = rt->gcGrayRootsTraceOp) { - gcmarker->setMarkColorGray(); - (*op)(gcmarker, rt->gcGrayRootsData); - gcmarker->drainMarkStack(); - MarkWeakReferences(gcmarker); + { + gcstats::AutoPhase ap1(rt->gcStats, gcstats::PHASE_MARK); + gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_MARK_OTHER); + MarkGrayAndWeak(cx); } - JS_ASSERT(gcmarker->isMarkStackEmpty()); - rt->gcIncrementalTracer = NULL; + JS_ASSERT(rt->gcMarker.isDrained()); - rt->gcStats.endPhase(gcstats::PHASE_MARK); +#ifdef DEBUG + if (rt->gcIncrementalState != NO_INCREMENTAL) + ValidateIncrementalMarking(cx); +#endif if (rt->gcCallback) (void) rt->gcCallback(cx, JSGC_MARK_END); @@ -2688,10 +3055,97 @@ EndMarkPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind) #endif } +#ifdef DEBUG static void -SweepPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind) +ValidateIncrementalMarking(JSContext *cx) { JSRuntime *rt = cx->runtime; + FullGCMarker *gcmarker = &rt->gcMarker; + + js::gc::State state = rt->gcIncrementalState; + rt->gcIncrementalState = NO_INCREMENTAL; + + /* As we're re-doing marking, we need to reset the weak map list. */ + WeakMapBase::resetWeakMapList(rt); + + JS_ASSERT(gcmarker->isDrained()); + gcmarker->reset(); + + typedef HashMap BitmapMap; + BitmapMap map(cx); + map.init(); + + for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront()) { + ChunkBitmap *bitmap = &r.front()->bitmap; + uintptr_t *entry = (uintptr_t *)js_malloc(sizeof(bitmap->bitmap)); + if (entry) + memcpy(entry, bitmap->bitmap, sizeof(bitmap->bitmap)); + map.putNew(r.front(), entry); + } + + for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront()) + r.front()->bitmap.clear(); + + MarkRuntime(gcmarker, true); + SliceBudget budget; + rt->gcMarker.drainMarkStack(budget); + MarkGrayAndWeak(cx); + + for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront()) { + Chunk *chunk = r.front(); + ChunkBitmap *bitmap = &chunk->bitmap; + uintptr_t *entry = map.lookup(r.front())->value; + ChunkBitmap incBitmap; + + if (!entry) + continue; + + memcpy(incBitmap.bitmap, entry, sizeof(incBitmap.bitmap)); + js_free(entry); + + for (size_t i = 0; i < ArenasPerChunk; i++) { + Arena *arena = &chunk->arenas[i]; + if (!arena->aheader.allocated()) + continue; + if (rt->gcCurrentCompartment && arena->aheader.compartment != rt->gcCurrentCompartment) + continue; + if (arena->aheader.allocatedDuringIncremental) + continue; + + AllocKind kind = arena->aheader.getAllocKind(); + uintptr_t thing = arena->thingsStart(kind); + uintptr_t end = arena->thingsEnd(); + while (thing < end) { + Cell *cell = (Cell *)thing; + if (bitmap->isMarked(cell, BLACK) && !incBitmap.isMarked(cell, BLACK)) { + JS_DumpHeap(cx, stdout, NULL, JSGCTraceKind(0), NULL, 100000, NULL); + printf("Assertion cell: %p (%d)\n", (void *)cell, cell->getAllocKind()); + } + JS_ASSERT_IF(bitmap->isMarked(cell, BLACK), incBitmap.isMarked(cell, BLACK)); + thing += Arena::thingSize(kind); + } + } + + memcpy(bitmap->bitmap, incBitmap.bitmap, sizeof(incBitmap.bitmap)); + } + + rt->gcIncrementalState = state; +} +#endif + +static void +SweepPhase(JSContext *cx, JSGCInvocationKind gckind) +{ + JSRuntime *rt = cx->runtime; + +#ifdef JS_THREADSAFE + if (rt->hasContexts() && rt->gcHelperThread.prepareForBackgroundSweep()) + cx->gcBackgroundFree = &rt->gcHelperThread; +#endif + + /* Purge the ArenaLists before sweeping. */ + for (GCCompartmentsIter c(rt); !c.done(); c.next()) + c->arenas.purge(); /* * Sweep phase. @@ -2710,7 +3164,7 @@ SweepPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind) gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP); /* Finalize unreachable (key,value) pairs in all weak maps. */ - WeakMapBase::sweepAll(gcmarker); + WeakMapBase::sweepAll(&rt->gcMarker); js_SweepAtomState(rt); @@ -2791,6 +3245,9 @@ SweepPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind) if (rt->gcCallback) (void) rt->gcCallback(cx, JSGC_FINALIZE_END); } + + for (CompartmentsIter c(rt); !c.done(); c.next()) + c->setGCLastBytes(c->gcBytes, gckind); } /* Perform mark-and-sweep GC. If comp is set, we perform a single-compartment GC. */ @@ -2798,55 +3255,57 @@ static void MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind) { JSRuntime *rt = cx->runtime; - rt->gcNumber++; - - /* Clear gcIsNeeded now, when we are about to start a normal GC cycle. */ - rt->gcIsNeeded = false; - rt->gcTriggerCompartment = NULL; - - /* Clear gcMallocBytes for all compartments */ - JSCompartment **read = rt->compartments.begin(); - JSCompartment **end = rt->compartments.end(); - JS_ASSERT(rt->compartments.length() >= 1); - - while (read < end) { - JSCompartment *compartment = *read++; - compartment->resetGCMallocBytes(); - } - - /* Reset weak map list. */ - WeakMapBase::resetWeakMapList(rt); - - /* Reset malloc counter. */ - rt->resetGCMallocBytes(); AutoUnlockGC unlock(rt); - GCMarker gcmarker(cx); - JS_ASSERT(IS_GC_MARKING_TRACER(&gcmarker)); - JS_ASSERT(gcmarker.getMarkColor() == BLACK); - rt->gcIncrementalTracer = &gcmarker; + rt->gcMarker.start(rt, cx); + JS_ASSERT(!rt->gcMarker.callback); - BeginMarkPhase(cx, &gcmarker, gckind); - gcmarker.drainMarkStack(); - EndMarkPhase(cx, &gcmarker, gckind); - SweepPhase(cx, &gcmarker, gckind); + BeginMarkPhase(cx); + { + gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_MARK); + SliceBudget budget; + rt->gcMarker.drainMarkStack(budget); + } + EndMarkPhase(cx); + SweepPhase(cx, gckind); + + rt->gcMarker.stop(); } -class AutoGCSession { +/* + * This class should be used by any code that needs to exclusive access to the + * heap in order to trace through it... + */ +class AutoHeapSession { public: - explicit AutoGCSession(JSContext *cx); + explicit AutoHeapSession(JSContext *cx); + ~AutoHeapSession(); + + protected: + JSContext *context; + + private: + AutoHeapSession(const AutoHeapSession&) MOZ_DELETE; + void operator=(const AutoHeapSession&) MOZ_DELETE; +}; + +/* ...while this class is to be used only for garbage collection. */ +class AutoGCSession : AutoHeapSession { + public: + explicit AutoGCSession(JSContext *cx, JSCompartment *comp); ~AutoGCSession(); private: - JSContext *context; - - AutoGCSession(const AutoGCSession&) MOZ_DELETE; - void operator=(const AutoGCSession&) MOZ_DELETE; + /* + * We should not be depending on cx->compartment in the GC, so set it to + * NULL to look for violations. + */ + SwitchToCompartment switcher; }; -/* Start a new GC session. */ -AutoGCSession::AutoGCSession(JSContext *cx) +/* Start a new heap session. */ +AutoHeapSession::AutoHeapSession(JSContext *cx) : context(cx) { JS_ASSERT(!cx->runtime->noGCOrAllocationCheck); @@ -2855,144 +3314,111 @@ AutoGCSession::AutoGCSession(JSContext *cx) rt->gcRunning = true; } -AutoGCSession::~AutoGCSession() +AutoHeapSession::~AutoHeapSession() { JSRuntime *rt = context->runtime; rt->gcRunning = false; } -/* - * GC, repeatedly if necessary, until we think we have not created any new - * garbage. We disable inlining to ensure that the bottom of the stack with - * possible GC roots recorded in js_GC excludes any pointers we use during the - * marking implementation. - */ -static JS_NEVER_INLINE void -GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) +AutoGCSession::AutoGCSession(JSContext *cx, JSCompartment *comp) + : AutoHeapSession(cx), + switcher(cx, (JSCompartment *)NULL) { JSRuntime *rt = cx->runtime; - JS_ASSERT_IF(comp, comp != rt->atomsCompartment); - JS_ASSERT_IF(comp, rt->gcMode == JSGC_MODE_COMPARTMENT); - - /* Recursive GC is no-op. */ - if (rt->gcMarkAndSweep) - return; - - AutoGCSession gcsession(cx); - - /* Don't GC if we are reporting an OOM. */ - if (rt->inOOMReport) - return; - - /* - * We should not be depending on cx->compartment in the GC, so set it to - * NULL to look for violations. - */ - SwitchToCompartment sc(cx, (JSCompartment *)NULL); - JS_ASSERT(!rt->gcCurrentCompartment); rt->gcCurrentCompartment = comp; - rt->gcMarkAndSweep = true; + rt->gcIsNeeded = false; + rt->gcTriggerCompartment = NULL; + rt->gcInterFrameGC = true; -#ifdef JS_THREADSAFE - /* - * As we about to purge caches and clear the mark bits we must wait for - * any background finalization to finish. We must also wait for the - * background allocation to finish so we can avoid taking the GC lock - * when manipulating the chunks during the GC. - */ - JS_ASSERT(!cx->gcBackgroundFree); - rt->gcHelperThread.waitBackgroundSweepOrAllocEnd(); - if (rt->hasContexts() && rt->gcHelperThread.prepareForBackgroundSweep()) - cx->gcBackgroundFree = &rt->gcHelperThread; -#endif + rt->gcNumber++; - MarkAndSweep(cx, gckind); - -#ifdef JS_THREADSAFE - if (cx->gcBackgroundFree) { - JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread); - cx->gcBackgroundFree = NULL; - rt->gcHelperThread.startBackgroundSweep(cx, gckind == GC_SHRINK); - } -#endif - - rt->gcMarkAndSweep = false; - rt->gcCurrentCompartment = NULL; + rt->resetGCMallocBytes(); + /* Clear gcMallocBytes for all compartments */ for (CompartmentsIter c(rt); !c.done(); c.next()) - c->setGCLastBytes(c->gcBytes, gckind); + c->resetGCMallocBytes(); } -void -js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, gcreason::Reason reason) +AutoGCSession::~AutoGCSession() { - JSRuntime *rt = cx->runtime; - JS_AbortIfWrongThread(rt); - -#ifdef JS_GC_ZEAL - struct AutoVerifyBarriers { - JSContext *cx; - bool inVerify; - AutoVerifyBarriers(JSContext *cx) : cx(cx), inVerify(cx->runtime->gcVerifyData) { - if (inVerify) EndVerifyBarriers(cx); - } - ~AutoVerifyBarriers() { if (inVerify) StartVerifyBarriers(cx); } - } av(cx); -#endif - - RecordNativeStackTopForGC(cx); - - gcstats::AutoGC agc(rt->gcStats, comp, reason); - - do { - /* - * Let the API user decide to defer a GC if it wants to (unless this - * is the last context). Invoke the callback regardless. Sample the - * callback in case we are freely racing with a JS_SetGCCallback{,RT} - * on another thread. - */ - if (JSGCCallback callback = rt->gcCallback) { - if (!callback(cx, JSGC_BEGIN) && rt->hasContexts()) - return; - } - - { - /* Lock out other GC allocator and collector invocations. */ - AutoLockGC lock(rt); - rt->gcPoke = false; - GCCycle(cx, comp, gckind); - } - - /* We re-sample the callback again as the finalizers can change it. */ - if (JSGCCallback callback = rt->gcCallback) - (void) callback(cx, JSGC_END); - - /* - * On shutdown, iterate until finalizers or the JSGC_END callback - * stop creating garbage. - */ - } while (!rt->hasContexts() && rt->gcPoke); + JSRuntime *rt = context->runtime; + rt->gcCurrentCompartment = NULL; rt->gcNextFullGCTime = PRMJ_Now() + GC_IDLE_FULL_SPAN; - rt->gcChunkAllocationSinceLastGC = false; } -namespace js { - -void -ShrinkGCBuffers(JSRuntime *rt) +static void +ResetIncrementalGC(JSRuntime *rt) { - AutoLockGC lock(rt); - JS_ASSERT(!rt->gcRunning); -#ifndef JS_THREADSAFE - ExpireChunksAndArenas(rt, true); -#else - rt->gcHelperThread.startBackgroundShrink(); -#endif + if (rt->gcIncrementalState == NO_INCREMENTAL) + return; + + for (CompartmentsIter c(rt); !c.done(); c.next()) { + if (!rt->gcIncrementalCompartment || rt->gcIncrementalCompartment == c) { + c->needsBarrier_ = false; + c->barrierMarker_.reset(); + c->barrierMarker_.stop(); + } + JS_ASSERT(!c->needsBarrier_); + } + + rt->gcIncrementalCompartment = NULL; + rt->gcMarker.reset(); + rt->gcMarker.stop(); + rt->gcIncrementalState = NO_INCREMENTAL; + + rt->gcStats.reset(); +} + +class AutoGCSlice { + public: + AutoGCSlice(JSContext *cx); + ~AutoGCSlice(); + + private: + JSContext *context; +}; + +AutoGCSlice::AutoGCSlice(JSContext *cx) + : context(cx) +{ + JSRuntime *rt = context->runtime; + + /* + * During incremental GC, the compartment's active flag determines whether + * there are stack frames active for any of its scripts. Normally this flag + * is set at the beginning of the mark phase. During incremental GC, we also + * set it at the start of every phase. + */ + rt->stackSpace.markActiveCompartments(); + + for (GCCompartmentsIter c(rt); !c.done(); c.next()) { + /* Clear this early so we don't do any write barriers during GC. */ + if (rt->gcIncrementalState == MARK) + c->needsBarrier_ = false; + else + JS_ASSERT(!c->needsBarrier_); + } +} + +AutoGCSlice::~AutoGCSlice() +{ + JSRuntime *rt = context->runtime; + + for (GCCompartmentsIter c(rt); !c.done(); c.next()) { + if (rt->gcIncrementalState == MARK) { + c->needsBarrier_ = true; + c->arenas.prepareForIncrementalGC(c); + } else { + JS_ASSERT(rt->gcIncrementalState == NO_INCREMENTAL); + + c->needsBarrier_ = false; + } + } } class AutoCopyFreeListToArenas { @@ -3011,6 +3437,296 @@ class AutoCopyFreeListToArenas { } }; +static void +IncrementalGCSlice(JSContext *cx, int64_t budget, JSGCInvocationKind gckind) +{ + JS_ASSERT(budget != SliceBudget::Unlimited); + + JSRuntime *rt = cx->runtime; + + AutoUnlockGC unlock(rt); + AutoGCSlice slice(cx); + + gc::State initialState = rt->gcIncrementalState; + + if (rt->gcIncrementalState == NO_INCREMENTAL) { + JS_ASSERT(!rt->gcIncrementalCompartment); + rt->gcIncrementalCompartment = rt->gcCurrentCompartment; + rt->gcIncrementalState = MARK_ROOTS; + rt->gcLastMarkSlice = false; + } + + if (rt->gcIncrementalState == MARK_ROOTS) { + rt->gcMarker.start(rt, cx); + JS_ASSERT(IS_GC_MARKING_TRACER(&rt->gcMarker)); + + for (GCCompartmentsIter c(rt); !c.done(); c.next()) { + c->discardJitCode(cx); + c->barrierMarker_.start(rt, NULL); + } + + BeginMarkPhase(cx); + + rt->gcIncrementalState = MARK; + } + + if (rt->gcIncrementalState == MARK) { + gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_MARK); + SliceBudget sliceBudget(budget); + + /* If we needed delayed marking for gray roots, then collect until done. */ + if (!rt->gcMarker.hasBufferedGrayRoots()) + sliceBudget.reset(); + + bool finished = rt->gcMarker.drainMarkStack(sliceBudget); + + for (GCCompartmentsIter c(rt); !c.done(); c.next()) { + c->barrierMarker_.context = cx; + finished &= c->barrierMarker_.drainMarkStack(sliceBudget); + c->barrierMarker_.context = NULL; + } + + if (finished) { + JS_ASSERT(rt->gcMarker.isDrained()); +#ifdef DEBUG + for (GCCompartmentsIter c(rt); !c.done(); c.next()) + JS_ASSERT(c->barrierMarker_.isDrained()); +#endif + if (initialState == MARK && !rt->gcLastMarkSlice) + rt->gcLastMarkSlice = true; + else + rt->gcIncrementalState = SWEEP; + } + } + + if (rt->gcIncrementalState == SWEEP) { + EndMarkPhase(cx); + SweepPhase(cx, gckind); + + rt->gcMarker.stop(); + + /* JIT code was already discarded during sweeping. */ + for (GCCompartmentsIter c(rt); !c.done(); c.next()) + c->barrierMarker_.stop(); + + rt->gcIncrementalCompartment = NULL; + + rt->gcIncrementalState = NO_INCREMENTAL; + } +} + +static bool +IsIncrementalGCSafe(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + + if (rt->gcCompartmentCreated) { + rt->gcCompartmentCreated = false; + return false; + } + + if (rt->gcKeepAtoms) + return false; + + for (GCCompartmentsIter c(rt); !c.done(); c.next()) { + if (c->activeAnalysis) + return false; + } + + if (rt->gcIncrementalState != NO_INCREMENTAL && + rt->gcCurrentCompartment != rt->gcIncrementalCompartment) + { + return false; + } + + if (!rt->gcIncrementalEnabled) + return false; + + return true; +} + +static bool +IsIncrementalGCAllowed(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + + if (rt->gcMode != JSGC_MODE_INCREMENTAL) + return false; + +#ifdef ANDROID + /* Incremental GC is disabled on Android for now. */ + return false; +#endif + + if (!IsIncrementalGCSafe(cx)) + return false; + + for (CompartmentsIter c(rt); !c.done(); c.next()) { + if (c->gcBytes > c->gcTriggerBytes) + return false; + } + + return true; +} + +/* + * GC, repeatedly if necessary, until we think we have not created any new + * garbage. We disable inlining to ensure that the bottom of the stack with + * possible GC roots recorded in js_GC excludes any pointers we use during the + * marking implementation. + */ +static JS_NEVER_INLINE void +GCCycle(JSContext *cx, JSCompartment *comp, int64_t budget, JSGCInvocationKind gckind) +{ + JSRuntime *rt = cx->runtime; + + JS_ASSERT_IF(comp, comp != rt->atomsCompartment); + JS_ASSERT_IF(comp, rt->gcMode != JSGC_MODE_GLOBAL); + + /* Recursive GC is no-op. */ + if (rt->gcRunning) + return; + + AutoGCSession gcsession(cx, comp); + + /* Don't GC if we are reporting an OOM. */ + if (rt->inOOMReport) + return; + +#ifdef JS_THREADSAFE + /* + * As we about to purge caches and clear the mark bits we must wait for + * any background finalization to finish. We must also wait for the + * background allocation to finish so we can avoid taking the GC lock + * when manipulating the chunks during the GC. + */ + JS_ASSERT(!cx->gcBackgroundFree); + rt->gcHelperThread.waitBackgroundSweepOrAllocEnd(); +#endif + + if (budget != SliceBudget::Unlimited) { + if (!IsIncrementalGCAllowed(cx)) + budget = SliceBudget::Unlimited; + } + + if (budget == SliceBudget::Unlimited) + ResetIncrementalGC(rt); + + AutoCopyFreeListToArenas copy(rt); + + if (budget == SliceBudget::Unlimited) + MarkAndSweep(cx, gckind); + else + IncrementalGCSlice(cx, budget, gckind); + +#ifdef DEBUG + if (rt->gcIncrementalState == NO_INCREMENTAL) { + for (CompartmentsIter c(rt); !c.done(); c.next()) + JS_ASSERT(!c->needsBarrier_); + } +#endif +#ifdef JS_THREADSAFE + if (rt->gcIncrementalState == NO_INCREMENTAL) { + if (cx->gcBackgroundFree) { + JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread); + cx->gcBackgroundFree = NULL; + rt->gcHelperThread.startBackgroundSweep(cx, gckind == GC_SHRINK); + } + } +#endif +} + +static void +Collect(JSContext *cx, JSCompartment *comp, int64_t budget, + JSGCInvocationKind gckind, gcreason::Reason reason) +{ + JSRuntime *rt = cx->runtime; + JS_AbortIfWrongThread(rt); + + JS_ASSERT_IF(budget != SliceBudget::Unlimited, JSGC_INCREMENTAL); + +#ifdef JS_GC_ZEAL + struct AutoVerifyBarriers { + JSContext *cx; + bool inVerify; + AutoVerifyBarriers(JSContext *cx) : cx(cx), inVerify(cx->runtime->gcVerifyData) { + if (inVerify) EndVerifyBarriers(cx); + } + ~AutoVerifyBarriers() { if (inVerify) StartVerifyBarriers(cx); } + } av(cx); +#endif + + RecordNativeStackTopForGC(cx); + + /* This is a heuristic to avoid resets. */ + if (rt->gcIncrementalState != NO_INCREMENTAL && !rt->gcIncrementalCompartment) + comp = NULL; + + gcstats::AutoGCSlice agc(rt->gcStats, comp, reason); + + do { + /* + * Let the API user decide to defer a GC if it wants to (unless this + * is the last context). Invoke the callback regardless. + */ + if (rt->gcIncrementalState == NO_INCREMENTAL) { + if (JSGCCallback callback = rt->gcCallback) { + if (!callback(cx, JSGC_BEGIN) && rt->hasContexts()) + return; + } + } + + { + /* Lock out other GC allocator and collector invocations. */ + AutoLockGC lock(rt); + rt->gcPoke = false; + GCCycle(cx, comp, budget, gckind); + } + + if (rt->gcIncrementalState == NO_INCREMENTAL) { + if (JSGCCallback callback = rt->gcCallback) + (void) callback(cx, JSGC_END); + } + + /* + * On shutdown, iterate until finalizers or the JSGC_END callback + * stop creating garbage. + */ + } while (!rt->hasContexts() && rt->gcPoke); +} + +namespace js { + +void +GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, gcreason::Reason reason) +{ + Collect(cx, comp, SliceBudget::Unlimited, gckind, reason); +} + +void +GCSlice(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, gcreason::Reason reason) +{ + Collect(cx, comp, cx->runtime->gcSliceBudget, gckind, reason); +} + +void +GCDebugSlice(JSContext *cx, int64_t objCount) +{ + Collect(cx, NULL, SliceBudget::WorkBudget(objCount), GC_NORMAL, gcreason::API); +} + +void +ShrinkGCBuffers(JSRuntime *rt) +{ + AutoLockGC lock(rt); + JS_ASSERT(!rt->gcRunning); +#ifndef JS_THREADSAFE + ExpireChunksAndArenas(rt, true); +#else + rt->gcHelperThread.startBackgroundShrink(); +#endif +} + void TraceRuntime(JSTracer *trc) { @@ -3022,7 +3738,7 @@ TraceRuntime(JSTracer *trc) JSRuntime *rt = cx->runtime; if (!rt->gcRunning) { AutoLockGC lock(rt); - AutoGCSession gcsession(cx); + AutoHeapSession session(cx); rt->gcHelperThread.waitBackgroundSweepEnd(); AutoUnlockGC unlock(rt); @@ -3083,7 +3799,7 @@ IterateCompartments(JSContext *cx, void *data, JS_ASSERT(!rt->gcRunning); AutoLockGC lock(rt); - AutoGCSession gcsession(cx); + AutoHeapSession session(cx); #ifdef JS_THREADSAFE rt->gcHelperThread.waitBackgroundSweepEnd(); #endif @@ -3107,7 +3823,7 @@ IterateCompartmentsArenasCells(JSContext *cx, void *data, JS_ASSERT(!rt->gcRunning); AutoLockGC lock(rt); - AutoGCSession gcsession(cx); + AutoHeapSession session(cx); #ifdef JS_THREADSAFE rt->gcHelperThread.waitBackgroundSweepEnd(); #endif @@ -3137,7 +3853,7 @@ IterateChunks(JSContext *cx, void *data, IterateChunkCallback chunkCallback) JS_ASSERT(!rt->gcRunning); AutoLockGC lock(rt); - AutoGCSession gcsession(cx); + AutoHeapSession session(cx); #ifdef JS_THREADSAFE rt->gcHelperThread.waitBackgroundSweepEnd(); #endif @@ -3158,7 +3874,7 @@ IterateCells(JSContext *cx, JSCompartment *compartment, AllocKind thingKind, JS_ASSERT(!rt->gcRunning); AutoLockGC lock(rt); - AutoGCSession gcsession(cx); + AutoHeapSession session(cx); #ifdef JS_THREADSAFE rt->gcHelperThread.waitBackgroundSweepEnd(); #endif @@ -3206,6 +3922,23 @@ NewCompartment(JSContext *cx, JSPrincipals *principals) */ { AutoLockGC lock(rt); + + /* + * If we're in the middle of an incremental GC, we cancel + * it. Otherwise we might fail the mark the newly created + * compartment fully. + */ + if (rt->gcIncrementalState == MARK) { + rt->gcCompartmentCreated = true; + + /* + * Start the tracer so that it's legal to stop() it when + * resetting the GC. + */ + if (!rt->gcIncrementalCompartment) + compartment->barrierMarker_.start(rt, NULL); + } + if (rt->compartments.append(compartment)) return compartment; } @@ -3249,7 +3982,7 @@ CheckStackRoot(JSTracer *trc, uintptr_t *w) if (test == CGCT_VALID) { JSContext *iter = NULL; bool matched = false; - JSRuntime *rt = trc->context->runtime; + JSRuntime *rt = trc->runtime; while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter)) { for (unsigned i = 0; i < THING_ROOT_COUNT; i++) { Root *rooter = acx->thingGCRooters[i]; @@ -3292,7 +4025,7 @@ CheckStackRoots(JSContext *cx) AutoCopyFreeListToArenas copy(cx->runtime); JSTracer checker; - JS_TRACER_INIT(&checker, cx, EmptyMarkCallback); + JS_TracerInit(&checker, cx, EmptyMarkCallback); ThreadData *td = JS_THREAD_DATA(cx); @@ -3370,7 +4103,7 @@ typedef HashMap NodeMap; */ struct VerifyTracer : JSTracer { /* The gcNumber when the verification began. */ - uint32_t number; + uint64_t number; /* This counts up to JS_VERIFIER_FREQ to decide whether to verify. */ uint32_t count; @@ -3382,10 +4115,8 @@ struct VerifyTracer : JSTracer { char *term; NodeMap nodemap; - /* A dummy marker used for the write barriers; stored in gcMarkingTracer. */ - GCMarker gcmarker; - - VerifyTracer(JSContext *cx) : nodemap(cx), gcmarker(cx) {} + VerifyTracer(JSContext *cx) : root(NULL), nodemap(cx) {} + ~VerifyTracer() { js_free(root); } }; /* @@ -3449,11 +4180,14 @@ StartVerifyBarriers(JSContext *cx) { JSRuntime *rt = cx->runtime; - if (rt->gcVerifyData) + if (rt->gcVerifyData || rt->gcIncrementalState != NO_INCREMENTAL) return; AutoLockGC lock(rt); - AutoGCSession gcsession(cx); + AutoHeapSession session(cx); + + if (!IsIncrementalGCSafe(cx)) + return; #ifdef JS_THREADSAFE rt->gcHelperThread.waitBackgroundSweepOrAllocEnd(); @@ -3467,27 +4201,10 @@ StartVerifyBarriers(JSContext *cx) for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront()) r.front()->bitmap.clear(); - /* - * Kick all frames on the stack into the interpreter, and release all JIT - * code in the compartment. - */ -#ifdef JS_METHODJIT - for (CompartmentsIter c(rt); !c.done(); c.next()) { - mjit::ClearAllFrames(c); + for (CompartmentsIter c(rt); !c.done(); c.next()) + c->discardJitCode(cx); - for (CellIterUnderGC i(c, FINALIZE_SCRIPT); !i.done(); i.next()) { - JSScript *script = i.get(); - mjit::ReleaseScriptCode(cx, script); - - /* - * Use counts for scripts are reset on GC. After discarding code we - * need to let it warm back up to get information like which opcodes - * are setting array holes or accessing getter properties. - */ - script->resetUseCount(); - } - } -#endif + PurgeRuntime(cx); VerifyTracer *trc = new (js_malloc(sizeof(VerifyTracer))) VerifyTracer(cx); @@ -3508,6 +4225,9 @@ StartVerifyBarriers(JSContext *cx) /* Create the root node. */ trc->curnode = MakeNode(trc, NULL, JSGCTraceKind(0)); + /* We want MarkRuntime to save the roots to gcSavedRoots. */ + rt->gcIncrementalState = MARK_ROOTS; + /* Make all the roots be edges emanating from the root node. */ MarkRuntime(trc); @@ -3532,26 +4252,35 @@ StartVerifyBarriers(JSContext *cx) } rt->gcVerifyData = trc; - rt->gcIncrementalTracer = &trc->gcmarker; + rt->gcIncrementalState = MARK; for (CompartmentsIter c(rt); !c.done(); c.next()) { - c->gcIncrementalTracer = &trc->gcmarker; c->needsBarrier_ = true; + c->barrierMarker_.start(rt, NULL); + c->arenas.prepareForIncrementalGC(c); } return; oom: - js_free(trc->root); + rt->gcIncrementalState = NO_INCREMENTAL; trc->~VerifyTracer(); js_free(trc); } static void -CheckAutorooter(JSTracer *jstrc, void **thingp, JSGCTraceKind kind) +MarkFromAutorooter(JSTracer *jstrc, void **thingp, JSGCTraceKind kind) { static_cast(*thingp)->markIfUnmarked(); } +static bool +IsMarkedOrAllocated(Cell *cell) +{ + return cell->isMarked() || cell->arenaHeader()->allocatedDuringIncremental; +} + +const static uint32_t MAX_VERIFIER_EDGES = 1000; + /* * This function is called by EndVerifyBarriers for every heap edge. If the edge * already existed in the original snapshot, we "cancel it out" by overwriting @@ -3565,6 +4294,10 @@ CheckEdge(JSTracer *jstrc, void **thingp, JSGCTraceKind kind) VerifyTracer *trc = (VerifyTracer *)jstrc; VerifyNode *node = trc->curnode; + /* Avoid n^2 behavior. */ + if (node->count > MAX_VERIFIER_EDGES) + return; + for (uint32_t i = 0; i < node->count; i++) { if (node->edges[i].thing == *thingp) { JS_ASSERT(node->edges[i].kind == kind); @@ -3572,6 +4305,21 @@ CheckEdge(JSTracer *jstrc, void **thingp, JSGCTraceKind kind) return; } } + + /* + * Anything that is reachable now should have been reachable before, or else + * it should be marked. + */ + NodeMap::Ptr p = trc->nodemap.lookup(*thingp); + JS_ASSERT_IF(!p, IsMarkedOrAllocated(static_cast(*thingp))); +} + +static void +CheckReachable(JSTracer *jstrc, void **thingp, JSGCTraceKind kind) +{ + VerifyTracer *trc = (VerifyTracer *)jstrc; + NodeMap::Ptr p = trc->nodemap.lookup(*thingp); + JS_ASSERT_IF(!p, IsMarkedOrAllocated(static_cast(*thingp))); } static void @@ -3580,7 +4328,7 @@ EndVerifyBarriers(JSContext *cx) JSRuntime *rt = cx->runtime; AutoLockGC lock(rt); - AutoGCSession gcsession(cx); + AutoHeapSession session(cx); #ifdef JS_THREADSAFE rt->gcHelperThread.waitBackgroundSweepOrAllocEnd(); @@ -3598,18 +4346,17 @@ EndVerifyBarriers(JSContext *cx) JS_ASSERT(trc->number == rt->gcNumber); - for (CompartmentsIter c(rt); !c.done(); c.next()) { - c->gcIncrementalTracer = NULL; + /* We need to disable barriers before tracing, which may invoke barriers. */ + for (CompartmentsIter c(rt); !c.done(); c.next()) c->needsBarrier_ = false; - } - if (rt->gcIncrementalTracer->hasDelayedChildren()) - rt->gcIncrementalTracer->markDelayedChildren(); + for (CompartmentsIter c(rt); !c.done(); c.next()) + c->discardJitCode(cx); rt->gcVerifyData = NULL; - rt->gcIncrementalTracer = NULL; + rt->gcIncrementalState = NO_INCREMENTAL; - JS_TracerInit(trc, cx, CheckAutorooter); + JS_TracerInit(trc, cx, MarkFromAutorooter); JSContext *iter = NULL; while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter)) { @@ -3617,34 +4364,65 @@ EndVerifyBarriers(JSContext *cx) acx->autoGCRooters->traceAll(trc); } - JS_TracerInit(trc, cx, CheckEdge); + if (IsIncrementalGCSafe(cx)) { + /* + * Verify that all the current roots were reachable previously, or else + * are marked. + */ + JS_TracerInit(trc, cx, CheckReachable); + MarkRuntime(trc, true); - /* Start after the roots. */ - VerifyNode *node = NextNode(trc->root); - int count = 0; + JS_TracerInit(trc, cx, CheckEdge); - while ((char *)node < trc->edgeptr) { - trc->curnode = node; - JS_TraceChildren(trc, node->thing, node->kind); + /* Start after the roots. */ + VerifyNode *node = NextNode(trc->root); + while ((char *)node < trc->edgeptr) { + trc->curnode = node; + JS_TraceChildren(trc, node->thing, node->kind); - for (uint32_t i = 0; i < node->count; i++) { - void *thing = node->edges[i].thing; - JS_ASSERT_IF(thing, static_cast(thing)->isMarked()); + if (node->count <= MAX_VERIFIER_EDGES) { + for (uint32_t i = 0; i < node->count; i++) { + void *thing = node->edges[i].thing; + JS_ASSERT_IF(thing, IsMarkedOrAllocated(static_cast(thing))); + } + } + + node = NextNode(node); } - - count++; - node = NextNode(node); } - js_free(trc->root); + for (CompartmentsIter c(rt); !c.done(); c.next()) { + c->barrierMarker_.reset(); + c->barrierMarker_.stop(); + } + trc->~VerifyTracer(); js_free(trc); } void -VerifyBarriers(JSContext *cx, bool always) +FinishVerifier(JSRuntime *rt) { - if (cx->runtime->gcZeal() < ZealVerifierThreshold) + if (VerifyTracer *trc = (VerifyTracer *)rt->gcVerifyData) { + trc->~VerifyTracer(); + js_free(trc); + } +} + +void +VerifyBarriers(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + if (rt->gcVerifyData) + EndVerifyBarriers(cx); + else + StartVerifyBarriers(cx); +} + +void +MaybeVerifyBarriers(JSContext *cx, bool always) +{ + if (cx->runtime->gcZeal() != ZealVerifierValue) return; uint32_t freq = cx->runtime->gcZealFrequency; @@ -3792,3 +4570,4 @@ js_NewGCXML(JSContext *cx) return NewGCThing(cx, js::gc::FINALIZE_XML, sizeof(JSXML)); } #endif + diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 1b38a6ef640..d9e2023a827 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -55,7 +55,6 @@ #include "jslock.h" #include "jsutil.h" #include "jsversion.h" -#include "jsgcstats.h" #include "jscell.h" #include "ds/BitArray.h" @@ -82,6 +81,14 @@ struct Shape; namespace gc { +enum State { + NO_INCREMENTAL, + MARK_ROOTS, + MARK, + SWEEP, + INVALID +}; + struct Arena; /* @@ -419,6 +426,10 @@ struct ArenaHeader { * not present in the stack we use an extra flag to tag arenas on the * stack. * + * Delayed marking is also used for arenas that we allocate into during an + * incremental GC. In this case, we intend to mark all the objects in the + * arena, and it's faster to do this marking in bulk. + * * To minimize the ArenaHeader size we record the next delayed marking * linkage as arenaAddress() >> ArenaShift and pack it with the allocKind * field and hasDelayedMarking flag. We use 8 bits for the allocKind, not @@ -427,7 +438,9 @@ struct ArenaHeader { */ public: size_t hasDelayedMarking : 1; - size_t nextDelayedMarking : JS_BITS_PER_WORD - 8 - 1; + size_t allocatedDuringIncremental : 1; + size_t markOverflow : 1; + size_t nextDelayedMarking : JS_BITS_PER_WORD - 8 - 1 - 1 - 1; static void staticAsserts() { /* We must be able to fit the allockind into uint8_t. */ @@ -437,7 +450,7 @@ struct ArenaHeader { * nextDelayedMarkingpacking assumes that ArenaShift has enough bits * to cover allocKind and hasDelayedMarking. */ - JS_STATIC_ASSERT(ArenaShift >= 8 + 1); + JS_STATIC_ASSERT(ArenaShift >= 8 + 1 + 1 + 1); } inline uintptr_t address() const; @@ -450,6 +463,8 @@ struct ArenaHeader { void init(JSCompartment *comp, AllocKind kind) { JS_ASSERT(!allocated()); + JS_ASSERT(!markOverflow); + JS_ASSERT(!allocatedDuringIncremental); JS_ASSERT(!hasDelayedMarking); compartment = comp; @@ -462,6 +477,8 @@ struct ArenaHeader { void setAsNotAllocated() { allocKind = size_t(FINALIZE_LIMIT); + markOverflow = 0; + allocatedDuringIncremental = 0; hasDelayedMarking = 0; nextDelayedMarking = 0; } @@ -507,8 +524,8 @@ struct ArenaHeader { void checkSynchronizedWithFreeList() const; #endif - inline Arena *getNextDelayedMarking() const; - inline void setNextDelayedMarking(Arena *arena); + inline ArenaHeader *getNextDelayedMarking() const; + inline void setNextDelayedMarking(ArenaHeader *aheader); }; struct Arena { @@ -908,25 +925,24 @@ ArenaHeader::getThingSize() const return Arena::thingSize(getAllocKind()); } -inline Arena * +inline ArenaHeader * ArenaHeader::getNextDelayedMarking() const { - return reinterpret_cast(nextDelayedMarking << ArenaShift); + return &reinterpret_cast(nextDelayedMarking << ArenaShift)->aheader; } inline void -ArenaHeader::setNextDelayedMarking(Arena *arena) +ArenaHeader::setNextDelayedMarking(ArenaHeader *aheader) { - JS_ASSERT(!hasDelayedMarking); + JS_ASSERT(!(uintptr_t(aheader) & ArenaMask)); hasDelayedMarking = 1; - nextDelayedMarking = arena->address() >> ArenaShift; + nextDelayedMarking = aheader->arenaAddress() >> ArenaShift; } JS_ALWAYS_INLINE void ChunkBitmap::getMarkWordAndMask(const Cell *cell, uint32_t color, uintptr_t **wordp, uintptr_t *maskp) { - JS_ASSERT(cell->chunk() == Chunk::fromAddress(reinterpret_cast(this))); size_t bit = (cell->address() & ChunkMask) / Cell::CellSize + color; JS_ASSERT(bit < ArenaBitmapBits * ArenasPerChunk); *maskp = uintptr_t(1) << (bit % JS_BITS_PER_WORD); @@ -970,21 +986,6 @@ Cell::compartment() const return arenaHeader()->compartment; } -/* - * Lower limit after which we limit the heap growth - */ -const size_t GC_ALLOCATION_THRESHOLD = 30 * 1024 * 1024; - -/* - * A GC is triggered once the number of newly allocated arenas is - * GC_HEAP_GROWTH_FACTOR times the number of live arenas after the last GC - * starting after the lower limit of GC_ALLOCATION_THRESHOLD. - */ -const float GC_HEAP_GROWTH_FACTOR = 3.0f; - -/* Perform a Full GC every 20 seconds if MaybeGC is called */ -static const int64_t GC_IDLE_FULL_SPAN = 20 * 1000 * 1000; - static inline JSGCTraceKind MapAllocToTraceKind(AllocKind thingKind) { @@ -1168,13 +1169,14 @@ struct ArenaLists { FreeSpan *headSpan = &freeLists[i]; if (!headSpan->isEmpty()) { ArenaHeader *aheader = headSpan->arenaHeader(); - JS_ASSERT(!aheader->hasFreeThings()); aheader->setFirstFreeSpan(headSpan); headSpan->initAsEmpty(); } } } + inline void prepareForIncrementalGC(JSCompartment *comp); + /* * Temporarily copy the free list heads to the arenas so the code can see * the proper value in ArenaHeader::freeList when accessing the latter @@ -1309,23 +1311,6 @@ typedef js::HashMap, js::SystemAllocPolicy> RootedValueMap; -/* If HashNumber grows, need to change WrapperHasher. */ -JS_STATIC_ASSERT(sizeof(HashNumber) == 4); - -struct WrapperHasher -{ - typedef Value Lookup; - - static HashNumber hash(Value key) { - uint64_t bits = JSVAL_TO_IMPL(key).asBits; - return uint32_t(bits) ^ uint32_t(bits >> 32); - } - - static bool match(const Value &l, const Value &k) { return l == k; } -}; - -typedef HashMap WrapperMap; - } /* namespace js */ extern JS_FRIEND_API(JSGCTraceKind) @@ -1376,6 +1361,9 @@ js_IsAddressableGCThing(JSRuntime *rt, uintptr_t w, js::gc::AllocKind *thingKind namespace js { +extern void +MarkCompartmentActive(js::StackFrame *fp); + extern void TraceRuntime(JSTracer *trc); @@ -1396,8 +1384,6 @@ MaybeGC(JSContext *cx); extern void ShrinkGCBuffers(JSRuntime *rt); -} /* namespace js */ - /* * Kinds of js_GC invocation. */ @@ -1411,10 +1397,21 @@ typedef enum JSGCInvocationKind { /* Pass NULL for |comp| to get a full GC. */ extern void -js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, js::gcreason::Reason r); +GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, js::gcreason::Reason reason); + +extern void +GCSlice(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, js::gcreason::Reason reason); + +extern void +GCDebugSlice(JSContext *cx, int64_t objCount); + +} /* namespace js */ namespace js { +void +InitTracer(JSTracer *trc, JSRuntime *rt, JSContext *cx, JSTraceCallback callback); + #ifdef JS_THREADSAFE class GCHelperThread { @@ -1572,17 +1569,56 @@ struct MarkStack { T *tos; T *limit; - bool push(T item) { - if (tos == limit) + T *ballast; + T *ballastLimit; + + MarkStack() + : stack(NULL), + tos(NULL), + limit(NULL), + ballast(NULL), + ballastLimit(NULL) { } + + ~MarkStack() { + if (stack != ballast) + js_free(stack); + js_free(ballast); + } + + bool init(size_t ballastcap) { + JS_ASSERT(!stack); + + if (ballastcap == 0) + return true; + + ballast = (T *)js_malloc(sizeof(T) * ballastcap); + if (!ballast) return false; + ballastLimit = ballast + ballastcap; + stack = ballast; + limit = ballastLimit; + tos = stack; + return true; + } + + bool push(T item) { + if (tos == limit) { + if (!enlarge()) + return false; + } + JS_ASSERT(tos < limit); *tos++ = item; return true; } bool push(T item1, T item2, T item3) { T *nextTos = tos + 3; - if (nextTos > limit) - return false; + if (nextTos > limit) { + if (!enlarge()) + return false; + nextTos = tos + 3; + } + JS_ASSERT(nextTos <= limit); tos[0] = item1; tos[1] = item2; tos[2] = item3; @@ -1599,61 +1635,130 @@ struct MarkStack { return *--tos; } - template - MarkStack(T (&buffer)[N]) - : stack(buffer), - tos(buffer), - limit(buffer + N) { } + ptrdiff_t position() const { + return tos - stack; + } + + void reset() { + if (stack != ballast) { + js_free(stack); + stack = ballast; + limit = ballastLimit; + } + tos = stack; + JS_ASSERT(limit == ballastLimit); + } + + bool enlarge() { + size_t tosIndex = tos - stack; + size_t cap = limit - stack; + size_t newcap = cap * 2; + if (newcap == 0) + newcap = 32; + + T *newStack; + if (stack == ballast) { + newStack = (T *)js_malloc(sizeof(T) * newcap); + if (!newStack) + return false; + for (T *src = stack, *dst = newStack; src < tos; ) + *dst++ = *src++; + } else { + newStack = (T *)js_realloc(stack, sizeof(T) * newcap); + if (!newStack) + return false; + } + stack = newStack; + tos = stack + tosIndex; + limit = newStack + newcap; + return true; + } +}; + +/* + * This class records how much work has been done in a given GC slice, so that + * we can return before pausing for too long. Some slices are allowed to run for + * unlimited time, and others are bounded. To reduce the number of gettimeofday + * calls, we only check the time every 1000 operations. + */ +struct SliceBudget { + int64_t deadline; /* in microseconds */ + intptr_t counter; + + static const intptr_t CounterReset = 1000; + + static const int64_t Unlimited = 0; + static int64_t TimeBudget(int64_t millis); + static int64_t WorkBudget(int64_t work); + + /* Equivalent to SliceBudget(UnlimitedBudget). */ + SliceBudget(); + + /* Instantiate as SliceBudget(Time/WorkBudget(n)). */ + SliceBudget(int64_t budget); + + void reset() { + deadline = INT64_MAX; + counter = INTPTR_MAX; + } + + void step() { + counter--; + } + + bool checkOverBudget(); + + bool isOverBudget() { + if (counter > 0) + return false; + return checkOverBudget(); + } }; static const size_t MARK_STACK_LENGTH = 32768; struct GCMarker : public JSTracer { + private: /* * We use a common mark stack to mark GC things of different types and use * the explicit tags to distinguish them when it cannot be deduced from * the context of push or pop operation. - * - * Currently we need only 4 tags. However that can be extended to 8 if - * necessary as we tag only GC things. */ enum StackTag { ValueArrayTag, ObjectTag, TypeTag, XmlTag, - LastTag = XmlTag + SavedValueArrayTag, + LastTag = SavedValueArrayTag }; - static const uintptr_t StackTagMask = 3; + static const uintptr_t StackTagMask = 7; static void staticAsserts() { JS_STATIC_ASSERT(StackTagMask >= uintptr_t(LastTag)); JS_STATIC_ASSERT(StackTagMask <= gc::Cell::CellMask); } - private: - /* The color is only applied to objects, functions and xml. */ - uint32_t color; - public: - /* Pointer to the top of the stack of arenas we are delaying marking on. */ - js::gc::Arena *unmarkedArenaStackTop; - /* Count of arenas that are currently in the stack. */ - DebugOnly markLaterArenas; + explicit GCMarker(); + bool init(bool lazy); -#ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS - js::gc::ConservativeGCStats conservativeStats; - Vector conservativeRoots; - const char *conservativeDumpFileName; - void dumpConservativeRoots(); -#endif + void start(JSRuntime *rt, JSContext *cx); + void stop(); + void reset(); - MarkStack stack; + void pushObject(JSObject *obj) { + pushTaggedPtr(ObjectTag, obj); + } - public: - explicit GCMarker(JSContext *cx); - ~GCMarker(); + void pushType(types::TypeObject *type) { + pushTaggedPtr(TypeTag, type); + } + + void pushXML(JSXML *xml) { + pushTaggedPtr(XmlTag, xml); + } uint32_t getMarkColor() const { return color; @@ -1668,43 +1773,123 @@ struct GCMarker : public JSTracer { * objects that are still reachable. */ void setMarkColorGray() { + JS_ASSERT(isDrained()); JS_ASSERT(color == gc::BLACK); color = gc::GRAY; } + inline void delayMarkingArena(gc::ArenaHeader *aheader); void delayMarkingChildren(const void *thing); - + void markDelayedChildren(gc::ArenaHeader *aheader); + bool markDelayedChildren(SliceBudget &budget); bool hasDelayedChildren() const { return !!unmarkedArenaStackTop; } - void markDelayedChildren(); + bool isDrained() { + return isMarkStackEmpty() && !unmarkedArenaStackTop; + } + + bool drainMarkStack(SliceBudget &budget); + + /* + * Gray marking must be done after all black marking is complete. However, + * we do not have write barriers on XPConnect roots. Therefore, XPConnect + * roots must be accumulated in the first slice of incremental GC. We + * accumulate these roots in the GrayRootMarker and then mark them later, + * after black marking is complete. This accumulation can fail, but in that + * case we switch to non-incremental GC. + */ + bool hasBufferedGrayRoots() const; + void startBufferingGrayRoots(); + void endBufferingGrayRoots(); + void markBufferedGrayRoots(); + + static void GrayCallback(JSTracer *trc, void **thing, JSGCTraceKind kind); + + MarkStack stack; + + private: +#ifdef DEBUG + void checkCompartment(void *p); +#else + void checkCompartment(void *p) {} +#endif + + void pushTaggedPtr(StackTag tag, void *ptr) { + checkCompartment(ptr); + uintptr_t addr = reinterpret_cast(ptr); + JS_ASSERT(!(addr & StackTagMask)); + if (!stack.push(addr | uintptr_t(tag))) + delayMarkingChildren(ptr); + } + + void pushValueArray(JSObject *obj, void *start, void *end) { + checkCompartment(obj); + + if (start == end) + return; + + JS_ASSERT(start <= end); + uintptr_t tagged = reinterpret_cast(obj) | GCMarker::ValueArrayTag; + uintptr_t startAddr = reinterpret_cast(start); + uintptr_t endAddr = reinterpret_cast(end); + + /* + * Push in the reverse order so obj will be on top. If we cannot push + * the array, we trigger delay marking for the whole object. + */ + if (!stack.push(endAddr, startAddr, tagged)) + delayMarkingChildren(obj); + } bool isMarkStackEmpty() { return stack.isEmpty(); } - void drainMarkStack(); + bool restoreValueArray(JSObject *obj, void **vpp, void **endp); + void saveValueRanges(); + inline void processMarkStackTop(SliceBudget &budget); - inline void processMarkStackTop(); + void appendGrayRoot(void *thing, JSGCTraceKind kind); - void pushObject(JSObject *obj) { - pushTaggedPtr(ObjectTag, obj); + /* The color is only applied to objects, functions and xml. */ + uint32_t color; + + DebugOnly started; + + /* Pointer to the top of the stack of arenas we are delaying marking on. */ + js::gc::ArenaHeader *unmarkedArenaStackTop; + /* Count of arenas that are currently in the stack. */ + DebugOnly markLaterArenas; + + struct GrayRoot { + void *thing; + JSGCTraceKind kind; +#ifdef DEBUG + JSTraceNamePrinter debugPrinter; + const void *debugPrintArg; + size_t debugPrintIndex; +#endif + + GrayRoot(void *thing, JSGCTraceKind kind) + : thing(thing), kind(kind) {} + }; + + bool grayFailed; + Vector grayRoots; +}; + +struct BarrierGCMarker : public GCMarker { + bool init() { + return GCMarker::init(true); } +}; - void pushType(types::TypeObject *type) { - pushTaggedPtr(TypeTag, type); - } - void pushXML(JSXML *xml) { - pushTaggedPtr(XmlTag, xml); - } - - void pushTaggedPtr(StackTag tag, void *ptr) { - uintptr_t addr = reinterpret_cast(ptr); - JS_ASSERT(!(addr & StackTagMask)); - if (!stack.push(addr | uintptr_t(tag))) - delayMarkingChildren(ptr); +struct FullGCMarker : public GCMarker { + bool init() { + return GCMarker::init(false); } }; @@ -1757,7 +1942,8 @@ js_FinalizeStringRT(JSRuntime *rt, JSString *str); /* * Macro to test if a traversal is the marking phase of the GC. */ -#define IS_GC_MARKING_TRACER(trc) ((trc)->callback == NULL) +#define IS_GC_MARKING_TRACER(trc) \ + ((trc)->callback == NULL || (trc)->callback == GCMarker::GrayCallback) namespace js { namespace gc { @@ -1778,20 +1964,30 @@ inline void MaybeCheckStackRoots(JSContext *cx) { CheckStackRoots(cx); } inline void MaybeCheckStackRoots(JSContext *cx) {} #endif -const int ZealPokeThreshold = 1; -const int ZealAllocThreshold = 2; -const int ZealVerifierThreshold = 4; +const int ZealPokeValue = 1; +const int ZealAllocValue = 2; +const int ZealFrameGCValue = 3; +const int ZealVerifierValue = 4; +const int ZealFrameVerifierValue = 5; #ifdef JS_GC_ZEAL /* Check that write barriers have been used correctly. See jsgc.cpp. */ void -VerifyBarriers(JSContext *cx, bool always = false); +VerifyBarriers(JSContext *cx); + +void +MaybeVerifyBarriers(JSContext *cx, bool always = false); #else static inline void -VerifyBarriers(JSContext *cx, bool always = false) +VerifyBarriers(JSContext *cx) +{ +} + +static inline void +MaybeVerifyBarriers(JSContext *cx, bool always = false) { } diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index 36627f39673..41000ebfe65 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -210,7 +210,7 @@ GCPoke(JSRuntime *rt, Value oldval) #ifdef JS_GC_ZEAL /* Schedule a GC to happen "soon" after a GC poke. */ - if (rt->gcZeal() >= js::gc::ZealPokeThreshold) + if (rt->gcZeal() == js::gc::ZealPokeValue) rt->gcNextScheduled = 1; #endif } @@ -262,14 +262,25 @@ class CellIterImpl CellIterImpl() { } - void init(JSCompartment *comp, AllocKind kind) { + void initSpan(JSCompartment *comp, AllocKind kind) { JS_ASSERT(comp->arenas.isSynchronizedFreeList(kind)); firstThingOffset = Arena::firstThingOffset(kind); thingSize = Arena::thingSize(kind); - aheader = comp->arenas.getFirstArena(kind); firstSpan.initAsEmpty(); span = &firstSpan; thing = span->first; + } + + void init(ArenaHeader *singleAheader) { + aheader = singleAheader; + initSpan(aheader->compartment, aheader->getAllocKind()); + next(); + aheader = NULL; + } + + void init(JSCompartment *comp, AllocKind kind) { + initSpan(comp, kind); + aheader = comp->arenas.getFirstArena(kind); next(); } @@ -311,13 +322,18 @@ class CellIterImpl } }; -class CellIterUnderGC : public CellIterImpl { - +class CellIterUnderGC : public CellIterImpl +{ public: CellIterUnderGC(JSCompartment *comp, AllocKind kind) { JS_ASSERT(comp->rt->gcRunning); init(comp, kind); } + + CellIterUnderGC(ArenaHeader *aheader) { + JS_ASSERT(aheader->compartment->rt->gcRunning); + init(aheader); + } }; /* @@ -325,7 +341,7 @@ class CellIterUnderGC : public CellIterImpl { * allocations of GC things are possible and that the background finalization * for the given thing kind is not enabled or is done. */ -class CellIter: public CellIterImpl +class CellIter : public CellIterImpl { ArenaLists *lists; AllocKind kind; @@ -335,7 +351,8 @@ class CellIter: public CellIterImpl public: CellIter(JSContext *cx, JSCompartment *comp, AllocKind kind) : lists(&comp->arenas), - kind(kind) { + kind(kind) + { #ifdef JS_THREADSAFE JS_ASSERT(comp->arenas.doneBackgroundFinalize(kind)); #endif @@ -397,6 +414,9 @@ NewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize) void *t = comp->arenas.allocateFromFreeList(kind, thingSize); if (!t) t = js::gc::ArenaLists::refillFreeList(cx, kind); + + JS_ASSERT_IF(t && comp->needsBarrier(), + static_cast(t)->arenaHeader()->allocatedDuringIncremental); return static_cast(t); } @@ -419,6 +439,8 @@ TryNewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize) #endif void *t = cx->compartment->arenas.allocateFromFreeList(kind, thingSize); + JS_ASSERT_IF(t && cx->compartment->needsBarrier(), + static_cast(t)->arenaHeader()->allocatedDuringIncremental); return static_cast(t); } diff --git a/js/src/jsgcmark.cpp b/js/src/jsgcmark.cpp index 6b359195d29..8b77c71ea40 100644 --- a/js/src/jsgcmark.cpp +++ b/js/src/jsgcmark.cpp @@ -45,9 +45,6 @@ * scanning functions, but they don't push onto an explicit stack. */ -using namespace js; -using namespace js::gc; - namespace js { namespace gc { @@ -64,7 +61,7 @@ static inline void PushMarkStack(GCMarker *gcmarker, JSScript *thing); static inline void -PushMarkStack(GCMarker *gcmarker, const Shape *thing); +PushMarkStack(GCMarker *gcmarker, Shape *thing); static inline void PushMarkStack(GCMarker *gcmarker, JSString *thing); @@ -106,7 +103,7 @@ MarkInternal(JSTracer *trc, T *thing) * GC. */ if (!rt->gcCurrentCompartment || thing->compartment() == rt->gcCurrentCompartment) { - if (IS_GC_MARKING_TRACER(trc)) { + if (!trc->callback) { PushMarkStack(static_cast(trc), thing); } else { void *tmp = (void *)thing; @@ -121,6 +118,12 @@ MarkInternal(JSTracer *trc, T *thing) #endif } +#define JS_ROOT_MARKING_ASSERT(trc) \ + JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc), \ + trc->runtime->gcIncrementalState == NO_INCREMENTAL || \ + trc->runtime->gcIncrementalState == MARK_ROOTS); + + template static void MarkUnbarriered(JSTracer *trc, T *thing, const char *name) @@ -131,23 +134,25 @@ MarkUnbarriered(JSTracer *trc, T *thing, const char *name) template static void -Mark(JSTracer *trc, const HeapPtr &thing, const char *name) +Mark(JSTracer *trc, HeapPtr *thing, const char *name) { JS_SET_TRACING_NAME(trc, name); - MarkInternal(trc, thing.get()); + MarkInternal(trc, thing->get()); } template static void -MarkRoot(JSTracer *trc, T *thing, const char *name) +MarkRoot(JSTracer *trc, T **thingp, const char *name) { + JS_ROOT_MARKING_ASSERT(trc); JS_SET_TRACING_NAME(trc, name); - MarkInternal(trc, thing); + MarkInternal(trc, *thingp); } template static void -MarkRange(JSTracer *trc, size_t len, HeapPtr *vec, const char *name) { +MarkRange(JSTracer *trc, size_t len, HeapPtr *vec, const char *name) +{ for (size_t i = 0; i < len; ++i) { if (T *obj = vec[i]) { JS_SET_TRACING_INDEX(trc, name, i); @@ -158,7 +163,9 @@ MarkRange(JSTracer *trc, size_t len, HeapPtr *vec, const char *name) { template static void -MarkRootRange(JSTracer *trc, size_t len, T **vec, const char *name) { +MarkRootRange(JSTracer *trc, size_t len, T **vec, const char *name) +{ + JS_ROOT_MARKING_ASSERT(trc); for (size_t i = 0; i < len; ++i) { JS_SET_TRACING_INDEX(trc, name, i); MarkInternal(trc, vec[i]); @@ -167,15 +174,15 @@ MarkRootRange(JSTracer *trc, size_t len, T **vec, const char *name) { #define DeclMarkerImpl(base, type) \ void \ -Mark##base(JSTracer *trc, const HeapPtr &thing, const char *name) \ +Mark##base(JSTracer *trc, HeapPtr *thing, const char *name) \ { \ Mark(trc, thing, name); \ } \ \ void \ -Mark##base##Root(JSTracer *trc, type *thing, const char *name) \ +Mark##base##Root(JSTracer *trc, type **thingp, const char *name) \ { \ - MarkRoot(trc, thing, name); \ + MarkRoot(trc, thingp, name); \ } \ \ void \ @@ -247,6 +254,7 @@ MarkKind(JSTracer *trc, void *thing, JSGCTraceKind kind) void MarkGCThingRoot(JSTracer *trc, void *thing, const char *name) { + JS_ROOT_MARKING_ASSERT(trc); JS_SET_TRACING_NAME(trc, name); if (!thing) return; @@ -256,24 +264,30 @@ MarkGCThingRoot(JSTracer *trc, void *thing, const char *name) /*** ID Marking ***/ static inline void -MarkIdInternal(JSTracer *trc, const jsid &id) +MarkIdInternal(JSTracer *trc, jsid *id) { - if (JSID_IS_STRING(id)) - MarkInternal(trc, JSID_TO_STRING(id)); - else if (JS_UNLIKELY(JSID_IS_OBJECT(id))) - MarkInternal(trc, JSID_TO_OBJECT(id)); + if (JSID_IS_STRING(*id)) { + JSString *str = JSID_TO_STRING(*id); + MarkInternal(trc, str); + *id = ATOM_TO_JSID(reinterpret_cast(str)); + } else if (JS_UNLIKELY(JSID_IS_OBJECT(*id))) { + JSObject *obj = JSID_TO_OBJECT(*id); + MarkInternal(trc, obj); + *id = OBJECT_TO_JSID(obj); + } } void -MarkId(JSTracer *trc, const HeapId &id, const char *name) +MarkId(JSTracer *trc, HeapId *id, const char *name) { JS_SET_TRACING_NAME(trc, name); - MarkIdInternal(trc, id); + MarkIdInternal(trc, id->unsafeGet()); } void -MarkIdRoot(JSTracer *trc, const jsid &id, const char *name) +MarkIdRoot(JSTracer *trc, jsid *id, const char *name) { + JS_ROOT_MARKING_ASSERT(trc); JS_SET_TRACING_NAME(trc, name); MarkIdInternal(trc, id); } @@ -283,59 +297,62 @@ MarkIdRange(JSTracer *trc, size_t len, HeapId *vec, const char *name) { for (size_t i = 0; i < len; ++i) { JS_SET_TRACING_INDEX(trc, name, i); - MarkIdInternal(trc, vec[i]); + MarkIdInternal(trc, vec[i].unsafeGet()); } } void MarkIdRootRange(JSTracer *trc, size_t len, jsid *vec, const char *name) { + JS_ROOT_MARKING_ASSERT(trc); for (size_t i = 0; i < len; ++i) { JS_SET_TRACING_INDEX(trc, name, i); - MarkIdInternal(trc, vec[i]); + MarkIdInternal(trc, &vec[i]); } } /*** Value Marking ***/ static inline void -MarkValueInternal(JSTracer *trc, const Value &v) +MarkValueInternal(JSTracer *trc, Value *v) { - if (v.isMarkable()) { - JS_ASSERT(v.toGCThing()); - return MarkKind(trc, v.toGCThing(), v.gcKind()); + if (v->isMarkable()) { + JS_ASSERT(v->toGCThing()); + return MarkKind(trc, v->toGCThing(), v->gcKind()); } } void -MarkValue(JSTracer *trc, const js::HeapValue &v, const char *name) +MarkValue(JSTracer *trc, HeapValue *v, const char *name) { + JS_SET_TRACING_NAME(trc, name); + MarkValueInternal(trc, v->unsafeGet()); +} + +void +MarkValueRoot(JSTracer *trc, Value *v, const char *name) +{ + JS_ROOT_MARKING_ASSERT(trc); JS_SET_TRACING_NAME(trc, name); MarkValueInternal(trc, v); } void -MarkValueRoot(JSTracer *trc, const Value &v, const char *name) -{ - JS_SET_TRACING_NAME(trc, name); - MarkValueInternal(trc, v); -} - -void -MarkValueRange(JSTracer *trc, size_t len, const HeapValue *vec, const char *name) +MarkValueRange(JSTracer *trc, size_t len, HeapValue *vec, const char *name) { for (size_t i = 0; i < len; ++i) { JS_SET_TRACING_INDEX(trc, name, i); - MarkValueInternal(trc, vec[i]); + MarkValueInternal(trc, vec[i].unsafeGet()); } } void -MarkValueRootRange(JSTracer *trc, size_t len, const Value *vec, const char *name) +MarkValueRootRange(JSTracer *trc, size_t len, Value *vec, const char *name) { + JS_ROOT_MARKING_ASSERT(trc); for (size_t i = 0; i < len; ++i) { JS_SET_TRACING_INDEX(trc, name, i); - MarkValueInternal(trc, vec[i]); + MarkValueInternal(trc, &vec[i]); } } @@ -353,28 +370,25 @@ MarkObject(JSTracer *trc, const HeapPtr &thing, const } void -MarkShape(JSTracer *trc, const HeapPtr &thing, const char *name) -{ - JS_SET_TRACING_NAME(trc, name); - MarkInternal(trc, const_cast(thing.get())); -} - -void -MarkValueUnbarriered(JSTracer *trc, const js::Value &v, const char *name) +MarkValueUnbarriered(JSTracer *trc, Value *v, const char *name) { JS_SET_TRACING_NAME(trc, name); MarkValueInternal(trc, v); } void -MarkCrossCompartmentValue(JSTracer *trc, const js::HeapValue &v, const char *name) +MarkCrossCompartmentValue(JSTracer *trc, HeapValue *v, const char *name) { - if (v.isMarkable()) { - js::gc::Cell *cell = (js::gc::Cell *)v.toGCThing(); + if (v->isMarkable()) { + Cell *cell = (Cell *)v->toGCThing(); JSRuntime *rt = trc->runtime; if (rt->gcCurrentCompartment && cell->compartment() != rt->gcCurrentCompartment) return; + /* In case we're called from a write barrier. */ + if (rt->gcIncrementalCompartment && cell->compartment() != rt->gcIncrementalCompartment) + return; + MarkValue(trc, v, name); } } @@ -444,10 +458,10 @@ PushMarkStack(GCMarker *gcmarker, JSScript *thing) } static void -ScanShape(GCMarker *gcmarker, const Shape *shape); +ScanShape(GCMarker *gcmarker, Shape *shape); static void -PushMarkStack(GCMarker *gcmarker, const Shape *thing) +PushMarkStack(GCMarker *gcmarker, Shape *thing) { JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing); @@ -470,12 +484,12 @@ PushMarkStack(GCMarker *gcmarker, BaseShape *thing) } static void -ScanShape(GCMarker *gcmarker, const Shape *shape) +ScanShape(GCMarker *gcmarker, Shape *shape) { restart: PushMarkStack(gcmarker, shape->base()); - jsid id = shape->maybePropid(); + const HeapId &id = shape->propidRef(); if (JSID_IS_STRING(id)) PushMarkStack(gcmarker, JSID_TO_STRING(id)); else if (JS_UNLIKELY(JSID_IS_OBJECT(id))) @@ -544,7 +558,7 @@ ScanLinearString(GCMarker *gcmarker, JSLinearString *str) static void ScanRope(GCMarker *gcmarker, JSRope *rope) { - uintptr_t *savedTos = gcmarker->stack.tos; + ptrdiff_t savedPos = gcmarker->stack.position(); for (;;) { JS_ASSERT(GetGCThingTraceKind(rope) == JSTRACE_STRING); JS_ASSERT(rope->JSString::isRope()); @@ -576,14 +590,14 @@ ScanRope(GCMarker *gcmarker, JSRope *rope) } if (next) { rope = next; - } else if (savedTos != gcmarker->stack.tos) { - JS_ASSERT(savedTos < gcmarker->stack.tos); + } else if (savedPos != gcmarker->stack.position()) { + JS_ASSERT(savedPos < gcmarker->stack.position()); rope = reinterpret_cast(gcmarker->stack.pop()); } else { break; } } - JS_ASSERT(savedTos == gcmarker->stack.tos); + JS_ASSERT(savedPos == gcmarker->stack.position()); } static inline void @@ -609,28 +623,10 @@ PushMarkStack(GCMarker *gcmarker, JSString *str) ScanString(gcmarker, str); } -static inline void -PushValueArray(GCMarker *gcmarker, JSObject* obj, HeapValue *start, HeapValue *end) -{ - JS_ASSERT(start <= end); - uintptr_t tagged = reinterpret_cast(obj) | GCMarker::ValueArrayTag; - uintptr_t startAddr = reinterpret_cast(start); - uintptr_t endAddr = reinterpret_cast(end); - - /* Push in the reverse order so obj will be on top. */ - if (!gcmarker->stack.push(endAddr, startAddr, tagged)) { - /* - * If we cannot push the array, we trigger delay marking for the whole - * object. - */ - gcmarker->delayMarkingChildren(obj); - } -} - void MarkChildren(JSTracer *trc, JSObject *obj) { - MarkTypeObject(trc, obj->typeFromGC(), "type"); + MarkTypeObject(trc, &obj->typeFromGC(), "type"); Shape *shape = obj->lastProperty(); MarkShapeUnbarriered(trc, shape, "shape"); @@ -643,7 +639,7 @@ MarkChildren(JSTracer *trc, JSObject *obj) uint32_t nslots = obj->slotSpan(); for (uint32_t i = 0; i < nslots; i++) { JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i); - MarkValueInternal(trc, obj->nativeGetSlot(i)); + MarkValueInternal(trc, obj->nativeGetSlotRef(i).unsafeGet()); } } } @@ -672,7 +668,10 @@ MarkChildren(JSTracer *trc, JSScript *script) JS_ASSERT_IF(trc->runtime->gcCheckCompartment, script->compartment() == trc->runtime->gcCheckCompartment); - MarkStringRootRange(trc, script->natoms, script->atoms, "atoms"); + for (uint32_t i = 0; i < script->natoms; ++i) { + if (JSAtom *p = script->atoms[i]) + MarkStringUnbarriered(trc, p, "atom"); + } if (JSScript::isValidOffset(script->objectsOffset)) { JSObjectArray *objarray = script->objects(); @@ -708,12 +707,12 @@ MarkChildren(JSTracer *trc, JSScript *script) } static void -MarkChildren(JSTracer *trc, const Shape *shape) +MarkChildren(JSTracer *trc, Shape *shape) { MarkBaseShapeUnbarriered(trc, shape->base(), "base"); - MarkId(trc, shape->maybePropid(), "propid"); + MarkId(trc, &shape->propidRef(), "propid"); if (shape->previous()) - MarkShape(trc, shape->previous(), "parent"); + MarkShape(trc, &shape->previousRef(), "parent"); } static inline void @@ -774,12 +773,12 @@ MarkCycleCollectorChildren(JSTracer *trc, BaseShape *base, JSObject **prevParent * parent pointer will only be marked once. */ void -MarkCycleCollectorChildren(JSTracer *trc, const Shape *shape) +MarkCycleCollectorChildren(JSTracer *trc, Shape *shape) { JSObject *prevParent = NULL; do { MarkCycleCollectorChildren(trc, shape->base(), &prevParent); - MarkId(trc, shape->maybePropid(), "propid"); + MarkId(trc, &shape->propidRef(), "propid"); shape = shape->previous(); } while (shape); } @@ -822,23 +821,23 @@ MarkChildren(JSTracer *trc, types::TypeObject *type) for (unsigned i = 0; i < count; i++) { types::Property *prop = type->getProperty(i); if (prop) - MarkId(trc, prop->id, "type_prop"); + MarkId(trc, &prop->id, "type_prop"); } } if (type->proto) - MarkObject(trc, type->proto, "type_proto"); + MarkObject(trc, &type->proto, "type_proto"); if (type->singleton && !type->lazy()) - MarkObject(trc, type->singleton, "type_singleton"); + MarkObject(trc, &type->singleton, "type_singleton"); if (type->newScript) { - MarkObject(trc, type->newScript->fun, "type_new_function"); - MarkShape(trc, type->newScript->shape, "type_new_shape"); + MarkObject(trc, &type->newScript->fun, "type_new_function"); + MarkShape(trc, &type->newScript->shape, "type_new_shape"); } if (type->interpretedFunction) - MarkObject(trc, type->interpretedFunction, "type_function"); + MarkObject(trc, &type->interpretedFunction, "type_function"); } #ifdef JS_HAS_XML_SUPPORT @@ -849,10 +848,163 @@ MarkChildren(JSTracer *trc, JSXML *xml) } #endif +template +void +PushArenaTyped(GCMarker *gcmarker, ArenaHeader *aheader) +{ + for (CellIterUnderGC i(aheader); !i.done(); i.next()) + PushMarkStack(gcmarker, i.get()); +} + +void +PushArena(GCMarker *gcmarker, ArenaHeader *aheader) +{ + switch (MapAllocToTraceKind(aheader->getAllocKind())) { + case JSTRACE_OBJECT: + PushArenaTyped(gcmarker, aheader); + break; + + case JSTRACE_STRING: + PushArenaTyped(gcmarker, aheader); + break; + + case JSTRACE_SCRIPT: + PushArenaTyped(gcmarker, aheader); + break; + + case JSTRACE_SHAPE: + PushArenaTyped(gcmarker, aheader); + break; + + case JSTRACE_BASE_SHAPE: + PushArenaTyped(gcmarker, aheader); + break; + + case JSTRACE_TYPE_OBJECT: + PushArenaTyped(gcmarker, aheader); + break; + +#if JS_HAS_XML_SUPPORT + case JSTRACE_XML: + PushArenaTyped(gcmarker, aheader); + break; +#endif + } +} + } /* namespace gc */ +using namespace js::gc; + +struct ValueArrayLayout +{ + union { + HeapValue *end; + js::Class *clasp; + }; + union { + HeapValue *start; + uintptr_t index; + }; + JSObject *obj; + + static void staticAsserts() { + /* This should have the same layout as three mark stack items. */ + JS_STATIC_ASSERT(sizeof(ValueArrayLayout) == 3 * sizeof(uintptr_t)); + } +}; + +/* + * During incremental GC, we return from drainMarkStack without having processed + * the entire stack. At that point, JS code can run and reallocate slot arrays + * that are stored on the stack. To prevent this from happening, we replace all + * ValueArrayTag stack items with SavedValueArrayTag. In the latter, slots + * pointers are replaced with slot indexes. + * + * We also replace the slot array end pointer (which can be derived from the obj + * pointer) with the object's class. During JS executation, array slowification + * can cause the layout of slots to change. We can observe that slowification + * happened if the class changed; in that case, we completely rescan the array. + */ +void +GCMarker::saveValueRanges() +{ + for (uintptr_t *p = stack.tos; p > stack.stack; ) { + uintptr_t tag = *--p & StackTagMask; + if (tag == ValueArrayTag) { + p -= 2; + ValueArrayLayout *arr = reinterpret_cast(p); + JSObject *obj = arr->obj; + + if (obj->getClass() == &ArrayClass) { + HeapValue *vp = obj->getDenseArrayElements(); + JS_ASSERT(arr->start >= vp && + arr->end == vp + obj->getDenseArrayInitializedLength()); + arr->index = arr->start - vp; + } else { + HeapValue *vp = obj->fixedSlots(); + unsigned nfixed = obj->numFixedSlots(); + if (arr->start >= vp && arr->start < vp + nfixed) { + JS_ASSERT(arr->end == vp + Min(nfixed, obj->slotSpan())); + arr->index = arr->start - vp; + } else { + JS_ASSERT(arr->start >= obj->slots && + arr->end == obj->slots + obj->slotSpan() - nfixed); + arr->index = (arr->start - obj->slots) + nfixed; + } + } + arr->clasp = obj->getClass(); + p[2] |= SavedValueArrayTag; + } else if (tag == SavedValueArrayTag) { + p -= 2; + } + } +} + +bool +GCMarker::restoreValueArray(JSObject *obj, void **vpp, void **endp) +{ + uintptr_t start = stack.pop(); + js::Class *clasp = reinterpret_cast(stack.pop()); + + JS_ASSERT(obj->getClass() == clasp || + (clasp == &ArrayClass && obj->getClass() == &SlowArrayClass)); + + if (clasp == &ArrayClass) { + if (obj->getClass() != &ArrayClass) + return false; + + uint32_t initlen = obj->getDenseArrayInitializedLength(); + HeapValue *vp = obj->getDenseArrayElements(); + if (start < initlen) { + *vpp = vp + start; + *endp = vp + initlen; + } else { + /* The object shrunk, in which case no scanning is needed. */ + *vpp = *endp = vp; + } + } else { + HeapValue *vp = obj->fixedSlots(); + unsigned nfixed = obj->numFixedSlots(); + unsigned nslots = obj->slotSpan(); + if (start < nfixed) { + *vpp = vp + start; + *endp = vp + Min(nfixed, nslots); + } else if (start < nslots) { + *vpp = obj->slots + start - nfixed; + *endp = obj->slots + nslots - nfixed; + } else { + /* The object shrunk, in which case no scanning is needed. */ + *vpp = *endp = obj->slots; + } + } + + JS_ASSERT(*vpp <= *endp); + return true; +} + inline void -GCMarker::processMarkStackTop() +GCMarker::processMarkStackTop(SliceBudget &budget) { /* * The function uses explicit goto and implements the scanning of the @@ -881,29 +1033,46 @@ GCMarker::processMarkStackTop() if (tag == ObjectTag) { obj = reinterpret_cast(addr); + JS_COMPARTMENT_ASSERT(runtime, obj); goto scan_obj; } if (tag == TypeTag) { ScanTypeObject(this, reinterpret_cast(addr)); + } else if (tag == SavedValueArrayTag) { + JS_ASSERT(!(addr & Cell::CellMask)); + obj = reinterpret_cast(addr); + if (restoreValueArray(obj, (void **)&vp, (void **)&end)) + goto scan_value_array; + else + goto scan_obj; } else { JS_ASSERT(tag == XmlTag); MarkChildren(this, reinterpret_cast(addr)); } + budget.step(); return; scan_value_array: JS_ASSERT(vp <= end); while (vp != end) { + budget.step(); + if (budget.isOverBudget()) { + pushValueArray(obj, vp, end); + return; + } + const Value &v = *vp++; if (v.isString()) { JSString *str = v.toString(); + JS_COMPARTMENT_ASSERT_STR(runtime, str); if (str->markIfUnmarked()) ScanString(this, str); } else if (v.isObject()) { JSObject *obj2 = &v.toObject(); + JS_COMPARTMENT_ASSERT(runtime, obj2); if (obj2->markIfUnmarked(getMarkColor())) { - PushValueArray(this, obj, vp, end); + pushValueArray(obj, vp, end); obj = obj2; goto scan_obj; } @@ -913,10 +1082,18 @@ GCMarker::processMarkStackTop() scan_obj: { + JS_COMPARTMENT_ASSERT(runtime, obj); + + budget.step(); + if (budget.isOverBudget()) { + pushObject(obj); + return; + } + types::TypeObject *type = obj->typeFromGC(); PushMarkStack(this, type); - js::Shape *shape = obj->lastProperty(); + Shape *shape = obj->lastProperty(); PushMarkStack(this, shape); /* Call the trace hook if necessary. */ @@ -927,6 +1104,9 @@ GCMarker::processMarkStackTop() vp = obj->getDenseArrayElements(); end = vp + obj->getDenseArrayInitializedLength(); goto scan_value_array; + } else { + JS_ASSERT_IF(runtime->gcIncrementalState != NO_INCREMENTAL, + clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS); } clasp->trace(this, obj); } @@ -939,7 +1119,7 @@ GCMarker::processMarkStackTop() if (obj->slots) { unsigned nfixed = obj->numFixedSlots(); if (nslots > nfixed) { - PushValueArray(this, obj, vp, vp + nfixed); + pushValueArray(obj, vp, vp + nfixed); vp = obj->slots; end = vp + (nslots - nfixed); goto scan_value_array; @@ -951,15 +1131,33 @@ GCMarker::processMarkStackTop() } } -void -GCMarker::drainMarkStack() +bool +GCMarker::drainMarkStack(SliceBudget &budget) { +#ifdef DEBUG JSRuntime *rt = runtime; - rt->gcCheckCompartment = rt->gcCurrentCompartment; + + struct AutoCheckCompartment { + JSRuntime *runtime; + AutoCheckCompartment(JSRuntime *rt) : runtime(rt) { + runtime->gcCheckCompartment = runtime->gcCurrentCompartment; + } + ~AutoCheckCompartment() { runtime->gcCheckCompartment = NULL; } + } acc(rt); +#endif + + if (budget.isOverBudget()) + return false; for (;;) { - while (!stack.isEmpty()) - processMarkStackTop(); + while (!stack.isEmpty()) { + processMarkStackTop(budget); + if (budget.isOverBudget()) { + saveValueRanges(); + return false; + } + } + if (!hasDelayedChildren()) break; @@ -968,10 +1166,13 @@ GCMarker::drainMarkStack() * above tracing. Don't do this until we're done with everything * else. */ - markDelayedChildren(); + if (!markDelayedChildren(budget)) { + saveValueRanges(); + return false; + } } - rt->gcCheckCompartment = NULL; + return true; } void diff --git a/js/src/jsgcmark.h b/js/src/jsgcmark.h index 57099af8218..c686618e25a 100644 --- a/js/src/jsgcmark.h +++ b/js/src/jsgcmark.h @@ -45,8 +45,8 @@ namespace gc { * defined for marking arrays of object pointers. */ #define DeclMarker(base, type) \ -void Mark##base(JSTracer *trc, const HeapPtr &thing, const char *name); \ -void Mark##base##Root(JSTracer *trc, type *thing, const char *name); \ +void Mark##base(JSTracer *trc, HeapPtr *thing, const char *name); \ +void Mark##base##Root(JSTracer *trc, type **thingp, const char *name); \ void Mark##base##Unbarriered(JSTracer *trc, type *thing, const char *name); \ void Mark##base##Range(JSTracer *trc, size_t len, HeapPtr *thing, const char *name); \ void Mark##base##RootRange(JSTracer *trc, size_t len, type **thing, const char *name); @@ -83,10 +83,10 @@ MarkGCThingRoot(JSTracer *trc, void *thing, const char *name); /*** ID Marking ***/ void -MarkId(JSTracer *trc, const HeapId &id, const char *name); +MarkId(JSTracer *trc, HeapId *id, const char *name); void -MarkIdRoot(JSTracer *trc, const jsid &id, const char *name); +MarkIdRoot(JSTracer *trc, jsid *id, const char *name); void MarkIdRange(JSTracer *trc, size_t len, js::HeapId *vec, const char *name); @@ -97,39 +97,35 @@ MarkIdRootRange(JSTracer *trc, size_t len, jsid *vec, const char *name); /*** Value Marking ***/ void -MarkValue(JSTracer *trc, const js::HeapValue &v, const char *name); +MarkValue(JSTracer *trc, HeapValue *v, const char *name); void -MarkValueRange(JSTracer *trc, size_t len, const HeapValue *vec, const char *name); +MarkValueRange(JSTracer *trc, size_t len, HeapValue *vec, const char *name); void -MarkValueRoot(JSTracer *trc, const Value &v, const char *name); +MarkValueRoot(JSTracer *trc, Value *v, const char *name); void -MarkValueRootRange(JSTracer *trc, size_t len, const Value *vec, const char *name); +MarkValueRootRange(JSTracer *trc, size_t len, Value *vec, const char *name); inline void -MarkValueRootRange(JSTracer *trc, const Value *begin, const Value *end, const char *name) +MarkValueRootRange(JSTracer *trc, Value *begin, Value *end, const char *name) { MarkValueRootRange(trc, end - begin, begin, name); } /*** Special Cases ***/ -/* TypeNewObject contains a HeapPtr that needs a unique cast. */ -void -MarkShape(JSTracer *trc, const HeapPtr &thing, const char *name); - /* Direct value access used by the write barriers and the methodjit */ void -MarkValueUnbarriered(JSTracer *trc, const js::Value &v, const char *name); +MarkValueUnbarriered(JSTracer *trc, Value *v, const char *name); /* * Mark a value that may be in a different compartment from the compartment * being GC'd. (Although it won't be marked if it's in the wrong compartment.) */ void -MarkCrossCompartmentValue(JSTracer *trc, const js::HeapValue &v, const char *name); +MarkCrossCompartmentValue(JSTracer *trc, HeapValue *v, const char *name); /* * MarkChildren is exposed solely for preWriteBarrier on @@ -144,34 +140,38 @@ MarkChildren(JSTracer *trc, JSObject *obj); * JS_TraceShapeCycleCollectorChildren. */ void -MarkCycleCollectorChildren(JSTracer *trc, const Shape *shape); +MarkCycleCollectorChildren(JSTracer *trc, Shape *shape); + +void +PushArena(GCMarker *gcmarker, ArenaHeader *aheader); /*** Generic ***/ + /* * The Mark() functions interface should only be used by code that must be * templated. Other uses should use the more specific, type-named functions. */ inline void -Mark(JSTracer *trc, const js::HeapValue &v, const char *name) +Mark(JSTracer *trc, HeapValue *v, const char *name) { MarkValue(trc, v, name); } inline void -Mark(JSTracer *trc, const HeapPtr &o, const char *name) +Mark(JSTracer *trc, HeapPtr *o, const char *name) { MarkObject(trc, o, name); } inline void -Mark(JSTracer *trc, const HeapPtr &xml, const char *name) +Mark(JSTracer *trc, HeapPtr *xml, const char *name) { MarkXML(trc, xml, name); } inline bool -IsMarked(const js::Value &v) +IsMarked(const Value &v) { if (v.isMarkable()) return !IsAboutToBeFinalized(v); diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 6b7c1fb075b..eedd148126e 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -2195,7 +2195,7 @@ TypeCompartment::nukeTypes(JSContext *cx) #ifdef JS_THREADSAFE AutoLockGC maybeLock; - if (!cx->runtime->gcMarkAndSweep) + if (!cx->runtime->gcRunning) maybeLock.lock(cx->runtime); #endif @@ -2618,7 +2618,7 @@ struct types::ObjectTableKey typedef JSObject * Lookup; static inline uint32_t hash(JSObject *obj) { - return (uint32_t) (JSID_BITS(obj->lastProperty()->propid()) ^ + return (uint32_t) (JSID_BITS(obj->lastProperty()->propid().get()) ^ obj->slotSpan() ^ obj->numFixedSlots() ^ ((uint32_t)(size_t)obj->getProto() >> 2)); } diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index 8a7fc83ed81..a536d520a8b 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -668,7 +668,7 @@ struct TypeNewScript * Shape to use for newly constructed objects. Reflects all definite * properties the object will have. */ - HeapPtr shape; + HeapPtrShape shape; /* * Order in which properties become initialized. We need this in case a diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index c936448c33c..dbdd6e66d1f 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -741,7 +741,7 @@ void TypeScript::trace(JSTracer *trc) { if (hasScope() && global) - gc::MarkObject(trc, global, "script_global"); + gc::MarkObject(trc, &global, "script_global"); /* Note: nesting does not keep anything alive. */ } @@ -1343,7 +1343,7 @@ TypeNewScript::writeBarrierPre(TypeNewScript *newScript) JSCompartment *comp = newScript->fun->compartment(); if (comp->needsBarrier()) { MarkObjectUnbarriered(comp->barrierTracer(), newScript->fun, "write barrier"); - MarkShape(comp->barrierTracer(), newScript->shape, "write barrier"); + MarkShape(comp->barrierTracer(), &newScript->shape, "write barrier"); } #endif } diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 794c480600f..bdd2f7b711c 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -1147,7 +1147,7 @@ js::AssertValidPropertyCacheHit(JSContext *cx, jsbytecode *pc; cx->stack.currentScript(&pc); - uint32_t sample = cx->runtime->gcNumber; + uint64_t sample = cx->runtime->gcNumber; PropertyCacheEntry savedEntry = *entry; PropertyName *name = GetNameFromBytecode(cx, pc, JSOp(*pc), js_CodeSpec[*pc]); @@ -1254,7 +1254,7 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode) { JSAutoResolveFlags rf(cx, RESOLVE_INFER); - gc::VerifyBarriers(cx, true); + gc::MaybeVerifyBarriers(cx, true); JS_ASSERT(!cx->compartment->activeAnalysis); @@ -1289,7 +1289,7 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode) # define DO_OP() JS_BEGIN_MACRO \ CHECK_PCCOUNT_INTERRUPTS(); \ - js::gc::VerifyBarriers(cx); \ + js::gc::MaybeVerifyBarriers(cx); \ JS_EXTENSION_(goto *jumpTable[op]); \ JS_END_MACRO # define DO_NEXT_OP(n) JS_BEGIN_MACRO \ @@ -1566,7 +1566,7 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode) do_op: CHECK_PCCOUNT_INTERRUPTS(); - js::gc::VerifyBarriers(cx); + js::gc::MaybeVerifyBarriers(cx); switchOp = intN(op) | switchMask; do_switch: switch (switchOp) { @@ -4424,6 +4424,6 @@ END_CASE(JSOP_ARRAYPUSH) leave_on_safe_point: #endif - gc::VerifyBarriers(cx, true); + gc::MaybeVerifyBarriers(cx, true); return interpReturnOK; } diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 965614c9a02..c476bc83472 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -89,7 +89,7 @@ static JSObject *iterator_iterator(JSContext *cx, JSObject *obj, JSBool keysonly Class js::IteratorClass = { "Iterator", - JSCLASS_HAS_PRIVATE | + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator), JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* delProperty */ @@ -148,9 +148,9 @@ void NativeIterator::mark(JSTracer *trc) { for (HeapPtr *str = begin(); str < end(); str++) - MarkString(trc, *str, "prop"); + MarkString(trc, str, "prop"); if (obj) - MarkObject(trc, obj, "obj"); + MarkObject(trc, &obj, "obj"); } static void @@ -1419,7 +1419,7 @@ generator_trace(JSTracer *trc, JSObject *obj) Class js::GeneratorClass = { "Generator", - JSCLASS_HAS_PRIVATE, + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS, JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* getProperty */ diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index 2dd2471e9ce..caf05ebef5d 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -490,10 +490,9 @@ Number(JSContext *cx, uintN argc, Value *vp) if (!isConstructing) return true; - JSObject *obj = NewBuiltinClassInstance(cx, &NumberClass); + JSObject *obj = NumberObject::create(cx, vp[0].toNumber()); if (!obj) return false; - obj->setPrimitiveThis(vp[0]); vp->setObject(*obj); return true; } diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 5fd9286f900..4405f4dd6ac 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -203,14 +203,8 @@ obj_setProto(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp) #endif /* !JS_HAS_OBJ_PROTO_PROP */ -static JSHashNumber -js_hash_object(const void *key) -{ - return JSHashNumber(uintptr_t(key) >> JS_GCTHING_ALIGN); -} - -static JSHashEntry * -MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap) +static bool +MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap, JSSharpInfo *value) { JS_CHECK_RECURSION(cx, return NULL); @@ -218,21 +212,15 @@ MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap) JSSharpObjectMap *map = &cx->sharpObjectMap; JS_ASSERT(map->depth >= 1); - JSHashTable *table = map->table; - JSHashNumber hash = js_hash_object(obj); - JSHashEntry **hep = JS_HashTableRawLookup(table, hash, obj); - JSHashEntry *he = *hep; - if (!he) { - jsatomid sharpid = 0; - he = JS_HashTableRawAdd(table, hep, hash, obj, (void *) sharpid); - if (!he) { - JS_ReportOutOfMemory(cx); - return NULL; - } + JSSharpInfo sharpid; + JSSharpTable::Ptr p = map->table.lookup(obj); + if (!p) { + if (!map->table.put(obj, sharpid)) + return false; ida = JS_Enumerate(cx, obj); if (!ida) - return NULL; + return false; bool ok = true; for (jsint i = 0, length = ida->length; i < length; i++) { @@ -261,7 +249,7 @@ MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap) if (hasSetter) { /* Mark the getter, then set val to setter. */ if (hasGetter && v.value().isObject()) { - ok = !!MarkSharpObjects(cx, &v.value().toObject(), NULL); + ok = MarkSharpObjects(cx, &v.value().toObject(), NULL, NULL); if (!ok) break; } @@ -271,7 +259,7 @@ MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap) if (!ok) break; } - if (v.value().isObject() && !MarkSharpObjects(cx, &v.value().toObject(), NULL)) { + if (v.value().isObject() && !MarkSharpObjects(cx, &v.value().toObject(), NULL, NULL)) { ok = false; break; } @@ -279,47 +267,42 @@ MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap) if (!ok || !idap) JS_DestroyIdArray(cx, ida); if (!ok) - return NULL; + return false; } else { - jsatomid sharpid = uintptr_t(he->value); - if (sharpid == 0) { - sharpid = ++map->sharpgen << SHARP_ID_SHIFT; - he->value = (void *) sharpid; + if (!p->value.hasGen && !p->value.isSharp) { + p->value.hasGen = true; } + sharpid = p->value; ida = NULL; } if (idap) *idap = ida; - return he; + if (value) + *value = sharpid; + return true; } -JSHashEntry * -js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, bool *alreadySeen) +bool +js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, bool *alreadySeen, bool *isSharp) { if (!JS_CHECK_OPERATION_LIMIT(cx)) - return NULL; + return false; *alreadySeen = false; JSSharpObjectMap *map = &cx->sharpObjectMap; - JSHashTable *table = map->table; - if (!table) { - table = JS_NewHashTable(8, js_hash_object, JS_CompareValues, - JS_CompareValues, NULL, NULL); - if (!table) { - JS_ReportOutOfMemory(cx); - return NULL; - } - map->table = table; - JS_KEEP_ATOMS(cx->runtime); - } - JSHashEntry *he; - jsatomid sharpid; + JS_ASSERT_IF(map->depth == 0, map->table.count() == 0); + JS_ASSERT_IF(map->table.count() == 0, map->depth == 0); + + JSSharpTable::Ptr p; + JSSharpInfo sharpid; JSIdArray *ida = NULL; /* From this point the control must flow either through out: or bad:. */ if (map->depth == 0) { + JS_KEEP_ATOMS(cx->runtime); + /* * Although MarkSharpObjects tries to avoid invoking getters, * it ends up doing so anyway under some circumstances; for @@ -332,21 +315,18 @@ js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, bool *alread * ensure that such a call doesn't free the hash table we're * still using. */ - ++map->depth; - he = MarkSharpObjects(cx, obj, &ida); - --map->depth; - if (!he) + map->depth = 1; + bool success = MarkSharpObjects(cx, obj, &ida, &sharpid); + JS_ASSERT(map->depth == 1); + map->depth = 0; + if (!success) goto bad; - JS_ASSERT((uintptr_t(he->value) & SHARP_BIT) == 0); + JS_ASSERT(!sharpid.isSharp); if (!idap) { JS_DestroyIdArray(cx, ida); ida = NULL; } } else { - JSHashNumber hash = js_hash_object(obj); - JSHashEntry **hep = JS_HashTableRawLookup(table, hash, obj); - he = *hep; - /* * It's possible that the value of a property has changed from the * first time the object's properties are traversed (when the property @@ -354,24 +334,20 @@ js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, bool *alread * converted to strings), i.e., the JSObject::getProperty() call is not * idempotent. */ - if (!he) { - he = JS_HashTableRawAdd(table, hep, hash, obj, NULL); - if (!he) { - JS_ReportOutOfMemory(cx); + p = map->table.lookup(obj); + if (!p) { + if (!map->table.put(obj, sharpid)) goto bad; - } - sharpid = 0; goto out; } + sharpid = p->value; } - sharpid = uintptr_t(he->value); - if (sharpid != 0) + if (sharpid.isSharp || sharpid.hasGen) *alreadySeen = true; out: - JS_ASSERT(he); - if ((sharpid & SHARP_BIT) == 0) { + if (!sharpid.isSharp) { if (idap && !ida) { ida = JS_Enumerate(cx, obj); if (!ida) @@ -382,17 +358,17 @@ out: if (idap) *idap = ida; - return he; + *isSharp = sharpid.isSharp; + return true; bad: /* Clean up the sharpObjectMap table on outermost error. */ if (map->depth == 0) { JS_UNKEEP_ATOMS(cx->runtime); map->sharpgen = 0; - JS_HashTableDestroy(map->table); - map->table = NULL; + map->table.clear(); } - return NULL; + return false; } void @@ -403,8 +379,7 @@ js_LeaveSharpObject(JSContext *cx, JSIdArray **idap) if (--map->depth == 0) { JS_UNKEEP_ATOMS(cx->runtime); map->sharpgen = 0; - JS_HashTableDestroy(map->table); - map->table = NULL; + map->table.clear(); } if (idap) { if (JSIdArray *ida = *idap) { @@ -414,18 +389,10 @@ js_LeaveSharpObject(JSContext *cx, JSIdArray **idap) } } -static intN -gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg) -{ - MarkObjectRoot((JSTracer *)arg, (JSObject *)he->key, "sharp table entry"); - return JS_DHASH_NEXT; -} - void js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map) { JS_ASSERT(map->depth > 0); - JS_ASSERT(map->table); /* * During recursive calls to MarkSharpObjects a non-native object or @@ -447,7 +414,11 @@ js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map) * with otherwise unreachable objects. But this is way too complex * to justify spending efforts. */ - JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, trc); + for (JSSharpTable::Range r = map->table.all(); !r.empty(); r.popFront()) { + JSObject *tmp = r.front().key; + MarkObjectRoot(trc, &tmp, "sharp table entry"); + JS_ASSERT(tmp == r.front().key); + } } #if JS_HAS_TOSOURCE @@ -475,8 +446,8 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp) JSIdArray *ida; bool alreadySeen = false; - JSHashEntry *he = js_EnterSharpObject(cx, obj, &ida, &alreadySeen); - if (!he) + bool isSharp = false; + if (!js_EnterSharpObject(cx, obj, &ida, &alreadySeen, &isSharp)) return false; if (!ida) { @@ -491,10 +462,14 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp) vp->setString(str); return true; } - JS_ASSERT(!IS_SHARP(he)); - if (alreadySeen) - MAKE_SHARP(he); + JS_ASSERT(!isSharp); + if (alreadySeen) { + JSSharpTable::Ptr p = cx->sharpObjectMap.table.lookup(obj); + JS_ASSERT(p); + JS_ASSERT(!p->value.isSharp); + p->value.isSharp = true; + } /* Automatically call js_LeaveSharpObject when we leave this frame. */ class AutoLeaveSharpObject { @@ -537,7 +512,6 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp) JSString *s = ToString(cx, IdToValue(id)); if (!s || !(idstr = s->ensureLinear(cx))) return false; - vp->setString(idstr); /* local root */ int valcnt = 0; if (prop) { @@ -576,7 +550,6 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp) s = js_QuoteString(cx, idstr, jschar('\'')); if (!s || !(idstr = s->ensureLinear(cx))) return false; - vp->setString(idstr); /* local root */ } for (int j = 0; j < valcnt; j++) { @@ -2790,6 +2763,13 @@ NewObject(JSContext *cx, Class *clasp, types::TypeObject *type, JSObject *parent if (!obj) return NULL; + /* + * This will cancel an already-running incremental GC from doing any more + * slices, and it will prevent any future incremental GCs. + */ + if (clasp->trace && !(clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS)) + cx->runtime->gcIncrementalEnabled = false; + Probes::createObject(cx, obj); return obj; } @@ -3502,7 +3482,7 @@ JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved & a->slots = reserved.newaslots; a->initSlotRange(0, reserved.bvals.begin(), bcap); if (a->hasPrivate()) - a->setPrivate(bpriv); + a->initPrivate(bpriv); if (b->isNative()) b->shape_->setNumFixedSlots(reserved.newbfixed); @@ -3512,7 +3492,7 @@ JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved & b->slots = reserved.newbslots; b->initSlotRange(0, reserved.avals.begin(), acap); if (b->hasPrivate()) - b->setPrivate(apriv); + b->initPrivate(apriv); /* Make sure the destructor for reserved doesn't free the slots. */ reserved.newaslots = NULL; @@ -3525,6 +3505,11 @@ JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved & types::TypeObject::writeBarrierPost(a->type_, &a->type_); types::TypeObject::writeBarrierPost(b->type_, &b->type_); #endif + + if (a->inDictionaryMode()) + a->lastProperty()->listp = &a->shape_; + if (b->inDictionaryMode()) + b->lastProperty()->listp = &b->shape_; } /* @@ -3595,7 +3580,7 @@ DefineStandardSlot(JSContext *cx, JSObject *obj, JSProtoKey key, JSAtom *atom, const Shape *shape = obj->nativeLookup(cx, id); if (!shape) { uint32_t slot = 2 * JSProto_LIMIT + key; - SetReservedSlot(obj, slot, v); + obj->setReservedSlot(slot, v); if (!obj->addProperty(cx, id, JS_PropertyStub, JS_StrictPropertyStub, slot, attrs, 0, 0)) return false; AddTypePropertyId(cx, obj, id, v); @@ -3618,8 +3603,8 @@ SetClassObject(JSObject *obj, JSProtoKey key, JSObject *cobj, JSObject *proto) if (!obj->isGlobal()) return; - SetReservedSlot(obj, key, ObjectOrNullValue(cobj)); - SetReservedSlot(obj, JSProto_LIMIT + key, ObjectOrNullValue(proto)); + obj->setReservedSlot(key, ObjectOrNullValue(cobj)); + obj->setReservedSlot(JSProto_LIMIT + key, ObjectOrNullValue(proto)); } static void @@ -5887,7 +5872,7 @@ DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp) &StringClass, ATOM_TO_JSID(cx->runtime->atomState.toStringAtom), js_str_toString)) { - *vp = obj->getPrimitiveThis(); + *vp = StringValue(obj->asString().unbox()); return true; } @@ -5910,7 +5895,9 @@ DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp) ClassMethodIsNative(cx, obj, &NumberClass, ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom), js_num_valueOf))) { - *vp = obj->getPrimitiveThis(); + *vp = obj->isString() + ? StringValue(obj->asString().unbox()) + : NumberValue(obj->asNumber().unbox()); return true; } diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 3f9a322e581..fa43153b695 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -868,7 +868,7 @@ struct JSObject : js::gc::Cell return type_; } - const js::HeapPtr &typeFromGC() const { + js::HeapPtr &typeFromGC() { /* Direct field access for use by GC. */ return type_; } @@ -954,6 +954,7 @@ struct JSObject : js::gc::Cell inline bool hasPrivate() const; inline void *getPrivate() const; inline void setPrivate(void *data); + inline void initPrivate(void *data); /* Access private data for an object with a known number of fixed slots. */ inline void *getPrivate(size_t nfixed) const; @@ -998,22 +999,6 @@ struct JSObject : js::gc::Cell bool isSealed(JSContext *cx, bool *resultp) { return isSealedOrFrozen(cx, SEAL, resultp); } bool isFrozen(JSContext *cx, bool *resultp) { return isSealedOrFrozen(cx, FREEZE, resultp); } - /* - * Primitive-specific getters and setters. - */ - - private: - static const uint32_t JSSLOT_PRIMITIVE_THIS = 0; - - public: - inline const js::Value &getPrimitiveThis() const; - inline void setPrimitiveThis(const js::Value &pthis); - - static size_t getPrimitiveThisOffset() { - /* All primitive objects have their value in a fixed slot. */ - return getFixedSlotOffset(JSSLOT_PRIMITIVE_THIS); - } - /* Accessors for elements. */ js::ObjectElements *getElementsHeader() const { @@ -1365,14 +1350,13 @@ struct JSObject : js::gc::Cell static bool thisObject(JSContext *cx, const js::Value &v, js::Value *vp); - inline JSObject *getThrowTypeError() const; - bool swap(JSContext *cx, JSObject *other); inline void initArrayClass(); static inline void writeBarrierPre(JSObject *obj); static inline void writeBarrierPost(JSObject *obj, void *addr); + static inline void readBarrier(JSObject *obj); inline void privateWriteBarrierPre(void **oldval); inline void privateWriteBarrierPost(void **oldval); @@ -1554,13 +1538,8 @@ class ValueArray { }; /* For manipulating JSContext::sharpObjectMap. */ -#define SHARP_BIT ((jsatomid) 1) -#define SHARP_ID_SHIFT 2 -#define IS_SHARP(he) (uintptr_t((he)->value) & SHARP_BIT) -#define MAKE_SHARP(he) ((he)->value = (void *) (uintptr_t((he)->value)|SHARP_BIT)) - -extern JSHashEntry * -js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, bool *alreadySeen); +extern bool +js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, bool *alreadySeen, bool *isSharp); extern void js_LeaveSharpObject(JSContext *cx, JSIdArray **idap); diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 395d06a5e3d..2455377c464 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -65,8 +65,12 @@ #include "gc/Barrier.h" #include "js/TemplateLib.h" + +#include "vm/BooleanObject.h" #include "vm/GlobalObject.h" +#include "vm/NumberObject.h" #include "vm/RegExpStatics.h" +#include "vm/StringObject.h" #include "jsatominlines.h" #include "jsfuninlines.h" @@ -115,6 +119,12 @@ JSObject::setPrivate(void *data) privateWriteBarrierPost(pprivate); } +inline void +JSObject::initPrivate(void *data) +{ + privateRef(numFixedSlots()) = data; +} + inline bool JSObject::enumerate(JSContext *cx, JSIterateOp iterop, js::Value *statep, jsid *idp) { @@ -436,20 +446,6 @@ JSObject::setReservedSlot(uintN index, const js::Value &v) setSlot(index, v); } -inline const js::Value & -JSObject::getPrimitiveThis() const -{ - JS_ASSERT(isPrimitive()); - return getFixedSlot(JSSLOT_PRIMITIVE_THIS); -} - -inline void -JSObject::setPrimitiveThis(const js::Value &pthis) -{ - JS_ASSERT(isPrimitive()); - setFixedSlot(JSSLOT_PRIMITIVE_THIS, pthis); -} - inline bool JSObject::hasContiguousSlots(size_t start, size_t count) const { @@ -612,20 +608,32 @@ JSObject::moveDenseArrayElements(uintN dstStart, uintN srcStart, uintN count) JS_ASSERT(srcStart + count <= getDenseArrayInitializedLength()); /* - * Use a custom write barrier here since it's performance sensitive. We - * only want to barrier the elements that are being overwritten. - */ - uintN markStart, markEnd; - if (dstStart > srcStart) { - markStart = js::Max(srcStart + count, dstStart); - markEnd = dstStart + count; + * Using memmove here would skip write barriers. Also, we need to consider + * an array containing [A, B, C], in the following situation: + * + * 1. Incremental GC marks slot 0 of array (i.e., A), then returns to JS code. + * 2. JS code moves slots 1..2 into slots 0..1, so it contains [B, C, C]. + * 3. Incremental GC finishes by marking slots 1 and 2 (i.e., C). + * + * Since normal marking never happens on B, it is very important that the + * write barrier is invoked here on B, despite the fact that it exists in + * the array before and after the move. + */ + if (compartment()->needsBarrier()) { + if (dstStart < srcStart) { + js::HeapValue *dst = elements + dstStart; + js::HeapValue *src = elements + srcStart; + for (unsigned i = 0; i < count; i++, dst++, src++) + *dst = *src; + } else { + js::HeapValue *dst = elements + dstStart + count - 1; + js::HeapValue *src = elements + srcStart + count - 1; + for (unsigned i = 0; i < count; i++, dst--, src--) + *dst = *src; + } } else { - markStart = dstStart; - markEnd = js::Min(dstStart + count, srcStart); + memmove(elements + dstStart, elements + srcStart, count * sizeof(js::Value)); } - prepareElementRangeForOverwrite(markStart, markEnd); - - memmove(elements + dstStart, elements + srcStart, count * sizeof(js::Value)); } inline void @@ -1916,6 +1924,7 @@ class PrimitiveBehavior { public: static inline bool isType(const Value &v) { return v.isString(); } static inline JSString *extract(const Value &v) { return v.toString(); } + static inline JSString *extract(JSObject &obj) { return obj.asString().unbox(); } static inline Class *getClass() { return &StringClass; } }; @@ -1924,6 +1933,7 @@ class PrimitiveBehavior { public: static inline bool isType(const Value &v) { return v.isBoolean(); } static inline bool extract(const Value &v) { return v.toBoolean(); } + static inline bool extract(JSObject &obj) { return obj.asBoolean().unbox(); } static inline Class *getClass() { return &BooleanClass; } }; @@ -1932,6 +1942,7 @@ class PrimitiveBehavior { public: static inline bool isType(const Value &v) { return v.isNumber(); } static inline double extract(const Value &v) { return v.toNumber(); } + static inline double extract(JSObject &obj) { return obj.asNumber().unbox(); } static inline Class *getClass() { return &NumberClass; } }; @@ -1968,7 +1979,7 @@ BoxedPrimitiveMethodGuard(JSContext *cx, CallArgs args, Native native, T *v, boo if (!NonGenericMethodGuard(cx, args, native, Behavior::getClass(), ok)) return false; - *v = Behavior::extract(thisv.toObject().getPrimitiveThis()); + *v = Behavior::extract(thisv.toObject()); return true; } @@ -2133,6 +2144,18 @@ JSObject::writeBarrierPre(JSObject *obj) #endif } +inline void +JSObject::readBarrier(JSObject *obj) +{ +#ifdef JSGC_INCREMENTAL + JSCompartment *comp = obj->compartment(); + if (comp->needsBarrier()) { + JS_ASSERT(!comp->rt->gcRunning); + MarkObjectUnbarriered(comp->barrierTracer(), obj, "read barrier"); + } +#endif +} + inline void JSObject::writeBarrierPost(JSObject *obj, void *addr) { diff --git a/js/src/jspropertycache.cpp b/js/src/jspropertycache.cpp index 5f76e3cbae1..3548f2ef6f7 100644 --- a/js/src/jspropertycache.cpp +++ b/js/src/jspropertycache.cpp @@ -282,7 +282,7 @@ PropertyCache::purge(JSContext *cx) #ifdef JS_THREADSAFE fprintf(fp, "thread %lu, ", (unsigned long) cx->thread->id); #endif - fprintf(fp, "GC %u\n", cx->runtime->gcNumber); + fprintf(fp, "GC %lu\n", (unsigned long)cx->runtime->gcNumber); # define P(mem) fprintf(fp, "%11s %10lu\n", #mem, (unsigned long)mem) P(fills); diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 599c125ded6..f0ddde305fc 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -57,7 +57,7 @@ using namespace js; using namespace js::gc; -static inline const HeapValue & +static inline HeapValue & GetCall(JSObject *proxy) { JS_ASSERT(IsFunctionProxy(proxy)); @@ -72,7 +72,7 @@ GetConstruct(JSObject *proxy) return proxy->getSlot(JSSLOT_PROXY_CONSTRUCT); } -static inline const HeapValue & +static inline HeapValue & GetFunctionProxyConstruct(JSObject *proxy) { JS_ASSERT(IsFunctionProxy(proxy)); @@ -92,6 +92,9 @@ OperationInProgress(JSContext *cx, JSObject *proxy) return false; } +static bool +FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp); + ProxyHandler::ProxyHandler(void *family) : mFamily(family) { } @@ -1243,12 +1246,12 @@ static void proxy_TraceObject(JSTracer *trc, JSObject *obj) { GetProxyHandler(obj)->trace(trc, obj); - MarkCrossCompartmentValue(trc, obj->getReservedSlotRef(JSSLOT_PROXY_PRIVATE), "private"); - MarkCrossCompartmentValue(trc, obj->getReservedSlotRef(JSSLOT_PROXY_EXTRA + 0), "extra0"); - MarkCrossCompartmentValue(trc, obj->getReservedSlotRef(JSSLOT_PROXY_EXTRA + 1), "extra1"); + MarkCrossCompartmentValue(trc, &obj->getReservedSlotRef(JSSLOT_PROXY_PRIVATE), "private"); + MarkCrossCompartmentValue(trc, &obj->getReservedSlotRef(JSSLOT_PROXY_EXTRA + 0), "extra0"); + MarkCrossCompartmentValue(trc, &obj->getReservedSlotRef(JSSLOT_PROXY_EXTRA + 1), "extra1"); if (IsFunctionProxy(obj)) { - MarkCrossCompartmentValue(trc, GetCall(obj), "call"); - MarkCrossCompartmentValue(trc, GetFunctionProxyConstruct(obj), "construct"); + MarkCrossCompartmentValue(trc, &GetCall(obj), "call"); + MarkCrossCompartmentValue(trc, &GetFunctionProxyConstruct(obj), "construct"); } } @@ -1256,8 +1259,8 @@ static void proxy_TraceFunction(JSTracer *trc, JSObject *obj) { proxy_TraceObject(trc, obj); - MarkCrossCompartmentValue(trc, GetCall(obj), "call"); - MarkCrossCompartmentValue(trc, GetFunctionProxyConstruct(obj), "construct"); + MarkCrossCompartmentValue(trc, &GetCall(obj), "call"); + MarkCrossCompartmentValue(trc, &GetFunctionProxyConstruct(obj), "construct"); } static JSBool @@ -1308,7 +1311,7 @@ proxy_TypeOf(JSContext *cx, JSObject *proxy) JS_FRIEND_DATA(Class) js::ObjectProxyClass = { "Proxy", - Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(4), + Class::NON_NATIVE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(4), JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* getProperty */ @@ -1364,7 +1367,7 @@ JS_FRIEND_DATA(Class) js::ObjectProxyClass = { JS_FRIEND_DATA(Class) js::OuterWindowProxyClass = { "Proxy", - Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(4), + Class::NON_NATIVE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(4), JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* getProperty */ @@ -1442,7 +1445,7 @@ proxy_Construct(JSContext *cx, uintN argc, Value *vp) JS_FRIEND_DATA(Class) js::FunctionProxyClass = { "Proxy", - Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(6), + Class::NON_NATIVE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(6), JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* getProperty */ @@ -1735,8 +1738,8 @@ Class js::CallableObjectClass = { callable_Construct, }; -JS_FRIEND_API(JSBool) -js::FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp) +static bool +FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp) { if (OperationInProgress(cx, proxy)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROXY_FIX); diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index baf5a4e86cb..4a37f634543 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -183,13 +183,6 @@ GetProxyPrivate(const JSObject *obj) return GetReservedSlot(obj, JSSLOT_PROXY_PRIVATE); } -inline void -SetProxyPrivate(JSObject *obj, const Value &priv) -{ - JS_ASSERT(IsProxy(obj)); - SetReservedSlot(obj, JSSLOT_PROXY_PRIVATE, priv); -} - inline const Value & GetProxyExtra(const JSObject *obj, size_t n) { @@ -210,9 +203,6 @@ NewProxyObject(JSContext *cx, ProxyHandler *handler, const Value &priv, JSObject *proto, JSObject *parent, JSObject *call = NULL, JSObject *construct = NULL); -JS_FRIEND_API(JSBool) -FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp); - } /* namespace js */ JS_BEGIN_EXTERN_C diff --git a/js/src/jsscope.cpp b/js/src/jsscope.cpp index d4ca4446d40..7d77726aff0 100644 --- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -1062,7 +1062,7 @@ JSObject::replaceWithNewEquivalentShape(JSContext *cx, Shape *oldShape, Shape *n { JS_ASSERT_IF(oldShape != lastProperty(), inDictionaryMode() && - nativeLookup(cx, oldShape->maybePropid()) == oldShape); + nativeLookup(cx, oldShape->propidRef()) == oldShape); JSObject *self = this; @@ -1086,7 +1086,7 @@ JSObject::replaceWithNewEquivalentShape(JSContext *cx, Shape *oldShape, Shape *n PropertyTable &table = self->lastProperty()->table(); Shape **spp = oldShape->isEmptyShape() ? NULL - : table.search(oldShape->maybePropid(), false); + : table.search(oldShape->propidRef(), false); /* * Splice the new shape into the same position as the old shape, preserving diff --git a/js/src/jsscope.h b/js/src/jsscope.h index 025ef99f5f0..6d608ae810e 100644 --- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -562,6 +562,10 @@ struct Shape : public js::gc::Cell return parent; } + HeapPtrShape &previousRef() { + return parent; + } + class Range { protected: friend struct Shape; @@ -770,8 +774,12 @@ struct Shape : public js::gc::Cell slotInfo = slotInfo | ((count + 1) << LINEAR_SEARCHES_SHIFT); } - jsid propid() const { JS_ASSERT(!isEmptyShape()); return maybePropid(); } - const HeapId &maybePropid() const { JS_ASSERT(!JSID_IS_VOID(propid_)); return propid_; } + const HeapId &propid() const { + JS_ASSERT(!isEmptyShape()); + JS_ASSERT(!JSID_IS_VOID(propid_)); + return propid_; + } + HeapId &propidRef() { JS_ASSERT(!JSID_IS_VOID(propid_)); return propid_; } int16_t shortid() const { JS_ASSERT(hasShortID()); return maybeShortid(); } int16_t maybeShortid() const { return shortid_; } @@ -995,7 +1003,7 @@ struct StackShape StackShape(const Shape *shape) : base(shape->base()->unowned()), - propid(shape->maybePropid()), + propid(const_cast(shape)->propidRef()), slot_(shape->slotInfo & Shape::SLOT_MASK), attrs(shape->attrs), flags(shape->flags), @@ -1081,7 +1089,7 @@ Shape::search(JSContext *cx, Shape *start, jsid id, Shape ***pspp, bool adding) return SHAPE_FETCH(spp); } } - /* + /* * No table built -- there weren't enough entries, or OOM occurred. * Don't increment numLinearSearches, to keep hasTable() false. */ @@ -1091,7 +1099,7 @@ Shape::search(JSContext *cx, Shape *start, jsid id, Shape ***pspp, bool adding) } for (Shape *shape = start; shape; shape = shape->parent) { - if (shape->maybePropid() == id) + if (shape->propidRef() == id) return shape; } diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index a7b8ca0e871..71076a571bb 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -319,7 +319,7 @@ void Bindings::trace(JSTracer *trc) { if (lastBinding) - MarkShape(trc, lastBinding, "shape"); + MarkShape(trc, &lastBinding, "shape"); } #ifdef JS_CRASH_DIAGNOSTICS @@ -1903,6 +1903,6 @@ JSScript::markTrapClosures(JSTracer *trc) for (unsigned i = 0; i < length; i++) { BreakpointSite *site = debug->breakpoints[i]; if (site && site->trapHandler) - MarkValue(trc, site->trapClosure, "trap closure"); + MarkValue(trc, &site->trapClosure, "trap closure"); } } diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index a877b259ecf..bb8af6008d5 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -399,7 +399,7 @@ static const uintN STRING_ELEMENT_ATTRS = JSPROP_ENUMERATE | JSPROP_READONLY | J static JSBool str_enumerate(JSContext *cx, JSObject *obj) { - JSString *str = obj->getPrimitiveThis().toString(); + JSString *str = obj->asString().unbox(); for (size_t i = 0, length = str->length(); i < length; i++) { JSString *str1 = js_NewDependentString(cx, str, i, 1); if (!str1) @@ -421,7 +421,7 @@ str_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, if (!JSID_IS_INT(id)) return JS_TRUE; - JSString *str = obj->getPrimitiveThis().toString(); + JSString *str = obj->asString().unbox(); jsint slot = JSID_TO_INT(id); if ((size_t)slot < str->length()) { @@ -472,8 +472,9 @@ ThisToStringForStringProto(JSContext *cx, CallReceiver call) ATOM_TO_JSID(cx->runtime->atomState.toStringAtom), js_str_toString)) { - call.thisv() = obj->getPrimitiveThis(); - return call.thisv().toString(); + JSString *str = obj->asString().unbox(); + call.thisv().setString(str); + return str; } } else if (call.thisv().isNullOrUndefined()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CONVERT_TO, diff --git a/js/src/jstypedarray.cpp b/js/src/jstypedarray.cpp index f3ac1049072..166b3ff8abc 100644 --- a/js/src/jstypedarray.cpp +++ b/js/src/jstypedarray.cpp @@ -1097,7 +1097,7 @@ class TypedArrayTemplate static void obj_trace(JSTracer *trc, JSObject *obj) { - MarkValue(trc, obj->getFixedSlotRef(FIELD_BUFFER), "typedarray.buffer"); + MarkValue(trc, &obj->getFixedSlotRef(FIELD_BUFFER), "typedarray.buffer"); } static JSBool @@ -2180,6 +2180,7 @@ Class ArrayBuffer::slowClass = { Class js::ArrayBufferClass = { "ArrayBuffer", JSCLASS_HAS_PRIVATE | + JSCLASS_IMPLEMENTS_BARRIERS | Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(ARRAYBUFFER_RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer), @@ -2298,7 +2299,7 @@ JSFunctionSpec _typedArray::jsfuncs[] = { \ { \ #_typedArray, \ JSCLASS_HAS_RESERVED_SLOTS(TypedArray::FIELD_MAX) | \ - JSCLASS_HAS_PRIVATE | \ + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | \ JSCLASS_FOR_OF_ITERATION | \ Class::NON_NATIVE, \ JS_PropertyStub, /* addProperty */ \ diff --git a/js/src/jswatchpoint.cpp b/js/src/jswatchpoint.cpp index 21e9f8f7100..a37a2eb8344 100644 --- a/js/src/jswatchpoint.cpp +++ b/js/src/jswatchpoint.cpp @@ -202,16 +202,20 @@ WatchpointMap::markIteratively(JSTracer *trc) bool objectIsLive = !IsAboutToBeFinalized(e.key.object); if (objectIsLive || e.value.held) { if (!objectIsLive) { - MarkObject(trc, e.key.object, "held Watchpoint object"); + HeapPtrObject tmp(e.key.object); + MarkObject(trc, &tmp, "held Watchpoint object"); + JS_ASSERT(tmp == e.key.object); marked = true; } const HeapId &id = e.key.id; JS_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id)); - MarkId(trc, id, "WatchKey::id"); + HeapId tmp(id.get()); + MarkId(trc, &tmp, "WatchKey::id"); + JS_ASSERT(tmp.get() == id.get()); if (e.value.closure && IsAboutToBeFinalized(e.value.closure)) { - MarkObject(trc, e.value.closure, "Watchpoint::closure"); + MarkObject(trc, &e.value.closure, "Watchpoint::closure"); marked = true; } } @@ -224,13 +228,17 @@ WatchpointMap::markAll(JSTracer *trc) { for (Map::Range r = map.all(); !r.empty(); r.popFront()) { Map::Entry &e = r.front(); - MarkObject(trc, e.key.object, "held Watchpoint object"); + HeapPtrObject tmpObj(e.key.object); + MarkObject(trc, &tmpObj, "held Watchpoint object"); + JS_ASSERT(tmpObj == e.key.object); const HeapId &id = e.key.id; JS_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id)); - MarkId(trc, id, "WatchKey::id"); + HeapId tmpId(id.get()); + MarkId(trc, &tmpId, "WatchKey::id"); + JS_ASSERT(tmpId.get() == id.get()); - MarkObject(trc, e.value.closure, "Watchpoint::closure"); + MarkObject(trc, &e.value.closure, "Watchpoint::closure"); } } diff --git a/js/src/jsweakmap.cpp b/js/src/jsweakmap.cpp index d757d8e7b87..a5e3a3aff51 100644 --- a/js/src/jsweakmap.cpp +++ b/js/src/jsweakmap.cpp @@ -62,7 +62,7 @@ bool WeakMapBase::markAllIteratively(JSTracer *tracer) { bool markedAny = false; - JSRuntime *rt = tracer->context->runtime; + JSRuntime *rt = tracer->runtime; for (WeakMapBase *m = rt->gcWeakMapList; m; m = m->next) { if (m->markIteratively(tracer)) markedAny = true; @@ -73,7 +73,7 @@ WeakMapBase::markAllIteratively(JSTracer *tracer) void WeakMapBase::sweepAll(JSTracer *tracer) { - JSRuntime *rt = tracer->context->runtime; + JSRuntime *rt = tracer->runtime; for (WeakMapBase *m = rt->gcWeakMapList; m; m = m->next) m->sweep(tracer); } @@ -112,13 +112,23 @@ GetObjectMap(JSObject *obj) } static JSObject * -NonNullObject(JSContext *cx, Value *vp) +GetKeyArg(JSContext *cx, CallArgs &args) { + Value *vp = &args[0]; if (vp->isPrimitive()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT); return NULL; } - return &vp->toObject(); + JSObject *key = &vp->toObject(); + if (!key) + return NULL; + + // If the key is from another compartment, and we store the wrapper as the key + // the wrapper might be GC-ed since it is not strong referenced (Bug 673468). + // To avoid this we always use the unwrapped object as the key instead of its + // security wrapper. This also means that if the keys are ever exposed they must + // be re-wrapped (see: JS_NondeterministicGetWeakMapKeys). + return JS_UnwrapObject(key); } static JSBool @@ -136,9 +146,10 @@ WeakMap_has(JSContext *cx, uintN argc, Value *vp) "WeakMap.has", "0", "s"); return false; } - JSObject *key = NonNullObject(cx, &args[0]); + JSObject *key = GetKeyArg(cx, args); if (!key) return false; + ObjectValueMap *map = GetObjectMap(obj); if (map) { ObjectValueMap::Ptr ptr = map->lookup(key); @@ -167,9 +178,10 @@ WeakMap_get(JSContext *cx, uintN argc, Value *vp) "WeakMap.get", "0", "s"); return false; } - JSObject *key = NonNullObject(cx, &args[0]); + JSObject *key = GetKeyArg(cx, args); if (!key) return false; + ObjectValueMap *map = GetObjectMap(obj); if (map) { ObjectValueMap::Ptr ptr = map->lookup(key); @@ -198,9 +210,10 @@ WeakMap_delete(JSContext *cx, uintN argc, Value *vp) "WeakMap.delete", "0", "s"); return false; } - JSObject *key = NonNullObject(cx, &args[0]); + JSObject *key = GetKeyArg(cx, args); if (!key) return false; + ObjectValueMap *map = GetObjectMap(obj); if (map) { ObjectValueMap::Ptr ptr = map->lookup(key); @@ -230,9 +243,10 @@ WeakMap_set(JSContext *cx, uintN argc, Value *vp) "WeakMap.set", "0", "s"); return false; } - JSObject *key = NonNullObject(cx, &args[0]); + JSObject *key = GetKeyArg(cx, args); if (!key) return false; + Value value = (args.length() > 1) ? args[1] : UndefinedValue(); ObjectValueMap *map = GetObjectMap(obj); @@ -277,7 +291,12 @@ JS_NondeterministicGetWeakMapKeys(JSContext *cx, JSObject *obj, JSObject **ret) ObjectValueMap *map = GetObjectMap(obj); if (map) { for (ObjectValueMap::Range r = map->nondeterministicAll(); !r.empty(); r.popFront()) { - if (!js_NewbornArrayPush(cx, arr, ObjectValue(*r.front().key))) + JSObject *key = r.front().key; + // Re-wrapping the key (see comment of GetKeyArg) + if (!JS_WrapObject(cx, &key)) + return false; + + if (!js_NewbornArrayPush(cx, arr, ObjectValue(*key))) return false; } } @@ -295,8 +314,16 @@ WeakMap_mark(JSTracer *trc, JSObject *obj) static void WeakMap_finalize(JSContext *cx, JSObject *obj) { - ObjectValueMap *map = GetObjectMap(obj); - cx->delete_(map); + if (ObjectValueMap *map = GetObjectMap(obj)) { + map->check(); +#ifdef DEBUG + map->~ObjectValueMap(); + memset(map, 0xdc, sizeof(ObjectValueMap)); + cx->free_(map); +#else + cx->delete_(map); +#endif + } } static JSBool @@ -312,7 +339,7 @@ WeakMap_construct(JSContext *cx, uintN argc, Value *vp) Class js::WeakMapClass = { "WeakMap", - JSCLASS_HAS_PRIVATE | + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap), JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* delProperty */ diff --git a/js/src/jsweakmap.h b/js/src/jsweakmap.h index 9494686eee4..9c1aa316bc5 100644 --- a/js/src/jsweakmap.h +++ b/js/src/jsweakmap.h @@ -91,7 +91,7 @@ namespace js { // bool isMarked(const Type &x) // Return true if x has been marked as live by the garbage collector. // -// bool mark(const Type &x) +// bool mark(Type &x) // Return false if x is already marked. Otherwise, mark x and return true. // // If omitted, the MarkPolicy parameter defaults to js::DefaultMarkPolicy, @@ -127,7 +127,7 @@ class WeakMapBase { // Add ourselves to the list if we are not already in the list. We can already // be in the list if the weak map is marked more than once due delayed marking. if (next == WeakMapNotInList) { - JSRuntime *rt = tracer->context->runtime; + JSRuntime *rt = tracer->runtime; next = rt->gcWeakMapList; rt->gcWeakMapList = this; } @@ -156,6 +156,8 @@ class WeakMapBase { // Trace all delayed weak map bindings. Used by the cycle collector. static void traceAllMappings(WeakMapTracer *tracer); + void check() { JS_ASSERT(next == WeakMapNotInList); } + // Remove everything from the live weak map list. static void resetWeakMapList(JSRuntime *rt); @@ -204,7 +206,7 @@ class WeakMap : public HashMap, publ void nonMarkingTrace(JSTracer *trc) { ValueMarkPolicy vp(trc); for (Range r = Base::all(); !r.empty(); r.popFront()) - vp.mark(r.front().value); + vp.mark(&r.front().value); } bool markIteratively(JSTracer *trc) { @@ -213,10 +215,10 @@ class WeakMap : public HashMap, publ bool markedAny = false; for (Range r = Base::all(); !r.empty(); r.popFront()) { const Key &k = r.front().key; - const Value &v = r.front().value; + Value &v = r.front().value; /* If the entry is live, ensure its key and value are marked. */ if (kp.isMarked(k)) { - markedAny |= vp.mark(v); + markedAny |= vp.mark(&v); } JS_ASSERT_IF(kp.isMarked(k), vp.isMarked(v)); } @@ -264,8 +266,8 @@ class DefaultMarkPolicy { return !IsAboutToBeFinalized(x); return true; } - bool mark(const HeapValue &x) { - if (isMarked(x)) + bool mark(HeapValue *x) { + if (isMarked(*x)) return false; js::gc::MarkValue(tracer, x, "WeakMap entry"); return true; @@ -281,8 +283,8 @@ class DefaultMarkPolicy { bool isMarked(const HeapPtrObject &x) { return !IsAboutToBeFinalized(x); } - bool mark(const HeapPtrObject &x) { - if (isMarked(x)) + bool mark(HeapPtrObject *x) { + if (isMarked(*x)) return false; js::gc::MarkObject(tracer, x, "WeakMap entry"); return true; @@ -298,8 +300,8 @@ class DefaultMarkPolicy { bool isMarked(const HeapPtrScript &x) { return !IsAboutToBeFinalized(x); } - bool mark(const HeapPtrScript &x) { - if (isMarked(x)) + bool mark(HeapPtrScript *x) { + if (isMarked(*x)) return false; js::gc::MarkScript(tracer, x, "WeakMap entry"); return true; diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 1e0f5af650f..23b6be62064 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -367,7 +367,7 @@ Wrapper::iteratorNext(JSContext *cx, JSObject *wrapper, Value *vp) void Wrapper::trace(JSTracer *trc, JSObject *wrapper) { - MarkValue(trc, wrapper->getReservedSlotRef(JSSLOT_PROXY_PRIVATE), "wrappedObject"); + MarkValue(trc, &wrapper->getReservedSlotRef(JSSLOT_PROXY_PRIVATE), "wrappedObject"); } JSObject * @@ -875,7 +875,7 @@ CrossCompartmentWrapper::iteratorNext(JSContext *cx, JSObject *wrapper, Value *v void CrossCompartmentWrapper::trace(JSTracer *trc, JSObject *wrapper) { - MarkCrossCompartmentValue(trc, wrapper->getReservedSlotRef(JSSLOT_PROXY_PRIVATE), + MarkCrossCompartmentValue(trc, &wrapper->getReservedSlotRef(JSSLOT_PROXY_PRIVATE), "wrappedObject"); } diff --git a/js/src/jsxml.cpp b/js/src/jsxml.cpp index b3a98d6487d..92e09070e2d 100644 --- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -866,7 +866,7 @@ js_XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor *cursor) { for (; cursor; cursor = cursor->next) { if (cursor->root) - MarkXML(trc, (const HeapPtr &)cursor->root, "cursor_root"); + MarkXML(trc, &(HeapPtr &)cursor->root, "cursor_root"); } } @@ -875,7 +875,7 @@ js_XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor *cursor) { for (; cursor; cursor = cursor->next) { if (cursor->root) - MarkObject(trc, (const HeapPtr &)cursor->root, "cursor_root"); + MarkObject(trc, &(HeapPtr &)cursor->root, "cursor_root"); } } @@ -5369,7 +5369,7 @@ out: JS_FRIEND_DATA(Class) js::XMLClass = { js_XML_str, - JSCLASS_HAS_PRIVATE | + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_CACHED_PROTO(JSProto_XML), JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* delProperty */ @@ -7328,15 +7328,15 @@ void js_TraceXML(JSTracer *trc, JSXML *xml) { if (xml->object) - MarkObject(trc, xml->object, "object"); + MarkObject(trc, &xml->object, "object"); if (xml->name) - MarkObject(trc, xml->name, "name"); + MarkObject(trc, &xml->name, "name"); if (xml->parent) - MarkXML(trc, xml->parent, "xml_parent"); + MarkXML(trc, &xml->parent, "xml_parent"); if (JSXML_HAS_VALUE(xml)) { if (xml->xml_value) - MarkString(trc, xml->xml_value, "value"); + MarkString(trc, &xml->xml_value, "value"); return; } @@ -7345,9 +7345,9 @@ js_TraceXML(JSTracer *trc, JSXML *xml) if (xml->xml_class == JSXML_CLASS_LIST) { if (xml->xml_target) - MarkXML(trc, xml->xml_target, "target"); + MarkXML(trc, &xml->xml_target, "target"); if (xml->xml_targetprop) - MarkObject(trc, xml->xml_targetprop, "targetprop"); + MarkObject(trc, &xml->xml_targetprop, "targetprop"); } else { MarkObjectRange(trc, xml->xml_namespaces.length, xml->xml_namespaces.vector, @@ -7898,11 +7898,11 @@ xmlfilter_trace(JSTracer *trc, JSObject *obj) return; JS_ASSERT(filter->list); - MarkXML(trc, filter->list, "list"); + MarkXML(trc, &filter->list, "list"); if (filter->result) - MarkXML(trc, filter->result, "result"); + MarkXML(trc, &filter->result, "result"); if (filter->kid) - MarkXML(trc, filter->kid, "kid"); + MarkXML(trc, &filter->kid, "kid"); /* * We do not need to trace the cursor as that would be done when @@ -7922,7 +7922,7 @@ xmlfilter_finalize(JSContext *cx, JSObject *obj) Class js_XMLFilterClass = { "XMLFilter", - JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS, + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_IS_ANONYMOUS, JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* getProperty */ diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 774fc11616c..139943112df 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -177,6 +177,11 @@ mjit::Compiler::checkAnalysis(JSScript *script) return Compile_Abort; } + if (JSOp(*script->code) == JSOP_GENERATOR) { + JaegerSpew(JSpew_Abort, "script is a generator\n"); + return Compile_Abort; + } + if (!script->ensureRanAnalysis(cx, NULL)) return Compile_Error; if (cx->typeInferenceEnabled() && !script->ensureRanInference(cx)) @@ -3919,7 +3924,7 @@ void mjit::Compiler::interruptCheckHelper() { Jump jump; - if (cx->runtime->gcZeal() >= js::gc::ZealVerifierThreshold) { + if (cx->runtime->gcZeal() == js::gc::ZealVerifierValue) { /* For barrier verification, always take the interrupt so we can verify. */ jump = masm.jump(); } else { @@ -6887,7 +6892,9 @@ mjit::Compiler::jsop_regexp() !cx->typeInferenceEnabled() || analysis->localsAliasStack() || types::TypeSet::HasObjectFlags(cx, globalObj->getType(cx), - types::OBJECT_FLAG_REGEXP_FLAGS_SET)) { + types::OBJECT_FLAG_REGEXP_FLAGS_SET) || + cx->runtime->gcIncrementalState == gc::MARK) + { prepareStubCall(Uses(0)); masm.move(ImmPtr(obj), Registers::ArgReg1); INLINE_STUBCALL(stubs::RegExp, REJOIN_FALLTHROUGH); @@ -6941,10 +6948,11 @@ mjit::Compiler::jsop_regexp() } /* - * Force creation of the RegExpShared in the script's RegExpObject - * so that we grab it in the getNewObject template copy. Note that - * JIT code is discarded on every GC, which permits us to burn in - * the pointer to the RegExpShared. + * Force creation of the RegExpShared in the script's RegExpObject so that + * we grab it in the getNewObject template copy. Note that JIT code is + * discarded on every GC, which permits us to burn in the pointer to the + * RegExpShared. We don't do this during an incremental + * GC, since we don't discard JIT code after every marking slice. */ if (!reobj->getShared(cx)) return false; diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index 6cb7caa1aea..2708b152047 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -484,7 +484,7 @@ private: bool hasGlobalReallocation; bool oomInVector; // True if we have OOM'd appending to a vector. bool overflowICSpace; // True if we added a constant pool in a reserved space. - uint32_t gcNumber; + uint64_t gcNumber; enum { NoApplyTricks, LazyArgsObj } applyTricks; PCLengthEntry *pcLengths; diff --git a/js/src/methodjit/MethodJIT.h b/js/src/methodjit/MethodJIT.h index eb23d88ad1b..bdb7c890444 100644 --- a/js/src/methodjit/MethodJIT.h +++ b/js/src/methodjit/MethodJIT.h @@ -402,7 +402,7 @@ struct RecompilationMonitor unsigned frameExpansions; /* If a GC occurs it may discard jit code on the stack. */ - unsigned gcNumber; + uint64_t gcNumber; RecompilationMonitor(JSContext *cx) : cx(cx), diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index 3d06b51a205..0441bc16a5a 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -53,6 +53,7 @@ #include "jsautooplen.h" #include "vm/ScopeObject-inl.h" +#include "vm/StringObject-inl.h" #if defined JS_POLYIC @@ -101,7 +102,7 @@ class PICStubCompiler : public BaseCompiler JSScript *script; ic::PICInfo &pic; void *stub; - uint32_t gcNumber; + uint64_t gcNumber; public: bool canCallHook; @@ -944,7 +945,7 @@ class GetPropCompiler : public PICStubCompiler Jump notStringObj = masm.guardShape(pic.objReg, obj); - masm.loadPayload(Address(pic.objReg, JSObject::getPrimitiveThisOffset()), pic.objReg); + masm.loadPayload(Address(pic.objReg, StringObject::getPrimitiveValueOffset()), pic.objReg); masm.loadPtr(Address(pic.objReg, JSString::offsetOfLengthAndFlags()), pic.objReg); masm.urshift32(Imm32(JSString::LENGTH_SHIFT), pic.objReg); masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg); @@ -1885,7 +1886,7 @@ GetPropMaybeCached(VMFrame &f, ic::PICInfo *pic, bool cached) LookupStatus status = cc.generateStringObjLengthStub(); if (status == Lookup_Error) THROW(); - JSString *str = obj->getPrimitiveThis().toString(); + JSString *str = obj->asString().unbox(); f.regs.sp[-1].setInt32(str->length()); } return; diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index c9a8d113432..3645a642dca 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -878,7 +878,7 @@ stubs::DebuggerStatement(VMFrame &f, jsbytecode *pc) void JS_FASTCALL stubs::Interrupt(VMFrame &f, jsbytecode *pc) { - gc::VerifyBarriers(f.cx); + gc::MaybeVerifyBarriers(f.cx); if (!js_HandleExecutionInterrupt(f.cx)) THROW(); @@ -1963,7 +1963,7 @@ stubs::ConvertToTypedFloat(JSContext *cx, Value *vp) void JS_FASTCALL stubs::WriteBarrier(VMFrame &f, Value *addr) { - js::gc::MarkValueUnbarriered(f.cx->compartment->barrierTracer(), *addr, "write barrier"); + gc::MarkValueUnbarriered(f.cx->compartment->barrierTracer(), addr, "write barrier"); } void JS_FASTCALL @@ -1971,5 +1971,5 @@ stubs::GCThingWriteBarrier(VMFrame &f, Value *addr) { gc::Cell *cell = (gc::Cell *)addr->toGCThing(); if (cell && !cell->isMarked()) - gc::MarkValueUnbarriered(f.cx->compartment->barrierTracer(), *addr, "write barrier"); + gc::MarkValueUnbarriered(f.cx->compartment->barrierTracer(), addr, "write barrier"); } diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 214c4c12774..d93c5db1b37 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -1286,6 +1286,7 @@ static const struct ParamPair { {"maxMallocBytes", JSGC_MAX_MALLOC_BYTES}, {"gcBytes", JSGC_BYTES}, {"gcNumber", JSGC_NUMBER}, + {"sliceTimeBudget", JSGC_SLICE_TIME_BUDGET} }; static JSBool @@ -1427,6 +1428,35 @@ ScheduleGC(JSContext *cx, uintN argc, jsval *vp) *vp = JSVAL_VOID; return JS_TRUE; } + +static JSBool +VerifyBarriers(JSContext *cx, uintN argc, jsval *vp) +{ + gc::VerifyBarriers(cx); + *vp = JSVAL_VOID; + return JS_TRUE; +} + +static JSBool +GCSlice(JSContext *cx, uintN argc, jsval *vp) +{ + uint32_t budget; + + if (argc != 1) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, + (argc < 1) + ? JSSMSG_NOT_ENOUGH_ARGS + : JSSMSG_TOO_MANY_ARGS, + "gcslice"); + return JS_FALSE; + } + if (!JS_ValueToECMAUint32(cx, vp[2], &budget)) + return JS_FALSE; + + GCDebugSlice(cx, budget); + *vp = JSVAL_VOID; + return JS_TRUE; +} #endif /* JS_GC_ZEAL */ typedef struct JSCountHeapNode JSCountHeapNode; @@ -1473,7 +1503,7 @@ CountHeapNotify(JSTracer *trc, void **thingp, JSGCTraceKind kind) if (node) { countTracer->recycleList = node->next; } else { - node = (JSCountHeapNode *) JS_malloc(trc->context, sizeof *node); + node = (JSCountHeapNode *) js_malloc(sizeof *node); if (!node) { countTracer->ok = JS_FALSE; return; @@ -1575,7 +1605,7 @@ CountHeap(JSContext *cx, uintN argc, jsval *vp) } while ((node = countTracer.recycleList) != NULL) { countTracer.recycleList = node->next; - JS_free(cx, node); + js_free(node); } JS_DHashTableFinish(&countTracer.visited); @@ -4001,6 +4031,8 @@ static JSFunctionSpec shell_functions[] = { #ifdef JS_GC_ZEAL JS_FN("gczeal", GCZeal, 2,0), JS_FN("schedulegc", ScheduleGC, 1,0), + JS_FN("verifybarriers", VerifyBarriers, 0,0), + JS_FN("gcslice", GCSlice, 1,0), #endif JS_FN("internalConst", InternalConst, 1,0), JS_FN("setDebug", SetDebug, 1,0), @@ -4114,6 +4146,8 @@ static const char *const shell_help_messages[] = { " How zealous the garbage collector should be", "schedulegc(num, [compartmentGC?])\n" " Schedule a GC to happen after num allocations", +"verifybarriers() Start or end a run of the write barrier verifier", +"gcslice(n) Run an incremental GC slice that marks ~n objects", #endif "internalConst(name)\n" " Query an internal constant for the engine. See InternalConst source for the\n" @@ -5457,7 +5491,7 @@ main(int argc, char **argv, char **envp) if (!cx) return 1; - JS_SetGCParameter(rt, JSGC_MODE, JSGC_MODE_COMPARTMENT); + JS_SetGCParameter(rt, JSGC_MODE, JSGC_MODE_INCREMENTAL); JS_SetGCParameterForThread(cx, JSGC_MAX_CODE_CACHE_BYTES, 16 * 1024 * 1024); /* Must be done before creating the global object */ diff --git a/js/src/vm/BooleanObject.h b/js/src/vm/BooleanObject.h index c4d5551e26e..33cd4af068d 100644 --- a/js/src/vm/BooleanObject.h +++ b/js/src/vm/BooleanObject.h @@ -67,14 +67,13 @@ class BooleanObject : public JSObject */ static inline BooleanObject *createWithProto(JSContext *cx, bool b, JSObject &proto); - Value unbox() const { - JS_ASSERT(getSlot(PRIMITIVE_VALUE_SLOT).isBoolean()); - return getSlot(PRIMITIVE_VALUE_SLOT); + bool unbox() const { + return getFixedSlot(PRIMITIVE_VALUE_SLOT).toBoolean(); } private: inline void setPrimitiveValue(bool b) { - setSlot(PRIMITIVE_VALUE_SLOT, BooleanValue(b)); + setFixedSlot(PRIMITIVE_VALUE_SLOT, BooleanValue(b)); } /* For access to init, as Boolean.prototype is special. */ diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index d0e707c5537..07497632200 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -1063,15 +1063,21 @@ Debugger::markKeysInCompartment(JSTracer *tracer) const ObjectMap &objStorage = objects; for (ObjectMap::Range r = objStorage.all(); !r.empty(); r.popFront()) { const HeapPtrObject &key = r.front().key; - if (key->compartment() == comp && IsAboutToBeFinalized(key)) - gc::MarkObject(tracer, key, "cross-compartment WeakMap key"); + if (key->compartment() == comp && IsAboutToBeFinalized(key)) { + HeapPtrObject tmp(key); + gc::MarkObject(tracer, &tmp, "cross-compartment WeakMap key"); + JS_ASSERT(tmp == key); + } } const ObjectMap &envStorage = environments; for (ObjectMap::Range r = envStorage.all(); !r.empty(); r.popFront()) { const HeapPtrObject &key = r.front().key; - if (key->compartment() == comp && IsAboutToBeFinalized(key)) - js::gc::MarkObject(tracer, key, "cross-compartment WeakMap key"); + if (key->compartment() == comp && IsAboutToBeFinalized(key)) { + HeapPtrObject tmp(key); + js::gc::MarkObject(tracer, &tmp, "cross-compartment WeakMap key"); + JS_ASSERT(tmp == key); + } } typedef HashMap, RuntimeAllocPolicy> @@ -1079,8 +1085,11 @@ Debugger::markKeysInCompartment(JSTracer *tracer) const ScriptMap &scriptStorage = scripts; for (ScriptMap::Range r = scriptStorage.all(); !r.empty(); r.popFront()) { const HeapPtrScript &key = r.front().key; - if (key->compartment() == comp && IsAboutToBeFinalized(key)) - gc::MarkScript(tracer, key, "cross-compartment WeakMap key"); + if (key->compartment() == comp && IsAboutToBeFinalized(key)) { + HeapPtrScript tmp(key); + gc::MarkScript(tracer, &tmp, "cross-compartment WeakMap key"); + JS_ASSERT(tmp == key); + } } } @@ -1176,7 +1185,7 @@ Debugger::markAllIteratively(GCMarker *trc) * - it isn't already marked * - it actually has hooks that might be called */ - const HeapPtrObject &dbgobj = dbg->toJSObject(); + HeapPtrObject &dbgobj = dbg->toJSObjectRef(); if (comp && comp != dbgobj->compartment()) continue; @@ -1186,7 +1195,7 @@ Debugger::markAllIteratively(GCMarker *trc) * obj could be reachable only via its live, enabled * debugger hooks, which may yet be called. */ - MarkObject(trc, dbgobj, "enabled Debugger"); + MarkObject(trc, &dbgobj, "enabled Debugger"); markedAny = true; dbgMarked = true; } @@ -1199,9 +1208,8 @@ Debugger::markAllIteratively(GCMarker *trc) * The debugger and the script are both live. * Therefore the breakpoint handler is live. */ - const HeapPtrObject &handler = bp->getHandler(); - if (IsAboutToBeFinalized(handler)) { - MarkObject(trc, bp->getHandler(), "breakpoint handler"); + if (IsAboutToBeFinalized(bp->getHandler())) { + MarkObject(trc, &bp->getHandlerRef(), "breakpoint handler"); markedAny = true; } } @@ -1224,7 +1232,7 @@ void Debugger::trace(JSTracer *trc) { if (uncaughtExceptionHook) - MarkObject(trc, uncaughtExceptionHook, "hooks"); + MarkObject(trc, &uncaughtExceptionHook, "hooks"); /* * Mark Debugger.Frame objects. These are all reachable from JS, because the @@ -1235,9 +1243,9 @@ Debugger::trace(JSTracer *trc) * frames.) */ for (FrameMap::Range r = frames.all(); !r.empty(); r.popFront()) { - const HeapPtrObject &frameobj = r.front().value; + HeapPtrObject &frameobj = r.front().value; JS_ASSERT(frameobj->getPrivate()); - MarkObject(trc, frameobj, "live Debugger.Frame"); + MarkObject(trc, &frameobj, "live Debugger.Frame"); } /* Trace the weak map from JSScript instances to Debugger.Script objects. */ @@ -1315,7 +1323,9 @@ Debugger::finalize(JSContext *cx, JSObject *obj) } Class Debugger::jsclass = { - "Debugger", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT), + "Debugger", + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | + JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Debugger::finalize, NULL, /* reserved0 */ @@ -1846,7 +1856,9 @@ DebuggerScript_trace(JSTracer *trc, JSObject *obj) } Class DebuggerScript_class = { - "Script", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSCRIPT_COUNT), + "Script", + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | + JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSCRIPT_COUNT), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, NULL, /* reserved0 */ @@ -2948,7 +2960,9 @@ DebuggerObject_trace(JSTracer *trc, JSObject *obj) } Class DebuggerObject_class = { - "Object", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGOBJECT_COUNT), + "Object", + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | + JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGOBJECT_COUNT), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, NULL, /* reserved0 */ @@ -3590,7 +3604,9 @@ DebuggerEnv_trace(JSTracer *trc, JSObject *obj) } Class DebuggerEnv_class = { - "Environment", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGENV_COUNT), + "Environment", + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | + JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGENV_COUNT), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, NULL, /* reserved0 */ diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h index e3a57754e23..59a712e6476 100644 --- a/js/src/vm/Debugger.h +++ b/js/src/vm/Debugger.h @@ -236,6 +236,7 @@ class Debugger { bool init(JSContext *cx); inline const js::HeapPtrObject &toJSObject() const; + inline js::HeapPtrObject &toJSObjectRef(); static inline Debugger *fromJSObject(JSObject *obj); static Debugger *fromChildJSObject(JSObject *obj); @@ -431,6 +432,7 @@ class Breakpoint { Breakpoint *nextInDebugger(); Breakpoint *nextInSite(); const HeapPtrObject &getHandler() const { return handler; } + HeapPtrObject &getHandlerRef() { return handler; } }; Debugger * @@ -455,6 +457,13 @@ Debugger::toJSObject() const return object; } +js::HeapPtrObject & +Debugger::toJSObjectRef() +{ + JS_ASSERT(object); + return object; +} + Debugger * Debugger::fromJSObject(JSObject *obj) { diff --git a/js/src/vm/NumberObject.h b/js/src/vm/NumberObject.h index f4703b24a20..c2d4cdb9b1e 100644 --- a/js/src/vm/NumberObject.h +++ b/js/src/vm/NumberObject.h @@ -67,14 +67,13 @@ class NumberObject : public JSObject */ static inline NumberObject *createWithProto(JSContext *cx, jsdouble d, JSObject &proto); - Value unbox() const { - JS_ASSERT(getSlot(PRIMITIVE_VALUE_SLOT).isNumber()); - return getSlot(PRIMITIVE_VALUE_SLOT); + double unbox() const { + return getFixedSlot(PRIMITIVE_VALUE_SLOT).toNumber(); } private: inline void setPrimitiveValue(jsdouble d) { - setSlot(PRIMITIVE_VALUE_SLOT, NumberValue(d)); + setFixedSlot(PRIMITIVE_VALUE_SLOT, NumberValue(d)); } /* For access to init, as Number.prototype is special. */ diff --git a/js/src/vm/RegExpObject-inl.h b/js/src/vm/RegExpObject-inl.h index 23bd372a64b..fbcad568ebb 100644 --- a/js/src/vm/RegExpObject-inl.h +++ b/js/src/vm/RegExpObject-inl.h @@ -80,6 +80,14 @@ RegExpObject::getShared(JSContext *cx) return createShared(cx); } +inline void +RegExpObject::setShared(JSContext *cx, RegExpShared *shared) +{ + if (shared) + shared->prepareForUse(cx); + JSObject::setPrivate(shared); +} + inline void RegExpObject::setLastIndex(const Value &v) { @@ -148,6 +156,12 @@ RegExpToShared(JSContext *cx, JSObject &obj) return Proxy::regexp_toShared(cx, &obj); } +inline void +RegExpShared::prepareForUse(JSContext *cx) +{ + gcNumberWhenUsed = cx->runtime->gcNumber; +} + } /* namespace js */ #endif diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index c276695b6a5..e837a3fef83 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -62,7 +62,7 @@ RegExpObjectBuilder::RegExpObjectBuilder(JSContext *cx, RegExpObject *reobj) : cx(cx), reobj_(reobj) { if (reobj_) - reobj_->setPrivate(NULL); + reobj_->setShared(cx, NULL); } bool @@ -74,7 +74,7 @@ RegExpObjectBuilder::getOrCreate() JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass); if (!obj) return false; - obj->setPrivate(NULL); + obj->initPrivate(NULL); reobj_ = &obj->asRegExp(); return true; @@ -88,7 +88,7 @@ RegExpObjectBuilder::getOrCreateClone(RegExpObject *proto) JSObject *clone = NewObjectWithGivenProto(cx, &RegExpClass, proto, proto->getParent()); if (!clone) return false; - clone->setPrivate(NULL); + clone->initPrivate(NULL); reobj_ = &clone->asRegExp(); return true; @@ -103,7 +103,7 @@ RegExpObjectBuilder::build(JSAtom *source, RegExpShared &shared) if (!reobj_->init(cx, source, shared.getFlags())) return NULL; - reobj_->setPrivate(&shared); + reobj_->setShared(cx, &shared); return reobj_; } @@ -330,13 +330,18 @@ RegExpCode::execute(JSContext *cx, const jschar *chars, size_t length, size_t st static void regexp_trace(JSTracer *trc, JSObject *obj) { - if (trc->runtime->gcRunning) + /* + * We have to check both conditions, since: + * 1. During TraceRuntime, gcRunning is set + * 2. When a write barrier executes, IS_GC_MARKING_TRACER is true. + */ + if (trc->runtime->gcRunning && IS_GC_MARKING_TRACER(trc)) obj->setPrivate(NULL); } Class js::RegExpClass = { js_RegExp_str, - JSCLASS_HAS_PRIVATE | + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(RegExpObject::RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp), JS_PropertyStub, /* addProperty */ @@ -360,8 +365,8 @@ Class js::RegExpClass = { regexp_trace }; -RegExpShared::RegExpShared(RegExpFlag flags) - : parenCount(0), flags(flags), activeUseCount(0) +RegExpShared::RegExpShared(JSRuntime *rt, RegExpFlag flags) + : parenCount(0), flags(flags), activeUseCount(0), gcNumberWhenUsed(rt->gcNumber) {} RegExpObject * @@ -402,7 +407,7 @@ RegExpObject::createShared(JSContext *cx) if (!shared) return NULL; - setPrivate(shared); + setShared(cx, shared); return shared; } @@ -616,11 +621,12 @@ RegExpCompartment::init(JSContext *cx) } void -RegExpCompartment::purge() +RegExpCompartment::sweep(JSRuntime *rt) { for (Map::Enum e(map_); !e.empty(); e.popFront()) { + /* See the comment on RegExpShared lifetime in RegExpObject.h. */ RegExpShared *shared = e.front().value; - if (shared->activeUseCount == 0) { + if (shared->activeUseCount == 0 && shared->gcNumberWhenUsed < rt->gcStartNumber) { Foreground::delete_(shared); e.removeFront(); } @@ -630,14 +636,14 @@ RegExpCompartment::purge() inline RegExpShared * RegExpCompartment::get(JSContext *cx, JSAtom *keyAtom, JSAtom *source, RegExpFlag flags, Type type) { - DebugOnly gcNumberBefore = cx->runtime->gcNumber; + DebugOnly gcNumberBefore = cx->runtime->gcNumber; Key key(keyAtom, flags, type); Map::AddPtr p = map_.lookupForAdd(key); if (p) return p->value; - RegExpShared *shared = cx->runtime->new_(flags); + RegExpShared *shared = cx->runtime->new_(cx->runtime, flags); if (!shared || !shared->compile(cx, source)) goto error; diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h index e5a8d2ed0a3..df52548e03f 100644 --- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -169,6 +169,7 @@ class RegExpObject : public JSObject inline RegExpShared &shared() const; inline RegExpShared *maybeShared(); inline RegExpShared *getShared(JSContext *cx); + inline void setShared(JSContext *cx, RegExpShared *shared); private: friend class RegExpObjectBuilder; @@ -190,6 +191,9 @@ class RegExpObject : public JSObject RegExpObject() MOZ_DELETE; RegExpObject &operator=(const RegExpObject &reo) MOZ_DELETE; + + /* Call setShared in preference to setPrivate. */ + void setPrivate(void *priv) MOZ_DELETE; }; class RegExpObjectBuilder @@ -293,7 +297,26 @@ class RegExpCode } /* namespace detail */ -/* The compiled representation of a regexp. */ +/* + * A RegExpShared is the compiled representation of a regexp. A RegExpShared is + * pointed to by potentially multiple RegExpObjects. Additionally, C++ code may + * have pointers to RegExpShareds on the stack. The RegExpShareds are tracked in + * a RegExpCompartment hashtable, and most are destroyed on every GC. + * + * During a GC, the trace hook for RegExpObject clears any pointers to + * RegExpShareds so that there will be no dangling pointers when they are + * deleted. However, some RegExpShareds are not deleted: + * + * 1. Any RegExpShared with pointers from the C++ stack is not deleted. + * 2. Any RegExpShared that was installed in a RegExpObject during an + * incremental GC is not deleted. This is because the RegExpObject may have + * been traced through before the new RegExpShared was installed, in which + * case deleting the RegExpShared would turn the RegExpObject's reference + * into a dangling pointer + * + * The activeUseCount and gcNumberWhenUsed fields are used to track these two + * conditions. + */ class RegExpShared { friend class RegExpCompartment; @@ -301,11 +324,12 @@ class RegExpShared detail::RegExpCode code; uintN parenCount; RegExpFlag flags; - size_t activeUseCount; + size_t activeUseCount; /* See comment above. */ + uint64_t gcNumberWhenUsed; /* See comment above. */ bool compile(JSContext *cx, JSAtom *source); - RegExpShared(RegExpFlag flags); + RegExpShared(JSRuntime *rt, RegExpFlag flags); JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR; public: @@ -338,6 +362,9 @@ class RegExpShared RegExpShared &operator*() { JS_ASSERT(initialized()); return *re_; } }; + /* Called when a RegExpShared is installed into a RegExpObject. */ + inline void prepareForUse(JSContext *cx); + /* Primary interface: run this regular expression on the given string. */ RegExpRunStatus @@ -388,7 +415,7 @@ class RegExpCompartment ~RegExpCompartment(); bool init(JSContext *cx); - void purge(); + void sweep(JSRuntime *rt); /* Return a regexp corresponding to the given (source, flags) pair. */ RegExpShared *get(JSContext *cx, JSAtom *source, RegExpFlag flags); diff --git a/js/src/vm/RegExpStatics.cpp b/js/src/vm/RegExpStatics.cpp index b34d0776931..f80bf30bc22 100644 --- a/js/src/vm/RegExpStatics.cpp +++ b/js/src/vm/RegExpStatics.cpp @@ -71,7 +71,7 @@ resc_trace(JSTracer *trc, JSObject *obj) Class js::RegExpStaticsClass = { "RegExpStatics", - JSCLASS_HAS_PRIVATE, + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS, JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* getProperty */ diff --git a/js/src/vm/RegExpStatics.h b/js/src/vm/RegExpStatics.h index 3dbbd3ca5b3..70cfd9a1d2e 100644 --- a/js/src/vm/RegExpStatics.h +++ b/js/src/vm/RegExpStatics.h @@ -108,7 +108,7 @@ class RegExpStatics #endif } - /* + /* * Since the first pair indicates the whole match, the paren pair * numbers have to be in the range [1, pairCount). */ @@ -205,11 +205,11 @@ class RegExpStatics return get(0, 1) - get(0, 0) > 0; } - void mark(JSTracer *trc) const { + void mark(JSTracer *trc) { if (pendingInput) - MarkString(trc, pendingInput, "res->pendingInput"); + MarkString(trc, &pendingInput, "res->pendingInput"); if (matchPairsInput) - MarkString(trc, matchPairsInput, "res->matchPairsInput"); + MarkString(trc, &matchPairsInput, "res->matchPairsInput"); } bool pairIsPresent(size_t pairNum) const { diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 3c1ce65eb54..cf0f8be1a59 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -275,7 +275,7 @@ StackFrame::mark(JSTracer *trc) } if (IS_GC_MARKING_TRACER(trc)) script()->compartment()->active = true; - gc::MarkValueUnbarriered(trc, returnValue(), "rval"); + gc::MarkValueUnbarriered(trc, &returnValue(), "rval"); } /*****************************************************************************/ @@ -485,7 +485,7 @@ StackSpace::markFrameSlots(JSTracer *trc, StackFrame *fp, Value *slotsEnd, jsbyt /* Will this slot be synced by the JIT? */ if (!analysis->trackSlot(slot) || analysis->liveness(slot).live(offset)) - gc::MarkValueRoot(trc, *vp, "vm_stack"); + gc::MarkValueRoot(trc, vp, "vm_stack"); else *vp = UndefinedValue(); } @@ -532,6 +532,15 @@ StackSpace::mark(JSTracer *trc) } } +void +StackSpace::markActiveCompartments() +{ + for (StackSegment *seg = seg_; seg; seg = seg->prevInMemory()) { + for (StackFrame *fp = seg->maybefp(); (Value *)fp > (Value *)seg; fp = fp->prev()) + MarkCompartmentActive(fp); + } +} + JS_FRIEND_API(bool) StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals, JSCompartment *dest) const diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 9b30e2500cd..274789b9c71 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -999,7 +999,7 @@ class StackFrame return !!(flags_ & HAS_RVAL); } - const Value &returnValue() { + Value &returnValue() { if (!(flags_ & HAS_RVAL)) rval_.setUndefined(); return rval_; @@ -1555,6 +1555,9 @@ class StackSpace void mark(JSTracer *trc); void markFrameSlots(JSTracer *trc, StackFrame *fp, Value *slotsEnd, jsbytecode *pc); + /* Called during GC: sets active flag on compartments with active frames. */ + void markActiveCompartments(); + /* We only report the committed size; uncommitted size is uninteresting. */ JS_FRIEND_API(size_t) sizeOfCommitted(); }; diff --git a/js/src/vm/StringObject.h b/js/src/vm/StringObject.h index ddcd69f7a3b..4b06e5cb176 100644 --- a/js/src/vm/StringObject.h +++ b/js/src/vm/StringObject.h @@ -50,7 +50,7 @@ namespace js { class StringObject : public JSObject { - static const uintN PRIMITIVE_THIS_SLOT = 0; + static const uintN PRIMITIVE_VALUE_SLOT = 0; static const uintN LENGTH_SLOT = 1; public: @@ -69,20 +69,24 @@ class StringObject : public JSObject static inline StringObject *createWithProto(JSContext *cx, JSString *str, JSObject &proto); JSString *unbox() const { - return getSlot(PRIMITIVE_THIS_SLOT).toString(); + return getFixedSlot(PRIMITIVE_VALUE_SLOT).toString(); } inline size_t length() const { - return size_t(getSlot(LENGTH_SLOT).toInt32()); + return size_t(getFixedSlot(LENGTH_SLOT).toInt32()); + } + + static size_t getPrimitiveValueOffset() { + return getFixedSlotOffset(PRIMITIVE_VALUE_SLOT); } private: inline bool init(JSContext *cx, JSString *str); void setStringThis(JSString *str) { - JS_ASSERT(getSlot(PRIMITIVE_THIS_SLOT).isUndefined()); - setSlot(PRIMITIVE_THIS_SLOT, StringValue(str)); - setSlot(LENGTH_SLOT, Int32Value(int32_t(str->length()))); + JS_ASSERT(getReservedSlot(PRIMITIVE_VALUE_SLOT).isUndefined()); + setFixedSlot(PRIMITIVE_VALUE_SLOT, StringValue(str)); + setFixedSlot(LENGTH_SLOT, Int32Value(int32_t(str->length()))); } /* For access to init, as String.prototype is special. */ diff --git a/js/xpconnect/idl/nsIXPConnect.idl b/js/xpconnect/idl/nsIXPConnect.idl index 4de0430fc80..686f5517a3d 100644 --- a/js/xpconnect/idl/nsIXPConnect.idl +++ b/js/xpconnect/idl/nsIXPConnect.idl @@ -398,7 +398,7 @@ enum nsGCType { }; %} -[uuid(686bb1d0-4711-11e1-b86c-0800200c9a66)] +[uuid(e92bf5e0-494c-11e1-b86c-0800200c9a66)] interface nsIXPConnect : nsISupports { %{ C++ @@ -734,6 +734,12 @@ interface nsIXPConnect : nsISupports */ void GarbageCollect(in PRUint32 reason, in PRUint32 kind); + /** + * Signals a good place to do an incremental GC slice, because the + * browser is drawing a frame. + */ + void NotifyDidPaint(); + /** * Define quick stubs on the given object, @a proto. * diff --git a/js/xpconnect/src/XPCInlines.h b/js/xpconnect/src/XPCInlines.h index 27a4d53d1c7..4d4af6e01d4 100644 --- a/js/xpconnect/src/XPCInlines.h +++ b/js/xpconnect/src/XPCInlines.h @@ -604,7 +604,8 @@ void XPCWrappedNativeTearOff::SetJSObject(JSObject* JSObj) inline XPCWrappedNativeTearOff::~XPCWrappedNativeTearOff() { - NS_ASSERTION(!(GetInterface()||GetNative()||GetJSObjectPreserveColor()), "tearoff not empty in dtor"); + NS_ASSERTION(!(GetInterface()||GetNative()||GetJSObjectPreserveColor()), + "tearoff not empty in dtor"); } /***************************************************************************/ diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 3bc55abf66d..9d649c4b8c1 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -911,6 +911,8 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status) #ifdef XPC_TRACK_DEFERRED_RELEASES printf("XPC - End deferred Releases\n"); #endif + + self->GetXPConnect()->ClearGCBeforeCC(); break; } default: @@ -1890,6 +1892,18 @@ AccumulateTelemetryCallback(int id, uint32_t sample) case JS_TELEMETRY_GC_SWEEP_MS: Telemetry::Accumulate(Telemetry::GC_SWEEP_MS, sample); break; + case JS_TELEMETRY_GC_SLICE_MS: + Telemetry::Accumulate(Telemetry::GC_SLICE_MS, sample); + break; + case JS_TELEMETRY_GC_MMU_50: + Telemetry::Accumulate(Telemetry::GC_MMU_50, sample); + break; + case JS_TELEMETRY_GC_RESET: + Telemetry::Accumulate(Telemetry::GC_RESET, sample); + break; + case JS_TELEMETRY_GC_INCREMENTAL_DISABLED: + Telemetry::Accumulate(Telemetry::GC_INCREMENTAL_DISABLED, sample); + break; } } diff --git a/js/xpconnect/src/XPCQuickStubs.h b/js/xpconnect/src/XPCQuickStubs.h index 1f709711dc3..f903ed633f4 100644 --- a/js/xpconnect/src/XPCQuickStubs.h +++ b/js/xpconnect/src/XPCQuickStubs.h @@ -539,8 +539,8 @@ castNativeFromWrapper(JSContext *cx, NS_ASSERTION(IS_WRAPPER_CLASS(js::GetObjectClass(cur)), "Not a wrapper?"); - XPCNativeScriptableSharedJSClass *clasp = - (XPCNativeScriptableSharedJSClass*)js::GetObjectClass(cur); + XPCWrappedNativeJSClass *clasp = + (XPCWrappedNativeJSClass*)js::GetObjectClass(cur); if (!(clasp->interfacesBitmap & (1 << interfaceBit))) return nsnull; diff --git a/js/xpconnect/src/XPCWrappedNative.cpp b/js/xpconnect/src/XPCWrappedNative.cpp index 93939fab1d8..4bd06c15afe 100644 --- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -1091,7 +1091,7 @@ XPCWrappedNative::Init(XPCCallContext& ccx, // create our flatJSObject - JSClass* jsclazz = si ? si->GetJSClass() : Jsvalify(&XPC_WN_NoHelper_JSClass); + JSClass* jsclazz = si ? si->GetJSClass() : Jsvalify(&XPC_WN_NoHelper_JSClass.base); if (isGlobal) { // Resolving a global object's class can cause us to create a global's diff --git a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp index 543217b2789..2367f62ece1 100644 --- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp +++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp @@ -814,7 +814,8 @@ XPC_WN_OuterObject(JSContext *cx, JSObject *obj) return obj; } -js::Class XPC_WN_NoHelper_JSClass = { +XPCWrappedNativeJSClass XPC_WN_NoHelper_JSClass = { + { // base "XPCWrappedNative_NoHelper", // name; WRAPPER_SLOTS | JSCLASS_PRIVATE_IS_NSISUPPORTS, // flags @@ -885,6 +886,8 @@ js::Class XPC_WN_NoHelper_JSClass = { XPC_WN_JSOp_ThisObject, XPC_WN_JSOp_Clear } + }, + 0 // interfacesBitmap }; @@ -1204,7 +1207,7 @@ XPC_WN_JSOp_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp) { js::Class *clazz = js::GetObjectClass(obj); - if (!IS_WRAPPER_CLASS(clazz) || clazz == &XPC_WN_NoHelper_JSClass) { + if (!IS_WRAPPER_CLASS(clazz) || clazz == &XPC_WN_NoHelper_JSClass.base) { // obj must be a prototype object or a wrapper w/o a // helper. Short circuit this call to the default // implementation. diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index d506d95af8b..e1cf2c327c7 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -406,8 +406,6 @@ nsXPConnect::Collect(PRUint32 reason, PRUint32 kind) // To improve debugging, if DEBUG_CC is defined all JS objects are // traversed. - mNeedGCBeforeCC = false; - XPCCallContext ccx(NATIVE_CALLER); if (!ccx.IsValid()) return; @@ -424,6 +422,8 @@ nsXPConnect::Collect(PRUint32 reason, PRUint32 kind) js::gcreason::Reason gcreason = (js::gcreason::Reason)reason; if (kind == nsGCShrinking) { js::ShrinkingGC(cx, gcreason); + } else if (kind == nsGCIncremental) { + js::IncrementalGC(cx, gcreason); } else { MOZ_ASSERT(kind == nsGCNormal); js::GCForReason(cx, gcreason); @@ -2825,6 +2825,23 @@ nsXPConnect::GetTelemetryValue(JSContext *cx, jsval *rval) return NS_OK; } +NS_IMETHODIMP +nsXPConnect::NotifyDidPaint() +{ + JSRuntime *rt = mRuntime->GetJSRuntime(); + if (!js::WantGCSlice(rt)) + return NS_OK; + + XPCCallContext ccx(NATIVE_CALLER); + if (!ccx.IsValid()) + return UnexpectedFailure(NS_ERROR_FAILURE); + + JSContext *cx = ccx.GetJSContext(); + + js::NotifyDidPaint(cx); + return NS_OK; +} + /* These are here to be callable from a debugger */ JS_BEGIN_EXTERN_C JS_EXPORT_API(void) DumpJSStack() diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 3cb76b5f23f..8f1c9d5753b 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -318,7 +318,8 @@ typedef nsDataHashtable XPCCompart return (result || !src) ? NS_OK : NS_ERROR_OUT_OF_MEMORY -#define WRAPPER_SLOTS (JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1)) +#define WRAPPER_SLOTS (JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | \ + JSCLASS_HAS_RESERVED_SLOTS(1)) #define INVALID_OBJECT ((JSObject *)1) @@ -520,6 +521,7 @@ public: JSBool IsShuttingDown() const {return mShuttingDown;} void EnsureGCBeforeCC() { mNeedGCBeforeCC = true; } + void ClearGCBeforeCC() { mNeedGCBeforeCC = false; } nsresult GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info); nsresult GetInfoForName(const char * name, nsIInterfaceInfo** info); @@ -1351,7 +1353,8 @@ private: // These are the various JSClasses and callbacks whose use that required // visibility from more than one .cpp file. -extern js::Class XPC_WN_NoHelper_JSClass; +struct XPCWrappedNativeJSClass; +extern XPCWrappedNativeJSClass XPC_WN_NoHelper_JSClass; extern js::Class XPC_WN_NoMods_WithCall_Proto_JSClass; extern js::Class XPC_WN_NoMods_NoCall_Proto_JSClass; extern js::Class XPC_WN_ModsAllowed_WithCall_Proto_JSClass; @@ -2035,7 +2038,10 @@ public: // was a big problem when wrappers are reparented to different scopes (and // thus different protos (the DOM does this). -struct XPCNativeScriptableSharedJSClass +// We maintain the invariant that every JSClass for which ext.isWrappedNative +// is true is a contained in an instance of this struct, and can thus be cast +// to it. +struct XPCWrappedNativeJSClass { js::Class base; PRUint32 interfacesBitmap; @@ -2077,7 +2083,7 @@ public: private: XPCNativeScriptableFlags mFlags; - XPCNativeScriptableSharedJSClass mJSClass; + XPCWrappedNativeJSClass mJSClass; JSBool mCanBeSlim; }; diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index 6aabd0d885c..709a6198ecf 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -75,7 +75,8 @@ xpc_CreateMTGlobalObject(JSContext *cx, JSClass *clasp, #define XPCONNECT_GLOBAL_FLAGS \ JSCLASS_XPCONNECT_GLOBAL | JSCLASS_HAS_PRIVATE | \ - JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(1) + JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_IMPLEMENTS_BARRIERS | \ + JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(1) void TraceXPCGlobal(JSTracer *trc, JSObject *obj); @@ -182,8 +183,12 @@ xpc_UnmarkGrayObjectRecursive(JSObject* obj); inline void xpc_UnmarkGrayObject(JSObject *obj) { - if (obj && xpc_IsGrayGCThing(obj)) - xpc_UnmarkGrayObjectRecursive(obj); + if (obj) { + if (xpc_IsGrayGCThing(obj)) + xpc_UnmarkGrayObjectRecursive(obj); + else if (js::IsIncrementalBarrierNeededOnObject(obj)) + js::IncrementalReferenceBarrier(obj); + } } // If aVariant is an XPCVariant, this marks the object to be in aGeneration. diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index bfe8c785b60..04d264048b6 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -2388,7 +2388,7 @@ nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame) */ static gfxPoint3D GetDeltaToMozTransformOrigin(const nsIFrame* aFrame, - float aFactor, + float aAppUnitsPerPixel, const nsRect* aBoundsOverride) { NS_PRECONDITION(aFrame, "Can't get delta for a null frame!"); @@ -2416,23 +2416,26 @@ gfxPoint3D GetDeltaToMozTransformOrigin(const nsIFrame* aFrame, const nsStyleCoord &coord = display->mTransformOrigin[index]; if (coord.GetUnit() == eStyleUnit_Calc) { const nsStyleCoord::Calc *calc = coord.GetCalcValue(); - *coords[index] = NSAppUnitsToFloatPixels(*dimensions[index], aFactor) * - calc->mPercent + - NSAppUnitsToFloatPixels(calc->mLength, aFactor); + *coords[index] = + NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) * + calc->mPercent + + NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel); } else if (coord.GetUnit() == eStyleUnit_Percent) { - *coords[index] = NSAppUnitsToFloatPixels(*dimensions[index], aFactor) * + *coords[index] = + NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) * coord.GetPercentValue(); } else { NS_ABORT_IF_FALSE(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit"); - *coords[index] = NSAppUnitsToFloatPixels(coord.GetCoordValue(), aFactor); + *coords[index] = + NSAppUnitsToFloatPixels(coord.GetCoordValue(), aAppUnitsPerPixel); } } - *coords[2] = NSAppUnitsToFloatPixels(display->mTransformOrigin[2].GetCoordValue(), aFactor); - + *coords[2] = NSAppUnitsToFloatPixels(display->mTransformOrigin[2].GetCoordValue(), + aAppUnitsPerPixel); /* Adjust based on the origin of the rectangle. */ - result.x += NSAppUnitsToFloatPixels(boundingRect.x, aFactor); - result.y += NSAppUnitsToFloatPixels(boundingRect.y, aFactor); + result.x += NSAppUnitsToFloatPixels(boundingRect.x, aAppUnitsPerPixel); + result.y += NSAppUnitsToFloatPixels(boundingRect.y, aAppUnitsPerPixel); return result; } @@ -2443,7 +2446,7 @@ gfxPoint3D GetDeltaToMozTransformOrigin(const nsIFrame* aFrame, */ static gfxPoint3D GetDeltaToMozPerspectiveOrigin(const nsIFrame* aFrame, - float aFactor, + float aAppUnitsPerPixel, const nsRect* aBoundsOverride) { NS_PRECONDITION(aFrame, "Can't get delta for a null frame!"); @@ -2478,22 +2481,26 @@ gfxPoint3D GetDeltaToMozPerspectiveOrigin(const nsIFrame* aFrame, const nsStyleCoord &coord = display->mPerspectiveOrigin[index]; if (coord.GetUnit() == eStyleUnit_Calc) { const nsStyleCoord::Calc *calc = coord.GetCalcValue(); - *coords[index] = NSAppUnitsToFloatPixels(*dimensions[index], aFactor) * - calc->mPercent + - NSAppUnitsToFloatPixels(calc->mLength, aFactor); + *coords[index] = + NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) * + calc->mPercent + + NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel); } else if (coord.GetUnit() == eStyleUnit_Percent) { - *coords[index] = NSAppUnitsToFloatPixels(*dimensions[index], aFactor) * + *coords[index] = + NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) * coord.GetPercentValue(); } else { NS_ABORT_IF_FALSE(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit"); - *coords[index] = NSAppUnitsToFloatPixels(coord.GetCoordValue(), aFactor); + *coords[index] = + NSAppUnitsToFloatPixels(coord.GetCoordValue(), aAppUnitsPerPixel); } } nsPoint parentOffset = aFrame->GetOffsetTo(parent); - gfxPoint3D gfxOffset(NSAppUnitsToFloatPixels(parentOffset.x, aFactor), - NSAppUnitsToFloatPixels(parentOffset.y, aFactor), - 0); + gfxPoint3D gfxOffset( + NSAppUnitsToFloatPixels(parentOffset.x, aAppUnitsPerPixel), + NSAppUnitsToFloatPixels(parentOffset.y, aAppUnitsPerPixel), + 0.0f); return result - gfxOffset; } @@ -2505,7 +2512,7 @@ gfxPoint3D GetDeltaToMozPerspectiveOrigin(const nsIFrame* aFrame, gfx3DMatrix nsDisplayTransform::GetResultingTransformMatrix(const nsIFrame* aFrame, const nsPoint &aOrigin, - float aFactor, + float aAppUnitsPerPixel, const nsRect* aBoundsOverride, nsIFrame** aOutAncestor) { @@ -2518,10 +2525,12 @@ nsDisplayTransform::GetResultingTransformMatrix(const nsIFrame* aFrame, /* Account for the -moz-transform-origin property by translating the * coordinate space to the new origin. */ - gfxPoint3D toMozOrigin = GetDeltaToMozTransformOrigin(aFrame, aFactor, aBoundsOverride); - gfxPoint3D newOrigin = gfxPoint3D(NSAppUnitsToFloatPixels(aOrigin.x, aFactor), - NSAppUnitsToFloatPixels(aOrigin.y, aFactor), - 0.0f); + gfxPoint3D toMozOrigin = + GetDeltaToMozTransformOrigin(aFrame, aAppUnitsPerPixel, aBoundsOverride); + gfxPoint3D newOrigin = + gfxPoint3D(NSAppUnitsToFloatPixels(aOrigin.x, aAppUnitsPerPixel), + NSAppUnitsToFloatPixels(aOrigin.y, aAppUnitsPerPixel), + 0.0f); /* Get the underlying transform matrix. This requires us to get the * bounds of the frame. @@ -2538,7 +2547,7 @@ nsDisplayTransform::GetResultingTransformMatrix(const nsIFrame* aFrame, result = nsStyleTransformMatrix::ReadTransforms(disp->mSpecifiedTransform, aFrame->GetStyleContext(), aFrame->PresContext(), - dummy, bounds, aFactor); + dummy, bounds, aAppUnitsPerPixel); } else { NS_ASSERTION(aFrame->GetStyleDisplay()->mTransformStyle == NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D, "If we don't have a transform, then we must be at least attempting to preserve the transforms of our children"); @@ -2555,11 +2564,11 @@ nsDisplayTransform::GetResultingTransformMatrix(const nsIFrame* aFrame, gfx3DMatrix perspective; perspective._34 = -1.0 / NSAppUnitsToFloatPixels(parentDisp->mChildPerspective.GetCoordValue(), - aFactor); + aAppUnitsPerPixel); /* At the point when perspective is applied, we have been translated to the transform origin. * The translation to the perspective origin is the difference between these values. */ - gfxPoint3D toPerspectiveOrigin = GetDeltaToMozPerspectiveOrigin(aFrame, aFactor, aBoundsOverride); + gfxPoint3D toPerspectiveOrigin = GetDeltaToMozPerspectiveOrigin(aFrame, aAppUnitsPerPixel, aBoundsOverride); result = result * nsLayoutUtils::ChangeMatrixBasis(toPerspectiveOrigin - toMozOrigin, perspective); } @@ -2570,7 +2579,7 @@ nsDisplayTransform::GetResultingTransformMatrix(const nsIFrame* aFrame, aFrame->GetParent()->Preserves3DChildren(), "Preserve3D mismatch!"); gfx3DMatrix parent = GetResultingTransformMatrix(aFrame->GetParent(), aOrigin - aFrame->GetPosition(), - aFactor, nsnull, aOutAncestor); + aAppUnitsPerPixel, nsnull, aOutAncestor); return nsLayoutUtils::ChangeMatrixBasis(newOrigin + toMozOrigin, result) * parent; } @@ -2607,14 +2616,14 @@ static bool IsFrameVisible(nsIFrame* aFrame, const gfx3DMatrix& aMatrix) } const gfx3DMatrix& -nsDisplayTransform::GetTransform(float aFactor) +nsDisplayTransform::GetTransform(float aAppUnitsPerPixel) { - if (mTransform.IsIdentity() || mCachedFactor != aFactor) { + if (mTransform.IsIdentity() || mCachedAppUnitsPerPixel != aAppUnitsPerPixel) { mTransform = GetResultingTransformMatrix(mFrame, ToReferenceFrame(), - aFactor, + aAppUnitsPerPixel, nsnull); - mCachedFactor = aFactor; + mCachedAppUnitsPerPixel = aAppUnitsPerPixel; } return mTransform; } @@ -2936,21 +2945,21 @@ nsRect nsDisplayTransform::TransformRectOut(const nsRect &aUntransformedBounds, bool nsDisplayTransform::UntransformRectMatrix(const nsRect &aUntransformedBounds, const gfx3DMatrix& aMatrix, - float aFactor, + float aAppUnitsPerPixel, nsRect *aOutRect) { if (aMatrix.IsSingular()) return false; - gfxRect result(NSAppUnitsToFloatPixels(aUntransformedBounds.x, aFactor), - NSAppUnitsToFloatPixels(aUntransformedBounds.y, aFactor), - NSAppUnitsToFloatPixels(aUntransformedBounds.width, aFactor), - NSAppUnitsToFloatPixels(aUntransformedBounds.height, aFactor)); + gfxRect result(NSAppUnitsToFloatPixels(aUntransformedBounds.x, aAppUnitsPerPixel), + NSAppUnitsToFloatPixels(aUntransformedBounds.y, aAppUnitsPerPixel), + NSAppUnitsToFloatPixels(aUntransformedBounds.width, aAppUnitsPerPixel), + NSAppUnitsToFloatPixels(aUntransformedBounds.height, aAppUnitsPerPixel)); /* We want to untransform the matrix, so invert the transformation first! */ result = aMatrix.Inverse().ProjectRectBounds(result); - *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(result, aFactor); + *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(result, aAppUnitsPerPixel); return true; } diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 98ccc0fa407..a5615cd4810 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -2136,7 +2136,7 @@ public: INDEX_MAX = PR_UINT32_MAX >> nsDisplayItem::TYPE_BITS }; - const gfx3DMatrix& GetTransform(float aFactor); + const gfx3DMatrix& GetTransform(float aAppUnitsPerPixel); float GetHitDepthAtPoint(const nsPoint& aPoint); @@ -2178,7 +2178,7 @@ public: static bool UntransformRectMatrix(const nsRect &aUntransformedBounds, const gfx3DMatrix& aMatrix, - float aFactor, + float aAppUnitsPerPixel, nsRect* aOutRect); /** @@ -2203,7 +2203,7 @@ public: * * @param aFrame The frame to get the matrix from. * @param aOrigin Relative to which point this transform should be applied. - * @param aScaleFactor The number of app units per graphics unit. + * @param aAppUnitsPerPixel The number of app units per graphics unit. * @param aBoundsOverride [optional] If this is nsnull (the default), the * computation will use the value of GetFrameBoundsForTransform(aFrame) * for the frame's bounding rectangle. Otherwise, it will use the @@ -2212,7 +2212,7 @@ public: */ static gfx3DMatrix GetResultingTransformMatrix(const nsIFrame* aFrame, const nsPoint& aOrigin, - float aFactor, + float aAppUnitsPerPixel, const nsRect* aBoundsOverride = nsnull, nsIFrame** aOutAncestor = nsnull); /** @@ -2225,7 +2225,7 @@ public: private: nsDisplayWrapList mStoredList; gfx3DMatrix mTransform; - float mCachedFactor; + float mCachedAppUnitsPerPixel; PRUint32 mIndex; }; diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 606c92add15..24d3ccdc196 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -5214,7 +5214,7 @@ void PresShell::SetDisplayPort(const nsRect& aDisplayPort) nsresult PresShell::SetResolution(float aXResolution, float aYResolution) { - if (!(aXResolution > 0.0 && aXResolution > 0.0)) { + if (!(aXResolution > 0.0 && aYResolution > 0.0)) { return NS_ERROR_ILLEGAL_VALUE; } if (aXResolution == mXResolution && aYResolution == mYResolution) { @@ -5428,6 +5428,24 @@ PresShell::ProcessSynthMouseMoveEvent(bool aFromScroll) } } +class nsAutoNotifyDidPaint +{ +public: + nsAutoNotifyDidPaint(bool aWillSendDidPaint) + : mWillSendDidPaint(aWillSendDidPaint) + { + } + ~nsAutoNotifyDidPaint() + { + if (!mWillSendDidPaint && nsContentUtils::XPConnect()) { + nsContentUtils::XPConnect()->NotifyDidPaint(); + } + } + +private: + bool mWillSendDidPaint; +}; + void PresShell::Paint(nsIView* aViewToPaint, nsIWidget* aWidgetToPaint, @@ -5451,6 +5469,8 @@ PresShell::Paint(nsIView* aViewToPaint, NS_ASSERTION(aViewToPaint, "null view"); NS_ASSERTION(aWidgetToPaint, "Can't paint without a widget"); + nsAutoNotifyDidPaint notifyDidPaint(aWillSendDidPaint); + nsPresContext* presContext = GetPresContext(); AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext, Paint); @@ -5744,6 +5764,7 @@ PresShell::RecordMouseLocation(nsGUIEvent* aEvent) } } +#ifdef MOZ_TOUCH static void EvictTouchPoint(nsCOMPtr& aTouch) { @@ -5791,6 +5812,7 @@ AppendToTouchList(const PRUint32& aKey, nsCOMPtr& aData, void *aTou touches->AppendElement(aData); return PL_DHASH_NEXT; } +#endif // MOZ_TOUCH nsresult PresShell::HandleEvent(nsIFrame *aFrame, @@ -7221,6 +7243,10 @@ PresShell::DidPaint() if (rootPresContext == mPresContext) { rootPresContext->UpdatePluginGeometry(); } + + if (nsContentUtils::XPConnect()) { + nsContentUtils::XPConnect()->NotifyDidPaint(); + } } bool diff --git a/layout/build/Makefile.in b/layout/build/Makefile.in index 0f899c5dfac..4c42e8bd4d4 100644 --- a/layout/build/Makefile.in +++ b/layout/build/Makefile.in @@ -148,10 +148,6 @@ LOCAL_INCLUDES += \ $(NULL) endif -ifdef MOZ_B2G_BT #{ -SHARED_LIBRARY_LIBS += $(DEPTH)/dom/bluetooth/$(LIB_PREFIX)dombluetooth_s.$(LIB_SUFFIX) -endif #} - ifdef MOZ_B2G_RIL #{ SHARED_LIBRARY_LIBS += $(DEPTH)/dom/system/b2g/$(LIB_PREFIX)domsystemb2g_s.$(LIB_SUFFIX) endif #} @@ -276,8 +272,5 @@ ifdef MOZ_B2G_RIL #{ LOCAL_INCLUDES += -I$(topsrcdir)/dom/system/b2g endif #} -ifdef MOZ_B2G_BT #{ -LOCAL_INCLUDES += -I$(topsrcdir)/dom/bluetooth -endif #} DEFINES += -D_IMPL_NS_LAYOUT diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index 590168d78c8..35bcdb02200 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -1939,10 +1939,11 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) needToRecoverState = false; // Update aState.mPrevChild as if we had reflowed all of the frames in - // this line. This is expensive in some cases, since it requires - // walking |GetNextSibling|. + // this line. if (line->IsDirty()) - aState.mPrevChild = line.prev()->LastChild(); + NS_ASSERTION(line->mFirstChild->GetPrevSibling() == + line.prev()->LastChild(), "unexpected line frames"); + aState.mPrevChild = line->mFirstChild->GetPrevSibling(); } // Now repair the line and update |aState.mY| by calling @@ -2132,9 +2133,11 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) aState.ReconstructMarginAbove(line); // Update aState.mPrevChild as if we had reflowed all of the frames in - // the last line. This is expensive in some cases, since it requires - // walking |GetNextSibling|. - aState.mPrevChild = line.prev()->LastChild(); + // the last line. + NS_ASSERTION(line == line_end || line->mFirstChild->GetPrevSibling() == + line.prev()->LastChild(), "unexpected line frames"); + aState.mPrevChild = + line == line_end ? mFrames.LastChild() : line->mFirstChild->GetPrevSibling(); } // Should we really have to do this? @@ -4396,7 +4399,12 @@ nsBlockFrame::PushLines(nsBlockReflowState& aState, if (firstLine) { mFrames.Clear(); } else { - mFrames.RemoveFramesAfter(aLineBefore->LastChild()); + nsIFrame* f = overBegin->mFirstChild; + nsIFrame* lineBeforeLastFrame = + f ? f->GetPrevSibling() : aLineBefore->LastChild(); + NS_ASSERTION(!f || lineBeforeLastFrame == aLineBefore->LastChild(), + "unexpected line frames"); + mFrames.RemoveFramesAfter(lineBeforeLastFrame); } if (!overflowLines->empty()) { // XXXbz If we switch overflow lines to nsFrameList, we should @@ -4713,7 +4721,9 @@ nsBlockFrame::AppendFrames(ChildListID aListID, } // Find the proper last-child for where the append should go - nsIFrame* lastKid = mLines.empty() ? nsnull : mLines.back()->LastChild(); + nsIFrame* lastKid = mFrames.LastChild(); + NS_ASSERTION((mLines.empty() ? nsnull : mLines.back()->LastChild()) == + lastKid, "out-of-sync mLines / mFrames"); // Add frames after the last child #ifdef NOISY_REFLOW_REASON @@ -5394,8 +5404,16 @@ nsBlockFrame::DoRemoveFrame(nsIFrame* aDeletedFrame, PRUint32 aFlags) // If the frame being deleted is the last one on the line then // optimize away the line->Contains(next-in-flow) call below. - bool isLastFrameOnLine = (1 == line->GetChildCount() || - line->LastChild() == aDeletedFrame); + bool isLastFrameOnLine = 1 == line->GetChildCount(); + if (!isLastFrameOnLine) { + line_iterator next = line.next(); + nsIFrame* lastFrame = next != line_end ? + next->mFirstChild->GetPrevSibling() : + (searchingOverflowList ? line->LastChild() : mFrames.LastChild()); + NS_ASSERTION(next == line_end || lastFrame == line->LastChild(), + "unexpected line frames"); + isLastFrameOnLine = lastFrame == aDeletedFrame; + } // Remove aDeletedFrame from the line nsIFrame* nextFrame = aDeletedFrame->GetNextSibling(); diff --git a/layout/generic/nsColumnSetFrame.cpp b/layout/generic/nsColumnSetFrame.cpp index 6a639dba195..9356fe303e8 100644 --- a/layout/generic/nsColumnSetFrame.cpp +++ b/layout/generic/nsColumnSetFrame.cpp @@ -366,7 +366,7 @@ nsColumnSetFrame::ChooseColumnStrategy(const nsHTMLReflowState& aReflowState) bool isBalancing = colStyle->mColumnFill == NS_STYLE_COLUMN_FILL_BALANCE; if (isBalancing) { - const PRUint32 MAX_NESTED_COLUMN_BALANCING = 5; + const PRUint32 MAX_NESTED_COLUMN_BALANCING = 2; PRUint32 cnt = 1; for (const nsHTMLReflowState* rs = aReflowState.parentReflowState; rs && cnt < MAX_NESTED_COLUMN_BALANCING; diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 7bc5d8bf515..38017b685ff 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -969,10 +969,14 @@ nsHTMLScrollFrame::GetFrameName(nsAString& aResult) const already_AddRefed nsHTMLScrollFrame::CreateAccessible() { - if (!IsFocusable()) { + // Create an accessible regardless of focusable state because the state can be + // changed during frame life cycle without any notifications to accessibility. + if (mContent->IsRootOfNativeAnonymousSubtree() || + GetScrollbarStyles() == nsIScrollableFrame:: + ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, NS_STYLE_OVERFLOW_HIDDEN) ) { return nsnull; } - // Focusable via CSS, so needs to be in accessibility hierarchy + nsAccessibilityService* accService = nsIPresShell::AccService(); if (accService) { return accService->CreateHyperTextAccessible(mContent, diff --git a/layout/generic/nsInlineFrame.cpp b/layout/generic/nsInlineFrame.cpp index 17121a8c243..eddff255c25 100644 --- a/layout/generic/nsInlineFrame.cpp +++ b/layout/generic/nsInlineFrame.cpp @@ -71,6 +71,20 @@ NS_NewInlineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) return new (aPresShell) nsInlineFrame(aContext); } +NS_IMETHODIMP +nsInlineFrame::Init(nsIContent* aContent, + nsIFrame* aParent, + nsIFrame* aPrevInFlow) +{ + // Let the base class do its processing + nsresult rv = nsContainerFrame::Init(aContent, aParent, aPrevInFlow); + + // Transforms do not affect regular inline elements (bug 722463) + mState &= ~NS_FRAME_MAY_BE_TRANSFORMED; + + return rv; +} + NS_IMPL_FRAMEARENA_HELPERS(nsInlineFrame) NS_QUERYFRAME_HEAD(nsInlineFrame) diff --git a/layout/generic/nsInlineFrame.h b/layout/generic/nsInlineFrame.h index 924c3561c8c..94faa93074b 100644 --- a/layout/generic/nsInlineFrame.h +++ b/layout/generic/nsInlineFrame.h @@ -69,6 +69,13 @@ public: friend nsIFrame* NS_NewInlineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); + /** sets defaults for inline-specific style. + * @see nsIFrame::Init + */ + NS_IMETHOD Init(nsIContent* aContent, + nsIFrame* aParent, + nsIFrame* aPrevInFlow); + // nsIFrame overrides NS_IMETHOD BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, diff --git a/layout/generic/nsObjectFrame.cpp b/layout/generic/nsObjectFrame.cpp index c2fdf060a37..a23ba83d453 100644 --- a/layout/generic/nsObjectFrame.cpp +++ b/layout/generic/nsObjectFrame.cpp @@ -480,20 +480,6 @@ nsObjectFrame::PrepForDrawing(nsIWidget *aWidget) break; } } - -#ifdef XP_MACOSX - // Now that we have a widget we want to set the event model before - // any events are processed. - nsCOMPtr pluginWidget = do_QueryInterface(mWidget); - if (!pluginWidget) - return NS_ERROR_FAILURE; - pluginWidget->SetPluginEventModel(mInstanceOwner->GetEventModel()); - pluginWidget->SetPluginDrawingModel(mInstanceOwner->GetDrawingModel()); - - if (mInstanceOwner->GetDrawingModel() == NPDrawingModelCoreAnimation) { - mInstanceOwner->SetupCARefresh(); - } -#endif } else { // Changing to windowless mode changes the NPWindow geometry. FixupWindow(GetContentRectRelativeToSelf().Size()); diff --git a/layout/reftests/canvas/726951-shadow-clips-ref.html b/layout/reftests/canvas/726951-shadow-clips-ref.html new file mode 100644 index 00000000000..b7be1ec0c7c --- /dev/null +++ b/layout/reftests/canvas/726951-shadow-clips-ref.html @@ -0,0 +1,18 @@ + + + + + + + diff --git a/layout/reftests/canvas/726951-shadow-clips.html b/layout/reftests/canvas/726951-shadow-clips.html new file mode 100644 index 00000000000..b14e0c307d8 --- /dev/null +++ b/layout/reftests/canvas/726951-shadow-clips.html @@ -0,0 +1,18 @@ + + + + + + + diff --git a/layout/reftests/canvas/reftest.list b/layout/reftests/canvas/reftest.list index 375880f3e96..cb8a38777b9 100644 --- a/layout/reftests/canvas/reftest.list +++ b/layout/reftests/canvas/reftest.list @@ -75,4 +75,6 @@ fails-if(/Mac\x20OS\x20X\x2010\.[56]/.test(http.oscpu)) == 672646-alpha-radial-g != 693610-1.html 693610-1-notref.html # bug 693610: multiple glyph runs should not be overprinted +== 726951-shadow-clips.html 726951-shadow-clips-ref.html + == transformed-clip.html transformed-clip-ref.html diff --git a/layout/reftests/svg/moz-only/reftest.list b/layout/reftests/svg/moz-only/reftest.list index 8118cedf14a..41a26dca8b6 100644 --- a/layout/reftests/svg/moz-only/reftest.list +++ b/layout/reftests/svg/moz-only/reftest.list @@ -18,4 +18,5 @@ fails == xbl-grad-ref--grad-in-resources-02.svg pass.svg == feImage-zoom-01a.svg feImage-zoom-01-ref.svg == feImage-zoom-01b.svg feImage-zoom-01-ref.svg == foreignObject-zoom-01.svg pass.svg +== zoom-invalidation-01.svg pass.svg skip == zoomed-svg-with-viewBox-01.svg zoomed-svg-with-viewBox-01-ref.svg diff --git a/layout/reftests/svg/moz-only/zoom-invalidation-01.svg b/layout/reftests/svg/moz-only/zoom-invalidation-01.svg new file mode 100644 index 00000000000..42fad444d38 --- /dev/null +++ b/layout/reftests/svg/moz-only/zoom-invalidation-01.svg @@ -0,0 +1,25 @@ + + + + Test invalidation of zoomed SVG + + + + + + + diff --git a/layout/reftests/svg/reftest.list b/layout/reftests/svg/reftest.list index defd30e3c08..e46701f3279 100644 --- a/layout/reftests/svg/reftest.list +++ b/layout/reftests/svg/reftest.list @@ -242,6 +242,7 @@ fails-if(Android) random-if(gtk2Widget) != text-language-01.xhtml text-language- == text-layout-03.svg text-layout-03-ref.svg == text-layout-04.svg text-layout-04-ref.svg == text-layout-05.svg text-layout-05-ref.svg +== text-layout-06.svg text-layout-06-ref.svg == text-scale-01.svg text-scale-01-ref.svg == text-stroke-scaling-01.svg text-stroke-scaling-01-ref.svg == stroke-dasharray-and-pathLength-01.svg pass.svg diff --git a/layout/reftests/svg/text-layout-06-ref.svg b/layout/reftests/svg/text-layout-06-ref.svg new file mode 100644 index 00000000000..b4276007cc1 --- /dev/null +++ b/layout/reftests/svg/text-layout-06-ref.svg @@ -0,0 +1,10 @@ + + + Reference to check fill and stroke handling + + A B + A B + diff --git a/layout/reftests/svg/text-layout-06.svg b/layout/reftests/svg/text-layout-06.svg new file mode 100644 index 00000000000..5e8e49b91df --- /dev/null +++ b/layout/reftests/svg/text-layout-06.svg @@ -0,0 +1,9 @@ + + + Testcase to check fill and stroke handling + + A B + diff --git a/layout/reftests/transform-3d/backface-visibility-1c.html b/layout/reftests/transform-3d/backface-visibility-1c.html new file mode 100644 index 00000000000..6129b443e02 --- /dev/null +++ b/layout/reftests/transform-3d/backface-visibility-1c.html @@ -0,0 +1,9 @@ + + + + +
+ Test Text +
+ + diff --git a/layout/reftests/transform-3d/reftest.list b/layout/reftests/transform-3d/reftest.list index 31e4e707070..be2b27f1f4a 100644 --- a/layout/reftests/transform-3d/reftest.list +++ b/layout/reftests/transform-3d/reftest.list @@ -31,6 +31,7 @@ fails-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == preserve3d-1a.html preser == rotate3d-2a.html rotatey-1-ref.html != backface-visibility-1a.html about:blank == backface-visibility-1b.html about:blank +== backface-visibility-1c.html about:blank != perspective-origin-1a.html rotatex-perspective-1a.html == perspective-origin-1b.html perspective-origin-1a.html == perspective-origin-2a.html perspective-origin-2-ref.html diff --git a/layout/reftests/transform/descendant-1.html b/layout/reftests/transform/descendant-1.html index 090084af2b9..6de49fc5f08 100644 --- a/layout/reftests/transform/descendant-1.html +++ b/layout/reftests/transform/descendant-1.html @@ -9,14 +9,14 @@ span { background: yellow; } - #div1 span {-moz-transform: translate(20px, 150px); - -moz-transform-origin: 0% 0%; + #div1 div {-moz-transform: translate(20px, 150px); + -moz-transform-origin: 0% 0%; }
- span 1 +
span 1
span 2 diff --git a/layout/reftests/transform/inline-1-ref.html b/layout/reftests/transform/inline-1-ref.html new file mode 100644 index 00000000000..84a2f6dd4a8 --- /dev/null +++ b/layout/reftests/transform/inline-1-ref.html @@ -0,0 +1,2 @@ + +This is some text
that is not transformed diff --git a/layout/reftests/transform/inline-1a.html b/layout/reftests/transform/inline-1a.html new file mode 100644 index 00000000000..fab499d72c1 --- /dev/null +++ b/layout/reftests/transform/inline-1a.html @@ -0,0 +1,3 @@ + +This is some text
+that is
not transformed diff --git a/layout/reftests/transform/reftest.list b/layout/reftests/transform/reftest.list index b57672abf0a..a8a70d98f8b 100644 --- a/layout/reftests/transform/reftest.list +++ b/layout/reftests/transform/reftest.list @@ -118,3 +118,5 @@ fails-if(Android) == stresstest-1.html stresstest-1-ref.html == table-1c.html table-1-ref.html == table-2a.html table-2-ref.html == table-2b.html table-2-ref.html +# Bug 722463 +== inline-1a.html inline-1-ref.html diff --git a/layout/style/nsDOMCSSAttrDeclaration.cpp b/layout/style/nsDOMCSSAttrDeclaration.cpp index ac9a50ee851..fd5bda8d211 100644 --- a/layout/style/nsDOMCSSAttrDeclaration.cpp +++ b/layout/style/nsDOMCSSAttrDeclaration.cpp @@ -49,6 +49,7 @@ #include "nsIPrincipal.h" #include "nsIURI.h" #include "nsNodeUtils.h" +#include "nsGenericElement.h" namespace css = mozilla::css; namespace dom = mozilla::dom; @@ -68,8 +69,26 @@ nsDOMCSSAttributeDeclaration::~nsDOMCSSAttributeDeclaration() MOZ_COUNT_DTOR(nsDOMCSSAttributeDeclaration); } +// If nsDOMCSSAttributeDeclaration is changed so that any additional +// fields are traversed by the cycle collector (for instance, if +// wrapper cache handling is changed) then CAN_SKIP must be updated. NS_IMPL_CYCLE_COLLECTION_1(nsDOMCSSAttributeDeclaration, mElement) +// nsDOMCSSAttributeDeclaration has only one cycle collected field, so +// if mElement is going to be skipped, the attribute declaration can't +// be part of a garbage cycle. +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDOMCSSAttributeDeclaration) + return !tmp->mElement || nsGenericElement::CanSkip(tmp->mElement, true); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDOMCSSAttributeDeclaration) + return !tmp->mElement || nsGenericElement::CanSkipInCC(tmp->mElement); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END + +// CanSkipThis returns false to avoid problems with incomplete unlinking. +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDOMCSSAttributeDeclaration) +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END + NS_INTERFACE_MAP_BEGIN(nsDOMCSSAttributeDeclaration) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsDOMCSSAttributeDeclaration) diff --git a/layout/style/nsDOMCSSAttrDeclaration.h b/layout/style/nsDOMCSSAttrDeclaration.h index fbe96390ad9..b5d0b911986 100644 --- a/layout/style/nsDOMCSSAttrDeclaration.h +++ b/layout/style/nsDOMCSSAttrDeclaration.h @@ -66,8 +66,8 @@ public: ~nsDOMCSSAttributeDeclaration(); NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMCSSAttributeDeclaration, - nsICSSDeclaration) + NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS_AMBIGUOUS(nsDOMCSSAttributeDeclaration, + nsICSSDeclaration) // If GetCSSDeclaration returns non-null, then the decl it returns // is owned by our current style rule. diff --git a/layout/style/test/test_priority_preservation.html b/layout/style/test/test_priority_preservation.html index 24f808320d4..346f4390da6 100644 --- a/layout/style/test/test_priority_preservation.html +++ b/layout/style/test/test_priority_preservation.html @@ -88,7 +88,8 @@ is(s.getPropertyPriority("z-index"), "", "z-index priority still stored"); // and overriding one element of that shorthand with some longhand -s.setProperty("font-style", "normal", ""); + // test omitting the third argument to setProperty too (bug 655478) +s.setProperty("font-style", "normal"); is(s.getPropertyValue("font-style"), "normal", "font-style overridden"); is(s.getPropertyPriority("font-style"), "", "font-style priority overridden"); diff --git a/layout/style/ua.css b/layout/style/ua.css index edfb8582d35..de336a9a1e7 100644 --- a/layout/style/ua.css +++ b/layout/style/ua.css @@ -77,6 +77,8 @@ /* Bug 722777 */ -moz-transform: inherit; -moz-transform-origin: inherit; + /* Bug 724750 */ + -moz-backface-visibility: inherit; } *|*::-moz-table-row { diff --git a/layout/svg/base/src/nsSVGAFrame.cpp b/layout/svg/base/src/nsSVGAFrame.cpp index 219783bdaf8..a0eb9a48034 100644 --- a/layout/svg/base/src/nsSVGAFrame.cpp +++ b/layout/svg/base/src/nsSVGAFrame.cpp @@ -179,7 +179,7 @@ nsSVGAFrame::GetCanvasTM() nsSVGContainerFrame *parent = static_cast(mParent); nsSVGAElement *content = static_cast(mContent); - gfxMatrix tm = content->PrependLocalTransformTo(parent->GetCanvasTM()); + gfxMatrix tm = content->PrependLocalTransformsTo(parent->GetCanvasTM()); mCanvasTM = new gfxMatrix(tm); } diff --git a/layout/svg/base/src/nsSVGClipPathFrame.cpp b/layout/svg/base/src/nsSVGClipPathFrame.cpp index dbef8f4075e..79f86c20339 100644 --- a/layout/svg/base/src/nsSVGClipPathFrame.cpp +++ b/layout/svg/base/src/nsSVGClipPathFrame.cpp @@ -327,8 +327,8 @@ nsSVGClipPathFrame::GetCanvasTM() nsSVGClipPathElement *content = static_cast(mContent); gfxMatrix tm = - content->PrependLocalTransformTo(mClipParentMatrix ? - *mClipParentMatrix : gfxMatrix()); + content->PrependLocalTransformsTo(mClipParentMatrix ? + *mClipParentMatrix : gfxMatrix()); return nsSVGUtils::AdjustMatrixForUnits(tm, &content->mEnumAttributes[nsSVGClipPathElement::CLIPPATHUNITS], diff --git a/layout/svg/base/src/nsSVGContainerFrame.cpp b/layout/svg/base/src/nsSVGContainerFrame.cpp index f5c092bf918..45930aa1ad4 100644 --- a/layout/svg/base/src/nsSVGContainerFrame.cpp +++ b/layout/svg/base/src/nsSVGContainerFrame.cpp @@ -266,7 +266,7 @@ nsSVGDisplayContainerFrame::GetBBoxContribution( nsIContent *content = kid->GetContent(); if (content->IsSVG() && !content->IsNodeOfType(nsINode::eTEXT)) { transform = static_cast(content)-> - PrependLocalTransformTo(aToBBoxUserspace); + PrependLocalTransformsTo(aToBBoxUserspace); } // We need to include zero width/height vertical/horizontal lines, so we have // to use UnionEdges, but we must special case the first bbox so that we don't diff --git a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp index 35169c61fbc..289919a713d 100644 --- a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp +++ b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp @@ -364,7 +364,7 @@ nsSVGForeignObjectFrame::UpdateCoveredRegion() // GetCanvasTM includes the x,y translation mRect = nsLayoutUtils::RoundGfxRectToAppRect( gfxRect(0.0, 0.0, w, h), - PresContext()->AppUnitsPerDevPixel()); + PresContext()->AppUnitsPerCSSPixel()); mCoveredRegion = ToCanvasBounds(gfxRect(0.0, 0.0, w, h), GetCanvasTM(), PresContext()); return NS_OK; @@ -495,7 +495,7 @@ nsSVGForeignObjectFrame::GetCanvasTM() nsSVGForeignObjectElement *content = static_cast(mContent); - gfxMatrix tm = content->PrependLocalTransformTo(parent->GetCanvasTM()); + gfxMatrix tm = content->PrependLocalTransformsTo(parent->GetCanvasTM()); mCanvasTM = new gfxMatrix(tm); } diff --git a/layout/svg/base/src/nsSVGGFrame.cpp b/layout/svg/base/src/nsSVGGFrame.cpp index ef949f53586..3f518cc3a2a 100644 --- a/layout/svg/base/src/nsSVGGFrame.cpp +++ b/layout/svg/base/src/nsSVGGFrame.cpp @@ -97,7 +97,7 @@ nsSVGGFrame::GetCanvasTM() nsSVGContainerFrame *parent = static_cast(mParent); nsSVGGraphicElement *content = static_cast(mContent); - gfxMatrix tm = content->PrependLocalTransformTo(parent->GetCanvasTM()); + gfxMatrix tm = content->PrependLocalTransformsTo(parent->GetCanvasTM()); mCanvasTM = new gfxMatrix(tm); } diff --git a/layout/svg/base/src/nsSVGGlyphFrame.cpp b/layout/svg/base/src/nsSVGGlyphFrame.cpp index 3a372b960d7..e8aebd2d8bf 100644 --- a/layout/svg/base/src/nsSVGGlyphFrame.cpp +++ b/layout/svg/base/src/nsSVGGlyphFrame.cpp @@ -374,7 +374,7 @@ nsSVGGlyphFrame::PaintSVG(nsSVGRenderState *aContext, iter.SetInitialMatrix(gfx); nsRefPtr strokePattern; - DrawMode drawMode = SetupCairoState(gfx, &strokePattern); + DrawMode drawMode = SetupCairoState(gfx, getter_AddRefs(strokePattern)); if (drawMode) { DrawCharacters(&iter, gfx, drawMode, strokePattern); @@ -491,7 +491,7 @@ nsSVGGlyphFrame::UpdateCoveredRegion() if (!extent.IsEmpty()) { mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent, - PresContext()->AppUnitsPerDevPixel()); + PresContext()->AppUnitsPerCSSPixel()); } // See bug 614732 comment 32. @@ -884,39 +884,45 @@ nsSVGGlyphFrame::GetBaselineOffset(float aMetricsScale) } DrawMode -nsSVGGlyphFrame::SetupCairoState(gfxContext *context, nsRefPtr *strokePattern) { +nsSVGGlyphFrame::SetupCairoState(gfxContext *aContext, gfxPattern **aStrokePattern) +{ DrawMode toDraw = DrawMode(0); const nsStyleSVG* style = GetStyleSVG(); if (HasStroke()) { - gfxContextMatrixAutoSaveRestore matrixRestore(context); - context->IdentityMatrix(); + gfxContextMatrixAutoSaveRestore matrixRestore(aContext); + aContext->IdentityMatrix(); toDraw = DrawMode(toDraw | gfxFont::GLYPH_STROKE); - SetupCairoStrokeHitGeometry(context); + SetupCairoStrokeHitGeometry(aContext); float opacity = style->mStrokeOpacity; nsSVGPaintServerFrame *ps = GetPaintServer(&style->mStroke, nsSVGEffects::StrokeProperty()); + nsRefPtr strokePattern; + if (ps) { // Gradient or Pattern: can get pattern directly from frame - *strokePattern = ps->GetPaintServerPattern(this, opacity); + strokePattern = ps->GetPaintServerPattern(this, opacity); + } - NS_ASSERTION(*strokePattern, "No pattern returned from paint server"); - } else { + if (!strokePattern) { nscolor color; - nsSVGUtils::GetFallbackOrPaintColor(context, GetStyleContext(), + nsSVGUtils::GetFallbackOrPaintColor(aContext, GetStyleContext(), &nsStyleSVG::mStroke, &opacity, &color); - *strokePattern = new gfxPattern(gfxRGBA(NS_GET_R(color) / 255.0, - NS_GET_G(color) / 255.0, - NS_GET_B(color) / 255.0, - NS_GET_A(color) / 255.0 * opacity)); + strokePattern = new gfxPattern(gfxRGBA(NS_GET_R(color) / 255.0, + NS_GET_G(color) / 255.0, + NS_GET_B(color) / 255.0, + NS_GET_A(color) / 255.0 * opacity)); } + + *aStrokePattern = nsnull; + strokePattern.swap(*aStrokePattern); } - if (SetupCairoFill(context)) { + if (SetupCairoFill(aContext)) { toDraw = DrawMode(toDraw | gfxFont::GLYPH_FILL); } diff --git a/layout/svg/base/src/nsSVGGlyphFrame.h b/layout/svg/base/src/nsSVGGlyphFrame.h index c9698635712..e0523f7d6d2 100644 --- a/layout/svg/base/src/nsSVGGlyphFrame.h +++ b/layout/svg/base/src/nsSVGGlyphFrame.h @@ -268,7 +268,8 @@ protected: bool mTrimTrailingWhitespace; private: - DrawMode SetupCairoState(gfxContext *context, nsRefPtr *strokePattern); + DrawMode SetupCairoState(gfxContext *aContext, + gfxPattern **aStrokePattern); }; #endif diff --git a/layout/svg/base/src/nsSVGImageFrame.cpp b/layout/svg/base/src/nsSVGImageFrame.cpp index 07e1eeb83dd..b0930b26ca4 100644 --- a/layout/svg/base/src/nsSVGImageFrame.cpp +++ b/layout/svg/base/src/nsSVGImageFrame.cpp @@ -492,7 +492,7 @@ nsSVGImageFrame::UpdateCoveredRegion() if (!extent.IsEmpty()) { mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent, - PresContext()->AppUnitsPerDevPixel()); + PresContext()->AppUnitsPerCSSPixel()); } // See bug 614732 comment 32. diff --git a/layout/svg/base/src/nsSVGInnerSVGFrame.cpp b/layout/svg/base/src/nsSVGInnerSVGFrame.cpp index 67016895cdf..6e3a98cd2fc 100644 --- a/layout/svg/base/src/nsSVGInnerSVGFrame.cpp +++ b/layout/svg/base/src/nsSVGInnerSVGFrame.cpp @@ -251,7 +251,7 @@ nsSVGInnerSVGFrame::GetCanvasTM() nsSVGContainerFrame *parent = static_cast(mParent); nsSVGSVGElement *content = static_cast(mContent); - gfxMatrix tm = content->PrependLocalTransformTo(parent->GetCanvasTM()); + gfxMatrix tm = content->PrependLocalTransformsTo(parent->GetCanvasTM()); mCanvasTM = new gfxMatrix(tm); } diff --git a/layout/svg/base/src/nsSVGPathGeometryFrame.cpp b/layout/svg/base/src/nsSVGPathGeometryFrame.cpp index e989c287eb5..e235a67a146 100644 --- a/layout/svg/base/src/nsSVGPathGeometryFrame.cpp +++ b/layout/svg/base/src/nsSVGPathGeometryFrame.cpp @@ -217,7 +217,7 @@ nsSVGPathGeometryFrame::UpdateCoveredRegion() nsSVGUtils::eBBoxIncludeStroke | nsSVGUtils::eBBoxIgnoreStrokeIfNone | nsSVGUtils::eBBoxIncludeMarkers); mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent, - PresContext()->AppUnitsPerDevPixel()); + PresContext()->AppUnitsPerCSSPixel()); // See bug 614732 comment 32. mCoveredRegion = nsSVGUtils::TransformFrameRectToOuterSVG( @@ -384,7 +384,7 @@ nsSVGPathGeometryFrame::GetCanvasTM() nsSVGContainerFrame *parent = static_cast(mParent); nsSVGGraphicElement *content = static_cast(mContent); - return content->PrependLocalTransformTo(parent->GetCanvasTM()); + return content->PrependLocalTransformsTo(parent->GetCanvasTM()); } //---------------------------------------------------------------------- diff --git a/layout/svg/base/src/nsSVGSwitchFrame.cpp b/layout/svg/base/src/nsSVGSwitchFrame.cpp index a8cb6bbde6e..f57c5798a39 100644 --- a/layout/svg/base/src/nsSVGSwitchFrame.cpp +++ b/layout/svg/base/src/nsSVGSwitchFrame.cpp @@ -208,7 +208,7 @@ nsSVGSwitchFrame::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace, gfxMatrix transform = aToBBoxUserspace; if (content->IsSVG()) { transform = static_cast(content)-> - PrependLocalTransformTo(aToBBoxUserspace); + PrependLocalTransformsTo(aToBBoxUserspace); } return svgKid->GetBBoxContribution(transform, aFlags); } diff --git a/layout/svg/base/src/nsSVGTextFrame.cpp b/layout/svg/base/src/nsSVGTextFrame.cpp index 5c90cd1b89f..fa1c64195b4 100644 --- a/layout/svg/base/src/nsSVGTextFrame.cpp +++ b/layout/svg/base/src/nsSVGTextFrame.cpp @@ -274,7 +274,7 @@ nsSVGTextFrame::GetCanvasTM() nsSVGContainerFrame *parent = static_cast(mParent); nsSVGGraphicElement *content = static_cast(mContent); - gfxMatrix tm = content->PrependLocalTransformTo(parent->GetCanvasTM()); + gfxMatrix tm = content->PrependLocalTransformsTo(parent->GetCanvasTM()); mCanvasTM = new gfxMatrix(tm); } diff --git a/layout/svg/base/src/nsSVGTextPathFrame.cpp b/layout/svg/base/src/nsSVGTextPathFrame.cpp index dcfdf53b39e..4b9c42895c6 100644 --- a/layout/svg/base/src/nsSVGTextPathFrame.cpp +++ b/layout/svg/base/src/nsSVGTextPathFrame.cpp @@ -147,7 +147,7 @@ nsSVGTextPathFrame::GetFlattenedPath() nsSVGPathGeometryElement *element = static_cast(path->GetContent()); - return element->GetFlattenedPath(element->PrependLocalTransformTo(gfxMatrix())); + return element->GetFlattenedPath(element->PrependLocalTransformsTo(gfxMatrix())); } return nsnull; } diff --git a/layout/svg/base/src/nsSVGUtils.cpp b/layout/svg/base/src/nsSVGUtils.cpp index e6ced730902..b2df85abe84 100644 --- a/layout/svg/base/src/nsSVGUtils.cpp +++ b/layout/svg/base/src/nsSVGUtils.cpp @@ -451,24 +451,21 @@ nsSVGUtils::GetNearestViewportElement(nsIContent *aContent) return nsnull; } -gfxMatrix -nsSVGUtils::GetCTM(nsSVGElement *aElement, bool aScreenCTM) +static gfxMatrix +GetCTMInternal(nsSVGElement *aElement, bool aScreenCTM, bool aHaveRecursed) { nsIDocument* currentDoc = aElement->GetCurrentDoc(); - if (currentDoc) { - // Flush all pending notifications so that our frames are up to date - currentDoc->FlushPendingNotifications(Flush_Layout); - } - gfxMatrix matrix = aElement->PrependLocalTransformTo(gfxMatrix()); + gfxMatrix matrix = aElement->PrependLocalTransformsTo(gfxMatrix(), + aHaveRecursed ? nsSVGElement::eAllTransforms : nsSVGElement::eUserSpaceToParent); nsSVGElement *element = aElement; nsIContent *ancestor = aElement->GetFlattenedTreeParent(); while (ancestor && ancestor->IsSVG() && ancestor->Tag() != nsGkAtoms::foreignObject) { element = static_cast(ancestor); - matrix *= element->PrependLocalTransformTo(gfxMatrix()); // i.e. *A*ppend - if (!aScreenCTM && EstablishesViewport(element)) { + matrix *= element->PrependLocalTransformsTo(gfxMatrix()); // i.e. *A*ppend + if (!aScreenCTM && nsSVGUtils::EstablishesViewport(element)) { if (!element->NodeInfo()->Equals(nsGkAtoms::svg, kNameSpaceID_SVG) && !element->NodeInfo()->Equals(nsGkAtoms::symbol, kNameSpaceID_SVG)) { NS_ERROR("New (SVG > 1.1) SVG viewport establishing element?"); @@ -490,7 +487,8 @@ nsSVGUtils::GetCTM(nsSVGElement *aElement, bool aScreenCTM) if (element->Tag() != nsGkAtoms::svg) { return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular } - return matrix * GetCTM(static_cast(ancestor), true); + return + matrix * GetCTMInternal(static_cast(ancestor), true, true); } // XXX this does not take into account CSS transform, or that the non-SVG // content that we've hit may itself be inside an SVG foreignObject higher up @@ -510,6 +508,17 @@ nsSVGUtils::GetCTM(nsSVGElement *aElement, bool aScreenCTM) return matrix * gfxMatrix().Translate(gfxPoint(x, y)); } +gfxMatrix +nsSVGUtils::GetCTM(nsSVGElement *aElement, bool aScreenCTM) +{ + nsIDocument* currentDoc = aElement->GetCurrentDoc(); + if (currentDoc) { + // Flush all pending notifications so that our frames are up to date + currentDoc->FlushPendingNotifications(Flush_Layout); + } + return GetCTMInternal(aElement, aScreenCTM, false); +} + nsSVGDisplayContainerFrame* nsSVGUtils::GetNearestSVGViewport(nsIFrame *aFrame) { @@ -1185,12 +1194,13 @@ nsSVGUtils::TransformOuterSVGPointToChildFrame(nsPoint aPoint, const gfxMatrix& aFrameToCanvasTM, nsPresContext* aPresContext) { - gfxMatrix devToUser = aFrameToCanvasTM; - devToUser.Invert(); - NS_ABORT_IF_FALSE(!devToUser.IsSingular(), "should not get here"); + NS_ABORT_IF_FALSE(!aFrameToCanvasTM.IsSingular(), + "Callers must not pass a singular matrix"); + gfxMatrix canvasDevToFrameUserSpace = aFrameToCanvasTM; + canvasDevToFrameUserSpace.Invert(); gfxPoint devPt = gfxPoint(aPoint.x, aPoint.y) / aPresContext->AppUnitsPerDevPixel(); - gfxPoint userPt = devToUser.Transform(devPt).Round(); + gfxPoint userPt = canvasDevToFrameUserSpace.Transform(devPt).Round(); gfxPoint appPt = userPt * aPresContext->AppUnitsPerCSSPixel(); userPt.x = clamped(appPt.x, gfxFloat(nscoord_MIN), gfxFloat(nscoord_MAX)); userPt.y = clamped(appPt.y, gfxFloat(nscoord_MIN), gfxFloat(nscoord_MAX)); @@ -1383,7 +1393,17 @@ nsSVGUtils::GetBBox(nsIFrame *aFrame, PRUint32 aFlags) } svg = do_QueryFrame(aFrame); } - bbox = svg->GetBBoxContribution(gfxMatrix(), aFlags); + gfxMatrix matrix; + if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) { + // The spec says getBBox "Returns the tight bounding box in *current user + // space*". So we should really be doing this for all elements, but that + // needs investigation to check that we won't break too much content. + NS_ABORT_IF_FALSE(aFrame->GetContent()->IsSVG(), "bad cast"); + nsSVGElement *element = static_cast(aFrame->GetContent()); + matrix = element->PrependLocalTransformsTo(matrix, + nsSVGElement::eChildToUserSpace); + } + bbox = svg->GetBBoxContribution(matrix, aFlags); } else { bbox = nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame); } diff --git a/layout/svg/crashtests/725918-1.svg b/layout/svg/crashtests/725918-1.svg new file mode 100644 index 00000000000..5ebdf33f691 --- /dev/null +++ b/layout/svg/crashtests/725918-1.svg @@ -0,0 +1,4 @@ + + t + + diff --git a/layout/svg/crashtests/crashtests.list b/layout/svg/crashtests/crashtests.list index ea35804fa5c..ed07648dd11 100644 --- a/layout/svg/crashtests/crashtests.list +++ b/layout/svg/crashtests/crashtests.list @@ -123,3 +123,4 @@ load 709920-1.svg load 709920-2.svg load 713413-1.svg load 722003-1.svg +load 725918-1.svg diff --git a/layout/xul/test/browser_bug703210.js b/layout/xul/test/browser_bug703210.js index 5a333067188..c6a61e7ba7d 100644 --- a/layout/xul/test/browser_bug703210.js +++ b/layout/xul/test/browser_bug703210.js @@ -1,5 +1,6 @@ function test() { waitForExplicitFinish(); + gBrowser.selectedTab = gBrowser.addTab(); SpecialPowers.setIntPref("ui.tooltipDelay", 0); @@ -60,8 +61,10 @@ function test() { EventUtils.synthesizeMouseAtCenter(p1, { type: "mousemove" }, win); } - gBrowser.selectedBrowser.addEventListener("load", - function () { setTimeout(onLoad, 0); }, true); + gBrowser.selectedBrowser.addEventListener("load", function loadListener() { + gBrowser.selectedBrowser.removeEventListener("load", loadListener, true); + setTimeout(onLoad, 0); + }, true); content.location = "data:text/html," + "

This paragraph has a tooltip.

" + diff --git a/media/libjpeg/MOZCHANGES b/media/libjpeg/MOZCHANGES index 78c2752a1b2..17267e5a5de 100644 --- a/media/libjpeg/MOZCHANGES +++ b/media/libjpeg/MOZCHANGES @@ -6,9 +6,15 @@ To upgrade to a new revision of libjpeg-turbo, do the following: * In a clean clone of mozilla-central, run the following commands - $ rm -rf jpeg - $ svn export --ignore-externals /path/to/libjpeg-turbo jpeg - $ cd jpeg + $ rm -rf media/libjpeg + $ svn export --ignore-externals /path/to/libjpeg-turbo media/libjpeg + $ cd media/libjpeg + +* Copy win/jsimdcfg.inc to simd/. + +* Since libjpeg-turbo normally creates config.h and jconfig.h at build time and + we use pre-generated versions, changes to jconfig.h.in and win/config.h.in + should be looked for and noted for later inclusion. * Now look through the new files and rm any which are npotb. When I upgraded to libjpeg-turbo 1.1.0, the only files I kept which didn't match @@ -32,13 +38,19 @@ To upgrade to a new revision of libjpeg-turbo, do the following: diff <(ls *.c | sort) <(grep -o '\w*\.c' Makefile.in | sort) - of course, libjpeg-turbo might have added some new source files, so you'll + Of course, libjpeg-turbo might have added some new source files, so you'll have to look though and figure out which of these files to keep. * Restore files modified in the Mozilla repository. - $ hg revert --no-backup Makefile.in jconfig.h jmorecfg.h simd/Makefile.in \ - simd/jsimdcfg.inc jchuff.c jdhuff.c jdhuff.h MOZCHANGES + $ hg revert --no-backup config.h jconfig.h Makefile.in MOZCHANGES \ + mozilla.diff simd/Makefile.in + +* Update config.h and jconfig.h as noted previously. + +* Apply Mozilla-specific changes to upstream files. + + $ patch -p0 -i mozilla.diff * Update Makefile.in to build any new files. @@ -47,6 +59,18 @@ To upgrade to a new revision of libjpeg-turbo, do the following: $ hg addremove +== February 10, 2012 (libjpeg-turbo v1.2.0 r807 2012-02-10) == + +* Imported jchuff.c, jdhuff.c, jdhuff.h under new licensing. + +* Created mozilla.diff for the required jmorecfg.h changes and to allow for any + future changes made by Mozilla to upstream files. + +* Removed the following files which are unused by the Mozilla build: + + cderror.h, cdjpeg.h, jconfig.h.in, transupp.h, simd/jsimdcfg.inc.h + + == March 28, 2011 (initial commit, libjpeg-turbo v1.1.0 r469 2011-02-27) == * Modified jmorecfg.h to define UINT8, UINT16, INT16, and INT32 in terms of diff --git a/media/libjpeg/Makefile.in b/media/libjpeg/Makefile.in index 1d82ea25790..f7507755bb7 100644 --- a/media/libjpeg/Makefile.in +++ b/media/libjpeg/Makefile.in @@ -103,6 +103,7 @@ CSRCS += \ jcphuff.c \ jcprepct.c \ jcsample.c \ + jctrans.c \ $(NULL) AS=$(LIBJPEG_TURBO_AS) @@ -115,14 +116,20 @@ ifeq ($(AS),yasm) endif # No SIMD support? -ifeq (,$(LIBJPEG_TURBO_X86_ASM)$(LIBJPEG_TURBO_X64_ASM)) +ifeq (,$(LIBJPEG_TURBO_X86_ASM)$(LIBJPEG_TURBO_X64_ASM)$(LIBJPEG_TURBO_ARM_ASM)) CSRCS += jsimd_none.c endif +ifeq (1,$(LIBJPEG_TURBO_ARM_ASM)) + CSRCS += simd/jsimd_arm.c + SSRCS += simd/jsimd_arm_neon.S +endif + ifeq (1,$(LIBJPEG_TURBO_X64_ASM)) CSRCS += simd/jsimd_x86_64.c ASFILES += \ simd/jccolss2-64.asm \ + simd/jcgrass2-64.asm \ simd/jcqnts2f-64.asm \ simd/jcqnts2i-64.asm \ simd/jcsamss2-64.asm \ @@ -144,6 +151,8 @@ ifeq (1,$(LIBJPEG_TURBO_X86_ASM)) ASFILES += \ simd/jccolmmx.asm \ simd/jccolss2.asm \ + simd/jcgrammx.asm \ + simd/jcgrass2.asm \ simd/jcqnt3dn.asm \ simd/jcqntmmx.asm \ simd/jcqnts2f.asm \ @@ -176,14 +185,14 @@ ifeq (1,$(LIBJPEG_TURBO_X86_ASM)) $(NULL) endif -EXPORTS = \ - jconfig.h \ - jerror.h \ - jinclude.h \ - jmorecfg.h \ - jpegint.h \ - jpeglib.h \ - $(NULL) +EXPORTS = \ + jconfig.h \ + jerror.h \ + jinclude.h \ + jmorecfg.h \ + jpegint.h \ + jpeglib.h \ + $(NULL) # need static lib for some of the libimg componentry to link properly FORCE_STATIC_LIB = 1 diff --git a/media/libjpeg/README b/media/libjpeg/README index 2ead09e64a4..0e9b4295e5b 100644 --- a/media/libjpeg/README +++ b/media/libjpeg/README @@ -1,7 +1,8 @@ -libjpeg-turbo note: This file is mostly taken from the libjpeg v8b README -file, and it is included only for reference. Some parts of it may not apply to -libjpeg-turbo. Please see README-turbo.txt for information specific to the -turbo version. +libjpeg-turbo note: This file contains portions of the libjpeg v6b and v8 +README files, with additional wordsmithing by The libjpeg-turbo Project. +It is included only for reference, as some parts of it may not apply to +libjpeg-turbo. Please see README-turbo.txt for information specific to +libjpeg-turbo. The Independent JPEG Group's JPEG software @@ -62,7 +63,7 @@ OVERVIEW This package contains C software to implement JPEG image encoding, decoding, and transcoding. JPEG (pronounced "jay-peg") is a standardized compression method for full-color and gray-scale images. JPEG's strong suit is compressing -photographic images or other types of images which have smooth color and +photographic images or other types of images that have smooth color and brightness transitions between neighboring pixels. Images with sharp lines or other abrupt features may not compress well with JPEG, and a higher JPEG quality may have to be used to avoid visible compression artifacts with such @@ -256,8 +257,8 @@ ARCHIVE LOCATIONS The "official" archive site for this software is www.ijg.org. The most recent released version can always be found there in directory "files". This particular version will be archived as -http://www.ijg.org/files/jpegsrc.v8b.tar.gz, and in Windows-compatible -"zip" archive format as http://www.ijg.org/files/jpegsr8b.zip. +http://www.ijg.org/files/jpegsrc.v8d.tar.gz, and in Windows-compatible +"zip" archive format as http://www.ijg.org/files/jpegsr8d.zip. The JPEG FAQ (Frequently Asked Questions) article is a source of some general information about JPEG. @@ -274,7 +275,7 @@ FILE FORMAT WARS ================ The ISO JPEG standards committee actually promotes different formats like -"JPEG 2000" or "JPEG XR" which are incompatible with original DCT-based +"JPEG 2000" or "JPEG XR", which are incompatible with original DCT-based JPEG. IJG therefore does not support these formats (see REFERENCES). Indeed, one of the original reasons for developing this free software was to help force convergence on common, interoperable format standards for JPEG files. @@ -286,4 +287,4 @@ image files indefinitely.) TO DO ===== -Please send bug reports, offers of help, etc. to jpeg-info@uc.ag. +Please send bug reports, offers of help, etc. to jpeg-info@jpegclub.org. diff --git a/media/libjpeg/README-turbo.txt b/media/libjpeg/README-turbo.txt index fec34e0cdab..899a3681987 100644 --- a/media/libjpeg/README-turbo.txt +++ b/media/libjpeg/README-turbo.txt @@ -2,51 +2,80 @@ ** Background ******************************************************************************* -libjpeg-turbo is a derivative of libjpeg which uses SIMD instructions (MMX, -SSE2, etc.) to accelerate baseline JPEG compression and decompression on x86 -and x86-64 systems. On such systems, libjpeg-turbo is generally 2-4x as fast -as the unmodified version of libjpeg, all else being equal. +libjpeg-turbo is a derivative of libjpeg that uses SIMD instructions (MMX, +SSE2, NEON) to accelerate baseline JPEG compression and decompression on x86, +x86-64, and ARM systems. On such systems, libjpeg-turbo is generally 2-4x as +fast as the unmodified version of libjpeg, all else being equal. libjpeg-turbo was originally based on libjpeg/SIMD by Miyasaka Masaru, but the TigerVNC and VirtualGL projects made numerous enhancements to the codec in 2009, including improved support for Mac OS X, 64-bit support, support for -32-bit and big endian pixel formats (RGBX, XBGR, etc.), accelerated Huffman -encoding/decoding, and various bug fixes. The goal was to produce a fully open -source codec that could replace the partially closed source TurboJPEG/IPP codec -used by VirtualGL and TurboVNC. libjpeg-turbo generally performs in the range -of 80-120% of TurboJPEG/IPP. It is faster in some areas but slower in others. +32-bit and big-endian pixel formats (RGBX, XBGR, etc.), accelerated Huffman +encoding/decoding, and various bug fixes. The goal was to produce a fully +open-source codec that could replace the partially closed-source TurboJPEG/IPP +codec used by VirtualGL and TurboVNC. libjpeg-turbo generally achieves 80-120% +of the performance of TurboJPEG/IPP. It is faster in some areas but slower in +others. In early 2010, libjpeg-turbo spun off into its own independent project, with the goal of making high-speed JPEG compression/decompression technology -available to a broader range of users and developers. The libjpeg-turbo shared -libraries can be used as drop-in replacements for libjpeg on most systems. +available to a broader range of users and developers. ******************************************************************************* ** License ******************************************************************************* -The TurboJPEG/OSS wrapper, as well as some of the optimizations to the Huffman -encoder (jchuff.c) and decoder (jdhuff.c), were borrowed from VirtualGL, and -thus any distribution of libjpeg-turbo which includes those files must, as a -whole, be subject to the terms of the wxWindows Library Licence, Version 3.1. -A copy of this license can be found in this directory under LICENSE.txt. The -wxWindows Library License is based on the LGPL but includes provisions which -allow the Library to be statically linked into proprietary libraries and -applications without requiring the resulting binaries to be distributed under -the terms of the LGPL. +Most of libjpeg-turbo inherits the non-restrictive, BSD-style license used by +libjpeg (see README.) The TurboJPEG/OSS wrapper (both C and Java versions) and +associated test programs bear a similar license, which is reproduced below: -The rest of the source code, apart from TurboJPEG/OSS and the Huffman codec -optimizations, falls under a less restrictive, BSD-style license (see README.) -You can choose to distribute libjpeg-turbo, as a whole, under this BSD-style -license by simply removing TurboJPEG/OSS and replacing the optimized jchuff.c -and jdhuff.c with their unoptimized counterparts from the libjpeg v6b source. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +- Neither the name of the libjpeg-turbo Project nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. ******************************************************************************* ** Using libjpeg-turbo ******************************************************************************* +libjpeg-turbo includes two APIs that can be used to compress and decompress +JPEG images: + + TurboJPEG API: This API provides an easy-to-use interface for compressing + and decompressing JPEG images in memory. It also provides some functionality + that would not be straightforward to achieve using the underlying libjpeg + API, such as generating planar YUV images and performing multiple + simultaneous lossless transforms on an image. The Java interface for + libjpeg-turbo is written on top of the TurboJPEG API. + + libjpeg API: This is the de facto industry-standard API for compressing and + decompressing JPEG images. It is more difficult to use than the TurboJPEG + API but also more powerful. libjpeg-turbo is both API/ABI-compatible and + mathematically compatible with libjpeg v6b. It can also optionally be + configured to be API/ABI-compatible with libjpeg v7 and v8 (see below.) + + ============================= Replacing libjpeg at Run Time ============================= @@ -72,13 +101,13 @@ NOTE: {lib} can be lib, lib32, lib64, or lib/64, depending on the O/S and architecture. System administrators can also replace the libjpeg sym links in /usr/{lib} with -links to the libjpeg dynamic library located in /opt/libjpeg-turbo/{lib}. This -will effectively accelerate every dynamically linked libjpeg application on the -system. +links to the libjpeg-turbo dynamic library located in /opt/libjpeg-turbo/{lib}. +This will effectively accelerate every application that uses the libjpeg +dynamic library on the system. The libjpeg-turbo SDK for Visual C++ installs the libjpeg-turbo DLL -(jpeg62.dll, jpeg7.dll, or jpeg8.dll, depending on whether libjpeg v6b, v7, or -v8 emulation is enabled) into c:\libjpeg-turbo[64]\bin, and the PATH +(jpeg62.dll, jpeg7.dll, or jpeg8.dll, depending on whether it was built with +libjpeg v6b, v7, or v8 emulation) into c:\libjpeg-turbo[64]\bin, and the PATH environment variable can be modified such that this directory is searched before any others that might contain a libjpeg DLL. However, if a libjpeg DLL exists in an application's install directory, then Windows will load this @@ -88,16 +117,16 @@ version of this DLL and copy c:\libjpeg-turbo[64]\bin\jpeg*.dll into the application's install directory to accelerate it. The version of the libjpeg-turbo DLL distributed in the libjpeg-turbo SDK for -Visual C++ requires the Visual C++ 2008 C run time DLL (msvcr90.dll). +Visual C++ requires the Visual C++ 2008 C run-time DLL (msvcr90.dll). msvcr90.dll ships with more recent versions of Windows, but users of older Windows releases can obtain it from the Visual C++ 2008 Redistributable Package, which is available as a free download from Microsoft's web site. -NOTE: Features of libjpeg which require passing a C run time structure, such +NOTE: Features of libjpeg that require passing a C run-time structure, such as a file handle, from an application to libjpeg will probably not work with the version of the libjpeg-turbo DLL distributed in the libjpeg-turbo SDK for Visual C++, unless the application is also built to use the Visual C++ 2008 C -run time DLL. In particular, this affects jpeg_stdio_dest() and +run-time DLL. In particular, this affects jpeg_stdio_dest() and jpeg_stdio_src(). Mac applications typically embed their own copies of the libjpeg dylib inside @@ -117,7 +146,7 @@ Replacing TurboJPEG/IPP libjpeg-turbo is a drop-in replacement for the TurboJPEG/IPP SDK used by VirtualGL 2.1.x and TurboVNC 0.6 (and prior.) libjpeg-turbo contains a wrapper library (TurboJPEG/OSS) that emulates the TurboJPEG API using libjpeg-turbo -instead of the closed source Intel Performance Primitives. You can replace the +instead of the closed-source Intel Performance Primitives. You can replace the TurboJPEG/IPP package on Linux systems with the libjpeg-turbo package in order to make existing releases of VirtualGL 2.1.x and TurboVNC 0.x use the new codec at run time. Note that the 64-bit libjpeg-turbo packages contain only 64-bit @@ -128,7 +157,7 @@ both the 64-bit and 32-bit versions of libjpeg-turbo. You can also build the VirtualGL 2.1.x and TurboVNC 0.6 source code with the libjpeg-turbo SDK instead of TurboJPEG/IPP. It should work identically. libjpeg-turbo also includes static library versions of TurboJPEG/OSS, which -are used to build TurboVNC 1.0 and later. +are used to build VirtualGL 2.2 and TurboVNC 1.0 and later. ======================================== Using libjpeg-turbo in Your Own Programs @@ -179,9 +208,9 @@ libjpeg-turbo) or jpeg-static.lib (to use the static version of libjpeg-turbo.) Colorspace Extensions ===================== -libjpeg-turbo includes extensions which allow JPEG images to be compressed -directly from (and decompressed directly to) buffers which use BGR, BGRX, -RGBX, XBGR, and XRGB pixel ordering. This is implemented with six new +libjpeg-turbo includes extensions that allow JPEG images to be compressed +directly from (and decompressed directly to) buffers that use BGR, BGRX, +RGBX, XBGR, and XRGB pixel ordering. This is implemented with ten new colorspace constants: JCS_EXT_RGB /* red/green/blue */ @@ -190,11 +219,15 @@ colorspace constants: JCS_EXT_BGRX /* blue/green/red/x */ JCS_EXT_XBGR /* x/blue/green/red */ JCS_EXT_XRGB /* x/red/green/blue */ + JCS_EXT_RGBA /* red/green/blue/alpha */ + JCS_EXT_BGRA /* blue/green/red/alpha */ + JCS_EXT_ABGR /* alpha/blue/green/red */ + JCS_EXT_ARGB /* alpha/red/green/blue */ Setting cinfo.in_color_space (compression) or cinfo.out_color_space (decompression) to one of these values will cause libjpeg-turbo to read the red, green, and blue values from (or write them to) the appropriate position in -the pixel when YUV conversion is performed. +the pixel when compressing from/decompressing to an RGB buffer. Your application can check for the existence of these extensions at compile time with: @@ -204,33 +237,41 @@ time with: At run time, attempting to use these extensions with a version of libjpeg that doesn't support them will result in a "Bogus input colorspace" error. +When using the RGBX, BGRX, XBGR, and XRGB colorspaces during decompression, the +X byte is undefined, and in order to ensure the best performance, libjpeg-turbo +can set that byte to whatever value it wishes. If an application expects the X +byte to be used as an alpha channel, then it should specify JCS_EXT_RGBA, +JCS_EXT_BGRA, JCS_EXT_ABGR, or JCS_EXT_ARGB. When these colorspace constants +are used, the X byte is guaranteed to be 0xFF, which is interpreted as opaque. + +Your application can check for the existence of the alpha channel colorspace +extensions at compile time with: + + #ifdef JCS_ALPHA_EXTENSIONS + +jcstest.c, located in the libjpeg-turbo source tree, demonstrates how to check +for the existence of the colorspace extensions at compile time and run time. + ================================= libjpeg v7 and v8 API/ABI support ================================= -libjpeg v7 and v8 added new features to the API/ABI, and, unfortunately, the -compression and decompression structures were extended in a backward- -incompatible manner to accommodate these features. Thus, programs which are +With libjpeg v7 and v8, new features were added that necessitated extending the +compression and decompression structures. Unfortunately, due to the exposed +nature of those structures, extending them also necessitated breaking backward +ABI compatibility with previous libjpeg releases. Thus, programs that are built to use libjpeg v7 or v8 did not work with libjpeg-turbo, since it is based on the libjpeg v6b code base. Although libjpeg v7 and v8 are still not as widely used as v6b, enough programs (including a few Linux distros) have made the switch that it was desirable to provide support for the libjpeg v7/v8 -API/ABI in libjpeg-turbo. - -Some of the libjpeg v7 and v8 features -- DCT scaling, to name one -- involve -deep modifications to the code which cannot be accommodated by libjpeg-turbo -without either breaking compatibility with libjpeg v6b or producing an -unsupportable mess. In order to fully support libjpeg v8 with all of its -features, we would have to essentially port the SIMD extensions to the libjpeg -v8 code base and maintain two separate code trees. We are hesitant to do this -until/unless the newer libjpeg code bases garner more community support and -involvement and until/unless we have some notion of whether future libjpeg -releases will also be backward-incompatible. +API/ABI in libjpeg-turbo. Although libjpeg-turbo can now be configured as a +drop-in replacement for libjpeg v7 or v8, it should be noted that not all of +the features in libjpeg v7 and v8 are supported (see below.) By passing an argument of --with-jpeg7 or --with-jpeg8 to configure, or an argument of -DWITH_JPEG7=1 or -DWITH_JPEG8=1 to cmake, you can build a version -of libjpeg-turbo which emulates the libjpeg v7 or v8 API/ABI, so that programs -which are built against libjpeg v7 or v8 can be run with libjpeg-turbo. The +of libjpeg-turbo that emulates the libjpeg v7 or v8 API/ABI, so that programs +that are built against libjpeg v7 or v8 can be run with libjpeg-turbo. The following section describes which libjpeg v7+ features are supported and which aren't. @@ -264,6 +305,16 @@ Not supported: -- libjpeg: DCT scaling in compressor cinfo.scale_num and cinfo.scale_denom are silently ignored. + There is no technical reason why DCT scaling cannot be supported, but + without the SmartScale extension (see below), it would only be able to + down-scale using ratios of 1/2, 8/15, 4/7, 8/13, 2/3, 8/11, 4/5, and 8/9, + which is of limited usefulness. + +-- libjpeg: SmartScale + cinfo.block_size is silently ignored. + SmartScale is an extension to the JPEG format that allows for DCT block + sizes other than 8x8. It would be difficult to support this feature while + retaining backward compatibility with libjpeg v6b. -- libjpeg: IDCT scaling extensions in decompressor libjpeg-turbo still supports IDCT scaling with scaling factors of 1/2, 1/4, @@ -271,9 +322,14 @@ Not supported: -- libjpeg: Fancy downsampling in compressor cinfo.do_fancy_downsampling is silently ignored. + This requires the DCT scaling feature, which is not supported. -- jpegtran: Scaling - Seems to depend on the DCT scaling feature, which isn't supported. + This requires both the DCT scaling and SmartScale features, which are not + supported. + +-- Lossless RGB JPEG files + This requires the SmartScale feature, which is not supported. ******************************************************************************* @@ -285,12 +341,13 @@ Restart Markers =============== The optimized Huffman decoder in libjpeg-turbo does not handle restart markers -in a way that makes libjpeg happy, so it is necessary to use the slow Huffman -decoder when decompressing a JPEG image that has restart markers. This can -cause the decompression performance to drop by as much as 20%, but the -performance will still be much much greater than that of libjpeg v6b. Many -consumer packages, such as PhotoShop, use restart markers when generating JPEG -images, so images generated by those programs will experience this issue. +in a way that makes the rest of the libjpeg infrastructure happy, so it is +necessary to use the slow Huffman decoder when decompressing a JPEG image that +has restart markers. This can cause the decompression performance to drop by +as much as 20%, but the performance will still be much greater than that of +libjpeg. Many consumer packages, such as PhotoShop, use restart markers when +generating JPEG images, so images generated by those programs will experience +this issue. =============================================== Fast Integer Forward DCT at High Quality Levels diff --git a/media/libjpeg/cderror.h b/media/libjpeg/cderror.h deleted file mode 100644 index e19c475c5c5..00000000000 --- a/media/libjpeg/cderror.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * cderror.h - * - * Copyright (C) 1994-1997, Thomas G. Lane. - * Modified 2009 by Guido Vollbeding. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - * This file defines the error and message codes for the cjpeg/djpeg - * applications. These strings are not needed as part of the JPEG library - * proper. - * Edit this file to add new codes, or to translate the message strings to - * some other language. - */ - -/* - * To define the enum list of message codes, include this file without - * defining macro JMESSAGE. To create a message string table, include it - * again with a suitable JMESSAGE definition (see jerror.c for an example). - */ -#ifndef JMESSAGE -#ifndef CDERROR_H -#define CDERROR_H -/* First time through, define the enum list */ -#define JMAKE_ENUM_LIST -#else -/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */ -#define JMESSAGE(code,string) -#endif /* CDERROR_H */ -#endif /* JMESSAGE */ - -#ifdef JMAKE_ENUM_LIST - -typedef enum { - -#define JMESSAGE(code,string) code , - -#endif /* JMAKE_ENUM_LIST */ - -JMESSAGE(JMSG_FIRSTADDONCODE=1000, NULL) /* Must be first entry! */ - -#ifdef BMP_SUPPORTED -JMESSAGE(JERR_BMP_BADCMAP, "Unsupported BMP colormap format") -JMESSAGE(JERR_BMP_BADDEPTH, "Only 8- and 24-bit BMP files are supported") -JMESSAGE(JERR_BMP_BADHEADER, "Invalid BMP file: bad header length") -JMESSAGE(JERR_BMP_BADPLANES, "Invalid BMP file: biPlanes not equal to 1") -JMESSAGE(JERR_BMP_COLORSPACE, "BMP output must be grayscale or RGB") -JMESSAGE(JERR_BMP_COMPRESSED, "Sorry, compressed BMPs not yet supported") -JMESSAGE(JERR_BMP_EMPTY, "Empty BMP image") -JMESSAGE(JERR_BMP_NOT, "Not a BMP file - does not start with BM") -JMESSAGE(JTRC_BMP, "%ux%u 24-bit BMP image") -JMESSAGE(JTRC_BMP_MAPPED, "%ux%u 8-bit colormapped BMP image") -JMESSAGE(JTRC_BMP_OS2, "%ux%u 24-bit OS2 BMP image") -JMESSAGE(JTRC_BMP_OS2_MAPPED, "%ux%u 8-bit colormapped OS2 BMP image") -#endif /* BMP_SUPPORTED */ - -#ifdef GIF_SUPPORTED -JMESSAGE(JERR_GIF_BUG, "GIF output got confused") -JMESSAGE(JERR_GIF_CODESIZE, "Bogus GIF codesize %d") -JMESSAGE(JERR_GIF_COLORSPACE, "GIF output must be grayscale or RGB") -JMESSAGE(JERR_GIF_IMAGENOTFOUND, "Too few images in GIF file") -JMESSAGE(JERR_GIF_NOT, "Not a GIF file") -JMESSAGE(JTRC_GIF, "%ux%ux%d GIF image") -JMESSAGE(JTRC_GIF_BADVERSION, - "Warning: unexpected GIF version number '%c%c%c'") -JMESSAGE(JTRC_GIF_EXTENSION, "Ignoring GIF extension block of type 0x%02x") -JMESSAGE(JTRC_GIF_NONSQUARE, "Caution: nonsquare pixels in input") -JMESSAGE(JWRN_GIF_BADDATA, "Corrupt data in GIF file") -JMESSAGE(JWRN_GIF_CHAR, "Bogus char 0x%02x in GIF file, ignoring") -JMESSAGE(JWRN_GIF_ENDCODE, "Premature end of GIF image") -JMESSAGE(JWRN_GIF_NOMOREDATA, "Ran out of GIF bits") -#endif /* GIF_SUPPORTED */ - -#ifdef PPM_SUPPORTED -JMESSAGE(JERR_PPM_COLORSPACE, "PPM output must be grayscale or RGB") -JMESSAGE(JERR_PPM_NONNUMERIC, "Nonnumeric data in PPM file") -JMESSAGE(JERR_PPM_NOT, "Not a PPM/PGM file") -JMESSAGE(JTRC_PGM, "%ux%u PGM image") -JMESSAGE(JTRC_PGM_TEXT, "%ux%u text PGM image") -JMESSAGE(JTRC_PPM, "%ux%u PPM image") -JMESSAGE(JTRC_PPM_TEXT, "%ux%u text PPM image") -#endif /* PPM_SUPPORTED */ - -#ifdef RLE_SUPPORTED -JMESSAGE(JERR_RLE_BADERROR, "Bogus error code from RLE library") -JMESSAGE(JERR_RLE_COLORSPACE, "RLE output must be grayscale or RGB") -JMESSAGE(JERR_RLE_DIMENSIONS, "Image dimensions (%ux%u) too large for RLE") -JMESSAGE(JERR_RLE_EMPTY, "Empty RLE file") -JMESSAGE(JERR_RLE_EOF, "Premature EOF in RLE header") -JMESSAGE(JERR_RLE_MEM, "Insufficient memory for RLE header") -JMESSAGE(JERR_RLE_NOT, "Not an RLE file") -JMESSAGE(JERR_RLE_TOOMANYCHANNELS, "Cannot handle %d output channels for RLE") -JMESSAGE(JERR_RLE_UNSUPPORTED, "Cannot handle this RLE setup") -JMESSAGE(JTRC_RLE, "%ux%u full-color RLE file") -JMESSAGE(JTRC_RLE_FULLMAP, "%ux%u full-color RLE file with map of length %d") -JMESSAGE(JTRC_RLE_GRAY, "%ux%u grayscale RLE file") -JMESSAGE(JTRC_RLE_MAPGRAY, "%ux%u grayscale RLE file with map of length %d") -JMESSAGE(JTRC_RLE_MAPPED, "%ux%u colormapped RLE file with map of length %d") -#endif /* RLE_SUPPORTED */ - -#ifdef TARGA_SUPPORTED -JMESSAGE(JERR_TGA_BADCMAP, "Unsupported Targa colormap format") -JMESSAGE(JERR_TGA_BADPARMS, "Invalid or unsupported Targa file") -JMESSAGE(JERR_TGA_COLORSPACE, "Targa output must be grayscale or RGB") -JMESSAGE(JTRC_TGA, "%ux%u RGB Targa image") -JMESSAGE(JTRC_TGA_GRAY, "%ux%u grayscale Targa image") -JMESSAGE(JTRC_TGA_MAPPED, "%ux%u colormapped Targa image") -#else -JMESSAGE(JERR_TGA_NOTCOMP, "Targa support was not compiled") -#endif /* TARGA_SUPPORTED */ - -JMESSAGE(JERR_BAD_CMAP_FILE, - "Color map file is invalid or of unsupported format") -JMESSAGE(JERR_TOO_MANY_COLORS, - "Output file format cannot handle %d colormap entries") -JMESSAGE(JERR_UNGETC_FAILED, "ungetc failed") -#ifdef TARGA_SUPPORTED -JMESSAGE(JERR_UNKNOWN_FORMAT, - "Unrecognized input file format --- perhaps you need -targa") -#else -JMESSAGE(JERR_UNKNOWN_FORMAT, "Unrecognized input file format") -#endif -JMESSAGE(JERR_UNSUPPORTED_FORMAT, "Unsupported output file format") - -#ifdef JMAKE_ENUM_LIST - - JMSG_LASTADDONCODE -} ADDON_MESSAGE_CODE; - -#undef JMAKE_ENUM_LIST -#endif /* JMAKE_ENUM_LIST */ - -/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */ -#undef JMESSAGE diff --git a/media/libjpeg/cdjpeg.h b/media/libjpeg/cdjpeg.h deleted file mode 100644 index ed024ac3ae8..00000000000 --- a/media/libjpeg/cdjpeg.h +++ /dev/null @@ -1,187 +0,0 @@ -/* - * cdjpeg.h - * - * Copyright (C) 1994-1997, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains common declarations for the sample applications - * cjpeg and djpeg. It is NOT used by the core JPEG library. - */ - -#define JPEG_CJPEG_DJPEG /* define proper options in jconfig.h */ -#define JPEG_INTERNAL_OPTIONS /* cjpeg.c,djpeg.c need to see xxx_SUPPORTED */ -#include "jinclude.h" -#include "jpeglib.h" -#include "jerror.h" /* get library error codes too */ -#include "cderror.h" /* get application-specific error codes */ - - -/* - * Object interface for cjpeg's source file decoding modules - */ - -typedef struct cjpeg_source_struct * cjpeg_source_ptr; - -struct cjpeg_source_struct { - JMETHOD(void, start_input, (j_compress_ptr cinfo, - cjpeg_source_ptr sinfo)); - JMETHOD(JDIMENSION, get_pixel_rows, (j_compress_ptr cinfo, - cjpeg_source_ptr sinfo)); - JMETHOD(void, finish_input, (j_compress_ptr cinfo, - cjpeg_source_ptr sinfo)); - - FILE *input_file; - - JSAMPARRAY buffer; - JDIMENSION buffer_height; -}; - - -/* - * Object interface for djpeg's output file encoding modules - */ - -typedef struct djpeg_dest_struct * djpeg_dest_ptr; - -struct djpeg_dest_struct { - /* start_output is called after jpeg_start_decompress finishes. - * The color map will be ready at this time, if one is needed. - */ - JMETHOD(void, start_output, (j_decompress_ptr cinfo, - djpeg_dest_ptr dinfo)); - /* Emit the specified number of pixel rows from the buffer. */ - JMETHOD(void, put_pixel_rows, (j_decompress_ptr cinfo, - djpeg_dest_ptr dinfo, - JDIMENSION rows_supplied)); - /* Finish up at the end of the image. */ - JMETHOD(void, finish_output, (j_decompress_ptr cinfo, - djpeg_dest_ptr dinfo)); - - /* Target file spec; filled in by djpeg.c after object is created. */ - FILE * output_file; - - /* Output pixel-row buffer. Created by module init or start_output. - * Width is cinfo->output_width * cinfo->output_components; - * height is buffer_height. - */ - JSAMPARRAY buffer; - JDIMENSION buffer_height; -}; - - -/* - * cjpeg/djpeg may need to perform extra passes to convert to or from - * the source/destination file format. The JPEG library does not know - * about these passes, but we'd like them to be counted by the progress - * monitor. We use an expanded progress monitor object to hold the - * additional pass count. - */ - -struct cdjpeg_progress_mgr { - struct jpeg_progress_mgr pub; /* fields known to JPEG library */ - int completed_extra_passes; /* extra passes completed */ - int total_extra_passes; /* total extra */ - /* last printed percentage stored here to avoid multiple printouts */ - int percent_done; -}; - -typedef struct cdjpeg_progress_mgr * cd_progress_ptr; - - -/* Short forms of external names for systems with brain-damaged linkers. */ - -#ifdef NEED_SHORT_EXTERNAL_NAMES -#define jinit_read_bmp jIRdBMP -#define jinit_write_bmp jIWrBMP -#define jinit_read_gif jIRdGIF -#define jinit_write_gif jIWrGIF -#define jinit_read_ppm jIRdPPM -#define jinit_write_ppm jIWrPPM -#define jinit_read_rle jIRdRLE -#define jinit_write_rle jIWrRLE -#define jinit_read_targa jIRdTarga -#define jinit_write_targa jIWrTarga -#define read_quant_tables RdQTables -#define read_scan_script RdScnScript -#define set_quality_ratings SetQRates -#define set_quant_slots SetQSlots -#define set_sample_factors SetSFacts -#define read_color_map RdCMap -#define enable_signal_catcher EnSigCatcher -#define start_progress_monitor StProgMon -#define end_progress_monitor EnProgMon -#define read_stdin RdStdin -#define write_stdout WrStdout -#endif /* NEED_SHORT_EXTERNAL_NAMES */ - -/* Module selection routines for I/O modules. */ - -EXTERN(cjpeg_source_ptr) jinit_read_bmp JPP((j_compress_ptr cinfo)); -EXTERN(djpeg_dest_ptr) jinit_write_bmp JPP((j_decompress_ptr cinfo, - boolean is_os2)); -EXTERN(cjpeg_source_ptr) jinit_read_gif JPP((j_compress_ptr cinfo)); -EXTERN(djpeg_dest_ptr) jinit_write_gif JPP((j_decompress_ptr cinfo)); -EXTERN(cjpeg_source_ptr) jinit_read_ppm JPP((j_compress_ptr cinfo)); -EXTERN(djpeg_dest_ptr) jinit_write_ppm JPP((j_decompress_ptr cinfo)); -EXTERN(cjpeg_source_ptr) jinit_read_rle JPP((j_compress_ptr cinfo)); -EXTERN(djpeg_dest_ptr) jinit_write_rle JPP((j_decompress_ptr cinfo)); -EXTERN(cjpeg_source_ptr) jinit_read_targa JPP((j_compress_ptr cinfo)); -EXTERN(djpeg_dest_ptr) jinit_write_targa JPP((j_decompress_ptr cinfo)); - -/* cjpeg support routines (in rdswitch.c) */ - -EXTERN(boolean) read_quant_tables JPP((j_compress_ptr cinfo, char * filename, - boolean force_baseline)); -EXTERN(boolean) read_scan_script JPP((j_compress_ptr cinfo, char * filename)); -EXTERN(boolean) set_quality_ratings JPP((j_compress_ptr cinfo, char *arg, - boolean force_baseline)); -EXTERN(boolean) set_quant_slots JPP((j_compress_ptr cinfo, char *arg)); -EXTERN(boolean) set_sample_factors JPP((j_compress_ptr cinfo, char *arg)); - -/* djpeg support routines (in rdcolmap.c) */ - -EXTERN(void) read_color_map JPP((j_decompress_ptr cinfo, FILE * infile)); - -/* common support routines (in cdjpeg.c) */ - -EXTERN(void) enable_signal_catcher JPP((j_common_ptr cinfo)); -EXTERN(void) start_progress_monitor JPP((j_common_ptr cinfo, - cd_progress_ptr progress)); -EXTERN(void) end_progress_monitor JPP((j_common_ptr cinfo)); -EXTERN(boolean) keymatch JPP((char * arg, const char * keyword, int minchars)); -EXTERN(FILE *) read_stdin JPP((void)); -EXTERN(FILE *) write_stdout JPP((void)); - -/* miscellaneous useful macros */ - -#ifdef DONT_USE_B_MODE /* define mode parameters for fopen() */ -#define READ_BINARY "r" -#define WRITE_BINARY "w" -#else -#ifdef VMS /* VMS is very nonstandard */ -#define READ_BINARY "rb", "ctx=stm" -#define WRITE_BINARY "wb", "ctx=stm" -#else /* standard ANSI-compliant case */ -#define READ_BINARY "rb" -#define WRITE_BINARY "wb" -#endif -#endif - -#ifndef EXIT_FAILURE /* define exit() codes if not provided */ -#define EXIT_FAILURE 1 -#endif -#ifndef EXIT_SUCCESS -#ifdef VMS -#define EXIT_SUCCESS 1 /* VMS is very nonstandard */ -#else -#define EXIT_SUCCESS 0 -#endif -#endif -#ifndef EXIT_WARNING -#ifdef VMS -#define EXIT_WARNING 1 /* VMS is very nonstandard */ -#else -#define EXIT_WARNING 2 -#endif -#endif diff --git a/media/libjpeg/config.h b/media/libjpeg/config.h new file mode 100644 index 00000000000..a0c56812753 --- /dev/null +++ b/media/libjpeg/config.h @@ -0,0 +1,6 @@ +#define VERSION "1.2.0" +#define BUILD "2012-02-10" +#define PACKAGE_NAME "libjpeg-turbo" + +/* Need to use Mozilla-specific function inlining. */ +#define INLINE NS_ALWAYS_INLINE diff --git a/media/libjpeg/jaricom.c b/media/libjpeg/jaricom.c index 2195a973315..f43e2ea7fac 100644 --- a/media/libjpeg/jaricom.c +++ b/media/libjpeg/jaricom.c @@ -150,3 +150,4 @@ const INT32 jpeg_aritab[113+1] = { * as recommended in Section 10.3 Table 5 of ITU-T Rec. T.851. */ V( 113, 0x5a1d, 113, 113, 0 ) +}; diff --git a/media/libjpeg/jccolext.c b/media/libjpeg/jccolext.c new file mode 100644 index 00000000000..acbfa235239 --- /dev/null +++ b/media/libjpeg/jccolext.c @@ -0,0 +1,114 @@ +/* + * jccolext.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * Copyright (C) 2009-2011, D. R. Commander. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains input colorspace conversion routines. + */ + + +/* This file is included by jccolor.c */ + + +/* + * Convert some rows of samples to the JPEG colorspace. + * + * Note that we change from the application's interleaved-pixel format + * to our internal noninterleaved, one-plane-per-component format. + * The input buffer is therefore three times as wide as the output buffer. + * + * A starting row offset is provided only for the output buffer. The caller + * can easily adjust the passed input_buf value to accommodate any row + * offset required on that side. + */ + +INLINE +LOCAL(void) +rgb_ycc_convert_internal (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int r, g, b; + register INT32 * ctab = cconvert->rgb_ycc_tab; + register JSAMPROW inptr; + register JSAMPROW outptr0, outptr1, outptr2; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->image_width; + + while (--num_rows >= 0) { + inptr = *input_buf++; + outptr0 = output_buf[0][output_row]; + outptr1 = output_buf[1][output_row]; + outptr2 = output_buf[2][output_row]; + output_row++; + for (col = 0; col < num_cols; col++) { + r = GETJSAMPLE(inptr[RGB_RED]); + g = GETJSAMPLE(inptr[RGB_GREEN]); + b = GETJSAMPLE(inptr[RGB_BLUE]); + inptr += RGB_PIXELSIZE; + /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations + * must be too; we do not need an explicit range-limiting operation. + * Hence the value being shifted is never negative, and we don't + * need the general RIGHT_SHIFT macro. + */ + /* Y */ + outptr0[col] = (JSAMPLE) + ((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF]) + >> SCALEBITS); + /* Cb */ + outptr1[col] = (JSAMPLE) + ((ctab[r+R_CB_OFF] + ctab[g+G_CB_OFF] + ctab[b+B_CB_OFF]) + >> SCALEBITS); + /* Cr */ + outptr2[col] = (JSAMPLE) + ((ctab[r+R_CR_OFF] + ctab[g+G_CR_OFF] + ctab[b+B_CR_OFF]) + >> SCALEBITS); + } + } +} + + +/**************** Cases other than RGB -> YCbCr **************/ + + +/* + * Convert some rows of samples to the JPEG colorspace. + * This version handles RGB->grayscale conversion, which is the same + * as the RGB->Y portion of RGB->YCbCr. + * We assume rgb_ycc_start has been called (we only use the Y tables). + */ + +INLINE +LOCAL(void) +rgb_gray_convert_internal (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int r, g, b; + register INT32 * ctab = cconvert->rgb_ycc_tab; + register JSAMPROW inptr; + register JSAMPROW outptr; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->image_width; + + while (--num_rows >= 0) { + inptr = *input_buf++; + outptr = output_buf[0][output_row]; + output_row++; + for (col = 0; col < num_cols; col++) { + r = GETJSAMPLE(inptr[RGB_RED]); + g = GETJSAMPLE(inptr[RGB_GREEN]); + b = GETJSAMPLE(inptr[RGB_BLUE]); + inptr += RGB_PIXELSIZE; + /* Y */ + outptr[col] = (JSAMPLE) + ((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF]) + >> SCALEBITS); + } + } +} diff --git a/media/libjpeg/jccolor.c b/media/libjpeg/jccolor.c index 73969901dcd..97305557424 100644 --- a/media/libjpeg/jccolor.c +++ b/media/libjpeg/jccolor.c @@ -3,7 +3,7 @@ * * Copyright (C) 1991-1996, Thomas G. Lane. * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright 2009 D. R. Commander + * Copyright (C) 2009-2011, D. R. Commander. * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * @@ -14,6 +14,7 @@ #include "jinclude.h" #include "jpeglib.h" #include "jsimd.h" +#include "config.h" /* Private subobject */ @@ -81,72 +82,97 @@ typedef my_color_converter * my_cconvert_ptr; #define TABLE_SIZE (8*(MAXJSAMPLE+1)) -#if BITS_IN_JSAMPLE == 8 +/* Include inline routines for colorspace extensions */ -static const unsigned char red_lut[256] = { - 0 , 0 , 1 , 1 , 1 , 1 , 2 , 2 , 2 , 3 , 3 , 3 , 4 , 4 , 4 , 4 , - 5 , 5 , 5 , 6 , 6 , 6 , 7 , 7 , 7 , 7 , 8 , 8 , 8 , 9 , 9 , 9 , - 10, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, - 14, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, - 19, 19, 20, 20, 20, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 24, - 24, 24, 25, 25, 25, 25, 26, 26, 26, 27, 27, 27, 28, 28, 28, 28, - 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 32, 32, 32, 33, 33, 33, - 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 36, 37, 37, 37, 38, 38, - 38, 39, 39, 39, 39, 40, 40, 40, 41, 41, 41, 42, 42, 42, 42, 43, - 43, 43, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 47, 47, 47, 48, - 48, 48, 48, 49, 49, 49, 50, 50, 50, 51, 51, 51, 51, 52, 52, 52, - 53, 53, 53, 54, 54, 54, 54, 55, 55, 55, 56, 56, 56, 57, 57, 57, - 57, 58, 58, 58, 59, 59, 59, 60, 60, 60, 60, 61, 61, 61, 62, 62, - 62, 62, 63, 63, 63, 64, 64, 64, 65, 65, 65, 65, 66, 66, 66, 67, - 67, 67, 68, 68, 68, 68, 69, 69, 69, 70, 70, 70, 71, 71, 71, 71, - 72, 72, 72, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 76, 76, 76 -}; +#include "jccolext.c" +#undef RGB_RED +#undef RGB_GREEN +#undef RGB_BLUE +#undef RGB_PIXELSIZE -static const unsigned char green_lut[256] = { - 0 , 1 , 1 , 2 , 2 , 3 , 4 , 4 , 5 , 5 , 6 , 6 , - 7 , 8 , 8 , 9 , 9 , 10 , 11 , 11 , 12 , 12 , 13 , 14 , - 14 , 15 , 15 , 16 , 16 , 17 , 18 , 18 , 19 , 19 , 20 , 21 , - 21 , 22 , 22 , 23 , 23 , 24 , 25 , 25 , 26 , 26 , 27 , 28 , - 28 , 29 , 29 , 30 , 31 , 31 , 32 , 32 , 33 , 33 , 34 , 35 , - 35 , 36 , 36 , 37 , 38 , 38 , 39 , 39 , 40 , 41 , 41 , 42 , - 42 , 43 , 43 , 44 , 45 , 45 , 46 , 46 , 47 , 48 , 48 , 49 , - 49 , 50 , 50 , 51 , 52 , 52 , 53 , 53 , 54 , 55 , 55 , 56 , - 56 , 57 , 58 , 58 , 59 , 59 , 60 , 60 , 61 , 62 , 62 , 63 , - 63 , 64 , 65 , 65 , 66 , 66 , 67 , 68 , 68 , 69 , 69 , 70 , - 70 , 71 , 72 , 72 , 73 , 73 , 74 , 75 , 75 , 76 , 76 , 77 , - 77 , 78 , 79 , 79 , 80 , 80 , 81 , 82 , 82 , 83 , 83 , 84 , - 85 , 85 , 86 , 86 , 87 , 87 , 88 , 89 , 89 , 90 , 90 , 91 , - 92 , 92 , 93 , 93 , 94 , 95 , 95 , 96 , 96 , 97 , 97 , 98 , - 99 , 99 , 100, 100, 101, 102, 102, 103, 103, 104, 104, 105, - 106, 106, 107, 107, 108, 109, 109, 110, 110, 111, 112, 112, - 113, 113, 114, 114, 115, 116, 116, 117, 117, 118, 119, 119, - 120, 120, 121, 122, 122, 123, 123, 124, 124, 125, 126, 126, - 127, 127, 128, 129, 129, 130, 130, 131, 131, 132, 133, 133, - 134, 134, 135, 136, 136, 137, 137, 138, 139, 139, 140, 140, - 141, 141, 142, 143, 143, 144, 144, 145, 146, 146, 147, 147, - 148, 149, 149, 150 -}; +#define RGB_RED EXT_RGB_RED +#define RGB_GREEN EXT_RGB_GREEN +#define RGB_BLUE EXT_RGB_BLUE +#define RGB_PIXELSIZE EXT_RGB_PIXELSIZE +#define rgb_ycc_convert_internal extrgb_ycc_convert_internal +#define rgb_gray_convert_internal extrgb_gray_convert_internal +#include "jccolext.c" +#undef RGB_RED +#undef RGB_GREEN +#undef RGB_BLUE +#undef RGB_PIXELSIZE +#undef rgb_ycc_convert_internal +#undef rgb_gray_convert_internal -static const unsigned char blue_lut[256] = { - 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 2 , 2 , - 2 , 2 , 2 , 2 , 2 , 2 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 4 , - 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 5 , 5 , 5 , 5 , 5 , 5 , 5 , 5 , - 5 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 7 , 7 , 7 , 7 , 7 , 7 , - 7 , 7 , 8 , 8 , 8 , 8 , 8 , 8 , 8 , 8 , 8 , 9 , 9 , 9 , 9 , 9 , - 9 , 9 , 9 , 9 , 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, - 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, - 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, - 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, - 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, - 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29 -}; +#define RGB_RED EXT_RGBX_RED +#define RGB_GREEN EXT_RGBX_GREEN +#define RGB_BLUE EXT_RGBX_BLUE +#define RGB_PIXELSIZE EXT_RGBX_PIXELSIZE +#define rgb_ycc_convert_internal extrgbx_ycc_convert_internal +#define rgb_gray_convert_internal extrgbx_gray_convert_internal +#include "jccolext.c" +#undef RGB_RED +#undef RGB_GREEN +#undef RGB_BLUE +#undef RGB_PIXELSIZE +#undef rgb_ycc_convert_internal +#undef rgb_gray_convert_internal -#endif +#define RGB_RED EXT_BGR_RED +#define RGB_GREEN EXT_BGR_GREEN +#define RGB_BLUE EXT_BGR_BLUE +#define RGB_PIXELSIZE EXT_BGR_PIXELSIZE +#define rgb_ycc_convert_internal extbgr_ycc_convert_internal +#define rgb_gray_convert_internal extbgr_gray_convert_internal +#include "jccolext.c" +#undef RGB_RED +#undef RGB_GREEN +#undef RGB_BLUE +#undef RGB_PIXELSIZE +#undef rgb_ycc_convert_internal +#undef rgb_gray_convert_internal + +#define RGB_RED EXT_BGRX_RED +#define RGB_GREEN EXT_BGRX_GREEN +#define RGB_BLUE EXT_BGRX_BLUE +#define RGB_PIXELSIZE EXT_BGRX_PIXELSIZE +#define rgb_ycc_convert_internal extbgrx_ycc_convert_internal +#define rgb_gray_convert_internal extbgrx_gray_convert_internal +#include "jccolext.c" +#undef RGB_RED +#undef RGB_GREEN +#undef RGB_BLUE +#undef RGB_PIXELSIZE +#undef rgb_ycc_convert_internal +#undef rgb_gray_convert_internal + +#define RGB_RED EXT_XBGR_RED +#define RGB_GREEN EXT_XBGR_GREEN +#define RGB_BLUE EXT_XBGR_BLUE +#define RGB_PIXELSIZE EXT_XBGR_PIXELSIZE +#define rgb_ycc_convert_internal extxbgr_ycc_convert_internal +#define rgb_gray_convert_internal extxbgr_gray_convert_internal +#include "jccolext.c" +#undef RGB_RED +#undef RGB_GREEN +#undef RGB_BLUE +#undef RGB_PIXELSIZE +#undef rgb_ycc_convert_internal +#undef rgb_gray_convert_internal + +#define RGB_RED EXT_XRGB_RED +#define RGB_GREEN EXT_XRGB_GREEN +#define RGB_BLUE EXT_XRGB_BLUE +#define RGB_PIXELSIZE EXT_XRGB_PIXELSIZE +#define rgb_ycc_convert_internal extxrgb_ycc_convert_internal +#define rgb_gray_convert_internal extxrgb_gray_convert_internal +#include "jccolext.c" +#undef RGB_RED +#undef RGB_GREEN +#undef RGB_BLUE +#undef RGB_PIXELSIZE +#undef rgb_ycc_convert_internal +#undef rgb_gray_convert_internal /* @@ -187,14 +213,6 @@ rgb_ycc_start (j_compress_ptr cinfo) /* * Convert some rows of samples to the JPEG colorspace. - * - * Note that we change from the application's interleaved-pixel format - * to our internal noninterleaved, one-plane-per-component format. - * The input buffer is therefore three times as wide as the output buffer. - * - * A starting row offset is provided only for the output buffer. The caller - * can easily adjust the passed input_buf value to accommodate any row - * offset required on that side. */ METHODDEF(void) @@ -202,43 +220,39 @@ rgb_ycc_convert (j_compress_ptr cinfo, JSAMPARRAY input_buf, JSAMPIMAGE output_buf, JDIMENSION output_row, int num_rows) { - my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; - register int r, g, b; - register INT32 * ctab = cconvert->rgb_ycc_tab; - register JSAMPROW inptr; - register JSAMPROW outptr0, outptr1, outptr2; - register JDIMENSION col; - JDIMENSION num_cols = cinfo->image_width; - - while (--num_rows >= 0) { - inptr = *input_buf++; - outptr0 = output_buf[0][output_row]; - outptr1 = output_buf[1][output_row]; - outptr2 = output_buf[2][output_row]; - output_row++; - for (col = 0; col < num_cols; col++) { - r = GETJSAMPLE(inptr[rgb_red[cinfo->in_color_space]]); - g = GETJSAMPLE(inptr[rgb_green[cinfo->in_color_space]]); - b = GETJSAMPLE(inptr[rgb_blue[cinfo->in_color_space]]); - inptr += rgb_pixelsize[cinfo->in_color_space]; - /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations - * must be too; we do not need an explicit range-limiting operation. - * Hence the value being shifted is never negative, and we don't - * need the general RIGHT_SHIFT macro. - */ - /* Y */ - outptr0[col] = (JSAMPLE) - ((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF]) - >> SCALEBITS); - /* Cb */ - outptr1[col] = (JSAMPLE) - ((ctab[r+R_CB_OFF] + ctab[g+G_CB_OFF] + ctab[b+B_CB_OFF]) - >> SCALEBITS); - /* Cr */ - outptr2[col] = (JSAMPLE) - ((ctab[r+R_CR_OFF] + ctab[g+G_CR_OFF] + ctab[b+B_CR_OFF]) - >> SCALEBITS); - } + switch (cinfo->in_color_space) { + case JCS_EXT_RGB: + extrgb_ycc_convert_internal(cinfo, input_buf, output_buf, output_row, + num_rows); + break; + case JCS_EXT_RGBX: + case JCS_EXT_RGBA: + extrgbx_ycc_convert_internal(cinfo, input_buf, output_buf, output_row, + num_rows); + break; + case JCS_EXT_BGR: + extbgr_ycc_convert_internal(cinfo, input_buf, output_buf, output_row, + num_rows); + break; + case JCS_EXT_BGRX: + case JCS_EXT_BGRA: + extbgrx_ycc_convert_internal(cinfo, input_buf, output_buf, output_row, + num_rows); + break; + case JCS_EXT_XBGR: + case JCS_EXT_ABGR: + extxbgr_ycc_convert_internal(cinfo, input_buf, output_buf, output_row, + num_rows); + break; + case JCS_EXT_XRGB: + case JCS_EXT_ARGB: + extxrgb_ycc_convert_internal(cinfo, input_buf, output_buf, output_row, + num_rows); + break; + default: + rgb_ycc_convert_internal(cinfo, input_buf, output_buf, output_row, + num_rows); + break; } } @@ -248,9 +262,6 @@ rgb_ycc_convert (j_compress_ptr cinfo, /* * Convert some rows of samples to the JPEG colorspace. - * This version handles RGB->grayscale conversion, which is the same - * as the RGB->Y portion of RGB->YCbCr. - * We assume rgb_ycc_start has been called (we only use the Y tables). */ METHODDEF(void) @@ -258,36 +269,39 @@ rgb_gray_convert (j_compress_ptr cinfo, JSAMPARRAY input_buf, JSAMPIMAGE output_buf, JDIMENSION output_row, int num_rows) { - #if BITS_IN_JSAMPLE != 8 - register INT32 * ctab = cconvert->rgb_ycc_tab; - #endif - register JSAMPROW inptr; - register JSAMPROW outptr; - JSAMPLE *maxoutptr; - JDIMENSION num_cols = cinfo->image_width; - int rindex = rgb_red[cinfo->in_color_space]; - int gindex = rgb_green[cinfo->in_color_space]; - int bindex = rgb_blue[cinfo->in_color_space]; - int rgbstride = rgb_pixelsize[cinfo->in_color_space]; - - while (--num_rows >= 0) { - inptr = *input_buf++; - outptr = output_buf[0][output_row]; - maxoutptr = &outptr[num_cols]; - output_row++; - for (; outptr < maxoutptr; outptr++, inptr += rgbstride) { - /* Y */ - #if BITS_IN_JSAMPLE == 8 - *outptr = red_lut[inptr[rindex]] + green_lut[inptr[gindex]] - + blue_lut[inptr[bindex]]; - #else - *outptr = (JSAMPLE) - ((ctab[GETJSAMPLE(inptr[rindex])+R_Y_OFF] - + ctab[GETJSAMPLE(inptr[gindex])+G_Y_OFF] - + ctab[GETJSAMPLE(inptr[bindex])+B_Y_OFF]) - >> SCALEBITS); - #endif - } + switch (cinfo->in_color_space) { + case JCS_EXT_RGB: + extrgb_gray_convert_internal(cinfo, input_buf, output_buf, output_row, + num_rows); + break; + case JCS_EXT_RGBX: + case JCS_EXT_RGBA: + extrgbx_gray_convert_internal(cinfo, input_buf, output_buf, output_row, + num_rows); + break; + case JCS_EXT_BGR: + extbgr_gray_convert_internal(cinfo, input_buf, output_buf, output_row, + num_rows); + break; + case JCS_EXT_BGRX: + case JCS_EXT_BGRA: + extbgrx_gray_convert_internal(cinfo, input_buf, output_buf, output_row, + num_rows); + break; + case JCS_EXT_XBGR: + case JCS_EXT_ABGR: + extxbgr_gray_convert_internal(cinfo, input_buf, output_buf, output_row, + num_rows); + break; + case JCS_EXT_XRGB: + case JCS_EXT_ARGB: + extxrgb_gray_convert_internal(cinfo, input_buf, output_buf, output_row, + num_rows); + break; + default: + rgb_gray_convert_internal(cinfo, input_buf, output_buf, output_row, + num_rows); + break; } } @@ -453,6 +467,10 @@ jinit_color_converter (j_compress_ptr cinfo) case JCS_EXT_BGRX: case JCS_EXT_XBGR: case JCS_EXT_XRGB: + case JCS_EXT_RGBA: + case JCS_EXT_BGRA: + case JCS_EXT_ABGR: + case JCS_EXT_ARGB: if (cinfo->input_components != rgb_pixelsize[cinfo->in_color_space]) ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); break; @@ -487,9 +505,17 @@ jinit_color_converter (j_compress_ptr cinfo) cinfo->in_color_space == JCS_EXT_BGR || cinfo->in_color_space == JCS_EXT_BGRX || cinfo->in_color_space == JCS_EXT_XBGR || - cinfo->in_color_space == JCS_EXT_XRGB) { - cconvert->pub.start_pass = rgb_ycc_start; - cconvert->pub.color_convert = rgb_gray_convert; + cinfo->in_color_space == JCS_EXT_XRGB || + cinfo->in_color_space == JCS_EXT_RGBA || + cinfo->in_color_space == JCS_EXT_BGRA || + cinfo->in_color_space == JCS_EXT_ABGR || + cinfo->in_color_space == JCS_EXT_ARGB) { + if (jsimd_can_rgb_gray()) + cconvert->pub.color_convert = jsimd_rgb_gray_convert; + else { + cconvert->pub.start_pass = rgb_ycc_start; + cconvert->pub.color_convert = rgb_gray_convert; + } } else if (cinfo->in_color_space == JCS_YCbCr) cconvert->pub.color_convert = grayscale_convert; else @@ -503,6 +529,10 @@ jinit_color_converter (j_compress_ptr cinfo) case JCS_EXT_BGRX: case JCS_EXT_XBGR: case JCS_EXT_XRGB: + case JCS_EXT_RGBA: + case JCS_EXT_BGRA: + case JCS_EXT_ABGR: + case JCS_EXT_ARGB: if (cinfo->num_components != 3) ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); if (cinfo->in_color_space == cinfo->jpeg_color_space && @@ -521,7 +551,11 @@ jinit_color_converter (j_compress_ptr cinfo) cinfo->in_color_space == JCS_EXT_BGR || cinfo->in_color_space == JCS_EXT_BGRX || cinfo->in_color_space == JCS_EXT_XBGR || - cinfo->in_color_space == JCS_EXT_XRGB) { + cinfo->in_color_space == JCS_EXT_XRGB || + cinfo->in_color_space == JCS_EXT_RGBA || + cinfo->in_color_space == JCS_EXT_BGRA || + cinfo->in_color_space == JCS_EXT_ABGR || + cinfo->in_color_space == JCS_EXT_ARGB) { if (jsimd_can_rgb_ycc()) cconvert->pub.color_convert = jsimd_rgb_ycc_convert; else { diff --git a/media/libjpeg/jcdctmgr.c b/media/libjpeg/jcdctmgr.c index 711f9dab629..12f88725ddf 100644 --- a/media/libjpeg/jcdctmgr.c +++ b/media/libjpeg/jcdctmgr.c @@ -182,7 +182,7 @@ compute_reciprocal (UINT16 divisor, DCTELEM * dtbl) /* fq will be one bit too large to fit in DCTELEM, so adjust */ fq >>= 1; r--; - } else if (fr <= (divisor / 2)) { /* fractional part is < 0.5 */ + } else if (fr <= (divisor / 2U)) { /* fractional part is < 0.5 */ c++; } else { /* fractional part is > 0.5 */ fq++; diff --git a/media/libjpeg/jchuff.c b/media/libjpeg/jchuff.c index f9fba90b6d4..fd4fa46ca53 100644 --- a/media/libjpeg/jchuff.c +++ b/media/libjpeg/jchuff.c @@ -2,6 +2,7 @@ * jchuff.c * * Copyright (C) 1991-1997, Thomas G. Lane. + * Copyright (C) 2009-2011, D. R. Commander. * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * @@ -18,6 +19,15 @@ #include "jinclude.h" #include "jpeglib.h" #include "jchuff.h" /* Declarations shared with jcphuff.c */ +#include + +static unsigned char jpeg_nbits_table[65536]; +static int jpeg_nbits_table_init = 0; + +#ifndef min + #define min(a,b) ((a)<(b)?(a):(b)) +#endif + /* Expanded entropy encoder object for Huffman encoding. * @@ -26,7 +36,7 @@ */ typedef struct { - INT32 put_buffer; /* current bit-accumulation buffer */ + size_t put_buffer; /* current bit-accumulation buffer */ int put_bits; /* # of bits now in it */ int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ } savable_state; @@ -260,6 +270,15 @@ jpeg_make_c_derived_tbl (j_compress_ptr cinfo, boolean isDC, int tblno, dtbl->ehufco[i] = huffcode[p]; dtbl->ehufsi[i] = huffsize[p]; } + + if(!jpeg_nbits_table_init) { + for(i = 0; i < 65536; i++) { + int nbits = 0, temp = i; + while (temp) {temp >>= 1; nbits++;} + jpeg_nbits_table[i] = nbits; + } + jpeg_nbits_table_init = 1; + } } @@ -279,6 +298,8 @@ dump_buffer (working_state * state) { struct jpeg_destination_mgr * dest = state->cinfo->dest; + dest->free_in_buffer = state->free_in_buffer; + if (! (*dest->empty_output_buffer) (state->cinfo)) return FALSE; /* After a successful buffer dump, must reset buffer pointers */ @@ -290,58 +311,138 @@ dump_buffer (working_state * state) /* Outputting bits to the file */ -/* Only the right 24 bits of put_buffer are used; the valid bits are - * left-justified in this part. At most 16 bits can be passed to emit_bits - * in one call, and we never retain more than 7 bits in put_buffer - * between calls, so 24 bits are sufficient. +/* These macros perform the same task as the emit_bits() function in the + * original libjpeg code. In addition to reducing overhead by explicitly + * inlining the code, additional performance is achieved by taking into + * account the size of the bit buffer and waiting until it is almost full + * before emptying it. This mostly benefits 64-bit platforms, since 6 + * bytes can be stored in a 64-bit bit buffer before it has to be emptied. */ -INLINE -LOCAL(boolean) -emit_bits (working_state * state, unsigned int code, int size) -/* Emit some bits; return TRUE if successful, FALSE if must suspend */ -{ - /* This routine is heavily used, so it's worth coding tightly. */ - register INT32 put_buffer = (INT32) code; - register int put_bits = state->cur.put_bits; +#define EMIT_BYTE() { \ + JOCTET c; \ + put_bits -= 8; \ + c = (JOCTET)GETJOCTET(put_buffer >> put_bits); \ + *buffer++ = c; \ + if (c == 0xFF) /* need to stuff a zero byte? */ \ + *buffer++ = 0; \ + } - /* if size is 0, caller used an invalid Huffman table entry */ - if (size == 0) - ERREXIT(state->cinfo, JERR_HUFF_MISSING_CODE); - - put_buffer &= (((INT32) 1)<cur.put_buffer; /* and merge with old buffer contents */ - - while (put_bits >= 8) { - int c = (int) ((put_buffer >> 16) & 0xFF); - - emit_byte(state, c, return FALSE); - if (c == 0xFF) { /* need to stuff a zero byte? */ - emit_byte(state, 0, return FALSE); - } - put_buffer <<= 8; - put_bits -= 8; - } - - state->cur.put_buffer = put_buffer; /* update state variables */ - state->cur.put_bits = put_bits; - - return TRUE; +#define PUT_BITS(code, size) { \ + put_bits += size; \ + put_buffer = (put_buffer << size) | code; \ } +#define CHECKBUF15() { \ + if (put_bits > 15) { \ + EMIT_BYTE() \ + EMIT_BYTE() \ + } \ +} + +#define CHECKBUF31() { \ + if (put_bits > 31) { \ + EMIT_BYTE() \ + EMIT_BYTE() \ + EMIT_BYTE() \ + EMIT_BYTE() \ + } \ +} + +#define CHECKBUF47() { \ + if (put_bits > 47) { \ + EMIT_BYTE() \ + EMIT_BYTE() \ + EMIT_BYTE() \ + EMIT_BYTE() \ + EMIT_BYTE() \ + EMIT_BYTE() \ + } \ +} + +#if __WORDSIZE==64 || defined(_WIN64) + +#define EMIT_BITS(code, size) { \ + CHECKBUF47() \ + PUT_BITS(code, size) \ +} + +#define EMIT_CODE(code, size) { \ + temp2 &= (((INT32) 1)<free_in_buffer < BUFSIZE) { \ + localbuf = 1; \ + buffer = _buffer; \ + } \ + else buffer = state->next_output_byte; \ + } + +#define STORE_BUFFER() { \ + if (localbuf) { \ + bytes = buffer - _buffer; \ + buffer = _buffer; \ + while (bytes > 0) { \ + bytestocopy = min(bytes, state->free_in_buffer); \ + MEMCOPY(state->next_output_byte, buffer, bytestocopy); \ + state->next_output_byte += bytestocopy; \ + buffer += bytestocopy; \ + state->free_in_buffer -= bytestocopy; \ + if (state->free_in_buffer == 0) \ + if (! dump_buffer(state)) return FALSE; \ + bytes -= bytestocopy; \ + } \ + } \ + else { \ + state->free_in_buffer -= (buffer - state->next_output_byte); \ + state->next_output_byte = buffer; \ + } \ + } + LOCAL(boolean) flush_bits (working_state * state) { - if (! emit_bits(state, 0x7F, 7)) /* fill any partial byte with ones */ - return FALSE; + JOCTET _buffer[BUFSIZE], *buffer; + size_t put_buffer; int put_bits; + size_t bytes, bytestocopy; int localbuf = 0; + + put_buffer = state->cur.put_buffer; + put_bits = state->cur.put_bits; + LOAD_BUFFER() + + /* fill any partial byte with ones */ + PUT_BITS(0x7F, 7) + while (put_bits >= 8) EMIT_BYTE() + state->cur.put_buffer = 0; /* and reset bit-buffer to empty */ state->cur.put_bits = 0; + STORE_BUFFER() + return TRUE; } @@ -352,91 +453,108 @@ LOCAL(boolean) encode_one_block (working_state * state, JCOEFPTR block, int last_dc_val, c_derived_tbl *dctbl, c_derived_tbl *actbl) { - register int temp, temp2; - register int nbits; - register int k, r, i; - + int temp, temp2, temp3; + int nbits; + int r, code, size; + JOCTET _buffer[BUFSIZE], *buffer; + size_t put_buffer; int put_bits; + int code_0xf0 = actbl->ehufco[0xf0], size_0xf0 = actbl->ehufsi[0xf0]; + size_t bytes, bytestocopy; int localbuf = 0; + + put_buffer = state->cur.put_buffer; + put_bits = state->cur.put_bits; + LOAD_BUFFER() + /* Encode the DC coefficient difference per section F.1.2.1 */ temp = temp2 = block[0] - last_dc_val; - if (temp < 0) { - temp = -temp; /* temp is abs value of input */ - /* For a negative input, want temp2 = bitwise complement of abs(input) */ - /* This code assumes we are on a two's complement machine */ - temp2--; - } - + /* This is a well-known technique for obtaining the absolute value without a + * branch. It is derived from an assembly language technique presented in + * "How to Optimize for the Pentium Processors", Copyright (c) 1996, 1997 by + * Agner Fog. + */ + temp3 = temp >> (CHAR_BIT * sizeof(int) - 1); + temp ^= temp3; + temp -= temp3; + + /* For a negative input, want temp2 = bitwise complement of abs(input) */ + /* This code assumes we are on a two's complement machine */ + temp2 += temp3; + /* Find the number of bits needed for the magnitude of the coefficient */ - nbits = 0; - while (temp) { - nbits++; - temp >>= 1; - } - /* Check for out-of-range coefficient values. - * Since we're encoding a difference, the range limit is twice as much. - */ - if (nbits > MAX_COEF_BITS+1) - ERREXIT(state->cinfo, JERR_BAD_DCT_COEF); - + nbits = jpeg_nbits_table[temp]; + /* Emit the Huffman-coded symbol for the number of bits */ - if (! emit_bits(state, dctbl->ehufco[nbits], dctbl->ehufsi[nbits])) - return FALSE; + code = dctbl->ehufco[nbits]; + size = dctbl->ehufsi[nbits]; + PUT_BITS(code, size) + CHECKBUF15() + + /* Mask off any extra bits in code */ + temp2 &= (((INT32) 1)< 15, must emit special run-length-16 codes (0xF0) */ - while (r > 15) { - if (! emit_bits(state, actbl->ehufco[0xF0], actbl->ehufsi[0xF0])) - return FALSE; - r -= 16; - } - temp2 = temp; - if (temp < 0) { - temp = -temp; /* temp is abs value of input */ - /* This code assumes we are on a two's complement machine */ - temp2--; - } - - /* Find the number of bits needed for the magnitude of the coefficient */ - nbits = 1; /* there must be at least one 1 bit */ - while ((temp >>= 1)) - nbits++; - /* Check for out-of-range coefficient values */ - if (nbits > MAX_COEF_BITS) - ERREXIT(state->cinfo, JERR_BAD_DCT_COEF); - - /* Emit Huffman symbol for run length / number of bits */ - i = (r << 4) + nbits; - if (! emit_bits(state, actbl->ehufco[i], actbl->ehufsi[i])) - return FALSE; +/* Manually unroll the k loop to eliminate the counter variable. This + * improves performance greatly on systems with a limited number of + * registers (such as x86.) + */ +#define kloop(jpeg_natural_order_of_k) { \ + if ((temp = block[jpeg_natural_order_of_k]) == 0) { \ + r++; \ + } else { \ + temp2 = temp; \ + /* Branch-less absolute value, bitwise complement, etc., same as above */ \ + temp3 = temp >> (CHAR_BIT * sizeof(int) - 1); \ + temp ^= temp3; \ + temp -= temp3; \ + temp2 += temp3; \ + nbits = jpeg_nbits_table[temp]; \ + /* if run length > 15, must emit special run-length-16 codes (0xF0) */ \ + while (r > 15) { \ + EMIT_BITS(code_0xf0, size_0xf0) \ + r -= 16; \ + } \ + /* Emit Huffman symbol for run length / number of bits */ \ + temp3 = (r << 4) + nbits; \ + code = actbl->ehufco[temp3]; \ + size = actbl->ehufsi[temp3]; \ + EMIT_CODE(code, size) \ + r = 0; \ + } \ +} - /* Emit that number of bits of the value, if positive, */ - /* or the complement of its magnitude, if negative. */ - if (! emit_bits(state, (unsigned int) temp2, nbits)) - return FALSE; - - r = 0; - } - } + /* One iteration for each value in jpeg_natural_order[] */ + kloop(1); kloop(8); kloop(16); kloop(9); kloop(2); kloop(3); + kloop(10); kloop(17); kloop(24); kloop(32); kloop(25); kloop(18); + kloop(11); kloop(4); kloop(5); kloop(12); kloop(19); kloop(26); + kloop(33); kloop(40); kloop(48); kloop(41); kloop(34); kloop(27); + kloop(20); kloop(13); kloop(6); kloop(7); kloop(14); kloop(21); + kloop(28); kloop(35); kloop(42); kloop(49); kloop(56); kloop(57); + kloop(50); kloop(43); kloop(36); kloop(29); kloop(22); kloop(15); + kloop(23); kloop(30); kloop(37); kloop(44); kloop(51); kloop(58); + kloop(59); kloop(52); kloop(45); kloop(38); kloop(31); kloop(39); + kloop(46); kloop(53); kloop(60); kloop(61); kloop(54); kloop(47); + kloop(55); kloop(62); kloop(63); /* If the last coef(s) were zero, emit an end-of-block code */ - if (r > 0) - if (! emit_bits(state, actbl->ehufco[0], actbl->ehufsi[0])) - return FALSE; + if (r > 0) { + code = actbl->ehufco[0]; + size = actbl->ehufsi[0]; + EMIT_BITS(code, size) + } + + state->cur.put_buffer = put_buffer; + state->cur.put_bits = put_bits; + STORE_BUFFER() return TRUE; } diff --git a/media/libjpeg/jcmainct.c b/media/libjpeg/jcmainct.c index e0279a7e017..bd0051af852 100644 --- a/media/libjpeg/jcmainct.c +++ b/media/libjpeg/jcmainct.c @@ -68,32 +68,32 @@ METHODDEF(void) process_data_buffer_main METHODDEF(void) start_pass_main (j_compress_ptr cinfo, J_BUF_MODE pass_mode) { - my_main_ptr main = (my_main_ptr) cinfo->main; + my_main_ptr main_ptr = (my_main_ptr) cinfo->main; /* Do nothing in raw-data mode. */ if (cinfo->raw_data_in) return; - main->cur_iMCU_row = 0; /* initialize counters */ - main->rowgroup_ctr = 0; - main->suspended = FALSE; - main->pass_mode = pass_mode; /* save mode for use by process_data */ + main_ptr->cur_iMCU_row = 0; /* initialize counters */ + main_ptr->rowgroup_ctr = 0; + main_ptr->suspended = FALSE; + main_ptr->pass_mode = pass_mode; /* save mode for use by process_data */ switch (pass_mode) { case JBUF_PASS_THRU: #ifdef FULL_MAIN_BUFFER_SUPPORTED - if (main->whole_image[0] != NULL) + if (main_ptr->whole_image[0] != NULL) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); #endif - main->pub.process_data = process_data_simple_main; + main_ptr->pub.process_data = process_data_simple_main; break; #ifdef FULL_MAIN_BUFFER_SUPPORTED case JBUF_SAVE_SOURCE: case JBUF_CRANK_DEST: case JBUF_SAVE_AND_PASS: - if (main->whole_image[0] == NULL) + if (main_ptr->whole_image[0] == NULL) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - main->pub.process_data = process_data_buffer_main; + main_ptr->pub.process_data = process_data_buffer_main; break; #endif default: @@ -114,46 +114,46 @@ process_data_simple_main (j_compress_ptr cinfo, JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail) { - my_main_ptr main = (my_main_ptr) cinfo->main; + my_main_ptr main_ptr = (my_main_ptr) cinfo->main; - while (main->cur_iMCU_row < cinfo->total_iMCU_rows) { + while (main_ptr->cur_iMCU_row < cinfo->total_iMCU_rows) { /* Read input data if we haven't filled the main buffer yet */ - if (main->rowgroup_ctr < DCTSIZE) + if (main_ptr->rowgroup_ctr < DCTSIZE) (*cinfo->prep->pre_process_data) (cinfo, input_buf, in_row_ctr, in_rows_avail, - main->buffer, &main->rowgroup_ctr, + main_ptr->buffer, &main_ptr->rowgroup_ctr, (JDIMENSION) DCTSIZE); /* If we don't have a full iMCU row buffered, return to application for * more data. Note that preprocessor will always pad to fill the iMCU row * at the bottom of the image. */ - if (main->rowgroup_ctr != DCTSIZE) + if (main_ptr->rowgroup_ctr != DCTSIZE) return; /* Send the completed row to the compressor */ - if (! (*cinfo->coef->compress_data) (cinfo, main->buffer)) { + if (! (*cinfo->coef->compress_data) (cinfo, main_ptr->buffer)) { /* If compressor did not consume the whole row, then we must need to * suspend processing and return to the application. In this situation * we pretend we didn't yet consume the last input row; otherwise, if * it happened to be the last row of the image, the application would * think we were done. */ - if (! main->suspended) { + if (! main_ptr->suspended) { (*in_row_ctr)--; - main->suspended = TRUE; + main_ptr->suspended = TRUE; } return; } /* We did finish the row. Undo our little suspension hack if a previous * call suspended; then mark the main buffer empty. */ - if (main->suspended) { + if (main_ptr->suspended) { (*in_row_ctr)++; - main->suspended = FALSE; + main_ptr->suspended = FALSE; } - main->rowgroup_ctr = 0; - main->cur_iMCU_row++; + main_ptr->rowgroup_ctr = 0; + main_ptr->cur_iMCU_row++; } } @@ -173,22 +173,22 @@ process_data_buffer_main (j_compress_ptr cinfo, my_main_ptr main = (my_main_ptr) cinfo->main; int ci; jpeg_component_info *compptr; - boolean writing = (main->pass_mode != JBUF_CRANK_DEST); + boolean writing = (main_ptr->pass_mode != JBUF_CRANK_DEST); - while (main->cur_iMCU_row < cinfo->total_iMCU_rows) { + while (main_ptr->cur_iMCU_row < cinfo->total_iMCU_rows) { /* Realign the virtual buffers if at the start of an iMCU row. */ - if (main->rowgroup_ctr == 0) { + if (main_ptr->rowgroup_ctr == 0) { for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - main->buffer[ci] = (*cinfo->mem->access_virt_sarray) - ((j_common_ptr) cinfo, main->whole_image[ci], - main->cur_iMCU_row * (compptr->v_samp_factor * DCTSIZE), + main_ptr->buffer[ci] = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, main_ptr->whole_image[ci], + main_ptr->cur_iMCU_row * (compptr->v_samp_factor * DCTSIZE), (JDIMENSION) (compptr->v_samp_factor * DCTSIZE), writing); } /* In a read pass, pretend we just read some source data. */ if (! writing) { *in_row_ctr += cinfo->max_v_samp_factor * DCTSIZE; - main->rowgroup_ctr = DCTSIZE; + main_ptr->rowgroup_ctr = DCTSIZE; } } @@ -197,40 +197,40 @@ process_data_buffer_main (j_compress_ptr cinfo, if (writing) { (*cinfo->prep->pre_process_data) (cinfo, input_buf, in_row_ctr, in_rows_avail, - main->buffer, &main->rowgroup_ctr, + main_ptr->buffer, &main_ptr->rowgroup_ctr, (JDIMENSION) DCTSIZE); /* Return to application if we need more data to fill the iMCU row. */ - if (main->rowgroup_ctr < DCTSIZE) + if (main_ptr->rowgroup_ctr < DCTSIZE) return; } /* Emit data, unless this is a sink-only pass. */ - if (main->pass_mode != JBUF_SAVE_SOURCE) { - if (! (*cinfo->coef->compress_data) (cinfo, main->buffer)) { + if (main_ptr->pass_mode != JBUF_SAVE_SOURCE) { + if (! (*cinfo->coef->compress_data) (cinfo, main_ptr->buffer)) { /* If compressor did not consume the whole row, then we must need to * suspend processing and return to the application. In this situation * we pretend we didn't yet consume the last input row; otherwise, if * it happened to be the last row of the image, the application would * think we were done. */ - if (! main->suspended) { + if (! main_ptr->suspended) { (*in_row_ctr)--; - main->suspended = TRUE; + main_ptr->suspended = TRUE; } return; } /* We did finish the row. Undo our little suspension hack if a previous * call suspended; then mark the main buffer empty. */ - if (main->suspended) { + if (main_ptr->suspended) { (*in_row_ctr)++; - main->suspended = FALSE; + main_ptr->suspended = FALSE; } } /* If get here, we are done with this iMCU row. Mark buffer empty. */ - main->rowgroup_ctr = 0; - main->cur_iMCU_row++; + main_ptr->rowgroup_ctr = 0; + main_ptr->cur_iMCU_row++; } } @@ -244,15 +244,15 @@ process_data_buffer_main (j_compress_ptr cinfo, GLOBAL(void) jinit_c_main_controller (j_compress_ptr cinfo, boolean need_full_buffer) { - my_main_ptr main; + my_main_ptr main_ptr; int ci; jpeg_component_info *compptr; - main = (my_main_ptr) + main_ptr = (my_main_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, SIZEOF(my_main_controller)); - cinfo->main = (struct jpeg_c_main_controller *) main; - main->pub.start_pass = start_pass_main; + cinfo->main = (struct jpeg_c_main_controller *) main_ptr; + main_ptr->pub.start_pass = start_pass_main; /* We don't need to create a buffer in raw-data mode. */ if (cinfo->raw_data_in) @@ -267,7 +267,7 @@ jinit_c_main_controller (j_compress_ptr cinfo, boolean need_full_buffer) /* Note we pad the bottom to a multiple of the iMCU height */ for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - main->whole_image[ci] = (*cinfo->mem->request_virt_sarray) + main_ptr->whole_image[ci] = (*cinfo->mem->request_virt_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, compptr->width_in_blocks * DCTSIZE, (JDIMENSION) jround_up((long) compptr->height_in_blocks, @@ -279,12 +279,12 @@ jinit_c_main_controller (j_compress_ptr cinfo, boolean need_full_buffer) #endif } else { #ifdef FULL_MAIN_BUFFER_SUPPORTED - main->whole_image[0] = NULL; /* flag for no virtual arrays */ + main_ptr->whole_image[0] = NULL; /* flag for no virtual arrays */ #endif /* Allocate a strip buffer for each component */ for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - main->buffer[ci] = (*cinfo->mem->alloc_sarray) + main_ptr->buffer[ci] = (*cinfo->mem->alloc_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, compptr->width_in_blocks * DCTSIZE, (JDIMENSION) (compptr->v_samp_factor * DCTSIZE)); diff --git a/media/libjpeg/jcmaster.c b/media/libjpeg/jcmaster.c index 74df5556ce2..3ca346ca80d 100644 --- a/media/libjpeg/jcmaster.c +++ b/media/libjpeg/jcmaster.c @@ -75,7 +75,9 @@ initial_setup (j_compress_ptr cinfo, boolean transcode_only) JDIMENSION jd_samplesperrow; #if JPEG_LIB_VERSION >= 70 +#if JPEG_LIB_VERSION >= 80 if (!transcode_only) +#endif jpeg_calc_jpeg_dimensions(cinfo); #endif diff --git a/media/libjpeg/jconfig.h b/media/libjpeg/jconfig.h index 4d3c49d37c9..f25cbc4d251 100644 --- a/media/libjpeg/jconfig.h +++ b/media/libjpeg/jconfig.h @@ -4,7 +4,10 @@ /* Export libjpeg v6.2's ABI. */ #define JPEG_LIB_VERSION 62 -/* Define if your compiler supports prototypes */ +/* libjpeg-turbo version */ +#define LIBJPEG_TURBO_VERSION 1.2.0 + +/* Compiler supports function prototypes. */ #define HAVE_PROTOTYPES 1 /* Define to 1 if you have the header file. */ @@ -13,25 +16,25 @@ /* Define to 1 if you have the header file. */ #define HAVE_STDLIB_H 1 -/* Define to 1 if the system has the type `unsigned char'. */ +/* Compiler supports 'unsigned char'. */ #define HAVE_UNSIGNED_CHAR 1 -/* Define to 1 if the system has the type `unsigned short'. */ +/* Compiler supports 'unsigned short'. */ #define HAVE_UNSIGNED_SHORT 1 -/* Define if you want use complete types */ +/* Compiler does not support pointers to unspecified structures. */ /* #define INCOMPLETE_TYPES_BROKEN 1 */ -/* Define if you have BSD-like bzero and bcopy */ +/* Compiler has rather than standard . */ /* #undef NEED_BSD_STRINGS */ -/* Define if you need short function names */ +/* Linker requires that global names be unique in first 15 characters. */ /* #undef NEED_SHORT_EXTERNAL_NAMES */ -/* Define if you have sys/types.h */ +/* Need to include in order to obtain size_t. */ #define NEED_SYS_TYPES_H 1 -/* Define if shift is unsigned */ +/* Broken compiler shifts signed values as an unsigned shift. */ /* #undef RIGHT_SHIFT_IS_UNSIGNED */ /* Use accelerated SIMD routines. */ @@ -45,15 +48,5 @@ /* Define to empty if `const' does not conform to ANSI C. */ /* #undef const */ -/* Define to `__inline__' or `__inline' if that's what the C compiler - calls it, or to nothing if 'inline' is not supported under any name. */ -#ifndef __cplusplus -/* #undef inline */ -#endif - /* Define to `unsigned int' if does not define. */ /* #undef size_t */ - -/* MOZILLA CHANGE: libjpeg-turbo doesn't define INLINE in its config file, so - * we define it here. */ -#define INLINE NS_ALWAYS_INLINE diff --git a/media/libjpeg/jconfig.h.in b/media/libjpeg/jconfig.h.in deleted file mode 100644 index 670afab53e4..00000000000 --- a/media/libjpeg/jconfig.h.in +++ /dev/null @@ -1,60 +0,0 @@ -/* Version ID for the JPEG library. - * Might be useful for tests like "#if JPEG_LIB_VERSION >= 60". - */ -#define JPEG_LIB_VERSION 62 /* Version 6b */ - -/* Support arithmetic encoding */ -#undef C_ARITH_CODING_SUPPORTED - -/* Support arithmetic decoding */ -#undef D_ARITH_CODING_SUPPORTED - -/* Define if your compiler supports prototypes */ -#undef HAVE_PROTOTYPES - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDDEF_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDLIB_H - -/* Define to 1 if the system has the type `unsigned char'. */ -#undef HAVE_UNSIGNED_CHAR - -/* Define to 1 if the system has the type `unsigned short'. */ -#undef HAVE_UNSIGNED_SHORT - -/* Define if you want use complete types */ -#undef INCOMPLETE_TYPES_BROKEN - -/* Define if you have BSD-like bzero and bcopy */ -#undef NEED_BSD_STRINGS - -/* Define if you need short function names */ -#undef NEED_SHORT_EXTERNAL_NAMES - -/* Define if you have sys/types.h */ -#undef NEED_SYS_TYPES_H - -/* Define if shift is unsigned */ -#undef RIGHT_SHIFT_IS_UNSIGNED - -/* Use accelerated SIMD routines. */ -#undef WITH_SIMD - -/* Define to 1 if type `char' is unsigned and you are not using gcc. */ -#ifndef __CHAR_UNSIGNED__ -# undef __CHAR_UNSIGNED__ -#endif - -/* Define to empty if `const' does not conform to ANSI C. */ -#undef const - -/* Define to `__inline__' or `__inline' if that's what the C compiler - calls it, or to nothing if 'inline' is not supported under any name. */ -#ifndef __cplusplus -#undef inline -#endif - -/* Define to `unsigned int' if does not define. */ -#undef size_t diff --git a/media/libjpeg/jcparam.c b/media/libjpeg/jcparam.c index 27b5a035b30..557fdc9f67b 100644 --- a/media/libjpeg/jcparam.c +++ b/media/libjpeg/jcparam.c @@ -3,7 +3,7 @@ * * Copyright (C) 1991-1998, Thomas G. Lane. * Modified 2003-2008 by Guido Vollbeding. - * Copyright (C) 2009-2010, D. R. Commander. + * Copyright (C) 2009-2011, D. R. Commander. * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * @@ -398,6 +398,10 @@ jpeg_default_colorspace (j_compress_ptr cinfo) case JCS_EXT_BGRX: case JCS_EXT_XBGR: case JCS_EXT_XRGB: + case JCS_EXT_RGBA: + case JCS_EXT_BGRA: + case JCS_EXT_ABGR: + case JCS_EXT_ARGB: jpeg_set_colorspace(cinfo, JCS_YCbCr); break; case JCS_YCbCr: diff --git a/media/libjpeg/jctrans.c b/media/libjpeg/jctrans.c new file mode 100644 index 00000000000..916e872faf0 --- /dev/null +++ b/media/libjpeg/jctrans.c @@ -0,0 +1,399 @@ +/* + * jctrans.c + * + * Copyright (C) 1995-1998, Thomas G. Lane. + * Modified 2000-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains library routines for transcoding compression, + * that is, writing raw DCT coefficient arrays to an output JPEG file. + * The routines in jcapimin.c will also be needed by a transcoder. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Forward declarations */ +LOCAL(void) transencode_master_selection + JPP((j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays)); +LOCAL(void) transencode_coef_controller + JPP((j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays)); + + +/* + * Compression initialization for writing raw-coefficient data. + * Before calling this, all parameters and a data destination must be set up. + * Call jpeg_finish_compress() to actually write the data. + * + * The number of passed virtual arrays must match cinfo->num_components. + * Note that the virtual arrays need not be filled or even realized at + * the time write_coefficients is called; indeed, if the virtual arrays + * were requested from this compression object's memory manager, they + * typically will be realized during this routine and filled afterwards. + */ + +GLOBAL(void) +jpeg_write_coefficients (j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays) +{ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Mark all tables to be written */ + jpeg_suppress_tables(cinfo, FALSE); + /* (Re)initialize error mgr and destination modules */ + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + (*cinfo->dest->init_destination) (cinfo); + /* Perform master selection of active modules */ + transencode_master_selection(cinfo, coef_arrays); + /* Wait for jpeg_finish_compress() call */ + cinfo->next_scanline = 0; /* so jpeg_write_marker works */ + cinfo->global_state = CSTATE_WRCOEFS; +} + + +/* + * Initialize the compression object with default parameters, + * then copy from the source object all parameters needed for lossless + * transcoding. Parameters that can be varied without loss (such as + * scan script and Huffman optimization) are left in their default states. + */ + +GLOBAL(void) +jpeg_copy_critical_parameters (j_decompress_ptr srcinfo, + j_compress_ptr dstinfo) +{ + JQUANT_TBL ** qtblptr; + jpeg_component_info *incomp, *outcomp; + JQUANT_TBL *c_quant, *slot_quant; + int tblno, ci, coefi; + + /* Safety check to ensure start_compress not called yet. */ + if (dstinfo->global_state != CSTATE_START) + ERREXIT1(dstinfo, JERR_BAD_STATE, dstinfo->global_state); + /* Copy fundamental image dimensions */ + dstinfo->image_width = srcinfo->image_width; + dstinfo->image_height = srcinfo->image_height; + dstinfo->input_components = srcinfo->num_components; + dstinfo->in_color_space = srcinfo->jpeg_color_space; +#if JPEG_LIB_VERSION >= 70 + dstinfo->jpeg_width = srcinfo->output_width; + dstinfo->jpeg_height = srcinfo->output_height; + dstinfo->min_DCT_h_scaled_size = srcinfo->min_DCT_h_scaled_size; + dstinfo->min_DCT_v_scaled_size = srcinfo->min_DCT_v_scaled_size; +#endif + /* Initialize all parameters to default values */ + jpeg_set_defaults(dstinfo); + /* jpeg_set_defaults may choose wrong colorspace, eg YCbCr if input is RGB. + * Fix it to get the right header markers for the image colorspace. + */ + jpeg_set_colorspace(dstinfo, srcinfo->jpeg_color_space); + dstinfo->data_precision = srcinfo->data_precision; + dstinfo->CCIR601_sampling = srcinfo->CCIR601_sampling; + /* Copy the source's quantization tables. */ + for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) { + if (srcinfo->quant_tbl_ptrs[tblno] != NULL) { + qtblptr = & dstinfo->quant_tbl_ptrs[tblno]; + if (*qtblptr == NULL) + *qtblptr = jpeg_alloc_quant_table((j_common_ptr) dstinfo); + MEMCOPY((*qtblptr)->quantval, + srcinfo->quant_tbl_ptrs[tblno]->quantval, + SIZEOF((*qtblptr)->quantval)); + (*qtblptr)->sent_table = FALSE; + } + } + /* Copy the source's per-component info. + * Note we assume jpeg_set_defaults has allocated the dest comp_info array. + */ + dstinfo->num_components = srcinfo->num_components; + if (dstinfo->num_components < 1 || dstinfo->num_components > MAX_COMPONENTS) + ERREXIT2(dstinfo, JERR_COMPONENT_COUNT, dstinfo->num_components, + MAX_COMPONENTS); + for (ci = 0, incomp = srcinfo->comp_info, outcomp = dstinfo->comp_info; + ci < dstinfo->num_components; ci++, incomp++, outcomp++) { + outcomp->component_id = incomp->component_id; + outcomp->h_samp_factor = incomp->h_samp_factor; + outcomp->v_samp_factor = incomp->v_samp_factor; + outcomp->quant_tbl_no = incomp->quant_tbl_no; + /* Make sure saved quantization table for component matches the qtable + * slot. If not, the input file re-used this qtable slot. + * IJG encoder currently cannot duplicate this. + */ + tblno = outcomp->quant_tbl_no; + if (tblno < 0 || tblno >= NUM_QUANT_TBLS || + srcinfo->quant_tbl_ptrs[tblno] == NULL) + ERREXIT1(dstinfo, JERR_NO_QUANT_TABLE, tblno); + slot_quant = srcinfo->quant_tbl_ptrs[tblno]; + c_quant = incomp->quant_table; + if (c_quant != NULL) { + for (coefi = 0; coefi < DCTSIZE2; coefi++) { + if (c_quant->quantval[coefi] != slot_quant->quantval[coefi]) + ERREXIT1(dstinfo, JERR_MISMATCHED_QUANT_TABLE, tblno); + } + } + /* Note: we do not copy the source's Huffman table assignments; + * instead we rely on jpeg_set_colorspace to have made a suitable choice. + */ + } + /* Also copy JFIF version and resolution information, if available. + * Strictly speaking this isn't "critical" info, but it's nearly + * always appropriate to copy it if available. In particular, + * if the application chooses to copy JFIF 1.02 extension markers from + * the source file, we need to copy the version to make sure we don't + * emit a file that has 1.02 extensions but a claimed version of 1.01. + * We will *not*, however, copy version info from mislabeled "2.01" files. + */ + if (srcinfo->saw_JFIF_marker) { + if (srcinfo->JFIF_major_version == 1) { + dstinfo->JFIF_major_version = srcinfo->JFIF_major_version; + dstinfo->JFIF_minor_version = srcinfo->JFIF_minor_version; + } + dstinfo->density_unit = srcinfo->density_unit; + dstinfo->X_density = srcinfo->X_density; + dstinfo->Y_density = srcinfo->Y_density; + } +} + + +/* + * Master selection of compression modules for transcoding. + * This substitutes for jcinit.c's initialization of the full compressor. + */ + +LOCAL(void) +transencode_master_selection (j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays) +{ + /* Although we don't actually use input_components for transcoding, + * jcmaster.c's initial_setup will complain if input_components is 0. + */ + cinfo->input_components = 1; + /* Initialize master control (includes parameter checking/processing) */ + jinit_c_master_control(cinfo, TRUE /* transcode only */); + + /* Entropy encoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { +#ifdef C_ARITH_CODING_SUPPORTED + jinit_arith_encoder(cinfo); +#else + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); +#endif + } else { + if (cinfo->progressive_mode) { +#ifdef C_PROGRESSIVE_SUPPORTED + jinit_phuff_encoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_encoder(cinfo); + } + + /* We need a special coefficient buffer controller. */ + transencode_coef_controller(cinfo, coef_arrays); + + jinit_marker_writer(cinfo); + + /* We can now tell the memory manager to allocate virtual arrays. */ + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + /* Write the datastream header (SOI, JFIF) immediately. + * Frame and scan headers are postponed till later. + * This lets application insert special markers after the SOI. + */ + (*cinfo->marker->write_file_header) (cinfo); +} + + +/* + * The rest of this file is a special implementation of the coefficient + * buffer controller. This is similar to jccoefct.c, but it handles only + * output from presupplied virtual arrays. Furthermore, we generate any + * dummy padding blocks on-the-fly rather than expecting them to be present + * in the arrays. + */ + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_c_coef_controller pub; /* public fields */ + + JDIMENSION iMCU_row_num; /* iMCU row # within image */ + JDIMENSION mcu_ctr; /* counts MCUs processed in current row */ + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + /* Virtual block array for each component. */ + jvirt_barray_ptr * whole_image; + + /* Workspace for constructing dummy blocks at right/bottom edges. */ + JBLOCKROW dummy_buffer[C_MAX_BLOCKS_IN_MCU]; +} my_coef_controller; + +typedef my_coef_controller * my_coef_ptr; + + +LOCAL(void) +start_iMCU_row (j_compress_ptr cinfo) +/* Reset within-iMCU-row counters for a new row */ +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + * But at the bottom of the image, process only what's left. + */ + if (cinfo->comps_in_scan > 1) { + coef->MCU_rows_per_iMCU_row = 1; + } else { + if (coef->iMCU_row_num < (cinfo->total_iMCU_rows-1)) + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + else + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + } + + coef->mcu_ctr = 0; + coef->MCU_vert_offset = 0; +} + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + if (pass_mode != JBUF_CRANK_DEST) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + coef->iMCU_row_num = 0; + start_iMCU_row(cinfo); +} + + +/* + * Process some data. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor block rows for each component in the scan. + * The data is obtained from the virtual arrays and fed to the entropy coder. + * Returns TRUE if the iMCU row is completed, FALSE if suspended. + * + * NB: input_buf is ignored; it is likely to be a NULL pointer. + */ + +METHODDEF(boolean) +compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + int blkn, ci, xindex, yindex, yoffset, blockcnt; + JDIMENSION start_col; + JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN]; + JBLOCKROW MCU_buffer[C_MAX_BLOCKS_IN_MCU]; + JBLOCKROW buffer_ptr; + jpeg_component_info *compptr; + + /* Align the virtual buffers for the components used in this scan. */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + buffer[ci] = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[compptr->component_index], + coef->iMCU_row_num * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + + /* Loop to process one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->mcu_ctr; MCU_col_num < cinfo->MCUs_per_row; + MCU_col_num++) { + /* Construct list of pointers to DCT blocks belonging to this MCU */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + start_col = MCU_col_num * compptr->MCU_width; + blockcnt = (MCU_col_num < last_MCU_col) ? compptr->MCU_width + : compptr->last_col_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + if (coef->iMCU_row_num < last_iMCU_row || + yindex+yoffset < compptr->last_row_height) { + /* Fill in pointers to real blocks in this row */ + buffer_ptr = buffer[ci][yindex+yoffset] + start_col; + for (xindex = 0; xindex < blockcnt; xindex++) + MCU_buffer[blkn++] = buffer_ptr++; + } else { + /* At bottom of image, need a whole row of dummy blocks */ + xindex = 0; + } + /* Fill in any dummy blocks needed in this row. + * Dummy blocks are filled in the same way as in jccoefct.c: + * all zeroes in the AC entries, DC entries equal to previous + * block's DC value. The init routine has already zeroed the + * AC entries, so we need only set the DC entries correctly. + */ + for (; xindex < compptr->MCU_width; xindex++) { + MCU_buffer[blkn] = coef->dummy_buffer[blkn]; + MCU_buffer[blkn][0][0] = MCU_buffer[blkn-1][0][0]; + blkn++; + } + } + } + /* Try to write the MCU. */ + if (! (*cinfo->entropy->encode_mcu) (cinfo, MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->mcu_ctr = MCU_col_num; + return FALSE; + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->mcu_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + coef->iMCU_row_num++; + start_iMCU_row(cinfo); + return TRUE; +} + + +/* + * Initialize coefficient buffer controller. + * + * Each passed coefficient array must be the right size for that + * coefficient: width_in_blocks wide and height_in_blocks high, + * with unitheight at least v_samp_factor. + */ + +LOCAL(void) +transencode_coef_controller (j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays) +{ + my_coef_ptr coef; + JBLOCKROW buffer; + int i; + + coef = (my_coef_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_coef_controller)); + cinfo->coef = (struct jpeg_c_coef_controller *) coef; + coef->pub.start_pass = start_pass_coef; + coef->pub.compress_data = compress_output; + + /* Save pointer to virtual arrays */ + coef->whole_image = coef_arrays; + + /* Allocate and pre-zero space for dummy DCT blocks. */ + buffer = (JBLOCKROW) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + jzero_far((void FAR *) buffer, C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + for (i = 0; i < C_MAX_BLOCKS_IN_MCU; i++) { + coef->dummy_buffer[i] = buffer + i; + } +} diff --git a/media/libjpeg/jdcolext.c b/media/libjpeg/jdcolext.c new file mode 100644 index 00000000000..07da949fae0 --- /dev/null +++ b/media/libjpeg/jdcolext.c @@ -0,0 +1,104 @@ +/* + * jdcolext.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * Copyright (C) 2009, 2011, D. R. Commander. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains output colorspace conversion routines. + */ + + +/* This file is included by jdcolor.c */ + + +/* + * Convert some rows of samples to the output colorspace. + * + * Note that we change from noninterleaved, one-plane-per-component format + * to interleaved-pixel format. The output buffer is therefore three times + * as wide as the input buffer. + * A starting row offset is provided only for the input buffer. The caller + * can easily adjust the passed output_buf value to accommodate any row + * offset required on that side. + */ + +INLINE +LOCAL(void) +ycc_rgb_convert_internal (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int y, cb, cr; + register JSAMPROW outptr; + register JSAMPROW inptr0, inptr1, inptr2; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->output_width; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + register int * Crrtab = cconvert->Cr_r_tab; + register int * Cbbtab = cconvert->Cb_b_tab; + register INT32 * Crgtab = cconvert->Cr_g_tab; + register INT32 * Cbgtab = cconvert->Cb_g_tab; + SHIFT_TEMPS + + while (--num_rows >= 0) { + inptr0 = input_buf[0][input_row]; + inptr1 = input_buf[1][input_row]; + inptr2 = input_buf[2][input_row]; + input_row++; + outptr = *output_buf++; + for (col = 0; col < num_cols; col++) { + y = GETJSAMPLE(inptr0[col]); + cb = GETJSAMPLE(inptr1[col]); + cr = GETJSAMPLE(inptr2[col]); + /* Range-limiting is essential due to noise introduced by DCT losses. */ + outptr[RGB_RED] = range_limit[y + Crrtab[cr]]; + outptr[RGB_GREEN] = range_limit[y + + ((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], + SCALEBITS))]; + outptr[RGB_BLUE] = range_limit[y + Cbbtab[cb]]; + /* Set unused byte to 0xFF so it can be interpreted as an opaque */ + /* alpha channel value */ +#ifdef RGB_ALPHA + outptr[RGB_ALPHA] = 0xFF; +#endif + outptr += RGB_PIXELSIZE; + } + } +} + + +/* + * Convert grayscale to RGB: just duplicate the graylevel three times. + * This is provided to support applications that don't want to cope + * with grayscale as a separate case. + */ + +INLINE +LOCAL(void) +gray_rgb_convert_internal (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + register JSAMPROW inptr, outptr; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->output_width; + + while (--num_rows >= 0) { + inptr = input_buf[0][input_row++]; + outptr = *output_buf++; + for (col = 0; col < num_cols; col++) { + /* We can dispense with GETJSAMPLE() here */ + outptr[RGB_RED] = outptr[RGB_GREEN] = outptr[RGB_BLUE] = inptr[col]; + /* Set unused byte to 0xFF so it can be interpreted as an opaque */ + /* alpha channel value */ +#ifdef RGB_ALPHA + outptr[RGB_ALPHA] = 0xFF; +#endif + outptr += RGB_PIXELSIZE; + } + } +} diff --git a/media/libjpeg/jdcolor.c b/media/libjpeg/jdcolor.c index bc73b3f4621..d9268dbb522 100644 --- a/media/libjpeg/jdcolor.c +++ b/media/libjpeg/jdcolor.c @@ -3,7 +3,7 @@ * * Copyright (C) 1991-1997, Thomas G. Lane. * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2009, D. R. Commander. + * Copyright (C) 2009, 2011, D. R. Commander. * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * @@ -14,6 +14,7 @@ #include "jinclude.h" #include "jpeglib.h" #include "jsimd.h" +#include "config.h" /* Private subobject */ @@ -65,6 +66,107 @@ typedef my_color_deconverter * my_cconvert_ptr; #define FIX(x) ((INT32) ((x) * (1L<RGB colorspace conversion. */ @@ -110,13 +212,6 @@ build_ycc_rgb_table (j_decompress_ptr cinfo) /* * Convert some rows of samples to the output colorspace. - * - * Note that we change from noninterleaved, one-plane-per-component format - * to interleaved-pixel format. The output buffer is therefore three times - * as wide as the input buffer. - * A starting row offset is provided only for the input buffer. The caller - * can easily adjust the passed output_buf value to accommodate any row - * offset required on that side. */ METHODDEF(void) @@ -124,38 +219,39 @@ ycc_rgb_convert (j_decompress_ptr cinfo, JSAMPIMAGE input_buf, JDIMENSION input_row, JSAMPARRAY output_buf, int num_rows) { - my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; - register int y, cb, cr; - register JSAMPROW outptr; - register JSAMPROW inptr0, inptr1, inptr2; - register JDIMENSION col; - JDIMENSION num_cols = cinfo->output_width; - /* copy these pointers into registers if possible */ - register JSAMPLE * range_limit = cinfo->sample_range_limit; - register int * Crrtab = cconvert->Cr_r_tab; - register int * Cbbtab = cconvert->Cb_b_tab; - register INT32 * Crgtab = cconvert->Cr_g_tab; - register INT32 * Cbgtab = cconvert->Cb_g_tab; - SHIFT_TEMPS - - while (--num_rows >= 0) { - inptr0 = input_buf[0][input_row]; - inptr1 = input_buf[1][input_row]; - inptr2 = input_buf[2][input_row]; - input_row++; - outptr = *output_buf++; - for (col = 0; col < num_cols; col++) { - y = GETJSAMPLE(inptr0[col]); - cb = GETJSAMPLE(inptr1[col]); - cr = GETJSAMPLE(inptr2[col]); - /* Range-limiting is essential due to noise introduced by DCT losses. */ - outptr[rgb_red[cinfo->out_color_space]] = range_limit[y + Crrtab[cr]]; - outptr[rgb_green[cinfo->out_color_space]] = range_limit[y + - ((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], - SCALEBITS))]; - outptr[rgb_blue[cinfo->out_color_space]] = range_limit[y + Cbbtab[cb]]; - outptr += rgb_pixelsize[cinfo->out_color_space]; - } + switch (cinfo->out_color_space) { + case JCS_EXT_RGB: + ycc_extrgb_convert_internal(cinfo, input_buf, input_row, output_buf, + num_rows); + break; + case JCS_EXT_RGBX: + case JCS_EXT_RGBA: + ycc_extrgbx_convert_internal(cinfo, input_buf, input_row, output_buf, + num_rows); + break; + case JCS_EXT_BGR: + ycc_extbgr_convert_internal(cinfo, input_buf, input_row, output_buf, + num_rows); + break; + case JCS_EXT_BGRX: + case JCS_EXT_BGRA: + ycc_extbgrx_convert_internal(cinfo, input_buf, input_row, output_buf, + num_rows); + break; + case JCS_EXT_XBGR: + case JCS_EXT_ABGR: + ycc_extxbgr_convert_internal(cinfo, input_buf, input_row, output_buf, + num_rows); + break; + case JCS_EXT_XRGB: + case JCS_EXT_ARGB: + ycc_extxrgb_convert_internal(cinfo, input_buf, input_row, output_buf, + num_rows); + break; + default: + ycc_rgb_convert_internal(cinfo, input_buf, input_row, output_buf, + num_rows); + break; } } @@ -211,9 +307,7 @@ grayscale_convert (j_decompress_ptr cinfo, /* - * Convert grayscale to RGB: just duplicate the graylevel three times. - * This is provided to support applications that don't want to cope - * with grayscale as a separate case. + * Convert grayscale to RGB */ METHODDEF(void) @@ -221,22 +315,39 @@ gray_rgb_convert (j_decompress_ptr cinfo, JSAMPIMAGE input_buf, JDIMENSION input_row, JSAMPARRAY output_buf, int num_rows) { - register JSAMPROW inptr, outptr; - JSAMPLE *maxinptr; - JDIMENSION num_cols = cinfo->output_width; - int rindex = rgb_red[cinfo->out_color_space]; - int gindex = rgb_green[cinfo->out_color_space]; - int bindex = rgb_blue[cinfo->out_color_space]; - int rgbstride = rgb_pixelsize[cinfo->out_color_space]; - - while (--num_rows >= 0) { - inptr = input_buf[0][input_row++]; - maxinptr = &inptr[num_cols]; - outptr = *output_buf++; - for (; inptr < maxinptr; inptr++, outptr += rgbstride) { - /* We can dispense with GETJSAMPLE() here */ - outptr[rindex] = outptr[gindex] = outptr[bindex] = *inptr; - } + switch (cinfo->out_color_space) { + case JCS_EXT_RGB: + gray_extrgb_convert_internal(cinfo, input_buf, input_row, output_buf, + num_rows); + break; + case JCS_EXT_RGBX: + case JCS_EXT_RGBA: + gray_extrgbx_convert_internal(cinfo, input_buf, input_row, output_buf, + num_rows); + break; + case JCS_EXT_BGR: + gray_extbgr_convert_internal(cinfo, input_buf, input_row, output_buf, + num_rows); + break; + case JCS_EXT_BGRX: + case JCS_EXT_BGRA: + gray_extbgrx_convert_internal(cinfo, input_buf, input_row, output_buf, + num_rows); + break; + case JCS_EXT_XBGR: + case JCS_EXT_ABGR: + gray_extxbgr_convert_internal(cinfo, input_buf, input_row, output_buf, + num_rows); + break; + case JCS_EXT_XRGB: + case JCS_EXT_ARGB: + gray_extxrgb_convert_internal(cinfo, input_buf, input_row, output_buf, + num_rows); + break; + default: + gray_rgb_convert_internal(cinfo, input_buf, input_row, output_buf, + num_rows); + break; } } @@ -369,6 +480,10 @@ jinit_color_deconverter (j_decompress_ptr cinfo) case JCS_EXT_BGRX: case JCS_EXT_XBGR: case JCS_EXT_XRGB: + case JCS_EXT_RGBA: + case JCS_EXT_BGRA: + case JCS_EXT_ABGR: + case JCS_EXT_ARGB: cinfo->out_color_components = rgb_pixelsize[cinfo->out_color_space]; if (cinfo->jpeg_color_space == JCS_YCbCr) { if (jsimd_can_ycc_rgb()) diff --git a/media/libjpeg/jdhuff.c b/media/libjpeg/jdhuff.c index b5ba39f736a..f822dba8604 100644 --- a/media/libjpeg/jdhuff.c +++ b/media/libjpeg/jdhuff.c @@ -2,6 +2,7 @@ * jdhuff.c * * Copyright (C) 1991-1997, Thomas G. Lane. + * Copyright (C) 2009-2011, D. R. Commander. * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * @@ -18,6 +19,7 @@ #include "jinclude.h" #include "jpeglib.h" #include "jdhuff.h" /* Declarations shared with jdphuff.c */ +#include "jpegcomp.h" /* @@ -122,7 +124,7 @@ start_pass_huff_decoder (j_decompress_ptr cinfo) if (compptr->component_needed) { entropy->dc_needed[blkn] = TRUE; /* we don't need the ACs if producing a 1/8th-size image */ - entropy->ac_needed[blkn] = (compptr->DCT_scaled_size > 1); + entropy->ac_needed[blkn] = (compptr->_DCT_scaled_size > 1); } else { entropy->dc_needed[blkn] = entropy->ac_needed[blkn] = FALSE; } @@ -225,6 +227,7 @@ jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, boolean isDC, int tblno, dtbl->maxcode[l] = -1; /* -1 if no codes of this length */ } } + dtbl->valoffset[17] = 0; dtbl->maxcode[17] = 0xFFFFFL; /* ensures jpeg_huff_decode terminates */ /* Compute lookahead tables to speed up decoding. @@ -234,7 +237,8 @@ jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, boolean isDC, int tblno, * with that code. */ - MEMZERO(dtbl->look_nbits, SIZEOF(dtbl->look_nbits)); + for (i = 0; i < (1 << HUFF_LOOKAHEAD); i++) + dtbl->lookup[i] = (HUFF_LOOKAHEAD + 1) << HUFF_LOOKAHEAD; p = 0; for (l = 1; l <= HUFF_LOOKAHEAD; l++) { @@ -243,8 +247,7 @@ jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, boolean isDC, int tblno, /* Generate left-justified code followed by all possible bit sequences */ lookbits = huffcode[p] << (HUFF_LOOKAHEAD-l); for (ctr = 1 << (HUFF_LOOKAHEAD-l); ctr > 0; ctr--) { - dtbl->look_nbits[lookbits] = l; - dtbl->look_sym[lookbits] = htbl->huffval[p]; + dtbl->lookup[lookbits] = (l << HUFF_LOOKAHEAD) | htbl->huffval[p]; lookbits++; } } @@ -389,6 +392,50 @@ jpeg_fill_bit_buffer (bitread_working_state * state, } +/* Macro version of the above, which performs much better but does not + handle markers. We have to hand off any blocks with markers to the + slower routines. */ + +#define GET_BYTE \ +{ \ + register int c0, c1; \ + c0 = GETJOCTET(*buffer++); \ + c1 = GETJOCTET(*buffer); \ + /* Pre-execute most common case */ \ + get_buffer = (get_buffer << 8) | c0; \ + bits_left += 8; \ + if (c0 == 0xFF) { \ + /* Pre-execute case of FF/00, which represents an FF data byte */ \ + buffer++; \ + if (c1 != 0) { \ + /* Oops, it's actually a marker indicating end of compressed data. */ \ + cinfo->unread_marker = c1; \ + /* Back out pre-execution and fill the buffer with zero bits */ \ + buffer -= 2; \ + get_buffer &= ~0xFF; \ + } \ + } \ +} + +#if __WORDSIZE == 64 || defined(_WIN64) + +/* Pre-fetch 48 bytes, because the holding register is 64-bit */ +#define FILL_BIT_BUFFER_FAST \ + if (bits_left < 16) { \ + GET_BYTE GET_BYTE GET_BYTE GET_BYTE GET_BYTE GET_BYTE \ + } + +#else + +/* Pre-fetch 16 bytes, because the holding register is 32-bit */ +#define FILL_BIT_BUFFER_FAST \ + if (bits_left < 16) { \ + GET_BYTE GET_BYTE \ + } + +#endif + + /* * Out-of-line code for Huffman code decoding. * See jdhuff.h for info about usage. @@ -438,9 +485,10 @@ jpeg_huff_decode (bitread_working_state * state, * On some machines, a shift and add will be faster than a table lookup. */ +#define AVOID_TABLES #ifdef AVOID_TABLES -#define HUFF_EXTEND(x,s) ((x) < (1<<((s)-1)) ? (x) + (((-1)<<(s)) + 1) : (x)) +#define HUFF_EXTEND(x,s) ((x) + ((((x) - (1<<((s)-1))) >> 31) & (((-1)<<(s)) + 1))) #else @@ -498,6 +546,187 @@ process_restart (j_decompress_ptr cinfo) } +LOCAL(boolean) +decode_mcu_slow (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + BITREAD_STATE_VARS; + int blkn; + savable_state state; + /* Outer loop handles each block in the MCU */ + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(state, entropy->saved); + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + JBLOCKROW block = MCU_data[blkn]; + d_derived_tbl * dctbl = entropy->dc_cur_tbls[blkn]; + d_derived_tbl * actbl = entropy->ac_cur_tbls[blkn]; + register int s, k, r; + + /* Decode a single block's worth of coefficients */ + + /* Section F.2.2.1: decode the DC coefficient difference */ + HUFF_DECODE(s, br_state, dctbl, return FALSE, label1); + if (s) { + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + } + + if (entropy->dc_needed[blkn]) { + /* Convert DC difference to actual value, update last_dc_val */ + int ci = cinfo->MCU_membership[blkn]; + s += state.last_dc_val[ci]; + state.last_dc_val[ci] = s; + /* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */ + (*block)[0] = (JCOEF) s; + } + + if (entropy->ac_needed[blkn]) { + + /* Section F.2.2.2: decode the AC coefficients */ + /* Since zeroes are skipped, output area must be cleared beforehand */ + for (k = 1; k < DCTSIZE2; k++) { + HUFF_DECODE(s, br_state, actbl, return FALSE, label2); + + r = s >> 4; + s &= 15; + + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + /* Output coefficient in natural (dezigzagged) order. + * Note: the extra entries in jpeg_natural_order[] will save us + * if k >= DCTSIZE2, which could happen if the data is corrupted. + */ + (*block)[jpeg_natural_order[k]] = (JCOEF) s; + } else { + if (r != 15) + break; + k += 15; + } + } + + } else { + + /* Section F.2.2.2: decode the AC coefficients */ + /* In this path we just discard the values */ + for (k = 1; k < DCTSIZE2; k++) { + HUFF_DECODE(s, br_state, actbl, return FALSE, label3); + + r = s >> 4; + s &= 15; + + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + DROP_BITS(s); + } else { + if (r != 15) + break; + k += 15; + } + } + } + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(entropy->saved, state); + return TRUE; +} + + +LOCAL(boolean) +decode_mcu_fast (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + BITREAD_STATE_VARS; + JOCTET *buffer; + int blkn; + savable_state state; + /* Outer loop handles each block in the MCU */ + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + buffer = (JOCTET *) br_state.next_input_byte; + ASSIGN_STATE(state, entropy->saved); + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + JBLOCKROW block = MCU_data[blkn]; + d_derived_tbl * dctbl = entropy->dc_cur_tbls[blkn]; + d_derived_tbl * actbl = entropy->ac_cur_tbls[blkn]; + register int s, k, r, l; + + HUFF_DECODE_FAST(s, l, dctbl); + if (s) { + FILL_BIT_BUFFER_FAST + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + } + + if (entropy->dc_needed[blkn]) { + int ci = cinfo->MCU_membership[blkn]; + s += state.last_dc_val[ci]; + state.last_dc_val[ci] = s; + (*block)[0] = (JCOEF) s; + } + + if (entropy->ac_needed[blkn]) { + + for (k = 1; k < DCTSIZE2; k++) { + HUFF_DECODE_FAST(s, l, actbl); + r = s >> 4; + s &= 15; + + if (s) { + k += r; + FILL_BIT_BUFFER_FAST + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + (*block)[jpeg_natural_order[k]] = (JCOEF) s; + } else { + if (r != 15) break; + k += 15; + } + } + + } else { + + for (k = 1; k < DCTSIZE2; k++) { + HUFF_DECODE_FAST(s, l, actbl); + r = s >> 4; + s &= 15; + + if (s) { + k += r; + FILL_BIT_BUFFER_FAST + DROP_BITS(s); + } else { + if (r != 15) break; + k += 15; + } + } + } + } + + if (cinfo->unread_marker != 0) { + cinfo->unread_marker = 0; + return FALSE; + } + + br_state.bytes_in_buffer -= (buffer - br_state.next_input_byte); + br_state.next_input_byte = buffer; + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(entropy->saved, state); + return TRUE; +} + + /* * Decode and return one MCU's worth of Huffman-compressed coefficients. * The coefficients are reordered from zigzag order into natural array order, @@ -513,111 +742,39 @@ process_restart (j_decompress_ptr cinfo) * this module, since we'll just re-assign them on the next call.) */ +#define BUFSIZE (DCTSIZE2 * 2) + METHODDEF(boolean) decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) { huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; - int blkn; - BITREAD_STATE_VARS; - savable_state state; + int usefast = 1; /* Process restart marker if needed; may have to suspend */ if (cinfo->restart_interval) { if (entropy->restarts_to_go == 0) if (! process_restart(cinfo)) return FALSE; + usefast = 0; } + if (cinfo->src->bytes_in_buffer < BUFSIZE * (size_t)cinfo->blocks_in_MCU + || cinfo->unread_marker != 0) + usefast = 0; + /* If we've run out of data, just leave the MCU set to zeroes. * This way, we return uniform gray for the remainder of the segment. */ if (! entropy->pub.insufficient_data) { - /* Load up working state */ - BITREAD_LOAD_STATE(cinfo,entropy->bitstate); - ASSIGN_STATE(state, entropy->saved); - - /* Outer loop handles each block in the MCU */ - - for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { - JBLOCKROW block = MCU_data[blkn]; - d_derived_tbl * dctbl = entropy->dc_cur_tbls[blkn]; - d_derived_tbl * actbl = entropy->ac_cur_tbls[blkn]; - register int s, k, r; - - /* Decode a single block's worth of coefficients */ - - /* Section F.2.2.1: decode the DC coefficient difference */ - HUFF_DECODE(s, br_state, dctbl, return FALSE, label1); - if (s) { - CHECK_BIT_BUFFER(br_state, s, return FALSE); - r = GET_BITS(s); - s = HUFF_EXTEND(r, s); - } - - if (entropy->dc_needed[blkn]) { - /* Convert DC difference to actual value, update last_dc_val */ - int ci = cinfo->MCU_membership[blkn]; - s += state.last_dc_val[ci]; - state.last_dc_val[ci] = s; - /* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */ - (*block)[0] = (JCOEF) s; - } - - if (entropy->ac_needed[blkn]) { - - /* Section F.2.2.2: decode the AC coefficients */ - /* Since zeroes are skipped, output area must be cleared beforehand */ - for (k = 1; k < DCTSIZE2; k++) { - HUFF_DECODE(s, br_state, actbl, return FALSE, label2); - - r = s >> 4; - s &= 15; - - if (s) { - k += r; - CHECK_BIT_BUFFER(br_state, s, return FALSE); - r = GET_BITS(s); - s = HUFF_EXTEND(r, s); - /* Output coefficient in natural (dezigzagged) order. - * Note: the extra entries in jpeg_natural_order[] will save us - * if k >= DCTSIZE2, which could happen if the data is corrupted. - */ - (*block)[jpeg_natural_order[k]] = (JCOEF) s; - } else { - if (r != 15) - break; - k += 15; - } - } - - } else { - - /* Section F.2.2.2: decode the AC coefficients */ - /* In this path we just discard the values */ - for (k = 1; k < DCTSIZE2; k++) { - HUFF_DECODE(s, br_state, actbl, return FALSE, label3); - - r = s >> 4; - s &= 15; - - if (s) { - k += r; - CHECK_BIT_BUFFER(br_state, s, return FALSE); - DROP_BITS(s); - } else { - if (r != 15) - break; - k += 15; - } - } - - } + if (usefast) { + if (!decode_mcu_fast(cinfo, MCU_data)) goto use_slow; + } + else { + use_slow: + if (!decode_mcu_slow(cinfo, MCU_data)) return FALSE; } - /* Completed MCU, so update state */ - BITREAD_SAVE_STATE(cinfo,entropy->bitstate); - ASSIGN_STATE(entropy->saved, state); } /* Account for restart interval (no-op if not using restarts) */ diff --git a/media/libjpeg/jdhuff.h b/media/libjpeg/jdhuff.h index ae19b6cafd7..96f2dabf11c 100644 --- a/media/libjpeg/jdhuff.h +++ b/media/libjpeg/jdhuff.h @@ -2,6 +2,7 @@ * jdhuff.h * * Copyright (C) 1991-1997, Thomas G. Lane. + * Copyright (C) 2010-2011, D. R. Commander. * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * @@ -27,7 +28,7 @@ typedef struct { /* Basic tables: (element [0] of each array is unused) */ INT32 maxcode[18]; /* largest code of length k (-1 if none) */ /* (maxcode[17] is a sentinel to ensure jpeg_huff_decode terminates) */ - INT32 valoffset[17]; /* huffval[] offset for codes of length k */ + INT32 valoffset[18]; /* huffval[] offset for codes of length k */ /* valoffset[k] = huffval[] index of 1st symbol of code length k, less * the smallest code of length k; so given a code of length k, the * corresponding symbol is huffval[code + valoffset[k]] @@ -36,13 +37,17 @@ typedef struct { /* Link to public Huffman table (needed only in jpeg_huff_decode) */ JHUFF_TBL *pub; - /* Lookahead tables: indexed by the next HUFF_LOOKAHEAD bits of + /* Lookahead table: indexed by the next HUFF_LOOKAHEAD bits of * the input data stream. If the next Huffman code is no more * than HUFF_LOOKAHEAD bits long, we can obtain its length and - * the corresponding symbol directly from these tables. + * the corresponding symbol directly from this tables. + * + * The lower 8 bits of each table entry contain the number of + * bits in the corresponding Huffman code, or HUFF_LOOKAHEAD + 1 + * if too long. The next 8 bits of each entry contain the + * symbol. */ - int look_nbits[1< 32 bits on your machine, and shifting/masking longs is * reasonably fast, making bit_buf_type be long and setting BIT_BUF_SIZE @@ -183,11 +197,10 @@ EXTERN(boolean) jpeg_fill_bit_buffer } \ } \ look = PEEK_BITS(HUFF_LOOKAHEAD); \ - if ((nb = htbl->look_nbits[look]) != 0) { \ + if ((nb = (htbl->lookup[look] >> HUFF_LOOKAHEAD)) <= HUFF_LOOKAHEAD) { \ DROP_BITS(nb); \ - result = htbl->look_sym[look]; \ + result = htbl->lookup[look] & ((1 << HUFF_LOOKAHEAD) - 1); \ } else { \ - nb = HUFF_LOOKAHEAD+1; \ slowlabel: \ if ((result=jpeg_huff_decode(&state,get_buffer,bits_left,htbl,nb)) < 0) \ { failaction; } \ @@ -195,6 +208,26 @@ slowlabel: \ } \ } +#define HUFF_DECODE_FAST(s,nb,htbl) \ + FILL_BIT_BUFFER_FAST; \ + s = PEEK_BITS(HUFF_LOOKAHEAD); \ + s = htbl->lookup[s]; \ + nb = s >> HUFF_LOOKAHEAD; \ + /* Pre-execute the common case of nb <= HUFF_LOOKAHEAD */ \ + DROP_BITS(nb); \ + s = s & ((1 << HUFF_LOOKAHEAD) - 1); \ + if (nb > HUFF_LOOKAHEAD) { \ + /* Equivalent of jpeg_huff_decode() */ \ + /* Don't use GET_BITS() here because we don't want to modify bits_left */ \ + s = (get_buffer >> bits_left) & ((1 << (nb)) - 1); \ + while (s > htbl->maxcode[nb]) { \ + s <<= 1; \ + s |= GET_BITS(1); \ + nb++; \ + } \ + s = htbl->pub->huffval[ (int) (s + htbl->valoffset[nb]) & 0xFF ]; \ + } + /* Out-of-line case for Huffman code fetching */ EXTERN(int) jpeg_huff_decode JPP((bitread_working_state * state, register bit_buf_type get_buffer, diff --git a/media/libjpeg/jdmainct.c b/media/libjpeg/jdmainct.c index 67f62153e67..eb32cae0941 100644 --- a/media/libjpeg/jdmainct.c +++ b/media/libjpeg/jdmainct.c @@ -161,7 +161,7 @@ alloc_funny_pointers (j_decompress_ptr cinfo) * This is done only once, not once per pass. */ { - my_main_ptr main = (my_main_ptr) cinfo->main; + my_main_ptr main_ptr = (my_main_ptr) cinfo->main; int ci, rgroup; int M = cinfo->_min_DCT_scaled_size; jpeg_component_info *compptr; @@ -170,10 +170,10 @@ alloc_funny_pointers (j_decompress_ptr cinfo) /* Get top-level space for component array pointers. * We alloc both arrays with one call to save a few cycles. */ - main->xbuffer[0] = (JSAMPIMAGE) + main_ptr->xbuffer[0] = (JSAMPIMAGE) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, cinfo->num_components * 2 * SIZEOF(JSAMPARRAY)); - main->xbuffer[1] = main->xbuffer[0] + cinfo->num_components; + main_ptr->xbuffer[1] = main_ptr->xbuffer[0] + cinfo->num_components; for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { @@ -186,9 +186,9 @@ alloc_funny_pointers (j_decompress_ptr cinfo) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, 2 * (rgroup * (M + 4)) * SIZEOF(JSAMPROW)); xbuf += rgroup; /* want one row group at negative offsets */ - main->xbuffer[0][ci] = xbuf; + main_ptr->xbuffer[0][ci] = xbuf; xbuf += rgroup * (M + 4); - main->xbuffer[1][ci] = xbuf; + main_ptr->xbuffer[1][ci] = xbuf; } } @@ -196,13 +196,13 @@ alloc_funny_pointers (j_decompress_ptr cinfo) LOCAL(void) make_funny_pointers (j_decompress_ptr cinfo) /* Create the funny pointer lists discussed in the comments above. - * The actual workspace is already allocated (in main->buffer), + * The actual workspace is already allocated (in main_ptr->buffer), * and the space for the pointer lists is allocated too. * This routine just fills in the curiously ordered lists. * This will be repeated at the beginning of each pass. */ { - my_main_ptr main = (my_main_ptr) cinfo->main; + my_main_ptr main_ptr = (my_main_ptr) cinfo->main; int ci, i, rgroup; int M = cinfo->_min_DCT_scaled_size; jpeg_component_info *compptr; @@ -212,10 +212,10 @@ make_funny_pointers (j_decompress_ptr cinfo) ci++, compptr++) { rgroup = (compptr->v_samp_factor * compptr->_DCT_scaled_size) / cinfo->_min_DCT_scaled_size; /* height of a row group of component */ - xbuf0 = main->xbuffer[0][ci]; - xbuf1 = main->xbuffer[1][ci]; + xbuf0 = main_ptr->xbuffer[0][ci]; + xbuf1 = main_ptr->xbuffer[1][ci]; /* First copy the workspace pointers as-is */ - buf = main->buffer[ci]; + buf = main_ptr->buffer[ci]; for (i = 0; i < rgroup * (M + 2); i++) { xbuf0[i] = xbuf1[i] = buf[i]; } @@ -242,7 +242,7 @@ set_wraparound_pointers (j_decompress_ptr cinfo) * This changes the pointer list state from top-of-image to the normal state. */ { - my_main_ptr main = (my_main_ptr) cinfo->main; + my_main_ptr main_ptr = (my_main_ptr) cinfo->main; int ci, i, rgroup; int M = cinfo->_min_DCT_scaled_size; jpeg_component_info *compptr; @@ -252,8 +252,8 @@ set_wraparound_pointers (j_decompress_ptr cinfo) ci++, compptr++) { rgroup = (compptr->v_samp_factor * compptr->_DCT_scaled_size) / cinfo->_min_DCT_scaled_size; /* height of a row group of component */ - xbuf0 = main->xbuffer[0][ci]; - xbuf1 = main->xbuffer[1][ci]; + xbuf0 = main_ptr->xbuffer[0][ci]; + xbuf1 = main_ptr->xbuffer[1][ci]; for (i = 0; i < rgroup; i++) { xbuf0[i - rgroup] = xbuf0[rgroup*(M+1) + i]; xbuf1[i - rgroup] = xbuf1[rgroup*(M+1) + i]; @@ -271,7 +271,7 @@ set_bottom_pointers (j_decompress_ptr cinfo) * Also sets rowgroups_avail to indicate number of nondummy row groups in row. */ { - my_main_ptr main = (my_main_ptr) cinfo->main; + my_main_ptr main_ptr = (my_main_ptr) cinfo->main; int ci, i, rgroup, iMCUheight, rows_left; jpeg_component_info *compptr; JSAMPARRAY xbuf; @@ -288,12 +288,12 @@ set_bottom_pointers (j_decompress_ptr cinfo) * so we need only do it once. */ if (ci == 0) { - main->rowgroups_avail = (JDIMENSION) ((rows_left-1) / rgroup + 1); + main_ptr->rowgroups_avail = (JDIMENSION) ((rows_left-1) / rgroup + 1); } /* Duplicate the last real sample row rgroup*2 times; this pads out the * last partial rowgroup and ensures at least one full rowgroup of context. */ - xbuf = main->xbuffer[main->whichptr][ci]; + xbuf = main_ptr->xbuffer[main_ptr->whichptr][ci]; for (i = 0; i < rgroup * 2; i++) { xbuf[rows_left + i] = xbuf[rows_left-1]; } @@ -308,27 +308,27 @@ set_bottom_pointers (j_decompress_ptr cinfo) METHODDEF(void) start_pass_main (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) { - my_main_ptr main = (my_main_ptr) cinfo->main; + my_main_ptr main_ptr = (my_main_ptr) cinfo->main; switch (pass_mode) { case JBUF_PASS_THRU: if (cinfo->upsample->need_context_rows) { - main->pub.process_data = process_data_context_main; + main_ptr->pub.process_data = process_data_context_main; make_funny_pointers(cinfo); /* Create the xbuffer[] lists */ - main->whichptr = 0; /* Read first iMCU row into xbuffer[0] */ - main->context_state = CTX_PREPARE_FOR_IMCU; - main->iMCU_row_ctr = 0; + main_ptr->whichptr = 0; /* Read first iMCU row into xbuffer[0] */ + main_ptr->context_state = CTX_PREPARE_FOR_IMCU; + main_ptr->iMCU_row_ctr = 0; } else { /* Simple case with no context needed */ - main->pub.process_data = process_data_simple_main; + main_ptr->pub.process_data = process_data_simple_main; } - main->buffer_full = FALSE; /* Mark buffer empty */ - main->rowgroup_ctr = 0; + main_ptr->buffer_full = FALSE; /* Mark buffer empty */ + main_ptr->rowgroup_ctr = 0; break; #ifdef QUANT_2PASS_SUPPORTED case JBUF_CRANK_DEST: /* For last pass of 2-pass quantization, just crank the postprocessor */ - main->pub.process_data = process_data_crank_post; + main_ptr->pub.process_data = process_data_crank_post; break; #endif default: @@ -348,14 +348,14 @@ process_data_simple_main (j_decompress_ptr cinfo, JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail) { - my_main_ptr main = (my_main_ptr) cinfo->main; + my_main_ptr main_ptr = (my_main_ptr) cinfo->main; JDIMENSION rowgroups_avail; /* Read input data if we haven't filled the main buffer yet */ - if (! main->buffer_full) { - if (! (*cinfo->coef->decompress_data) (cinfo, main->buffer)) + if (! main_ptr->buffer_full) { + if (! (*cinfo->coef->decompress_data) (cinfo, main_ptr->buffer)) return; /* suspension forced, can do nothing more */ - main->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ + main_ptr->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ } /* There are always min_DCT_scaled_size row groups in an iMCU row. */ @@ -366,14 +366,14 @@ process_data_simple_main (j_decompress_ptr cinfo, */ /* Feed the postprocessor */ - (*cinfo->post->post_process_data) (cinfo, main->buffer, - &main->rowgroup_ctr, rowgroups_avail, + (*cinfo->post->post_process_data) (cinfo, main_ptr->buffer, + &main_ptr->rowgroup_ctr, rowgroups_avail, output_buf, out_row_ctr, out_rows_avail); /* Has postprocessor consumed all the data yet? If so, mark buffer empty */ - if (main->rowgroup_ctr >= rowgroups_avail) { - main->buffer_full = FALSE; - main->rowgroup_ctr = 0; + if (main_ptr->rowgroup_ctr >= rowgroups_avail) { + main_ptr->buffer_full = FALSE; + main_ptr->rowgroup_ctr = 0; } } @@ -388,15 +388,15 @@ process_data_context_main (j_decompress_ptr cinfo, JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail) { - my_main_ptr main = (my_main_ptr) cinfo->main; + my_main_ptr main_ptr = (my_main_ptr) cinfo->main; /* Read input data if we haven't filled the main buffer yet */ - if (! main->buffer_full) { + if (! main_ptr->buffer_full) { if (! (*cinfo->coef->decompress_data) (cinfo, - main->xbuffer[main->whichptr])) + main_ptr->xbuffer[main_ptr->whichptr])) return; /* suspension forced, can do nothing more */ - main->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ - main->iMCU_row_ctr++; /* count rows received */ + main_ptr->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ + main_ptr->iMCU_row_ctr++; /* count rows received */ } /* Postprocessor typically will not swallow all the input data it is handed @@ -404,47 +404,47 @@ process_data_context_main (j_decompress_ptr cinfo, * to exit and restart. This switch lets us keep track of how far we got. * Note that each case falls through to the next on successful completion. */ - switch (main->context_state) { + switch (main_ptr->context_state) { case CTX_POSTPONED_ROW: /* Call postprocessor using previously set pointers for postponed row */ - (*cinfo->post->post_process_data) (cinfo, main->xbuffer[main->whichptr], - &main->rowgroup_ctr, main->rowgroups_avail, + (*cinfo->post->post_process_data) (cinfo, main_ptr->xbuffer[main_ptr->whichptr], + &main_ptr->rowgroup_ctr, main_ptr->rowgroups_avail, output_buf, out_row_ctr, out_rows_avail); - if (main->rowgroup_ctr < main->rowgroups_avail) + if (main_ptr->rowgroup_ctr < main_ptr->rowgroups_avail) return; /* Need to suspend */ - main->context_state = CTX_PREPARE_FOR_IMCU; + main_ptr->context_state = CTX_PREPARE_FOR_IMCU; if (*out_row_ctr >= out_rows_avail) return; /* Postprocessor exactly filled output buf */ /*FALLTHROUGH*/ case CTX_PREPARE_FOR_IMCU: /* Prepare to process first M-1 row groups of this iMCU row */ - main->rowgroup_ctr = 0; - main->rowgroups_avail = (JDIMENSION) (cinfo->_min_DCT_scaled_size - 1); + main_ptr->rowgroup_ctr = 0; + main_ptr->rowgroups_avail = (JDIMENSION) (cinfo->_min_DCT_scaled_size - 1); /* Check for bottom of image: if so, tweak pointers to "duplicate" * the last sample row, and adjust rowgroups_avail to ignore padding rows. */ - if (main->iMCU_row_ctr == cinfo->total_iMCU_rows) + if (main_ptr->iMCU_row_ctr == cinfo->total_iMCU_rows) set_bottom_pointers(cinfo); - main->context_state = CTX_PROCESS_IMCU; + main_ptr->context_state = CTX_PROCESS_IMCU; /*FALLTHROUGH*/ case CTX_PROCESS_IMCU: /* Call postprocessor using previously set pointers */ - (*cinfo->post->post_process_data) (cinfo, main->xbuffer[main->whichptr], - &main->rowgroup_ctr, main->rowgroups_avail, + (*cinfo->post->post_process_data) (cinfo, main_ptr->xbuffer[main_ptr->whichptr], + &main_ptr->rowgroup_ctr, main_ptr->rowgroups_avail, output_buf, out_row_ctr, out_rows_avail); - if (main->rowgroup_ctr < main->rowgroups_avail) + if (main_ptr->rowgroup_ctr < main_ptr->rowgroups_avail) return; /* Need to suspend */ /* After the first iMCU, change wraparound pointers to normal state */ - if (main->iMCU_row_ctr == 1) + if (main_ptr->iMCU_row_ctr == 1) set_wraparound_pointers(cinfo); /* Prepare to load new iMCU row using other xbuffer list */ - main->whichptr ^= 1; /* 0=>1 or 1=>0 */ - main->buffer_full = FALSE; + main_ptr->whichptr ^= 1; /* 0=>1 or 1=>0 */ + main_ptr->buffer_full = FALSE; /* Still need to process last row group of this iMCU row, */ /* which is saved at index M+1 of the other xbuffer */ - main->rowgroup_ctr = (JDIMENSION) (cinfo->_min_DCT_scaled_size + 1); - main->rowgroups_avail = (JDIMENSION) (cinfo->_min_DCT_scaled_size + 2); - main->context_state = CTX_POSTPONED_ROW; + main_ptr->rowgroup_ctr = (JDIMENSION) (cinfo->_min_DCT_scaled_size + 1); + main_ptr->rowgroups_avail = (JDIMENSION) (cinfo->_min_DCT_scaled_size + 2); + main_ptr->context_state = CTX_POSTPONED_ROW; } } @@ -477,15 +477,15 @@ process_data_crank_post (j_decompress_ptr cinfo, GLOBAL(void) jinit_d_main_controller (j_decompress_ptr cinfo, boolean need_full_buffer) { - my_main_ptr main; + my_main_ptr main_ptr; int ci, rgroup, ngroups; jpeg_component_info *compptr; - main = (my_main_ptr) + main_ptr = (my_main_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, SIZEOF(my_main_controller)); - cinfo->main = (struct jpeg_d_main_controller *) main; - main->pub.start_pass = start_pass_main; + cinfo->main = (struct jpeg_d_main_controller *) main_ptr; + main_ptr->pub.start_pass = start_pass_main; if (need_full_buffer) /* shouldn't happen */ ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); @@ -506,7 +506,7 @@ jinit_d_main_controller (j_decompress_ptr cinfo, boolean need_full_buffer) ci++, compptr++) { rgroup = (compptr->v_samp_factor * compptr->_DCT_scaled_size) / cinfo->_min_DCT_scaled_size; /* height of a row group of component */ - main->buffer[ci] = (*cinfo->mem->alloc_sarray) + main_ptr->buffer[ci] = (*cinfo->mem->alloc_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, compptr->width_in_blocks * compptr->_DCT_scaled_size, (JDIMENSION) (rgroup * ngroups)); diff --git a/media/libjpeg/jdmarker.c b/media/libjpeg/jdmarker.c index f4cca8cc835..d8dcba98fa0 100644 --- a/media/libjpeg/jdmarker.c +++ b/media/libjpeg/jdmarker.c @@ -2,6 +2,7 @@ * jdmarker.c * * Copyright (C) 1991-1998, Thomas G. Lane. + * Copyright (C) 2012, D. R. Commander. * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * @@ -322,13 +323,16 @@ get_sos (j_decompress_ptr cinfo) /* Collect the component-spec parameters */ + for (i = 0; i < cinfo->num_components; i++) + cinfo->cur_comp_info[i] = NULL; + for (i = 0; i < n; i++) { INPUT_BYTE(cinfo, cc, return FALSE); INPUT_BYTE(cinfo, c, return FALSE); for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - if (cc == compptr->component_id) + if (cc == compptr->component_id && !cinfo->cur_comp_info[ci]) goto id_found; } diff --git a/media/libjpeg/jdmaster.c b/media/libjpeg/jdmaster.c index 14520da884c..c73ec027178 100644 --- a/media/libjpeg/jdmaster.c +++ b/media/libjpeg/jdmaster.c @@ -2,7 +2,7 @@ * jdmaster.c * * Copyright (C) 1991-1997, Thomas G. Lane. - * Copyright (C) 2009-2010, D. R. Commander. + * Copyright (C) 2009-2011, D. R. Commander. * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * @@ -57,7 +57,11 @@ use_merged_upsample (j_decompress_ptr cinfo) cinfo->out_color_space != JCS_EXT_BGR && cinfo->out_color_space != JCS_EXT_BGRX && cinfo->out_color_space != JCS_EXT_XBGR && - cinfo->out_color_space != JCS_EXT_XRGB) || + cinfo->out_color_space != JCS_EXT_XRGB && + cinfo->out_color_space != JCS_EXT_RGBA && + cinfo->out_color_space != JCS_EXT_BGRA && + cinfo->out_color_space != JCS_EXT_ABGR && + cinfo->out_color_space != JCS_EXT_ARGB) || cinfo->out_color_components != rgb_pixelsize[cinfo->out_color_space]) return FALSE; /* and it only handles 2h1v or 2h2v sampling ratios */ @@ -209,6 +213,10 @@ jpeg_calc_output_dimensions (j_decompress_ptr cinfo) case JCS_EXT_BGRX: case JCS_EXT_XBGR: case JCS_EXT_XRGB: + case JCS_EXT_RGBA: + case JCS_EXT_BGRA: + case JCS_EXT_ABGR: + case JCS_EXT_ARGB: cinfo->out_color_components = rgb_pixelsize[cinfo->out_color_space]; break; case JCS_YCbCr: diff --git a/media/libjpeg/jdmerge.c b/media/libjpeg/jdmerge.c index edf061a737b..cfa3bb921d3 100644 --- a/media/libjpeg/jdmerge.c +++ b/media/libjpeg/jdmerge.c @@ -3,7 +3,7 @@ * * Copyright (C) 1994-1996, Thomas G. Lane. * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2009, D. R. Commander. + * Copyright (C) 2009, 2011, D. R. Commander. * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * @@ -38,6 +38,7 @@ #include "jinclude.h" #include "jpeglib.h" #include "jsimd.h" +#include "config.h" #ifdef UPSAMPLE_MERGING_SUPPORTED @@ -77,6 +78,99 @@ typedef my_upsampler * my_upsample_ptr; #define FIX(x) ((INT32) ((x) * (1L<RGB colorspace conversion. * This is taken directly from jdcolor.c; see that file for more info. @@ -230,55 +324,39 @@ h2v1_merged_upsample (j_decompress_ptr cinfo, JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf) { - my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; - register int y, cred, cgreen, cblue; - int cb, cr; - register JSAMPROW outptr; - JSAMPROW inptr0, inptr1, inptr2; - JDIMENSION col; - /* copy these pointers into registers if possible */ - register JSAMPLE * range_limit = cinfo->sample_range_limit; - int * Crrtab = upsample->Cr_r_tab; - int * Cbbtab = upsample->Cb_b_tab; - INT32 * Crgtab = upsample->Cr_g_tab; - INT32 * Cbgtab = upsample->Cb_g_tab; - SHIFT_TEMPS - - inptr0 = input_buf[0][in_row_group_ctr]; - inptr1 = input_buf[1][in_row_group_ctr]; - inptr2 = input_buf[2][in_row_group_ctr]; - outptr = output_buf[0]; - /* Loop for each pair of output pixels */ - for (col = cinfo->output_width >> 1; col > 0; col--) { - /* Do the chroma part of the calculation */ - cb = GETJSAMPLE(*inptr1++); - cr = GETJSAMPLE(*inptr2++); - cred = Crrtab[cr]; - cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); - cblue = Cbbtab[cb]; - /* Fetch 2 Y values and emit 2 pixels */ - y = GETJSAMPLE(*inptr0++); - outptr[rgb_red[cinfo->out_color_space]] = range_limit[y + cred]; - outptr[rgb_green[cinfo->out_color_space]] = range_limit[y + cgreen]; - outptr[rgb_blue[cinfo->out_color_space]] = range_limit[y + cblue]; - outptr += rgb_pixelsize[cinfo->out_color_space]; - y = GETJSAMPLE(*inptr0++); - outptr[rgb_red[cinfo->out_color_space]] = range_limit[y + cred]; - outptr[rgb_green[cinfo->out_color_space]] = range_limit[y + cgreen]; - outptr[rgb_blue[cinfo->out_color_space]] = range_limit[y + cblue]; - outptr += rgb_pixelsize[cinfo->out_color_space]; - } - /* If image width is odd, do the last output column separately */ - if (cinfo->output_width & 1) { - cb = GETJSAMPLE(*inptr1); - cr = GETJSAMPLE(*inptr2); - cred = Crrtab[cr]; - cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); - cblue = Cbbtab[cb]; - y = GETJSAMPLE(*inptr0); - outptr[rgb_red[cinfo->out_color_space]] = range_limit[y + cred]; - outptr[rgb_green[cinfo->out_color_space]] = range_limit[y + cgreen]; - outptr[rgb_blue[cinfo->out_color_space]] = range_limit[y + cblue]; + switch (cinfo->out_color_space) { + case JCS_EXT_RGB: + extrgb_h2v1_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr, + output_buf); + break; + case JCS_EXT_RGBX: + case JCS_EXT_RGBA: + extrgbx_h2v1_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr, + output_buf); + break; + case JCS_EXT_BGR: + extbgr_h2v1_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr, + output_buf); + break; + case JCS_EXT_BGRX: + case JCS_EXT_BGRA: + extbgrx_h2v1_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr, + output_buf); + break; + case JCS_EXT_XBGR: + case JCS_EXT_ABGR: + extxbgr_h2v1_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr, + output_buf); + break; + case JCS_EXT_XRGB: + case JCS_EXT_ARGB: + extxrgb_h2v1_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr, + output_buf); + break; + default: + h2v1_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr, + output_buf); + break; } } @@ -292,71 +370,39 @@ h2v2_merged_upsample (j_decompress_ptr cinfo, JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf) { - my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; - register int y, cred, cgreen, cblue; - int cb, cr; - register JSAMPROW outptr0, outptr1; - JSAMPROW inptr00, inptr01, inptr1, inptr2; - JDIMENSION col; - /* copy these pointers into registers if possible */ - register JSAMPLE * range_limit = cinfo->sample_range_limit; - int * Crrtab = upsample->Cr_r_tab; - int * Cbbtab = upsample->Cb_b_tab; - INT32 * Crgtab = upsample->Cr_g_tab; - INT32 * Cbgtab = upsample->Cb_g_tab; - SHIFT_TEMPS - - inptr00 = input_buf[0][in_row_group_ctr*2]; - inptr01 = input_buf[0][in_row_group_ctr*2 + 1]; - inptr1 = input_buf[1][in_row_group_ctr]; - inptr2 = input_buf[2][in_row_group_ctr]; - outptr0 = output_buf[0]; - outptr1 = output_buf[1]; - /* Loop for each group of output pixels */ - for (col = cinfo->output_width >> 1; col > 0; col--) { - /* Do the chroma part of the calculation */ - cb = GETJSAMPLE(*inptr1++); - cr = GETJSAMPLE(*inptr2++); - cred = Crrtab[cr]; - cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); - cblue = Cbbtab[cb]; - /* Fetch 4 Y values and emit 4 pixels */ - y = GETJSAMPLE(*inptr00++); - outptr0[rgb_red[cinfo->out_color_space]] = range_limit[y + cred]; - outptr0[rgb_green[cinfo->out_color_space]] = range_limit[y + cgreen]; - outptr0[rgb_blue[cinfo->out_color_space]] = range_limit[y + cblue]; - outptr0 += RGB_PIXELSIZE; - y = GETJSAMPLE(*inptr00++); - outptr0[rgb_red[cinfo->out_color_space]] = range_limit[y + cred]; - outptr0[rgb_green[cinfo->out_color_space]] = range_limit[y + cgreen]; - outptr0[rgb_blue[cinfo->out_color_space]] = range_limit[y + cblue]; - outptr0 += RGB_PIXELSIZE; - y = GETJSAMPLE(*inptr01++); - outptr1[rgb_red[cinfo->out_color_space]] = range_limit[y + cred]; - outptr1[rgb_green[cinfo->out_color_space]] = range_limit[y + cgreen]; - outptr1[rgb_blue[cinfo->out_color_space]] = range_limit[y + cblue]; - outptr1 += RGB_PIXELSIZE; - y = GETJSAMPLE(*inptr01++); - outptr1[rgb_red[cinfo->out_color_space]] = range_limit[y + cred]; - outptr1[rgb_green[cinfo->out_color_space]] = range_limit[y + cgreen]; - outptr1[rgb_blue[cinfo->out_color_space]] = range_limit[y + cblue]; - outptr1 += RGB_PIXELSIZE; - } - /* If image width is odd, do the last output column separately */ - if (cinfo->output_width & 1) { - cb = GETJSAMPLE(*inptr1); - cr = GETJSAMPLE(*inptr2); - cred = Crrtab[cr]; - cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); - cblue = Cbbtab[cb]; - y = GETJSAMPLE(*inptr00); - outptr0[rgb_red[cinfo->out_color_space]] = range_limit[y + cred]; - outptr0[rgb_green[cinfo->out_color_space]] = range_limit[y + cgreen]; - outptr0[rgb_blue[cinfo->out_color_space]] = range_limit[y + cblue]; - y = GETJSAMPLE(*inptr01); - outptr1[rgb_red[cinfo->out_color_space]] = range_limit[y + cred]; - outptr1[rgb_green[cinfo->out_color_space]] = range_limit[y + cgreen]; - outptr1[rgb_blue[cinfo->out_color_space]] = range_limit[y + cblue]; + switch (cinfo->out_color_space) { + case JCS_EXT_RGB: + extrgb_h2v2_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr, + output_buf); + break; + case JCS_EXT_RGBX: + case JCS_EXT_RGBA: + extrgbx_h2v2_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr, + output_buf); + break; + case JCS_EXT_BGR: + extbgr_h2v2_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr, + output_buf); + break; + case JCS_EXT_BGRX: + case JCS_EXT_BGRA: + extbgrx_h2v2_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr, + output_buf); + break; + case JCS_EXT_XBGR: + case JCS_EXT_ABGR: + extxbgr_h2v2_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr, + output_buf); + break; + case JCS_EXT_XRGB: + case JCS_EXT_ARGB: + extxrgb_h2v2_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr, + output_buf); + break; + default: + h2v2_merged_upsample_internal(cinfo, input_buf, in_row_group_ctr, + output_buf); + break; } } diff --git a/media/libjpeg/jdmrgext.c b/media/libjpeg/jdmrgext.c new file mode 100644 index 00000000000..95ddd556230 --- /dev/null +++ b/media/libjpeg/jdmrgext.c @@ -0,0 +1,156 @@ +/* + * jdmrgext.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains code for merged upsampling/color conversion. + */ + + +/* This file is included by jdmerge.c */ + + +/* + * Upsample and color convert for the case of 2:1 horizontal and 1:1 vertical. + */ + +INLINE +LOCAL(void) +h2v1_merged_upsample_internal (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION in_row_group_ctr, + JSAMPARRAY output_buf) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + register int y, cred, cgreen, cblue; + int cb, cr; + register JSAMPROW outptr; + JSAMPROW inptr0, inptr1, inptr2; + JDIMENSION col; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + int * Crrtab = upsample->Cr_r_tab; + int * Cbbtab = upsample->Cb_b_tab; + INT32 * Crgtab = upsample->Cr_g_tab; + INT32 * Cbgtab = upsample->Cb_g_tab; + SHIFT_TEMPS + + inptr0 = input_buf[0][in_row_group_ctr]; + inptr1 = input_buf[1][in_row_group_ctr]; + inptr2 = input_buf[2][in_row_group_ctr]; + outptr = output_buf[0]; + /* Loop for each pair of output pixels */ + for (col = cinfo->output_width >> 1; col > 0; col--) { + /* Do the chroma part of the calculation */ + cb = GETJSAMPLE(*inptr1++); + cr = GETJSAMPLE(*inptr2++); + cred = Crrtab[cr]; + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + cblue = Cbbtab[cb]; + /* Fetch 2 Y values and emit 2 pixels */ + y = GETJSAMPLE(*inptr0++); + outptr[RGB_RED] = range_limit[y + cred]; + outptr[RGB_GREEN] = range_limit[y + cgreen]; + outptr[RGB_BLUE] = range_limit[y + cblue]; + outptr += RGB_PIXELSIZE; + y = GETJSAMPLE(*inptr0++); + outptr[RGB_RED] = range_limit[y + cred]; + outptr[RGB_GREEN] = range_limit[y + cgreen]; + outptr[RGB_BLUE] = range_limit[y + cblue]; + outptr += RGB_PIXELSIZE; + } + /* If image width is odd, do the last output column separately */ + if (cinfo->output_width & 1) { + cb = GETJSAMPLE(*inptr1); + cr = GETJSAMPLE(*inptr2); + cred = Crrtab[cr]; + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + cblue = Cbbtab[cb]; + y = GETJSAMPLE(*inptr0); + outptr[RGB_RED] = range_limit[y + cred]; + outptr[RGB_GREEN] = range_limit[y + cgreen]; + outptr[RGB_BLUE] = range_limit[y + cblue]; + } +} + + +/* + * Upsample and color convert for the case of 2:1 horizontal and 2:1 vertical. + */ + +INLINE +LOCAL(void) +h2v2_merged_upsample_internal (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION in_row_group_ctr, + JSAMPARRAY output_buf) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + register int y, cred, cgreen, cblue; + int cb, cr; + register JSAMPROW outptr0, outptr1; + JSAMPROW inptr00, inptr01, inptr1, inptr2; + JDIMENSION col; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + int * Crrtab = upsample->Cr_r_tab; + int * Cbbtab = upsample->Cb_b_tab; + INT32 * Crgtab = upsample->Cr_g_tab; + INT32 * Cbgtab = upsample->Cb_g_tab; + SHIFT_TEMPS + + inptr00 = input_buf[0][in_row_group_ctr*2]; + inptr01 = input_buf[0][in_row_group_ctr*2 + 1]; + inptr1 = input_buf[1][in_row_group_ctr]; + inptr2 = input_buf[2][in_row_group_ctr]; + outptr0 = output_buf[0]; + outptr1 = output_buf[1]; + /* Loop for each group of output pixels */ + for (col = cinfo->output_width >> 1; col > 0; col--) { + /* Do the chroma part of the calculation */ + cb = GETJSAMPLE(*inptr1++); + cr = GETJSAMPLE(*inptr2++); + cred = Crrtab[cr]; + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + cblue = Cbbtab[cb]; + /* Fetch 4 Y values and emit 4 pixels */ + y = GETJSAMPLE(*inptr00++); + outptr0[RGB_RED] = range_limit[y + cred]; + outptr0[RGB_GREEN] = range_limit[y + cgreen]; + outptr0[RGB_BLUE] = range_limit[y + cblue]; + outptr0 += RGB_PIXELSIZE; + y = GETJSAMPLE(*inptr00++); + outptr0[RGB_RED] = range_limit[y + cred]; + outptr0[RGB_GREEN] = range_limit[y + cgreen]; + outptr0[RGB_BLUE] = range_limit[y + cblue]; + outptr0 += RGB_PIXELSIZE; + y = GETJSAMPLE(*inptr01++); + outptr1[RGB_RED] = range_limit[y + cred]; + outptr1[RGB_GREEN] = range_limit[y + cgreen]; + outptr1[RGB_BLUE] = range_limit[y + cblue]; + outptr1 += RGB_PIXELSIZE; + y = GETJSAMPLE(*inptr01++); + outptr1[RGB_RED] = range_limit[y + cred]; + outptr1[RGB_GREEN] = range_limit[y + cgreen]; + outptr1[RGB_BLUE] = range_limit[y + cblue]; + outptr1 += RGB_PIXELSIZE; + } + /* If image width is odd, do the last output column separately */ + if (cinfo->output_width & 1) { + cb = GETJSAMPLE(*inptr1); + cr = GETJSAMPLE(*inptr2); + cred = Crrtab[cr]; + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + cblue = Cbbtab[cb]; + y = GETJSAMPLE(*inptr00); + outptr0[RGB_RED] = range_limit[y + cred]; + outptr0[RGB_GREEN] = range_limit[y + cgreen]; + outptr0[RGB_BLUE] = range_limit[y + cblue]; + y = GETJSAMPLE(*inptr01); + outptr1[RGB_RED] = range_limit[y + cred]; + outptr1[RGB_GREEN] = range_limit[y + cgreen]; + outptr1[RGB_BLUE] = range_limit[y + cblue]; + } +} diff --git a/media/libjpeg/jdtrans.c b/media/libjpeg/jdtrans.c index 9d9c1b1d5e0..f0cd0aef95f 100644 --- a/media/libjpeg/jdtrans.c +++ b/media/libjpeg/jdtrans.c @@ -99,6 +99,11 @@ transdecode_master_selection (j_decompress_ptr cinfo) /* This is effectively a buffered-image operation. */ cinfo->buffered_image = TRUE; +#if JPEG_LIB_VERSION >= 80 + /* Compute output image dimensions and related values. */ + jpeg_core_output_dimensions(cinfo); +#endif + /* Entropy decoding: either Huffman or arithmetic coding. */ if (cinfo->arith_code) { #ifdef D_ARITH_CODING_SUPPORTED diff --git a/media/libjpeg/jerror.h b/media/libjpeg/jerror.h index 88f019e8f94..275086e675e 100644 --- a/media/libjpeg/jerror.h +++ b/media/libjpeg/jerror.h @@ -40,15 +40,23 @@ typedef enum { JMESSAGE(JMSG_NOMESSAGE, "Bogus message code %d") /* Must be first entry! */ /* For maintenance convenience, list is alphabetical by message code name */ +#if JPEG_LIB_VERSION < 70 JMESSAGE(JERR_ARITH_NOTIMPL, "Sorry, arithmetic coding is not implemented") +#endif JMESSAGE(JERR_BAD_ALIGN_TYPE, "ALIGN_TYPE is wrong, please fix") JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix") JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode") JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") +#if JPEG_LIB_VERSION >= 70 JMESSAGE(JERR_BAD_CROP_SPEC, "Invalid crop request") +#endif JMESSAGE(JERR_BAD_DCT_COEF, "DCT coefficient out of range") JMESSAGE(JERR_BAD_DCTSIZE, "IDCT output block size %d not supported") +#if JPEG_LIB_VERSION >= 70 +JMESSAGE(JERR_BAD_DROP_SAMPLING, + "Component index %d: mismatching sampling ratio %d:%d, %d:%d, %c") +#endif JMESSAGE(JERR_BAD_HUFF_TABLE, "Bogus Huffman table definition") JMESSAGE(JERR_BAD_IN_COLORSPACE, "Bogus input colorspace") JMESSAGE(JERR_BAD_J_COLORSPACE, "Bogus JPEG colorspace") @@ -95,7 +103,9 @@ JMESSAGE(JERR_MISSING_DATA, "Scan script does not transmit all data") JMESSAGE(JERR_MODE_CHANGE, "Invalid color quantization mode change") JMESSAGE(JERR_NOTIMPL, "Not implemented yet") JMESSAGE(JERR_NOT_COMPILED, "Requested feature was omitted at compile time") +#if JPEG_LIB_VERSION >= 70 JMESSAGE(JERR_NO_ARITH_TABLE, "Arithmetic table 0x%02x was not defined") +#endif JMESSAGE(JERR_NO_BACKING_STORE, "Backing store not supported") JMESSAGE(JERR_NO_HUFF_TABLE, "Huffman table 0x%02x was not defined") JMESSAGE(JERR_NO_IMAGE, "JPEG datastream contains no image") @@ -173,7 +183,9 @@ JMESSAGE(JTRC_UNKNOWN_IDS, JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u") JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u") JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d") +#if JPEG_LIB_VERSION >= 70 JMESSAGE(JWRN_ARITH_BAD_CODE, "Corrupt JPEG data: bad arithmetic code") +#endif JMESSAGE(JWRN_BOGUS_PROGRESSION, "Inconsistent progression sequence for component %d coefficient %d") JMESSAGE(JWRN_EXTRANEOUS_DATA, @@ -186,6 +198,13 @@ JMESSAGE(JWRN_MUST_RESYNC, "Corrupt JPEG data: found marker 0x%02x instead of RST%d") JMESSAGE(JWRN_NOT_SEQUENTIAL, "Invalid SOS parameters for sequential JPEG") JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") +#if JPEG_LIB_VERSION < 70 +JMESSAGE(JERR_BAD_CROP_SPEC, "Invalid crop request") +#if defined(C_ARITH_CODING_SUPPORTED) || defined(D_ARITH_CODING_SUPPORTED) +JMESSAGE(JERR_NO_ARITH_TABLE, "Arithmetic table 0x%02x was not defined") +JMESSAGE(JWRN_ARITH_BAD_CODE, "Corrupt JPEG data: bad arithmetic code") +#endif +#endif #ifdef JMAKE_ENUM_LIST diff --git a/media/libjpeg/jmemmgr.c b/media/libjpeg/jmemmgr.c index 54589521a62..cf32524ea70 100644 --- a/media/libjpeg/jmemmgr.c +++ b/media/libjpeg/jmemmgr.c @@ -37,6 +37,15 @@ extern char * getenv JPP((const char * name)); #endif +LOCAL(size_t) +round_up_pow2 (size_t a, size_t b) +/* a rounded up to the next multiple of b, i.e. ceil(a/b)*b */ +/* Assumes a >= 0, b > 0, and b is a power of 2 */ +{ + return ((a + b - 1) & (~(b - 1))); +} + + /* * Some important notes: * The allocation routines provided here must never return NULL. @@ -265,7 +274,7 @@ alloc_small (j_common_ptr cinfo, int pool_id, size_t sizeofobject) * and so that algorithms can straddle outside the proper area up * to the next alignment. */ - sizeofobject = jround_up(sizeofobject, ALIGN_SIZE); + sizeofobject = round_up_pow2(sizeofobject, ALIGN_SIZE); /* Check for unsatisfiable request (do now to ensure no overflow below) */ if ((SIZEOF(small_pool_hdr) + sizeofobject + ALIGN_SIZE - 1) > MAX_ALLOC_CHUNK) @@ -354,7 +363,7 @@ alloc_large (j_common_ptr cinfo, int pool_id, size_t sizeofobject) * algorithms can straddle outside the proper area up to the next * alignment. */ - sizeofobject = jround_up(sizeofobject, ALIGN_SIZE); + sizeofobject = round_up_pow2(sizeofobject, ALIGN_SIZE); /* Check for unsatisfiable request (do now to ensure no overflow below) */ if ((SIZEOF(large_pool_hdr) + sizeofobject + ALIGN_SIZE - 1) > MAX_ALLOC_CHUNK) @@ -420,7 +429,7 @@ alloc_sarray (j_common_ptr cinfo, int pool_id, /* Make sure each row is properly aligned */ if ((ALIGN_SIZE % SIZEOF(JSAMPLE)) != 0) out_of_memory(cinfo, 5); /* safety check */ - samplesperrow = (JDIMENSION)jround_up(samplesperrow, (2 * ALIGN_SIZE) / SIZEOF(JSAMPLE)); + samplesperrow = (JDIMENSION)round_up_pow2(samplesperrow, (2 * ALIGN_SIZE) / SIZEOF(JSAMPLE)); /* Calculate max # of rows allowed in one allocation chunk */ ltemp = (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) / diff --git a/media/libjpeg/jmorecfg.h b/media/libjpeg/jmorecfg.h index c91399732dd..6c28c606d87 100644 --- a/media/libjpeg/jmorecfg.h +++ b/media/libjpeg/jmorecfg.h @@ -2,7 +2,7 @@ * jmorecfg.h * * Copyright (C) 1991-1997, Thomas G. Lane. - * Copyright (C) 2009, D. R. Commander. + * Copyright (C) 2009, 2011, D. R. Commander. * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * @@ -242,8 +242,6 @@ typedef int boolean; * (You may HAVE to do that if your compiler doesn't like null source files.) */ -/* Arithmetic coding is unsupported for legal reasons. Complaints to IBM. */ - /* Capability options common to encoder and decoder: */ #define DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */ @@ -252,7 +250,6 @@ typedef int boolean; /* Encoder capability options: */ -#undef C_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ #define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ #define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ #define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ @@ -268,7 +265,6 @@ typedef int boolean; /* Decoder capability options: */ -#undef D_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ #define D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ #define D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ #define SAVE_MARKERS_SUPPORTED /* jpeg_save_markers() needed? */ @@ -302,22 +298,60 @@ typedef int boolean; #define RGB_BLUE 2 /* Offset of Blue */ #define RGB_PIXELSIZE 3 /* JSAMPLEs per RGB scanline element */ -#define JPEG_NUMCS 12 +#define JPEG_NUMCS 16 + +#define EXT_RGB_RED 0 +#define EXT_RGB_GREEN 1 +#define EXT_RGB_BLUE 2 +#define EXT_RGB_PIXELSIZE 3 + +#define EXT_RGBX_RED 0 +#define EXT_RGBX_GREEN 1 +#define EXT_RGBX_BLUE 2 +#define EXT_RGBX_PIXELSIZE 4 + +#define EXT_BGR_RED 2 +#define EXT_BGR_GREEN 1 +#define EXT_BGR_BLUE 0 +#define EXT_BGR_PIXELSIZE 3 + +#define EXT_BGRX_RED 2 +#define EXT_BGRX_GREEN 1 +#define EXT_BGRX_BLUE 0 +#define EXT_BGRX_PIXELSIZE 4 + +#define EXT_XBGR_RED 3 +#define EXT_XBGR_GREEN 2 +#define EXT_XBGR_BLUE 1 +#define EXT_XBGR_PIXELSIZE 4 + +#define EXT_XRGB_RED 1 +#define EXT_XRGB_GREEN 2 +#define EXT_XRGB_BLUE 3 +#define EXT_XRGB_PIXELSIZE 4 static const int rgb_red[JPEG_NUMCS] = { - -1, -1, RGB_RED, -1, -1, -1, 0, 0, 2, 2, 3, 1 + -1, -1, RGB_RED, -1, -1, -1, EXT_RGB_RED, EXT_RGBX_RED, + EXT_BGR_RED, EXT_BGRX_RED, EXT_XBGR_RED, EXT_XRGB_RED, + EXT_RGBX_RED, EXT_BGRX_RED, EXT_XBGR_RED, EXT_XRGB_RED }; static const int rgb_green[JPEG_NUMCS] = { - -1, -1, RGB_GREEN, -1, -1, -1, 1, 1, 1, 1, 2, 2 + -1, -1, RGB_GREEN, -1, -1, -1, EXT_RGB_GREEN, EXT_RGBX_GREEN, + EXT_BGR_GREEN, EXT_BGRX_GREEN, EXT_XBGR_GREEN, EXT_XRGB_GREEN, + EXT_RGBX_GREEN, EXT_BGRX_GREEN, EXT_XBGR_GREEN, EXT_XRGB_GREEN }; static const int rgb_blue[JPEG_NUMCS] = { - -1, -1, RGB_BLUE, -1, -1, -1, 2, 2, 0, 0, 1, 3 + -1, -1, RGB_BLUE, -1, -1, -1, EXT_RGB_BLUE, EXT_RGBX_BLUE, + EXT_BGR_BLUE, EXT_BGRX_BLUE, EXT_XBGR_BLUE, EXT_XRGB_BLUE, + EXT_RGBX_BLUE, EXT_BGRX_BLUE, EXT_XBGR_BLUE, EXT_XRGB_BLUE }; static const int rgb_pixelsize[JPEG_NUMCS] = { - -1, -1, RGB_PIXELSIZE, -1, -1, -1, 3, 4, 3, 4, 4, 4 + -1, -1, RGB_PIXELSIZE, -1, -1, -1, EXT_RGB_PIXELSIZE, EXT_RGBX_PIXELSIZE, + EXT_BGR_PIXELSIZE, EXT_BGRX_PIXELSIZE, EXT_XBGR_PIXELSIZE, EXT_XRGB_PIXELSIZE, + EXT_RGBX_PIXELSIZE, EXT_BGRX_PIXELSIZE, EXT_XBGR_PIXELSIZE, EXT_XRGB_PIXELSIZE }; /* Definitions for speed-related optimizations. */ diff --git a/media/libjpeg/jpegint.h b/media/libjpeg/jpegint.h index 3ba7be0827f..78717482ebf 100644 --- a/media/libjpeg/jpegint.h +++ b/media/libjpeg/jpegint.h @@ -375,7 +375,7 @@ EXTERN(void) jinit_memory_mgr JPP((j_common_ptr cinfo)); /* Utility routines in jutils.c */ EXTERN(long) jdiv_round_up JPP((long a, long b)); -EXTERN(size_t) jround_up JPP((size_t a, size_t b)); +EXTERN(long) jround_up JPP((long a, long b)); EXTERN(void) jcopy_sample_rows JPP((JSAMPARRAY input_array, int source_row, JSAMPARRAY output_array, int dest_row, int num_rows, JDIMENSION num_cols)); diff --git a/media/libjpeg/jpeglib.h b/media/libjpeg/jpeglib.h index cb3acaf910a..d19a3ef2e58 100644 --- a/media/libjpeg/jpeglib.h +++ b/media/libjpeg/jpeglib.h @@ -3,7 +3,7 @@ * * Copyright (C) 1991-1998, Thomas G. Lane. * Modified 2002-2009 by Guido Vollbeding. - * Copyright (C) 2009-2010, D. R. Commander. + * Copyright (C) 2009-2011, D. R. Commander. * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * @@ -211,12 +211,13 @@ struct jpeg_marker_struct { /* Known color spaces. */ #define JCS_EXTENSIONS 1 +#define JCS_ALPHA_EXTENSIONS 1 typedef enum { JCS_UNKNOWN, /* error/unspecified */ JCS_GRAYSCALE, /* monochrome */ JCS_RGB, /* red/green/blue as specified by the RGB_RED, RGB_GREEN, - RGB_BLUE, and RGB_PIXELSIZE macros */ + RGB_BLUE, and RGB_PIXELSIZE macros */ JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */ JCS_CMYK, /* C/M/Y/K */ JCS_YCCK, /* Y/Cb/Cr/K */ @@ -225,7 +226,18 @@ typedef enum { JCS_EXT_BGR, /* blue/green/red */ JCS_EXT_BGRX, /* blue/green/red/x */ JCS_EXT_XBGR, /* x/blue/green/red */ - JCS_EXT_XRGB /* x/red/green/blue */ + JCS_EXT_XRGB, /* x/red/green/blue */ + /* When out_color_space it set to JCS_EXT_RGBX, JCS_EXT_BGRX, + JCS_EXT_XBGR, or JCS_EXT_XRGB during decompression, the X byte is + undefined, and in order to ensure the best performance, + libjpeg-turbo can set that byte to whatever value it wishes. Use + the following colorspace constants to ensure that the X byte is set + to 0xFF, so that it can be interpreted as an opaque alpha + channel. */ + JCS_EXT_RGBA, /* red/green/blue/alpha */ + JCS_EXT_BGRA, /* blue/green/red/alpha */ + JCS_EXT_ABGR, /* alpha/blue/green/red */ + JCS_EXT_ARGB /* alpha/red/green/blue */ } J_COLOR_SPACE; /* DCT/IDCT algorithm options. */ diff --git a/media/libjpeg/jsimd.h b/media/libjpeg/jsimd.h index b6637915372..3fa2c43271c 100644 --- a/media/libjpeg/jsimd.h +++ b/media/libjpeg/jsimd.h @@ -2,6 +2,7 @@ * jsimd.h * * Copyright 2009 Pierre Ossman for Cendio AB + * Copyright 2011 D. R. Commander * * Based on the x86 SIMD extension for IJG JPEG library, * Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -13,8 +14,10 @@ #ifdef NEED_SHORT_EXTERNAL_NAMES #define jsimd_can_rgb_ycc jSCanRgbYcc +#define jsimd_can_rgb_gray jSCanRgbGry #define jsimd_can_ycc_rgb jSCanYccRgb #define jsimd_rgb_ycc_convert jSRgbYccConv +#define jsimd_rgb_gray_convert jSRgbGryConv #define jsimd_ycc_rgb_convert jSYccRgbConv #define jsimd_can_h2v2_downsample jSCanH2V2Down #define jsimd_can_h2v1_downsample jSCanH2V1Down @@ -35,12 +38,17 @@ #endif /* NEED_SHORT_EXTERNAL_NAMES */ EXTERN(int) jsimd_can_rgb_ycc JPP((void)); +EXTERN(int) jsimd_can_rgb_gray JPP((void)); EXTERN(int) jsimd_can_ycc_rgb JPP((void)); EXTERN(void) jsimd_rgb_ycc_convert JPP((j_compress_ptr cinfo, JSAMPARRAY input_buf, JSAMPIMAGE output_buf, JDIMENSION output_row, int num_rows)); +EXTERN(void) jsimd_rgb_gray_convert + JPP((j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); EXTERN(void) jsimd_ycc_rgb_convert JPP((j_decompress_ptr cinfo, JSAMPIMAGE input_buf, JDIMENSION input_row, diff --git a/media/libjpeg/jsimd_none.c b/media/libjpeg/jsimd_none.c index 7ff30742cc9..9787902a8bd 100644 --- a/media/libjpeg/jsimd_none.c +++ b/media/libjpeg/jsimd_none.c @@ -2,7 +2,7 @@ * jsimd_none.c * * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright 2009 D. R. Commander + * Copyright 2009-2011 D. R. Commander * * Based on the x86 SIMD extension for IJG JPEG library, * Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -24,6 +24,12 @@ jsimd_can_rgb_ycc (void) return 0; } +GLOBAL(int) +jsimd_can_rgb_gray (void) +{ + return 0; +} + GLOBAL(int) jsimd_can_ycc_rgb (void) { @@ -37,6 +43,13 @@ jsimd_rgb_ycc_convert (j_compress_ptr cinfo, { } +GLOBAL(void) +jsimd_rgb_gray_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ +} + GLOBAL(void) jsimd_ycc_rgb_convert (j_decompress_ptr cinfo, JSAMPIMAGE input_buf, JDIMENSION input_row, diff --git a/media/libjpeg/jutils.c b/media/libjpeg/jutils.c index 98b54f5f107..d18a9555621 100644 --- a/media/libjpeg/jutils.c +++ b/media/libjpeg/jutils.c @@ -77,8 +77,8 @@ jdiv_round_up (long a, long b) } -GLOBAL(size_t) -jround_up (size_t a, size_t b) +GLOBAL(long) +jround_up (long a, long b) /* Compute a rounded up to next multiple of b, ie, ceil(a/b)*b */ /* Assumes a >= 0, b > 0 */ { diff --git a/media/libjpeg/jversion.h b/media/libjpeg/jversion.h index 119c481c3f2..71d7b91319c 100644 --- a/media/libjpeg/jversion.h +++ b/media/libjpeg/jversion.h @@ -2,7 +2,7 @@ * jversion.h * * Copyright (C) 1991-2010, Thomas G. Lane, Guido Vollbeding. - * Copyright (C) 2010, D. R. Commander. + * Copyright (C) 2010, 2012, D. R. Commander. * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * @@ -14,24 +14,18 @@ #define JVERSION "8b 16-May-2010" -#define JCOPYRIGHT "Copyright (C) 2010, Thomas G. Lane, Guido Vollbeding" - #elif JPEG_LIB_VERSION >= 70 #define JVERSION "7 27-Jun-2009" -#define JCOPYRIGHT "Copyright (C) 2009, Thomas G. Lane, Guido Vollbeding" - #else #define JVERSION "6b 27-Mar-1998" -#define JCOPYRIGHT "Copyright (C) 1998, Thomas G. Lane" - #endif -#define LJTCOPYRIGHT "Copyright (C) 1999-2006 MIYASAKA Masaru\n" \ - "Copyright (C) 2004 Landmark Graphics Corporation\n" \ - "Copyright (C) 2005-2007 Sun Microsystems, Inc.\n" \ +#define JCOPYRIGHT "Copyright (C) 1991-2010 Thomas G. Lane, Guido Vollbeding\n" \ + "Copyright (C) 1999-2006 MIYASAKA Masaru\n" \ "Copyright (C) 2009 Pierre Ossman for Cendio AB\n" \ - "Copyright (C) 2009-2011 D. R. Commander" + "Copyright (C) 2009-2012 D. R. Commander\n" \ + "Copyright (C) 2009-2011 Nokia Corporation and/or its subsidiary(-ies)" diff --git a/media/libjpeg/mozilla.diff b/media/libjpeg/mozilla.diff new file mode 100644 index 00000000000..83b340f357b --- /dev/null +++ b/media/libjpeg/mozilla.diff @@ -0,0 +1,70 @@ +--- jmorecfg.h 2012-01-27 00:46:32 -0500 ++++ jmorecfg.h 2012-02-10 23:08:03 -0500 +@@ -6,16 +6,17 @@ + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains additional configuration options that customize the + * JPEG software for special applications or support machine-dependent + * optimizations. Most users will not need to touch this file. + */ + ++#include "prtypes.h" + + /* + * Define BITS_IN_JSAMPLE as either + * 8 for 8-bit sample values (the usual setting) + * 12 for 12-bit sample values + * Only 8 and 12 are legal data precisions for lossy JPEG according to the + * JPEG standard, and the IJG code does not support anything else! + * We do not support run-time selection of data precision, sorry. +@@ -127,45 +128,29 @@ typedef char JOCTET; + * They must be at least as wide as specified; but making them too big + * won't cost a huge amount of memory, so we don't provide special + * extraction code like we did for JSAMPLE. (In other words, these + * typedefs live at a different point on the speed/space tradeoff curve.) + */ + + /* UINT8 must hold at least the values 0..255. */ + +-#ifdef HAVE_UNSIGNED_CHAR +-typedef unsigned char UINT8; +-#else /* not HAVE_UNSIGNED_CHAR */ +-#ifdef __CHAR_UNSIGNED__ +-typedef char UINT8; +-#else /* not __CHAR_UNSIGNED__ */ +-typedef short UINT8; +-#endif /* __CHAR_UNSIGNED__ */ +-#endif /* HAVE_UNSIGNED_CHAR */ ++typedef PRUint8 UINT8; + + /* UINT16 must hold at least the values 0..65535. */ + +-#ifdef HAVE_UNSIGNED_SHORT +-typedef unsigned short UINT16; +-#else /* not HAVE_UNSIGNED_SHORT */ +-typedef unsigned int UINT16; +-#endif /* HAVE_UNSIGNED_SHORT */ ++typedef PRUint16 UINT16; + + /* INT16 must hold at least the values -32768..32767. */ + +-#ifndef XMD_H /* X11/xmd.h correctly defines INT16 */ +-typedef short INT16; +-#endif ++typedef PRInt16 INT16; + + /* INT32 must hold at least signed 32-bit values. */ + +-#ifndef XMD_H /* X11/xmd.h correctly defines INT32 */ +-typedef long INT32; +-#endif ++typedef PRInt32 INT32; + + /* Datatype used for image dimensions. The JPEG standard only supports + * images up to 64K*64K due to 16-bit fields in SOF markers. Therefore + * "unsigned int" is sufficient on all machines. However, if you need to + * handle larger images and you don't mind deviating from the spec, you + * can change this datatype. + */ + diff --git a/media/libjpeg/simd/jcclrmmx.asm b/media/libjpeg/simd/jcclrmmx.asm index b6b89121b5f..e09525310e8 100644 --- a/media/libjpeg/simd/jcclrmmx.asm +++ b/media/libjpeg/simd/jcclrmmx.asm @@ -19,8 +19,6 @@ %include "jcolsamp.inc" ; -------------------------------------------------------------------------- - SECTION SEG_TEXT - BITS 32 ; ; Convert some rows of samples to the output colorspace. ; diff --git a/media/libjpeg/simd/jcclrss2-64.asm b/media/libjpeg/simd/jcclrss2-64.asm index 8ca47aa06cf..f5d6bed955c 100644 --- a/media/libjpeg/simd/jcclrss2-64.asm +++ b/media/libjpeg/simd/jcclrss2-64.asm @@ -17,8 +17,6 @@ %include "jcolsamp.inc" ; -------------------------------------------------------------------------- - SECTION SEG_TEXT - BITS 64 ; ; Convert some rows of samples to the output colorspace. ; diff --git a/media/libjpeg/simd/jcclrss2.asm b/media/libjpeg/simd/jcclrss2.asm index 8def718cb17..517b7056388 100644 --- a/media/libjpeg/simd/jcclrss2.asm +++ b/media/libjpeg/simd/jcclrss2.asm @@ -16,8 +16,6 @@ %include "jcolsamp.inc" ; -------------------------------------------------------------------------- - SECTION SEG_TEXT - BITS 32 ; ; Convert some rows of samples to the output colorspace. ; diff --git a/media/libjpeg/simd/jccolmmx.asm b/media/libjpeg/simd/jccolmmx.asm index 5e7f3be994e..9650e47d475 100644 --- a/media/libjpeg/simd/jccolmmx.asm +++ b/media/libjpeg/simd/jccolmmx.asm @@ -51,16 +51,19 @@ PD_ONEHALF times 2 dd (1 << (SCALEBITS-1)) alignz 16 ; -------------------------------------------------------------------------- + SECTION SEG_TEXT + BITS 32 + %include "jcclrmmx.asm" %undef RGB_RED %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 0 -%define RGB_GREEN 1 -%define RGB_BLUE 2 -%define RGB_PIXELSIZE 3 +%define RGB_RED EXT_RGB_RED +%define RGB_GREEN EXT_RGB_GREEN +%define RGB_BLUE EXT_RGB_BLUE +%define RGB_PIXELSIZE EXT_RGB_PIXELSIZE %define jsimd_rgb_ycc_convert_mmx jsimd_extrgb_ycc_convert_mmx %include "jcclrmmx.asm" @@ -68,10 +71,10 @@ PD_ONEHALF times 2 dd (1 << (SCALEBITS-1)) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 0 -%define RGB_GREEN 1 -%define RGB_BLUE 2 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_RGBX_RED +%define RGB_GREEN EXT_RGBX_GREEN +%define RGB_BLUE EXT_RGBX_BLUE +%define RGB_PIXELSIZE EXT_RGBX_PIXELSIZE %define jsimd_rgb_ycc_convert_mmx jsimd_extrgbx_ycc_convert_mmx %include "jcclrmmx.asm" @@ -79,10 +82,10 @@ PD_ONEHALF times 2 dd (1 << (SCALEBITS-1)) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 2 -%define RGB_GREEN 1 -%define RGB_BLUE 0 -%define RGB_PIXELSIZE 3 +%define RGB_RED EXT_BGR_RED +%define RGB_GREEN EXT_BGR_GREEN +%define RGB_BLUE EXT_BGR_BLUE +%define RGB_PIXELSIZE EXT_BGR_PIXELSIZE %define jsimd_rgb_ycc_convert_mmx jsimd_extbgr_ycc_convert_mmx %include "jcclrmmx.asm" @@ -90,10 +93,10 @@ PD_ONEHALF times 2 dd (1 << (SCALEBITS-1)) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 2 -%define RGB_GREEN 1 -%define RGB_BLUE 0 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_BGRX_RED +%define RGB_GREEN EXT_BGRX_GREEN +%define RGB_BLUE EXT_BGRX_BLUE +%define RGB_PIXELSIZE EXT_BGRX_PIXELSIZE %define jsimd_rgb_ycc_convert_mmx jsimd_extbgrx_ycc_convert_mmx %include "jcclrmmx.asm" @@ -101,10 +104,10 @@ PD_ONEHALF times 2 dd (1 << (SCALEBITS-1)) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 3 -%define RGB_GREEN 2 -%define RGB_BLUE 1 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_XBGR_RED +%define RGB_GREEN EXT_XBGR_GREEN +%define RGB_BLUE EXT_XBGR_BLUE +%define RGB_PIXELSIZE EXT_XBGR_PIXELSIZE %define jsimd_rgb_ycc_convert_mmx jsimd_extxbgr_ycc_convert_mmx %include "jcclrmmx.asm" @@ -112,9 +115,9 @@ PD_ONEHALF times 2 dd (1 << (SCALEBITS-1)) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 1 -%define RGB_GREEN 2 -%define RGB_BLUE 3 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_XRGB_RED +%define RGB_GREEN EXT_XRGB_GREEN +%define RGB_BLUE EXT_XRGB_BLUE +%define RGB_PIXELSIZE EXT_XRGB_PIXELSIZE %define jsimd_rgb_ycc_convert_mmx jsimd_extxrgb_ycc_convert_mmx %include "jcclrmmx.asm" diff --git a/media/libjpeg/simd/jccolss2-64.asm b/media/libjpeg/simd/jccolss2-64.asm index 64ee0ba85d5..ae6014885ed 100644 --- a/media/libjpeg/simd/jccolss2-64.asm +++ b/media/libjpeg/simd/jccolss2-64.asm @@ -48,16 +48,19 @@ PD_ONEHALF times 4 dd (1 << (SCALEBITS-1)) alignz 16 ; -------------------------------------------------------------------------- + SECTION SEG_TEXT + BITS 64 + %include "jcclrss2-64.asm" %undef RGB_RED %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 0 -%define RGB_GREEN 1 -%define RGB_BLUE 2 -%define RGB_PIXELSIZE 3 +%define RGB_RED EXT_RGB_RED +%define RGB_GREEN EXT_RGB_GREEN +%define RGB_BLUE EXT_RGB_BLUE +%define RGB_PIXELSIZE EXT_RGB_PIXELSIZE %define jsimd_rgb_ycc_convert_sse2 jsimd_extrgb_ycc_convert_sse2 %include "jcclrss2-64.asm" @@ -65,10 +68,10 @@ PD_ONEHALF times 4 dd (1 << (SCALEBITS-1)) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 0 -%define RGB_GREEN 1 -%define RGB_BLUE 2 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_RGBX_RED +%define RGB_GREEN EXT_RGBX_GREEN +%define RGB_BLUE EXT_RGBX_BLUE +%define RGB_PIXELSIZE EXT_RGBX_PIXELSIZE %define jsimd_rgb_ycc_convert_sse2 jsimd_extrgbx_ycc_convert_sse2 %include "jcclrss2-64.asm" @@ -76,10 +79,10 @@ PD_ONEHALF times 4 dd (1 << (SCALEBITS-1)) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 2 -%define RGB_GREEN 1 -%define RGB_BLUE 0 -%define RGB_PIXELSIZE 3 +%define RGB_RED EXT_BGR_RED +%define RGB_GREEN EXT_BGR_GREEN +%define RGB_BLUE EXT_BGR_BLUE +%define RGB_PIXELSIZE EXT_BGR_PIXELSIZE %define jsimd_rgb_ycc_convert_sse2 jsimd_extbgr_ycc_convert_sse2 %include "jcclrss2-64.asm" @@ -87,10 +90,10 @@ PD_ONEHALF times 4 dd (1 << (SCALEBITS-1)) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 2 -%define RGB_GREEN 1 -%define RGB_BLUE 0 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_BGRX_RED +%define RGB_GREEN EXT_BGRX_GREEN +%define RGB_BLUE EXT_BGRX_BLUE +%define RGB_PIXELSIZE EXT_BGRX_PIXELSIZE %define jsimd_rgb_ycc_convert_sse2 jsimd_extbgrx_ycc_convert_sse2 %include "jcclrss2-64.asm" @@ -98,10 +101,10 @@ PD_ONEHALF times 4 dd (1 << (SCALEBITS-1)) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 3 -%define RGB_GREEN 2 -%define RGB_BLUE 1 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_XBGR_RED +%define RGB_GREEN EXT_XBGR_GREEN +%define RGB_BLUE EXT_XBGR_BLUE +%define RGB_PIXELSIZE EXT_XBGR_PIXELSIZE %define jsimd_rgb_ycc_convert_sse2 jsimd_extxbgr_ycc_convert_sse2 %include "jcclrss2-64.asm" @@ -109,9 +112,9 @@ PD_ONEHALF times 4 dd (1 << (SCALEBITS-1)) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 1 -%define RGB_GREEN 2 -%define RGB_BLUE 3 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_XRGB_RED +%define RGB_GREEN EXT_XRGB_GREEN +%define RGB_BLUE EXT_XRGB_BLUE +%define RGB_PIXELSIZE EXT_XRGB_PIXELSIZE %define jsimd_rgb_ycc_convert_sse2 jsimd_extxrgb_ycc_convert_sse2 %include "jcclrss2-64.asm" diff --git a/media/libjpeg/simd/jccolss2.asm b/media/libjpeg/simd/jccolss2.asm index 8d1f73406e8..ac001d18618 100644 --- a/media/libjpeg/simd/jccolss2.asm +++ b/media/libjpeg/simd/jccolss2.asm @@ -48,16 +48,19 @@ PD_ONEHALF times 4 dd (1 << (SCALEBITS-1)) alignz 16 ; -------------------------------------------------------------------------- + SECTION SEG_TEXT + BITS 32 + %include "jcclrss2.asm" %undef RGB_RED %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 0 -%define RGB_GREEN 1 -%define RGB_BLUE 2 -%define RGB_PIXELSIZE 3 +%define RGB_RED EXT_RGB_RED +%define RGB_GREEN EXT_RGB_GREEN +%define RGB_BLUE EXT_RGB_BLUE +%define RGB_PIXELSIZE EXT_RGB_PIXELSIZE %define jsimd_rgb_ycc_convert_sse2 jsimd_extrgb_ycc_convert_sse2 %include "jcclrss2.asm" @@ -65,10 +68,10 @@ PD_ONEHALF times 4 dd (1 << (SCALEBITS-1)) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 0 -%define RGB_GREEN 1 -%define RGB_BLUE 2 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_RGBX_RED +%define RGB_GREEN EXT_RGBX_GREEN +%define RGB_BLUE EXT_RGBX_BLUE +%define RGB_PIXELSIZE EXT_RGBX_PIXELSIZE %define jsimd_rgb_ycc_convert_sse2 jsimd_extrgbx_ycc_convert_sse2 %include "jcclrss2.asm" @@ -76,10 +79,10 @@ PD_ONEHALF times 4 dd (1 << (SCALEBITS-1)) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 2 -%define RGB_GREEN 1 -%define RGB_BLUE 0 -%define RGB_PIXELSIZE 3 +%define RGB_RED EXT_BGR_RED +%define RGB_GREEN EXT_BGR_GREEN +%define RGB_BLUE EXT_BGR_BLUE +%define RGB_PIXELSIZE EXT_BGR_PIXELSIZE %define jsimd_rgb_ycc_convert_sse2 jsimd_extbgr_ycc_convert_sse2 %include "jcclrss2.asm" @@ -87,10 +90,10 @@ PD_ONEHALF times 4 dd (1 << (SCALEBITS-1)) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 2 -%define RGB_GREEN 1 -%define RGB_BLUE 0 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_BGRX_RED +%define RGB_GREEN EXT_BGRX_GREEN +%define RGB_BLUE EXT_BGRX_BLUE +%define RGB_PIXELSIZE EXT_BGRX_PIXELSIZE %define jsimd_rgb_ycc_convert_sse2 jsimd_extbgrx_ycc_convert_sse2 %include "jcclrss2.asm" @@ -98,10 +101,10 @@ PD_ONEHALF times 4 dd (1 << (SCALEBITS-1)) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 3 -%define RGB_GREEN 2 -%define RGB_BLUE 1 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_XBGR_RED +%define RGB_GREEN EXT_XBGR_GREEN +%define RGB_BLUE EXT_XBGR_BLUE +%define RGB_PIXELSIZE EXT_XBGR_PIXELSIZE %define jsimd_rgb_ycc_convert_sse2 jsimd_extxbgr_ycc_convert_sse2 %include "jcclrss2.asm" @@ -109,9 +112,9 @@ PD_ONEHALF times 4 dd (1 << (SCALEBITS-1)) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 1 -%define RGB_GREEN 2 -%define RGB_BLUE 3 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_XRGB_RED +%define RGB_GREEN EXT_XRGB_GREEN +%define RGB_BLUE EXT_XRGB_BLUE +%define RGB_PIXELSIZE EXT_XRGB_PIXELSIZE %define jsimd_rgb_ycc_convert_sse2 jsimd_extxrgb_ycc_convert_sse2 %include "jcclrss2.asm" diff --git a/media/libjpeg/simd/jcgrammx.asm b/media/libjpeg/simd/jcgrammx.asm new file mode 100644 index 00000000000..b8b8dd3ad54 --- /dev/null +++ b/media/libjpeg/simd/jcgrammx.asm @@ -0,0 +1,116 @@ +; +; jcgrammx.asm - grayscale colorspace conversion (MMX) +; +; Copyright 2009 Pierre Ossman for Cendio AB +; Copyright 2011 D. R. Commander +; +; Based on +; x86 SIMD extension for IJG JPEG library +; Copyright (C) 1999-2006, MIYASAKA Masaru. +; For conditions of distribution and use, see copyright notice in jsimdext.inc +; +; This file should be assembled with NASM (Netwide Assembler), +; can *not* be assembled with Microsoft's MASM or any compatible +; assembler (including Borland's Turbo Assembler). +; NASM is available from http://nasm.sourceforge.net/ or +; http://sourceforge.net/project/showfiles.php?group_id=6208 +; +; [TAB8] + +%include "jsimdext.inc" + +; -------------------------------------------------------------------------- + +%define SCALEBITS 16 + +F_0_114 equ 7471 ; FIX(0.11400) +F_0_250 equ 16384 ; FIX(0.25000) +F_0_299 equ 19595 ; FIX(0.29900) +F_0_587 equ 38470 ; FIX(0.58700) +F_0_337 equ (F_0_587 - F_0_250) ; FIX(0.58700) - FIX(0.25000) + +; -------------------------------------------------------------------------- + SECTION SEG_CONST + + alignz 16 + global EXTN(jconst_rgb_gray_convert_mmx) + +EXTN(jconst_rgb_gray_convert_mmx): + +PW_F0299_F0337 times 2 dw F_0_299, F_0_337 +PW_F0114_F0250 times 2 dw F_0_114, F_0_250 +PD_ONEHALF times 2 dd (1 << (SCALEBITS-1)) + + alignz 16 + +; -------------------------------------------------------------------------- + SECTION SEG_TEXT + BITS 32 + +%include "jcgrymmx.asm" + +%undef RGB_RED +%undef RGB_GREEN +%undef RGB_BLUE +%undef RGB_PIXELSIZE +%define RGB_RED EXT_RGB_RED +%define RGB_GREEN EXT_RGB_GREEN +%define RGB_BLUE EXT_RGB_BLUE +%define RGB_PIXELSIZE EXT_RGB_PIXELSIZE +%define jsimd_rgb_gray_convert_mmx jsimd_extrgb_gray_convert_mmx +%include "jcgrymmx.asm" + +%undef RGB_RED +%undef RGB_GREEN +%undef RGB_BLUE +%undef RGB_PIXELSIZE +%define RGB_RED EXT_RGBX_RED +%define RGB_GREEN EXT_RGBX_GREEN +%define RGB_BLUE EXT_RGBX_BLUE +%define RGB_PIXELSIZE EXT_RGBX_PIXELSIZE +%define jsimd_rgb_gray_convert_mmx jsimd_extrgbx_gray_convert_mmx +%include "jcgrymmx.asm" + +%undef RGB_RED +%undef RGB_GREEN +%undef RGB_BLUE +%undef RGB_PIXELSIZE +%define RGB_RED EXT_BGR_RED +%define RGB_GREEN EXT_BGR_GREEN +%define RGB_BLUE EXT_BGR_BLUE +%define RGB_PIXELSIZE EXT_BGR_PIXELSIZE +%define jsimd_rgb_gray_convert_mmx jsimd_extbgr_gray_convert_mmx +%include "jcgrymmx.asm" + +%undef RGB_RED +%undef RGB_GREEN +%undef RGB_BLUE +%undef RGB_PIXELSIZE +%define RGB_RED EXT_BGRX_RED +%define RGB_GREEN EXT_BGRX_GREEN +%define RGB_BLUE EXT_BGRX_BLUE +%define RGB_PIXELSIZE EXT_BGRX_PIXELSIZE +%define jsimd_rgb_gray_convert_mmx jsimd_extbgrx_gray_convert_mmx +%include "jcgrymmx.asm" + +%undef RGB_RED +%undef RGB_GREEN +%undef RGB_BLUE +%undef RGB_PIXELSIZE +%define RGB_RED EXT_XBGR_RED +%define RGB_GREEN EXT_XBGR_GREEN +%define RGB_BLUE EXT_XBGR_BLUE +%define RGB_PIXELSIZE EXT_XBGR_PIXELSIZE +%define jsimd_rgb_gray_convert_mmx jsimd_extxbgr_gray_convert_mmx +%include "jcgrymmx.asm" + +%undef RGB_RED +%undef RGB_GREEN +%undef RGB_BLUE +%undef RGB_PIXELSIZE +%define RGB_RED EXT_XRGB_RED +%define RGB_GREEN EXT_XRGB_GREEN +%define RGB_BLUE EXT_XRGB_BLUE +%define RGB_PIXELSIZE EXT_XRGB_PIXELSIZE +%define jsimd_rgb_gray_convert_mmx jsimd_extxrgb_gray_convert_mmx +%include "jcgrymmx.asm" diff --git a/media/libjpeg/simd/jcgrass2-64.asm b/media/libjpeg/simd/jcgrass2-64.asm new file mode 100644 index 00000000000..ba28cc31dbd --- /dev/null +++ b/media/libjpeg/simd/jcgrass2-64.asm @@ -0,0 +1,113 @@ +; +; jcgrass2-64.asm - grayscale colorspace conversion (64-bit SSE2) +; +; x86 SIMD extension for IJG JPEG library +; Copyright (C) 1999-2006, MIYASAKA Masaru. +; Copyright (C) 2011, D. R. Commander. +; For conditions of distribution and use, see copyright notice in jsimdext.inc +; +; This file should be assembled with NASM (Netwide Assembler), +; can *not* be assembled with Microsoft's MASM or any compatible +; assembler (including Borland's Turbo Assembler). +; NASM is available from http://nasm.sourceforge.net/ or +; http://sourceforge.net/project/showfiles.php?group_id=6208 +; +; [TAB8] + +%include "jsimdext.inc" + +; -------------------------------------------------------------------------- + +%define SCALEBITS 16 + +F_0_114 equ 7471 ; FIX(0.11400) +F_0_250 equ 16384 ; FIX(0.25000) +F_0_299 equ 19595 ; FIX(0.29900) +F_0_587 equ 38470 ; FIX(0.58700) +F_0_337 equ (F_0_587 - F_0_250) ; FIX(0.58700) - FIX(0.25000) + +; -------------------------------------------------------------------------- + SECTION SEG_CONST + + alignz 16 + global EXTN(jconst_rgb_gray_convert_sse2) + +EXTN(jconst_rgb_gray_convert_sse2): + +PW_F0299_F0337 times 4 dw F_0_299, F_0_337 +PW_F0114_F0250 times 4 dw F_0_114, F_0_250 +PD_ONEHALF times 4 dd (1 << (SCALEBITS-1)) + + alignz 16 + +; -------------------------------------------------------------------------- + SECTION SEG_TEXT + BITS 64 + +%include "jcgryss2-64.asm" + +%undef RGB_RED +%undef RGB_GREEN +%undef RGB_BLUE +%undef RGB_PIXELSIZE +%define RGB_RED EXT_RGB_RED +%define RGB_GREEN EXT_RGB_GREEN +%define RGB_BLUE EXT_RGB_BLUE +%define RGB_PIXELSIZE EXT_RGB_PIXELSIZE +%define jsimd_rgb_gray_convert_sse2 jsimd_extrgb_gray_convert_sse2 +%include "jcgryss2-64.asm" + +%undef RGB_RED +%undef RGB_GREEN +%undef RGB_BLUE +%undef RGB_PIXELSIZE +%define RGB_RED EXT_RGBX_RED +%define RGB_GREEN EXT_RGBX_GREEN +%define RGB_BLUE EXT_RGBX_BLUE +%define RGB_PIXELSIZE EXT_RGBX_PIXELSIZE +%define jsimd_rgb_gray_convert_sse2 jsimd_extrgbx_gray_convert_sse2 +%include "jcgryss2-64.asm" + +%undef RGB_RED +%undef RGB_GREEN +%undef RGB_BLUE +%undef RGB_PIXELSIZE +%define RGB_RED EXT_BGR_RED +%define RGB_GREEN EXT_BGR_GREEN +%define RGB_BLUE EXT_BGR_BLUE +%define RGB_PIXELSIZE EXT_BGR_PIXELSIZE +%define jsimd_rgb_gray_convert_sse2 jsimd_extbgr_gray_convert_sse2 +%include "jcgryss2-64.asm" + +%undef RGB_RED +%undef RGB_GREEN +%undef RGB_BLUE +%undef RGB_PIXELSIZE +%define RGB_RED EXT_BGRX_RED +%define RGB_GREEN EXT_BGRX_GREEN +%define RGB_BLUE EXT_BGRX_BLUE +%define RGB_PIXELSIZE EXT_BGRX_PIXELSIZE +%define jsimd_rgb_gray_convert_sse2 jsimd_extbgrx_gray_convert_sse2 +%include "jcgryss2-64.asm" + +%undef RGB_RED +%undef RGB_GREEN +%undef RGB_BLUE +%undef RGB_PIXELSIZE +%define RGB_RED EXT_XBGR_RED +%define RGB_GREEN EXT_XBGR_GREEN +%define RGB_BLUE EXT_XBGR_BLUE +%define RGB_PIXELSIZE EXT_XBGR_PIXELSIZE +%define jsimd_rgb_gray_convert_sse2 jsimd_extxbgr_gray_convert_sse2 +%include "jcgryss2-64.asm" + +%undef RGB_RED +%undef RGB_GREEN +%undef RGB_BLUE +%undef RGB_PIXELSIZE +%define RGB_RED EXT_XRGB_RED +%define RGB_GREEN EXT_XRGB_GREEN +%define RGB_BLUE EXT_XRGB_BLUE +%define RGB_PIXELSIZE EXT_XRGB_PIXELSIZE +%define jsimd_rgb_gray_convert_sse2 jsimd_extxrgb_gray_convert_sse2 +%include "jcgryss2-64.asm" diff --git a/media/libjpeg/simd/jcgrass2.asm b/media/libjpeg/simd/jcgrass2.asm new file mode 100644 index 00000000000..998968e7635 --- /dev/null +++ b/media/libjpeg/simd/jcgrass2.asm @@ -0,0 +1,113 @@ +; +; jcgrass2.asm - grayscale colorspace conversion (SSE2) +; +; x86 SIMD extension for IJG JPEG library +; Copyright (C) 1999-2006, MIYASAKA Masaru. +; Copyright (C) 2011, D. R. Commander. +; For conditions of distribution and use, see copyright notice in jsimdext.inc +; +; This file should be assembled with NASM (Netwide Assembler), +; can *not* be assembled with Microsoft's MASM or any compatible +; assembler (including Borland's Turbo Assembler). +; NASM is available from http://nasm.sourceforge.net/ or +; http://sourceforge.net/project/showfiles.php?group_id=6208 +; +; [TAB8] + +%include "jsimdext.inc" + +; -------------------------------------------------------------------------- + +%define SCALEBITS 16 + +F_0_114 equ 7471 ; FIX(0.11400) +F_0_250 equ 16384 ; FIX(0.25000) +F_0_299 equ 19595 ; FIX(0.29900) +F_0_587 equ 38470 ; FIX(0.58700) +F_0_337 equ (F_0_587 - F_0_250) ; FIX(0.58700) - FIX(0.25000) + +; -------------------------------------------------------------------------- + SECTION SEG_CONST + + alignz 16 + global EXTN(jconst_rgb_gray_convert_sse2) + +EXTN(jconst_rgb_gray_convert_sse2): + +PW_F0299_F0337 times 4 dw F_0_299, F_0_337 +PW_F0114_F0250 times 4 dw F_0_114, F_0_250 +PD_ONEHALF times 4 dd (1 << (SCALEBITS-1)) + + alignz 16 + +; -------------------------------------------------------------------------- + SECTION SEG_TEXT + BITS 32 + +%include "jcgryss2.asm" + +%undef RGB_RED +%undef RGB_GREEN +%undef RGB_BLUE +%undef RGB_PIXELSIZE +%define RGB_RED EXT_RGB_RED +%define RGB_GREEN EXT_RGB_GREEN +%define RGB_BLUE EXT_RGB_BLUE +%define RGB_PIXELSIZE EXT_RGB_PIXELSIZE +%define jsimd_rgb_gray_convert_sse2 jsimd_extrgb_gray_convert_sse2 +%include "jcgryss2.asm" + +%undef RGB_RED +%undef RGB_GREEN +%undef RGB_BLUE +%undef RGB_PIXELSIZE +%define RGB_RED EXT_RGBX_RED +%define RGB_GREEN EXT_RGBX_GREEN +%define RGB_BLUE EXT_RGBX_BLUE +%define RGB_PIXELSIZE EXT_RGBX_PIXELSIZE +%define jsimd_rgb_gray_convert_sse2 jsimd_extrgbx_gray_convert_sse2 +%include "jcgryss2.asm" + +%undef RGB_RED +%undef RGB_GREEN +%undef RGB_BLUE +%undef RGB_PIXELSIZE +%define RGB_RED EXT_BGR_RED +%define RGB_GREEN EXT_BGR_GREEN +%define RGB_BLUE EXT_BGR_BLUE +%define RGB_PIXELSIZE EXT_BGR_PIXELSIZE +%define jsimd_rgb_gray_convert_sse2 jsimd_extbgr_gray_convert_sse2 +%include "jcgryss2.asm" + +%undef RGB_RED +%undef RGB_GREEN +%undef RGB_BLUE +%undef RGB_PIXELSIZE +%define RGB_RED EXT_BGRX_RED +%define RGB_GREEN EXT_BGRX_GREEN +%define RGB_BLUE EXT_BGRX_BLUE +%define RGB_PIXELSIZE EXT_BGRX_PIXELSIZE +%define jsimd_rgb_gray_convert_sse2 jsimd_extbgrx_gray_convert_sse2 +%include "jcgryss2.asm" + +%undef RGB_RED +%undef RGB_GREEN +%undef RGB_BLUE +%undef RGB_PIXELSIZE +%define RGB_RED EXT_XBGR_RED +%define RGB_GREEN EXT_XBGR_GREEN +%define RGB_BLUE EXT_XBGR_BLUE +%define RGB_PIXELSIZE EXT_XBGR_PIXELSIZE +%define jsimd_rgb_gray_convert_sse2 jsimd_extxbgr_gray_convert_sse2 +%include "jcgryss2.asm" + +%undef RGB_RED +%undef RGB_GREEN +%undef RGB_BLUE +%undef RGB_PIXELSIZE +%define RGB_RED EXT_XRGB_RED +%define RGB_GREEN EXT_XRGB_GREEN +%define RGB_BLUE EXT_XRGB_BLUE +%define RGB_PIXELSIZE EXT_XRGB_PIXELSIZE +%define jsimd_rgb_gray_convert_sse2 jsimd_extxrgb_gray_convert_sse2 +%include "jcgryss2.asm" diff --git a/media/libjpeg/simd/jcgrymmx.asm b/media/libjpeg/simd/jcgrymmx.asm new file mode 100644 index 00000000000..bbeea09bec9 --- /dev/null +++ b/media/libjpeg/simd/jcgrymmx.asm @@ -0,0 +1,357 @@ +; +; jcgrymmx.asm - grayscale colorspace conversion (MMX) +; +; Copyright 2009 Pierre Ossman for Cendio AB +; Copyright 2011 D. R. Commander +; +; Based on +; x86 SIMD extension for IJG JPEG library +; Copyright (C) 1999-2006, MIYASAKA Masaru. +; For conditions of distribution and use, see copyright notice in jsimdext.inc +; +; This file should be assembled with NASM (Netwide Assembler), +; can *not* be assembled with Microsoft's MASM or any compatible +; assembler (including Borland's Turbo Assembler). +; NASM is available from http://nasm.sourceforge.net/ or +; http://sourceforge.net/project/showfiles.php?group_id=6208 +; +; [TAB8] + +%include "jcolsamp.inc" + +; -------------------------------------------------------------------------- +; +; Convert some rows of samples to the output colorspace. +; +; GLOBAL(void) +; jsimd_rgb_gray_convert_mmx (JDIMENSION img_width, +; JSAMPARRAY input_buf, JSAMPIMAGE output_buf, +; JDIMENSION output_row, int num_rows); +; + +%define img_width(b) (b)+8 ; JDIMENSION img_width +%define input_buf(b) (b)+12 ; JSAMPARRAY input_buf +%define output_buf(b) (b)+16 ; JSAMPIMAGE output_buf +%define output_row(b) (b)+20 ; JDIMENSION output_row +%define num_rows(b) (b)+24 ; int num_rows + +%define original_ebp ebp+0 +%define wk(i) ebp-(WK_NUM-(i))*SIZEOF_MMWORD ; mmword wk[WK_NUM] +%define WK_NUM 2 +%define gotptr wk(0)-SIZEOF_POINTER ; void * gotptr + + align 16 + global EXTN(jsimd_rgb_gray_convert_mmx) + +EXTN(jsimd_rgb_gray_convert_mmx): + push ebp + mov eax,esp ; eax = original ebp + sub esp, byte 4 + and esp, byte (-SIZEOF_MMWORD) ; align to 64 bits + mov [esp],eax + mov ebp,esp ; ebp = aligned ebp + lea esp, [wk(0)] + pushpic eax ; make a room for GOT address + push ebx +; push ecx ; need not be preserved +; push edx ; need not be preserved + push esi + push edi + + get_GOT ebx ; get GOT address + movpic POINTER [gotptr], ebx ; save GOT address + + mov ecx, JDIMENSION [img_width(eax)] ; num_cols + test ecx,ecx + jz near .return + + push ecx + + mov esi, JSAMPIMAGE [output_buf(eax)] + mov ecx, JDIMENSION [output_row(eax)] + mov edi, JSAMPARRAY [esi+0*SIZEOF_JSAMPARRAY] + lea edi, [edi+ecx*SIZEOF_JSAMPROW] + + pop ecx + + mov esi, JSAMPARRAY [input_buf(eax)] + mov eax, INT [num_rows(eax)] + test eax,eax + jle near .return + alignx 16,7 +.rowloop: + pushpic eax + push edi + push esi + push ecx ; col + + mov esi, JSAMPROW [esi] ; inptr + mov edi, JSAMPROW [edi] ; outptr0 + movpic eax, POINTER [gotptr] ; load GOT address (eax) + + cmp ecx, byte SIZEOF_MMWORD + jae short .columnloop + alignx 16,7 + +%if RGB_PIXELSIZE == 3 ; --------------- + +.column_ld1: + push eax + push edx + lea ecx,[ecx+ecx*2] ; imul ecx,RGB_PIXELSIZE + test cl, SIZEOF_BYTE + jz short .column_ld2 + sub ecx, byte SIZEOF_BYTE + xor eax,eax + mov al, BYTE [esi+ecx] +.column_ld2: + test cl, SIZEOF_WORD + jz short .column_ld4 + sub ecx, byte SIZEOF_WORD + xor edx,edx + mov dx, WORD [esi+ecx] + shl eax, WORD_BIT + or eax,edx +.column_ld4: + movd mmA,eax + pop edx + pop eax + test cl, SIZEOF_DWORD + jz short .column_ld8 + sub ecx, byte SIZEOF_DWORD + movd mmG, DWORD [esi+ecx] + psllq mmA, DWORD_BIT + por mmA,mmG +.column_ld8: + test cl, SIZEOF_MMWORD + jz short .column_ld16 + movq mmG,mmA + movq mmA, MMWORD [esi+0*SIZEOF_MMWORD] + mov ecx, SIZEOF_MMWORD + jmp short .rgb_gray_cnv +.column_ld16: + test cl, 2*SIZEOF_MMWORD + mov ecx, SIZEOF_MMWORD + jz short .rgb_gray_cnv + movq mmF,mmA + movq mmA, MMWORD [esi+0*SIZEOF_MMWORD] + movq mmG, MMWORD [esi+1*SIZEOF_MMWORD] + jmp short .rgb_gray_cnv + alignx 16,7 + +.columnloop: + movq mmA, MMWORD [esi+0*SIZEOF_MMWORD] + movq mmG, MMWORD [esi+1*SIZEOF_MMWORD] + movq mmF, MMWORD [esi+2*SIZEOF_MMWORD] + +.rgb_gray_cnv: + ; mmA=(00 10 20 01 11 21 02 12) + ; mmG=(22 03 13 23 04 14 24 05) + ; mmF=(15 25 06 16 26 07 17 27) + + movq mmD,mmA + psllq mmA,4*BYTE_BIT ; mmA=(-- -- -- -- 00 10 20 01) + psrlq mmD,4*BYTE_BIT ; mmD=(11 21 02 12 -- -- -- --) + + punpckhbw mmA,mmG ; mmA=(00 04 10 14 20 24 01 05) + psllq mmG,4*BYTE_BIT ; mmG=(-- -- -- -- 22 03 13 23) + + punpcklbw mmD,mmF ; mmD=(11 15 21 25 02 06 12 16) + punpckhbw mmG,mmF ; mmG=(22 26 03 07 13 17 23 27) + + movq mmE,mmA + psllq mmA,4*BYTE_BIT ; mmA=(-- -- -- -- 00 04 10 14) + psrlq mmE,4*BYTE_BIT ; mmE=(20 24 01 05 -- -- -- --) + + punpckhbw mmA,mmD ; mmA=(00 02 04 06 10 12 14 16) + psllq mmD,4*BYTE_BIT ; mmD=(-- -- -- -- 11 15 21 25) + + punpcklbw mmE,mmG ; mmE=(20 22 24 26 01 03 05 07) + punpckhbw mmD,mmG ; mmD=(11 13 15 17 21 23 25 27) + + pxor mmH,mmH + + movq mmC,mmA + punpcklbw mmA,mmH ; mmA=(00 02 04 06) + punpckhbw mmC,mmH ; mmC=(10 12 14 16) + + movq mmB,mmE + punpcklbw mmE,mmH ; mmE=(20 22 24 26) + punpckhbw mmB,mmH ; mmB=(01 03 05 07) + + movq mmF,mmD + punpcklbw mmD,mmH ; mmD=(11 13 15 17) + punpckhbw mmF,mmH ; mmF=(21 23 25 27) + +%else ; RGB_PIXELSIZE == 4 ; ----------- + +.column_ld1: + test cl, SIZEOF_MMWORD/8 + jz short .column_ld2 + sub ecx, byte SIZEOF_MMWORD/8 + movd mmA, DWORD [esi+ecx*RGB_PIXELSIZE] +.column_ld2: + test cl, SIZEOF_MMWORD/4 + jz short .column_ld4 + sub ecx, byte SIZEOF_MMWORD/4 + movq mmF,mmA + movq mmA, MMWORD [esi+ecx*RGB_PIXELSIZE] +.column_ld4: + test cl, SIZEOF_MMWORD/2 + mov ecx, SIZEOF_MMWORD + jz short .rgb_gray_cnv + movq mmD,mmA + movq mmC,mmF + movq mmA, MMWORD [esi+0*SIZEOF_MMWORD] + movq mmF, MMWORD [esi+1*SIZEOF_MMWORD] + jmp short .rgb_gray_cnv + alignx 16,7 + +.columnloop: + movq mmA, MMWORD [esi+0*SIZEOF_MMWORD] + movq mmF, MMWORD [esi+1*SIZEOF_MMWORD] + movq mmD, MMWORD [esi+2*SIZEOF_MMWORD] + movq mmC, MMWORD [esi+3*SIZEOF_MMWORD] + +.rgb_gray_cnv: + ; mmA=(00 10 20 30 01 11 21 31) + ; mmF=(02 12 22 32 03 13 23 33) + ; mmD=(04 14 24 34 05 15 25 35) + ; mmC=(06 16 26 36 07 17 27 37) + + movq mmB,mmA + punpcklbw mmA,mmF ; mmA=(00 02 10 12 20 22 30 32) + punpckhbw mmB,mmF ; mmB=(01 03 11 13 21 23 31 33) + + movq mmG,mmD + punpcklbw mmD,mmC ; mmD=(04 06 14 16 24 26 34 36) + punpckhbw mmG,mmC ; mmG=(05 07 15 17 25 27 35 37) + + movq mmE,mmA + punpcklwd mmA,mmD ; mmA=(00 02 04 06 10 12 14 16) + punpckhwd mmE,mmD ; mmE=(20 22 24 26 30 32 34 36) + + movq mmH,mmB + punpcklwd mmB,mmG ; mmB=(01 03 05 07 11 13 15 17) + punpckhwd mmH,mmG ; mmH=(21 23 25 27 31 33 35 37) + + pxor mmF,mmF + + movq mmC,mmA + punpcklbw mmA,mmF ; mmA=(00 02 04 06) + punpckhbw mmC,mmF ; mmC=(10 12 14 16) + + movq mmD,mmB + punpcklbw mmB,mmF ; mmB=(01 03 05 07) + punpckhbw mmD,mmF ; mmD=(11 13 15 17) + + movq mmG,mmE + punpcklbw mmE,mmF ; mmE=(20 22 24 26) + punpckhbw mmG,mmF ; mmG=(30 32 34 36) + + punpcklbw mmF,mmH + punpckhbw mmH,mmH + psrlw mmF,BYTE_BIT ; mmF=(21 23 25 27) + psrlw mmH,BYTE_BIT ; mmH=(31 33 35 37) + +%endif ; RGB_PIXELSIZE ; --------------- + + ; mm0=(R0 R2 R4 R6)=RE, mm2=(G0 G2 G4 G6)=GE, mm4=(B0 B2 B4 B6)=BE + ; mm1=(R1 R3 R5 R7)=RO, mm3=(G1 G3 G5 G7)=GO, mm5=(B1 B3 B5 B7)=BO + + ; (Original) + ; Y = 0.29900 * R + 0.58700 * G + 0.11400 * B + ; + ; (This implementation) + ; Y = 0.29900 * R + 0.33700 * G + 0.11400 * B + 0.25000 * G + + movq mm6,mm1 + punpcklwd mm1,mm3 + punpckhwd mm6,mm3 + pmaddwd mm1,[GOTOFF(eax,PW_F0299_F0337)] ; mm1=ROL*FIX(0.299)+GOL*FIX(0.337) + pmaddwd mm6,[GOTOFF(eax,PW_F0299_F0337)] ; mm6=ROH*FIX(0.299)+GOH*FIX(0.337) + + movq mm7, mm6 ; mm7=ROH*FIX(0.299)+GOH*FIX(0.337) + + movq mm6,mm0 + punpcklwd mm0,mm2 + punpckhwd mm6,mm2 + pmaddwd mm0,[GOTOFF(eax,PW_F0299_F0337)] ; mm0=REL*FIX(0.299)+GEL*FIX(0.337) + pmaddwd mm6,[GOTOFF(eax,PW_F0299_F0337)] ; mm6=REH*FIX(0.299)+GEH*FIX(0.337) + + movq MMWORD [wk(0)], mm0 ; wk(0)=REL*FIX(0.299)+GEL*FIX(0.337) + movq MMWORD [wk(1)], mm6 ; wk(1)=REH*FIX(0.299)+GEH*FIX(0.337) + + movq mm0, mm5 ; mm0=BO + movq mm6, mm4 ; mm6=BE + + movq mm4,mm0 + punpcklwd mm0,mm3 + punpckhwd mm4,mm3 + pmaddwd mm0,[GOTOFF(eax,PW_F0114_F0250)] ; mm0=BOL*FIX(0.114)+GOL*FIX(0.250) + pmaddwd mm4,[GOTOFF(eax,PW_F0114_F0250)] ; mm4=BOH*FIX(0.114)+GOH*FIX(0.250) + + movq mm3,[GOTOFF(eax,PD_ONEHALF)] ; mm3=[PD_ONEHALF] + + paddd mm0, mm1 + paddd mm4, mm7 + paddd mm0,mm3 + paddd mm4,mm3 + psrld mm0,SCALEBITS ; mm0=YOL + psrld mm4,SCALEBITS ; mm4=YOH + packssdw mm0,mm4 ; mm0=YO + + movq mm4,mm6 + punpcklwd mm6,mm2 + punpckhwd mm4,mm2 + pmaddwd mm6,[GOTOFF(eax,PW_F0114_F0250)] ; mm6=BEL*FIX(0.114)+GEL*FIX(0.250) + pmaddwd mm4,[GOTOFF(eax,PW_F0114_F0250)] ; mm4=BEH*FIX(0.114)+GEH*FIX(0.250) + + movq mm2,[GOTOFF(eax,PD_ONEHALF)] ; mm2=[PD_ONEHALF] + + paddd mm6, MMWORD [wk(0)] + paddd mm4, MMWORD [wk(1)] + paddd mm6,mm2 + paddd mm4,mm2 + psrld mm6,SCALEBITS ; mm6=YEL + psrld mm4,SCALEBITS ; mm4=YEH + packssdw mm6,mm4 ; mm6=YE + + psllw mm0,BYTE_BIT + por mm6,mm0 ; mm6=Y + movq MMWORD [edi], mm6 ; Save Y + + sub ecx, byte SIZEOF_MMWORD + add esi, byte RGB_PIXELSIZE*SIZEOF_MMWORD ; inptr + add edi, byte SIZEOF_MMWORD ; outptr0 + cmp ecx, byte SIZEOF_MMWORD + jae near .columnloop + test ecx,ecx + jnz near .column_ld1 + + pop ecx ; col + pop esi + pop edi + poppic eax + + add esi, byte SIZEOF_JSAMPROW ; input_buf + add edi, byte SIZEOF_JSAMPROW + dec eax ; num_rows + jg near .rowloop + + emms ; empty MMX state + +.return: + pop edi + pop esi +; pop edx ; need not be preserved +; pop ecx ; need not be preserved + pop ebx + mov esp,ebp ; esp <- aligned ebp + pop esp ; esp <- original ebp + pop ebp + ret + +; For some reason, the OS X linker does not honor the request to align the +; segment unless we do this. + align 16 diff --git a/media/libjpeg/simd/jcgryss2-64.asm b/media/libjpeg/simd/jcgryss2-64.asm new file mode 100644 index 00000000000..23ae8afb153 --- /dev/null +++ b/media/libjpeg/simd/jcgryss2-64.asm @@ -0,0 +1,364 @@ +; +; jcgryss2-64.asm - grayscale colorspace conversion (64-bit SSE2) +; +; x86 SIMD extension for IJG JPEG library +; Copyright (C) 1999-2006, MIYASAKA Masaru. +; Copyright (C) 2011, D. R. Commander. +; For conditions of distribution and use, see copyright notice in jsimdext.inc +; +; This file should be assembled with NASM (Netwide Assembler), +; can *not* be assembled with Microsoft's MASM or any compatible +; assembler (including Borland's Turbo Assembler). +; NASM is available from http://nasm.sourceforge.net/ or +; http://sourceforge.net/project/showfiles.php?group_id=6208 +; +; [TAB8] + +%include "jcolsamp.inc" + +; -------------------------------------------------------------------------- +; +; Convert some rows of samples to the output colorspace. +; +; GLOBAL(void) +; jsimd_rgb_gray_convert_sse2 (JDIMENSION img_width, +; JSAMPARRAY input_buf, JSAMPIMAGE output_buf, +; JDIMENSION output_row, int num_rows); +; + +; r10 = JDIMENSION img_width +; r11 = JSAMPARRAY input_buf +; r12 = JSAMPIMAGE output_buf +; r13 = JDIMENSION output_row +; r14 = int num_rows + +%define wk(i) rbp-(WK_NUM-(i))*SIZEOF_XMMWORD ; xmmword wk[WK_NUM] +%define WK_NUM 2 + + align 16 + + global EXTN(jsimd_rgb_gray_convert_sse2) + +EXTN(jsimd_rgb_gray_convert_sse2): + push rbp + mov rax,rsp ; rax = original rbp + sub rsp, byte 4 + and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits + mov [rsp],rax + mov rbp,rsp ; rbp = aligned rbp + lea rsp, [wk(0)] + collect_args + push rbx + + mov rcx, r10 + test rcx,rcx + jz near .return + + push rcx + + mov rsi, r12 + mov rcx, r13 + mov rdi, JSAMPARRAY [rsi+0*SIZEOF_JSAMPARRAY] + lea rdi, [rdi+rcx*SIZEOF_JSAMPROW] + + pop rcx + + mov rsi, r11 + mov eax, r14d + test rax,rax + jle near .return +.rowloop: + push rdi + push rsi + push rcx ; col + + mov rsi, JSAMPROW [rsi] ; inptr + mov rdi, JSAMPROW [rdi] ; outptr0 + + cmp rcx, byte SIZEOF_XMMWORD + jae near .columnloop + +%if RGB_PIXELSIZE == 3 ; --------------- + +.column_ld1: + push rax + push rdx + lea rcx,[rcx+rcx*2] ; imul ecx,RGB_PIXELSIZE + test cl, SIZEOF_BYTE + jz short .column_ld2 + sub rcx, byte SIZEOF_BYTE + movzx rax, BYTE [rsi+rcx] +.column_ld2: + test cl, SIZEOF_WORD + jz short .column_ld4 + sub rcx, byte SIZEOF_WORD + movzx rdx, WORD [rsi+rcx] + shl rax, WORD_BIT + or rax,rdx +.column_ld4: + movd xmmA,eax + pop rdx + pop rax + test cl, SIZEOF_DWORD + jz short .column_ld8 + sub rcx, byte SIZEOF_DWORD + movd xmmF, XMM_DWORD [rsi+rcx] + pslldq xmmA, SIZEOF_DWORD + por xmmA,xmmF +.column_ld8: + test cl, SIZEOF_MMWORD + jz short .column_ld16 + sub rcx, byte SIZEOF_MMWORD + movq xmmB, XMM_MMWORD [rsi+rcx] + pslldq xmmA, SIZEOF_MMWORD + por xmmA,xmmB +.column_ld16: + test cl, SIZEOF_XMMWORD + jz short .column_ld32 + movdqa xmmF,xmmA + movdqu xmmA, XMMWORD [rsi+0*SIZEOF_XMMWORD] + mov rcx, SIZEOF_XMMWORD + jmp short .rgb_gray_cnv +.column_ld32: + test cl, 2*SIZEOF_XMMWORD + mov rcx, SIZEOF_XMMWORD + jz short .rgb_gray_cnv + movdqa xmmB,xmmA + movdqu xmmA, XMMWORD [rsi+0*SIZEOF_XMMWORD] + movdqu xmmF, XMMWORD [rsi+1*SIZEOF_XMMWORD] + jmp short .rgb_gray_cnv + +.columnloop: + movdqu xmmA, XMMWORD [rsi+0*SIZEOF_XMMWORD] + movdqu xmmF, XMMWORD [rsi+1*SIZEOF_XMMWORD] + movdqu xmmB, XMMWORD [rsi+2*SIZEOF_XMMWORD] + +.rgb_gray_cnv: + ; xmmA=(00 10 20 01 11 21 02 12 22 03 13 23 04 14 24 05) + ; xmmF=(15 25 06 16 26 07 17 27 08 18 28 09 19 29 0A 1A) + ; xmmB=(2A 0B 1B 2B 0C 1C 2C 0D 1D 2D 0E 1E 2E 0F 1F 2F) + + movdqa xmmG,xmmA + pslldq xmmA,8 ; xmmA=(-- -- -- -- -- -- -- -- 00 10 20 01 11 21 02 12) + psrldq xmmG,8 ; xmmG=(22 03 13 23 04 14 24 05 -- -- -- -- -- -- -- --) + + punpckhbw xmmA,xmmF ; xmmA=(00 08 10 18 20 28 01 09 11 19 21 29 02 0A 12 1A) + pslldq xmmF,8 ; xmmF=(-- -- -- -- -- -- -- -- 15 25 06 16 26 07 17 27) + + punpcklbw xmmG,xmmB ; xmmG=(22 2A 03 0B 13 1B 23 2B 04 0C 14 1C 24 2C 05 0D) + punpckhbw xmmF,xmmB ; xmmF=(15 1D 25 2D 06 0E 16 1E 26 2E 07 0F 17 1F 27 2F) + + movdqa xmmD,xmmA + pslldq xmmA,8 ; xmmA=(-- -- -- -- -- -- -- -- 00 08 10 18 20 28 01 09) + psrldq xmmD,8 ; xmmD=(11 19 21 29 02 0A 12 1A -- -- -- -- -- -- -- --) + + punpckhbw xmmA,xmmG ; xmmA=(00 04 08 0C 10 14 18 1C 20 24 28 2C 01 05 09 0D) + pslldq xmmG,8 ; xmmG=(-- -- -- -- -- -- -- -- 22 2A 03 0B 13 1B 23 2B) + + punpcklbw xmmD,xmmF ; xmmD=(11 15 19 1D 21 25 29 2D 02 06 0A 0E 12 16 1A 1E) + punpckhbw xmmG,xmmF ; xmmG=(22 26 2A 2E 03 07 0B 0F 13 17 1B 1F 23 27 2B 2F) + + movdqa xmmE,xmmA + pslldq xmmA,8 ; xmmA=(-- -- -- -- -- -- -- -- 00 04 08 0C 10 14 18 1C) + psrldq xmmE,8 ; xmmE=(20 24 28 2C 01 05 09 0D -- -- -- -- -- -- -- --) + + punpckhbw xmmA,xmmD ; xmmA=(00 02 04 06 08 0A 0C 0E 10 12 14 16 18 1A 1C 1E) + pslldq xmmD,8 ; xmmD=(-- -- -- -- -- -- -- -- 11 15 19 1D 21 25 29 2D) + + punpcklbw xmmE,xmmG ; xmmE=(20 22 24 26 28 2A 2C 2E 01 03 05 07 09 0B 0D 0F) + punpckhbw xmmD,xmmG ; xmmD=(11 13 15 17 19 1B 1D 1F 21 23 25 27 29 2B 2D 2F) + + pxor xmmH,xmmH + + movdqa xmmC,xmmA + punpcklbw xmmA,xmmH ; xmmA=(00 02 04 06 08 0A 0C 0E) + punpckhbw xmmC,xmmH ; xmmC=(10 12 14 16 18 1A 1C 1E) + + movdqa xmmB,xmmE + punpcklbw xmmE,xmmH ; xmmE=(20 22 24 26 28 2A 2C 2E) + punpckhbw xmmB,xmmH ; xmmB=(01 03 05 07 09 0B 0D 0F) + + movdqa xmmF,xmmD + punpcklbw xmmD,xmmH ; xmmD=(11 13 15 17 19 1B 1D 1F) + punpckhbw xmmF,xmmH ; xmmF=(21 23 25 27 29 2B 2D 2F) + +%else ; RGB_PIXELSIZE == 4 ; ----------- + +.column_ld1: + test cl, SIZEOF_XMMWORD/16 + jz short .column_ld2 + sub rcx, byte SIZEOF_XMMWORD/16 + movd xmmA, XMM_DWORD [rsi+rcx*RGB_PIXELSIZE] +.column_ld2: + test cl, SIZEOF_XMMWORD/8 + jz short .column_ld4 + sub rcx, byte SIZEOF_XMMWORD/8 + movq xmmE, XMM_MMWORD [rsi+rcx*RGB_PIXELSIZE] + pslldq xmmA, SIZEOF_MMWORD + por xmmA,xmmE +.column_ld4: + test cl, SIZEOF_XMMWORD/4 + jz short .column_ld8 + sub rcx, byte SIZEOF_XMMWORD/4 + movdqa xmmE,xmmA + movdqu xmmA, XMMWORD [rsi+rcx*RGB_PIXELSIZE] +.column_ld8: + test cl, SIZEOF_XMMWORD/2 + mov rcx, SIZEOF_XMMWORD + jz short .rgb_gray_cnv + movdqa xmmF,xmmA + movdqa xmmH,xmmE + movdqu xmmA, XMMWORD [rsi+0*SIZEOF_XMMWORD] + movdqu xmmE, XMMWORD [rsi+1*SIZEOF_XMMWORD] + jmp short .rgb_gray_cnv + +.columnloop: + movdqu xmmA, XMMWORD [rsi+0*SIZEOF_XMMWORD] + movdqu xmmE, XMMWORD [rsi+1*SIZEOF_XMMWORD] + movdqu xmmF, XMMWORD [rsi+2*SIZEOF_XMMWORD] + movdqu xmmH, XMMWORD [rsi+3*SIZEOF_XMMWORD] + +.rgb_gray_cnv: + ; xmmA=(00 10 20 30 01 11 21 31 02 12 22 32 03 13 23 33) + ; xmmE=(04 14 24 34 05 15 25 35 06 16 26 36 07 17 27 37) + ; xmmF=(08 18 28 38 09 19 29 39 0A 1A 2A 3A 0B 1B 2B 3B) + ; xmmH=(0C 1C 2C 3C 0D 1D 2D 3D 0E 1E 2E 3E 0F 1F 2F 3F) + + movdqa xmmD,xmmA + punpcklbw xmmA,xmmE ; xmmA=(00 04 10 14 20 24 30 34 01 05 11 15 21 25 31 35) + punpckhbw xmmD,xmmE ; xmmD=(02 06 12 16 22 26 32 36 03 07 13 17 23 27 33 37) + + movdqa xmmC,xmmF + punpcklbw xmmF,xmmH ; xmmF=(08 0C 18 1C 28 2C 38 3C 09 0D 19 1D 29 2D 39 3D) + punpckhbw xmmC,xmmH ; xmmC=(0A 0E 1A 1E 2A 2E 3A 3E 0B 0F 1B 1F 2B 2F 3B 3F) + + movdqa xmmB,xmmA + punpcklwd xmmA,xmmF ; xmmA=(00 04 08 0C 10 14 18 1C 20 24 28 2C 30 34 38 3C) + punpckhwd xmmB,xmmF ; xmmB=(01 05 09 0D 11 15 19 1D 21 25 29 2D 31 35 39 3D) + + movdqa xmmG,xmmD + punpcklwd xmmD,xmmC ; xmmD=(02 06 0A 0E 12 16 1A 1E 22 26 2A 2E 32 36 3A 3E) + punpckhwd xmmG,xmmC ; xmmG=(03 07 0B 0F 13 17 1B 1F 23 27 2B 2F 33 37 3B 3F) + + movdqa xmmE,xmmA + punpcklbw xmmA,xmmD ; xmmA=(00 02 04 06 08 0A 0C 0E 10 12 14 16 18 1A 1C 1E) + punpckhbw xmmE,xmmD ; xmmE=(20 22 24 26 28 2A 2C 2E 30 32 34 36 38 3A 3C 3E) + + movdqa xmmH,xmmB + punpcklbw xmmB,xmmG ; xmmB=(01 03 05 07 09 0B 0D 0F 11 13 15 17 19 1B 1D 1F) + punpckhbw xmmH,xmmG ; xmmH=(21 23 25 27 29 2B 2D 2F 31 33 35 37 39 3B 3D 3F) + + pxor xmmF,xmmF + + movdqa xmmC,xmmA + punpcklbw xmmA,xmmF ; xmmA=(00 02 04 06 08 0A 0C 0E) + punpckhbw xmmC,xmmF ; xmmC=(10 12 14 16 18 1A 1C 1E) + + movdqa xmmD,xmmB + punpcklbw xmmB,xmmF ; xmmB=(01 03 05 07 09 0B 0D 0F) + punpckhbw xmmD,xmmF ; xmmD=(11 13 15 17 19 1B 1D 1F) + + movdqa xmmG,xmmE + punpcklbw xmmE,xmmF ; xmmE=(20 22 24 26 28 2A 2C 2E) + punpckhbw xmmG,xmmF ; xmmG=(30 32 34 36 38 3A 3C 3E) + + punpcklbw xmmF,xmmH + punpckhbw xmmH,xmmH + psrlw xmmF,BYTE_BIT ; xmmF=(21 23 25 27 29 2B 2D 2F) + psrlw xmmH,BYTE_BIT ; xmmH=(31 33 35 37 39 3B 3D 3F) + +%endif ; RGB_PIXELSIZE ; --------------- + + ; xmm0=R(02468ACE)=RE, xmm2=G(02468ACE)=GE, xmm4=B(02468ACE)=BE + ; xmm1=R(13579BDF)=RO, xmm3=G(13579BDF)=GO, xmm5=B(13579BDF)=BO + + ; (Original) + ; Y = 0.29900 * R + 0.58700 * G + 0.11400 * B + ; + ; (This implementation) + ; Y = 0.29900 * R + 0.33700 * G + 0.11400 * B + 0.25000 * G + + movdqa xmm6,xmm1 + punpcklwd xmm1,xmm3 + punpckhwd xmm6,xmm3 + pmaddwd xmm1,[rel PW_F0299_F0337] ; xmm1=ROL*FIX(0.299)+GOL*FIX(0.337) + pmaddwd xmm6,[rel PW_F0299_F0337] ; xmm6=ROH*FIX(0.299)+GOH*FIX(0.337) + + movdqa xmm7, xmm6 ; xmm7=ROH*FIX(0.299)+GOH*FIX(0.337) + + movdqa xmm6,xmm0 + punpcklwd xmm0,xmm2 + punpckhwd xmm6,xmm2 + pmaddwd xmm0,[rel PW_F0299_F0337] ; xmm0=REL*FIX(0.299)+GEL*FIX(0.337) + pmaddwd xmm6,[rel PW_F0299_F0337] ; xmm6=REH*FIX(0.299)+GEH*FIX(0.337) + + movdqa XMMWORD [wk(0)], xmm0 ; wk(0)=REL*FIX(0.299)+GEL*FIX(0.337) + movdqa XMMWORD [wk(1)], xmm6 ; wk(1)=REH*FIX(0.299)+GEH*FIX(0.337) + + movdqa xmm0, xmm5 ; xmm0=BO + movdqa xmm6, xmm4 ; xmm6=BE + + movdqa xmm4,xmm0 + punpcklwd xmm0,xmm3 + punpckhwd xmm4,xmm3 + pmaddwd xmm0,[rel PW_F0114_F0250] ; xmm0=BOL*FIX(0.114)+GOL*FIX(0.250) + pmaddwd xmm4,[rel PW_F0114_F0250] ; xmm4=BOH*FIX(0.114)+GOH*FIX(0.250) + + movdqa xmm3,[rel PD_ONEHALF] ; xmm3=[PD_ONEHALF] + + paddd xmm0, xmm1 + paddd xmm4, xmm7 + paddd xmm0,xmm3 + paddd xmm4,xmm3 + psrld xmm0,SCALEBITS ; xmm0=YOL + psrld xmm4,SCALEBITS ; xmm4=YOH + packssdw xmm0,xmm4 ; xmm0=YO + + movdqa xmm4,xmm6 + punpcklwd xmm6,xmm2 + punpckhwd xmm4,xmm2 + pmaddwd xmm6,[rel PW_F0114_F0250] ; xmm6=BEL*FIX(0.114)+GEL*FIX(0.250) + pmaddwd xmm4,[rel PW_F0114_F0250] ; xmm4=BEH*FIX(0.114)+GEH*FIX(0.250) + + movdqa xmm2,[rel PD_ONEHALF] ; xmm2=[PD_ONEHALF] + + paddd xmm6, XMMWORD [wk(0)] + paddd xmm4, XMMWORD [wk(1)] + paddd xmm6,xmm2 + paddd xmm4,xmm2 + psrld xmm6,SCALEBITS ; xmm6=YEL + psrld xmm4,SCALEBITS ; xmm4=YEH + packssdw xmm6,xmm4 ; xmm6=YE + + psllw xmm0,BYTE_BIT + por xmm6,xmm0 ; xmm6=Y + movdqa XMMWORD [rdi], xmm6 ; Save Y + + sub rcx, byte SIZEOF_XMMWORD + add rsi, byte RGB_PIXELSIZE*SIZEOF_XMMWORD ; inptr + add rdi, byte SIZEOF_XMMWORD ; outptr0 + cmp rcx, byte SIZEOF_XMMWORD + jae near .columnloop + test rcx,rcx + jnz near .column_ld1 + + pop rcx ; col + pop rsi + pop rdi + + add rsi, byte SIZEOF_JSAMPROW ; input_buf + add rdi, byte SIZEOF_JSAMPROW + dec rax ; num_rows + jg near .rowloop + +.return: + pop rbx + uncollect_args + mov rsp,rbp ; rsp <- aligned rbp + pop rsp ; rsp <- original rbp + pop rbp + ret + +; For some reason, the OS X linker does not honor the request to align the +; segment unless we do this. + align 16 diff --git a/media/libjpeg/simd/jcgryss2.asm b/media/libjpeg/simd/jcgryss2.asm new file mode 100644 index 00000000000..c294287939e --- /dev/null +++ b/media/libjpeg/simd/jcgryss2.asm @@ -0,0 +1,383 @@ +; +; jcgryss2.asm - grayscale colorspace conversion (SSE2) +; +; x86 SIMD extension for IJG JPEG library +; Copyright (C) 1999-2006, MIYASAKA Masaru. +; Copyright (C) 2011, D. R. Commander. +; For conditions of distribution and use, see copyright notice in jsimdext.inc +; +; This file should be assembled with NASM (Netwide Assembler), +; can *not* be assembled with Microsoft's MASM or any compatible +; assembler (including Borland's Turbo Assembler). +; NASM is available from http://nasm.sourceforge.net/ or +; http://sourceforge.net/project/showfiles.php?group_id=6208 +; +; [TAB8] + +%include "jcolsamp.inc" + +; -------------------------------------------------------------------------- +; +; Convert some rows of samples to the output colorspace. +; +; GLOBAL(void) +; jsimd_rgb_gray_convert_sse2 (JDIMENSION img_width, +; JSAMPARRAY input_buf, JSAMPIMAGE output_buf, +; JDIMENSION output_row, int num_rows); +; + +%define img_width(b) (b)+8 ; JDIMENSION img_width +%define input_buf(b) (b)+12 ; JSAMPARRAY input_buf +%define output_buf(b) (b)+16 ; JSAMPIMAGE output_buf +%define output_row(b) (b)+20 ; JDIMENSION output_row +%define num_rows(b) (b)+24 ; int num_rows + +%define original_ebp ebp+0 +%define wk(i) ebp-(WK_NUM-(i))*SIZEOF_XMMWORD ; xmmword wk[WK_NUM] +%define WK_NUM 2 +%define gotptr wk(0)-SIZEOF_POINTER ; void * gotptr + + align 16 + + global EXTN(jsimd_rgb_gray_convert_sse2) + +EXTN(jsimd_rgb_gray_convert_sse2): + push ebp + mov eax,esp ; eax = original ebp + sub esp, byte 4 + and esp, byte (-SIZEOF_XMMWORD) ; align to 128 bits + mov [esp],eax + mov ebp,esp ; ebp = aligned ebp + lea esp, [wk(0)] + pushpic eax ; make a room for GOT address + push ebx +; push ecx ; need not be preserved +; push edx ; need not be preserved + push esi + push edi + + get_GOT ebx ; get GOT address + movpic POINTER [gotptr], ebx ; save GOT address + + mov ecx, JDIMENSION [img_width(eax)] + test ecx,ecx + jz near .return + + push ecx + + mov esi, JSAMPIMAGE [output_buf(eax)] + mov ecx, JDIMENSION [output_row(eax)] + mov edi, JSAMPARRAY [esi+0*SIZEOF_JSAMPARRAY] + lea edi, [edi+ecx*SIZEOF_JSAMPROW] + + pop ecx + + mov esi, JSAMPARRAY [input_buf(eax)] + mov eax, INT [num_rows(eax)] + test eax,eax + jle near .return + alignx 16,7 +.rowloop: + pushpic eax + push edi + push esi + push ecx ; col + + mov esi, JSAMPROW [esi] ; inptr + mov edi, JSAMPROW [edi] ; outptr0 + movpic eax, POINTER [gotptr] ; load GOT address (eax) + + cmp ecx, byte SIZEOF_XMMWORD + jae near .columnloop + alignx 16,7 + +%if RGB_PIXELSIZE == 3 ; --------------- + +.column_ld1: + push eax + push edx + lea ecx,[ecx+ecx*2] ; imul ecx,RGB_PIXELSIZE + test cl, SIZEOF_BYTE + jz short .column_ld2 + sub ecx, byte SIZEOF_BYTE + movzx eax, BYTE [esi+ecx] +.column_ld2: + test cl, SIZEOF_WORD + jz short .column_ld4 + sub ecx, byte SIZEOF_WORD + movzx edx, WORD [esi+ecx] + shl eax, WORD_BIT + or eax,edx +.column_ld4: + movd xmmA,eax + pop edx + pop eax + test cl, SIZEOF_DWORD + jz short .column_ld8 + sub ecx, byte SIZEOF_DWORD + movd xmmF, XMM_DWORD [esi+ecx] + pslldq xmmA, SIZEOF_DWORD + por xmmA,xmmF +.column_ld8: + test cl, SIZEOF_MMWORD + jz short .column_ld16 + sub ecx, byte SIZEOF_MMWORD + movq xmmB, XMM_MMWORD [esi+ecx] + pslldq xmmA, SIZEOF_MMWORD + por xmmA,xmmB +.column_ld16: + test cl, SIZEOF_XMMWORD + jz short .column_ld32 + movdqa xmmF,xmmA + movdqu xmmA, XMMWORD [esi+0*SIZEOF_XMMWORD] + mov ecx, SIZEOF_XMMWORD + jmp short .rgb_gray_cnv +.column_ld32: + test cl, 2*SIZEOF_XMMWORD + mov ecx, SIZEOF_XMMWORD + jz short .rgb_gray_cnv + movdqa xmmB,xmmA + movdqu xmmA, XMMWORD [esi+0*SIZEOF_XMMWORD] + movdqu xmmF, XMMWORD [esi+1*SIZEOF_XMMWORD] + jmp short .rgb_gray_cnv + alignx 16,7 + +.columnloop: + movdqu xmmA, XMMWORD [esi+0*SIZEOF_XMMWORD] + movdqu xmmF, XMMWORD [esi+1*SIZEOF_XMMWORD] + movdqu xmmB, XMMWORD [esi+2*SIZEOF_XMMWORD] + +.rgb_gray_cnv: + ; xmmA=(00 10 20 01 11 21 02 12 22 03 13 23 04 14 24 05) + ; xmmF=(15 25 06 16 26 07 17 27 08 18 28 09 19 29 0A 1A) + ; xmmB=(2A 0B 1B 2B 0C 1C 2C 0D 1D 2D 0E 1E 2E 0F 1F 2F) + + movdqa xmmG,xmmA + pslldq xmmA,8 ; xmmA=(-- -- -- -- -- -- -- -- 00 10 20 01 11 21 02 12) + psrldq xmmG,8 ; xmmG=(22 03 13 23 04 14 24 05 -- -- -- -- -- -- -- --) + + punpckhbw xmmA,xmmF ; xmmA=(00 08 10 18 20 28 01 09 11 19 21 29 02 0A 12 1A) + pslldq xmmF,8 ; xmmF=(-- -- -- -- -- -- -- -- 15 25 06 16 26 07 17 27) + + punpcklbw xmmG,xmmB ; xmmG=(22 2A 03 0B 13 1B 23 2B 04 0C 14 1C 24 2C 05 0D) + punpckhbw xmmF,xmmB ; xmmF=(15 1D 25 2D 06 0E 16 1E 26 2E 07 0F 17 1F 27 2F) + + movdqa xmmD,xmmA + pslldq xmmA,8 ; xmmA=(-- -- -- -- -- -- -- -- 00 08 10 18 20 28 01 09) + psrldq xmmD,8 ; xmmD=(11 19 21 29 02 0A 12 1A -- -- -- -- -- -- -- --) + + punpckhbw xmmA,xmmG ; xmmA=(00 04 08 0C 10 14 18 1C 20 24 28 2C 01 05 09 0D) + pslldq xmmG,8 ; xmmG=(-- -- -- -- -- -- -- -- 22 2A 03 0B 13 1B 23 2B) + + punpcklbw xmmD,xmmF ; xmmD=(11 15 19 1D 21 25 29 2D 02 06 0A 0E 12 16 1A 1E) + punpckhbw xmmG,xmmF ; xmmG=(22 26 2A 2E 03 07 0B 0F 13 17 1B 1F 23 27 2B 2F) + + movdqa xmmE,xmmA + pslldq xmmA,8 ; xmmA=(-- -- -- -- -- -- -- -- 00 04 08 0C 10 14 18 1C) + psrldq xmmE,8 ; xmmE=(20 24 28 2C 01 05 09 0D -- -- -- -- -- -- -- --) + + punpckhbw xmmA,xmmD ; xmmA=(00 02 04 06 08 0A 0C 0E 10 12 14 16 18 1A 1C 1E) + pslldq xmmD,8 ; xmmD=(-- -- -- -- -- -- -- -- 11 15 19 1D 21 25 29 2D) + + punpcklbw xmmE,xmmG ; xmmE=(20 22 24 26 28 2A 2C 2E 01 03 05 07 09 0B 0D 0F) + punpckhbw xmmD,xmmG ; xmmD=(11 13 15 17 19 1B 1D 1F 21 23 25 27 29 2B 2D 2F) + + pxor xmmH,xmmH + + movdqa xmmC,xmmA + punpcklbw xmmA,xmmH ; xmmA=(00 02 04 06 08 0A 0C 0E) + punpckhbw xmmC,xmmH ; xmmC=(10 12 14 16 18 1A 1C 1E) + + movdqa xmmB,xmmE + punpcklbw xmmE,xmmH ; xmmE=(20 22 24 26 28 2A 2C 2E) + punpckhbw xmmB,xmmH ; xmmB=(01 03 05 07 09 0B 0D 0F) + + movdqa xmmF,xmmD + punpcklbw xmmD,xmmH ; xmmD=(11 13 15 17 19 1B 1D 1F) + punpckhbw xmmF,xmmH ; xmmF=(21 23 25 27 29 2B 2D 2F) + +%else ; RGB_PIXELSIZE == 4 ; ----------- + +.column_ld1: + test cl, SIZEOF_XMMWORD/16 + jz short .column_ld2 + sub ecx, byte SIZEOF_XMMWORD/16 + movd xmmA, XMM_DWORD [esi+ecx*RGB_PIXELSIZE] +.column_ld2: + test cl, SIZEOF_XMMWORD/8 + jz short .column_ld4 + sub ecx, byte SIZEOF_XMMWORD/8 + movq xmmE, XMM_MMWORD [esi+ecx*RGB_PIXELSIZE] + pslldq xmmA, SIZEOF_MMWORD + por xmmA,xmmE +.column_ld4: + test cl, SIZEOF_XMMWORD/4 + jz short .column_ld8 + sub ecx, byte SIZEOF_XMMWORD/4 + movdqa xmmE,xmmA + movdqu xmmA, XMMWORD [esi+ecx*RGB_PIXELSIZE] +.column_ld8: + test cl, SIZEOF_XMMWORD/2 + mov ecx, SIZEOF_XMMWORD + jz short .rgb_gray_cnv + movdqa xmmF,xmmA + movdqa xmmH,xmmE + movdqu xmmA, XMMWORD [esi+0*SIZEOF_XMMWORD] + movdqu xmmE, XMMWORD [esi+1*SIZEOF_XMMWORD] + jmp short .rgb_gray_cnv + alignx 16,7 + +.columnloop: + movdqu xmmA, XMMWORD [esi+0*SIZEOF_XMMWORD] + movdqu xmmE, XMMWORD [esi+1*SIZEOF_XMMWORD] + movdqu xmmF, XMMWORD [esi+2*SIZEOF_XMMWORD] + movdqu xmmH, XMMWORD [esi+3*SIZEOF_XMMWORD] + +.rgb_gray_cnv: + ; xmmA=(00 10 20 30 01 11 21 31 02 12 22 32 03 13 23 33) + ; xmmE=(04 14 24 34 05 15 25 35 06 16 26 36 07 17 27 37) + ; xmmF=(08 18 28 38 09 19 29 39 0A 1A 2A 3A 0B 1B 2B 3B) + ; xmmH=(0C 1C 2C 3C 0D 1D 2D 3D 0E 1E 2E 3E 0F 1F 2F 3F) + + movdqa xmmD,xmmA + punpcklbw xmmA,xmmE ; xmmA=(00 04 10 14 20 24 30 34 01 05 11 15 21 25 31 35) + punpckhbw xmmD,xmmE ; xmmD=(02 06 12 16 22 26 32 36 03 07 13 17 23 27 33 37) + + movdqa xmmC,xmmF + punpcklbw xmmF,xmmH ; xmmF=(08 0C 18 1C 28 2C 38 3C 09 0D 19 1D 29 2D 39 3D) + punpckhbw xmmC,xmmH ; xmmC=(0A 0E 1A 1E 2A 2E 3A 3E 0B 0F 1B 1F 2B 2F 3B 3F) + + movdqa xmmB,xmmA + punpcklwd xmmA,xmmF ; xmmA=(00 04 08 0C 10 14 18 1C 20 24 28 2C 30 34 38 3C) + punpckhwd xmmB,xmmF ; xmmB=(01 05 09 0D 11 15 19 1D 21 25 29 2D 31 35 39 3D) + + movdqa xmmG,xmmD + punpcklwd xmmD,xmmC ; xmmD=(02 06 0A 0E 12 16 1A 1E 22 26 2A 2E 32 36 3A 3E) + punpckhwd xmmG,xmmC ; xmmG=(03 07 0B 0F 13 17 1B 1F 23 27 2B 2F 33 37 3B 3F) + + movdqa xmmE,xmmA + punpcklbw xmmA,xmmD ; xmmA=(00 02 04 06 08 0A 0C 0E 10 12 14 16 18 1A 1C 1E) + punpckhbw xmmE,xmmD ; xmmE=(20 22 24 26 28 2A 2C 2E 30 32 34 36 38 3A 3C 3E) + + movdqa xmmH,xmmB + punpcklbw xmmB,xmmG ; xmmB=(01 03 05 07 09 0B 0D 0F 11 13 15 17 19 1B 1D 1F) + punpckhbw xmmH,xmmG ; xmmH=(21 23 25 27 29 2B 2D 2F 31 33 35 37 39 3B 3D 3F) + + pxor xmmF,xmmF + + movdqa xmmC,xmmA + punpcklbw xmmA,xmmF ; xmmA=(00 02 04 06 08 0A 0C 0E) + punpckhbw xmmC,xmmF ; xmmC=(10 12 14 16 18 1A 1C 1E) + + movdqa xmmD,xmmB + punpcklbw xmmB,xmmF ; xmmB=(01 03 05 07 09 0B 0D 0F) + punpckhbw xmmD,xmmF ; xmmD=(11 13 15 17 19 1B 1D 1F) + + movdqa xmmG,xmmE + punpcklbw xmmE,xmmF ; xmmE=(20 22 24 26 28 2A 2C 2E) + punpckhbw xmmG,xmmF ; xmmG=(30 32 34 36 38 3A 3C 3E) + + punpcklbw xmmF,xmmH + punpckhbw xmmH,xmmH + psrlw xmmF,BYTE_BIT ; xmmF=(21 23 25 27 29 2B 2D 2F) + psrlw xmmH,BYTE_BIT ; xmmH=(31 33 35 37 39 3B 3D 3F) + +%endif ; RGB_PIXELSIZE ; --------------- + + ; xmm0=R(02468ACE)=RE, xmm2=G(02468ACE)=GE, xmm4=B(02468ACE)=BE + ; xmm1=R(13579BDF)=RO, xmm3=G(13579BDF)=GO, xmm5=B(13579BDF)=BO + + ; (Original) + ; Y = 0.29900 * R + 0.58700 * G + 0.11400 * B + ; + ; (This implementation) + ; Y = 0.29900 * R + 0.33700 * G + 0.11400 * B + 0.25000 * G + + movdqa xmm6,xmm1 + punpcklwd xmm1,xmm3 + punpckhwd xmm6,xmm3 + pmaddwd xmm1,[GOTOFF(eax,PW_F0299_F0337)] ; xmm1=ROL*FIX(0.299)+GOL*FIX(0.337) + pmaddwd xmm6,[GOTOFF(eax,PW_F0299_F0337)] ; xmm6=ROH*FIX(0.299)+GOH*FIX(0.337) + + movdqa xmm7, xmm6 ; xmm7=ROH*FIX(0.299)+GOH*FIX(0.337) + + movdqa xmm6,xmm0 + punpcklwd xmm0,xmm2 + punpckhwd xmm6,xmm2 + pmaddwd xmm0,[GOTOFF(eax,PW_F0299_F0337)] ; xmm0=REL*FIX(0.299)+GEL*FIX(0.337) + pmaddwd xmm6,[GOTOFF(eax,PW_F0299_F0337)] ; xmm6=REH*FIX(0.299)+GEH*FIX(0.337) + + movdqa XMMWORD [wk(0)], xmm0 ; wk(0)=REL*FIX(0.299)+GEL*FIX(0.337) + movdqa XMMWORD [wk(1)], xmm6 ; wk(1)=REH*FIX(0.299)+GEH*FIX(0.337) + + movdqa xmm0, xmm5 ; xmm0=BO + movdqa xmm6, xmm4 ; xmm6=BE + + movdqa xmm4,xmm0 + punpcklwd xmm0,xmm3 + punpckhwd xmm4,xmm3 + pmaddwd xmm0,[GOTOFF(eax,PW_F0114_F0250)] ; xmm0=BOL*FIX(0.114)+GOL*FIX(0.250) + pmaddwd xmm4,[GOTOFF(eax,PW_F0114_F0250)] ; xmm4=BOH*FIX(0.114)+GOH*FIX(0.250) + + movdqa xmm3,[GOTOFF(eax,PD_ONEHALF)] ; xmm3=[PD_ONEHALF] + + paddd xmm0, xmm1 + paddd xmm4, xmm7 + paddd xmm0,xmm3 + paddd xmm4,xmm3 + psrld xmm0,SCALEBITS ; xmm0=YOL + psrld xmm4,SCALEBITS ; xmm4=YOH + packssdw xmm0,xmm4 ; xmm0=YO + + movdqa xmm4,xmm6 + punpcklwd xmm6,xmm2 + punpckhwd xmm4,xmm2 + pmaddwd xmm6,[GOTOFF(eax,PW_F0114_F0250)] ; xmm6=BEL*FIX(0.114)+GEL*FIX(0.250) + pmaddwd xmm4,[GOTOFF(eax,PW_F0114_F0250)] ; xmm4=BEH*FIX(0.114)+GEH*FIX(0.250) + + movdqa xmm2,[GOTOFF(eax,PD_ONEHALF)] ; xmm2=[PD_ONEHALF] + + paddd xmm6, XMMWORD [wk(0)] + paddd xmm4, XMMWORD [wk(1)] + paddd xmm6,xmm2 + paddd xmm4,xmm2 + psrld xmm6,SCALEBITS ; xmm6=YEL + psrld xmm4,SCALEBITS ; xmm4=YEH + packssdw xmm6,xmm4 ; xmm6=YE + + psllw xmm0,BYTE_BIT + por xmm6,xmm0 ; xmm6=Y + movdqa XMMWORD [edi], xmm6 ; Save Y + + sub ecx, byte SIZEOF_XMMWORD + add esi, byte RGB_PIXELSIZE*SIZEOF_XMMWORD ; inptr + add edi, byte SIZEOF_XMMWORD ; outptr0 + cmp ecx, byte SIZEOF_XMMWORD + jae near .columnloop + test ecx,ecx + jnz near .column_ld1 + + pop ecx ; col + pop esi + pop edi + poppic eax + + add esi, byte SIZEOF_JSAMPROW ; input_buf + add edi, byte SIZEOF_JSAMPROW + dec eax ; num_rows + jg near .rowloop + +.return: + pop edi + pop esi +; pop edx ; need not be preserved +; pop ecx ; need not be preserved + pop ebx + mov esp,ebp ; esp <- aligned ebp + pop esp ; esp <- original ebp + pop ebp + ret + +; For some reason, the OS X linker does not honor the request to align the +; segment unless we do this. + align 16 diff --git a/media/libjpeg/simd/jdclrmmx.asm b/media/libjpeg/simd/jdclrmmx.asm index 79772e0c272..1c255e802d4 100644 --- a/media/libjpeg/simd/jdclrmmx.asm +++ b/media/libjpeg/simd/jdclrmmx.asm @@ -19,8 +19,6 @@ %include "jcolsamp.inc" ; -------------------------------------------------------------------------- - SECTION SEG_TEXT - BITS 32 ; ; Convert some rows of samples to the output colorspace. ; diff --git a/media/libjpeg/simd/jdclrss2-64.asm b/media/libjpeg/simd/jdclrss2-64.asm index 4282bd269f3..696a383bddf 100644 --- a/media/libjpeg/simd/jdclrss2-64.asm +++ b/media/libjpeg/simd/jdclrss2-64.asm @@ -20,8 +20,6 @@ %include "jcolsamp.inc" ; -------------------------------------------------------------------------- - SECTION SEG_TEXT - BITS 64 ; ; Convert some rows of samples to the output colorspace. ; @@ -292,6 +290,41 @@ EXTN(jsimd_ycc_rgb_convert_sse2): movdqa xmmA,xmmD sub rcx, byte SIZEOF_XMMWORD .column_st15: +%ifdef STRICT_MEMORY_ACCESS + ; Store the lower 8 bytes of xmmA to the output when it has enough + ; space. + cmp rcx, byte SIZEOF_MMWORD + jb short .column_st7 + movq MMWORD [rdi], xmmA + add rdi, byte SIZEOF_MMWORD + sub rcx, byte SIZEOF_MMWORD + psrldq xmmA, SIZEOF_MMWORD +.column_st7: + ; Store the lower 4 bytes of xmmA to the output when it has enough + ; space. + cmp rcx, byte SIZEOF_DWORD + jb short .column_st3 + movd DWORD [rdi], xmmA + add rdi, byte SIZEOF_DWORD + sub rcx, byte SIZEOF_DWORD + psrldq xmmA, SIZEOF_DWORD +.column_st3: + ; Store the lower 2 bytes of rax to the output when it has enough + ; space. + movd eax, xmmA + cmp rcx, byte SIZEOF_WORD + jb short .column_st1 + mov WORD [rdi], ax + add rdi, byte SIZEOF_WORD + sub rcx, byte SIZEOF_WORD + shr rax, 16 +.column_st1: + ; Store the lower 1 byte of rax to the output when it has enough + ; space. + test rcx, rcx + jz short .nextrow + mov BYTE [rdi], al +%else mov rax,rcx xor rcx, byte 0x0F shl rcx, 2 @@ -331,6 +364,7 @@ EXTN(jsimd_ycc_rgb_convert_sse2): por xmmE,xmmC .adj0: ; ---------------- maskmovdqu xmmA,xmmE ; movntdqu XMMWORD [rdi], xmmA +%endif ; STRICT_MEMORY_ACCESS ; --------------- %else ; RGB_PIXELSIZE == 4 ; ----------- @@ -415,6 +449,22 @@ EXTN(jsimd_ycc_rgb_convert_sse2): movdqa xmmA,xmmD sub rcx, byte SIZEOF_XMMWORD/4 .column_st15: +%ifdef STRICT_MEMORY_ACCESS + ; Store two pixels (8 bytes) of xmmA to the output when it has enough + ; space. + cmp rcx, byte SIZEOF_XMMWORD/8 + jb short .column_st7 + movq MMWORD [rdi], xmmA + add rdi, byte SIZEOF_XMMWORD/8*4 + sub rcx, byte SIZEOF_XMMWORD/8 + psrldq xmmA, SIZEOF_XMMWORD/8*4 +.column_st7: + ; Store one pixel (4 bytes) of xmmA to the output when it has enough + ; space. + test rcx, rcx + jz short .nextrow + movd DWORD [rdi], xmmA +%else cmp rcx, byte SIZEOF_XMMWORD/16 jb near .nextrow mov rax,rcx @@ -454,6 +504,7 @@ EXTN(jsimd_ycc_rgb_convert_sse2): por xmmE,xmmG .adj0: ; ---------------- maskmovdqu xmmA,xmmE ; movntdqu XMMWORD [rdi], xmmA +%endif ; STRICT_MEMORY_ACCESS ; --------------- %endif ; RGB_PIXELSIZE ; --------------- diff --git a/media/libjpeg/simd/jdclrss2.asm b/media/libjpeg/simd/jdclrss2.asm index 865fa8245d3..7f519e6ff30 100644 --- a/media/libjpeg/simd/jdclrss2.asm +++ b/media/libjpeg/simd/jdclrss2.asm @@ -19,8 +19,6 @@ %include "jcolsamp.inc" ; -------------------------------------------------------------------------- - SECTION SEG_TEXT - BITS 32 ; ; Convert some rows of samples to the output colorspace. ; @@ -304,6 +302,41 @@ EXTN(jsimd_ycc_rgb_convert_sse2): movdqa xmmA,xmmD sub ecx, byte SIZEOF_XMMWORD .column_st15: +%ifdef STRICT_MEMORY_ACCESS + ; Store the lower 8 bytes of xmmA to the output when it has enough + ; space. + cmp ecx, byte SIZEOF_MMWORD + jb short .column_st7 + movq MMWORD [edi], xmmA + add edi, byte SIZEOF_MMWORD + sub ecx, byte SIZEOF_MMWORD + psrldq xmmA, SIZEOF_MMWORD +.column_st7: + ; Store the lower 4 bytes of xmmA to the output when it has enough + ; space. + cmp ecx, byte SIZEOF_DWORD + jb short .column_st3 + movd DWORD [edi], xmmA + add edi, byte SIZEOF_DWORD + sub ecx, byte SIZEOF_DWORD + psrldq xmmA, SIZEOF_DWORD +.column_st3: + ; Store the lower 2 bytes of eax to the output when it has enough + ; space. + movd eax, xmmA + cmp ecx, byte SIZEOF_WORD + jb short .column_st1 + mov WORD [edi], ax + add edi, byte SIZEOF_WORD + sub ecx, byte SIZEOF_WORD + shr eax, 16 +.column_st1: + ; Store the lower 1 byte of eax to the output when it has enough + ; space. + test ecx, ecx + jz short .nextrow + mov BYTE [edi], al +%else mov eax,ecx xor ecx, byte 0x0F shl ecx, 2 @@ -343,6 +376,7 @@ EXTN(jsimd_ycc_rgb_convert_sse2): por xmmE,xmmC .adj0: ; ---------------- maskmovdqu xmmA,xmmE ; movntdqu XMMWORD [edi], xmmA +%endif ; STRICT_MEMORY_ACCESS ; --------------- %else ; RGB_PIXELSIZE == 4 ; ----------- @@ -428,6 +462,22 @@ EXTN(jsimd_ycc_rgb_convert_sse2): movdqa xmmA,xmmD sub ecx, byte SIZEOF_XMMWORD/4 .column_st15: +%ifdef STRICT_MEMORY_ACCESS + ; Store two pixels (8 bytes) of xmmA to the output when it has enough + ; space. + cmp ecx, byte SIZEOF_XMMWORD/8 + jb short .column_st7 + movq MMWORD [edi], xmmA + add edi, byte SIZEOF_XMMWORD/8*4 + sub ecx, byte SIZEOF_XMMWORD/8 + psrldq xmmA, SIZEOF_XMMWORD/8*4 +.column_st7: + ; Store one pixel (4 bytes) of xmmA to the output when it has enough + ; space. + test ecx, ecx + jz short .nextrow + movd DWORD [edi], xmmA +%else cmp ecx, byte SIZEOF_XMMWORD/16 jb short .nextrow mov eax,ecx @@ -467,6 +517,7 @@ EXTN(jsimd_ycc_rgb_convert_sse2): por xmmE,xmmG .adj0: ; ---------------- maskmovdqu xmmA,xmmE ; movntdqu XMMWORD [edi], xmmA +%endif ; STRICT_MEMORY_ACCESS ; --------------- %endif ; RGB_PIXELSIZE ; --------------- diff --git a/media/libjpeg/simd/jdcolmmx.asm b/media/libjpeg/simd/jdcolmmx.asm index 58775e8547d..5e4e47d425a 100644 --- a/media/libjpeg/simd/jdcolmmx.asm +++ b/media/libjpeg/simd/jdcolmmx.asm @@ -48,16 +48,19 @@ PD_ONEHALF times 2 dd 1 << (SCALEBITS-1) alignz 16 ; -------------------------------------------------------------------------- + SECTION SEG_TEXT + BITS 32 + %include "jdclrmmx.asm" %undef RGB_RED %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 0 -%define RGB_GREEN 1 -%define RGB_BLUE 2 -%define RGB_PIXELSIZE 3 +%define RGB_RED EXT_RGB_RED +%define RGB_GREEN EXT_RGB_GREEN +%define RGB_BLUE EXT_RGB_BLUE +%define RGB_PIXELSIZE EXT_RGB_PIXELSIZE %define jsimd_ycc_rgb_convert_mmx jsimd_ycc_extrgb_convert_mmx %include "jdclrmmx.asm" @@ -65,10 +68,10 @@ PD_ONEHALF times 2 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 0 -%define RGB_GREEN 1 -%define RGB_BLUE 2 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_RGBX_RED +%define RGB_GREEN EXT_RGBX_GREEN +%define RGB_BLUE EXT_RGBX_BLUE +%define RGB_PIXELSIZE EXT_RGBX_PIXELSIZE %define jsimd_ycc_rgb_convert_mmx jsimd_ycc_extrgbx_convert_mmx %include "jdclrmmx.asm" @@ -76,10 +79,10 @@ PD_ONEHALF times 2 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 2 -%define RGB_GREEN 1 -%define RGB_BLUE 0 -%define RGB_PIXELSIZE 3 +%define RGB_RED EXT_BGR_RED +%define RGB_GREEN EXT_BGR_GREEN +%define RGB_BLUE EXT_BGR_BLUE +%define RGB_PIXELSIZE EXT_BGR_PIXELSIZE %define jsimd_ycc_rgb_convert_mmx jsimd_ycc_extbgr_convert_mmx %include "jdclrmmx.asm" @@ -87,10 +90,10 @@ PD_ONEHALF times 2 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 2 -%define RGB_GREEN 1 -%define RGB_BLUE 0 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_BGRX_RED +%define RGB_GREEN EXT_BGRX_GREEN +%define RGB_BLUE EXT_BGRX_BLUE +%define RGB_PIXELSIZE EXT_BGRX_PIXELSIZE %define jsimd_ycc_rgb_convert_mmx jsimd_ycc_extbgrx_convert_mmx %include "jdclrmmx.asm" @@ -98,10 +101,10 @@ PD_ONEHALF times 2 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 3 -%define RGB_GREEN 2 -%define RGB_BLUE 1 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_XBGR_RED +%define RGB_GREEN EXT_XBGR_GREEN +%define RGB_BLUE EXT_XBGR_BLUE +%define RGB_PIXELSIZE EXT_XBGR_PIXELSIZE %define jsimd_ycc_rgb_convert_mmx jsimd_ycc_extxbgr_convert_mmx %include "jdclrmmx.asm" @@ -109,9 +112,9 @@ PD_ONEHALF times 2 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 1 -%define RGB_GREEN 2 -%define RGB_BLUE 3 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_XRGB_RED +%define RGB_GREEN EXT_XRGB_GREEN +%define RGB_BLUE EXT_XRGB_BLUE +%define RGB_PIXELSIZE EXT_XRGB_PIXELSIZE %define jsimd_ycc_rgb_convert_mmx jsimd_ycc_extxrgb_convert_mmx %include "jdclrmmx.asm" diff --git a/media/libjpeg/simd/jdcolss2-64.asm b/media/libjpeg/simd/jdcolss2-64.asm index 2e97d593083..01b3dce6933 100644 --- a/media/libjpeg/simd/jdcolss2-64.asm +++ b/media/libjpeg/simd/jdcolss2-64.asm @@ -48,16 +48,19 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) alignz 16 ; -------------------------------------------------------------------------- + SECTION SEG_TEXT + BITS 64 + %include "jdclrss2-64.asm" %undef RGB_RED %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 0 -%define RGB_GREEN 1 -%define RGB_BLUE 2 -%define RGB_PIXELSIZE 3 +%define RGB_RED EXT_RGB_RED +%define RGB_GREEN EXT_RGB_GREEN +%define RGB_BLUE EXT_RGB_BLUE +%define RGB_PIXELSIZE EXT_RGB_PIXELSIZE %define jsimd_ycc_rgb_convert_sse2 jsimd_ycc_extrgb_convert_sse2 %include "jdclrss2-64.asm" @@ -65,10 +68,10 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 0 -%define RGB_GREEN 1 -%define RGB_BLUE 2 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_RGBX_RED +%define RGB_GREEN EXT_RGBX_GREEN +%define RGB_BLUE EXT_RGBX_BLUE +%define RGB_PIXELSIZE EXT_RGBX_PIXELSIZE %define jsimd_ycc_rgb_convert_sse2 jsimd_ycc_extrgbx_convert_sse2 %include "jdclrss2-64.asm" @@ -76,10 +79,10 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 2 -%define RGB_GREEN 1 -%define RGB_BLUE 0 -%define RGB_PIXELSIZE 3 +%define RGB_RED EXT_BGR_RED +%define RGB_GREEN EXT_BGR_GREEN +%define RGB_BLUE EXT_BGR_BLUE +%define RGB_PIXELSIZE EXT_BGR_PIXELSIZE %define jsimd_ycc_rgb_convert_sse2 jsimd_ycc_extbgr_convert_sse2 %include "jdclrss2-64.asm" @@ -87,10 +90,10 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 2 -%define RGB_GREEN 1 -%define RGB_BLUE 0 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_BGRX_RED +%define RGB_GREEN EXT_BGRX_GREEN +%define RGB_BLUE EXT_BGRX_BLUE +%define RGB_PIXELSIZE EXT_BGRX_PIXELSIZE %define jsimd_ycc_rgb_convert_sse2 jsimd_ycc_extbgrx_convert_sse2 %include "jdclrss2-64.asm" @@ -98,10 +101,10 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 3 -%define RGB_GREEN 2 -%define RGB_BLUE 1 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_XBGR_RED +%define RGB_GREEN EXT_XBGR_GREEN +%define RGB_BLUE EXT_XBGR_BLUE +%define RGB_PIXELSIZE EXT_XBGR_PIXELSIZE %define jsimd_ycc_rgb_convert_sse2 jsimd_ycc_extxbgr_convert_sse2 %include "jdclrss2-64.asm" @@ -109,9 +112,9 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 1 -%define RGB_GREEN 2 -%define RGB_BLUE 3 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_XRGB_RED +%define RGB_GREEN EXT_XRGB_GREEN +%define RGB_BLUE EXT_XRGB_BLUE +%define RGB_PIXELSIZE EXT_XRGB_PIXELSIZE %define jsimd_ycc_rgb_convert_sse2 jsimd_ycc_extxrgb_convert_sse2 %include "jdclrss2-64.asm" diff --git a/media/libjpeg/simd/jdcolss2.asm b/media/libjpeg/simd/jdcolss2.asm index 7ae985d6a9a..1912d92e4ff 100644 --- a/media/libjpeg/simd/jdcolss2.asm +++ b/media/libjpeg/simd/jdcolss2.asm @@ -48,16 +48,19 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) alignz 16 ; -------------------------------------------------------------------------- + SECTION SEG_TEXT + BITS 32 + %include "jdclrss2.asm" %undef RGB_RED %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 0 -%define RGB_GREEN 1 -%define RGB_BLUE 2 -%define RGB_PIXELSIZE 3 +%define RGB_RED EXT_RGB_RED +%define RGB_GREEN EXT_RGB_GREEN +%define RGB_BLUE EXT_RGB_BLUE +%define RGB_PIXELSIZE EXT_RGB_PIXELSIZE %define jsimd_ycc_rgb_convert_sse2 jsimd_ycc_extrgb_convert_sse2 %include "jdclrss2.asm" @@ -65,10 +68,10 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 0 -%define RGB_GREEN 1 -%define RGB_BLUE 2 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_RGBX_RED +%define RGB_GREEN EXT_RGBX_GREEN +%define RGB_BLUE EXT_RGBX_BLUE +%define RGB_PIXELSIZE EXT_RGBX_PIXELSIZE %define jsimd_ycc_rgb_convert_sse2 jsimd_ycc_extrgbx_convert_sse2 %include "jdclrss2.asm" @@ -76,10 +79,10 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 2 -%define RGB_GREEN 1 -%define RGB_BLUE 0 -%define RGB_PIXELSIZE 3 +%define RGB_RED EXT_BGR_RED +%define RGB_GREEN EXT_BGR_GREEN +%define RGB_BLUE EXT_BGR_BLUE +%define RGB_PIXELSIZE EXT_BGR_PIXELSIZE %define jsimd_ycc_rgb_convert_sse2 jsimd_ycc_extbgr_convert_sse2 %include "jdclrss2.asm" @@ -87,10 +90,10 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 2 -%define RGB_GREEN 1 -%define RGB_BLUE 0 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_BGRX_RED +%define RGB_GREEN EXT_BGRX_GREEN +%define RGB_BLUE EXT_BGRX_BLUE +%define RGB_PIXELSIZE EXT_BGRX_PIXELSIZE %define jsimd_ycc_rgb_convert_sse2 jsimd_ycc_extbgrx_convert_sse2 %include "jdclrss2.asm" @@ -98,10 +101,10 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 3 -%define RGB_GREEN 2 -%define RGB_BLUE 1 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_XBGR_RED +%define RGB_GREEN EXT_XBGR_GREEN +%define RGB_BLUE EXT_XBGR_BLUE +%define RGB_PIXELSIZE EXT_XBGR_PIXELSIZE %define jsimd_ycc_rgb_convert_sse2 jsimd_ycc_extxbgr_convert_sse2 %include "jdclrss2.asm" @@ -109,9 +112,9 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 1 -%define RGB_GREEN 2 -%define RGB_BLUE 3 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_XRGB_RED +%define RGB_GREEN EXT_XRGB_GREEN +%define RGB_BLUE EXT_XRGB_BLUE +%define RGB_PIXELSIZE EXT_XRGB_PIXELSIZE %define jsimd_ycc_rgb_convert_sse2 jsimd_ycc_extxrgb_convert_sse2 %include "jdclrss2.asm" diff --git a/media/libjpeg/simd/jdmermmx.asm b/media/libjpeg/simd/jdmermmx.asm index fd587fbc124..7b86c7493ad 100644 --- a/media/libjpeg/simd/jdmermmx.asm +++ b/media/libjpeg/simd/jdmermmx.asm @@ -48,16 +48,19 @@ PD_ONEHALF times 2 dd 1 << (SCALEBITS-1) alignz 16 ; -------------------------------------------------------------------------- + SECTION SEG_TEXT + BITS 32 + %include "jdmrgmmx.asm" %undef RGB_RED %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 0 -%define RGB_GREEN 1 -%define RGB_BLUE 2 -%define RGB_PIXELSIZE 3 +%define RGB_RED EXT_RGB_RED +%define RGB_GREEN EXT_RGB_GREEN +%define RGB_BLUE EXT_RGB_BLUE +%define RGB_PIXELSIZE EXT_RGB_PIXELSIZE %define jsimd_h2v1_merged_upsample_mmx jsimd_h2v1_extrgb_merged_upsample_mmx %define jsimd_h2v2_merged_upsample_mmx jsimd_h2v2_extrgb_merged_upsample_mmx %include "jdmrgmmx.asm" @@ -66,10 +69,10 @@ PD_ONEHALF times 2 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 0 -%define RGB_GREEN 1 -%define RGB_BLUE 2 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_RGBX_RED +%define RGB_GREEN EXT_RGBX_GREEN +%define RGB_BLUE EXT_RGBX_BLUE +%define RGB_PIXELSIZE EXT_RGBX_PIXELSIZE %define jsimd_h2v1_merged_upsample_mmx jsimd_h2v1_extrgbx_merged_upsample_mmx %define jsimd_h2v2_merged_upsample_mmx jsimd_h2v2_extrgbx_merged_upsample_mmx %include "jdmrgmmx.asm" @@ -78,10 +81,10 @@ PD_ONEHALF times 2 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 2 -%define RGB_GREEN 1 -%define RGB_BLUE 0 -%define RGB_PIXELSIZE 3 +%define RGB_RED EXT_BGR_RED +%define RGB_GREEN EXT_BGR_GREEN +%define RGB_BLUE EXT_BGR_BLUE +%define RGB_PIXELSIZE EXT_BGR_PIXELSIZE %define jsimd_h2v1_merged_upsample_mmx jsimd_h2v1_extbgr_merged_upsample_mmx %define jsimd_h2v2_merged_upsample_mmx jsimd_h2v2_extbgr_merged_upsample_mmx %include "jdmrgmmx.asm" @@ -90,10 +93,10 @@ PD_ONEHALF times 2 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 2 -%define RGB_GREEN 1 -%define RGB_BLUE 0 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_BGRX_RED +%define RGB_GREEN EXT_BGRX_GREEN +%define RGB_BLUE EXT_BGRX_BLUE +%define RGB_PIXELSIZE EXT_BGRX_PIXELSIZE %define jsimd_h2v1_merged_upsample_mmx jsimd_h2v1_extbgrx_merged_upsample_mmx %define jsimd_h2v2_merged_upsample_mmx jsimd_h2v2_extbgrx_merged_upsample_mmx %include "jdmrgmmx.asm" @@ -102,10 +105,10 @@ PD_ONEHALF times 2 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 3 -%define RGB_GREEN 2 -%define RGB_BLUE 1 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_XBGR_RED +%define RGB_GREEN EXT_XBGR_GREEN +%define RGB_BLUE EXT_XBGR_BLUE +%define RGB_PIXELSIZE EXT_XBGR_PIXELSIZE %define jsimd_h2v1_merged_upsample_mmx jsimd_h2v1_extxbgr_merged_upsample_mmx %define jsimd_h2v2_merged_upsample_mmx jsimd_h2v2_extxbgr_merged_upsample_mmx %include "jdmrgmmx.asm" @@ -114,10 +117,10 @@ PD_ONEHALF times 2 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 1 -%define RGB_GREEN 2 -%define RGB_BLUE 3 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_XRGB_RED +%define RGB_GREEN EXT_XRGB_GREEN +%define RGB_BLUE EXT_XRGB_BLUE +%define RGB_PIXELSIZE EXT_XRGB_PIXELSIZE %define jsimd_h2v1_merged_upsample_mmx jsimd_h2v1_extxrgb_merged_upsample_mmx %define jsimd_h2v2_merged_upsample_mmx jsimd_h2v2_extxrgb_merged_upsample_mmx %include "jdmrgmmx.asm" diff --git a/media/libjpeg/simd/jdmerss2-64.asm b/media/libjpeg/simd/jdmerss2-64.asm index 1f0b10faac8..a184ea69b5e 100644 --- a/media/libjpeg/simd/jdmerss2-64.asm +++ b/media/libjpeg/simd/jdmerss2-64.asm @@ -48,16 +48,19 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) alignz 16 ; -------------------------------------------------------------------------- + SECTION SEG_TEXT + BITS 64 + %include "jdmrgss2-64.asm" %undef RGB_RED %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 0 -%define RGB_GREEN 1 -%define RGB_BLUE 2 -%define RGB_PIXELSIZE 3 +%define RGB_RED EXT_RGB_RED +%define RGB_GREEN EXT_RGB_GREEN +%define RGB_BLUE EXT_RGB_BLUE +%define RGB_PIXELSIZE EXT_RGB_PIXELSIZE %define jsimd_h2v1_merged_upsample_sse2 jsimd_h2v1_extrgb_merged_upsample_sse2 %define jsimd_h2v2_merged_upsample_sse2 jsimd_h2v2_extrgb_merged_upsample_sse2 %include "jdmrgss2-64.asm" @@ -66,10 +69,10 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 0 -%define RGB_GREEN 1 -%define RGB_BLUE 2 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_RGBX_RED +%define RGB_GREEN EXT_RGBX_GREEN +%define RGB_BLUE EXT_RGBX_BLUE +%define RGB_PIXELSIZE EXT_RGBX_PIXELSIZE %define jsimd_h2v1_merged_upsample_sse2 jsimd_h2v1_extrgbx_merged_upsample_sse2 %define jsimd_h2v2_merged_upsample_sse2 jsimd_h2v2_extrgbx_merged_upsample_sse2 %include "jdmrgss2-64.asm" @@ -78,10 +81,10 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 2 -%define RGB_GREEN 1 -%define RGB_BLUE 0 -%define RGB_PIXELSIZE 3 +%define RGB_RED EXT_BGR_RED +%define RGB_GREEN EXT_BGR_GREEN +%define RGB_BLUE EXT_BGR_BLUE +%define RGB_PIXELSIZE EXT_BGR_PIXELSIZE %define jsimd_h2v1_merged_upsample_sse2 jsimd_h2v1_extbgr_merged_upsample_sse2 %define jsimd_h2v2_merged_upsample_sse2 jsimd_h2v2_extbgr_merged_upsample_sse2 %include "jdmrgss2-64.asm" @@ -90,10 +93,10 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 2 -%define RGB_GREEN 1 -%define RGB_BLUE 0 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_BGRX_RED +%define RGB_GREEN EXT_BGRX_GREEN +%define RGB_BLUE EXT_BGRX_BLUE +%define RGB_PIXELSIZE EXT_BGRX_PIXELSIZE %define jsimd_h2v1_merged_upsample_sse2 jsimd_h2v1_extbgrx_merged_upsample_sse2 %define jsimd_h2v2_merged_upsample_sse2 jsimd_h2v2_extbgrx_merged_upsample_sse2 %include "jdmrgss2-64.asm" @@ -102,10 +105,10 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 3 -%define RGB_GREEN 2 -%define RGB_BLUE 1 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_XBGR_RED +%define RGB_GREEN EXT_XBGR_GREEN +%define RGB_BLUE EXT_XBGR_BLUE +%define RGB_PIXELSIZE EXT_XBGR_PIXELSIZE %define jsimd_h2v1_merged_upsample_sse2 jsimd_h2v1_extxbgr_merged_upsample_sse2 %define jsimd_h2v2_merged_upsample_sse2 jsimd_h2v2_extxbgr_merged_upsample_sse2 %include "jdmrgss2-64.asm" @@ -114,10 +117,10 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 1 -%define RGB_GREEN 2 -%define RGB_BLUE 3 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_XRGB_RED +%define RGB_GREEN EXT_XRGB_GREEN +%define RGB_BLUE EXT_XRGB_BLUE +%define RGB_PIXELSIZE EXT_XRGB_PIXELSIZE %define jsimd_h2v1_merged_upsample_sse2 jsimd_h2v1_extxrgb_merged_upsample_sse2 %define jsimd_h2v2_merged_upsample_sse2 jsimd_h2v2_extxrgb_merged_upsample_sse2 %include "jdmrgss2-64.asm" diff --git a/media/libjpeg/simd/jdmerss2.asm b/media/libjpeg/simd/jdmerss2.asm index 2294e0d3ef3..e536c802ea8 100644 --- a/media/libjpeg/simd/jdmerss2.asm +++ b/media/libjpeg/simd/jdmerss2.asm @@ -48,16 +48,19 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) alignz 16 ; -------------------------------------------------------------------------- + SECTION SEG_TEXT + BITS 32 + %include "jdmrgss2.asm" %undef RGB_RED %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 0 -%define RGB_GREEN 1 -%define RGB_BLUE 2 -%define RGB_PIXELSIZE 3 +%define RGB_RED EXT_RGB_RED +%define RGB_GREEN EXT_RGB_GREEN +%define RGB_BLUE EXT_RGB_BLUE +%define RGB_PIXELSIZE EXT_RGB_PIXELSIZE %define jsimd_h2v1_merged_upsample_sse2 jsimd_h2v1_extrgb_merged_upsample_sse2 %define jsimd_h2v2_merged_upsample_sse2 jsimd_h2v2_extrgb_merged_upsample_sse2 %include "jdmrgss2.asm" @@ -66,10 +69,10 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 0 -%define RGB_GREEN 1 -%define RGB_BLUE 2 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_RGBX_RED +%define RGB_GREEN EXT_RGBX_GREEN +%define RGB_BLUE EXT_RGBX_BLUE +%define RGB_PIXELSIZE EXT_RGBX_PIXELSIZE %define jsimd_h2v1_merged_upsample_sse2 jsimd_h2v1_extrgbx_merged_upsample_sse2 %define jsimd_h2v2_merged_upsample_sse2 jsimd_h2v2_extrgbx_merged_upsample_sse2 %include "jdmrgss2.asm" @@ -78,10 +81,10 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 2 -%define RGB_GREEN 1 -%define RGB_BLUE 0 -%define RGB_PIXELSIZE 3 +%define RGB_RED EXT_BGR_RED +%define RGB_GREEN EXT_BGR_GREEN +%define RGB_BLUE EXT_BGR_BLUE +%define RGB_PIXELSIZE EXT_BGR_PIXELSIZE %define jsimd_h2v1_merged_upsample_sse2 jsimd_h2v1_extbgr_merged_upsample_sse2 %define jsimd_h2v2_merged_upsample_sse2 jsimd_h2v2_extbgr_merged_upsample_sse2 %include "jdmrgss2.asm" @@ -90,10 +93,10 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 2 -%define RGB_GREEN 1 -%define RGB_BLUE 0 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_BGRX_RED +%define RGB_GREEN EXT_BGRX_GREEN +%define RGB_BLUE EXT_BGRX_BLUE +%define RGB_PIXELSIZE EXT_BGRX_PIXELSIZE %define jsimd_h2v1_merged_upsample_sse2 jsimd_h2v1_extbgrx_merged_upsample_sse2 %define jsimd_h2v2_merged_upsample_sse2 jsimd_h2v2_extbgrx_merged_upsample_sse2 %include "jdmrgss2.asm" @@ -102,10 +105,10 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 3 -%define RGB_GREEN 2 -%define RGB_BLUE 1 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_XBGR_RED +%define RGB_GREEN EXT_XBGR_GREEN +%define RGB_BLUE EXT_XBGR_BLUE +%define RGB_PIXELSIZE EXT_XBGR_PIXELSIZE %define jsimd_h2v1_merged_upsample_sse2 jsimd_h2v1_extxbgr_merged_upsample_sse2 %define jsimd_h2v2_merged_upsample_sse2 jsimd_h2v2_extxbgr_merged_upsample_sse2 %include "jdmrgss2.asm" @@ -114,10 +117,10 @@ PD_ONEHALF times 4 dd 1 << (SCALEBITS-1) %undef RGB_GREEN %undef RGB_BLUE %undef RGB_PIXELSIZE -%define RGB_RED 1 -%define RGB_GREEN 2 -%define RGB_BLUE 3 -%define RGB_PIXELSIZE 4 +%define RGB_RED EXT_XRGB_RED +%define RGB_GREEN EXT_XRGB_GREEN +%define RGB_BLUE EXT_XRGB_BLUE +%define RGB_PIXELSIZE EXT_XRGB_PIXELSIZE %define jsimd_h2v1_merged_upsample_sse2 jsimd_h2v1_extxrgb_merged_upsample_sse2 %define jsimd_h2v2_merged_upsample_sse2 jsimd_h2v2_extxrgb_merged_upsample_sse2 %include "jdmrgss2.asm" diff --git a/media/libjpeg/simd/jdmrgmmx.asm b/media/libjpeg/simd/jdmrgmmx.asm index b5777a3e165..d0800a737d7 100644 --- a/media/libjpeg/simd/jdmrgmmx.asm +++ b/media/libjpeg/simd/jdmrgmmx.asm @@ -19,8 +19,6 @@ %include "jcolsamp.inc" ; -------------------------------------------------------------------------- - SECTION SEG_TEXT - BITS 32 ; ; Upsample and color convert for the case of 2:1 horizontal and 1:1 vertical. ; diff --git a/media/libjpeg/simd/jdmrgss2-64.asm b/media/libjpeg/simd/jdmrgss2-64.asm index 121bb82bc55..a64a6b33fcd 100644 --- a/media/libjpeg/simd/jdmrgss2-64.asm +++ b/media/libjpeg/simd/jdmrgss2-64.asm @@ -20,8 +20,6 @@ %include "jcolsamp.inc" ; -------------------------------------------------------------------------- - SECTION SEG_TEXT - BITS 64 ; ; Upsample and color convert for the case of 2:1 horizontal and 1:1 vertical. ; @@ -296,6 +294,41 @@ EXTN(jsimd_h2v1_merged_upsample_sse2): movdqa xmmA,xmmD sub rcx, byte SIZEOF_XMMWORD .column_st15: +%ifdef STRICT_MEMORY_ACCESS + ; Store the lower 8 bytes of xmmA to the output when it has enough + ; space. + cmp rcx, byte SIZEOF_MMWORD + jb short .column_st7 + movq MMWORD [rdi], xmmA + add rdi, byte SIZEOF_MMWORD + sub rcx, byte SIZEOF_MMWORD + psrldq xmmA, SIZEOF_MMWORD +.column_st7: + ; Store the lower 4 bytes of xmmA to the output when it has enough + ; space. + cmp rcx, byte SIZEOF_DWORD + jb short .column_st3 + movd DWORD [rdi], xmmA + add rdi, byte SIZEOF_DWORD + sub rcx, byte SIZEOF_DWORD + psrldq xmmA, SIZEOF_DWORD +.column_st3: + ; Store the lower 2 bytes of rax to the output when it has enough + ; space. + movd eax, xmmA + cmp rcx, byte SIZEOF_WORD + jb short .column_st1 + mov WORD [rdi], ax + add rdi, byte SIZEOF_WORD + sub rcx, byte SIZEOF_WORD + shr rax, 16 +.column_st1: + ; Store the lower 1 byte of rax to the output when it has enough + ; space. + test rcx, rcx + jz short .endcolumn + mov BYTE [rdi], al +%else mov rax,rcx xor rcx, byte 0x0F shl rcx, 2 @@ -335,6 +368,7 @@ EXTN(jsimd_h2v1_merged_upsample_sse2): por xmmE,xmmC .adj0: ; ---------------- maskmovdqu xmmA,xmmE ; movntdqu XMMWORD [edi], xmmA +%endif ; STRICT_MEMORY_ACCESS ; --------------- %else ; RGB_PIXELSIZE == 4 ; ----------- @@ -422,6 +456,22 @@ EXTN(jsimd_h2v1_merged_upsample_sse2): movdqa xmmA,xmmD sub rcx, byte SIZEOF_XMMWORD/4 .column_st15: +%ifdef STRICT_MEMORY_ACCESS + ; Store two pixels (8 bytes) of xmmA to the output when it has enough + ; space. + cmp rcx, byte SIZEOF_XMMWORD/8 + jb short .column_st7 + movq MMWORD [rdi], xmmA + add rdi, byte SIZEOF_XMMWORD/8*4 + sub rcx, byte SIZEOF_XMMWORD/8 + psrldq xmmA, SIZEOF_XMMWORD/8*4 +.column_st7: + ; Store one pixel (4 bytes) of xmmA to the output when it has enough + ; space. + test rcx, rcx + jz short .endcolumn + movd DWORD [rdi], xmmA +%else cmp rcx, byte SIZEOF_XMMWORD/16 jb near .endcolumn mov rax,rcx @@ -461,6 +511,7 @@ EXTN(jsimd_h2v1_merged_upsample_sse2): por xmmE,xmmG .adj0: ; ---------------- maskmovdqu xmmA,xmmE ; movntdqu XMMWORD [edi], xmmA +%endif ; STRICT_MEMORY_ACCESS ; --------------- %endif ; RGB_PIXELSIZE ; --------------- diff --git a/media/libjpeg/simd/jdmrgss2.asm b/media/libjpeg/simd/jdmrgss2.asm index 99b7eb9f0f3..04089aa3c67 100644 --- a/media/libjpeg/simd/jdmrgss2.asm +++ b/media/libjpeg/simd/jdmrgss2.asm @@ -19,8 +19,6 @@ %include "jcolsamp.inc" ; -------------------------------------------------------------------------- - SECTION SEG_TEXT - BITS 32 ; ; Upsample and color convert for the case of 2:1 horizontal and 1:1 vertical. ; @@ -309,6 +307,41 @@ EXTN(jsimd_h2v1_merged_upsample_sse2): movdqa xmmA,xmmD sub ecx, byte SIZEOF_XMMWORD .column_st15: +%ifdef STRICT_MEMORY_ACCESS + ; Store the lower 8 bytes of xmmA to the output when it has enough + ; space. + cmp ecx, byte SIZEOF_MMWORD + jb short .column_st7 + movq MMWORD [edi], xmmA + add edi, byte SIZEOF_MMWORD + sub ecx, byte SIZEOF_MMWORD + psrldq xmmA, SIZEOF_MMWORD +.column_st7: + ; Store the lower 4 bytes of xmmA to the output when it has enough + ; space. + cmp ecx, byte SIZEOF_DWORD + jb short .column_st3 + movd DWORD [edi], xmmA + add edi, byte SIZEOF_DWORD + sub ecx, byte SIZEOF_DWORD + psrldq xmmA, SIZEOF_DWORD +.column_st3: + ; Store the lower 2 bytes of eax to the output when it has enough + ; space. + movd eax, xmmA + cmp ecx, byte SIZEOF_WORD + jb short .column_st1 + mov WORD [edi], ax + add edi, byte SIZEOF_WORD + sub ecx, byte SIZEOF_WORD + shr eax, 16 +.column_st1: + ; Store the lower 1 byte of eax to the output when it has enough + ; space. + test ecx, ecx + jz short .endcolumn + mov BYTE [edi], al +%else mov eax,ecx xor ecx, byte 0x0F shl ecx, 2 @@ -348,6 +381,7 @@ EXTN(jsimd_h2v1_merged_upsample_sse2): por xmmE,xmmC .adj0: ; ---------------- maskmovdqu xmmA,xmmE ; movntdqu XMMWORD [edi], xmmA +%endif ; STRICT_MEMORY_ACCESS ; --------------- %else ; RGB_PIXELSIZE == 4 ; ----------- @@ -436,6 +470,22 @@ EXTN(jsimd_h2v1_merged_upsample_sse2): movdqa xmmA,xmmD sub ecx, byte SIZEOF_XMMWORD/4 .column_st15: +%ifdef STRICT_MEMORY_ACCESS + ; Store two pixels (8 bytes) of xmmA to the output when it has enough + ; space. + cmp ecx, byte SIZEOF_XMMWORD/8 + jb short .column_st7 + movq MMWORD [edi], xmmA + add edi, byte SIZEOF_XMMWORD/2 + sub ecx, byte SIZEOF_XMMWORD/8 + psrldq xmmA, 64 +.column_st7: + ; Store one pixel (4 bytes) of xmmA to the output when it has enough + ; space. + test ecx, ecx + jz short .endcolumn + movd DWORD [edi], xmmA +%else cmp ecx, byte SIZEOF_XMMWORD/16 jb short .endcolumn mov eax,ecx @@ -475,6 +525,7 @@ EXTN(jsimd_h2v1_merged_upsample_sse2): por xmmE,xmmG .adj0: ; ---------------- maskmovdqu xmmA,xmmE ; movntdqu XMMWORD [edi], xmmA +%endif ; STRICT_MEMORY_ACCESS ; --------------- %endif ; RGB_PIXELSIZE ; --------------- diff --git a/media/libjpeg/simd/jsimd.h b/media/libjpeg/simd/jsimd.h index 89ac1b75ebf..6ee99cc6587 100644 --- a/media/libjpeg/simd/jsimd.h +++ b/media/libjpeg/simd/jsimd.h @@ -2,6 +2,7 @@ * simd/jsimd.h * * Copyright 2009 Pierre Ossman for Cendio AB + * Copyright 2011 D. R. Commander * * Based on the x86 SIMD extension for IJG JPEG library, * Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -11,11 +12,12 @@ /* Bitmask for supported acceleration methods */ -#define JSIMD_NONE 0x00 -#define JSIMD_MMX 0x01 -#define JSIMD_3DNOW 0x02 -#define JSIMD_SSE 0x04 -#define JSIMD_SSE2 0x08 +#define JSIMD_NONE 0x00 +#define JSIMD_MMX 0x01 +#define JSIMD_3DNOW 0x02 +#define JSIMD_SSE 0x04 +#define JSIMD_SSE2 0x08 +#define JSIMD_ARM_NEON 0x10 /* Short forms of external names for systems with brain-damaged linkers. */ @@ -28,6 +30,13 @@ #define jsimd_extbgrx_ycc_convert_mmx jSEXTBGRXYCCM #define jsimd_extxbgr_ycc_convert_mmx jSEXTXBGRYCCM #define jsimd_extxrgb_ycc_convert_mmx jSEXTXRGBYCCM +#define jsimd_rgb_gray_convert_mmx jSRGBGRYM +#define jsimd_extrgb_gray_convert_mmx jSEXTRGBGRYM +#define jsimd_extrgbx_gray_convert_mmx jSEXTRGBXGRYM +#define jsimd_extbgr_gray_convert_mmx jSEXTBGRGRYM +#define jsimd_extbgrx_gray_convert_mmx jSEXTBGRXGRYM +#define jsimd_extxbgr_gray_convert_mmx jSEXTXBGRGRYM +#define jsimd_extxrgb_gray_convert_mmx jSEXTXRGBGRYM #define jsimd_ycc_rgb_convert_mmx jSYCCRGBM #define jsimd_ycc_extrgb_convert_mmx jSYCCEXTRGBM #define jsimd_ycc_extrgbx_convert_mmx jSYCCEXTRGBXM @@ -43,6 +52,14 @@ #define jsimd_extbgrx_ycc_convert_sse2 jSEXTBGRXYCCS2 #define jsimd_extxbgr_ycc_convert_sse2 jSEXTXBGRYCCS2 #define jsimd_extxrgb_ycc_convert_sse2 jSEXTXRGBYCCS2 +#define jconst_rgb_gray_convert_sse2 jSCRGBGRYS2 +#define jsimd_rgb_gray_convert_sse2 jSRGBGRYS2 +#define jsimd_extrgb_gray_convert_sse2 jSEXTRGBGRYS2 +#define jsimd_extrgbx_gray_convert_sse2 jSEXTRGBXGRYS2 +#define jsimd_extbgr_gray_convert_sse2 jSEXTBGRGRYS2 +#define jsimd_extbgrx_gray_convert_sse2 jSEXTBGRXGRYS2 +#define jsimd_extxbgr_gray_convert_sse2 jSEXTXBGRGRYS2 +#define jsimd_extxrgb_gray_convert_sse2 jSEXTXRGBGRYS2 #define jconst_ycc_rgb_convert_sse2 jSCYCCRGBS2 #define jsimd_ycc_rgb_convert_sse2 jSYCCRGBS2 #define jsimd_ycc_extrgb_convert_sse2 jSYCCEXTRGBS2 @@ -163,6 +180,35 @@ EXTERN(void) jsimd_extxrgb_ycc_convert_mmx JSAMPARRAY input_buf, JSAMPIMAGE output_buf, JDIMENSION output_row, int num_rows)); +EXTERN(void) jsimd_rgb_gray_convert_mmx + JPP((JDIMENSION img_width, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +EXTERN(void) jsimd_extrgb_gray_convert_mmx + JPP((JDIMENSION img_width, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +EXTERN(void) jsimd_extrgbx_gray_convert_mmx + JPP((JDIMENSION img_width, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +EXTERN(void) jsimd_extbgr_gray_convert_mmx + JPP((JDIMENSION img_width, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +EXTERN(void) jsimd_extbgrx_gray_convert_mmx + JPP((JDIMENSION img_width, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +EXTERN(void) jsimd_extxbgr_gray_convert_mmx + JPP((JDIMENSION img_width, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +EXTERN(void) jsimd_extxrgb_gray_convert_mmx + JPP((JDIMENSION img_width, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); + EXTERN(void) jsimd_ycc_rgb_convert_mmx JPP((JDIMENSION out_width, JSAMPIMAGE input_buf, JDIMENSION input_row, @@ -222,6 +268,36 @@ EXTERN(void) jsimd_extxrgb_ycc_convert_sse2 JSAMPARRAY input_buf, JSAMPIMAGE output_buf, JDIMENSION output_row, int num_rows)); +extern const int jconst_rgb_gray_convert_sse2[]; +EXTERN(void) jsimd_rgb_gray_convert_sse2 + JPP((JDIMENSION img_width, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +EXTERN(void) jsimd_extrgb_gray_convert_sse2 + JPP((JDIMENSION img_width, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +EXTERN(void) jsimd_extrgbx_gray_convert_sse2 + JPP((JDIMENSION img_width, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +EXTERN(void) jsimd_extbgr_gray_convert_sse2 + JPP((JDIMENSION img_width, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +EXTERN(void) jsimd_extbgrx_gray_convert_sse2 + JPP((JDIMENSION img_width, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +EXTERN(void) jsimd_extxbgr_gray_convert_sse2 + JPP((JDIMENSION img_width, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +EXTERN(void) jsimd_extxrgb_gray_convert_sse2 + JPP((JDIMENSION img_width, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); + extern const int jconst_ycc_rgb_convert_sse2[]; EXTERN(void) jsimd_ycc_rgb_convert_sse2 JPP((JDIMENSION out_width, @@ -252,6 +328,64 @@ EXTERN(void) jsimd_ycc_extxrgb_convert_sse2 JSAMPIMAGE input_buf, JDIMENSION input_row, JSAMPARRAY output_buf, int num_rows)); +EXTERN(void) jsimd_rgb_ycc_convert_neon + JPP((JDIMENSION img_width, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +EXTERN(void) jsimd_extrgb_ycc_convert_neon + JPP((JDIMENSION img_width, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +EXTERN(void) jsimd_extrgbx_ycc_convert_neon + JPP((JDIMENSION img_width, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +EXTERN(void) jsimd_extbgr_ycc_convert_neon + JPP((JDIMENSION img_width, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +EXTERN(void) jsimd_extbgrx_ycc_convert_neon + JPP((JDIMENSION img_width, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +EXTERN(void) jsimd_extxbgr_ycc_convert_neon + JPP((JDIMENSION img_width, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +EXTERN(void) jsimd_extxrgb_ycc_convert_neon + JPP((JDIMENSION img_width, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); + +EXTERN(void) jsimd_ycc_rgb_convert_neon + JPP((JDIMENSION out_width, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows)); +EXTERN(void) jsimd_ycc_extrgb_convert_neon + JPP((JDIMENSION out_width, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows)); +EXTERN(void) jsimd_ycc_extrgbx_convert_neon + JPP((JDIMENSION out_width, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows)); +EXTERN(void) jsimd_ycc_extbgr_convert_neon + JPP((JDIMENSION out_width, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows)); +EXTERN(void) jsimd_ycc_extbgrx_convert_neon + JPP((JDIMENSION out_width, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows)); +EXTERN(void) jsimd_ycc_extxbgr_convert_neon + JPP((JDIMENSION out_width, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows)); +EXTERN(void) jsimd_ycc_extxrgb_convert_neon + JPP((JDIMENSION out_width, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows)); + /* SIMD Downsample */ EXTERN(void) jsimd_h2v2_downsample_mmx JPP((JDIMENSION image_width, int max_v_samp_factor, @@ -397,6 +531,10 @@ EXTERN(void) jsimd_convsamp_sse2 JPP((JSAMPARRAY sample_data, JDIMENSION start_col, DCTELEM * workspace)); +EXTERN(void) jsimd_convsamp_neon JPP((JSAMPARRAY sample_data, + JDIMENSION start_col, + DCTELEM * workspace)); + EXTERN(void) jsimd_convsamp_float_3dnow JPP((JSAMPARRAY sample_data, JDIMENSION start_col, FAST_FLOAT * workspace)); @@ -418,6 +556,8 @@ EXTERN(void) jsimd_fdct_islow_sse2 JPP((DCTELEM * data)); extern const int jconst_fdct_islow_sse2[]; EXTERN(void) jsimd_fdct_ifast_sse2 JPP((DCTELEM * data)); +EXTERN(void) jsimd_fdct_ifast_neon JPP((DCTELEM * data)); + EXTERN(void) jsimd_fdct_float_3dnow JPP((FAST_FLOAT * data)); extern const int jconst_fdct_float_sse[]; @@ -432,6 +572,10 @@ EXTERN(void) jsimd_quantize_sse2 JPP((JCOEFPTR coef_block, DCTELEM * divisors, DCTELEM * workspace)); +EXTERN(void) jsimd_quantize_neon JPP((JCOEFPTR coef_block, + DCTELEM * divisors, + DCTELEM * workspace)); + EXTERN(void) jsimd_quantize_float_3dnow JPP((JCOEFPTR coef_block, FAST_FLOAT * divisors, FAST_FLOAT * workspace)); @@ -464,6 +608,15 @@ EXTERN(void) jsimd_idct_4x4_sse2 JPP((void * dct_table, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jsimd_idct_2x2_neon JPP((void * dct_table, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, + JDIMENSION output_col)); +EXTERN(void) jsimd_idct_4x4_neon JPP((void * dct_table, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, + JDIMENSION output_col)); + /* SIMD Inverse DCT */ EXTERN(void) jsimd_idct_islow_mmx JPP((void * dct_table, JCOEFPTR coef_block, @@ -485,6 +638,15 @@ EXTERN(void) jsimd_idct_ifast_sse2 JPP((void * dct_table, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jsimd_idct_islow_neon JPP((void * dct_table, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, + JDIMENSION output_col)); +EXTERN(void) jsimd_idct_ifast_neon JPP((void * dct_table, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, + JDIMENSION output_col)); + EXTERN(void) jsimd_idct_float_3dnow JPP((void * dct_table, JCOEFPTR coef_block, JSAMPARRAY output_buf, diff --git a/media/libjpeg/simd/jsimd_arm.c b/media/libjpeg/simd/jsimd_arm.c new file mode 100644 index 00000000000..af0c2c8afb7 --- /dev/null +++ b/media/libjpeg/simd/jsimd_arm.c @@ -0,0 +1,670 @@ +/* + * jsimd_arm.c + * + * Copyright 2009 Pierre Ossman for Cendio AB + * Copyright 2009-2011 D. R. Commander + * + * Based on the x86 SIMD extension for IJG JPEG library, + * Copyright (C) 1999-2006, MIYASAKA Masaru. + * For conditions of distribution and use, see copyright notice in jsimdext.inc + * + * This file contains the interface between the "normal" portions + * of the library and the SIMD implementations when running on + * ARM architecture. + * + * Based on the stubs from 'jsimd_none.c' + */ + +#define JPEG_INTERNALS +#include "../jinclude.h" +#include "../jpeglib.h" +#include "../jsimd.h" +#include "../jdct.h" +#include "../jsimddct.h" +#include "jsimd.h" + +#include +#include +#include + +static unsigned int simd_support = ~0; + +#if defined(__linux__) || defined(ANDROID) || defined(__ANDROID__) + +#define SOMEWHAT_SANE_PROC_CPUINFO_SIZE_LIMIT (1024 * 1024) + +LOCAL(int) +check_feature (char *buffer, char *feature) +{ + char *p; + if (*feature == 0) + return 0; + if (strncmp(buffer, "Features", 8) != 0) + return 0; + buffer += 8; + while (isspace(*buffer)) + buffer++; + + /* Check if 'feature' is present in the buffer as a separate word */ + while ((p = strstr(buffer, feature))) { + if (p > buffer && !isspace(*(p - 1))) { + buffer++; + continue; + } + p += strlen(feature); + if (*p != 0 && !isspace(*p)) { + buffer++; + continue; + } + return 1; + } + return 0; +} + +LOCAL(int) +parse_proc_cpuinfo (int bufsize) +{ + char *buffer = (char *)malloc(bufsize); + FILE *fd; + simd_support = 0; + + if (!buffer) + return 0; + + fd = fopen("/proc/cpuinfo", "r"); + if (fd) { + while (fgets(buffer, bufsize, fd)) { + if (!strchr(buffer, '\n') && !feof(fd)) { + /* "impossible" happened - insufficient size of the buffer! */ + fclose(fd); + free(buffer); + return 0; + } + if (check_feature(buffer, "neon")) + simd_support |= JSIMD_ARM_NEON; + } + fclose(fd); + } + free(buffer); + return 1; +} + +#endif + +/* + * Check what SIMD accelerations are supported. + * + * FIXME: This code is racy under a multi-threaded environment. + */ +LOCAL(void) +init_simd (void) +{ + char *env = NULL; +#if !defined(__ARM_NEON__) && defined(__linux__) || defined(ANDROID) || defined(__ANDROID__) + int bufsize = 1024; /* an initial guess for the line buffer size limit */ +#endif + + if (simd_support != ~0U) + return; + + simd_support = 0; + +#if defined(__ARM_NEON__) + simd_support |= JSIMD_ARM_NEON; +#elif defined(__linux__) || defined(ANDROID) || defined(__ANDROID__) + /* We still have a chance to use NEON regardless of globally used + * -mcpu/-mfpu options passed to gcc by performing runtime detection via + * /proc/cpuinfo parsing on linux/android */ + while (!parse_proc_cpuinfo(bufsize)) { + bufsize *= 2; + if (bufsize > SOMEWHAT_SANE_PROC_CPUINFO_SIZE_LIMIT) + break; + } +#endif + + /* Force different settings through environment variables */ + env = getenv("JSIMD_FORCE_ARM_NEON"); + if ((env != NULL) && (strcmp(env, "1") == 0)) + simd_support &= JSIMD_ARM_NEON; + env = getenv("JSIMD_FORCE_NO_SIMD"); + if ((env != NULL) && (strcmp(env, "1") == 0)) + simd_support = 0; +} + +GLOBAL(int) +jsimd_can_rgb_ycc (void) +{ + init_simd(); + + /* The code is optimised for these values only */ + if (BITS_IN_JSAMPLE != 8) + return 0; + if (sizeof(JDIMENSION) != 4) + return 0; + if ((RGB_PIXELSIZE != 3) && (RGB_PIXELSIZE != 4)) + return 0; + + if (simd_support & JSIMD_ARM_NEON) + return 1; + + return 0; +} + +GLOBAL(int) +jsimd_can_rgb_gray (void) +{ + init_simd(); + + return 0; +} + +GLOBAL(int) +jsimd_can_ycc_rgb (void) +{ + init_simd(); + + /* The code is optimised for these values only */ + if (BITS_IN_JSAMPLE != 8) + return 0; + if (sizeof(JDIMENSION) != 4) + return 0; + if ((RGB_PIXELSIZE != 3) && (RGB_PIXELSIZE != 4)) + return 0; + if (simd_support & JSIMD_ARM_NEON) + return 1; + + return 0; +} + +GLOBAL(void) +jsimd_rgb_ycc_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + void (*neonfct)(JDIMENSION, JSAMPARRAY, JSAMPIMAGE, JDIMENSION, int); + + switch(cinfo->in_color_space) + { + case JCS_EXT_RGB: + neonfct=jsimd_extrgb_ycc_convert_neon; + break; + case JCS_EXT_RGBX: + case JCS_EXT_RGBA: + neonfct=jsimd_extrgbx_ycc_convert_neon; + break; + case JCS_EXT_BGR: + neonfct=jsimd_extbgr_ycc_convert_neon; + break; + case JCS_EXT_BGRX: + case JCS_EXT_BGRA: + neonfct=jsimd_extbgrx_ycc_convert_neon; + break; + case JCS_EXT_XBGR: + case JCS_EXT_ABGR: + neonfct=jsimd_extxbgr_ycc_convert_neon; + break; + case JCS_EXT_XRGB: + case JCS_EXT_ARGB: + neonfct=jsimd_extxrgb_ycc_convert_neon; + break; + default: + neonfct=jsimd_extrgb_ycc_convert_neon; + break; + } + + if (simd_support & JSIMD_ARM_NEON) + neonfct(cinfo->image_width, input_buf, + output_buf, output_row, num_rows); +} + +GLOBAL(void) +jsimd_rgb_gray_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ +} + +GLOBAL(void) +jsimd_ycc_rgb_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + void (*neonfct)(JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY, int); + + switch(cinfo->out_color_space) + { + case JCS_EXT_RGB: + neonfct=jsimd_ycc_extrgb_convert_neon; + break; + case JCS_EXT_RGBX: + case JCS_EXT_RGBA: + neonfct=jsimd_ycc_extrgbx_convert_neon; + break; + case JCS_EXT_BGR: + neonfct=jsimd_ycc_extbgr_convert_neon; + break; + case JCS_EXT_BGRX: + case JCS_EXT_BGRA: + neonfct=jsimd_ycc_extbgrx_convert_neon; + break; + case JCS_EXT_XBGR: + case JCS_EXT_ABGR: + neonfct=jsimd_ycc_extxbgr_convert_neon; + break; + case JCS_EXT_XRGB: + case JCS_EXT_ARGB: + neonfct=jsimd_ycc_extxrgb_convert_neon; + break; + default: + neonfct=jsimd_ycc_extrgb_convert_neon; + break; + } + + if (simd_support & JSIMD_ARM_NEON) + neonfct(cinfo->output_width, input_buf, + input_row, output_buf, num_rows); +} + +GLOBAL(int) +jsimd_can_h2v2_downsample (void) +{ + init_simd(); + + return 0; +} + +GLOBAL(int) +jsimd_can_h2v1_downsample (void) +{ + init_simd(); + + return 0; +} + +GLOBAL(void) +jsimd_h2v2_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ +} + +GLOBAL(void) +jsimd_h2v1_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ +} + +GLOBAL(int) +jsimd_can_h2v2_upsample (void) +{ + init_simd(); + + return 0; +} + +GLOBAL(int) +jsimd_can_h2v1_upsample (void) +{ + init_simd(); + + return 0; +} + +GLOBAL(void) +jsimd_h2v2_upsample (j_decompress_ptr cinfo, + jpeg_component_info * compptr, + JSAMPARRAY input_data, + JSAMPARRAY * output_data_ptr) +{ +} + +GLOBAL(void) +jsimd_h2v1_upsample (j_decompress_ptr cinfo, + jpeg_component_info * compptr, + JSAMPARRAY input_data, + JSAMPARRAY * output_data_ptr) +{ +} + +GLOBAL(int) +jsimd_can_h2v2_fancy_upsample (void) +{ + init_simd(); + + return 0; +} + +GLOBAL(int) +jsimd_can_h2v1_fancy_upsample (void) +{ + init_simd(); + + return 0; +} + +GLOBAL(void) +jsimd_h2v2_fancy_upsample (j_decompress_ptr cinfo, + jpeg_component_info * compptr, + JSAMPARRAY input_data, + JSAMPARRAY * output_data_ptr) +{ +} + +GLOBAL(void) +jsimd_h2v1_fancy_upsample (j_decompress_ptr cinfo, + jpeg_component_info * compptr, + JSAMPARRAY input_data, + JSAMPARRAY * output_data_ptr) +{ +} + +GLOBAL(int) +jsimd_can_h2v2_merged_upsample (void) +{ + init_simd(); + + return 0; +} + +GLOBAL(int) +jsimd_can_h2v1_merged_upsample (void) +{ + init_simd(); + + return 0; +} + +GLOBAL(void) +jsimd_h2v2_merged_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION in_row_group_ctr, + JSAMPARRAY output_buf) +{ +} + +GLOBAL(void) +jsimd_h2v1_merged_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION in_row_group_ctr, + JSAMPARRAY output_buf) +{ +} + +GLOBAL(int) +jsimd_can_convsamp (void) +{ + init_simd(); + + /* The code is optimised for these values only */ + if (DCTSIZE != 8) + return 0; + if (BITS_IN_JSAMPLE != 8) + return 0; + if (sizeof(JDIMENSION) != 4) + return 0; + if (sizeof(DCTELEM) != 2) + return 0; + + if (simd_support & JSIMD_ARM_NEON) + return 1; + + return 0; +} + +GLOBAL(int) +jsimd_can_convsamp_float (void) +{ + init_simd(); + + return 0; +} + +GLOBAL(void) +jsimd_convsamp (JSAMPARRAY sample_data, JDIMENSION start_col, + DCTELEM * workspace) +{ + if (simd_support & JSIMD_ARM_NEON) + jsimd_convsamp_neon(sample_data, start_col, workspace); +} + +GLOBAL(void) +jsimd_convsamp_float (JSAMPARRAY sample_data, JDIMENSION start_col, + FAST_FLOAT * workspace) +{ +} + +GLOBAL(int) +jsimd_can_fdct_islow (void) +{ + init_simd(); + + return 0; +} + +GLOBAL(int) +jsimd_can_fdct_ifast (void) +{ + init_simd(); + + /* The code is optimised for these values only */ + if (DCTSIZE != 8) + return 0; + if (sizeof(DCTELEM) != 2) + return 0; + + if (simd_support & JSIMD_ARM_NEON) + return 1; + + return 0; +} + +GLOBAL(int) +jsimd_can_fdct_float (void) +{ + init_simd(); + + return 0; +} + +GLOBAL(void) +jsimd_fdct_islow (DCTELEM * data) +{ +} + +GLOBAL(void) +jsimd_fdct_ifast (DCTELEM * data) +{ + if (simd_support & JSIMD_ARM_NEON) + jsimd_fdct_ifast_neon(data); +} + +GLOBAL(void) +jsimd_fdct_float (FAST_FLOAT * data) +{ +} + +GLOBAL(int) +jsimd_can_quantize (void) +{ + init_simd(); + + /* The code is optimised for these values only */ + if (DCTSIZE != 8) + return 0; + if (sizeof(JCOEF) != 2) + return 0; + if (sizeof(DCTELEM) != 2) + return 0; + + if (simd_support & JSIMD_ARM_NEON) + return 1; + + return 0; +} + +GLOBAL(int) +jsimd_can_quantize_float (void) +{ + init_simd(); + + return 0; +} + +GLOBAL(void) +jsimd_quantize (JCOEFPTR coef_block, DCTELEM * divisors, + DCTELEM * workspace) +{ + if (simd_support & JSIMD_ARM_NEON) + jsimd_quantize_neon(coef_block, divisors, workspace); +} + +GLOBAL(void) +jsimd_quantize_float (JCOEFPTR coef_block, FAST_FLOAT * divisors, + FAST_FLOAT * workspace) +{ +} + +GLOBAL(int) +jsimd_can_idct_2x2 (void) +{ + init_simd(); + + /* The code is optimised for these values only */ + if (DCTSIZE != 8) + return 0; + if (sizeof(JCOEF) != 2) + return 0; + if (BITS_IN_JSAMPLE != 8) + return 0; + if (sizeof(JDIMENSION) != 4) + return 0; + if (sizeof(ISLOW_MULT_TYPE) != 2) + return 0; + + if ((simd_support & JSIMD_ARM_NEON)) + return 1; + + return 0; +} + +GLOBAL(int) +jsimd_can_idct_4x4 (void) +{ + init_simd(); + + /* The code is optimised for these values only */ + if (DCTSIZE != 8) + return 0; + if (sizeof(JCOEF) != 2) + return 0; + if (BITS_IN_JSAMPLE != 8) + return 0; + if (sizeof(JDIMENSION) != 4) + return 0; + if (sizeof(ISLOW_MULT_TYPE) != 2) + return 0; + + if ((simd_support & JSIMD_ARM_NEON)) + return 1; + + return 0; +} + +GLOBAL(void) +jsimd_idct_2x2 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, + JDIMENSION output_col) +{ + if ((simd_support & JSIMD_ARM_NEON)) + jsimd_idct_2x2_neon(compptr->dct_table, coef_block, output_buf, output_col); +} + +GLOBAL(void) +jsimd_idct_4x4 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, + JDIMENSION output_col) +{ + if ((simd_support & JSIMD_ARM_NEON)) + jsimd_idct_4x4_neon(compptr->dct_table, coef_block, output_buf, output_col); +} + +GLOBAL(int) +jsimd_can_idct_islow (void) +{ + init_simd(); + + /* The code is optimised for these values only */ + if (DCTSIZE != 8) + return 0; + if (sizeof(JCOEF) != 2) + return 0; + if (BITS_IN_JSAMPLE != 8) + return 0; + if (sizeof(JDIMENSION) != 4) + return 0; + if (sizeof(ISLOW_MULT_TYPE) != 2) + return 0; + + if (simd_support & JSIMD_ARM_NEON) + return 1; + + return 0; +} + +GLOBAL(int) +jsimd_can_idct_ifast (void) +{ + init_simd(); + + /* The code is optimised for these values only */ + if (DCTSIZE != 8) + return 0; + if (sizeof(JCOEF) != 2) + return 0; + if (BITS_IN_JSAMPLE != 8) + return 0; + if (sizeof(JDIMENSION) != 4) + return 0; + if (sizeof(IFAST_MULT_TYPE) != 2) + return 0; + if (IFAST_SCALE_BITS != 2) + return 0; + + if ((simd_support & JSIMD_ARM_NEON)) + return 1; + + return 0; +} + +GLOBAL(int) +jsimd_can_idct_float (void) +{ + init_simd(); + + return 0; +} + +GLOBAL(void) +jsimd_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, + JDIMENSION output_col) +{ + if ((simd_support & JSIMD_ARM_NEON)) + jsimd_idct_islow_neon(compptr->dct_table, coef_block, output_buf, output_col); +} + +GLOBAL(void) +jsimd_idct_ifast (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, + JDIMENSION output_col) +{ + if ((simd_support & JSIMD_ARM_NEON)) + jsimd_idct_ifast_neon(compptr->dct_table, coef_block, output_buf, output_col); +} + +GLOBAL(void) +jsimd_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, + JDIMENSION output_col) +{ +} + diff --git a/media/libjpeg/simd/jsimd_arm_neon.S b/media/libjpeg/simd/jsimd_arm_neon.S new file mode 100644 index 00000000000..b2f9c2ae829 --- /dev/null +++ b/media/libjpeg/simd/jsimd_arm_neon.S @@ -0,0 +1,2159 @@ +/* + * ARM NEON optimizations for libjpeg-turbo + * + * Copyright (C) 2009-2011 Nokia Corporation and/or its subsidiary(-ies). + * All rights reserved. + * Author: Siarhei Siamashka + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits /* mark stack as non-executable */ +#endif + +.text +.fpu neon +.arch armv7a +.object_arch armv4 +.arm + + +#define RESPECT_STRICT_ALIGNMENT 1 + +/*****************************************************************************/ + +/* Supplementary macro for setting function attributes */ +.macro asm_function fname +#ifdef __APPLE__ + .func _\fname + .globl _\fname +_\fname: +#else + .func \fname + .global \fname +#ifdef __ELF__ + .hidden \fname + .type \fname, %function +#endif +\fname: +#endif +.endm + +/* Transpose a block of 4x4 coefficients in four 64-bit registers */ +.macro transpose_4x4 x0, x1, x2, x3 + vtrn.16 \x0, \x1 + vtrn.16 \x2, \x3 + vtrn.32 \x0, \x2 + vtrn.32 \x1, \x3 +.endm + +#define CENTERJSAMPLE 128 + +/*****************************************************************************/ + +/* + * Perform dequantization and inverse DCT on one block of coefficients. + * + * GLOBAL(void) + * jsimd_idct_islow_neon (void * dct_table, JCOEFPTR coef_block, + * JSAMPARRAY output_buf, JDIMENSION output_col) + */ + +#define FIX_0_298631336 (2446) +#define FIX_0_390180644 (3196) +#define FIX_0_541196100 (4433) +#define FIX_0_765366865 (6270) +#define FIX_0_899976223 (7373) +#define FIX_1_175875602 (9633) +#define FIX_1_501321110 (12299) +#define FIX_1_847759065 (15137) +#define FIX_1_961570560 (16069) +#define FIX_2_053119869 (16819) +#define FIX_2_562915447 (20995) +#define FIX_3_072711026 (25172) + +#define FIX_1_175875602_MINUS_1_961570560 (FIX_1_175875602 - FIX_1_961570560) +#define FIX_1_175875602_MINUS_0_390180644 (FIX_1_175875602 - FIX_0_390180644) +#define FIX_0_541196100_MINUS_1_847759065 (FIX_0_541196100 - FIX_1_847759065) +#define FIX_3_072711026_MINUS_2_562915447 (FIX_3_072711026 - FIX_2_562915447) +#define FIX_0_298631336_MINUS_0_899976223 (FIX_0_298631336 - FIX_0_899976223) +#define FIX_1_501321110_MINUS_0_899976223 (FIX_1_501321110 - FIX_0_899976223) +#define FIX_2_053119869_MINUS_2_562915447 (FIX_2_053119869 - FIX_2_562915447) +#define FIX_0_541196100_PLUS_0_765366865 (FIX_0_541196100 + FIX_0_765366865) + +/* + * Reference SIMD-friendly 1-D ISLOW iDCT C implementation. + * Uses some ideas from the comments in 'simd/jiss2int-64.asm' + */ +#define REF_1D_IDCT(xrow0, xrow1, xrow2, xrow3, xrow4, xrow5, xrow6, xrow7) \ +{ \ + DCTELEM row0, row1, row2, row3, row4, row5, row6, row7; \ + INT32 q1, q2, q3, q4, q5, q6, q7; \ + INT32 tmp11_plus_tmp2, tmp11_minus_tmp2; \ + \ + /* 1-D iDCT input data */ \ + row0 = xrow0; \ + row1 = xrow1; \ + row2 = xrow2; \ + row3 = xrow3; \ + row4 = xrow4; \ + row5 = xrow5; \ + row6 = xrow6; \ + row7 = xrow7; \ + \ + q5 = row7 + row3; \ + q4 = row5 + row1; \ + q6 = MULTIPLY(q5, FIX_1_175875602_MINUS_1_961570560) + \ + MULTIPLY(q4, FIX_1_175875602); \ + q7 = MULTIPLY(q5, FIX_1_175875602) + \ + MULTIPLY(q4, FIX_1_175875602_MINUS_0_390180644); \ + q2 = MULTIPLY(row2, FIX_0_541196100) + \ + MULTIPLY(row6, FIX_0_541196100_MINUS_1_847759065); \ + q4 = q6; \ + q3 = ((INT32) row0 - (INT32) row4) << 13; \ + q6 += MULTIPLY(row5, -FIX_2_562915447) + \ + MULTIPLY(row3, FIX_3_072711026_MINUS_2_562915447); \ + /* now we can use q1 (reloadable constants have been used up) */ \ + q1 = q3 + q2; \ + q4 += MULTIPLY(row7, FIX_0_298631336_MINUS_0_899976223) + \ + MULTIPLY(row1, -FIX_0_899976223); \ + q5 = q7; \ + q1 = q1 + q6; \ + q7 += MULTIPLY(row7, -FIX_0_899976223) + \ + MULTIPLY(row1, FIX_1_501321110_MINUS_0_899976223); \ + \ + /* (tmp11 + tmp2) has been calculated (out_row1 before descale) */ \ + tmp11_plus_tmp2 = q1; \ + row1 = 0; \ + \ + q1 = q1 - q6; \ + q5 += MULTIPLY(row5, FIX_2_053119869_MINUS_2_562915447) + \ + MULTIPLY(row3, -FIX_2_562915447); \ + q1 = q1 - q6; \ + q6 = MULTIPLY(row2, FIX_0_541196100_PLUS_0_765366865) + \ + MULTIPLY(row6, FIX_0_541196100); \ + q3 = q3 - q2; \ + \ + /* (tmp11 - tmp2) has been calculated (out_row6 before descale) */ \ + tmp11_minus_tmp2 = q1; \ + \ + q1 = ((INT32) row0 + (INT32) row4) << 13; \ + q2 = q1 + q6; \ + q1 = q1 - q6; \ + \ + /* pick up the results */ \ + tmp0 = q4; \ + tmp1 = q5; \ + tmp2 = (tmp11_plus_tmp2 - tmp11_minus_tmp2) / 2; \ + tmp3 = q7; \ + tmp10 = q2; \ + tmp11 = (tmp11_plus_tmp2 + tmp11_minus_tmp2) / 2; \ + tmp12 = q3; \ + tmp13 = q1; \ +} + +#define XFIX_0_899976223 d0[0] +#define XFIX_0_541196100 d0[1] +#define XFIX_2_562915447 d0[2] +#define XFIX_0_298631336_MINUS_0_899976223 d0[3] +#define XFIX_1_501321110_MINUS_0_899976223 d1[0] +#define XFIX_2_053119869_MINUS_2_562915447 d1[1] +#define XFIX_0_541196100_PLUS_0_765366865 d1[2] +#define XFIX_1_175875602 d1[3] +#define XFIX_1_175875602_MINUS_0_390180644 d2[0] +#define XFIX_0_541196100_MINUS_1_847759065 d2[1] +#define XFIX_3_072711026_MINUS_2_562915447 d2[2] +#define XFIX_1_175875602_MINUS_1_961570560 d2[3] + +.balign 16 +jsimd_idct_islow_neon_consts: + .short FIX_0_899976223 /* d0[0] */ + .short FIX_0_541196100 /* d0[1] */ + .short FIX_2_562915447 /* d0[2] */ + .short FIX_0_298631336_MINUS_0_899976223 /* d0[3] */ + .short FIX_1_501321110_MINUS_0_899976223 /* d1[0] */ + .short FIX_2_053119869_MINUS_2_562915447 /* d1[1] */ + .short FIX_0_541196100_PLUS_0_765366865 /* d1[2] */ + .short FIX_1_175875602 /* d1[3] */ + /* reloadable constants */ + .short FIX_1_175875602_MINUS_0_390180644 /* d2[0] */ + .short FIX_0_541196100_MINUS_1_847759065 /* d2[1] */ + .short FIX_3_072711026_MINUS_2_562915447 /* d2[2] */ + .short FIX_1_175875602_MINUS_1_961570560 /* d2[3] */ + +asm_function jsimd_idct_islow_neon + + DCT_TABLE .req r0 + COEF_BLOCK .req r1 + OUTPUT_BUF .req r2 + OUTPUT_COL .req r3 + TMP1 .req r0 + TMP2 .req r1 + TMP3 .req r2 + TMP4 .req ip + + ROW0L .req d16 + ROW0R .req d17 + ROW1L .req d18 + ROW1R .req d19 + ROW2L .req d20 + ROW2R .req d21 + ROW3L .req d22 + ROW3R .req d23 + ROW4L .req d24 + ROW4R .req d25 + ROW5L .req d26 + ROW5R .req d27 + ROW6L .req d28 + ROW6R .req d29 + ROW7L .req d30 + ROW7R .req d31 + + /* Load and dequantize coefficients into NEON registers + * with the following allocation: + * 0 1 2 3 | 4 5 6 7 + * ---------+-------- + * 0 | d16 | d17 ( q8 ) + * 1 | d18 | d19 ( q9 ) + * 2 | d20 | d21 ( q10 ) + * 3 | d22 | d23 ( q11 ) + * 4 | d24 | d25 ( q12 ) + * 5 | d26 | d27 ( q13 ) + * 6 | d28 | d29 ( q14 ) + * 7 | d30 | d31 ( q15 ) + */ + adr ip, jsimd_idct_islow_neon_consts + vld1.16 {d16, d17, d18, d19}, [COEF_BLOCK, :128]! + vld1.16 {d0, d1, d2, d3}, [DCT_TABLE, :128]! + vld1.16 {d20, d21, d22, d23}, [COEF_BLOCK, :128]! + vmul.s16 q8, q8, q0 + vld1.16 {d4, d5, d6, d7}, [DCT_TABLE, :128]! + vmul.s16 q9, q9, q1 + vld1.16 {d24, d25, d26, d27}, [COEF_BLOCK, :128]! + vmul.s16 q10, q10, q2 + vld1.16 {d0, d1, d2, d3}, [DCT_TABLE, :128]! + vmul.s16 q11, q11, q3 + vld1.16 {d28, d29, d30, d31}, [COEF_BLOCK, :128] + vmul.s16 q12, q12, q0 + vld1.16 {d4, d5, d6, d7}, [DCT_TABLE, :128]! + vmul.s16 q14, q14, q2 + vmul.s16 q13, q13, q1 + vld1.16 {d0, d1, d2, d3}, [ip, :128] /* load constants */ + add ip, ip, #16 + vmul.s16 q15, q15, q3 + vpush {d8-d15} /* save NEON registers */ + /* 1-D IDCT, pass 1, left 4x8 half */ + vadd.s16 d4, ROW7L, ROW3L + vadd.s16 d5, ROW5L, ROW1L + vmull.s16 q6, d4, XFIX_1_175875602_MINUS_1_961570560 + vmlal.s16 q6, d5, XFIX_1_175875602 + vmull.s16 q7, d4, XFIX_1_175875602 + /* Check for the zero coefficients in the right 4x8 half */ + push {r4, r5} + vmlal.s16 q7, d5, XFIX_1_175875602_MINUS_0_390180644 + vsubl.s16 q3, ROW0L, ROW4L + ldrd r4, [COEF_BLOCK, #(-96 + 2 * (4 + 1 * 8))] + vmull.s16 q2, ROW2L, XFIX_0_541196100 + vmlal.s16 q2, ROW6L, XFIX_0_541196100_MINUS_1_847759065 + orr r0, r4, r5 + vmov q4, q6 + vmlsl.s16 q6, ROW5L, XFIX_2_562915447 + ldrd r4, [COEF_BLOCK, #(-96 + 2 * (4 + 2 * 8))] + vmlal.s16 q6, ROW3L, XFIX_3_072711026_MINUS_2_562915447 + vshl.s32 q3, q3, #13 + orr r0, r0, r4 + vmlsl.s16 q4, ROW1L, XFIX_0_899976223 + orr r0, r0, r5 + vadd.s32 q1, q3, q2 + ldrd r4, [COEF_BLOCK, #(-96 + 2 * (4 + 3 * 8))] + vmov q5, q7 + vadd.s32 q1, q1, q6 + orr r0, r0, r4 + vmlsl.s16 q7, ROW7L, XFIX_0_899976223 + orr r0, r0, r5 + vmlal.s16 q7, ROW1L, XFIX_1_501321110_MINUS_0_899976223 + vrshrn.s32 ROW1L, q1, #11 + ldrd r4, [COEF_BLOCK, #(-96 + 2 * (4 + 4 * 8))] + vsub.s32 q1, q1, q6 + vmlal.s16 q5, ROW5L, XFIX_2_053119869_MINUS_2_562915447 + orr r0, r0, r4 + vmlsl.s16 q5, ROW3L, XFIX_2_562915447 + orr r0, r0, r5 + vsub.s32 q1, q1, q6 + vmull.s16 q6, ROW2L, XFIX_0_541196100_PLUS_0_765366865 + ldrd r4, [COEF_BLOCK, #(-96 + 2 * (4 + 5 * 8))] + vmlal.s16 q6, ROW6L, XFIX_0_541196100 + vsub.s32 q3, q3, q2 + orr r0, r0, r4 + vrshrn.s32 ROW6L, q1, #11 + orr r0, r0, r5 + vadd.s32 q1, q3, q5 + ldrd r4, [COEF_BLOCK, #(-96 + 2 * (4 + 6 * 8))] + vsub.s32 q3, q3, q5 + vaddl.s16 q5, ROW0L, ROW4L + orr r0, r0, r4 + vrshrn.s32 ROW2L, q1, #11 + orr r0, r0, r5 + vrshrn.s32 ROW5L, q3, #11 + ldrd r4, [COEF_BLOCK, #(-96 + 2 * (4 + 7 * 8))] + vshl.s32 q5, q5, #13 + vmlal.s16 q4, ROW7L, XFIX_0_298631336_MINUS_0_899976223 + orr r0, r0, r4 + vadd.s32 q2, q5, q6 + orrs r0, r0, r5 + vsub.s32 q1, q5, q6 + vadd.s32 q6, q2, q7 + ldrd r4, [COEF_BLOCK, #(-96 + 2 * (4 + 0 * 8))] + vsub.s32 q2, q2, q7 + vadd.s32 q5, q1, q4 + orr r0, r4, r5 + vsub.s32 q3, q1, q4 + pop {r4, r5} + vrshrn.s32 ROW7L, q2, #11 + vrshrn.s32 ROW3L, q5, #11 + vrshrn.s32 ROW0L, q6, #11 + vrshrn.s32 ROW4L, q3, #11 + + beq 3f /* Go to do some special handling for the sparse right 4x8 half */ + + /* 1-D IDCT, pass 1, right 4x8 half */ + vld1.s16 {d2}, [ip, :64] /* reload constants */ + vadd.s16 d10, ROW7R, ROW3R + vadd.s16 d8, ROW5R, ROW1R + /* Transpose left 4x8 half */ + vtrn.16 ROW6L, ROW7L + vmull.s16 q6, d10, XFIX_1_175875602_MINUS_1_961570560 + vmlal.s16 q6, d8, XFIX_1_175875602 + vtrn.16 ROW2L, ROW3L + vmull.s16 q7, d10, XFIX_1_175875602 + vmlal.s16 q7, d8, XFIX_1_175875602_MINUS_0_390180644 + vtrn.16 ROW0L, ROW1L + vsubl.s16 q3, ROW0R, ROW4R + vmull.s16 q2, ROW2R, XFIX_0_541196100 + vmlal.s16 q2, ROW6R, XFIX_0_541196100_MINUS_1_847759065 + vtrn.16 ROW4L, ROW5L + vmov q4, q6 + vmlsl.s16 q6, ROW5R, XFIX_2_562915447 + vmlal.s16 q6, ROW3R, XFIX_3_072711026_MINUS_2_562915447 + vtrn.32 ROW1L, ROW3L + vshl.s32 q3, q3, #13 + vmlsl.s16 q4, ROW1R, XFIX_0_899976223 + vtrn.32 ROW4L, ROW6L + vadd.s32 q1, q3, q2 + vmov q5, q7 + vadd.s32 q1, q1, q6 + vtrn.32 ROW0L, ROW2L + vmlsl.s16 q7, ROW7R, XFIX_0_899976223 + vmlal.s16 q7, ROW1R, XFIX_1_501321110_MINUS_0_899976223 + vrshrn.s32 ROW1R, q1, #11 + vtrn.32 ROW5L, ROW7L + vsub.s32 q1, q1, q6 + vmlal.s16 q5, ROW5R, XFIX_2_053119869_MINUS_2_562915447 + vmlsl.s16 q5, ROW3R, XFIX_2_562915447 + vsub.s32 q1, q1, q6 + vmull.s16 q6, ROW2R, XFIX_0_541196100_PLUS_0_765366865 + vmlal.s16 q6, ROW6R, XFIX_0_541196100 + vsub.s32 q3, q3, q2 + vrshrn.s32 ROW6R, q1, #11 + vadd.s32 q1, q3, q5 + vsub.s32 q3, q3, q5 + vaddl.s16 q5, ROW0R, ROW4R + vrshrn.s32 ROW2R, q1, #11 + vrshrn.s32 ROW5R, q3, #11 + vshl.s32 q5, q5, #13 + vmlal.s16 q4, ROW7R, XFIX_0_298631336_MINUS_0_899976223 + vadd.s32 q2, q5, q6 + vsub.s32 q1, q5, q6 + vadd.s32 q6, q2, q7 + vsub.s32 q2, q2, q7 + vadd.s32 q5, q1, q4 + vsub.s32 q3, q1, q4 + vrshrn.s32 ROW7R, q2, #11 + vrshrn.s32 ROW3R, q5, #11 + vrshrn.s32 ROW0R, q6, #11 + vrshrn.s32 ROW4R, q3, #11 + /* Transpose right 4x8 half */ + vtrn.16 ROW6R, ROW7R + vtrn.16 ROW2R, ROW3R + vtrn.16 ROW0R, ROW1R + vtrn.16 ROW4R, ROW5R + vtrn.32 ROW1R, ROW3R + vtrn.32 ROW4R, ROW6R + vtrn.32 ROW0R, ROW2R + vtrn.32 ROW5R, ROW7R + +1: /* 1-D IDCT, pass 2 (normal variant), left 4x8 half */ + vld1.s16 {d2}, [ip, :64] /* reload constants */ + vmull.s16 q6, ROW1R, XFIX_1_175875602 /* ROW5L <-> ROW1R */ + vmlal.s16 q6, ROW1L, XFIX_1_175875602 + vmlal.s16 q6, ROW3R, XFIX_1_175875602_MINUS_1_961570560 /* ROW7L <-> ROW3R */ + vmlal.s16 q6, ROW3L, XFIX_1_175875602_MINUS_1_961570560 + vmull.s16 q7, ROW3R, XFIX_1_175875602 /* ROW7L <-> ROW3R */ + vmlal.s16 q7, ROW3L, XFIX_1_175875602 + vmlal.s16 q7, ROW1R, XFIX_1_175875602_MINUS_0_390180644 /* ROW5L <-> ROW1R */ + vmlal.s16 q7, ROW1L, XFIX_1_175875602_MINUS_0_390180644 + vsubl.s16 q3, ROW0L, ROW0R /* ROW4L <-> ROW0R */ + vmull.s16 q2, ROW2L, XFIX_0_541196100 + vmlal.s16 q2, ROW2R, XFIX_0_541196100_MINUS_1_847759065 /* ROW6L <-> ROW2R */ + vmov q4, q6 + vmlsl.s16 q6, ROW1R, XFIX_2_562915447 /* ROW5L <-> ROW1R */ + vmlal.s16 q6, ROW3L, XFIX_3_072711026_MINUS_2_562915447 + vshl.s32 q3, q3, #13 + vmlsl.s16 q4, ROW1L, XFIX_0_899976223 + vadd.s32 q1, q3, q2 + vmov q5, q7 + vadd.s32 q1, q1, q6 + vmlsl.s16 q7, ROW3R, XFIX_0_899976223 /* ROW7L <-> ROW3R */ + vmlal.s16 q7, ROW1L, XFIX_1_501321110_MINUS_0_899976223 + vshrn.s32 ROW1L, q1, #16 + vsub.s32 q1, q1, q6 + vmlal.s16 q5, ROW1R, XFIX_2_053119869_MINUS_2_562915447 /* ROW5L <-> ROW1R */ + vmlsl.s16 q5, ROW3L, XFIX_2_562915447 + vsub.s32 q1, q1, q6 + vmull.s16 q6, ROW2L, XFIX_0_541196100_PLUS_0_765366865 + vmlal.s16 q6, ROW2R, XFIX_0_541196100 /* ROW6L <-> ROW2R */ + vsub.s32 q3, q3, q2 + vshrn.s32 ROW2R, q1, #16 /* ROW6L <-> ROW2R */ + vadd.s32 q1, q3, q5 + vsub.s32 q3, q3, q5 + vaddl.s16 q5, ROW0L, ROW0R /* ROW4L <-> ROW0R */ + vshrn.s32 ROW2L, q1, #16 + vshrn.s32 ROW1R, q3, #16 /* ROW5L <-> ROW1R */ + vshl.s32 q5, q5, #13 + vmlal.s16 q4, ROW3R, XFIX_0_298631336_MINUS_0_899976223 /* ROW7L <-> ROW3R */ + vadd.s32 q2, q5, q6 + vsub.s32 q1, q5, q6 + vadd.s32 q6, q2, q7 + vsub.s32 q2, q2, q7 + vadd.s32 q5, q1, q4 + vsub.s32 q3, q1, q4 + vshrn.s32 ROW3R, q2, #16 /* ROW7L <-> ROW3R */ + vshrn.s32 ROW3L, q5, #16 + vshrn.s32 ROW0L, q6, #16 + vshrn.s32 ROW0R, q3, #16 /* ROW4L <-> ROW0R */ + /* 1-D IDCT, pass 2, right 4x8 half */ + vld1.s16 {d2}, [ip, :64] /* reload constants */ + vmull.s16 q6, ROW5R, XFIX_1_175875602 + vmlal.s16 q6, ROW5L, XFIX_1_175875602 /* ROW5L <-> ROW1R */ + vmlal.s16 q6, ROW7R, XFIX_1_175875602_MINUS_1_961570560 + vmlal.s16 q6, ROW7L, XFIX_1_175875602_MINUS_1_961570560 /* ROW7L <-> ROW3R */ + vmull.s16 q7, ROW7R, XFIX_1_175875602 + vmlal.s16 q7, ROW7L, XFIX_1_175875602 /* ROW7L <-> ROW3R */ + vmlal.s16 q7, ROW5R, XFIX_1_175875602_MINUS_0_390180644 + vmlal.s16 q7, ROW5L, XFIX_1_175875602_MINUS_0_390180644 /* ROW5L <-> ROW1R */ + vsubl.s16 q3, ROW4L, ROW4R /* ROW4L <-> ROW0R */ + vmull.s16 q2, ROW6L, XFIX_0_541196100 /* ROW6L <-> ROW2R */ + vmlal.s16 q2, ROW6R, XFIX_0_541196100_MINUS_1_847759065 + vmov q4, q6 + vmlsl.s16 q6, ROW5R, XFIX_2_562915447 + vmlal.s16 q6, ROW7L, XFIX_3_072711026_MINUS_2_562915447 /* ROW7L <-> ROW3R */ + vshl.s32 q3, q3, #13 + vmlsl.s16 q4, ROW5L, XFIX_0_899976223 /* ROW5L <-> ROW1R */ + vadd.s32 q1, q3, q2 + vmov q5, q7 + vadd.s32 q1, q1, q6 + vmlsl.s16 q7, ROW7R, XFIX_0_899976223 + vmlal.s16 q7, ROW5L, XFIX_1_501321110_MINUS_0_899976223 /* ROW5L <-> ROW1R */ + vshrn.s32 ROW5L, q1, #16 /* ROW5L <-> ROW1R */ + vsub.s32 q1, q1, q6 + vmlal.s16 q5, ROW5R, XFIX_2_053119869_MINUS_2_562915447 + vmlsl.s16 q5, ROW7L, XFIX_2_562915447 /* ROW7L <-> ROW3R */ + vsub.s32 q1, q1, q6 + vmull.s16 q6, ROW6L, XFIX_0_541196100_PLUS_0_765366865 /* ROW6L <-> ROW2R */ + vmlal.s16 q6, ROW6R, XFIX_0_541196100 + vsub.s32 q3, q3, q2 + vshrn.s32 ROW6R, q1, #16 + vadd.s32 q1, q3, q5 + vsub.s32 q3, q3, q5 + vaddl.s16 q5, ROW4L, ROW4R /* ROW4L <-> ROW0R */ + vshrn.s32 ROW6L, q1, #16 /* ROW6L <-> ROW2R */ + vshrn.s32 ROW5R, q3, #16 + vshl.s32 q5, q5, #13 + vmlal.s16 q4, ROW7R, XFIX_0_298631336_MINUS_0_899976223 + vadd.s32 q2, q5, q6 + vsub.s32 q1, q5, q6 + vadd.s32 q6, q2, q7 + vsub.s32 q2, q2, q7 + vadd.s32 q5, q1, q4 + vsub.s32 q3, q1, q4 + vshrn.s32 ROW7R, q2, #16 + vshrn.s32 ROW7L, q5, #16 /* ROW7L <-> ROW3R */ + vshrn.s32 ROW4L, q6, #16 /* ROW4L <-> ROW0R */ + vshrn.s32 ROW4R, q3, #16 + +2: /* Descale to 8-bit and range limit */ + vqrshrn.s16 d16, q8, #2 + vqrshrn.s16 d17, q9, #2 + vqrshrn.s16 d18, q10, #2 + vqrshrn.s16 d19, q11, #2 + vpop {d8-d15} /* restore NEON registers */ + vqrshrn.s16 d20, q12, #2 + /* Transpose the final 8-bit samples and do signed->unsigned conversion */ + vtrn.16 q8, q9 + vqrshrn.s16 d21, q13, #2 + vqrshrn.s16 d22, q14, #2 + vmov.u8 q0, #(CENTERJSAMPLE) + vqrshrn.s16 d23, q15, #2 + vtrn.8 d16, d17 + vtrn.8 d18, d19 + vadd.u8 q8, q8, q0 + vadd.u8 q9, q9, q0 + vtrn.16 q10, q11 + /* Store results to the output buffer */ + ldmia OUTPUT_BUF!, {TMP1, TMP2} + add TMP1, TMP1, OUTPUT_COL + add TMP2, TMP2, OUTPUT_COL + vst1.8 {d16}, [TMP1] + vtrn.8 d20, d21 + vst1.8 {d17}, [TMP2] + ldmia OUTPUT_BUF!, {TMP1, TMP2} + add TMP1, TMP1, OUTPUT_COL + add TMP2, TMP2, OUTPUT_COL + vst1.8 {d18}, [TMP1] + vadd.u8 q10, q10, q0 + vst1.8 {d19}, [TMP2] + ldmia OUTPUT_BUF, {TMP1, TMP2, TMP3, TMP4} + add TMP1, TMP1, OUTPUT_COL + add TMP2, TMP2, OUTPUT_COL + add TMP3, TMP3, OUTPUT_COL + add TMP4, TMP4, OUTPUT_COL + vtrn.8 d22, d23 + vst1.8 {d20}, [TMP1] + vadd.u8 q11, q11, q0 + vst1.8 {d21}, [TMP2] + vst1.8 {d22}, [TMP3] + vst1.8 {d23}, [TMP4] + bx lr + +3: /* Left 4x8 half is done, right 4x8 half contains mostly zeros */ + + /* Transpose left 4x8 half */ + vtrn.16 ROW6L, ROW7L + vtrn.16 ROW2L, ROW3L + vtrn.16 ROW0L, ROW1L + vtrn.16 ROW4L, ROW5L + vshl.s16 ROW0R, ROW0R, #2 /* PASS1_BITS */ + vtrn.32 ROW1L, ROW3L + vtrn.32 ROW4L, ROW6L + vtrn.32 ROW0L, ROW2L + vtrn.32 ROW5L, ROW7L + + cmp r0, #0 + beq 4f /* Right 4x8 half has all zeros, go to 'sparse' second pass */ + + /* Only row 0 is non-zero for the right 4x8 half */ + vdup.s16 ROW1R, ROW0R[1] + vdup.s16 ROW2R, ROW0R[2] + vdup.s16 ROW3R, ROW0R[3] + vdup.s16 ROW4R, ROW0R[0] + vdup.s16 ROW5R, ROW0R[1] + vdup.s16 ROW6R, ROW0R[2] + vdup.s16 ROW7R, ROW0R[3] + vdup.s16 ROW0R, ROW0R[0] + b 1b /* Go to 'normal' second pass */ + +4: /* 1-D IDCT, pass 2 (sparse variant with zero rows 4-7), left 4x8 half */ + vld1.s16 {d2}, [ip, :64] /* reload constants */ + vmull.s16 q6, ROW1L, XFIX_1_175875602 + vmlal.s16 q6, ROW3L, XFIX_1_175875602_MINUS_1_961570560 + vmull.s16 q7, ROW3L, XFIX_1_175875602 + vmlal.s16 q7, ROW1L, XFIX_1_175875602_MINUS_0_390180644 + vmull.s16 q2, ROW2L, XFIX_0_541196100 + vshll.s16 q3, ROW0L, #13 + vmov q4, q6 + vmlal.s16 q6, ROW3L, XFIX_3_072711026_MINUS_2_562915447 + vmlsl.s16 q4, ROW1L, XFIX_0_899976223 + vadd.s32 q1, q3, q2 + vmov q5, q7 + vmlal.s16 q7, ROW1L, XFIX_1_501321110_MINUS_0_899976223 + vadd.s32 q1, q1, q6 + vadd.s32 q6, q6, q6 + vmlsl.s16 q5, ROW3L, XFIX_2_562915447 + vshrn.s32 ROW1L, q1, #16 + vsub.s32 q1, q1, q6 + vmull.s16 q6, ROW2L, XFIX_0_541196100_PLUS_0_765366865 + vsub.s32 q3, q3, q2 + vshrn.s32 ROW2R, q1, #16 /* ROW6L <-> ROW2R */ + vadd.s32 q1, q3, q5 + vsub.s32 q3, q3, q5 + vshll.s16 q5, ROW0L, #13 + vshrn.s32 ROW2L, q1, #16 + vshrn.s32 ROW1R, q3, #16 /* ROW5L <-> ROW1R */ + vadd.s32 q2, q5, q6 + vsub.s32 q1, q5, q6 + vadd.s32 q6, q2, q7 + vsub.s32 q2, q2, q7 + vadd.s32 q5, q1, q4 + vsub.s32 q3, q1, q4 + vshrn.s32 ROW3R, q2, #16 /* ROW7L <-> ROW3R */ + vshrn.s32 ROW3L, q5, #16 + vshrn.s32 ROW0L, q6, #16 + vshrn.s32 ROW0R, q3, #16 /* ROW4L <-> ROW0R */ + /* 1-D IDCT, pass 2 (sparse variant with zero rows 4-7), right 4x8 half */ + vld1.s16 {d2}, [ip, :64] /* reload constants */ + vmull.s16 q6, ROW5L, XFIX_1_175875602 + vmlal.s16 q6, ROW7L, XFIX_1_175875602_MINUS_1_961570560 + vmull.s16 q7, ROW7L, XFIX_1_175875602 + vmlal.s16 q7, ROW5L, XFIX_1_175875602_MINUS_0_390180644 + vmull.s16 q2, ROW6L, XFIX_0_541196100 + vshll.s16 q3, ROW4L, #13 + vmov q4, q6 + vmlal.s16 q6, ROW7L, XFIX_3_072711026_MINUS_2_562915447 + vmlsl.s16 q4, ROW5L, XFIX_0_899976223 + vadd.s32 q1, q3, q2 + vmov q5, q7 + vmlal.s16 q7, ROW5L, XFIX_1_501321110_MINUS_0_899976223 + vadd.s32 q1, q1, q6 + vadd.s32 q6, q6, q6 + vmlsl.s16 q5, ROW7L, XFIX_2_562915447 + vshrn.s32 ROW5L, q1, #16 /* ROW5L <-> ROW1R */ + vsub.s32 q1, q1, q6 + vmull.s16 q6, ROW6L, XFIX_0_541196100_PLUS_0_765366865 + vsub.s32 q3, q3, q2 + vshrn.s32 ROW6R, q1, #16 + vadd.s32 q1, q3, q5 + vsub.s32 q3, q3, q5 + vshll.s16 q5, ROW4L, #13 + vshrn.s32 ROW6L, q1, #16 /* ROW6L <-> ROW2R */ + vshrn.s32 ROW5R, q3, #16 + vadd.s32 q2, q5, q6 + vsub.s32 q1, q5, q6 + vadd.s32 q6, q2, q7 + vsub.s32 q2, q2, q7 + vadd.s32 q5, q1, q4 + vsub.s32 q3, q1, q4 + vshrn.s32 ROW7R, q2, #16 + vshrn.s32 ROW7L, q5, #16 /* ROW7L <-> ROW3R */ + vshrn.s32 ROW4L, q6, #16 /* ROW4L <-> ROW0R */ + vshrn.s32 ROW4R, q3, #16 + b 2b /* Go to epilogue */ + + .unreq DCT_TABLE + .unreq COEF_BLOCK + .unreq OUTPUT_BUF + .unreq OUTPUT_COL + .unreq TMP1 + .unreq TMP2 + .unreq TMP3 + .unreq TMP4 + + .unreq ROW0L + .unreq ROW0R + .unreq ROW1L + .unreq ROW1R + .unreq ROW2L + .unreq ROW2R + .unreq ROW3L + .unreq ROW3R + .unreq ROW4L + .unreq ROW4R + .unreq ROW5L + .unreq ROW5R + .unreq ROW6L + .unreq ROW6R + .unreq ROW7L + .unreq ROW7R +.endfunc + +/*****************************************************************************/ + +/* + * jsimd_idct_ifast_neon + * + * This function contains a fast, not so accurate integer implementation of + * the inverse DCT (Discrete Cosine Transform). It uses the same calculations + * and produces exactly the same output as IJG's original 'jpeg_idct_ifast' + * function from jidctfst.c + * + * Normally 1-D AAN DCT needs 5 multiplications and 29 additions. + * But in ARM NEON case some extra additions are required because VQDMULH + * instruction can't handle the constants larger than 1. So the expressions + * like "x * 1.082392200" have to be converted to "x * 0.082392200 + x", + * which introduces an extra addition. Overall, there are 6 extra additions + * per 1-D IDCT pass, totalling to 5 VQDMULH and 35 VADD/VSUB instructions. + */ + +#define XFIX_1_082392200 d0[0] +#define XFIX_1_414213562 d0[1] +#define XFIX_1_847759065 d0[2] +#define XFIX_2_613125930 d0[3] + +.balign 16 +jsimd_idct_ifast_neon_consts: + .short (277 * 128 - 256 * 128) /* XFIX_1_082392200 */ + .short (362 * 128 - 256 * 128) /* XFIX_1_414213562 */ + .short (473 * 128 - 256 * 128) /* XFIX_1_847759065 */ + .short (669 * 128 - 512 * 128) /* XFIX_2_613125930 */ + +asm_function jsimd_idct_ifast_neon + + DCT_TABLE .req r0 + COEF_BLOCK .req r1 + OUTPUT_BUF .req r2 + OUTPUT_COL .req r3 + TMP1 .req r0 + TMP2 .req r1 + TMP3 .req r2 + TMP4 .req ip + + /* Load and dequantize coefficients into NEON registers + * with the following allocation: + * 0 1 2 3 | 4 5 6 7 + * ---------+-------- + * 0 | d16 | d17 ( q8 ) + * 1 | d18 | d19 ( q9 ) + * 2 | d20 | d21 ( q10 ) + * 3 | d22 | d23 ( q11 ) + * 4 | d24 | d25 ( q12 ) + * 5 | d26 | d27 ( q13 ) + * 6 | d28 | d29 ( q14 ) + * 7 | d30 | d31 ( q15 ) + */ + adr ip, jsimd_idct_ifast_neon_consts + vld1.16 {d16, d17, d18, d19}, [COEF_BLOCK, :128]! + vld1.16 {d0, d1, d2, d3}, [DCT_TABLE, :128]! + vld1.16 {d20, d21, d22, d23}, [COEF_BLOCK, :128]! + vmul.s16 q8, q8, q0 + vld1.16 {d4, d5, d6, d7}, [DCT_TABLE, :128]! + vmul.s16 q9, q9, q1 + vld1.16 {d24, d25, d26, d27}, [COEF_BLOCK, :128]! + vmul.s16 q10, q10, q2 + vld1.16 {d0, d1, d2, d3}, [DCT_TABLE, :128]! + vmul.s16 q11, q11, q3 + vld1.16 {d28, d29, d30, d31}, [COEF_BLOCK, :128] + vmul.s16 q12, q12, q0 + vld1.16 {d4, d5, d6, d7}, [DCT_TABLE, :128]! + vmul.s16 q14, q14, q2 + vmul.s16 q13, q13, q1 + vld1.16 {d0}, [ip, :64] /* load constants */ + vmul.s16 q15, q15, q3 + vpush {d8-d13} /* save NEON registers */ + /* 1-D IDCT, pass 1 */ + vsub.s16 q2, q10, q14 + vadd.s16 q14, q10, q14 + vsub.s16 q1, q11, q13 + vadd.s16 q13, q11, q13 + vsub.s16 q5, q9, q15 + vadd.s16 q15, q9, q15 + vqdmulh.s16 q4, q2, XFIX_1_414213562 + vqdmulh.s16 q6, q1, XFIX_2_613125930 + vadd.s16 q3, q1, q1 + vsub.s16 q1, q5, q1 + vadd.s16 q10, q2, q4 + vqdmulh.s16 q4, q1, XFIX_1_847759065 + vsub.s16 q2, q15, q13 + vadd.s16 q3, q3, q6 + vqdmulh.s16 q6, q2, XFIX_1_414213562 + vadd.s16 q1, q1, q4 + vqdmulh.s16 q4, q5, XFIX_1_082392200 + vsub.s16 q10, q10, q14 + vadd.s16 q2, q2, q6 + vsub.s16 q6, q8, q12 + vadd.s16 q12, q8, q12 + vadd.s16 q9, q5, q4 + vadd.s16 q5, q6, q10 + vsub.s16 q10, q6, q10 + vadd.s16 q6, q15, q13 + vadd.s16 q8, q12, q14 + vsub.s16 q3, q6, q3 + vsub.s16 q12, q12, q14 + vsub.s16 q3, q3, q1 + vsub.s16 q1, q9, q1 + vadd.s16 q2, q3, q2 + vsub.s16 q15, q8, q6 + vadd.s16 q1, q1, q2 + vadd.s16 q8, q8, q6 + vadd.s16 q14, q5, q3 + vsub.s16 q9, q5, q3 + vsub.s16 q13, q10, q2 + vadd.s16 q10, q10, q2 + /* Transpose */ + vtrn.16 q8, q9 + vsub.s16 q11, q12, q1 + vtrn.16 q14, q15 + vadd.s16 q12, q12, q1 + vtrn.16 q10, q11 + vtrn.16 q12, q13 + vtrn.32 q9, q11 + vtrn.32 q12, q14 + vtrn.32 q8, q10 + vtrn.32 q13, q15 + vswp d28, d21 + vswp d26, d19 + /* 1-D IDCT, pass 2 */ + vsub.s16 q2, q10, q14 + vswp d30, d23 + vadd.s16 q14, q10, q14 + vswp d24, d17 + vsub.s16 q1, q11, q13 + vadd.s16 q13, q11, q13 + vsub.s16 q5, q9, q15 + vadd.s16 q15, q9, q15 + vqdmulh.s16 q4, q2, XFIX_1_414213562 + vqdmulh.s16 q6, q1, XFIX_2_613125930 + vadd.s16 q3, q1, q1 + vsub.s16 q1, q5, q1 + vadd.s16 q10, q2, q4 + vqdmulh.s16 q4, q1, XFIX_1_847759065 + vsub.s16 q2, q15, q13 + vadd.s16 q3, q3, q6 + vqdmulh.s16 q6, q2, XFIX_1_414213562 + vadd.s16 q1, q1, q4 + vqdmulh.s16 q4, q5, XFIX_1_082392200 + vsub.s16 q10, q10, q14 + vadd.s16 q2, q2, q6 + vsub.s16 q6, q8, q12 + vadd.s16 q12, q8, q12 + vadd.s16 q9, q5, q4 + vadd.s16 q5, q6, q10 + vsub.s16 q10, q6, q10 + vadd.s16 q6, q15, q13 + vadd.s16 q8, q12, q14 + vsub.s16 q3, q6, q3 + vsub.s16 q12, q12, q14 + vsub.s16 q3, q3, q1 + vsub.s16 q1, q9, q1 + vadd.s16 q2, q3, q2 + vsub.s16 q15, q8, q6 + vadd.s16 q1, q1, q2 + vadd.s16 q8, q8, q6 + vadd.s16 q14, q5, q3 + vsub.s16 q9, q5, q3 + vsub.s16 q13, q10, q2 + vpop {d8-d13} /* restore NEON registers */ + vadd.s16 q10, q10, q2 + vsub.s16 q11, q12, q1 + vadd.s16 q12, q12, q1 + /* Descale to 8-bit and range limit */ + vmov.u8 q0, #0x80 + vqshrn.s16 d16, q8, #5 + vqshrn.s16 d17, q9, #5 + vqshrn.s16 d18, q10, #5 + vqshrn.s16 d19, q11, #5 + vqshrn.s16 d20, q12, #5 + vqshrn.s16 d21, q13, #5 + vqshrn.s16 d22, q14, #5 + vqshrn.s16 d23, q15, #5 + vadd.u8 q8, q8, q0 + vadd.u8 q9, q9, q0 + vadd.u8 q10, q10, q0 + vadd.u8 q11, q11, q0 + /* Transpose the final 8-bit samples */ + vtrn.16 q8, q9 + vtrn.16 q10, q11 + vtrn.32 q8, q10 + vtrn.32 q9, q11 + vtrn.8 d16, d17 + vtrn.8 d18, d19 + /* Store results to the output buffer */ + ldmia OUTPUT_BUF!, {TMP1, TMP2} + add TMP1, TMP1, OUTPUT_COL + add TMP2, TMP2, OUTPUT_COL + vst1.8 {d16}, [TMP1] + vst1.8 {d17}, [TMP2] + ldmia OUTPUT_BUF!, {TMP1, TMP2} + add TMP1, TMP1, OUTPUT_COL + add TMP2, TMP2, OUTPUT_COL + vst1.8 {d18}, [TMP1] + vtrn.8 d20, d21 + vst1.8 {d19}, [TMP2] + ldmia OUTPUT_BUF, {TMP1, TMP2, TMP3, TMP4} + add TMP1, TMP1, OUTPUT_COL + add TMP2, TMP2, OUTPUT_COL + add TMP3, TMP3, OUTPUT_COL + add TMP4, TMP4, OUTPUT_COL + vst1.8 {d20}, [TMP1] + vtrn.8 d22, d23 + vst1.8 {d21}, [TMP2] + vst1.8 {d22}, [TMP3] + vst1.8 {d23}, [TMP4] + bx lr + + .unreq DCT_TABLE + .unreq COEF_BLOCK + .unreq OUTPUT_BUF + .unreq OUTPUT_COL + .unreq TMP1 + .unreq TMP2 + .unreq TMP3 + .unreq TMP4 +.endfunc + +/*****************************************************************************/ + +/* + * jsimd_idct_4x4_neon + * + * This function contains inverse-DCT code for getting reduced-size + * 4x4 pixels output from an 8x8 DCT block. It uses the same calculations + * and produces exactly the same output as IJG's original 'jpeg_idct_4x4' + * function from jpeg-6b (jidctred.c). + * + * NOTE: jpeg-8 has an improved implementation of 4x4 inverse-DCT, which + * requires much less arithmetic operations and hence should be faster. + * The primary purpose of this particular NEON optimized function is + * bit exact compatibility with jpeg-6b. + * + * TODO: a bit better instructions scheduling can be achieved by expanding + * idct_helper/transpose_4x4 macros and reordering instructions, + * but readability will suffer somewhat. + */ + +#define CONST_BITS 13 + +#define FIX_0_211164243 (1730) /* FIX(0.211164243) */ +#define FIX_0_509795579 (4176) /* FIX(0.509795579) */ +#define FIX_0_601344887 (4926) /* FIX(0.601344887) */ +#define FIX_0_720959822 (5906) /* FIX(0.720959822) */ +#define FIX_0_765366865 (6270) /* FIX(0.765366865) */ +#define FIX_0_850430095 (6967) /* FIX(0.850430095) */ +#define FIX_0_899976223 (7373) /* FIX(0.899976223) */ +#define FIX_1_061594337 (8697) /* FIX(1.061594337) */ +#define FIX_1_272758580 (10426) /* FIX(1.272758580) */ +#define FIX_1_451774981 (11893) /* FIX(1.451774981) */ +#define FIX_1_847759065 (15137) /* FIX(1.847759065) */ +#define FIX_2_172734803 (17799) /* FIX(2.172734803) */ +#define FIX_2_562915447 (20995) /* FIX(2.562915447) */ +#define FIX_3_624509785 (29692) /* FIX(3.624509785) */ + +.balign 16 +jsimd_idct_4x4_neon_consts: + .short FIX_1_847759065 /* d0[0] */ + .short -FIX_0_765366865 /* d0[1] */ + .short -FIX_0_211164243 /* d0[2] */ + .short FIX_1_451774981 /* d0[3] */ + .short -FIX_2_172734803 /* d1[0] */ + .short FIX_1_061594337 /* d1[1] */ + .short -FIX_0_509795579 /* d1[2] */ + .short -FIX_0_601344887 /* d1[3] */ + .short FIX_0_899976223 /* d2[0] */ + .short FIX_2_562915447 /* d2[1] */ + .short 1 << (CONST_BITS+1) /* d2[2] */ + .short 0 /* d2[3] */ + +.macro idct_helper x4, x6, x8, x10, x12, x14, x16, shift, y26, y27, y28, y29 + vmull.s16 q14, \x4, d2[2] + vmlal.s16 q14, \x8, d0[0] + vmlal.s16 q14, \x14, d0[1] + + vmull.s16 q13, \x16, d1[2] + vmlal.s16 q13, \x12, d1[3] + vmlal.s16 q13, \x10, d2[0] + vmlal.s16 q13, \x6, d2[1] + + vmull.s16 q15, \x4, d2[2] + vmlsl.s16 q15, \x8, d0[0] + vmlsl.s16 q15, \x14, d0[1] + + vmull.s16 q12, \x16, d0[2] + vmlal.s16 q12, \x12, d0[3] + vmlal.s16 q12, \x10, d1[0] + vmlal.s16 q12, \x6, d1[1] + + vadd.s32 q10, q14, q13 + vsub.s32 q14, q14, q13 + +.if \shift > 16 + vrshr.s32 q10, q10, #\shift + vrshr.s32 q14, q14, #\shift + vmovn.s32 \y26, q10 + vmovn.s32 \y29, q14 +.else + vrshrn.s32 \y26, q10, #\shift + vrshrn.s32 \y29, q14, #\shift +.endif + + vadd.s32 q10, q15, q12 + vsub.s32 q15, q15, q12 + +.if \shift > 16 + vrshr.s32 q10, q10, #\shift + vrshr.s32 q15, q15, #\shift + vmovn.s32 \y27, q10 + vmovn.s32 \y28, q15 +.else + vrshrn.s32 \y27, q10, #\shift + vrshrn.s32 \y28, q15, #\shift +.endif + +.endm + +asm_function jsimd_idct_4x4_neon + + DCT_TABLE .req r0 + COEF_BLOCK .req r1 + OUTPUT_BUF .req r2 + OUTPUT_COL .req r3 + TMP1 .req r0 + TMP2 .req r1 + TMP3 .req r2 + TMP4 .req ip + + vpush {d8-d15} + + /* Load constants (d3 is just used for padding) */ + adr TMP4, jsimd_idct_4x4_neon_consts + vld1.16 {d0, d1, d2, d3}, [TMP4, :128] + + /* Load all COEF_BLOCK into NEON registers with the following allocation: + * 0 1 2 3 | 4 5 6 7 + * ---------+-------- + * 0 | d4 | d5 + * 1 | d6 | d7 + * 2 | d8 | d9 + * 3 | d10 | d11 + * 4 | - | - + * 5 | d12 | d13 + * 6 | d14 | d15 + * 7 | d16 | d17 + */ + vld1.16 {d4, d5, d6, d7}, [COEF_BLOCK, :128]! + vld1.16 {d8, d9, d10, d11}, [COEF_BLOCK, :128]! + add COEF_BLOCK, COEF_BLOCK, #16 + vld1.16 {d12, d13, d14, d15}, [COEF_BLOCK, :128]! + vld1.16 {d16, d17}, [COEF_BLOCK, :128]! + /* dequantize */ + vld1.16 {d18, d19, d20, d21}, [DCT_TABLE, :128]! + vmul.s16 q2, q2, q9 + vld1.16 {d22, d23, d24, d25}, [DCT_TABLE, :128]! + vmul.s16 q3, q3, q10 + vmul.s16 q4, q4, q11 + add DCT_TABLE, DCT_TABLE, #16 + vld1.16 {d26, d27, d28, d29}, [DCT_TABLE, :128]! + vmul.s16 q5, q5, q12 + vmul.s16 q6, q6, q13 + vld1.16 {d30, d31}, [DCT_TABLE, :128]! + vmul.s16 q7, q7, q14 + vmul.s16 q8, q8, q15 + + /* Pass 1 */ + idct_helper d4, d6, d8, d10, d12, d14, d16, 12, d4, d6, d8, d10 + transpose_4x4 d4, d6, d8, d10 + idct_helper d5, d7, d9, d11, d13, d15, d17, 12, d5, d7, d9, d11 + transpose_4x4 d5, d7, d9, d11 + + /* Pass 2 */ + idct_helper d4, d6, d8, d10, d7, d9, d11, 19, d26, d27, d28, d29 + transpose_4x4 d26, d27, d28, d29 + + /* Range limit */ + vmov.u16 q15, #0x80 + vadd.s16 q13, q13, q15 + vadd.s16 q14, q14, q15 + vqmovun.s16 d26, q13 + vqmovun.s16 d27, q14 + + /* Store results to the output buffer */ + ldmia OUTPUT_BUF, {TMP1, TMP2, TMP3, TMP4} + add TMP1, TMP1, OUTPUT_COL + add TMP2, TMP2, OUTPUT_COL + add TMP3, TMP3, OUTPUT_COL + add TMP4, TMP4, OUTPUT_COL + +#if defined(__ARMEL__) && !RESPECT_STRICT_ALIGNMENT + /* We can use much less instructions on little endian systems if the + * OS kernel is not configured to trap unaligned memory accesses + */ + vst1.32 {d26[0]}, [TMP1]! + vst1.32 {d27[0]}, [TMP3]! + vst1.32 {d26[1]}, [TMP2]! + vst1.32 {d27[1]}, [TMP4]! +#else + vst1.8 {d26[0]}, [TMP1]! + vst1.8 {d27[0]}, [TMP3]! + vst1.8 {d26[1]}, [TMP1]! + vst1.8 {d27[1]}, [TMP3]! + vst1.8 {d26[2]}, [TMP1]! + vst1.8 {d27[2]}, [TMP3]! + vst1.8 {d26[3]}, [TMP1]! + vst1.8 {d27[3]}, [TMP3]! + + vst1.8 {d26[4]}, [TMP2]! + vst1.8 {d27[4]}, [TMP4]! + vst1.8 {d26[5]}, [TMP2]! + vst1.8 {d27[5]}, [TMP4]! + vst1.8 {d26[6]}, [TMP2]! + vst1.8 {d27[6]}, [TMP4]! + vst1.8 {d26[7]}, [TMP2]! + vst1.8 {d27[7]}, [TMP4]! +#endif + + vpop {d8-d15} + bx lr + + .unreq DCT_TABLE + .unreq COEF_BLOCK + .unreq OUTPUT_BUF + .unreq OUTPUT_COL + .unreq TMP1 + .unreq TMP2 + .unreq TMP3 + .unreq TMP4 +.endfunc + +.purgem idct_helper + +/*****************************************************************************/ + +/* + * jsimd_idct_2x2_neon + * + * This function contains inverse-DCT code for getting reduced-size + * 2x2 pixels output from an 8x8 DCT block. It uses the same calculations + * and produces exactly the same output as IJG's original 'jpeg_idct_2x2' + * function from jpeg-6b (jidctred.c). + * + * NOTE: jpeg-8 has an improved implementation of 2x2 inverse-DCT, which + * requires much less arithmetic operations and hence should be faster. + * The primary purpose of this particular NEON optimized function is + * bit exact compatibility with jpeg-6b. + */ + +.balign 8 +jsimd_idct_2x2_neon_consts: + .short -FIX_0_720959822 /* d0[0] */ + .short FIX_0_850430095 /* d0[1] */ + .short -FIX_1_272758580 /* d0[2] */ + .short FIX_3_624509785 /* d0[3] */ + +.macro idct_helper x4, x6, x10, x12, x16, shift, y26, y27 + vshll.s16 q14, \x4, #15 + vmull.s16 q13, \x6, d0[3] + vmlal.s16 q13, \x10, d0[2] + vmlal.s16 q13, \x12, d0[1] + vmlal.s16 q13, \x16, d0[0] + + vadd.s32 q10, q14, q13 + vsub.s32 q14, q14, q13 + +.if \shift > 16 + vrshr.s32 q10, q10, #\shift + vrshr.s32 q14, q14, #\shift + vmovn.s32 \y26, q10 + vmovn.s32 \y27, q14 +.else + vrshrn.s32 \y26, q10, #\shift + vrshrn.s32 \y27, q14, #\shift +.endif + +.endm + +asm_function jsimd_idct_2x2_neon + + DCT_TABLE .req r0 + COEF_BLOCK .req r1 + OUTPUT_BUF .req r2 + OUTPUT_COL .req r3 + TMP1 .req r0 + TMP2 .req ip + + vpush {d8-d15} + + /* Load constants */ + adr TMP2, jsimd_idct_2x2_neon_consts + vld1.16 {d0}, [TMP2, :64] + + /* Load all COEF_BLOCK into NEON registers with the following allocation: + * 0 1 2 3 | 4 5 6 7 + * ---------+-------- + * 0 | d4 | d5 + * 1 | d6 | d7 + * 2 | - | - + * 3 | d10 | d11 + * 4 | - | - + * 5 | d12 | d13 + * 6 | - | - + * 7 | d16 | d17 + */ + vld1.16 {d4, d5, d6, d7}, [COEF_BLOCK, :128]! + add COEF_BLOCK, COEF_BLOCK, #16 + vld1.16 {d10, d11}, [COEF_BLOCK, :128]! + add COEF_BLOCK, COEF_BLOCK, #16 + vld1.16 {d12, d13}, [COEF_BLOCK, :128]! + add COEF_BLOCK, COEF_BLOCK, #16 + vld1.16 {d16, d17}, [COEF_BLOCK, :128]! + /* Dequantize */ + vld1.16 {d18, d19, d20, d21}, [DCT_TABLE, :128]! + vmul.s16 q2, q2, q9 + vmul.s16 q3, q3, q10 + add DCT_TABLE, DCT_TABLE, #16 + vld1.16 {d24, d25}, [DCT_TABLE, :128]! + vmul.s16 q5, q5, q12 + add DCT_TABLE, DCT_TABLE, #16 + vld1.16 {d26, d27}, [DCT_TABLE, :128]! + vmul.s16 q6, q6, q13 + add DCT_TABLE, DCT_TABLE, #16 + vld1.16 {d30, d31}, [DCT_TABLE, :128]! + vmul.s16 q8, q8, q15 + + /* Pass 1 */ +#if 0 + idct_helper d4, d6, d10, d12, d16, 13, d4, d6 + transpose_4x4 d4, d6, d8, d10 + idct_helper d5, d7, d11, d13, d17, 13, d5, d7 + transpose_4x4 d5, d7, d9, d11 +#else + vmull.s16 q13, d6, d0[3] + vmlal.s16 q13, d10, d0[2] + vmlal.s16 q13, d12, d0[1] + vmlal.s16 q13, d16, d0[0] + vmull.s16 q12, d7, d0[3] + vmlal.s16 q12, d11, d0[2] + vmlal.s16 q12, d13, d0[1] + vmlal.s16 q12, d17, d0[0] + vshll.s16 q14, d4, #15 + vshll.s16 q15, d5, #15 + vadd.s32 q10, q14, q13 + vsub.s32 q14, q14, q13 + vrshrn.s32 d4, q10, #13 + vrshrn.s32 d6, q14, #13 + vadd.s32 q10, q15, q12 + vsub.s32 q14, q15, q12 + vrshrn.s32 d5, q10, #13 + vrshrn.s32 d7, q14, #13 + vtrn.16 q2, q3 + vtrn.32 q3, q5 +#endif + + /* Pass 2 */ + idct_helper d4, d6, d10, d7, d11, 20, d26, d27 + + /* Range limit */ + vmov.u16 q15, #0x80 + vadd.s16 q13, q13, q15 + vqmovun.s16 d26, q13 + vqmovun.s16 d27, q13 + + /* Store results to the output buffer */ + ldmia OUTPUT_BUF, {TMP1, TMP2} + add TMP1, TMP1, OUTPUT_COL + add TMP2, TMP2, OUTPUT_COL + + vst1.8 {d26[0]}, [TMP1]! + vst1.8 {d27[4]}, [TMP1]! + vst1.8 {d26[1]}, [TMP2]! + vst1.8 {d27[5]}, [TMP2]! + + vpop {d8-d15} + bx lr + + .unreq DCT_TABLE + .unreq COEF_BLOCK + .unreq OUTPUT_BUF + .unreq OUTPUT_COL + .unreq TMP1 + .unreq TMP2 +.endfunc + +.purgem idct_helper + +/*****************************************************************************/ + +/* + * jsimd_ycc_extrgb_convert_neon + * jsimd_ycc_extbgr_convert_neon + * jsimd_ycc_extrgbx_convert_neon + * jsimd_ycc_extbgrx_convert_neon + * jsimd_ycc_extxbgr_convert_neon + * jsimd_ycc_extxrgb_convert_neon + * + * Colorspace conversion YCbCr -> RGB + */ + + +.macro do_load size + .if \size == 8 + vld1.8 {d4}, [U, :64]! + vld1.8 {d5}, [V, :64]! + vld1.8 {d0}, [Y, :64]! + pld [U, #64] + pld [V, #64] + pld [Y, #64] + .elseif \size == 4 + vld1.8 {d4[0]}, [U]! + vld1.8 {d4[1]}, [U]! + vld1.8 {d4[2]}, [U]! + vld1.8 {d4[3]}, [U]! + vld1.8 {d5[0]}, [V]! + vld1.8 {d5[1]}, [V]! + vld1.8 {d5[2]}, [V]! + vld1.8 {d5[3]}, [V]! + vld1.8 {d0[0]}, [Y]! + vld1.8 {d0[1]}, [Y]! + vld1.8 {d0[2]}, [Y]! + vld1.8 {d0[3]}, [Y]! + .elseif \size == 2 + vld1.8 {d4[4]}, [U]! + vld1.8 {d4[5]}, [U]! + vld1.8 {d5[4]}, [V]! + vld1.8 {d5[5]}, [V]! + vld1.8 {d0[4]}, [Y]! + vld1.8 {d0[5]}, [Y]! + .elseif \size == 1 + vld1.8 {d4[6]}, [U]! + vld1.8 {d5[6]}, [V]! + vld1.8 {d0[6]}, [Y]! + .else + .error unsupported macroblock size + .endif +.endm + +.macro do_store bpp, size + .if \bpp == 24 + .if \size == 8 + vst3.8 {d10, d11, d12}, [RGB]! + .elseif \size == 4 + vst3.8 {d10[0], d11[0], d12[0]}, [RGB]! + vst3.8 {d10[1], d11[1], d12[1]}, [RGB]! + vst3.8 {d10[2], d11[2], d12[2]}, [RGB]! + vst3.8 {d10[3], d11[3], d12[3]}, [RGB]! + .elseif \size == 2 + vst3.8 {d10[4], d11[4], d12[4]}, [RGB]! + vst3.8 {d10[5], d11[5], d12[5]}, [RGB]! + .elseif \size == 1 + vst3.8 {d10[6], d11[6], d12[6]}, [RGB]! + .else + .error unsupported macroblock size + .endif + .elseif \bpp == 32 + .if \size == 8 + vst4.8 {d10, d11, d12, d13}, [RGB]! + .elseif \size == 4 + vst4.8 {d10[0], d11[0], d12[0], d13[0]}, [RGB]! + vst4.8 {d10[1], d11[1], d12[1], d13[1]}, [RGB]! + vst4.8 {d10[2], d11[2], d12[2], d13[2]}, [RGB]! + vst4.8 {d10[3], d11[3], d12[3], d13[3]}, [RGB]! + .elseif \size == 2 + vst4.8 {d10[4], d11[4], d12[4], d13[4]}, [RGB]! + vst4.8 {d10[5], d11[5], d12[5], d13[5]}, [RGB]! + .elseif \size == 1 + vst4.8 {d10[6], d11[6], d12[6], d13[6]}, [RGB]! + .else + .error unsupported macroblock size + .endif + .else + .error unsupported bpp + .endif +.endm + +.macro generate_jsimd_ycc_rgb_convert_neon colorid, bpp, r_offs, g_offs, b_offs + +/* + * 2 stage pipelined YCbCr->RGB conversion + */ + +.macro do_yuv_to_rgb_stage1 + vaddw.u8 q3, q1, d4 /* q3 = u - 128 */ + vaddw.u8 q4, q1, d5 /* q2 = v - 128 */ + vmull.s16 q10, d6, d1[1] /* multiply by -11277 */ + vmlal.s16 q10, d8, d1[2] /* multiply by -23401 */ + vmull.s16 q11, d7, d1[1] /* multiply by -11277 */ + vmlal.s16 q11, d9, d1[2] /* multiply by -23401 */ + vmull.s16 q12, d8, d1[0] /* multiply by 22971 */ + vmull.s16 q13, d9, d1[0] /* multiply by 22971 */ + vmull.s16 q14, d6, d1[3] /* multiply by 29033 */ + vmull.s16 q15, d7, d1[3] /* multiply by 29033 */ +.endm + +.macro do_yuv_to_rgb_stage2 + vrshrn.s32 d20, q10, #15 + vrshrn.s32 d21, q11, #15 + vrshrn.s32 d24, q12, #14 + vrshrn.s32 d25, q13, #14 + vrshrn.s32 d28, q14, #14 + vrshrn.s32 d29, q15, #14 + vaddw.u8 q10, q10, d0 + vaddw.u8 q12, q12, d0 + vaddw.u8 q14, q14, d0 + vqmovun.s16 d1\g_offs, q10 + vqmovun.s16 d1\r_offs, q12 + vqmovun.s16 d1\b_offs, q14 +.endm + +.macro do_yuv_to_rgb_stage2_store_load_stage1 + vld1.8 {d4}, [U, :64]! + vrshrn.s32 d20, q10, #15 + vrshrn.s32 d21, q11, #15 + vrshrn.s32 d24, q12, #14 + vrshrn.s32 d25, q13, #14 + vrshrn.s32 d28, q14, #14 + vld1.8 {d5}, [V, :64]! + vrshrn.s32 d29, q15, #14 + vaddw.u8 q10, q10, d0 + vaddw.u8 q12, q12, d0 + vaddw.u8 q14, q14, d0 + vqmovun.s16 d1\g_offs, q10 + vld1.8 {d0}, [Y, :64]! + vqmovun.s16 d1\r_offs, q12 + pld [U, #64] + pld [V, #64] + pld [Y, #64] + vqmovun.s16 d1\b_offs, q14 + vaddw.u8 q3, q1, d4 /* q3 = u - 128 */ + vaddw.u8 q4, q1, d5 /* q2 = v - 128 */ + do_store \bpp, 8 + vmull.s16 q10, d6, d1[1] /* multiply by -11277 */ + vmlal.s16 q10, d8, d1[2] /* multiply by -23401 */ + vmull.s16 q11, d7, d1[1] /* multiply by -11277 */ + vmlal.s16 q11, d9, d1[2] /* multiply by -23401 */ + vmull.s16 q12, d8, d1[0] /* multiply by 22971 */ + vmull.s16 q13, d9, d1[0] /* multiply by 22971 */ + vmull.s16 q14, d6, d1[3] /* multiply by 29033 */ + vmull.s16 q15, d7, d1[3] /* multiply by 29033 */ +.endm + +.macro do_yuv_to_rgb + do_yuv_to_rgb_stage1 + do_yuv_to_rgb_stage2 +.endm + +/* Apple gas crashes on adrl, work around that by using adr. + * But this requires a copy of these constants for each function. + */ + +.balign 16 +jsimd_ycc_\colorid\()_neon_consts: + .short 0, 0, 0, 0 + .short 22971, -11277, -23401, 29033 + .short -128, -128, -128, -128 + .short -128, -128, -128, -128 + +asm_function jsimd_ycc_\colorid\()_convert_neon + OUTPUT_WIDTH .req r0 + INPUT_BUF .req r1 + INPUT_ROW .req r2 + OUTPUT_BUF .req r3 + NUM_ROWS .req r4 + + INPUT_BUF0 .req r5 + INPUT_BUF1 .req r6 + INPUT_BUF2 .req INPUT_BUF + + RGB .req r7 + Y .req r8 + U .req r9 + V .req r10 + N .req ip + + /* Load constants to d1, d2, d3 (d0 is just used for padding) */ + adr ip, jsimd_ycc_\colorid\()_neon_consts + vld1.16 {d0, d1, d2, d3}, [ip, :128] + + /* Save ARM registers and handle input arguments */ + push {r4, r5, r6, r7, r8, r9, r10, lr} + ldr NUM_ROWS, [sp, #(4 * 8)] + ldr INPUT_BUF0, [INPUT_BUF] + ldr INPUT_BUF1, [INPUT_BUF, #4] + ldr INPUT_BUF2, [INPUT_BUF, #8] + .unreq INPUT_BUF + + /* Save NEON registers */ + vpush {d8-d15} + + /* Initially set d10, d11, d12, d13 to 0xFF */ + vmov.u8 q5, #255 + vmov.u8 q6, #255 + + /* Outer loop over scanlines */ + cmp NUM_ROWS, #1 + blt 9f +0: + ldr Y, [INPUT_BUF0, INPUT_ROW, lsl #2] + ldr U, [INPUT_BUF1, INPUT_ROW, lsl #2] + mov N, OUTPUT_WIDTH + ldr V, [INPUT_BUF2, INPUT_ROW, lsl #2] + add INPUT_ROW, INPUT_ROW, #1 + ldr RGB, [OUTPUT_BUF], #4 + + /* Inner loop over pixels */ + subs N, N, #8 + blt 3f + do_load 8 + do_yuv_to_rgb_stage1 + subs N, N, #8 + blt 2f +1: + do_yuv_to_rgb_stage2_store_load_stage1 + subs N, N, #8 + bge 1b +2: + do_yuv_to_rgb_stage2 + do_store \bpp, 8 + tst N, #7 + beq 8f +3: + tst N, #4 + beq 3f + do_load 4 +3: + tst N, #2 + beq 4f + do_load 2 +4: + tst N, #1 + beq 5f + do_load 1 +5: + do_yuv_to_rgb + tst N, #4 + beq 6f + do_store \bpp, 4 +6: + tst N, #2 + beq 7f + do_store \bpp, 2 +7: + tst N, #1 + beq 8f + do_store \bpp, 1 +8: + subs NUM_ROWS, NUM_ROWS, #1 + bgt 0b +9: + /* Restore all registers and return */ + vpop {d8-d15} + pop {r4, r5, r6, r7, r8, r9, r10, pc} + + .unreq OUTPUT_WIDTH + .unreq INPUT_ROW + .unreq OUTPUT_BUF + .unreq NUM_ROWS + .unreq INPUT_BUF0 + .unreq INPUT_BUF1 + .unreq INPUT_BUF2 + .unreq RGB + .unreq Y + .unreq U + .unreq V + .unreq N +.endfunc + +.purgem do_yuv_to_rgb +.purgem do_yuv_to_rgb_stage1 +.purgem do_yuv_to_rgb_stage2 +.purgem do_yuv_to_rgb_stage2_store_load_stage1 + +.endm + +/*--------------------------------- id ----- bpp R G B */ +generate_jsimd_ycc_rgb_convert_neon extrgb, 24, 0, 1, 2 +generate_jsimd_ycc_rgb_convert_neon extbgr, 24, 2, 1, 0 +generate_jsimd_ycc_rgb_convert_neon extrgbx, 32, 0, 1, 2 +generate_jsimd_ycc_rgb_convert_neon extbgrx, 32, 2, 1, 0 +generate_jsimd_ycc_rgb_convert_neon extxbgr, 32, 3, 2, 1 +generate_jsimd_ycc_rgb_convert_neon extxrgb, 32, 1, 2, 3 + +.purgem do_load +.purgem do_store + +/*****************************************************************************/ + +/* + * jsimd_extrgb_ycc_convert_neon + * jsimd_extbgr_ycc_convert_neon + * jsimd_extrgbx_ycc_convert_neon + * jsimd_extbgrx_ycc_convert_neon + * jsimd_extxbgr_ycc_convert_neon + * jsimd_extxrgb_ycc_convert_neon + * + * Colorspace conversion RGB -> YCbCr + */ + +.macro do_store size + .if \size == 8 + vst1.8 {d20}, [Y]! + vst1.8 {d21}, [U]! + vst1.8 {d22}, [V]! + .elseif \size == 4 + vst1.8 {d20[0]}, [Y]! + vst1.8 {d20[1]}, [Y]! + vst1.8 {d20[2]}, [Y]! + vst1.8 {d20[3]}, [Y]! + vst1.8 {d21[0]}, [U]! + vst1.8 {d21[1]}, [U]! + vst1.8 {d21[2]}, [U]! + vst1.8 {d21[3]}, [U]! + vst1.8 {d22[0]}, [V]! + vst1.8 {d22[1]}, [V]! + vst1.8 {d22[2]}, [V]! + vst1.8 {d22[3]}, [V]! + .elseif \size == 2 + vst1.8 {d20[4]}, [Y]! + vst1.8 {d20[5]}, [Y]! + vst1.8 {d21[4]}, [U]! + vst1.8 {d21[5]}, [U]! + vst1.8 {d22[4]}, [V]! + vst1.8 {d22[5]}, [V]! + .elseif \size == 1 + vst1.8 {d20[6]}, [Y]! + vst1.8 {d21[6]}, [U]! + vst1.8 {d22[6]}, [V]! + .else + .error unsupported macroblock size + .endif +.endm + +.macro do_load bpp, size + .if \bpp == 24 + .if \size == 8 + vld3.8 {d10, d11, d12}, [RGB]! + pld [RGB, #128] + .elseif \size == 4 + vld3.8 {d10[0], d11[0], d12[0]}, [RGB]! + vld3.8 {d10[1], d11[1], d12[1]}, [RGB]! + vld3.8 {d10[2], d11[2], d12[2]}, [RGB]! + vld3.8 {d10[3], d11[3], d12[3]}, [RGB]! + .elseif \size == 2 + vld3.8 {d10[4], d11[4], d12[4]}, [RGB]! + vld3.8 {d10[5], d11[5], d12[5]}, [RGB]! + .elseif \size == 1 + vld3.8 {d10[6], d11[6], d12[6]}, [RGB]! + .else + .error unsupported macroblock size + .endif + .elseif \bpp == 32 + .if \size == 8 + vld4.8 {d10, d11, d12, d13}, [RGB]! + pld [RGB, #128] + .elseif \size == 4 + vld4.8 {d10[0], d11[0], d12[0], d13[0]}, [RGB]! + vld4.8 {d10[1], d11[1], d12[1], d13[1]}, [RGB]! + vld4.8 {d10[2], d11[2], d12[2], d13[2]}, [RGB]! + vld4.8 {d10[3], d11[3], d12[3], d13[3]}, [RGB]! + .elseif \size == 2 + vld4.8 {d10[4], d11[4], d12[4], d13[4]}, [RGB]! + vld4.8 {d10[5], d11[5], d12[5], d13[5]}, [RGB]! + .elseif \size == 1 + vld4.8 {d10[6], d11[6], d12[6], d13[6]}, [RGB]! + .else + .error unsupported macroblock size + .endif + .else + .error unsupported bpp + .endif +.endm + +.macro generate_jsimd_rgb_ycc_convert_neon colorid, bpp, r_offs, g_offs, b_offs + +/* + * 2 stage pipelined RGB->YCbCr conversion + */ + +.macro do_rgb_to_yuv_stage1 + vmovl.u8 q2, d1\r_offs /* r = { d4, d5 } */ + vmovl.u8 q3, d1\g_offs /* g = { d6, d7 } */ + vmovl.u8 q4, d1\b_offs /* b = { d8, d9 } */ + vmull.u16 q7, d4, d0[0] + vmlal.u16 q7, d6, d0[1] + vmlal.u16 q7, d8, d0[2] + vmull.u16 q8, d5, d0[0] + vmlal.u16 q8, d7, d0[1] + vmlal.u16 q8, d9, d0[2] + vrev64.32 q9, q1 + vrev64.32 q13, q1 + vmlsl.u16 q9, d4, d0[3] + vmlsl.u16 q9, d6, d1[0] + vmlal.u16 q9, d8, d1[1] + vmlsl.u16 q13, d5, d0[3] + vmlsl.u16 q13, d7, d1[0] + vmlal.u16 q13, d9, d1[1] + vrev64.32 q14, q1 + vrev64.32 q15, q1 + vmlal.u16 q14, d4, d1[1] + vmlsl.u16 q14, d6, d1[2] + vmlsl.u16 q14, d8, d1[3] + vmlal.u16 q15, d5, d1[1] + vmlsl.u16 q15, d7, d1[2] + vmlsl.u16 q15, d9, d1[3] +.endm + +.macro do_rgb_to_yuv_stage2 + vrshrn.u32 d20, q7, #16 + vrshrn.u32 d21, q8, #16 + vshrn.u32 d22, q9, #16 + vshrn.u32 d23, q13, #16 + vshrn.u32 d24, q14, #16 + vshrn.u32 d25, q15, #16 + vmovn.u16 d20, q10 /* d20 = y */ + vmovn.u16 d21, q11 /* d21 = u */ + vmovn.u16 d22, q12 /* d22 = v */ +.endm + +.macro do_rgb_to_yuv + do_rgb_to_yuv_stage1 + do_rgb_to_yuv_stage2 +.endm + +.macro do_rgb_to_yuv_stage2_store_load_stage1 + vrshrn.u32 d20, q7, #16 + vrshrn.u32 d21, q8, #16 + vshrn.u32 d22, q9, #16 + vrev64.32 q9, q1 + vshrn.u32 d23, q13, #16 + vrev64.32 q13, q1 + vshrn.u32 d24, q14, #16 + vshrn.u32 d25, q15, #16 + do_load \bpp, 8 + vmovn.u16 d20, q10 /* d20 = y */ + vmovl.u8 q2, d1\r_offs /* r = { d4, d5 } */ + vmovn.u16 d21, q11 /* d21 = u */ + vmovl.u8 q3, d1\g_offs /* g = { d6, d7 } */ + vmovn.u16 d22, q12 /* d22 = v */ + vmovl.u8 q4, d1\b_offs /* b = { d8, d9 } */ + vmull.u16 q7, d4, d0[0] + vmlal.u16 q7, d6, d0[1] + vmlal.u16 q7, d8, d0[2] + vst1.8 {d20}, [Y]! + vmull.u16 q8, d5, d0[0] + vmlal.u16 q8, d7, d0[1] + vmlal.u16 q8, d9, d0[2] + vmlsl.u16 q9, d4, d0[3] + vmlsl.u16 q9, d6, d1[0] + vmlal.u16 q9, d8, d1[1] + vst1.8 {d21}, [U]! + vmlsl.u16 q13, d5, d0[3] + vmlsl.u16 q13, d7, d1[0] + vmlal.u16 q13, d9, d1[1] + vrev64.32 q14, q1 + vrev64.32 q15, q1 + vmlal.u16 q14, d4, d1[1] + vmlsl.u16 q14, d6, d1[2] + vmlsl.u16 q14, d8, d1[3] + vst1.8 {d22}, [V]! + vmlal.u16 q15, d5, d1[1] + vmlsl.u16 q15, d7, d1[2] + vmlsl.u16 q15, d9, d1[3] +.endm + +.balign 16 +jsimd_\colorid\()_ycc_neon_consts: + .short 19595, 38470, 7471, 11059 + .short 21709, 32768, 27439, 5329 + .short 32767, 128, 32767, 128 + .short 32767, 128, 32767, 128 + +asm_function jsimd_\colorid\()_ycc_convert_neon + OUTPUT_WIDTH .req r0 + INPUT_BUF .req r1 + OUTPUT_BUF .req r2 + OUTPUT_ROW .req r3 + NUM_ROWS .req r4 + + OUTPUT_BUF0 .req r5 + OUTPUT_BUF1 .req r6 + OUTPUT_BUF2 .req OUTPUT_BUF + + RGB .req r7 + Y .req r8 + U .req r9 + V .req r10 + N .req ip + + /* Load constants to d0, d1, d2, d3 */ + adr ip, jsimd_\colorid\()_ycc_neon_consts + vld1.16 {d0, d1, d2, d3}, [ip, :128] + + /* Save ARM registers and handle input arguments */ + push {r4, r5, r6, r7, r8, r9, r10, lr} + ldr NUM_ROWS, [sp, #(4 * 8)] + ldr OUTPUT_BUF0, [OUTPUT_BUF] + ldr OUTPUT_BUF1, [OUTPUT_BUF, #4] + ldr OUTPUT_BUF2, [OUTPUT_BUF, #8] + .unreq OUTPUT_BUF + + /* Save NEON registers */ + vpush {d8-d15} + + /* Outer loop over scanlines */ + cmp NUM_ROWS, #1 + blt 9f +0: + ldr Y, [OUTPUT_BUF0, OUTPUT_ROW, lsl #2] + ldr U, [OUTPUT_BUF1, OUTPUT_ROW, lsl #2] + mov N, OUTPUT_WIDTH + ldr V, [OUTPUT_BUF2, OUTPUT_ROW, lsl #2] + add OUTPUT_ROW, OUTPUT_ROW, #1 + ldr RGB, [INPUT_BUF], #4 + + /* Inner loop over pixels */ + subs N, N, #8 + blt 3f + do_load \bpp, 8 + do_rgb_to_yuv_stage1 + subs N, N, #8 + blt 2f +1: + do_rgb_to_yuv_stage2_store_load_stage1 + subs N, N, #8 + bge 1b +2: + do_rgb_to_yuv_stage2 + do_store 8 + tst N, #7 + beq 8f +3: + tst N, #4 + beq 3f + do_load \bpp, 4 +3: + tst N, #2 + beq 4f + do_load \bpp, 2 +4: + tst N, #1 + beq 5f + do_load \bpp, 1 +5: + do_rgb_to_yuv + tst N, #4 + beq 6f + do_store 4 +6: + tst N, #2 + beq 7f + do_store 2 +7: + tst N, #1 + beq 8f + do_store 1 +8: + subs NUM_ROWS, NUM_ROWS, #1 + bgt 0b +9: + /* Restore all registers and return */ + vpop {d8-d15} + pop {r4, r5, r6, r7, r8, r9, r10, pc} + + .unreq OUTPUT_WIDTH + .unreq OUTPUT_ROW + .unreq INPUT_BUF + .unreq NUM_ROWS + .unreq OUTPUT_BUF0 + .unreq OUTPUT_BUF1 + .unreq OUTPUT_BUF2 + .unreq RGB + .unreq Y + .unreq U + .unreq V + .unreq N +.endfunc + +.purgem do_rgb_to_yuv +.purgem do_rgb_to_yuv_stage1 +.purgem do_rgb_to_yuv_stage2 +.purgem do_rgb_to_yuv_stage2_store_load_stage1 + +.endm + +/*--------------------------------- id ----- bpp R G B */ +generate_jsimd_rgb_ycc_convert_neon extrgb, 24, 0, 1, 2 +generate_jsimd_rgb_ycc_convert_neon extbgr, 24, 2, 1, 0 +generate_jsimd_rgb_ycc_convert_neon extrgbx, 32, 0, 1, 2 +generate_jsimd_rgb_ycc_convert_neon extbgrx, 32, 2, 1, 0 +generate_jsimd_rgb_ycc_convert_neon extxbgr, 32, 3, 2, 1 +generate_jsimd_rgb_ycc_convert_neon extxrgb, 32, 1, 2, 3 + +.purgem do_load +.purgem do_store + +/*****************************************************************************/ + +/* + * Load data into workspace, applying unsigned->signed conversion + * + * TODO: can be combined with 'jsimd_fdct_ifast_neon' to get + * rid of VST1.16 instructions + */ + +asm_function jsimd_convsamp_neon + SAMPLE_DATA .req r0 + START_COL .req r1 + WORKSPACE .req r2 + TMP1 .req r3 + TMP2 .req r4 + TMP3 .req r5 + TMP4 .req ip + + push {r4, r5} + vmov.u8 d0, #128 + + ldmia SAMPLE_DATA!, {TMP1, TMP2, TMP3, TMP4} + add TMP1, TMP1, START_COL + add TMP2, TMP2, START_COL + add TMP3, TMP3, START_COL + add TMP4, TMP4, START_COL + vld1.8 {d16}, [TMP1] + vsubl.u8 q8, d16, d0 + vld1.8 {d18}, [TMP2] + vsubl.u8 q9, d18, d0 + vld1.8 {d20}, [TMP3] + vsubl.u8 q10, d20, d0 + vld1.8 {d22}, [TMP4] + ldmia SAMPLE_DATA!, {TMP1, TMP2, TMP3, TMP4} + vsubl.u8 q11, d22, d0 + vst1.16 {d16, d17, d18, d19}, [WORKSPACE, :128]! + add TMP1, TMP1, START_COL + add TMP2, TMP2, START_COL + vst1.16 {d20, d21, d22, d23}, [WORKSPACE, :128]! + add TMP3, TMP3, START_COL + add TMP4, TMP4, START_COL + vld1.8 {d24}, [TMP1] + vsubl.u8 q12, d24, d0 + vld1.8 {d26}, [TMP2] + vsubl.u8 q13, d26, d0 + vld1.8 {d28}, [TMP3] + vsubl.u8 q14, d28, d0 + vld1.8 {d30}, [TMP4] + vsubl.u8 q15, d30, d0 + vst1.16 {d24, d25, d26, d27}, [WORKSPACE, :128]! + vst1.16 {d28, d29, d30, d31}, [WORKSPACE, :128]! + pop {r4, r5} + bx lr + + .unreq SAMPLE_DATA + .unreq START_COL + .unreq WORKSPACE + .unreq TMP1 + .unreq TMP2 + .unreq TMP3 + .unreq TMP4 +.endfunc + +/*****************************************************************************/ + +/* + * jsimd_fdct_ifast_neon + * + * This function contains a fast, not so accurate integer implementation of + * the forward DCT (Discrete Cosine Transform). It uses the same calculations + * and produces exactly the same output as IJG's original 'jpeg_fdct_ifast' + * function from jfdctfst.c + * + * TODO: can be combined with 'jsimd_convsamp_neon' to get + * rid of a bunch of VLD1.16 instructions + */ + +#define XFIX_0_382683433 d0[0] +#define XFIX_0_541196100 d0[1] +#define XFIX_0_707106781 d0[2] +#define XFIX_1_306562965 d0[3] + +.balign 16 +jsimd_fdct_ifast_neon_consts: + .short (98 * 128) /* XFIX_0_382683433 */ + .short (139 * 128) /* XFIX_0_541196100 */ + .short (181 * 128) /* XFIX_0_707106781 */ + .short (334 * 128 - 256 * 128) /* XFIX_1_306562965 */ + +asm_function jsimd_fdct_ifast_neon + + DATA .req r0 + TMP .req ip + + vpush {d8-d15} + + /* Load constants */ + adr TMP, jsimd_fdct_ifast_neon_consts + vld1.16 {d0}, [TMP, :64] + + /* Load all DATA into NEON registers with the following allocation: + * 0 1 2 3 | 4 5 6 7 + * ---------+-------- + * 0 | d16 | d17 | q8 + * 1 | d18 | d19 | q9 + * 2 | d20 | d21 | q10 + * 3 | d22 | d23 | q11 + * 4 | d24 | d25 | q12 + * 5 | d26 | d27 | q13 + * 6 | d28 | d29 | q14 + * 7 | d30 | d31 | q15 + */ + + vld1.16 {d16, d17, d18, d19}, [DATA, :128]! + vld1.16 {d20, d21, d22, d23}, [DATA, :128]! + vld1.16 {d24, d25, d26, d27}, [DATA, :128]! + vld1.16 {d28, d29, d30, d31}, [DATA, :128] + sub DATA, DATA, #(128 - 32) + + mov TMP, #2 +1: + /* Transpose */ + vtrn.16 q12, q13 + vtrn.16 q10, q11 + vtrn.16 q8, q9 + vtrn.16 q14, q15 + vtrn.32 q9, q11 + vtrn.32 q13, q15 + vtrn.32 q8, q10 + vtrn.32 q12, q14 + vswp d30, d23 + vswp d24, d17 + vswp d26, d19 + /* 1-D FDCT */ + vadd.s16 q2, q11, q12 + vswp d28, d21 + vsub.s16 q12, q11, q12 + vsub.s16 q6, q10, q13 + vadd.s16 q10, q10, q13 + vsub.s16 q7, q9, q14 + vadd.s16 q9, q9, q14 + vsub.s16 q1, q8, q15 + vadd.s16 q8, q8, q15 + vsub.s16 q4, q9, q10 + vsub.s16 q5, q8, q2 + vadd.s16 q3, q9, q10 + vadd.s16 q4, q4, q5 + vadd.s16 q2, q8, q2 + vqdmulh.s16 q4, q4, XFIX_0_707106781 + vadd.s16 q11, q12, q6 + vadd.s16 q8, q2, q3 + vsub.s16 q12, q2, q3 + vadd.s16 q3, q6, q7 + vadd.s16 q7, q7, q1 + vqdmulh.s16 q3, q3, XFIX_0_707106781 + vsub.s16 q6, q11, q7 + vadd.s16 q10, q5, q4 + vqdmulh.s16 q6, q6, XFIX_0_382683433 + vsub.s16 q14, q5, q4 + vqdmulh.s16 q11, q11, XFIX_0_541196100 + vqdmulh.s16 q5, q7, XFIX_1_306562965 + vadd.s16 q4, q1, q3 + vsub.s16 q3, q1, q3 + vadd.s16 q7, q7, q6 + vadd.s16 q11, q11, q6 + vadd.s16 q7, q7, q5 + vadd.s16 q13, q3, q11 + vsub.s16 q11, q3, q11 + vadd.s16 q9, q4, q7 + vsub.s16 q15, q4, q7 + subs TMP, TMP, #1 + bne 1b + + /* store results */ + vst1.16 {d16, d17, d18, d19}, [DATA, :128]! + vst1.16 {d20, d21, d22, d23}, [DATA, :128]! + vst1.16 {d24, d25, d26, d27}, [DATA, :128]! + vst1.16 {d28, d29, d30, d31}, [DATA, :128] + + vpop {d8-d15} + bx lr + + .unreq DATA + .unreq TMP +.endfunc + +/*****************************************************************************/ + +/* + * GLOBAL(void) + * jsimd_quantize_neon (JCOEFPTR coef_block, DCTELEM * divisors, + * DCTELEM * workspace); + * + * Note: the code uses 2 stage pipelining in order to improve instructions + * scheduling and eliminate stalls (this provides ~15% better + * performance for this function on both ARM Cortex-A8 and + * ARM Cortex-A9 when compared to the non-pipelined variant). + * The instructions which belong to the second stage use different + * indentation for better readiability. + */ +asm_function jsimd_quantize_neon + + COEF_BLOCK .req r0 + DIVISORS .req r1 + WORKSPACE .req r2 + + RECIPROCAL .req DIVISORS + CORRECTION .req r3 + SHIFT .req ip + LOOP_COUNT .req r4 + + vld1.16 {d0, d1, d2, d3}, [WORKSPACE, :128]! + vabs.s16 q12, q0 + add CORRECTION, DIVISORS, #(64 * 2) + add SHIFT, DIVISORS, #(64 * 6) + vld1.16 {d20, d21, d22, d23}, [CORRECTION, :128]! + vabs.s16 q13, q1 + vld1.16 {d16, d17, d18, d19}, [RECIPROCAL, :128]! + vadd.u16 q12, q12, q10 /* add correction */ + vadd.u16 q13, q13, q11 + vmull.u16 q10, d24, d16 /* multiply by reciprocal */ + vmull.u16 q11, d25, d17 + vmull.u16 q8, d26, d18 + vmull.u16 q9, d27, d19 + vld1.16 {d24, d25, d26, d27}, [SHIFT, :128]! + vshrn.u32 d20, q10, #16 + vshrn.u32 d21, q11, #16 + vshrn.u32 d22, q8, #16 + vshrn.u32 d23, q9, #16 + vneg.s16 q12, q12 + vneg.s16 q13, q13 + vshr.s16 q2, q0, #15 /* extract sign */ + vshr.s16 q3, q1, #15 + vshl.u16 q14, q10, q12 /* shift */ + vshl.u16 q15, q11, q13 + + push {r4, r5} + mov LOOP_COUNT, #3 +1: + vld1.16 {d0, d1, d2, d3}, [WORKSPACE, :128]! + veor.u16 q14, q14, q2 /* restore sign */ + vabs.s16 q12, q0 + vld1.16 {d20, d21, d22, d23}, [CORRECTION, :128]! + vabs.s16 q13, q1 + veor.u16 q15, q15, q3 + vld1.16 {d16, d17, d18, d19}, [RECIPROCAL, :128]! + vadd.u16 q12, q12, q10 /* add correction */ + vadd.u16 q13, q13, q11 + vmull.u16 q10, d24, d16 /* multiply by reciprocal */ + vmull.u16 q11, d25, d17 + vmull.u16 q8, d26, d18 + vmull.u16 q9, d27, d19 + vsub.u16 q14, q14, q2 + vld1.16 {d24, d25, d26, d27}, [SHIFT, :128]! + vsub.u16 q15, q15, q3 + vshrn.u32 d20, q10, #16 + vshrn.u32 d21, q11, #16 + vst1.16 {d28, d29, d30, d31}, [COEF_BLOCK, :128]! + vshrn.u32 d22, q8, #16 + vshrn.u32 d23, q9, #16 + vneg.s16 q12, q12 + vneg.s16 q13, q13 + vshr.s16 q2, q0, #15 /* extract sign */ + vshr.s16 q3, q1, #15 + vshl.u16 q14, q10, q12 /* shift */ + vshl.u16 q15, q11, q13 + subs LOOP_COUNT, LOOP_COUNT, #1 + bne 1b + pop {r4, r5} + + veor.u16 q14, q14, q2 /* restore sign */ + veor.u16 q15, q15, q3 + vsub.u16 q14, q14, q2 + vsub.u16 q15, q15, q3 + vst1.16 {d28, d29, d30, d31}, [COEF_BLOCK, :128]! + + bx lr /* return */ + + .unreq COEF_BLOCK + .unreq DIVISORS + .unreq WORKSPACE + .unreq RECIPROCAL + .unreq CORRECTION + .unreq SHIFT + .unreq LOOP_COUNT +.endfunc diff --git a/media/libjpeg/simd/jsimd_i386.c b/media/libjpeg/simd/jsimd_i386.c index d9bb774352a..e96f5b8d79c 100644 --- a/media/libjpeg/simd/jsimd_i386.c +++ b/media/libjpeg/simd/jsimd_i386.c @@ -2,7 +2,7 @@ * jsimd_i386.c * * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright 2009 D. R. Commander + * Copyright 2009-2011 D. R. Commander * * Based on the x86 SIMD extension for IJG JPEG library, * Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -41,7 +41,7 @@ init_simd (void) { char *env = NULL; - if (simd_support != ~0) + if (simd_support != ~0U) return; simd_support = jpeg_simd_cpu_support(); @@ -83,6 +83,28 @@ jsimd_can_rgb_ycc (void) return 0; } +GLOBAL(int) +jsimd_can_rgb_gray (void) +{ + init_simd(); + + /* The code is optimised for these values only */ + if (BITS_IN_JSAMPLE != 8) + return 0; + if (sizeof(JDIMENSION) != 4) + return 0; + if ((RGB_PIXELSIZE != 3) && (RGB_PIXELSIZE != 4)) + return 0; + + if ((simd_support & JSIMD_SSE2) && + IS_ALIGNED_SSE(jconst_rgb_gray_convert_sse2)) + return 1; + if (simd_support & JSIMD_MMX) + return 1; + + return 0; +} + GLOBAL(int) jsimd_can_ycc_rgb (void) { @@ -120,6 +142,7 @@ jsimd_rgb_ycc_convert (j_compress_ptr cinfo, mmxfct=jsimd_extrgb_ycc_convert_mmx; break; case JCS_EXT_RGBX: + case JCS_EXT_RGBA: sse2fct=jsimd_extrgbx_ycc_convert_sse2; mmxfct=jsimd_extrgbx_ycc_convert_mmx; break; @@ -128,14 +151,17 @@ jsimd_rgb_ycc_convert (j_compress_ptr cinfo, mmxfct=jsimd_extbgr_ycc_convert_mmx; break; case JCS_EXT_BGRX: + case JCS_EXT_BGRA: sse2fct=jsimd_extbgrx_ycc_convert_sse2; mmxfct=jsimd_extbgrx_ycc_convert_mmx; break; case JCS_EXT_XBGR: + case JCS_EXT_ABGR: sse2fct=jsimd_extxbgr_ycc_convert_sse2; mmxfct=jsimd_extxbgr_ycc_convert_mmx; break; case JCS_EXT_XRGB: + case JCS_EXT_ARGB: sse2fct=jsimd_extxrgb_ycc_convert_sse2; mmxfct=jsimd_extxrgb_ycc_convert_mmx; break; @@ -154,6 +180,59 @@ jsimd_rgb_ycc_convert (j_compress_ptr cinfo, output_buf, output_row, num_rows); } +GLOBAL(void) +jsimd_rgb_gray_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + void (*sse2fct)(JDIMENSION, JSAMPARRAY, JSAMPIMAGE, JDIMENSION, int); + void (*mmxfct)(JDIMENSION, JSAMPARRAY, JSAMPIMAGE, JDIMENSION, int); + + switch(cinfo->in_color_space) + { + case JCS_EXT_RGB: + sse2fct=jsimd_extrgb_gray_convert_sse2; + mmxfct=jsimd_extrgb_gray_convert_mmx; + break; + case JCS_EXT_RGBX: + case JCS_EXT_RGBA: + sse2fct=jsimd_extrgbx_gray_convert_sse2; + mmxfct=jsimd_extrgbx_gray_convert_mmx; + break; + case JCS_EXT_BGR: + sse2fct=jsimd_extbgr_gray_convert_sse2; + mmxfct=jsimd_extbgr_gray_convert_mmx; + break; + case JCS_EXT_BGRX: + case JCS_EXT_BGRA: + sse2fct=jsimd_extbgrx_gray_convert_sse2; + mmxfct=jsimd_extbgrx_gray_convert_mmx; + break; + case JCS_EXT_XBGR: + case JCS_EXT_ABGR: + sse2fct=jsimd_extxbgr_gray_convert_sse2; + mmxfct=jsimd_extxbgr_gray_convert_mmx; + break; + case JCS_EXT_XRGB: + case JCS_EXT_ARGB: + sse2fct=jsimd_extxrgb_gray_convert_sse2; + mmxfct=jsimd_extxrgb_gray_convert_mmx; + break; + default: + sse2fct=jsimd_rgb_gray_convert_sse2; + mmxfct=jsimd_rgb_gray_convert_mmx; + break; + } + + if ((simd_support & JSIMD_SSE2) && + IS_ALIGNED_SSE(jconst_rgb_gray_convert_sse2)) + sse2fct(cinfo->image_width, input_buf, + output_buf, output_row, num_rows); + else if (simd_support & JSIMD_MMX) + mmxfct(cinfo->image_width, input_buf, + output_buf, output_row, num_rows); +} + GLOBAL(void) jsimd_ycc_rgb_convert (j_decompress_ptr cinfo, JSAMPIMAGE input_buf, JDIMENSION input_row, @@ -169,6 +248,7 @@ jsimd_ycc_rgb_convert (j_decompress_ptr cinfo, mmxfct=jsimd_ycc_extrgb_convert_mmx; break; case JCS_EXT_RGBX: + case JCS_EXT_RGBA: sse2fct=jsimd_ycc_extrgbx_convert_sse2; mmxfct=jsimd_ycc_extrgbx_convert_mmx; break; @@ -177,14 +257,17 @@ jsimd_ycc_rgb_convert (j_decompress_ptr cinfo, mmxfct=jsimd_ycc_extbgr_convert_mmx; break; case JCS_EXT_BGRX: + case JCS_EXT_BGRA: sse2fct=jsimd_ycc_extbgrx_convert_sse2; mmxfct=jsimd_ycc_extbgrx_convert_mmx; break; case JCS_EXT_XBGR: + case JCS_EXT_ABGR: sse2fct=jsimd_ycc_extxbgr_convert_sse2; mmxfct=jsimd_ycc_extxbgr_convert_mmx; break; case JCS_EXT_XRGB: + case JCS_EXT_ARGB: sse2fct=jsimd_ycc_extxrgb_convert_sse2; mmxfct=jsimd_ycc_extxrgb_convert_mmx; break; @@ -461,6 +544,7 @@ jsimd_h2v2_merged_upsample (j_decompress_ptr cinfo, mmxfct=jsimd_h2v2_extrgb_merged_upsample_mmx; break; case JCS_EXT_RGBX: + case JCS_EXT_RGBA: sse2fct=jsimd_h2v2_extrgbx_merged_upsample_sse2; mmxfct=jsimd_h2v2_extrgbx_merged_upsample_mmx; break; @@ -469,14 +553,17 @@ jsimd_h2v2_merged_upsample (j_decompress_ptr cinfo, mmxfct=jsimd_h2v2_extbgr_merged_upsample_mmx; break; case JCS_EXT_BGRX: + case JCS_EXT_BGRA: sse2fct=jsimd_h2v2_extbgrx_merged_upsample_sse2; mmxfct=jsimd_h2v2_extbgrx_merged_upsample_mmx; break; case JCS_EXT_XBGR: + case JCS_EXT_ABGR: sse2fct=jsimd_h2v2_extxbgr_merged_upsample_sse2; mmxfct=jsimd_h2v2_extxbgr_merged_upsample_mmx; break; case JCS_EXT_XRGB: + case JCS_EXT_ARGB: sse2fct=jsimd_h2v2_extxrgb_merged_upsample_sse2; mmxfct=jsimd_h2v2_extxrgb_merged_upsample_mmx; break; @@ -511,6 +598,7 @@ jsimd_h2v1_merged_upsample (j_decompress_ptr cinfo, mmxfct=jsimd_h2v1_extrgb_merged_upsample_mmx; break; case JCS_EXT_RGBX: + case JCS_EXT_RGBA: sse2fct=jsimd_h2v1_extrgbx_merged_upsample_sse2; mmxfct=jsimd_h2v1_extrgbx_merged_upsample_mmx; break; @@ -519,14 +607,17 @@ jsimd_h2v1_merged_upsample (j_decompress_ptr cinfo, mmxfct=jsimd_h2v1_extbgr_merged_upsample_mmx; break; case JCS_EXT_BGRX: + case JCS_EXT_BGRA: sse2fct=jsimd_h2v1_extbgrx_merged_upsample_sse2; mmxfct=jsimd_h2v1_extbgrx_merged_upsample_mmx; break; case JCS_EXT_XBGR: + case JCS_EXT_ABGR: sse2fct=jsimd_h2v1_extxbgr_merged_upsample_sse2; mmxfct=jsimd_h2v1_extxbgr_merged_upsample_mmx; break; case JCS_EXT_XRGB: + case JCS_EXT_ARGB: sse2fct=jsimd_h2v1_extxrgb_merged_upsample_sse2; mmxfct=jsimd_h2v1_extxrgb_merged_upsample_mmx; break; diff --git a/media/libjpeg/simd/jsimd_x86_64.c b/media/libjpeg/simd/jsimd_x86_64.c index 7659249e14c..8d17db30967 100644 --- a/media/libjpeg/simd/jsimd_x86_64.c +++ b/media/libjpeg/simd/jsimd_x86_64.c @@ -2,7 +2,7 @@ * jsimd_x86_64.c * * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright 2009 D. R. Commander + * Copyright 2009-2011 D. R. Commander * * Based on the x86 SIMD extension for IJG JPEG library, * Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -46,6 +46,23 @@ jsimd_can_rgb_ycc (void) return 1; } +GLOBAL(int) +jsimd_can_rgb_gray (void) +{ + /* The code is optimised for these values only */ + if (BITS_IN_JSAMPLE != 8) + return 0; + if (sizeof(JDIMENSION) != 4) + return 0; + if ((RGB_PIXELSIZE != 3) && (RGB_PIXELSIZE != 4)) + return 0; + + if (!IS_ALIGNED_SSE(jconst_rgb_gray_convert_sse2)) + return 0; + + return 1; +} + GLOBAL(int) jsimd_can_ycc_rgb (void) { @@ -76,18 +93,22 @@ jsimd_rgb_ycc_convert (j_compress_ptr cinfo, sse2fct=jsimd_extrgb_ycc_convert_sse2; break; case JCS_EXT_RGBX: + case JCS_EXT_RGBA: sse2fct=jsimd_extrgbx_ycc_convert_sse2; break; case JCS_EXT_BGR: sse2fct=jsimd_extbgr_ycc_convert_sse2; break; case JCS_EXT_BGRX: + case JCS_EXT_BGRA: sse2fct=jsimd_extbgrx_ycc_convert_sse2; break; case JCS_EXT_XBGR: + case JCS_EXT_ABGR: sse2fct=jsimd_extxbgr_ycc_convert_sse2; break; case JCS_EXT_XRGB: + case JCS_EXT_ARGB: sse2fct=jsimd_extxrgb_ycc_convert_sse2; break; default: @@ -98,6 +119,45 @@ jsimd_rgb_ycc_convert (j_compress_ptr cinfo, sse2fct(cinfo->image_width, input_buf, output_buf, output_row, num_rows); } +GLOBAL(void) +jsimd_rgb_gray_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + void (*sse2fct)(JDIMENSION, JSAMPARRAY, JSAMPIMAGE, JDIMENSION, int); + + switch(cinfo->in_color_space) + { + case JCS_EXT_RGB: + sse2fct=jsimd_extrgb_gray_convert_sse2; + break; + case JCS_EXT_RGBX: + case JCS_EXT_RGBA: + sse2fct=jsimd_extrgbx_gray_convert_sse2; + break; + case JCS_EXT_BGR: + sse2fct=jsimd_extbgr_gray_convert_sse2; + break; + case JCS_EXT_BGRX: + case JCS_EXT_BGRA: + sse2fct=jsimd_extbgrx_gray_convert_sse2; + break; + case JCS_EXT_XBGR: + case JCS_EXT_ABGR: + sse2fct=jsimd_extxbgr_gray_convert_sse2; + break; + case JCS_EXT_XRGB: + case JCS_EXT_ARGB: + sse2fct=jsimd_extxrgb_gray_convert_sse2; + break; + default: + sse2fct=jsimd_rgb_gray_convert_sse2; + break; + } + + sse2fct(cinfo->image_width, input_buf, output_buf, output_row, num_rows); +} + GLOBAL(void) jsimd_ycc_rgb_convert (j_decompress_ptr cinfo, JSAMPIMAGE input_buf, JDIMENSION input_row, @@ -111,18 +171,22 @@ jsimd_ycc_rgb_convert (j_decompress_ptr cinfo, sse2fct=jsimd_ycc_extrgb_convert_sse2; break; case JCS_EXT_RGBX: + case JCS_EXT_RGBA: sse2fct=jsimd_ycc_extrgbx_convert_sse2; break; case JCS_EXT_BGR: sse2fct=jsimd_ycc_extbgr_convert_sse2; break; case JCS_EXT_BGRX: + case JCS_EXT_BGRA: sse2fct=jsimd_ycc_extbgrx_convert_sse2; break; case JCS_EXT_XBGR: + case JCS_EXT_ABGR: sse2fct=jsimd_ycc_extxbgr_convert_sse2; break; case JCS_EXT_XRGB: + case JCS_EXT_ARGB: sse2fct=jsimd_ycc_extxrgb_convert_sse2; break; default: @@ -321,18 +385,22 @@ jsimd_h2v2_merged_upsample (j_decompress_ptr cinfo, sse2fct=jsimd_h2v2_extrgb_merged_upsample_sse2; break; case JCS_EXT_RGBX: + case JCS_EXT_RGBA: sse2fct=jsimd_h2v2_extrgbx_merged_upsample_sse2; break; case JCS_EXT_BGR: sse2fct=jsimd_h2v2_extbgr_merged_upsample_sse2; break; case JCS_EXT_BGRX: + case JCS_EXT_BGRA: sse2fct=jsimd_h2v2_extbgrx_merged_upsample_sse2; break; case JCS_EXT_XBGR: + case JCS_EXT_ABGR: sse2fct=jsimd_h2v2_extxbgr_merged_upsample_sse2; break; case JCS_EXT_XRGB: + case JCS_EXT_ARGB: sse2fct=jsimd_h2v2_extxrgb_merged_upsample_sse2; break; default: @@ -357,18 +425,22 @@ jsimd_h2v1_merged_upsample (j_decompress_ptr cinfo, sse2fct=jsimd_h2v1_extrgb_merged_upsample_sse2; break; case JCS_EXT_RGBX: + case JCS_EXT_RGBA: sse2fct=jsimd_h2v1_extrgbx_merged_upsample_sse2; break; case JCS_EXT_BGR: sse2fct=jsimd_h2v1_extbgr_merged_upsample_sse2; break; case JCS_EXT_BGRX: + case JCS_EXT_BGRA: sse2fct=jsimd_h2v1_extbgrx_merged_upsample_sse2; break; case JCS_EXT_XBGR: + case JCS_EXT_ABGR: sse2fct=jsimd_h2v1_extxbgr_merged_upsample_sse2; break; case JCS_EXT_XRGB: + case JCS_EXT_ARGB: sse2fct=jsimd_h2v1_extxrgb_merged_upsample_sse2; break; default: diff --git a/media/libjpeg/simd/jsimdcfg.inc b/media/libjpeg/simd/jsimdcfg.inc index 68e22e8691f..9d4aedec9e3 100644 --- a/media/libjpeg/simd/jsimdcfg.inc +++ b/media/libjpeg/simd/jsimdcfg.inc @@ -13,6 +13,31 @@ %define RGB_GREEN 1 %define RGB_BLUE 2 %define RGB_PIXELSIZE 3 +%define EXT_RGB_RED 0 +%define EXT_RGB_GREEN 1 +%define EXT_RGB_BLUE 2 +%define EXT_RGB_PIXELSIZE 3 +%define EXT_RGBX_RED 0 +%define EXT_RGBX_GREEN 1 +%define EXT_RGBX_BLUE 2 +%define EXT_RGBX_PIXELSIZE 4 +%define EXT_BGR_RED 2 +%define EXT_BGR_GREEN 1 +%define EXT_BGR_BLUE 0 +%define EXT_BGR_PIXELSIZE 3 +%define EXT_BGRX_RED 2 +%define EXT_BGRX_GREEN 1 +%define EXT_BGRX_BLUE 0 +%define EXT_BGRX_PIXELSIZE 4 +%define EXT_XBGR_RED 3 +%define EXT_XBGR_GREEN 2 +%define EXT_XBGR_BLUE 1 +%define EXT_XBGR_PIXELSIZE 4 +%define EXT_XRGB_RED 1 +%define EXT_XRGB_GREEN 2 +%define EXT_XRGB_BLUE 3 +%define EXT_XRGB_PIXELSIZE 4 +%define RGBX_FILLER_0XFF 1 ; Representation of a single sample (pixel element value). ; On this SIMD implementation, this must be 'unsigned char'. ; diff --git a/media/libjpeg/simd/jsimdcfg.inc.h b/media/libjpeg/simd/jsimdcfg.inc.h deleted file mode 100644 index 4876038bc8c..00000000000 --- a/media/libjpeg/simd/jsimdcfg.inc.h +++ /dev/null @@ -1,168 +0,0 @@ -// This file generates the include file for the assembly -// implementations by abusing the C preprocessor. -// -// Note: Some things are manually defined as they need to -// be mapped to NASM types. - -; -; Automatically generated include file from jsimdcfg.inc.h -; - -#define JPEG_INTERNALS - -#include "../jpeglib.h" -#include "../jconfig.h" -#include "../jmorecfg.h" -#include "jsimd.h" - -#define define(var) %define _cpp_protection_##var -#define definev(var) %define _cpp_protection_##var var - -; -; -- jpeglib.h -; - -definev(DCTSIZE) -definev(DCTSIZE2) - -; -; -- jmorecfg.h -; - -definev(RGB_RED) -definev(RGB_GREEN) -definev(RGB_BLUE) - -definev(RGB_PIXELSIZE) - -; Representation of a single sample (pixel element value). -; On this SIMD implementation, this must be 'unsigned char'. -; - -%define JSAMPLE byte ; unsigned char -%define SIZEOF_JSAMPLE SIZEOF_BYTE ; sizeof(JSAMPLE) - -definev(CENTERJSAMPLE) - -; Representation of a DCT frequency coefficient. -; On this SIMD implementation, this must be 'short'. -; -%define JCOEF word ; short -%define SIZEOF_JCOEF SIZEOF_WORD ; sizeof(JCOEF) - -; Datatype used for image dimensions. -; On this SIMD implementation, this must be 'unsigned int'. -; -%define JDIMENSION dword ; unsigned int -%define SIZEOF_JDIMENSION SIZEOF_DWORD ; sizeof(JDIMENSION) - -%define JSAMPROW POINTER ; JSAMPLE FAR * (jpeglib.h) -%define JSAMPARRAY POINTER ; JSAMPROW * (jpeglib.h) -%define JSAMPIMAGE POINTER ; JSAMPARRAY * (jpeglib.h) -%define JCOEFPTR POINTER ; JCOEF FAR * (jpeglib.h) -%define SIZEOF_JSAMPROW SIZEOF_POINTER ; sizeof(JSAMPROW) -%define SIZEOF_JSAMPARRAY SIZEOF_POINTER ; sizeof(JSAMPARRAY) -%define SIZEOF_JSAMPIMAGE SIZEOF_POINTER ; sizeof(JSAMPIMAGE) -%define SIZEOF_JCOEFPTR SIZEOF_POINTER ; sizeof(JCOEFPTR) - -; -; -- jdct.h -; - -; A forward DCT routine is given a pointer to a work area of type DCTELEM[]; -; the DCT is to be performed in-place in that buffer. -; To maximize parallelism, Type DCTELEM is changed to short (originally, int). -; -%define DCTELEM word ; short -%define SIZEOF_DCTELEM SIZEOF_WORD ; sizeof(DCTELEM) - -%define FAST_FLOAT FP32 ; float -%define SIZEOF_FAST_FLOAT SIZEOF_FP32 ; sizeof(FAST_FLOAT) - -; To maximize parallelism, Type MULTIPLIER is changed to short. -; -%define ISLOW_MULT_TYPE word ; must be short -%define SIZEOF_ISLOW_MULT_TYPE SIZEOF_WORD ; sizeof(ISLOW_MULT_TYPE) - -%define IFAST_MULT_TYPE word ; must be short -%define SIZEOF_IFAST_MULT_TYPE SIZEOF_WORD ; sizeof(IFAST_MULT_TYPE) -%define IFAST_SCALE_BITS 2 ; fractional bits in scale factors - -%define FLOAT_MULT_TYPE FP32 ; must be float -%define SIZEOF_FLOAT_MULT_TYPE SIZEOF_FP32 ; sizeof(FLOAT_MULT_TYPE) - -; -; -- jsimd.h -; - -definev(JSIMD_NONE) -definev(JSIMD_MMX) -definev(JSIMD_3DNOW) -definev(JSIMD_SSE) -definev(JSIMD_SSE2) - -; Short forms of external names for systems with brain-damaged linkers. -; -#ifdef NEED_SHORT_EXTERNAL_NAMES -definev(jpeg_simd_cpu_support) -definev(jsimd_rgb_ycc_convert_mmx) -definev(jsimd_ycc_rgb_convert_mmx) -definev(jconst_rgb_ycc_convert_sse2) -definev(jsimd_rgb_ycc_convert_sse2) -definev(jconst_ycc_rgb_convert_sse2) -definev(jsimd_ycc_rgb_convert_sse2) -definev(jsimd_h2v2_downsample_mmx) -definev(jsimd_h2v1_downsample_mmx) -definev(jsimd_h2v2_downsample_sse2) -definev(jsimd_h2v1_downsample_sse2) -definev(jsimd_h2v2_upsample_mmx) -definev(jsimd_h2v1_upsample_mmx) -definev(jsimd_h2v1_fancy_upsample_mmx) -definev(jsimd_h2v2_fancy_upsample_mmx) -definev(jsimd_h2v1_merged_upsample_mmx) -definev(jsimd_h2v2_merged_upsample_mmx) -definev(jsimd_h2v2_upsample_sse2) -definev(jsimd_h2v1_upsample_sse2) -definev(jconst_fancy_upsample_sse2) -definev(jsimd_h2v1_fancy_upsample_sse2) -definev(jsimd_h2v2_fancy_upsample_sse2) -definev(jconst_merged_upsample_sse2) -definev(jsimd_h2v1_merged_upsample_sse2) -definev(jsimd_h2v2_merged_upsample_sse2) -definev(jsimd_convsamp_mmx) -definev(jsimd_convsamp_sse2) -definev(jsimd_convsamp_float_3dnow) -definev(jsimd_convsamp_float_sse) -definev(jsimd_convsamp_float_sse2) -definev(jsimd_fdct_islow_mmx) -definev(jsimd_fdct_ifast_mmx) -definev(jconst_fdct_islow_sse2) -definev(jsimd_fdct_islow_sse2) -definev(jconst_fdct_ifast_sse2) -definev(jsimd_fdct_ifast_sse2) -definev(jsimd_fdct_float_3dnow) -definev(jconst_fdct_float_sse) -definev(jsimd_fdct_float_sse) -definev(jsimd_quantize_mmx) -definev(jsimd_quantize_sse2) -definev(jsimd_quantize_float_3dnow) -definev(jsimd_quantize_float_sse) -definev(jsimd_quantize_float_sse2) -definev(jsimd_idct_2x2_mmx) -definev(jsimd_idct_4x4_mmx) -definev(jconst_idct_red_sse2) -definev(jsimd_idct_2x2_sse2) -definev(jsimd_idct_4x4_sse2) -definev(jsimd_idct_islow_mmx) -definev(jsimd_idct_ifast_mmx) -definev(jconst_idct_islow_sse2) -definev(jsimd_idct_islow_sse2) -definev(jconst_idct_ifast_sse2) -definev(jsimd_idct_ifast_sse2) -definev(jsimd_idct_float_3dnow) -definev(jconst_idct_float_sse) -definev(jsimd_idct_float_sse) -definev(jconst_idct_float_sse2) -definev(jsimd_idct_float_sse2) -#endif /* NEED_SHORT_EXTERNAL_NAMES */ - diff --git a/media/libjpeg/simd/jsimdext.inc b/media/libjpeg/simd/jsimdext.inc index 4ea3d17c577..4ab9bc0facd 100644 --- a/media/libjpeg/simd/jsimdext.inc +++ b/media/libjpeg/simd/jsimdext.inc @@ -38,19 +38,27 @@ ; -- segment definition -- ; +%ifdef __YASM_VER__ +%define SEG_TEXT .text align=16 +%define SEG_CONST .rdata align=16 +%else %define SEG_TEXT .text align=16 public use32 class=CODE %define SEG_CONST .rdata align=16 public use32 class=CONST +%endif %elifdef WIN64 ; ----(nasm -fwin64 -DWIN64 ...)-------- ; * Microsoft Visual C++ ; -- segment definition -- ; +%ifdef __YASM_VER__ +%define SEG_TEXT .text align=16 +%define SEG_CONST .rdata align=16 +%else %define SEG_TEXT .text align=16 public use64 class=CODE %define SEG_CONST .rdata align=16 public use64 class=CONST -%ifdef MSVC -%define EXTN(name) name ; foo() -> foo %endif +%define EXTN(name) name ; foo() -> foo %elifdef OBJ32 ; ----(nasm -fobj -DOBJ32 ...)---------- ; * Borland C++ (Win32) @@ -78,6 +86,8 @@ section .note.GNU-stack noalloc noexec nowrite progbits %define SEG_CONST .rodata progbits alloc noexec nowrite align=16 %endif +%define STRICT_MEMORY_ACCESS 1 + ; To make the code position-independent, append -DPIC to the commandline ; %define GOT_SYMBOL _GLOBAL_OFFSET_TABLE_ ; ELF supports PIC @@ -299,8 +309,6 @@ const_base: %ifdef WIN64 %imacro collect_args 0 - push r10 - push r11 push r12 push r13 push r14 @@ -330,8 +338,6 @@ const_base: pop r14 pop r13 pop r12 - pop r11 - pop r10 %endmacro %else diff --git a/media/libjpeg/transupp.h b/media/libjpeg/transupp.h deleted file mode 100644 index 7c16c19c440..00000000000 --- a/media/libjpeg/transupp.h +++ /dev/null @@ -1,210 +0,0 @@ -/* - * transupp.h - * - * Copyright (C) 1997-2009, Thomas G. Lane, Guido Vollbeding. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains declarations for image transformation routines and - * other utility code used by the jpegtran sample application. These are - * NOT part of the core JPEG library. But we keep these routines separate - * from jpegtran.c to ease the task of maintaining jpegtran-like programs - * that have other user interfaces. - * - * NOTE: all the routines declared here have very specific requirements - * about when they are to be executed during the reading and writing of the - * source and destination files. See the comments in transupp.c, or see - * jpegtran.c for an example of correct usage. - */ - -/* If you happen not to want the image transform support, disable it here */ -#ifndef TRANSFORMS_SUPPORTED -#define TRANSFORMS_SUPPORTED 1 /* 0 disables transform code */ -#endif - -/* - * Although rotating and flipping data expressed as DCT coefficients is not - * hard, there is an asymmetry in the JPEG format specification for images - * whose dimensions aren't multiples of the iMCU size. The right and bottom - * image edges are padded out to the next iMCU boundary with junk data; but - * no padding is possible at the top and left edges. If we were to flip - * the whole image including the pad data, then pad garbage would become - * visible at the top and/or left, and real pixels would disappear into the - * pad margins --- perhaps permanently, since encoders & decoders may not - * bother to preserve DCT blocks that appear to be completely outside the - * nominal image area. So, we have to exclude any partial iMCUs from the - * basic transformation. - * - * Transpose is the only transformation that can handle partial iMCUs at the - * right and bottom edges completely cleanly. flip_h can flip partial iMCUs - * at the bottom, but leaves any partial iMCUs at the right edge untouched. - * Similarly flip_v leaves any partial iMCUs at the bottom edge untouched. - * The other transforms are defined as combinations of these basic transforms - * and process edge blocks in a way that preserves the equivalence. - * - * The "trim" option causes untransformable partial iMCUs to be dropped; - * this is not strictly lossless, but it usually gives the best-looking - * result for odd-size images. Note that when this option is active, - * the expected mathematical equivalences between the transforms may not hold. - * (For example, -rot 270 -trim trims only the bottom edge, but -rot 90 -trim - * followed by -rot 180 -trim trims both edges.) - * - * We also offer a lossless-crop option, which discards data outside a given - * image region but losslessly preserves what is inside. Like the rotate and - * flip transforms, lossless crop is restricted by the JPEG format: the upper - * left corner of the selected region must fall on an iMCU boundary. If this - * does not hold for the given crop parameters, we silently move the upper left - * corner up and/or left to make it so, simultaneously increasing the region - * dimensions to keep the lower right crop corner unchanged. (Thus, the - * output image covers at least the requested region, but may cover more.) - * - * We also provide a lossless-resize option, which is kind of a lossless-crop - * operation in the DCT coefficient block domain - it discards higher-order - * coefficients and losslessly preserves lower-order coefficients of a - * sub-block. - * - * Rotate/flip transform, resize, and crop can be requested together in a - * single invocation. The crop is applied last --- that is, the crop region - * is specified in terms of the destination image after transform/resize. - * - * We also offer a "force to grayscale" option, which simply discards the - * chrominance channels of a YCbCr image. This is lossless in the sense that - * the luminance channel is preserved exactly. It's not the same kind of - * thing as the rotate/flip transformations, but it's convenient to handle it - * as part of this package, mainly because the transformation routines have to - * be aware of the option to know how many components to work on. - */ - - -/* Short forms of external names for systems with brain-damaged linkers. */ - -#ifdef NEED_SHORT_EXTERNAL_NAMES -#define jtransform_parse_crop_spec jTrParCrop -#define jtransform_request_workspace jTrRequest -#define jtransform_adjust_parameters jTrAdjust -#define jtransform_execute_transform jTrExec -#define jtransform_perfect_transform jTrPerfect -#define jcopy_markers_setup jCMrkSetup -#define jcopy_markers_execute jCMrkExec -#endif /* NEED_SHORT_EXTERNAL_NAMES */ - - -/* - * Codes for supported types of image transformations. - */ - -typedef enum { - JXFORM_NONE, /* no transformation */ - JXFORM_FLIP_H, /* horizontal flip */ - JXFORM_FLIP_V, /* vertical flip */ - JXFORM_TRANSPOSE, /* transpose across UL-to-LR axis */ - JXFORM_TRANSVERSE, /* transpose across UR-to-LL axis */ - JXFORM_ROT_90, /* 90-degree clockwise rotation */ - JXFORM_ROT_180, /* 180-degree rotation */ - JXFORM_ROT_270 /* 270-degree clockwise (or 90 ccw) */ -} JXFORM_CODE; - -/* - * Codes for crop parameters, which can individually be unspecified, - * positive, or negative. (Negative width or height makes no sense, though.) - */ - -typedef enum { - JCROP_UNSET, - JCROP_POS, - JCROP_NEG -} JCROP_CODE; - -/* - * Transform parameters struct. - * NB: application must not change any elements of this struct after - * calling jtransform_request_workspace. - */ - -typedef struct { - /* Options: set by caller */ - JXFORM_CODE transform; /* image transform operator */ - boolean perfect; /* if TRUE, fail if partial MCUs are requested */ - boolean trim; /* if TRUE, trim partial MCUs as needed */ - boolean force_grayscale; /* if TRUE, convert color image to grayscale */ - boolean crop; /* if TRUE, crop source image */ - - /* Crop parameters: application need not set these unless crop is TRUE. - * These can be filled in by jtransform_parse_crop_spec(). - */ - JDIMENSION crop_width; /* Width of selected region */ - JCROP_CODE crop_width_set; - JDIMENSION crop_height; /* Height of selected region */ - JCROP_CODE crop_height_set; - JDIMENSION crop_xoffset; /* X offset of selected region */ - JCROP_CODE crop_xoffset_set; /* (negative measures from right edge) */ - JDIMENSION crop_yoffset; /* Y offset of selected region */ - JCROP_CODE crop_yoffset_set; /* (negative measures from bottom edge) */ - - /* Internal workspace: caller should not touch these */ - int num_components; /* # of components in workspace */ - jvirt_barray_ptr * workspace_coef_arrays; /* workspace for transformations */ - JDIMENSION output_width; /* cropped destination dimensions */ - JDIMENSION output_height; - JDIMENSION x_crop_offset; /* destination crop offsets measured in iMCUs */ - JDIMENSION y_crop_offset; - int iMCU_sample_width; /* destination iMCU size */ - int iMCU_sample_height; -} jpeg_transform_info; - - -#if TRANSFORMS_SUPPORTED - -/* Parse a crop specification (written in X11 geometry style) */ -EXTERN(boolean) jtransform_parse_crop_spec - JPP((jpeg_transform_info *info, const char *spec)); -/* Request any required workspace */ -EXTERN(boolean) jtransform_request_workspace - JPP((j_decompress_ptr srcinfo, jpeg_transform_info *info)); -/* Adjust output image parameters */ -EXTERN(jvirt_barray_ptr *) jtransform_adjust_parameters - JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, - jvirt_barray_ptr *src_coef_arrays, - jpeg_transform_info *info)); -/* Execute the actual transformation, if any */ -EXTERN(void) jtransform_execute_transform - JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, - jvirt_barray_ptr *src_coef_arrays, - jpeg_transform_info *info)); -/* Determine whether lossless transformation is perfectly - * possible for a specified image and transformation. - */ -EXTERN(boolean) jtransform_perfect_transform - JPP((JDIMENSION image_width, JDIMENSION image_height, - int MCU_width, int MCU_height, - JXFORM_CODE transform)); - -/* jtransform_execute_transform used to be called - * jtransform_execute_transformation, but some compilers complain about - * routine names that long. This macro is here to avoid breaking any - * old source code that uses the original name... - */ -#define jtransform_execute_transformation jtransform_execute_transform - -#endif /* TRANSFORMS_SUPPORTED */ - - -/* - * Support for copying optional markers from source to destination file. - */ - -typedef enum { - JCOPYOPT_NONE, /* copy no optional markers */ - JCOPYOPT_COMMENTS, /* copy only comment (COM) markers */ - JCOPYOPT_ALL /* copy all optional markers */ -} JCOPY_OPTION; - -#define JCOPYOPT_DEFAULT JCOPYOPT_COMMENTS /* recommended default */ - -/* Setup decompression object to save desired markers in memory */ -EXTERN(void) jcopy_markers_setup - JPP((j_decompress_ptr srcinfo, JCOPY_OPTION option)); -/* Copy markers saved in the given source object to the destination object */ -EXTERN(void) jcopy_markers_execute - JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, - JCOPY_OPTION option)); diff --git a/mfbt/GuardObjects.h b/mfbt/GuardObjects.h index 2e50405b062..68d5eb35d06 100644 --- a/mfbt/GuardObjects.h +++ b/mfbt/GuardObjects.h @@ -163,6 +163,8 @@ class MOZ_EXPORT_API(GuardObjectNotificationReceiver) , const mozilla::detail::GuardObjectNotifier& _notifier # define MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT \ , _notifier +# define MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_TO_PARENT \ + _notifier # define MOZ_GUARD_OBJECT_NOTIFIER_INIT \ do { _mCheckNotUsedAsTemporary.init(_notifier); } while (0) #else @@ -170,6 +172,7 @@ class MOZ_EXPORT_API(GuardObjectNotificationReceiver) # define MOZ_GUARD_OBJECT_NOTIFIER_PARAM # define MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM # define MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL +# define MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_TO_PARENT # define MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT # define MOZ_GUARD_OBJECT_NOTIFIER_INIT do { } while (0) #endif diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 2ff56c2a674..c55b95d3208 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -623,25 +623,7 @@ abstract public class GeckoApp int sh = forceBigSceenshot ? mLayerClient.getHeight(): tab.getMinScreenshotHeight(); int dw = forceBigSceenshot ? sw : tab.getThumbnailWidth(); int dh = forceBigSceenshot ? sh : tab.getThumbnailHeight(); - try { - JSONObject message = new JSONObject(); - message.put("tabID", tab.getId()); - - JSONObject source = new JSONObject(); - source.put("width", sw); - source.put("height", sh); - message.put("source", source); - - JSONObject destination = new JSONObject(); - destination.put("width", dw); - destination.put("height", dh); - message.put("destination", destination); - - String json = message.toString(); - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Tab:Screenshot", json)); - } catch(JSONException jsonEx) { - Log.w(LOGTAG, "Constructing the JSON data for Tab:Screenshot event failed", jsonEx); - } + GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(), sw, sh, dw, dh)); } } @@ -1645,7 +1627,8 @@ abstract public class GeckoApp WindowManager.LayoutParams.FLAG_FULLSCREEN : 0, WindowManager.LayoutParams.FLAG_FULLSCREEN); - window.getDecorView().setSystemUiVisibility(fullscreen ? 1 : 0); + if (Build.VERSION.SDK_INT >= 11) + window.getDecorView().setSystemUiVisibility(fullscreen ? 1 : 0); } }); } diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index b2a98b9195e..7413cfd673f 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -116,6 +116,14 @@ public class GeckoAppShell private static HashMap> mEventListeners; + /* Is the value in sVibrationEndTime valid? */ + private static boolean sVibrationMaybePlaying = false; + + /* Time (in System.nanoTime() units) when the currently-playing vibration + * is scheduled to end. This value is valid only when + * sVibrationMaybePlaying is true. */ + private static long sVibrationEndTime = 0; + /* The Android-side API: API methods that Android calls */ // Initialization methods @@ -560,12 +568,14 @@ public class GeckoAppShell mInputConnection.notifyIMEChange(text, start, end, newEnd); } - public static void notifyScreenShot(ByteBuffer data, int tabId, int width, int height) { - final Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); - b.copyPixelsFromBuffer(data); - final Tab tab = Tabs.getInstance().getTab(tabId); + public static void notifyScreenShot(final ByteBuffer data, final int tabId, + final int width, final int height) { getHandler().post(new Runnable() { public void run() { + Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + b.copyPixelsFromBuffer(data); + freeDirectBuffer(data); + final Tab tab = Tabs.getInstance().getTab(tabId); GeckoApp.mAppContext.processThumbnail(tab, b, null); } }); @@ -1125,11 +1135,15 @@ public class GeckoAppShell } public static void performHapticFeedback(boolean aIsLongPress) { - LayerController layerController = GeckoApp.mAppContext.getLayerController(); - LayerView layerView = layerController.getView(); - layerView.performHapticFeedback(aIsLongPress ? - HapticFeedbackConstants.LONG_PRESS : - HapticFeedbackConstants.VIRTUAL_KEY); + // Don't perform haptic feedback if a vibration is currently playing, + // because the haptic feedback will nuke the vibration. + if (!sVibrationMaybePlaying || System.nanoTime() >= sVibrationEndTime) { + LayerController layerController = GeckoApp.mAppContext.getLayerController(); + LayerView layerView = layerController.getView(); + layerView.performHapticFeedback(aIsLongPress ? + HapticFeedbackConstants.LONG_PRESS : + HapticFeedbackConstants.VIRTUAL_KEY); + } } private static Vibrator vibrator() { @@ -1140,14 +1154,28 @@ public class GeckoAppShell } public static void vibrate(long milliseconds) { + sVibrationEndTime = System.nanoTime() + milliseconds * 1000000; + sVibrationMaybePlaying = true; vibrator().vibrate(milliseconds); } public static void vibrate(long[] pattern, int repeat) { + // If pattern.length is even, the last element in the pattern is a + // meaningless delay, so don't include it in vibrationDuration. + long vibrationDuration = 0; + int iterLen = pattern.length - (pattern.length % 2 == 0 ? 1 : 0); + for (int i = 0; i < iterLen; i++) { + vibrationDuration += pattern[i]; + } + + sVibrationEndTime = System.nanoTime() + vibrationDuration * 1000000; + sVibrationMaybePlaying = true; vibrator().vibrate(pattern, repeat); } public static void cancelVibrate() { + sVibrationMaybePlaying = false; + sVibrationEndTime = 0; vibrator().cancel(); } diff --git a/mobile/android/base/GeckoEvent.java b/mobile/android/base/GeckoEvent.java index eb622c7770d..bfea49b4edc 100644 --- a/mobile/android/base/GeckoEvent.java +++ b/mobile/android/base/GeckoEvent.java @@ -88,6 +88,7 @@ public class GeckoEvent { private static final int NETWORK_CHANGED = 22; private static final int PROXIMITY_EVENT = 23; private static final int ACTIVITY_RESUMING = 24; + private static final int SCREENSHOT = 25; public static final int IME_COMPOSITION_END = 0; public static final int IME_COMPOSITION_BEGIN = 1; @@ -399,4 +400,13 @@ public class GeckoEvent { event.mCanBeMetered = canBeMetered; return event; } + + public static GeckoEvent createScreenshotEvent(int tabId, int sw, int sh, int dw, int dh) { + GeckoEvent event = new GeckoEvent(SCREENSHOT); + event.mPoints = new Point[2]; + event.mPoints[0] = new Point(sw, sh); + event.mPoints[1] = new Point(dw, dh); + event.mMetaState = tabId; + return event; + } } diff --git a/mobile/android/base/sync/CollectionKeys.java b/mobile/android/base/sync/CollectionKeys.java index dfd12275560..8a8f0903c9f 100644 --- a/mobile/android/base/sync/CollectionKeys.java +++ b/mobile/android/base/sync/CollectionKeys.java @@ -42,12 +42,11 @@ import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Map.Entry; -import org.mozilla.apache.commons.codec.binary.Base64; import org.json.JSONException; import org.json.simple.JSONArray; import org.json.simple.parser.ParseException; +import org.mozilla.apache.commons.codec.binary.Base64; import org.mozilla.gecko.sync.crypto.CryptoException; -import org.mozilla.gecko.sync.crypto.Cryptographer; import org.mozilla.gecko.sync.crypto.KeyBundle; import android.util.Log; @@ -68,9 +67,16 @@ public class CollectionKeys { } } + /** + * Randomly generate a basic CollectionKeys object. + * @throws CryptoException + */ public static CollectionKeys generateCollectionKeys() throws CryptoException { CollectionKeys ck = new CollectionKeys(); - ck.populate(); + ck.clear(); + ck.defaultKeyBundle = KeyBundle.withRandomKeys(); + // TODO: eventually we would like to keep per-collection keys, just generate + // new ones as appropriate. return ck; } @@ -104,7 +110,7 @@ public class CollectionKeys { private static KeyBundle arrayToKeyBundle(JSONArray array) throws UnsupportedEncodingException { String encKeyStr = (String) array.get(0); String hmacKeyStr = (String) array.get(1); - return KeyBundle.decodeKeyStrings(encKeyStr, hmacKeyStr); + return KeyBundle.fromBase64EncodedKeys(encKeyStr, hmacKeyStr); } @SuppressWarnings("unchecked") @@ -204,15 +210,4 @@ public class CollectionKeys { this.defaultKeyBundle = null; this.collectionKeyBundles = new HashMap(); } - - /** - * Randomly generate a basic CollectionKeys object. - * @throws CryptoException - */ - public void populate() throws CryptoException { - this.clear(); - this.defaultKeyBundle = Cryptographer.generateKeys(); - // TODO: eventually we would like to keep per-collection keys, just generate - // new ones as appropriate. - } } diff --git a/mobile/android/base/sync/CryptoRecord.java b/mobile/android/base/sync/CryptoRecord.java index 912fb77417f..e5dc6e6c841 100644 --- a/mobile/android/base/sync/CryptoRecord.java +++ b/mobile/android/base/sync/CryptoRecord.java @@ -46,7 +46,6 @@ import org.json.simple.parser.ParseException; import org.mozilla.apache.commons.codec.binary.Base64; import org.mozilla.gecko.sync.crypto.CryptoException; import org.mozilla.gecko.sync.crypto.CryptoInfo; -import org.mozilla.gecko.sync.crypto.Cryptographer; import org.mozilla.gecko.sync.crypto.KeyBundle; import org.mozilla.gecko.sync.crypto.MissingCryptoInputException; import org.mozilla.gecko.sync.crypto.NoKeyBundleException; @@ -65,6 +64,8 @@ import org.mozilla.gecko.sync.repositories.domain.Record; * Until there's some benefit to the abstraction, we're simply going to * call this CryptoRecord. * + * CryptoRecord uses CryptoInfo to do the actual encryption and decryption. + * * @author rnewman * */ @@ -92,7 +93,8 @@ public class CryptoRecord extends Record { byte[] ciphertext = Base64.decodeBase64(((String) payload.get(KEY_CIPHERTEXT)).getBytes("UTF-8")); byte[] iv = Base64.decodeBase64(((String) payload.get(KEY_IV)).getBytes("UTF-8")); byte[] hmac = Utils.hex2Byte((String) payload.get(KEY_HMAC)); - return Cryptographer.decrypt(new CryptoInfo(ciphertext, iv, hmac, keybundle)); + + return CryptoInfo.decrypt(ciphertext, iv, hmac, keybundle).getMessage(); } // The encrypted JSON body object. @@ -221,8 +223,7 @@ public class CryptoRecord extends Record { } String cleartext = payload.toJSONString(); byte[] cleartextBytes = cleartext.getBytes("UTF-8"); - CryptoInfo info = new CryptoInfo(cleartextBytes, keyBundle); - Cryptographer.encrypt(info); + CryptoInfo info = CryptoInfo.encrypt(cleartextBytes, keyBundle); String message = new String(Base64.encodeBase64(info.getMessage())); String iv = new String(Base64.encodeBase64(info.getIV())); String hmac = Utils.byte2hex(info.getHMAC()); diff --git a/mobile/android/base/sync/DelayedWorkTracker.java b/mobile/android/base/sync/DelayedWorkTracker.java index ec45b91c9e1..aef33f8a1aa 100644 --- a/mobile/android/base/sync/DelayedWorkTracker.java +++ b/mobile/android/base/sync/DelayedWorkTracker.java @@ -37,8 +37,6 @@ package org.mozilla.gecko.sync; -import android.util.Log; - /** * A little class to allow us to maintain a count of extant * things (in our case, callbacks that need to fire), and @@ -53,13 +51,13 @@ public class DelayedWorkTracker { protected int outstandingCount = 0; public int incrementOutstanding() { - Log.d(LOG_TAG, "Incrementing outstanding."); + Logger.trace(LOG_TAG, "Incrementing outstanding."); synchronized(this) { return ++outstandingCount; } } public int decrementOutstanding() { - Log.d(LOG_TAG, "Decrementing outstanding."); + Logger.trace(LOG_TAG, "Decrementing outstanding."); Runnable job = null; int count; synchronized(this) { @@ -81,10 +79,10 @@ public class DelayedWorkTracker { } } public void delayWorkItem(Runnable item) { - Log.d(LOG_TAG, "delayWorkItem."); + Logger.trace(LOG_TAG, "delayWorkItem."); boolean runnableNow = false; synchronized(this) { - Log.d(LOG_TAG, "outstandingCount: " + outstandingCount); + Logger.trace(LOG_TAG, "outstandingCount: " + outstandingCount); if (outstandingCount == 0) { runnableNow = true; } else { @@ -95,7 +93,7 @@ public class DelayedWorkTracker { } } if (runnableNow) { - Log.d(LOG_TAG, "Running item now."); + Logger.trace(LOG_TAG, "Running item now."); item.run(); } } diff --git a/mobile/android/base/sync/GlobalSession.java b/mobile/android/base/sync/GlobalSession.java index c9ea8028aaf..d448ba8fa39 100644 --- a/mobile/android/base/sync/GlobalSession.java +++ b/mobile/android/base/sync/GlobalSession.java @@ -58,7 +58,6 @@ import org.mozilla.gecko.sync.net.SyncStorageRecordRequest; import org.mozilla.gecko.sync.net.SyncStorageRequest; import org.mozilla.gecko.sync.net.SyncStorageRequestDelegate; import org.mozilla.gecko.sync.net.SyncStorageResponse; -import org.mozilla.gecko.sync.repositories.RepositorySessionBundle; import org.mozilla.gecko.sync.stage.AndroidBrowserBookmarksServerSyncStage; import org.mozilla.gecko.sync.stage.AndroidBrowserHistoryServerSyncStage; import org.mozilla.gecko.sync.stage.CheckPreconditionsStage; @@ -71,12 +70,11 @@ import org.mozilla.gecko.sync.stage.GlobalSyncStage; import org.mozilla.gecko.sync.stage.GlobalSyncStage.Stage; import org.mozilla.gecko.sync.stage.NoSuchStageException; -import ch.boye.httpclientandroidlib.HttpResponse; - import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; import android.util.Log; +import ch.boye.httpclientandroidlib.HttpResponse; public class GlobalSession implements CredentialsSource, PrefsSource { private static final String LOG_TAG = "GlobalSession"; @@ -150,7 +148,7 @@ public class GlobalSession implements CredentialsSource, PrefsSource { KeyBundle syncKeyBundle, GlobalSessionCallback callback, Context context, - Bundle persisted) + Bundle extras) throws SyncConfigurationException, IllegalArgumentException, IOException, ParseException, NonObjectJSONException { if (callback == null) { throw new IllegalArgumentException("Must provide a callback to GlobalSession constructor."); @@ -160,7 +158,7 @@ public class GlobalSession implements CredentialsSource, PrefsSource { throw new SyncConfigurationException(); } - Log.i(LOG_TAG, "GlobalSession initialized with bundle " + persisted); + Log.i(LOG_TAG, "GlobalSession initialized with bundle " + extras); URI serverURI; try { serverURI = (serverURL == null) ? null : new URI(serverURL); @@ -183,9 +181,7 @@ public class GlobalSession implements CredentialsSource, PrefsSource { config.username = username; config.password = password; config.syncKeyBundle = syncKeyBundle; - // clusterURL and syncID are set through `persisted`, or fetched from the server. - assert(null == persisted); prepareStages(); } diff --git a/mobile/android/base/sync/Logger.java b/mobile/android/base/sync/Logger.java new file mode 100644 index 00000000000..a41fd220dce --- /dev/null +++ b/mobile/android/base/sync/Logger.java @@ -0,0 +1,168 @@ +/* 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/. */ + +package org.mozilla.gecko.sync; + +import java.util.IdentityHashMap; +import java.util.Map; + +import android.util.Log; + +/** + * Logging helper class. Serializes all log operations (by synchronizing), + * and caches log level settings. + * + * Ultimately this will also be a hook point for our own logging system. + * + * @author rnewman + * + */ +public class Logger { + + // For extra debugging. + public static boolean LOG_PERSONAL_INFORMATION = false; + + // If true, log to System.out as well as using Android's Log.* calls. + public static boolean LOG_TO_STDOUT = false; + + // I can't believe we have to implement this ourselves. + // These aren't synchronized (and neither are the setters) because + // the logging calls themselves are synchronized. + private static Map isErrorLoggable = new IdentityHashMap(); + private static Map isWarnLoggable = new IdentityHashMap(); + private static Map isInfoLoggable = new IdentityHashMap(); + private static Map isDebugLoggable = new IdentityHashMap(); + private static Map isVerboseLoggable = new IdentityHashMap(); + + /** + * Empty the caches of log levels. + */ + public synchronized void refreshLogLevels() { + isErrorLoggable = new IdentityHashMap(); + isWarnLoggable = new IdentityHashMap(); + isInfoLoggable = new IdentityHashMap(); + isDebugLoggable = new IdentityHashMap(); + isVerboseLoggable = new IdentityHashMap(); + } + + private static boolean shouldLogError(String logTag) { + Boolean out = isErrorLoggable.get(logTag); + if (out != null) { + return out.booleanValue(); + } + out = Log.isLoggable(logTag, Log.ERROR); + isErrorLoggable.put(logTag, out); + return out; + } + + private static boolean shouldLogWarn(String logTag) { + Boolean out = isWarnLoggable.get(logTag); + if (out != null) { + return out.booleanValue(); + } + out = Log.isLoggable(logTag, Log.WARN); + isWarnLoggable.put(logTag, out); + return out; + } + + private static boolean shouldLogInfo(String logTag) { + Boolean out = isInfoLoggable.get(logTag); + if (out != null) { + return out.booleanValue(); + } + Log.d("XXX", "Calling out to isLoggable for INFO!"); + out = Log.isLoggable(logTag, Log.INFO); + isInfoLoggable.put(logTag, out); + return out; + } + + private static boolean shouldLogDebug(String logTag) { + Boolean out = isDebugLoggable.get(logTag); + if (out != null) { + return out.booleanValue(); + } + Log.d("XXX", "Calling out to isLoggable for DEBUG!"); + out = Log.isLoggable(logTag, Log.DEBUG); + isDebugLoggable.put(logTag, out); + return out; + } + + private static boolean shouldLogVerbose(String logTag) { + Boolean out = isVerboseLoggable.get(logTag); + if (out != null) { + return out.booleanValue(); + } + Log.d("XXX", "Calling out to isLoggable for VERBOSE!"); + out = Log.isLoggable(logTag, Log.VERBOSE); + isVerboseLoggable.put(logTag, out); + return out; + } + + // Synchronized version for other classes to use. + public static synchronized boolean logVerbose(String logTag) { + return shouldLogVerbose(logTag); + } + + private static void logToStdout(String... s) { + if (LOG_TO_STDOUT) { + for (String string : s) { + System.out.print(string); + } + System.out.println(""); + } + } + + public static void error(String logTag, String message) { + Logger.error(logTag, message, null); + } + + public static synchronized void error(String logTag, String message, Throwable error) { + logToStdout(logTag, " :: ERROR: ", message); + if (shouldLogError(logTag)) { + Log.e(logTag, message, error); + } + } + + public static void warn(String logTag, String message) { + Logger.warn(logTag, message, null); + } + + public static synchronized void warn(String logTag, String message, Throwable error) { + logToStdout(logTag, " :: WARN: ", message); + if (shouldLogWarn(logTag)) { + Log.w(logTag, message, error); + } + } + + public static synchronized void info(String logTag, String message) { + logToStdout(logTag, " :: INFO: ", message); + if (shouldLogInfo(logTag)) { + Log.i(logTag, message); + } + } + + public static void debug(String logTag, String message) { + Logger.debug(logTag, message, null); + } + + public static synchronized void debug(String logTag, String message, Throwable error) { + logToStdout(logTag, " :: DEBUG: ", message); + if (shouldLogDebug(logTag)) { + Log.d(logTag, message, error); + } + } + + public static synchronized void trace(String logTag, String message) { + logToStdout(logTag, " :: TRACE: ", message); + if (shouldLogVerbose(logTag)) { + Log.v(logTag, message); + } + } + + public static void pii(String logTag, String message) { + if (LOG_PERSONAL_INFORMATION) { + Logger.debug(logTag, "$$PII$$: " + message); + } + } +} diff --git a/mobile/android/base/sync/SynchronizerConfiguration.java b/mobile/android/base/sync/SynchronizerConfiguration.java index fcc06d89d35..7ba744aa16e 100644 --- a/mobile/android/base/sync/SynchronizerConfiguration.java +++ b/mobile/android/base/sync/SynchronizerConfiguration.java @@ -47,7 +47,7 @@ import android.content.SharedPreferences.Editor; import android.util.Log; public class SynchronizerConfiguration { - private static final String LOG_TAG = "SynchronizerConfiguration"; + private static final String LOG_TAG = "SynczrConfiguration"; public String syncID; public RepositorySessionBundle remoteBundle; diff --git a/mobile/android/base/sync/SynchronizerConfigurations.java b/mobile/android/base/sync/SynchronizerConfigurations.java index d571fd1e5cd..f7ccc9114eb 100644 --- a/mobile/android/base/sync/SynchronizerConfigurations.java +++ b/mobile/android/base/sync/SynchronizerConfigurations.java @@ -39,7 +39,6 @@ package org.mozilla.gecko.sync; import java.io.IOException; import java.util.HashMap; -import java.util.Map.Entry; import java.util.Set; import org.json.simple.parser.ParseException; @@ -48,7 +47,6 @@ import org.mozilla.gecko.sync.repositories.RepositorySessionBundle; import android.os.Bundle; public class SynchronizerConfigurations { - private static final int CONFIGURATION_VERSION = 1; private HashMap engines; protected HashMap enginesMapFromBundleV1(Bundle engineBundle) throws IOException, ParseException, NonObjectJSONException { diff --git a/mobile/android/base/sync/Utils.java b/mobile/android/base/sync/Utils.java index 0f5be6122bf..a12ea17e168 100644 --- a/mobile/android/base/sync/Utils.java +++ b/mobile/android/base/sync/Utils.java @@ -40,103 +40,86 @@ package org.mozilla.gecko.sync; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; +import java.math.BigInteger; +import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; import java.util.HashMap; -import java.util.Random; import org.mozilla.apache.commons.codec.binary.Base32; import org.mozilla.apache.commons.codec.binary.Base64; -import org.mozilla.gecko.sync.crypto.Cryptographer; import android.content.Context; import android.content.SharedPreferences; -import android.util.Log; public class Utils { private static final String LOG_TAG = "Utils"; + private static SecureRandom sharedSecureRandom = new SecureRandom(); + // See public static final int SHARED_PREFERENCES_MODE = 0; - - // We don't really have a trace logger, so use this to toggle - // some debug logging. - // This is awful. I'm so sorry. - public static boolean ENABLE_TRACE_LOGGING = true; - - // If true, log to System.out as well as using Android's Log.* calls. - public static boolean LOG_TO_STDOUT = false; - public static void logToStdout(String... s) { - if (LOG_TO_STDOUT) { - for (String string : s) { - System.out.print(string); - } - System.out.println(""); - } - } - - public static void error(String logTag, String message) { - logToStdout(logTag, " :: ERROR: ", message); - Log.i(logTag, message); - } - - public static void info(String logTag, String message) { - logToStdout(logTag, " :: INFO: ", message); - Log.i(logTag, message); - } - - public static void debug(String logTag, String message) { - logToStdout(logTag, " :: DEBUG: ", message); - Log.d(logTag, message); - } - - public static void trace(String logTag, String message) { - if (!ENABLE_TRACE_LOGGING) { - return; - } - logToStdout(logTag, " :: TRACE: ", message); - Log.d(logTag, message); - } - public static String generateGuid() { byte[] encodedBytes = Base64.encodeBase64(generateRandomBytes(9), false); return new String(encodedBytes).replace("+", "-").replace("/", "_"); } - private static byte[] generateRandomBytes(int length) { + /* + * Helper to generate secure random bytes. + * + * @param length Number of bytes to generate. + */ + public static byte[] generateRandomBytes(int length) { byte[] bytes = new byte[length]; - Random random = new Random(System.nanoTime()); - random.nextBytes(bytes); + sharedSecureRandom.nextBytes(bytes); return bytes; } + /* + * Helper to generate a random integer in a specified range. + * + * @param r Generate an integer between 0 and r-1 inclusive. + */ + public static BigInteger generateBigIntegerLessThan(BigInteger r) { + int maxBytes = (int) Math.ceil(((double) r.bitLength()) / 8); + BigInteger randInt = new BigInteger(generateRandomBytes(maxBytes)); + return randInt.mod(r); + } + + /* + * Helper to reseed the shared secure random number generator. + */ + public static void reseedSharedRandom() { + sharedSecureRandom.setSeed(sharedSecureRandom.generateSeed(8)); + } + /* * Helper to convert Byte Array to a Hex String * Input: byte[] array * Output: Hex string */ public static String byte2hex(byte[] b) { - - // String Buffer can be used instead - String hs = ""; - String stmp = ""; - - for (int n = 0; n < b.length; n++) { - stmp = (java.lang.Integer.toHexString(b[n] & 0XFF)); - - if (stmp.length() == 1) { - hs = hs + "0" + stmp; - } else { - hs = hs + stmp; - } - - if (n < b.length - 1) { - hs = hs + ""; - } + // StringBuffer should be used instead. + String hs = ""; + String stmp; + + for (int n = 0; n < b.length; n++) { + stmp = java.lang.Integer.toHexString(b[n] & 0XFF); + + if (stmp.length() == 1) { + hs = hs + "0" + stmp; + } else { + hs = hs + stmp; } - - return hs; + + if (n < b.length - 1) { + hs = hs + ""; + } + } + + return hs; } /* @@ -145,21 +128,21 @@ public class Utils { * Output: A concatenated version of them */ public static byte[] concatAll(byte[] first, byte[]... rest) { - int totalLength = first.length; - for (byte[] array : rest) { - totalLength += array.length; - } - - byte[] result = new byte[totalLength]; - int offset = first.length; + int totalLength = first.length; + for (byte[] array : rest) { + totalLength += array.length; + } - System.arraycopy(first, 0, result, 0, offset); - - for (byte[] array : rest) { - System.arraycopy(array, 0, result, offset, array.length); - offset += array.length; - } - return result; + byte[] result = new byte[totalLength]; + int offset = first.length; + + System.arraycopy(first, 0, result, 0, offset); + + for (byte[] array : rest) { + System.arraycopy(array, 0, result, offset, array.length); + offset += array.length; + } + return result; } /** @@ -174,16 +157,16 @@ public class Utils { * Should not occur. */ public static byte[] decodeBase64(String base64) throws UnsupportedEncodingException { - return Base64.decodeBase64(base64.getBytes("UTF-8")); + return Base64.decodeBase64(base64.getBytes("UTF-8")); } /* * Decode a friendly base32 string. */ public static byte[] decodeFriendlyBase32(String base32) { - Base32 converter = new Base32(); - return converter.decode(base32.replace('8', 'l').replace('9', 'o') - .toUpperCase()); + Base32 converter = new Base32(); + final String translated = base32.replace('8', 'l').replace('9', 'o'); + return converter.decode(translated.toUpperCase()); } /* @@ -191,19 +174,16 @@ public class Utils { * Input: Hex string * Output: byte[] version of hex string */ - public static byte[] hex2Byte(String str) - { - if (str.length() % 2 == 1) { - str = "0" + str; - } - - byte[] bytes = new byte[str.length() / 2]; - for (int i = 0; i < bytes.length; i++) - { - bytes[i] = (byte) Integer - .parseInt(str.substring(2 * i, 2 * i + 2), 16); - } - return bytes; + public static byte[] hex2Byte(String str) { + if (str.length() % 2 == 1) { + str = "0" + str; + } + + byte[] bytes = new byte[str.length() / 2]; + for (int i = 0; i < bytes.length; i++) { + bytes[i] = (byte) Integer.parseInt(str.substring(2 * i, 2 * i + 2), 16); + } + return bytes; } public static String millisecondsToDecimalSecondsString(long ms) { @@ -231,15 +211,25 @@ public class Utils { return (long)(decimal * 1000); } + public static byte[] sha1(String utf8) + throws NoSuchAlgorithmException, UnsupportedEncodingException { + MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); + return sha1.digest(utf8.getBytes("UTF-8")); + } + + public static String sha1Base32(String utf8) + throws NoSuchAlgorithmException, UnsupportedEncodingException { + return new Base32().encodeAsString(sha1(utf8)).toLowerCase(); + } public static String getPrefsPath(String username, String serverURL) throws NoSuchAlgorithmException, UnsupportedEncodingException { - return "sync.prefs." + Cryptographer.sha1Base32(serverURL + ":" + username); + return "sync.prefs." + sha1Base32(serverURL + ":" + username); } public static SharedPreferences getSharedPreferences(Context context, String username, String serverURL) throws NoSuchAlgorithmException, UnsupportedEncodingException { String prefsPath = getPrefsPath(username, serverURL); - Log.d(LOG_TAG, "Shared preferences: " + prefsPath); + Logger.debug(LOG_TAG, "Shared preferences: " + prefsPath); return context.getSharedPreferences(prefsPath, SHARED_PREFERENCES_MODE); } diff --git a/mobile/android/base/sync/crypto/CryptoInfo.java b/mobile/android/base/sync/crypto/CryptoInfo.java index eb38568dcb5..c3d17ff5316 100644 --- a/mobile/android/base/sync/crypto/CryptoInfo.java +++ b/mobile/android/base/sync/crypto/CryptoInfo.java @@ -4,16 +4,61 @@ package org.mozilla.gecko.sync.crypto; +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.HashMap; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.mozilla.apache.commons.codec.binary.Base64; + /* * All info in these objects should be decoded (i.e. not BaseXX encoded). */ public class CryptoInfo { + private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding"; + private static final String KEY_ALGORITHM_SPEC = "AES"; private byte[] message; private byte[] iv; private byte[] hmac; private KeyBundle keys; + /** + * Return a CryptoInfo with given plaintext encrypted using given keys. + */ + public static CryptoInfo encrypt(byte[] plaintextBytes, KeyBundle keys) throws CryptoException { + CryptoInfo info = new CryptoInfo(plaintextBytes, keys); + info.encrypt(); + return info; + } + + /** + * Return a CryptoInfo with given plaintext encrypted using given keys and initial vector. + */ + public static CryptoInfo encrypt(byte[] plaintextBytes, byte[] iv, KeyBundle keys) throws CryptoException { + CryptoInfo info = new CryptoInfo(plaintextBytes, iv, null, keys); + info.encrypt(); + return info; + } + + /** + * Return a CryptoInfo with given ciphertext decrypted using given keys and initial vector, verifying that given HMAC validates. + */ + public static CryptoInfo decrypt(byte[] ciphertext, byte[] iv, byte[] hmac, KeyBundle keys) throws CryptoException { + CryptoInfo info = new CryptoInfo(ciphertext, iv, hmac, keys); + info.decrypt(); + return info; + } + /* * Constructor typically used when encrypting. */ @@ -63,4 +108,134 @@ public class CryptoInfo { public void setKeys(KeyBundle keys) { this.keys = keys; } + + /* + * Generate HMAC for given cipher text. + */ + public static byte[] generatedHMACFor(byte[] message, KeyBundle keys) throws NoSuchAlgorithmException, InvalidKeyException { + Mac hmacHasher = HKDF.makeHMACHasher(keys.getHMACKey()); + return hmacHasher.doFinal(Base64.encodeBase64(message)); + } + + /* + * Return true if generated HMAC is the same as the specified HMAC. + */ + public boolean generatedHMACIsHMAC() throws NoSuchAlgorithmException, InvalidKeyException { + byte[] generatedHMAC = generatedHMACFor(getMessage(), getKeys()); + byte[] expectedHMAC = getHMAC(); + return Arrays.equals(generatedHMAC, expectedHMAC); + } + + /** + * Performs functionality common to both encryption and decryption. + * + * @param cipher + * @param inputMessage non-BaseXX-encoded message + * @return encrypted/decrypted message + * @throws CryptoException + */ + private static byte[] commonCrypto(Cipher cipher, byte[] inputMessage) + throws CryptoException { + byte[] outputMessage = null; + try { + outputMessage = cipher.doFinal(inputMessage); + } catch (IllegalBlockSizeException e) { + throw new CryptoException(e); + } catch (BadPaddingException e) { + throw new CryptoException(e); + } + return outputMessage; + } + + /** + * Encrypt a CryptoInfo in-place. + * + * @throws CryptoException + */ + public void encrypt() throws CryptoException { + + Cipher cipher = CryptoInfo.getCipher(TRANSFORMATION); + try { + byte[] encryptionKey = getKeys().getEncryptionKey(); + SecretKeySpec spec = new SecretKeySpec(encryptionKey, KEY_ALGORITHM_SPEC); + + // If no IV is provided, we allow the cipher to provide one. + if (getIV() == null || getIV().length == 0) { + cipher.init(Cipher.ENCRYPT_MODE, spec); + } else { + cipher.init(Cipher.ENCRYPT_MODE, spec, new IvParameterSpec(getIV())); + } + } catch (GeneralSecurityException ex) { + throw new CryptoException(ex); + } + + // Encrypt. + byte[] encryptedBytes = commonCrypto(cipher, getMessage()); + byte[] iv = cipher.getIV(); + + byte[] hmac; + // Generate HMAC. + try { + hmac = generatedHMACFor(encryptedBytes, keys); + } catch (NoSuchAlgorithmException e) { + throw new CryptoException(e); + } catch (InvalidKeyException e) { + throw new CryptoException(e); + } + + // Update in place. keys is already set. + this.setHMAC(hmac); + this.setIV(iv); + this.setMessage(encryptedBytes); + } + + /** + * Decrypt a CryptoInfo in-place. + * + * @throws CryptoException + */ + public void decrypt() throws CryptoException { + + // Check HMAC. + try { + if (!generatedHMACIsHMAC()) { + throw new HMACVerificationException(); + } + } catch (NoSuchAlgorithmException e) { + throw new CryptoException(e); + } catch (InvalidKeyException e) { + throw new CryptoException(e); + } + + Cipher cipher = CryptoInfo.getCipher(TRANSFORMATION); + try { + byte[] encryptionKey = getKeys().getEncryptionKey(); + SecretKeySpec spec = new SecretKeySpec(encryptionKey, KEY_ALGORITHM_SPEC); + cipher.init(Cipher.DECRYPT_MODE, spec, new IvParameterSpec(getIV())); + } catch (GeneralSecurityException ex) { + throw new CryptoException(ex); + } + byte[] decryptedBytes = commonCrypto(cipher, getMessage()); + byte[] iv = cipher.getIV(); + + // Update in place. keys is already set. + this.setHMAC(null); + this.setIV(iv); + this.setMessage(decryptedBytes); + } + + /** + * Helper to get a Cipher object. + * + * @param transformation The type of Cipher to get. + */ + private static Cipher getCipher(String transformation) throws CryptoException { + try { + return Cipher.getInstance(transformation); + } catch (NoSuchAlgorithmException e) { + throw new CryptoException(e); + } catch (NoSuchPaddingException e) { + throw new CryptoException(e); + } + } } diff --git a/mobile/android/base/sync/crypto/Cryptographer.java b/mobile/android/base/sync/crypto/Cryptographer.java deleted file mode 100644 index 1594a8abfc1..00000000000 --- a/mobile/android/base/sync/crypto/Cryptographer.java +++ /dev/null @@ -1,234 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Android Sync Client. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Jason Voll - * Richard Newman - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -package org.mozilla.gecko.sync.crypto; - -import java.io.UnsupportedEncodingException; -import java.security.GeneralSecurityException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.KeyGenerator; -import javax.crypto.Mac; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -import org.mozilla.apache.commons.codec.binary.Base32; -import org.mozilla.apache.commons.codec.binary.Base64; -import org.mozilla.gecko.sync.Utils; -import java.security.InvalidKeyException; - -/* - * Implements the basic required cryptography options. - */ -public class Cryptographer { - - private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding"; - private static final String KEY_ALGORITHM_SPEC = "AES"; - private static final int KEY_SIZE = 256; - - public static CryptoInfo encrypt(CryptoInfo info) throws CryptoException { - - Cipher cipher = getCipher(); - try { - byte[] encryptionKey = info.getKeys().getEncryptionKey(); - SecretKeySpec spec = new SecretKeySpec(encryptionKey, KEY_ALGORITHM_SPEC); - - // If no IV is provided, we allow the cipher to provide one. - if (info.getIV() == null || - info.getIV().length == 0) { - cipher.init(Cipher.ENCRYPT_MODE, spec); - } else { - System.out.println("IV is " + info.getIV().length); - cipher.init(Cipher.ENCRYPT_MODE, spec, new IvParameterSpec(info.getIV())); - } - } catch (GeneralSecurityException ex) { - throw new CryptoException(ex); - } - - // Encrypt. - byte[] encryptedBytes = commonCrypto(cipher, info.getMessage()); - info.setMessage(encryptedBytes); - - // Save IV. - info.setIV(cipher.getIV()); - - // Generate HMAC. - try { - info.setHMAC(generateHMAC(info)); - } catch (NoSuchAlgorithmException e) { - throw new CryptoException(e); - } catch (InvalidKeyException e) { - throw new CryptoException(e); - } - - return info; - - } - - /* - * Perform a decryption. - * - * @argument info info bundle for decryption - * - * @return decrypted byte[] - * - * @throws CryptoException - */ - public static byte[] decrypt(CryptoInfo info) throws CryptoException { - - // Check HMAC. - try { - if (!verifyHMAC(info)) { - throw new HMACVerificationException(); - } - } catch (NoSuchAlgorithmException e) { - throw new CryptoException(e); - } catch (InvalidKeyException e) { - throw new CryptoException(e); - } - - Cipher cipher = getCipher(); - try { - byte[] encryptionKey = info.getKeys().getEncryptionKey(); - SecretKeySpec spec = new SecretKeySpec(encryptionKey, KEY_ALGORITHM_SPEC); - cipher.init(Cipher.DECRYPT_MODE, spec, new IvParameterSpec(info.getIV())); - } catch (GeneralSecurityException ex) { - ex.printStackTrace(); - throw new CryptoException(ex); - } - return commonCrypto(cipher, info.getMessage()); - } - - /* - * Make 2 random 256 bit keys (encryption and HMAC). - */ - public static KeyBundle generateKeys() throws CryptoException { - KeyGenerator keygen; - try { - keygen = KeyGenerator.getInstance(KEY_ALGORITHM_SPEC); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - throw new CryptoException(e); - } - - keygen.init(KEY_SIZE); - byte[] encryptionKey = keygen.generateKey().getEncoded(); - byte[] hmacKey = keygen.generateKey().getEncoded(); - return new KeyBundle(encryptionKey, hmacKey); - } - - /* - * Performs functionality common to both the encryption and decryption - * operations. - * - * Input: Cipher object, non-BaseXX-encoded byte[] input Output: - * encrypted/decrypted byte[] - */ - private static byte[] commonCrypto(Cipher cipher, byte[] inputMessage) - throws CryptoException { - byte[] outputMessage = null; - try { - outputMessage = cipher.doFinal(inputMessage); - } catch (IllegalBlockSizeException e) { - e.printStackTrace(); - throw new CryptoException(e); - } catch (BadPaddingException e) { - e.printStackTrace(); - throw new CryptoException(e); - } - return outputMessage; - } - - /* - * Helper to get a Cipher object. - * Input: None. - * Output: Cipher object. - */ - private static Cipher getCipher() throws CryptoException { - Cipher cipher = null; - try { - cipher = Cipher.getInstance(TRANSFORMATION); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - throw new CryptoException(e); - } catch (NoSuchPaddingException e) { - e.printStackTrace(); - throw new CryptoException(e); - } - return cipher; - } - - /* - * Helper to verify HMAC Input: CryptoInfo Output: true if HMAC is correct - */ - private static boolean verifyHMAC(CryptoInfo bundle) throws NoSuchAlgorithmException, InvalidKeyException { - byte[] generatedHMAC = generateHMAC(bundle); - byte[] expectedHMAC = bundle.getHMAC(); - boolean eq = Arrays.equals(generatedHMAC, expectedHMAC); - if (!eq) { - System.err.println("Failed HMAC verification."); - System.err.println("Expecting: " + Utils.byte2hex(generatedHMAC)); - System.err.println("Got: " + Utils.byte2hex(expectedHMAC)); - } - return eq; - } - - /* - * Helper to generate HMAC Input: CryptoInfo Output: a generated HMAC for - * given cipher text - */ - private static byte[] generateHMAC(CryptoInfo bundle) throws NoSuchAlgorithmException, InvalidKeyException { - Mac hmacHasher = HKDF.makeHMACHasher(bundle.getKeys().getHMACKey()); - return hmacHasher.doFinal(Base64.encodeBase64(bundle.getMessage())); - } - - public static byte[] sha1(String utf8) throws NoSuchAlgorithmException, UnsupportedEncodingException { - MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); - return sha1.digest(utf8.getBytes("UTF-8")); - } - - public static String sha1Base32(String utf8) throws NoSuchAlgorithmException, UnsupportedEncodingException { - return new Base32().encodeAsString(sha1(utf8)).toLowerCase(); - } -} diff --git a/mobile/android/base/sync/crypto/KeyBundle.java b/mobile/android/base/sync/crypto/KeyBundle.java index ded6372bf30..06a3256980a 100644 --- a/mobile/android/base/sync/crypto/KeyBundle.java +++ b/mobile/android/base/sync/crypto/KeyBundle.java @@ -41,6 +41,7 @@ package org.mozilla.gecko.sync.crypto; import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; +import javax.crypto.KeyGenerator; import javax.crypto.Mac; import org.mozilla.apache.commons.codec.binary.Base64; @@ -49,6 +50,8 @@ import org.mozilla.gecko.sync.crypto.CryptoException; import java.security.InvalidKeyException; public class KeyBundle { + private static final String KEY_ALGORITHM_SPEC = "AES"; + private static final int KEY_SIZE = 256; private byte[] encryptionKey; private byte[] hmacKey; @@ -75,7 +78,7 @@ public class KeyBundle { if (account.matches("^[A-Za-z0-9._-]+$")) { return account; } - return Cryptographer.sha1Base32(account); + return Utils.sha1Base32(account); } // If we encounter characters not allowed by the API (as found for @@ -130,6 +133,36 @@ public class KeyBundle { this.setHMACKey(hmacKey); } + /** + * Make a KeyBundle with the specified base64-encoded keys. + * + * @return A KeyBundle with the specified keys. + */ + public static KeyBundle fromBase64EncodedKeys(String base64EncryptionKey, String base64HmacKey) throws UnsupportedEncodingException { + return new KeyBundle(Base64.decodeBase64(base64EncryptionKey.getBytes("UTF-8")), + Base64.decodeBase64(base64HmacKey.getBytes("UTF-8"))); + } + + /** + * Make a KeyBundle with two random 256 bit keys (encryption and HMAC). + * + * @return A KeyBundle with random keys. + */ + public static KeyBundle withRandomKeys() throws CryptoException { + KeyGenerator keygen; + try { + keygen = KeyGenerator.getInstance(KEY_ALGORITHM_SPEC); + } catch (NoSuchAlgorithmException e) { + throw new CryptoException(e); + } + + keygen.init(KEY_SIZE); + byte[] encryptionKey = keygen.generateKey().getEncoded(); + byte[] hmacKey = keygen.generateKey().getEncoded(); + + return new KeyBundle(encryptionKey, hmacKey); + } + public byte[] getEncryptionKey() { return encryptionKey; } @@ -145,9 +178,4 @@ public class KeyBundle { public void setHMACKey(byte[] hmacKey) { this.hmacKey = hmacKey; } - - public static KeyBundle decodeKeyStrings(String base64EncryptionKey, String base64HmacKey) throws UnsupportedEncodingException { - return new KeyBundle(Base64.decodeBase64(base64EncryptionKey.getBytes("UTF-8")), - Base64.decodeBase64(base64HmacKey.getBytes("UTF-8"))); - } } diff --git a/mobile/android/base/sync/cryptographer/CryptoStatusBundle.java b/mobile/android/base/sync/cryptographer/CryptoStatusBundle.java deleted file mode 100644 index d06cb257f9c..00000000000 --- a/mobile/android/base/sync/cryptographer/CryptoStatusBundle.java +++ /dev/null @@ -1,75 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Android Sync Client. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Jason Voll - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -package org.mozilla.gecko.sync.cryptographer; - -public class CryptoStatusBundle { - - public enum CryptoStatus { - OK, - MISSING_KEYS, - HMAC_VERIFY_FAIL, - INVALID_JSON, - INVALID_KEYS_BUNDLE, - MISSING_SYNCKEY_OR_USER - } - - private CryptoStatus status; - private String json; - - public CryptoStatusBundle (CryptoStatus status, String json) { - this.setStatus(status); - this.setJson(json); - } - - public CryptoStatus getStatus() { - return status; - } - - public void setStatus(CryptoStatus status) { - this.status = status; - } - - public String getJson() { - return json; - } - - public void setJson(String json) { - this.json = json; - } - -} diff --git a/mobile/android/base/sync/cryptographer/SyncCryptographer.java b/mobile/android/base/sync/cryptographer/SyncCryptographer.java deleted file mode 100644 index b9530549ae8..00000000000 --- a/mobile/android/base/sync/cryptographer/SyncCryptographer.java +++ /dev/null @@ -1,370 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Android Sync Client. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Jason Voll - * Richard Newman - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -package org.mozilla.gecko.sync.cryptographer; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StringReader; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; - -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; -import org.mozilla.apache.commons.codec.binary.Base64; -import org.mozilla.gecko.sync.ExtendedJSONObject; -import org.mozilla.gecko.sync.Utils; -import org.mozilla.gecko.sync.crypto.CryptoException; -import org.mozilla.gecko.sync.crypto.CryptoInfo; -import org.mozilla.gecko.sync.crypto.Cryptographer; -import org.mozilla.gecko.sync.crypto.KeyBundle; -import org.mozilla.gecko.sync.cryptographer.CryptoStatusBundle.CryptoStatus; -import java.security.GeneralSecurityException; - -/* - * This class acts as a wrapper for the Cryptographer class. - * The goal here is to take care of all JSON parsing and BaseXX - * encoding/decoding so that the Cryptographer class doesn't need - * to know anything about the format of the original data. It also - * enables classes to get crypto services based on a WBO, rather than - * having to parse JSON themselves. - * - * This class decouples the Cryptographer from the Sync client. - */ -public class SyncCryptographer { - - // JSON related constants. - private static final String KEY_CIPHER_TEXT = "ciphertext"; - private static final String KEY_HMAC = "hmac"; - private static final String KEY_IV = "IV"; - private static final String KEY_PAYLOAD = "payload"; - private static final String KEY_ID = "id"; - private static final String KEY_COLLECTION = "collection"; - private static final String KEY_COLLECTIONS = "collections"; - private static final String KEY_DEFAULT_COLLECTION = "default"; - - private static final String ID_CRYPTO_KEYS = "keys"; - private static final String CRYPTO_KEYS_COLLECTION = "crypto"; - - public String syncKey; - private String username; - private KeyBundle keys; - - /* - * Constructors. - */ - public SyncCryptographer(String username) throws UnsupportedEncodingException { - this(username, "", "", ""); - } - - public SyncCryptographer(String username, String friendlyBase32SyncKey) throws UnsupportedEncodingException { - this(username, friendlyBase32SyncKey, "", ""); - } - - public SyncCryptographer(String username, String friendlyBase32SyncKey, - String base64EncryptionKey, String base64HmacKey) throws UnsupportedEncodingException { - this.setUsername(username); - this.syncKey = friendlyBase32SyncKey; - this.setKeys(base64EncryptionKey, base64HmacKey); - } - - /* - * Same as above...but for JSONArray - */ - @SuppressWarnings("unchecked") - private static final ArrayList asAList(JSONArray j) { - return j; - } - - /* - * Input: A string representation of a WBO (JSON) payload to be encrypted. - * Output: CryptoStatusBundle with a JSON payload containing - * crypto information (ciphertext, IV, HMAC). - */ - public CryptoStatusBundle encryptWBO(String jsonString) throws CryptoException { - // Verify that encryption keys are set. - if (keys == null) { - return new CryptoStatusBundle(CryptoStatus.MISSING_KEYS, jsonString); - } - return encrypt(jsonString, keys); - } - - /* - * Input: A string representation of the WBO (JSON). - * Output: the decrypted payload and status. - */ - public CryptoStatusBundle decryptWBO(String jsonString) throws CryptoException, UnsupportedEncodingException { - // Get JSON from string. - JSONObject json = null; - JSONObject payload = null; - try { - json = getJSONObject(jsonString); - payload = getJSONObject(json, KEY_PAYLOAD); - - } catch (Exception e) { - return new CryptoStatusBundle(CryptoStatus.INVALID_JSON, jsonString); - } - - // Check that payload contains all pieces for crypto. - if (!payload.containsKey(KEY_CIPHER_TEXT) || - !payload.containsKey(KEY_IV) || - !payload.containsKey(KEY_HMAC)) { - return new CryptoStatusBundle(CryptoStatus.INVALID_JSON, jsonString); - } - - String id = (String) json.get(KEY_ID); - if (id.equalsIgnoreCase(ID_CRYPTO_KEYS)) { - // If this is a crypto keys bundle, handle it separately. - return decryptKeysWBO(payload); - } else if (keys == null) { - // Otherwise, make sure we have crypto keys before continuing. - return new CryptoStatusBundle(CryptoStatus.MISSING_KEYS, jsonString); - } - - byte[] clearText = decryptPayload(payload, this.keys); - return new CryptoStatusBundle(CryptoStatus.OK, new String(clearText)); - } - - /* - * Handles the case where we are decrypting the crypto/keys bundle. - * Uses the sync key and username to get keys for decrypting this - * bundle. Once bundle is decrypted the keys are saved to this - * object for future use and the decrypted payload is returned. - * - * Input: JSONObject payload containing crypto/keys JSON. - * Output: Decrypted crypto/keys String. - */ - private CryptoStatusBundle decryptKeysWBO(JSONObject payload) throws CryptoException, UnsupportedEncodingException { - // Get the keys to decrypt the crypto keys bundle. - KeyBundle cryptoKeysBundleKeys; - try { - cryptoKeysBundleKeys = getCryptoKeysBundleKeys(); - } catch (Exception e) { - return new CryptoStatusBundle(CryptoStatus.MISSING_SYNCKEY_OR_USER, payload.toString()); - } - - byte[] cryptoKeysBundle = decryptPayload(payload, cryptoKeysBundleKeys); - - // Extract decrypted keys. - InputStream stream = new ByteArrayInputStream(cryptoKeysBundle); - Reader in = new InputStreamReader(stream); - JSONObject json = null; - try { - json = (JSONObject) new JSONParser().parse(in); - } catch (Exception e) { - e.printStackTrace(); - } - - if (json == null) { - throw new CryptoException(new GeneralSecurityException("Could not decrypt JSON payload")); - } - - // Verify that this is indeed the crypto/keys bundle and that - // decryption worked. - String id = (String) json.get(KEY_ID); - String collection = (String) json.get(KEY_COLLECTION); - - if (id.equalsIgnoreCase(ID_CRYPTO_KEYS) && - collection.equalsIgnoreCase(CRYPTO_KEYS_COLLECTION) && - json.containsKey(KEY_DEFAULT_COLLECTION)) { - - // Extract the keys and add them to this. - Object jsonKeysObj = json.get(KEY_DEFAULT_COLLECTION); - if (jsonKeysObj.getClass() != JSONArray.class) { - return new CryptoStatusBundle(CryptoStatus.INVALID_KEYS_BUNDLE, - json.toString()); - } - - JSONArray jsonKeys = (JSONArray) jsonKeysObj; - this.setKeys((String) jsonKeys.get(0), (String) jsonKeys.get(1)); - - // Return the string containing the decrypted payload. - return new CryptoStatusBundle(CryptoStatus.OK, - new String(cryptoKeysBundle)); - } else { - return new CryptoStatusBundle(CryptoStatus.INVALID_KEYS_BUNDLE, - json.toString()); - } - } - - /** - * Generates new encryption keys and creates the crypto/keys - * payload, encrypted using Sync Key. - * - * @return The crypto/keys payload encrypted for sending to - * the server. - * @throws CryptoException - * @throws UnsupportedEncodingException - */ - public CryptoStatusBundle generateCryptoKeysWBOPayload() throws CryptoException, UnsupportedEncodingException { - - // Generate the keys and save for later use. - KeyBundle cryptoKeys = Cryptographer.generateKeys(); - setKeys(new String(Base64.encodeBase64(cryptoKeys.getEncryptionKey())), - new String(Base64.encodeBase64(cryptoKeys.getHMACKey()))); - - // Generate JSON. - JSONArray keysArray = new JSONArray(); - asAList(keysArray).add(new String(Base64.encodeBase64(cryptoKeys.getEncryptionKey()))); - asAList(keysArray).add(new String(Base64.encodeBase64(cryptoKeys.getHMACKey()))); - ExtendedJSONObject json = new ExtendedJSONObject(); - json.put(KEY_ID, ID_CRYPTO_KEYS); - json.put(KEY_COLLECTION, CRYPTO_KEYS_COLLECTION); - json.put(KEY_COLLECTIONS, "{}"); - json.put(KEY_DEFAULT_COLLECTION, keysArray); - - // Get the keys to encrypt the crypto/keys bundle. - KeyBundle cryptoKeysBundleKeys; - try { - cryptoKeysBundleKeys = getCryptoKeysBundleKeys(); - } catch (Exception e) { - return new CryptoStatusBundle(CryptoStatus.MISSING_SYNCKEY_OR_USER, ""); - } - - return encrypt(json.toString(), cryptoKeysBundleKeys); - } - - /////////////////////// HELPERS ///////////////////////////// - - /* - * Helper method for doing actual encryption. - * - * Input: Message to encrypt, Keys for encryption/hmac - * Output: CryptoStatusBundle with a JSON payload containing - * crypto information (ciphertext, iv, hmac). - */ - private CryptoStatusBundle encrypt(String message, KeyBundle keys) throws CryptoException { - CryptoInfo encrypted = Cryptographer.encrypt(new CryptoInfo(message.getBytes(), keys)); - String payload = createJSONBundle(encrypted); - return new CryptoStatusBundle(CryptoStatus.OK, payload); - } - - /** - * Helper method for doing actual decryption. - * - * Input: JSONObject containing a valid payload (cipherText, IV, HMAC), - * KeyBundle with keys for decryption. - * Output: byte[] clearText - * @throws CryptoException - * @throws UnsupportedEncodingException - */ - private byte[] decryptPayload(JSONObject payload, KeyBundle keybundle) throws CryptoException, UnsupportedEncodingException { - byte[] clearText = Cryptographer.decrypt( - new CryptoInfo ( - Base64.decodeBase64(((String) payload.get(KEY_CIPHER_TEXT)).getBytes("UTF-8")), - Base64.decodeBase64(((String) payload.get(KEY_IV)).getBytes("UTF-8")), - Utils.hex2Byte( (String) payload.get(KEY_HMAC) ), - keybundle - ) - ); - - return clearText; - } - - /** - * Helper method to get a JSONObject from a String. - * Input: String containing JSON. - * Output: Extracted JSONObject. - * Throws: Exception if JSON is invalid. - */ - private JSONObject getJSONObject(String jsonString) throws Exception { - Reader in = new StringReader(jsonString); - try { - return (JSONObject) new JSONParser().parse(in); - } catch (Exception e) { - throw e; - } - } - - /** - * Helper method for extracting a JSONObject - * from within another JSONObject. - * - * Input: JSONObject and key. - * Output: JSONObject extracted. - * Throws: Exception if JSON is invalid. - */ - private JSONObject getJSONObject(JSONObject json, String key) throws Exception { - try { - return getJSONObject((String) json.get(key)); - } catch (Exception e) { - throw e; - } - } - - /* - * Helper to create JSON bundle for encrypted objects. - */ - private String createJSONBundle(CryptoInfo info) { - ExtendedJSONObject json = new ExtendedJSONObject(); - json.put(KEY_CIPHER_TEXT, new String(Base64.encodeBase64(info.getMessage()))); - json.put(KEY_IV, new String(Base64.encodeBase64(info.getIV()))); - json.put(KEY_HMAC, Utils.byte2hex(info.getHMAC())); - return json.toString(); - } - - /* - * Get the keys needed to encrypt the crypto/keys bundle. - */ - public KeyBundle getCryptoKeysBundleKeys() throws CryptoException { - return new KeyBundle(username, syncKey); - } - - public KeyBundle getKeys() { - return keys; - } - - /* - * Input: Base64 encoded encryption and HMAC keys. - */ - public void setKeys(String base64EncryptionKey, String base64HmacKey) throws UnsupportedEncodingException { - this.keys = new KeyBundle(Base64.decodeBase64(base64EncryptionKey.getBytes("UTF-8")), - Base64.decodeBase64(base64HmacKey.getBytes("UTF-8"))); - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } -} diff --git a/mobile/android/base/sync/jpake/Gx4IsOneException.java b/mobile/android/base/sync/jpake/Gx3OrGx4IsZeroOrOneException.java similarity index 93% rename from mobile/android/base/sync/jpake/Gx4IsOneException.java rename to mobile/android/base/sync/jpake/Gx3OrGx4IsZeroOrOneException.java index e23ab7efc3c..1a791b2bf41 100644 --- a/mobile/android/base/sync/jpake/Gx4IsOneException.java +++ b/mobile/android/base/sync/jpake/Gx3OrGx4IsZeroOrOneException.java @@ -37,6 +37,6 @@ package org.mozilla.gecko.sync.jpake; -public class Gx4IsOneException extends Exception { - private static final long serialVersionUID = 815366241052296473L; +public class Gx3OrGx4IsZeroOrOneException extends Exception { + private static final long serialVersionUID = 7347530447460039679L; } diff --git a/mobile/android/base/sync/jpake/JPakeClient.java b/mobile/android/base/sync/jpake/JPakeClient.java index ed2296cb891..5f7d766e652 100644 --- a/mobile/android/base/sync/jpake/JPakeClient.java +++ b/mobile/android/base/sync/jpake/JPakeClient.java @@ -44,7 +44,8 @@ import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.net.URISyntaxException; import java.security.GeneralSecurityException; -import java.util.Random; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; import java.util.Timer; import java.util.TimerTask; @@ -53,12 +54,12 @@ import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import org.mozilla.apache.commons.codec.binary.Base64; import org.mozilla.gecko.sync.ExtendedJSONObject; +import org.mozilla.gecko.sync.Logger; import org.mozilla.gecko.sync.NonObjectJSONException; import org.mozilla.gecko.sync.ThreadPool; import org.mozilla.gecko.sync.Utils; import org.mozilla.gecko.sync.crypto.CryptoException; import org.mozilla.gecko.sync.crypto.CryptoInfo; -import org.mozilla.gecko.sync.crypto.Cryptographer; import org.mozilla.gecko.sync.crypto.KeyBundle; import org.mozilla.gecko.sync.crypto.NoKeyBundleException; import org.mozilla.gecko.sync.net.ResourceDelegate; @@ -74,8 +75,6 @@ import ch.boye.httpclientandroidlib.client.methods.HttpRequestBase; import ch.boye.httpclientandroidlib.entity.StringEntity; import ch.boye.httpclientandroidlib.impl.client.DefaultHttpClient; import ch.boye.httpclientandroidlib.message.BasicHeader; -import java.security.NoSuchAlgorithmException; -import java.security.InvalidKeyException; public class JPakeClient implements JPakeRequestDelegate { private static String LOG_TAG = "JPakeClient"; @@ -106,6 +105,7 @@ public class JPakeClient implements JPakeRequestDelegate { private String myEtag; private String mySignerId; + @SuppressWarnings("unused") private String theirEtag; private String theirSignerId; private String jpakeServer; @@ -236,20 +236,26 @@ public class JPakeClient implements JPakeRequestDelegate { * (Receiver Only) Request channel for J-PAKE from server. */ private void getChannel() { - Log.d(LOG_TAG, "Getting channel."); - if (finished) + Logger.debug(LOG_TAG, "Getting channel."); + if (finished) { + Logger.debug(LOG_TAG, "Finished; returning."); return; + } - JPakeRequest channelRequest = null; try { - channelRequest = new JPakeRequest(jpakeServer + "new_channel", - makeRequestResourceDelegate()); + final String uri = jpakeServer + "new_channel"; + Logger.debug(LOG_TAG, "Fetching " + uri); + JPakeRequest channelRequest = new JPakeRequest(uri, makeRequestResourceDelegate()); + channelRequest.get(); } catch (URISyntaxException e) { - e.printStackTrace(); + Log.e(LOG_TAG, "URISyntaxException", e); + abort(Constants.JPAKE_ERROR_CHANNEL); + return; + } catch (Exception e) { + Log.e(LOG_TAG, "Unexpected exception in getChannel().", e); abort(Constants.JPAKE_ERROR_CHANNEL); return; } - channelRequest.get(); } /* @@ -257,7 +263,7 @@ public class JPakeClient implements JPakeRequestDelegate { * jOutgoing JSONObject. */ private void putStep() { - Log.d(LOG_TAG, "Uploading message."); + Logger.debug(LOG_TAG, "Uploading message."); runOnThread(new Runnable() { @Override public void run() { @@ -266,7 +272,7 @@ public class JPakeClient implements JPakeRequestDelegate { putRequest = new JPakeRequest(channelUrl, makeRequestResourceDelegate()); } catch (URISyntaxException e) { - e.printStackTrace(); + Log.e(LOG_TAG, "URISyntaxException", e); abort(Constants.JPAKE_ERROR_CHANNEL); return; } @@ -275,7 +281,7 @@ public class JPakeClient implements JPakeRequestDelegate { } catch (UnsupportedEncodingException e) { e.printStackTrace(); } - Log.d(LOG_TAG, "outgoing: " + jOutgoing.toJSONString()); + Logger.debug(LOG_TAG, "outgoing: " + jOutgoing.toJSONString()); } }); } @@ -283,8 +289,8 @@ public class JPakeClient implements JPakeRequestDelegate { /* * Step One of J-PAKE protocol. */ - private void computeStepOne() { - Log.d(LOG_TAG, "Computing round 1."); + private void computeStepOne() throws NoSuchAlgorithmException, UnsupportedEncodingException { + Logger.debug(LOG_TAG, "Computing round 1."); JPakeCrypto.round1(jParty, numGen); @@ -307,7 +313,7 @@ public class JPakeClient implements JPakeRequestDelegate { jOutgoing.put(Constants.JSON_KEY_TYPE, mySignerId + "1"); jOutgoing.put(Constants.JSON_KEY_PAYLOAD, jOne); jOutgoing.put(Constants.JSON_KEY_VERSION, KEYEXCHANGE_VERSION); - Log.d(LOG_TAG, "Sending: " + jOutgoing.toJSONString()); + Logger.debug(LOG_TAG, "Sending: " + jOutgoing.toJSONString()); // Store context to determine next step after PUT request. stateContext = pairWithPin ? State.SNDR_STEP_ONE : State.RCVR_STEP_ONE; @@ -321,8 +327,8 @@ public class JPakeClient implements JPakeRequestDelegate { * Verifies message computed by other party in their Step One. Creates Step * Two message to be sent. */ - private void computeStepTwo() { - Log.d(LOG_TAG, "Computing round 2."); + private void computeStepTwo() throws NonObjectJSONException { + Logger.debug(LOG_TAG, "Computing round 2."); // Check incoming message sender. if (!jIncoming.get(Constants.JSON_KEY_TYPE).equals(theirSignerId + "1")) { @@ -332,75 +338,70 @@ public class JPakeClient implements JPakeRequestDelegate { } // Check incoming message fields. - ExtendedJSONObject iPayload = null; - try { - iPayload = jIncoming.getObject(Constants.JSON_KEY_PAYLOAD); - if (iPayload == null - || iPayload.getObject(Constants.ZKP_KEY_ZKP_X1) == null - || !theirSignerId.equals(iPayload.getObject(Constants.ZKP_KEY_ZKP_X1) - .get(Constants.ZKP_KEY_ID)) - || iPayload.getObject(Constants.ZKP_KEY_ZKP_X2) == null - || !theirSignerId.equals(iPayload.getObject(Constants.ZKP_KEY_ZKP_X2) - .get(Constants.ZKP_KEY_ID))) { - Log.e(LOG_TAG, "Invalid round 1 message: " + jIncoming.toJSONString()); - abort(Constants.JPAKE_ERROR_WRONGMESSAGE); - return; - } - } catch (NonObjectJSONException e) { - e.printStackTrace(); + ExtendedJSONObject iPayload = jIncoming.getObject(Constants.JSON_KEY_PAYLOAD); + if (iPayload == null) { + Log.e(LOG_TAG, "Invalid round 1 message: " + jIncoming.toJSONString()); + abort(Constants.JPAKE_ERROR_WRONGMESSAGE); + return; + } + + ExtendedJSONObject zkpPayload3 = iPayload.getObject(Constants.ZKP_KEY_ZKP_X1); + ExtendedJSONObject zkpPayload4 = iPayload.getObject(Constants.ZKP_KEY_ZKP_X2); + if (zkpPayload3 == null || zkpPayload4 == null) { + Log.e(LOG_TAG, "Invalid round 1 zkpPayload message"); + abort(Constants.JPAKE_ERROR_WRONGMESSAGE); + return; + } + + if (!theirSignerId.equals(zkpPayload3.get(Constants.ZKP_KEY_ID)) || + !theirSignerId.equals(zkpPayload4.get(Constants.ZKP_KEY_ID))) { + Log.e(LOG_TAG, "Invalid round 1 zkpPayload message"); + abort(Constants.JPAKE_ERROR_WRONGMESSAGE); + return; } // Extract message fields. - jParty.gx3 = new BigInteger((String) iPayload.get(Constants.ZKP_KEY_GX1), - 16); - jParty.gx4 = new BigInteger((String) iPayload.get(Constants.ZKP_KEY_GX2), - 16); - - ExtendedJSONObject zkpPayload3 = null; - ExtendedJSONObject zkpPayload4 = null; - try { - zkpPayload3 = iPayload.getObject(Constants.ZKP_KEY_ZKP_X1); - zkpPayload4 = iPayload.getObject(Constants.ZKP_KEY_ZKP_X2); - if (zkpPayload3 == null || zkpPayload4 == null) { - Log.e(LOG_TAG, "Invalid round 1 zkpPayload message"); - abort(Constants.JPAKE_ERROR_WRONGMESSAGE); - return; - } - } catch (NonObjectJSONException e) { - e.printStackTrace(); - } + jParty.gx3 = new BigInteger((String) iPayload.get(Constants.ZKP_KEY_GX1), 16); + jParty.gx4 = new BigInteger((String) iPayload.get(Constants.ZKP_KEY_GX2), 16); // Extract ZKPs. String zkp3_gr = (String) zkpPayload3.get(Constants.ZKP_KEY_GR); - String zkp3_b = (String) zkpPayload3.get(Constants.ZKP_KEY_B); + String zkp3_b = (String) zkpPayload3.get(Constants.ZKP_KEY_B); String zkp3_id = (String) zkpPayload3.get(Constants.ZKP_KEY_ID); String zkp4_gr = (String) zkpPayload4.get(Constants.ZKP_KEY_GR); - String zkp4_b = (String) zkpPayload4.get(Constants.ZKP_KEY_B); + String zkp4_b = (String) zkpPayload4.get(Constants.ZKP_KEY_B); String zkp4_id = (String) zkpPayload4.get(Constants.ZKP_KEY_ID); - jParty.zkp3 = new Zkp(new BigInteger(zkp3_gr, 16), new BigInteger(zkp3_b, - 16), zkp3_id); - jParty.zkp4 = new Zkp(new BigInteger(zkp4_gr, 16), new BigInteger(zkp4_b, - 16), zkp4_id); + jParty.zkp3 = new Zkp(new BigInteger(zkp3_gr, 16), new BigInteger(zkp3_b, 16), zkp3_id); + jParty.zkp4 = new Zkp(new BigInteger(zkp4_gr, 16), new BigInteger(zkp4_b, 16), zkp4_id); - // Jpake round 2 + // J-PAKE round 2. try { - JPakeCrypto.round2(secret, jParty, numGen); - } catch (Gx4IsOneException e) { - Log.e(LOG_TAG, "gx4 cannot equal 1."); + JPakeCrypto.round2(JPakeClient.secretAsBigInteger(secret), jParty, numGen); + } catch (Gx3OrGx4IsZeroOrOneException e) { + Log.e(LOG_TAG, "gx3 and gx4 cannot equal 0 or 1."); abort(Constants.JPAKE_ERROR_INTERNAL); + return; } catch (IncorrectZkpException e) { Log.e(LOG_TAG, "ZKP mismatch"); abort(Constants.JPAKE_ERROR_WRONGMESSAGE); + return; + } catch (NoSuchAlgorithmException e) { + Log.e(LOG_TAG, "NoSuchAlgorithmException", e); + abort(Constants.JPAKE_ERROR_INTERNAL); + return; + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, "UnsupportedEncodingException", e); + abort(Constants.JPAKE_ERROR_INTERNAL); + return; } // Make outgoing payload. Zkp zkpA = jParty.thisZkpA; ExtendedJSONObject oPayload = new ExtendedJSONObject(); ExtendedJSONObject jZkpA = makeJZkp(zkpA.gr, zkpA.b, zkpA.id); - oPayload.put(Constants.ZKP_KEY_A, - BigIntegerHelper.toEvenLengthHex(jParty.thisA)); + oPayload.put(Constants.ZKP_KEY_A, BigIntegerHelper.toEvenLengthHex(jParty.thisA)); oPayload.put(Constants.ZKP_KEY_ZKP_A, jZkpA); // Make outgoing message. @@ -427,8 +428,8 @@ public class JPakeClient implements JPakeRequestDelegate { * Verifies message computed by other party in Step Two. Creates or fetches * encrypted message for verification of successful key exchange. */ - private void computeFinal() { - Log.d(LOG_TAG, "Computing final round."); + private void computeFinal() throws NonObjectJSONException { + Logger.debug(LOG_TAG, "Computing final round."); // Check incoming message type. if (!jIncoming.get(Constants.JSON_KEY_TYPE).equals(theirSignerId + "2")) { Log.e(LOG_TAG, "Invalid round 2 message: " + jIncoming.toJSONString()); @@ -437,61 +438,68 @@ public class JPakeClient implements JPakeRequestDelegate { } // Check incoming message fields. - ExtendedJSONObject iPayload = null; - try { - iPayload = jIncoming.getObject(Constants.JSON_KEY_PAYLOAD); - if (iPayload == null - || iPayload.getObject(Constants.ZKP_KEY_ZKP_A) == null - || !theirSignerId.equals(iPayload.getObject(Constants.ZKP_KEY_ZKP_A) - .get(Constants.ZKP_KEY_ID))) { - Log.e(LOG_TAG, "Invalid round 2 message: " + jIncoming.toJSONString()); - abort(Constants.JPAKE_ERROR_WRONGMESSAGE); - return; - } - } catch (NonObjectJSONException e) { - e.printStackTrace(); + ExtendedJSONObject iPayload = jIncoming.getObject(Constants.JSON_KEY_PAYLOAD); + if (iPayload == null || + iPayload.getObject(Constants.ZKP_KEY_ZKP_A) == null) { + Log.e(LOG_TAG, "Invalid round 2 message: " + jIncoming.toJSONString()); + abort(Constants.JPAKE_ERROR_WRONGMESSAGE); + return; + } + ExtendedJSONObject zkpPayload = iPayload.getObject(Constants.ZKP_KEY_ZKP_A); + if (!theirSignerId.equals(zkpPayload.get(Constants.ZKP_KEY_ID))) { + Log.e(LOG_TAG, "Invalid round 2 message: " + jIncoming.toJSONString()); + abort(Constants.JPAKE_ERROR_WRONGMESSAGE); + return; } - // Extract fields. - jParty.otherA = new BigInteger((String) iPayload.get(Constants.ZKP_KEY_A), - 16); - ExtendedJSONObject zkpPayload = null; - try { - zkpPayload = iPayload.getObject(Constants.ZKP_KEY_ZKP_A); - } catch (NonObjectJSONException e) { - e.printStackTrace(); - } + // Extract fields. + jParty.otherA = new BigInteger((String) iPayload.get(Constants.ZKP_KEY_A), 16); + // Extract ZKP. String gr = (String) zkpPayload.get(Constants.ZKP_KEY_GR); - String b = (String) zkpPayload.get(Constants.ZKP_KEY_B); + String b = (String) zkpPayload.get(Constants.ZKP_KEY_B); String id = (String) zkpPayload.get(Constants.ZKP_KEY_ID); - jParty.otherZkpA = new Zkp(new BigInteger(gr, 16), new BigInteger(b, 16), - id); + jParty.otherZkpA = new Zkp(new BigInteger(gr, 16), new BigInteger(b, 16), id); myKeyBundle = null; try { - myKeyBundle = JPakeCrypto.finalRound(secret, jParty); + myKeyBundle = JPakeCrypto.finalRound(JPakeClient.secretAsBigInteger(secret), jParty); } catch (IncorrectZkpException e) { Log.e(LOG_TAG, "ZKP mismatch"); abort(Constants.JPAKE_ERROR_WRONGMESSAGE); - e.printStackTrace(); + return; } catch (NoSuchAlgorithmException e) { Log.e(LOG_TAG, "NoSuchAlgorithmException", e); abort(Constants.JPAKE_ERROR_INTERNAL); - e.printStackTrace(); + return; } catch (InvalidKeyException e) { Log.e(LOG_TAG, "InvalidKeyException", e); abort(Constants.JPAKE_ERROR_INTERNAL); - e.printStackTrace(); + return; + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, "UnsupportedEncodingException", e); + abort(Constants.JPAKE_ERROR_INTERNAL); + return; } if (pairWithPin) { // Wait for other device to send verification of keys. - Log.d(LOG_TAG, "get: verifyPairing"); + Logger.debug(LOG_TAG, "get: verifyPairing"); this.state = State.VERIFY_PAIRING; scheduleGetRequest(jpakePollInterval); } else { // Prepare and send verification of keys. - jOutgoing = computeKeyVerification(myKeyBundle); + try { + jOutgoing = computeKeyVerification(myKeyBundle); + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, "Failed to encrypt key verification value.", e); + abort(Constants.JPAKE_ERROR_INTERNAL); + return; + } catch (CryptoException e) { + Log.e(LOG_TAG, "Failed to encrypt key verification value.", e); + abort(Constants.JPAKE_ERROR_INTERNAL); + return; + } + stateContext = State.VERIFY_KEY; this.state = State.PUT; putStep(); @@ -502,26 +510,12 @@ public class JPakeClient implements JPakeRequestDelegate { * (Receiver Only) Helper method to compute verification message from JPake * key bundle, to be sent to other device for verification. */ - public ExtendedJSONObject computeKeyVerification(KeyBundle keyBundle) { - Log.d(LOG_TAG, "Encrypting key verification value."); + public ExtendedJSONObject computeKeyVerification(KeyBundle keyBundle) + throws UnsupportedEncodingException, CryptoException + { + Logger.debug(LOG_TAG, "Encrypting key verification value."); // KeyBundle not null - ExtendedJSONObject jPayload = null; - try { - jPayload = encryptPayload(JPAKE_VERIFY_VALUE, keyBundle); - } catch (UnsupportedEncodingException e) { - Log.e(LOG_TAG, "Failed to encrypt key verification value.", e); - abort(Constants.JPAKE_ERROR_INTERNAL); - } catch (CryptoException e) { - Log.e(LOG_TAG, "Failed to encrypt key verification value.", e); - abort(Constants.JPAKE_ERROR_INTERNAL); - } - Log.d( - LOG_TAG, - "enc key64: " - + new String(Base64.encodeBase64(keyBundle.getEncryptionKey()))); - Log.e(LOG_TAG, - "hmac64: " + new String(Base64.encodeBase64(keyBundle.getHMACKey()))); - + ExtendedJSONObject jPayload = encryptPayload(JPAKE_VERIFY_VALUE, keyBundle); ExtendedJSONObject result = new ExtendedJSONObject(); result.put(Constants.JSON_KEY_TYPE, mySignerId + "3"); result.put(Constants.JSON_KEY_VERSION, KEYEXCHANGE_VERSION); @@ -538,8 +532,7 @@ public class JPakeClient implements JPakeRequestDelegate { NonObjectJSONException { if (!verificationObject.get(Constants.JSON_KEY_TYPE).equals( theirSignerId + "3")) { - Log.e(LOG_TAG, - "Invalid round 3 message: " + verificationObject.toJSONString()); + Log.e(LOG_TAG, "Invalid round 3 message: " + verificationObject.toJSONString()); abort(Constants.JPAKE_ERROR_WRONGMESSAGE); return false; } @@ -559,11 +552,8 @@ public class JPakeClient implements JPakeRequestDelegate { public boolean verifyCiphertext(String theirCiphertext, String iv, KeyBundle keyBundle) throws UnsupportedEncodingException, CryptoException { byte[] cleartextBytes = JPAKE_VERIFY_VALUE.getBytes("UTF-8"); - CryptoInfo info = new CryptoInfo(cleartextBytes, keyBundle); - info.setIV(Base64.decodeBase64(iv)); - - Cryptographer.encrypt(info); - String myCiphertext = new String(Base64.encodeBase64(info.getMessage())); + CryptoInfo encrypted = CryptoInfo.encrypt(cleartextBytes, Base64.decodeBase64(iv), keyBundle); + String myCiphertext = new String(Base64.encodeBase64(encrypted.getMessage())); return myCiphertext.equals(theirCiphertext); } @@ -594,16 +584,18 @@ public class JPakeClient implements JPakeRequestDelegate { * @param payload Credentials data to be encrypted. */ private void encryptData(KeyBundle keyBundle, String payload) { - Log.d(LOG_TAG, "Encrypting data."); + Logger.debug(LOG_TAG, "Encrypting data."); ExtendedJSONObject jPayload = null; try { jPayload = encryptPayload(payload, keyBundle); } catch (UnsupportedEncodingException e) { Log.e(LOG_TAG, "Failed to encrypt data.", e); abort(Constants.JPAKE_ERROR_INTERNAL); + return; } catch (CryptoException e) { Log.e(LOG_TAG, "Failed to encrypt data.", e); abort(Constants.JPAKE_ERROR_INTERNAL); + return; } jOutgoing = new ExtendedJSONObject(); jOutgoing.put(Constants.JSON_KEY_TYPE, mySignerId + "3"); @@ -619,7 +611,7 @@ public class JPakeClient implements JPakeRequestDelegate { * @param keyBundle */ private void decryptData(KeyBundle keyBundle) { - Log.d(LOG_TAG, "Verifying their key"); + Logger.debug(LOG_TAG, "Verifying their key"); if (!(theirSignerId + "3").equals((String) jIncoming .get(Constants.JSON_KEY_TYPE))) { try { @@ -628,6 +620,7 @@ public class JPakeClient implements JPakeRequestDelegate { e.printStackTrace(); } abort(Constants.JPAKE_ERROR_WRONGMESSAGE); + return; } // Decrypt payload and verify HMAC. @@ -637,17 +630,20 @@ public class JPakeClient implements JPakeRequestDelegate { } catch (NonObjectJSONException e1) { Log.e(LOG_TAG, "Invalid round 3 data.", e1); abort(Constants.JPAKE_ERROR_WRONGMESSAGE); + return; } - Log.d(LOG_TAG, "Decrypting data."); + Logger.debug(LOG_TAG, "Decrypting data."); String cleartext = null; try { cleartext = new String(decryptPayload(iPayload, keyBundle), "UTF-8"); } catch (UnsupportedEncodingException e1) { Log.e(LOG_TAG, "Failed to decrypt data.", e1); abort(Constants.JPAKE_ERROR_INTERNAL); + return; } catch (CryptoException e1) { Log.e(LOG_TAG, "Failed to decrypt data.", e1); abort(Constants.JPAKE_ERROR_KEYMISMATCH); + return; } JSONObject jCreds = null; try { @@ -666,12 +662,12 @@ public class JPakeClient implements JPakeRequestDelegate { * @param jCreds Credentials to be stored by controller. May be null if device is sender. */ private void complete(JSONObject jCreds) { - Log.d(LOG_TAG, "Exchange complete."); + Logger.debug(LOG_TAG, "Exchange complete."); finished = true; ssActivity.onComplete(jCreds); } - /* JpakeRequestDelegate methods */ + // JPakeRequestDelegate methods. @Override public void onRequestFailure(HttpResponse res) { JPakeResponse response = new JPakeResponse(res); @@ -690,7 +686,7 @@ public class JPakeClient implements JPakeRequestDelegate { int statusCode = response.getStatusCode(); switch (statusCode) { case 304: - Log.d(LOG_TAG, "Channel hasn't been updated yet. Will try again later"); + Logger.debug(LOG_TAG, "Channel hasn't been updated yet. Will try again later"); if (pollTries >= jpakeMaxTries) { Log.e(LOG_TAG, "Tried for " + pollTries + " times, maxTries " + jpakeMaxTries + ", aborting"); abort(Constants.JPAKE_ERROR_TIMEOUT); @@ -706,20 +702,18 @@ public class JPakeClient implements JPakeRequestDelegate { abort(Constants.JPAKE_ERROR_NODATA); break; case 412: // "Precondition failed" - Log.d(LOG_TAG, "Message already replaced on server by other party."); + Logger.debug(LOG_TAG, "Message already replaced on server by other party."); onRequestSuccess(res); break; default: - Log.e(LOG_TAG, "Could not retrieve data. Server responded with HTTP " - + statusCode); + Log.e(LOG_TAG, "Could not retrieve data. Server responded with HTTP " + statusCode); abort(Constants.JPAKE_ERROR_SERVER); return; } pollTries = 0; break; case PUT: - Log.e(LOG_TAG, "Could not upload data. Server responded with HTTP " - + response.getStatusCode()); + Log.e(LOG_TAG, "Could not upload data. Server responded with HTTP " + response.getStatusCode()); abort(Constants.JPAKE_ERROR_SERVER); break; case ABORT: @@ -763,13 +757,23 @@ public class JPakeClient implements JPakeRequestDelegate { return; } channelUrl = jpakeServer + channel; - Log.d(LOG_TAG, "using channel " + channel); + Logger.debug(LOG_TAG, "using channel " + channel); ssActivity.displayPin(secret + channel); // Set up next step. this.state = State.RCVR_STEP_ONE; - computeStepOne(); + try { + computeStepOne(); + } catch (NoSuchAlgorithmException e) { + Log.e(LOG_TAG, "NoSuchAlgorithmException", e); + abort(Constants.JPAKE_ERROR_INTERNAL); + return; + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, "UnsupportedEncodingException", e); + abort(Constants.JPAKE_ERROR_INTERNAL); + return; + } break; // Results from GET request. Continue flow depending on case. @@ -786,8 +790,7 @@ public class JPakeClient implements JPakeRequestDelegate { etagHeaders = response.httpResponse().getHeaders("etag"); if (etagHeaders == null) { try { - Log.e(LOG_TAG, - "Server did not supply ETag for message: " + response.body()); + Log.e(LOG_TAG, "Server did not supply ETag for message: " + response.body()); abort(Constants.JPAKE_ERROR_SERVER); } catch (IllegalStateException e) { e.printStackTrace(); @@ -811,19 +814,40 @@ public class JPakeClient implements JPakeRequestDelegate { abort(Constants.JPAKE_ERROR_INVALID); return; } - Log.d(LOG_TAG, "incoming message: " + jIncoming.toJSONString()); + Logger.debug(LOG_TAG, "incoming message: " + jIncoming.toJSONString()); if (this.state == State.SNDR_STEP_ZERO) { - computeStepOne(); + try { + computeStepOne(); + } catch (NoSuchAlgorithmException e) { + Log.e(LOG_TAG, "NoSuchAlgorithmException", e); + abort(Constants.JPAKE_ERROR_INTERNAL); + return; + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, "UnsupportedEncodingException", e); + abort(Constants.JPAKE_ERROR_INTERNAL); + return; + } } else if (this.state == State.RCVR_STEP_ONE || this.state == State.SNDR_STEP_ONE) { - computeStepTwo(); + try { + computeStepTwo(); + } catch (NonObjectJSONException e) { + Log.e(LOG_TAG, "NonObjectJSONException", e); + abort(Constants.JPAKE_ERROR_INVALID); + return; + } } else if (this.state == State.SNDR_STEP_TWO) { stateContext = state; state = State.PUT; putStep(); } else if (this.state == State.RCVR_STEP_TWO) { - computeFinal(); + try { + computeFinal(); + } catch (NonObjectJSONException e) { + abort(Constants.JPAKE_ERROR_INVALID); + return; + } } else if (this.state == State.VERIFY_KEY) { decryptData(myKeyBundle); } else if (this.state == State.VERIFY_PAIRING) { @@ -856,11 +880,23 @@ public class JPakeClient implements JPakeRequestDelegate { ssActivity.onPaired(); } if (state == State.SNDR_STEP_ONE) { - computeStepTwo(); + try { + computeStepTwo(); + } catch (NonObjectJSONException e) { + Log.e(LOG_TAG, "NonObjectJSONException", e); + abort(Constants.JPAKE_ERROR_INVALID); + return; + } return; // No need to wait for response from PUT request. } if (state == State.SNDR_STEP_TWO) { - computeFinal(); + try { + computeFinal(); + } catch (NonObjectJSONException e) { + Log.e(LOG_TAG, "NonObjectJSONException", e); + abort(Constants.JPAKE_ERROR_INVALID); + return; + } return; // No need to wait for response from PUT request. } @@ -884,20 +920,20 @@ public class JPakeClient implements JPakeRequestDelegate { /* ResourceDelegate that handles Resource responses */ public ResourceDelegate makeRequestResourceDelegate() { - return new JpakeRequestResourceDelegate(this); + return new JPakeRequestResourceDelegate(this); } - public class JpakeRequestResourceDelegate implements ResourceDelegate { + public class JPakeRequestResourceDelegate implements ResourceDelegate { private JPakeRequestDelegate requestDelegate; - public JpakeRequestResourceDelegate(JPakeRequestDelegate delegate) { + public JPakeRequestResourceDelegate(JPakeRequestDelegate delegate) { this.requestDelegate = delegate; } @Override public String getCredentials() { - // Jpake setup has no credentials + // J-PAKE setup has no credentials return null; } @@ -1007,13 +1043,13 @@ public class JPakeClient implements JPakeRequestDelegate { * Defaults to user abort */ public void abort(String error) { - Log.d(LOG_TAG, "aborting..."); + Logger.debug(LOG_TAG, "aborting..."); finished = true; if (error == null) { error = Constants.JPAKE_ERROR_USERABORT; } - Log.d(LOG_TAG, error); + Logger.debug(LOG_TAG, error); if (Constants.JPAKE_ERROR_CHANNEL.equals(error) || Constants.JPAKE_ERROR_NETWORK.equals(error) @@ -1029,7 +1065,7 @@ public class JPakeClient implements JPakeRequestDelegate { * Make a /report post to to server */ private void reportFailure(String error) { - Log.d(LOG_TAG, "reporting error to server"); + Logger.debug(LOG_TAG, "reporting error to server"); this.error = error; runOnThread(new Runnable() { @Override @@ -1055,7 +1091,7 @@ public class JPakeClient implements JPakeRequestDelegate { * Generates and sets a clientId for communications with JPAKE setup server. */ private void setClientId() { - byte[] rBytes = generateRandomBytes(JPAKE_LENGTH_CLIENTID / 2); + byte[] rBytes = Utils.generateRandomBytes(JPAKE_LENGTH_CLIENTID / 2); StringBuilder id = new StringBuilder(); for (byte b : rBytes) { @@ -1077,7 +1113,7 @@ public class JPakeClient implements JPakeRequestDelegate { String key = "23456789abcdefghijkmnpqrstuvwxyz"; int keylen = key.length(); - byte[] rBytes = generateRandomBytes(JPAKE_LENGTH_SECRET); + byte[] rBytes = Utils.generateRandomBytes(JPAKE_LENGTH_SECRET); StringBuilder secret = new StringBuilder(); for (byte b : rBytes) { secret.append(key.charAt(Math.abs(b) * keylen / 256)); @@ -1125,7 +1161,7 @@ public class JPakeClient implements JPakeRequestDelegate { try { getRequest = new JPakeRequest(channelUrl, makeRequestResourceDelegate()); } catch (URISyntaxException e) { - e.printStackTrace(); + Log.e(LOG_TAG, "URISyntaxException", e); abort(Constants.JPAKE_ERROR_CHANNEL); return; } @@ -1148,17 +1184,13 @@ public class JPakeClient implements JPakeRequestDelegate { } /* - * Helper to generate random bytes - * - * @param length Number of bytes to generate + * Helper for turning a string secret into a numeric secret. */ - private static byte[] generateRandomBytes(int length) { - byte[] bytes = new byte[length]; - Random random = new Random(System.nanoTime()); - random.nextBytes(bytes); - return bytes; + public static BigInteger secretAsBigInteger(String secretString) throws UnsupportedEncodingException { + return new BigInteger(secretString.getBytes("UTF-8")); } + /* * Helper function to generate a JSON encoded ZKP. */ @@ -1185,9 +1217,7 @@ public class JPakeClient implements JPakeRequestDelegate { .get(Constants.JSON_KEY_CIPHERTEXT)); byte[] iv = Utils.decodeBase64((String) payload.get(Constants.JSON_KEY_IV)); byte[] hmac = Utils.hex2Byte((String) payload.get(Constants.JSON_KEY_HMAC)); - byte[] plainbytes = Cryptographer.decrypt(new CryptoInfo(ciphertext, iv, - hmac, keybundle)); - return plainbytes; + return CryptoInfo.decrypt(ciphertext, iv, hmac, keybundle).getMessage(); } /** @@ -1206,8 +1236,7 @@ public class JPakeClient implements JPakeRequestDelegate { throw new NoKeyBundleException(); } byte[] cleartextBytes = data.getBytes("UTF-8"); - CryptoInfo info = new CryptoInfo(cleartextBytes, keyBundle); - Cryptographer.encrypt(info); + CryptoInfo info = CryptoInfo.encrypt(cleartextBytes, keyBundle); String message = new String(Base64.encodeBase64(info.getMessage())); String iv = new String(Base64.encodeBase64(info.getIV())); diff --git a/mobile/android/base/sync/jpake/JPakeCrypto.java b/mobile/android/base/sync/jpake/JPakeCrypto.java index ea37a57a2cd..d3f5104b71e 100644 --- a/mobile/android/base/sync/jpake/JPakeCrypto.java +++ b/mobile/android/base/sync/jpake/JPakeCrypto.java @@ -101,8 +101,9 @@ public class JPakeCrypto { * * @param mySignerId * @param valuesOut + * @throws NoSuchAlgorithmException */ - public static void round1(JPakeParty jp, JPakeNumGenerator gen) { + public static void round1(JPakeParty jp, JPakeNumGenerator gen) throws NoSuchAlgorithmException, UnsupportedEncodingException { // Randomly select x1 from [0,q), x2 from [1,q). BigInteger x1 = gen.generateFromRange(Q); // [0, q) BigInteger x2 = jp.x2 = BigInteger.ONE.add(gen.generateFromRange(Q @@ -131,16 +132,19 @@ public class JPakeCrypto { * @param gx4 * @param zkp3 * @param zkp4 - * @throws Gx4IsOneException * @throws IncorrectZkpException + * @throws NoSuchAlgorithmException */ - public static void round2(String secret, JPakeParty jp, - JPakeNumGenerator gen) throws Gx4IsOneException, IncorrectZkpException { + public static void round2(BigInteger secretValue, JPakeParty jp, JPakeNumGenerator gen) + throws IncorrectZkpException, NoSuchAlgorithmException, + Gx3OrGx4IsZeroOrOneException, UnsupportedEncodingException { Log.d(LOG_TAG, "round2 started."); - if (BigInteger.ONE.compareTo(jp.gx4) == 0) { - throw new Gx4IsOneException(); + // checkZkp does some additional checks, but we can throw a more informative exception here. + if (BigInteger.ZERO.compareTo(jp.gx3) == 0 || BigInteger.ONE.compareTo(jp.gx3) == 0 || + BigInteger.ZERO.compareTo(jp.gx4) == 0 || BigInteger.ONE.compareTo(jp.gx4) == 0) { + throw new Gx3OrGx4IsZeroOrOneException(); } // Check ZKP. @@ -149,13 +153,7 @@ public class JPakeCrypto { // Compute a = g^[(x1+x3+x4)*(x2*secret)]. BigInteger y1 = jp.gx3.multiply(jp.gx4).mod(P).multiply(jp.gx1).mod(P); - BigInteger y2 = null; - try { - y2 = jp.x2.multiply(new BigInteger(secret.getBytes("US-ASCII"))).mod(P); - } catch (UnsupportedEncodingException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } + BigInteger y2 = jp.x2.multiply(secretValue).mod(P); BigInteger a = y1.modPow(y2, P); jp.thisZkpA = createZkp(y1, y2, a, jp.signerId, gen); @@ -174,8 +172,8 @@ public class JPakeCrypto { * @return KeyBundle * @throws IncorrectZkpException */ - public static KeyBundle finalRound(String secret, JPakeParty jp) - throws IncorrectZkpException, NoSuchAlgorithmException, InvalidKeyException { + public static KeyBundle finalRound(BigInteger secretValue, JPakeParty jp) + throws IncorrectZkpException, NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException { Log.d(LOG_TAG, "Final round started."); BigInteger gb = jp.gx1.multiply(jp.gx2).mod(P).multiply(jp.gx3) .mod(P); @@ -183,7 +181,7 @@ public class JPakeCrypto { // Calculate shared key g^(x1+x3)*x2*x4*secret, which is equivalent to // (B/g^(x2*x4*s))^x2 = (B*(g^x4)^x2^s^-1)^2. - BigInteger k = jp.gx4.modPow(jp.x2.multiply(new BigInteger(secret.getBytes())).negate().mod(Q), P).multiply(jp.otherA) + BigInteger k = jp.gx4.modPow(jp.x2.multiply(secretValue).negate().mod(Q), P).multiply(jp.otherA) .modPow(jp.x2, P); byte[] enc = new byte[32]; @@ -216,7 +214,7 @@ public class JPakeCrypto { * pass in gx to save on an exponentiation of g^x) */ private static Zkp createZkp(BigInteger g, BigInteger x, BigInteger gx, - String id, JPakeNumGenerator gen) { + String id, JPakeNumGenerator gen) throws NoSuchAlgorithmException, UnsupportedEncodingException { // Generate random r for exponent. BigInteger r = gen.generateFromRange(Q); @@ -237,13 +235,13 @@ public class JPakeCrypto { * Verify ZKP. */ private static void checkZkp(BigInteger g, BigInteger gx, Zkp zkp) - throws IncorrectZkpException { + throws IncorrectZkpException, NoSuchAlgorithmException, UnsupportedEncodingException { BigInteger h = computeBHash(g, zkp.gr, gx, zkp.id); // Check parameters of zkp, and compare to computed hash. These shouldn't // fail. - if (gx.compareTo(BigInteger.ZERO) < 1) {// g^x > 1 + if (gx.compareTo(BigInteger.ONE) < 1) { // g^x > 1. Log.e(LOG_TAG, "g^x > 1 fails."); throw new IncorrectZkpException(); } @@ -277,33 +275,22 @@ public class JPakeCrypto { * form hash. */ private static BigInteger computeBHash(BigInteger g, BigInteger gr, BigInteger gx, - String id) { - MessageDigest sha = null; - try { - sha = MessageDigest.getInstance("SHA-256"); - sha.reset(); + String id) throws NoSuchAlgorithmException, UnsupportedEncodingException { + MessageDigest sha = MessageDigest.getInstance("SHA-256"); + sha.reset(); - /* - * Note: you should ensure the items in H(...) have clear boundaries. It - * is simple if the other party knows sizes of g, gr, gx and signerID and - * hence the boundary is unambiguous. If not, you'd better prepend each - * item with its byte length, but I've omitted that here. - */ + /* + * Note: you should ensure the items in H(...) have clear boundaries. It + * is simple if the other party knows sizes of g, gr, gx and signerID and + * hence the boundary is unambiguous. If not, you'd better prepend each + * item with its byte length, but I've omitted that here. + */ - hashByteArrayWithLength(sha, - BigIntegerHelper.BigIntegerToByteArrayWithoutSign(g)); - hashByteArrayWithLength(sha, - BigIntegerHelper.BigIntegerToByteArrayWithoutSign(gr)); - hashByteArrayWithLength(sha, - BigIntegerHelper.BigIntegerToByteArrayWithoutSign(gx)); - hashByteArrayWithLength(sha, id.getBytes("US-ASCII")); + hashByteArrayWithLength(sha, BigIntegerHelper.BigIntegerToByteArrayWithoutSign(g)); + hashByteArrayWithLength(sha, BigIntegerHelper.BigIntegerToByteArrayWithoutSign(gr)); + hashByteArrayWithLength(sha, BigIntegerHelper.BigIntegerToByteArrayWithoutSign(gx)); + hashByteArrayWithLength(sha, id.getBytes("UTF-8")); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } catch (UnsupportedEncodingException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } byte[] hash = sha.digest(); return BigIntegerHelper.ByteArrayToBigIntegerWithoutSign(hash); diff --git a/mobile/android/base/sync/jpake/JPakeNumGeneratorRandom.java b/mobile/android/base/sync/jpake/JPakeNumGeneratorRandom.java index 6e47df798b0..c2077f75819 100644 --- a/mobile/android/base/sync/jpake/JPakeNumGeneratorRandom.java +++ b/mobile/android/base/sync/jpake/JPakeNumGeneratorRandom.java @@ -38,7 +38,8 @@ package org.mozilla.gecko.sync.jpake; import java.math.BigInteger; -import java.security.SecureRandom; + +import org.mozilla.gecko.sync.Utils; /** * Helper Function to generate a uniformly random value in [0, r). @@ -47,14 +48,6 @@ public class JPakeNumGeneratorRandom implements JPakeNumGenerator { @Override public BigInteger generateFromRange(BigInteger r) { - int maxBytes = (int) Math.ceil(((double) r.bitLength()) / 8); - - byte[] bytes = new byte[maxBytes]; - new SecureRandom().nextBytes(bytes); - BigInteger randInt = new BigInteger(bytes); - // TODO: is this going to be very slow? - // bit shifting/masking to decrease mod computation - return randInt.mod(r); + return Utils.generateBigIntegerLessThan(r); } - } diff --git a/mobile/android/base/sync/jpake/JPakeUtils.java b/mobile/android/base/sync/jpake/JPakeUtils.java deleted file mode 100644 index 2b862eb7e6f..00000000000 --- a/mobile/android/base/sync/jpake/JPakeUtils.java +++ /dev/null @@ -1,50 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Android Sync Client. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Chenxia Liu - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -package org.mozilla.gecko.sync.jpake; - -import java.util.Random; - -public class JPakeUtils { - - public static byte[] generateRandomBytes(int length) { - byte[] bytes = new byte[length]; - Random random = new Random(System.nanoTime()); - random.nextBytes(bytes); - return bytes; - } -} diff --git a/mobile/android/base/sync/middleware/Crypto5MiddlewareRepositorySession.java b/mobile/android/base/sync/middleware/Crypto5MiddlewareRepositorySession.java index b6977be428f..03c0c0a0613 100644 --- a/mobile/android/base/sync/middleware/Crypto5MiddlewareRepositorySession.java +++ b/mobile/android/base/sync/middleware/Crypto5MiddlewareRepositorySession.java @@ -146,7 +146,7 @@ public class Crypto5MiddlewareRepositorySession extends RepositorySession { } @Override - public void onFetchSucceeded(Record[] records, long end) { + public void onFetchSucceeded(Record[] records, long fetchEnd) { for (Record record : records) { try { this.onFetchedRecord(record); @@ -154,12 +154,12 @@ public class Crypto5MiddlewareRepositorySession extends RepositorySession { this.onFetchFailed(e, record); } } - this.onFetchCompleted(end); + this.onFetchCompleted(fetchEnd); } @Override - public void onFetchCompleted(long end) { - next.onFetchCompleted(end); + public void onFetchCompleted(final long fetchEnd) { + next.onFetchCompleted(fetchEnd); } @Override @@ -237,4 +237,9 @@ public class Crypto5MiddlewareRepositorySession extends RepositorySession { public void storeDone() { inner.storeDone(); } + + @Override + public void storeDone(long end) { + inner.storeDone(end); + } } diff --git a/mobile/android/base/sync/net/SyncStorageRequest.java b/mobile/android/base/sync/net/SyncStorageRequest.java index 7c9f626bcbd..a9258382bdf 100644 --- a/mobile/android/base/sync/net/SyncStorageRequest.java +++ b/mobile/android/base/sync/net/SyncStorageRequest.java @@ -170,10 +170,13 @@ public class SyncStorageRequest implements Resource { if (ifUnmodifiedSince != null) { request.setHeader("x-weave-if-unmodified-since", ifUnmodifiedSince); } + if (request.getMethod().equalsIgnoreCase("DELETE")) { + request.addHeader("x-confirm-delete", "1"); + } } } - public static String USER_AGENT = "Firefox AndroidSync 0.4"; + public static String USER_AGENT = "Firefox AndroidSync 0.5"; protected SyncResourceDelegate resourceDelegate; public SyncStorageRequestDelegate delegate; protected BaseResource resource; @@ -192,7 +195,6 @@ public class SyncStorageRequest implements Resource { } public void delete() { - this.resource.request.addHeader("x-confirm-delete", "1"); this.resource.delete(); } diff --git a/mobile/android/base/sync/net/TLSSocketFactory.java b/mobile/android/base/sync/net/TLSSocketFactory.java index 30339cbf715..5ddfdbfbcb3 100644 --- a/mobile/android/base/sync/net/TLSSocketFactory.java +++ b/mobile/android/base/sync/net/TLSSocketFactory.java @@ -98,9 +98,4 @@ public class TLSSocketFactory extends SSLSocketFactory { setEnabledCipherSuites(socket); return socket; } - - @Override - public boolean isSecure(Socket sock) throws IllegalArgumentException { - return true; - } } \ No newline at end of file diff --git a/mobile/android/base/sync/repositories/RepositorySession.java b/mobile/android/base/sync/repositories/RepositorySession.java index d2626c084db..cd2e85e4650 100644 --- a/mobile/android/base/sync/repositories/RepositorySession.java +++ b/mobile/android/base/sync/repositories/RepositorySession.java @@ -41,7 +41,7 @@ package org.mozilla.gecko.sync.repositories; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import org.mozilla.gecko.sync.Utils; +import org.mozilla.gecko.sync.Logger; import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionBeginDelegate; import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFetchRecordsDelegate; import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFinishDelegate; @@ -77,11 +77,11 @@ public abstract class RepositorySession { private static final String LOG_TAG = "RepositorySession"; private static void error(String message) { - Utils.error(LOG_TAG, message); + Logger.error(LOG_TAG, message); } protected static void trace(String message) { - Utils.trace(LOG_TAG, message); + Logger.trace(LOG_TAG, message); } protected SessionStatus status = SessionStatus.UNSTARTED; @@ -147,11 +147,18 @@ public abstract class RepositorySession { public abstract void store(Record record) throws NoStoreDelegateException; public void storeDone() { + // Our default behavior will be to assume that the Runnable is + // executed as soon as all the stores synchronously finish, so + // our end timestamp can just be… now. + storeDone(now()); + } + + public void storeDone(final long end) { Log.d(LOG_TAG, "Scheduling onStoreCompleted for after storing is done."); Runnable command = new Runnable() { @Override public void run() { - delegate.onStoreCompleted(); + delegate.onStoreCompleted(end); } }; storeWorkQueue.execute(command); diff --git a/mobile/android/base/sync/repositories/RepositorySessionBundle.java b/mobile/android/base/sync/repositories/RepositorySessionBundle.java index 3645e47a1a9..84630846e93 100644 --- a/mobile/android/base/sync/repositories/RepositorySessionBundle.java +++ b/mobile/android/base/sync/repositories/RepositorySessionBundle.java @@ -41,6 +41,7 @@ import java.io.IOException; import org.json.simple.parser.ParseException; import org.mozilla.gecko.sync.ExtendedJSONObject; +import org.mozilla.gecko.sync.Logger; import org.mozilla.gecko.sync.NonObjectJSONException; import android.util.Log; @@ -75,8 +76,11 @@ public class RepositorySessionBundle extends ExtendedJSONObject { } public void bumpTimestamp(long timestamp) { - if (timestamp > this.getTimestamp()) { + long existing = this.getTimestamp(); + if (timestamp > existing) { this.setTimestamp(timestamp); + } else { + Logger.debug(LOG_TAG, "Timestamp " + timestamp + " not greater than " + existing + "; not bumping."); } } } diff --git a/mobile/android/base/sync/repositories/Server11RepositorySession.java b/mobile/android/base/sync/repositories/Server11RepositorySession.java index e92a354bb47..28afc59bebb 100644 --- a/mobile/android/base/sync/repositories/Server11RepositorySession.java +++ b/mobile/android/base/sync/repositories/Server11RepositorySession.java @@ -44,6 +44,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Date; +import java.util.concurrent.atomic.AtomicLong; import org.json.simple.JSONArray; import org.mozilla.gecko.sync.CryptoRecord; @@ -82,7 +83,7 @@ public class Server11RepositorySession extends RepositorySession { } } - public static final String LOG_TAG = "Server11RepositorySession"; + public static final String LOG_TAG = "Server11Session"; private static final int UPLOAD_BYTE_THRESHOLD = 1024 * 1024; // 1MB. private static final int UPLOAD_ITEM_THRESHOLD = 50; @@ -186,6 +187,20 @@ public class Server11RepositorySession extends RepositorySession { Server11Repository serverRepository; + AtomicLong uploadTimestamp = new AtomicLong(0); + + private void bumpUploadTimestamp(long ts) { + while (true) { + long existing = uploadTimestamp.get(); + if (existing > ts) { + return; + } + if (uploadTimestamp.compareAndSet(existing, ts)) { + return; + } + } + } + public Server11RepositorySession(Repository repository) { super(repository); serverRepository = (Server11Repository) repository; @@ -320,7 +335,7 @@ public class Server11RepositorySession extends RepositorySession { public void storeDone() { synchronized (recordsBufferMonitor) { flush(); - super.storeDone(); + storeDone(uploadTimestamp.get()); } } @@ -379,6 +394,10 @@ public class Server11RepositorySession extends RepositorySession { (success.size() > 0)) { Log.d(LOG_TAG, "Successful records: " + success.toString()); // TODO: how do we notify without the whole record? + + long ts = response.normalizedWeaveTimestamp(); + Log.d(LOG_TAG, "Passing back upload X-Weave-Timestamp: " + ts); + bumpUploadTimestamp(ts); } if ((failed != null) && (failed.object.size() > 0)) { diff --git a/mobile/android/base/sync/repositories/StoreTrackingRepositorySession.java b/mobile/android/base/sync/repositories/StoreTrackingRepositorySession.java index 9574652062d..cded12065af 100644 --- a/mobile/android/base/sync/repositories/StoreTrackingRepositorySession.java +++ b/mobile/android/base/sync/repositories/StoreTrackingRepositorySession.java @@ -11,7 +11,7 @@ import org.mozilla.gecko.sync.repositories.domain.Record; import android.util.Log; public abstract class StoreTrackingRepositorySession extends RepositorySession { - private static final String LOG_TAG = "StoreTrackingRepositorySession"; + private static final String LOG_TAG = "StoreTrackSession"; protected StoreTracker storeTracker; protected static StoreTracker createStoreTracker() { @@ -59,4 +59,4 @@ public abstract class StoreTrackingRepositorySession extends RepositorySession { this.storeTracker = null; super.finish(delegate); } -} \ No newline at end of file +} diff --git a/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java b/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java index d788617be4e..652861a2979 100644 --- a/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java +++ b/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java @@ -53,7 +53,7 @@ import android.util.Log; public class AndroidBrowserBookmarksDataAccessor extends AndroidBrowserRepositoryDataAccessor { - private static final String LOG_TAG = "AndroidBrowserBookmarksDataAccessor"; + private static final String LOG_TAG = "BookmarksDataAccessor"; /* * Fragments of SQL to make our lives easier. diff --git a/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java b/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java index 1e0d525c4d0..7df6cee5f6f 100644 --- a/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java +++ b/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java @@ -41,8 +41,8 @@ import java.util.ArrayList; import java.util.HashMap; import org.json.simple.JSONArray; +import org.mozilla.gecko.sync.Logger; import org.mozilla.gecko.sync.Utils; -import org.mozilla.gecko.sync.repositories.BookmarkNeedsReparentingException; import org.mozilla.gecko.sync.repositories.NoGuidForIdException; import org.mozilla.gecko.sync.repositories.NullCursorException; import org.mozilla.gecko.sync.repositories.ParentNotFoundException; @@ -54,7 +54,6 @@ import org.mozilla.gecko.sync.repositories.domain.Record; import android.content.Context; import android.database.Cursor; -import android.util.Log; public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepositorySession { @@ -122,7 +121,7 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo parentName = RepoUtils.getStringFromCursor(name, BrowserContract.Bookmarks.TITLE); } else { - Log.e(LOG_TAG, "Couldn't find record with guid '" + parentGUID + "' when looking for parent name."); + Logger.error(LOG_TAG, "Couldn't find record with guid '" + parentGUID + "' when looking for parent name."); throw new ParentNotFoundException(null); } } finally { @@ -157,13 +156,13 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo int childPosition = (int) RepoUtils.getLongFromCursor(children, BrowserContract.Bookmarks.POSITION); trace(" Child position: " + childPosition); if (childPosition >= count) { - Log.w(LOG_TAG, "Child position " + childPosition + " greater than expected children " + count); + Logger.warn(LOG_TAG, "Child position " + childPosition + " greater than expected children " + count); broken.put(childGuid, 0L); } else { String existing = kids[childPosition]; if (existing != null) { - Log.w(LOG_TAG, "Child position " + childPosition + " already occupied! (" + - childGuid + ", " + existing + ")"); + Logger.warn(LOG_TAG, "Child position " + childPosition + " already occupied! (" + + childGuid + ", " + existing + ")"); broken.put(childGuid, 0L); } else { kids[childPosition] = childGuid; @@ -175,7 +174,7 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo try { Utils.fillArraySpaces(kids, broken); } catch (Exception e) { - Log.e(LOG_TAG, "Unable to reposition children to yield a valid sequence. Data loss may result.", e); + Logger.error(LOG_TAG, "Unable to reposition children to yield a valid sequence. Data loss may result.", e); } // TODO: now use 'broken' to edit the records on disk. @@ -187,8 +186,9 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo } childArray.add(kid); } - if (Utils.ENABLE_TRACE_LOGGING) { - Log.d(LOG_TAG, "Output child array: " + childArray.toJSONString()); + if (Logger.logVerbose(LOG_TAG)) { + // Don't JSON-encode unless we're logging. + Logger.trace(LOG_TAG, "Output child array: " + childArray.toJSONString()); } } finally { children.close(); @@ -199,10 +199,10 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo @Override protected Record recordFromMirrorCursor(Cursor cur) throws NoGuidForIdException, NullCursorException, ParentNotFoundException { String recordGUID = getGUID(cur); - Log.d(LOG_TAG, "Record from mirror cursor: " + recordGUID); + Logger.trace(LOG_TAG, "Record from mirror cursor: " + recordGUID); if (forbiddenGUID(recordGUID)) { - Log.d(LOG_TAG, "Ignoring " + recordGUID + " record in recordFromMirrorCursor."); + Logger.debug(LOG_TAG, "Ignoring " + recordGUID + " record in recordFromMirrorCursor."); return null; } @@ -210,10 +210,10 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo String androidParentGUID = getGUIDForID(androidParentID); if (androidParentGUID == null) { - Log.d(LOG_TAG, "No parent GUID for record " + recordGUID + " with parent " + androidParentID); + Logger.debug(LOG_TAG, "No parent GUID for record " + recordGUID + " with parent " + androidParentID); // If the parent has been stored and somehow has a null GUID, throw an error. if (idToGuid.containsKey(androidParentID)) { - Log.e(LOG_TAG, "Have the parent android ID for the record but the parent's GUID wasn't found."); + Logger.error(LOG_TAG, "Have the parent android ID for the record but the parent's GUID wasn't found."); throw new NoGuidForIdException(null); } } @@ -227,13 +227,13 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo protected JSONArray getChildArrayForCursor(Cursor cur, String recordGUID) throws NullCursorException { JSONArray childArray = null; boolean isFolder = rowIsFolder(cur); - Log.d(LOG_TAG, "Record " + recordGUID + " is a " + (isFolder ? "folder." : "bookmark.")); + Logger.debug(LOG_TAG, "Record " + recordGUID + " is a " + (isFolder ? "folder." : "bookmark.")); if (isFolder) { long androidID = guidToID.get(recordGUID); childArray = getChildren(androidID); } if (childArray != null) { - Log.d(LOG_TAG, "Fetched " + childArray.size() + " children for " + recordGUID); + Logger.debug(LOG_TAG, "Fetched " + childArray.size() + " children for " + recordGUID); } return childArray; } @@ -245,7 +245,7 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo bmk.type.equalsIgnoreCase(AndroidBrowserBookmarksDataAccessor.TYPE_FOLDER)) { return true; } - Log.i(LOG_TAG, "Ignoring record with guid: " + record.guid + " and type: " + ((BookmarkRecord)record).type); + Logger.info(LOG_TAG, "Ignoring record with guid: " + record.guid + " and type: " + ((BookmarkRecord)record).type); return false; } @@ -255,12 +255,12 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo // and insert them if they don't exist. Cursor cur; try { - Log.d(LOG_TAG, "Check and build special GUIDs."); + Logger.debug(LOG_TAG, "Check and build special GUIDs."); dataAccessor.checkAndBuildSpecialGuids(); cur = dataAccessor.getGuidsIDsForFolders(); - Log.d(LOG_TAG, "Got GUIDs for folders."); + Logger.debug(LOG_TAG, "Got GUIDs for folders."); } catch (android.database.sqlite.SQLiteConstraintException e) { - Log.e(LOG_TAG, "Got sqlite constraint exception working with Fennec bookmark DB.", e); + Logger.error(LOG_TAG, "Got sqlite constraint exception working with Fennec bookmark DB.", e); delegate.onBeginFailed(e); return; } catch (NullCursorException e) { @@ -274,7 +274,7 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo // To deal with parent mapping of bookmarks we have to do some // hairy stuff. Here's the setup for it. - Log.d(LOG_TAG, "Preparing folder ID mappings."); + Logger.debug(LOG_TAG, "Preparing folder ID mappings."); idToGuid.put(0L, "places"); // Fake our root. try { cur.moveToFirst(); @@ -283,13 +283,13 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo long id = RepoUtils.getLongFromCursor(cur, BrowserContract.Bookmarks._ID); guidToID.put(guid, id); idToGuid.put(id, guid); - Log.d(LOG_TAG, "GUID " + guid + " maps to " + id); + Logger.debug(LOG_TAG, "GUID " + guid + " maps to " + id); cur.moveToNext(); } } finally { cur.close(); } - Log.d(LOG_TAG, "Done with initial setup of bookmarks session."); + Logger.debug(LOG_TAG, "Done with initial setup of bookmarks session."); super.begin(delegate); } @@ -298,8 +298,8 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo // Override finish to do this check; make sure all records // needing re-parenting have been re-parented. if (needsReparenting != 0) { - Log.e(LOG_TAG, "Finish called but " + needsReparenting + - " bookmark(s) have been placed in unsorted bookmarks and not been reparented."); + Logger.error(LOG_TAG, "Finish called but " + needsReparenting + + " bookmark(s) have been placed in unsorted bookmarks and not been reparented."); // TODO: handling of failed reparenting. // E.g., delegate.onFinishFailed(new BookmarkNeedsReparentingException(null)); @@ -337,16 +337,28 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo missingParentToChildren.put(bmk.parentID, children); } - if (bmk.isFolder()) { - Log.d(LOG_TAG, "Inserting folder " + bmk.guid + ", " + bmk.title + - " with parent " + bmk.androidParentID + - " (" + bmk.parentID + ", " + bmk.parentName + - ", " + bmk.pos + ")"); + if (Logger.LOG_PERSONAL_INFORMATION) { + if (bmk.isFolder()) { + Logger.pii(LOG_TAG, "Inserting folder " + bmk.guid + ", " + bmk.title + + " with parent " + bmk.androidParentID + + " (" + bmk.parentID + ", " + bmk.parentName + + ", " + bmk.pos + ")"); + } else { + Logger.pii(LOG_TAG, "Inserting bookmark " + bmk.guid + ", " + bmk.title + ", " + + bmk.bookmarkURI + " with parent " + bmk.androidParentID + + " (" + bmk.parentID + ", " + bmk.parentName + + ", " + bmk.pos + ")"); + } } else { - Log.d(LOG_TAG, "Inserting bookmark " + bmk.guid + ", " + bmk.title + ", " + - bmk.bookmarkURI + " with parent " + bmk.androidParentID + - " (" + bmk.parentID + ", " + bmk.parentName + - ", " + bmk.pos + ")"); + if (bmk.isFolder()) { + Logger.debug(LOG_TAG, "Inserting folder " + bmk.guid + ", parent " + + bmk.androidParentID + + " (" + bmk.parentID + ", " + bmk.pos + ")"); + } else { + Logger.debug(LOG_TAG, "Inserting bookmark " + bmk.guid + " with parent " + + bmk.androidParentID + + " (" + bmk.parentID + ", " + ", " + bmk.pos + ")"); + } } return bmk; } diff --git a/mobile/android/base/sync/repositories/android/AndroidBrowserHistoryDataExtender.java b/mobile/android/base/sync/repositories/android/AndroidBrowserHistoryDataExtender.java index 9c6d9df86d9..82359a3ef96 100644 --- a/mobile/android/base/sync/repositories/android/AndroidBrowserHistoryDataExtender.java +++ b/mobile/android/base/sync/repositories/android/AndroidBrowserHistoryDataExtender.java @@ -145,12 +145,15 @@ public class AndroidBrowserHistoryDataExtender extends SQLiteOpenHelper { } public Cursor fetch(String guid) throws NullCursorException { + String where = COL_GUID + " = ?"; + String[] args = new String[] { guid }; + SQLiteDatabase db = this.getCachedReadableDatabase(); long queryStart = System.currentTimeMillis(); Cursor cur = db.query(TBL_HISTORY_EXT, new String[] { COL_GUID, COL_VISITS }, - COL_GUID + " = '" + guid + "'", - null, null, null, null); + where, args, + null, null, null); RepoUtils.queryTimeLogger("AndroidBrowserHistoryDataExtender.fetch(guid)", queryStart, System.currentTimeMillis()); if (cur == null) { Log.e(TAG, "Got a null cursor while doing fetch for guid " + guid + " on history extension table"); @@ -160,7 +163,10 @@ public class AndroidBrowserHistoryDataExtender extends SQLiteOpenHelper { } public void delete(String guid) { + String where = COL_GUID + " = ?"; + String[] args = new String[] { guid }; + SQLiteDatabase db = this.getCachedWritableDatabase(); - db.delete(TBL_HISTORY_EXT, COL_GUID + " = '" + guid + "'", null); + db.delete(TBL_HISTORY_EXT, where, args); } } diff --git a/mobile/android/base/sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java b/mobile/android/base/sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java index 25f85fa99b2..2459f91e9a6 100644 --- a/mobile/android/base/sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java +++ b/mobile/android/base/sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java @@ -51,7 +51,7 @@ public abstract class AndroidBrowserRepositoryDataAccessor { private static final String[] GUID_COLUMNS = new String[] { BrowserContract.SyncColumns.GUID }; protected Context context; - protected String LOG_TAG = "AndroidBrowserRepositoryDataAccessor"; + protected static String LOG_TAG = "BrowserDataAccessor"; private final RepoUtils.QueryHelper queryHelper; public AndroidBrowserRepositoryDataAccessor(Context context) { diff --git a/mobile/android/base/sync/repositories/android/AndroidBrowserRepositorySession.java b/mobile/android/base/sync/repositories/android/AndroidBrowserRepositorySession.java index 3ac08dfcb8a..928c255b8f7 100644 --- a/mobile/android/base/sync/repositories/android/AndroidBrowserRepositorySession.java +++ b/mobile/android/base/sync/repositories/android/AndroidBrowserRepositorySession.java @@ -41,7 +41,7 @@ package org.mozilla.gecko.sync.repositories.android; import java.util.ArrayList; import java.util.HashMap; -import org.mozilla.gecko.sync.Utils; +import org.mozilla.gecko.sync.Logger; import org.mozilla.gecko.sync.repositories.InactiveSessionException; import org.mozilla.gecko.sync.repositories.InvalidRequestException; import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException; @@ -62,7 +62,6 @@ import org.mozilla.gecko.sync.repositories.domain.Record; import android.database.Cursor; import android.net.Uri; -import android.util.Log; /** * You'll notice that all delegate calls *either*: @@ -89,7 +88,7 @@ import android.util.Log; public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepositorySession { protected AndroidBrowserRepositoryDataAccessor dbHelper; - public static final String LOG_TAG = "AndroidBrowserRepositorySession"; + public static final String LOG_TAG = "BrowserRepoSession"; private HashMap recordToGuid; public AndroidBrowserRepositorySession(Repository repository) { @@ -142,7 +141,7 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos // is no way of knowing which call would be hit first. checkDatabase(); } catch (ProfileDatabaseException e) { - Log.e(LOG_TAG, "ProfileDatabaseException from begin. Fennec must be launched once until this error is fixed"); + Logger.error(LOG_TAG, "ProfileDatabaseException from begin. Fennec must be launched once until this error is fixed"); deferredDelegate.onBeginFailed(e); return; } catch (NullCursorException e) { @@ -159,10 +158,10 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos protected abstract String buildRecordString(Record record); protected void checkDatabase() throws ProfileDatabaseException, NullCursorException { - Utils.info(LOG_TAG, "BEGIN: checking database."); + Logger.info(LOG_TAG, "BEGIN: checking database."); try { dbHelper.fetch(new String[] { "none" }).close(); - Utils.info(LOG_TAG, "END: checking database."); + Logger.info(LOG_TAG, "END: checking database."); } catch (NullPointerException e) { throw new ProfileDatabaseException(e); } @@ -215,7 +214,7 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos cur.moveToNext(); } } finally { - Log.d(LOG_TAG, "Closing cursor after guidsSince."); + Logger.debug(LOG_TAG, "Closing cursor after guidsSince."); cur.close(); } @@ -240,7 +239,7 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos } protected void fetchFromCursor(Cursor cursor, RecordFilter filter, long end) { - Log.d(LOG_TAG, "Fetch from cursor:"); + Logger.debug(LOG_TAG, "Fetch from cursor:"); try { try { if (!cursor.moveToFirst()) { @@ -248,28 +247,28 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos return; } while (!cursor.isAfterLast()) { - Log.d(LOG_TAG, "... one more record."); Record r = recordFromMirrorCursor(cursor); if (r != null) { if (filter == null || !filter.excludeRecord(r)) { + Logger.trace(LOG_TAG, "Processing record " + r.guid); delegate.onFetchedRecord(transformRecord(r)); } else { - Log.d(LOG_TAG, "Filter says to skip record."); + Logger.debug(LOG_TAG, "Skipping filtered record " + r.guid); } } cursor.moveToNext(); } delegate.onFetchCompleted(end); } catch (NoGuidForIdException e) { - Log.w(LOG_TAG, "No GUID for ID.", e); + Logger.warn(LOG_TAG, "No GUID for ID.", e); delegate.onFetchFailed(e, null); } catch (Exception e) { - Log.w(LOG_TAG, "Exception in fetchFromCursor.", e); + Logger.warn(LOG_TAG, "Exception in fetchFromCursor.", e); delegate.onFetchFailed(e, null); return; } } finally { - Log.d(LOG_TAG, "Closing cursor after fetch."); + Logger.trace(LOG_TAG, "Closing cursor after fetch."); cursor.close(); } } @@ -298,7 +297,7 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos } if (guids == null || guids.length < 1) { - Log.e(LOG_TAG, "No guids sent to fetch"); + Logger.error(LOG_TAG, "No guids sent to fetch"); delegate.onFetchFailed(new InvalidRequestException(null), null); return; } @@ -319,7 +318,7 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos throw new IllegalStateException("Store tracker not yet initialized!"); } - Log.i(LOG_TAG, "Running fetchSince(" + timestamp + ")."); + Logger.info(LOG_TAG, "Running fetchSince(" + timestamp + ")."); FetchSinceRunnable command = new FetchSinceRunnable(timestamp, now(), this.storeTracker.getFilter(), delegate); delegateQueue.execute(command); } @@ -367,7 +366,7 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos throw new NoStoreDelegateException(); } if (record == null) { - Log.e(LOG_TAG, "Record sent to store was null"); + Logger.error(LOG_TAG, "Record sent to store was null"); throw new IllegalArgumentException("Null record passed to AndroidBrowserRepositorySession.store()."); } @@ -388,7 +387,7 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos // See Bug 708149. This might be resolved by Fennec changing its database // schema, or by Sync storing non-applied records in its own private database. if (!checkRecordType(record)) { - Log.d(LOG_TAG, "Ignoring record " + record.guid + " due to unknown record type."); + Logger.debug(LOG_TAG, "Ignoring record " + record.guid + " due to unknown record type."); // Don't throw: we don't want to abort the entire sync when we get a livemark! // delegate.onRecordStoreFailed(new InvalidBookmarkTypeException(null)); @@ -445,7 +444,7 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos trace("Remote is older, local is not deleted. Ignoring."); if (!locallyModified) { - Log.w(LOG_TAG, "Inconsistency: old remote record is deleted, but local record not modified!"); + Logger.warn(LOG_TAG, "Inconsistency: old remote record is deleted, but local record not modified!"); // Ensure that this is tracked for upload. } return; @@ -475,35 +474,35 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos Record toStore = reconcileRecords(record, existingRecord, lastRemoteRetrieval, lastLocalRetrieval); if (toStore == null) { - Log.d(LOG_TAG, "Reconciling returned null. Not inserting a record."); + Logger.debug(LOG_TAG, "Reconciling returned null. Not inserting a record."); return; } // TODO: pass in timestamps? - Log.d(LOG_TAG, "Replacing " + existingRecord.guid + " with record " + toStore.guid); + Logger.debug(LOG_TAG, "Replacing " + existingRecord.guid + " with record " + toStore.guid); Record replaced = replace(toStore, existingRecord); // Note that we don't track records here; deciding that is the job // of reconcileRecords. - Log.d(LOG_TAG, "Calling delegate callback with guid " + replaced.guid + - "(" + replaced.androidID + ")"); + Logger.debug(LOG_TAG, "Calling delegate callback with guid " + replaced.guid + + "(" + replaced.androidID + ")"); delegate.onRecordStoreSucceeded(replaced); return; } catch (MultipleRecordsForGuidException e) { - Log.e(LOG_TAG, "Multiple records returned for given guid: " + record.guid); + Logger.error(LOG_TAG, "Multiple records returned for given guid: " + record.guid); delegate.onRecordStoreFailed(e); return; } catch (NoGuidForIdException e) { - Log.e(LOG_TAG, "Store failed for " + record.guid, e); + Logger.error(LOG_TAG, "Store failed for " + record.guid, e); delegate.onRecordStoreFailed(e); return; } catch (NullCursorException e) { - Log.e(LOG_TAG, "Store failed for " + record.guid, e); + Logger.error(LOG_TAG, "Store failed for " + record.guid, e); delegate.onRecordStoreFailed(e); return; } catch (Exception e) { - Log.e(LOG_TAG, "Store failed for " + record.guid, e); + Logger.error(LOG_TAG, "Store failed for " + record.guid, e); delegate.onRecordStoreFailed(e); return; } @@ -523,11 +522,11 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos Record toStore = prepareRecord(record); Uri recordURI = dbHelper.insert(toStore); long id = RepoUtils.getAndroidIdFromUri(recordURI); - Log.d(LOG_TAG, "Inserted as " + id); + Logger.debug(LOG_TAG, "Inserted as " + id); toStore.androidID = id; updateBookkeeping(toStore); - Log.d(LOG_TAG, "insert() returning record " + toStore.guid); + Logger.debug(LOG_TAG, "insert() returning record " + toStore.guid); return toStore; } @@ -537,7 +536,7 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos // newRecord should already have suitable androidID and guid. dbHelper.update(existingRecord.guid, toStore); updateBookkeeping(toStore); - Log.d(LOG_TAG, "replace() returning record " + toStore.guid); + Logger.debug(LOG_TAG, "replace() returning record " + toStore.guid); return toStore; } @@ -583,15 +582,15 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos protected Record findExistingRecord(Record record) throws MultipleRecordsForGuidException, NoGuidForIdException, NullCursorException, ParentNotFoundException { - Log.d(LOG_TAG, "Finding existing record for incoming record with GUID " + record.guid); + Logger.debug(LOG_TAG, "Finding existing record for incoming record with GUID " + record.guid); String recordString = buildRecordString(record); - Log.d(LOG_TAG, "Searching with record string " + recordString); + Logger.debug(LOG_TAG, "Searching with record string " + recordString); String guid = getRecordToGuidMap().get(recordString); if (guid != null) { - Log.d(LOG_TAG, "Found one. Returning computed record."); + Logger.debug(LOG_TAG, "Found one. Returning computed record."); return recordForGUID(guid); } - Log.d(LOG_TAG, "findExistingRecord failed to find one for " + record.guid); + Logger.debug(LOG_TAG, "findExistingRecord failed to find one for " + record.guid); return null; } @@ -603,7 +602,7 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos } private void createRecordToGuidMap() throws NoGuidForIdException, NullCursorException, ParentNotFoundException { - Utils.info(LOG_TAG, "BEGIN: creating record -> GUID map."); + Logger.info(LOG_TAG, "BEGIN: creating record -> GUID map."); recordToGuid = new HashMap(); Cursor cur = dbHelper.fetchAll(); try { @@ -620,7 +619,7 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos } finally { cur.close(); } - Utils.info(LOG_TAG, "END: creating record -> GUID map."); + Logger.info(LOG_TAG, "END: creating record -> GUID map."); } public void putRecordToGuidMap(String recordString, String guid) throws NoGuidForIdException, NullCursorException, ParentNotFoundException { diff --git a/mobile/android/base/sync/repositories/android/RepoUtils.java b/mobile/android/base/sync/repositories/android/RepoUtils.java index ce96c801db2..2e29e0616a0 100644 --- a/mobile/android/base/sync/repositories/android/RepoUtils.java +++ b/mobile/android/base/sync/repositories/android/RepoUtils.java @@ -46,6 +46,7 @@ import org.json.simple.JSONArray; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import org.mozilla.gecko.R; +import org.mozilla.gecko.sync.Logger; import org.mozilla.gecko.sync.repositories.NullCursorException; import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord; import org.mozilla.gecko.sync.repositories.domain.HistoryRecord; @@ -53,8 +54,8 @@ import org.mozilla.gecko.sync.repositories.domain.PasswordRecord; import android.content.Context; import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; import android.net.Uri; -import android.util.Log; public class RepoUtils { @@ -165,6 +166,7 @@ public class RepoUtils { return this.query(null, projection, selection, selectionArgs, sortOrder); } + // For ContentProvider queries. public Cursor query(String label, String[] projection, String selection, String[] selectionArgs, String sortOrder) { String logLabel = (label == null) ? this.tag : this.tag + label; long queryStart = android.os.SystemClock.uptimeMillis(); @@ -174,10 +176,32 @@ public class RepoUtils { return c; } + // For SQLiteOpenHelper queries. + public Cursor query(SQLiteDatabase db, String label, String table, String[] columns, + String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit) { + String logLabel = (label == null) ? this.tag : this.tag + label; + long queryStart = android.os.SystemClock.uptimeMillis(); + Cursor c = db.query(table, columns, selection, selectionArgs, groupBy, having, orderBy, limit); + long queryEnd = android.os.SystemClock.uptimeMillis(); + RepoUtils.queryTimeLogger(logLabel, queryStart, queryEnd); + return c; + } + public Cursor safeQuery(String label, String[] projection, String selection, String[] selectionArgs, String sortOrder) throws NullCursorException { Cursor c = this.query(label, projection, selection, selectionArgs, sortOrder); if (c == null) { - Log.e(tag, "Got null cursor exception in " + tag + ((label == null) ? "" : label)); + Logger.error(tag, "Got null cursor exception in " + tag + ((label == null) ? "" : label)); + throw new NullCursorException(null); + } + return c; + } + + public Cursor safeQuery(SQLiteDatabase db, String label, String table, String[] columns, + String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit) throws NullCursorException { + Cursor c = this.query(db, label, table, columns, selection, selectionArgs, + groupBy, having, orderBy, limit); + if (c == null) { + Logger.error(tag, "Got null cursor exception in " + tag + ((label == null) ? "" : label)); throw new NullCursorException(null); } return c; @@ -206,7 +230,7 @@ public class RepoUtils { try { return (JSONArray) new JSONParser().parse(getStringFromCursor(cur, colId)); } catch (ParseException e) { - Log.e(LOG_TAG, "JSON parsing error for " + colId, e); + Logger.error(LOG_TAG, "JSON parsing error for " + colId, e); return null; } } @@ -223,7 +247,7 @@ public class RepoUtils { final String guid = rec.guid; if (guid == null) { // Oh dear. - Log.e(LOG_TAG, "No guid in computeParentFields!"); + Logger.error(LOG_TAG, "No guid in computeParentFields!"); return null; } @@ -232,13 +256,13 @@ public class RepoUtils { // No magic parent. Use whatever the caller suggests. realParent = suggestedParentID; } else { - Log.d(LOG_TAG, "Ignoring suggested parent ID " + suggestedParentID + - " for " + guid + "; using " + realParent); + Logger.debug(LOG_TAG, "Ignoring suggested parent ID " + suggestedParentID + + " for " + guid + "; using " + realParent); } if (realParent == null) { // Oh dear. - Log.e(LOG_TAG, "No parent for record " + guid); + Logger.error(LOG_TAG, "No parent for record " + guid); return null; } @@ -283,18 +307,24 @@ public class RepoUtils { private static BookmarkRecord logBookmark(BookmarkRecord rec) { try { - Log.d(LOG_TAG, "Returning bookmark record " + rec.guid + " (" + rec.androidID + - ", " + rec.parentName + ":" + rec.parentID + ")"); - Log.d(LOG_TAG, "> Title: " + rec.title); - Log.d(LOG_TAG, "> Type: " + rec.type); - Log.d(LOG_TAG, "> URI: " + rec.bookmarkURI); - Log.d(LOG_TAG, "> Android position: " + rec.androidPosition); - Log.d(LOG_TAG, "> Position: " + rec.pos); - if (rec.isFolder()) { - Log.d(LOG_TAG, "FOLDER: Children are " + (rec.children == null ? "null" : rec.children.toJSONString())); + Logger.debug(LOG_TAG, "Returning bookmark record " + rec.guid + " (" + rec.androidID + + ", parent " + rec.parentID + ")"); + if (Logger.LOG_PERSONAL_INFORMATION) { + Logger.pii(LOG_TAG, "> Parent name: " + rec.parentName); + Logger.pii(LOG_TAG, "> Title: " + rec.title); + Logger.pii(LOG_TAG, "> Type: " + rec.type); + Logger.pii(LOG_TAG, "> URI: " + rec.bookmarkURI); + Logger.pii(LOG_TAG, "> Android position: " + rec.androidPosition); + Logger.pii(LOG_TAG, "> Position: " + rec.pos); + if (rec.isFolder()) { + Logger.pii(LOG_TAG, "FOLDER: Children are " + + (rec.children == null ? + "null" : + rec.children.toJSONString())); + } } } catch (Exception e) { - Log.d(LOG_TAG, "Exception logging bookmark record " + rec, e); + Logger.debug(LOG_TAG, "Exception logging bookmark record " + rec, e); } return rec; } @@ -319,13 +349,15 @@ public class RepoUtils { private static HistoryRecord logHistory(HistoryRecord rec) { try { - Log.d(LOG_TAG, "Returning history record " + rec.guid + " (" + rec.androidID + ")"); - Log.d(LOG_TAG, "> Title: " + rec.title); - Log.d(LOG_TAG, "> URI: " + rec.histURI); - Log.d(LOG_TAG, "> Visited: " + rec.fennecDateVisited); - Log.d(LOG_TAG, "> Visits: " + rec.fennecVisitCount); + Logger.debug(LOG_TAG, "Returning history record " + rec.guid + " (" + rec.androidID + ")"); + Logger.debug(LOG_TAG, "> Visited: " + rec.fennecDateVisited); + Logger.debug(LOG_TAG, "> Visits: " + rec.fennecVisitCount); + if (Logger.LOG_PERSONAL_INFORMATION) { + Logger.pii(LOG_TAG, "> Title: " + rec.title); + Logger.pii(LOG_TAG, "> URI: " + rec.histURI); + } } catch (Exception e) { - Log.d(LOG_TAG, "Exception logging bookmark record " + rec, e); + Logger.debug(LOG_TAG, "Exception logging bookmark record " + rec, e); } return rec; } @@ -356,7 +388,7 @@ public class RepoUtils { public static void queryTimeLogger(String methodCallingQuery, long queryStart, long queryEnd) { long elapsedTime = queryEnd - queryStart; - Log.i(LOG_TAG, "Query timer: " + methodCallingQuery + " took " + elapsedTime + "ms."); + Logger.debug(LOG_TAG, "Query timer: " + methodCallingQuery + " took " + elapsedTime + "ms."); } public static boolean stringsEqual(String a, String b) { diff --git a/mobile/android/base/sync/repositories/delegates/DeferredRepositorySessionFetchRecordsDelegate.java b/mobile/android/base/sync/repositories/delegates/DeferredRepositorySessionFetchRecordsDelegate.java index 3815e276ea9..b733d9d8d66 100644 --- a/mobile/android/base/sync/repositories/delegates/DeferredRepositorySessionFetchRecordsDelegate.java +++ b/mobile/android/base/sync/repositories/delegates/DeferredRepositorySessionFetchRecordsDelegate.java @@ -61,11 +61,11 @@ public class DeferredRepositorySessionFetchRecordsDelegate implements Repository } @Override - public void onFetchSucceeded(final Record[] records, final long end) { + public void onFetchSucceeded(final Record[] records, final long fetchEnd) { executor.execute(new Runnable() { @Override public void run() { - inner.onFetchSucceeded(records, end); + inner.onFetchSucceeded(records, fetchEnd); } }); } @@ -81,11 +81,11 @@ public class DeferredRepositorySessionFetchRecordsDelegate implements Repository } @Override - public void onFetchCompleted(final long end) { + public void onFetchCompleted(final long fetchEnd) { executor.execute(new Runnable() { @Override public void run() { - inner.onFetchCompleted(end); + inner.onFetchCompleted(fetchEnd); } }); } @@ -97,4 +97,4 @@ public class DeferredRepositorySessionFetchRecordsDelegate implements Repository } throw new IllegalArgumentException("Can't re-defer this delegate."); } -} \ No newline at end of file +} diff --git a/mobile/android/base/sync/repositories/delegates/DeferredRepositorySessionStoreDelegate.java b/mobile/android/base/sync/repositories/delegates/DeferredRepositorySessionStoreDelegate.java index 62291272f05..7e98ef1d6f0 100644 --- a/mobile/android/base/sync/repositories/delegates/DeferredRepositorySessionStoreDelegate.java +++ b/mobile/android/base/sync/repositories/delegates/DeferredRepositorySessionStoreDelegate.java @@ -81,12 +81,12 @@ public class DeferredRepositorySessionStoreDelegate implements } @Override - public void onStoreCompleted() { + public void onStoreCompleted(final long storeEnd) { executor.execute(new Runnable() { @Override public void run() { - inner.onStoreCompleted(); + inner.onStoreCompleted(storeEnd); } }); } -} \ No newline at end of file +} diff --git a/mobile/android/base/sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java b/mobile/android/base/sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java index 729c65191a1..5983f13d279 100644 --- a/mobile/android/base/sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java +++ b/mobile/android/base/sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java @@ -55,11 +55,11 @@ public interface RepositorySessionFetchRecordsDelegate { * which the request was received. * E.g., the (normalized) value of the X-Weave-Timestamp header. */ - public void onFetchCompleted(long end); + public void onFetchCompleted(final long fetchEnd); // Shorthand for calling onFetchedRecord for each record in turn, then // calling onFetchCompleted. - public void onFetchSucceeded(Record[] records, long end); + public void onFetchSucceeded(Record[] records, final long fetchEnd); public RepositorySessionFetchRecordsDelegate deferredFetchDelegate(ExecutorService executor); } diff --git a/mobile/android/base/sync/repositories/delegates/RepositorySessionStoreDelegate.java b/mobile/android/base/sync/repositories/delegates/RepositorySessionStoreDelegate.java index 80c91519f2d..d7c501bad2f 100644 --- a/mobile/android/base/sync/repositories/delegates/RepositorySessionStoreDelegate.java +++ b/mobile/android/base/sync/repositories/delegates/RepositorySessionStoreDelegate.java @@ -51,6 +51,6 @@ import org.mozilla.gecko.sync.repositories.domain.Record; public interface RepositorySessionStoreDelegate { public void onRecordStoreFailed(Exception ex); public void onRecordStoreSucceeded(Record record); - public void onStoreCompleted(); + public void onStoreCompleted(long storeEnd); public RepositorySessionStoreDelegate deferredStoreDelegate(ExecutorService executor); } diff --git a/mobile/android/base/sync/repositories/domain/BookmarkRecord.java b/mobile/android/base/sync/repositories/domain/BookmarkRecord.java index 00d3fa4233c..3b2409994ab 100644 --- a/mobile/android/base/sync/repositories/domain/BookmarkRecord.java +++ b/mobile/android/base/sync/repositories/domain/BookmarkRecord.java @@ -41,6 +41,7 @@ package org.mozilla.gecko.sync.repositories.domain; import org.json.simple.JSONArray; import org.mozilla.gecko.sync.CryptoRecord; import org.mozilla.gecko.sync.ExtendedJSONObject; +import org.mozilla.gecko.sync.Logger; import org.mozilla.gecko.sync.NonArrayJSONException; import org.mozilla.gecko.sync.Utils; import org.mozilla.gecko.sync.repositories.android.RepoUtils; @@ -224,7 +225,7 @@ public class BookmarkRecord extends Record { } private void trace(String s) { - Utils.trace(LOG_TAG, s); + Logger.trace(LOG_TAG, s); } @Override diff --git a/mobile/android/base/sync/repositories/domain/HistoryRecord.java b/mobile/android/base/sync/repositories/domain/HistoryRecord.java index 4cd32fcc86b..07a1b04f942 100644 --- a/mobile/android/base/sync/repositories/domain/HistoryRecord.java +++ b/mobile/android/base/sync/repositories/domain/HistoryRecord.java @@ -43,12 +43,11 @@ import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.mozilla.gecko.sync.CryptoRecord; import org.mozilla.gecko.sync.ExtendedJSONObject; +import org.mozilla.gecko.sync.Logger; import org.mozilla.gecko.sync.NonArrayJSONException; import org.mozilla.gecko.sync.Utils; import org.mozilla.gecko.sync.repositories.android.RepoUtils; -import android.util.Log; - /** * Visits are in microsecond precision. * @@ -124,7 +123,7 @@ public class HistoryRecord extends Record { try { this.visits = p.getArray("visits"); } catch (NonArrayJSONException e) { - Log.e(LOG_TAG, "Got non-array visits in history record " + this.guid, e); + Logger.error(LOG_TAG, "Got non-array visits in history record " + this.guid, e); this.visits = new JSONArray(); } } @@ -133,7 +132,7 @@ public class HistoryRecord extends Record { public CryptoRecord getPayload() { CryptoRecord rec = new CryptoRecord(this); rec.payload = new ExtendedJSONObject(); - Log.d(LOG_TAG, "Getting payload for history record " + this.guid + " (" + this.guid.length() + ")."); + Logger.debug(LOG_TAG, "Getting payload for history record " + this.guid + " (" + this.guid.length() + ")."); rec.payload.put("id", this.guid); rec.payload.put("title", this.title); rec.payload.put("histUri", this.histURI); // TODO: encoding? @@ -162,12 +161,12 @@ public class HistoryRecord extends Record { @Override public boolean equalPayloads(Object o) { if (o == null || !(o instanceof HistoryRecord)) { - Log.d(LOG_TAG, "Not a HistoryRecord: " + o); + Logger.debug(LOG_TAG, "Not a HistoryRecord: " + o); return false; } HistoryRecord other = (HistoryRecord) o; if (!super.equalPayloads(other)) { - Log.d(LOG_TAG, "super.equalPayloads returned false."); + Logger.debug(LOG_TAG, "super.equalPayloads returned false."); return false; } return RepoUtils.stringsEqual(this.title, other.title) && @@ -191,10 +190,11 @@ public class HistoryRecord extends Record { } private boolean checkVisitsEquals(HistoryRecord other) { - Log.d(LOG_TAG, "Checking visits."); - if (Utils.ENABLE_TRACE_LOGGING) { - Log.d(LOG_TAG, ">> Mine: " + ((this.visits == null) ? "null" : this.visits.toJSONString())); - Log.d(LOG_TAG, ">> Theirs: " + ((other.visits == null) ? "null" : other.visits.toJSONString())); + Logger.debug(LOG_TAG, "Checking visits."); + if (Logger.logVerbose(LOG_TAG)) { + // Don't JSON-encode unless we're logging. + Logger.trace(LOG_TAG, ">> Mine: " + ((this.visits == null) ? "null" : this.visits.toJSONString())); + Logger.trace(LOG_TAG, ">> Theirs: " + ((other.visits == null) ? "null" : other.visits.toJSONString())); } // Handle nulls. diff --git a/mobile/android/base/sync/setup/SyncAuthenticatorService.java b/mobile/android/base/sync/setup/SyncAuthenticatorService.java index 635c2705003..6a47e279a63 100644 --- a/mobile/android/base/sync/setup/SyncAuthenticatorService.java +++ b/mobile/android/base/sync/setup/SyncAuthenticatorService.java @@ -41,6 +41,7 @@ package org.mozilla.gecko.sync.setup; import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; +import org.mozilla.gecko.sync.Logger; import org.mozilla.gecko.sync.crypto.KeyBundle; import org.mozilla.gecko.sync.setup.activities.SetupSyncActivity; @@ -54,25 +55,23 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.IBinder; -import android.util.Log; public class SyncAuthenticatorService extends Service { - private static final String LOG_TAG = "SyncAuthenticatorService"; + private static final String LOG_TAG = "SyncAuthService"; private SyncAccountAuthenticator sAccountAuthenticator = null; @Override public void onCreate() { - Log.d(LOG_TAG, "onCreate"); + Logger.debug(LOG_TAG, "onCreate"); sAccountAuthenticator = getAuthenticator(); } @Override public IBinder onBind(Intent intent) { - IBinder ret = null; - if (intent.getAction().equals( - android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT)) - ret = getAuthenticator().getIBinder(); - return ret; + if (intent.getAction().equals(android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT)) { + return getAuthenticator().getIBinder(); + } + return null; } private SyncAccountAuthenticator getAuthenticator() { @@ -82,8 +81,7 @@ public class SyncAuthenticatorService extends Service { return sAccountAuthenticator; } - private static class SyncAccountAuthenticator extends - AbstractAccountAuthenticator { + private static class SyncAccountAuthenticator extends AbstractAccountAuthenticator { private Context mContext; public SyncAccountAuthenticator(Context context) { super(context); @@ -94,10 +92,10 @@ public class SyncAuthenticatorService extends Service { public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException { - Log.d(LOG_TAG, "addAccount()"); + Logger.debug(LOG_TAG, "addAccount()"); final Intent intent = new Intent(mContext, SetupSyncActivity.class); intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, - response); + response); intent.putExtra("accountType", Constants.ACCOUNTTYPE_SYNC); intent.putExtra(Constants.INTENT_EXTRA_IS_SETUP, true); @@ -109,15 +107,16 @@ public class SyncAuthenticatorService extends Service { @Override public Bundle confirmCredentials(AccountAuthenticatorResponse response, - Account account, Bundle options) throws NetworkErrorException { - Log.d(LOG_TAG, "confirmCredentials()"); + Account account, + Bundle options) throws NetworkErrorException { + Logger.debug(LOG_TAG, "confirmCredentials()"); return null; } @Override public Bundle editProperties(AccountAuthenticatorResponse response, - String accountType) { - Log.d(LOG_TAG, "editProperties"); + String accountType) { + Logger.debug(LOG_TAG, "editProperties"); return null; } @@ -125,7 +124,7 @@ public class SyncAuthenticatorService extends Service { public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException { - Log.d(LOG_TAG, "getAuthToken()"); + Logger.debug(LOG_TAG, "getAuthToken()"); if (!authTokenType.equals(Constants.AUTHTOKEN_TYPE_PLAIN)) { final Bundle result = new Bundle(); result.putString(AccountManager.KEY_ERROR_MESSAGE, @@ -135,7 +134,7 @@ public class SyncAuthenticatorService extends Service { // Extract the username and password from the Account Manager, and ask // the server for an appropriate AuthToken. - Log.d(LOG_TAG, "AccountManager.get(" + mContext + ")"); + Logger.info(LOG_TAG, "AccountManager.get(" + mContext + ")"); final AccountManager am = AccountManager.get(mContext); final String password = am.getPassword(account); if (password != null) { @@ -154,7 +153,8 @@ public class SyncAuthenticatorService extends Service { // Username after hashing. try { String username = KeyBundle.usernameFromAccount(account.name); - Log.i("rnewman", "Account " + account.name + " hashes to " + username); + Logger.pii(LOG_TAG, "Account " + account.name + " hashes to " + username); + Logger.info(LOG_TAG, "Setting username. Null?" + (username == null)); result.putString(Constants.OPTION_USERNAME, username); } catch (NoSuchAlgorithmException e) { // Do nothing. Calling code must check for missing value. @@ -164,27 +164,27 @@ public class SyncAuthenticatorService extends Service { // Sync key. final String syncKey = am.getUserData(account, Constants.OPTION_SYNCKEY); - Log.i("rnewman", "Setting Sync Key to " + syncKey); + Logger.info(LOG_TAG, "Setting Sync Key. Null? " + (syncKey == null)); result.putString(Constants.OPTION_SYNCKEY, syncKey); // Password. result.putString(AccountManager.KEY_AUTHTOKEN, password); return result; } - Log.w(LOG_TAG, "Returning null bundle for getAuthToken."); + Logger.warn(LOG_TAG, "Returning null bundle for getAuthToken."); return null; } @Override public String getAuthTokenLabel(String authTokenType) { - Log.d(LOG_TAG, "getAuthTokenLabel()"); + Logger.debug(LOG_TAG, "getAuthTokenLabel()"); return null; } @Override public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException { - Log.d(LOG_TAG, "hasFeatures()"); + Logger.debug(LOG_TAG, "hasFeatures()"); return null; } @@ -192,7 +192,7 @@ public class SyncAuthenticatorService extends Service { public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException { - Log.d(LOG_TAG, "updateCredentials()"); + Logger.debug(LOG_TAG, "updateCredentials()"); return null; } } diff --git a/mobile/android/base/sync/setup/activities/AccountActivity.java b/mobile/android/base/sync/setup/activities/AccountActivity.java index 54a02c70db6..dadca4a907f 100644 --- a/mobile/android/base/sync/setup/activities/AccountActivity.java +++ b/mobile/android/base/sync/setup/activities/AccountActivity.java @@ -230,7 +230,8 @@ public class AccountActivity extends AccountAuthenticatorActivity { AccountManager accountManager, String username, String syncKey, - String password, String serverURL) { + String password, + String serverURL) { final Account account = new Account(username, Constants.ACCOUNTTYPE_SYNC); final Bundle userbundle = new Bundle(); @@ -246,7 +247,7 @@ public class AccountActivity extends AccountAuthenticatorActivity { Log.d(LOG_TAG, "Adding account for " + Constants.ACCOUNTTYPE_SYNC); boolean result = accountManager.addAccountExplicitly(account, password, userbundle); - Log.d(LOG_TAG, "Account: " + account.toString() + " added successfully? " + result); + Log.d(LOG_TAG, "Account: " + account + " added successfully? " + result); if (!result) { Log.e(LOG_TAG, "Error adding account!"); } @@ -254,6 +255,7 @@ public class AccountActivity extends AccountAuthenticatorActivity { // Set components to sync (default: all). ContentResolver.setMasterSyncAutomatically(true); ContentResolver.setSyncAutomatically(account, Authorities.BROWSER_AUTHORITY, true); + ContentResolver.setIsSyncable(account, Authorities.BROWSER_AUTHORITY, 1); // TODO: add other ContentProviders as needed (e.g. passwords) // TODO: for each, also add to res/xml to make visible in account settings @@ -274,6 +276,7 @@ public class AccountActivity extends AccountAuthenticatorActivity { return intent; } + @SuppressWarnings("unused") private void authFailure() { enableCredEntry(true); Intent intent = new Intent(mContext, SetupFailureActivity.class); diff --git a/mobile/android/base/sync/setup/activities/SetupSuccessActivity.java b/mobile/android/base/sync/setup/activities/SetupSuccessActivity.java index 74505c42fef..b4802ad3e8a 100644 --- a/mobile/android/base/sync/setup/activities/SetupSuccessActivity.java +++ b/mobile/android/base/sync/setup/activities/SetupSuccessActivity.java @@ -49,6 +49,7 @@ import android.view.View; import android.widget.TextView; public class SetupSuccessActivity extends Activity { + @SuppressWarnings("unused") private final static String LOG_TAG = "SetupSuccessActivity"; private TextView setupSubtitle; private Context mContext; diff --git a/mobile/android/base/sync/stage/AndroidBrowserHistoryServerSyncStage.java b/mobile/android/base/sync/stage/AndroidBrowserHistoryServerSyncStage.java index d745591df1b..ab52ff13060 100644 --- a/mobile/android/base/sync/stage/AndroidBrowserHistoryServerSyncStage.java +++ b/mobile/android/base/sync/stage/AndroidBrowserHistoryServerSyncStage.java @@ -50,7 +50,7 @@ public class AndroidBrowserHistoryServerSyncStage extends ServerSyncStage { // Eventually this kind of sync stage will be data-driven, // and all this hard-coding can go away. private static final String HISTORY_SORT = "index"; - private static final long HISTORY_REQUEST_LIMIT = 500; + private static final long HISTORY_REQUEST_LIMIT = 250; @Override public void execute(org.mozilla.gecko.sync.GlobalSession session) throws NoSuchStageException { diff --git a/mobile/android/base/sync/stage/FetchInfoCollectionsStage.java b/mobile/android/base/sync/stage/FetchInfoCollectionsStage.java index e6688c10cdd..97e78638424 100644 --- a/mobile/android/base/sync/stage/FetchInfoCollectionsStage.java +++ b/mobile/android/base/sync/stage/FetchInfoCollectionsStage.java @@ -47,7 +47,7 @@ import org.mozilla.gecko.sync.net.SyncStorageResponse; import android.util.Log; public class FetchInfoCollectionsStage implements GlobalSyncStage { - private static final String LOG_TAG = "FetchInfoCollectionsStage"; + private static final String LOG_TAG = "FetchInfoCollStage"; public class StageInfoCollectionsDelegate implements InfoCollectionsDelegate { diff --git a/mobile/android/base/sync/syncadapter/SyncAdapter.java b/mobile/android/base/sync/syncadapter/SyncAdapter.java index 023039ff4ec..69843293457 100644 --- a/mobile/android/base/sync/syncadapter/SyncAdapter.java +++ b/mobile/android/base/sync/syncadapter/SyncAdapter.java @@ -39,7 +39,6 @@ package org.mozilla.gecko.sync.syncadapter; import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; import java.util.concurrent.TimeUnit; @@ -51,7 +50,6 @@ import org.mozilla.gecko.sync.SyncConfiguration; import org.mozilla.gecko.sync.SyncConfigurationException; import org.mozilla.gecko.sync.SyncException; import org.mozilla.gecko.sync.Utils; -import org.mozilla.gecko.sync.crypto.Cryptographer; import org.mozilla.gecko.sync.crypto.KeyBundle; import org.mozilla.gecko.sync.delegates.GlobalSessionCallback; import org.mozilla.gecko.sync.setup.Constants; @@ -222,6 +220,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe final String authority, final ContentProviderClient provider, final SyncResult syncResult) { + Utils.reseedSharedRandom(); // Make sure we don't work with the same random seed for too long. long delay = delayMilliseconds(); if (delay > 0) { diff --git a/mobile/android/base/sync/synchronizer/ConcurrentRecordConsumer.java b/mobile/android/base/sync/synchronizer/ConcurrentRecordConsumer.java index 0bc02316f94..00399348c1c 100644 --- a/mobile/android/base/sync/synchronizer/ConcurrentRecordConsumer.java +++ b/mobile/android/base/sync/synchronizer/ConcurrentRecordConsumer.java @@ -37,7 +37,7 @@ package org.mozilla.gecko.sync.synchronizer; -import org.mozilla.gecko.sync.Utils; +import org.mozilla.gecko.sync.Logger; import org.mozilla.gecko.sync.repositories.domain.Record; import android.util.Log; @@ -51,7 +51,7 @@ import android.util.Log; * */ class ConcurrentRecordConsumer extends RecordConsumer { - private static final String LOG_TAG = "ConcurrentRecordConsumer"; + private static final String LOG_TAG = "CRecordConsumer"; /** * When this is true and all records have been processed, the consumer @@ -65,15 +65,15 @@ class ConcurrentRecordConsumer extends RecordConsumer { } private static void info(String message) { - Utils.info(LOG_TAG, message); + Logger.info(LOG_TAG, message); } private static void debug(String message) { - Utils.debug(LOG_TAG, message); + Logger.debug(LOG_TAG, message); } private static void trace(String message) { - Utils.trace(LOG_TAG, message); + Logger.trace(LOG_TAG, message); } private Object monitor = new Object(); @@ -104,7 +104,7 @@ class ConcurrentRecordConsumer extends RecordConsumer { private Object countMonitor = new Object(); @Override public void stored() { - debug("Record stored. Notifying."); + trace("Record stored. Notifying."); synchronized (countMonitor) { counter++; } diff --git a/mobile/android/base/sync/synchronizer/RecordsChannel.java b/mobile/android/base/sync/synchronizer/RecordsChannel.java index d93bda2dd9c..ff936364c12 100644 --- a/mobile/android/base/sync/synchronizer/RecordsChannel.java +++ b/mobile/android/base/sync/synchronizer/RecordsChannel.java @@ -40,8 +40,8 @@ package org.mozilla.gecko.sync.synchronizer; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; +import org.mozilla.gecko.sync.Logger; import org.mozilla.gecko.sync.ThreadPool; -import org.mozilla.gecko.sync.Utils; import org.mozilla.gecko.sync.repositories.NoStoreDelegateException; import org.mozilla.gecko.sync.repositories.RepositorySession; import org.mozilla.gecko.sync.repositories.delegates.DeferredRepositorySessionBeginDelegate; @@ -51,8 +51,6 @@ import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFetchRecor import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionStoreDelegate; import org.mozilla.gecko.sync.repositories.domain.Record; -import android.util.Log; - /** * Pulls records from `source`, applying them to `sink`. * Notifies its delegate of errors and completion. @@ -104,7 +102,7 @@ class RecordsChannel implements public RepositorySession sink; private RecordsChannelDelegate delegate; private long timestamp; - private long end = -1; // Oo er, missus. + private long fetchEnd = -1; public RecordsChannel(RepositorySession source, RepositorySession sink, RecordsChannelDelegate delegate) { this.source = source; @@ -135,30 +133,6 @@ class RecordsChannel implements return source.isActive() && sink.isActive(); } - - private static void info(String message) { - Utils.logToStdout(LOG_TAG, "::INFO: ", message); - Log.i(LOG_TAG, message); - } - - private static void trace(String message) { - if (!Utils.ENABLE_TRACE_LOGGING) { - return; - } - Utils.logToStdout(LOG_TAG, "::TRACE: ", message); - Log.d(LOG_TAG, message); - } - - private static void error(String message, Exception e) { - Utils.logToStdout(LOG_TAG, "::ERROR: ", message); - Log.e(LOG_TAG, message, e); - } - - private static void warn(String message, Exception e) { - Utils.logToStdout(LOG_TAG, "::WARN: ", message); - Log.w(LOG_TAG, message, e); - } - /** * Attempt to abort an outstanding fetch. Finish both sessions. */ @@ -194,7 +168,7 @@ class RecordsChannel implements * Begin both sessions, invoking flow() when done. */ public void beginAndFlow() { - info("Beginning source."); + Logger.info(LOG_TAG, "Beginning source."); source.begin(this); } @@ -203,7 +177,7 @@ class RecordsChannel implements try { sink.store(record); } catch (NoStoreDelegateException e) { - error("Got NoStoreDelegateException in RecordsChannel.store(). This should not occur. Aborting.", e); + Logger.error(LOG_TAG, "Got NoStoreDelegateException in RecordsChannel.store(). This should not occur. Aborting.", e); delegate.onFlowStoreFailed(this, e); this.abort(); } @@ -211,7 +185,7 @@ class RecordsChannel implements @Override public void onFetchFailed(Exception ex, Record record) { - warn("onFetchFailed. Calling for immediate stop.", ex); + Logger.warn(LOG_TAG, "onFetchFailed. Calling for immediate stop.", ex); this.consumer.halt(); } @@ -222,19 +196,19 @@ class RecordsChannel implements } @Override - public void onFetchSucceeded(Record[] records, long end) { + public void onFetchSucceeded(Record[] records, final long fetchEnd) { for (Record record : records) { this.toProcess.add(record); } this.consumer.doNotify(); - this.onFetchCompleted(end); + this.onFetchCompleted(fetchEnd); } @Override - public void onFetchCompleted(long end) { - info("onFetchCompleted. Stopping consumer once stores are done."); - info("Fetch timestamp is " + end); - this.end = end; + public void onFetchCompleted(final long fetchEnd) { + Logger.info(LOG_TAG, "onFetchCompleted. Stopping consumer once stores are done."); + Logger.info(LOG_TAG, "Fetch timestamp is " + fetchEnd); + this.fetchEnd = fetchEnd; this.consumer.queueFilled(); } @@ -253,7 +227,7 @@ class RecordsChannel implements @Override public void consumerIsDone(boolean allRecordsQueued) { - trace("Consumer is done. Are we waiting for it? " + waitingForQueueDone); + Logger.trace(LOG_TAG, "Consumer is done. Are we waiting for it? " + waitingForQueueDone); if (waitingForQueueDone) { waitingForQueueDone = false; this.sink.storeDone(); // Now we'll be waiting for onStoreCompleted. @@ -261,10 +235,11 @@ class RecordsChannel implements } @Override - public void onStoreCompleted() { - info("onStoreCompleted. Notifying delegate of onFlowCompleted. End is " + end); + public void onStoreCompleted(long storeEnd) { + Logger.info(LOG_TAG, "onStoreCompleted. Notifying delegate of onFlowCompleted. " + + "Fetch end is " + fetchEnd + ", store end is " + storeEnd); // TODO: synchronize on consumer callback? - delegate.onFlowCompleted(this, end); + delegate.onFlowCompleted(this, fetchEnd, storeEnd); } @Override @@ -275,11 +250,11 @@ class RecordsChannel implements @Override public void onBeginSucceeded(RepositorySession session) { if (session == source) { - info("Source session began. Beginning sink session."); + Logger.info(LOG_TAG, "Source session began. Beginning sink session."); sink.begin(this); } if (session == sink) { - info("Sink session began. Beginning flow."); + Logger.info(LOG_TAG, "Sink session began. Beginning flow."); this.flow(); return; } diff --git a/mobile/android/base/sync/synchronizer/RecordsChannelDelegate.java b/mobile/android/base/sync/synchronizer/RecordsChannelDelegate.java index 13566f8e2dd..273f655ff07 100644 --- a/mobile/android/base/sync/synchronizer/RecordsChannelDelegate.java +++ b/mobile/android/base/sync/synchronizer/RecordsChannelDelegate.java @@ -38,7 +38,7 @@ package org.mozilla.gecko.sync.synchronizer; public interface RecordsChannelDelegate { - public void onFlowCompleted(RecordsChannel recordsChannel, long end); + public void onFlowCompleted(RecordsChannel recordsChannel, long fetchEnd, long storeEnd); public void onFlowBeginFailed(RecordsChannel recordsChannel, Exception ex); public void onFlowStoreFailed(RecordsChannel recordsChannel, Exception ex); public void onFlowFinishFailed(RecordsChannel recordsChannel, Exception ex); diff --git a/mobile/android/base/sync/synchronizer/Synchronizer.java b/mobile/android/base/sync/synchronizer/Synchronizer.java index c41ab288bc4..4baaf7a4de6 100644 --- a/mobile/android/base/sync/synchronizer/Synchronizer.java +++ b/mobile/android/base/sync/synchronizer/Synchronizer.java @@ -63,7 +63,7 @@ public class Synchronizer { public class SynchronizerDelegateSessionDelegate implements SynchronizerSessionDelegate { - private static final String LOG_TAG = "SynchronizerDelegateSessionDelegate"; + private static final String LOG_TAG = "SyncDelSDelegate"; private SynchronizerDelegate synchronizerDelegate; private SynchronizerSession session; diff --git a/mobile/android/base/sync/synchronizer/SynchronizerSession.java b/mobile/android/base/sync/synchronizer/SynchronizerSession.java index a06d9304f99..4f124d8bfa9 100644 --- a/mobile/android/base/sync/synchronizer/SynchronizerSession.java +++ b/mobile/android/base/sync/synchronizer/SynchronizerSession.java @@ -66,8 +66,15 @@ implements RecordsChannelDelegate, private RepositorySession sessionB; private RepositorySessionBundle bundleA; private RepositorySessionBundle bundleB; + + // Bug 726054: just like desktop, we track our last interaction with the server, + // not the last record timestamp that we fetched. This ensures that we don't re- + // download the records we just uploaded, at the cost of skipping any records + // that a concurrently syncing client has uploaded. private long pendingATimestamp = -1; private long pendingBTimestamp = -1; + private long storeEndATimestamp = -1; + private long storeEndBTimestamp = -1; private boolean flowAToBCompleted = false; private boolean flowBToACompleted = false; @@ -133,9 +140,11 @@ implements RecordsChannelDelegate, // TODO: failed record handling. final RecordsChannel channelBToA = new RecordsChannel(this.sessionB, this.sessionA, this); RecordsChannelDelegate channelDelegate = new RecordsChannelDelegate() { - public void onFlowCompleted(RecordsChannel recordsChannel, long end) { - info("First RecordsChannel flow completed. End is " + end + ". Starting next."); - pendingATimestamp = end; + public void onFlowCompleted(RecordsChannel recordsChannel, long fetchEnd, long storeEnd) { + info("First RecordsChannel flow completed. Fetch end is " + fetchEnd + + ". Store end is " + storeEnd + ". Starting next."); + pendingATimestamp = fetchEnd; + storeEndBTimestamp = storeEnd; flowAToBCompleted = true; channelBToA.flow(); } @@ -165,9 +174,12 @@ implements RecordsChannelDelegate, } @Override - public void onFlowCompleted(RecordsChannel channel, long end) { - info("Second RecordsChannel flow completed. End is " + end + ". Finishing."); - pendingBTimestamp = end; + public void onFlowCompleted(RecordsChannel channel, long fetchEnd, long storeEnd) { + info("Second RecordsChannel flow completed. Fetch end is " + fetchEnd + + ". Store end is " + storeEnd + ". Finishing."); + + pendingBTimestamp = fetchEnd; + storeEndATimestamp = storeEnd; flowBToACompleted = true; // Finish the two sessions. @@ -278,8 +290,8 @@ implements RecordsChannelDelegate, if (session == sessionA) { if (flowAToBCompleted) { - info("onFinishSucceeded: bumping session A's timestamp to " + pendingATimestamp); - bundle.bumpTimestamp(pendingATimestamp); + info("onFinishSucceeded: bumping session A's timestamp to " + pendingATimestamp + " or " + storeEndATimestamp); + bundle.bumpTimestamp(Math.max(pendingATimestamp, storeEndATimestamp)); this.synchronizer.bundleA = bundle; } if (this.sessionB != null) { @@ -289,8 +301,8 @@ implements RecordsChannelDelegate, } } else if (session == sessionB) { if (flowBToACompleted) { - info("onFinishSucceeded: bumping session B's timestamp to " + pendingBTimestamp); - bundle.bumpTimestamp(pendingBTimestamp); + info("onFinishSucceeded: bumping session B's timestamp to " + pendingBTimestamp + " or " + storeEndBTimestamp); + bundle.bumpTimestamp(Math.max(pendingBTimestamp, storeEndBTimestamp)); this.synchronizer.bundleB = bundle; info("Notifying delegate.onSynchronized."); this.delegate.onSynchronized(this); diff --git a/mobile/android/base/tests/robocop.ini b/mobile/android/base/tests/robocop.ini index 3fe41bbbbba..d7ccbb2f483 100644 --- a/mobile/android/base/tests/robocop.ini +++ b/mobile/android/base/tests/robocop.ini @@ -8,6 +8,7 @@ [testOverscroll] [testAxisLocking] [testAboutPage] +[testWebContentContextMenu] # Used for Talos, please don't use in mochitest #[testPan] diff --git a/mobile/android/base/tests/robocop_big_link.html b/mobile/android/base/tests/robocop_big_link.html new file mode 100644 index 00000000000..588f90d57d7 --- /dev/null +++ b/mobile/android/base/tests/robocop_big_link.html @@ -0,0 +1,12 @@ + + + Big Link + + + + + + + diff --git a/mobile/android/base/tests/testWebContentContextMenu.java.in b/mobile/android/base/tests/testWebContentContextMenu.java.in new file mode 100644 index 00000000000..91abeddb038 --- /dev/null +++ b/mobile/android/base/tests/testWebContentContextMenu.java.in @@ -0,0 +1,40 @@ +#filter substitution +package @ANDROID_PACKAGE_NAME@.tests; + +import @ANDROID_PACKAGE_NAME@.*; +import android.app.Activity; +import android.util.Log; +import android.util.DisplayMetrics; + +public class testWebContentContextMenu extends BaseTest { + public void testWebContentContextMenu() { + setTestType("mochitest"); + mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); + + // Load the about: page + String url = getAbsoluteUrl("/robocop/robocop_big_link.html"); + loadUrl(url); + + DisplayMetrics dm = new DisplayMetrics(); + getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm); + + // The link has a 60px height, so let's try to hit the middle + float top = mDriver.getGeckoTop() + 30 * dm.density; + float left = mDriver.getGeckoLeft() + mDriver.getGeckoWidth() / 2; + mSolo.clickLongOnScreen(left, top); + + mAsserter.ok(mSolo.waitForText("Open"), "looking for context menu action", "found 'Open Link'"); + + Actions.EventExpecter tabEventExpecter = mActions.expectGeckoEvent("Tab:Added"); + Actions.EventExpecter contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded"); + mSolo.clickOnText("Open"); + + // Wait for the new tab and page to load + tabEventExpecter.blockForEvent(); + contentEventExpecter.blockForEvent(); + + // See tab count + Element tabCount = mDriver.findElement(getActivity(), "tabs_count"); + mAsserter.is(tabCount.getText(), "2", "Number of tabs has increased"); + } +} diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 5284f93c1a7..68add999387 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -208,11 +208,12 @@ var BrowserApp = { getBridge().setDrawMetadataProvider(MetadataProvider); + getBridge().browserApp = this; + Services.obs.addObserver(this, "Tab:Add", false); Services.obs.addObserver(this, "Tab:Load", false); Services.obs.addObserver(this, "Tab:Selected", false); Services.obs.addObserver(this, "Tab:Closed", false); - Services.obs.addObserver(this, "Tab:Screenshot", false); Services.obs.addObserver(this, "Session:Back", false); Services.obs.addObserver(this, "Session:Forward", false); Services.obs.addObserver(this, "Session:Reload", false); @@ -563,36 +564,6 @@ var BrowserApp = { this._tabs.splice(this._tabs.indexOf(aTab), 1); }, - screenshotQueue: null, - - screenshotTab: function screenshotTab(aData) { - if (this.screenshotQueue == null) { - this.screenShotQueue = []; - this.doScreenshotTab(aData); - } else { - this.screenshotQueue.push(aData); - } - }, - - doNextScreenshot: function() { - if (this.screenshotQueue == null || this.screenshotQueue.length == 0) { - this.screenshotQueue = null; - return; - } - let data = this.screenshotQueue.pop(); - if (data == null) { - this.screenshotQueue = null; - return; - } - this.doScreenshotTab(data); - }, - - doScreenshotTab: function doScreenshotTab(aData) { - let json = JSON.parse(aData); - let tab = this.getTabForId(parseInt(json.tabID)); - tab.screenshot(json.source, json.destination); - }, - // Use this method to select a tab from JS. This method sends a message // to Java to select the tab in the Java UI (we'll get a Tab:Selected message // back from Java when that happens). @@ -963,10 +934,6 @@ var BrowserApp = { this._handleTabSelected(this.getTabForId(parseInt(aData))); } else if (aTopic == "Tab:Closed") { this._handleTabClosed(this.getTabForId(parseInt(aData))); - } else if (aTopic == "Tab:Screenshot") { - this.screenshotTab(aData); - } else if (aTopic == "Tab:Screenshot:Cancel") { - this.screenshotQueue = null; } else if (aTopic == "Browser:Quit") { this.quit(); } else if (aTopic == "SaveAs:PDF") { @@ -1001,7 +968,16 @@ var BrowserApp = { delete this.defaultBrowserWidth; let width = Services.prefs.getIntPref("browser.viewport.desktopWidth"); return this.defaultBrowserWidth = width; + }, + + // nsIAndroidBrowserApp + getWindowForTab: function(tabId) { + let tab = this.getTabForId(tabId); + if (!tab.browser) + return null; + return tab.browser.contentWindow; } + }; var NativeWindow = { @@ -1135,6 +1111,15 @@ var NativeWindow = { NativeWindow.toast.show(label, "short"); }); + this.add(Strings.browser.GetStringFromName("contextmenu.shareLink"), + this.linkShareableContext, + function(aTarget) { + let url = NativeWindow.contextmenus._getLinkURL(aTarget); + let title = aTarget.textContent || aTarget.title; + let sharing = Cc["@mozilla.org/uriloader/external-sharing-app-service;1"].getService(Ci.nsIExternalSharingAppService); + sharing.shareWithDefault(url, "text/plain", title); + }); + this.add(Strings.browser.GetStringFromName("contextmenu.fullScreen"), this.SelectorContext("video:not(:-moz-full-screen)"), function(aTarget) { @@ -1224,6 +1209,32 @@ var NativeWindow = { } }, + linkShareableContext: { + matches: function linkShareableContextMatches(aElement) { + if (aElement.nodeType == Ci.nsIDOMNode.ELEMENT_NODE && + ((aElement instanceof Ci.nsIDOMHTMLAnchorElement && aElement.href) || + (aElement instanceof Ci.nsIDOMHTMLAreaElement && aElement.href) || + aElement instanceof Ci.nsIDOMHTMLLinkElement || + aElement.getAttributeNS(kXLinkNamespace, "type") == "simple")) { + let uri; + try { + let url = NativeWindow.contextmenus._getLinkURL(aElement); + uri = Services.io.newURI(url, null, null); + } catch (e) { + return false; + } + + let scheme = uri.scheme; + if (!scheme) + return false; + + let dontShare = /^(chrome|about|file|javascript|resource)$/; + return (scheme && !dontShare.test(scheme)); + } + return false; + } + }, + textContext: { matches: function textContext(aElement) { return ((aElement instanceof Ci.nsIDOMHTMLInputElement && aElement.mozIsTextField(false)) @@ -1619,15 +1630,17 @@ Tab.prototype = { this.browser.contentDocument.documentElement); }, - screenshot: function(aSrc, aDst) { - // FIXME: Reenable - //if (!this.browser || !this.browser.contentWindow) - return; + updateTransform: function() { + let hasZoom = (Math.abs(this._viewport.zoom - 1.0) >= 1e-6); + let x = this._viewport.offsetX + Math.round(-this.viewportExcess.x * this._viewport.zoom); + let y = this._viewport.offsetY + Math.round(-this.viewportExcess.y * this._viewport.zoom); + let transform = + "translate(" + x + "px, " + + y + "px)"; + if (hasZoom) + transform += " scale(" + this._viewport.zoom + ")"; - getBridge().takeScreenshot(this.browser.contentWindow, 0, 0, aSrc.width, aSrc.height, aDst.width, aDst.height, this.id); - Services.tm.mainThread.dispatch(function() { - BrowserApp.doNextScreenshot() - }, Ci.nsIThread.DISPATCH_NORMAL); + this.browser.style.MozTransform = transform; }, get viewport() { diff --git a/mobile/android/locales/en-US/chrome/browser.properties b/mobile/android/locales/en-US/chrome/browser.properties index 85dac68bbf1..612105f5cae 100644 --- a/mobile/android/locales/en-US/chrome/browser.properties +++ b/mobile/android/locales/en-US/chrome/browser.properties @@ -198,6 +198,7 @@ selectionHelper.textCopied=Text copied to clipboard # Context menu contextmenu.openInNewTab=Open Link in New Tab +contextmenu.shareLink=Share Link contextmenu.changeInputMethod=Select Input Method contextmenu.fullScreen=Full Screen contextmenu.saveImage=Save Image diff --git a/mobile/android/sync/java-sources.mn b/mobile/android/sync/java-sources.mn index 0511919e52a..a66c6734466 100644 --- a/mobile/android/sync/java-sources.mn +++ b/mobile/android/sync/java-sources.mn @@ -1 +1 @@ -sync/AlreadySyncingException.java sync/CollectionKeys.java sync/CredentialsSource.java sync/crypto/CryptoException.java sync/crypto/Cryptographer.java sync/crypto/CryptoInfo.java sync/crypto/HKDF.java sync/crypto/HMACVerificationException.java sync/crypto/KeyBundle.java sync/crypto/MissingCryptoInputException.java sync/crypto/NoKeyBundleException.java sync/cryptographer/CryptoStatusBundle.java sync/cryptographer/SyncCryptographer.java sync/CryptoRecord.java sync/DelayedWorkTracker.java sync/delegates/FreshStartDelegate.java sync/delegates/GlobalSessionCallback.java sync/delegates/InfoCollectionsDelegate.java sync/delegates/KeyUploadDelegate.java sync/delegates/MetaGlobalDelegate.java sync/delegates/WipeServerDelegate.java sync/ExtendedJSONObject.java sync/GlobalSession.java sync/HTTPFailureException.java sync/InfoCollections.java sync/jpake/BigIntegerHelper.java sync/jpake/Gx4IsOneException.java sync/jpake/IncorrectZkpException.java sync/jpake/JPakeClient.java sync/jpake/JPakeCrypto.java sync/jpake/JPakeNoActivePairingException.java sync/jpake/JPakeNumGenerator.java sync/jpake/JPakeNumGeneratorRandom.java sync/jpake/JPakeParty.java sync/jpake/JPakeRequest.java sync/jpake/JPakeRequestDelegate.java sync/jpake/JPakeResponse.java sync/jpake/JPakeUtils.java sync/jpake/Zkp.java sync/MetaGlobal.java sync/MetaGlobalException.java sync/MetaGlobalMissingEnginesException.java sync/MetaGlobalNotSetException.java sync/middleware/Crypto5MiddlewareRepository.java sync/middleware/Crypto5MiddlewareRepositorySession.java sync/middleware/MiddlewareRepository.java sync/net/BaseResource.java sync/net/CompletedEntity.java sync/net/HandleProgressException.java sync/net/Resource.java sync/net/ResourceDelegate.java sync/net/SyncResourceDelegate.java sync/net/SyncResponse.java sync/net/SyncStorageCollectionRequest.java sync/net/SyncStorageCollectionRequestDelegate.java sync/net/SyncStorageRecordRequest.java sync/net/SyncStorageRequest.java sync/net/SyncStorageRequestDelegate.java sync/net/SyncStorageRequestIncrementalDelegate.java sync/net/SyncStorageResponse.java sync/net/TLSSocketFactory.java sync/net/WBOCollectionRequestDelegate.java sync/NoCollectionKeysSetException.java sync/NonArrayJSONException.java sync/NonObjectJSONException.java sync/PrefsSource.java sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java sync/repositories/android/AndroidBrowserBookmarksRepository.java sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java sync/repositories/android/AndroidBrowserHistoryDataAccessor.java sync/repositories/android/AndroidBrowserHistoryDataExtender.java sync/repositories/android/AndroidBrowserHistoryRepository.java sync/repositories/android/AndroidBrowserHistoryRepositorySession.java sync/repositories/android/AndroidBrowserPasswordsDataAccessor.java sync/repositories/android/AndroidBrowserPasswordsRepository.java sync/repositories/android/AndroidBrowserPasswordsRepositorySession.java sync/repositories/android/AndroidBrowserRepository.java sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java sync/repositories/android/AndroidBrowserRepositorySession.java sync/repositories/android/BrowserContract.java sync/repositories/android/PasswordColumns.java sync/repositories/android/RepoUtils.java sync/repositories/BookmarkNeedsReparentingException.java sync/repositories/BookmarksRepository.java sync/repositories/ConstrainedServer11Repository.java sync/repositories/delegates/DeferrableRepositorySessionCreationDelegate.java sync/repositories/delegates/DeferredRepositorySessionBeginDelegate.java sync/repositories/delegates/DeferredRepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/DeferredRepositorySessionFinishDelegate.java sync/repositories/delegates/DeferredRepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionBeginDelegate.java sync/repositories/delegates/RepositorySessionCleanDelegate.java sync/repositories/delegates/RepositorySessionCreationDelegate.java sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/RepositorySessionFinishDelegate.java sync/repositories/delegates/RepositorySessionGuidsSinceDelegate.java sync/repositories/delegates/RepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionWipeDelegate.java sync/repositories/domain/BookmarkRecord.java sync/repositories/domain/BookmarkRecordFactory.java sync/repositories/domain/HistoryRecord.java sync/repositories/domain/HistoryRecordFactory.java sync/repositories/domain/PasswordRecord.java sync/repositories/domain/Record.java sync/repositories/HashSetStoreTracker.java sync/repositories/HistoryRepository.java sync/repositories/IdentityRecordFactory.java sync/repositories/InactiveSessionException.java sync/repositories/InvalidBookmarkTypeException.java sync/repositories/InvalidRequestException.java sync/repositories/InvalidSessionTransitionException.java sync/repositories/MultipleRecordsForGuidException.java sync/repositories/NoGuidForIdException.java sync/repositories/NoStoreDelegateException.java sync/repositories/NullCursorException.java sync/repositories/ParentNotFoundException.java sync/repositories/ProfileDatabaseException.java sync/repositories/RecordFactory.java sync/repositories/RecordFilter.java sync/repositories/Repository.java sync/repositories/RepositorySession.java sync/repositories/RepositorySessionBundle.java sync/repositories/Server11Repository.java sync/repositories/Server11RepositorySession.java sync/repositories/StoreTracker.java sync/repositories/StoreTrackingRepositorySession.java sync/setup/activities/AccountActivity.java sync/setup/activities/SetupFailureActivity.java sync/setup/activities/SetupSuccessActivity.java sync/setup/activities/SetupSyncActivity.java sync/setup/Constants.java sync/setup/SyncAuthenticatorService.java sync/stage/AndroidBrowserBookmarksServerSyncStage.java sync/stage/AndroidBrowserHistoryServerSyncStage.java sync/stage/CheckPreconditionsStage.java sync/stage/CompletedStage.java sync/stage/EnsureClusterURLStage.java sync/stage/EnsureKeysStage.java sync/stage/FetchInfoCollectionsStage.java sync/stage/FetchMetaGlobalStage.java sync/stage/GlobalSyncStage.java sync/stage/NoSuchStageException.java sync/stage/NoSyncIDException.java sync/stage/ServerSyncStage.java sync/StubActivity.java sync/syncadapter/SyncAdapter.java sync/syncadapter/SyncService.java sync/SyncConfiguration.java sync/SyncConfigurationException.java sync/SyncException.java sync/synchronizer/ConcurrentRecordConsumer.java sync/synchronizer/RecordConsumer.java sync/synchronizer/RecordsChannel.java sync/synchronizer/RecordsChannelDelegate.java sync/synchronizer/RecordsConsumerDelegate.java sync/synchronizer/SerialRecordConsumer.java sync/synchronizer/SessionNotBegunException.java sync/synchronizer/Synchronizer.java sync/synchronizer/SynchronizerDelegate.java sync/synchronizer/SynchronizerSession.java sync/synchronizer/SynchronizerSessionDelegate.java sync/synchronizer/UnbundleError.java sync/synchronizer/UnexpectedSessionException.java sync/SynchronizerConfiguration.java sync/SynchronizerConfigurations.java sync/ThreadPool.java sync/UnexpectedJSONException.java sync/UnknownSynchronizerConfigurationVersionException.java sync/Utils.java +sync/AlreadySyncingException.java sync/CollectionKeys.java sync/CredentialsSource.java sync/crypto/CryptoException.java sync/crypto/CryptoInfo.java sync/crypto/HKDF.java sync/crypto/HMACVerificationException.java sync/crypto/KeyBundle.java sync/crypto/MissingCryptoInputException.java sync/crypto/NoKeyBundleException.java sync/CryptoRecord.java sync/DelayedWorkTracker.java sync/delegates/FreshStartDelegate.java sync/delegates/GlobalSessionCallback.java sync/delegates/InfoCollectionsDelegate.java sync/delegates/KeyUploadDelegate.java sync/delegates/MetaGlobalDelegate.java sync/delegates/WipeServerDelegate.java sync/ExtendedJSONObject.java sync/GlobalSession.java sync/HTTPFailureException.java sync/InfoCollections.java sync/jpake/BigIntegerHelper.java sync/jpake/Gx3OrGx4IsZeroOrOneException.java sync/jpake/IncorrectZkpException.java sync/jpake/JPakeClient.java sync/jpake/JPakeCrypto.java sync/jpake/JPakeNoActivePairingException.java sync/jpake/JPakeNumGenerator.java sync/jpake/JPakeNumGeneratorRandom.java sync/jpake/JPakeParty.java sync/jpake/JPakeRequest.java sync/jpake/JPakeRequestDelegate.java sync/jpake/JPakeResponse.java sync/jpake/Zkp.java sync/Logger.java sync/MetaGlobal.java sync/MetaGlobalException.java sync/MetaGlobalMissingEnginesException.java sync/MetaGlobalNotSetException.java sync/middleware/Crypto5MiddlewareRepository.java sync/middleware/Crypto5MiddlewareRepositorySession.java sync/middleware/MiddlewareRepository.java sync/net/BaseResource.java sync/net/CompletedEntity.java sync/net/HandleProgressException.java sync/net/Resource.java sync/net/ResourceDelegate.java sync/net/SyncResourceDelegate.java sync/net/SyncResponse.java sync/net/SyncStorageCollectionRequest.java sync/net/SyncStorageCollectionRequestDelegate.java sync/net/SyncStorageRecordRequest.java sync/net/SyncStorageRequest.java sync/net/SyncStorageRequestDelegate.java sync/net/SyncStorageRequestIncrementalDelegate.java sync/net/SyncStorageResponse.java sync/net/TLSSocketFactory.java sync/net/WBOCollectionRequestDelegate.java sync/NoCollectionKeysSetException.java sync/NonArrayJSONException.java sync/NonObjectJSONException.java sync/PrefsSource.java sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java sync/repositories/android/AndroidBrowserBookmarksRepository.java sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java sync/repositories/android/AndroidBrowserHistoryDataAccessor.java sync/repositories/android/AndroidBrowserHistoryDataExtender.java sync/repositories/android/AndroidBrowserHistoryRepository.java sync/repositories/android/AndroidBrowserHistoryRepositorySession.java sync/repositories/android/AndroidBrowserPasswordsDataAccessor.java sync/repositories/android/AndroidBrowserPasswordsRepository.java sync/repositories/android/AndroidBrowserPasswordsRepositorySession.java sync/repositories/android/AndroidBrowserRepository.java sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java sync/repositories/android/AndroidBrowserRepositorySession.java sync/repositories/android/BrowserContract.java sync/repositories/android/PasswordColumns.java sync/repositories/android/RepoUtils.java sync/repositories/BookmarkNeedsReparentingException.java sync/repositories/BookmarksRepository.java sync/repositories/ConstrainedServer11Repository.java sync/repositories/delegates/DeferrableRepositorySessionCreationDelegate.java sync/repositories/delegates/DeferredRepositorySessionBeginDelegate.java sync/repositories/delegates/DeferredRepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/DeferredRepositorySessionFinishDelegate.java sync/repositories/delegates/DeferredRepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionBeginDelegate.java sync/repositories/delegates/RepositorySessionCleanDelegate.java sync/repositories/delegates/RepositorySessionCreationDelegate.java sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/RepositorySessionFinishDelegate.java sync/repositories/delegates/RepositorySessionGuidsSinceDelegate.java sync/repositories/delegates/RepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionWipeDelegate.java sync/repositories/domain/BookmarkRecord.java sync/repositories/domain/BookmarkRecordFactory.java sync/repositories/domain/HistoryRecord.java sync/repositories/domain/HistoryRecordFactory.java sync/repositories/domain/PasswordRecord.java sync/repositories/domain/Record.java sync/repositories/HashSetStoreTracker.java sync/repositories/HistoryRepository.java sync/repositories/IdentityRecordFactory.java sync/repositories/InactiveSessionException.java sync/repositories/InvalidBookmarkTypeException.java sync/repositories/InvalidRequestException.java sync/repositories/InvalidSessionTransitionException.java sync/repositories/MultipleRecordsForGuidException.java sync/repositories/NoGuidForIdException.java sync/repositories/NoStoreDelegateException.java sync/repositories/NullCursorException.java sync/repositories/ParentNotFoundException.java sync/repositories/ProfileDatabaseException.java sync/repositories/RecordFactory.java sync/repositories/RecordFilter.java sync/repositories/Repository.java sync/repositories/RepositorySession.java sync/repositories/RepositorySessionBundle.java sync/repositories/Server11Repository.java sync/repositories/Server11RepositorySession.java sync/repositories/StoreTracker.java sync/repositories/StoreTrackingRepositorySession.java sync/setup/activities/AccountActivity.java sync/setup/activities/SetupFailureActivity.java sync/setup/activities/SetupSuccessActivity.java sync/setup/activities/SetupSyncActivity.java sync/setup/Constants.java sync/setup/SyncAuthenticatorService.java sync/stage/AndroidBrowserBookmarksServerSyncStage.java sync/stage/AndroidBrowserHistoryServerSyncStage.java sync/stage/CheckPreconditionsStage.java sync/stage/CompletedStage.java sync/stage/EnsureClusterURLStage.java sync/stage/EnsureKeysStage.java sync/stage/FetchInfoCollectionsStage.java sync/stage/FetchMetaGlobalStage.java sync/stage/GlobalSyncStage.java sync/stage/NoSuchStageException.java sync/stage/NoSyncIDException.java sync/stage/ServerSyncStage.java sync/StubActivity.java sync/syncadapter/SyncAdapter.java sync/syncadapter/SyncService.java sync/SyncConfiguration.java sync/SyncConfigurationException.java sync/SyncException.java sync/synchronizer/ConcurrentRecordConsumer.java sync/synchronizer/RecordConsumer.java sync/synchronizer/RecordsChannel.java sync/synchronizer/RecordsChannelDelegate.java sync/synchronizer/RecordsConsumerDelegate.java sync/synchronizer/SerialRecordConsumer.java sync/synchronizer/SessionNotBegunException.java sync/synchronizer/Synchronizer.java sync/synchronizer/SynchronizerDelegate.java sync/synchronizer/SynchronizerSession.java sync/synchronizer/SynchronizerSessionDelegate.java sync/synchronizer/UnbundleError.java sync/synchronizer/UnexpectedSessionException.java sync/SynchronizerConfiguration.java sync/SynchronizerConfigurations.java sync/ThreadPool.java sync/UnexpectedJSONException.java sync/UnknownSynchronizerConfigurationVersionException.java sync/Utils.java diff --git a/mobile/xul/config/mozconfigs/android/debug b/mobile/xul/config/mozconfigs/android/debug index 5a8f6aff0f1..795fa131cf6 100644 --- a/mobile/xul/config/mozconfigs/android/debug +++ b/mobile/xul/config/mozconfigs/android/debug @@ -4,7 +4,6 @@ ac_add_options --enable-debug # Build Fennec ac_add_options --enable-application=mobile -ac_add_options --disable-elf-hack # Android ac_add_options --target=arm-linux-androideabi diff --git a/mobile/xul/config/mozconfigs/android/nightly b/mobile/xul/config/mozconfigs/android/nightly index 053ca9b2215..01fbe1470e6 100644 --- a/mobile/xul/config/mozconfigs/android/nightly +++ b/mobile/xul/config/mozconfigs/android/nightly @@ -6,7 +6,6 @@ ac_add_options --enable-js-diagnostics # Build Fennec ac_add_options --enable-application=mobile -ac_add_options --disable-elf-hack # Android ac_add_options --target=arm-linux-androideabi diff --git a/mobile/xul/installer/package-manifest.in b/mobile/xul/installer/package-manifest.in index e03157b5edd..32a7f989175 100644 --- a/mobile/xul/installer/package-manifest.in +++ b/mobile/xul/installer/package-manifest.in @@ -157,9 +157,6 @@ @BINPATH@/components/dom_system_b2g.xpt #endif @BINPATH@/components/dom_battery.xpt -#ifdef MOZ_B2G_BT -@BINPATH@/components/dom_bluetooth.xpt -#endif @BINPATH@/components/dom_canvas.xpt @BINPATH@/components/dom_core.xpt @BINPATH@/components/dom_css.xpt diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index a8fa6375806..984e2604c78 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -656,6 +656,8 @@ pref("javascript.options.typeinference", true); pref("javascript.options.mem.high_water_mark", 128); pref("javascript.options.mem.max", -1); pref("javascript.options.mem.gc_per_compartment", true); +pref("javascript.options.mem.gc_incremental", true); +pref("javascript.options.mem.gc_incremental_slice_ms", 10); pref("javascript.options.mem.log", false); pref("javascript.options.gc_on_memory_pressure", true); @@ -1036,6 +1038,9 @@ pref("network.negotiate-auth.trusted-uris", ""); // This list controls which URIs can support delegation. pref("network.negotiate-auth.delegation-uris", ""); +// Do not allow SPNEGO by default when challenged by a local server. +pref("network.negotiate-auth.allow-non-fqdn", false); + // Allow SPNEGO by default when challenged by a proxy server. pref("network.negotiate-auth.allow-proxies", true); @@ -1066,6 +1071,7 @@ pref("network.auth.force-generic-ntlm", false); // Window's domain logon. The trusted-uris pref follows the format of the // trusted-uris pref for negotiate authentication. pref("network.automatic-ntlm-auth.allow-proxies", true); +pref("network.automatic-ntlm-auth.allow-non-fqdn", false); pref("network.automatic-ntlm-auth.trusted-uris", ""); // This preference controls whether or not the LM hash will be included in diff --git a/mozglue/android/nsGeckoUtils.cpp b/mozglue/android/nsGeckoUtils.cpp index 59487842c7d..2dfa0dca9e1 100644 --- a/mozglue/android/nsGeckoUtils.cpp +++ b/mozglue/android/nsGeckoUtils.cpp @@ -36,6 +36,9 @@ * ***** END LICENSE BLOCK ***** */ #include +// Wrap malloc and free to use jemalloc +#define malloc __wrap_malloc +#define free __wrap_free #include extern "C" diff --git a/mozglue/linker/CustomElf.cpp b/mozglue/linker/CustomElf.cpp index b4ba0cd9c55..f9139503dda 100644 --- a/mozglue/linker/CustomElf.cpp +++ b/mozglue/linker/CustomElf.cpp @@ -175,10 +175,20 @@ CustomElf::Load(Mappable *mappable, const char *path, int flags) } /* Reserve enough memory to map the complete virtual address space for this - * library. */ - elf->base.Assign(mmap(NULL, max_vaddr, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, + * library. + * As we are using the base address from here to mmap something else with + * MAP_FIXED | MAP_SHARED, we need to make sure these mmaps will work. For + * instance, on armv6, MAP_SHARED mappings require a 16k alignment, but mmap + * MAP_PRIVATE only returns a 4k aligned address. So we first get a base + * address with MAP_SHARED, which guarantees the kernel returns an address + * that we'll be able to use with MAP_FIXED, and then remap MAP_PRIVATE at + * the same address, because of some bad side effects of keeping it as + * MAP_SHARED. */ + elf->base.Assign(mmap(NULL, max_vaddr, PROT_NONE, MAP_SHARED | MAP_ANONYMOUS, -1, 0), max_vaddr); - if (elf->base == MAP_FAILED) { + if ((elf->base == MAP_FAILED) || + (mmap(elf->base, max_vaddr, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != elf->base)) { log("%s: Failed to mmap", elf->GetPath()); return NULL; } diff --git a/netwerk/protocol/device/GonkCaptureProvider.cpp b/netwerk/protocol/device/GonkCaptureProvider.cpp index a5fff3a4820..fe24536a60f 100644 --- a/netwerk/protocol/device/GonkCaptureProvider.cpp +++ b/netwerk/protocol/device/GonkCaptureProvider.cpp @@ -4,6 +4,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include "android/log.h" #include "cutils/properties.h" #include "base/basictypes.h" @@ -12,6 +13,7 @@ #include "nsStreamUtils.h" #include "nsThreadUtils.h" #include "nsRawStructs.h" +#include "prinit.h" #define USE_GS2_LIBCAMERA #define CameraHardwareInterface CameraHardwareInterface_SGS2 @@ -48,11 +50,11 @@ using namespace mozilla; class CameraHardwareInterface { public: - typedef enum { + enum Type { CAMERA_SGS2, CAMERA_MAGURO, CAMERA_DEFAULT - } Type; + }; static Type getType() { char propValue[PROPERTY_VALUE_MAX]; @@ -60,7 +62,7 @@ class CameraHardwareInterface { if (!strcmp(propValue, "GT-I9100")) return CAMERA_SGS2; - if (!strcmp(propValue, "MSM7627A_SKU1") || !strcmp(propValue, "MSM7627A_SKU3")) + if (!strcmp(propValue, "msm7627a_sku1") || !strcmp(propValue, "MSM7627A_SKU3")) return CAMERA_MAGURO; printf_stderr("CameraHardwareInterface : unsupported camera for device %s\n", propValue); @@ -89,30 +91,25 @@ class CameraHardwareInterface { CameraHardwareInterface(PRUint32 aCamera = 0) { }; }; -class DlopenWrapper { - public: - DlopenWrapper() : mHandle(nsnull) { }; +// Intentionally not trying to dlclose() this handle. That's playing +// Russian roulette with security bugs. +static void* sCameraLib; +static PRCallOnceType sInitCameraLib; - DlopenWrapper(const char* aLibrary) : mHandle(nsnull) { - mHandle = dlopen(aLibrary, RTLD_LAZY); - }; +static PRStatus +InitCameraLib() +{ + sCameraLib = dlopen("/system/lib/libcamera.so", RTLD_LAZY); + // We might fail to open the camera lib. That's OK. + return PR_SUCCESS; +} - ~DlopenWrapper() { - if (mHandle) - dlclose(mHandle); - }; - - bool opened() { - return mHandle != nsnull; - }; - - void* dlsym(const char* aFunction) { - return ::dlsym(mHandle, aFunction); - }; - - protected: - void* mHandle; -}; +static void* +GetCameraLibHandle() +{ + PR_CallOnce(&sInitCameraLib, InitCameraLib); + return sCameraLib; +} template class CameraImpl : public CameraHardwareInterface { public: @@ -121,14 +118,13 @@ template class CameraImpl : public CameraHardwareInterface { typedef sp (*HAL_openCameraHardware_MAGURO)(int, int); CameraImpl(PRUint32 aCamera = 0) : mOk(false), mCamera(nsnull) { - DlopenWrapper wrapper("/system/lib/libcamera.so"); - - if (!wrapper.opened()) + void* cameraLib = GetCameraLibHandle(); + if (!cameraLib) { + printf_stderr("CameraImpl: Failed to dlopen() camera library."); return; + } - mOk = true; - - void *hal = wrapper.dlsym("HAL_openCameraHardware"); + void *hal = dlsym(cameraLib, "HAL_openCameraHardware"); HAL_openCameraHardware_DEFAULT funct0; HAL_openCameraHardware_SGS2 funct1; HAL_openCameraHardware_MAGURO funct2; @@ -146,6 +142,11 @@ template class CameraImpl : public CameraHardwareInterface { mCamera = funct0(aCamera); break; } + + mOk = mCamera != nsnull; + if (!mOk) { + printf_stderr("CameraImpl: HAL_openCameraHardware() returned NULL (no camera interface)."); + } } bool ok() { @@ -251,12 +252,11 @@ GonkCameraInputStream::DataCallback(int32_t aMsgType, const sp& aDataPt PRUint32 GonkCameraInputStream::getNumberOfCameras() { typedef int (*HAL_getNumberOfCamerasFunct)(void); - DlopenWrapper wrapper("/system/lib/libcamera.so"); - - if (!wrapper.opened()) + void* cameraLib = GetCameraLibHandle(); + if (!cameraLib) return 0; - void *hal = wrapper.dlsym("HAL_getNumberOfCameras"); + void *hal = dlsym(cameraLib, "HAL_getNumberOfCameras"); if (nsnull == hal) return 0; diff --git a/netwerk/protocol/http/HttpChannelParent.cpp b/netwerk/protocol/http/HttpChannelParent.cpp index bf83cb43e5d..c0d2fff0fc5 100644 --- a/netwerk/protocol/http/HttpChannelParent.cpp +++ b/netwerk/protocol/http/HttpChannelParent.cpp @@ -356,15 +356,15 @@ HttpChannelParent::RecvRedirect2Verify(const nsresult& result, if (!mRedirectCallback) { // Bug 621446 investigation (optimization turned off above) if (mReceivedRedirect2Verify) - ::PR_Abort(); + NS_RUNTIMEABORT("Duplicate fire"); if (mSentRedirect1BeginFailed) - ::PR_Abort(); + NS_RUNTIMEABORT("Send to child failed"); if (mSentRedirect1Begin && NS_FAILED(result)) - ::PR_Abort(); + NS_RUNTIMEABORT("Redirect failed"); if (mSentRedirect1Begin && NS_SUCCEEDED(result)) - ::PR_Abort(); + NS_RUNTIMEABORT("Redirect succeeded"); if (!mRedirectChannel) - ::PR_Abort(); + NS_RUNTIMEABORT("Missing redirect channel"); } mReceivedRedirect2Verify = true; diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index 2571f2aa168..15152983ec8 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -307,15 +307,7 @@ nsHttpHandler::Init() rv = InitConnectionMgr(); if (NS_FAILED(rv)) return rv; -#ifdef ANDROID - mProductSub.AssignLiteral(MOZ_APP_UA_VERSION); -#else - mProductSub.AssignLiteral(MOZ_UA_BUILDID); -#endif - if (mProductSub.IsEmpty() && appInfo) - appInfo->GetPlatformBuildID(mProductSub); - if (mProductSub.Length() > 8) - mProductSub.SetLength(8); + mProductSub.AssignLiteral(MOZILLA_VERSION); // Startup the http category // Bring alive the objects in the http-protocol-startup category diff --git a/netwerk/protocol/http/nsHttpNTLMAuth.cpp b/netwerk/protocol/http/nsHttpNTLMAuth.cpp index f43770c3797..97d8b989cbd 100644 --- a/netwerk/protocol/http/nsHttpNTLMAuth.cpp +++ b/netwerk/protocol/http/nsHttpNTLMAuth.cpp @@ -45,6 +45,7 @@ #include "nsIAuthModule.h" #include "nsCOMPtr.h" #include "plbase64.h" +#include "prnetdb.h" //----------------------------------------------------------------------------- @@ -58,6 +59,7 @@ #include "nsISSLStatusProvider.h" static const char kAllowProxies[] = "network.automatic-ntlm-auth.allow-proxies"; +static const char kAllowNonFqdn[] = "network.automatic-ntlm-auth.allow-non-fqdn"; static const char kTrustedURIs[] = "network.automatic-ntlm-auth.trusted-uris"; static const char kForceGeneric[] = "network.auth.force-generic-ntlm"; @@ -121,6 +123,20 @@ MatchesBaseURI(const nsCSubstring &matchScheme, return false; } +static bool +IsNonFqdn(nsIURI *uri) +{ + nsCAutoString host; + PRNetAddr addr; + + if (NS_FAILED(uri->GetAsciiHost(host))) + return false; + + // return true if host does not contain a dot and is not an ip address + return !host.IsEmpty() && host.FindChar('.') == kNotFound && + PR_StringToNetAddr(host.BeginReading(), &addr) != PR_SUCCESS; +} + static bool TestPref(nsIURI *uri, const char *pref) { @@ -210,6 +226,15 @@ CanUseDefaultCredentials(nsIHttpAuthenticableChannel *channel, nsCOMPtr uri; channel->GetURI(getter_AddRefs(uri)); + + bool allowNonFqdn; + if (NS_FAILED(prefs->GetBoolPref(kAllowNonFqdn, &allowNonFqdn))) + allowNonFqdn = false; + if (allowNonFqdn && uri && IsNonFqdn(uri)) { + LOG(("Host is non-fqdn, default credentials are allowed\n")); + return true; + } + bool isTrustedHost = (uri && TestPref(uri, kTrustedURIs)); LOG(("Default credentials allowed for host: %d\n", isTrustedHost)); return isTrustedHost; diff --git a/netwerk/test/TestProtocols.cpp b/netwerk/test/TestProtocols.cpp index f602ec04e92..6dd81710103 100644 --- a/netwerk/test/TestProtocols.cpp +++ b/netwerk/test/TestProtocols.cpp @@ -797,6 +797,12 @@ nsresult LoadURLsFromFile(char *aFileName) if (urlString.Length()) { LOG(("\t%s\n", urlString.get())); rv = StartLoadingURL(urlString.get()); + if (NS_FAILED(rv)) { + // No need to log an error -- StartLoadingURL already + // did that for us, probably. + PR_Close(fd); + return rv; + } } } } diff --git a/nsprpub/TAG-INFO b/nsprpub/TAG-INFO index d5a8ed326a2..b9a6431a747 100644 --- a/nsprpub/TAG-INFO +++ b/nsprpub/TAG-INFO @@ -1 +1 @@ -NSPR_4_9_BETA7 +NSPR_4_9_RTM diff --git a/nsprpub/config/prdepend.h b/nsprpub/config/prdepend.h index c06078c629a..55444c40b3a 100644 --- a/nsprpub/config/prdepend.h +++ b/nsprpub/config/prdepend.h @@ -43,4 +43,3 @@ #error "Do not include this header file." - diff --git a/nsprpub/pr/include/prinit.h b/nsprpub/pr/include/prinit.h index e53b82143e1..7b34b33dcbe 100644 --- a/nsprpub/pr/include/prinit.h +++ b/nsprpub/pr/include/prinit.h @@ -63,11 +63,11 @@ PR_BEGIN_EXTERN_C ** The format of the version string is ** ".[.] []" */ -#define PR_VERSION "4.9 Beta" +#define PR_VERSION "4.9" #define PR_VMAJOR 4 #define PR_VMINOR 9 #define PR_VPATCH 0 -#define PR_BETA PR_TRUE +#define PR_BETA PR_FALSE /* ** PRVersionCheck diff --git a/security/coreconf/coreconf.dep b/security/coreconf/coreconf.dep index b75161110bb..b536cfc01b9 100644 --- a/security/coreconf/coreconf.dep +++ b/security/coreconf/coreconf.dep @@ -42,3 +42,4 @@ */ #error "Do not include this header file." + diff --git a/security/manager/Makefile.in b/security/manager/Makefile.in index b57c8683947..1da94ccd215 100644 --- a/security/manager/Makefile.in +++ b/security/manager/Makefile.in @@ -269,7 +269,7 @@ endif ifdef WRAP_LDFLAGS DEFAULT_GMAKE_FLAGS += \ LDFLAGS="$(LDFLAGS) $(WRAP_LDFLAGS)" \ - DSO_LDOPTS="-shared $(LDFLAGS) $(WRAP_LDFLAGS)" \ + DSO_LDOPTS="$(DSO_LDOPTS) $(LDFLAGS) $(WRAP_LDFLAGS)" \ $(NULL) endif diff --git a/security/manager/ssl/src/nsSmartCardEvent.cpp b/security/manager/ssl/src/nsSmartCardEvent.cpp index eeee5c24c89..b8f5bdef5ba 100644 --- a/security/manager/ssl/src/nsSmartCardEvent.cpp +++ b/security/manager/ssl/src/nsSmartCardEvent.cpp @@ -152,12 +152,6 @@ NS_IMETHODIMP nsSmartCardEvent::GetExplicitOriginalTarget(nsIDOMEventTarget * *a return mNSEvent->GetExplicitOriginalTarget(aTarget); } -NS_IMETHODIMP nsSmartCardEvent::GetTmpRealOriginalTarget(nsIDOMEventTarget * *aTarget) -{ - NS_ASSERTION(mNSEvent, "SmartCardEvent called without Init"); - return mNSEvent->GetTmpRealOriginalTarget(aTarget); -} - NS_IMETHODIMP nsSmartCardEvent::PreventBubble(void) { NS_ASSERTION(mNSEvent, "SmartCardEvent called without Init"); diff --git a/security/nss/TAG-INFO b/security/nss/TAG-INFO index ce7da6e81f8..afb975faf11 100644 --- a/security/nss/TAG-INFO +++ b/security/nss/TAG-INFO @@ -1 +1 @@ -NSS_3_13_2_RC0 +NSS_3_13_2_RTM diff --git a/security/nss/cmd/tstclnt/tstclnt.c b/security/nss/cmd/tstclnt/tstclnt.c index 8a8071b371b..52ef9a15f72 100644 --- a/security/nss/cmd/tstclnt/tstclnt.c +++ b/security/nss/cmd/tstclnt/tstclnt.c @@ -325,7 +325,8 @@ ownAuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig, { ServerCertAuth * serverCertAuth = (ServerCertAuth *) arg; - FPRINTF(stderr, "using asynchronous certificate validation\n"); + FPRINTF(stderr, "%s: using asynchronous certificate validation\n", + progName); PORT_Assert(serverCertAuth->shouldPause); PORT_Assert(!serverCertAuth->isPaused); diff --git a/security/nss/lib/nss/nss.h b/security/nss/lib/nss/nss.h index 83393ed8e89..405c0596df9 100644 --- a/security/nss/lib/nss/nss.h +++ b/security/nss/lib/nss/nss.h @@ -36,7 +36,7 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -/* $Id: nss.h,v 1.88 2012/02/13 21:34:08 kaie%kuix.de Exp $ */ +/* $Id: nss.h,v 1.89 2012/02/15 21:56:55 kaie%kuix.de Exp $ */ #ifndef __nss_h_ #define __nss_h_ @@ -66,11 +66,11 @@ * The format of the version string should be * ".[.[.]][ ][ ]" */ -#define NSS_VERSION "3.13.2.0" _NSS_ECC_STRING _NSS_CUSTOMIZED +#define NSS_VERSION "3.13.2.1" _NSS_ECC_STRING _NSS_CUSTOMIZED #define NSS_VMAJOR 3 #define NSS_VMINOR 13 #define NSS_VPATCH 2 -#define NSS_VBUILD 0 +#define NSS_VBUILD 1 #define NSS_BETA PR_FALSE #ifndef RC_INVOKED diff --git a/security/nss/lib/softoken/softkver.h b/security/nss/lib/softoken/softkver.h index d2c2763c1ea..4a3bf4c3347 100644 --- a/security/nss/lib/softoken/softkver.h +++ b/security/nss/lib/softoken/softkver.h @@ -57,11 +57,11 @@ * The format of the version string should be * ".[.[.]][ ][ ]" */ -#define SOFTOKEN_VERSION "3.13.2.0" SOFTOKEN_ECC_STRING +#define SOFTOKEN_VERSION "3.13.2.1" SOFTOKEN_ECC_STRING #define SOFTOKEN_VMAJOR 3 #define SOFTOKEN_VMINOR 13 #define SOFTOKEN_VPATCH 2 -#define SOFTOKEN_VBUILD 0 +#define SOFTOKEN_VBUILD 1 #define SOFTOKEN_BETA PR_FALSE #endif /* _SOFTKVER_H_ */ diff --git a/security/nss/lib/ssl/ssl.h b/security/nss/lib/ssl/ssl.h index f93a2e27f2c..74da9119b7b 100644 --- a/security/nss/lib/ssl/ssl.h +++ b/security/nss/lib/ssl/ssl.h @@ -36,7 +36,7 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -/* $Id: ssl.h,v 1.48 2012/02/11 12:58:47 kaie%kuix.de Exp $ */ +/* $Id: ssl.h,v 1.49 2012/02/15 21:52:08 kaie%kuix.de Exp $ */ #ifndef __ssl_h_ #define __ssl_h_ @@ -347,11 +347,14 @@ SSL_IMPORT CERTCertificate *SSL_PeerCertificate(PRFileDesc *fd); ** ** If the authenticate certificate hook returns SECFailure, then the bad cert ** hook will be called. The bad cert handler is NEVER called if the -** authenticate certificate hook returns SECWouldBlock. +** authenticate certificate hook returns SECWouldBlock. If the application +** needs to handle and/or override a bad cert, it should do so before it +** calls SSL_AuthCertificateComplete (modifying the error it passes to +** SSL_AuthCertificateComplete as needed). ** ** See the documentation for SSL_AuthCertificateComplete for more information ** about the asynchronous behavior that occurs when the authenticate -** certificate hook returns SECWouldBlock +** certificate hook returns SECWouldBlock. */ typedef SECStatus (PR_CALLBACK *SSLAuthCertificate)(void *arg, PRFileDesc *fd, PRBool checkSig, @@ -772,11 +775,11 @@ extern const char *NSSSSL_GetVersion(void); * a connection; it does not work for the server role. * * The application must call SSL_AuthCertificateComplete with 0 as the value of - * status parameter after it has successfully validated the peer's certificate, - * in order to continue the SSL handshake. + * the error parameter after it has successfully validated the peer's + * certificate, in order to continue the SSL handshake. * * The application may call SSL_AuthCertificateComplete with a non-zero value - * for status (e.g. SEC_ERROR_REVOKED_CERTIFICATE) when certificate validation + * for error (e.g. SEC_ERROR_REVOKED_CERTIFICATE) when certificate validation * fails, before it closes the connection. If the application does so, an * alert corresponding to the error (e.g. certificate_revoked) will be sent to * the peer. See the source code of the internal function @@ -816,10 +819,16 @@ extern const char *NSSSSL_GetVersion(void); * Returns SECFailure on failure, SECSuccess on success. Never returns * SECWouldBlock. Note that SSL_AuthCertificateComplete will (usually) return * SECSuccess; do not interpret the return value of SSL_AuthCertificateComplete - * as an indicator of whether it is OK to continue using the connection. + * as an indicator of whether it is OK to continue using the connection. For + * example, SSL_AuthCertificateComplete(fd, SEC_ERROR_REVOKED_CERTIFICATE) will + * return SECSuccess (normally), but that does not mean that the application + * should continue using the connection. If the application passes a non-zero + * value for second argument (error), or if SSL_AuthCertificateComplete returns + * anything other than SECSuccess, then the application should close the + * connection. */ SSL_IMPORT SECStatus SSL_AuthCertificateComplete(PRFileDesc *fd, - PRErrorCode status); + PRErrorCode error); SEC_END_PROTOS #endif /* __ssl_h_ */ diff --git a/security/nss/lib/ssl/ssl3con.c b/security/nss/lib/ssl/ssl3con.c index f17d4c1d724..a9161534c08 100644 --- a/security/nss/lib/ssl/ssl3con.c +++ b/security/nss/lib/ssl/ssl3con.c @@ -39,7 +39,7 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -/* $Id: ssl3con.c,v 1.162 2012/02/11 13:03:08 kaie%kuix.de Exp $ */ +/* $Id: ssl3con.c,v 1.163 2012/02/15 21:52:08 kaie%kuix.de Exp $ */ #include "cert.h" #include "ssl.h" @@ -8146,7 +8146,7 @@ ssl3_AlwaysFail(sslSocket * ss) /* Caller must hold 1stHandshakeLock. */ SECStatus -ssl3_AuthCertificateComplete(sslSocket *ss, PRErrorCode status) +ssl3_AuthCertificateComplete(sslSocket *ss, PRErrorCode error) { SECStatus rv; @@ -8168,9 +8168,9 @@ ssl3_AuthCertificateComplete(sslSocket *ss, PRErrorCode status) ss->ssl3.hs.authCertificatePending = PR_FALSE; - if (status != 0) { + if (error != 0) { ss->ssl3.hs.restartTarget = ssl3_AlwaysFail; - ssl3_SendAlertForCertError(ss, status); + ssl3_SendAlertForCertError(ss, error); rv = SECSuccess; } else if (ss->ssl3.hs.restartTarget != NULL) { sslRestartTarget target = ss->ssl3.hs.restartTarget; diff --git a/security/nss/lib/ssl/ssl3ext.c b/security/nss/lib/ssl/ssl3ext.c index f0cd26e3b00..6aa00c368b9 100644 --- a/security/nss/lib/ssl/ssl3ext.c +++ b/security/nss/lib/ssl/ssl3ext.c @@ -41,7 +41,7 @@ * ***** END LICENSE BLOCK ***** */ /* TLS extension code moved here from ssl3ecc.c */ -/* $Id: ssl3ext.c,v 1.20 2011/11/16 19:12:35 kaie%kuix.de Exp $ */ +/* $Id: ssl3ext.c,v 1.21 2012/02/15 21:52:08 kaie%kuix.de Exp $ */ #include "nssrenam.h" #include "nss.h" @@ -241,7 +241,7 @@ static const ssl3HelloExtensionHandler clientHelloHandlers[] = { #endif { ssl_session_ticket_xtn, &ssl3_ServerHandleSessionTicketXtn }, { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn }, - { ssl_next_proto_neg_xtn, &ssl3_ServerHandleNextProtoNegoXtn }, + { ssl_next_proto_nego_xtn, &ssl3_ServerHandleNextProtoNegoXtn }, { -1, NULL } }; @@ -252,7 +252,7 @@ static const ssl3HelloExtensionHandler serverHelloHandlersTLS[] = { /* TODO: add a handler for ssl_ec_point_formats_xtn */ { ssl_session_ticket_xtn, &ssl3_ClientHandleSessionTicketXtn }, { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn }, - { ssl_next_proto_neg_xtn, &ssl3_ClientHandleNextProtoNegoXtn }, + { ssl_next_proto_nego_xtn, &ssl3_ClientHandleNextProtoNegoXtn }, { -1, NULL } }; @@ -276,7 +276,7 @@ ssl3HelloExtensionSender clientHelloSendersTLS[SSL_MAX_EXTENSIONS] = { { ssl_ec_point_formats_xtn, &ssl3_SendSupportedPointFormatsXtn }, #endif { ssl_session_ticket_xtn, &ssl3_SendSessionTicketXtn }, - { ssl_next_proto_neg_xtn, &ssl3_ClientSendNextProtoNegoXtn } + { ssl_next_proto_nego_xtn, &ssl3_ClientSendNextProtoNegoXtn } /* any extra entries will appear as { 0, NULL } */ }; @@ -641,14 +641,14 @@ ssl3_ClientSendNextProtoNegoXtn(sslSocket * ss, PRBool append, if (append && maxBytes >= extension_length) { SECStatus rv; - rv = ssl3_AppendHandshakeNumber(ss, ssl_next_proto_neg_xtn, 2); + rv = ssl3_AppendHandshakeNumber(ss, ssl_next_proto_nego_xtn, 2); if (rv != SECSuccess) goto loser; rv = ssl3_AppendHandshakeNumber(ss, 0, 2); if (rv != SECSuccess) goto loser; ss->xtnData.advertised[ss->xtnData.numAdvertised++] = - ssl_next_proto_neg_xtn; + ssl_next_proto_nego_xtn; } else if (maxBytes < extension_length) { return 0; } diff --git a/security/nss/lib/ssl/sslimpl.h b/security/nss/lib/ssl/sslimpl.h index f2573536dfc..123fce81010 100644 --- a/security/nss/lib/ssl/sslimpl.h +++ b/security/nss/lib/ssl/sslimpl.h @@ -39,7 +39,7 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -/* $Id: sslimpl.h,v 1.93 2012/02/11 12:58:47 kaie%kuix.de Exp $ */ +/* $Id: sslimpl.h,v 1.94 2012/02/15 21:52:08 kaie%kuix.de Exp $ */ #ifndef __sslimpl_h_ #define __sslimpl_h_ @@ -1359,7 +1359,7 @@ extern void ssl_FreeSocket(struct sslSocketStr *ssl); extern SECStatus SSL3_SendAlert(sslSocket *ss, SSL3AlertLevel level, SSL3AlertDescription desc); -extern SECStatus ssl3_AuthCertificateComplete(sslSocket *ss, PRErrorCode status); +extern SECStatus ssl3_AuthCertificateComplete(sslSocket *ss, PRErrorCode error); /* * for dealing with SSL 3.0 clients sending SSL 2.0 format hellos diff --git a/security/nss/lib/ssl/sslsecur.c b/security/nss/lib/ssl/sslsecur.c index 71b87aba52e..61148d33408 100644 --- a/security/nss/lib/ssl/sslsecur.c +++ b/security/nss/lib/ssl/sslsecur.c @@ -37,7 +37,7 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -/* $Id: sslsecur.c,v 1.56 2012/02/11 12:58:47 kaie%kuix.de Exp $ */ +/* $Id: sslsecur.c,v 1.57 2012/02/15 21:52:08 kaie%kuix.de Exp $ */ #include "cert.h" #include "secitem.h" #include "keyhi.h" @@ -1488,7 +1488,7 @@ SSL_RestartHandshakeAfterServerCert(sslSocket * ss) /* See documentation in ssl.h */ SECStatus -SSL_AuthCertificateComplete(PRFileDesc *fd, PRErrorCode status) +SSL_AuthCertificateComplete(PRFileDesc *fd, PRErrorCode error) { SECStatus rv; sslSocket *ss = ssl_FindSocket(fd); @@ -1508,7 +1508,7 @@ SSL_AuthCertificateComplete(PRFileDesc *fd, PRErrorCode status) PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2); rv = SECFailure; } else { - rv = ssl3_AuthCertificateComplete(ss, status); + rv = ssl3_AuthCertificateComplete(ss, error); } ssl_Release1stHandshakeLock(ss); diff --git a/security/nss/lib/ssl/sslsock.c b/security/nss/lib/ssl/sslsock.c index af9dcf506d2..22206f7fa14 100644 --- a/security/nss/lib/ssl/sslsock.c +++ b/security/nss/lib/ssl/sslsock.c @@ -40,7 +40,7 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -/* $Id: sslsock.c,v 1.81 2012/02/11 13:20:53 kaie%kuix.de Exp $ */ +/* $Id: sslsock.c,v 1.82 2012/02/15 21:52:08 kaie%kuix.de Exp $ */ #include "seccomon.h" #include "cert.h" #include "keyhi.h" @@ -1964,8 +1964,23 @@ ssl_Poll(PRFileDesc *fd, PRInt16 how_flags, PRInt16 *p_out_flags) * the caller to poll the socket unless there is pending write data. */ if (ss->lastWriteBlocked && ss->pendingBuf.len != 0) { + /* Ignore any newly-received data on the socket, but do wait for + * the socket to become writable again. Here, it is OK for an error + * to be detected, because our logic for sending pending write data + * will allow us to report the error to the caller without the risk + * of the application spinning. + */ new_flags &= (PR_POLL_WRITE | PR_POLL_EXCEPT); } else { + /* Unfortunately, clearing new_flags will make it impossible for + * the application to detect errors that it would otherwise be + * able to detect with PR_POLL_EXCEPT, until the asynchronous + * callback completes. However, we must clear all the flags to + * prevent the application from spinning (alternating between + * calling PR_Poll that would return PR_POLL_EXCEPT, and send/recv + * which won't actually report the I/O error while we are waiting + * for the asynchronous callback to complete). + */ new_flags = 0; } } diff --git a/security/nss/lib/ssl/sslt.h b/security/nss/lib/ssl/sslt.h index 644668801d4..8991fb4e04d 100644 --- a/security/nss/lib/ssl/sslt.h +++ b/security/nss/lib/ssl/sslt.h @@ -37,7 +37,7 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -/* $Id: sslt.h,v 1.17 2011/10/29 00:29:11 bsmith%mozilla.com Exp $ */ +/* $Id: sslt.h,v 1.18 2012/02/15 21:52:08 kaie%kuix.de Exp $ */ #ifndef __sslt_h_ #define __sslt_h_ @@ -203,7 +203,7 @@ typedef enum { ssl_ec_point_formats_xtn = 11, #endif ssl_session_ticket_xtn = 35, - ssl_next_proto_neg_xtn = 13172, + ssl_next_proto_nego_xtn = 13172, ssl_renegotiation_info_xtn = 0xff01 /* experimental number */ } SSLExtensionType; diff --git a/security/nss/lib/util/nssutil.h b/security/nss/lib/util/nssutil.h index f32f270bcd6..d6cedb3b585 100644 --- a/security/nss/lib/util/nssutil.h +++ b/security/nss/lib/util/nssutil.h @@ -51,11 +51,11 @@ * The format of the version string should be * ".[.[.]][ ]" */ -#define NSSUTIL_VERSION "3.13.2.0" +#define NSSUTIL_VERSION "3.13.2.1" #define NSSUTIL_VMAJOR 3 #define NSSUTIL_VMINOR 13 #define NSSUTIL_VPATCH 2 -#define NSSUTIL_VBUILD 0 +#define NSSUTIL_VBUILD 1 #define NSSUTIL_BETA PR_FALSE SEC_BEGIN_PROTOS diff --git a/security/patches/bug-717906-lowhash b/security/patches/bug-717906-lowhash deleted file mode 100644 index a2824c508b6..00000000000 --- a/security/patches/bug-717906-lowhash +++ /dev/null @@ -1,115 +0,0 @@ -diff --git a/security/coreconf/Linux.mk b/security/coreconf/Linux.mk ---- a/security/coreconf/Linux.mk -+++ b/security/coreconf/Linux.mk -@@ -171,16 +171,17 @@ CPU_TAG = _$(CPU_ARCH) - # - # On Linux 2.6 or later, build libfreebl3.so with no NSPR and libnssutil3.so - # dependencies by default. Set FREEBL_NO_DEPEND to 0 in the environment to - # override this. - # - ifeq (2.6,$(firstword $(sort 2.6 $(OS_RELEASE)))) - ifndef FREEBL_NO_DEPEND - FREEBL_NO_DEPEND = 1 -+FREEBL_LOWHASH = 1 - endif - endif - - USE_SYSTEM_ZLIB = 1 - ZLIB_LIBS = -lz - - # The -rpath '$$ORIGIN' linker option instructs this library to search for its - # dependencies in the same directory where it resides. -diff --git a/security/nss/lib/freebl/Makefile b/security/nss/lib/freebl/Makefile ---- a/security/nss/lib/freebl/Makefile -+++ b/security/nss/lib/freebl/Makefile -@@ -71,17 +71,21 @@ ifdef USE_64 - endif - - ifdef USE_ABI32_FPU - DEFINES += -DNSS_USE_ABI32_FPU - endif - - ifeq ($(FREEBL_NO_DEPEND),1) - DEFINES += -DFREEBL_NO_DEPEND -- LOWHASH_SRCS = stubs.c nsslowhash.c -+ STUBS_SRCS = stubs.c -+endif -+ -+ifeq ($(FREEBL_LOWHASH),1) -+ LOWHASH_SRCS = nsslowhash.c - LOWHASH_EXPORTS = nsslowhash.h - MAPFILE_SOURCE = freebl_hash.def - else - MAPFILE_SOURCE = freebl.def - endif - - # FREEBL_USE_PRELINK - # -diff --git a/security/nss/lib/freebl/manifest.mn b/security/nss/lib/freebl/manifest.mn ---- a/security/nss/lib/freebl/manifest.mn -+++ b/security/nss/lib/freebl/manifest.mn -@@ -145,16 +145,17 @@ CSRCS = \ - rsa.c \ - shvfy.c \ - tlsprfalg.c \ - seed.c \ - jpake.c \ - $(MPI_SRCS) \ - $(MPCPU_SRCS) \ - $(ECL_SRCS) \ -+ $(STUBS_SRCS) \ - $(LOWHASH_SRCS) \ - $(NULL) - - ALL_CSRCS := $(CSRCS) - - ALL_HDRS = \ - alghmac.h \ - blapi.h \ -diff --git a/security/nss/lib/freebl/nsslowhash.c b/security/nss/lib/freebl/nsslowhash.c ---- a/security/nss/lib/freebl/nsslowhash.c -+++ b/security/nss/lib/freebl/nsslowhash.c -@@ -30,17 +30,19 @@ - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - /* $Id: nsslowhash.c,v 1.6 2010/09/10 00:42:36 emaldona%redhat.com Exp $ */ - -+#ifdef FREEBL_NO_DEPEND - #include "stubs.h" -+#endif - #include "prtypes.h" - #include "secerr.h" - #include "pkcs11t.h" - #include "blapi.h" - #include "sechash.h" - #include "nsslowhash.h" - - /* FIPS preprocessor directives for message digests */ -@@ -312,21 +314,23 @@ static int post_failed = 0; - - static NSSLOWInitContext dummyContext = { 0 }; - - NSSLOWInitContext * - NSSLOW_Init(void) - { - SECStatus rv; - CK_RV crv; -+#ifdef FREEBL_NO_DEPEND - PRBool nsprAvailable = PR_FALSE; - - - rv = FREEBL_InitStubs(); - nsprAvailable = (rv == SECSuccess ) ? PR_TRUE : PR_FALSE; -+#endif - - if (post_failed) { - return NULL; - } - - - if (!post && nsslow_GetFIPSEnabled()) { - crv = freebl_fipsPowerUpSelfTest(); diff --git a/toolkit/components/places/Database.cpp b/toolkit/components/places/Database.cpp index afbac50a248..f0badbf7167 100644 --- a/toolkit/components/places/Database.cpp +++ b/toolkit/components/places/Database.cpp @@ -251,6 +251,49 @@ SetJournalMode(nsCOMPtr& aDBConn, return JOURNAL_DELETE; } +class BlockingConnectionCloseCallback : public mozIStorageCompletionCallback { + bool mDone; + +public: + NS_DECL_ISUPPORTS + NS_DECL_MOZISTORAGECOMPLETIONCALLBACK + BlockingConnectionCloseCallback(); + void Spin(); +}; + +NS_IMETHODIMP +BlockingConnectionCloseCallback::Complete() +{ + mDone = true; + nsCOMPtr os = mozilla::services::GetObserverService(); + MOZ_ASSERT(os); + if (!os) + return NS_OK; + DebugOnly rv = os->NotifyObservers(nsnull, + TOPIC_PLACES_CONNECTION_CLOSED, + nsnull); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + return NS_OK; +} + +BlockingConnectionCloseCallback::BlockingConnectionCloseCallback() + : mDone(false) +{ + MOZ_ASSERT(NS_IsMainThread()); +} + +void BlockingConnectionCloseCallback::Spin() { + nsCOMPtr thread = do_GetCurrentThread(); + while (!mDone) { + NS_ProcessNextEvent(thread); + } +} + +NS_IMPL_THREADSAFE_ISUPPORTS1( + BlockingConnectionCloseCallback +, mozIStorageCompletionCallback +) + nsresult CreateRoot(nsCOMPtr& aDBConn, const nsCString& aRootName, @@ -701,6 +744,13 @@ Database::InitSchema(bool* aDatabaseMigrated) // Firefox 12 uses schema version 17. + if (currentSchemaVersion < 18) { + rv = MigrateV18Up(); + NS_ENSURE_SUCCESS(rv, rv); + } + + // Firefox 13 uses schema version 18. + // Schema Upgrades must add migration code here. rv = UpdateBookmarkRootTitles(); @@ -747,8 +797,6 @@ Database::InitSchema(bool* aDatabaseMigrated) // moz_hosts. rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_HOSTS); NS_ENSURE_SUCCESS(rv, rv); - rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_HOSTS_FRECENCYHOST); - NS_ENSURE_SUCCESS(rv, rv); // moz_bookmarks. rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_BOOKMARKS); @@ -918,7 +966,9 @@ Database::InitTempTriggers() NS_ENSURE_SUCCESS(rv, rv); rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERDELETE_TRIGGER); NS_ENSURE_SUCCESS(rv, rv); - rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERUPDATE_TRIGGER); + rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERUPDATE_FRECENCY_TRIGGER); + NS_ENSURE_SUCCESS(rv, rv); + rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERUPDATE_TYPED_TRIGGER); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; @@ -1632,8 +1682,6 @@ Database::MigrateV17Up() // Add the moz_hosts table so we can get hostnames for URL autocomplete. rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_HOSTS); NS_ENSURE_SUCCESS(rv, rv); - rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_HOSTS_FRECENCYHOST); - NS_ENSURE_SUCCESS(rv, rv); } // Fill the moz_hosts table with all the domains in moz_places. @@ -1657,6 +1705,50 @@ Database::MigrateV17Up() return NS_OK; } +nsresult +Database::MigrateV18Up() +{ + MOZ_ASSERT(NS_IsMainThread()); + + // moz_hosts should distinguish on typed entries. + + // Check if the profile already has a typed column. + nsCOMPtr stmt; + nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING( + "SELECT typed FROM moz_hosts" + ), getter_AddRefs(stmt)); + if (NS_FAILED(rv)) { + rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "ALTER TABLE moz_hosts ADD COLUMN typed NOT NULL DEFAULT 0" + )); + NS_ENSURE_SUCCESS(rv, rv); + } + + // With the addition of the typed column the covering index loses its + // advantages. On the other side querying on host and (optionally) typed + // largely restricts the number of results, making scans decently fast. + rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP INDEX IF EXISTS moz_hosts_frecencyhostindex" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Update typed data. + nsCOMPtr updateTypedStmt; + rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING( + "UPDATE moz_hosts SET typed = 1 WHERE host IN ( " + "SELECT fixup_url(get_unreversed_host(rev_host)) " + "FROM moz_places WHERE typed = 1 " + ") " + ), getter_AddRefs(updateTypedStmt)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr ps; + rv = updateTypedStmt->ExecuteAsync(nsnull, getter_AddRefs(ps)); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + void Database::Shutdown() { @@ -1672,9 +1764,10 @@ Database::Shutdown() ); DispatchToAsyncThread(event); - nsRefPtr closeListener = - new PlacesEvent(TOPIC_PLACES_CONNECTION_CLOSED); + nsRefPtr closeListener = + new BlockingConnectionCloseCallback(); (void)mMainConn->AsyncClose(closeListener); + closeListener->Spin(); // Don't set this earlier, otherwise some internal helper used on shutdown // may bail out. diff --git a/toolkit/components/places/Database.h b/toolkit/components/places/Database.h index 8bcd13f2aae..09570e4939a 100644 --- a/toolkit/components/places/Database.h +++ b/toolkit/components/places/Database.h @@ -47,7 +47,7 @@ // This is the schema version. Update it at any schema change and add a // corresponding migrateVxx method below. -#define DATABASE_SCHEMA_VERSION 17 +#define DATABASE_SCHEMA_VERSION 18 // Fired after Places inited. #define TOPIC_PLACES_INIT_COMPLETE "places-init-complete" @@ -300,6 +300,7 @@ protected: nsresult MigrateV15Up(); nsresult MigrateV16Up(); nsresult MigrateV17Up(); + nsresult MigrateV18Up(); nsresult UpdateBookmarkRootTitles(); nsresult CheckAndUpdateGUIDs(); diff --git a/toolkit/components/places/Helpers.cpp b/toolkit/components/places/Helpers.cpp index 6e70f2dcfd0..0f6f7499dce 100644 --- a/toolkit/components/places/Helpers.cpp +++ b/toolkit/components/places/Helpers.cpp @@ -400,13 +400,6 @@ PlacesEvent::Run() return NS_OK; } -NS_IMETHODIMP -PlacesEvent::Complete() -{ - Notify(); - return NS_OK; -} - void PlacesEvent::Notify() { @@ -417,9 +410,8 @@ PlacesEvent::Notify() } } -NS_IMPL_THREADSAFE_ISUPPORTS2( +NS_IMPL_THREADSAFE_ISUPPORTS1( PlacesEvent -, mozIStorageCompletionCallback , nsIRunnable ) diff --git a/toolkit/components/places/Helpers.h b/toolkit/components/places/Helpers.h index a8d00f26f3e..ebfeb5e4ba8 100644 --- a/toolkit/components/places/Helpers.h +++ b/toolkit/components/places/Helpers.h @@ -247,12 +247,10 @@ bool GetHiddenState(bool aIsRedirect, * Notifies a specified topic via the observer service. */ class PlacesEvent : public nsRunnable - , public mozIStorageCompletionCallback { public: NS_DECL_ISUPPORTS NS_DECL_NSIRUNNABLE - NS_DECL_MOZISTORAGECOMPLETIONCALLBACK PlacesEvent(const char* aTopic); protected: diff --git a/toolkit/components/places/nsAnnotationService.cpp b/toolkit/components/places/nsAnnotationService.cpp index 6c9eb49a140..a12e4138820 100644 --- a/toolkit/components/places/nsAnnotationService.cpp +++ b/toolkit/components/places/nsAnnotationService.cpp @@ -2037,31 +2037,17 @@ nsAnnotationService::Observe(nsISupports *aSubject, "DELETE FROM moz_annos WHERE expiration = :expire_session" ); NS_ENSURE_STATE(pageAnnoStmt); + nsresult rv = pageAnnoStmt->BindInt32ByName(NS_LITERAL_CSTRING("expire_session"), + EXPIRE_SESSION); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr itemAnnoStmt = mDB->GetAsyncStatement( "DELETE FROM moz_items_annos WHERE expiration = :expire_session" ); NS_ENSURE_STATE(itemAnnoStmt); - -# define ASYNC_BIND(_stmt) \ - PR_BEGIN_MACRO \ - nsCOMPtr paramsArray; \ - nsresult rv = _stmt->NewBindingParamsArray(getter_AddRefs(paramsArray)); \ - NS_ENSURE_SUCCESS(rv, rv); \ - nsCOMPtr params; \ - rv = paramsArray->NewBindingParams(getter_AddRefs(params)); \ - NS_ENSURE_SUCCESS(rv, rv); \ - rv = params->BindInt32ByName(NS_LITERAL_CSTRING("expire_session"), EXPIRE_SESSION); \ - NS_ENSURE_SUCCESS(rv, rv); \ - rv = paramsArray->AddParams(params); \ - NS_ENSURE_SUCCESS(rv, rv); \ - rv = _stmt->BindParameters(paramsArray); \ - NS_ENSURE_SUCCESS(rv, rv); \ - PR_END_MACRO - - ASYNC_BIND(pageAnnoStmt); - ASYNC_BIND(itemAnnoStmt); - -# undef ASYNC_BIND + rv = itemAnnoStmt->BindInt32ByName(NS_LITERAL_CSTRING("expire_session"), + EXPIRE_SESSION); + NS_ENSURE_SUCCESS(rv, rv); mozIStorageBaseStatement *stmts[] = { pageAnnoStmt.get() @@ -2069,8 +2055,8 @@ nsAnnotationService::Observe(nsISupports *aSubject, }; nsCOMPtr ps; - nsresult rv = mDB->MainConn()->ExecuteAsync(stmts, ArrayLength(stmts), - nsnull, getter_AddRefs(ps)); + rv = mDB->MainConn()->ExecuteAsync(stmts, ArrayLength(stmts), nsnull, + getter_AddRefs(ps)); NS_ENSURE_SUCCESS(rv, rv); } } diff --git a/toolkit/components/places/nsNavHistory.cpp b/toolkit/components/places/nsNavHistory.cpp index 6b91f77a3db..f346ac26f50 100644 --- a/toolkit/components/places/nsNavHistory.cpp +++ b/toolkit/components/places/nsNavHistory.cpp @@ -5328,31 +5328,18 @@ nsNavHistory::UpdateFrecency(PRInt64 aPlaceId) "WHERE id = :page_id" ); NS_ENSURE_STATE(updateFrecencyStmt); + nsresult rv = updateFrecencyStmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), + aPlaceId); + NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr updateHiddenStmt = mDB->GetAsyncStatement( "UPDATE moz_places " "SET hidden = 0 " "WHERE id = :page_id AND frecency <> 0" ); NS_ENSURE_STATE(updateHiddenStmt); - -#define ASYNC_BIND(_stmt) \ - PR_BEGIN_MACRO \ - nsCOMPtr paramsArray; \ - nsresult rv = _stmt->NewBindingParamsArray(getter_AddRefs(paramsArray)); \ - NS_ENSURE_SUCCESS(rv, rv); \ - nsCOMPtr params; \ - rv = paramsArray->NewBindingParams(getter_AddRefs(params)); \ - NS_ENSURE_SUCCESS(rv, rv); \ - rv = params->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), aPlaceId); \ - NS_ENSURE_SUCCESS(rv, rv); \ - rv = paramsArray->AddParams(params); \ - NS_ENSURE_SUCCESS(rv, rv); \ - rv = _stmt->BindParameters(paramsArray); \ - NS_ENSURE_SUCCESS(rv, rv); \ - PR_END_MACRO - - ASYNC_BIND(updateFrecencyStmt); - ASYNC_BIND(updateHiddenStmt); + rv = updateHiddenStmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), + aPlaceId); + NS_ENSURE_SUCCESS(rv, rv); mozIStorageBaseStatement *stmts[] = { updateFrecencyStmt.get() @@ -5362,12 +5349,11 @@ nsNavHistory::UpdateFrecency(PRInt64 aPlaceId) nsRefPtr cb = new AsyncStatementCallbackNotifier(TOPIC_FRECENCY_UPDATED); nsCOMPtr ps; - nsresult rv = mDB->MainConn()->ExecuteAsync(stmts, ArrayLength(stmts), cb, - getter_AddRefs(ps)); + rv = mDB->MainConn()->ExecuteAsync(stmts, ArrayLength(stmts), cb, + getter_AddRefs(ps)); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; -#undef ASYNC_BIND } diff --git a/toolkit/components/places/nsPlacesAutoComplete.js b/toolkit/components/places/nsPlacesAutoComplete.js index e10e61b1eae..72126c45ada 100644 --- a/toolkit/components/places/nsPlacesAutoComplete.js +++ b/toolkit/components/places/nsPlacesAutoComplete.js @@ -102,7 +102,11 @@ const kQueryTypeFiltered = 1; const kTitleTagsSeparator = " \u2013 "; const kBrowserUrlbarBranch = "browser.urlbar."; -const kBrowserUrlbarAutofillPref = "browser.urlbar.autoFill"; + +// Toggle autoFill. +const kBrowserUrlbarAutofillPref = "autoFill"; +// Whether to search only typed entries. +const kBrowserUrlbarAutofillTypedPref = "autoFill.typed"; //////////////////////////////////////////////////////////////////////////////// //// Globals @@ -840,8 +844,6 @@ nsPlacesAutoComplete.prototype = { */ _loadPrefs: function PAC_loadPrefs(aRegisterObserver) { - let self = this; - this._enabled = safePrefGetter(this._prefs, "autocomplete.enabled", true); this._matchBehavior = safePrefGetter(this._prefs, "matchBehavior", @@ -1287,8 +1289,7 @@ nsPlacesAutoComplete.prototype = { function urlInlineComplete() { this._loadPrefs(true); - // register observers - Services.obs.addObserver(this, kTopicShutdown, false); + Services.obs.addObserver(this, kTopicShutdown, true); } urlInlineComplete.prototype = { @@ -1301,10 +1302,8 @@ urlInlineComplete.prototype = { get _db() { if (!this.__db && this._autofill) { - this.__db = Cc["@mozilla.org/browser/nav-history-service;1"]. - getService(Ci.nsPIPlacesDatabase). - DBConnection. - clone(true); + this.__db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase). + DBConnection.clone(true); } return this.__db; }, @@ -1317,9 +1316,11 @@ urlInlineComplete.prototype = { // Add a trailing slash at the end of the hostname, since we always // want to complete up to and including a URL separator. this.__syncQuery = this._db.createStatement( - "SELECT host || '/' " + "/* do not warn (bug no): could index on (typed,frecency) but not worth it */ " + + "SELECT host || '/' " + "FROM moz_hosts " + "WHERE host BETWEEN :search_string AND :search_string || X'FFFF' " + + (this._autofillTyped ? "AND typed = 1 " : "") + "ORDER BY frecency DESC " + "LIMIT 1" ); @@ -1333,9 +1334,11 @@ urlInlineComplete.prototype = { { if (!this.__asyncQuery) { this.__asyncQuery = this._db.createAsyncStatement( - "SELECT h.url " + "/* do not warn (bug no): can't use an index */ " + + "SELECT h.url " + "FROM moz_places h " + "WHERE h.frecency <> 0 " + + (this._autofillTyped ? "AND h.typed = 1 " : "") + "AND AUTOCOMPLETE_MATCH(:searchString, h.url, " + "h.title, '', " + "h.visit_count, h.typed, 0, 0, " @@ -1463,11 +1466,15 @@ urlInlineComplete.prototype = { */ _loadPrefs: function UIC_loadPrefs(aRegisterObserver) { - this._autofill = safePrefGetter(Services.prefs, + let prefBranch = Services.prefs.getBranch(kBrowserUrlbarBranch); + this._autofill = safePrefGetter(prefBranch, kBrowserUrlbarAutofillPref, true); + this._autofillTyped = safePrefGetter(prefBranch, + kBrowserUrlbarAutofillTypedPref, + true); if (aRegisterObserver) { - Services.prefs.addObserver(kBrowserUrlbarAutofillPref, this, true); + Services.prefs.addObserver(kBrowserUrlbarBranch, this, true); } }, @@ -1511,27 +1518,31 @@ urlInlineComplete.prototype = { ////////////////////////////////////////////////////////////////////////////// //// nsIObserver - observe: function PAC_observe(aSubject, aTopic, aData) + observe: function UIC_observe(aSubject, aTopic, aData) { if (aTopic == kTopicShutdown) { - Services.obs.removeObserver(this, kTopicShutdown); this._closeDatabase(); } - else if (aTopic == kPrefChanged) { + else if (aTopic == kPrefChanged && + (aData.substr(kBrowserUrlbarBranch.length) == kBrowserUrlbarAutofillPref || + aData.substr(kBrowserUrlbarBranch.length) == kBrowserUrlbarAutofillTypedPref)) { + let previousAutofillTyped = this._autofillTyped; this._loadPrefs(); if (!this._autofill) { this.stopSearch(); this._closeDatabase(); } + else if (this._autofillTyped != previousAutofillTyped) { + // Invalidate the statements to update them for the new typed status. + this._invalidateStatements(); + } } }, /** - * - * Finalize and close the database safely - * - **/ - _closeDatabase: function UIC_closeDatabase() + * Finalizes and invalidates cached statements. + */ + _invalidateStatements: function UIC_invalidateStatements() { // Finalize the statements that we have used. let stmts = [ @@ -1546,6 +1557,14 @@ urlInlineComplete.prototype = { this[stmts[i]] = null; } } + }, + + /** + * Closes the database. + */ + _closeDatabase: function UIC_closeDatabase() + { + this._invalidateStatements(); if (this.__db) { this._db.asyncClose(); this.__db = null; diff --git a/toolkit/components/places/nsPlacesIndexes.h b/toolkit/components/places/nsPlacesIndexes.h index dc8ed9e6f7f..76e5ea21876 100644 --- a/toolkit/components/places/nsPlacesIndexes.h +++ b/toolkit/components/places/nsPlacesIndexes.h @@ -136,15 +136,6 @@ "placeattributeindex", "moz_annos", "place_id, anno_attribute_id", "UNIQUE" \ ) -/** - * moz_hosts - */ - -#define CREATE_IDX_MOZ_HOSTS_FRECENCYHOST \ - CREATE_PLACES_IDX( \ - "frecencyhostindex", "moz_hosts", "frecency, host", "" \ - ) - /** * moz_items_annos */ diff --git a/toolkit/components/places/nsPlacesTables.h b/toolkit/components/places/nsPlacesTables.h index 89411c8a78a..9bc7c8c93b6 100644 --- a/toolkit/components/places/nsPlacesTables.h +++ b/toolkit/components/places/nsPlacesTables.h @@ -167,6 +167,7 @@ " id INTEGER PRIMARY KEY" \ ", host TEXT NOT NULL UNIQUE" \ ", frecency INTEGER" \ + ", typed INTEGER NOT NULL DEFAULT 0" \ ")" \ ) diff --git a/toolkit/components/places/nsPlacesTriggers.h b/toolkit/components/places/nsPlacesTriggers.h index 1ff95249239..4169b2d362d 100644 --- a/toolkit/components/places/nsPlacesTriggers.h +++ b/toolkit/components/places/nsPlacesTriggers.h @@ -87,11 +87,12 @@ "AFTER INSERT ON moz_places FOR EACH ROW " \ "WHEN LENGTH(NEW.rev_host) > 1 " \ "BEGIN " \ - "INSERT OR REPLACE INTO moz_hosts (id, host, frecency) " \ + "INSERT OR REPLACE INTO moz_hosts (id, host, frecency, typed) " \ "VALUES (" \ "(SELECT id FROM moz_hosts WHERE host = fixup_url(get_unreversed_host(NEW.rev_host))), " \ "fixup_url(get_unreversed_host(NEW.rev_host)), " \ - "MAX((SELECT frecency FROM moz_hosts WHERE host = fixup_url(get_unreversed_host(NEW.rev_host))), NEW.frecency) " \ + "MAX((SELECT frecency FROM moz_hosts WHERE host = fixup_url(get_unreversed_host(NEW.rev_host))), NEW.frecency), " \ + "MAX(IFNULL((SELECT typed FROM moz_hosts WHERE host = fixup_url(get_unreversed_host(NEW.rev_host))), 0), NEW.typed) " \ "); " \ "END" \ ) @@ -110,7 +111,7 @@ // frecency changes by a meaningful percentage. This is because the frecency // decay algorithm requires to update all the frecencies at once, causing a // too high overhead, while leaving the ordering unchanged. -#define CREATE_PLACES_AFTERUPDATE_TRIGGER NS_LITERAL_CSTRING( \ +#define CREATE_PLACES_AFTERUPDATE_FRECENCY_TRIGGER NS_LITERAL_CSTRING( \ "CREATE TEMP TRIGGER moz_places_afterupdate_frecency_trigger " \ "AFTER UPDATE OF frecency ON moz_places FOR EACH ROW " \ "WHEN NEW.frecency >= 0 " \ @@ -127,6 +128,17 @@ "END" \ ) +#define CREATE_PLACES_AFTERUPDATE_TYPED_TRIGGER NS_LITERAL_CSTRING( \ + "CREATE TEMP TRIGGER moz_places_afterupdate_typed_trigger " \ + "AFTER UPDATE OF typed ON moz_places FOR EACH ROW " \ + "WHEN NEW.typed = 1 " \ + "BEGIN " \ + "UPDATE moz_hosts " \ + "SET typed = 1 " \ + "WHERE host = fixup_url(get_unreversed_host(NEW.rev_host)); " \ + "END" \ +) + /** * This trigger removes a row from moz_openpages_temp when open_count reaches 0. * diff --git a/toolkit/components/places/tests/expiration/test_annos_expire_session.js b/toolkit/components/places/tests/expiration/test_annos_expire_session.js index 5a1e7aeb6de..d4c997edb3f 100644 --- a/toolkit/components/places/tests/expiration/test_annos_expire_session.js +++ b/toolkit/components/places/tests/expiration/test_annos_expire_session.js @@ -86,28 +86,27 @@ function run_test() { items = as.getItemsWithAnnotation("test2"); do_check_eq(items.length, 10); - shutdownPlaces(); - - let stmt = DBConn(true).createAsyncStatement( - "SELECT id FROM moz_annos " - + "UNION " - + "SELECT id FROM moz_items_annos " - ); - stmt.executeAsync({ - handleResult: function(aResultSet) - { - do_throw("Should not find any leftover session annotations"); - }, - handleError: function(aError) - { - do_throw("Error code " + aError.result + " with message '" + - aError.message + "' returned."); - }, - handleCompletion: function(aReason) - { - do_check_eq(aReason, Ci.mozIStorageStatementCallback.REASON_FINISHED); - do_test_finished(); - } + waitForConnectionClosed(function() { + let stmt = DBConn(true).createAsyncStatement( + "SELECT id FROM moz_annos " + + "UNION ALL " + + "SELECT id FROM moz_items_annos " + ); + stmt.executeAsync({ + handleResult: function(aResultSet) { + dump_table("moz_annos"); + dump_table("moz_items_annos"); + do_throw("Should not find any leftover session annotations"); + }, + handleError: function(aError) { + do_throw("Error code " + aError.result + " with message '" + + aError.message + "' returned."); + }, + handleCompletion: function(aReason) { + do_check_eq(aReason, Ci.mozIStorageStatementCallback.REASON_FINISHED); + do_test_finished(); + } + }); + stmt.finalize(); }); - stmt.finalize(); } diff --git a/toolkit/components/places/tests/head_common.js b/toolkit/components/places/tests/head_common.js index 5a0af5be5bf..1eba000c58b 100644 --- a/toolkit/components/places/tests/head_common.js +++ b/toolkit/components/places/tests/head_common.js @@ -35,7 +35,7 @@ * * ***** END LICENSE BLOCK ***** */ -const CURRENT_SCHEMA_VERSION = 17; +const CURRENT_SCHEMA_VERSION = 18; const NS_APP_USER_PROFILE_50_DIR = "ProfD"; const NS_APP_PROFILE_DIR_STARTUP = "ProfDS"; @@ -644,6 +644,21 @@ function waitForAsyncUpdates(aCallback, aScope, aArguments) commit.finalize(); } +/** + * Shutdowns Places, invoking the callback when the connection has been closed. + * + * @param aCallback + * Function to be called when done. + */ +function waitForConnectionClosed(aCallback) +{ + Services.obs.addObserver(function WFCCCallback() { + Services.obs.removeObserver(WFCCCallback, "places-connection-closed"); + aCallback(); + }, "places-connection-closed", false); + shutdownPlaces(); +} + /** * Tests if a given guid is valid for use in Places or not. * diff --git a/toolkit/components/places/tests/inline/head_autocomplete.js b/toolkit/components/places/tests/inline/head_autocomplete.js index d475468a0ae..b310d5ddbc0 100644 --- a/toolkit/components/places/tests/inline/head_autocomplete.js +++ b/toolkit/components/places/tests/inline/head_autocomplete.js @@ -21,29 +21,6 @@ XPCOMUtils.defineLazyServiceGetter(this, "gHistory", "@mozilla.org/browser/history;1", "mozIAsyncHistory"); -function VisitInfo(aTransitionType, aVisitTime) -{ - this.transitionType = - aTransitionType === undefined ? TRANSITION_LINK : aTransitionType; - this.visitDate = aVisitTime || Date.now() * 1000; -} - -function addVisits(aUrls) -{ - let places = []; - aUrls.forEach(function(url) { - places.push({ - uri: url.url, - title: "test for " + url.url, - visits: [ - new VisitInfo(url.transition), - ], - }); - }); - - gHistory.updatePlaces(places); -} - /** * @param aSearches * Array of AutoCompleteSearch names. @@ -168,8 +145,10 @@ function ensure_results(aSearchString, aExpectedValue) { function run_test() { Services.prefs.setBoolPref("browser.urlbar.autoFill", true); + Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false); do_register_cleanup(function () { Services.prefs.clearUserPref("browser.urlbar.autoFill"); + Services.prefs.clearUserPref("browser.urlbar.autoFill.typed"); }); gAutoCompleteTests.forEach(function (testData) { @@ -214,3 +193,26 @@ function addBookmark(aBookmarkObj) { PlacesUtils.bookmarks.setKeywordForBookmark(itemId, aBookmarkObj.keyword); } } + +function VisitInfo(aTransitionType, aVisitTime) +{ + this.transitionType = + aTransitionType === undefined ? TRANSITION_LINK : aTransitionType; + this.visitDate = aVisitTime || Date.now() * 1000; +} + +function addVisits(aUrls) +{ + let places = []; + aUrls.forEach(function(url) { + places.push({ + uri: url.url, + title: "test for " + url.url, + visits: [ + new VisitInfo(url.transition), + ], + }); + }); + + gHistory.updatePlaces(places); +} diff --git a/toolkit/components/places/tests/inline/test_typed.js b/toolkit/components/places/tests/inline/test_typed.js new file mode 100644 index 00000000000..708c08d7a59 --- /dev/null +++ b/toolkit/components/places/tests/inline/test_typed.js @@ -0,0 +1,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/. */ + +// First do searches with typed behavior forced to false, so later tests will +// ensure autocomplete is able to dinamically switch behavior. + +add_autocomplete_test([ + "Searching for domain should autoFill it", + "moz", + "mozilla.org/", + function () { + Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false); + addVisits([ { url: NetUtil.newURI("http://mozilla.org/link/") + , transition: TRANSITION_LINK } + ]); + } +]); + +add_autocomplete_test([ + "Searching for url should autoFill it", + "mozilla.org/li", + "mozilla.org/link/", + function () { + Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false); + addVisits([ { url: NetUtil.newURI("http://mozilla.org/link/") + , transition: TRANSITION_LINK } + ]); + } +]); + +// Now do searches with typed behavior forced to true. + +add_autocomplete_test([ + "Searching for non-typed domain should not autoFill it", + "moz", + "moz", + function () { + Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", true); + addVisits([ { url: NetUtil.newURI("http://mozilla.org/link/") + , transition: TRANSITION_LINK } + ]); + } +]); + +add_autocomplete_test([ + "Searching for typed domain should autoFill it", + "moz", + "mozilla.org/", + function () { + Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", true); + addVisits([ { url: NetUtil.newURI("http://mozilla.org/typed/") + , transition: TRANSITION_TYPED } + ]); + } +]); + +add_autocomplete_test([ + "Searching for non-typed url should not autoFill it", + "mozilla.org/li", + "mozilla.org/li", + function () { + Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", true); + addVisits([ { url: NetUtil.newURI("http://mozilla.org/link/") + , transition: TRANSITION_LINK } + ]); + } +]); + +add_autocomplete_test([ + "Searching for typed url should autoFill it", + "mozilla.org/li", + "mozilla.org/link/", + function () { + Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", true); + addVisits([ { url: NetUtil.newURI("http://mozilla.org/link/") + , transition: TRANSITION_TYPED } + ]); + } +]); diff --git a/toolkit/components/places/tests/inline/xpcshell.ini b/toolkit/components/places/tests/inline/xpcshell.ini index 2a4d0089541..7556fbc7ea9 100644 --- a/toolkit/components/places/tests/inline/xpcshell.ini +++ b/toolkit/components/places/tests/inline/xpcshell.ini @@ -5,3 +5,4 @@ tail = [test_autocomplete_functional.js] [test_casing.js] [test_keywords.js] +[test_typed.js] diff --git a/toolkit/components/places/tests/migration/test_current_from_v10.js b/toolkit/components/places/tests/migration/test_current_from_v10.js index 8da67247f2e..0bc2c15c564 100644 --- a/toolkit/components/places/tests/migration/test_current_from_v10.js +++ b/toolkit/components/places/tests/migration/test_current_from_v10.js @@ -282,30 +282,40 @@ function test_moz_hosts() { // This will throw if the column does not exist let stmt = DBConn().createStatement( - "SELECT host, frecency " + "SELECT host, frecency, typed " + "FROM moz_hosts " ); stmt.finalize(); + // moz_hosts is populated asynchronously, so query asynchronously to serialize + // to that. // check the number of entries in moz_hosts equals the number of // unique rev_host in moz_places - var query = "SELECT (" - + "SELECT COUNT(host) " - + "FROM moz_hosts), (" - + "SELECT COUNT(DISTINCT rev_host) " - + "FROM moz_places " - + "WHERE LENGTH(rev_host) > 1)"; - - stmt = DBConn().createStatement(query); + stmt = DBConn().createAsyncStatement( + "SELECT (" + + "SELECT COUNT(host) " + + "FROM moz_hosts), (" + + "SELECT COUNT(DISTINCT rev_host) " + + "FROM moz_places " + + "WHERE LENGTH(rev_host) > 1)" + ); try { - stmt.executeStep(); - let mozPlacesCount = stmt.getInt32(0); - let mozHostsCount = stmt.getInt32(1); - do_check_eq(mozPlacesCount, mozHostsCount); + stmt.executeAsync({ + handleResult: function (aResultSet) { + let row = aResult.getNextRow(); + let mozPlacesCount = row.getResultByIndex(0); + let mozHostsCount = row.getResultByIndex(1); + do_check_eq(mozPlacesCount, mozHostsCount); + }, + handleError: function () {}, + handleCompletion: function (aReason) { + do_check_eq(aReason, Ci.mozIStorageStatementCallback.REASON_FINISHED); + run_next_test(); + } + }); } finally { stmt.finalize(); - run_next_test(); } } @@ -328,8 +338,6 @@ function test_final_state() do_check_true(db.indexExists("moz_places_guid_uniqueindex")); do_check_true(db.indexExists("moz_favicons_guid_uniqueindex")); - do_check_true(db.indexExists("moz_hosts_frecencyhostindex")); - do_check_eq(db.schemaVersion, CURRENT_SCHEMA_VERSION); db.close(); diff --git a/toolkit/components/places/tests/unit/test_hosts_triggers.js b/toolkit/components/places/tests/unit/test_hosts_triggers.js index 7b0596aef1d..acb4955bb13 100644 --- a/toolkit/components/places/tests/unit/test_hosts_triggers.js +++ b/toolkit/components/places/tests/unit/test_hosts_triggers.js @@ -5,6 +5,10 @@ * This file tests the validity of various triggers that add remove hosts from moz_hosts */ +XPCOMUtils.defineLazyServiceGetter(this, "gHistory", + "@mozilla.org/browser/history;1", + "mozIAsyncHistory"); + // add some visits and remove them, add a bookmark, // change its uri, then remove it, and // for each change check that moz_hosts has correctly been updated. @@ -28,10 +32,10 @@ function isHostInMozPlaces(aURI) return result; } -function isHostInMozHosts(aURI) +function isHostInMozHosts(aURI, aTyped) { let stmt = DBConn().createStatement( - "SELECT host " + "SELECT host, typed " + "FROM moz_hosts " + "WHERE host = :host" ); @@ -39,7 +43,10 @@ function isHostInMozHosts(aURI) stmt.params.host = aURI.host; while(stmt.executeStep()) { if (stmt.row.host == aURI.host) { - result = true; + if (aTyped != null) + result = aTyped == stmt.row.typed; + else + result = true; break; } } @@ -49,12 +56,15 @@ function isHostInMozHosts(aURI) let urls = [{uri: NetUtil.newURI("http://visit1.mozilla.org"), expected: "visit1.mozilla.org", + typed: 0 }, {uri: NetUtil.newURI("http://visit2.mozilla.org"), expected: "visit2.mozilla.org", + typed: 0 }, {uri: NetUtil.newURI("http://www.foo.mozilla.org"), expected: "foo.mozilla.org", + typed: 1 }, ]; @@ -75,16 +85,12 @@ function test_moz_hosts_update() uri: url.uri, title: "test for " + url.url, visits: [ - new VisitInfo(), + new VisitInfo(url.typed ? TRANSITION_TYPED : undefined), ], }; places.push(place); }); - XPCOMUtils.defineLazyServiceGetter(this, "gHistory", - "@mozilla.org/browser/history;1", - "mozIAsyncHistory"); - gHistory.updatePlaces(places, { handleResult: function () { }, @@ -92,10 +98,11 @@ function test_moz_hosts_update() do_throw("gHistory.updatePlaces() failed"); }, handleCompletion: function () { - do_check_true(isHostInMozHosts(urls[0].uri)); - do_check_true(isHostInMozHosts(urls[1].uri)); + do_check_true(isHostInMozHosts(urls[0].uri, urls[0].typed)); + do_check_true(isHostInMozHosts(urls[1].uri, urls[1].typed)); // strip the WWW from the url before testing... - do_check_true(isHostInMozHosts(NetUtil.newURI("http://foo.mozilla.org"))); + do_check_true(isHostInMozHosts(NetUtil.newURI("http://foo.mozilla.org"), + urls[2].typed)); run_next_test(); } }); @@ -148,7 +155,29 @@ function test_bookmark_removal() do_check_false(isHostInMozHosts(newUri)); run_next_test(); }); +} +function test_moz_hosts_typed_update() +{ + const TEST_URI = NetUtil.newURI("http://typed.mozilla.com"); + let places = [{ uri: TEST_URI + , title: "test for " + TEST_URI.spec + , visits: [ new VisitInfo(TRANSITION_LINK) + , new VisitInfo(TRANSITION_TYPED) + ] + }]; + + gHistory.updatePlaces(places, { + handleResult: function () { + }, + handleError: function () { + do_throw("gHistory.updatePlaces() failed"); + }, + handleCompletion: function () { + do_check_true(isHostInMozHosts(TEST_URI, true)); + run_next_test(); + } + }); } //////////////////////////////////////////////////////////////////////////////// @@ -159,6 +188,7 @@ function test_bookmark_removal() test_remove_places, test_bookmark_changes, test_bookmark_removal, + test_moz_hosts_typed_update, ].forEach(add_test); function run_test() diff --git a/toolkit/components/remote/nsGTKRemoteService.cpp b/toolkit/components/remote/nsGTKRemoteService.cpp index 4c8c3c3026e..9da8f057a86 100644 --- a/toolkit/components/remote/nsGTKRemoteService.cpp +++ b/toolkit/components/remote/nsGTKRemoteService.cpp @@ -164,19 +164,20 @@ nsGTKRemoteService::Shutdown() // Set desktop startup ID to the passed ID, if there is one, so that any created // windows get created with the right window manager metadata, and any windows // that get new tabs and are activated also get the right WM metadata. -// If there is no desktop startup ID, then use the X event's timestamp -// for _NET_ACTIVE_WINDOW when the window gets focused or shown. +// The timestamp will be used if there is no desktop startup ID, or if we're +// raising an existing window rather than showing a new window for the first time. void nsGTKRemoteService::SetDesktopStartupIDOrTimestamp(const nsACString& aDesktopStartupID, PRUint32 aTimestamp) { nsGTKToolkit* toolkit = nsGTKToolkit::GetToolkit(); if (!toolkit) return; + if (!aDesktopStartupID.IsEmpty()) { toolkit->SetDesktopStartupID(aDesktopStartupID); - } else { - toolkit->SetFocusTimestamp(aTimestamp); } + + toolkit->SetFocusTimestamp(aTimestamp); } diff --git a/toolkit/components/telemetry/Telemetry.cpp b/toolkit/components/telemetry/Telemetry.cpp index e67bd740f39..6a2d3fadeaf 100644 --- a/toolkit/components/telemetry/Telemetry.cpp +++ b/toolkit/components/telemetry/Telemetry.cpp @@ -146,6 +146,27 @@ private: bool ShouldReflectHistogram(Histogram *h); void IdentifyCorruptHistograms(StatisticsRecorder::Histograms &hs); typedef StatisticsRecorder::Histograms::iterator HistogramIterator; + + struct AddonHistogramInfo { + PRUint32 min; + PRUint32 max; + PRUint32 bucketCount; + PRUint32 histogramType; + Histogram *h; + }; + typedef nsBaseHashtableET AddonHistogramEntryType; + typedef AutoHashtable AddonHistogramMapType; + typedef nsBaseHashtableET AddonEntryType; + typedef AutoHashtable AddonMapType; + struct AddonEnumeratorArgs { + JSContext *cx; + JSObject *obj; + }; + static bool AddonHistogramReflector(AddonHistogramEntryType *entry, + JSContext *cx, JSObject *obj); + static bool AddonReflector(AddonEntryType *entry, JSContext *cx, JSObject *obj); + AddonMapType mAddonMap; + // This is used for speedy string->Telemetry::ID conversions typedef nsBaseHashtableET CharPtrEntryType; typedef AutoHashtable HistogramMapType; @@ -612,6 +633,111 @@ TelemetryImpl::ShouldReflectHistogram(Histogram *h) } } +// Compute the name to pass into Histogram for the addon histogram +// 'name' from the addon 'id'. We can't use 'name' directly because it +// might conflict with other histograms in other addons or even with our +// own. +void +AddonHistogramName(const nsACString &id, const nsACString &name, + nsACString &ret) +{ + ret.Append(id); + ret.Append(NS_LITERAL_CSTRING(":")); + ret.Append(name); +} + +NS_IMETHODIMP +TelemetryImpl::RegisterAddonHistogram(const nsACString &id, + const nsACString &name, + PRUint32 min, PRUint32 max, + PRUint32 bucketCount, + PRUint32 histogramType) +{ + AddonEntryType *addonEntry = mAddonMap.GetEntry(id); + if (!addonEntry) { + addonEntry = mAddonMap.PutEntry(id); + if (NS_UNLIKELY(!addonEntry)) { + return NS_ERROR_OUT_OF_MEMORY; + } + addonEntry->mData = new AddonHistogramMapType(); + } + + AddonHistogramMapType *histogramMap = addonEntry->mData; + AddonHistogramEntryType *histogramEntry = histogramMap->GetEntry(name); + // Can't re-register the same histogram. + if (histogramEntry) { + return NS_ERROR_FAILURE; + } + + histogramEntry = histogramMap->PutEntry(name); + if (NS_UNLIKELY(!histogramEntry)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + AddonHistogramInfo &info = histogramEntry->mData; + info.min = min; + info.max = max; + info.bucketCount = bucketCount; + info.histogramType = histogramType; + + return NS_OK; +} + +NS_IMETHODIMP +TelemetryImpl::GetAddonHistogram(const nsACString &id, const nsACString &name, + JSContext *cx, jsval *ret) +{ + AddonEntryType *addonEntry = mAddonMap.GetEntry(id); + // The given id has not been registered. + if (!addonEntry) { + return NS_ERROR_INVALID_ARG; + } + + AddonHistogramMapType *histogramMap = addonEntry->mData; + AddonHistogramEntryType *histogramEntry = histogramMap->GetEntry(name); + // The given histogram name has not been registered. + if (!histogramEntry) { + return NS_ERROR_INVALID_ARG; + } + + AddonHistogramInfo &info = histogramEntry->mData; + Histogram *h; + if (info.h) { + h = info.h; + } else { + nsCAutoString actualName; + AddonHistogramName(id, name, actualName); + nsresult rv = HistogramGet(PromiseFlatCString(actualName).get(), + info.min, info.max, info.bucketCount, + info.histogramType, &h); + if (NS_FAILED(rv)) { + return rv; + } + // Don't let this histogram be reported via the normal means + // (e.g. Telemetry.registeredHistograms); we'll make it available in + // other ways. + h->ClearFlags(Histogram::kUmaTargetedHistogramFlag); + info.h = h; + } + return WrapAndReturnHistogram(h, cx, ret); +} + +NS_IMETHODIMP +TelemetryImpl::UnregisterAddonHistograms(const nsACString &id) +{ + AddonEntryType *addonEntry = mAddonMap.GetEntry(id); + if (addonEntry) { + // Histogram's destructor is private, so this is the best we can do. + // The histograms the addon created *will* stick around, but they + // will be deleted if and when the addon registers histograms with + // the same names. + delete addonEntry->mData; + mAddonMap.RemoveEntry(id); + } + + return NS_OK; +} + NS_IMETHODIMP TelemetryImpl::GetHistogramSnapshots(JSContext *cx, jsval *ret) { @@ -660,6 +786,73 @@ TelemetryImpl::GetHistogramSnapshots(JSContext *cx, jsval *ret) return NS_OK; } +bool +TelemetryImpl::AddonHistogramReflector(AddonHistogramEntryType *entry, + JSContext *cx, JSObject *obj) +{ + // Never even accessed the histogram. + if (!entry->mData.h) { + return true; + } + + JSObject *snapshot = JS_NewObject(cx, NULL, NULL, NULL); + js::AutoObjectRooter r(cx, snapshot); + switch (ReflectHistogramSnapshot(cx, snapshot, entry->mData.h)) { + case REFLECT_FAILURE: + case REFLECT_CORRUPT: + return false; + case REFLECT_OK: + const nsACString &histogramName = entry->GetKey(); + if (!JS_DefineProperty(cx, obj, + PromiseFlatCString(histogramName).get(), + OBJECT_TO_JSVAL(snapshot), NULL, NULL, + JSPROP_ENUMERATE)) { + return false; + } + break; + } + return true; +} + +bool +TelemetryImpl::AddonReflector(AddonEntryType *entry, + JSContext *cx, JSObject *obj) +{ + const nsACString &addonId = entry->GetKey(); + JSObject *subobj = JS_NewObject(cx, NULL, NULL, NULL); + if (!subobj) { + return false; + } + js::AutoObjectRooter r(cx, subobj); + + AddonHistogramMapType *map = entry->mData; + if (!(map->ReflectHashtable(AddonHistogramReflector, cx, subobj) + && JS_DefineProperty(cx, obj, + PromiseFlatCString(addonId).get(), + OBJECT_TO_JSVAL(subobj), NULL, NULL, + JSPROP_ENUMERATE))) { + return false; + } + return true; +} + +NS_IMETHODIMP +TelemetryImpl::GetAddonHistogramSnapshots(JSContext *cx, jsval *ret) +{ + *ret = JSVAL_VOID; + JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL); + if (!obj) { + return NS_ERROR_FAILURE; + } + js::AutoObjectRooter r(cx, obj); + + if (!mAddonMap.ReflectHashtable(AddonReflector, cx, obj)) { + return NS_ERROR_FAILURE; + } + *ret = OBJECT_TO_JSVAL(obj); + return NS_OK; +} + NS_IMETHODIMP TelemetryImpl::GetSlowSQL(JSContext *cx, jsval *ret) { diff --git a/toolkit/components/telemetry/TelemetryHistograms.h b/toolkit/components/telemetry/TelemetryHistograms.h index d3eb9200f3d..ba98645a537 100644 --- a/toolkit/components/telemetry/TelemetryHistograms.h +++ b/toolkit/components/telemetry/TelemetryHistograms.h @@ -81,6 +81,10 @@ HISTOGRAM_BOOLEAN(GC_IS_COMPARTMENTAL, "Is it a compartmental GC?") HISTOGRAM(GC_MS, 1, 10000, 50, EXPONENTIAL, "Time spent running JS GC (ms)") HISTOGRAM(GC_MARK_MS, 1, 10000, 50, EXPONENTIAL, "Time spent running JS GC mark phase (ms)") HISTOGRAM(GC_SWEEP_MS, 1, 10000, 50, EXPONENTIAL, "Time spent running JS GC sweep phase (ms)") +HISTOGRAM(GC_SLICE_MS, 1, 10000, 50, EXPONENTIAL, "Time spent running a JS GC slice (ms)") +HISTOGRAM(GC_MMU_50, 1, 100, 20, LINEAR, "Minimum percentage of time spent outside GC over any 50ms window") +HISTOGRAM_BOOLEAN(GC_RESET, "Was an incremental GC canceled?") +HISTOGRAM_BOOLEAN(GC_INCREMENTAL_DISABLED, "Is incremental GC permanently disabled?") HISTOGRAM(TELEMETRY_PING, 1, 3000, 10, EXPONENTIAL, "Time taken to submit telemetry info (ms)") HISTOGRAM_BOOLEAN(TELEMETRY_SUCCESS, "Successful telemetry submission") @@ -344,6 +348,15 @@ HISTOGRAM(FX_BOOKMARKS_TOOLBAR_INIT_MS, 50, 5000, 10, EXPONENTIAL, "Firefox: Tim HISTOGRAM(FX_THUMBNAILS_CAPTURE_TIME_MS, 1, 500, 15, EXPONENTIAL, "THUMBNAILS: Time (ms) it takes to capture a thumbnail") HISTOGRAM(FX_THUMBNAILS_STORE_TIME_MS, 1, 500, 15, EXPONENTIAL, "THUMBNAILS: Time (ms) it takes to store a thumbnail in the cache") HISTOGRAM(FX_THUMBNAILS_HIT_OR_MISS, 0, 1, 2, BOOLEAN, "THUMBNAILS: Thumbnail found") + +/** + * Session restore telemetry + */ +HISTOGRAM(FX_SESSION_RESTORE_COLLECT_DATA_MS, 1, 30000, 10, EXPONENTIAL, "Session restore: Time to collect all window and tab data (ms)") +HISTOGRAM(FX_SESSION_RESTORE_SERIALIZE_DATA_MS, 1, 1000, 10, EXPONENTIAL, "Session restore: Time to JSON serialize session data (ms)") +HISTOGRAM(FX_SESSION_RESTORE_READ_FILE_MS, 1, 3000, 10, EXPONENTIAL, "Session restore: Time to read the session data from the file on disk (ms)") +HISTOGRAM(FX_SESSION_RESTORE_WRITE_FILE_MS, 1, 3000, 10, EXPONENTIAL, "Session restore: Time to write the session data to the file on disk (ms)") +HISTOGRAM_BOOLEAN(FX_SESSION_RESTORE_CORRUPT_FILE, "Session restore: Whether the file read on startup contained parse-able JSON") // #endif HISTOGRAM_BOOLEAN(INNERWINDOWS_WITH_MUTATION_LISTENERS, "Deleted or to-be-reused innerwindow which has had mutation event listeners.") diff --git a/toolkit/components/telemetry/TelemetryPing.js b/toolkit/components/telemetry/TelemetryPing.js index 405a15b9688..4673b3011b3 100644 --- a/toolkit/components/telemetry/TelemetryPing.js +++ b/toolkit/components/telemetry/TelemetryPing.js @@ -1,3 +1,4 @@ +/* -*- indent-tabs-mode: nil -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -187,63 +188,83 @@ TelemetryPing.prototype = { _slowSQLStartup: {}, /** - * Returns a set of histograms that can be converted into JSON - * @return a snapshot of the histograms of form: - * { histogram_name: {range:[minvalue,maxvalue], bucket_count:, - * histogram_type: <0 for exponential, 1 for linear>, bucketX:countX, ....} ...} - * where bucket[XY], count[XY] are positive integers. + * When reflecting a histogram into JS, Telemetry hands us an object + * with the following properties: + * + * - min, max, histogram_type, sum: simple integers; + * - counts: array of counts for histogram buckets; + * - ranges: array of calculated bucket sizes. + * + * This format is not straightforward to read and potentially bulky + * with lots of zeros in the counts array. Packing histograms makes + * raw histograms easier to read and compresses the data a little bit. + * + * Returns an object: + * { range: [min, max], bucket_count: , + * histogram_type: , sum: + * values: { bucket1: count1, bucket2: count2, ... } } */ + packHistogram: function packHistogram(hgram) { + let r = hgram.ranges;; + let c = hgram.counts; + let retgram = { + range: [r[1], r[r.length - 1]], + bucket_count: r.length, + histogram_type: hgram.histogram_type, + values: {}, + sum: hgram.sum + }; + let first = true; + let last = 0; + + for (let i = 0; i < c.length; i++) { + let value = c[i]; + if (!value) + continue; + + // add a lower bound + if (i && first) { + retgram.values[r[i - 1]] = 0; + } + first = false; + last = i + 1; + retgram.values[r[i]] = value; + } + + // add an upper bound + if (last && last < c.length) + retgram.values[r[last]] = 0; + return retgram; + }, + getHistograms: function getHistograms() { let hls = Telemetry.histogramSnapshots; let info = Telemetry.registeredHistograms; let ret = {}; - function processHistogram(name, hgram) { - let r = hgram.ranges;; - let c = hgram.counts; - let retgram = { - range: [r[1], r[r.length - 1]], - bucket_count: r.length, - histogram_type: hgram.histogram_type, - values: {}, - sum: hgram.sum - }; - let first = true; - let last = 0; - - for (let i = 0; i < c.length; i++) { - let value = c[i]; - if (!value) - continue; - - // add a lower bound - if (i && first) { - first = false; - retgram.values[r[i - 1]] = 0; - } - first = false; - last = i + 1; - retgram.values[r[i]] = value; - } - - // add an upper bound - if (last && last < c.length) - retgram.values[r[last]] = 0; - ret[name] = retgram; - }; - for (let name in hls) { if (info[name]) { - processHistogram(name, hls[name]); + ret[name] = this.packHistogram(hls[name]); let startup_name = "STARTUP_" + name; if (hls[startup_name]) - processHistogram(startup_name, hls[startup_name]); + ret[startup_name] = this.packHistogram(hls[startup_name]); } } return ret; }, + getAddonHistograms: function getAddonHistograms() { + let ahs = Telemetry.addonHistogramSnapshots; + let ret = {}; + + for (let name in ahs) { + ret[name] = this.convertHistogram(ahs[name]); + } + + return ret; + }, + addValue: function addValue(name, id, val) { let h = this._histograms[name]; if (!h) { @@ -416,7 +437,8 @@ TelemetryPing.prototype = { info: this.getMetadata(reason), simpleMeasurements: getSimpleMeasurements(), histograms: this.getHistograms(), - slowSQL: Telemetry.slowSQL + slowSQL: Telemetry.slowSQL, + addonHistograms: this.getAddonHistograms() }; if (Object.keys(this._slowSQLStartup.mainThread).length || Object.keys(this._slowSQLStartup.otherThreads).length) { diff --git a/toolkit/components/telemetry/nsITelemetry.idl b/toolkit/components/telemetry/nsITelemetry.idl index c86a07c2932..0c0288eeed8 100644 --- a/toolkit/components/telemetry/nsITelemetry.idl +++ b/toolkit/components/telemetry/nsITelemetry.idl @@ -58,7 +58,7 @@ interface nsITelemetry : nsISupports * where data is consists of the following properties: * min - Minimal bucket size * max - Maximum bucket size - * histogram_type - HISTOGRAM_EXPONENTIAL or HISTOGRAM_LINEAR + * histogram_type - HISTOGRAM_EXPONENTIAL, HISTOGRAM_LINEAR, or HISTOGRAM_BOOLEAN * counts - array representing contents of the buckets in the histogram * ranges - an array with calculated bucket sizes * sum - sum of the bucket contents @@ -94,13 +94,13 @@ interface nsITelemetry : nsISupports readonly attribute jsval registeredHistograms; /** - * Create and return a histogram where bucket sizes increase exponentially. Parameters: + * Create and return a histogram. Parameters: * * @param name Unique histogram name * @param min - Minimal bucket size * @param max - Maximum bucket size * @param bucket_count - number of buckets in the histogram. - * @param type - HISTOGRAM_EXPONENTIAL or HISTOGRAM_LINEAR + * @param type - HISTOGRAM_EXPONENTIAL, HISTOGRAM_LINEAR or HISTOGRAM_BOOLEAN * The returned object has the following functions: * add(int) - Adds an int value to the appropriate bucket * snapshot() - Returns a snapshot of the histogram with the same data fields as in histogramSnapshots() @@ -136,4 +136,56 @@ interface nsITelemetry : nsISupports * A flag indicating whether Telemetry can submit official results. */ readonly attribute boolean canSend; + + /** Addon telemetry hooks */ + + /** + * Register a histogram for an addon. Throws an error if the + * histogram name has been registered previously. + * + * @param addon_id - Unique ID of the addon + * @param name - Unique histogram name + * @param min - Minimal bucket size + * @param max - Maximum bucket size + * @param bucket_count - number of buckets in the histogram + * @param histogram_type - HISTOGRAM_EXPONENTIAL, HISTOGRAM_LINEAR, or + * HISTOGRAM_BOOLEAN + */ + void registerAddonHistogram(in ACString addon_id, in ACString name, + in PRUint32 min, in PRUint32 max, + in PRUint32 bucket_count, + in unsigned long histogram_type); + + /** + * Return a histogram previously registered via + * registerAddonHistogram. Throws an error if the id/name combo has + * not been registered via registerAddonHistogram. + * + * @param addon_id - Unique ID of the addon + * @param name - Registered histogram name + * + * The returned object has the same functions as a histogram returned + * from newHistogram. + */ + [implicit_jscontext] + jsval getAddonHistogram(in ACString addon_id, in ACString name); + + /** + * Delete all histograms associated with the given addon id. + * + * @param addon_id - Unique ID of the addon + */ + void unregisterAddonHistograms(in ACString addon_id); + + /** + * An object containing a snapshot from all of the currently + * registered addon histograms. + * { addon-id1 : data1, ... } + * + * where data is an object whose properties are the names of the + * addon's histograms and whose corresponding values are as in + * histogramSnapshots. + */ + [implicit_jscontext] + readonly attribute jsval addonHistogramSnapshots; }; diff --git a/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js b/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js index e0711d7cedd..e7eca13bf57 100644 --- a/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js +++ b/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js @@ -52,6 +52,17 @@ function expect_fail(f) { do_check_true(failed); } +function expect_success(f) { + let succeeded = false; + try { + f(); + succeeded = true; + } catch (e) { + succeeded = false; + } + do_check_true(succeeded); +} + function test_boolean_histogram() { var h = Telemetry.newHistogram("test::boolean histogram", 99,1,4, Telemetry.HISTOGRAM_BOOLEAN); @@ -93,6 +104,78 @@ function test_getSlowSQL() { do_check_true(("mainThread" in slow) && ("otherThreads" in slow)); } +function test_addons() { + var addon_id = "testing-addon"; + var fake_addon_id = "fake-addon"; + var name1 = "testing-histogram1"; + var register = Telemetry.registerAddonHistogram; + expect_success(function () + register(addon_id, name1, 1, 5, 6, Telemetry.HISTOGRAM_LINEAR)); + // Can't register the same histogram multiple times. + expect_fail(function () + register(addon_id, name1, 1, 5, 6, Telemetry.HISTOGRAM_LINEAR)); + // Make sure we can't get at it with another name. + expect_fail(function () Telemetry.getAddonHistogram(fake_addon_id, name1)); + + // Check for reflection capabilities. + var h1 = Telemetry.getAddonHistogram(addon_id, name1); + h1.add(1); + h1.add(3); + var s1 = h1.snapshot(); + do_check_eq(s1.histogram_type, Telemetry.HISTOGRAM_LINEAR); + do_check_eq(s1.min, 1); + do_check_eq(s1.max, 5); + do_check_eq(s1.counts[1], 1); + do_check_eq(s1.counts[3], 1); + + var name2 = "testing-histogram2"; + expect_success(function () + register(addon_id, name2, 2, 4, 4, Telemetry.HISTOGRAM_LINEAR)); + + var h2 = Telemetry.getAddonHistogram(addon_id, name2); + h2.add(2); + h2.add(3); + var s2 = h2.snapshot(); + do_check_eq(s2.histogram_type, Telemetry.HISTOGRAM_LINEAR); + do_check_eq(s2.min, 2); + do_check_eq(s2.max, 4); + do_check_eq(s2.counts[1], 1); + do_check_eq(s2.counts[2], 1); + + // Check that we can register histograms for a different addon with + // identical names. + var extra_addon = "testing-extra-addon"; + expect_success(function () + register(extra_addon, name1, 0, 1, 2, Telemetry.HISTOGRAM_BOOLEAN)); + + // Check that we reflect registered addons and histograms. + snapshots = Telemetry.addonHistogramSnapshots; + do_check_true(addon_id in snapshots) + do_check_true(extra_addon in snapshots); + + // Check that we have data for our created histograms. + do_check_true(name1 in snapshots[addon_id]); + do_check_true(name2 in snapshots[addon_id]); + var s1_alt = snapshots[addon_id][name1]; + var s2_alt = snapshots[addon_id][name2]; + do_check_eq(s1_alt.min, s1.min); + do_check_eq(s1_alt.max, s1.max); + do_check_eq(s1_alt.histogram_type, s1.histogram_type); + do_check_eq(s2_alt.min, s2.min); + do_check_eq(s2_alt.max, s2.max); + do_check_eq(s2_alt.histogram_type, s2.histogram_type); + + // Even though we've registered it, it shouldn't show up until data is added to it. + do_check_false(name1 in snapshots[extra_addon]); + + // Check that we can remove addon histograms. + Telemetry.unregisterAddonHistograms(addon_id); + snapshots = Telemetry.addonHistogramSnapshots; + do_check_false(addon_id in snapshots); + // Make sure other addons are unaffected. + do_check_true(extra_addon in snapshots); +} + // Check that telemetry doesn't record in private mode function test_privateMode() { var h = Telemetry.newHistogram("test::private_mode_boolean", 1,2,3, Telemetry.HISTOGRAM_BOOLEAN); @@ -121,4 +204,5 @@ function run_test() test_getHistogramById(); test_getSlowSQL(); test_privateMode(); + test_addons(); } diff --git a/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp index 8db15d0da6c..f62f165ce7d 100644 --- a/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp +++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp @@ -109,7 +109,8 @@ nsTypeAheadFind::nsTypeAheadFind(): mStartLinksOnlyPref(false), mCaretBrowsingOn(false), mLastFindLength(0), - mIsSoundInitialized(false) + mIsSoundInitialized(false), + mCaseSensitive(false) { } @@ -129,8 +130,7 @@ nsTypeAheadFind::Init(nsIDocShell* aDocShell) mSearchRange = new nsRange(); mStartPointRange = new nsRange(); mEndPointRange = new nsRange(); - mFind = do_CreateInstance(NS_FIND_CONTRACTID); - if (!prefInternal || !mFind) + if (!prefInternal || !EnsureFind()) return NS_ERROR_FAILURE; SetDocShell(aDocShell); @@ -142,10 +142,6 @@ nsTypeAheadFind::Init(nsIDocShell* aDocShell) // ----------- Get initial preferences ---------- PrefsReset(); - // ----------- Set search options --------------- - mFind->SetCaseSensitive(false); - mFind->SetWordBreaker(nsnull); - return rv; } @@ -176,14 +172,20 @@ nsTypeAheadFind::PrefsReset() NS_IMETHODIMP nsTypeAheadFind::SetCaseSensitive(bool isCaseSensitive) { - mFind->SetCaseSensitive(isCaseSensitive); + mCaseSensitive = isCaseSensitive; + + if (mFind) { + mFind->SetCaseSensitive(mCaseSensitive); + } + return NS_OK; } NS_IMETHODIMP nsTypeAheadFind::GetCaseSensitive(bool* isCaseSensitive) { - mFind->GetCaseSensitive(isCaseSensitive); + *isCaseSensitive = mCaseSensitive; + return NS_OK; } @@ -202,6 +204,7 @@ nsTypeAheadFind::SetDocShell(nsIDocShell* aDocShell) mStartFindRange = nsnull; mStartPointRange = new nsRange(); mSearchRange = new nsRange(); + mEndPointRange = new nsRange(); mFoundLink = nsnull; mFoundEditable = nsnull; @@ -209,6 +212,8 @@ nsTypeAheadFind::SetDocShell(nsIDocShell* aDocShell) mSelectionController = nsnull; + mFind = nsnull; + return NS_OK; } @@ -393,7 +398,7 @@ nsTypeAheadFind::FindItNow(nsIPresShell *aPresShell, bool aIsLinksOnly, // No need to wrap find in doc if starting at beginning bool hasWrapped = (rangeCompareResult < 0); - if (mTypeAheadBuffer.IsEmpty()) + if (mTypeAheadBuffer.IsEmpty() || !EnsureFind()) return NS_ERROR_FAILURE; mFind->SetFindBackwards(aFindPrev); diff --git a/toolkit/components/typeaheadfind/nsTypeAheadFind.h b/toolkit/components/typeaheadfind/nsTypeAheadFind.h index 6fe1cfeea46..2c360a1c878 100644 --- a/toolkit/components/typeaheadfind/nsTypeAheadFind.h +++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.h @@ -128,6 +128,25 @@ protected: // Cached useful interfaces nsCOMPtr mFind; + + bool mCaseSensitive; + + bool EnsureFind() { + if (mFind) { + return true; + } + + mFind = do_CreateInstance("@mozilla.org/embedcomp/rangefind;1"); + if (!mFind) { + return false; + } + + mFind->SetCaseSensitive(mCaseSensitive); + mFind->SetWordBreaker(nsnull); + + return true; + } + nsCOMPtr mWebBrowserFind; // The focused content window that we're listening to and its cached objects diff --git a/toolkit/components/url-classifier/Classifier.cpp b/toolkit/components/url-classifier/Classifier.cpp index 51231f16288..d0afcd7b3c7 100644 --- a/toolkit/components/url-classifier/Classifier.cpp +++ b/toolkit/components/url-classifier/Classifier.cpp @@ -531,6 +531,7 @@ Classifier::ApplyTableUpdates(nsTArray* aUpdates, NS_ENSURE_SUCCESS(rv, rv); rv = store->AugmentAdds(AddPrefixHashes); NS_ENSURE_SUCCESS(rv, rv); + AddPrefixHashes.Clear(); uint32 applied = 0; bool updateFreshness = false; diff --git a/toolkit/components/url-classifier/HashStore.h b/toolkit/components/url-classifier/HashStore.h index fca2d2aa27c..e832cb9e35b 100644 --- a/toolkit/components/url-classifier/HashStore.h +++ b/toolkit/components/url-classifier/HashStore.h @@ -120,10 +120,10 @@ public: ChunkSet& AddChunks() { return mAddChunks; } ChunkSet& SubChunks() { return mSubChunks; } - const AddPrefixArray& AddPrefixes() const { return mAddPrefixes; } - const AddCompleteArray& AddCompletes() const { return mAddCompletes; } - const SubPrefixArray& SubPrefixes() const { return mSubPrefixes; } - const SubCompleteArray& SubCompletes() const { return mSubCompletes; } + AddPrefixArray& AddPrefixes() { return mAddPrefixes; } + AddCompleteArray& AddCompletes() { return mAddCompletes; } + SubPrefixArray& SubPrefixes() { return mSubPrefixes; } + SubCompleteArray& SubCompletes() { return mSubCompletes; } // ======= // Updates diff --git a/toolkit/components/url-classifier/LookupCache.cpp b/toolkit/components/url-classifier/LookupCache.cpp index 9702385b68a..ef70f7b568e 100644 --- a/toolkit/components/url-classifier/LookupCache.cpp +++ b/toolkit/components/url-classifier/LookupCache.cpp @@ -171,26 +171,27 @@ LookupCache::Reset() nsresult -LookupCache::Build(const AddPrefixArray& aAddPrefixes, - const AddCompleteArray& aAddCompletes) +LookupCache::Build(AddPrefixArray& aAddPrefixes, + AddCompleteArray& aAddCompletes) { + Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LC_COMPLETIONS, + static_cast(aAddCompletes.Length())); + mCompletions.Clear(); mCompletions.SetCapacity(aAddCompletes.Length()); for (uint32 i = 0; i < aAddCompletes.Length(); i++) { mCompletions.AppendElement(aAddCompletes[i].CompleteHash()); } + aAddCompletes.Clear(); mCompletions.Sort(); - Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LC_COMPLETIONS, - static_cast(mCompletions.Length())); + Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LC_PREFIXES, + static_cast(aAddPrefixes.Length())); nsresult rv = ConstructPrefixSet(aAddPrefixes); NS_ENSURE_SUCCESS(rv, rv); mPrimed = true; - Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LC_PREFIXES, - static_cast(aAddPrefixes.Length())); - return NS_OK; } @@ -679,18 +680,22 @@ bool LookupCache::IsPrimed() } nsresult -LookupCache::ConstructPrefixSet(const AddPrefixArray& aAddPrefixes) +LookupCache::ConstructPrefixSet(AddPrefixArray& aAddPrefixes) { Telemetry::AutoTimer timer; - nsTArray array; - array.SetCapacity(aAddPrefixes.Length()); + FallibleTArray array; + nsresult rv; + if (!array.SetCapacity(aAddPrefixes.Length())) { + rv = NS_ERROR_OUT_OF_MEMORY; + goto error_bailout; + } for (uint32 i = 0; i < aAddPrefixes.Length(); i++) { array.AppendElement(aAddPrefixes[i].PrefixHash().ToUint32()); } + aAddPrefixes.Clear(); - // clear old tree if (array.IsEmpty()) { // DB is empty, but put a sentinel to show that we looked array.AppendElement(0); @@ -699,7 +704,7 @@ LookupCache::ConstructPrefixSet(const AddPrefixArray& aAddPrefixes) array.Sort(); // construct new one, replace old entries - nsresult rv = mPrefixSet->SetPrefixes(array.Elements(), array.Length()); + rv = mPrefixSet->SetPrefixes(array.Elements(), array.Length()); if (NS_FAILED(rv)) { goto error_bailout; } diff --git a/toolkit/components/url-classifier/LookupCache.h b/toolkit/components/url-classifier/LookupCache.h index 5ba07d5fe09..ba50701b1a9 100644 --- a/toolkit/components/url-classifier/LookupCache.h +++ b/toolkit/components/url-classifier/LookupCache.h @@ -137,8 +137,9 @@ public: nsresult Init(); nsresult Open(); - nsresult Build(const AddPrefixArray& aAddPrefixes, - const AddCompleteArray& aAddCompletes); + // This will Clear() the passed arrays when done. + nsresult Build(AddPrefixArray& aAddPrefixes, + AddCompleteArray& aAddCompletes); nsresult GetPrefixes(nsTArray* aAddPrefixes); #if DEBUG && defined(PR_LOGGING) @@ -160,9 +161,10 @@ private: nsresult ReadHeader(); nsresult EnsureSizeConsistent(); nsresult ReadCompletions(); - // Construct a Prefix Set with known prefixes nsresult LoadPrefixSet(); - nsresult ConstructPrefixSet(const AddPrefixArray& aAddPrefixes); + // Construct a Prefix Set with known prefixes. + // This will Clear() aAddPrefixes when done. + nsresult ConstructPrefixSet(AddPrefixArray& aAddPrefixes); struct Header { uint32 magic; diff --git a/toolkit/content/aboutSupport.js b/toolkit/content/aboutSupport.js index 33a9bb8671d..1e648dad65d 100644 --- a/toolkit/content/aboutSupport.js +++ b/toolkit/content/aboutSupport.js @@ -116,6 +116,7 @@ window.onload = function () { populatePreferencesSection(); populateExtensionsSection(); populateGraphicsSection(); + populateJavaScriptSection(); } function populateExtensionsSection() { @@ -382,6 +383,13 @@ function populateGraphicsSection() { ]); } +function populateJavaScriptSection() { + let enabled = window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils) + .isIncrementalGCEnabled(); + document.getElementById("javascript-incremental-gc").textContent = enabled ? "1" : "0"; +} + function getPrefValue(aName) { let value = ""; let type = Services.prefs.getPrefType(aName); diff --git a/toolkit/content/aboutSupport.xhtml b/toolkit/content/aboutSupport.xhtml index fd02b55cae6..e92194bdcc6 100644 --- a/toolkit/content/aboutSupport.xhtml +++ b/toolkit/content/aboutSupport.xhtml @@ -243,6 +243,24 @@ + +

+ &aboutSupport.jsTitle; +

+ + + + + + + + + +
+ &aboutSupport.jsIncrementalGC; + +
+ diff --git a/toolkit/content/tests/browser/Makefile.in b/toolkit/content/tests/browser/Makefile.in index 37c978860d6..b2e977544f1 100644 --- a/toolkit/content/tests/browser/Makefile.in +++ b/toolkit/content/tests/browser/Makefile.in @@ -56,6 +56,7 @@ _BROWSER_TEST_FILES = \ browser_bug594509.js \ browser_Geometry.js \ browser_save_resend_postdata.js \ + browser_browserDrop.js \ browser_Services.js \ $(NULL) diff --git a/toolkit/content/tests/browser/browser_browserDrop.js b/toolkit/content/tests/browser/browser_browserDrop.js new file mode 100644 index 00000000000..10af83461b7 --- /dev/null +++ b/toolkit/content/tests/browser/browser_browserDrop.js @@ -0,0 +1,61 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +function test() { + waitForExplicitFinish(); + + let newTab = gBrowser.selectedTab = gBrowser.addTab(); + registerCleanupFunction(function () { + gBrowser.removeTab(newTab); + }); + + let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]. + getService(Ci.mozIJSSubScriptLoader); + let chromeUtils = {}; + scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", chromeUtils); + + let browser = gBrowser.selectedBrowser; + + var linkHandlerActivated = 0; + // Don't worry about clobbering the droppedLinkHandler, since we're closing + // this tab after the test anyways + browser.droppedLinkHandler = function dlh(e, url, name) { + linkHandlerActivated++; + ok(!/(javascript|data)/i.test(url), "javascript link should not be dropped"); + } + + var receivedDropCount = 0; + function dropListener() { + receivedDropCount++; + if (receivedDropCount == triggeredDropCount) { + // Wait for the browser's system-phase event handler to run. + executeSoon(function () { + is(linkHandlerActivated, validDropCount, + "link handler was called correct number of times"); + finish(); + }) + } + } + browser.addEventListener("drop", dropListener, false); + registerCleanupFunction(function () { + browser.removeEventListener("drop", dropListener, false); + }); + + var triggeredDropCount = 0; + var validDropCount = 0; + function drop(text, valid) { + triggeredDropCount++; + if (valid) + validDropCount++; + executeSoon(function () { + chromeUtils.synthesizeDrop(browser, browser, [[{type: "text/plain", data: text}]], "copy", window, EventUtils); + }); + } + + drop("mochi.test/first", true); + drop("javascript:'bad'"); + drop("jAvascript:'also bad'"); + drop("mochi.test/second", true); + drop("data:text/html,bad"); + drop("mochi.test/third", true); +} diff --git a/toolkit/content/tests/chrome/Makefile.in b/toolkit/content/tests/chrome/Makefile.in index 5e5fd4d640b..6bf70985530 100644 --- a/toolkit/content/tests/chrome/Makefile.in +++ b/toolkit/content/tests/chrome/Makefile.in @@ -87,6 +87,7 @@ _TEST_FILES = findbar_window.xul \ test_autocomplete2.xul \ test_autocomplete3.xul \ test_autocomplete4.xul \ + test_autocomplete5.xul \ test_autocomplete_delayOnPaste.xul \ test_keys.xul \ window_keys.xul \ diff --git a/toolkit/content/tests/chrome/test_autocomplete5.xul b/toolkit/content/tests/chrome/test_autocomplete5.xul new file mode 100644 index 00000000000..855148dadcd --- /dev/null +++ b/toolkit/content/tests/chrome/test_autocomplete5.xul @@ -0,0 +1,151 @@ + + + + + + + + + +

+

+ +
+
+ + +
diff --git a/toolkit/content/widgets/autocomplete.xml b/toolkit/content/widgets/autocomplete.xml index 107b95a2fe3..5e31d8a35e5 100644 --- a/toolkit/content/widgets/autocomplete.xml +++ b/toolkit/content/widgets/autocomplete.xml @@ -91,9 +91,19 @@ null false + null + null + null + null + @@ -249,13 +260,16 @@ else this.removeAttribute("nomatch"); - this.fireEvent("searchcomplete"); + if (this._searchCompleteHandler) + this._searchCompleteHandler(); ]]> @@ -263,7 +277,8 @@ @@ -441,18 +456,14 @@ - + diff --git a/toolkit/content/widgets/browser.xml b/toolkit/content/widgets/browser.xml index e81e2c6c2e3..ceaa3e1a5fa 100644 --- a/toolkit/content/widgets/browser.xml +++ b/toolkit/content/widgets/browser.xml @@ -1299,15 +1299,12 @@ let linkHandler = Components.classes["@mozilla.org/content/dropped-link-handler;1"]. getService(Components.interfaces.nsIDroppedLinkHandler); try { - var uri = linkHandler.dropLink(event, name); + // Pass true to prevent the dropping of javascript:/data: URIs + var uri = linkHandler.dropLink(event, name, true); } catch (ex) { return; } - // don't allow dropping javascript or data urls - if (/^\s*(javascript|data):/.test(uri)) - return; - if (uri) { this.droppedLinkHandler(event, uri, name.value); } diff --git a/toolkit/content/widgets/colorpicker.xml b/toolkit/content/widgets/colorpicker.xml index 5d37202c65c..3eda7d537e7 100644 --- a/toolkit/content/widgets/colorpicker.xml +++ b/toolkit/content/widgets/colorpicker.xml @@ -445,7 +445,7 @@ return this.getAttribute("color"); ]]> diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc index 54a529a2923..5946256c20d 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc @@ -190,14 +190,18 @@ bool ExceptionHandler::InstallHandlers() { // such a small stack. static const unsigned kSigStackSize = 8192; - signal_stack = malloc(kSigStackSize); stack_t stack; - memset(&stack, 0, sizeof(stack)); - stack.ss_sp = signal_stack; - stack.ss_size = kSigStackSize; + // Only set an alternative stack if there isn't already one, or if the current + // one is too small. + if (sys_sigaltstack(NULL, &stack) == -1 || !stack.ss_sp || + stack.ss_size < kSigStackSize) { + memset(&stack, 0, sizeof(stack)); + stack.ss_sp = malloc(kSigStackSize); + stack.ss_size = kSigStackSize; - if (sys_sigaltstack(&stack, NULL) == -1) - return false; + if (sys_sigaltstack(&stack, NULL) == -1) + return false; + } struct sigaction sa; memset(&sa, 0, sizeof(sa)); diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h index 355e8a63a93..60e69ef2c62 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h @@ -259,7 +259,6 @@ class ExceptionHandler { const char* next_minidump_id_c_; const bool handler_installed_; - void* signal_stack; // the handler stack. HandlerCallback crash_handler_; // The global exception handler stack. This is need becuase there may exist diff --git a/toolkit/devtools/debugger/server/dbg-script-actors.js b/toolkit/devtools/debugger/server/dbg-script-actors.js index e11211a4541..3238f75afa9 100644 --- a/toolkit/devtools/debugger/server/dbg-script-actors.js +++ b/toolkit/devtools/debugger/server/dbg-script-actors.js @@ -120,6 +120,10 @@ ThreadActor.prototype = { }, disconnect: function TA_disconnect() { + if (this._state == "paused") { + this.onResume(); + } + this._state = "exited"; if (this.dbg) { this.dbg.enabled = false; diff --git a/toolkit/library/Makefile.in b/toolkit/library/Makefile.in index 8162e70ea21..2b8e9fd8596 100644 --- a/toolkit/library/Makefile.in +++ b/toolkit/library/Makefile.in @@ -398,9 +398,6 @@ OS_LIBS += \ -lbinder \ -lsensorservice \ $(NULL) -ifdef MOZ_B2G_BT -OS_LIBS += -lbluedroid -endif endif EXTRA_DEPS += \ diff --git a/toolkit/locales/en-US/chrome/global/aboutSupport.dtd b/toolkit/locales/en-US/chrome/global/aboutSupport.dtd index da50829c6e2..bfe1095f813 100644 --- a/toolkit/locales/en-US/chrome/global/aboutSupport.dtd +++ b/toolkit/locales/en-US/chrome/global/aboutSupport.dtd @@ -44,6 +44,9 @@ variant of aboutSupport.showDir.label. --> + + + diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index 117491b4745..e8375cd6137 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -2066,8 +2066,27 @@ AndroidBridge::HideSurface(jobject surface) } -/* void takeScreenshot (in nsIDOMWindow win, in PRInt32 srcX, in PRInt32 srcY, in PRInt32 srcW, in PRInt32 srcH, in PRInt32 dstX, in PRInt32 dstY, in PRInt32 dstW, in PRInt32 dstH, in AString color); */ -NS_IMETHODIMP nsAndroidBridge::TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX, PRInt32 srcY, PRInt32 srcW, PRInt32 srcH, PRInt32 dstW, PRInt32 dstH, PRInt32 tabId) +/* attribute nsIAndroidBrowserApp browserApp; */ +NS_IMETHODIMP nsAndroidBridge::GetBrowserApp(nsIAndroidBrowserApp * *aBrowserApp) +{ + if (nsAppShell::gAppShell) + nsAppShell::gAppShell->GetBrowserApp(aBrowserApp); + return NS_OK; +} +NS_IMETHODIMP nsAndroidBridge::SetBrowserApp(nsIAndroidBrowserApp *aBrowserApp) +{ + if (nsAppShell::gAppShell) + nsAppShell::gAppShell->SetBrowserApp(aBrowserApp); + return NS_OK; +} + +extern "C" +__attribute__ ((visibility("default"))) +jobject JNICALL +Java_org_mozilla_gecko_GeckoAppShell_allocateDirectBuffer(JNIEnv *jenv, jclass, jlong size); + + +nsresult AndroidBridge::TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX, PRInt32 srcY, PRInt32 srcW, PRInt32 srcH, PRInt32 dstW, PRInt32 dstH, PRInt32 tabId) { nsCOMPtr win = do_QueryInterface(window); if (!win) @@ -2088,22 +2107,23 @@ NS_IMETHODIMP nsAndroidBridge::TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX nsPresContext::CSSPixelsToAppUnits(srcW), nsPresContext::CSSPixelsToAppUnits(srcH)); - nsRefPtr surf = new gfxImageSurface(nsIntSize(dstW, dstH), gfxASurface::ImageFormatRGB16_565); + JNIEnv* jenv = AndroidBridge::GetJNIEnv(); + if (!jenv) + return NS_OK; + + PRUint32 stride = dstW * 2; + PRUint32 bufferSize = dstH * stride; + + jobject buffer = Java_org_mozilla_gecko_GeckoAppShell_allocateDirectBuffer(jenv, NULL, bufferSize); + if (!buffer) + return NS_OK; + + void* data = jenv->GetDirectBufferAddress(buffer); + nsRefPtr surf = new gfxImageSurface(static_cast(data), nsIntSize(dstW, dstH), stride, gfxASurface::ImageFormatRGB16_565); nsRefPtr context = new gfxContext(surf); nsresult rv = presShell->RenderDocument(r, renderDocFlags, bgColor, context); NS_ENSURE_SUCCESS(rv, rv); - AndroidBridge::Bridge()->NotifyScreenshot(surf->Data(), surf->GetDataSize(), tabId, dstW, dstH); + AndroidBridge::AutoLocalJNIFrame jniFrame(jenv, 1); + jenv->CallStaticVoidMethod(AndroidBridge::Bridge()->mGeckoAppShellClass, AndroidBridge::Bridge()->jNotifyScreenShot, buffer, tabId, dstW, dstH); return NS_OK; } - -void AndroidBridge::NotifyScreenshot(unsigned char* data, int size, int tabId, int width, int height) -{ - JNIEnv* jenv = GetJNIEnv(); - if (!jenv) - return; - AutoLocalJNIFrame jniFrame(jenv, 1); - jobject buffer = jenv->NewDirectByteBuffer(data, size); - if (!buffer) - return; - jenv->CallStaticVoidMethod(mGeckoAppShellClass, jNotifyScreenShot, buffer, tabId, width, height); -} diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index 9fba2d9d7af..0b3cd8e6e62 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -171,7 +171,7 @@ public: static void NotifyIMEChange(const PRUnichar *aText, PRUint32 aTextLen, int aStart, int aEnd, int aNewEnd); - void NotifyScreenshot(unsigned char* data, int size, int tabId, int width, int height); + nsresult TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX, PRInt32 srcY, PRInt32 srcW, PRInt32 srcH, PRInt32 dstW, PRInt32 dstH, PRInt32 tabId); void AcknowledgeEventSync(); diff --git a/widget/android/AndroidJavaWrappers.cpp b/widget/android/AndroidJavaWrappers.cpp index 801eaefb040..ced9ccbdc11 100644 --- a/widget/android/AndroidJavaWrappers.cpp +++ b/widget/android/AndroidJavaWrappers.cpp @@ -616,6 +616,11 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj) break; } + case SCREENSHOT: { + mMetaState = jenv->GetIntField(jobj, jMetaStateField); + ReadPointArray(mPoints, jenv, jPoints, 2); + } + default: break; } diff --git a/widget/android/AndroidJavaWrappers.h b/widget/android/AndroidJavaWrappers.h index 57b8e2c46ff..f096a132e32 100644 --- a/widget/android/AndroidJavaWrappers.h +++ b/widget/android/AndroidJavaWrappers.h @@ -652,6 +652,7 @@ public: NETWORK_CHANGED = 22, PROXIMITY_EVENT = 23, ACTIVITY_RESUMING = 24, + SCREENSHOT = 25, dummy_java_enum_list_end }; diff --git a/widget/android/nsAppShell.cpp b/widget/android/nsAppShell.cpp index e7e570ef7ea..37192c2c24a 100644 --- a/widget/android/nsAppShell.cpp +++ b/widget/android/nsAppShell.cpp @@ -95,7 +95,8 @@ nsAppShell::nsAppShell() mCondLock("nsAppShell.mCondLock"), mQueueCond(mCondLock, "nsAppShell.mQueueCond"), mNumDraws(0), - mNumViewports(0) + mNumViewports(0), + mPendingOrientationEvents(false) { gAppShell = this; } @@ -342,6 +343,7 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) -curEvent->Alpha(), curEvent->Beta(), curEvent->Gamma()); + mPendingOrientationEvents = false; break; case AndroidGeckoEvent::LOCATION_EVENT: { @@ -434,6 +436,23 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) break; } + case AndroidGeckoEvent::SCREENSHOT: { + if (!mBrowserApp) + break; + + AndroidBridge* bridge = AndroidBridge::Bridge(); + if (!bridge) + break; + + nsCOMPtr domWindow; + mBrowserApp->GetWindowForTab(curEvent->MetaState(), getter_AddRefs(domWindow)); + nsTArray points = curEvent->Points(); + NS_ASSERTION(points.Length() != 2, "Screenshot event does not have enough coordinates"); + if (domWindow) + bridge->TakeScreenshot(domWindow, 0, 0, points[0].x, points[0].y, points[1].x, points[1].y, curEvent->MetaState()); + break; + } + case AndroidGeckoEvent::VIEWPORT: case AndroidGeckoEvent::BROADCAST: { @@ -572,6 +591,10 @@ nsAppShell::PostEvent(AndroidGeckoEvent *ae) delete event; } } + } else if (ae->Type() == AndroidGeckoEvent::ORIENTATION_EVENT) { + if (!mPendingOrientationEvents) + mEventQueue.AppendElement(ae); + mPendingOrientationEvents = true; } else { mEventQueue.AppendElement(ae); } diff --git a/widget/android/nsAppShell.h b/widget/android/nsAppShell.h index 80dfa530a98..ef43ae356e2 100644 --- a/widget/android/nsAppShell.h +++ b/widget/android/nsAppShell.h @@ -45,6 +45,7 @@ #include "nsCOMPtr.h" #include "nsTArray.h" #include "nsInterfaceHashtable.h" +#include "nsIAndroidBridge.h" namespace mozilla { class AndroidGeckoEvent; @@ -84,6 +85,14 @@ public: void NotifyObservers(nsISupports *aSupports, const char *aTopic, const PRUnichar *aData); void ResendLastResizeEvent(nsWindow* aDest); + void SetBrowserApp(nsIAndroidBrowserApp* aBrowserApp) { + mBrowserApp = aBrowserApp; + } + + void GetBrowserApp(nsIAndroidBrowserApp* *aBrowserApp) { + *aBrowserApp = mBrowserApp; + } + protected: virtual void ScheduleNativeEventCallback(); virtual ~nsAppShell(); @@ -99,6 +108,9 @@ protected: mozilla::AndroidGeckoEvent *PopNextEvent(); mozilla::AndroidGeckoEvent *PeekNextEvent(); + + nsCOMPtr mBrowserApp; + bool mPendingOrientationEvents; }; #endif // nsAppShell_h__ diff --git a/widget/android/nsIAndroidBridge.idl b/widget/android/nsIAndroidBridge.idl index c1636f863f6..433f476ad03 100644 --- a/widget/android/nsIAndroidBridge.idl +++ b/widget/android/nsIAndroidBridge.idl @@ -12,11 +12,15 @@ interface nsIAndroidDrawMetadataProvider : nsISupports { boolean paintingSuppressed(); }; +[scriptable, uuid(d10377b4-1c90-493a-a532-63cb3f16ee2b)] +interface nsIAndroidBrowserApp : nsISupports { + nsIDOMWindow getWindowForTab(in PRInt32 tabId); +}; + [scriptable, uuid(7dd8441a-4f38-49b2-bd90-da69d02a96cf)] interface nsIAndroidBridge : nsISupports { AString handleGeckoMessage(in AString message); void setDrawMetadataProvider(in nsIAndroidDrawMetadataProvider provider); - void takeScreenshot(in nsIDOMWindow win, in PRInt32 srcX, in PRInt32 srcY, in PRInt32 srcW, in PRInt32 srcH, - in PRInt32 dstW, in PRInt32 dstH, in PRInt32 tabId); + attribute nsIAndroidBrowserApp browserApp; }; diff --git a/widget/cocoa/nsCocoaWindow.h b/widget/cocoa/nsCocoaWindow.h index b114319611a..f01aebe2697 100644 --- a/widget/cocoa/nsCocoaWindow.h +++ b/widget/cocoa/nsCocoaWindow.h @@ -197,6 +197,21 @@ typedef struct _nsCocoaWindowList { - (void)setDrawsContentsIntoWindowFrame:(BOOL)aState; @end +#if !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7) +enum { + NSWindowAnimationBehaviorDefault = 0, + NSWindowAnimationBehaviorNone = 2, + NSWindowAnimationBehaviorDocumentWindow = 3, + NSWindowAnimationBehaviorUtilityWindow = 4, + NSWindowAnimationBehaviorAlertPanel = 5 +}; +typedef NSInteger NSWindowAnimationBehavior; + +@interface NSWindow (LionWindowFeatures) +- (void)setAnimationBehavior:(NSWindowAnimationBehavior)newAnimationBehavior; +@end +#endif + class nsCocoaWindow : public nsBaseWidget, public nsPIWidgetCocoa { private: @@ -266,6 +281,7 @@ public: virtual void SetTransparencyMode(nsTransparencyMode aMode); NS_IMETHOD SetWindowShadowStyle(PRInt32 aStyle); virtual void SetShowsToolbarButton(bool aShow); + virtual void SetWindowAnimationType(WindowAnimationType aType); NS_IMETHOD SetWindowTitlebarColor(nscolor aColor, bool aActive); virtual void SetDrawsInTitlebar(bool aState); virtual nsresult SynthesizeNativeMouseEvent(nsIntPoint aPoint, @@ -335,12 +351,16 @@ protected: PRInt32 mShadowStyle; NSUInteger mWindowFilter; + WindowAnimationType mAnimationType; + bool mWindowMadeHere; // true if we created the window, false for embedding bool mSheetNeedsShow; // if this is a sheet, are we waiting to be shown? // this is used for sibling sheet contention only bool mFullScreen; bool mModal; + bool mIsAnimationSuppressed; + bool mInReportMoveEvent; // true if in a call to ReportMoveEvent(). PRInt32 mNumModalDescendents; diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm index c4c5b598448..7175679bc08 100644 --- a/widget/cocoa/nsCocoaWindow.mm +++ b/widget/cocoa/nsCocoaWindow.mm @@ -135,10 +135,12 @@ nsCocoaWindow::nsCocoaWindow() , mPopupContentView(nil) , mShadowStyle(NS_STYLE_WINDOW_SHADOW_DEFAULT) , mWindowFilter(0) +, mAnimationType(nsIWidget::eGenericWindowAnimation) , mWindowMadeHere(false) , mSheetNeedsShow(false) , mFullScreen(false) , mModal(false) +, mIsAnimationSuppressed(false) , mInReportMoveEvent(false) , mNumModalDescendents(0) { @@ -304,6 +306,8 @@ nsresult nsCocoaWindow::Create(nsIWidget *aParent, return CreatePopupContentView(newBounds, aHandleEventFunction, aContext); } + mIsAnimationSuppressed = aInitData->mIsAnimationSuppressed; + return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; @@ -745,6 +749,26 @@ NS_IMETHODIMP nsCocoaWindow::Show(bool bState) } else { NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK; + if (mWindowType == eWindowType_toplevel && + [mWindow respondsToSelector:@selector(setAnimationBehavior:)]) { + NSWindowAnimationBehavior behavior; + if (mIsAnimationSuppressed) { + behavior = NSWindowAnimationBehaviorNone; + } else { + switch (mAnimationType) { + case nsIWidget::eDocumentWindowAnimation: + behavior = NSWindowAnimationBehaviorDocumentWindow; + break; + default: + NS_NOTREACHED("unexpected mAnimationType value"); + // fall through + case nsIWidget::eGenericWindowAnimation: + behavior = NSWindowAnimationBehaviorDefault; + break; + } + } + [mWindow setAnimationBehavior:behavior]; + } [mWindow makeKeyAndOrderFront:nil]; NS_OBJC_END_TRY_LOGONLY_BLOCK; SendSetZLevelEvent(); @@ -1618,6 +1642,11 @@ void nsCocoaWindow::SetShowsToolbarButton(bool aShow) NS_OBJC_END_TRY_ABORT_BLOCK; } +void nsCocoaWindow::SetWindowAnimationType(nsIWidget::WindowAnimationType aType) +{ + mAnimationType = aType; +} + NS_IMETHODIMP nsCocoaWindow::SetWindowTitlebarColor(nscolor aColor, bool aActive) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; diff --git a/widget/gtk2/nsWindow.cpp b/widget/gtk2/nsWindow.cpp index 6ef9829e12e..9d77d274a88 100644 --- a/widget/gtk2/nsWindow.cpp +++ b/widget/gtk2/nsWindow.cpp @@ -1438,6 +1438,9 @@ SetUserTimeAndStartupIDForActivatedWindow(GtkWidget* aWindow) sn_display_unref(snd); #endif + // If we used the startup ID, that already contains the focus timestamp; + // we don't want to reuse the timestamp next time we raise the window + GTKToolkit->SetFocusTimestamp(0); GTKToolkit->SetDesktopStartupID(EmptyCString()); } @@ -1501,11 +1504,20 @@ nsWindow::SetFocus(bool aRaise) if (gRaiseWindows && owningWindow->mIsShown && owningWindow->mShell && !gtk_window_is_active(GTK_WINDOW(owningWindow->mShell))) { + PRUint32 timestamp = GDK_CURRENT_TIME; + + nsGTKToolkit* GTKToolkit = nsGTKToolkit::GetToolkit(); + if (GTKToolkit) + timestamp = GTKToolkit->GetFocusTimestamp(); + LOGFOCUS((" requesting toplevel activation [%p]\n", (void *)this)); NS_ASSERTION(owningWindow->mWindowType != eWindowType_popup || mParent, "Presenting an override-redirect window"); - gtk_window_present(GTK_WINDOW(owningWindow->mShell)); + gtk_window_present_with_time(GTK_WINDOW(owningWindow->mShell), timestamp); + + if (GTKToolkit) + GTKToolkit->SetFocusTimestamp(0); } return NS_OK; diff --git a/widget/nsITransferable.idl b/widget/nsITransferable.idl index 3601154ec9c..34dc5613d71 100644 --- a/widget/nsITransferable.idl +++ b/widget/nsITransferable.idl @@ -63,6 +63,13 @@ #define kNativeImageMime "application/x-moz-nativeimage" #define kNativeHTMLMime "application/x-moz-nativehtml" +// These are used to indicate the context for a fragment of HTML source, such +// that some parent structure and style can be preserved. kHTMLContext +// contains the serialized ancestor elements, whereas kHTMLInfo are numbers +// identifying where in the context the fragment was from. +#define kHTMLContext "text/_moz_htmlcontext" +#define kHTMLInfo "text/_moz_htmlinfo" + // the source URL for a file promise #define kFilePromiseURLMime "application/x-moz-file-promise-url" // the destination filename for a file promise diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h index 394a81ac1f8..5e75c9dfcd4 100644 --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -1017,6 +1017,20 @@ class nsIWidget : public nsISupports { */ virtual void SetShowsToolbarButton(bool aShow) = 0; + enum WindowAnimationType { + eGenericWindowAnimation, + eDocumentWindowAnimation + }; + + /** + * Sets the kind of top-level window animation this widget should have. On + * Mac OS X, this causes a particular kind of animation to be shown when the + * window is first made visible. + * + * Ignored on child widgets and on non-Mac platforms. + */ + virtual void SetWindowAnimationType(WindowAnimationType aType) = 0; + /** * Hide window chrome (borders, buttons) for this widget. * diff --git a/widget/nsWidgetInitData.h b/widget/nsWidgetInitData.h index 34ffae23071..71c76828650 100644 --- a/widget/nsWidgetInitData.h +++ b/widget/nsWidgetInitData.h @@ -133,7 +133,8 @@ struct nsWidgetInitData { mUnicode(true), mRTL(false), mNoAutoHide(false), - mIsDragPopup(false) + mIsDragPopup(false), + mIsAnimationSuppressed(false) { } @@ -148,6 +149,8 @@ struct nsWidgetInitData { bool mRTL; bool mNoAutoHide; // true for noautohide panels bool mIsDragPopup; // true for drag feedback panels + bool mIsAnimationSuppressed; // true if window creation animation is + // suppressed, e.g. for session restore }; #endif // nsWidgetInitData_h__ diff --git a/widget/windows/AudioSession.cpp b/widget/windows/AudioSession.cpp index a782fe3c9dc..e4cfa2c81e8 100644 --- a/widget/windows/AudioSession.cpp +++ b/widget/windows/AudioSession.cpp @@ -110,7 +110,8 @@ public: STARTED, // Started CLONED, // SetSessionInfoCalled, Start not called FAILED, // The autdio session failed to start - STOPPED // Stop called + STOPPED, // Stop called + AUDIO_SESSION_DISCONNECTED // Audio session disconnected }; protected: nsRefPtr mAudioSessionControl; @@ -206,7 +207,9 @@ AudioSession::QueryInterface(REFIID iid, void **ppv) nsresult AudioSession::Start() { - NS_ABORT_IF_FALSE(mState == UNINITIALIZED || mState == CLONED, + NS_ABORT_IF_FALSE(mState == UNINITIALIZED || + mState == CLONED || + mState == AUDIO_SESSION_DISCONNECTED, "State invariants violated"); const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); @@ -453,6 +456,8 @@ AudioSession::OnSessionDisconnectedInternal() mAudioSessionControl->UnregisterAudioSessionNotification(this); mAudioSessionControl = nsnull; + mState = AUDIO_SESSION_DISCONNECTED; + CoUninitialize(); Start(); // If it fails there's not much we can do. return NS_OK; } diff --git a/widget/windows/GfxInfo.cpp b/widget/windows/GfxInfo.cpp index 8adc7076196..10d281d6690 100644 --- a/widget/windows/GfxInfo.cpp +++ b/widget/windows/GfxInfo.cpp @@ -511,6 +511,10 @@ GfxInfo::Init() driverDate2 = value; dwcbData = sizeof(value); result = RegQueryValueExW(key, L"Device Description", NULL, NULL, (LPBYTE)value, &dwcbData); + if (result != ERROR_SUCCESS) { + dwcbData = sizeof(value); + result = RegQueryValueExW(key, L"DriverDesc", NULL, NULL, (LPBYTE)value, &dwcbData); + } RegCloseKey(key); if (result == ERROR_SUCCESS) { mHasDualGPU = true; diff --git a/widget/windows/JumpListItem.cpp b/widget/windows/JumpListItem.cpp index 6394db6bb43..6984223bee4 100644 --- a/widget/windows/JumpListItem.cpp +++ b/widget/windows/JumpListItem.cpp @@ -737,11 +737,6 @@ nsresult JumpListLink::GetShellItem(nsCOMPtr& item, nsRefPtrGetSpec(spec); NS_ENSURE_SUCCESS(rv, rv); - // Load vista+ SHCreateItemFromParsingName - if (!WinUtils::VistaCreateItemFromParsingNameInit()) { - return NS_ERROR_UNEXPECTED; - } - // Create the IShellItem if (FAILED(WinUtils::SHCreateItemFromParsingName( NS_ConvertASCIItoUTF16(spec).get(), NULL, IID_PPV_ARGS(&psi)))) { diff --git a/widget/windows/WinUtils.cpp b/widget/windows/WinUtils.cpp index 5b96db52c1d..d5763a81e90 100644 --- a/widget/windows/WinUtils.cpp +++ b/widget/windows/WinUtils.cpp @@ -393,10 +393,24 @@ HRESULT WinUtils::SHCreateItemFromParsingName(PCWSTR pszPath, IBindCtx *pbc, REFIID riid, void **ppv) { - NS_ENSURE_TRUE(sCreateItemFromParsingName, E_FAIL); + if (!VistaCreateItemFromParsingNameInit()) + return E_FAIL; return sCreateItemFromParsingName(pszPath, pbc, riid, ppv); } +/* static */ +bool +WinUtils::GetShellItemPath(IShellItem* aItem, + nsString& aResultString) +{ + NS_ENSURE_TRUE(aItem, false); + LPWSTR str = NULL; + if (FAILED(aItem->GetDisplayName(SIGDN_FILESYSPATH, &str))) + return false; + aResultString.Assign(str); + CoTaskMemFree(str); + return !aResultString.IsEmpty(); +} } // namespace widget } // namespace mozilla diff --git a/widget/windows/WinUtils.h b/widget/windows/WinUtils.h index e9fa5536fd0..3153d8f817f 100644 --- a/widget/windows/WinUtils.h +++ b/widget/windows/WinUtils.h @@ -52,6 +52,8 @@ #include "nscore.h" #include #include +#include "nsAutoPtr.h" +#include "nsString.h" class nsWindow; @@ -200,13 +202,6 @@ public: */ static PRUint16 GetMouseInputSource(); - /** - * VistaCreateItemFromParsingNameInit() initializes the static pointer for - * SHCreateItemFromParsingName() API which is usable only on Vista and later. - * This returns TRUE if the API is available. Otherwise, FALSE. - */ - static bool VistaCreateItemFromParsingNameInit(); - /** * SHCreateItemFromParsingName() calls native SHCreateItemFromParsingName() * API. Note that you must call VistaCreateItemFromParsingNameInit() before @@ -215,12 +210,30 @@ public: static HRESULT SHCreateItemFromParsingName(PCWSTR pszPath, IBindCtx *pbc, REFIID riid, void **ppv); + /** + * GetShellItemPath return the file or directory path of a shell item. + * Internally calls IShellItem's GetDisplayName. + * + * aItem the shell item containing the path. + * aResultString the resulting string path. + * returns true if a path was retreived. + */ + static bool GetShellItemPath(IShellItem* aItem, + nsString& aResultString); + private: typedef HRESULT (WINAPI * SHCreateItemFromParsingNamePtr)(PCWSTR pszPath, IBindCtx *pbc, REFIID riid, void **ppv); static SHCreateItemFromParsingNamePtr sCreateItemFromParsingName; + + /** + * VistaCreateItemFromParsingNameInit() initializes the static pointer for + * SHCreateItemFromParsingName() API which is usable only on Vista and later. + * This returns TRUE if the API is available. Otherwise, FALSE. + */ + static bool VistaCreateItemFromParsingNameInit(); }; } // namespace widget diff --git a/widget/windows/nsFilePicker.cpp b/widget/windows/nsFilePicker.cpp index 21a4aa17103..e377e755658 100644 --- a/widget/windows/nsFilePicker.cpp +++ b/widget/windows/nsFilePicker.cpp @@ -206,9 +206,7 @@ private: nsFilePicker::nsFilePicker() : mSelectedType(1) , mDlgWnd(NULL) -#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN , mFDECookie(0) -#endif { CoInitialize(NULL); } @@ -225,7 +223,6 @@ nsFilePicker::~nsFilePicker() NS_IMPL_ISUPPORTS1(nsFilePicker, nsIFilePicker) -#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN STDMETHODIMP nsFilePicker::QueryInterface(REFIID refiid, void** ppvResult) { *ppvResult = NULL; @@ -241,7 +238,6 @@ STDMETHODIMP nsFilePicker::QueryInterface(REFIID refiid, void** ppvResult) return E_NOINTERFACE; } -#endif /* * XP picker callbacks @@ -435,8 +431,6 @@ nsFilePicker::MultiFilePickerHook(HWND hwnd, * Vista+ callbacks */ -#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN - HRESULT nsFilePicker::OnFileOk(IFileDialog *pfd) { @@ -499,8 +493,6 @@ nsFilePicker::OnOverwrite(IFileDialog *pfd, return S_OK; } -#endif // MOZ_NTDDI_LONGHORN - /* * Close on parent close logic */ @@ -598,8 +590,6 @@ nsFilePicker::ShowXPFolderPicker(const nsString& aInitialDir) return result; } -#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN - bool nsFilePicker::ShowFolderPicker(const nsString& aInitialDir) { @@ -618,8 +608,7 @@ nsFilePicker::ShowFolderPicker(const nsString& aInitialDir) // initial strings dialog->SetTitle(mTitle.get()); - if (!aInitialDir.IsEmpty() && - WinUtils::VistaCreateItemFromParsingNameInit()) { + if (!aInitialDir.IsEmpty()) { nsRefPtr folder; if (SUCCEEDED( WinUtils::SHCreateItemFromParsingName(aInitialDir.get(), NULL, @@ -641,18 +630,25 @@ nsFilePicker::ShowFolderPicker(const nsString& aInitialDir) return false; } dialog->Unadvise(mFDECookie); - - // results - LPWSTR str = NULL; - if (FAILED(item->GetDisplayName(SIGDN_FILESYSPATH, &str))) - return false; - mUnicodeFile.Assign(str); - CoTaskMemFree(str); - - return true; -} -#endif // MOZ_WINSDK_TARGETVER + // results + + // If the user chose a Win7 Library, resolve to the library's + // default save folder. + nsRefPtr folderPath; + nsRefPtr shellLib; + CoCreateInstance(CLSID_ShellLibrary, NULL, CLSCTX_INPROC, IID_IShellLibrary, + getter_AddRefs(shellLib)); + if (shellLib && + SUCCEEDED(shellLib->LoadLibraryFromItem(item, STGM_READ)) && + SUCCEEDED(shellLib->GetDefaultSaveFolder(DSFT_DETECT, IID_IShellItem, + getter_AddRefs(folderPath)))) { + item.swap(folderPath); + } + + // get the folder's file system path + return WinUtils::GetShellItemPath(item, mUnicodeFile); +} /* * File open and save picker invocation @@ -870,8 +866,6 @@ nsFilePicker::ShowXPFilePicker(const nsString& aInitialDir) return true; } -#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN - bool nsFilePicker::ShowFilePicker(const nsString& aInitialDir) { @@ -947,8 +941,7 @@ nsFilePicker::ShowFilePicker(const nsString& aInitialDir) } // initial location - if (!aInitialDir.IsEmpty() && - WinUtils::VistaCreateItemFromParsingNameInit()) { + if (!aInitialDir.IsEmpty()) { nsRefPtr folder; if (SUCCEEDED( WinUtils::SHCreateItemFromParsingName(aInitialDir.get(), NULL, @@ -990,16 +983,9 @@ nsFilePicker::ShowFilePicker(const nsString& aInitialDir) // single selection if (mMode != modeOpenMultiple) { nsRefPtr item; - if (FAILED(dialog->GetResult(getter_AddRefs(item))) || !item) { + if (FAILED(dialog->GetResult(getter_AddRefs(item))) || !item) return false; - } - - LPWSTR str = NULL; - if (FAILED(item->GetDisplayName(SIGDN_FILESYSPATH, &str))) - return false; - mUnicodeFile.Assign(str); - CoTaskMemFree(str); - return true; + return WinUtils::GetShellItemPath(item, mUnicodeFile); } // multiple selection @@ -1019,21 +1005,18 @@ nsFilePicker::ShowFilePicker(const nsString& aInitialDir) items->GetCount(&count); for (unsigned int idx = 0; idx < count; idx++) { nsRefPtr item; + nsAutoString str; if (SUCCEEDED(items->GetItemAt(idx, getter_AddRefs(item)))) { - LPWSTR str = NULL; - if (FAILED(item->GetDisplayName(SIGDN_FILESYSPATH, &str))) + if (!WinUtils::GetShellItemPath(item, str)) continue; nsCOMPtr file = do_CreateInstance("@mozilla.org/file/local;1"); - if (file && NS_SUCCEEDED(file->InitWithPath(nsDependentString(str)))) + if (file && NS_SUCCEEDED(file->InitWithPath(str))) mFiles.AppendObject(file); - CoTaskMemFree(str); } } return true; } -#endif // MOZ_WINSDK_TARGETVER - /////////////////////////////////////////////////////////////////////////////// // nsIFilePicker impl. @@ -1062,23 +1045,15 @@ nsFilePicker::ShowW(PRInt16 *aReturnVal) bool result = false; if (mMode == modeGetFolder) { -#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) result = ShowFolderPicker(initialDir); else result = ShowXPFolderPicker(initialDir); -#else - result = ShowXPFolderPicker(initialDir); -#endif } else { -#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) result = ShowFilePicker(initialDir); else result = ShowXPFilePicker(initialDir); -#else - result = ShowXPFilePicker(initialDir); -#endif } // exit, and return returnCancel in aReturnVal @@ -1347,8 +1322,6 @@ nsFilePicker::IsDefaultPathHtml() return false; } -#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN - void nsFilePicker::ComDlgFilterSpec::Append(const nsAString& aTitle, const nsAString& aFilter) { @@ -1378,5 +1351,3 @@ nsFilePicker::ComDlgFilterSpec::Append(const nsAString& aTitle, const nsAString& } pSpecForward->pszSpec = pStr->get(); } - -#endif // MOZ_WINSDK_TARGETVER diff --git a/widget/windows/nsFilePicker.h b/widget/windows/nsFilePicker.h index c91fd182a7a..125f349f0e6 100644 --- a/widget/windows/nsFilePicker.h +++ b/widget/windows/nsFilePicker.h @@ -44,8 +44,8 @@ #include -#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN -// For Vista IFileDialog interfaces +// For Vista IFileDialog interfaces which aren't exposed +// unless _WIN32_WINNT >= _WIN32_WINNT_LONGHORN. #if _WIN32_WINNT < _WIN32_WINNT_LONGHORN #define _WIN32_WINNT_bak _WIN32_WINNT #undef _WIN32_WINNT @@ -54,7 +54,6 @@ #undef _WIN32_IE #define _WIN32_IE _WIN32_IE_IE70 #endif -#endif #include "nsILocalFile.h" #include "nsITimer.h" @@ -72,10 +71,9 @@ * Native Windows FileSelector wrapper */ -class nsFilePicker : public nsBaseFilePicker -#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN - , public IFileDialogEvents -#endif +class nsFilePicker : + public nsBaseFilePicker, + public IFileDialogEvents { public: nsFilePicker(); @@ -83,10 +81,8 @@ public: NS_DECL_ISUPPORTS -#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN // IUnknown's QueryInterface STDMETHODIMP QueryInterface(REFIID refiid, void** ppvResult); -#endif // nsIFilePicker (less what's in nsBaseFilePicker) NS_IMETHOD GetDefaultString(nsAString& aDefaultString); @@ -102,7 +98,6 @@ public: NS_IMETHOD ShowW(PRInt16 *aReturnVal); NS_IMETHOD AppendFilter(const nsAString& aTitle, const nsAString& aFilter); -#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN // IFileDialogEvents HRESULT STDMETHODCALLTYPE OnFileOk(IFileDialog *pfd); HRESULT STDMETHODCALLTYPE OnFolderChanging(IFileDialog *pfd, IShellItem *psiFolder); @@ -111,7 +106,6 @@ public: HRESULT STDMETHODCALLTYPE OnShareViolation(IFileDialog *pfd, IShellItem *psi, FDE_SHAREVIOLATION_RESPONSE *pResponse); HRESULT STDMETHODCALLTYPE OnTypeChange(IFileDialog *pfd); HRESULT STDMETHODCALLTYPE OnOverwrite(IFileDialog *pfd, IShellItem *psi, FDE_OVERWRITE_RESPONSE *pResponse); -#endif protected: enum PickerType { @@ -128,10 +122,8 @@ protected: bool FilePickerWrapper(OPENFILENAMEW* ofn, PickerType aType); bool ShowXPFolderPicker(const nsString& aInitialDir); bool ShowXPFilePicker(const nsString& aInitialDir); -#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN bool ShowFolderPicker(const nsString& aInitialDir); bool ShowFilePicker(const nsString& aInitialDir); -#endif void AppendXPFilter(const nsAString& aTitle, const nsAString& aFilter); void RememberLastUsedDirectory(); bool IsPrivacyModeEnabled(); @@ -158,7 +150,6 @@ protected: static PRUnichar *mLastUsedUnicodeDirectory; HWND mDlgWnd; -#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN class ComDlgFilterSpec { public: @@ -185,16 +176,13 @@ protected: ComDlgFilterSpec mComFilterList; DWORD mFDECookie; -#endif }; -#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN #if defined(_WIN32_WINNT_bak) #undef _WIN32_WINNT #define _WIN32_WINNT _WIN32_WINNT_bak #undef _WIN32_IE #define _WIN32_IE _WIN32_IE_bak #endif -#endif #endif // nsFilePicker_h__ diff --git a/widget/xpwidgets/nsBaseWidget.h b/widget/xpwidgets/nsBaseWidget.h index 8455e0cebb3..eb313ee5317 100644 --- a/widget/xpwidgets/nsBaseWidget.h +++ b/widget/xpwidgets/nsBaseWidget.h @@ -123,6 +123,7 @@ public: virtual void GetWindowClipRegion(nsTArray* aRects); NS_IMETHOD SetWindowShadowStyle(PRInt32 aStyle); virtual void SetShowsToolbarButton(bool aShow) {} + virtual void SetWindowAnimationType(WindowAnimationType aType) {} NS_IMETHOD HideWindowChrome(bool aShouldHide); NS_IMETHOD MakeFullScreen(bool aFullScreen); virtual nsDeviceContext* GetDeviceContext(); diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp index d013f288615..0a1a92f38a9 100644 --- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -1416,11 +1416,36 @@ GraphWalker::DoWalk(nsDeque &aQueue) #endif } +struct CCGraphDescriber +{ + CCGraphDescriber() + : mAddress("0x"), mToAddress("0x"), mCnt(0), mType(eUnknown) {} + + enum Type + { + eRefCountedObject, + eGCedObject, + eGCMarkedObject, + eEdge, + eRoot, + eGarbage, + eUnknown + }; + + nsCString mAddress; + nsCString mToAddress; + nsCString mName; + PRUint32 mCnt; + Type mType; +}; class nsCycleCollectorLogger : public nsICycleCollectorListener { public: - nsCycleCollectorLogger() : mStream(nsnull), mWantAllTraces(false) + nsCycleCollectorLogger() : + mStream(nsnull), mWantAllTraces(false), + mDisableLog(false), mWantAfterProcessing(false), + mNextIndex(0) { } ~nsCycleCollectorLogger() @@ -1433,19 +1458,49 @@ public: NS_IMETHOD AllTraces(nsICycleCollectorListener** aListener) { - mWantAllTraces = true; - NS_ADDREF(*aListener = this); - return NS_OK; + mWantAllTraces = true; + NS_ADDREF(*aListener = this); + return NS_OK; } NS_IMETHOD GetWantAllTraces(bool* aAllTraces) { - *aAllTraces = mWantAllTraces; - return NS_OK; + *aAllTraces = mWantAllTraces; + return NS_OK; + } + + NS_IMETHOD GetDisableLog(bool* aDisableLog) + { + *aDisableLog = mDisableLog; + return NS_OK; + } + + NS_IMETHOD SetDisableLog(bool aDisableLog) + { + mDisableLog = aDisableLog; + return NS_OK; + } + + NS_IMETHOD GetWantAfterProcessing(bool* aWantAfterProcessing) + { + *aWantAfterProcessing = mWantAfterProcessing; + return NS_OK; + } + + NS_IMETHOD SetWantAfterProcessing(bool aWantAfterProcessing) + { + mWantAfterProcessing = aWantAfterProcessing; + return NS_OK; } NS_IMETHOD Begin() { + mCurrentAddress.AssignLiteral("0x"); + mDescribers.Clear(); + mNextIndex = 0; + if (mDisableLog) { + return NS_OK; + } char basename[MAXPATHLEN] = {'\0'}; char ccname[MAXPATHLEN] = {'\0'}; #ifdef XP_WIN @@ -1498,55 +1553,150 @@ public: NS_IMETHOD NoteRefCountedObject(PRUint64 aAddress, PRUint32 refCount, const char *aObjectDescription) { - fprintf(mStream, "%p [rc=%u] %s\n", (void*)aAddress, refCount, - aObjectDescription); - + if (!mDisableLog) { + fprintf(mStream, "%p [rc=%u] %s\n", (void*)aAddress, refCount, + aObjectDescription); + } + if (mWantAfterProcessing) { + CCGraphDescriber* d = mDescribers.AppendElement(); + NS_ENSURE_TRUE(d, NS_ERROR_OUT_OF_MEMORY); + mCurrentAddress.AssignLiteral("0x"); + mCurrentAddress.AppendInt(aAddress, 16); + d->mType = CCGraphDescriber::eRefCountedObject; + d->mAddress = mCurrentAddress; + d->mCnt = refCount; + d->mName.Append(aObjectDescription); + } return NS_OK; } NS_IMETHOD NoteGCedObject(PRUint64 aAddress, bool aMarked, const char *aObjectDescription) { - fprintf(mStream, "%p [gc%s] %s\n", (void*)aAddress, - aMarked ? ".marked" : "", aObjectDescription); - + if (!mDisableLog) { + fprintf(mStream, "%p [gc%s] %s\n", (void*)aAddress, + aMarked ? ".marked" : "", aObjectDescription); + } + if (mWantAfterProcessing) { + CCGraphDescriber* d = mDescribers.AppendElement(); + NS_ENSURE_TRUE(d, NS_ERROR_OUT_OF_MEMORY); + mCurrentAddress.AssignLiteral("0x"); + mCurrentAddress.AppendInt(aAddress, 16); + d->mType = aMarked ? CCGraphDescriber::eGCMarkedObject : + CCGraphDescriber::eGCedObject; + d->mAddress = mCurrentAddress; + d->mName.Append(aObjectDescription); + } return NS_OK; } NS_IMETHOD NoteEdge(PRUint64 aToAddress, const char *aEdgeName) { - fprintf(mStream, "> %p %s\n", (void*)aToAddress, aEdgeName); - + if (!mDisableLog) { + fprintf(mStream, "> %p %s\n", (void*)aToAddress, aEdgeName); + } + if (mWantAfterProcessing) { + CCGraphDescriber* d = mDescribers.AppendElement(); + NS_ENSURE_TRUE(d, NS_ERROR_OUT_OF_MEMORY); + d->mType = CCGraphDescriber::eEdge; + d->mAddress = mCurrentAddress; + d->mToAddress.AppendInt(aToAddress, 16); + d->mName.Append(aEdgeName); + } return NS_OK; } NS_IMETHOD BeginResults() { - fputs("==========\n", mStream); - + if (!mDisableLog) { + fputs("==========\n", mStream); + } return NS_OK; } NS_IMETHOD DescribeRoot(PRUint64 aAddress, PRUint32 aKnownEdges) { - fprintf(mStream, "%p [known=%u]\n", (void*)aAddress, aKnownEdges); - + if (!mDisableLog) { + fprintf(mStream, "%p [known=%u]\n", (void*)aAddress, aKnownEdges); + } + if (mWantAfterProcessing) { + CCGraphDescriber* d = mDescribers.AppendElement(); + NS_ENSURE_TRUE(d, NS_ERROR_OUT_OF_MEMORY); + d->mType = CCGraphDescriber::eRoot; + d->mAddress.AppendInt(aAddress, 16); + d->mCnt = aKnownEdges; + } return NS_OK; } NS_IMETHOD DescribeGarbage(PRUint64 aAddress) { - fprintf(mStream, "%p [garbage]\n", (void*)aAddress); - + if (!mDisableLog) { + fprintf(mStream, "%p [garbage]\n", (void*)aAddress); + } + if (mWantAfterProcessing) { + CCGraphDescriber* d = mDescribers.AppendElement(); + NS_ENSURE_TRUE(d, NS_ERROR_OUT_OF_MEMORY); + d->mType = CCGraphDescriber::eGarbage; + d->mAddress.AppendInt(aAddress, 16); + } return NS_OK; } NS_IMETHOD End() { - fclose(mStream); - mStream = nsnull; - + if (!mDisableLog) { + fclose(mStream); + mStream = nsnull; + } return NS_OK; } + NS_IMETHOD ProcessNext(nsICycleCollectorHandler* aHandler, + bool* aCanContinue) + { + NS_ENSURE_STATE(aHandler && mWantAfterProcessing); + if (mNextIndex < mDescribers.Length()) { + CCGraphDescriber& d = mDescribers[mNextIndex++]; + switch (d.mType) { + case CCGraphDescriber::eRefCountedObject: + aHandler->NoteRefCountedObject(d.mAddress, + d.mCnt, + d.mName); + break; + case CCGraphDescriber::eGCedObject: + case CCGraphDescriber::eGCMarkedObject: + aHandler->NoteGCedObject(d.mAddress, + d.mType == + CCGraphDescriber::eGCMarkedObject, + d.mName); + break; + case CCGraphDescriber::eEdge: + aHandler->NoteEdge(d.mAddress, + d.mToAddress, + d.mName); + break; + case CCGraphDescriber::eRoot: + aHandler->DescribeRoot(d.mAddress, + d.mCnt); + break; + case CCGraphDescriber::eGarbage: + aHandler->DescribeGarbage(d.mAddress); + break; + case CCGraphDescriber::eUnknown: + NS_NOTREACHED("CCGraphDescriber::eUnknown"); + break; + } + } + if (!(*aCanContinue = mNextIndex < mDescribers.Length())) { + mCurrentAddress.AssignLiteral("0x"); + mDescribers.Clear(); + mNextIndex = 0; + } + return NS_OK; + } private: FILE *mStream; bool mWantAllTraces; - + bool mDisableLog; + bool mWantAfterProcessing; + nsCString mCurrentAddress; + nsTArray mDescribers; + PRUint32 mNextIndex; static PRUint32 gLogCounter; }; diff --git a/xpcom/base/nsICycleCollectorListener.idl b/xpcom/base/nsICycleCollectorListener.idl index 4e185e5902d..9cdfde6827a 100644 --- a/xpcom/base/nsICycleCollectorListener.idl +++ b/xpcom/base/nsICycleCollectorListener.idl @@ -36,6 +36,23 @@ #include "nsISupports.idl" +[scriptable, uuid(0ef15f15-7783-4991-af68-4976d7ec2267)] +interface nsICycleCollectorHandler : nsISupports +{ + void noteRefCountedObject(in ACString aAddress, + in unsigned long aRefCount, + in ACString aObjectDescription); + void noteGCedObject(in ACString aAddress, + in boolean aMarked, + in ACString aObjectDescription); + void noteEdge(in ACString aFromAddress, + in ACString aToAddress, + in ACString aEdgeName); + void describeRoot(in ACString aAddress, + in unsigned long aKnownEdges); + void describeGarbage(in ACString aAddress); +}; + /** Interface to pass to the cycle collector to get information about * the CC graph while it's being built. The order of calls will be a * call to begin(); then for every node in the graph a call to either @@ -49,13 +66,18 @@ * a call to end(). If begin() returns an error none of the other * functions will be called. */ -[scriptable, builtinclass, uuid(e7e9a010-d02f-4137-94c8-6d73605fe623)] +[scriptable, builtinclass, uuid(5d1c5d51-2022-4242-8c33-0a942b5fed06)] interface nsICycleCollectorListener : nsISupports { nsICycleCollectorListener allTraces(); // false if allTraces() has not been called. readonly attribute boolean wantAllTraces; + // The default implementation of this interface will print out + // a log to a file unless disableLog is set to true. + attribute boolean disableLog; + attribute boolean wantAfterProcessing; + void begin(); void noteRefCountedObject (in unsigned long long aAddress, in unsigned long aRefCount, @@ -70,4 +92,7 @@ interface nsICycleCollectorListener : nsISupports in unsigned long aKnownEdges); void describeGarbage(in unsigned long long aAddress); void end(); + + // Returns false if there isn't anything more to process. + boolean processNext(in nsICycleCollectorHandler aHandler); }; diff --git a/xpcom/io/nsLocalFileWin.cpp b/xpcom/io/nsLocalFileWin.cpp index e41573ea46b..34a1d4e4580 100644 --- a/xpcom/io/nsLocalFileWin.cpp +++ b/xpcom/io/nsLocalFileWin.cpp @@ -261,6 +261,9 @@ static nsresult ConvertWinError(DWORD winErr) case ERROR_FILENAME_EXCED_RANGE: rv = NS_ERROR_FILE_NAME_TOO_LONG; break; + case ERROR_DIRECTORY: + rv = NS_ERROR_FILE_NOT_DIRECTORY; + break; case 0: rv = NS_OK; break; @@ -377,7 +380,7 @@ OpenFile(const nsAFlatString &name, PRIntn osflags, PRIntn mode, flag6 |= FILE_FLAG_DELETE_ON_CLOSE; } - if (osflags && nsILocalFile::OS_READAHEAD) { + if (osflags & nsILocalFile::OS_READAHEAD) { flag6 |= FILE_FLAG_SEQUENTIAL_SCAN; } @@ -490,10 +493,12 @@ OpenDir(const nsAFlatString &name, nsDir * *dir) filename.ReplaceChar(L'/', L'\\'); + // FindFirstFileW Will have a last error of ERROR_DIRECTORY if + // \* is passed in. If \* is passed in then + // ERROR_PATH_NOT_FOUND will be the last error. d->handle = ::FindFirstFileW(filename.get(), &(d->data) ); - if ( d->handle == INVALID_HANDLE_VALUE ) - { + if (d->handle == INVALID_HANDLE_VALUE) { PR_Free(d); return ConvertWinError(GetLastError()); } @@ -589,6 +594,8 @@ class nsDirEnumerator : public nsISimpleEnumerator, return NS_ERROR_UNEXPECTED; } + // IsDirectory is not needed here because OpenDir will return + // NS_ERROR_FILE_NOT_DIRECTORY if the passed in path is a file. nsresult rv = OpenDir(filepath, &mDir); if (NS_FAILED(rv)) return rv; @@ -2539,31 +2546,17 @@ nsLocalFile::IsExecutable(bool *_retval) NS_IMETHODIMP nsLocalFile::IsDirectory(bool *_retval) { - nsresult rv = IsFile(_retval); - if (NS_FAILED(rv)) { - return rv; - } - - *_retval = !*_retval; - return NS_OK; + return HasFileAttribute(FILE_ATTRIBUTE_DIRECTORY, _retval); } NS_IMETHODIMP nsLocalFile::IsFile(bool *_retval) { - NS_ENSURE_ARG(_retval); - nsresult rv = Resolve(); - if (NS_FAILED(rv)) { + nsresult rv = HasFileAttribute(FILE_ATTRIBUTE_DIRECTORY, _retval); + if (NS_SUCCEEDED(rv)) { + *_retval = !*_retval; + } return rv; - } - - DWORD attributes = GetFileAttributes(mResolvedPath.get()); - if (INVALID_FILE_ATTRIBUTES == attributes) { - return NS_ERROR_FILE_NOT_FOUND; - } - - *_retval = !(attributes & FILE_ATTRIBUTE_DIRECTORY); - return NS_OK; } NS_IMETHODIMP @@ -2577,16 +2570,17 @@ nsLocalFile::HasFileAttribute(DWORD fileAttrib, bool *_retval) { NS_ENSURE_ARG(_retval); - nsresult rv = ResolveAndStat(); - if (NS_FAILED(rv)) + nsresult rv = Resolve(); + if (NS_FAILED(rv)) { return rv; + } - // get the file attributes for the correct item depending on following symlinks - const PRUnichar *filePath = mFollowSymlinks ? - mResolvedPath.get() : mWorkingPath.get(); - DWORD word = ::GetFileAttributesW(filePath); + DWORD attributes = GetFileAttributesW(mResolvedPath.get()); + if (INVALID_FILE_ATTRIBUTES == attributes) { + return ConvertWinError(GetLastError()); + } - *_retval = ((word & fileAttrib) != 0); + *_retval = ((attributes & fileAttrib) != 0); return NS_OK; } @@ -2736,13 +2730,6 @@ nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator * *entries) return NS_OK; } - bool isDir; - rv = IsDirectory(&isDir); - if (NS_FAILED(rv)) - return rv; - if (!isDir) - return NS_ERROR_FILE_NOT_DIRECTORY; - nsDirEnumerator* dirEnum = new nsDirEnumerator(); if (dirEnum == nsnull) return NS_ERROR_OUT_OF_MEMORY; diff --git a/xpcom/io/nsScriptableInputStream.cpp b/xpcom/io/nsScriptableInputStream.cpp index 30790de2115..374a7a4cce8 100644 --- a/xpcom/io/nsScriptableInputStream.cpp +++ b/xpcom/io/nsScriptableInputStream.cpp @@ -72,7 +72,8 @@ nsScriptableInputStream::Read(PRUint32 aCount, char **_retval) { rv = mInputStream->Available(&count); if (NS_FAILED(rv)) return rv; - count = NS_MIN(count, aCount); + // bug716556 - Ensure count+1 doesn't overflow + count = NS_MIN(NS_MIN(count, aCount), PR_UINT32_MAX - 1); buffer = (char*)nsMemory::Alloc(count+1); // make room for '\0' if (!buffer) return NS_ERROR_OUT_OF_MEMORY; diff --git a/xpcom/tests/TestDeque.cpp b/xpcom/tests/TestDeque.cpp index e0fd85a0e20..36d465c3c78 100644 --- a/xpcom/tests/TestDeque.cpp +++ b/xpcom/tests/TestDeque.cpp @@ -52,7 +52,6 @@ private: int AssignFlaw(); int TestRemove(); }; -static _TestDeque sTestDeque; class _Dealloc: public nsDequeFunctor { virtual void* operator()(void* aObject) { diff --git a/xpcom/typelib/xpt/tools/xpt.py b/xpcom/typelib/xpt/tools/xpt.py index e00a5892658..f751782a6c1 100644 --- a/xpcom/typelib/xpt/tools/xpt.py +++ b/xpcom/typelib/xpt/tools/xpt.py @@ -870,6 +870,7 @@ class Interface(object): self._descriptor_offset = 0 self._name_offset = 0 self._namespace_offset = 0 + self.xpt_filename = None def __repr__(self): return "Interface('%s', '%s', '%s', methods=%s)" % (self.name, self.iid, self.namespace, self.methods) @@ -1095,6 +1096,7 @@ class Typelib(object): namespace = Typelib.read_string(map, data_pool_offset, ide[2]) iface = Interface(name, iid, namespace) iface._descriptor_offset = ide[3] + iface.xpt_filename = xpt.filename xpt.interfaces.append(iface) for iface in xpt.interfaces: iface.read_descriptor(xpt, map, data_pool_offset) @@ -1217,7 +1219,8 @@ class Typelib(object): raise DataError, \ "Typelibs contain definitions of interface %s" \ " with different IIDs (%s (%s) vs %s (%s))!" % \ - (i.name, i.iid, other.filename, j.iid, self.filename) + (i.name, i.iid, i.xpt_filename or other.filename, \ + j.iid, j.xpt_filename or self.filename) elif i.iid == j.iid and i.iid != Interface.UNRESOLVED_IID: # Same IID but different names: raise an exception. # self.* is the (target) Typelib being merged into, @@ -1225,7 +1228,8 @@ class Typelib(object): raise DataError, \ "Typelibs contain definitions of interface %s" \ " with different names (%s (%s) vs %s (%s))!" % \ - (i.iid, i.name, other.filename, j.name, self.filename) + (i.iid, i.name, i.xpt_filename or other.filename, \ + j.name, j.xpt_filename or self.filename) if not merged: # No partially matching interfaces, so just take this interface self.interfaces.append(i) diff --git a/xpfe/appshell/src/nsAppShellService.cpp b/xpfe/appshell/src/nsAppShellService.cpp index 92e5cacaedb..bb91c183a48 100644 --- a/xpfe/appshell/src/nsAppShellService.cpp +++ b/xpfe/appshell/src/nsAppShellService.cpp @@ -308,6 +308,9 @@ nsAppShellService::JustCreateTopWindow(nsIXULWindow *aParent, if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_POPUP) widgetInitData.mWindowType = eWindowType_popup; + if (aChromeMask & nsIWebBrowserChrome::CHROME_MAC_SUPPRESS_ANIMATION) + widgetInitData.mIsAnimationSuppressed = true; + #ifdef XP_MACOSX // Mac OS X sheet support // Adding CHROME_OPENAS_CHROME to sheetMask makes modal windows opened from diff --git a/xpfe/appshell/src/nsXULWindow.cpp b/xpfe/appshell/src/nsXULWindow.cpp index c72964f9d93..d9701a92690 100644 --- a/xpfe/appshell/src/nsXULWindow.cpp +++ b/xpfe/appshell/src/nsXULWindow.cpp @@ -1412,6 +1412,12 @@ void nsXULWindow::SyncAttributesToWidget() if (NS_SUCCEEDED(rv)) { mWindow->SetShowsToolbarButton(attr.LowerCaseEqualsLiteral("true")); } + + // "macanimationtype" attribute + rv = windowElement->GetAttribute(NS_LITERAL_STRING("macanimationtype"), attr); + if (NS_SUCCEEDED(rv) && attr.EqualsLiteral("document")) { + mWindow->SetWindowAnimationType(nsIWidget::eDocumentWindowAnimation); + } } NS_IMETHODIMP nsXULWindow::SavePersistentAttributes()