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/src/html/nsHyperTextAccessible.cpp b/accessible/src/html/nsHyperTextAccessible.cpp index 1b0fce62ff2..c1060c31700 100644 --- a/accessible/src/html/nsHyperTextAccessible.cpp +++ b/accessible/src/html/nsHyperTextAccessible.cpp @@ -1631,6 +1631,9 @@ nsHyperTextAccessible::GetCaretOffset(PRInt32 *aCaretOffset) NS_ENSURE_ARG_POINTER(aCaretOffset); *aCaretOffset = -1; + if (IsDefunct()) + return NS_ERROR_FAILURE; + // Not focused focusable accessible except document accessible doesn't have // a caret. if (!IsDoc() && !FocusMgr()->IsFocused(this) && 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/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 b5e32b0390f..72e51b953ed 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -536,7 +536,8 @@ if (!this.mBlank) { if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) { this.mTab.setAttribute("busy", "true"); - this.mTabBrowser.setTabTitleLoading(this.mTab); + if (!(this.mBrowser.docShell.loadType & Ci.nsIDocShell.LOAD_CMD_RELOAD)) + this.mTabBrowser.setTabTitleLoading(this.mTab); } if (this.mTab.selected) @@ -1265,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); } @@ -1364,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) @@ -1556,15 +1564,26 @@ -1) { - Services.console.unregisterListener(consoleListener); ok(true, "drop was blocked"); executeSoon(finish); } } } Services.console.registerListener(consoleListener); + registerCleanupFunction(function () { + Services.console.unregisterListener(consoleListener); + }); // The drop handler throws an exception when dragging URIs that inherit // principal, e.g. javascript: diff --git a/browser/branding/official/configure.sh b/browser/branding/official/configure.sh index 4d3d2973770..55884683d15 100644 --- a/browser/branding/official/configure.sh +++ b/browser/branding/official/configure.sh @@ -1,2 +1 @@ MOZ_APP_DISPLAYNAME=Firefox -MOZ_UA_BUILDID=20100101 diff --git a/browser/components/sessionstore/src/nsSessionStartup.js b/browser/components/sessionstore/src/nsSessionStartup.js index d399232b89d..3aed091278c 100644 --- a/browser/components/sessionstore/src/nsSessionStartup.js +++ b/browser/components/sessionstore/src/nsSessionStartup.js @@ -72,6 +72,7 @@ const Cr = Components.results; const Cu = Components.utils; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource:///modules/TelemetryStopwatch.jsm"); const STATE_RUNNING_STR = "running"; const MAX_FILE_SIZE = 100 * 1024 * 1024; // 100 megabytes @@ -127,23 +128,30 @@ SessionStartup.prototype = { return; // parse the session state into a JS object + // remove unneeded braces (added for compatibility with Firefox 2.0 and 3.0) + if (iniString.charAt(0) == '(') + iniString = iniString.slice(1, -1); + let corruptFile = false; try { - // remove unneeded braces (added for compatibility with Firefox 2.0 and 3.0) - if (iniString.charAt(0) == '(') - iniString = iniString.slice(1, -1); + this._initialState = JSON.parse(iniString); + } + catch (ex) { + debug("The session file contained un-parse-able JSON: " + ex); + // Try to eval. + // evalInSandbox will throw if iniString is not parse-able. try { - this._initialState = JSON.parse(iniString); - } - catch (exJSON) { var s = new Cu.Sandbox("about:blank", {sandboxName: 'nsSessionStartup'}); this._initialState = Cu.evalInSandbox("(" + iniString + ")", s); + } catch(ex) { + debug("The session file contained un-eval-able JSON: " + ex); + corruptFile = true; } - - // If this is a normal restore then throw away any previous session - if (!doResumeSessionOnce) - delete this._initialState.lastSessionState; } - catch (ex) { debug("The session file is invalid: " + ex); } + Services.telemetry.getHistogramById("FX_SESSION_RESTORE_CORRUPT_FILE").add(corruptFile); + + // If this is a normal restore then throw away any previous session + if (!doResumeSessionOnce) + delete this._initialState.lastSessionState; let resumeFromCrash = prefBranch.getBoolPref("sessionstore.resume_from_crash"); let lastSessionCrashed = @@ -154,8 +162,7 @@ SessionStartup.prototype = { // Report shutdown success via telemetry. Shortcoming here are // being-killed-by-OS-shutdown-logic, shutdown freezing after // session restore was written, etc. - let Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry); - Telemetry.getHistogramById("SHUTDOWN_OK").add(!lastSessionCrashed); + Services.telemetry.getHistogramById("SHUTDOWN_OK").add(!lastSessionCrashed); // set the startup type if (lastSessionCrashed && resumeFromCrash) @@ -296,9 +303,11 @@ SessionStartup.prototype = { * @returns a session state string */ _readStateFile: function sss_readStateFile(aFile) { + TelemetryStopwatch.start("FX_SESSION_RESTORE_READ_FILE_MS"); var stateString = Cc["@mozilla.org/supports-string;1"]. createInstance(Ci.nsISupportsString); stateString.data = this._readFile(aFile) || ""; + TelemetryStopwatch.finish("FX_SESSION_RESTORE_READ_FILE_MS"); Services.obs.notifyObservers(stateString, "sessionstore-state-read", ""); diff --git a/browser/components/sessionstore/src/nsSessionStore.js b/browser/components/sessionstore/src/nsSessionStore.js index d933d4bcb9d..f7599b75b53 100644 --- a/browser/components/sessionstore/src/nsSessionStore.js +++ b/browser/components/sessionstore/src/nsSessionStore.js @@ -131,6 +131,7 @@ Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/debug.js"); Cu.import("resource:///modules/TelemetryTimestamps.jsm"); +Cu.import("resource:///modules/TelemetryStopwatch.jsm"); XPCOMUtils.defineLazyGetter(this, "NetUtil", function() { Cu.import("resource://gre/modules/NetUtil.jsm"); @@ -3653,6 +3654,8 @@ SessionStoreService.prototype = { // if we crash. let pinnedOnly = this._loadState == STATE_RUNNING && !this._resume_from_crash; + TelemetryStopwatch.start("FX_SESSION_RESTORE_COLLECT_DATA_MS"); + var oState = this._getCurrentState(aUpdateAll, pinnedOnly); if (!oState) return; @@ -3691,6 +3694,8 @@ SessionStoreService.prototype = { if (this._lastSessionState) oState.lastSessionState = this._lastSessionState; + TelemetryStopwatch.finish("FX_SESSION_RESTORE_COLLECT_DATA_MS"); + this._saveStateObject(oState); }, @@ -3698,9 +3703,11 @@ SessionStoreService.prototype = { * write a state object to disk */ _saveStateObject: function sss_saveStateObject(aStateObj) { + TelemetryStopwatch.start("FX_SESSION_RESTORE_SERIALIZE_DATA_MS"); var stateString = Cc["@mozilla.org/supports-string;1"]. createInstance(Ci.nsISupportsString); stateString.data = this._toJSONString(aStateObj); + TelemetryStopwatch.finish("FX_SESSION_RESTORE_SERIALIZE_DATA_MS"); Services.obs.notifyObservers(stateString, "sessionstore-state-write", ""); @@ -3809,7 +3816,7 @@ SessionStoreService.prototype = { argString.data = ""; // Build feature string - let features = "chrome,dialog=no,all"; + let features = "chrome,dialog=no,macsuppressanimation,all"; let winState = aState.windows[0]; WINDOW_ATTRIBUTES.forEach(function(aFeature) { // Use !isNaN as an easy way to ignore sizemode and check for numbers @@ -4427,6 +4434,7 @@ SessionStoreService.prototype = { * String data */ _writeFile: function sss_writeFile(aFile, aData) { + TelemetryStopwatch.start("FX_SESSION_RESTORE_WRITE_FILE_MS"); // Initialize the file output stream. var ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"]. createInstance(Ci.nsIFileOutputStream); @@ -4442,6 +4450,7 @@ SessionStoreService.prototype = { var self = this; NetUtil.asyncCopy(istream, ostream, function(rc) { if (Components.isSuccessCode(rc)) { + TelemetryStopwatch.finish("FX_SESSION_RESTORE_WRITE_FILE_MS"); Services.obs.notifyObservers(null, "sessionstore-state-write-complete", ""); diff --git a/browser/config/mozconfigs/macosx-lion-universal/nightly b/browser/config/mozconfigs/macosx-lion-universal/nightly new file mode 100644 index 00000000000..da575599dbd --- /dev/null +++ b/browser/config/mozconfigs/macosx-lion-universal/nightly @@ -0,0 +1,24 @@ +. $topsrcdir/build/macosx/universal/mozconfig + +# Universal builds override the default of browser (bug 575283 comment 29) +ac_add_options --enable-application=browser + +ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} +ac_add_options --enable-update-packaging +ac_add_options --enable-codesighs +ac_add_options --disable-install-strip + +# Nightlies only since this has a cost in performance +ac_add_options --enable-js-diagnostics + +# Needed to enable breakpad in application.ini +export MOZILLA_OFFICIAL=1 + +export MOZ_TELEMETRY_REPORTING=1 +mk_add_options MOZ_MAKE_FLAGS="-j12" + +ac_add_options --with-macbundlename-prefix=Firefox + +# Treat warnings as errors in directories with FAIL_ON_WARNINGS. +ac_add_options --enable-warnings-as-errors +ac_add_options --with-ccache diff --git a/browser/config/mozconfigs/macosx-lion-universal/release b/browser/config/mozconfigs/macosx-lion-universal/release new file mode 100644 index 00000000000..0acefb03ecd --- /dev/null +++ b/browser/config/mozconfigs/macosx-lion-universal/release @@ -0,0 +1,20 @@ +. $topsrcdir/build/macosx/universal/mozconfig + +# Universal builds override the default of browser (bug 575283 comment 29) +ac_add_options --enable-application=browser + +ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} +ac_add_options --enable-update-packaging +ac_add_options --enable-official-branding + +# Needed to enable breakpad in application.ini +export MOZILLA_OFFICIAL=1 + +export MOZ_TELEMETRY_REPORTING=1 + +# Treat warnings as errors in directories with FAIL_ON_WARNINGS. +ac_add_options --enable-warnings-as-errors + +# Enable parallel compiling +mk_add_options MOZ_MAKE_FLAGS="-j4" +ac_add_options --with-ccache diff --git a/browser/config/mozconfigs/macosx-lion-universal/shark b/browser/config/mozconfigs/macosx-lion-universal/shark new file mode 100644 index 00000000000..48f355dbe1b --- /dev/null +++ b/browser/config/mozconfigs/macosx-lion-universal/shark @@ -0,0 +1,26 @@ +# Just like nightlies, but without tests, not on an update channel, and with +# shark and dtrace enabled +. $topsrcdir/build/macosx/universal/mozconfig + +# Universal builds override the default of browser (bug 575283 comment 29) +ac_add_options --enable-application=browser + +ac_add_options --disable-tests +ac_add_options --disable-install-strip + +# Needed to enable breakpad in application.ini +export MOZILLA_OFFICIAL=1 + +# Enable parallel compiling +mk_add_options MOZ_MAKE_FLAGS="-j12" + +# shark specific options +ac_add_options --enable-shark +ac_add_options --enable-dtrace + +# Need this to prevent name conflicts with the normal nightly build packages +export MOZ_PKG_SPECIAL="shark" + +# Treat warnings as errors in directories with FAIL_ON_WARNINGS. +ac_add_options --enable-warnings-as-errors +ac_add_options --with-ccache diff --git a/browser/config/mozconfigs/macosx32-lion/debug b/browser/config/mozconfigs/macosx32-lion/debug new file mode 100644 index 00000000000..cc7a05b93b5 --- /dev/null +++ b/browser/config/mozconfigs/macosx32-lion/debug @@ -0,0 +1,12 @@ +. $topsrcdir/build/macosx/mozconfig.leopard +ac_add_options --enable-debug +ac_add_options --enable-trace-malloc + +# Enable parallel compiling +mk_add_options MOZ_MAKE_FLAGS="-j12" + +# Needed to enable breakpad in application.ini +export MOZILLA_OFFICIAL=1 + +ac_add_options --with-macbundlename-prefix=Firefox +ac_add_options --with-ccache diff --git a/browser/config/mozconfigs/macosx64-lion/debug b/browser/config/mozconfigs/macosx64-lion/debug new file mode 100644 index 00000000000..73cbd46a179 --- /dev/null +++ b/browser/config/mozconfigs/macosx64-lion/debug @@ -0,0 +1,17 @@ +. $topsrcdir/build/macosx/common + +ac_add_options --enable-debug +ac_add_options --enable-trace-malloc +ac_add_options --enable-accessibility + +# Enable parallel compiling +mk_add_options MOZ_MAKE_FLAGS="-j12" + +# Needed to enable breakpad in application.ini +export MOZILLA_OFFICIAL=1 + +ac_add_options --with-macbundlename-prefix=Firefox + +# Treat warnings as errors in directories with FAIL_ON_WARNINGS. +ac_add_options --enable-warnings-as-errors +ac_add_options --with-ccache diff --git a/browser/config/mozconfigs/macosx64-lion/l10n-mozconfig b/browser/config/mozconfigs/macosx64-lion/l10n-mozconfig new file mode 100644 index 00000000000..304914322ca --- /dev/null +++ b/browser/config/mozconfigs/macosx64-lion/l10n-mozconfig @@ -0,0 +1,5 @@ +ac_add_options --with-l10n-base=../../l10n-central +ac_add_options --enable-official-branding +ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} +ac_add_options --enable-update-packaging +ac_add_options --with-ccache diff --git a/browser/devtools/debugger/test/Makefile.in b/browser/devtools/debugger/test/Makefile.in index 5fded0cdd63..d85d6668285 100644 --- a/browser/devtools/debugger/test/Makefile.in +++ b/browser/devtools/debugger/test/Makefile.in @@ -72,6 +72,7 @@ _BROWSER_TEST_FILES = \ browser_dbg_pause-resume.js \ browser_dbg_update-editor-mode.js \ browser_dbg_select-line.js \ + browser_dbg_clean-exit.js \ head.js \ $(NULL) diff --git a/browser/devtools/debugger/test/browser_dbg_clean-exit.js b/browser/devtools/debugger/test/browser_dbg_clean-exit.js new file mode 100644 index 00000000000..9530cc8f4cd --- /dev/null +++ b/browser/devtools/debugger/test/browser_dbg_clean-exit.js @@ -0,0 +1,42 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// Test that closing a tab with the debugger in a paused state exits cleanly. + +var gPane = null; +var gTab = null; +var gDebuggee = null; +var gDebugger = null; + +const DEBUGGER_TAB_URL = "http://example.com/browser/browser/devtools/" + + "debugger/test/" + + "browser_dbg_debuggerstatement.html"; + +function test() { + debug_tab_pane(DEBUGGER_TAB_URL, function(aTab, aDebuggee, aPane) { + gTab = aTab; + gDebuggee = aDebuggee; + gPane = aPane; + gDebugger = gPane.debuggerWindow; + + testCleanExit(); + }); +} + +function testCleanExit() { + gPane.activeThread.addOneTimeListener("framesadded", function() { + Services.tm.currentThread.dispatch({ run: function() { + is(gDebugger.StackFrames.activeThread.paused, true, + "Should be paused after the debugger statement."); + + gPane._client.addOneTimeListener("tabDetached", function () { + finish(); + }); + removeTab(gTab); + }}, 0); + }); + + gTab.linkedBrowser.contentWindow.wrappedJSObject.runDebuggerStatement(); +} diff --git a/browser/devtools/debugger/test/browser_dbg_select-line.js b/browser/devtools/debugger/test/browser_dbg_select-line.js index 57e7b8186b9..885e5786c96 100644 --- a/browser/devtools/debugger/test/browser_dbg_select-line.js +++ b/browser/devtools/debugger/test/browser_dbg_select-line.js @@ -43,37 +43,41 @@ function testSelectLine() { ok(gDebugger.editor.getText().search(/debugger/) != -1, "The correct script was loaded initially."); - // getCaretPosition is 0-based. - is(gDebugger.editor.getCaretPosition().line, 5, - "The correct line is selected."); + // Yield control back to the event loop so that the debugger has a + // chance to highlight the proper line. + executeSoon(function(){ + // getCaretPosition is 0-based. + is(gDebugger.editor.getCaretPosition().line, 5, + "The correct line is selected."); - gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, - function onChange() { - gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED, - onChange); - ok(gDebugger.editor.getText().search(/debugger/) == -1, - "The second script is no longer displayed."); + gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, + function onChange() { + gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED, + onChange); + ok(gDebugger.editor.getText().search(/debugger/) == -1, + "The second script is no longer displayed."); - ok(gDebugger.editor.getText().search(/firstCall/) != -1, - "The first script is displayed."); + ok(gDebugger.editor.getText().search(/firstCall/) != -1, + "The first script is displayed."); - // Yield control back to the event loop so that the debugger has a - // chance to highlight the proper line. - executeSoon(function(){ - // getCaretPosition is 0-based. - is(gDebugger.editor.getCaretPosition().line, 4, - "The correct line is selected."); + // Yield control back to the event loop so that the debugger has a + // chance to highlight the proper line. + executeSoon(function(){ + // getCaretPosition is 0-based. + is(gDebugger.editor.getCaretPosition().line, 4, + "The correct line is selected."); - gDebugger.StackFrames.activeThread.resume(function() { - removeTab(gTab); - finish(); + gDebugger.StackFrames.activeThread.resume(function() { + removeTab(gTab); + finish(); + }); }); }); - }); - // Click the oldest stack frame. - let element = gDebugger.document.getElementById("stackframe-3"); - EventUtils.synthesizeMouseAtCenter(element, {}, gDebugger); + // Click the oldest stack frame. + let element = gDebugger.document.getElementById("stackframe-3"); + EventUtils.synthesizeMouseAtCenter(element, {}, gDebugger); + }); }}, 0); }); diff --git a/browser/devtools/highlighter/inspector.jsm b/browser/devtools/highlighter/inspector.jsm index 60e7f5e3b30..8191e658689 100644 --- a/browser/devtools/highlighter/inspector.jsm +++ b/browser/devtools/highlighter/inspector.jsm @@ -763,6 +763,9 @@ InspectorUI.prototype = { this.boundRuleViewChanged = this.ruleViewChanged.bind(this); this.ruleView.element.addEventListener("CssRuleViewChanged", this.boundRuleViewChanged); + this.cssRuleViewBoundCSSLinkClicked = this.ruleViewCSSLinkClicked.bind(this); + this.ruleView.element.addEventListener("CssRuleViewCSSLinkClicked", + this.cssRuleViewBoundCSSLinkClicked); doc.documentElement.appendChild(this.ruleView.element); this.ruleView.highlight(this.selection); @@ -800,6 +803,30 @@ InspectorUI.prototype = { this.nodeChanged(this.ruleViewObject); }, + /** + * 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 event containing the style rule to act on + */ + ruleViewCSSLinkClicked: function(aEvent) + { + if (!this.chromeWin) { + return; + } + + let rule = aEvent.detail.rule; + let styleSheet = rule.sheet; + + if (styleSheet) { + this.chromeWin.StyleEditor.openChrome(styleSheet, rule.ruleLine); + } else { + let href = rule.elementStyle.element.ownerDocument.location.href; + this.chromeWin.openUILinkIn("view-source:" + href, "window"); + } + }, + /** * Destroy the rule view. */ @@ -811,6 +838,8 @@ InspectorUI.prototype = { if (this.ruleView) { this.ruleView.element.removeEventListener("CssRuleViewChanged", this.boundRuleViewChanged); + this.ruleView.element.removeEventListener("CssRuleViewCSSLinkClicked", + this.cssRuleViewBoundCSSLinkClicked); delete boundRuleViewChanged; this.ruleView.clear(); delete this.ruleView; diff --git a/browser/devtools/scratchpad/scratchpad.xul b/browser/devtools/scratchpad/scratchpad.xul index c8a4c7e3f7b..eb3c17b08c2 100644 --- a/browser/devtools/scratchpad/scratchpad.xul +++ b/browser/devtools/scratchpad/scratchpad.xul @@ -51,6 +51,7 @@ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" title="&window.title;" windowtype="devtools:scratchpad" + macanimationtype="document" screenX="4" screenY="4" width="640" height="480" persist="screenX screenY width height sizemode"> 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/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/mobile/robocop/Actions.java.in b/build/mobile/robocop/Actions.java.in index 7be67df3cf5..0c123016921 100644 --- a/build/mobile/robocop/Actions.java.in +++ b/build/mobile/robocop/Actions.java.in @@ -41,43 +41,55 @@ package @ANDROID_PACKAGE_NAME@; import java.util.List; public interface Actions { - public enum SpecialKey { - DOWN, UP, LEFT, RIGHT, ENTER, MENU, BACK - } - public interface EventExpecter { - /** Blocks until the event has been received. Subsequent calls will return immediately. */ - public void blockForEvent(); - /** Polls to see if the event has been received. Once this returns true, subsequent calls will also return true. */ - public boolean eventReceived(); - } + /** Special keys supported by sendSpecialKey() */ + public enum SpecialKey { + DOWN, UP, LEFT, RIGHT, ENTER, MENU, BACK + } - public interface RepeatedEventExpecter extends EventExpecter { - /** Blocks until at least one event has been received, and no events have been received in the last millis milliseconds. */ - public void blockUntilClear(long millis); - } + public interface EventExpecter { + /** Blocks until the event has been received. Subsequent calls will return immediately. */ + public void blockForEvent(); - /** - * Listens for a gecko event to be sent from the Gecko instance. - * The returned object can be used to test if the event has been - * received. Note that only one event is listened for. - * - * @param geckoEvent The geckoEvent JSONObject's type - */ - EventExpecter expectGeckoEvent(String geckoEvent); + /** Polls to see if the event has been received. Once this returns true, subsequent calls will also return true. */ + public boolean eventReceived(); + } - /** - * Listens for a paint event. Note that calling expectPaint() will - * invalidate the event expecters returned from any previous calls - * to expectPaint(); calling any methods on those invalidated objects - * will result in undefined behaviour. - */ - RepeatedEventExpecter expectPaint(); + public interface RepeatedEventExpecter extends EventExpecter { + /** Blocks until at least one event has been received, and no events have been received in the last millis milliseconds. */ + public void blockUntilClear(long millis); + } - // Send the string kewsToSend to the application - void sendKeys(String keysToSend); - //Send any of the above keys to the element - void sendSpecialKey(SpecialKey button); + /** + * Listens for a gecko event to be sent from the Gecko instance. + * The returned object can be used to test if the event has been + * received. Note that only one event is listened for. + * + * @param geckoEvent The geckoEvent JSONObject's type + */ + EventExpecter expectGeckoEvent(String geckoEvent); - void drag(int startingX, int endingX, int startingY, int endingY); + /** + * Listens for a paint event. Note that calling expectPaint() will + * invalidate the event expecters returned from any previous calls + * to expectPaint(); calling any methods on those invalidated objects + * will result in undefined behaviour. + */ + RepeatedEventExpecter expectPaint(); + + /** + * Send a string to the application + * + * @param keysToSend The string to send + */ + void sendKeys(String keysToSend); + + /** + * Send a special keycode to the element + * + * @param key The special key to send + */ + void sendSpecialKey(SpecialKey key); + + void drag(int startingX, int endingX, int startingY, int endingY); } diff --git a/build/mobile/robocop/Assert.java.in b/build/mobile/robocop/Assert.java.in index d3afb7bd31c..28a14dbdb5c 100644 --- a/build/mobile/robocop/Assert.java.in +++ b/build/mobile/robocop/Assert.java.in @@ -40,19 +40,19 @@ package @ANDROID_PACKAGE_NAME@; public interface Assert { - void dumpLog(String message); - void setLogFile(String filename); - void setTestName(String testName); + void dumpLog(String message); + void setLogFile(String filename); + void setTestName(String testName); - void finalize(); - void ok(boolean condition, String name, String diag); - void is(Object a, Object b, String name); - void isnot(Object a, Object b, String name); - void todo(boolean condition, String name, String diag); - void todo_is(Object a, Object b, String name); - void todo_isnot(Object a, Object b, String name); - void info(String name, String message); + void finalize(); + void ok(boolean condition, String name, String diag); + void is(Object a, Object b, String name); + void isnot(Object a, Object b, String name); + void todo(boolean condition, String name, String diag); + void todo_is(Object a, Object b, String name); + void todo_isnot(Object a, Object b, String name); + void info(String name, String message); - // robocop-specific asserts - void ispixel(int actual, int r, int g, int b, String name); + // robocop-specific asserts + void ispixel(int actual, int r, int g, int b, String name); } diff --git a/build/mobile/robocop/Driver.java.in b/build/mobile/robocop/Driver.java.in index b026fe47a23..e358ec89a23 100644 --- a/build/mobile/robocop/Driver.java.in +++ b/build/mobile/robocop/Driver.java.in @@ -43,39 +43,39 @@ import java.util.List; import android.app.Activity; public interface Driver { - /** - * Find the first Element using the given method. - * - * @param activity The activity the element belongs to - * @param name The name of the element - * @return The first matching element on the current context - * @throws RoboCopException If no matching elements are found - */ - Element findElement(Activity activity, String name); + /** + * Find the first Element using the given method. + * + * @param activity The activity the element belongs to + * @param name The name of the element + * @return The first matching element on the current context + * @throws RoboCopException If no matching elements are found + */ + Element findElement(Activity activity, String name); - /** - * Sets up scroll handling so that data is received from the extension. - */ - void setupScrollHandling(); + /** + * Sets up scroll handling so that data is received from the extension. + */ + void setupScrollHandling(); - int getPageHeight(); - int getScrollHeight(); - int getHeight(); - int getGeckoTop(); - int getGeckoLeft(); - int getGeckoWidth(); - int getGeckoHeight(); + int getPageHeight(); + int getScrollHeight(); + int getHeight(); + int getGeckoTop(); + int getGeckoLeft(); + int getGeckoWidth(); + int getGeckoHeight(); - void startFrameRecording(); - int stopFrameRecording(); + void startFrameRecording(); + int stopFrameRecording(); - void startCheckerboardRecording(); - float stopCheckerboardRecording(); + void startCheckerboardRecording(); + float stopCheckerboardRecording(); - /** - * Get a copy of the painted content region. - * @return A 2-D array of pixels (indexed by y, then x). The pixels - * are in ARGB-8888 format. - */ - int[][] getPaintedSurface(); + /** + * Get a copy of the painted content region. + * @return A 2-D array of pixels (indexed by y, then x). The pixels + * are in ARGB-8888 format. + */ + int[][] getPaintedSurface(); } diff --git a/build/mobile/robocop/Element.java.in b/build/mobile/robocop/Element.java.in index 76bbbd71673..2c87bd208f9 100644 --- a/build/mobile/robocop/Element.java.in +++ b/build/mobile/robocop/Element.java.in @@ -39,13 +39,21 @@ package @ANDROID_PACKAGE_NAME@; +/** + * Element provides access to a specific UI view (android.view.View). + * See also Driver.findElement(). + */ public interface Element { - //Click on the element - void click(); - //Returns true if the element is currently displayed - boolean isDisplayed(); - //Returns the text currently displayed on the element. - String getText(); - //Returns view ID. - Integer getId(); + + /** Click on the element */ + void click(); + + /** Returns true if the element is currently displayed */ + boolean isDisplayed(); + + /** Returns the text currently displayed on the element */ + String getText(); + + /** Returns the view ID */ + Integer getId(); } diff --git a/build/mobile/robocop/FennecMochitestAssert.java.in b/build/mobile/robocop/FennecMochitestAssert.java.in index 35a59652242..20199ec5110 100644 --- a/build/mobile/robocop/FennecMochitestAssert.java.in +++ b/build/mobile/robocop/FennecMochitestAssert.java.in @@ -38,202 +38,194 @@ package @ANDROID_PACKAGE_NAME@; import java.util.LinkedList; -import java.util.List; -import java.util.Date; +import android.os.SystemClock; public class FennecMochitestAssert implements Assert { - // Objects for reflexive access of fennec classes. + private LinkedList mTestList = new LinkedList(); - private LinkedList testList = new LinkedList(); + // Internal state variables to make logging match up with existing mochitests + private int mLineNumber = 0; + private int mPassed = 0; + private int mFailed = 0; + private int mTodo = 0; + + // Used to write the first line of the test file + private boolean mLogStarted = false; - // Internal state variables to make logging match up with existing mochitests - private int lineNumber = 0; - private int passed = 0; - private int failed = 0; - private int todo = 0; - - // Used to write the first line of the test file - private boolean logStarted = false; + // Used to write the test-start/test-end log lines + private String mLogTestName = ""; - // Used to write the test-start/test-end log lines - private String logTestName = ""; + // Measure the time it takes to run test case + private long mStartTime = 0; - // Measure the time it takes to run test case - private long startTime = 0; - - public FennecMochitestAssert() { - } - - // Write information to a logfile and logcat - public void dumpLog(String message) - { - FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_INFO, message); - } - - // Set the filename used for dumpLog. - public void setLogFile(String filename) - { - FennecNativeDriver.setLogFile(filename); - - String message; - if (!logStarted) { - dumpLog(Integer.toString(lineNumber++) + " INFO SimpleTest START"); - logStarted = true; + public FennecMochitestAssert() { } - if (logTestName != "") { - long diff = (new Date().getTime()) - startTime; - message = Integer.toString(lineNumber++) + " INFO TEST-END | " + logTestName; - message += " | finished in " + diff + "ms"; - dumpLog(message); - logTestName = ""; - } - } - - public void setTestName(String testName) - { - String[] nameParts = testName.split("\\."); - logTestName = nameParts[nameParts.length - 1]; - startTime = new Date().getTime(); - - dumpLog(Integer.toString(lineNumber++) + " INFO TEST-START | " + logTestName); - } - - class testInfo { - public boolean result; - public String name; - public String diag; - public boolean todo; - public testInfo(boolean r, String n, String d, boolean t) { - result = r; - name = n; - diag = d; - todo = t; + /** Write information to a logfile and logcat */ + public void dumpLog(String message) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_INFO, message); } - } + /** Set the filename used for dumpLog. */ + public void setLogFile(String filename) { + FennecNativeDriver.setLogFile(filename); - private void _logMochitestResult(testInfo test, String passString, String failString) - { - boolean isError = true; - String resultString = failString; - if (test.result || test.todo) { - isError = false; - } - if (test.result) - { - resultString = passString; - } - String diag = test.name; - if (test.diag != null) diag += " - " + test.diag; + String message; + if (!mLogStarted) { + dumpLog(Integer.toString(mLineNumber++) + " INFO SimpleTest START"); + mLogStarted = true; + } - String message = Integer.toString(lineNumber++) + " INFO " + resultString + " | " + logTestName + " | " + diag; - dumpLog(message); - - if (test.todo) { - todo++; - } else if (isError) { - failed++; - } else { - passed++; - } - if (isError) { - junit.framework.Assert.fail(message); - } - } - - public void finalize() - { - // It appears that we call finalize during cleanup, this might be an invalid assertion. - String message; - - if (logTestName != "") { - long diff = (new Date().getTime()) - startTime; - message = Integer.toString(lineNumber++) + " INFO TEST-END | " + logTestName; - message += " | finished in " + diff + "ms"; - dumpLog(message); - logTestName = ""; + if (mLogTestName != "") { + long diff = SystemClock.uptimeMillis() - mStartTime; + message = Integer.toString(mLineNumber++) + " INFO TEST-END | " + mLogTestName; + message += " | finished in " + diff + "ms"; + dumpLog(message); + mLogTestName = ""; + } } - message = Integer.toString(lineNumber++) + " INFO TEST-START | Shutdown"; - dumpLog(message); - message = Integer.toString(lineNumber++) + " INFO Passed: " + Integer.toString(passed); - dumpLog(message); - message = Integer.toString(lineNumber++) + " INFO Failed: " + Integer.toString(failed); - dumpLog(message); - message = Integer.toString(lineNumber++) + " INFO Todo: " + Integer.toString(todo); - dumpLog(message); - message = Integer.toString(lineNumber++) + " INFO SimpleTest FINISHED"; - dumpLog(message); - } + public void setTestName(String testName) { + String[] nameParts = testName.split("\\."); + mLogTestName = nameParts[nameParts.length - 1]; + mStartTime = SystemClock.uptimeMillis(); - public void ok(boolean condition, String name, String diag) { - testInfo test = new testInfo(condition, name, diag, false); - _logMochitestResult(test, "TEST-PASS", "TEST-UNEXPECTED-FAIL"); - testList.add(test); - } - - public void is(Object a, Object b, String name) { - boolean pass = a.equals(b); - String diag = "got " + a.toString() + ", expected " + b.toString(); - if(pass) { - diag = a.toString() + " should equal " + b.toString(); + dumpLog(Integer.toString(mLineNumber++) + " INFO TEST-START | " + mLogTestName); } - ok(pass, name, diag); - } - - public void isnot(Object a, Object b, String name) { - boolean pass = !a.equals(b); - String diag = "didn't expect " + a.toString() + ", but got it"; - if(pass) { - diag = a.toString() + " should not equal " + b.toString(); + + class testInfo { + public boolean mResult; + public String mName; + public String mDiag; + public boolean mTodo; + public testInfo(boolean r, String n, String d, boolean t) { + mResult = r; + mName = n; + mDiag = d; + mTodo = t; + } + } - ok(pass, name, diag); - } - public void ispixel(int actual, int r, int g, int b, String name) { - // When we read GL pixels the GPU has already processed them and they - // are usually off by a little bit. For example a CSS-color pixel of color #64FFF5 - // was turned into #63FFF7 when it came out of glReadPixels. So in order to compare - // against the expected value, we use a little fuzz factor. For the alpha we just - // make sure it is always 0xFF. - int aAlpha = ((actual >> 24) & 0xFF); - int aR = ((actual >> 16) & 0xFF); - int aG = ((actual >> 8) & 0xFF); - int aB = (actual & 0xFF); - boolean pass = (aAlpha == 0xFF) /* alpha */ - && (Math.abs(aR - r) < 8) /* red */ - && (Math.abs(aG - g) < 8) /* green */ - && (Math.abs(aB - b) < 8); /* blue */ - ok(pass, name, "Color rgba(" + aR + "," + aG + "," + aB + "," + aAlpha + ")" + (pass ? " " : " not") + " close enough to expected rgb(" + r + "," + g + "," + b + ")"); - } + private void _logMochitestResult(testInfo test, String passString, String failString) { + boolean isError = true; + String resultString = failString; + if (test.mResult || test.mTodo) { + isError = false; + } + if (test.mResult) + { + resultString = passString; + } + String diag = test.mName; + if (test.mDiag != null) diag += " - " + test.mDiag; - public void todo(boolean condition, String name, String diag) { - testInfo test = new testInfo(condition, name, diag, true); - _logMochitestResult(test, "TEST-UNEXPECTED-PASS", "TEST-KNOWN-FAIL"); - testList.add(test); - } + String message = Integer.toString(mLineNumber++) + " INFO " + resultString + " | " + mLogTestName + " | " + diag; + dumpLog(message); - public void todo_is(Object a, Object b, String name) { - boolean pass = a.equals(b); - String diag = "got " + a.toString() + ", expected " + b.toString(); - if(pass) { - diag = a.toString() + " should equal " + b.toString(); + if (test.mTodo) { + mTodo++; + } else if (isError) { + mFailed++; + } else { + mPassed++; + } + if (isError) { + junit.framework.Assert.fail(message); + } } - todo(pass, name, diag); - } - - public void todo_isnot(Object a, Object b, String name) { - boolean pass = !a.equals(b); - String diag = "didn't expect " + a.toString() + ", but got it"; - if(pass) { - diag = a.toString() + " should not equal " + b.toString(); - } - todo(pass, name, diag); - } - public void info(String name, String message) { - testInfo test = new testInfo(true, name, message, false); - _logMochitestResult(test, "TEST-INFO", "INFO FAILED?"); - } + public void finalize() { + // It appears that we call finalize during cleanup, this might be an invalid assertion. + String message; + + if (mLogTestName != "") { + long diff = SystemClock.uptimeMillis() - mStartTime; + message = Integer.toString(mLineNumber++) + " INFO TEST-END | " + mLogTestName; + message += " | finished in " + diff + "ms"; + dumpLog(message); + mLogTestName = ""; + } + + message = Integer.toString(mLineNumber++) + " INFO TEST-START | Shutdown"; + dumpLog(message); + message = Integer.toString(mLineNumber++) + " INFO Passed: " + Integer.toString(mPassed); + dumpLog(message); + message = Integer.toString(mLineNumber++) + " INFO Failed: " + Integer.toString(mFailed); + dumpLog(message); + message = Integer.toString(mLineNumber++) + " INFO Todo: " + Integer.toString(mTodo); + dumpLog(message); + message = Integer.toString(mLineNumber++) + " INFO SimpleTest FINISHED"; + dumpLog(message); + } + + public void ok(boolean condition, String name, String diag) { + testInfo test = new testInfo(condition, name, diag, false); + _logMochitestResult(test, "TEST-PASS", "TEST-UNEXPECTED-FAIL"); + mTestList.add(test); + } + + public void is(Object a, Object b, String name) { + boolean pass = a.equals(b); + String diag = "got " + a.toString() + ", expected " + b.toString(); + if (pass) { + diag = a.toString() + " should equal " + b.toString(); + } + ok(pass, name, diag); + } + + public void isnot(Object a, Object b, String name) { + boolean pass = !a.equals(b); + String diag = "didn't expect " + a.toString() + ", but got it"; + if (pass) { + diag = a.toString() + " should not equal " + b.toString(); + } + ok(pass, name, diag); + } + + public void ispixel(int actual, int r, int g, int b, String name) { + // When we read GL pixels the GPU has already processed them and they + // are usually off by a little bit. For example a CSS-color pixel of color #64FFF5 + // was turned into #63FFF7 when it came out of glReadPixels. So in order to compare + // against the expected value, we use a little fuzz factor. For the alpha we just + // make sure it is always 0xFF. + int aAlpha = ((actual >> 24) & 0xFF); + int aR = ((actual >> 16) & 0xFF); + int aG = ((actual >> 8) & 0xFF); + int aB = (actual & 0xFF); + boolean pass = (aAlpha == 0xFF) /* alpha */ + && (Math.abs(aR - r) < 8) /* red */ + && (Math.abs(aG - g) < 8) /* green */ + && (Math.abs(aB - b) < 8); /* blue */ + ok(pass, name, "Color rgba(" + aR + "," + aG + "," + aB + "," + aAlpha + ")" + (pass ? " " : " not") + " close enough to expected rgb(" + r + "," + g + "," + b + ")"); + } + + public void todo(boolean condition, String name, String diag) { + testInfo test = new testInfo(condition, name, diag, true); + _logMochitestResult(test, "TEST-UNEXPECTED-PASS", "TEST-KNOWN-FAIL"); + mTestList.add(test); + } + + public void todo_is(Object a, Object b, String name) { + boolean pass = a.equals(b); + String diag = "got " + a.toString() + ", expected " + b.toString(); + if (pass) { + diag = a.toString() + " should equal " + b.toString(); + } + todo(pass, name, diag); + } + + public void todo_isnot(Object a, Object b, String name) { + boolean pass = !a.equals(b); + String diag = "didn't expect " + a.toString() + ", but got it"; + if (pass) { + diag = a.toString() + " should not equal " + b.toString(); + } + todo(pass, name, diag); + } + + public void info(String name, String message) { + testInfo test = new testInfo(true, name, message, false); + _logMochitestResult(test, "TEST-INFO", "INFO FAILED?"); + } } diff --git a/build/mobile/robocop/FennecNativeActions.java.in b/build/mobile/robocop/FennecNativeActions.java.in index 1992d87c83e..973a3fd70bb 100644 --- a/build/mobile/robocop/FennecNativeActions.java.in +++ b/build/mobile/robocop/FennecNativeActions.java.in @@ -59,295 +59,294 @@ import org.json.*; import com.jayway.android.robotium.solo.Solo; public class FennecNativeActions implements Actions { - private Solo solo; - private Instrumentation instr; - private Activity geckoApp; + private Solo mSolo; + private Instrumentation mInstr; + private Activity mGeckoApp; - // Objects for reflexive access of fennec classes. - private ClassLoader classLoader; - private Class gel; - private Class ge; - private Class gas; - private Class drawListener; - private Method registerGEL; - private Method unregisterGEL; - private Method sendGE; - private Method getLayerClient; - private Method setDrawListener; + // Objects for reflexive access of fennec classes. + private ClassLoader mClassLoader; + private Class mGel; + private Class mGe; + private Class mGas; + private Class mDrawListener; + private Method mRegisterGEL; + private Method mUnregisterGEL; + private Method mSendGE; + private Method mGetLayerClient; + private Method mSetDrawListener; - public FennecNativeActions(Activity activity, Solo robocop, Instrumentation instrumentation){ - this.solo = robocop; - this.instr = instrumentation; - this.geckoApp = activity; - // Set up reflexive access of java classes and methods. - try { - classLoader = activity.getClassLoader(); - gel = classLoader.loadClass("org.mozilla.gecko.GeckoEventListener"); - ge = classLoader.loadClass("org.mozilla.gecko.GeckoEvent"); - gas = classLoader.loadClass("org.mozilla.gecko.GeckoAppShell"); - Class [] parameters = new Class[2]; - parameters[0] = String.class; - parameters[1] = gel; - registerGEL = gas.getMethod("registerGeckoEventListener", parameters); - unregisterGEL = gas.getMethod("unregisterGeckoEventListener", parameters); - parameters = new Class[1]; - parameters[0] = ge; - sendGE = gas.getMethod("sendEventToGecko", parameters); - - getLayerClient = activity.getClass().getMethod("getSoftwareLayerClient"); - Class gslc = classLoader.loadClass("org.mozilla.gecko.gfx.GeckoSoftwareLayerClient"); - drawListener = classLoader.loadClass("org.mozilla.gecko.gfx.GeckoSoftwareLayerClient$DrawListener"); - setDrawListener = gslc.getDeclaredMethod("setDrawListener", drawListener); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } catch (SecurityException e) { - e.printStackTrace(); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } - } - - class wakeInvocationHandler implements InvocationHandler { - private final GeckoEventExpecter mEventExpecter; - - public wakeInvocationHandler(GeckoEventExpecter expecter) { - mEventExpecter = expecter; - } - - public Object invoke(Object proxy, Method method, Object[] args) { - String methodName = method.getName(); - //Depending on the method, return a completely different type. - if(methodName.equals("toString")) { - return "wakeInvocationHandler"; - } - if(methodName.equals("equals")) { - return this == args[0]; - } - if(methodName.equals("clone")) { - return this; - } - if(methodName.equals("hashCode")) { - return 314; - } - FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG, - "Waking up on "+methodName); - mEventExpecter.notifyOfEvent(); - return null; - } - } - - class GeckoEventExpecter implements EventExpecter { - private final String mGeckoEvent; - private final Object[] mRegistrationParams; - private boolean mEventReceived; - - GeckoEventExpecter(String geckoEvent, Object[] registrationParams) { - mGeckoEvent = geckoEvent; - mRegistrationParams = registrationParams; - } - - public synchronized void blockForEvent() { - while (! mEventReceived) { + public FennecNativeActions(Activity activity, Solo robocop, Instrumentation instrumentation) { + mSolo = robocop; + mInstr = instrumentation; + mGeckoApp = activity; + // Set up reflexive access of java classes and methods. try { - this.wait(); - } catch (InterruptedException ie) { - ie.printStackTrace(); - break; + mClassLoader = activity.getClassLoader(); + mGel = mClassLoader.loadClass("org.mozilla.gecko.GeckoEventListener"); + mGe = mClassLoader.loadClass("org.mozilla.gecko.GeckoEvent"); + mGas = mClassLoader.loadClass("org.mozilla.gecko.GeckoAppShell"); + Class [] parameters = new Class[2]; + parameters[0] = String.class; + parameters[1] = mGel; + mRegisterGEL = mGas.getMethod("registerGeckoEventListener", parameters); + mUnregisterGEL = mGas.getMethod("unregisterGeckoEventListener", parameters); + parameters = new Class[1]; + parameters[0] = mGe; + mSendGE = mGas.getMethod("sendEventToGecko", parameters); + + mGetLayerClient = activity.getClass().getMethod("getSoftwareLayerClient"); + Class gslc = mClassLoader.loadClass("org.mozilla.gecko.gfx.GeckoSoftwareLayerClient"); + mDrawListener = mClassLoader.loadClass("org.mozilla.gecko.gfx.GeckoSoftwareLayerClient$DrawListener"); + mSetDrawListener = gslc.getDeclaredMethod("setDrawListener", mDrawListener); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (SecurityException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } + } + + class wakeInvocationHandler implements InvocationHandler { + private final GeckoEventExpecter mEventExpecter; + + public wakeInvocationHandler(GeckoEventExpecter expecter) { + mEventExpecter = expecter; + } + + public Object invoke(Object proxy, Method method, Object[] args) { + String methodName = method.getName(); + //Depending on the method, return a completely different type. + if(methodName.equals("toString")) { + return "wakeInvocationHandler"; + } + if(methodName.equals("equals")) { + return this == args[0]; + } + if(methodName.equals("clone")) { + return this; + } + if(methodName.equals("hashCode")) { + return 314; + } + FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG, + "Waking up on "+methodName); + mEventExpecter.notifyOfEvent(); + return null; } - } - FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG, - "unblocked on expecter for " + mGeckoEvent); } - public synchronized boolean eventReceived() { - return mEventReceived; - } + class GeckoEventExpecter implements EventExpecter { + private final String mGeckoEvent; + private final Object[] mRegistrationParams; + private boolean mEventReceived; - void notifyOfEvent() { - try { - unregisterGEL.invoke(null, mRegistrationParams); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } - FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG, - "received event " + mGeckoEvent); - synchronized (this) { - mEventReceived = true; - this.notifyAll(); - } - } - } - - public EventExpecter expectGeckoEvent(String geckoEvent) { - FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG, - "waiting for "+geckoEvent); - try { - Class [] interfaces = new Class[1]; - interfaces[0] = gel; - Object[] finalParams = new Object[2]; - finalParams[0] = geckoEvent; - - GeckoEventExpecter expecter = new GeckoEventExpecter(geckoEvent, finalParams); - wakeInvocationHandler wIH = new wakeInvocationHandler(expecter); - Object proxy = Proxy.newProxyInstance(classLoader, interfaces, wIH); - finalParams[1] = proxy; - registerGEL.invoke(null, finalParams); - - return expecter; - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } - return null; - } + GeckoEventExpecter(String geckoEvent, Object[] registrationParams) { + mGeckoEvent = geckoEvent; + mRegistrationParams = registrationParams; + } - class DrawListenerProxy implements InvocationHandler { - private final PaintExpecter mPaintExpecter; + public synchronized void blockForEvent() { + while (! mEventReceived) { + try { + this.wait(); + } catch (InterruptedException ie) { + ie.printStackTrace(); + break; + } + } + FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG, + "unblocked on expecter for " + mGeckoEvent); + } - DrawListenerProxy(PaintExpecter paintExpecter) { - mPaintExpecter = paintExpecter; + public synchronized boolean eventReceived() { + return mEventReceived; + } + + void notifyOfEvent() { + try { + mUnregisterGEL.invoke(null, mRegistrationParams); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG, + "received event " + mGeckoEvent); + synchronized (this) { + mEventReceived = true; + this.notifyAll(); + } + } } - - public Object invoke(Object proxy, Method method, Object[] args) { - String methodName = method.getName(); - if ("drawFinished".equals(methodName)) { + + public EventExpecter expectGeckoEvent(String geckoEvent) { FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG, - "Received drawFinished notification"); - mPaintExpecter.notifyOfEvent(); - } else if ("toString".equals(methodName)) { - return "DrawListenerProxy"; - } else if ("equals".equals(methodName)) { - return false; - } else if ("hashCode".equals(methodName)) { - return 0; - } - return null; - } - } - - class PaintExpecter implements RepeatedEventExpecter { - private Object mLayerClient; - private boolean mPaintDone; - - PaintExpecter() throws IllegalAccessException, InvocationTargetException { - mLayerClient = getLayerClient.invoke(geckoApp); - setDrawListener.invoke(mLayerClient, Proxy.newProxyInstance(classLoader, new Class[] { drawListener }, new DrawListenerProxy(this))); - } - - void notifyOfEvent() { - synchronized (this) { - mPaintDone = true; - this.notifyAll(); - } - } - - public synchronized void blockForEvent() { - while (! mPaintDone) { + "waiting for "+geckoEvent); try { - this.wait(); - } catch (InterruptedException ie) { - ie.printStackTrace(); - break; + Class [] interfaces = new Class[1]; + interfaces[0] = mGel; + Object[] finalParams = new Object[2]; + finalParams[0] = geckoEvent; + + GeckoEventExpecter expecter = new GeckoEventExpecter(geckoEvent, finalParams); + wakeInvocationHandler wIH = new wakeInvocationHandler(expecter); + Object proxy = Proxy.newProxyInstance(mClassLoader, interfaces, wIH); + finalParams[1] = proxy; + mRegisterGEL.invoke(null, finalParams); + + return expecter; + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); } - } - try { - setDrawListener.invoke(mLayerClient, (Object)null); - } catch (Exception e) { - e.printStackTrace(); - } + return null; } - public synchronized boolean eventReceived() { - return mPaintDone; + class DrawListenerProxy implements InvocationHandler { + private final PaintExpecter mPaintExpecter; + + DrawListenerProxy(PaintExpecter paintExpecter) { + mPaintExpecter = paintExpecter; + } + + public Object invoke(Object proxy, Method method, Object[] args) { + String methodName = method.getName(); + if ("drawFinished".equals(methodName)) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG, + "Received drawFinished notification"); + mPaintExpecter.notifyOfEvent(); + } else if ("toString".equals(methodName)) { + return "DrawListenerProxy"; + } else if ("equals".equals(methodName)) { + return false; + } else if ("hashCode".equals(methodName)) { + return 0; + } + return null; + } } - public synchronized void blockUntilClear(long millis) { - if (millis <= 0) { - throw new IllegalArgumentException("millis must be > 0"); - } - // wait for at least one event - while (! mPaintDone) { + class PaintExpecter implements RepeatedEventExpecter { + private Object mLayerClient; + private boolean mPaintDone; + + PaintExpecter() throws IllegalAccessException, InvocationTargetException { + mLayerClient = mGetLayerClient.invoke(mGeckoApp); + mSetDrawListener.invoke(mLayerClient, Proxy.newProxyInstance(mClassLoader, new Class[] { mDrawListener }, new DrawListenerProxy(this))); + } + + void notifyOfEvent() { + synchronized (this) { + mPaintDone = true; + this.notifyAll(); + } + } + + public synchronized void blockForEvent() { + while (!mPaintDone) { + try { + this.wait(); + } catch (InterruptedException ie) { + ie.printStackTrace(); + break; + } + } + try { + mSetDrawListener.invoke(mLayerClient, (Object)null); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public synchronized boolean eventReceived() { + return mPaintDone; + } + + public synchronized void blockUntilClear(long millis) { + if (millis <= 0) { + throw new IllegalArgumentException("millis must be > 0"); + } + // wait for at least one event + while (!mPaintDone) { + try { + this.wait(); + } catch (InterruptedException ie) { + ie.printStackTrace(); + break; + } + } + // now wait for a period of millis where we don't get an event + long startTime = SystemClock.uptimeMillis(); + while (true) { + try { + this.wait(millis); + } catch (InterruptedException ie) { + ie.printStackTrace(); + break; + } + long endTime = SystemClock.uptimeMillis(); + if (endTime - startTime >= millis) { + // success + break; + } + // we got a notify() before we could wait long enough, so we need to start over + startTime = endTime; + } + try { + mSetDrawListener.invoke(mLayerClient, (Object)null); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public RepeatedEventExpecter expectPaint() { try { - this.wait(); - } catch (InterruptedException ie) { - ie.printStackTrace(); - break; + return new PaintExpecter(); + } catch (Exception e) { + e.printStackTrace(); + return null; } - } - // now wait for a period of millis where we don't get an event - long startTime = SystemClock.uptimeMillis(); - while (true) { - try { - this.wait(millis); - } catch (InterruptedException ie) { - ie.printStackTrace(); - break; + } + + public void sendSpecialKey(SpecialKey button) { + switch(button) { + case DOWN: + mInstr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_DOWN); + break; + case UP: + mInstr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_UP); + break; + case LEFT: + mInstr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_LEFT); + break; + case RIGHT: + mInstr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_RIGHT); + break; + case ENTER: + mInstr.sendCharacterSync(KeyEvent.KEYCODE_ENTER); + break; + case MENU: + mInstr.sendCharacterSync(KeyEvent.KEYCODE_MENU); + break; + case BACK: + mInstr.sendCharacterSync(KeyEvent.KEYCODE_BACK); + break; + default: + break; } - long endTime = SystemClock.uptimeMillis(); - if (endTime - startTime >= millis) { - // success - break; - } - // we got a notify() before we could wait long enough, so we need to start over - startTime = endTime; - } - try { - setDrawListener.invoke(mLayerClient, (Object)null); - } catch (Exception e) { - e.printStackTrace(); - } } - } - public RepeatedEventExpecter expectPaint() { - try { - return new PaintExpecter(); - } catch (Exception e) { - e.printStackTrace(); - return null; + @Override + public void sendKeys(String input) { + mInstr.sendStringSync(input); } - } - public void sendSpecialKey(SpecialKey button) { - switch( button) { - case DOWN: - instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_DOWN); - break; - case UP: - instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_UP); - break; - case LEFT: - instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_LEFT); - break; - case RIGHT: - instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_RIGHT); - break; - case ENTER: - instr.sendCharacterSync(KeyEvent.KEYCODE_ENTER); - break; - case MENU: - instr.sendCharacterSync(KeyEvent.KEYCODE_MENU); - break; - case BACK: - instr.sendCharacterSync(KeyEvent.KEYCODE_BACK); - break; - default: - break; + public void drag(int startingX, int endingX, int startingY, int endingY) { + mSolo.drag(startingX, endingX, startingY, endingY, 10); } - } - - @Override - public void sendKeys(String input) { - instr.sendStringSync(input); - } - - - public void drag(int startingX, int endingX, int startingY, int endingY) { - solo.drag(startingX, endingX, startingY, endingY, 10); - } } diff --git a/build/mobile/robocop/FennecNativeDriver.java.in b/build/mobile/robocop/FennecNativeDriver.java.in index 76263ca7d78..eb6d048dcb5 100644 --- a/build/mobile/robocop/FennecNativeDriver.java.in +++ b/build/mobile/robocop/FennecNativeDriver.java.in @@ -46,8 +46,6 @@ import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.nio.IntBuffer; -import java.util.ArrayList; -import java.util.LinkedList; import java.util.HashMap; import java.util.List; @@ -68,396 +66,405 @@ import org.json.*; import com.jayway.android.robotium.solo.Solo; public class FennecNativeDriver implements Driver { - // Map of IDs to element names. - private HashMap locators = null; - private Activity activity; - private Solo solo; + // Map of IDs to element names. + private HashMap mLocators = null; + private Activity mActivity; + private Solo mSolo; - private static String mLogFile = null; - private static LogLevel mLogLevel = LogLevel.LOG_LEVEL_INFO; + private static String mLogFile = null; + private static LogLevel mLogLevel = LogLevel.LOG_LEVEL_INFO; - // Objects for reflexive access of fennec classes. - private ClassLoader classLoader; - private Class gel; - private Class ge; - private Class gas; - private Method registerGEL; - private Method unregisterGEL; - private Method sendGE; - private Method _startFrameRecording; - private Method _stopFrameRecording; - private Method _startCheckerboardRecording; - private Method _stopCheckerboardRecording; - private Method _getPixels; + // Objects for reflexive access of fennec classes. + private ClassLoader mClassLoader; + private Class mGel; + private Class mGe; + private Class mGas; + private Method mRegisterGEL; + private Method mUnregisterGEL; + private Method mSendGE; + private Method _startFrameRecording; + private Method _stopFrameRecording; + private Method _startCheckerboardRecording; + private Method _stopCheckerboardRecording; + private Method _getPixels; - public enum LogLevel { - LOG_LEVEL_DEBUG(1), - LOG_LEVEL_INFO(2), - LOG_LEVEL_WARN(3), - LOG_LEVEL_ERROR(4); + public enum LogLevel { + LOG_LEVEL_DEBUG(1), + LOG_LEVEL_INFO(2), + LOG_LEVEL_WARN(3), + LOG_LEVEL_ERROR(4); - private int mValue; - LogLevel(int value) { - mValue = value; - } - public boolean isEnabled(LogLevel configuredLevel) { - return mValue >= configuredLevel.getValue(); - } - private int getValue() { - return mValue; - } - } - - public FennecNativeDriver(Activity activity, Solo robocop){ - this.activity = activity; - this.solo = robocop; - - // Set up table of fennec_ids. - locators = convertTextToTable(getFile("/mnt/sdcard/fennec_ids.txt")); - - // Set up reflexive access of java classes and methods. - try { - classLoader = activity.getClassLoader(); - gel = classLoader.loadClass("org.mozilla.gecko.GeckoEventListener"); - ge = classLoader.loadClass("org.mozilla.gecko.GeckoEvent"); - gas = classLoader.loadClass("org.mozilla.gecko.GeckoAppShell"); - Class [] parameters = new Class[2]; - parameters[0] = String.class; - parameters[1] = gel; - registerGEL = gas.getMethod("registerGeckoEventListener", parameters); - unregisterGEL = gas.getMethod("unregisterGeckoEventListener", parameters); - parameters = new Class[1]; - parameters[0] = ge; - sendGE = gas.getMethod("sendEventToGecko", parameters); - - Class gfx = classLoader.loadClass("org.mozilla.gecko.gfx.PanningPerfAPI"); - _startFrameRecording = gfx.getDeclaredMethod("startFrameTimeRecording"); - _stopFrameRecording = gfx.getDeclaredMethod("stopFrameTimeRecording"); - _startCheckerboardRecording = gfx.getDeclaredMethod("startCheckerboardRecording"); - _stopCheckerboardRecording = gfx.getDeclaredMethod("stopCheckerboardRecording"); - - Class layerView = classLoader.loadClass("org.mozilla.gecko.gfx.LayerView"); - _getPixels = layerView.getDeclaredMethod("getPixels"); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } catch (SecurityException e) { - e.printStackTrace(); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } - } - - //Information on the location of the Gecko Frame. - private boolean geckoInfo = false; - private int geckoTop = 100; - private int geckoLeft = 0; - private int geckoHeight= 700; - private int geckoWidth = 1024; - - private void getGeckoInfo() { - View geckoLayout = activity.findViewById(Integer.decode((String)locators.get("gecko_layout"))); - if (geckoLayout != null) { - int[] pos = new int[2]; - geckoLayout.getLocationOnScreen(pos); - geckoTop = pos[1]; - geckoLeft = pos[0]; - geckoWidth = geckoLayout.getWidth(); - geckoHeight = geckoLayout.getHeight(); - geckoInfo = true; - } else { - throw new RoboCopException("Unable to find view gecko_layout"); - } - } - - public int getGeckoTop() { - if(!geckoInfo) { - getGeckoInfo(); - } - return geckoTop; - } - - public int getGeckoLeft() { - if(!geckoInfo) { - getGeckoInfo(); - } - return geckoLeft; - } - - public int getGeckoHeight() { - if(!geckoInfo) { - getGeckoInfo(); - } - return geckoHeight; - } - public int getGeckoWidth() { - if(!geckoInfo) { - getGeckoInfo(); - } - return geckoWidth; - } - - public Element findElement(Activity activity, String name) { - if (name == null) - throw new IllegalArgumentException("Can not findElements when passed a null"); - if (locators.containsKey(name)){ - return new FennecNativeElement(Integer.decode((String)locators.get(name)), activity, solo); - } - throw new RoboCopException("Element does not exist in the list"); - } - - public void startFrameRecording() { - try { - Object [] params = null; - _startFrameRecording.invoke(null, params); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } - } - - public int stopFrameRecording() { - Class [] parameters = new Class[1]; - parameters[0] = null; - List frames; - - try { - Object [] params = null; - frames = (List)_stopFrameRecording.invoke(null, params); - Object [] framearray = frames.toArray(); - Long last = new Long(0); - Long threshold = new Long(17); - int numDelays = 0; - for (int i=0; i < framearray.length; i++) { - Long val = (Long)framearray[i]; - if ((val - last) > threshold) { - numDelays++; + private int mValue; + LogLevel(int value) { + mValue = value; } - last = val; - } - return numDelays; - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } - - return 0; - } - - public void startCheckerboardRecording() { - try { - Object [] params = null; - _startCheckerboardRecording.invoke(null, params); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } - } - - public float stopCheckerboardRecording() { - Class [] parameters = new Class[1]; - parameters[0] = null; - List checkerboard; - - try { - Object [] params = null; - checkerboard = (List)_stopCheckerboardRecording.invoke(null, params); - Object [] amountarray = checkerboard.toArray(); - double completeness = 0; - for (Object obj : amountarray) { - float val = (Float)obj; - completeness += (1.0 - (double)val) / (double)amountarray.length; - } - return (float)completeness; - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } - - return 0.0f; - } - - private GLSurfaceView getSurfaceView() { - for (View v : solo.getCurrentViews()) { - if (v instanceof GLSurfaceView) { - return (GLSurfaceView)v; - } - } - return null; - } - - public int[][] getPaintedSurface() { - GLSurfaceView view = getSurfaceView(); - if (view == null) { - return null; - } - IntBuffer pixelBuffer; - try { - pixelBuffer = (IntBuffer)_getPixels.invoke(view); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - - // now we need to (1) flip the image, because GL likes to do things up-side-down, - // and (2) rearrange the bits from AGBR-8888 to ARGB-8888. - int w = view.getWidth(); - int h = view.getHeight(); - pixelBuffer.position(0); - int[][] pixels = new int[h][w]; - for (int y = h - 1; y >= 0; y--) { - for (int x = 0; x < w; x++) { - int agbr = pixelBuffer.get(); - pixels[y][x] = (agbr & 0xFF00FF00) | ((agbr >> 16) & 0x000000FF) | ((agbr << 16) & 0x00FF0000); - } - } - return pixels; - } - - class scrollHandler implements InvocationHandler { - public scrollHandler(){}; - public Object invoke(Object proxy, Method method, Object[] args) { - try{ - //Disect the JSON object into the appropriate variables - JSONObject jo = ((JSONObject)args[1]); - scrollHeight = jo.getInt("y"); - height = jo.getInt("cheight"); - //We don't want a height of 0. That means it's a bad response. - if( height > 0) { - pageHeight = jo.getInt("height"); + public boolean isEnabled(LogLevel configuredLevel) { + return mValue >= configuredLevel.getValue(); + } + private int getValue() { + return mValue; } - - } catch( Throwable e) { - FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_WARN, - "WARNING: ScrollReceived, but read wrong!"); - } - return null; - } - } - public int getScrollHeight() { - return scrollHeight; - } - public int getPageHeight() { - return pageHeight; - } - public int getHeight() { - return height; - } - - public int height=0; - public int scrollHeight=0; - public int pageHeight=10; - public void setupScrollHandling() { - //Setup scrollHandler to catch "robocop:scroll" events. - try { - Class [] interfaces = new Class[1]; - interfaces[0] = gel; - Object[] finalParams = new Object[2]; - finalParams[0] = "robocop:scroll"; - finalParams[1] = Proxy.newProxyInstance(classLoader, interfaces, new scrollHandler()); - registerGEL.invoke(null, finalParams); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); } - } + public FennecNativeDriver(Activity activity, Solo robocop) { + mActivity = activity; + mSolo = robocop; - //Takes a filename, loads the file, - // and returns a string version of the entire file. - public static String getFile(String filename) - { - StringBuilder text = new StringBuilder(); + // Set up table of fennec_ids. + mLocators = convertTextToTable(getFile("/mnt/sdcard/fennec_ids.txt")); - BufferedReader br = null; - try { - br = new BufferedReader(new FileReader(filename)); - String line; - - while ((line = br.readLine()) != null) { - text.append(line); - text.append('\n'); - } - } catch(IOException e) { - e.printStackTrace(); - } finally { - try { - br.close(); - } catch (IOException e) { - } - } - return text.toString(); - } - - // Takes a string of "key=value" pairs split by \n and creates a hash table. - public static HashMap convertTextToTable(String data) - { - HashMap retVal = new HashMap(); - - String[] lines = data.split("\n"); - for (int i = 0; i < lines.length; i++) { - String[] parts = lines[i].split("="); - retVal.put(parts[0].trim(), parts[1].trim()); - } - return retVal; - } - - // Set the filename used for logging. If the file already exists, delete it - // as a safe-guard against accidentally appending to an old log file. - public static void setLogFile(String filename) { - mLogFile = filename; - File file = new File(mLogFile); - if (file.exists()) { - file.delete(); - } - } - - public static void setLogLevel(LogLevel level) { - mLogLevel = level; - } - - public static void log(LogLevel level, String message) { - if (mLogFile == null) { - assert(false); - } - - if (level.isEnabled(mLogLevel)) { - File file = new File(mLogFile); - BufferedWriter bw = null; - - try { - bw = new BufferedWriter(new FileWriter(mLogFile, true)); - bw.write(message); - bw.newLine(); - } catch(IOException e) { - Log.e("Robocop", "exception with file writer on: " + mLogFile); - } finally { + // Set up reflexive access of java classes and methods. try { - if (bw != null) { - bw.flush(); - bw.close(); - } - } catch (IOException ex) { - ex.printStackTrace(); - } - } + mClassLoader = activity.getClassLoader(); + mGel = mClassLoader.loadClass("org.mozilla.gecko.GeckoEventListener"); + mGe = mClassLoader.loadClass("org.mozilla.gecko.GeckoEvent"); + mGas = mClassLoader.loadClass("org.mozilla.gecko.GeckoAppShell"); + Class [] parameters = new Class[2]; + parameters[0] = String.class; + parameters[1] = mGel; + mRegisterGEL = mGas.getMethod("registerGeckoEventListener", parameters); + mUnregisterGEL = mGas.getMethod("unregisterGeckoEventListener", parameters); + parameters = new Class[1]; + parameters[0] = mGe; + mSendGE = mGas.getMethod("sendEventToGecko", parameters); + + Class gfx = mClassLoader.loadClass("org.mozilla.gecko.gfx.PanningPerfAPI"); + _startFrameRecording = gfx.getDeclaredMethod("startFrameTimeRecording"); + _stopFrameRecording = gfx.getDeclaredMethod("stopFrameTimeRecording"); + _startCheckerboardRecording = gfx.getDeclaredMethod("startCheckerboardRecording"); + _stopCheckerboardRecording = gfx.getDeclaredMethod("stopCheckerboardRecording"); + + Class layerView = mClassLoader.loadClass("org.mozilla.gecko.gfx.LayerView"); + _getPixels = layerView.getDeclaredMethod("getPixels"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (SecurityException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } } - if (level == LogLevel.LOG_LEVEL_INFO) { - Log.i("Robocop", message); - } else if (level == LogLevel.LOG_LEVEL_DEBUG) { - Log.d("Robocop", message); - } else if (level == LogLevel.LOG_LEVEL_WARN) { - Log.w("Robocop", message); - } else if (level == LogLevel.LOG_LEVEL_ERROR) { - Log.e("Robocop", message); + //Information on the location of the Gecko Frame. + private boolean mGeckoInfo = false; + private int mGeckoTop = 100; + private int mGeckoLeft = 0; + private int mGeckoHeight= 700; + private int mGeckoWidth = 1024; + + private void getGeckoInfo() { + View geckoLayout = mActivity.findViewById(Integer.decode((String)mLocators.get("gecko_layout"))); + if (geckoLayout != null) { + int[] pos = new int[2]; + geckoLayout.getLocationOnScreen(pos); + mGeckoTop = pos[1]; + mGeckoLeft = pos[0]; + mGeckoWidth = geckoLayout.getWidth(); + mGeckoHeight = geckoLayout.getHeight(); + mGeckoInfo = true; + } else { + throw new RoboCopException("Unable to find view gecko_layout"); + } + } + + public int getGeckoTop() { + if (!mGeckoInfo) { + getGeckoInfo(); + } + return mGeckoTop; + } + + public int getGeckoLeft() { + if (!mGeckoInfo) { + getGeckoInfo(); + } + return mGeckoLeft; + } + + public int getGeckoHeight() { + if (!mGeckoInfo) { + getGeckoInfo(); + } + return mGeckoHeight; + } + + public int getGeckoWidth() { + if (!mGeckoInfo) { + getGeckoInfo(); + } + return mGeckoWidth; + } + + public Element findElement(Activity activity, String name) { + if (name == null) { + throw new IllegalArgumentException("Can not findElements when passed a null"); + } + if (mLocators.containsKey(name)) { + return new FennecNativeElement(Integer.decode((String)mLocators.get(name)), activity, mSolo); + } + throw new RoboCopException("Element does not exist in the list"); + } + + public void startFrameRecording() { + try { + Object [] params = null; + _startFrameRecording.invoke(null, params); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + } + + public int stopFrameRecording() { + Class [] parameters = new Class[1]; + parameters[0] = null; + List frames; + + try { + Object [] params = null; + frames = (List)_stopFrameRecording.invoke(null, params); + Object [] framearray = frames.toArray(); + Long last = new Long(0); + Long threshold = new Long(17); + int numDelays = 0; + for (int i=0; i < framearray.length; i++) { + Long val = (Long)framearray[i]; + if ((val - last) > threshold) { + numDelays++; + } + last = val; + } + return numDelays; + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + + return 0; + } + + public void startCheckerboardRecording() { + try { + Object [] params = null; + _startCheckerboardRecording.invoke(null, params); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + } + + public float stopCheckerboardRecording() { + Class [] parameters = new Class[1]; + parameters[0] = null; + List checkerboard; + + try { + Object [] params = null; + checkerboard = (List)_stopCheckerboardRecording.invoke(null, params); + Object [] amountarray = checkerboard.toArray(); + double completeness = 0; + for (Object obj : amountarray) { + float val = (Float)obj; + completeness += (1.0 - (double)val) / (double)amountarray.length; + } + return (float)completeness; + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + + return 0.0f; + } + + private GLSurfaceView getSurfaceView() { + for (View v : mSolo.getCurrentViews()) { + if (v instanceof GLSurfaceView) { + return (GLSurfaceView)v; + } + } + return null; + } + + public int[][] getPaintedSurface() { + GLSurfaceView view = getSurfaceView(); + if (view == null) { + return null; + } + IntBuffer pixelBuffer; + try { + pixelBuffer = (IntBuffer)_getPixels.invoke(view); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + + // now we need to (1) flip the image, because GL likes to do things up-side-down, + // and (2) rearrange the bits from AGBR-8888 to ARGB-8888. + int w = view.getWidth(); + int h = view.getHeight(); + pixelBuffer.position(0); + int[][] pixels = new int[h][w]; + for (int y = h - 1; y >= 0; y--) { + for (int x = 0; x < w; x++) { + int agbr = pixelBuffer.get(); + pixels[y][x] = (agbr & 0xFF00FF00) | ((agbr >> 16) & 0x000000FF) | ((agbr << 16) & 0x00FF0000); + } + } + return pixels; + } + + public int mHeight=0; + public int mScrollHeight=0; + public int mPageHeight=10; + + class scrollHandler implements InvocationHandler { + public scrollHandler(){}; + public Object invoke(Object proxy, Method method, Object[] args) { + try { + // Disect the JSON object into the appropriate variables + JSONObject jo = ((JSONObject)args[1]); + mScrollHeight = jo.getInt("y"); + mHeight = jo.getInt("cheight"); + // We don't want a height of 0. That means it's a bad response. + if (mHeight > 0) { + mPageHeight = jo.getInt("height"); + } + + } catch( Throwable e) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_WARN, + "WARNING: ScrollReceived, but read wrong!"); + } + return null; + } + } + + public int getScrollHeight() { + return mScrollHeight; + } + public int getPageHeight() { + return mPageHeight; + } + public int getHeight() { + return mHeight; + } + + public void setupScrollHandling() { + //Setup scrollHandler to catch "robocop:scroll" events. + try { + Class [] interfaces = new Class[1]; + interfaces[0] = mGel; + Object[] finalParams = new Object[2]; + finalParams[0] = "robocop:scroll"; + finalParams[1] = Proxy.newProxyInstance(mClassLoader, interfaces, new scrollHandler()); + mRegisterGEL.invoke(null, finalParams); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + + } + + /** + * Takes a filename, loads the file, and returns a string version of the entire file. + */ + public static String getFile(String filename) + { + StringBuilder text = new StringBuilder(); + + BufferedReader br = null; + try { + br = new BufferedReader(new FileReader(filename)); + String line; + + while ((line = br.readLine()) != null) { + text.append(line); + text.append('\n'); + } + } catch(IOException e) { + e.printStackTrace(); + } finally { + try { + br.close(); + } catch (IOException e) { + } + } + return text.toString(); + } + + /** + * Takes a string of "key=value" pairs split by \n and creates a hash table. + */ + public static HashMap convertTextToTable(String data) + { + HashMap retVal = new HashMap(); + + String[] lines = data.split("\n"); + for (int i = 0; i < lines.length; i++) { + String[] parts = lines[i].split("="); + retVal.put(parts[0].trim(), parts[1].trim()); + } + return retVal; + } + + /** + * Set the filename used for logging. If the file already exists, delete it + * as a safe-guard against accidentally appending to an old log file. + */ + public static void setLogFile(String filename) { + mLogFile = filename; + File file = new File(mLogFile); + if (file.exists()) { + file.delete(); + } + } + + public static void setLogLevel(LogLevel level) { + mLogLevel = level; + } + + public static void log(LogLevel level, String message) { + if (mLogFile == null) { + assert(false); + } + + if (level.isEnabled(mLogLevel)) { + File file = new File(mLogFile); + BufferedWriter bw = null; + + try { + bw = new BufferedWriter(new FileWriter(mLogFile, true)); + bw.write(message); + bw.newLine(); + } catch(IOException e) { + Log.e("Robocop", "exception with file writer on: " + mLogFile); + } finally { + try { + if (bw != null) { + bw.flush(); + bw.close(); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + } + + if (level == LogLevel.LOG_LEVEL_INFO) { + Log.i("Robocop", message); + } else if (level == LogLevel.LOG_LEVEL_DEBUG) { + Log.d("Robocop", message); + } else if (level == LogLevel.LOG_LEVEL_WARN) { + Log.w("Robocop", message); + } else if (level == LogLevel.LOG_LEVEL_ERROR) { + Log.e("Robocop", message); + } } - } } diff --git a/build/mobile/robocop/FennecNativeElement.java.in b/build/mobile/robocop/FennecNativeElement.java.in index bbbe0430404..0785a0de2bd 100644 --- a/build/mobile/robocop/FennecNativeElement.java.in +++ b/build/mobile/robocop/FennecNativeElement.java.in @@ -42,7 +42,6 @@ package @ANDROID_PACKAGE_NAME@; import java.util.List; import android.app.Activity; -import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; import android.widget.Button; @@ -55,110 +54,110 @@ import com.jayway.android.robotium.solo.Solo; import java.util.concurrent.SynchronousQueue; public class FennecNativeElement implements Element { - private final Activity mActivity; - private Integer id; - private Solo robocop; + private final Activity mActivity; + private Integer mId; + private Solo mSolo; - public FennecNativeElement(Integer id, Activity activity, Solo solo){ - this.id = id; - mActivity = activity; - robocop = solo; - } - - public Integer getId() { - return id; - } - - public void click() { - final SynchronousQueue syncQueue = new SynchronousQueue(); - mActivity.runOnUiThread( - new Runnable() { - public void run() { - View view = (View)mActivity.findViewById(id); - if(view != null) { - if (!view.performClick()) { - FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_WARN, - "Robocop called click on an element with no listener"); - } - } else { - throw new RoboCopException("click: unable to find view "+id); - } - syncQueue.offer(new Object()); - } - }); - try { - syncQueue.take(); - } catch (InterruptedException e) { - e.printStackTrace(); + public FennecNativeElement(Integer id, Activity activity, Solo solo) { + mId = id; + mActivity = activity; + mSolo = solo; } - } - private Object text; + public Integer getId() { + return mId; + } - public String getText() { - final SynchronousQueue syncQueue = new SynchronousQueue(); - mActivity.runOnUiThread( - new Runnable() { - public void run() { - View v = mActivity.findViewById(id); - if(v instanceof EditText) { - EditText et = (EditText)v; - text = et.getEditableText(); - }else if(v instanceof TextSwitcher) { - TextSwitcher ts = (TextSwitcher)v; - ts.getNextView(); - text = ((TextView)ts.getCurrentView()).getText(); - }else if(v instanceof ViewGroup) { - ViewGroup vg = (ViewGroup)v; - for(int i = 0; i < vg.getChildCount(); i++) { - if(vg.getChildAt(i) instanceof TextView) { - text = ((TextView)vg.getChildAt(i)).getText(); + public void click() { + final SynchronousQueue syncQueue = new SynchronousQueue(); + mActivity.runOnUiThread( + new Runnable() { + public void run() { + View view = (View)mActivity.findViewById(mId); + if(view != null) { + if (!view.performClick()) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_WARN, + "Robocop called click on an element with no listener"); + } + } else { + throw new RoboCopException("click: unable to find view "+mId); + } + syncQueue.offer(new Object()); } - } //end of for - } else if(v instanceof TextView) { - text = ((TextView)v).getText(); - } else if(v == null) { - throw new RoboCopException("getText: unable to find view "+id); - } else { - throw new RoboCopException("getText: unhandled type for view "+id); - } - syncQueue.offer(new Object()); - } // end of run() method definition - } // end of anonymous Runnable object instantiation - ); - try { - //Wait for the UiThread code to finish running - syncQueue.take(); - } catch (InterruptedException e) { - e.printStackTrace(); + }); + try { + syncQueue.take(); + } catch (InterruptedException e) { + e.printStackTrace(); + } } - if(text == null) { - throw new RoboCopException("getText: Text is null for view "+id); - } - return text.toString(); - } - private boolean displayed; + private Object mText; - public boolean isDisplayed() { - final SynchronousQueue syncQueue = new SynchronousQueue(); - displayed = false; - mActivity.runOnUiThread( - new Runnable() { - public void run() { - View view = (View)mActivity.findViewById(id); - if(view != null) { - displayed = true; - } - syncQueue.offer(new Object()); - } - }); - try { - syncQueue.take(); - } catch (InterruptedException e) { - e.printStackTrace(); + public String getText() { + final SynchronousQueue syncQueue = new SynchronousQueue(); + mActivity.runOnUiThread( + new Runnable() { + public void run() { + View v = mActivity.findViewById(mId); + if (v instanceof EditText) { + EditText et = (EditText)v; + mText = et.getEditableText(); + } else if (v instanceof TextSwitcher) { + TextSwitcher ts = (TextSwitcher)v; + ts.getNextView(); + mText = ((TextView)ts.getCurrentView()).getText(); + } else if (v instanceof ViewGroup) { + ViewGroup vg = (ViewGroup)v; + for (int i = 0; i < vg.getChildCount(); i++) { + if (vg.getChildAt(i) instanceof TextView) { + mText = ((TextView)vg.getChildAt(i)).getText(); + } + } + } else if (v instanceof TextView) { + mText = ((TextView)v).getText(); + } else if (v == null) { + throw new RoboCopException("getText: unable to find view "+mId); + } else { + throw new RoboCopException("getText: unhandled type for view "+mId); + } + syncQueue.offer(new Object()); + } // end of run() method definition + } // end of anonymous Runnable object instantiation + ); + try { + // Wait for the UiThread code to finish running + syncQueue.take(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + if (mText == null) { + throw new RoboCopException("getText: Text is null for view "+mId); + } + return mText.toString(); + } + + private boolean mDisplayed; + + public boolean isDisplayed() { + final SynchronousQueue syncQueue = new SynchronousQueue(); + mDisplayed = false; + mActivity.runOnUiThread( + new Runnable() { + public void run() { + View view = (View)mActivity.findViewById(mId); + if (view != null) { + mDisplayed = true; + } + syncQueue.offer(new Object()); + } + }); + try { + syncQueue.take(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return mDisplayed; } - return displayed; - } } diff --git a/build/mobile/robocop/FennecTalosAssert.java.in b/build/mobile/robocop/FennecTalosAssert.java.in index 5fa85a4405f..9a9d75511d4 100644 --- a/build/mobile/robocop/FennecTalosAssert.java.in +++ b/build/mobile/robocop/FennecTalosAssert.java.in @@ -39,52 +39,40 @@ package @ANDROID_PACKAGE_NAME@; public class FennecTalosAssert implements Assert { - - public FennecTalosAssert() { - } + + public FennecTalosAssert() { } - // Write information to a logfile and logcat - public void dumpLog(String message) - { - FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_INFO, message); - } + /** + * Write information to a logfile and logcat + */ + public void dumpLog(String message) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_INFO, message); + } - // Set the filename used for dumpLog. - public void setLogFile(String filename) - { - FennecNativeDriver.setLogFile(filename); - } + /** + * Set the filename used for dumpLog. + */ + public void setLogFile(String filename) { + FennecNativeDriver.setLogFile(filename); + } - public void setTestName(String testName) - { - } + public void setTestName(String testName) { } + public void finalize() { } - public void finalize() - { - } + public void ok(boolean condition, String name, String diag) { } - public void ok(boolean condition, String name, String diag) { - } + public void is(Object a, Object b, String name) { } + + public void isnot(Object a, Object b, String name) { } - public void is(Object a, Object b, String name) { - } - - public void isnot(Object a, Object b, String name) { - } + public void ispixel(int actual, int r, int g, int b, String name) { } - public void ispixel(int actual, int r, int g, int b, String name) { - } + public void todo(boolean condition, String name, String diag) { } - public void todo(boolean condition, String name, String diag) { - } + public void todo_is(Object a, Object b, String name) { } + + public void todo_isnot(Object a, Object b, String name) { } - public void todo_is(Object a, Object b, String name) { - } - - public void todo_isnot(Object a, Object b, String name) { - } - - public void info(String name, String message) { - } + public void info(String name, String message) { } } diff --git a/build/mobile/robocop/RoboCopException.java.in b/build/mobile/robocop/RoboCopException.java.in index de908cc3213..adc5b6af519 100644 --- a/build/mobile/robocop/RoboCopException.java.in +++ b/build/mobile/robocop/RoboCopException.java.in @@ -40,20 +40,20 @@ package @ANDROID_PACKAGE_NAME@; public class RoboCopException extends RuntimeException { - - public RoboCopException(){ - super(); - } - - public RoboCopException(String message){ - super(message); - } - - public RoboCopException(Throwable cause){ - super(cause); - } - - public RoboCopException(String message, Throwable cause){ - super(message, cause); - } + + public RoboCopException() { + super(); + } + + public RoboCopException(String message) { + super(message); + } + + public RoboCopException(Throwable cause) { + super(cause); + } + + public RoboCopException(String message, Throwable cause) { + super(message, cause); + } } 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= @@ -4605,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 @@ -6163,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. @@ -8565,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. @@ -8736,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 @@ -9070,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/nsAttrAndChildArray.h b/content/base/src/nsAttrAndChildArray.h index a3e66c1e401..2e56ac5ddfe 100644 --- a/content/base/src/nsAttrAndChildArray.h +++ b/content/base/src/nsAttrAndChildArray.h @@ -136,6 +136,10 @@ public: } PRInt64 SizeOf() const; + bool HasMappedAttrs() const + { + return MappedAttrCount(); + } private: nsAttrAndChildArray(const nsAttrAndChildArray& aOther) MOZ_DELETE; diff --git a/content/base/src/nsAttrValue.cpp b/content/base/src/nsAttrValue.cpp index f0791f2b99a..dd9b995358f 100644 --- a/content/base/src/nsAttrValue.cpp +++ b/content/base/src/nsAttrValue.cpp @@ -54,6 +54,8 @@ namespace css = mozilla::css; +using mozilla::SVGAttrValueWrapper; + #define MISC_STR_PTR(_cont) \ reinterpret_cast((_cont)->mStringBits & NS_ATTRVALUE_POINTERVALUE_MASK) @@ -76,6 +78,12 @@ nsAttrValue::nsAttrValue(const nsAString& aValue) SetTo(aValue); } +nsAttrValue::nsAttrValue(nsIAtom* aValue) + : mBits(0) +{ + SetTo(aValue); +} + nsAttrValue::nsAttrValue(css::StyleRule* aValue, const nsAString* aSerialized) : mBits(0) { @@ -260,7 +268,13 @@ nsAttrValue::SetTo(const nsAttrValue& aOther) } default: { - NS_NOTREACHED("unknown type stored in MiscContainer"); + if (IsSVGType(otherCont->mType)) { + // All SVG types are just pointers to classes and will therefore have + // the same size so it doesn't really matter which one we assign + cont->mSVGAngle = otherCont->mSVGAngle; + } else { + NS_NOTREACHED("unknown type stored in MiscContainer"); + } break; } } @@ -290,6 +304,16 @@ nsAttrValue::SetTo(const nsAString& aValue) } } +void +nsAttrValue::SetTo(nsIAtom* aValue) +{ + ResetIfSet(); + if (aValue) { + NS_ADDREF(aValue); + SetPtrValueAndType(aValue, eAtomBase); + } +} + void nsAttrValue::SetTo(PRInt16 aInt) { @@ -297,6 +321,24 @@ nsAttrValue::SetTo(PRInt16 aInt) SetIntValueAndType(aInt, eInteger, nsnull); } +void +nsAttrValue::SetTo(PRInt32 aInt, const nsAString* aSerialized) +{ + ResetIfSet(); + SetIntValueAndType(aInt, eInteger, aSerialized); +} + +void +nsAttrValue::SetTo(double aValue, const nsAString* aSerialized) +{ + if (EnsureEmptyMiscContainer()) { + MiscContainer* cont = GetMiscContainer(); + cont->mDoubleValue = aValue; + cont->mType = eDoubleValue; + SetMiscAtomOrString(aSerialized); + } +} + void nsAttrValue::SetTo(css::StyleRule* aValue, const nsAString* aSerialized) { @@ -331,6 +373,115 @@ nsAttrValue::SetToSerialized(const nsAttrValue& aOther) } } +void +nsAttrValue::SetTo(const nsSVGAngle& aValue, const nsAString* aSerialized) +{ + SetSVGType(eSVGAngle, &aValue, aSerialized); +} + +void +nsAttrValue::SetTo(const nsSVGIntegerPair& aValue, const nsAString* aSerialized) +{ + SetSVGType(eSVGIntegerPair, &aValue, aSerialized); +} + +void +nsAttrValue::SetTo(const nsSVGLength2& aValue, const nsAString* aSerialized) +{ + SetSVGType(eSVGLength, &aValue, aSerialized); +} + +void +nsAttrValue::SetTo(const mozilla::SVGLengthList& aValue, + const nsAString* aSerialized) +{ + // While an empty string will parse as a length list, there's no need to store + // it (and SetMiscAtomOrString will assert if we try) + if (aSerialized && aSerialized->IsEmpty()) { + aSerialized = nsnull; + } + SetSVGType(eSVGLengthList, &aValue, aSerialized); +} + +void +nsAttrValue::SetTo(const mozilla::SVGNumberList& aValue, + const nsAString* aSerialized) +{ + // While an empty string will parse as a number list, there's no need to store + // it (and SetMiscAtomOrString will assert if we try) + if (aSerialized && aSerialized->IsEmpty()) { + aSerialized = nsnull; + } + SetSVGType(eSVGNumberList, &aValue, aSerialized); +} + +void +nsAttrValue::SetTo(const nsSVGNumberPair& aValue, const nsAString* aSerialized) +{ + SetSVGType(eSVGNumberPair, &aValue, aSerialized); +} + +void +nsAttrValue::SetTo(const mozilla::SVGPathData& aValue, + const nsAString* aSerialized) +{ + // While an empty string will parse as path data, there's no need to store it + // (and SetMiscAtomOrString will assert if we try) + if (aSerialized && aSerialized->IsEmpty()) { + aSerialized = nsnull; + } + SetSVGType(eSVGPathData, &aValue, aSerialized); +} + +void +nsAttrValue::SetTo(const mozilla::SVGPointList& aValue, + const nsAString* aSerialized) +{ + // While an empty string will parse as a point list, there's no need to store + // it (and SetMiscAtomOrString will assert if we try) + if (aSerialized && aSerialized->IsEmpty()) { + aSerialized = nsnull; + } + SetSVGType(eSVGPointList, &aValue, aSerialized); +} + +void +nsAttrValue::SetTo(const mozilla::SVGAnimatedPreserveAspectRatio& aValue, + const nsAString* aSerialized) +{ + SetSVGType(eSVGPreserveAspectRatio, &aValue, aSerialized); +} + +void +nsAttrValue::SetTo(const mozilla::SVGStringList& aValue, + const nsAString* aSerialized) +{ + // While an empty string will parse as a string list, there's no need to store + // it (and SetMiscAtomOrString will assert if we try) + if (aSerialized && aSerialized->IsEmpty()) { + aSerialized = nsnull; + } + SetSVGType(eSVGStringList, &aValue, aSerialized); +} + +void +nsAttrValue::SetTo(const mozilla::SVGTransformList& aValue, + const nsAString* aSerialized) +{ + // While an empty string will parse as a transform list, there's no need to + // store it (and SetMiscAtomOrString will assert if we try) + if (aSerialized && aSerialized->IsEmpty()) { + aSerialized = nsnull; + } + SetSVGType(eSVGTransformList, &aValue, aSerialized); +} + +void +nsAttrValue::SetTo(const nsSVGViewBox& aValue, const nsAString* aSerialized) +{ + SetSVGType(eSVGViewBox, &aValue, aSerialized); +} + void nsAttrValue::SwapValueWith(nsAttrValue& aOther) { @@ -428,6 +579,73 @@ nsAttrValue::ToString(nsAString& aResult) const aResult.AppendFloat(GetDoubleValue()); break; } + case eSVGAngle: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGAngle, aResult); + break; + } + case eSVGIntegerPair: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGIntegerPair, + aResult); + break; + } + case eSVGLength: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGLength, aResult); + break; + } + case eSVGLengthList: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGLengthList, + aResult); + break; + } + case eSVGNumberList: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGNumberList, + aResult); + break; + } + case eSVGNumberPair: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGNumberPair, + aResult); + break; + } + case eSVGPathData: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGPathData, aResult); + break; + } + case eSVGPointList: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGPointList, aResult); + break; + } + case eSVGPreserveAspectRatio: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGPreserveAspectRatio, + aResult); + break; + } + case eSVGStringList: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGStringList, + aResult); + break; + } + case eSVGTransformList: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGTransformList, + aResult); + break; + } + case eSVGViewBox: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGViewBox, aResult); + break; + } default: { aResult.Truncate(); @@ -614,6 +832,10 @@ nsAttrValue::HashValue() const } default: { + if (IsSVGType(cont->mType)) { + // All SVG types are just pointers to classes so we can treat them alike + return NS_PTR_TO_INT32(cont->mSVGAngle); + } NS_NOTREACHED("unknown type stored in MiscContainer"); return 0; } @@ -706,6 +928,16 @@ nsAttrValue::Equals(const nsAttrValue& aOther) const } default: { + if (IsSVGType(thisCont->mType)) { + // Currently this method is never called for nsAttrValue objects that + // point to SVG data types. + // If that changes then we probably want to add methods to the + // corresponding SVG types to compare their base values. + // As a shortcut, however, we can begin by comparing the pointers. + NS_ABORT_IF_FALSE(false, "Comparing nsAttrValues that point to SVG " + "data"); + return false; + } NS_NOTREACHED("unknown type stored in MiscContainer"); return false; } @@ -1351,6 +1583,22 @@ nsAttrValue::ResetMiscAtomOrString() } } +void +nsAttrValue::SetSVGType(ValueType aType, const void* aValue, + const nsAString* aSerialized) { + NS_ABORT_IF_FALSE(IsSVGType(aType), "Not an SVG type"); + if (EnsureEmptyMiscContainer()) { + MiscContainer* cont = GetMiscContainer(); + // All SVG types are just pointers to classes so just setting any of them + // will do. We'll lose type-safety but the signature of the calling + // function should ensure we don't get anything unexpected, and once we + // stick aValue in a union we lose type information anyway. + cont->mSVGAngle = static_cast(aValue); + cont->mType = aType; + SetMiscAtomOrString(aSerialized); + } +} + bool nsAttrValue::EnsureEmptyMiscContainer() { diff --git a/content/base/src/nsAttrValue.h b/content/base/src/nsAttrValue.h index 8b797248f11..7e90f880750 100644 --- a/content/base/src/nsAttrValue.h +++ b/content/base/src/nsAttrValue.h @@ -51,6 +51,7 @@ #include "nsCaseTreatment.h" #include "nsMargin.h" #include "nsCOMPtr.h" +#include "SVGAttrValueWrapper.h" typedef PRUptrdiff PtrBits; class nsAString; @@ -102,6 +103,7 @@ public: nsAttrValue(); nsAttrValue(const nsAttrValue& aOther); explicit nsAttrValue(const nsAString& aValue); + explicit nsAttrValue(nsIAtom* aValue); nsAttrValue(mozilla::css::StyleRule* aValue, const nsAString* aSerialized); explicit nsAttrValue(const nsIntMargin& aValue); ~nsAttrValue(); @@ -123,9 +125,23 @@ public: // Values below here won't matter, they'll be always stored in the 'misc' // struct. eCSSStyleRule = 0x10 - ,eAtomArray = 0x11 + ,eAtomArray = 0x11 ,eDoubleValue = 0x12 ,eIntMarginValue = 0x13 + ,eSVGTypesBegin = 0x14 + ,eSVGAngle = eSVGTypesBegin + ,eSVGIntegerPair = 0x15 + ,eSVGLength = 0x16 + ,eSVGLengthList = 0x17 + ,eSVGNumberList = 0x18 + ,eSVGNumberPair = 0x19 + ,eSVGPathData = 0x20 + ,eSVGPointList = 0x21 + ,eSVGPreserveAspectRatio = 0x22 + ,eSVGStringList = 0x23 + ,eSVGTransformList = 0x24 + ,eSVGViewBox = 0x25 + ,eSVGTypesEnd = 0x34 }; ValueType Type() const; @@ -134,9 +150,29 @@ public: void SetTo(const nsAttrValue& aOther); void SetTo(const nsAString& aValue); + void SetTo(nsIAtom* aValue); void SetTo(PRInt16 aInt); + void SetTo(PRInt32 aInt, const nsAString* aSerialized); + void SetTo(double aValue, const nsAString* aSerialized); void SetTo(mozilla::css::StyleRule* aValue, const nsAString* aSerialized); void SetTo(const nsIntMargin& aValue); + void SetTo(const nsSVGAngle& aValue, const nsAString* aSerialized); + void SetTo(const nsSVGIntegerPair& aValue, const nsAString* aSerialized); + void SetTo(const nsSVGLength2& aValue, const nsAString* aSerialized); + void SetTo(const mozilla::SVGLengthList& aValue, + const nsAString* aSerialized); + void SetTo(const mozilla::SVGNumberList& aValue, + const nsAString* aSerialized); + void SetTo(const nsSVGNumberPair& aValue, const nsAString* aSerialized); + void SetTo(const mozilla::SVGPathData& aValue, const nsAString* aSerialized); + void SetTo(const mozilla::SVGPointList& aValue, const nsAString* aSerialized); + void SetTo(const mozilla::SVGAnimatedPreserveAspectRatio& aValue, + const nsAString* aSerialized); + void SetTo(const mozilla::SVGStringList& aValue, + const nsAString* aSerialized); + void SetTo(const mozilla::SVGTransformList& aValue, + const nsAString* aSerialized); + void SetTo(const nsSVGViewBox& aValue, const nsAString* aSerialized); /** * Sets this object with the string or atom representation of aValue. @@ -368,10 +404,23 @@ private: AtomArray* mAtomArray; double mDoubleValue; nsIntMargin* mIntMargin; + const nsSVGAngle* mSVGAngle; + const nsSVGIntegerPair* mSVGIntegerPair; + const nsSVGLength2* mSVGLength; + const mozilla::SVGLengthList* mSVGLengthList; + const mozilla::SVGNumberList* mSVGNumberList; + const nsSVGNumberPair* mSVGNumberPair; + const mozilla::SVGPathData* mSVGPathData; + const mozilla::SVGPointList* mSVGPointList; + const mozilla::SVGAnimatedPreserveAspectRatio* mSVGPreserveAspectRatio; + const mozilla::SVGStringList* mSVGStringList; + const mozilla::SVGTransformList* mSVGTransformList; + const nsSVGViewBox* mSVGViewBox; }; }; inline ValueBaseType BaseType() const; + inline bool IsSVGType(ValueType aType) const; /** * Get the index of an EnumTable in the sEnumTableArray. @@ -388,6 +437,8 @@ private: void SetColorValue(nscolor aColor, const nsAString& aString); void SetMiscAtomOrString(const nsAString* aValue); void ResetMiscAtomOrString(); + void SetSVGType(ValueType aType, const void* aValue, + const nsAString* aSerialized); inline void ResetIfSet(); inline void* GetPtr() const; @@ -502,6 +553,12 @@ nsAttrValue::BaseType() const return static_cast(mBits & NS_ATTRVALUE_BASETYPE_MASK); } +inline bool +nsAttrValue::IsSVGType(ValueType aType) const +{ + return aType >= eSVGTypesBegin && aType <= eSVGTypesEnd; +} + inline void nsAttrValue::SetPtrValueAndType(void* aValue, ValueBaseType aType) { 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/nsEventSource.cpp b/content/base/src/nsEventSource.cpp index cb91fea9bdb..b0860e28151 100644 --- a/content/base/src/nsEventSource.cpp +++ b/content/base/src/nsEventSource.cpp @@ -89,6 +89,7 @@ nsEventSource::nsEventSource() : mErrorLoadOnRedirect(false), mGoingToDispatchAllMessages(false), mWithCredentials(false), + mWaitingForOnStopRequest(false), mLastConvertionResult(NS_OK), mReadyState(nsIEventSource::CONNECTING), mScriptLine(0), @@ -108,13 +109,19 @@ nsEventSource::~nsEventSource() NS_IMPL_CYCLE_COLLECTION_CLASS(nsEventSource) NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsEventSource) - if (tmp->IsBlack()) { + bool isBlack = tmp->IsBlack(); + if (isBlack || tmp->mWaitingForOnStopRequest) { if (tmp->mListenerManager) { tmp->mListenerManager->UnmarkGrayJSListeners(); NS_UNMARK_LISTENER_WRAPPER(Open) NS_UNMARK_LISTENER_WRAPPER(Message) NS_UNMARK_LISTENER_WRAPPER(Error) } + if (!isBlack) { + xpc_UnmarkGrayObject(tmp->PreservingWrapper() ? + tmp->GetWrapperPreserveColor() : + tmp->GetExpandoObjectPreserveColor()); + } return true; } NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END @@ -599,6 +606,8 @@ nsEventSource::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext, nsresult aStatusCode) { + mWaitingForOnStopRequest = false; + if (mReadyState == nsIEventSource::CLOSED) { return NS_ERROR_ABORT; } @@ -949,7 +958,11 @@ nsEventSource::InitChannelAndRequestEventSource() NS_ENSURE_SUCCESS(rv, rv); // Start reading from the channel - return mHttpChannel->AsyncOpen(listener, nsnull); + rv = mHttpChannel->AsyncOpen(listener, nsnull); + if (NS_SUCCEEDED(rv)) { + mWaitingForOnStopRequest = true; + } + return rv; } void diff --git a/content/base/src/nsEventSource.h b/content/base/src/nsEventSource.h index 4ee1c32b004..0b04bce34e5 100644 --- a/content/base/src/nsEventSource.h +++ b/content/base/src/nsEventSource.h @@ -216,6 +216,7 @@ protected: bool mErrorLoadOnRedirect; bool mGoingToDispatchAllMessages; bool mWithCredentials; + bool mWaitingForOnStopRequest; // used while reading the input streams nsCOMPtr mUnicodeDecoder; 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/src/nsWebSocket.cpp b/content/base/src/nsWebSocket.cpp index 1a6f1457120..b5b53594db3 100644 --- a/content/base/src/nsWebSocket.cpp +++ b/content/base/src/nsWebSocket.cpp @@ -439,7 +439,8 @@ nsWebSocket::~nsWebSocket() NS_IMPL_CYCLE_COLLECTION_CLASS(nsWebSocket) NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsWebSocket) - if (tmp->IsBlack()) { + bool isBlack = tmp->IsBlack(); + if (isBlack|| tmp->mKeepingAlive) { if (tmp->mListenerManager) { tmp->mListenerManager->UnmarkGrayJSListeners(); NS_UNMARK_LISTENER_WRAPPER(Open) @@ -447,6 +448,11 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsWebSocket) NS_UNMARK_LISTENER_WRAPPER(Message) NS_UNMARK_LISTENER_WRAPPER(Close) } + if (!isBlack) { + xpc_UnmarkGrayObject(tmp->PreservingWrapper() ? + tmp->GetWrapperPreserveColor() : + tmp->GetExpandoObjectPreserveColor()); + } return true; } NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END diff --git a/content/base/src/nsXMLHttpRequest.cpp b/content/base/src/nsXMLHttpRequest.cpp index 170b68fa459..bb86e90fece 100644 --- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -445,8 +445,8 @@ nsXMLHttpRequest::nsXMLHttpRequest() mProgressSinceLastProgressEvent(false), mUploadProgress(0), mUploadProgressMax(0), mRequestSentTime(0), mTimeoutMilliseconds(0), - mErrorLoad(false), mProgressTimerIsActive(false), - mProgressEventWasDelayed(false), + mErrorLoad(false), mWaitingForOnStopRequest(false), + mProgressTimerIsActive(false), mProgressEventWasDelayed(false), mIsHtml(false), mWarnAboutMultipartHtml(false), mWarnAboutSyncHtml(false), @@ -602,7 +602,8 @@ nsXMLHttpRequest::SetRequestObserver(nsIRequestObserver* aObserver) NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequest) NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXMLHttpRequest) - if (tmp->IsBlack()) { + bool isBlack = tmp->IsBlack(); + if (isBlack || tmp->mWaitingForOnStopRequest) { if (tmp->mListenerManager) { tmp->mListenerManager->UnmarkGrayJSListeners(); NS_UNMARK_LISTENER_WRAPPER(Load) @@ -614,6 +615,11 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXMLHttpRequest) NS_UNMARK_LISTENER_WRAPPER(UploadProgress) NS_UNMARK_LISTENER_WRAPPER(Readystatechange) } + if (!isBlack) { + xpc_UnmarkGrayObject(tmp->PreservingWrapper() ? + tmp->GetWrapperPreserveColor() : + tmp->GetExpandoObjectPreserveColor()); + } return true; } NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END @@ -2140,6 +2146,8 @@ nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult return NS_OK; } + mWaitingForOnStopRequest = false; + nsresult rv = NS_OK; // If we're loading a multipart stream of XML documents, we'll get @@ -2774,6 +2782,9 @@ nsXMLHttpRequest::Send(nsIVariant *aBody) return rv; } + // Either AsyncOpen was called, or CORS will open the channel later. + mWaitingForOnStopRequest = true; + // If we're synchronous, spin an event loop here and wait if (!(mState & XML_HTTP_REQUEST_ASYNC)) { mState |= XML_HTTP_REQUEST_SYNCLOOPING; diff --git a/content/base/src/nsXMLHttpRequest.h b/content/base/src/nsXMLHttpRequest.h index 3d5a1ab8424..f25c953d6a6 100644 --- a/content/base/src/nsXMLHttpRequest.h +++ b/content/base/src/nsXMLHttpRequest.h @@ -365,7 +365,7 @@ protected: void HandleTimeoutCallback(); bool mErrorLoad; - + bool mWaitingForOnStopRequest; bool mProgressTimerIsActive; bool mProgressEventWasDelayed; bool mIsHtml; 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_bug338679.html b/content/base/test/test_bug338679.html index cea297736a2..ac34bfc0156 100644 --- a/content/base/test/test_bug338679.html +++ b/content/base/test/test_bug338679.html @@ -49,7 +49,7 @@ function attr_modified(ev) { e_prev = e_new; if (!recursive) { recursive = true; - e_new = "width: 0pt;"; + e_new = "width: 0px;"; testDiv.style.width = "0"; } else { recursive = false; 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 5f1d14181b1..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: @@ -2388,6 +2388,7 @@ public: NS_DECL_ISUPPORTS NS_DECL_NSIWEBGLEXTENSION + virtual ~WebGLExtension() {} }; inline const WebGLRectangleObject *WebGLContext::FramebufferRectangleObject() const { 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/html/content/src/nsDOMValidityState.h b/content/html/content/src/nsDOMValidityState.h index 8370b45b9a2..5980757f234 100644 --- a/content/html/content/src/nsDOMValidityState.h +++ b/content/html/content/src/nsDOMValidityState.h @@ -42,7 +42,7 @@ #include "nsIConstraintValidation.h" -class nsDOMValidityState : public nsIDOMValidityState +class nsDOMValidityState MOZ_FINAL : public nsIDOMValidityState { public: NS_DECL_ISUPPORTS diff --git a/content/html/content/src/nsHTMLInputElement.cpp b/content/html/content/src/nsHTMLInputElement.cpp index a17eb86e732..0c862a57b98 100644 --- a/content/html/content/src/nsHTMLInputElement.cpp +++ b/content/html/content/src/nsHTMLInputElement.cpp @@ -185,7 +185,7 @@ static const nsAttrValue::EnumTable* kInputDefaultAutocomplete = &kInputAutocomp {0xb5, 0x13, 0x7b, 0x36, 0x93, 0x43, 0xe3, 0xa0} \ } -class nsHTMLInputElementState : public nsISupports +class nsHTMLInputElementState MOZ_FINAL : public nsISupports { public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_INPUT_ELEMENT_STATE_IID) diff --git a/content/html/content/src/nsHTMLInputElement.h b/content/html/content/src/nsHTMLInputElement.h index c462c85b6ea..453b8f7a06f 100644 --- a/content/html/content/src/nsHTMLInputElement.h +++ b/content/html/content/src/nsHTMLInputElement.h @@ -57,7 +57,7 @@ class nsIRadioGroupContainer; class nsIRadioGroupVisitor; class nsIRadioVisitor; -class UploadLastDir : public nsIObserver, public nsSupportsWeakReference { +class UploadLastDir MOZ_FINAL : public nsIObserver, public nsSupportsWeakReference { public: NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER diff --git a/content/html/content/src/nsHTMLMediaElement.cpp b/content/html/content/src/nsHTMLMediaElement.cpp index c4cc3d966c2..fcb41e0946c 100644 --- a/content/html/content/src/nsHTMLMediaElement.cpp +++ b/content/html/content/src/nsHTMLMediaElement.cpp @@ -243,10 +243,10 @@ public: * to an nsIChannel, which holds a reference to this listener. * We break the reference cycle in OnStartRequest by clearing mElement. */ -class nsHTMLMediaElement::MediaLoadListener : public nsIStreamListener, - public nsIChannelEventSink, - public nsIInterfaceRequestor, - public nsIObserver +class nsHTMLMediaElement::MediaLoadListener MOZ_FINAL : public nsIStreamListener, + public nsIChannelEventSink, + public nsIInterfaceRequestor, + public nsIObserver { NS_DECL_ISUPPORTS NS_DECL_NSIREQUESTOBSERVER diff --git a/content/html/content/src/nsMediaError.h b/content/html/content/src/nsMediaError.h index 33d657f650c..c540afd9233 100644 --- a/content/html/content/src/nsMediaError.h +++ b/content/html/content/src/nsMediaError.h @@ -37,8 +37,9 @@ * ***** END LICENSE BLOCK ***** */ #include "nsIDOMMediaError.h" #include "nsISupports.h" +#include "mozilla/Attributes.h" -class nsMediaError : public nsIDOMMediaError +class nsMediaError MOZ_FINAL : public nsIDOMMediaError { public: nsMediaError(PRUint16 aCode); diff --git a/content/html/content/src/nsTimeRanges.h b/content/html/content/src/nsTimeRanges.h index 172cadb5b85..8359b2378de 100644 --- a/content/html/content/src/nsTimeRanges.h +++ b/content/html/content/src/nsTimeRanges.h @@ -45,7 +45,7 @@ // Implements media TimeRanges: // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#timeranges -class nsTimeRanges : public nsIDOMTimeRanges { +class nsTimeRanges MOZ_FINAL : public nsIDOMTimeRanges { public: NS_DECL_ISUPPORTS NS_DECL_NSIDOMTIMERANGES diff --git a/content/media/nsBuiltinDecoder.cpp b/content/media/nsBuiltinDecoder.cpp index c4b0c294b2e..a0f40e5802f 100644 --- a/content/media/nsBuiltinDecoder.cpp +++ b/content/media/nsBuiltinDecoder.cpp @@ -216,6 +216,7 @@ nsresult nsBuiltinDecoder::Load(MediaResource* aResource, ReentrantMonitorAutoEnter mon(mReentrantMonitor); mDecoderStateMachine->SetSeekable(mSeekable); mDecoderStateMachine->SetDuration(mDuration); + mDecoderStateMachine->SetVolume(mInitialVolume); if (mFrameBufferLength > 0) { // The valid mFrameBufferLength value was specified earlier diff --git a/content/svg/content/src/DOMSVGLength.cpp b/content/svg/content/src/DOMSVGLength.cpp index 8e0de116e52..ada2143a204 100644 --- a/content/svg/content/src/DOMSVGLength.cpp +++ b/content/svg/content/src/DOMSVGLength.cpp @@ -156,8 +156,14 @@ DOMSVGLength::SetValue(float aUserUnitValue) // unit as it is. if (HasOwner()) { - if (InternalItem().SetFromUserUnitValue(aUserUnitValue, Element(), Axis())) { - Element()->DidChangeLengthList(mAttrEnum, true); + if (InternalItem().GetValueInUserUnits(Element(), Axis()) == + aUserUnitValue) { + return NS_OK; + } + nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(mAttrEnum); + if (InternalItem().SetFromUserUnitValue(aUserUnitValue, Element(), Axis())) + { + Element()->DidChangeLengthList(mAttrEnum, emptyOrOldValue); if (mList->mAList->IsAnimating()) { Element()->AnimationNeedsResample(); } @@ -195,8 +201,12 @@ DOMSVGLength::SetValueInSpecifiedUnits(float aValue) } if (HasOwner()) { + if (InternalItem().GetValueInCurrentUnits() == aValue) { + return NS_OK; + } + nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(mAttrEnum); InternalItem().SetValueInCurrentUnits(aValue); - Element()->DidChangeLengthList(mAttrEnum, true); + Element()->DidChangeLengthList(mAttrEnum, emptyOrOldValue); if (mList->mAList->IsAnimating()) { Element()->AnimationNeedsResample(); } @@ -218,8 +228,12 @@ DOMSVGLength::SetValueAsString(const nsAString& aValue) return NS_ERROR_DOM_SYNTAX_ERR; } if (HasOwner()) { + if (InternalItem() == value) { + return NS_OK; + } + nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(mAttrEnum); InternalItem() = value; - Element()->DidChangeLengthList(mAttrEnum, true); + Element()->DidChangeLengthList(mAttrEnum, emptyOrOldValue); if (mList->mAList->IsAnimating()) { Element()->AnimationNeedsResample(); } @@ -259,8 +273,13 @@ DOMSVGLength::NewValueSpecifiedUnits(PRUint16 aUnit, float aValue) return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } if (HasOwner()) { + if (InternalItem().GetUnit() == aUnit && + InternalItem().GetValueInCurrentUnits() == aValue) { + return NS_OK; + } + nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(mAttrEnum); InternalItem().SetValueAndUnit(aValue, PRUint8(aUnit)); - Element()->DidChangeLengthList(mAttrEnum, true); + Element()->DidChangeLengthList(mAttrEnum, emptyOrOldValue); if (mList->mAList->IsAnimating()) { Element()->AnimationNeedsResample(); } @@ -282,7 +301,12 @@ DOMSVGLength::ConvertToSpecifiedUnits(PRUint16 aUnit) return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } if (HasOwner()) { + if (InternalItem().GetUnit() == aUnit) { + return NS_OK; + } + nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(mAttrEnum); if (InternalItem().ConvertToUnit(PRUint8(aUnit), Element(), Axis())) { + Element()->DidChangeLengthList(mAttrEnum, emptyOrOldValue); return NS_OK; } } else { diff --git a/content/svg/content/src/DOMSVGLengthList.cpp b/content/svg/content/src/DOMSVGLengthList.cpp index b59930d9bdd..f5b46c46e0a 100644 --- a/content/svg/content/src/DOMSVGLengthList.cpp +++ b/content/svg/content/src/DOMSVGLengthList.cpp @@ -171,6 +171,7 @@ DOMSVGLengthList::Clear() } if (Length() > 0) { + nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(AttrEnum()); // Notify any existing DOM items of removal *before* truncating the lists // so that they can find their SVGLength internal counterparts and copy // their values. This also notifies the animVal list: @@ -178,7 +179,7 @@ DOMSVGLengthList::Clear() mItems.Clear(); InternalList().Clear(); - Element()->DidChangeLengthList(AttrEnum(), true); + Element()->DidChangeLengthList(AttrEnum(), emptyOrOldValue); if (mAList->IsAnimating()) { Element()->AnimationNeedsResample(); } @@ -256,6 +257,7 @@ DOMSVGLengthList::InsertItemBefore(nsIDOMSVGLength *newItem, return NS_ERROR_OUT_OF_MEMORY; } + nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(AttrEnum()); // Now that we know we're inserting, keep animVal list in sync as necessary. MaybeInsertNullInAnimValListAt(index); @@ -269,7 +271,7 @@ DOMSVGLengthList::InsertItemBefore(nsIDOMSVGLength *newItem, UpdateListIndicesFromIndex(mItems, index + 1); - Element()->DidChangeLengthList(AttrEnum(), true); + Element()->DidChangeLengthList(AttrEnum(), emptyOrOldValue); if (mAList->IsAnimating()) { Element()->AnimationNeedsResample(); } @@ -298,6 +300,7 @@ DOMSVGLengthList::ReplaceItem(nsIDOMSVGLength *newItem, domItem = domItem->Copy(); // must do this before changing anything! } + nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(AttrEnum()); if (mItems[index]) { // Notify any existing DOM item of removal *before* modifying the lists so // that the DOM item can copy the *old* value at its index: @@ -311,7 +314,7 @@ DOMSVGLengthList::ReplaceItem(nsIDOMSVGLength *newItem, // would end up reading bad data from InternalList()! domItem->InsertingIntoList(this, AttrEnum(), index, IsAnimValList()); - Element()->DidChangeLengthList(AttrEnum(), true); + Element()->DidChangeLengthList(AttrEnum(), emptyOrOldValue); if (mAList->IsAnimating()) { Element()->AnimationNeedsResample(); } @@ -332,6 +335,7 @@ DOMSVGLengthList::RemoveItem(PRUint32 index, return NS_ERROR_DOM_INDEX_SIZE_ERR; } + nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(AttrEnum()); // Now that we know we're removing, keep animVal list in sync as necessary. // Do this *before* touching InternalList() so the removed item can get its // internal value. @@ -350,7 +354,7 @@ DOMSVGLengthList::RemoveItem(PRUint32 index, UpdateListIndicesFromIndex(mItems, index); - Element()->DidChangeLengthList(AttrEnum(), true); + Element()->DidChangeLengthList(AttrEnum(), emptyOrOldValue); if (mAList->IsAnimating()) { Element()->AnimationNeedsResample(); } diff --git a/content/svg/content/src/DOMSVGNumber.cpp b/content/svg/content/src/DOMSVGNumber.cpp index 2648f32b1d4..a9f815964a2 100644 --- a/content/svg/content/src/DOMSVGNumber.cpp +++ b/content/svg/content/src/DOMSVGNumber.cpp @@ -123,8 +123,12 @@ DOMSVGNumber::SetValue(float aValue) NS_ENSURE_FINITE(aValue, NS_ERROR_ILLEGAL_VALUE); if (HasOwner()) { + if (InternalItem() == aValue) { + return NS_OK; + } + nsAttrValue emptyOrOldValue = Element()->WillChangeNumberList(mAttrEnum); InternalItem() = aValue; - Element()->DidChangeNumberList(mAttrEnum, true); + Element()->DidChangeNumberList(mAttrEnum, emptyOrOldValue); if (mList->mAList->IsAnimating()) { Element()->AnimationNeedsResample(); } diff --git a/content/svg/content/src/DOMSVGNumberList.cpp b/content/svg/content/src/DOMSVGNumberList.cpp index 6e9d8b80458..1f458180f52 100644 --- a/content/svg/content/src/DOMSVGNumberList.cpp +++ b/content/svg/content/src/DOMSVGNumberList.cpp @@ -171,6 +171,7 @@ DOMSVGNumberList::Clear() } if (Length() > 0) { + nsAttrValue emptyOrOldValue = Element()->WillChangeNumberList(AttrEnum()); // Notify any existing DOM items of removal *before* truncating the lists // so that they can find their SVGNumber internal counterparts and copy // their values. This also notifies the animVal list: @@ -178,7 +179,7 @@ DOMSVGNumberList::Clear() mItems.Clear(); InternalList().Clear(); - Element()->DidChangeNumberList(AttrEnum(), true); + Element()->DidChangeNumberList(AttrEnum(), emptyOrOldValue); if (mAList->IsAnimating()) { Element()->AnimationNeedsResample(); } @@ -256,6 +257,7 @@ DOMSVGNumberList::InsertItemBefore(nsIDOMSVGNumber *newItem, return NS_ERROR_OUT_OF_MEMORY; } + nsAttrValue emptyOrOldValue = Element()->WillChangeNumberList(AttrEnum()); // Now that we know we're inserting, keep animVal list in sync as necessary. MaybeInsertNullInAnimValListAt(index); @@ -269,7 +271,7 @@ DOMSVGNumberList::InsertItemBefore(nsIDOMSVGNumber *newItem, UpdateListIndicesFromIndex(mItems, index + 1); - Element()->DidChangeNumberList(AttrEnum(), true); + Element()->DidChangeNumberList(AttrEnum(), emptyOrOldValue); if (mAList->IsAnimating()) { Element()->AnimationNeedsResample(); } @@ -298,6 +300,7 @@ DOMSVGNumberList::ReplaceItem(nsIDOMSVGNumber *newItem, domItem = domItem->Clone(); // must do this before changing anything! } + nsAttrValue emptyOrOldValue = Element()->WillChangeNumberList(AttrEnum()); if (mItems[index]) { // Notify any existing DOM item of removal *before* modifying the lists so // that the DOM item can copy the *old* value at its index: @@ -311,7 +314,7 @@ DOMSVGNumberList::ReplaceItem(nsIDOMSVGNumber *newItem, // would end up reading bad data from InternalList()! domItem->InsertingIntoList(this, AttrEnum(), index, IsAnimValList()); - Element()->DidChangeNumberList(AttrEnum(), true); + Element()->DidChangeNumberList(AttrEnum(), emptyOrOldValue); if (mAList->IsAnimating()) { Element()->AnimationNeedsResample(); } @@ -340,6 +343,7 @@ DOMSVGNumberList::RemoveItem(PRUint32 index, // We have to return the removed item, so make sure it exists: EnsureItemAt(index); + nsAttrValue emptyOrOldValue = Element()->WillChangeNumberList(AttrEnum()); // Notify the DOM item of removal *before* modifying the lists so that the // DOM item can copy its *old* value: mItems[index]->RemovingFromList(); @@ -350,7 +354,7 @@ DOMSVGNumberList::RemoveItem(PRUint32 index, UpdateListIndicesFromIndex(mItems, index); - Element()->DidChangeNumberList(AttrEnum(), true); + Element()->DidChangeNumberList(AttrEnum(), emptyOrOldValue); if (mAList->IsAnimating()) { Element()->AnimationNeedsResample(); } diff --git a/content/svg/content/src/DOMSVGPathSeg.cpp b/content/svg/content/src/DOMSVGPathSeg.cpp index 53c5680923f..36c90033217 100644 --- a/content/svg/content/src/DOMSVGPathSeg.cpp +++ b/content/svg/content/src/DOMSVGPathSeg.cpp @@ -245,9 +245,13 @@ DOMSVGPathSeg::IndexIsValid() } \ NS_ENSURE_FINITE(float(a##propName), NS_ERROR_ILLEGAL_VALUE); \ if (HasOwner()) { \ + if (InternalItem()[1+index] == float(a##propName)) { \ + return NS_OK; \ + } \ + NS_ABORT_IF_FALSE(IsInList(), "Will/DidChangePathSegList() is wrong"); \ + nsAttrValue emptyOrOldValue = Element()->WillChangePathSegList(); \ InternalItem()[1+index] = float(a##propName); \ - NS_ABORT_IF_FALSE(IsInList(), "DidChangePathSegList() is wrong"); \ - Element()->DidChangePathSegList(true); \ + Element()->DidChangePathSegList(emptyOrOldValue); \ if (mList->AttrIsAnimating()) { \ Element()->AnimationNeedsResample(); \ } \ diff --git a/content/svg/content/src/DOMSVGPathSegList.cpp b/content/svg/content/src/DOMSVGPathSegList.cpp index ff0c9ad645d..b2ae7ab3ecb 100644 --- a/content/svg/content/src/DOMSVGPathSegList.cpp +++ b/content/svg/content/src/DOMSVGPathSegList.cpp @@ -269,6 +269,7 @@ DOMSVGPathSegList::Clear() } if (Length() > 0) { + nsAttrValue emptyOrOldValue = Element()->WillChangePathSegList(); // DOM list items that are to be removed must be removed before we change // the internal list, otherwise they wouldn't be able to copy their // internal counterparts' values! @@ -285,7 +286,7 @@ DOMSVGPathSegList::Clear() } InternalList().Clear(); - Element()->DidChangePathSegList(true); + Element()->DidChangePathSegList(emptyOrOldValue); if (AttrIsAnimating()) { Element()->AnimationNeedsResample(); } @@ -371,6 +372,7 @@ DOMSVGPathSegList::InsertItemBefore(nsIDOMSVGPathSeg *aNewItem, return NS_ERROR_OUT_OF_MEMORY; } + nsAttrValue emptyOrOldValue = Element()->WillChangePathSegList(); // Now that we know we're inserting, keep animVal list in sync as necessary. MaybeInsertNullInAnimValListAt(aIndex, internalIndex, argCount); @@ -387,7 +389,7 @@ DOMSVGPathSegList::InsertItemBefore(nsIDOMSVGPathSeg *aNewItem, UpdateListIndicesFromIndex(aIndex + 1, argCount + 1); - Element()->DidChangePathSegList(true); + Element()->DidChangePathSegList(emptyOrOldValue); if (AttrIsAnimating()) { Element()->AnimationNeedsResample(); } @@ -416,6 +418,7 @@ DOMSVGPathSegList::ReplaceItem(nsIDOMSVGPathSeg *aNewItem, domItem = domItem->Clone(); // must do this before changing anything! } + nsAttrValue emptyOrOldValue = Element()->WillChangePathSegList(); if (ItemAt(aIndex)) { // Notify any existing DOM item of removal *before* modifying the lists so // that the DOM item can copy the *old* value at its index: @@ -451,7 +454,7 @@ DOMSVGPathSegList::ReplaceItem(nsIDOMSVGPathSeg *aNewItem, } } - Element()->DidChangePathSegList(true); + Element()->DidChangePathSegList(emptyOrOldValue); if (AttrIsAnimating()) { Element()->AnimationNeedsResample(); } @@ -474,6 +477,7 @@ DOMSVGPathSegList::RemoveItem(PRUint32 aIndex, // We have to return the removed item, so make sure it exists: EnsureItemAt(aIndex); + nsAttrValue emptyOrOldValue = Element()->WillChangePathSegList(); // Notify the DOM item of removal *before* modifying the lists so that the // DOM item can copy its *old* value: ItemAt(aIndex)->RemovingFromList(); @@ -493,7 +497,7 @@ DOMSVGPathSegList::RemoveItem(PRUint32 aIndex, UpdateListIndicesFromIndex(aIndex, -(argCount + 1)); - Element()->DidChangePathSegList(true); + Element()->DidChangePathSegList(emptyOrOldValue); if (AttrIsAnimating()) { Element()->AnimationNeedsResample(); } diff --git a/content/svg/content/src/DOMSVGPoint.cpp b/content/svg/content/src/DOMSVGPoint.cpp index ba3d6ef946b..dbe96022c94 100644 --- a/content/svg/content/src/DOMSVGPoint.cpp +++ b/content/svg/content/src/DOMSVGPoint.cpp @@ -98,8 +98,12 @@ DOMSVGPoint::SetX(float aX) NS_ENSURE_FINITE(aX, NS_ERROR_ILLEGAL_VALUE); if (HasOwner()) { + if (InternalItem().mX == aX) { + return NS_OK; + } + nsAttrValue emptyOrOldValue = Element()->WillChangePointList(); InternalItem().mX = aX; - Element()->DidChangePointList(true); + Element()->DidChangePointList(emptyOrOldValue); if (mList->AttrIsAnimating()) { Element()->AnimationNeedsResample(); } @@ -129,8 +133,12 @@ DOMSVGPoint::SetY(float aY) NS_ENSURE_FINITE(aY, NS_ERROR_ILLEGAL_VALUE); if (HasOwner()) { + if (InternalItem().mY == aY) { + return NS_OK; + } + nsAttrValue emptyOrOldValue = Element()->WillChangePointList(); InternalItem().mY = aY; - Element()->DidChangePointList(true); + Element()->DidChangePointList(emptyOrOldValue); if (mList->AttrIsAnimating()) { Element()->AnimationNeedsResample(); } diff --git a/content/svg/content/src/DOMSVGPointList.cpp b/content/svg/content/src/DOMSVGPointList.cpp index 18baadb595c..aa660fe4187 100644 --- a/content/svg/content/src/DOMSVGPointList.cpp +++ b/content/svg/content/src/DOMSVGPointList.cpp @@ -213,6 +213,7 @@ DOMSVGPointList::Clear() } if (Length() > 0) { + nsAttrValue emptyOrOldValue = Element()->WillChangePointList(); // DOM list items that are to be removed must be removed before we change // the internal list, otherwise they wouldn't be able to copy their // internal counterparts' values! @@ -229,7 +230,7 @@ DOMSVGPointList::Clear() } InternalList().Clear(); - Element()->DidChangePointList(true); + Element()->DidChangePointList(emptyOrOldValue); if (AttrIsAnimating()) { Element()->AnimationNeedsResample(); } @@ -307,6 +308,7 @@ DOMSVGPointList::InsertItemBefore(nsIDOMSVGPoint *aNewItem, return NS_ERROR_OUT_OF_MEMORY; } + nsAttrValue emptyOrOldValue = Element()->WillChangePointList(); // Now that we know we're inserting, keep animVal list in sync as necessary. MaybeInsertNullInAnimValListAt(aIndex); @@ -320,7 +322,7 @@ DOMSVGPointList::InsertItemBefore(nsIDOMSVGPoint *aNewItem, UpdateListIndicesFromIndex(mItems, aIndex + 1); - Element()->DidChangePointList(true); + Element()->DidChangePointList(emptyOrOldValue); if (AttrIsAnimating()) { Element()->AnimationNeedsResample(); } @@ -349,6 +351,7 @@ DOMSVGPointList::ReplaceItem(nsIDOMSVGPoint *aNewItem, domItem = domItem->Clone(); // must do this before changing anything! } + nsAttrValue emptyOrOldValue = Element()->WillChangePointList(); if (mItems[aIndex]) { // Notify any existing DOM item of removal *before* modifying the lists so // that the DOM item can copy the *old* value at its index: @@ -362,7 +365,7 @@ DOMSVGPointList::ReplaceItem(nsIDOMSVGPoint *aNewItem, // would end up reading bad data from InternalList()! domItem->InsertingIntoList(this, aIndex, IsAnimValList()); - Element()->DidChangePointList(true); + Element()->DidChangePointList(emptyOrOldValue); if (AttrIsAnimating()) { Element()->AnimationNeedsResample(); } @@ -383,6 +386,7 @@ DOMSVGPointList::RemoveItem(PRUint32 aIndex, return NS_ERROR_DOM_INDEX_SIZE_ERR; } + nsAttrValue emptyOrOldValue = Element()->WillChangePointList(); // Now that we know we're removing, keep animVal list in sync as necessary. // Do this *before* touching InternalList() so the removed item can get its // internal value. @@ -401,7 +405,7 @@ DOMSVGPointList::RemoveItem(PRUint32 aIndex, UpdateListIndicesFromIndex(mItems, aIndex); - Element()->DidChangePointList(true); + Element()->DidChangePointList(emptyOrOldValue); if (AttrIsAnimating()) { Element()->AnimationNeedsResample(); } diff --git a/content/svg/content/src/DOMSVGStringList.cpp b/content/svg/content/src/DOMSVGStringList.cpp index 8fd2a53d605..7f0966b1b55 100644 --- a/content/svg/content/src/DOMSVGStringList.cpp +++ b/content/svg/content/src/DOMSVGStringList.cpp @@ -109,9 +109,12 @@ NS_IMETHODIMP DOMSVGStringList::Clear() { if (InternalList().IsExplicitlySet()) { + nsAttrValue emptyOrOldValue = + mElement->WillChangeStringList(mIsConditionalProcessingAttribute, + mAttrEnum); InternalList().Clear(); mElement->DidChangeStringList(mIsConditionalProcessingAttribute, - mAttrEnum); + mAttrEnum, emptyOrOldValue); } return NS_OK; } @@ -151,9 +154,13 @@ DOMSVGStringList::InsertItemBefore(const nsAString & newItem, return NS_ERROR_OUT_OF_MEMORY; } + nsAttrValue emptyOrOldValue = + mElement->WillChangeStringList(mIsConditionalProcessingAttribute, + mAttrEnum); InternalList().InsertItem(index, newItem); - mElement->DidChangeStringList(mIsConditionalProcessingAttribute, mAttrEnum); + mElement->DidChangeStringList(mIsConditionalProcessingAttribute, mAttrEnum, + emptyOrOldValue); _retval = newItem; return NS_OK; } @@ -171,9 +178,13 @@ DOMSVGStringList::ReplaceItem(const nsAString & newItem, } _retval = InternalList()[index]; + nsAttrValue emptyOrOldValue = + mElement->WillChangeStringList(mIsConditionalProcessingAttribute, + mAttrEnum); InternalList().ReplaceItem(index, newItem); - mElement->DidChangeStringList(mIsConditionalProcessingAttribute, mAttrEnum); + mElement->DidChangeStringList(mIsConditionalProcessingAttribute, mAttrEnum, + emptyOrOldValue); return NS_OK; } @@ -185,9 +196,13 @@ DOMSVGStringList::RemoveItem(PRUint32 index, return NS_ERROR_DOM_INDEX_SIZE_ERR; } + nsAttrValue emptyOrOldValue = + mElement->WillChangeStringList(mIsConditionalProcessingAttribute, + mAttrEnum); InternalList().RemoveItem(index); - mElement->DidChangeStringList(mIsConditionalProcessingAttribute, mAttrEnum); + mElement->DidChangeStringList(mIsConditionalProcessingAttribute, mAttrEnum, + emptyOrOldValue); return NS_OK; } diff --git a/content/svg/content/src/DOMSVGStringList.h b/content/svg/content/src/DOMSVGStringList.h index 6bb74f3c562..1437687db75 100644 --- a/content/svg/content/src/DOMSVGStringList.h +++ b/content/svg/content/src/DOMSVGStringList.h @@ -111,8 +111,6 @@ private: ~DOMSVGStringList(); - void DidChangeStringList(PRUint8 aAttrEnum, bool aDoSetAttr); - SVGStringList &InternalList(); // Strong ref to our element to keep it alive. diff --git a/content/svg/content/src/DOMSVGTests.cpp b/content/svg/content/src/DOMSVGTests.cpp index cdfa3585214..77e4bdcddaf 100644 --- a/content/svg/content/src/DOMSVGTests.cpp +++ b/content/svg/content/src/DOMSVGTests.cpp @@ -48,13 +48,18 @@ using namespace mozilla; NS_IMPL_ISUPPORTS1(DOMSVGTests, nsIDOMSVGTests) -DOMSVGTests::StringListInfo DOMSVGTests::sStringListInfo[3] = +nsIAtom** DOMSVGTests::sStringListNames[3] = { - { &nsGkAtoms::requiredFeatures, false }, - { &nsGkAtoms::requiredExtensions, false }, - { &nsGkAtoms::systemLanguage, true } + &nsGkAtoms::requiredFeatures, + &nsGkAtoms::requiredExtensions, + &nsGkAtoms::systemLanguage, }; +DOMSVGTests::DOMSVGTests() +{ + mStringListAttributes[LANGUAGE].SetIsCommaSeparated(true); +} + /* readonly attribute nsIDOMSVGStringList requiredFeatures; */ NS_IMETHODIMP DOMSVGTests::GetRequiredFeatures(nsIDOMSVGStringList * *aRequiredFeatures) @@ -96,8 +101,8 @@ DOMSVGTests::HasExtension(const nsAString & extension, bool *_retval) bool DOMSVGTests::IsConditionalProcessingAttribute(const nsIAtom* aAttribute) const { - for (PRUint32 i = 0; i < ArrayLength(sStringListInfo); i++) { - if (aAttribute == *sStringListInfo[i].mName) { + for (PRUint32 i = 0; i < ArrayLength(sStringListNames); i++) { + if (aAttribute == *sStringListNames[i]) { return true; } } @@ -222,10 +227,9 @@ DOMSVGTests::ParseConditionalProcessingAttribute(nsIAtom* aAttribute, const nsAString& aValue, nsAttrValue& aResult) { - for (PRUint32 i = 0; i < ArrayLength(sStringListInfo); i++) { - if (aAttribute == *sStringListInfo[i].mName) { - nsresult rv = mStringListAttributes[i].SetValue( - aValue, sStringListInfo[i].mIsCommaSeparated); + for (PRUint32 i = 0; i < ArrayLength(sStringListNames); i++) { + if (aAttribute == *sStringListNames[i]) { + nsresult rv = mStringListAttributes[i].SetValue(aValue); if (NS_FAILED(rv)) { mStringListAttributes[i].Clear(); } @@ -236,20 +240,11 @@ DOMSVGTests::ParseConditionalProcessingAttribute(nsIAtom* aAttribute, return false; } -void -DOMSVGTests::GetValue(PRUint8 aAttrEnum, nsAString& aValue) const -{ - NS_ABORT_IF_FALSE(aAttrEnum >= 0 && aAttrEnum < ArrayLength(sStringListInfo), - "aAttrEnum out of range"); - mStringListAttributes[aAttrEnum].GetValue( - aValue, sStringListInfo[aAttrEnum].mIsCommaSeparated); -} - void DOMSVGTests::UnsetAttr(const nsIAtom* aAttribute) { - for (PRUint32 i = 0; i < ArrayLength(sStringListInfo); i++) { - if (aAttribute == *sStringListInfo[i].mName) { + for (PRUint32 i = 0; i < ArrayLength(sStringListNames); i++) { + if (aAttribute == *sStringListNames[i]) { mStringListAttributes[i].Clear(); MaybeInvalidate(); return; @@ -257,22 +252,18 @@ DOMSVGTests::UnsetAttr(const nsIAtom* aAttribute) } } -void -DOMSVGTests::DidChangeStringList(PRUint8 aAttrEnum) +nsIAtom* +DOMSVGTests::GetAttrName(PRUint8 aAttrEnum) const { - NS_ASSERTION(aAttrEnum < ArrayLength(sStringListInfo), "aAttrEnum out of range"); + return *sStringListNames[aAttrEnum]; +} - nsCOMPtr element = do_QueryInterface(this); - - nsAutoString serializedValue; - GetValue(aAttrEnum, serializedValue); - - nsAttrValue attrValue(serializedValue); - element->SetParsedAttr(kNameSpaceID_None, - *sStringListInfo[aAttrEnum].mName, - nsnull, attrValue, true); - - MaybeInvalidate(); +void +DOMSVGTests::GetAttrValue(PRUint8 aAttrEnum, nsAttrValue& aValue) const +{ + NS_ABORT_IF_FALSE(aAttrEnum >= 0 && aAttrEnum < ArrayLength(sStringListNames), + "aAttrEnum out of range"); + aValue.SetTo(mStringListAttributes[aAttrEnum], nsnull); } void @@ -281,7 +272,7 @@ DOMSVGTests::MaybeInvalidate() nsCOMPtr element = do_QueryInterface(this); nsIContent* parent = element->GetFlattenedTreeParent(); - + if (parent && parent->NodeInfo()->Equals(nsGkAtoms::svgSwitch, kNameSpaceID_SVG)) { static_cast(parent)->MaybeInvalidate(); diff --git a/content/svg/content/src/DOMSVGTests.h b/content/svg/content/src/DOMSVGTests.h index f1332a8ac22..7fc3cb232c3 100644 --- a/content/svg/content/src/DOMSVGTests.h +++ b/content/svg/content/src/DOMSVGTests.h @@ -59,6 +59,8 @@ public: friend class mozilla::DOMSVGStringList; typedef mozilla::SVGStringList SVGStringList; + DOMSVGTests(); + /** * Compare the language name(s) in a systemLanguage attribute to the * user's language preferences, as defined in @@ -103,30 +105,20 @@ public: const nsAString& aValue, nsAttrValue& aResult); - /** - * Serialises the conditional processing attribute. - */ - void GetValue(PRUint8 aAttrEnum, nsAString& aValue) const; - /** * Unsets a conditional processing attribute. */ void UnsetAttr(const nsIAtom* aAttribute); - void DidChangeStringList(PRUint8 aAttrEnum); + nsIAtom* GetAttrName(PRUint8 aAttrEnum) const; + void GetAttrValue(PRUint8 aAttrEnum, nsAttrValue &aValue) const; void MaybeInvalidate(); private: - - struct StringListInfo { - nsIAtom** mName; - bool mIsCommaSeparated; - }; - enum { FEATURES, EXTENSIONS, LANGUAGE }; SVGStringList mStringListAttributes[3]; - static StringListInfo sStringListInfo[3]; + static nsIAtom** sStringListNames[3]; }; #endif // MOZILLA_DOMSVGTESTS_H__ diff --git a/content/svg/content/src/DOMSVGTransform.cpp b/content/svg/content/src/DOMSVGTransform.cpp index 94d36007c25..8f8956464c3 100644 --- a/content/svg/content/src/DOMSVGTransform.cpp +++ b/content/svg/content/src/DOMSVGTransform.cpp @@ -173,9 +173,7 @@ DOMSVGTransform::SetMatrix(nsIDOMSVGMatrix *matrix) if (!domMatrix) return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR; - Transform().SetMatrix(domMatrix->Matrix()); - NotifyElementOfChange(); - + SetMatrix(domMatrix->Matrix()); return NS_OK; } @@ -188,8 +186,14 @@ DOMSVGTransform::SetTranslate(float tx, float ty) } NS_ENSURE_FINITE2(tx, ty, NS_ERROR_ILLEGAL_VALUE); + if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_TRANSLATE && + Matrix().x0 == tx && Matrix().y0 == ty) { + return NS_OK; + } + + nsAttrValue emptyOrOldValue = NotifyElementWillChange(); Transform().SetTranslate(tx, ty); - NotifyElementOfChange(); + NotifyElementDidChange(emptyOrOldValue); return NS_OK; } @@ -203,8 +207,14 @@ DOMSVGTransform::SetScale(float sx, float sy) } NS_ENSURE_FINITE2(sx, sy, NS_ERROR_ILLEGAL_VALUE); + if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_SCALE && + Matrix().xx == sx && Matrix().yy == sy) { + return NS_OK; + } + + nsAttrValue emptyOrOldValue = NotifyElementWillChange(); Transform().SetScale(sx, sy); - NotifyElementOfChange(); + NotifyElementDidChange(emptyOrOldValue); return NS_OK; } @@ -218,8 +228,17 @@ DOMSVGTransform::SetRotate(float angle, float cx, float cy) } NS_ENSURE_FINITE3(angle, cx, cy, NS_ERROR_ILLEGAL_VALUE); + if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_ROTATE) { + float currentCx, currentCy; + Transform().GetRotationOrigin(currentCx, currentCy); + if (Transform().Angle() == angle && currentCx == cx && currentCy == cy) { + return NS_OK; + } + } + + nsAttrValue emptyOrOldValue = NotifyElementWillChange(); Transform().SetRotate(angle, cx, cy); - NotifyElementOfChange(); + NotifyElementDidChange(emptyOrOldValue); return NS_OK; } @@ -233,10 +252,16 @@ DOMSVGTransform::SetSkewX(float angle) } NS_ENSURE_FINITE(angle, NS_ERROR_ILLEGAL_VALUE); + if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_SKEWX && + Transform().Angle() == angle) { + return NS_OK; + } + + nsAttrValue emptyOrOldValue = NotifyElementWillChange(); nsresult rv = Transform().SetSkewX(angle); if (NS_FAILED(rv)) return rv; - NotifyElementOfChange(); + NotifyElementDidChange(emptyOrOldValue); return NS_OK; } @@ -250,10 +275,16 @@ DOMSVGTransform::SetSkewY(float angle) } NS_ENSURE_FINITE(angle, NS_ERROR_ILLEGAL_VALUE); + if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_SKEWY && + Transform().Angle() == angle) { + return NS_OK; + } + + nsAttrValue emptyOrOldValue = NotifyElementWillChange(); nsresult rv = Transform().SetSkewY(angle); if (NS_FAILED(rv)) return rv; - NotifyElementOfChange(); + NotifyElementDidChange(emptyOrOldValue); return NS_OK; } @@ -324,8 +355,15 @@ DOMSVGTransform::SetMatrix(const gfxMatrix& aMatrix) { NS_ABORT_IF_FALSE(!mIsAnimValItem, "Attempting to modify read-only transform"); + + if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_MATRIX && + SVGTransform::MatricesEqual(Matrix(), aMatrix)) { + return; + } + + nsAttrValue emptyOrOldValue = NotifyElementWillChange(); Transform().SetMatrix(aMatrix); - NotifyElementOfChange(); + NotifyElementDidChange(emptyOrOldValue); } void @@ -341,10 +379,10 @@ DOMSVGTransform::ClearMatrixTearoff(DOMSVGMatrix* aMatrix) // Implementation helpers void -DOMSVGTransform::NotifyElementOfChange() +DOMSVGTransform::NotifyElementDidChange(const nsAttrValue& aEmptyOrOldValue) { if (HasOwner()) { - Element()->DidChangeTransformList(true); + Element()->DidChangeTransformList(aEmptyOrOldValue); if (mList->mAList->IsAnimating()) { Element()->AnimationNeedsResample(); } diff --git a/content/svg/content/src/DOMSVGTransform.h b/content/svg/content/src/DOMSVGTransform.h index 58654e0b213..7f1f6ec6fa8 100644 --- a/content/svg/content/src/DOMSVGTransform.h +++ b/content/svg/content/src/DOMSVGTransform.h @@ -206,7 +206,8 @@ private: SVGTransform& Transform() { return HasOwner() ? InternalItem() : *mTransform; } - void NotifyElementOfChange(); + inline nsAttrValue NotifyElementWillChange(); + void NotifyElementDidChange(const nsAttrValue& aEmptyOrOldValue); nsRefPtr mList; @@ -235,6 +236,16 @@ private: NS_DEFINE_STATIC_IID_ACCESSOR(DOMSVGTransform, MOZILLA_DOMSVGTRANSFORM_IID) +nsAttrValue +DOMSVGTransform::NotifyElementWillChange() +{ + nsAttrValue result; + if (HasOwner()) { + result = Element()->WillChangeTransformList(); + } + return result; +} + } // namespace mozilla #undef MOZ_SVG_LIST_INDEX_BIT_COUNT diff --git a/content/svg/content/src/DOMSVGTransformList.cpp b/content/svg/content/src/DOMSVGTransformList.cpp index 6484e15b5c5..c8c89f18c02 100644 --- a/content/svg/content/src/DOMSVGTransformList.cpp +++ b/content/svg/content/src/DOMSVGTransformList.cpp @@ -184,6 +184,7 @@ DOMSVGTransformList::Clear() } if (Length() > 0) { + nsAttrValue emptyOrOldValue = Element()->WillChangeTransformList(); // Notify any existing DOM items of removal *before* truncating the lists // so that they can find their SVGTransform internal counterparts and copy // their values. This also notifies the animVal list: @@ -191,7 +192,7 @@ DOMSVGTransformList::Clear() mItems.Clear(); InternalList().Clear(); - Element()->DidChangeTransformList(true); + Element()->DidChangeTransformList(emptyOrOldValue); if (mAList->IsAnimating()) { Element()->AnimationNeedsResample(); } @@ -272,6 +273,7 @@ DOMSVGTransformList::InsertItemBefore(nsIDOMSVGTransform *newItem, return NS_ERROR_OUT_OF_MEMORY; } + nsAttrValue emptyOrOldValue = Element()->WillChangeTransformList(); // Now that we know we're inserting, keep animVal list in sync as necessary. MaybeInsertNullInAnimValListAt(index); @@ -285,7 +287,7 @@ DOMSVGTransformList::InsertItemBefore(nsIDOMSVGTransform *newItem, UpdateListIndicesFromIndex(mItems, index + 1); - Element()->DidChangeTransformList(true); + Element()->DidChangeTransformList(emptyOrOldValue); if (mAList->IsAnimating()) { Element()->AnimationNeedsResample(); } @@ -316,6 +318,7 @@ DOMSVGTransformList::ReplaceItem(nsIDOMSVGTransform *newItem, domItem = domItem->Clone(); // must do this before changing anything! } + nsAttrValue emptyOrOldValue = Element()->WillChangeTransformList(); if (mItems[index]) { // Notify any existing DOM item of removal *before* modifying the lists so // that the DOM item can copy the *old* value at its index: @@ -329,7 +332,7 @@ DOMSVGTransformList::ReplaceItem(nsIDOMSVGTransform *newItem, // would end up reading bad data from InternalList()! domItem->InsertingIntoList(this, index, IsAnimValList()); - Element()->DidChangeTransformList(true); + Element()->DidChangeTransformList(emptyOrOldValue); if (mAList->IsAnimating()) { Element()->AnimationNeedsResample(); } @@ -350,6 +353,7 @@ DOMSVGTransformList::RemoveItem(PRUint32 index, nsIDOMSVGTransform **_retval) return NS_ERROR_DOM_INDEX_SIZE_ERR; } + nsAttrValue emptyOrOldValue = Element()->WillChangeTransformList(); // Now that we know we're removing, keep animVal list in sync as necessary. // Do this *before* touching InternalList() so the removed item can get its // internal value. @@ -368,7 +372,7 @@ DOMSVGTransformList::RemoveItem(PRUint32 index, nsIDOMSVGTransform **_retval) UpdateListIndicesFromIndex(mItems, index); - Element()->DidChangeTransformList(true); + Element()->DidChangeTransformList(emptyOrOldValue); if (mAList->IsAnimating()) { Element()->AnimationNeedsResample(); } diff --git a/content/svg/content/src/Makefile.in b/content/svg/content/src/Makefile.in index f27cb4d4517..601a5e30d1c 100644 --- a/content/svg/content/src/Makefile.in +++ b/content/svg/content/src/Makefile.in @@ -146,6 +146,7 @@ CPPSRCS = \ nsSVGAnimationElement.cpp \ nsSVGMpathElement.cpp \ nsSVGSetElement.cpp \ + SVGAttrValueWrapper.cpp \ SVGIntegerPairSMILType.cpp \ SVGLengthListSMILType.cpp \ SVGMotionSMILType.cpp \ @@ -169,6 +170,7 @@ FORCE_STATIC_LIB = 1 EXPORTS = \ nsSVGFeatures.h \ nsSVGRect.h \ + SVGAttrValueWrapper.h \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/content/svg/content/src/SVGAnimatedPreserveAspectRatio.cpp b/content/svg/content/src/SVGAnimatedPreserveAspectRatio.cpp index 9905e3a4831..429de200e13 100644 --- a/content/svg/content/src/SVGAnimatedPreserveAspectRatio.cpp +++ b/content/svg/content/src/SVGAnimatedPreserveAspectRatio.cpp @@ -250,7 +250,8 @@ SVGAnimatedPreserveAspectRatio::SetBaseValueString( } void -SVGAnimatedPreserveAspectRatio::GetBaseValueString(nsAString & aValueAsString) +SVGAnimatedPreserveAspectRatio::GetBaseValueString( + nsAString& aValueAsString) const { nsAutoString tmpString; @@ -276,12 +277,17 @@ nsresult SVGAnimatedPreserveAspectRatio::SetBaseAlign(PRUint16 aAlign, nsSVGElement *aSVGElement) { + if (mIsBaseSet && mBaseVal.GetAlign() == aAlign) { + return NS_OK; + } + + nsAttrValue emptyOrOldValue = aSVGElement->WillChangePreserveAspectRatio(); nsresult rv = mBaseVal.SetAlign(aAlign); NS_ENSURE_SUCCESS(rv, rv); mIsBaseSet = true; mAnimVal.mAlign = mBaseVal.mAlign; - aSVGElement->DidChangePreserveAspectRatio(true); + aSVGElement->DidChangePreserveAspectRatio(emptyOrOldValue); if (mIsAnimated) { aSVGElement->AnimationNeedsResample(); } @@ -293,12 +299,17 @@ nsresult SVGAnimatedPreserveAspectRatio::SetBaseMeetOrSlice(PRUint16 aMeetOrSlice, nsSVGElement *aSVGElement) { + if (mIsBaseSet && mBaseVal.GetMeetOrSlice() == aMeetOrSlice) { + return NS_OK; + } + + nsAttrValue emptyOrOldValue = aSVGElement->WillChangePreserveAspectRatio(); nsresult rv = mBaseVal.SetMeetOrSlice(aMeetOrSlice); NS_ENSURE_SUCCESS(rv, rv); mIsBaseSet = true; mAnimVal.mMeetOrSlice = mBaseVal.mMeetOrSlice; - aSVGElement->DidChangePreserveAspectRatio(true); + aSVGElement->DidChangePreserveAspectRatio(emptyOrOldValue); if (mIsAnimated) { aSVGElement->AnimationNeedsResample(); } diff --git a/content/svg/content/src/SVGAnimatedPreserveAspectRatio.h b/content/svg/content/src/SVGAnimatedPreserveAspectRatio.h index b8c353481d0..5b2e8396aef 100644 --- a/content/svg/content/src/SVGAnimatedPreserveAspectRatio.h +++ b/content/svg/content/src/SVGAnimatedPreserveAspectRatio.h @@ -118,7 +118,7 @@ public: nsresult SetBaseValueString(const nsAString& aValue, nsSVGElement *aSVGElement); - void GetBaseValueString(nsAString& aValue); + void GetBaseValueString(nsAString& aValue) const; nsresult SetBaseAlign(PRUint16 aAlign, nsSVGElement *aSVGElement); nsresult SetBaseMeetOrSlice(PRUint16 aMeetOrSlice, nsSVGElement *aSVGElement); diff --git a/content/svg/content/src/SVGAttrValueWrapper.cpp b/content/svg/content/src/SVGAttrValueWrapper.cpp new file mode 100644 index 00000000000..af3f5455c1c --- /dev/null +++ b/content/svg/content/src/SVGAttrValueWrapper.cpp @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : + * ***** 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 the Mozilla SVG project. + * + * The Initial Developer of the Original Code is Mozilla Japan. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * 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 ***** */ + +#include "SVGAttrValueWrapper.h" +#include "nsSVGAngle.h" +#include "nsSVGIntegerPair.h" +#include "nsSVGLength2.h" +#include "nsSVGNumberPair.h" +#include "nsSVGViewBox.h" +#include "SVGAnimatedPreserveAspectRatio.h" +#include "SVGLengthList.h" +#include "SVGNumberList.h" +#include "SVGPathData.h" +#include "SVGPointList.h" +#include "SVGStringList.h" +#include "SVGTransformList.h" + +using namespace mozilla; + +/*static*/ void +SVGAttrValueWrapper::ToString(const nsSVGAngle* aAngle, nsAString& aResult) +{ + aAngle->GetBaseValueString(aResult); +} + +/*static*/ void +SVGAttrValueWrapper::ToString(const nsSVGIntegerPair* aIntegerPair, + nsAString& aResult) +{ + aIntegerPair->GetBaseValueString(aResult); +} + +/*static*/ void +SVGAttrValueWrapper::ToString(const nsSVGLength2* aLength, nsAString& aResult) +{ + aLength->GetBaseValueString(aResult); +} + +/*static*/ void +SVGAttrValueWrapper::ToString(const SVGLengthList* aLengthList, + nsAString& aResult) +{ + aLengthList->GetValueAsString(aResult); +} + +/*static*/ void +SVGAttrValueWrapper::ToString(const SVGNumberList* aNumberList, + nsAString& aResult) +{ + aNumberList->GetValueAsString(aResult); +} + +/*static*/ void +SVGAttrValueWrapper::ToString(const nsSVGNumberPair* aNumberPair, + nsAString& aResult) +{ + aNumberPair->GetBaseValueString(aResult); +} + +/*static*/ void +SVGAttrValueWrapper::ToString(const SVGPathData* aPathData, nsAString& aResult) +{ + aPathData->GetValueAsString(aResult); +} + +/*static*/ void +SVGAttrValueWrapper::ToString(const SVGPointList* aPointList, + nsAString& aResult) +{ + aPointList->GetValueAsString(aResult); +} + +/*static*/ void +SVGAttrValueWrapper::ToString( + const SVGAnimatedPreserveAspectRatio* aPreserveAspectRatio, + nsAString& aResult) +{ + aPreserveAspectRatio->GetBaseValueString(aResult); +} + +/*static*/ void +SVGAttrValueWrapper::ToString(const SVGStringList* aStringList, + nsAString& aResult) +{ + aStringList->GetValue(aResult); +} + +/*static*/ void +SVGAttrValueWrapper::ToString(const SVGTransformList* aTransformList, + nsAString& aResult) +{ + aTransformList->GetValueAsString(aResult); +} + +/*static*/ void +SVGAttrValueWrapper::ToString(const nsSVGViewBox* aViewBox, nsAString& aResult) +{ + aViewBox->GetBaseValueString(aResult); +} diff --git a/content/svg/content/src/SVGAttrValueWrapper.h b/content/svg/content/src/SVGAttrValueWrapper.h new file mode 100644 index 00000000000..b0419d57099 --- /dev/null +++ b/content/svg/content/src/SVGAttrValueWrapper.h @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : + * ***** 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 the Mozilla SVG project. + * + * The Initial Developer of the Original Code is Mozilla Japan. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * 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 ***** */ + +#ifndef MOZILLA_SVGATTRVALUEWRAPPER_H__ +#define MOZILLA_SVGATTRVALUEWRAPPER_H__ + +/** + * Utility wrapper for handling SVG types used inside nsAttrValue so that these + * types don't need to be exported outside the SVG module. + */ + +#include "nsString.h" + +class nsSVGAngle; +class nsSVGIntegerPair; +class nsSVGLength2; +class nsSVGNumberPair; +class nsSVGViewBox; + +namespace mozilla { +class SVGLengthList; +class SVGNumberList; +class SVGPathData; +class SVGPointList; +class SVGAnimatedPreserveAspectRatio; +class SVGStringList; +class SVGTransformList; +} + +namespace mozilla { + +class SVGAttrValueWrapper +{ +public: + static void ToString(const nsSVGAngle* aAngle, nsAString& aResult); + static void ToString(const nsSVGIntegerPair* aIntegerPair, + nsAString& aResult); + static void ToString(const nsSVGLength2* aLength, nsAString& aResult); + static void ToString(const mozilla::SVGLengthList* aLengthList, + nsAString& aResult); + static void ToString(const mozilla::SVGNumberList* aNumberList, + nsAString& aResult); + static void ToString(const nsSVGNumberPair* aNumberPair, nsAString& aResult); + static void ToString(const mozilla::SVGPathData* aPathData, + nsAString& aResult); + static void ToString(const mozilla::SVGPointList* aPointList, + nsAString& aResult); + static void ToString( + const mozilla::SVGAnimatedPreserveAspectRatio* aPreserveAspectRatio, + nsAString& aResult); + static void ToString(const mozilla::SVGStringList* aStringList, + nsAString& aResult); + static void ToString(const mozilla::SVGTransformList* aTransformList, + nsAString& aResult); + static void ToString(const nsSVGViewBox* aViewBox, nsAString& aResult); +}; + +} /* namespace mozilla */ + +#endif // MOZILLA_SVGATTRVALUEWRAPPER_H__ diff --git a/content/svg/content/src/SVGStringList.cpp b/content/svg/content/src/SVGStringList.cpp index 6cb8a69fd1b..a8a608135da 100644 --- a/content/svg/content/src/SVGStringList.cpp +++ b/content/svg/content/src/SVGStringList.cpp @@ -62,14 +62,14 @@ SVGStringList::CopyFrom(const SVGStringList& rhs) } void -SVGStringList::GetValue(nsAString& aValue, bool aIsCommaSeparated) const +SVGStringList::GetValue(nsAString& aValue) const { aValue.Truncate(); PRUint32 last = mStrings.Length() - 1; for (PRUint32 i = 0; i < mStrings.Length(); ++i) { aValue.Append(mStrings[i]); if (i != last) { - if (aIsCommaSeparated) { + if (mIsCommaSeparated) { aValue.Append(','); } aValue.Append(' '); @@ -78,11 +78,11 @@ SVGStringList::GetValue(nsAString& aValue, bool aIsCommaSeparated) const } nsresult -SVGStringList::SetValue(const nsAString& aValue, bool aIsCommaSeparated) +SVGStringList::SetValue(const nsAString& aValue) { SVGStringList temp; - if (aIsCommaSeparated) { + if (mIsCommaSeparated) { nsCharSeparatedTokenizerTemplate tokenizer(aValue, ','); diff --git a/content/svg/content/src/SVGStringList.h b/content/svg/content/src/SVGStringList.h index 4548564c95d..5452d944f8d 100644 --- a/content/svg/content/src/SVGStringList.h +++ b/content/svg/content/src/SVGStringList.h @@ -54,10 +54,13 @@ class SVGStringList public: - SVGStringList() : mIsSet(false) {} + SVGStringList() : mIsSet(false), mIsCommaSeparated(false) {} ~SVGStringList(){} - nsresult SetValue(const nsAString& aValue, bool aIsCommaSeparated); + void SetIsCommaSeparated(bool aIsCommaSeparated) { + mIsCommaSeparated = aIsCommaSeparated; + } + nsresult SetValue(const nsAString& aValue); void Clear() { mStrings.Clear(); @@ -65,7 +68,7 @@ public: } /// This may return an incomplete string on OOM, but that's acceptable. - void GetValue(nsAString& aValue, bool aIsCommaSeparated) const; + void GetValue(nsAString& aValue) const; bool IsEmpty() const { return mStrings.IsEmpty(); @@ -91,9 +94,8 @@ public: mStrings.Compact(); } - // Returns true if the animated value of this stringlist has been explicitly - // set by taking on the base value which has been explicitly set by markup - // or a DOM call, false otherwise. + // Returns true if the value of this stringlist has been explicitly + // set by markup or a DOM call, false otherwise. bool IsExplicitlySet() const { return mIsSet; } @@ -168,6 +170,7 @@ protected: */ nsTArray mStrings; bool mIsSet; + bool mIsCommaSeparated; }; } // namespace mozilla diff --git a/content/svg/content/src/SVGTransform.h b/content/svg/content/src/SVGTransform.h index b4d661e9e91..51bf7c66034 100644 --- a/content/svg/content/src/SVGTransform.h +++ b/content/svg/content/src/SVGTransform.h @@ -97,7 +97,6 @@ public: nsresult SetSkewX(float aAngle); nsresult SetSkewY(float aAngle); -protected: static bool MatricesEqual(const gfxMatrix& a, const gfxMatrix& b) { return a.xx == b.xx && @@ -108,6 +107,7 @@ protected: a.y0 == b.y0; } +protected: gfxMatrix mMatrix; float mAngle, mOriginX, mOriginY; PRUint16 mType; diff --git a/content/svg/content/src/nsSVGAngle.cpp b/content/svg/content/src/nsSVGAngle.cpp index 087d00f2ee8..68979970836 100644 --- a/content/svg/content/src/nsSVGAngle.cpp +++ b/content/svg/content/src/nsSVGAngle.cpp @@ -72,7 +72,7 @@ public: NS_IMETHOD SetValue(float aValue) { NS_ENSURE_FINITE(aValue, NS_ERROR_ILLEGAL_VALUE); - mVal.SetBaseValue(aValue, nsnull); + mVal.SetBaseValue(aValue, nsnull, true); return NS_OK; } @@ -258,6 +258,11 @@ void nsSVGAngle::SetBaseValueInSpecifiedUnits(float aValue, nsSVGElement *aSVGElement) { + if (mBaseVal == aValue) { + return; + } + + nsAttrValue emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum); mBaseVal = aValue; if (!mIsAnimated) { mAnimVal = mBaseVal; @@ -265,7 +270,7 @@ nsSVGAngle::SetBaseValueInSpecifiedUnits(float aValue, else { aSVGElement->AnimationNeedsResample(); } - aSVGElement->DidChangeAngle(mAttrEnum, true); + aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue); } nsresult @@ -275,9 +280,19 @@ nsSVGAngle::ConvertToSpecifiedUnits(PRUint16 unitType, if (!IsValidUnitType(unitType)) return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + if (mBaseValUnit == PRUint8(unitType)) + return NS_OK; + + nsAttrValue emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum); + float valueInUserUnits = mBaseVal * GetDegreesPerUnit(mBaseValUnit); mBaseValUnit = PRUint8(unitType); - SetBaseValue(valueInUserUnits, aSVGElement); + // Setting aDoSetAttr to false here will ensure we don't call + // Will/DidChangeAngle a second time (and dispatch duplicate notifications). + SetBaseValue(valueInUserUnits, aSVGElement, false); + + aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue); + return NS_OK; } @@ -291,6 +306,13 @@ nsSVGAngle::NewValueSpecifiedUnits(PRUint16 unitType, if (!IsValidUnitType(unitType)) return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + if (mBaseVal == valueInSpecifiedUnits && mBaseValUnit == PRUint8(unitType)) + return NS_OK; + + nsAttrValue emptyOrOldValue; + if (aSVGElement) { + emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum); + } mBaseVal = valueInSpecifiedUnits; mBaseValUnit = PRUint8(unitType); if (!mIsAnimated) { @@ -301,7 +323,7 @@ nsSVGAngle::NewValueSpecifiedUnits(PRUint16 unitType, aSVGElement->AnimationNeedsResample(); } if (aSVGElement) { - aSVGElement->DidChangeAngle(mAttrEnum, true); + aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue); } return NS_OK; } @@ -342,7 +364,14 @@ nsSVGAngle::SetBaseValueString(const nsAString &aValueAsString, if (NS_FAILED(rv)) { return rv; } + if (mBaseVal == value && mBaseValUnit == PRUint8(unitType)) { + return NS_OK; + } + nsAttrValue emptyOrOldValue; + if (aDoSetAttr) { + emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum); + } mBaseVal = value; mBaseValUnit = PRUint8(unitType); if (!mIsAnimated) { @@ -353,27 +382,36 @@ nsSVGAngle::SetBaseValueString(const nsAString &aValueAsString, aSVGElement->AnimationNeedsResample(); } - // We don't need to call DidChange* here - we're only called by - // nsSVGElement::ParseAttribute under nsGenericElement::SetAttr, - // which takes care of notifying. + if (aDoSetAttr) { + aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue); + } return NS_OK; } void -nsSVGAngle::GetBaseValueString(nsAString & aValueAsString) +nsSVGAngle::GetBaseValueString(nsAString & aValueAsString) const { GetValueString(aValueAsString, mBaseVal, mBaseValUnit); } void -nsSVGAngle::GetAnimValueString(nsAString & aValueAsString) +nsSVGAngle::GetAnimValueString(nsAString & aValueAsString) const { GetValueString(aValueAsString, mAnimVal, mAnimValUnit); } void -nsSVGAngle::SetBaseValue(float aValue, nsSVGElement *aSVGElement) +nsSVGAngle::SetBaseValue(float aValue, nsSVGElement *aSVGElement, + bool aDoSetAttr) { + if (mBaseVal == aValue * GetDegreesPerUnit(mBaseValUnit)) { + return; + } + nsAttrValue emptyOrOldValue; + if (aSVGElement && aDoSetAttr) { + emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum); + } + mBaseVal = aValue / GetDegreesPerUnit(mBaseValUnit); if (!mIsAnimated) { mAnimVal = mBaseVal; @@ -381,8 +419,8 @@ nsSVGAngle::SetBaseValue(float aValue, nsSVGElement *aSVGElement) else { aSVGElement->AnimationNeedsResample(); } - if (aSVGElement) { - aSVGElement->DidChangeAngle(mAttrEnum, true); + if (aSVGElement && aDoSetAttr) { + aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue); } } diff --git a/content/svg/content/src/nsSVGAngle.h b/content/svg/content/src/nsSVGAngle.h index ff518f1834c..10ab5d3e7b0 100644 --- a/content/svg/content/src/nsSVGAngle.h +++ b/content/svg/content/src/nsSVGAngle.h @@ -67,15 +67,15 @@ public: nsresult SetBaseValueString(const nsAString& aValue, nsSVGElement *aSVGElement, bool aDoSetAttr); - void GetBaseValueString(nsAString& aValue); - void GetAnimValueString(nsAString& aValue); + void GetBaseValueString(nsAString& aValue) const; + void GetAnimValueString(nsAString& aValue) const; float GetBaseValue() const { return mBaseVal * GetDegreesPerUnit(mBaseValUnit); } float GetAnimValue() const { return mAnimVal * GetDegreesPerUnit(mAnimValUnit); } - void SetBaseValue(float aValue, nsSVGElement *aSVGElement); + void SetBaseValue(float aValue, nsSVGElement *aSVGElement, bool aDoSetAttr); void SetAnimValue(float aValue, PRUint8 aUnit, nsSVGElement *aSVGElement); PRUint8 GetBaseValueUnit() const { return mBaseValUnit; } @@ -125,7 +125,7 @@ public: NS_IMETHOD GetValue(float* aResult) { *aResult = mVal->GetBaseValue(); return NS_OK; } NS_IMETHOD SetValue(float aValue) - { mVal->SetBaseValue(aValue, mSVGElement); return NS_OK; } + { mVal->SetBaseValue(aValue, mSVGElement, true); return NS_OK; } NS_IMETHOD GetValueInSpecifiedUnits(float* aResult) { *aResult = mVal->mBaseVal; return NS_OK; } diff --git a/content/svg/content/src/nsSVGBoolean.cpp b/content/svg/content/src/nsSVGBoolean.cpp index 2b7db25d221..c31fca96e4d 100644 --- a/content/svg/content/src/nsSVGBoolean.cpp +++ b/content/svg/content/src/nsSVGBoolean.cpp @@ -70,13 +70,26 @@ GetValueFromString(const nsAString &aValueAsString, return NS_ERROR_DOM_SYNTAX_ERR; } +static nsresult +GetValueFromAtom(const nsIAtom* aValueAsAtom, bool *aValue) +{ + if (aValueAsAtom == nsGkAtoms::_true) { + *aValue = true; + return NS_OK; + } + if (aValueAsAtom == nsGkAtoms::_false) { + *aValue = false; + return NS_OK; + } + return NS_ERROR_DOM_SYNTAX_ERR; +} + nsresult -nsSVGBoolean::SetBaseValueString(const nsAString &aValueAsString, - nsSVGElement *aSVGElement) +nsSVGBoolean::SetBaseValueAtom(const nsIAtom* aValue, nsSVGElement *aSVGElement) { bool val; - nsresult rv = GetValueFromString(aValueAsString, &val); + nsresult rv = GetValueFromAtom(aValue, &val); if (NS_FAILED(rv)) { return rv; } @@ -95,30 +108,28 @@ nsSVGBoolean::SetBaseValueString(const nsAString &aValueAsString, return NS_OK; } -void -nsSVGBoolean::GetBaseValueString(nsAString & aValueAsString) +nsIAtom* +nsSVGBoolean::GetBaseValueAtom() const { - aValueAsString.Assign(mBaseVal - ? NS_LITERAL_STRING("true") - : NS_LITERAL_STRING("false")); + return mBaseVal ? nsGkAtoms::_true : nsGkAtoms::_false; } void -nsSVGBoolean::SetBaseValue(bool aValue, - nsSVGElement *aSVGElement) +nsSVGBoolean::SetBaseValue(bool aValue, nsSVGElement *aSVGElement) { NS_PRECONDITION(aValue == true || aValue == false, "Boolean out of range"); - if (aValue != mBaseVal) { - mBaseVal = aValue; - if (!mIsAnimated) { - mAnimVal = mBaseVal; - } - else { - aSVGElement->AnimationNeedsResample(); - } - aSVGElement->DidChangeBoolean(mAttrEnum, true); + if (aValue == mBaseVal) { + return; } + + mBaseVal = aValue; + if (!mIsAnimated) { + mAnimVal = mBaseVal; + } else { + aSVGElement->AnimationNeedsResample(); + } + aSVGElement->DidChangeBoolean(mAttrEnum); } void diff --git a/content/svg/content/src/nsSVGBoolean.h b/content/svg/content/src/nsSVGBoolean.h index 4ca77411d0a..f649c37ce47 100644 --- a/content/svg/content/src/nsSVGBoolean.h +++ b/content/svg/content/src/nsSVGBoolean.h @@ -58,9 +58,8 @@ public: mIsAnimated = false; } - nsresult SetBaseValueString(const nsAString& aValue, - nsSVGElement *aSVGElement); - void GetBaseValueString(nsAString& aValue); + nsresult SetBaseValueAtom(const nsIAtom* aValue, nsSVGElement *aSVGElement); + nsIAtom* GetBaseValueAtom() const; void SetBaseValue(bool aValue, nsSVGElement *aSVGElement); bool GetBaseValue() const diff --git a/content/svg/content/src/nsSVGElement.cpp b/content/svg/content/src/nsSVGElement.cpp index 61c693aa4dd..2e6ef2b09ab 100644 --- a/content/svg/content/src/nsSVGElement.cpp +++ b/content/svg/content/src/nsSVGElement.cpp @@ -89,6 +89,7 @@ #include #include "nsSMILMappedAttribute.h" #include "SVGMotionSMILAttr.h" +#include "nsAttrValueOrString.h" using namespace mozilla; @@ -262,6 +263,15 @@ nsresult nsSVGElement::AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName, const nsAttrValue* aValue, bool aNotify) { + // We don't currently use nsMappedAttributes within SVG. If this changes, we + // need to be very careful because some nsAttrValues used by SVG point to + // member data of SVG elements and if an nsAttrValue outlives the SVG element + // whose data it points to (by virtue of being stored in + // mAttrsAndChildren->mMappedAttributes, meaning it's shared between + // elements), the pointer will dangle. See bug 724680. + NS_ABORT_IF_FALSE(!mAttrsAndChildren.HasMappedAttrs(), + "Unexpected use of nsMappedAttributes within SVG"); + // If this is an svg presentation attribute we need to map it into // the content stylerule. // XXX For some reason incremental mapping doesn't work, so for now @@ -290,6 +300,8 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID, { nsresult rv = NS_OK; bool foundMatch = false; + bool didSetResult = false; + if (aNamespaceID == kNameSpaceID_None) { // Check for nsSVGLength2 attribute @@ -301,6 +313,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID, rv = lengthInfo.mLengths[i].SetBaseValueString(aValue, this, false); if (NS_FAILED(rv)) { lengthInfo.Reset(i); + } else { + aResult.SetTo(lengthInfo.mLengths[i], &aValue); + didSetResult = true; } foundMatch = true; break; @@ -315,6 +330,10 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID, rv = lengthListInfo.mLengthLists[i].SetBaseValueString(aValue); if (NS_FAILED(rv)) { lengthListInfo.Reset(i); + } else { + aResult.SetTo(lengthListInfo.mLengthLists[i].GetBaseValue(), + &aValue); + didSetResult = true; } foundMatch = true; break; @@ -330,6 +349,10 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID, rv = numberListInfo.mNumberLists[i].SetBaseValueString(aValue); if (NS_FAILED(rv)) { numberListInfo.Reset(i); + } else { + aResult.SetTo(numberListInfo.mNumberLists[i].GetBaseValue(), + &aValue); + didSetResult = true; } foundMatch = true; break; @@ -342,11 +365,12 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID, if (GetPointListAttrName() == aAttribute) { SVGAnimatedPointList* pointList = GetAnimatedPointList(); if (pointList) { - rv = pointList->SetBaseValueString(aValue); - if (NS_FAILED(rv)) { - // The spec says we parse everything up to the failure, so we don't - // call pointList->ClearBaseValue() - } + pointList->SetBaseValueString(aValue); + // The spec says we parse everything up to the failure, so we DON'T + // need to check the result of SetBaseValueString or call + // pointList->ClearBaseValue() if it fails + aResult.SetTo(pointList->GetBaseValue(), &aValue); + didSetResult = true; foundMatch = true; } } @@ -357,11 +381,12 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID, if (GetPathDataAttrName() == aAttribute) { SVGAnimatedPathSegList* segList = GetAnimPathSegList(); if (segList) { - rv = segList->SetBaseValueString(aValue); - if (NS_FAILED(rv)) { - // The spec says we parse everything up to the failure, so we don't - // call segList->ClearBaseValue() - } + segList->SetBaseValueString(aValue); + // The spec says we parse everything up to the failure, so we DON'T + // need to check the result of SetBaseValueString or call + // segList->ClearBaseValue() if it fails + aResult.SetTo(segList->GetBaseValue(), &aValue); + didSetResult = true; foundMatch = true; } } @@ -375,6 +400,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID, rv = numberInfo.mNumbers[i].SetBaseValueString(aValue, this); if (NS_FAILED(rv)) { numberInfo.Reset(i); + } else { + aResult.SetTo(numberInfo.mNumbers[i].GetBaseValue(), &aValue); + didSetResult = true; } foundMatch = true; break; @@ -390,6 +418,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID, rv = numberPairInfo.mNumberPairs[i].SetBaseValueString(aValue, this); if (NS_FAILED(rv)) { numberPairInfo.Reset(i); + } else { + aResult.SetTo(numberPairInfo.mNumberPairs[i], &aValue); + didSetResult = true; } foundMatch = true; break; @@ -405,6 +436,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID, rv = integerInfo.mIntegers[i].SetBaseValueString(aValue, this); if (NS_FAILED(rv)) { integerInfo.Reset(i); + } else { + aResult.SetTo(integerInfo.mIntegers[i].GetBaseValue(), &aValue); + didSetResult = true; } foundMatch = true; break; @@ -417,9 +451,13 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID, IntegerPairAttributesInfo integerPairInfo = GetIntegerPairInfo(); for (i = 0; i < integerPairInfo.mIntegerPairCount; i++) { if (aAttribute == *integerPairInfo.mIntegerPairInfo[i].mName) { - rv = integerPairInfo.mIntegerPairs[i].SetBaseValueString(aValue, this); + rv = + integerPairInfo.mIntegerPairs[i].SetBaseValueString(aValue, this); if (NS_FAILED(rv)) { integerPairInfo.Reset(i); + } else { + aResult.SetTo(integerPairInfo.mIntegerPairs[i], &aValue); + didSetResult = true; } foundMatch = true; break; @@ -435,6 +473,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID, rv = angleInfo.mAngles[i].SetBaseValueString(aValue, this, false); if (NS_FAILED(rv)) { angleInfo.Reset(i); + } else { + aResult.SetTo(angleInfo.mAngles[i], &aValue); + didSetResult = true; } foundMatch = true; break; @@ -447,9 +488,13 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID, BooleanAttributesInfo booleanInfo = GetBooleanInfo(); for (i = 0; i < booleanInfo.mBooleanCount; i++) { if (aAttribute == *booleanInfo.mBooleanInfo[i].mName) { - rv = booleanInfo.mBooleans[i].SetBaseValueString(aValue, this); + nsCOMPtr valAtom = do_GetAtom(aValue); + rv = booleanInfo.mBooleans[i].SetBaseValueAtom(valAtom, this); if (NS_FAILED(rv)) { booleanInfo.Reset(i); + } else { + aResult.SetTo(valAtom); + didSetResult = true; } foundMatch = true; break; @@ -462,9 +507,13 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID, EnumAttributesInfo enumInfo = GetEnumInfo(); for (i = 0; i < enumInfo.mEnumCount; i++) { if (aAttribute == *enumInfo.mEnumInfo[i].mName) { - rv = enumInfo.mEnums[i].SetBaseValueString(aValue, this); + nsCOMPtr valAtom = do_GetAtom(aValue); + rv = enumInfo.mEnums[i].SetBaseValueAtom(valAtom, this); if (NS_FAILED(rv)) { enumInfo.Reset(i); + } else { + aResult.SetTo(valAtom); + didSetResult = true; } foundMatch = true; break; @@ -486,9 +535,12 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID, StringListAttributesInfo stringListInfo = GetStringListInfo(); for (i = 0; i < stringListInfo.mStringListCount; i++) { if (aAttribute == *stringListInfo.mStringListInfo[i].mName) { - rv = stringListInfo.mStringLists[i].SetValue(aValue, false); + rv = stringListInfo.mStringLists[i].SetValue(aValue); if (NS_FAILED(rv)) { stringListInfo.Reset(i); + } else { + aResult.SetTo(stringListInfo.mStringLists[i], &aValue); + didSetResult = true; } foundMatch = true; break; @@ -504,6 +556,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID, rv = viewBox->SetBaseValueString(aValue, this); if (NS_FAILED(rv)) { viewBox->Init(); + } else { + aResult.SetTo(*viewBox, &aValue); + didSetResult = true; } foundMatch = true; } @@ -515,6 +570,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID, rv = preserveAspectRatio->SetBaseValueString(aValue, this); if (NS_FAILED(rv)) { preserveAspectRatio->Init(); + } else { + aResult.SetTo(*preserveAspectRatio, &aValue); + didSetResult = true; } foundMatch = true; } @@ -525,6 +583,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID, rv = transformList->SetBaseValueString(aValue); if (NS_FAILED(rv)) { transformList->ClearBaseValue(); + } else { + aResult.SetTo(transformList->GetBaseValue(), &aValue); + didSetResult = true; } foundMatch = true; } @@ -550,7 +611,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID, ReportAttributeParseFailure(OwnerDoc(), aAttribute, aValue); return false; } - aResult.SetTo(aValue); + if (!didSetResult) { + aResult.SetTo(aValue); + } return true; } @@ -584,8 +647,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName, for (PRUint32 i = 0; i < lenInfo.mLengthCount; i++) { if (aName == *lenInfo.mLengthInfo[i].mName) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); lenInfo.Reset(i); - DidChangeLength(i, false); return; } } @@ -595,8 +658,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName, for (PRUint32 i = 0; i < lengthListInfo.mLengthListCount; i++) { if (aName == *lengthListInfo.mLengthListInfo[i].mName) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); lengthListInfo.Reset(i); - DidChangeLengthList(i, false); return; } } @@ -606,8 +669,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName, for (PRUint32 i = 0; i < numberListInfo.mNumberListCount; i++) { if (aName == *numberListInfo.mNumberListInfo[i].mName) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); numberListInfo.Reset(i); - DidChangeNumberList(i, false); return; } } @@ -616,6 +679,7 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName, if (GetPointListAttrName() == aName) { SVGAnimatedPointList *pointList = GetAnimatedPointList(); if (pointList) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); pointList->ClearBaseValue(); return; } @@ -625,8 +689,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName, if (GetPathDataAttrName() == aName) { SVGAnimatedPathSegList *segList = GetAnimPathSegList(); if (segList) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); segList->ClearBaseValue(); - DidChangePathSegList(false); return; } } @@ -637,7 +701,6 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName, for (PRUint32 i = 0; i < numInfo.mNumberCount; i++) { if (aName == *numInfo.mNumberInfo[i].mName) { numInfo.Reset(i); - DidChangeNumber(i, false); return; } } @@ -647,8 +710,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName, for (PRUint32 i = 0; i < numPairInfo.mNumberPairCount; i++) { if (aName == *numPairInfo.mNumberPairInfo[i].mName) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); numPairInfo.Reset(i); - DidChangeNumberPair(i, false); return; } } @@ -659,7 +722,6 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName, for (PRUint32 i = 0; i < intInfo.mIntegerCount; i++) { if (aName == *intInfo.mIntegerInfo[i].mName) { intInfo.Reset(i); - DidChangeInteger(i, false); return; } } @@ -669,8 +731,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName, for (PRUint32 i = 0; i < intPairInfo.mIntegerPairCount; i++) { if (aName == *intPairInfo.mIntegerPairInfo[i].mName) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); intPairInfo.Reset(i); - DidChangeIntegerPair(i, false); return; } } @@ -680,8 +742,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName, for (PRUint32 i = 0; i < angleInfo.mAngleCount; i++) { if (aName == *angleInfo.mAngleInfo[i].mName) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); angleInfo.Reset(i); - DidChangeAngle(i, false); return; } } @@ -692,7 +754,6 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName, for (PRUint32 i = 0; i < boolInfo.mBooleanCount; i++) { if (aName == *boolInfo.mBooleanInfo[i].mName) { boolInfo.Reset(i); - DidChangeBoolean(i, false); return; } } @@ -703,7 +764,6 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName, for (PRUint32 i = 0; i < enumInfo.mEnumCount; i++) { if (aName == *enumInfo.mEnumInfo[i].mName) { enumInfo.Reset(i); - DidChangeEnum(i, false); return; } } @@ -712,8 +772,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName, if (aName == nsGkAtoms::viewBox) { nsSVGViewBox* viewBox = GetViewBox(); if (viewBox) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); viewBox->Init(); - DidChangeViewBox(false); return; } } @@ -722,10 +782,9 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName, if (aName == nsGkAtoms::preserveAspectRatio) { SVGAnimatedPreserveAspectRatio *preserveAspectRatio = GetPreserveAspectRatio(); - if (preserveAspectRatio) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); preserveAspectRatio->Init(); - DidChangePreserveAspectRatio(false); return; } } @@ -734,8 +793,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName, if (GetTransformListAttrName() == aName) { SVGAnimatedTransformList *transformList = GetAnimatedTransformList(); if (transformList) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); transformList->ClearBaseValue(); - DidChangeTransformList(false); return; } } @@ -743,6 +802,7 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName, // Check for conditional processing attributes nsCOMPtr tests(do_QueryInterface(this)); if (tests && tests->IsConditionalProcessingAttribute(aName)) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); tests->UnsetAttr(aName); return; } @@ -752,6 +812,7 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName, for (PRUint32 i = 0; i < stringListInfo.mStringListCount; i++) { if (aName == *stringListInfo.mStringListInfo[i].mName) { + MaybeSerializeAttrBeforeRemoval(aName, aNotify); stringListInfo.Reset(i); return; } @@ -765,7 +826,6 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName, if (aNamespaceID == stringInfo.mStringInfo[i].mNamespaceID && aName == *stringInfo.mStringInfo[i].mName) { stringInfo.Reset(i); - DidChangeString(i); return; } } @@ -1247,6 +1307,137 @@ nsSVGElement::GetAnimatedContentStyleRule() nsnull)); } +/** + * Helper methods for the type-specific WillChangeXXX methods. + * + * This method sends out appropriate pre-change notifications so that selector + * restyles (e.g. due to changes that cause |elem[attr="val"]| to start/stop + * matching) work, and it returns an nsAttrValue that _may_ contain the + * attribute's pre-change value. + * + * The nsAttrValue returned by this method depends on whether there are + * mutation event listeners listening for changes to this element's attributes. + * If not, then the object returned is empty. If there are, then the + * nsAttrValue returned contains a serialized copy of the attribute's value + * prior to the change, and this object should be passed to the corresponding + * DidChangeXXX method call (assuming a WillChangeXXX call is required for the + * SVG type - see comment below). This is necessary so that the 'prevValue' + * property of the mutation event that is dispatched will correctly contain the + * old value. + * + * The reason we need to serialize the old value if there are mutation + * event listeners is because the underlying nsAttrValue for the attribute + * points directly to a parsed representation of the attribute (e.g. an + * SVGAnimatedLengthList*) that is a member of the SVG element. That object + * will have changed by the time DidChangeXXX has been called, so without the + * serialization of the old attribute value that we provide, DidChangeXXX + * would have no way to get the old value to pass to SetAttrAndNotify. + * + * We only return the old value when there are mutation event listeners because + * it's not needed otherwise, and because it's expensive to serialize the old + * value. This is especially true for list type attributes, which may be built + * up via the SVG DOM resulting in a large number of Will/DidModifyXXX calls + * before the script finally finishes setting the attribute. + * + * Note that unlike using SetParsedAttr, using Will/DidChangeXXX does NOT check + * and filter out redundant changes. Before calling WillChangeXXX, the caller + * should check whether the new and old values are actually the same, and skip + * calling Will/DidChangeXXX if they are. + * + * Also note that not all SVG types use this scheme. For types that can be + * represented by an nsAttrValue without pointing back to an SVG object (e.g. + * enums, booleans, integers) we can simply use SetParsedAttr which will do all + * of the above for us. For such types there is no matching WillChangeXXX + * method, only DidChangeXXX which calls SetParsedAttr. + */ +nsAttrValue +nsSVGElement::WillChangeValue(nsIAtom* aName) +{ + // We need an empty attr value: + // a) to pass to BeforeSetAttr when GetParsedAttr returns nsnull + // b) to store the old value in the case we have mutation listeners + // We can use the same value for both purposes since (a) happens before (b). + // Also, we should be careful to always return this value to benefit from + // return value optimization. + nsAttrValue emptyOrOldAttrValue; + const nsAttrValue* attrValue = GetParsedAttr(aName); + + // This is not strictly correct--the attribute value parameter for + // BeforeSetAttr should reflect the value that *will* be set but that implies + // allocating, e.g. an extra nsSVGLength2, and isn't necessary at the moment + // since no SVG elements overload BeforeSetAttr. For now we just pass the + // current value. + nsAttrValueOrString attrStringOrValue(attrValue ? *attrValue + : emptyOrOldAttrValue); + DebugOnly rv = + BeforeSetAttr(kNameSpaceID_None, aName, &attrStringOrValue, + kNotifyDocumentObservers); + // SVG elements aren't expected to overload BeforeSetAttr in such a way that + // it may fail. So long as this is the case we don't need to check and pass on + // the return value which simplifies the calling code significantly. + NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "Unexpected failure from BeforeSetAttr"); + + // We only need to set the old value if we have listeners since otherwise it + // isn't used. + if (attrValue && + nsContentUtils::HasMutationListeners(this, + NS_EVENT_BITS_MUTATION_ATTRMODIFIED, + this)) { + emptyOrOldAttrValue.SetToSerialized(*attrValue); + } + + PRUint8 modType = attrValue + ? static_cast(nsIDOMMutationEvent::MODIFICATION) + : static_cast(nsIDOMMutationEvent::ADDITION); + nsNodeUtils::AttributeWillChange(this, kNameSpaceID_None, aName, modType); + + return emptyOrOldAttrValue; +} + +/** + * Helper methods for the type-specific DidChangeXXX methods. + * + * aEmptyOrOldValue will normally be the object returned from the corresponding + * WillChangeXXX call. This is because: + * a) WillChangeXXX will ensure the object is set when we have mutation + * listeners, and + * b) WillChangeXXX will ensure the object represents a serialized version of + * the old attribute value so that the value doesn't change when the + * underlying SVG type is updated. + */ +void +nsSVGElement::DidChangeValue(nsIAtom* aName, + const nsAttrValue& aEmptyOrOldValue, + nsAttrValue& aNewValue) +{ + bool hasListeners = + nsContentUtils::HasMutationListeners(this, + NS_EVENT_BITS_MUTATION_ATTRMODIFIED, + this); + PRUint8 modType = HasAttr(kNameSpaceID_None, aName) + ? static_cast(nsIDOMMutationEvent::MODIFICATION) + : static_cast(nsIDOMMutationEvent::ADDITION); + SetAttrAndNotify(kNameSpaceID_None, aName, nsnull, aEmptyOrOldValue, + aNewValue, modType, hasListeners, kNotifyDocumentObservers, + kCallAfterSetAttr); +} + +void +nsSVGElement::MaybeSerializeAttrBeforeRemoval(nsIAtom* aName, bool aNotify) +{ + if (!aNotify || + !nsContentUtils::HasMutationListeners(this, + NS_EVENT_BITS_MUTATION_ATTRMODIFIED, + this)) { + return; + } + + nsAutoString serializedValue; + mAttrsAndChildren.GetAttr(aName)->ToString(serializedValue); + nsAttrValue attrValue(serializedValue); + mAttrsAndChildren.SetAndTakeAttr(aName, attrValue); +} + /* static */ nsIAtom* nsSVGElement::GetEventNameForAttr(nsIAtom* aAttr) { @@ -1295,7 +1486,8 @@ nsSVGElement::GetCtx() const } /* virtual */ gfxMatrix -nsSVGElement::PrependLocalTransformTo(const gfxMatrix &aMatrix) const +nsSVGElement::PrependLocalTransformsTo(const gfxMatrix &aMatrix, + TransformTypes aWhich) const { return aMatrix; } @@ -1329,25 +1521,27 @@ nsSVGElement::SetLength(nsIAtom* aName, const nsSVGLength2 &aLength) NS_ABORT_IF_FALSE(false, "no length found to set"); } -void -nsSVGElement::DidChangeLength(PRUint8 aAttrEnum, bool aDoSetAttr) +nsAttrValue +nsSVGElement::WillChangeLength(PRUint8 aAttrEnum) { - if (!aDoSetAttr) - return; + return WillChangeValue(*GetLengthInfo().mLengthInfo[aAttrEnum].mName); +} +void +nsSVGElement::DidChangeLength(PRUint8 aAttrEnum, + const nsAttrValue& aEmptyOrOldValue) +{ LengthAttributesInfo info = GetLengthInfo(); NS_ASSERTION(info.mLengthCount > 0, "DidChangeLength on element with no length attribs"); - NS_ASSERTION(aAttrEnum < info.mLengthCount, "aAttrEnum out of range"); - nsAutoString serializedValue; - info.mLengths[aAttrEnum].GetBaseValueString(serializedValue); + nsAttrValue newValue; + newValue.SetTo(info.mLengths[aAttrEnum], nsnull); - nsAttrValue attrValue(serializedValue); - SetParsedAttr(kNameSpaceID_None, *info.mLengthInfo[aAttrEnum].mName, nsnull, - attrValue, true); + DidChangeValue(*info.mLengthInfo[aAttrEnum].mName, aEmptyOrOldValue, + newValue); } void @@ -1424,24 +1618,27 @@ nsSVGElement::LengthListAttributesInfo::Reset(PRUint8 aAttrEnum) // caller notifies } -void -nsSVGElement::DidChangeLengthList(PRUint8 aAttrEnum, bool aDoSetAttr) +nsAttrValue +nsSVGElement::WillChangeLengthList(PRUint8 aAttrEnum) { - if (!aDoSetAttr) - return; + return WillChangeValue(*GetLengthListInfo().mLengthListInfo[aAttrEnum].mName); +} +void +nsSVGElement::DidChangeLengthList(PRUint8 aAttrEnum, + const nsAttrValue& aEmptyOrOldValue) +{ LengthListAttributesInfo info = GetLengthListInfo(); NS_ASSERTION(info.mLengthListCount > 0, "DidChangeLengthList on element with no length list attribs"); NS_ASSERTION(aAttrEnum < info.mLengthListCount, "aAttrEnum out of range"); - nsAutoString serializedValue; - info.mLengthLists[aAttrEnum].GetBaseValue().GetValueAsString(serializedValue); + nsAttrValue newValue; + newValue.SetTo(info.mLengthLists[aAttrEnum].GetBaseValue(), nsnull); - nsAttrValue attrValue(serializedValue); - SetParsedAttr(kNameSpaceID_None, *info.mLengthListInfo[aAttrEnum].mName, - nsnull, attrValue, true); + DidChangeValue(*info.mLengthListInfo[aAttrEnum].mName, aEmptyOrOldValue, + newValue); } void @@ -1506,24 +1703,28 @@ nsSVGElement::NumberListAttributesInfo::Reset(PRUint8 aAttrEnum) // caller notifies } -void -nsSVGElement::DidChangeNumberList(PRUint8 aAttrEnum, bool aDoSetAttr) +nsAttrValue +nsSVGElement::WillChangeNumberList(PRUint8 aAttrEnum) { - if (!aDoSetAttr) - return; + return WillChangeValue(*GetNumberListInfo().mNumberListInfo[aAttrEnum].mName); +} +void +nsSVGElement::DidChangeNumberList(PRUint8 aAttrEnum, + const nsAttrValue& aEmptyOrOldValue) +{ NumberListAttributesInfo info = GetNumberListInfo(); NS_ABORT_IF_FALSE(info.mNumberListCount > 0, - "DidChangeNumberList on element with no number list attribs"); - NS_ABORT_IF_FALSE(aAttrEnum < info.mNumberListCount, "aAttrEnum out of range"); + "DidChangeNumberList on element with no number list attribs"); + NS_ABORT_IF_FALSE(aAttrEnum < info.mNumberListCount, + "aAttrEnum out of range"); - nsAutoString serializedValue; - info.mNumberLists[aAttrEnum].GetBaseValue().GetValueAsString(serializedValue); + nsAttrValue newValue; + newValue.SetTo(info.mNumberLists[aAttrEnum].GetBaseValue(), nsnull); - nsAttrValue attrValue(serializedValue); - SetParsedAttr(kNameSpaceID_None, *info.mNumberListInfo[aAttrEnum].mName, - nsnull, attrValue, true); + DidChangeValue(*info.mNumberListInfo[aAttrEnum].mName, aEmptyOrOldValue, + newValue); } void @@ -1565,20 +1766,24 @@ nsSVGElement::GetAnimatedNumberList(nsIAtom *aAttrName) return nsnull; } -void -nsSVGElement::DidChangePointList(bool aDoSetAttr) +nsAttrValue +nsSVGElement::WillChangePointList() { - NS_ABORT_IF_FALSE(GetPointListAttrName(), "Changing non-existent point list?"); + NS_ABORT_IF_FALSE(GetPointListAttrName(), + "Changing non-existent point list?"); + return WillChangeValue(GetPointListAttrName()); +} - if (!aDoSetAttr) - return; +void +nsSVGElement::DidChangePointList(const nsAttrValue& aEmptyOrOldValue) +{ + NS_ABORT_IF_FALSE(GetPointListAttrName(), + "Changing non-existent point list?"); - nsAutoString serializedValue; - GetAnimatedPointList()->GetBaseValue().GetValueAsString(serializedValue); + nsAttrValue newValue; + newValue.SetTo(GetAnimatedPointList()->GetBaseValue(), nsnull); - nsAttrValue attrValue(serializedValue); - SetParsedAttr(kNameSpaceID_None, GetPointListAttrName(), nsnull, - attrValue, true); + DidChangeValue(GetPointListAttrName(), aEmptyOrOldValue, newValue); } void @@ -1596,18 +1801,24 @@ nsSVGElement::DidAnimatePointList() } } -void -nsSVGElement::DidChangePathSegList(bool aDoSetAttr) +nsAttrValue +nsSVGElement::WillChangePathSegList() { - if (!aDoSetAttr) - return; + NS_ABORT_IF_FALSE(GetPathDataAttrName(), + "Changing non-existent path seg list?"); + return WillChangeValue(GetPathDataAttrName()); +} - nsAutoString serializedValue; - GetAnimPathSegList()->GetBaseValue().GetValueAsString(serializedValue); +void +nsSVGElement::DidChangePathSegList(const nsAttrValue& aEmptyOrOldValue) +{ + NS_ABORT_IF_FALSE(GetPathDataAttrName(), + "Changing non-existent path seg list?"); - nsAttrValue attrValue(serializedValue); - SetParsedAttr(kNameSpaceID_None, GetPathDataAttrName(), nsnull, - attrValue, true); + nsAttrValue newValue; + newValue.SetTo(GetAnimPathSegList()->GetBaseValue(), nsnull); + + DidChangeValue(GetPathDataAttrName(), aEmptyOrOldValue, newValue); } void @@ -1638,22 +1849,17 @@ void nsSVGElement::NumberAttributesInfo::Reset(PRUint8 aAttrEnum) } void -nsSVGElement::DidChangeNumber(PRUint8 aAttrEnum, bool aDoSetAttr) +nsSVGElement::DidChangeNumber(PRUint8 aAttrEnum) { - if (!aDoSetAttr) - return; - NumberAttributesInfo info = GetNumberInfo(); NS_ASSERTION(info.mNumberCount > 0, "DidChangeNumber on element with no number attribs"); - NS_ASSERTION(aAttrEnum < info.mNumberCount, "aAttrEnum out of range"); - nsAutoString serializedValue; - info.mNumbers[aAttrEnum].GetBaseValueString(serializedValue); + nsAttrValue attrValue; + attrValue.SetTo(info.mNumbers[aAttrEnum].GetBaseValue(), nsnull); - nsAttrValue attrValue(serializedValue); SetParsedAttr(kNameSpaceID_None, *info.mNumberInfo[aAttrEnum].mName, nsnull, attrValue, true); } @@ -1705,25 +1911,27 @@ void nsSVGElement::NumberPairAttributesInfo::Reset(PRUint8 aAttrEnum) mNumberPairInfo[aAttrEnum].mDefaultValue2); } -void -nsSVGElement::DidChangeNumberPair(PRUint8 aAttrEnum, bool aDoSetAttr) +nsAttrValue +nsSVGElement::WillChangeNumberPair(PRUint8 aAttrEnum) { - if (!aDoSetAttr) - return; + return WillChangeValue(*GetNumberPairInfo().mNumberPairInfo[aAttrEnum].mName); +} +void +nsSVGElement::DidChangeNumberPair(PRUint8 aAttrEnum, + const nsAttrValue& aEmptyOrOldValue) +{ NumberPairAttributesInfo info = GetNumberPairInfo(); NS_ASSERTION(info.mNumberPairCount > 0, "DidChangePairNumber on element with no number pair attribs"); - NS_ASSERTION(aAttrEnum < info.mNumberPairCount, "aAttrEnum out of range"); - nsAutoString serializedValue; - info.mNumberPairs[aAttrEnum].GetBaseValueString(serializedValue); + nsAttrValue newValue; + newValue.SetTo(info.mNumberPairs[aAttrEnum], nsnull); - nsAttrValue attrValue(serializedValue); - SetParsedAttr(kNameSpaceID_None, *info.mNumberPairInfo[aAttrEnum].mName, nsnull, - attrValue, true); + DidChangeValue(*info.mNumberPairInfo[aAttrEnum].mName, aEmptyOrOldValue, + newValue); } void @@ -1752,22 +1960,17 @@ void nsSVGElement::IntegerAttributesInfo::Reset(PRUint8 aAttrEnum) } void -nsSVGElement::DidChangeInteger(PRUint8 aAttrEnum, bool aDoSetAttr) +nsSVGElement::DidChangeInteger(PRUint8 aAttrEnum) { - if (!aDoSetAttr) - return; - IntegerAttributesInfo info = GetIntegerInfo(); NS_ASSERTION(info.mIntegerCount > 0, "DidChangeInteger on element with no integer attribs"); - NS_ASSERTION(aAttrEnum < info.mIntegerCount, "aAttrEnum out of range"); - nsAutoString serializedValue; - info.mIntegers[aAttrEnum].GetBaseValueString(serializedValue); + nsAttrValue attrValue; + attrValue.SetTo(info.mIntegers[aAttrEnum].GetBaseValue(), nsnull); - nsAttrValue attrValue(serializedValue); SetParsedAttr(kNameSpaceID_None, *info.mIntegerInfo[aAttrEnum].mName, nsnull, attrValue, true); } @@ -1819,25 +2022,28 @@ void nsSVGElement::IntegerPairAttributesInfo::Reset(PRUint8 aAttrEnum) mIntegerPairInfo[aAttrEnum].mDefaultValue2); } -void -nsSVGElement::DidChangeIntegerPair(PRUint8 aAttrEnum, bool aDoSetAttr) +nsAttrValue +nsSVGElement::WillChangeIntegerPair(PRUint8 aAttrEnum) { - if (!aDoSetAttr) - return; + return WillChangeValue( + *GetIntegerPairInfo().mIntegerPairInfo[aAttrEnum].mName); +} +void +nsSVGElement::DidChangeIntegerPair(PRUint8 aAttrEnum, + const nsAttrValue& aEmptyOrOldValue) +{ IntegerPairAttributesInfo info = GetIntegerPairInfo(); NS_ASSERTION(info.mIntegerPairCount > 0, "DidChangeIntegerPair on element with no integer pair attribs"); - NS_ASSERTION(aAttrEnum < info.mIntegerPairCount, "aAttrEnum out of range"); - nsAutoString serializedValue; - info.mIntegerPairs[aAttrEnum].GetBaseValueString(serializedValue); + nsAttrValue newValue; + newValue.SetTo(info.mIntegerPairs[aAttrEnum], nsnull); - nsAttrValue attrValue(serializedValue); - SetParsedAttr(kNameSpaceID_None, *info.mIntegerPairInfo[aAttrEnum].mName, nsnull, - attrValue, true); + DidChangeValue(*info.mIntegerPairInfo[aAttrEnum].mName, aEmptyOrOldValue, + newValue); } void @@ -1866,25 +2072,26 @@ void nsSVGElement::AngleAttributesInfo::Reset(PRUint8 aAttrEnum) mAngleInfo[aAttrEnum].mDefaultUnitType); } -void -nsSVGElement::DidChangeAngle(PRUint8 aAttrEnum, bool aDoSetAttr) +nsAttrValue +nsSVGElement::WillChangeAngle(PRUint8 aAttrEnum) { - if (!aDoSetAttr) - return; + return WillChangeValue(*GetAngleInfo().mAngleInfo[aAttrEnum].mName); +} +void +nsSVGElement::DidChangeAngle(PRUint8 aAttrEnum, + const nsAttrValue& aEmptyOrOldValue) +{ AngleAttributesInfo info = GetAngleInfo(); NS_ASSERTION(info.mAngleCount > 0, "DidChangeAngle on element with no angle attribs"); - NS_ASSERTION(aAttrEnum < info.mAngleCount, "aAttrEnum out of range"); - nsAutoString serializedValue; - info.mAngles[aAttrEnum].GetBaseValueString(serializedValue); + nsAttrValue newValue; + newValue.SetTo(info.mAngles[aAttrEnum], nsnull); - nsAttrValue attrValue(serializedValue); - SetParsedAttr(kNameSpaceID_None, *info.mAngleInfo[aAttrEnum].mName, nsnull, - attrValue, true); + DidChangeValue(*info.mAngleInfo[aAttrEnum].mName, aEmptyOrOldValue, newValue); } void @@ -1913,22 +2120,15 @@ void nsSVGElement::BooleanAttributesInfo::Reset(PRUint8 aAttrEnum) } void -nsSVGElement::DidChangeBoolean(PRUint8 aAttrEnum, bool aDoSetAttr) +nsSVGElement::DidChangeBoolean(PRUint8 aAttrEnum) { - if (!aDoSetAttr) - return; - BooleanAttributesInfo info = GetBooleanInfo(); NS_ASSERTION(info.mBooleanCount > 0, "DidChangeBoolean on element with no boolean attribs"); - NS_ASSERTION(aAttrEnum < info.mBooleanCount, "aAttrEnum out of range"); - nsAutoString serializedValue; - info.mBooleans[aAttrEnum].GetBaseValueString(serializedValue); - - nsAttrValue attrValue(serializedValue); + nsAttrValue attrValue(info.mBooleans[aAttrEnum].GetBaseValueAtom()); SetParsedAttr(kNameSpaceID_None, *info.mBooleanInfo[aAttrEnum].mName, nsnull, attrValue, true); } @@ -1959,22 +2159,15 @@ void nsSVGElement::EnumAttributesInfo::Reset(PRUint8 aAttrEnum) } void -nsSVGElement::DidChangeEnum(PRUint8 aAttrEnum, bool aDoSetAttr) +nsSVGElement::DidChangeEnum(PRUint8 aAttrEnum) { - if (!aDoSetAttr) - return; - EnumAttributesInfo info = GetEnumInfo(); NS_ASSERTION(info.mEnumCount > 0, "DidChangeEnum on element with no enum attribs"); - NS_ASSERTION(aAttrEnum < info.mEnumCount, "aAttrEnum out of range"); - nsAutoString serializedValue; - info.mEnums[aAttrEnum].GetBaseValueString(serializedValue, this); - - nsAttrValue attrValue(serializedValue); + nsAttrValue attrValue(info.mEnums[aAttrEnum].GetBaseValueAtom(this)); SetParsedAttr(kNameSpaceID_None, *info.mEnumInfo[aAttrEnum].mName, nsnull, attrValue, true); } @@ -1998,22 +2191,23 @@ nsSVGElement::GetViewBox() return nsnull; } -void -nsSVGElement::DidChangeViewBox(bool aDoSetAttr) +nsAttrValue +nsSVGElement::WillChangeViewBox() { - if (!aDoSetAttr) - return; + return WillChangeValue(nsGkAtoms::viewBox); +} +void +nsSVGElement::DidChangeViewBox(const nsAttrValue& aEmptyOrOldValue) +{ nsSVGViewBox *viewBox = GetViewBox(); NS_ASSERTION(viewBox, "DidChangeViewBox on element with no viewBox attrib"); - nsAutoString serializedValue; - viewBox->GetBaseValueString(serializedValue); + nsAttrValue newValue; + newValue.SetTo(*viewBox, nsnull); - nsAttrValue attrValue(serializedValue); - SetParsedAttr(kNameSpaceID_None, nsGkAtoms::viewBox, nsnull, - attrValue, true); + DidChangeValue(nsGkAtoms::viewBox, aEmptyOrOldValue, newValue); } void @@ -2034,24 +2228,26 @@ nsSVGElement::GetPreserveAspectRatio() return nsnull; } -void -nsSVGElement::DidChangePreserveAspectRatio(bool aDoSetAttr) +nsAttrValue +nsSVGElement::WillChangePreserveAspectRatio() { - if (!aDoSetAttr) - return; + return WillChangeValue(nsGkAtoms::preserveAspectRatio); +} +void +nsSVGElement::DidChangePreserveAspectRatio(const nsAttrValue& aEmptyOrOldValue) +{ SVGAnimatedPreserveAspectRatio *preserveAspectRatio = GetPreserveAspectRatio(); NS_ASSERTION(preserveAspectRatio, - "DidChangePreserveAspectRatio on element with no preserveAspectRatio attrib"); + "DidChangePreserveAspectRatio on element with no " + "preserveAspectRatio attrib"); - nsAutoString serializedValue; - preserveAspectRatio->GetBaseValueString(serializedValue); + nsAttrValue newValue; + newValue.SetTo(*preserveAspectRatio, nsnull); - nsAttrValue attrValue(serializedValue); - SetParsedAttr(kNameSpaceID_None, nsGkAtoms::preserveAspectRatio, nsnull, - attrValue, true); + DidChangeValue(nsGkAtoms::preserveAspectRatio, aEmptyOrOldValue, newValue); } void @@ -2066,22 +2262,22 @@ nsSVGElement::DidAnimatePreserveAspectRatio() } } -void -nsSVGElement::DidChangeTransformList(bool aDoSetAttr) +nsAttrValue +nsSVGElement::WillChangeTransformList() { - if (!aDoSetAttr) - return; + return WillChangeValue(GetTransformListAttrName()); +} - SVGAnimatedTransformList* transformList = GetAnimatedTransformList(); - NS_ABORT_IF_FALSE(transformList, - "DidChangeTransformList on element with no transform list"); +void +nsSVGElement::DidChangeTransformList(const nsAttrValue& aEmptyOrOldValue) +{ + NS_ABORT_IF_FALSE(GetTransformListAttrName(), + "Changing non-existent transform list?"); - nsAutoString serializedValue; - transformList->GetBaseValue().GetValueAsString(serializedValue); + nsAttrValue newValue; + newValue.SetTo(GetAnimatedTransformList()->GetBaseValue(), nsnull); - nsAttrValue attrValue(serializedValue); - SetParsedAttr(kNameSpaceID_None, GetTransformListAttrName(), nsnull, - attrValue, true); + DidChangeValue(GetTransformListAttrName(), aEmptyOrOldValue, newValue); } void @@ -2155,29 +2351,49 @@ nsSVGElement::GetStringListInfo() return StringListAttributesInfo(nsnull, nsnull, 0); } -void -nsSVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute, - PRUint8 aAttrEnum) +nsAttrValue +nsSVGElement::WillChangeStringList(bool aIsConditionalProcessingAttribute, + PRUint8 aAttrEnum) { + nsIAtom* name; if (aIsConditionalProcessingAttribute) { nsCOMPtr tests(do_QueryInterface(this)); - tests->DidChangeStringList(aAttrEnum); - return; + name = tests->GetAttrName(aAttrEnum); + } else { + name = *GetStringListInfo().mStringListInfo[aAttrEnum].mName; + } + return WillChangeValue(name); +} + +void +nsSVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute, + PRUint8 aAttrEnum, + const nsAttrValue& aEmptyOrOldValue) +{ + nsIAtom* name; + nsAttrValue newValue; + nsCOMPtr tests; + + if (aIsConditionalProcessingAttribute) { + tests = do_QueryInterface(this); + name = tests->GetAttrName(aAttrEnum); + tests->GetAttrValue(aAttrEnum, newValue); + } else { + StringListAttributesInfo info = GetStringListInfo(); + + NS_ASSERTION(info.mStringListCount > 0, + "DidChangeStringList on element with no string list attribs"); + NS_ASSERTION(aAttrEnum < info.mStringListCount, "aAttrEnum out of range"); + + name = *info.mStringListInfo[aAttrEnum].mName; + newValue.SetTo(info.mStringLists[aAttrEnum], nsnull); } - StringListAttributesInfo info = GetStringListInfo(); + DidChangeValue(name, aEmptyOrOldValue, newValue); - NS_ASSERTION(info.mStringListCount > 0, - "DidChangeStringList on element with no string list attribs"); - - NS_ASSERTION(aAttrEnum < info.mStringListCount, "aAttrEnum out of range"); - - nsAutoString serializedValue; - info.mStringLists[aAttrEnum].GetValue(serializedValue, this); - - nsAttrValue attrValue(serializedValue); - SetParsedAttr(kNameSpaceID_None, *info.mStringListInfo[aAttrEnum].mName, - nsnull, attrValue, true); + if (aIsConditionalProcessingAttribute) { + tests->MaybeInvalidate(); + } } void diff --git a/content/svg/content/src/nsSVGElement.h b/content/svg/content/src/nsSVGElement.h index 53c9c4914bf..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 @@ -169,24 +194,44 @@ public: return GetNumberInfo().mNumberInfo[aAttrEnum].mPercentagesAllowed; } void SetLength(nsIAtom* aName, const nsSVGLength2 &aLength); - virtual void DidChangeLength(PRUint8 aAttrEnum, bool aDoSetAttr); - virtual void DidChangeNumber(PRUint8 aAttrEnum, bool aDoSetAttr); - virtual void DidChangeNumberPair(PRUint8 aAttrEnum, bool aDoSetAttr); - virtual void DidChangeInteger(PRUint8 aAttrEnum, bool aDoSetAttr); - virtual void DidChangeIntegerPair(PRUint8 aAttrEnum, bool aDoSetAttr); - virtual void DidChangeAngle(PRUint8 aAttrEnum, bool aDoSetAttr); - virtual void DidChangeBoolean(PRUint8 aAttrEnum, bool aDoSetAttr); - virtual void DidChangeEnum(PRUint8 aAttrEnum, bool aDoSetAttr); - virtual void DidChangeViewBox(bool aDoSetAttr); - virtual void DidChangePreserveAspectRatio(bool aDoSetAttr); - virtual void DidChangeNumberList(PRUint8 aAttrEnum, bool aDoSetAttr); - virtual void DidChangeLengthList(PRUint8 aAttrEnum, bool aDoSetAttr); - virtual void DidChangePointList(bool aDoSetAttr); - virtual void DidChangePathSegList(bool aDoSetAttr); - virtual void DidChangeTransformList(bool aDoSetAttr); - virtual void DidChangeString(PRUint8 aAttrEnum) {} + + nsAttrValue WillChangeLength(PRUint8 aAttrEnum); + nsAttrValue WillChangeNumberPair(PRUint8 aAttrEnum); + nsAttrValue WillChangeIntegerPair(PRUint8 aAttrEnum); + nsAttrValue WillChangeAngle(PRUint8 aAttrEnum); + nsAttrValue WillChangeViewBox(); + nsAttrValue WillChangePreserveAspectRatio(); + nsAttrValue WillChangeNumberList(PRUint8 aAttrEnum); + nsAttrValue WillChangeLengthList(PRUint8 aAttrEnum); + nsAttrValue WillChangePointList(); + nsAttrValue WillChangePathSegList(); + nsAttrValue WillChangeTransformList(); + nsAttrValue WillChangeStringList(bool aIsConditionalProcessingAttribute, + PRUint8 aAttrEnum); + + void DidChangeLength(PRUint8 aAttrEnum, const nsAttrValue& aEmptyOrOldValue); + void DidChangeNumber(PRUint8 aAttrEnum); + void DidChangeNumberPair(PRUint8 aAttrEnum, + const nsAttrValue& aEmptyOrOldValue); + void DidChangeInteger(PRUint8 aAttrEnum); + void DidChangeIntegerPair(PRUint8 aAttrEnum, + const nsAttrValue& aEmptyOrOldValue); + void DidChangeAngle(PRUint8 aAttrEnum, const nsAttrValue& aEmptyOrOldValue); + void DidChangeBoolean(PRUint8 aAttrEnum); + void DidChangeEnum(PRUint8 aAttrEnum); + void DidChangeViewBox(const nsAttrValue& aEmptyOrOldValue); + void DidChangePreserveAspectRatio(const nsAttrValue& aEmptyOrOldValue); + void DidChangeNumberList(PRUint8 aAttrEnum, + const nsAttrValue& aEmptyOrOldValue); + void DidChangeLengthList(PRUint8 aAttrEnum, + const nsAttrValue& aEmptyOrOldValue); + void DidChangePointList(const nsAttrValue& aEmptyOrOldValue); + void DidChangePathSegList(const nsAttrValue& aEmptyOrOldValue); + void DidChangeTransformList(const nsAttrValue& aEmptyOrOldValue); + void DidChangeString(PRUint8 aAttrEnum) {} void DidChangeStringList(bool aIsConditionalProcessingAttribute, - PRUint8 aAttrEnum); + PRUint8 aAttrEnum, + const nsAttrValue& aEmptyOrOldValue); virtual void DidAnimateLength(PRUint8 aAttrEnum); virtual void DidAnimateNumber(PRUint8 aAttrEnum); @@ -249,6 +294,16 @@ public: } protected: +#ifdef DEBUG + // We define BeforeSetAttr here and mark it MOZ_FINAL to ensure it is NOT used + // by SVG elements. + // This is because we're not currently passing the correct value for aValue to + // BeforeSetAttr since it would involve allocating extra SVG value types. + // See the comment in nsSVGElement::WillChangeValue. + virtual nsresult BeforeSetAttr(PRInt32 aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) MOZ_FINAL { return NS_OK; } +#endif // DEBUG virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName, const nsAttrValue* aValue, bool aNotify); virtual bool ParseAttribute(PRInt32 aNamespaceID, nsIAtom* aAttribute, @@ -264,6 +319,11 @@ protected: void UpdateAnimatedContentStyleRule(); mozilla::css::StyleRule* GetAnimatedContentStyleRule(); + nsAttrValue WillChangeValue(nsIAtom* aName); + void DidChangeValue(nsIAtom* aName, const nsAttrValue& aEmptyOrOldValue, + nsAttrValue& aNewValue); + void MaybeSerializeAttrBeforeRemoval(nsIAtom* aName, bool aNotify); + static nsIAtom* GetEventNameForAttr(nsIAtom* aAttr); struct LengthInfo { diff --git a/content/svg/content/src/nsSVGEnum.cpp b/content/svg/content/src/nsSVGEnum.cpp index 696c436b3f8..3e5e5317dc6 100644 --- a/content/svg/content/src/nsSVGEnum.cpp +++ b/content/svg/content/src/nsSVGEnum.cpp @@ -67,15 +67,12 @@ nsSVGEnum::GetMapping(nsSVGElement *aSVGElement) } nsresult -nsSVGEnum::SetBaseValueString(const nsAString& aValue, - nsSVGElement *aSVGElement) +nsSVGEnum::SetBaseValueAtom(const nsIAtom* aValue, nsSVGElement *aSVGElement) { - nsCOMPtr valAtom = do_GetAtom(aValue); - nsSVGEnumMapping *mapping = GetMapping(aSVGElement); while (mapping && mapping->mKey) { - if (valAtom == *(mapping->mKey)) { + if (aValue == *(mapping->mKey)) { mIsBaseSet = true; if (mBaseVal != mapping->mVal) { mBaseVal = mapping->mVal; @@ -99,19 +96,19 @@ nsSVGEnum::SetBaseValueString(const nsAString& aValue, return NS_ERROR_DOM_SYNTAX_ERR; } -void -nsSVGEnum::GetBaseValueString(nsAString& aValue, nsSVGElement *aSVGElement) +nsIAtom* +nsSVGEnum::GetBaseValueAtom(nsSVGElement *aSVGElement) { nsSVGEnumMapping *mapping = GetMapping(aSVGElement); while (mapping && mapping->mKey) { if (mBaseVal == mapping->mVal) { - (*mapping->mKey)->ToString(aValue); - return; + return *mapping->mKey; } mapping++; } NS_ERROR("unknown enumeration value"); + return nsGkAtoms::_empty; } nsresult @@ -131,7 +128,7 @@ nsSVGEnum::SetBaseValue(PRUint16 aValue, else { aSVGElement->AnimationNeedsResample(); } - aSVGElement->DidChangeEnum(mAttrEnum, true); + aSVGElement->DidChangeEnum(mAttrEnum); } return NS_OK; } diff --git a/content/svg/content/src/nsSVGEnum.h b/content/svg/content/src/nsSVGEnum.h index c59cd36d9e6..50253767554 100644 --- a/content/svg/content/src/nsSVGEnum.h +++ b/content/svg/content/src/nsSVGEnum.h @@ -65,11 +65,8 @@ public: mIsBaseSet = false; } - nsresult SetBaseValueString(const nsAString& aValue, - nsSVGElement *aSVGElement); - void GetBaseValueString(nsAString& aValue, - nsSVGElement *aSVGElement); - + nsresult SetBaseValueAtom(const nsIAtom* aValue, nsSVGElement *aSVGElement); + nsIAtom* GetBaseValueAtom(nsSVGElement *aSVGElement); nsresult SetBaseValue(PRUint16 aValue, nsSVGElement *aSVGElement); PRUint16 GetBaseValue() const 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/nsSVGInteger.cpp b/content/svg/content/src/nsSVGInteger.cpp index dced1e93840..8a1f06dbb0d 100644 --- a/content/svg/content/src/nsSVGInteger.cpp +++ b/content/svg/content/src/nsSVGInteger.cpp @@ -106,9 +106,16 @@ nsSVGInteger::GetBaseValueString(nsAString & aValueAsString) } void -nsSVGInteger::SetBaseValue(int aValue, - nsSVGElement *aSVGElement) +nsSVGInteger::SetBaseValue(int aValue, nsSVGElement *aSVGElement) { + // We can't just rely on SetParsedAttrValue (as called by DidChangeInteger) + // detecting redundant changes since it will compare false if the existing + // attribute value has an associated serialized version (a string value) even + // if the integers match due to the way integers are stored in nsAttrValue. + if (aValue == mBaseVal && mIsBaseSet) { + return; + } + mBaseVal = aValue; mIsBaseSet = true; if (!mIsAnimated) { @@ -117,7 +124,7 @@ nsSVGInteger::SetBaseValue(int aValue, else { aSVGElement->AnimationNeedsResample(); } - aSVGElement->DidChangeInteger(mAttrEnum, true); + aSVGElement->DidChangeInteger(mAttrEnum); } void diff --git a/content/svg/content/src/nsSVGIntegerPair.cpp b/content/svg/content/src/nsSVGIntegerPair.cpp index b5f5d8b8596..dc35ada0e4e 100644 --- a/content/svg/content/src/nsSVGIntegerPair.cpp +++ b/content/svg/content/src/nsSVGIntegerPair.cpp @@ -128,7 +128,7 @@ nsSVGIntegerPair::SetBaseValueString(const nsAString &aValueAsString, } void -nsSVGIntegerPair::GetBaseValueString(nsAString &aValueAsString) +nsSVGIntegerPair::GetBaseValueString(nsAString &aValueAsString) const { aValueAsString.Truncate(); aValueAsString.AppendInt(mBaseVal[0]); @@ -143,6 +143,11 @@ nsSVGIntegerPair::SetBaseValue(PRInt32 aValue, PairIndex aPairIndex, nsSVGElement *aSVGElement) { PRUint32 index = (aPairIndex == eFirst ? 0 : 1); + if (mIsBaseSet && mBaseVal[index] == aValue) { + return; + } + + nsAttrValue emptyOrOldValue = aSVGElement->WillChangeIntegerPair(mAttrEnum); mBaseVal[index] = aValue; mIsBaseSet = true; if (!mIsAnimated) { @@ -151,13 +156,18 @@ nsSVGIntegerPair::SetBaseValue(PRInt32 aValue, PairIndex aPairIndex, else { aSVGElement->AnimationNeedsResample(); } - aSVGElement->DidChangeIntegerPair(mAttrEnum, true); + aSVGElement->DidChangeIntegerPair(mAttrEnum, emptyOrOldValue); } void nsSVGIntegerPair::SetBaseValues(PRInt32 aValue1, PRInt32 aValue2, nsSVGElement *aSVGElement) { + if (mIsBaseSet && mBaseVal[0] == aValue1 && mBaseVal[1] == aValue2) { + return; + } + + nsAttrValue emptyOrOldValue = aSVGElement->WillChangeIntegerPair(mAttrEnum); mBaseVal[0] = aValue1; mBaseVal[1] = aValue2; mIsBaseSet = true; @@ -168,7 +178,7 @@ nsSVGIntegerPair::SetBaseValues(PRInt32 aValue1, PRInt32 aValue2, else { aSVGElement->AnimationNeedsResample(); } - aSVGElement->DidChangeIntegerPair(mAttrEnum, true); + aSVGElement->DidChangeIntegerPair(mAttrEnum, emptyOrOldValue); } void diff --git a/content/svg/content/src/nsSVGIntegerPair.h b/content/svg/content/src/nsSVGIntegerPair.h index 63ae5441216..2e181615b95 100644 --- a/content/svg/content/src/nsSVGIntegerPair.h +++ b/content/svg/content/src/nsSVGIntegerPair.h @@ -66,7 +66,7 @@ public: nsresult SetBaseValueString(const nsAString& aValue, nsSVGElement *aSVGElement); - void GetBaseValueString(nsAString& aValue); + void GetBaseValueString(nsAString& aValue) const; void SetBaseValue(PRInt32 aValue, PairIndex aIndex, nsSVGElement *aSVGElement); void SetBaseValues(PRInt32 aValue1, PRInt32 aValue2, nsSVGElement *aSVGElement); diff --git a/content/svg/content/src/nsSVGLength2.cpp b/content/svg/content/src/nsSVGLength2.cpp index 4709ae0e8ba..40301209dc0 100644 --- a/content/svg/content/src/nsSVGLength2.cpp +++ b/content/svg/content/src/nsSVGLength2.cpp @@ -307,8 +307,17 @@ nsSVGLength2::GetUnitScaleFactor(nsIFrame *aFrame, PRUint8 aUnitType) const void nsSVGLength2::SetBaseValueInSpecifiedUnits(float aValue, - nsSVGElement *aSVGElement) + nsSVGElement *aSVGElement, + bool aDoSetAttr) { + if (mIsBaseSet && mBaseVal == aValue) { + return; + } + + nsAttrValue emptyOrOldValue; + if (aDoSetAttr) { + emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum); + } mBaseVal = aValue; mIsBaseSet = true; if (!mIsAnimated) { @@ -317,7 +326,9 @@ nsSVGLength2::SetBaseValueInSpecifiedUnits(float aValue, else { aSVGElement->AnimationNeedsResample(); } - aSVGElement->DidChangeLength(mAttrEnum, true); + if (aDoSetAttr) { + aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue); + } } nsresult @@ -327,10 +338,23 @@ nsSVGLength2::ConvertToSpecifiedUnits(PRUint16 unitType, if (!IsValidUnitType(unitType)) return NS_ERROR_DOM_NOT_SUPPORTED_ERR; - float valueInUserUnits = + if (mIsBaseSet && mSpecifiedUnitType == PRUint8(unitType)) + return NS_OK; + + // Even though we're not changing the visual effect this length will have + // on the document, we still need to send out notifications in case we have + // mutation listeners, since the actual string value of the attribute will + // change. + nsAttrValue emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum); + + float valueInUserUnits = mBaseVal / GetUnitScaleFactor(aSVGElement, mSpecifiedUnitType); mSpecifiedUnitType = PRUint8(unitType); - SetBaseValue(valueInUserUnits, aSVGElement); + // Setting aDoSetAttr to false here will ensure we don't call + // Will/DidChangeAngle a second time (and dispatch duplicate notifications). + SetBaseValue(valueInUserUnits, aSVGElement, false); + + aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue); return NS_OK; } @@ -345,6 +369,12 @@ nsSVGLength2::NewValueSpecifiedUnits(PRUint16 unitType, if (!IsValidUnitType(unitType)) return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + if (mIsBaseSet && mBaseVal == valueInSpecifiedUnits && + mSpecifiedUnitType == PRUint8(unitType)) { + return NS_OK; + } + + nsAttrValue emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum); mBaseVal = valueInSpecifiedUnits; mIsBaseSet = true; mSpecifiedUnitType = PRUint8(unitType); @@ -354,7 +384,7 @@ nsSVGLength2::NewValueSpecifiedUnits(PRUint16 unitType, else { aSVGElement->AnimationNeedsResample(); } - aSVGElement->DidChangeLength(mAttrEnum, true); + aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue); return NS_OK; } @@ -407,12 +437,21 @@ nsSVGLength2::SetBaseValueString(const nsAString &aValueAsString, { float value; PRUint16 unitType; - + nsresult rv = GetValueFromString(aValueAsString, &value, &unitType); if (NS_FAILED(rv)) { return rv; } - + + if (mIsBaseSet && mBaseVal == value && + mSpecifiedUnitType == PRUint8(unitType)) { + return NS_OK; + } + + nsAttrValue emptyOrOldValue; + if (aDoSetAttr) { + emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum); + } mBaseVal = value; mIsBaseSet = true; mSpecifiedUnitType = PRUint8(unitType); @@ -423,28 +462,31 @@ nsSVGLength2::SetBaseValueString(const nsAString &aValueAsString, aSVGElement->AnimationNeedsResample(); } - aSVGElement->DidChangeLength(mAttrEnum, aDoSetAttr); + if (aDoSetAttr) { + aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue); + } return NS_OK; } void -nsSVGLength2::GetBaseValueString(nsAString & aValueAsString) +nsSVGLength2::GetBaseValueString(nsAString & aValueAsString) const { GetValueString(aValueAsString, mBaseVal, mSpecifiedUnitType); } void -nsSVGLength2::GetAnimValueString(nsAString & aValueAsString) +nsSVGLength2::GetAnimValueString(nsAString & aValueAsString) const { GetValueString(aValueAsString, mAnimVal, mSpecifiedUnitType); } void -nsSVGLength2::SetBaseValue(float aValue, nsSVGElement *aSVGElement) +nsSVGLength2::SetBaseValue(float aValue, nsSVGElement *aSVGElement, + bool aDoSetAttr) { SetBaseValueInSpecifiedUnits(aValue * GetUnitScaleFactor(aSVGElement, mSpecifiedUnitType), - aSVGElement); + aSVGElement, aDoSetAttr); } void diff --git a/content/svg/content/src/nsSVGLength2.h b/content/svg/content/src/nsSVGLength2.h index 1882ef2ec7f..1d555b0826b 100644 --- a/content/svg/content/src/nsSVGLength2.h +++ b/content/svg/content/src/nsSVGLength2.h @@ -82,8 +82,8 @@ public: nsresult SetBaseValueString(const nsAString& aValue, nsSVGElement *aSVGElement, bool aDoSetAttr); - void GetBaseValueString(nsAString& aValue); - void GetAnimValueString(nsAString& aValue); + void GetBaseValueString(nsAString& aValue) const; + void GetAnimValueString(nsAString& aValue) const; float GetBaseValue(nsSVGElement* aSVGElement) const { return mBaseVal / GetUnitScaleFactor(aSVGElement, mSpecifiedUnitType); } @@ -148,8 +148,9 @@ private: float GetUnitScaleFactor(nsSVGSVGElement *aCtx, PRUint8 aUnitType) const; // SetBaseValue and SetAnimValue set the value in user units - void SetBaseValue(float aValue, nsSVGElement *aSVGElement); - void SetBaseValueInSpecifiedUnits(float aValue, nsSVGElement *aSVGElement); + void SetBaseValue(float aValue, nsSVGElement *aSVGElement, bool aDoSetAttr); + void SetBaseValueInSpecifiedUnits(float aValue, nsSVGElement *aSVGElement, + bool aDoSetAttr); void SetAnimValue(float aValue, nsSVGElement *aSVGElement); void SetAnimValueInSpecifiedUnits(float aValue, nsSVGElement *aSVGElement); nsresult NewValueSpecifiedUnits(PRUint16 aUnitType, float aValue, @@ -180,7 +181,7 @@ private: if (!NS_finite(aValue)) { return NS_ERROR_ILLEGAL_VALUE; } - mVal->SetBaseValue(aValue, mSVGElement); + mVal->SetBaseValue(aValue, mSVGElement, true); return NS_OK; } @@ -191,7 +192,7 @@ private: if (!NS_finite(aValue)) { return NS_ERROR_ILLEGAL_VALUE; } - mVal->SetBaseValueInSpecifiedUnits(aValue, mSVGElement); + mVal->SetBaseValueInSpecifiedUnits(aValue, mSVGElement, true); return NS_OK; } diff --git a/content/svg/content/src/nsSVGMarkerElement.cpp b/content/svg/content/src/nsSVGMarkerElement.cpp index e16500449b4..91c6e2950fc 100644 --- a/content/svg/content/src/nsSVGMarkerElement.cpp +++ b/content/svg/content/src/nsSVGMarkerElement.cpp @@ -224,7 +224,7 @@ NS_IMETHODIMP nsSVGMarkerElement::SetOrientToAngle(nsIDOMSVGAngle *angle) nsresult rv = angle->GetValue(&f); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_FINITE(f, NS_ERROR_DOM_SVG_WRONG_TYPE_ERR); - mAngleAttributes[ORIENT].SetBaseValue(f, this); + mAngleAttributes[ORIENT].SetBaseValue(f, this, true); return NS_OK; } diff --git a/content/svg/content/src/nsSVGNumber2.cpp b/content/svg/content/src/nsSVGNumber2.cpp index 5a0450c132e..9cc4d64632a 100644 --- a/content/svg/content/src/nsSVGNumber2.cpp +++ b/content/svg/content/src/nsSVGNumber2.cpp @@ -149,9 +149,12 @@ nsSVGNumber2::GetBaseValueString(nsAString & aValueAsString) } void -nsSVGNumber2::SetBaseValue(float aValue, - nsSVGElement *aSVGElement) +nsSVGNumber2::SetBaseValue(float aValue, nsSVGElement *aSVGElement) { + if (mIsBaseSet && aValue == mBaseVal) { + return; + } + mBaseVal = aValue; mIsBaseSet = true; if (!mIsAnimated) { @@ -160,7 +163,7 @@ nsSVGNumber2::SetBaseValue(float aValue, else { aSVGElement->AnimationNeedsResample(); } - aSVGElement->DidChangeNumber(mAttrEnum, true); + aSVGElement->DidChangeNumber(mAttrEnum); } void diff --git a/content/svg/content/src/nsSVGNumberPair.cpp b/content/svg/content/src/nsSVGNumberPair.cpp index 38254fbfcd0..825704e0476 100644 --- a/content/svg/content/src/nsSVGNumberPair.cpp +++ b/content/svg/content/src/nsSVGNumberPair.cpp @@ -121,14 +121,14 @@ nsSVGNumberPair::SetBaseValueString(const nsAString &aValueAsString, aSVGElement->AnimationNeedsResample(); } - // We don't need to call DidChange* here - we're only called by + // We don't need to call Will/DidChange* here - we're only called by // nsSVGElement::ParseAttribute under nsGenericElement::SetAttr, // which takes care of notifying. return NS_OK; } void -nsSVGNumberPair::GetBaseValueString(nsAString &aValueAsString) +nsSVGNumberPair::GetBaseValueString(nsAString &aValueAsString) const { aValueAsString.Truncate(); aValueAsString.AppendFloat(mBaseVal[0]); @@ -143,6 +143,10 @@ nsSVGNumberPair::SetBaseValue(float aValue, PairIndex aPairIndex, nsSVGElement *aSVGElement) { PRUint32 index = (aPairIndex == eFirst ? 0 : 1); + if (mIsBaseSet && mBaseVal[index] == aValue) { + return; + } + nsAttrValue emptyOrOldValue = aSVGElement->WillChangeNumberPair(mAttrEnum); mBaseVal[index] = aValue; mIsBaseSet = true; if (!mIsAnimated) { @@ -151,13 +155,17 @@ nsSVGNumberPair::SetBaseValue(float aValue, PairIndex aPairIndex, else { aSVGElement->AnimationNeedsResample(); } - aSVGElement->DidChangeNumberPair(mAttrEnum, true); + aSVGElement->DidChangeNumberPair(mAttrEnum, emptyOrOldValue); } void nsSVGNumberPair::SetBaseValues(float aValue1, float aValue2, nsSVGElement *aSVGElement) { + if (mIsBaseSet && mBaseVal[0] == aValue1 && mBaseVal[1] == aValue2) { + return; + } + nsAttrValue emptyOrOldValue = aSVGElement->WillChangeNumberPair(mAttrEnum); mBaseVal[0] = aValue1; mBaseVal[1] = aValue2; mIsBaseSet = true; @@ -168,7 +176,7 @@ nsSVGNumberPair::SetBaseValues(float aValue1, float aValue2, else { aSVGElement->AnimationNeedsResample(); } - aSVGElement->DidChangeNumberPair(mAttrEnum, true); + aSVGElement->DidChangeNumberPair(mAttrEnum, emptyOrOldValue); } void diff --git a/content/svg/content/src/nsSVGNumberPair.h b/content/svg/content/src/nsSVGNumberPair.h index e520ec523ca..f6987724c87 100644 --- a/content/svg/content/src/nsSVGNumberPair.h +++ b/content/svg/content/src/nsSVGNumberPair.h @@ -67,7 +67,7 @@ public: nsresult SetBaseValueString(const nsAString& aValue, nsSVGElement *aSVGElement); - void GetBaseValueString(nsAString& aValue); + void GetBaseValueString(nsAString& aValue) const; void SetBaseValue(float aValue, PairIndex aIndex, nsSVGElement *aSVGElement); void SetBaseValues(float aValue1, float aValue2, nsSVGElement *aSVGElement); 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/src/nsSVGViewBox.cpp b/content/svg/content/src/nsSVGViewBox.cpp index 3c34bb5fa41..3d29219439f 100644 --- a/content/svg/content/src/nsSVGViewBox.cpp +++ b/content/svg/content/src/nsSVGViewBox.cpp @@ -127,10 +127,16 @@ void nsSVGViewBox::SetBaseValue(float aX, float aY, float aWidth, float aHeight, nsSVGElement *aSVGElement) { + if (mHasBaseVal && mBaseVal == nsSVGViewBoxRect(aX, aY, aWidth, aHeight)) { + return; + } + + nsAttrValue emptyOrOldValue = aSVGElement->WillChangeViewBox(); + mBaseVal = nsSVGViewBoxRect(aX, aY, aWidth, aHeight); mHasBaseVal = true; - aSVGElement->DidChangeViewBox(true); + aSVGElement->DidChangeViewBox(emptyOrOldValue); if (mAnimVal) { aSVGElement->AnimationNeedsResample(); } @@ -185,7 +191,7 @@ nsSVGViewBox::SetBaseValueString(const nsAString& aValue, if (mAnimVal) { aSVGElement->AnimationNeedsResample(); } - // We don't need to call DidChange* here - we're only called by + // We don't need to call Will/DidChange* here - we're only called by // nsSVGElement::ParseAttribute under nsGenericElement::SetAttr, // which takes care of notifying. } diff --git a/content/svg/content/test/Makefile.in b/content/svg/content/test/Makefile.in index 0925d9de0b0..5bcc478ca3b 100644 --- a/content/svg/content/test/Makefile.in +++ b/content/svg/content/test/Makefile.in @@ -50,6 +50,7 @@ include $(topsrcdir)/config/rules.mk _TEST_FILES = \ matrixUtils.js \ + MutationEventChecker.js \ test_a_href_01.xhtml \ test_a_href_02.xhtml \ a_href_destination.svg \ @@ -65,6 +66,7 @@ _TEST_FILES = \ bbox-helper.svg \ bounds-helper.svg \ test_dataTypes.html \ + test_dataTypesModEvents.html \ dataTypes-helper.svg \ getCTM-helper.svg \ test_getCTM.html \ @@ -88,7 +90,9 @@ _TEST_FILES = \ test_SVGLengthList.xhtml \ test_SVGLengthList-2.xhtml \ test_SVGMatrix.xhtml \ + test_SVGNumberList.xhtml \ test_SVGPathSegList.xhtml \ + test_SVGPointList.xhtml \ test_SVGStyleElement.xhtml \ test_SVGStringList.xhtml \ test_SVGTransformList.xhtml \ diff --git a/content/svg/content/test/MutationEventChecker.js b/content/svg/content/test/MutationEventChecker.js new file mode 100644 index 00000000000..1394b6ca164 --- /dev/null +++ b/content/svg/content/test/MutationEventChecker.js @@ -0,0 +1,245 @@ +// Helper class to check DOM MutationEvents +// +// Usage: +// +// * Create a new event checker: +// var eventChecker = new MutationEventChecker; +// * Set the attribute to watch +// eventChecker.watchAttr(, ""); +// * Set the events to expect (0..n) +// eventChecker.expect("add", "modify"); +// OR +// eventChecker.expect("add modify"); +// OR +// eventChecker.expect(MutationEvent.ADDITION, MutationEvent.MODIFICATION); +// +// An empty string or empty set of arguments is also fine as a way of checking +// that all expected events have been received and indicating no events are +// expected from the following code, e.g. +// +// eventChecker.expect(""); +// // changes that are not expected to generate events +// eventChecker.expect("modify"); +// // change that is expected to generate an event +// ... +// +// * Either finish listening or set the next attribute to watch +// eventChecker.finish(); +// eventChecker.watchAttr(element, "nextAttribute"); +// +// In either case a check is performed that all expected events have been +// received. +// +// * Event checking can be temporarily disabled with ignoreEvents(). The next +// call to expect() will cause it to resume. + +function MutationEventChecker() +{ + this.expectedEvents = []; + + this.watchAttr = function(element, attr) + { + if (this.attr) { + this.finish(); + } + + this.expectedEvents = []; + this.element = element; + this.attr = attr; + this.oldValue = element.getAttribute(attr); + this.giveUp = false; + this.ignore = false; + + this.element.addEventListener('DOMAttrModified', this._listener, false); + } + + this.expect = function() + { + if (this.giveUp) { + return; + } + + ok(this.expectedEvents.length == 0, + "Expecting new events for " + this.attr + + " but the following previously expected events have still not been " + + "received: " + this._stillExpecting()); + if (this.expectedEvents.length != 0) { + this.giveUp = true; + return; + } + + this.ignore = false; + + if (arguments.length == 0 || + arguments.length == 1 && arguments[0] == "") { + return; + } + + // Turn arguments object into an array + var args = Array.prototype.slice.call(arguments); + // Check for whitespace separated keywords + if (args.length == 1 && typeof args[0] === 'string' && + args[0].indexOf(' ') > 0) { + args = args[0].split(' '); + } + // Convert strings to event Ids + this.expectedEvents = args.map(this._argToEventId); + } + + // Temporarily disable event checking + this.ignoreEvents = function() + { + // Check all events have been received + ok(this.giveUp || this.expectedEvents.length == 0, + "Going to ignore subsequent events on " + this.attr + + " attribute, but we're still expecting the following events: " + + this._stillExpecting()); + + this.ignore = true; + } + + this.finish = function() + { + // Check all events have been received + ok(this.giveUp || this.expectedEvents.length == 0, + "Finishing listening to " + this.attr + + " attribute, but we're still expecting the following events: " + + this._stillExpecting()); + + this.element.removeEventListener('DOMAttrModified', this._listener, false); + this.attr = ""; + } + + this._receiveEvent = function(e) + { + if (this.giveUp || this.ignore) { + this.oldValue = e.newValue; + return; + } + + // Make sure we're expecting something at all + if (this.expectedEvents.length == 0) { + ok(false, 'Unexpected ' + this._eventToName(e.attrChange) + + ' event when none expected on ' + this.attr + ' attribute.'); + return; + } + + var expectedEvent = this.expectedEvents.shift(); + + // Make sure we got the event we expected + if (e.attrChange != expectedEvent) { + ok(false, 'Unexpected ' + this._eventToName(e.attrChange) + + ' on ' + this.attr + ' attribute. Expected ' + + this._eventToName(expectedEvent) + ' (followed by: ' + + this._stillExpecting() + ")"); + // If we get events out of sequence, it doesn't make sense to do any + // further testing since we don't really know what to expect + this.giveUp = true; + return; + } + + // Common param checking + is(e.target, this.element, + 'Unexpected node for mutation event on ' + this.attr + ' attribute'); + is(e.attrName, this.attr, 'Unexpected attribute name for mutation event'); + + // Don't bother testing e.relatedNode since Attr nodes are on the way + // out anyway (but then, so are mutation events...) + + // Event-specific checking + if (e.attrChange == MutationEvent.MODIFICATION) { + ok(this.element.hasAttribute(this.attr), + 'Attribute not set after modification'); + is(e.prevValue, this.oldValue, + 'Unexpected old value for modification to ' + this.attr + + ' attribute'); + isnot(e.newValue, this.oldValue, + 'Unexpected new value for modification to ' + this.attr + + ' attribute'); + } else if (e.attrChange == MutationEvent.REMOVAL) { + ok(!this.element.hasAttribute(this.attr), 'Attribute set after removal'); + is(e.prevValue, this.oldValue, + 'Unexpected old value for removal of ' + this.attr + + ' attribute'); + // DOM 3 Events doesn't say what value newValue will be for a removal + // event but generally empty strings are used for other events when an + // attribute isn't relevant + ok(e.newValue === "", + 'Unexpected new value for removal of ' + this.attr + + ' attribute'); + } else if (e.attrChange == MutationEvent.ADDITION) { + ok(this.element.hasAttribute(this.attr), + 'Attribute not set after addition'); + // DOM 3 Events doesn't say what value prevValue will be for an addition + // event but generally empty strings are used for other events when an + // attribute isn't relevant + ok(e.prevValue === "", + 'Unexpected old value for addition of ' + this.attr + + ' attribute'); + ok(typeof(e.newValue) == 'string' && e.newValue !== "", + 'Unexpected new value for addition of ' + this.attr + + ' attribute'); + } else { + ok(false, 'Unexpected mutation event type: ' + e.attrChange); + this.giveUp = true; + } + this.oldValue = e.newValue; + } + this._listener = this._receiveEvent.bind(this); + + this._stillExpecting = function() + { + if (this.expectedEvents.length == 0) { + return "(nothing)"; + } + var eventNames = []; + for (var i=0; i < this.expectedEvents.length; i++) { + eventNames.push(this._eventToName(this.expectedEvents[i])); + } + return eventNames.join(", "); + } + + this._eventToName = function(evtId) + { + switch (evtId) + { + case MutationEvent.MODIFICATION: + return "modification"; + case MutationEvent.ADDITION: + return "addition"; + case MutationEvent.REMOVAL: + return "removal"; + } + } + + this._argToEventId = function(arg) + { + if (typeof arg === 'number') + return arg; + + if (typeof arg !== 'string') { + ok(false, "Unexpected event type: " + arg); + return 0; + } + + switch (arg.toLowerCase()) + { + case "mod": + case "modify": + case "modification": + return MutationEvent.MODIFICATION; + + case "add": + case "addition": + return MutationEvent.ADDITION; + + case "removal": + case "remove": + return MutationEvent.REMOVAL; + + default: + ok(false, "Unexpected event name: " + arg); + return 0; + } + } +} 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_SVGLengthList.xhtml b/content/svg/content/test/test_SVGLengthList.xhtml index 876421df3f6..fe96cb2c205 100644 --- a/content/svg/content/test/test_SVGLengthList.xhtml +++ b/content/svg/content/test/test_SVGLengthList.xhtml @@ -5,6 +5,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=515116 Tests specific to SVGLengthList + @@ -36,11 +37,31 @@ function run_tests() is(lengths.numberOfItems, 0, 'Checking numberOfItems'); -/* - is(lengths.numberOfItems, 2, 'Checking numberOfItems'); - is(lengths.getItem(1).valueInSpecifiedUnits == 20, 'Checking the value of the second length'); - is(lengths.getItem(1).unitType == SVGLength.SVG_LENGTHTYPE_CM, 'Checking the unit of the second length'); -*/ + // Test mutation events + // --- Initialization + eventChecker = new MutationEventChecker; + eventChecker.watchAttr(text, "x"); + eventChecker.expect("modify"); + text.textContent = "abc"; + text.setAttribute("x", "10 20 30"); + is(lengths.numberOfItems, 3, 'Checking numberOfItems'); + // -- Actual changes + eventChecker.expect("modify modify modify modify modify"); + lengths[0].value = 8; + lengths[0].valueInSpecifiedUnits = 9; + lengths[0].valueAsString = "10"; + lengths[0].convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_CM); + lengths[0].newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_MM, 11); + // -- Redundant changes + eventChecker.expect("modify"); + lengths[0].valueAsString = "10"; + eventChecker.expect(""); + lengths[0].value = 10; + lengths[0].valueInSpecifiedUnits = 10; + lengths[0].valueAsString = "10"; + lengths[0].convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_NUMBER); + lengths[0].newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_NUMBER, 10); + eventChecker.finish(); SimpleTest.finish(); } diff --git a/content/svg/content/test/test_SVGNumberList.xhtml b/content/svg/content/test/test_SVGNumberList.xhtml new file mode 100644 index 00000000000..fcd471f4c4f --- /dev/null +++ b/content/svg/content/test/test_SVGNumberList.xhtml @@ -0,0 +1,64 @@ + + + + Tests specific to SVGNumberList + + + + + +Mozilla Bug 629200 +

+ +
+
+
+ + diff --git a/content/svg/content/test/test_SVGPathSegList.xhtml b/content/svg/content/test/test_SVGPathSegList.xhtml index 73513197f4f..b1c42bc1e8b 100644 --- a/content/svg/content/test/test_SVGPathSegList.xhtml +++ b/content/svg/content/test/test_SVGPathSegList.xhtml @@ -5,6 +5,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=611138 Generic tests for SVG animated length lists + @@ -86,17 +87,25 @@ function run_tests() ok(list.numberOfItems == 1 && list.getItem(0) == seg, 'initialize should be able initialize an invalid path with a non-moveto item'); + // Test mutation events + + eventChecker = new MutationEventChecker; d = 'M0,0 L12,34' path.setAttribute('d', d); - function check_old_value(e) { - is(e.target, path, 'check mutation event is for expected node'); - is(e.attrName, 'd', 'check mutation event is for expected attribute'); - is(e.prevValue, d, 'check old attribute value is correctly reported'); - isnot(e.newValue, d, 'check attribute value has changed'); - } - path.addEventListener('DOMAttrModified', check_old_value, false); - list.getItem(1).y = 35; - path.removeEventListener('DOMAttrModified', check_old_value, false); + eventChecker.watchAttr(path, "d"); + + // -- Actual changes + eventChecker.expect("modify modify modify"); + list[0].x = 10; + list[0].y = 5; + path.setAttribute("d", "M20,5 L12,34"); + + // -- Redundant changes + eventChecker.expect(""); + list[0].x = 20; + list[1].y = 34; + path.setAttribute("d", "M20,5 L12,34"); + eventChecker.finish(); SimpleTest.finish(); } diff --git a/content/svg/content/test/test_SVGPointList.xhtml b/content/svg/content/test/test_SVGPointList.xhtml new file mode 100644 index 00000000000..fbeaacf77a5 --- /dev/null +++ b/content/svg/content/test/test_SVGPointList.xhtml @@ -0,0 +1,64 @@ + + + + Tests specific to SVGPointList + + + + + +Mozilla Bug 629200 +

+ +
+
+
+ + diff --git a/content/svg/content/test/test_SVGTransformList.xhtml b/content/svg/content/test/test_SVGTransformList.xhtml index 00793cc2169..453f1efefb1 100644 --- a/content/svg/content/test/test_SVGTransformList.xhtml +++ b/content/svg/content/test/test_SVGTransformList.xhtml @@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=602759 + @@ -41,7 +42,8 @@ function main() testCreateSVGTransformFromMatrix, testReadOnly, testOrphan, - testFailedSet + testFailedSet, + testMutationEvents ]; for (var i = 0; i < tests.length; i++) { tests[i](g); @@ -355,6 +357,76 @@ function testFailedSet(g) "Animated transform list should also be empty after setting bad value"); } +function testMutationEvents(g) +{ + // Check mutation events + + // Set initial value + g.setAttribute("transform", "translate(50 90)"); + var list = g.transform.baseVal; + is(list.numberOfItems, 1, "Unexpected initial length of list"); + eventChecker = new MutationEventChecker; + eventChecker.watchAttr(g, "transform"); + + // consolidate + // + // Consolidate happens to generate two modification events in our + // implementation--it's not ideal but it's better than none + eventChecker.expect("modify modify modify"); + g.setAttribute("transform", "translate(10 10) translate(10 10)"); + list.consolidate(); + + // In the following, each of the operations is performed twice but only one + // mutation event is expected. This is to check that redundant mutation + // events are not sent. + + // transform.setMatrix + eventChecker.expect("modify"); + var mx = $('svg').createSVGMatrix(); + list[0].setMatrix(mx); + list[0].setMatrix(mx); + + // transform.setTranslate + eventChecker.expect("modify"); + list[0].setTranslate(10, 10); + list[0].setTranslate(10, 10); + + // transform.setScale + eventChecker.expect("modify"); + list[0].setScale(2, 2); + list[0].setScale(2, 2); + + // transform.setRotate + eventChecker.expect("modify"); + list[0].setRotate(45, 1, 2); + list[0].setRotate(45, 1, 2); + + // transform.setSkewX + eventChecker.expect("modify"); + list[0].setSkewX(45); + list[0].setSkewX(45); + + // transform.setSkewY + eventChecker.expect("modify"); + list[0].setSkewY(25); + list[0].setSkewY(25); + + // transform.matrix + eventChecker.expect("modify modify"); + list[0].matrix.a = 1; + list[0].matrix.a = 1; + list[0].matrix.e = 5; + list[0].matrix.e = 5; + + // setAttribute interaction + eventChecker.expect("modify"); + list[0].setMatrix(mx); + eventChecker.expect(""); + g.setAttribute("transform", "matrix(1, 0, 0, 1, 0, 0)"); + list[0].setMatrix(mx); + eventChecker.finish(); +} + window.addEventListener("load", main, false); ]]> diff --git a/content/svg/content/test/test_SVGxxxList.xhtml b/content/svg/content/test/test_SVGxxxList.xhtml index e13171b968b..08286d487ff 100644 --- a/content/svg/content/test/test_SVGxxxList.xhtml +++ b/content/svg/content/test/test_SVGxxxList.xhtml @@ -6,6 +6,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=515116 Generic tests for SVG animated length lists + @@ -452,6 +453,7 @@ function get_array_of_list_items(list) function run_baseVal_API_tests() { var res, threw, items; + var eventChecker = new MutationEventChecker; for each (var t in tests) { @@ -462,6 +464,8 @@ function run_baseVal_API_tests() is(t.baseVal.numberOfItems, 4, 'The '+t.list_type+' object should contain four list items.'); + eventChecker.watchAttr(t.element, t.attr_name); + eventChecker.expect("modify"); res = t.baseVal.clear(); is(t.baseVal.numberOfItems, 0, @@ -469,13 +473,48 @@ function run_baseVal_API_tests() ' object.'); is(res, undefined, 'The method '+t.list_type+'.clear() should not return a value.'); + ok(t.element.hasAttribute(t.attr_name), + 'The method '+t.list_type+'.clear() should not remove the attribute.'); + ok(t.element.getAttribute(t.attr_name) === "", + 'Cleared '+t.attr_name+' ('+t.list_type+') but did not get an '+ + 'empty string back.'); + + eventChecker.expect(""); + t.baseVal.clear(); + eventChecker.ignoreEvents(); + + // Test empty strings + + t.element.setAttribute(t.attr_name, ""); + ok(t.element.getAttribute(t.attr_name) === "", + 'Set an empty attribute value for '+t.attr_name+' ('+t.list_type+ + ') but did not get an empty string back.'); + + // Test removed attributes + + t.element.removeAttribute(t.attr_name); + ok(t.element.getAttribute(t.attr_name) === null, + 'Removed attribute value for '+t.attr_name+' ('+t.list_type+ + ') but did not get null back.'); + ok(!t.element.hasAttribute(t.attr_name), + 'Removed attribute value for '+t.attr_name+' ('+t.list_type+ + ') but hasAttribute still returns true.'); // Test .initialize(): t.element.setAttribute(t.attr_name, t.attr_val_4); var item = t.item_constructor(); + // Our current implementation of 'initialize' for most list types performs + // a 'clear' followed by an 'insertItemBefore'. This results in two + // modification events being dispatched. SVGStringList however avoids the + // additional clear. + var expectedModEvents = + t.item_type == "DOMString" ? "modify" : "modify modify"; + eventChecker.expect(expectedModEvents); var res = t.baseVal.initialize(item); + eventChecker.ignoreEvents(); + is(t.baseVal.numberOfItems, 1, 'The '+t.list_type+' object should contain one list item.'); @@ -515,6 +554,7 @@ function run_baseVal_API_tests() 'is passed in, even if that object is the only item in that list.'); // [SVGWG issue] not what the spec currently says + eventChecker.expect(""); threw = false; try { t.baseVal.initialize({}); @@ -524,6 +564,7 @@ function run_baseVal_API_tests() ok(threw, 'The method '+t.list_type+'.initialize() should throw if passed an '+ 'object of the wrong type.'); + eventChecker.ignoreEvents(); } // Test .insertItemBefore(): @@ -532,7 +573,9 @@ function run_baseVal_API_tests() old_items = get_array_of_list_items(t.baseVal); item = t.item_constructor(); + eventChecker.expect("modify"); res = t.baseVal.insertItemBefore(item, 2); + eventChecker.ignoreEvents(); is(t.baseVal.numberOfItems, 5, 'The '+t.list_type+' object should contain five list items.'); @@ -587,6 +630,7 @@ function run_baseVal_API_tests() 'the list at the index specified.'); // [SVGWG issue] not what the spec currently says + eventChecker.expect(""); threw = false; try { t.baseVal.insertItemBefore({}, 2); @@ -596,6 +640,7 @@ function run_baseVal_API_tests() ok(threw, 'The method '+t.list_type+'.insertItemBefore() should throw if passed '+ 'an object of the wrong type.'); + eventChecker.ignoreEvents(); } // Test .replaceItem(): @@ -604,7 +649,9 @@ function run_baseVal_API_tests() old_items = get_array_of_list_items(t.baseVal); item = t.item_constructor(); + eventChecker.expect("modify"); res = t.baseVal.replaceItem(item, 2); + eventChecker.ignoreEvents(); is(t.baseVal.numberOfItems, 4, 'The '+t.list_type+' object should contain four list items.'); @@ -627,6 +674,7 @@ function run_baseVal_API_tests() item = t.item_constructor(); + eventChecker.expect(""); threw = false; try { t.baseVal.replaceItem(item, 100); @@ -636,6 +684,7 @@ function run_baseVal_API_tests() ok(threw, 'The method '+t.list_type+'.replaceItem() should throw if passed '+ 'an index that is out of bounds.'); + eventChecker.ignoreEvents(); old_items = get_array_of_list_items(t.baseVal); item = t.baseVal.getItem(3); @@ -683,7 +732,9 @@ function run_baseVal_API_tests() old_items = get_array_of_list_items(t.baseVal); item = t.baseVal.getItem(2); + eventChecker.expect("modify"); res = t.baseVal.removeItem(2); + eventChecker.ignoreEvents(); is(t.baseVal.numberOfItems, 3, 'The '+t.list_type+' object should contain three list items.'); @@ -701,6 +752,7 @@ function run_baseVal_API_tests() 'the item at index 2 was removed using the '+t.list_type+ '.replaceItem() method.'); + eventChecker.expect(""); threw = false; try { t.baseVal.removeItem(100); @@ -710,6 +762,7 @@ function run_baseVal_API_tests() ok(threw, 'The method '+t.list_type+'.removeItem() should throw if passed '+ 'an index that is out of bounds.'); + eventChecker.ignoreEvents(); // Test .appendItem(): @@ -717,7 +770,9 @@ function run_baseVal_API_tests() old_items = get_array_of_list_items(t.baseVal); item = t.item_constructor(); + eventChecker.expect("modify"); res = t.baseVal.appendItem(item); + eventChecker.ignoreEvents(); is(t.baseVal.numberOfItems, 5, 'The '+t.list_type+' object should contain five list items.'); @@ -763,6 +818,7 @@ function run_baseVal_API_tests() 'that list.'); // [SVGWG issue] not what the spec currently says + eventChecker.expect(""); threw = false; try { t.baseVal.appendItem({}); @@ -772,7 +828,15 @@ function run_baseVal_API_tests() ok(threw, 'The method '+t.list_type+'.appendItem() should throw if passed '+ 'an object of the wrong type.'); + eventChecker.ignoreEvents(); } + + // Test removal and addition events + + eventChecker.expect("remove add"); + t.element.removeAttribute(t.attr_name); + res = t.baseVal.appendItem(item); + eventChecker.finish(); } } @@ -880,7 +944,7 @@ function run_animVal_API_tests() /** * This function runs some basic tests to check the effect of setAttribute() - * calls on object identidy, without taking SMIL animation into consideration. + * calls on object identity, without taking SMIL animation into consideration. */ function run_basic_setAttribute_tests() { 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_dataTypes.html b/content/svg/content/test/test_dataTypes.html index 75930af3020..a6035cfdd71 100644 --- a/content/svg/content/test/test_dataTypes.html +++ b/content/svg/content/test/test_dataTypes.html @@ -38,8 +38,11 @@ function runTests() is(filter.className.animVal, "bar", "className animVal"); filter.removeAttribute("class"); is(filter.hasAttribute("class"), false, "class attribute"); + ok(filter.getAttribute("class") === null, "removed class attribute"); is(filter.className.baseVal, "", "className baseVal"); is(filter.className.animVal, "", "className animVal"); + filter.setAttribute("class", ""); + ok(filter.getAttribute("class") === "", "empty class attribute"); // length attribute @@ -57,6 +60,11 @@ function runTests() is(marker.markerWidth.animVal.value, 7.5, "length animVal"); is(marker.getAttribute("markerWidth"), "7.5", "length attribute"); + marker.setAttribute("markerWidth", ""); + ok(marker.getAttribute("markerWidth") === "", "empty length attribute"); + marker.removeAttribute("markerWidth"); + ok(marker.getAttribute("markerWidth") === null, "removed length attribute"); + // number attribute convolve.setAttribute("divisor", "12.5"); @@ -67,6 +75,11 @@ function runTests() is(convolve.divisor.animVal, 7.5, "number animVal"); is(convolve.getAttribute("divisor"), "7.5", "number attribute"); + convolve.setAttribute("divisor", ""); + ok(convolve.getAttribute("divisor") === "", "empty number attribute"); + convolve.removeAttribute("divisor"); + ok(convolve.getAttribute("divisor") === null, "removed number attribute"); + // number-optional-number attribute blur.setAttribute("stdDeviation", "20.5"); @@ -89,6 +102,13 @@ function runTests() is(blur.stdDeviationY.baseVal, 0.5, "integer-optional-integer second baseVal"); is(blur.stdDeviationY.animVal, 0.5, "integer-optional-integer second animVal"); + blur.setAttribute("stdDeviation", ""); + ok(blur.getAttribute("stdDeviation") === "", + "empty number-optional-number attribute"); + blur.removeAttribute("stdDeviation"); + ok(blur.getAttribute("stdDeviation") === null, + "removed number-optional-number attribute"); + // integer attribute convolve.setAttribute("targetX", "12"); @@ -97,6 +117,10 @@ function runTests() convolve.targetX.baseVal = 7; is(convolve.targetX.animVal, 7, "integer animVal"); is(convolve.getAttribute("targetX"), "7", "integer attribute"); + convolve.setAttribute("targetX", ""); + ok(convolve.getAttribute("targetX") === "", "empty integer attribute"); + convolve.removeAttribute("targetX"); + ok(convolve.getAttribute("targetX") === null, "removed integer attribute"); // integer-optional-integer attribute @@ -120,6 +144,13 @@ function runTests() is(filter.filterResY.baseVal, 90, "integer-optional-integer second baseVal"); is(filter.filterResY.animVal, 90, "integer-optional-integer second animVal"); + filter.setAttribute("filterRes", ""); + ok(filter.getAttribute("filterRes") === "", + "empty integer-optional-integer attribute"); + filter.removeAttribute("filterRes"); + ok(filter.getAttribute("filterRes") === null, + "removed integer-optional-integer attribute"); + // angle attribute marker.setAttribute("orient", "90deg"); @@ -136,6 +167,11 @@ function runTests() is(marker.orientAngle.animVal.value, 30, "angle animVal"); is(marker.getAttribute("orient"), "30deg", "angle attribute"); + marker.setAttribute("orient", ""); + ok(marker.getAttribute("orient") === "", "empty angle attribute"); + marker.removeAttribute("orient"); + ok(marker.getAttribute("orient") === null, "removed angle attribute"); + // boolean attribute convolve.setAttribute("preserveAlpha", "false"); @@ -144,6 +180,11 @@ function runTests() convolve.preserveAlpha.baseVal = true; is(convolve.preserveAlpha.animVal, true, "boolean animVal"); is(convolve.getAttribute("preserveAlpha"), "true", "boolean attribute"); + convolve.setAttribute("preserveAlpha", ""); + ok(convolve.getAttribute("preserveAlpha") === "", "empty boolean attribute"); + convolve.removeAttribute("preserveAlpha"); + ok(convolve.getAttribute("preserveAlpha") === null, + "removed boolean attribute"); // enum attribute @@ -153,6 +194,10 @@ function runTests() convolve.edgeMode.baseVal = 1; is(convolve.edgeMode.animVal, 1, "enum animVal"); is(convolve.getAttribute("edgeMode"), "duplicate", "enum attribute"); + convolve.setAttribute("edgeMode", ""); + ok(convolve.getAttribute("edgeMode") === "", "empty enum attribute"); + convolve.removeAttribute("edgeMode"); + ok(convolve.getAttribute("edgeMode") === null, "removed enum attribute"); // string attribute @@ -162,6 +207,10 @@ function runTests() convolve.result.baseVal = "bar"; is(convolve.result.animVal, "bar", "string animVal"); is(convolve.getAttribute("result"), "bar", "string attribute"); + convolve.setAttribute("result", ""); + ok(convolve.getAttribute("result") === "", "empty string attribute"); + convolve.removeAttribute("result"); + ok(convolve.getAttribute("result") === null, "removed string attribute"); // preserveAspectRatio attribute @@ -186,6 +235,13 @@ function runTests() is(basePreserveAspectRatio.meetOrSlice, 2, "preserveAspectRatio.meetOrSlice baseVal"); is(animPreserveAspectRatio.meetOrSlice, 2, "preserveAspectRatio.meetOrSlice animVal"); + marker.setAttribute("preserveAspectRatio", ""); + ok(marker.getAttribute("preserveAspectRatio") === "", + "empty preserveAspectRatio attribute"); + marker.removeAttribute("preserveAspectRatio"); + ok(marker.getAttribute("preserveAspectRatio") === null, + "removed preserveAspectRatio attribute"); + // viewBox attribute var baseViewBox = marker.viewBox.baseVal; var animViewBox = marker.viewBox.animVal; @@ -223,6 +279,10 @@ function runTests() is(marker.getAttribute("viewBox"), "5 6 7 8", "viewBox attribute"); marker.removeAttribute("viewBox"); is(marker.hasAttribute("viewBox"), false, "viewBox hasAttribute"); + ok(marker.getAttribute("viewBox") === null, "removed viewBox attribute"); + + marker.setAttribute("viewBox", ""); + ok(marker.getAttribute("viewBox") === "", "empty viewBox attribute"); SimpleTest.finish(); } diff --git a/content/svg/content/test/test_dataTypesModEvents.html b/content/svg/content/test/test_dataTypesModEvents.html new file mode 100644 index 00000000000..e45c6415a61 --- /dev/null +++ b/content/svg/content/test/test_dataTypesModEvents.html @@ -0,0 +1,245 @@ + + + + + Test for Bug 629200 + + + + + +Mozilla + Bug 629200 +

+ + + + +
+
+
+ + 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 7faa360321a..6fd6c8cf816 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -4121,7 +4121,7 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI, 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 && aFailedChannel) { + if (mUseErrorPages && aURI) { // Display an error page LoadErrorPage(aURI, aURL, errorPage.get(), error.get(), messageStr.get(), cssClass.get(), aFailedChannel); 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/android/ANPNativeWindow.cpp b/dom/plugins/base/android/ANPNativeWindow.cpp index 4359688a859..b693284f3d5 100644 --- a/dom/plugins/base/android/ANPNativeWindow.cpp +++ b/dom/plugins/base/android/ANPNativeWindow.cpp @@ -52,14 +52,16 @@ using namespace mozilla; #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) #define ASSIGN(obj, name) (obj)->name = anp_native_window_##name -static ANPNativeWindow anp_native_window_acquireNativeWindow(NPP instance) { +static nsresult GetOwner(NPP instance, nsPluginInstanceOwner** owner) { nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); - nsPluginInstanceOwner* owner; - if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) { - return NULL; - } + return pinst->GetOwner((nsIPluginInstanceOwner**)owner); +} +static ANPNativeWindow anp_native_window_acquireNativeWindow(NPP instance) { + nsRefPtr owner; + if (NS_FAILED(GetOwner(instance, getter_AddRefs(owner)))) + return NULL; ANPNativeWindow window = owner->Layer()->GetNativeWindowForContent(); owner->Invalidate(); @@ -68,12 +70,9 @@ static ANPNativeWindow anp_native_window_acquireNativeWindow(NPP instance) { } static void anp_native_window_invertPluginContent(NPP instance, bool isContentInverted) { - nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); - - nsPluginInstanceOwner* owner; - if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) { + nsRefPtr owner; + if (NS_FAILED(GetOwner(instance, getter_AddRefs(owner)))) return; - } owner->Layer()->SetInverted(isContentInverted); } diff --git a/dom/plugins/base/android/ANPVideo.cpp b/dom/plugins/base/android/ANPVideo.cpp index 6b75425a046..9f49c5f1909 100644 --- a/dom/plugins/base/android/ANPVideo.cpp +++ b/dom/plugins/base/android/ANPVideo.cpp @@ -35,22 +35,23 @@ using namespace mozilla; -static AndroidMediaLayer* GetLayerForInstance(NPP instance) { +static nsresult GetOwner(NPP instance, nsPluginInstanceOwner** owner) { nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); - nsPluginInstanceOwner* owner; - if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) { - return NULL; - } + return pinst->GetOwner((nsIPluginInstanceOwner**)owner); +} +static AndroidMediaLayer* GetLayerForInstance(NPP instance) { + nsRefPtr owner; + if (NS_FAILED(GetOwner(instance, getter_AddRefs(owner)))) + return NULL; + return owner->Layer(); } static void Invalidate(NPP instance) { - nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); - - nsPluginInstanceOwner* owner; - if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) + nsRefPtr owner; + if (NS_FAILED(GetOwner(instance, getter_AddRefs(owner)))) return; owner->Invalidate(); diff --git a/dom/plugins/base/android/ANPWindow.cpp b/dom/plugins/base/android/ANPWindow.cpp index 75b643df386..ca9c202a33d 100644 --- a/dom/plugins/base/android/ANPWindow.cpp +++ b/dom/plugins/base/android/ANPWindow.cpp @@ -85,6 +85,12 @@ anp_window_requestCenterFitZoom(NPP instance) NOT_IMPLEMENTED(); } +static nsresult GetOwner(NPP instance, nsPluginInstanceOwner** owner) { + nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); + + return pinst->GetOwner((nsIPluginInstanceOwner**)owner); +} + ANPRectI anp_window_visibleRect(NPP instance) { @@ -92,8 +98,8 @@ anp_window_visibleRect(NPP instance) nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); - nsPluginInstanceOwner* owner; - if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) { + nsRefPtr owner; + if (NS_FAILED(GetOwner(instance, getter_AddRefs(owner)))) { return rect; } diff --git a/dom/plugins/base/nsNPAPIPluginInstance.cpp b/dom/plugins/base/nsNPAPIPluginInstance.cpp index 0f90ad6ded6..e77d98cca14 100644 --- a/dom/plugins/base/nsNPAPIPluginInstance.cpp +++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp @@ -90,6 +90,7 @@ nsNPAPIPluginInstance::nsNPAPIPluginInstance(nsNPAPIPlugin* plugin) #ifdef MOZ_WIDGET_ANDROID mSurface(nsnull), mANPDrawingModel(0), + mOnScreen(true), #endif mRunning(NOT_STARTED), mWindowless(false), @@ -724,6 +725,44 @@ void nsNPAPIPluginInstance::SetEventModel(NPEventModel aModel) #endif #if defined(MOZ_WIDGET_ANDROID) + +static void SendLifecycleEvent(nsNPAPIPluginInstance* aInstance, PRUint32 aAction) +{ + ANPEvent event; + event.inSize = sizeof(ANPEvent); + event.eventType = kLifecycle_ANPEventType; + event.data.lifecycle.action = aAction; + aInstance->HandleEvent(&event, nsnull); +} + +void nsNPAPIPluginInstance::NotifyForeground(bool aForeground) +{ + PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::SetForeground this=%p\n foreground=%d",this, aForeground)); + if (RUNNING != mRunning) + return; + + SendLifecycleEvent(this, aForeground ? kResume_ANPLifecycleAction : kPause_ANPLifecycleAction); +} + +void nsNPAPIPluginInstance::NotifyOnScreen(bool aOnScreen) +{ + PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::SetOnScreen this=%p\n onScreen=%d",this, aOnScreen)); + if (RUNNING != mRunning || mOnScreen == aOnScreen) + return; + + mOnScreen = aOnScreen; + SendLifecycleEvent(this, aOnScreen ? kOnScreen_ANPLifecycleAction : kOffScreen_ANPLifecycleAction); +} + +void nsNPAPIPluginInstance::MemoryPressure() +{ + PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::MemoryPressure this=%p\n",this)); + if (RUNNING != mRunning) + return; + + SendLifecycleEvent(this, kFreeMemory_ANPLifecycleAction); +} + void nsNPAPIPluginInstance::SetANPDrawingModel(PRUint32 aModel) { mANPDrawingModel = aModel; diff --git a/dom/plugins/base/nsNPAPIPluginInstance.h b/dom/plugins/base/nsNPAPIPluginInstance.h index ca13f71accf..38094948a7a 100644 --- a/dom/plugins/base/nsNPAPIPluginInstance.h +++ b/dom/plugins/base/nsNPAPIPluginInstance.h @@ -147,6 +147,14 @@ public: #endif #ifdef MOZ_WIDGET_ANDROID + void NotifyForeground(bool aForeground); + void NotifyOnScreen(bool aOnScreen); + void MemoryPressure(); + + bool IsOnScreen() { + return mOnScreen; + } + PRUint32 GetANPDrawingModel() { return mANPDrawingModel; } void SetANPDrawingModel(PRUint32 aModel); @@ -282,6 +290,7 @@ private: bool mUsePluginLayersPref; #ifdef MOZ_WIDGET_ANDROID void* mSurface; + bool mOnScreen; #endif }; diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp index 94d2bee76f1..bf0caf76302 100644 --- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -375,6 +375,10 @@ nsPluginHost::nsPluginHost() if (obsService) { obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); obsService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, false); +#ifdef MOZ_WIDGET_ANDROID + obsService->AddObserver(this, "application-foreground", false); + obsService->AddObserver(this, "application-background", false); +#endif } #ifdef PLUGIN_LOGGING @@ -3111,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 @@ -3381,6 +3390,24 @@ NS_IMETHODIMP nsPluginHost::Observe(nsISupports *aSubject, mInstances[i]->PrivateModeStateChanged(); } } +#ifdef MOZ_WIDGET_ANDROID + if (!nsCRT::strcmp("application-background", aTopic)) { + for(PRUint32 i = 0; i < mInstances.Length(); i++) { + mInstances[i]->NotifyForeground(false); + } + } + if (!nsCRT::strcmp("application-foreground", aTopic)) { + for(PRUint32 i = 0; i < mInstances.Length(); i++) { + if (mInstances[i]->IsOnScreen()) + mInstances[i]->NotifyForeground(true); + } + } + if (!nsCRT::strcmp("memory-pressure", aTopic)) { + for(PRUint32 i = 0; i < mInstances.Length(); i++) { + mInstances[i]->MemoryPressure(); + } + } +#endif return NS_OK; } diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp index 05e39f1c8d9..ce741634823 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -339,9 +339,8 @@ nsPluginInstanceOwner::nsPluginInstanceOwner() mWaitingForPaint = false; #ifdef MOZ_WIDGET_ANDROID - mOnScreen = false; mInverted = false; - mLayer = new AndroidMediaLayer(); + mLayer = nsnull; #endif } @@ -393,10 +392,7 @@ nsPluginInstanceOwner::~nsPluginInstanceOwner() mPluginWindow = nsnull; #ifdef MOZ_WIDGET_ANDROID - if (mLayer) { - delete mLayer; - mLayer = nsnull; - } + RemovePluginView(); #endif if (mInstance) { @@ -1394,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) { @@ -1402,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(); @@ -1420,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) { @@ -1438,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) { @@ -1688,22 +1683,6 @@ void nsPluginInstanceOwner::SendSize(int width, int height) mInstance->HandleEvent(&event, nsnull); } -void nsPluginInstanceOwner::SendOnScreenEvent(bool onScreen) -{ - if (!mInstance) - return; - - if ((onScreen && !mOnScreen) || (!onScreen && mOnScreen)) { - ANPEvent event; - event.inSize = sizeof(ANPEvent); - event.eventType = kLifecycle_ANPEventType; - event.data.lifecycle.action = onScreen ? kOnScreen_ANPLifecycleAction : kOffScreen_ANPLifecycleAction; - mInstance->HandleEvent(&event, nsnull); - - mOnScreen = onScreen; - } -} - bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect) { void* javaSurface = mInstance->GetJavaSurface(); @@ -1754,14 +1733,12 @@ bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect) aRect.height); #endif - SendOnScreenEvent(true); - return true; } void nsPluginInstanceOwner::RemovePluginView() { - if (!mInstance || !mObjectFrame | !mOnScreen) + if (!mInstance || !mObjectFrame) return; void* surface = mInstance->GetJavaSurface(); @@ -1779,7 +1756,6 @@ void nsPluginInstanceOwner::RemovePluginView() "removePluginView", "(Landroid/view/View;)V"); env->CallStaticVoidMethod(cls, method, surface); - SendOnScreenEvent(false); } void nsPluginInstanceOwner::Invalidate() { @@ -2722,7 +2698,7 @@ nsPluginInstanceOwner::Destroy() CancelTimer(); #endif #ifdef XP_MACOSX - RemoveFromCARefreshTimer(this); + RemoveFromCARefreshTimer(); if (mColorProfile) ::CGColorSpaceRelease(mColorProfile); #endif @@ -2759,6 +2735,14 @@ nsPluginInstanceOwner::Destroy() mContent->RemoveEventListener(NS_LITERAL_STRING("text"), this, true); #endif +#if MOZ_WIDGET_ANDROID + RemovePluginView(); + + if (mLayer) + mLayer->SetVisible(false); + +#endif + if (mWidget) { if (mPluginWindow) { mPluginWindow->SetPluginWidget(nsnull); @@ -2867,7 +2851,7 @@ void nsPluginInstanceOwner::Paint(gfxContext* aContext, const gfxRect& aFrameRect, const gfxRect& aDirtyRect) { - if (!mInstance || !mObjectFrame) + if (!mInstance || !mObjectFrame || !mPluginDocumentActiveState) return; PRInt32 model = mInstance->GetANPDrawingModel(); @@ -2880,11 +2864,13 @@ void nsPluginInstanceOwner::Paint(gfxContext* aContext, } if (model == kOpenGL_ANPDrawingModel) { + if (!mLayer) + mLayer = new AndroidMediaLayer(); + // FIXME: this is gross float zoomLevel = aFrameRect.width / (float)mPluginWindow->width; mLayer->UpdatePosition(aFrameRect, zoomLevel); - SendOnScreenEvent(true); SendSize((int)aFrameRect.width, (int)aFrameRect.height); return; } @@ -3290,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) { @@ -3331,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) { @@ -3587,17 +3588,6 @@ void nsPluginInstanceOwner::UpdateWindowPositionAndClipRect(bool aSetWindow) } else { mPluginWindow->clipRect.right = 0; mPluginWindow->clipRect.bottom = 0; -#if 0 //MOZ_WIDGET_ANDROID - if (mInstance) { - PRInt32 model = mInstance->GetANPDrawingModel(); - - if (model == kSurface_ANPDrawingModel) { - RemovePluginView(); - } else if (model == kOpenGL_ANPDrawingModel) { - HidePluginLayer(); - } - } -#endif } if (!aSetWindow) @@ -3625,6 +3615,26 @@ nsPluginInstanceOwner::UpdateDocumentActiveState(bool aIsActive) { mPluginDocumentActiveState = aIsActive; UpdateWindowPositionAndClipRect(true); + +#ifdef MOZ_WIDGET_ANDROID + if (mInstance) { + if (mLayer) + mLayer->SetVisible(mPluginDocumentActiveState); + + if (!mPluginDocumentActiveState) + RemovePluginView(); + + mInstance->NotifyOnScreen(mPluginDocumentActiveState); + + // This is, perhaps, incorrect. It is supposed to be sent + // when "the webview has paused or resumed". The side effect + // is that Flash video players pause or resume (if they were + // playing before) based on the value here. I personally think + // we want that on Android when switching to another tab, so + // that's why we call it here. + mInstance->NotifyForeground(mPluginDocumentActiveState); + } +#endif } #endif // XP_MACOSX diff --git a/dom/plugins/base/nsPluginInstanceOwner.h b/dom/plugins/base/nsPluginInstanceOwner.h index b328650548b..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(); @@ -328,16 +327,14 @@ private: void FixUpURLS(const nsString &name, nsAString &value); #ifdef MOZ_WIDGET_ANDROID void SendSize(int width, int height); - void SendOnScreenEvent(bool onScreen); bool AddPluginView(const gfxRect& aRect); void RemovePluginView(); - bool mOnScreen; bool mInverted; // For kOpenGL_ANPDrawingModel - mozilla::AndroidMediaLayer *mLayer; + nsRefPtr mLayer; #endif nsPluginNativeWindow *mPluginWindow; 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/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/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 fe9c39462fc..f982ce6e52e 100644 --- a/gfx/gl/GLContext.cpp +++ b/gfx/gl/GLContext.cpp @@ -1452,6 +1452,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 a7b1f1f9052..84f8bb40bb7 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), @@ -862,23 +864,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() { @@ -886,32 +932,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) { @@ -954,8 +1006,6 @@ public: } private: - GLuint mPrevDrawFBOBinding; - GLuint mPrevReadFBOBinding; bool mOffscreenFBOsDirty; void GetShaderPrecisionFormatNonES2(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) { @@ -980,40 +1030,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; @@ -1027,7 +1066,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; @@ -1037,7 +1076,7 @@ private: LOCAL_GL_NEAREST); BindDrawFBO(prevDraw); - BindReadFBO(mOffscreenReadFBO); + BindReadFBO(prevRead); if (scissor) fEnable(LOCAL_GL_SCISSOR_TEST); @@ -1045,10 +1084,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: @@ -1997,12 +2035,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/gl/GLContextProviderEGL.cpp b/gfx/gl/GLContextProviderEGL.cpp index a428af51719..2b4ad969e8f 100644 --- a/gfx/gl/GLContextProviderEGL.cpp +++ b/gfx/gl/GLContextProviderEGL.cpp @@ -1133,10 +1133,18 @@ public: // still expensive. #ifndef MOZ_WIDGET_QT if (!mSurface) { - EGLConfig config; - CreateConfig(&config); - mSurface = CreateSurfaceForWindow(NULL, config); - aForce = true; + // We need to be able to bind the surface when we don't + // have access to a surface. We wont be drawing to the screen + // but we will be able to do things like resource releases. + succeeded = sEGLLibrary.fMakeCurrent(EGL_DISPLAY(), + EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT); + if (!succeeded && sEGLLibrary.fGetError() == LOCAL_EGL_CONTEXT_LOST) { + mContextLost = true; + NS_WARNING("EGL context has been lost."); + } + NS_ASSERTION(succeeded, "Failed to make GL context current!"); + return succeeded; } #endif if (aForce || sEGLLibrary.fGetCurrentContext() != mContext) { 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/layers/basic/BasicImages.cpp b/gfx/layers/basic/BasicImages.cpp index 5fd5942c9c0..90806cd38e1 100644 --- a/gfx/layers/basic/BasicImages.cpp +++ b/gfx/layers/basic/BasicImages.cpp @@ -62,8 +62,9 @@ public: BasicPlanarYCbCrImage(const gfxIntSize& aScaleHint, gfxImageFormat aOffscreenFormat, BufferRecycleBin *aRecycleBin) : PlanarYCbCrImage(aRecycleBin) , mScaleHint(aScaleHint) - , mOffscreenFormat(aOffscreenFormat) - {} + { + SetOffscreenFormat(aOffscreenFormat); + } ~BasicPlanarYCbCrImage() { @@ -79,7 +80,6 @@ public: private: gfxIntSize mScaleHint; - gfxImageFormat mOffscreenFormat; int mStride; nsAutoArrayPtr mDecodedBuffer; }; 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 c2abe7a2235..a36e4984b14 100644 --- a/hal/gonk/GonkHal.cpp +++ b/hal/gonk/GonkHal.cpp @@ -256,6 +256,10 @@ DisableBatteryNotifications() void GetCurrentBatteryInformation(hal::BatteryInformation *aBatteryInfo) { + static const int BATTERY_NOT_CHARGING = 0; + static const int BATTERY_CHARGING_USB = 1; + static const int BATTERY_CHARGING_AC = 2; + FILE *capacityFile = fopen("/sys/class/power_supply/battery/capacity", "r"); double capacity = dom::battery::kDefaultLevel * 100; if (capacityFile) { @@ -264,14 +268,42 @@ 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 + if (chargingSrc != BATTERY_NOT_CHARGING && + chargingSrc != BATTERY_CHARGING_USB && + chargingSrc != BATTERY_CHARGING_AC) { + HAL_LOG(("charging_source contained unknown value: %d", chargingSrc)); + } + #endif + aBatteryInfo->level() = capacity / 100; - aBatteryInfo->charging() = chargingSrc == 1; + aBatteryInfo->charging() = (chargingSrc == BATTERY_CHARGING_USB || + chargingSrc == BATTERY_CHARGING_AC); aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime; } 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..bf7f5f3778e 100644 --- a/js/src/builtin/MapObject.cpp +++ b/js/src/builtin/MapObject.cpp @@ -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"); } } } @@ -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/gc/Barrier-inl.h b/js/src/gc/Barrier-inl.h index b7310137494..5fdcad67baf 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 } diff --git a/js/src/gc/Barrier.h b/js/src/gc/Barrier.h index 78d5fa03f56..1f96b29d090 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(); } 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 07db567e83c..8e1bb1769ee 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2642,8 +2642,9 @@ typedef struct JSDumpingTracer { } JSDumpingTracer; static void -DumpNotify(JSTracer *trc, void *thing, JSGCTraceKind kind) +DumpNotify(JSTracer *trc, void **thingp, JSGCTraceKind kind) { + void *thing = *thingp; JSDumpingTracer *dtrc; JSContext *cx; JSDHashEntryStub *entry; @@ -4414,20 +4415,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 *) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 30d9874c69a..7fbbd780683 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1860,7 +1860,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; } @@ -3098,7 +3102,7 @@ JSVAL_TRACE_KIND(jsval v) * wants to use the existing liveness of entries. */ typedef void -(* JSTraceCallback)(JSTracer *trc, void *thing, JSGCTraceKind kind); +(* JSTraceCallback)(JSTracer *trc, void **thingp, JSGCTraceKind kind); struct JSTracer { JSRuntime *runtime; @@ -5314,6 +5318,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/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..66ffc9db423 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -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..c17fd19d8b5 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 { @@ -1121,6 +1132,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 +1571,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..99a17e66177 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -416,8 +416,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 diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index 5ee3aaf47a9..ebde6e30574 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -432,9 +432,8 @@ exn_trace(JSTracer *trc, JSObject *obj) 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 e323ca29cac..93d98bc54ed 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -483,13 +483,14 @@ struct JSDumpHeapTracer : public JSTracer { }; static void -DumpHeapVisitChild(JSTracer *trc, void *thing, JSGCTraceKind kind); +DumpHeapVisitChild(JSTracer *trc, void **thingp, JSGCTraceKind kind); static void -DumpHeapPushIfNew(JSTracer *trc, void *thing, JSGCTraceKind kind) +DumpHeapPushIfNew(JSTracer *trc, void **thingp, JSGCTraceKind kind) { JS_ASSERT(trc->callback == DumpHeapPushIfNew || trc->callback == DumpHeapVisitChild); + void *thing = *thingp; JSDumpHeapTracer *dtrc = static_cast(trc); /* @@ -509,13 +510,13 @@ DumpHeapPushIfNew(JSTracer *trc, void *thing, JSGCTraceKind kind) } static void -DumpHeapVisitChild(JSTracer *trc, void *thing, JSGCTraceKind kind) +DumpHeapVisitChild(JSTracer *trc, void **thingp, JSGCTraceKind kind) { JS_ASSERT(trc->callback == DumpHeapVisitChild); JSDumpHeapTracer *dtrc = static_cast(trc); const char *edgeName = JS_GetTraceEdgeName(dtrc, dtrc->buffer, sizeof(dtrc->buffer)); - fprintf(dtrc->output, "> %p %s\n", (void *)thing, edgeName); - DumpHeapPushIfNew(dtrc, thing, kind); + fprintf(dtrc->output, "> %p %s\n", *thingp, edgeName); + DumpHeapPushIfNew(dtrc, thingp, kind); } void diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 1c57cb9b42a..dbe974513f5 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); /* @@ -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); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 13cc77f5f28..8a5b8835b34 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1815,7 +1815,7 @@ GCMarker::markDelayedChildren() #ifdef DEBUG static void -EmptyMarkCallback(JSTracer *trc, void *thing, JSGCTraceKind kind) +EmptyMarkCallback(JSTracer *trc, void **thingp, JSGCTraceKind kind) { } #endif @@ -1855,7 +1855,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 @@ -1883,7 +1883,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 +1905,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; } @@ -1917,7 +1917,7 @@ AutoGCRooter::trace(JSTracer *trc) PropertyDescriptor &desc = *static_cast(this); if (desc.obj) MarkObjectRoot(trc, desc.obj, "Descriptor::obj"); - MarkValueRoot(trc, desc.value, "Descriptor::value"); + 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) @@ -1996,26 +1996,6 @@ 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) { @@ -2053,7 +2033,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) @@ -3393,7 +3373,7 @@ struct VerifyTracer : JSTracer { * node. */ static void -AccumulateEdge(JSTracer *jstrc, void *thing, JSGCTraceKind kind) +AccumulateEdge(JSTracer *jstrc, void **thingp, JSGCTraceKind kind) { VerifyTracer *trc = (VerifyTracer *)jstrc; @@ -3406,7 +3386,7 @@ AccumulateEdge(JSTracer *jstrc, void *thing, JSGCTraceKind kind) VerifyNode *node = trc->curnode; uint32_t i = node->count; - node->edges[i].thing = thing; + node->edges[i].thing = *thingp; node->edges[i].kind = kind; node->edges[i].label = trc->debugPrinter ? NULL : (char *)trc->debugPrintArg; node->count++; @@ -3547,9 +3527,9 @@ oom: } static void -CheckAutorooter(JSTracer *jstrc, void *thing, JSGCTraceKind kind) +CheckAutorooter(JSTracer *jstrc, void **thingp, JSGCTraceKind kind) { - static_cast(thing)->markIfUnmarked(); + static_cast(*thingp)->markIfUnmarked(); } /* @@ -3560,13 +3540,13 @@ CheckAutorooter(JSTracer *jstrc, void *thing, JSGCTraceKind kind) * modified) must point to marked objects. */ static void -CheckEdge(JSTracer *jstrc, void *thing, JSGCTraceKind kind) +CheckEdge(JSTracer *jstrc, void **thingp, JSGCTraceKind kind) { VerifyTracer *trc = (VerifyTracer *)jstrc; VerifyNode *node = trc->curnode; for (uint32_t i = 0; i < node->count; i++) { - if (node->edges[i].thing == thing) { + if (node->edges[i].thing == *thingp) { JS_ASSERT(node->edges[i].kind == kind); node->edges[i].thing = NULL; return; diff --git a/js/src/jsgcmark.cpp b/js/src/jsgcmark.cpp index 42409b8e6e5..ec8ed69284d 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 { @@ -106,10 +103,13 @@ MarkInternal(JSTracer *trc, T *thing) * GC. */ if (!rt->gcCurrentCompartment || thing->compartment() == rt->gcCurrentCompartment) { - if (IS_GC_MARKING_TRACER(trc)) + if (IS_GC_MARKING_TRACER(trc)) { PushMarkStack(static_cast(trc), thing); - else - trc->callback(trc, (void *)thing, GetGCThingTraceKind(thing)); + } else { + void *tmp = (void *)thing; + trc->callback(trc, &tmp, GetGCThingTraceKind(thing)); + JS_ASSERT(tmp == thing); + } } #ifdef DEBUG @@ -144,7 +144,8 @@ MarkRoot(JSTracer *trc, T *thing, const char *name) 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); @@ -155,7 +156,8 @@ 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) +{ for (size_t i = 0; i < len; ++i) { JS_SET_TRACING_INDEX(trc, name, i); MarkInternal(trc, vec[i]); @@ -296,43 +298,43 @@ MarkIdRootRange(JSTracer *trc, size_t len, jsid *vec, const char *name) /*** 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_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) { for (size_t i = 0; i < len; ++i) { JS_SET_TRACING_INDEX(trc, name, i); - MarkValueInternal(trc, vec[i]); + MarkValueInternal(trc, &vec[i]); } } @@ -357,17 +359,17 @@ MarkShape(JSTracer *trc, const HeapPtr &thing, const char *name) } 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; @@ -640,7 +642,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()); } } } @@ -669,7 +671,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(); @@ -848,6 +853,8 @@ MarkChildren(JSTracer *trc, JSXML *xml) } /* namespace gc */ +using namespace js::gc; + inline void GCMarker::processMarkStackTop() { @@ -913,7 +920,7 @@ GCMarker::processMarkStackTop() 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. */ diff --git a/js/src/jsgcmark.h b/js/src/jsgcmark.h index 57099af8218..d587225e0c7 100644 --- a/js/src/jsgcmark.h +++ b/js/src/jsgcmark.h @@ -97,19 +97,19 @@ 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); } @@ -122,14 +122,14 @@ 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 @@ -153,7 +153,7 @@ MarkCycleCollectorChildren(JSTracer *trc, const Shape *shape); */ inline void -Mark(JSTracer *trc, const js::HeapValue &v, const char *name) +Mark(JSTracer *trc, HeapValue *v, const char *name) { MarkValue(trc, v, name); } @@ -171,7 +171,7 @@ Mark(JSTracer *trc, const HeapPtr &xml, const char *name) } inline bool -IsMarked(const js::Value &v) +IsMarked(const Value &v) { if (v.isMarkable()) return !IsAboutToBeFinalized(v); diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index fee3778edb2..794c480600f 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -3070,7 +3070,18 @@ BEGIN_CASE(JSOP_DEFCONST) BEGIN_CASE(JSOP_DEFVAR) { PropertyName *dn = atoms[GET_INDEX(regs.pc)]->asPropertyName(); - if (!DefVarOrConstOperation(cx, op, dn, regs.fp())) + + /* ES5 10.5 step 8 (with subsequent errata). */ + uintN attrs = JSPROP_ENUMERATE; + if (!regs.fp()->isEvalFrame()) + attrs |= JSPROP_PERMANENT; + if (op == JSOP_DEFCONST) + attrs |= JSPROP_READONLY; + + /* Step 8b. */ + JSObject &obj = regs.fp()->varObj(); + + if (!DefVarOrConstOperation(cx, obj, dn, attrs)) goto error; } END_CASE(JSOP_DEFVAR) diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index 56ddf2abc68..cc5812a3d73 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -439,27 +439,19 @@ NameOperation(JSContext *cx, jsbytecode *pc, Value *vp) } inline bool -DefVarOrConstOperation(JSContext *cx, JSOp op, PropertyName *dn, StackFrame *fp) +DefVarOrConstOperation(JSContext *cx, JSObject &varobj, PropertyName *dn, uintN attrs) { - /* ES5 10.5 step 8 (with subsequent errata). */ - uintN attrs = JSPROP_ENUMERATE; - if (!fp->isEvalFrame()) - attrs |= JSPROP_PERMANENT; - if (op == JSOP_DEFCONST) - attrs |= JSPROP_READONLY; - - /* Step 8b. */ - JSObject &obj = fp->varObj(); - JS_ASSERT(!obj.getOps()->defineProperty); + JS_ASSERT(varobj.isVarObj()); + JS_ASSERT(!varobj.getOps()->defineProperty); JSProperty *prop; JSObject *obj2; - if (!obj.lookupProperty(cx, dn, &obj2, &prop)) + if (!varobj.lookupProperty(cx, dn, &obj2, &prop)) return false; /* Steps 8c, 8d. */ - if (!prop || (obj2 != &obj && obj.isGlobal())) { - if (!DefineNativeProperty(cx, &obj, dn, UndefinedValue(), + if (!prop || (obj2 != &varobj && varobj.isGlobal())) { + if (!DefineNativeProperty(cx, &varobj, dn, UndefinedValue(), JS_PropertyStub, JS_StrictPropertyStub, attrs, 0, 0)) { return false; @@ -470,7 +462,7 @@ DefVarOrConstOperation(JSContext *cx, JSOp op, PropertyName *dn, StackFrame *fp) * see a redeclaration that's |const|, we consider it a conflict. */ uintN oldAttrs; - if (!obj.getPropertyAttributes(cx, dn, &oldAttrs)) + if (!varobj.getPropertyAttributes(cx, dn, &oldAttrs)) return false; if (attrs & JSPROP_READONLY) { JSAutoByteString bytes; 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..636ad42329e 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,8 @@ 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()) + MarkObjectRoot(trc, r.front().key, "sharp table entry"); } #if JS_HAS_TOSOURCE @@ -475,8 +443,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 +459,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 +509,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 +547,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++) { @@ -3525,6 +3495,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 +3570,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 +3593,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 +5862,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 +5885,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..8b0bd037cfb 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -998,22 +998,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,8 +1349,6 @@ 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(); @@ -1554,13 +1536,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..930a807d0b3 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" @@ -436,20 +440,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 { @@ -1916,6 +1906,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 +1915,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 +1924,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 +1961,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; } diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index dcd05a1511b..97eb9b25075 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -384,25 +384,25 @@ js_DisassembleAtPC(JSContext *cx, JSScript *script, JSBool lines, jsbytecode *pc jsbytecode *next, *end; uintN len; - SprintCString(sp, "loc "); + sp->put("loc "); if (lines) - SprintCString(sp, "line"); - SprintCString(sp, " op\n"); - SprintCString(sp, "----- "); + sp->put("line"); + sp->put(" op\n"); + sp->put("----- "); if (lines) - SprintCString(sp, "----"); - SprintCString(sp, " --\n"); + sp->put("----"); + sp->put(" --\n"); next = script->code; end = next + script->length; while (next < end) { if (next == script->main()) - SprintCString(sp, "main:\n"); + sp->put("main:\n"); if (pc != NULL) { if (pc == next) - SprintCString(sp, "--> "); + sp->put("--> "); else - SprintCString(sp, " "); + sp->put(" "); } len = js_Disassemble1(cx, script, next, next - script->code, lines, sp); if (!len) @@ -716,7 +716,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, return 0; } } - SprintCString(sp, "\n"); + sp->put("\n"); return len; } @@ -863,6 +863,12 @@ Sprinter::put(const char *s, size_t len) return oldOffset; } +ptrdiff_t +Sprinter::put(const char *s) +{ + return put(s, strlen(s)); +} + ptrdiff_t Sprinter::putString(JSString *s) { @@ -934,24 +940,6 @@ Sprinter::getOffsetOf(const char *string) const return string - base; } -ptrdiff_t -js::SprintPut(Sprinter *sp, const char *s, size_t len) -{ - return sp->put(s, len); -} - -ptrdiff_t -js::SprintCString(Sprinter *sp, const char *s) -{ - return SprintPut(sp, s, strlen(s)); -} - -ptrdiff_t -js::SprintString(Sprinter *sp, JSString *str) -{ - return sp->putString(str); -} - ptrdiff_t js::Sprint(Sprinter *sp, const char *format, ...) { @@ -966,7 +954,7 @@ js::Sprint(Sprinter *sp, const char *format, ...) JS_ReportOutOfMemory(sp->context); return -1; } - offset = SprintCString(sp, bp); + offset = sp->put(bp); sp->context->free_(bp); return offset; } @@ -1242,7 +1230,7 @@ js_printf(JSPrinter *jp, const char *format, ...) } cc = strlen(bp); - if (SprintPut(&jp->sprinter, bp, (size_t)cc) < 0) + if (jp->sprinter.put(bp, (size_t)cc) < 0) cc = -1; jp->sprinter.context->free_(bp); @@ -1253,7 +1241,7 @@ js_printf(JSPrinter *jp, const char *format, ...) JSBool js_puts(JSPrinter *jp, const char *s) { - return SprintCString(&jp->sprinter, s) >= 0; + return jp->sprinter.put(s) >= 0; } /************************************************************************/ @@ -1342,7 +1330,7 @@ SprintOpcode(SprintStack *ss, const char *str, jsbytecode *pc, } ptrdiff_t offset = ss->sprinter.getOffset(); UpdateDecompiledParent(ss->printer, pc, parentpc, offset - startOffset); - SprintCString(&ss->sprinter, str); + ss->sprinter.put(str); } /* @@ -1425,7 +1413,7 @@ GetOff(SprintStack *ss, uintN i) if (!bytes) return 0; if (bytes != FAILED_EXPRESSION_DECOMPILER) { - off = SprintCString(&ss->sprinter, bytes); + off = ss->sprinter.put(bytes); if (off < 0) off = 0; ss->offsets[i] = off; @@ -1491,7 +1479,7 @@ PushOff(SprintStack *ss, ptrdiff_t off, JSOp op, jsbytecode *pc = NULL) static bool PushStr(SprintStack *ss, const char *str, JSOp op) { - ptrdiff_t off = SprintCString(&ss->sprinter, str); + ptrdiff_t off = ss->sprinter.put(str); if (off < 0) return false; return PushOff(ss, off, op); @@ -1615,16 +1603,15 @@ SprintDoubleValue(Sprinter *sp, jsval v, JSOp *opp) JS_ASSERT(JSVAL_IS_DOUBLE(v)); d = JSVAL_TO_DOUBLE(v); if (JSDOUBLE_IS_NEGZERO(d)) { - todo = SprintCString(sp, "-0"); + todo = sp->put("-0"); *opp = JSOP_NEG; } else if (!JSDOUBLE_IS_FINITE(d)) { - /* Don't use Infinity and NaN, they're mutable. */ - todo = SprintCString(sp, - JSDOUBLE_IS_NaN(d) - ? "0 / 0" - : (d < 0) - ? "1 / -0" - : "1 / 0"); + /* Don't use Infinity and NaN, as local variables may shadow them. */ + todo = sp->put(JSDOUBLE_IS_NaN(d) + ? "0 / 0" + : (d < 0) + ? "1 / -0" + : "1 / 0"); *opp = JSOP_DIV; } else { ToCStringBuf cbuf; @@ -1950,7 +1937,7 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, JS switch (op) { case JSOP_POP: *hole = JS_TRUE; - if (SprintPut(&ss->sprinter, ", ", 2) < 0) + if (ss->sprinter.put(", ", 2) < 0) return NULL; break; @@ -1994,7 +1981,7 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, JS * in-place so pop/concat this pushed string. */ lval = PopStr(ss, JSOP_NOP); - if (SprintCString(&ss->sprinter, lval) < 0) + if (ss->sprinter.put(lval) < 0) return NULL; LOCAL_ASSERT(*pc == JSOP_POP); @@ -2034,7 +2021,7 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, JS return NULL; } else { lval = GetLocal(ss, i); - if (!lval || SprintCString(&ss->sprinter, lval) < 0) + if (!lval || ss->sprinter.put(lval) < 0) return NULL; } if (op != JSOP_SETLOCALPOP) { @@ -2071,10 +2058,10 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, JS ss->sprinter.setOffset(todo); if (*lval == '\0') { /* lval is from JSOP_BINDNAME, so just print xval. */ - todo = SprintCString(&ss->sprinter, xval); + todo = ss->sprinter.put(xval); } else if (*xval == '\0') { /* xval is from JSOP_SETCALL or JSOP_BINDXMLNAME, print lval. */ - todo = SprintCString(&ss->sprinter, lval); + todo = ss->sprinter.put(lval); } else { todo = Sprint(&ss->sprinter, (JOF_OPMODE(ss->opcodes[ss->top+1]) == JOF_XMLNAME) @@ -2117,7 +2104,7 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, * chars so the destructuring decompilation accumulates contiguously in * ss->sprinter starting with "[". */ - ptrdiff_t head = SprintPut(&ss->sprinter, "[", 1); + ptrdiff_t head = ss->sprinter.put("[", 1); if (head < 0 || !PushOff(ss, head, JSOP_NOP)) return NULL; ss->sprinter.setOffset(ss->sprinter.getOffset() - PAREN_SLOP); @@ -2178,7 +2165,7 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, /* Fill in any holes (holes at the end don't matter). */ while (++lasti < i) { - if (SprintPut(&ss->sprinter, ", ", 2) < 0) + if (ss->sprinter.put(", ", 2) < 0) return NULL; } } @@ -2196,7 +2183,7 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, #endif if (!QuoteString(&ss->sprinter, atom, IsIdentifier(atom) ? 0 : (jschar)'\'')) return NULL; - if (SprintPut(&ss->sprinter, ": ", 2) < 0) + if (ss->sprinter.put(": ", 2) < 0) return NULL; break; } @@ -2268,7 +2255,7 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, break; } - if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0) + if (!hole && ss->sprinter.put(", ", 2) < 0) return NULL; pc += JSOP_DUP_LENGTH; @@ -2276,7 +2263,7 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, out: const char *lval = ss->sprinter.stringAt(head); - if (SprintPut(&ss->sprinter, (*lval == '[') ? "]" : "}", 1) < 0) + if (ss->sprinter.put((*lval == '[') ? "]" : "}", 1) < 0) return NULL; return pc; } @@ -2312,12 +2299,12 @@ DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, LOAD_OP_DATA(pc); if (op != JSOP_GETLOCAL) break; - if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0) + if (!hole && ss->sprinter.put(", ", 2) < 0) return NULL; } LOCAL_ASSERT(op == JSOP_POPN); - if (SprintPut(&ss->sprinter, "] = [", 5) < 0) + if (ss->sprinter.put("] = [", 5) < 0) return NULL; end = ss->top - 1; @@ -2331,7 +2318,7 @@ DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, } } - if (SprintPut(&ss->sprinter, "]", 1) < 0) + if (ss->sprinter.put("]", 1) < 0) return NULL; ss->sprinter.setOffset(ss->offsets[i]); ss->top = start; @@ -2867,7 +2854,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) (void)PopStr(ss, op, &lastlvalpc); /* Print only the right operand of the assignment-op. */ - todo = SprintCString(&ss->sprinter, rval); + todo = ss->sprinter.put(rval); } else if (!inXML) { rval = PopStrPrecDupe(ss, cs->prec + !!(cs->format & JOF_LEFTASSOC), &rvalpc); lval = PopStrPrec(ss, cs->prec + !(cs->format & JOF_LEFTASSOC), &lvalpc); @@ -2886,7 +2873,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case 1: rval = PopStrDupe(ss, op, &rvalpc); - todo = SprintCString(&ss->sprinter, token); + todo = ss->sprinter.put(token); SprintOpcode(ss, rval, rvalpc, pc, todo); break; @@ -2894,10 +2881,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) sn = js_GetSrcNote(jp->script, pc); if (sn && SN_TYPE(sn) == SRC_CONTINUE) { /* Hoisted let decl (e.g. 'y' in 'let (x) { let y; }'). */ - todo = SprintCString(&ss->sprinter, SkipString); + todo = ss->sprinter.put(SkipString); break; } - todo = SprintCString(&ss->sprinter, token); + todo = ss->sprinter.put(token); break; default: @@ -3084,7 +3071,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) return NULL; } } - if (SprintPut(&ss->sprinter, "]", 1) < 0) + if (ss->sprinter.put("]", 1) < 0) return NULL; /* @@ -3117,7 +3104,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) * NB: todo at this point indexes space in ss->sprinter * that is liable to be overwritten. The code below knows * exactly how long rval lives, or else copies it down via - * SprintCString. + * Sprinter::put. */ rval = ss->sprinter.stringAt(todo); rvalpc = NULL; @@ -3144,7 +3131,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) */ if (GET_UINT16(pc) == 0) break; - todo = SprintCString(&ss->sprinter, rval); + todo = ss->sprinter.put(rval); saveop = JSOP_NOP; } } @@ -3214,7 +3201,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) rval = PopStrDupe(ss, op, &rvalpc); todo = ss->sprinter.getOffset(); SprintOpcode(ss, lval, lvalpc, pushpc, todo); - SprintCString(&ss->sprinter, ", "); + ss->sprinter.put(", "); SprintOpcode(ss, rval, rvalpc, pushpc, todo); break; @@ -3423,7 +3410,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) ss->top = top; ss->sprinter.setOffset(GetOff(ss, top)); if (op == JSOP_LEAVEBLOCKEXPR) - todo = SprintCString(&ss->sprinter, rval); + todo = ss->sprinter.put(rval); break; } @@ -3484,10 +3471,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) return NULL; if (groupAssign) { - if (SprintCString(&ss->sprinter, rhs) < 0) + if (ss->sprinter.put(rhs) < 0) return NULL; } else if (!strncmp(rhs, DestructuredString, DestructuredStringLength)) { - if (SprintCString(&ss->sprinter, rhs + DestructuredStringLength) < 0) + if (ss->sprinter.put(rhs + DestructuredStringLength) < 0) return NULL; } else { JS_ASSERT(atoms[i] != cx->runtime->atomState.emptyAtom); @@ -3745,7 +3732,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) ? "%s (%s)" : "%s %s", js_yield_str, rval) - : SprintCString(&ss->sprinter, js_yield_str); + : ss->sprinter.put(js_yield_str); break; } @@ -4108,9 +4095,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) rval = PopStrDupe(ss, op, &rvalpc); todo = ss->sprinter.getOffset(); SprintOpcode(ss, xval, xvalpc, pushpc, todo); - SprintCString(&ss->sprinter, " ? "); + ss->sprinter.put(" ? "); SprintOpcode(ss, lval, lvalpc, pushpc, todo); - SprintCString(&ss->sprinter, " : "); + ss->sprinter.put(" : "); SprintOpcode(ss, rval, rvalpc, pushpc, todo); break; @@ -4175,7 +4162,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) rval = POP_STR(); LOCAL_ASSERT(strcmp(rval, forelem_cookie) == 0); if (*xval == '\0') { - todo = SprintCString(&ss->sprinter, lval); + todo = ss->sprinter.put(lval); } else { todo = Sprint(&ss->sprinter, (JOF_OPMODE(lastop) == JOF_XMLNAME) @@ -4192,7 +4179,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_DUP2: rval = GetStr(ss, ss->top-2); - todo = SprintCString(&ss->sprinter, rval); + todo = ss->sprinter.put(rval); if (todo < 0 || !PushOff(ss, todo, (JSOp) ss->opcodes[ss->top-2])) { return NULL; @@ -4311,7 +4298,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) rval = GetStr(ss, ss->top-1); saveop = (JSOp) ss->opcodes[ss->top-1]; - todo = SprintCString(&ss->sprinter, rval); + todo = ss->sprinter.put(rval); break; case JSOP_SWAP: @@ -4417,14 +4404,14 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) Sprint(&ss->sprinter, "%s ", js_new_str); } SprintOpcode(ss, argv[0], lvalpc, pc, todo); - SprintCString(&ss->sprinter, lval); + ss->sprinter.put(lval); for (i = 1; i <= argc; i++) { SprintOpcode(ss, argv[i], argbytecodes[i], pc, todo); if (i < argc) - SprintCString(&ss->sprinter, ", "); + ss->sprinter.put(", "); } - SprintCString(&ss->sprinter, rval); + ss->sprinter.put(rval); cx->free_(argv); cx->free_(argbytecodes); @@ -4666,9 +4653,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) SprintOpcode(ss, lval, lvalpc, pc, todo); if (*xval != '\0') { bool xml = (JOF_OPMODE(lastop) == JOF_XMLNAME); - SprintCString(&ss->sprinter, xml ? "." : "["); + ss->sprinter.put(xml ? "." : "["); SprintOpcode(ss, xval, xvalpc, pc, todo); - SprintCString(&ss->sprinter, xml ? "" : "]"); + ss->sprinter.put(xml ? "" : "]"); } break; @@ -4690,9 +4677,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) &lastlvalpc, &lastrvalpc); todo = ss->sprinter.getOffset(); SprintOpcode(ss, lval, lvalpc, pc, todo); - SprintCString(&ss->sprinter, xml ? "." : "["); + ss->sprinter.put(xml ? "." : "["); SprintOpcode(ss, xval, xvalpc, pc, todo); - SprintCString(&ss->sprinter, xml ? "" : "]"); + ss->sprinter.put(xml ? "" : "]"); Sprint(&ss->sprinter, " %s= ", token); SprintOpcode(ss, rval, rvalpc, pc, todo); break; @@ -4897,7 +4884,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) las.releaseEarly(); if (!rval) return NULL; - todo = SprintCString(&ss->sprinter, rval); + todo = ss->sprinter.put(rval); cx->free_((void *)rval); break; } @@ -4921,12 +4908,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) return NULL; } sprint_string: - todo = SprintString(&ss->sprinter, str); + todo = ss->sprinter.putString(str); break; case JSOP_CALLEE: JS_ASSERT(jp->fun && jp->fun->atom); - todo = SprintString(&ss->sprinter, jp->fun->atom); + todo = ss->sprinter.putString(jp->fun->atom); break; case JSOP_OBJECT: @@ -5138,7 +5125,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) goto do_function; case JSOP_HOLE: - todo = SprintPut(&ss->sprinter, "", 0); + todo = ss->sprinter.put("", 0); break; case JSOP_NEWINIT: @@ -5149,10 +5136,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) todo = ss->sprinter.getOffset(); if (i == JSProto_Array) { ++ss->inArrayInit; - if (SprintCString(&ss->sprinter, "[") < 0) + if (ss->sprinter.put("[") < 0) return NULL; } else { - if (SprintCString(&ss->sprinter, "{") < 0) + if (ss->sprinter.put("{") < 0) return NULL; } break; @@ -5162,7 +5149,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) { todo = ss->sprinter.getOffset(); ++ss->inArrayInit; - if (SprintCString(&ss->sprinter, "[") < 0) + if (ss->sprinter.put("[") < 0) return NULL; break; } @@ -5170,7 +5157,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_NEWOBJECT: { todo = ss->sprinter.getOffset(); - if (SprintCString(&ss->sprinter, "{") < 0) + if (ss->sprinter.put("{") < 0) return NULL; break; } @@ -5281,9 +5268,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_ANYNAME: if (pc[JSOP_ANYNAME_LENGTH] == JSOP_TOATTRNAME) { len += JSOP_TOATTRNAME_LENGTH; - todo = SprintPut(&ss->sprinter, "@*", 2); + todo = ss->sprinter.put("@*", 2); } else { - todo = SprintPut(&ss->sprinter, "*", 1); + todo = ss->sprinter.put("*", 1); } break; @@ -5391,18 +5378,18 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_XMLCDATA: LOAD_ATOM(0); - todo = SprintPut(&ss->sprinter, "sprinter.put("sprinter, atom, DONT_ESCAPE)) return NULL; - SprintPut(&ss->sprinter, "]]>", 3); + ss->sprinter.put("]]>", 3); break; case JSOP_XMLCOMMENT: LOAD_ATOM(0); - todo = SprintPut(&ss->sprinter, "", 3); + ss->sprinter.put("-->", 3); break; case JSOP_XMLPI: @@ -5410,19 +5397,19 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) rval = JS_strdup(cx, POP_STR()); if (!rval) return NULL; - todo = SprintPut(&ss->sprinter, "sprinter.put("sprinter, atom, 0) && (*rval == '\0' || - (SprintPut(&ss->sprinter, " ", 1) >= 0 && - SprintCString(&ss->sprinter, rval))); + (ss->sprinter.put(" ", 1) >= 0 && + ss->sprinter.put(rval))); cx->free_((char *)rval); if (!ok) return NULL; - SprintPut(&ss->sprinter, "?>", 2); + ss->sprinter.put("?>", 2); break; case JSOP_GETFUNNS: - todo = SprintPut(&ss->sprinter, js_function_str, 8); + todo = ss->sprinter.put(js_function_str, 8); break; #endif /* JS_HAS_XML_SUPPORT */ @@ -5671,7 +5658,7 @@ js_DecompileFunction(JSPrinter *jp) LOCAL_ASSERT(*pc == JSOP_POP); pc += JSOP_POP_LENGTH; lval = PopStr(&ss, JSOP_NOP); - todo = SprintCString(&jp->sprinter, lval); + todo = jp->sprinter.put(lval); if (todo < 0) { ok = JS_FALSE; break; diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index e924d847304..b446324dc11 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -504,6 +504,7 @@ class Sprinter * the beginning of this new data */ ptrdiff_t put(const char *s, size_t len); + ptrdiff_t put(const char *s); ptrdiff_t putString(JSString *str); /* Prints a formatted string into the buffer */ @@ -518,15 +519,6 @@ class Sprinter ptrdiff_t getOffsetOf(const char *string) const; }; -extern ptrdiff_t -SprintPut(Sprinter *sp, const char *s, size_t len); - -extern ptrdiff_t -SprintCString(Sprinter *sp, const char *s); - -extern ptrdiff_t -SprintString(Sprinter *sp, JSString *str); - extern ptrdiff_t Sprint(Sprinter *sp, const char *format, ...); diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 599c125ded6..3ea285e4388 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 @@ -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/jsscript.cpp b/js/src/jsscript.cpp index a7b8ca0e871..d995d5af0d5 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -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..1fa5bc56732 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 diff --git a/js/src/jsweakmap.cpp b/js/src/jsweakmap.cpp index d757d8e7b87..c2a15a680a2 100644 --- a/js/src/jsweakmap.cpp +++ b/js/src/jsweakmap.cpp @@ -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; } } diff --git a/js/src/jsweakmap.h b/js/src/jsweakmap.h index 9494686eee4..12f0b75a385 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, @@ -213,7 +213,7 @@ 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); @@ -264,10 +264,10 @@ class DefaultMarkPolicy { return !IsAboutToBeFinalized(x); return true; } - bool mark(const HeapValue &x) { + bool mark(HeapValue &x) { if (isMarked(x)) return false; - js::gc::MarkValue(tracer, x, "WeakMap entry"); + js::gc::MarkValue(tracer, &x, "WeakMap entry"); return true; } }; @@ -281,7 +281,7 @@ class DefaultMarkPolicy { bool isMarked(const HeapPtrObject &x) { return !IsAboutToBeFinalized(x); } - bool mark(const HeapPtrObject &x) { + bool mark(HeapPtrObject &x) { if (isMarked(x)) return false; js::gc::MarkObject(tracer, x, "WeakMap entry"); @@ -298,7 +298,7 @@ class DefaultMarkPolicy { bool isMarked(const HeapPtrScript &x) { return !IsAboutToBeFinalized(x); } - bool mark(const HeapPtrScript &x) { + bool mark(HeapPtrScript &x) { if (isMarked(x)) return false; js::gc::MarkScript(tracer, x, "WeakMap entry"); 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/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 774fc11616c..0fa45cb53ed 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)) diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index 3d06b51a205..85786eca773 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 @@ -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 8de50b4f190..9f8ecc40425 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -1665,7 +1665,15 @@ stubs::DelElem(VMFrame &f) void JS_FASTCALL stubs::DefVarOrConst(VMFrame &f, PropertyName *dn) { - if (!DefVarOrConstOperation(f.cx, JSOp(*f.regs.pc), dn, f.fp())) + uintN attrs = JSPROP_ENUMERATE; + if (!f.fp()->isEvalFrame()) + attrs |= JSPROP_PERMANENT; + if (JSOp(*f.regs.pc) == JSOP_DEFCONST) + attrs |= JSPROP_READONLY; + + JSObject &obj = f.fp()->varObj(); + + if (!DefVarOrConstOperation(f.cx, obj, dn, attrs)) THROW(); } @@ -1955,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 @@ -1963,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 9199a8deb42..214c4c12774 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -1446,11 +1446,12 @@ typedef struct JSCountHeapTracer { } JSCountHeapTracer; static void -CountHeapNotify(JSTracer *trc, void *thing, JSGCTraceKind kind) +CountHeapNotify(JSTracer *trc, void **thingp, JSGCTraceKind kind) { JSCountHeapTracer *countTracer; JSDHashEntryStub *entry; JSCountHeapNode *node; + void *thing = *thingp; JS_ASSERT(trc->callback == CountHeapNotify); countTracer = (JSCountHeapTracer *)trc; diff --git a/js/src/shell/jsheaptools.cpp b/js/src/shell/jsheaptools.cpp index eda952c94ea..04ed84d0c72 100644 --- a/js/src/shell/jsheaptools.cpp +++ b/js/src/shell/jsheaptools.cpp @@ -232,7 +232,7 @@ class HeapReverser : public JSTracer { * A stack of work items. We represent the stack explicitly to avoid * overflowing the C++ stack when traversing long chains of objects. */ - Vector work; + Vector work; /* When traverseEdge is called, the Cell and kind at which the edge originated. */ void *parent; @@ -249,19 +249,17 @@ class HeapReverser : public JSTracer { bool traversalStatus; /* Static member function wrapping 'traverseEdge'. */ - static void traverseEdgeWithThis(JSTracer *tracer, void *cell, JSGCTraceKind kind) { + static void traverseEdgeWithThis(JSTracer *tracer, void **thingp, JSGCTraceKind kind) { HeapReverser *reverser = static_cast(tracer); - reverser->traversalStatus = reverser->traverseEdge(cell, kind); + reverser->traversalStatus = reverser->traverseEdge(*thingp, kind); } /* Return a jsval representing a node, if possible; otherwise, return JSVAL_VOID. */ jsval nodeToValue(void *cell, int kind) { - if (kind == JSTRACE_OBJECT) { - JSObject *object = static_cast(cell); - return OBJECT_TO_JSVAL(object); - } else { + if (kind != JSTRACE_OBJECT) return JSVAL_VOID; - } + JSObject *object = static_cast(cell); + return OBJECT_TO_JSVAL(object); } }; diff --git a/js/src/tests/ecma_5/String/string-upper-lower-mapping.js b/js/src/tests/ecma_5/String/string-upper-lower-mapping.js index 93a7deef461..35ea051c011 100644 --- a/js/src/tests/ecma_5/String/string-upper-lower-mapping.js +++ b/js/src/tests/ecma_5/String/string-upper-lower-mapping.js @@ -619,7 +619,7 @@ var mapping = [ [0x194, 0x263], /* LATIN SMALL LETTER GAMMA */ [0x264, 0x264], /* LATIN SMALL LETTER RAMS HORN (LATIN SMALL LETTER BABY GAMMA) */ [0xa78d, 0x265], /* LATIN SMALL LETTER TURNED H */ - [0x266, 0x266], /* LATIN SMALL LETTER H WITH HOOK (LATIN SMALL LETTER H HOOK) */ + [0xa7aa, 0x266], /* LATIN SMALL LETTER H WITH HOOK (LATIN SMALL LETTER H HOOK) */ [0x267, 0x267], /* LATIN SMALL LETTER HENG WITH HOOK (LATIN SMALL LETTER HENG HOOK) */ [0x197, 0x268], /* LATIN SMALL LETTER I WITH STROKE (LATIN SMALL LETTER BARRED I) */ [0x196, 0x269], /* LATIN SMALL LETTER IOTA */ @@ -1428,7 +1428,7 @@ var mapping = [ [0x58c, 0x58c], [0x58d, 0x58d], [0x58e, 0x58e], - [0x58f, 0x58f], + [0x58f, 0x58f], /* ARMENIAN DRAM SIGN */ [0x590, 0x590], [0x591, 0x591], /* HEBREW ACCENT ETNAHTA */ [0x592, 0x592], /* HEBREW ACCENT SEGOL */ @@ -1545,7 +1545,7 @@ var mapping = [ [0x601, 0x601], /* ARABIC SIGN SANAH */ [0x602, 0x602], /* ARABIC FOOTNOTE MARKER */ [0x603, 0x603], /* ARABIC SIGN SAFHA */ - [0x604, 0x604], + [0x604, 0x604], /* ARABIC SIGN SAMVAT */ [0x605, 0x605], [0x606, 0x606], /* ARABIC-INDIC CUBE ROOT */ [0x607, 0x607], /* ARABIC-INDIC FOURTH ROOT */ @@ -2213,19 +2213,19 @@ var mapping = [ [0x89d, 0x89d], [0x89e, 0x89e], [0x89f, 0x89f], - [0x8a0, 0x8a0], + [0x8a0, 0x8a0], /* ARABIC LETTER BEH WITH SMALL V BELOW */ [0x8a1, 0x8a1], - [0x8a2, 0x8a2], - [0x8a3, 0x8a3], - [0x8a4, 0x8a4], - [0x8a5, 0x8a5], - [0x8a6, 0x8a6], - [0x8a7, 0x8a7], - [0x8a8, 0x8a8], - [0x8a9, 0x8a9], - [0x8aa, 0x8aa], - [0x8ab, 0x8ab], - [0x8ac, 0x8ac], + [0x8a2, 0x8a2], /* ARABIC LETTER JEEM WITH TWO DOTS ABOVE */ + [0x8a3, 0x8a3], /* ARABIC LETTER TAH WITH TWO DOTS ABOVE */ + [0x8a4, 0x8a4], /* ARABIC LETTER FEH WITH DOT BELOW AND THREE DOTS ABOVE */ + [0x8a5, 0x8a5], /* ARABIC LETTER QAF WITH DOT BELOW */ + [0x8a6, 0x8a6], /* ARABIC LETTER LAM WITH DOUBLE BAR */ + [0x8a7, 0x8a7], /* ARABIC LETTER MEEM WITH THREE DOTS ABOVE */ + [0x8a8, 0x8a8], /* ARABIC LETTER YEH WITH TWO DOTS BELOW AND HAMZA ABOVE */ + [0x8a9, 0x8a9], /* ARABIC LETTER YEH WITH TWO DOTS BELOW AND DOT ABOVE */ + [0x8aa, 0x8aa], /* ARABIC LETTER REH WITH LOOP */ + [0x8ab, 0x8ab], /* ARABIC LETTER WAW WITH DOT WITHIN */ + [0x8ac, 0x8ac], /* ARABIC LETTER ROHINGYA YEH */ [0x8ad, 0x8ad], [0x8ae, 0x8ae], [0x8af, 0x8af], @@ -2281,33 +2281,33 @@ var mapping = [ [0x8e1, 0x8e1], [0x8e2, 0x8e2], [0x8e3, 0x8e3], - [0x8e4, 0x8e4], - [0x8e5, 0x8e5], - [0x8e6, 0x8e6], - [0x8e7, 0x8e7], - [0x8e8, 0x8e8], - [0x8e9, 0x8e9], - [0x8ea, 0x8ea], - [0x8eb, 0x8eb], - [0x8ec, 0x8ec], - [0x8ed, 0x8ed], - [0x8ee, 0x8ee], - [0x8ef, 0x8ef], - [0x8f0, 0x8f0], - [0x8f1, 0x8f1], - [0x8f2, 0x8f2], - [0x8f3, 0x8f3], - [0x8f4, 0x8f4], - [0x8f5, 0x8f5], - [0x8f6, 0x8f6], - [0x8f7, 0x8f7], - [0x8f8, 0x8f8], - [0x8f9, 0x8f9], - [0x8fa, 0x8fa], - [0x8fb, 0x8fb], - [0x8fc, 0x8fc], - [0x8fd, 0x8fd], - [0x8fe, 0x8fe], + [0x8e4, 0x8e4], /* ARABIC CURLY FATHA */ + [0x8e5, 0x8e5], /* ARABIC CURLY DAMMA */ + [0x8e6, 0x8e6], /* ARABIC CURLY KASRA */ + [0x8e7, 0x8e7], /* ARABIC CURLY FATHATAN */ + [0x8e8, 0x8e8], /* ARABIC CURLY DAMMATAN */ + [0x8e9, 0x8e9], /* ARABIC CURLY KASRATAN */ + [0x8ea, 0x8ea], /* ARABIC TONE ONE DOT ABOVE */ + [0x8eb, 0x8eb], /* ARABIC TONE TWO DOTS ABOVE */ + [0x8ec, 0x8ec], /* ARABIC TONE LOOP ABOVE */ + [0x8ed, 0x8ed], /* ARABIC TONE ONE DOT BELOW */ + [0x8ee, 0x8ee], /* ARABIC TONE TWO DOTS BELOW */ + [0x8ef, 0x8ef], /* ARABIC TONE LOOP BELOW */ + [0x8f0, 0x8f0], /* ARABIC OPEN FATHATAN */ + [0x8f1, 0x8f1], /* ARABIC OPEN DAMMATAN */ + [0x8f2, 0x8f2], /* ARABIC OPEN KASRATAN */ + [0x8f3, 0x8f3], /* ARABIC SMALL HIGH WAW */ + [0x8f4, 0x8f4], /* ARABIC FATHA WITH RING */ + [0x8f5, 0x8f5], /* ARABIC FATHA WITH DOT ABOVE */ + [0x8f6, 0x8f6], /* ARABIC KASRA WITH DOT BELOW */ + [0x8f7, 0x8f7], /* ARABIC LEFT ARROWHEAD ABOVE */ + [0x8f8, 0x8f8], /* ARABIC RIGHT ARROWHEAD ABOVE */ + [0x8f9, 0x8f9], /* ARABIC LEFT ARROWHEAD BELOW */ + [0x8fa, 0x8fa], /* ARABIC RIGHT ARROWHEAD BELOW */ + [0x8fb, 0x8fb], /* ARABIC DOUBLE RIGHT ARROWHEAD ABOVE */ + [0x8fc, 0x8fc], /* ARABIC DOUBLE RIGHT ARROWHEAD ABOVE WITH DOT */ + [0x8fd, 0x8fd], /* ARABIC RIGHT ARROWHEAD ABOVE WITH DOT */ + [0x8fe, 0x8fe], /* ARABIC DAMMA WITH DOT */ [0x8ff, 0x8ff], [0x900, 0x900], /* DEVANAGARI SIGN INVERTED CANDRABINDU */ [0x901, 0x901], /* DEVANAGARI SIGN CANDRABINDU */ @@ -2805,7 +2805,7 @@ var mapping = [ [0xaed, 0xaed], /* GUJARATI DIGIT SEVEN */ [0xaee, 0xaee], /* GUJARATI DIGIT EIGHT */ [0xaef, 0xaef], /* GUJARATI DIGIT NINE */ - [0xaf0, 0xaf0], + [0xaf0, 0xaf0], /* GUJARATI ABBREVIATION SIGN */ [0xaf1, 0xaf1], /* GUJARATI RUPEE SIGN */ [0xaf2, 0xaf2], [0xaf3, 0xaf3], @@ -3811,8 +3811,8 @@ var mapping = [ [0xedb, 0xedb], [0xedc, 0xedc], /* LAO HO NO */ [0xedd, 0xedd], /* LAO HO MO */ - [0xede, 0xede], - [0xedf, 0xedf], + [0xede, 0xede], /* LAO LETTER KHMU GO */ + [0xedf, 0xedf], /* LAO LETTER KHMU NYO */ [0xee0, 0xee0], [0xee1, 0xee1], [0xee2, 0xee2], @@ -4300,13 +4300,13 @@ var mapping = [ [0x10c4, 0x2d24], /* GEORGIAN CAPITAL LETTER HAR */ [0x10c5, 0x2d25], /* GEORGIAN CAPITAL LETTER HOE */ [0x10c6, 0x10c6], - [0x10c7, 0x10c7], + [0x10c7, 0x2d27], /* GEORGIAN CAPITAL LETTER YN */ [0x10c8, 0x10c8], [0x10c9, 0x10c9], [0x10ca, 0x10ca], [0x10cb, 0x10cb], [0x10cc, 0x10cc], - [0x10cd, 0x10cd], + [0x10cd, 0x2d2d], /* GEORGIAN CAPITAL LETTER AEN */ [0x10ce, 0x10ce], [0x10cf, 0x10cf], [0x10d0, 0x10d0], /* GEORGIAN LETTER AN (GEORGIAN SMALL LETTER AN) */ @@ -4354,9 +4354,9 @@ var mapping = [ [0x10fa, 0x10fa], /* GEORGIAN LETTER AIN */ [0x10fb, 0x10fb], /* GEORGIAN PARAGRAPH SEPARATOR */ [0x10fc, 0x10fc], /* MODIFIER LETTER GEORGIAN NAR */ - [0x10fd, 0x10fd], - [0x10fe, 0x10fe], - [0x10ff, 0x10ff], + [0x10fd, 0x10fd], /* GEORGIAN LETTER AEN */ + [0x10fe, 0x10fe], /* GEORGIAN LETTER HARD SIGN */ + [0x10ff, 0x10ff], /* GEORGIAN LETTER LABIAL SIGN */ [0x1100, 0x1100], /* HANGUL CHOSEONG KIYEOK */ [0x1101, 0x1101], /* HANGUL CHOSEONG SSANGKIYEOK */ [0x1102, 0x1102], /* HANGUL CHOSEONG NIEUN */ @@ -7088,9 +7088,9 @@ var mapping = [ [0x1ba8, 0x1ba8], /* SUNDANESE VOWEL SIGN PAMEPET */ [0x1ba9, 0x1ba9], /* SUNDANESE VOWEL SIGN PANEULEUNG */ [0x1baa, 0x1baa], /* SUNDANESE SIGN PAMAAEH */ - [0x1bab, 0x1bab], - [0x1bac, 0x1bac], - [0x1bad, 0x1bad], + [0x1bab, 0x1bab], /* SUNDANESE SIGN VIRAMA */ + [0x1bac, 0x1bac], /* SUNDANESE CONSONANT SIGN PASANGAN MA */ + [0x1bad, 0x1bad], /* SUNDANESE CONSONANT SIGN PASANGAN WA */ [0x1bae, 0x1bae], /* SUNDANESE LETTER KHA */ [0x1baf, 0x1baf], /* SUNDANESE LETTER SYA */ [0x1bb0, 0x1bb0], /* SUNDANESE DIGIT ZERO */ @@ -7103,12 +7103,12 @@ var mapping = [ [0x1bb7, 0x1bb7], /* SUNDANESE DIGIT SEVEN */ [0x1bb8, 0x1bb8], /* SUNDANESE DIGIT EIGHT */ [0x1bb9, 0x1bb9], /* SUNDANESE DIGIT NINE */ - [0x1bba, 0x1bba], - [0x1bbb, 0x1bbb], - [0x1bbc, 0x1bbc], - [0x1bbd, 0x1bbd], - [0x1bbe, 0x1bbe], - [0x1bbf, 0x1bbf], + [0x1bba, 0x1bba], /* SUNDANESE AVAGRAHA */ + [0x1bbb, 0x1bbb], /* SUNDANESE LETTER REU */ + [0x1bbc, 0x1bbc], /* SUNDANESE LETTER LEU */ + [0x1bbd, 0x1bbd], /* SUNDANESE LETTER BHA */ + [0x1bbe, 0x1bbe], /* SUNDANESE LETTER FINAL K */ + [0x1bbf, 0x1bbf], /* SUNDANESE LETTER FINAL M */ [0x1bc0, 0x1bc0], /* BATAK LETTER A */ [0x1bc1, 0x1bc1], /* BATAK LETTER SIMALUNGUN A */ [0x1bc2, 0x1bc2], /* BATAK LETTER HA */ @@ -7365,14 +7365,14 @@ var mapping = [ [0x1cbd, 0x1cbd], [0x1cbe, 0x1cbe], [0x1cbf, 0x1cbf], - [0x1cc0, 0x1cc0], - [0x1cc1, 0x1cc1], - [0x1cc2, 0x1cc2], - [0x1cc3, 0x1cc3], - [0x1cc4, 0x1cc4], - [0x1cc5, 0x1cc5], - [0x1cc6, 0x1cc6], - [0x1cc7, 0x1cc7], + [0x1cc0, 0x1cc0], /* SUNDANESE PUNCTUATION BINDU SURYA */ + [0x1cc1, 0x1cc1], /* SUNDANESE PUNCTUATION BINDU PANGLONG */ + [0x1cc2, 0x1cc2], /* SUNDANESE PUNCTUATION BINDU PURNAMA */ + [0x1cc3, 0x1cc3], /* SUNDANESE PUNCTUATION BINDU CAKRA */ + [0x1cc4, 0x1cc4], /* SUNDANESE PUNCTUATION BINDU LEU SATANGA */ + [0x1cc5, 0x1cc5], /* SUNDANESE PUNCTUATION BINDU KA SATANGA */ + [0x1cc6, 0x1cc6], /* SUNDANESE PUNCTUATION BINDU DA SATANGA */ + [0x1cc7, 0x1cc7], /* SUNDANESE PUNCTUATION BINDU BA SATANGA */ [0x1cc8, 0x1cc8], [0x1cc9, 0x1cc9], [0x1cca, 0x1cca], @@ -7416,10 +7416,10 @@ var mapping = [ [0x1cf0, 0x1cf0], /* VEDIC SIGN RTHANG LONG ANUSVARA */ [0x1cf1, 0x1cf1], /* VEDIC SIGN ANUSVARA UBHAYATO MUKHA */ [0x1cf2, 0x1cf2], /* VEDIC SIGN ARDHAVISARGA */ - [0x1cf3, 0x1cf3], - [0x1cf4, 0x1cf4], - [0x1cf5, 0x1cf5], - [0x1cf6, 0x1cf6], + [0x1cf3, 0x1cf3], /* VEDIC SIGN ROTATED ARDHAVISARGA */ + [0x1cf4, 0x1cf4], /* VEDIC TONE CANDRA ABOVE */ + [0x1cf5, 0x1cf5], /* VEDIC SIGN JIHVAMULIYA */ + [0x1cf6, 0x1cf6], /* VEDIC SIGN UPADHMANIYA */ [0x1cf7, 0x1cf7], [0x1cf8, 0x1cf8], [0x1cf9, 0x1cf9], @@ -10192,9 +10192,9 @@ var mapping = [ [0x27c8, 0x27c8], /* REVERSE SOLIDUS PRECEDING SUBSET */ [0x27c9, 0x27c9], /* SUPERSET PRECEDING SOLIDUS */ [0x27ca, 0x27ca], /* VERTICAL BAR WITH HORIZONTAL STROKE */ - [0x27cb, 0x27cb], + [0x27cb, 0x27cb], /* MATHEMATICAL RISING DIAGONAL */ [0x27cc, 0x27cc], /* LONG DIVISION */ - [0x27cd, 0x27cd], + [0x27cd, 0x27cd], /* MATHEMATICAL FALLING DIAGONAL */ [0x27ce, 0x27ce], /* SQUARED LOGICAL AND */ [0x27cf, 0x27cf], /* SQUARED LOGICAL OR */ [0x27d0, 0x27d0], /* WHITE DIAMOND WITH CENTRED DOT */ @@ -11511,8 +11511,8 @@ var mapping = [ [0x2cef, 0x2cef], /* COPTIC COMBINING NI ABOVE */ [0x2cf0, 0x2cf0], /* COPTIC COMBINING SPIRITUS ASPER */ [0x2cf1, 0x2cf1], /* COPTIC COMBINING SPIRITUS LENIS */ - [0x2cf2, 0x2cf2], - [0x2cf3, 0x2cf3], + [0x2cf2, 0x2cf3], /* COPTIC CAPITAL LETTER BOHAIRIC KHEI */ + [0x2cf2, 0x2cf3], /* COPTIC SMALL LETTER BOHAIRIC KHEI */ [0x2cf4, 0x2cf4], [0x2cf5, 0x2cf5], [0x2cf6, 0x2cf6], @@ -11564,13 +11564,13 @@ var mapping = [ [0x10c4, 0x2d24], /* GEORGIAN SMALL LETTER HAR */ [0x10c5, 0x2d25], /* GEORGIAN SMALL LETTER HOE */ [0x2d26, 0x2d26], - [0x2d27, 0x2d27], + [0x10c7, 0x2d27], /* GEORGIAN SMALL LETTER YN */ [0x2d28, 0x2d28], [0x2d29, 0x2d29], [0x2d2a, 0x2d2a], [0x2d2b, 0x2d2b], [0x2d2c, 0x2d2c], - [0x2d2d, 0x2d2d], + [0x10cd, 0x2d2d], /* GEORGIAN SMALL LETTER AEN */ [0x2d2e, 0x2d2e], [0x2d2f, 0x2d2f], [0x2d30, 0x2d30], /* TIFINAGH LETTER YA */ @@ -11627,8 +11627,8 @@ var mapping = [ [0x2d63, 0x2d63], /* TIFINAGH LETTER YAZ */ [0x2d64, 0x2d64], /* TIFINAGH LETTER TAWELLEMET YAZ */ [0x2d65, 0x2d65], /* TIFINAGH LETTER YAZZ */ - [0x2d66, 0x2d66], - [0x2d67, 0x2d67], + [0x2d66, 0x2d66], /* TIFINAGH LETTER YE */ + [0x2d67, 0x2d67], /* TIFINAGH LETTER YO */ [0x2d68, 0x2d68], [0x2d69, 0x2d69], [0x2d6a, 0x2d6a], @@ -11831,16 +11831,16 @@ var mapping = [ [0x2e2f, 0x2e2f], /* VERTICAL TILDE */ [0x2e30, 0x2e30], /* RING POINT */ [0x2e31, 0x2e31], /* WORD SEPARATOR MIDDLE DOT */ - [0x2e32, 0x2e32], - [0x2e33, 0x2e33], - [0x2e34, 0x2e34], - [0x2e35, 0x2e35], - [0x2e36, 0x2e36], - [0x2e37, 0x2e37], - [0x2e38, 0x2e38], - [0x2e39, 0x2e39], - [0x2e3a, 0x2e3a], - [0x2e3b, 0x2e3b], + [0x2e32, 0x2e32], /* TURNED COMMA */ + [0x2e33, 0x2e33], /* RAISED DOT */ + [0x2e34, 0x2e34], /* RAISED COMMA */ + [0x2e35, 0x2e35], /* TURNED SEMICOLON */ + [0x2e36, 0x2e36], /* DAGGER WITH LEFT GUARD */ + [0x2e37, 0x2e37], /* DAGGER WITH RIGHT GUARD */ + [0x2e38, 0x2e38], /* TURNED DAGGER */ + [0x2e39, 0x2e39], /* TOP HALF SECTION SIGN */ + [0x2e3a, 0x2e3a], /* TWO-EM DASH */ + [0x2e3b, 0x2e3b], /* THREE-EM DASH */ [0x2e3c, 0x2e3c], [0x2e3d, 0x2e3d], [0x2e3e, 0x2e3e], @@ -40913,7 +40913,7 @@ var mapping = [ [0x9fc9, 0x9fc9], /* CJK Ideograph */ [0x9fca, 0x9fca], /* CJK Ideograph */ [0x9fcb, 0x9fcb], /* CJK Ideograph */ - [0x9fcc, 0x9fcc], + [0x9fcc, 0x9fcc], /* CJK Ideograph */ [0x9fcd, 0x9fcd], [0x9fce, 0x9fce], [0x9fcf, 0x9fcf], @@ -42617,14 +42617,14 @@ var mapping = [ [0xa671, 0xa671], /* COMBINING CYRILLIC HUNDRED MILLIONS SIGN */ [0xa672, 0xa672], /* COMBINING CYRILLIC THOUSAND MILLIONS SIGN */ [0xa673, 0xa673], /* SLAVONIC ASTERISK */ - [0xa674, 0xa674], - [0xa675, 0xa675], - [0xa676, 0xa676], - [0xa677, 0xa677], - [0xa678, 0xa678], - [0xa679, 0xa679], - [0xa67a, 0xa67a], - [0xa67b, 0xa67b], + [0xa674, 0xa674], /* COMBINING CYRILLIC LETTER UKRAINIAN IE */ + [0xa675, 0xa675], /* COMBINING CYRILLIC LETTER I */ + [0xa676, 0xa676], /* COMBINING CYRILLIC LETTER YI */ + [0xa677, 0xa677], /* COMBINING CYRILLIC LETTER U */ + [0xa678, 0xa678], /* COMBINING CYRILLIC LETTER HARD SIGN */ + [0xa679, 0xa679], /* COMBINING CYRILLIC LETTER YERU */ + [0xa67a, 0xa67a], /* COMBINING CYRILLIC LETTER SOFT SIGN */ + [0xa67b, 0xa67b], /* COMBINING CYRILLIC LETTER OMEGA */ [0xa67c, 0xa67c], /* COMBINING CYRILLIC KAVYKA */ [0xa67d, 0xa67d], /* COMBINING CYRILLIC PAYEROK */ [0xa67e, 0xa67e], /* CYRILLIC KAVYKA */ @@ -42660,7 +42660,7 @@ var mapping = [ [0xa69c, 0xa69c], [0xa69d, 0xa69d], [0xa69e, 0xa69e], - [0xa69f, 0xa69f], + [0xa69f, 0xa69f], /* COMBINING CYRILLIC LETTER IOTIFIED E */ [0xa6a0, 0xa6a0], /* BAMUM LETTER A */ [0xa6a1, 0xa6a1], /* BAMUM LETTER KA */ [0xa6a2, 0xa6a2], /* BAMUM LETTER U */ @@ -42903,8 +42903,8 @@ var mapping = [ [0xa78f, 0xa78f], [0xa790, 0xa791], /* LATIN CAPITAL LETTER N WITH DESCENDER */ [0xa790, 0xa791], /* LATIN SMALL LETTER N WITH DESCENDER */ - [0xa792, 0xa792], - [0xa793, 0xa793], + [0xa792, 0xa793], /* LATIN CAPITAL LETTER C WITH BAR */ + [0xa792, 0xa793], /* LATIN SMALL LETTER C WITH BAR */ [0xa794, 0xa794], [0xa795, 0xa795], [0xa796, 0xa796], @@ -42927,7 +42927,7 @@ var mapping = [ [0xa7a6, 0xa7a7], /* LATIN SMALL LETTER R WITH OBLIQUE STROKE */ [0xa7a8, 0xa7a9], /* LATIN CAPITAL LETTER S WITH OBLIQUE STROKE */ [0xa7a8, 0xa7a9], /* LATIN SMALL LETTER S WITH OBLIQUE STROKE */ - [0xa7aa, 0xa7aa], + [0xa7aa, 0x266], /* LATIN CAPITAL LETTER H WITH HOOK */ [0xa7ab, 0xa7ab], [0xa7ac, 0xa7ac], [0xa7ad, 0xa7ad], @@ -43005,8 +43005,8 @@ var mapping = [ [0xa7f5, 0xa7f5], [0xa7f6, 0xa7f6], [0xa7f7, 0xa7f7], - [0xa7f8, 0xa7f8], - [0xa7f9, 0xa7f9], + [0xa7f8, 0xa7f8], /* MODIFIER LETTER CAPITAL H WITH STROKE */ + [0xa7f9, 0xa7f9], /* MODIFIER LETTER SMALL LIGATURE OE */ [0xa7fa, 0xa7fa], /* LATIN LETTER SMALL CAPITAL TURNED M */ [0xa7fb, 0xa7fb], /* LATIN EPIGRAPHIC LETTER REVERSED F */ [0xa7fc, 0xa7fc], /* LATIN EPIGRAPHIC LETTER REVERSED P */ @@ -43749,29 +43749,29 @@ var mapping = [ [0xaadd, 0xaadd], /* TAI VIET SYMBOL SAM */ [0xaade, 0xaade], /* TAI VIET SYMBOL HO HOI */ [0xaadf, 0xaadf], /* TAI VIET SYMBOL KOI KOI */ - [0xaae0, 0xaae0], - [0xaae1, 0xaae1], - [0xaae2, 0xaae2], - [0xaae3, 0xaae3], - [0xaae4, 0xaae4], - [0xaae5, 0xaae5], - [0xaae6, 0xaae6], - [0xaae7, 0xaae7], - [0xaae8, 0xaae8], - [0xaae9, 0xaae9], - [0xaaea, 0xaaea], - [0xaaeb, 0xaaeb], - [0xaaec, 0xaaec], - [0xaaed, 0xaaed], - [0xaaee, 0xaaee], - [0xaaef, 0xaaef], - [0xaaf0, 0xaaf0], - [0xaaf1, 0xaaf1], - [0xaaf2, 0xaaf2], - [0xaaf3, 0xaaf3], - [0xaaf4, 0xaaf4], - [0xaaf5, 0xaaf5], - [0xaaf6, 0xaaf6], + [0xaae0, 0xaae0], /* MEETEI MAYEK LETTER E */ + [0xaae1, 0xaae1], /* MEETEI MAYEK LETTER O */ + [0xaae2, 0xaae2], /* MEETEI MAYEK LETTER CHA */ + [0xaae3, 0xaae3], /* MEETEI MAYEK LETTER NYA */ + [0xaae4, 0xaae4], /* MEETEI MAYEK LETTER TTA */ + [0xaae5, 0xaae5], /* MEETEI MAYEK LETTER TTHA */ + [0xaae6, 0xaae6], /* MEETEI MAYEK LETTER DDA */ + [0xaae7, 0xaae7], /* MEETEI MAYEK LETTER DDHA */ + [0xaae8, 0xaae8], /* MEETEI MAYEK LETTER NNA */ + [0xaae9, 0xaae9], /* MEETEI MAYEK LETTER SHA */ + [0xaaea, 0xaaea], /* MEETEI MAYEK LETTER SSA */ + [0xaaeb, 0xaaeb], /* MEETEI MAYEK VOWEL SIGN II */ + [0xaaec, 0xaaec], /* MEETEI MAYEK VOWEL SIGN UU */ + [0xaaed, 0xaaed], /* MEETEI MAYEK VOWEL SIGN AAI */ + [0xaaee, 0xaaee], /* MEETEI MAYEK VOWEL SIGN AU */ + [0xaaef, 0xaaef], /* MEETEI MAYEK VOWEL SIGN AAU */ + [0xaaf0, 0xaaf0], /* MEETEI MAYEK CHEIKHAN */ + [0xaaf1, 0xaaf1], /* MEETEI MAYEK AHANG KHUDAM */ + [0xaaf2, 0xaaf2], /* MEETEI MAYEK ANJI */ + [0xaaf3, 0xaaf3], /* MEETEI MAYEK SYLLABLE REPETITION MARK */ + [0xaaf4, 0xaaf4], /* MEETEI MAYEK WORD REPETITION MARK */ + [0xaaf5, 0xaaf5], /* MEETEI MAYEK VOWEL SIGN VISARGA */ + [0xaaf6, 0xaaf6], /* MEETEI MAYEK VIRAMA */ [0xaaf7, 0xaaf7], [0xaaf8, 0xaaf8], [0xaaf9, 0xaaf9], @@ -64051,8 +64051,8 @@ var mapping = [ [0xfa2b, 0xfa2b], /* CJK COMPATIBILITY IDEOGRAPH-FA2B */ [0xfa2c, 0xfa2c], /* CJK COMPATIBILITY IDEOGRAPH-FA2C */ [0xfa2d, 0xfa2d], /* CJK COMPATIBILITY IDEOGRAPH-FA2D */ - [0xfa2e, 0xfa2e], - [0xfa2f, 0xfa2f], + [0xfa2e, 0xfa2e], /* CJK COMPATIBILITY IDEOGRAPH-FA2E */ + [0xfa2f, 0xfa2f], /* CJK COMPATIBILITY IDEOGRAPH-FA2F */ [0xfa30, 0xfa30], /* CJK COMPATIBILITY IDEOGRAPH-FA30 */ [0xfa31, 0xfa31], /* CJK COMPATIBILITY IDEOGRAPH-FA31 */ [0xfa32, 0xfa32], /* CJK COMPATIBILITY IDEOGRAPH-FA32 */ diff --git a/js/src/tests/js1_5/Regress/regress-343713.js b/js/src/tests/js1_5/Regress/regress-343713.js index 0b2082a3272..1320a7774a8 100644 --- a/js/src/tests/js1_5/Regress/regress-343713.js +++ b/js/src/tests/js1_5/Regress/regress-343713.js @@ -37,7 +37,7 @@ //----------------------------------------------------------------------------- var BUGNUMBER = 343713; -var summary = 'Do not JS_Assert with nested function evaluation'; +var summary = 'Do not assert with nested function evaluation'; var actual = 'No Crash'; var expect = 'No Crash'; 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/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/Stack.cpp b/js/src/vm/Stack.cpp index 3c1ce65eb54..720e7bda6bb 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(); } diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 9b30e2500cd..c2690d87af8 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_; 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/src/vm/Unicode.cpp b/js/src/vm/Unicode.cpp index 55e33575241..8d92a067826 100644 --- a/js/src/vm/Unicode.cpp +++ b/js/src/vm/Unicode.cpp @@ -111,6 +111,7 @@ const CharacterInfo js_charinfo[] = { {65333, 0, 2}, {65329, 0, 2}, {42893, 613, 10}, + {42922, 614, 10}, {65327, 0, 2}, {65325, 0, 2}, {10743, 0, 2}, @@ -198,23 +199,23 @@ const CharacterInfo js_charinfo[] = { const uint8_t index1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, - 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 34, - 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, - 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 26, 26, 26, 26, - 26, 67, 68, 69, 70, 71, 72, 73, 74, 26, 26, 26, 26, 26, 26, 26, 26, 75, - 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 34, - 93, 94, 95, 96, 97, 98, 34, 99, 26, 100, 26, 101, 102, 102, 103, 102, 104, 105, - 106, 107, 108, 109, 110, 111, 112, 113, 114, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 115, 116, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 117, 118, 102, 119, - 120, 121, 122, 123, 124, 34, 34, 34, 34, 34, 34, 34, 125, 74, 126, 127, 128, 26, - 129, 130, 34, 34, 34, 34, 34, 34, 34, 34, 26, 26, 26, 26, 26, 26, 26, 26, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 26, 26, 26, 26, + 26, 68, 69, 70, 71, 72, 73, 74, 75, 26, 26, 26, 26, 26, 26, 26, 26, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 94, 101, 26, 102, 26, 103, 104, 104, 105, 104, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 117, 118, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 119, 120, 104, 121, + 122, 123, 124, 125, 126, 94, 94, 94, 94, 94, 94, 94, 127, 75, 128, 129, 130, 26, + 131, 132, 94, 94, 94, 94, 94, 94, 94, 94, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 85, 34, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 86, 94, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, @@ -232,10 +233,10 @@ const uint8_t index1[] = { 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 131, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 132, 133, 26, 26, 26, 26, 134, 135, - 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, - 154, 34, 34, 155, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 133, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 133, 134, 26, 26, 26, 26, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 94, 94, 156, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, @@ -244,16 +245,16 @@ const uint8_t index1[] = { 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 156, 157, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 26, 26, 26, 26, 158, 158, 26, 159, 160, 161, 162, 163, - 26, 26, 26, 26, 164, 165, 166, 167, 168, 169, 26, 170, 171, 172, 173, 174, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 157, 158, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 26, 26, 26, 26, 26, 159, 26, 160, 161, 162, 163, 164, + 26, 26, 26, 26, 165, 166, 167, 168, 169, 170, 26, 171, 172, 173, 174, 175, }; const uint8_t index2[] = { @@ -291,9 +292,9 @@ const uint8_t index2[] = { 8, 9, 8, 9, 8, 9, 5, 5, 5, 5, 5, 5, 40, 8, 9, 41, 42, 43, 43, 8, 9, 44, 45, 46, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 47, 48, 49, 50, 51, 5, 52, 52, 5, 53, 5, 54, 5, 5, 5, 5, 52, 5, 5, 55, - 5, 56, 5, 5, 57, 58, 5, 59, 5, 5, 5, 58, 5, 60, 61, 5, 5, 62, - 5, 5, 5, 5, 5, 5, 5, 63, 5, 5, 64, 5, 5, 64, 5, 5, 5, 5, - 64, 65, 66, 66, 67, 5, 5, 5, 5, 5, 68, 5, 5, 5, 5, 5, 5, 5, + 5, 56, 57, 5, 58, 59, 5, 60, 5, 5, 5, 59, 5, 61, 62, 5, 5, 63, + 5, 5, 5, 5, 5, 5, 5, 64, 5, 5, 65, 5, 5, 65, 5, 5, 5, 5, + 65, 66, 67, 67, 68, 5, 5, 5, 5, 5, 69, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, @@ -303,39 +304,39 @@ const uint8_t index2[] = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 69, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 70, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 8, 9, 8, 9, 5, 0, 8, 9, 0, 0, 5, 27, 27, 27, 0, 0, 0, 0, 0, 0, - 0, 0, 70, 0, 71, 71, 71, 0, 72, 0, 73, 73, 5, 3, 3, 3, 3, 3, + 0, 0, 71, 0, 72, 72, 72, 0, 73, 0, 74, 74, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 74, 75, 75, 75, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 76, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 77, 78, 78, 79, 80, 81, 5, 5, 5, 82, 83, 84, 8, 9, 8, 9, 8, 9, + 3, 3, 3, 3, 75, 76, 76, 76, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 77, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 78, 79, 79, 80, 81, 82, 5, 5, 5, 83, 84, 85, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, - 85, 86, 87, 5, 88, 89, 0, 8, 9, 90, 8, 9, 5, 39, 39, 39, 91, 91, - 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 3, 3, 3, 3, + 86, 87, 88, 5, 89, 90, 0, 8, 9, 91, 8, 9, 5, 39, 39, 39, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, + 4, 4, 4, 4, 4, 4, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, - 8, 9, 0, 2, 2, 2, 2, 2, 92, 92, 8, 9, 8, 9, 8, 9, 8, 9, + 8, 9, 0, 2, 2, 2, 2, 2, 93, 93, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, - 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 93, 8, 9, 8, 9, 8, 9, 8, - 9, 8, 9, 8, 9, 8, 9, 94, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, + 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 94, 8, 9, 8, 9, 8, 9, 8, + 9, 8, 9, 8, 9, 8, 9, 95, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, - 8, 9, 8, 9, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 0, - 0, 5, 0, 0, 0, 0, 0, 0, 0, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 8, 9, 8, 9, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 5, 0, 0, 0, 0, 0, 0, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 0, + 0, 5, 0, 0, 0, 0, 0, 0, 0, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 0, 2, 2, 0, @@ -379,507 +380,510 @@ const uint8_t index2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 97, 5, 5, 5, 5, 5, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, + 2, 2, 2, 98, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 97, 2, 5, 97, 97, - 97, 2, 2, 2, 2, 2, 2, 2, 2, 97, 97, 97, 97, 2, 97, 97, 5, 2, - 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, - 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 5, 5, 5, 5, 5, - 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 0, 2, 97, 97, 0, 5, 5, 5, - 5, 5, 5, 5, 5, 0, 0, 5, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, - 5, 5, 5, 5, 5, 0, 5, 0, 0, 0, 5, 5, 5, 5, 0, 0, 2, 5, - 97, 97, 97, 2, 2, 2, 2, 0, 0, 97, 97, 0, 0, 97, 97, 2, 5, 0, - 0, 0, 0, 0, 0, 0, 0, 97, 0, 0, 0, 0, 5, 5, 0, 5, 5, 5, - 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 97, 0, 5, - 5, 5, 5, 5, 5, 0, 0, 0, 0, 5, 5, 0, 0, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 2, 98, 2, 5, 98, 98, 98, 2, 2, 2, 2, 2, 2, 2, + 2, 98, 98, 98, 98, 2, 98, 98, 5, 2, 2, 2, 2, 2, 2, 2, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 0, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, + 5, 5, 0, 2, 98, 98, 0, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 5, + 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, + 0, 0, 5, 5, 5, 5, 0, 0, 2, 5, 98, 98, 98, 2, 2, 2, 2, 0, + 0, 98, 98, 0, 0, 98, 98, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 98, + 0, 0, 0, 0, 5, 5, 0, 5, 5, 5, 2, 2, 0, 0, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 2, 98, 0, 5, 5, 5, 5, 5, 5, 0, 0, 0, + 0, 5, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 0, + 5, 5, 0, 5, 5, 0, 5, 5, 0, 0, 2, 0, 98, 98, 98, 2, 2, 0, + 0, 0, 0, 2, 2, 0, 0, 2, 2, 2, 0, 0, 0, 2, 0, 0, 0, 0, + 0, 0, 0, 5, 5, 5, 5, 0, 5, 0, 0, 0, 0, 0, 0, 0, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2, 2, 98, 0, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 0, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, + 5, 0, 5, 5, 0, 5, 5, 5, 5, 5, 0, 0, 2, 5, 98, 98, 98, 2, + 2, 2, 2, 2, 0, 2, 2, 98, 0, 98, 98, 2, 0, 0, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 2, 2, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 98, 98, 0, 5, 5, 5, 5, 5, + 5, 5, 5, 0, 0, 5, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, + 5, 5, 5, 0, 5, 5, 0, 5, 5, 5, 5, 5, 0, 0, 2, 5, 98, 2, + 98, 2, 2, 2, 2, 0, 0, 98, 98, 0, 0, 98, 98, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 98, 0, 0, 0, 0, 5, 5, 0, 5, 5, 5, 2, 2, + 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 0, 5, 5, 5, + 5, 5, 5, 0, 0, 0, 5, 5, 5, 0, 5, 5, 5, 5, 0, 0, 0, 5, + 5, 0, 5, 0, 5, 5, 0, 0, 0, 5, 5, 0, 0, 0, 5, 5, 5, 0, + 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, + 98, 98, 2, 98, 98, 0, 0, 0, 98, 98, 98, 0, 98, 98, 98, 2, 0, 0, + 5, 0, 0, 0, 0, 0, 0, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 98, 98, 0, 5, + 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, - 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 0, 5, 5, 0, 5, 5, 0, 0, - 2, 0, 97, 97, 97, 2, 2, 0, 0, 0, 0, 2, 2, 0, 0, 2, 2, 2, - 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 0, 5, 0, - 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 5, 5, 5, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 97, - 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 0, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 0, 0, + 0, 5, 2, 2, 2, 98, 98, 98, 98, 0, 2, 2, 2, 0, 2, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 5, 5, 0, 0, 0, 0, 0, 0, + 5, 5, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 98, + 0, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 0, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 0, 5, 5, 5, 5, 5, - 0, 0, 2, 5, 97, 97, 97, 2, 2, 2, 2, 2, 0, 2, 2, 97, 0, 97, - 97, 2, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 5, 5, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, - 97, 97, 0, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 5, 5, 0, 0, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 0, 5, 5, 5, - 5, 5, 0, 0, 2, 5, 97, 2, 97, 2, 2, 2, 2, 0, 0, 97, 97, 0, - 0, 97, 97, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 97, 0, 0, 0, 0, - 5, 5, 0, 5, 5, 5, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2, 5, 0, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 5, 5, 0, - 5, 5, 5, 5, 0, 0, 0, 5, 5, 0, 5, 0, 5, 5, 0, 0, 0, 5, - 5, 0, 0, 0, 5, 5, 5, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 0, 0, 0, 0, 97, 97, 2, 97, 97, 0, 0, 0, 97, 97, - 97, 0, 97, 97, 97, 2, 0, 0, 5, 0, 0, 0, 0, 0, 0, 97, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 97, 97, 97, 0, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, - 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 0, 5, 5, 5, 5, 5, 0, 0, 0, 5, 2, 2, 2, 97, 97, 97, 97, 0, - 2, 2, 2, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, - 5, 5, 0, 0, 0, 0, 0, 0, 5, 5, 2, 2, 0, 0, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 97, 97, 0, 5, 5, 5, 5, 5, 5, 5, 5, 0, - 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 0, 5, 5, 5, 5, 5, 0, 0, 2, 5, 97, 2, 97, 97, 97, 97, - 97, 0, 2, 97, 97, 0, 97, 97, 2, 2, 0, 0, 0, 0, 0, 0, 0, 97, - 97, 0, 0, 0, 0, 0, 0, 0, 5, 0, 5, 5, 2, 2, 0, 0, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 0, 5, 5, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 97, 97, 0, 5, 5, 5, 5, 5, 5, 5, - 5, 0, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 5, 97, 97, 97, 2, - 2, 2, 2, 0, 97, 97, 97, 0, 97, 97, 97, 2, 5, 0, 0, 0, 0, 0, - 0, 0, 0, 97, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 2, 2, 0, 0, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 5, 5, 5, 5, 5, 5, 0, 0, 97, 97, 0, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 0, - 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 2, 0, 0, 0, 0, 97, 97, 97, - 2, 2, 2, 0, 2, 0, 97, 97, 97, 97, 97, 97, 97, 97, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 97, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, + 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, + 0, 0, 2, 5, 98, 2, 98, 98, 98, 98, 98, 0, 2, 98, 98, 0, 98, 98, + 2, 2, 0, 0, 0, 0, 0, 0, 0, 98, 98, 0, 0, 0, 0, 0, 0, 0, + 5, 0, 5, 5, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 0, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 98, 98, 0, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 2, 5, 5, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, - 0, 0, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 0, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 0, 0, 5, 98, 98, 98, 2, 2, 2, 2, 0, 98, 98, 98, 0, + 98, 98, 98, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 98, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 5, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, + 0, 0, 98, 98, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 0, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 0, + 0, 0, 2, 0, 0, 0, 0, 98, 98, 98, 2, 2, 2, 0, 2, 0, 98, 98, + 98, 98, 98, 98, 98, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 98, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 5, 5, + 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, + 5, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 0, 5, 0, - 0, 5, 5, 0, 5, 0, 0, 5, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, - 0, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 0, 5, 0, 5, 0, 0, - 5, 5, 0, 5, 5, 5, 5, 2, 5, 5, 2, 2, 2, 2, 2, 2, 0, 2, - 2, 5, 0, 0, 5, 5, 5, 5, 5, 0, 5, 0, 2, 2, 2, 2, 2, 2, - 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 5, 5, 0, 0, + 0, 0, 0, 0, 0, 5, 5, 0, 5, 0, 0, 5, 5, 0, 5, 0, 0, 5, + 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, + 0, 5, 5, 5, 0, 5, 0, 5, 0, 0, 5, 5, 0, 5, 5, 5, 5, 2, + 5, 5, 2, 2, 2, 2, 2, 2, 0, 2, 2, 5, 0, 0, 5, 5, 5, 5, + 5, 0, 5, 0, 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 0, 0, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 2, - 0, 0, 0, 0, 97, 97, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, + 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, + 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 0, 2, 0, 2, 0, 0, 0, 0, 98, 98, 5, 5, + 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 97, 2, 2, - 2, 2, 2, 0, 2, 2, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 98, 2, 2, 2, 2, 2, 0, 2, 2, 5, 5, + 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 97, 97, 2, 2, 2, 2, 97, 2, 2, 2, 2, - 2, 2, 97, 2, 2, 97, 97, 2, 2, 5, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 97, 97, 2, 2, - 5, 5, 5, 5, 2, 2, 2, 5, 97, 97, 97, 5, 5, 97, 97, 97, 97, 97, - 97, 97, 5, 5, 5, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 2, 97, 97, 2, 2, 97, 97, 97, 97, 97, 97, 2, 5, 97, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 97, 97, 97, 2, 0, 0, 98, 98, - 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, - 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, - 5, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, - 0, 0, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 5, 5, 5, 5, 0, 0, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 98, + 98, 2, 2, 2, 2, 98, 2, 2, 2, 2, 2, 2, 98, 2, 2, 98, 98, 2, + 2, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 98, 98, 2, 2, 5, 5, 5, 5, 2, 2, 2, 5, + 98, 98, 98, 5, 5, 98, 98, 98, 98, 98, 98, 98, 5, 5, 5, 2, 2, 2, + 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 98, 98, 2, + 2, 98, 98, 98, 98, 98, 98, 2, 5, 98, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 98, 98, 98, 2, 0, 0, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 0, 99, 0, 0, 0, 0, 0, 99, + 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 0, 0, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 0, 0, 5, 5, - 5, 5, 5, 5, 5, 0, 5, 0, 5, 5, 5, 5, 0, 0, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 0, 5, 0, 5, 5, 5, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 0, 5, 5, 5, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, + 5, 5, 5, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 0, 0, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 2, 2, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 5, + 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 2, 2, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, + 5, 5, 5, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, + 98, 2, 2, 2, 2, 2, 2, 2, 98, 98, 98, 98, 98, 98, 98, 98, 2, 98, + 98, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 5, 0, 0, + 0, 0, 5, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 1, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 5, 0, 0, 0, 0, 0, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, + 0, 0, 2, 2, 2, 98, 98, 98, 98, 2, 2, 98, 98, 98, 0, 0, 0, 0, + 98, 98, 2, 98, 98, 98, 98, 98, 98, 2, 2, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 5, 5, 5, 5, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, + 0, 0, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 5, 5, 5, 5, 5, 5, 5, 98, 98, 0, 0, 0, 0, 0, 0, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 98, + 98, 98, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 98, 2, 98, 2, 2, 2, 2, 2, 2, 2, 0, 2, 98, + 2, 98, 98, 2, 2, 2, 2, 2, 2, 2, 2, 98, 98, 98, 98, 98, 98, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, + 98, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 98, 2, 2, 2, 2, + 2, 98, 2, 98, 98, 98, 98, 98, 2, 98, 98, 5, 5, 5, 5, 5, 5, 5, + 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, + 98, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 98, 2, 2, 2, 2, + 98, 98, 2, 2, 98, 2, 98, 98, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 98, 2, 2, 98, 98, 98, 2, + 98, 2, 2, 2, 98, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 98, 98, 98, 98, 98, 98, 98, 98, 2, 2, 2, 2, 2, 2, 2, 2, 98, 98, + 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 0, 0, 0, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 98, + 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 2, 5, 5, 5, 5, 98, 98, + 2, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 100, 5, 5, + 5, 101, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 8, 9, 8, 9, + 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, + 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, + 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, + 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, + 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 5, 5, 5, 5, 5, 102, 5, 5, + 103, 5, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, + 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 104, 104, + 104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104, + 104, 104, 0, 0, 105, 105, 105, 105, 105, 105, 0, 0, 104, 104, 104, 104, 104, 104, + 104, 104, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104, 104, 104, 104, 104, + 105, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104, 104, 104, 0, 0, 105, 105, + 105, 105, 105, 105, 0, 0, 5, 104, 5, 104, 5, 104, 5, 104, 0, 105, 0, 105, + 0, 105, 0, 105, 104, 104, 104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, + 105, 105, 106, 106, 107, 107, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 0, 0, + 104, 104, 104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104, + 104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104, + 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104, 5, 112, 5, 0, + 5, 5, 105, 105, 113, 113, 114, 0, 115, 0, 0, 0, 5, 112, 5, 0, 5, 5, + 116, 116, 116, 116, 114, 0, 0, 0, 104, 104, 5, 5, 0, 0, 5, 5, 105, 105, + 117, 117, 0, 0, 0, 0, 104, 104, 5, 5, 5, 88, 5, 5, 105, 105, 118, 118, + 91, 0, 0, 0, 0, 0, 5, 112, 5, 0, 5, 5, 119, 119, 120, 120, 114, 0, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 93, 93, 93, 93, 2, 93, 93, 93, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 5, 0, 0, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 0, 0, 5, 5, 5, 5, 5, + 0, 0, 0, 0, 0, 0, 5, 0, 121, 0, 5, 0, 122, 123, 5, 5, 0, 5, + 5, 5, 124, 5, 5, 5, 5, 5, 5, 5, 0, 0, 5, 5, 5, 5, 0, 0, + 0, 0, 0, 5, 5, 5, 5, 5, 0, 0, 0, 0, 125, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 5, 5, 5, 8, 9, 5, 5, 5, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 0, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 0, 8, 9, 130, 131, 132, 133, 134, 8, 9, 8, + 9, 8, 9, 135, 136, 137, 138, 5, 8, 9, 5, 8, 9, 5, 5, 5, 5, 5, + 5, 5, 139, 139, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, + 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, + 8, 9, 8, 9, 5, 0, 0, 0, 0, 0, 0, 8, 9, 8, 9, 2, 2, 2, + 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 0, 140, + 0, 0, 0, 0, 0, 140, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, + 5, 0, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 0, + 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, + 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, + 5, 5, 5, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 5, 5, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, + 2, 2, 98, 98, 0, 5, 5, 5, 5, 5, 0, 0, 5, 5, 5, 5, 5, 0, + 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 0, 0, 2, 2, 0, 0, 5, 5, 5, 0, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, - 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 8, 9, 8, 9, 8, 9, + 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, + 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, + 8, 9, 5, 2, 93, 93, 93, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 0, 5, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, + 8, 9, 8, 9, 8, 9, 8, 9, 0, 0, 0, 0, 0, 0, 0, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 0, 0, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 5, 5, + 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, + 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, + 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, + 8, 9, 8, 9, 8, 9, 8, 9, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8, + 9, 8, 9, 100, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 5, 0, 0, 8, + 9, 56, 5, 0, 8, 9, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 57, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 5, 5, 5, 2, 5, 5, 5, + 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 98, 98, 2, 2, 98, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 98, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, + 5, 5, 5, 5, 0, 0, 0, 5, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, + 2, 2, 2, 2, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 98, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 2, 2, 2, 98, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 98, 98, 2, 2, 2, 2, + 98, 98, 2, 98, 98, 98, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 2, 2, 2, 2, 2, 2, 98, 98, 2, 2, 98, 98, 2, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 2, 5, 5, 5, 5, 5, 5, + 5, 5, 2, 98, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 98, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 5, 2, 2, 2, 5, + 5, 2, 2, 5, 5, 5, 5, 5, 2, 2, 5, 2, 5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 5, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 98, + 2, 2, 98, 98, 0, 0, 5, 5, 5, 98, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 5, 5, 5, 5, 5, 0, 0, 5, 5, 5, 5, 5, 5, 0, + 0, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, + 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 98, 98, 2, 98, 98, 2, 98, + 98, 0, 98, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 0, 0, 0, 0, + 0, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 0, 5, 0, + 5, 5, 0, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 0, 0, 0, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 0, 5, 5, 5, 5, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 0, 2, 2, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 0, 0, 97, 2, 2, 2, 2, 2, 2, 2, 97, 97, - 97, 97, 97, 97, 97, 97, 2, 97, 97, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 0, 0, 0, 5, 0, 0, 0, 0, 5, 2, 0, 0, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 2, 2, 2, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, - 5, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 2, 2, 2, 97, 97, 97, 97, 2, - 2, 97, 97, 97, 0, 0, 0, 0, 97, 97, 2, 97, 97, 97, 97, 97, 97, 2, - 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, - 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 5, 5, 5, 5, 5, 5, 5, 97, 97, - 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 2, 2, 97, 97, 97, 0, 0, 0, 0, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 97, 2, 97, 2, 2, - 2, 2, 2, 2, 2, 0, 2, 97, 2, 97, 97, 2, 2, 2, 2, 2, 2, 2, - 2, 97, 97, 97, 97, 97, 97, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, - 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 97, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 97, 2, 2, 2, 2, 2, 97, - 2, 97, 97, 97, 97, 97, 2, 97, 97, 5, 5, 5, 5, 5, 5, 5, 0, 0, - 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 97, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 97, 2, 2, 2, 2, 97, 97, - 2, 2, 97, 0, 0, 0, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 2, 97, 2, 2, 97, 97, 97, 2, 97, 2, - 2, 2, 97, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 97, 97, - 97, 97, 97, 97, 97, 97, 2, 2, 2, 2, 2, 2, 2, 2, 97, 97, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 0, 0, 0, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, - 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 97, 2, 2, - 2, 2, 2, 2, 2, 5, 5, 5, 5, 2, 5, 5, 5, 5, 97, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 99, 5, 5, 5, 100, - 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 8, 9, 8, 9, 8, 9, - 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, - 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, - 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, - 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, - 8, 9, 8, 9, 8, 9, 8, 9, 5, 5, 5, 5, 5, 101, 5, 5, 102, 5, - 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, - 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 103, 103, 103, 103, - 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 104, 103, 103, 103, 103, 103, 103, - 0, 0, 104, 104, 104, 104, 104, 104, 0, 0, 103, 103, 103, 103, 103, 103, 103, 103, - 104, 104, 104, 104, 104, 104, 104, 104, 103, 103, 103, 103, 103, 103, 103, 103, 104, 104, - 104, 104, 104, 104, 104, 104, 103, 103, 103, 103, 103, 103, 0, 0, 104, 104, 104, 104, - 104, 104, 0, 0, 5, 103, 5, 103, 5, 103, 5, 103, 0, 104, 0, 104, 0, 104, - 0, 104, 103, 103, 103, 103, 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 104, - 105, 105, 106, 106, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 0, 0, 103, 103, - 103, 103, 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 104, 103, 103, 103, 103, - 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 104, 103, 103, 103, 103, 103, 103, - 103, 103, 104, 104, 104, 104, 104, 104, 104, 104, 103, 103, 5, 111, 5, 0, 5, 5, - 104, 104, 112, 112, 113, 0, 114, 0, 0, 0, 5, 111, 5, 0, 5, 5, 115, 115, - 115, 115, 113, 0, 0, 0, 103, 103, 5, 5, 0, 0, 5, 5, 104, 104, 116, 116, - 0, 0, 0, 0, 103, 103, 5, 5, 5, 87, 5, 5, 104, 104, 117, 117, 90, 0, - 0, 0, 0, 0, 5, 111, 5, 0, 5, 5, 118, 118, 119, 119, 113, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 92, 92, 92, 92, 2, 92, 92, 92, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 5, 0, 0, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 0, 5, 0, 0, 0, 5, 5, 5, 5, 5, 0, 0, - 0, 0, 0, 0, 5, 0, 120, 0, 5, 0, 121, 122, 5, 5, 0, 5, 5, 5, - 123, 5, 5, 5, 5, 5, 5, 5, 0, 0, 5, 5, 5, 5, 0, 0, 0, 0, - 0, 5, 5, 5, 5, 5, 0, 0, 0, 0, 124, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 125, 125, 125, 125, 125, 125, 125, - 125, 125, 125, 125, 125, 125, 125, 125, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 5, 5, 5, 8, 9, 5, 5, 5, 5, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 0, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 0, 8, 9, 129, 130, 131, 132, 133, 8, 9, 8, 9, 8, - 9, 134, 135, 136, 137, 5, 8, 9, 5, 8, 9, 5, 5, 5, 5, 5, 5, 5, - 138, 138, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, - 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, - 8, 9, 5, 0, 0, 0, 0, 0, 0, 8, 9, 8, 9, 2, 2, 2, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, 139, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 0, - 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, - 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, - 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, - 5, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 5, 5, 5, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, - 2, 2, 0, 5, 5, 5, 5, 5, 0, 0, 5, 5, 5, 5, 5, 0, 0, 0, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 0, 0, 2, 2, 0, 0, 5, 5, 5, 0, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, - 5, 5, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, - 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, - 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 5, 2, 92, 92, 92, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2, 2, 0, 5, 8, 9, 8, 9, 8, 9, 8, 9, - 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 0, 0, - 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 8, 9, 8, 9, 8, 9, 8, 9, - 8, 9, 8, 9, 8, 9, 5, 5, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, - 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, - 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, - 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 8, 9, 8, 9, 99, 8, 9, 8, 9, 8, 9, - 8, 9, 8, 9, 5, 0, 0, 8, 9, 56, 5, 0, 8, 9, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 8, 9, 8, 9, 8, 9, - 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, - 2, 5, 5, 5, 2, 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 97, 97, 2, - 2, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 97, 97, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 2, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0, 0, - 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 97, 97, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 0, 0, 0, 2, 2, 2, 97, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 2, 97, 97, 2, 2, 2, 2, 97, 97, 2, 97, 97, 97, 97, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 97, - 97, 2, 2, 97, 97, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, - 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 2, 97, 0, 0, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, - 0, 0, 5, 97, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 2, 5, 2, 2, 2, 5, 5, 2, 2, 5, 5, 5, 5, 5, 2, 2, - 5, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 0, - 0, 5, 5, 5, 5, 5, 5, 0, 0, 5, 5, 5, 5, 5, 5, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, - 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 97, 97, 2, 97, 97, 2, 97, 97, 0, 97, 2, 0, 0, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, - 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, - 5, 5, 5, 5, 0, 0, 0, 0, 0, 5, 2, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, - 5, 5, 5, 5, 5, 0, 5, 0, 5, 5, 0, 5, 5, 0, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 0, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 0, 0, 0, 0, 2, 0, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 5, 5, 5, - 5, 5, 0, 0, 5, 5, 5, 5, 5, 5, 0, 0, 5, 5, 5, 5, 5, 5, - 0, 0, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, + 0, 0, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, + 0, 2, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 0, 0, 0, 5, 5, 5, 5, 5, 5, 0, 0, 5, 5, 5, 5, + 5, 5, 0, 0, 5, 5, 5, 5, 5, 5, 0, 0, 5, 5, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; } /* namespace unicode */ diff --git a/js/src/vm/UnicodeData.txt b/js/src/vm/UnicodeData.txt index 8d7222b1378..9f204050c6b 100644 --- a/js/src/vm/UnicodeData.txt +++ b/js/src/vm/UnicodeData.txt @@ -165,10 +165,10 @@ 00A4;CURRENCY SIGN;Sc;0;ET;;;;;N;;;;; 00A5;YEN SIGN;Sc;0;ET;;;;;N;;;;; 00A6;BROKEN BAR;So;0;ON;;;;;N;BROKEN VERTICAL BAR;;;; -00A7;SECTION SIGN;So;0;ON;;;;;N;;;;; +00A7;SECTION SIGN;Po;0;ON;;;;;N;;;;; 00A8;DIAERESIS;Sk;0;ON; 0020 0308;;;;N;SPACING DIAERESIS;;;; 00A9;COPYRIGHT SIGN;So;0;ON;;;;;N;;;;; -00AA;FEMININE ORDINAL INDICATOR;Ll;0;L; 0061;;;;N;;;;; +00AA;FEMININE ORDINAL INDICATOR;Lo;0;L; 0061;;;;N;;;;; 00AB;LEFT-POINTING DOUBLE ANGLE QUOTATION MARK;Pi;0;ON;;;;;Y;LEFT POINTING GUILLEMET;;;; 00AC;NOT SIGN;Sm;0;ON;;;;;N;;;;; 00AD;SOFT HYPHEN;Cf;0;BN;;;;;N;;;;; @@ -180,11 +180,11 @@ 00B3;SUPERSCRIPT THREE;No;0;EN; 0033;;3;3;N;SUPERSCRIPT DIGIT THREE;;;; 00B4;ACUTE ACCENT;Sk;0;ON; 0020 0301;;;;N;SPACING ACUTE;;;; 00B5;MICRO SIGN;Ll;0;L; 03BC;;;;N;;;039C;;039C -00B6;PILCROW SIGN;So;0;ON;;;;;N;PARAGRAPH SIGN;;;; +00B6;PILCROW SIGN;Po;0;ON;;;;;N;PARAGRAPH SIGN;;;; 00B7;MIDDLE DOT;Po;0;ON;;;;;N;;;;; 00B8;CEDILLA;Sk;0;ON; 0020 0327;;;;N;SPACING CEDILLA;;;; 00B9;SUPERSCRIPT ONE;No;0;EN; 0031;;1;1;N;SUPERSCRIPT DIGIT ONE;;;; -00BA;MASCULINE ORDINAL INDICATOR;Ll;0;L; 006F;;;;N;;;;; +00BA;MASCULINE ORDINAL INDICATOR;Lo;0;L; 006F;;;;N;;;;; 00BB;RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK;Pf;0;ON;;;;;Y;RIGHT POINTING GUILLEMET;;;; 00BC;VULGAR FRACTION ONE QUARTER;No;0;ON; 0031 2044 0034;;;1/4;N;FRACTION ONE QUARTER;;;; 00BD;VULGAR FRACTION ONE HALF;No;0;ON; 0031 2044 0032;;;1/2;N;FRACTION ONE HALF;;;; @@ -612,7 +612,7 @@ 0263;LATIN SMALL LETTER GAMMA;Ll;0;L;;;;;N;;;0194;;0194 0264;LATIN SMALL LETTER RAMS HORN;Ll;0;L;;;;;N;LATIN SMALL LETTER BABY GAMMA;;;; 0265;LATIN SMALL LETTER TURNED H;Ll;0;L;;;;;N;;;A78D;;A78D -0266;LATIN SMALL LETTER H WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER H HOOK;;;; +0266;LATIN SMALL LETTER H WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER H HOOK;;A7AA;;A7AA 0267;LATIN SMALL LETTER HENG WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER HENG HOOK;;;; 0268;LATIN SMALL LETTER I WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED I;;0197;;0197 0269;LATIN SMALL LETTER IOTA;Ll;0;L;;;;;N;;;0196;;0196 @@ -1394,6 +1394,7 @@ 0587;ARMENIAN SMALL LIGATURE ECH YIWN;Ll;0;L; 0565 0582;;;;N;;;;; 0589;ARMENIAN FULL STOP;Po;0;L;;;;;N;ARMENIAN PERIOD;;;; 058A;ARMENIAN HYPHEN;Pd;0;ON;;;;;N;;;;; +058F;ARMENIAN DRAM SIGN;Sc;0;ET;;;;;N;;;;; 0591;HEBREW ACCENT ETNAHTA;Mn;220;NSM;;;;;N;;;;; 0592;HEBREW ACCENT SEGOL;Mn;230;NSM;;;;;N;;;;; 0593;HEBREW ACCENT SHALSHELET;Mn;230;NSM;;;;;N;;;;; @@ -1485,6 +1486,7 @@ 0601;ARABIC SIGN SANAH;Cf;0;AN;;;;;N;;;;; 0602;ARABIC FOOTNOTE MARKER;Cf;0;AN;;;;;N;;;;; 0603;ARABIC SIGN SAFHA;Cf;0;AN;;;;;N;;;;; +0604;ARABIC SIGN SAMVAT;Cf;0;AN;;;;;N;;;;; 0606;ARABIC-INDIC CUBE ROOT;Sm;0;ON;;;;;N;;;;; 0607;ARABIC-INDIC FOURTH ROOT;Sm;0;ON;;;;;N;;;;; 0608;ARABIC RAY;Sm;0;AL;;;;;N;;;;; @@ -1747,7 +1749,7 @@ 070B;SYRIAC HARKLEAN OBELUS;Po;0;AL;;;;;N;;;;; 070C;SYRIAC HARKLEAN METOBELUS;Po;0;AL;;;;;N;;;;; 070D;SYRIAC HARKLEAN ASTERISCUS;Po;0;AL;;;;;N;;;;; -070F;SYRIAC ABBREVIATION MARK;Cf;0;AN;;;;;N;;;;; +070F;SYRIAC ABBREVIATION MARK;Cf;0;AL;;;;;N;;;;; 0710;SYRIAC LETTER ALAPH;Lo;0;AL;;;;;N;;;;; 0711;SYRIAC LETTER SUPERSCRIPT ALAPH;Mn;36;NSM;;;;;N;;;;; 0712;SYRIAC LETTER BETH;Lo;0;AL;;;;;N;;;;; @@ -2057,6 +2059,45 @@ 085A;MANDAIC VOCALIZATION MARK;Mn;220;NSM;;;;;N;;;;; 085B;MANDAIC GEMINATION MARK;Mn;220;NSM;;;;;N;;;;; 085E;MANDAIC PUNCTUATION;Po;0;R;;;;;N;;;;; +08A0;ARABIC LETTER BEH WITH SMALL V BELOW;Lo;0;AL;;;;;N;;;;; +08A2;ARABIC LETTER JEEM WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +08A3;ARABIC LETTER TAH WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +08A4;ARABIC LETTER FEH WITH DOT BELOW AND THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +08A5;ARABIC LETTER QAF WITH DOT BELOW;Lo;0;AL;;;;;N;;;;; +08A6;ARABIC LETTER LAM WITH DOUBLE BAR;Lo;0;AL;;;;;N;;;;; +08A7;ARABIC LETTER MEEM WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +08A8;ARABIC LETTER YEH WITH TWO DOTS BELOW AND HAMZA ABOVE;Lo;0;AL;;;;;N;;;;; +08A9;ARABIC LETTER YEH WITH TWO DOTS BELOW AND DOT ABOVE;Lo;0;AL;;;;;N;;;;; +08AA;ARABIC LETTER REH WITH LOOP;Lo;0;AL;;;;;N;;;;; +08AB;ARABIC LETTER WAW WITH DOT WITHIN;Lo;0;AL;;;;;N;;;;; +08AC;ARABIC LETTER ROHINGYA YEH;Lo;0;AL;;;;;N;;;;; +08E4;ARABIC CURLY FATHA;Mn;230;NSM;;;;;N;;;;; +08E5;ARABIC CURLY DAMMA;Mn;230;NSM;;;;;N;;;;; +08E6;ARABIC CURLY KASRA;Mn;220;NSM;;;;;N;;;;; +08E7;ARABIC CURLY FATHATAN;Mn;230;NSM;;;;;N;;;;; +08E8;ARABIC CURLY DAMMATAN;Mn;230;NSM;;;;;N;;;;; +08E9;ARABIC CURLY KASRATAN;Mn;220;NSM;;;;;N;;;;; +08EA;ARABIC TONE ONE DOT ABOVE;Mn;230;NSM;;;;;N;;;;; +08EB;ARABIC TONE TWO DOTS ABOVE;Mn;230;NSM;;;;;N;;;;; +08EC;ARABIC TONE LOOP ABOVE;Mn;230;NSM;;;;;N;;;;; +08ED;ARABIC TONE ONE DOT BELOW;Mn;220;NSM;;;;;N;;;;; +08EE;ARABIC TONE TWO DOTS BELOW;Mn;220;NSM;;;;;N;;;;; +08EF;ARABIC TONE LOOP BELOW;Mn;220;NSM;;;;;N;;;;; +08F0;ARABIC OPEN FATHATAN;Mn;27;NSM;;;;;N;;;;; +08F1;ARABIC OPEN DAMMATAN;Mn;28;NSM;;;;;N;;;;; +08F2;ARABIC OPEN KASRATAN;Mn;29;NSM;;;;;N;;;;; +08F3;ARABIC SMALL HIGH WAW;Mn;230;NSM;;;;;N;;;;; +08F4;ARABIC FATHA WITH RING;Mn;230;NSM;;;;;N;;;;; +08F5;ARABIC FATHA WITH DOT ABOVE;Mn;230;NSM;;;;;N;;;;; +08F6;ARABIC KASRA WITH DOT BELOW;Mn;220;NSM;;;;;N;;;;; +08F7;ARABIC LEFT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;; +08F8;ARABIC RIGHT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;; +08F9;ARABIC LEFT ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;; +08FA;ARABIC RIGHT ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;; +08FB;ARABIC DOUBLE RIGHT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;; +08FC;ARABIC DOUBLE RIGHT ARROWHEAD ABOVE WITH DOT;Mn;230;NSM;;;;;N;;;;; +08FD;ARABIC RIGHT ARROWHEAD ABOVE WITH DOT;Mn;230;NSM;;;;;N;;;;; +08FE;ARABIC DAMMA WITH DOT;Mn;230;NSM;;;;;N;;;;; 0900;DEVANAGARI SIGN INVERTED CANDRABINDU;Mn;0;NSM;;;;;N;;;;; 0901;DEVANAGARI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; 0902;DEVANAGARI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; @@ -2437,6 +2478,7 @@ 0AED;GUJARATI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 0AEE;GUJARATI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 0AEF;GUJARATI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0AF0;GUJARATI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; 0AF1;GUJARATI RUPEE SIGN;Sc;0;ET;;;;;N;;;;; 0B01;ORIYA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; 0B02;ORIYA SIGN ANUSVARA;Mc;0;L;;;;;N;;;;; @@ -3109,6 +3151,8 @@ 0ED9;LAO DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 0EDC;LAO HO NO;Lo;0;L; 0EAB 0E99;;;;N;;;;; 0EDD;LAO HO MO;Lo;0;L; 0EAB 0EA1;;;;N;;;;; +0EDE;LAO LETTER KHMU GO;Lo;0;L;;;;;N;;;;; +0EDF;LAO LETTER KHMU NYO;Lo;0;L;;;;;N;;;;; 0F00;TIBETAN SYLLABLE OM;Lo;0;L;;;;;N;;;;; 0F01;TIBETAN MARK GTER YIG MGO TRUNCATED A;So;0;L;;;;;N;;;;; 0F02;TIBETAN MARK GTER YIG MGO -UM RNAM BCAD MA;So;0;L;;;;;N;;;;; @@ -3129,7 +3173,7 @@ 0F11;TIBETAN MARK RIN CHEN SPUNGS SHAD;Po;0;L;;;;;N;TIBETAN RINCHANPHUNGSHAD;;;; 0F12;TIBETAN MARK RGYA GRAM SHAD;Po;0;L;;;;;N;;;;; 0F13;TIBETAN MARK CARET -DZUD RTAGS ME LONG CAN;So;0;L;;;;;N;;;;; -0F14;TIBETAN MARK GTER TSHEG;So;0;L;;;;;N;TIBETAN COMMA;;;; +0F14;TIBETAN MARK GTER TSHEG;Po;0;L;;;;;N;TIBETAN COMMA;;;; 0F15;TIBETAN LOGOTYPE SIGN CHAD RTAGS;So;0;L;;;;;N;;;;; 0F16;TIBETAN LOGOTYPE SIGN LHAG RTAGS;So;0;L;;;;;N;;;;; 0F17;TIBETAN ASTROLOGICAL SIGN SGRA GCAN -CHAR RTAGS;So;0;L;;;;;N;;;;; @@ -3518,6 +3562,8 @@ 10C3;GEORGIAN CAPITAL LETTER WE;Lu;0;L;;;;;N;;;;2D23; 10C4;GEORGIAN CAPITAL LETTER HAR;Lu;0;L;;;;;N;;;;2D24; 10C5;GEORGIAN CAPITAL LETTER HOE;Lu;0;L;;;;;N;;;;2D25; +10C7;GEORGIAN CAPITAL LETTER YN;Lu;0;L;;;;;N;;;;2D27; +10CD;GEORGIAN CAPITAL LETTER AEN;Lu;0;L;;;;;N;;;;2D2D; 10D0;GEORGIAN LETTER AN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER AN;;;; 10D1;GEORGIAN LETTER BAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER BAN;;;; 10D2;GEORGIAN LETTER GAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER GAN;;;; @@ -3563,6 +3609,9 @@ 10FA;GEORGIAN LETTER AIN;Lo;0;L;;;;;N;;;;; 10FB;GEORGIAN PARAGRAPH SEPARATOR;Po;0;L;;;;;N;;;;; 10FC;MODIFIER LETTER GEORGIAN NAR;Lm;0;L; 10DC;;;;N;;;;; +10FD;GEORGIAN LETTER AEN;Lo;0;L;;;;;N;;;;; +10FE;GEORGIAN LETTER HARD SIGN;Lo;0;L;;;;;N;;;;; +10FF;GEORGIAN LETTER LABIAL SIGN;Lo;0;L;;;;;N;;;;; 1100;HANGUL CHOSEONG KIYEOK;Lo;0;L;;;;;N;;;;; 1101;HANGUL CHOSEONG SSANGKIYEOK;Lo;0;L;;;;;N;;;;; 1102;HANGUL CHOSEONG NIEUN;Lo;0;L;;;;;N;;;;; @@ -4148,7 +4197,7 @@ 135D;ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK;Mn;230;NSM;;;;;N;;;;; 135E;ETHIOPIC COMBINING VOWEL LENGTH MARK;Mn;230;NSM;;;;;N;;;;; 135F;ETHIOPIC COMBINING GEMINATION MARK;Mn;230;NSM;;;;;N;;;;; -1360;ETHIOPIC SECTION MARK;So;0;L;;;;;N;;;;; +1360;ETHIOPIC SECTION MARK;Po;0;L;;;;;N;;;;; 1361;ETHIOPIC WORDSPACE;Po;0;L;;;;;N;;;;; 1362;ETHIOPIC FULL STOP;Po;0;L;;;;;N;;;;; 1363;ETHIOPIC COMMA;Po;0;L;;;;;N;;;;; @@ -5171,8 +5220,8 @@ 17B1;KHMER INDEPENDENT VOWEL QOO TYPE ONE;Lo;0;L;;;;;N;;;;; 17B2;KHMER INDEPENDENT VOWEL QOO TYPE TWO;Lo;0;L;;;;;N;;;;; 17B3;KHMER INDEPENDENT VOWEL QAU;Lo;0;L;;;;;N;;;;; -17B4;KHMER VOWEL INHERENT AQ;Cf;0;L;;;;;N;;;;; -17B5;KHMER VOWEL INHERENT AA;Cf;0;L;;;;;N;;;;; +17B4;KHMER VOWEL INHERENT AQ;Mn;0;NSM;;;;;N;;;;; +17B5;KHMER VOWEL INHERENT AA;Mn;0;NSM;;;;;N;;;;; 17B6;KHMER VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; 17B7;KHMER VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; 17B8;KHMER VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; @@ -5996,6 +6045,9 @@ 1BA8;SUNDANESE VOWEL SIGN PAMEPET;Mn;0;NSM;;;;;N;;;;; 1BA9;SUNDANESE VOWEL SIGN PANEULEUNG;Mn;0;NSM;;;;;N;;;;; 1BAA;SUNDANESE SIGN PAMAAEH;Mc;9;L;;;;;N;;;;; +1BAB;SUNDANESE SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +1BAC;SUNDANESE CONSONANT SIGN PASANGAN MA;Mc;0;L;;;;;N;;;;; +1BAD;SUNDANESE CONSONANT SIGN PASANGAN WA;Mc;0;L;;;;;N;;;;; 1BAE;SUNDANESE LETTER KHA;Lo;0;L;;;;;N;;;;; 1BAF;SUNDANESE LETTER SYA;Lo;0;L;;;;;N;;;;; 1BB0;SUNDANESE DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; @@ -6008,6 +6060,12 @@ 1BB7;SUNDANESE DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; 1BB8;SUNDANESE DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; 1BB9;SUNDANESE DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +1BBA;SUNDANESE AVAGRAHA;Lo;0;L;;;;;N;;;;; +1BBB;SUNDANESE LETTER REU;Lo;0;L;;;;;N;;;;; +1BBC;SUNDANESE LETTER LEU;Lo;0;L;;;;;N;;;;; +1BBD;SUNDANESE LETTER BHA;Lo;0;L;;;;;N;;;;; +1BBE;SUNDANESE LETTER FINAL K;Lo;0;L;;;;;N;;;;; +1BBF;SUNDANESE LETTER FINAL M;Lo;0;L;;;;;N;;;;; 1BC0;BATAK LETTER A;Lo;0;L;;;;;N;;;;; 1BC1;BATAK LETTER SIMALUNGUN A;Lo;0;L;;;;;N;;;;; 1BC2;BATAK LETTER HA;Lo;0;L;;;;;N;;;;; @@ -6186,6 +6244,14 @@ 1C7D;OL CHIKI AHAD;Lm;0;L;;;;;N;;;;; 1C7E;OL CHIKI PUNCTUATION MUCAAD;Po;0;L;;;;;N;;;;; 1C7F;OL CHIKI PUNCTUATION DOUBLE MUCAAD;Po;0;L;;;;;N;;;;; +1CC0;SUNDANESE PUNCTUATION BINDU SURYA;Po;0;L;;;;;N;;;;; +1CC1;SUNDANESE PUNCTUATION BINDU PANGLONG;Po;0;L;;;;;N;;;;; +1CC2;SUNDANESE PUNCTUATION BINDU PURNAMA;Po;0;L;;;;;N;;;;; +1CC3;SUNDANESE PUNCTUATION BINDU CAKRA;Po;0;L;;;;;N;;;;; +1CC4;SUNDANESE PUNCTUATION BINDU LEU SATANGA;Po;0;L;;;;;N;;;;; +1CC5;SUNDANESE PUNCTUATION BINDU KA SATANGA;Po;0;L;;;;;N;;;;; +1CC6;SUNDANESE PUNCTUATION BINDU DA SATANGA;Po;0;L;;;;;N;;;;; +1CC7;SUNDANESE PUNCTUATION BINDU BA SATANGA;Po;0;L;;;;;N;;;;; 1CD0;VEDIC TONE KARSHANA;Mn;230;NSM;;;;;N;;;;; 1CD1;VEDIC TONE SHARA;Mn;230;NSM;;;;;N;;;;; 1CD2;VEDIC TONE PRENKHA;Mn;230;NSM;;;;;N;;;;; @@ -6221,6 +6287,10 @@ 1CF0;VEDIC SIGN RTHANG LONG ANUSVARA;Lo;0;L;;;;;N;;;;; 1CF1;VEDIC SIGN ANUSVARA UBHAYATO MUKHA;Lo;0;L;;;;;N;;;;; 1CF2;VEDIC SIGN ARDHAVISARGA;Mc;0;L;;;;;N;;;;; +1CF3;VEDIC SIGN ROTATED ARDHAVISARGA;Mc;0;L;;;;;N;;;;; +1CF4;VEDIC TONE CANDRA ABOVE;Mn;230;NSM;;;;;N;;;;; +1CF5;VEDIC SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;; +1CF6;VEDIC SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;; 1D00;LATIN LETTER SMALL CAPITAL A;Ll;0;L;;;;;N;;;;; 1D01;LATIN LETTER SMALL CAPITAL AE;Ll;0;L;;;;;N;;;;; 1D02;LATIN SMALL LETTER TURNED AE;Ll;0;L;;;;;N;;;;; @@ -6319,15 +6389,15 @@ 1D5F;MODIFIER LETTER SMALL DELTA;Lm;0;L; 03B4;;;;N;;;;; 1D60;MODIFIER LETTER SMALL GREEK PHI;Lm;0;L; 03C6;;;;N;;;;; 1D61;MODIFIER LETTER SMALL CHI;Lm;0;L; 03C7;;;;N;;;;; -1D62;LATIN SUBSCRIPT SMALL LETTER I;Ll;0;L; 0069;;;;N;;;;; -1D63;LATIN SUBSCRIPT SMALL LETTER R;Ll;0;L; 0072;;;;N;;;;; -1D64;LATIN SUBSCRIPT SMALL LETTER U;Ll;0;L; 0075;;;;N;;;;; -1D65;LATIN SUBSCRIPT SMALL LETTER V;Ll;0;L; 0076;;;;N;;;;; -1D66;GREEK SUBSCRIPT SMALL LETTER BETA;Ll;0;L; 03B2;;;;N;;;;; -1D67;GREEK SUBSCRIPT SMALL LETTER GAMMA;Ll;0;L; 03B3;;;;N;;;;; -1D68;GREEK SUBSCRIPT SMALL LETTER RHO;Ll;0;L; 03C1;;;;N;;;;; -1D69;GREEK SUBSCRIPT SMALL LETTER PHI;Ll;0;L; 03C6;;;;N;;;;; -1D6A;GREEK SUBSCRIPT SMALL LETTER CHI;Ll;0;L; 03C7;;;;N;;;;; +1D62;LATIN SUBSCRIPT SMALL LETTER I;Lm;0;L; 0069;;;;N;;;;; +1D63;LATIN SUBSCRIPT SMALL LETTER R;Lm;0;L; 0072;;;;N;;;;; +1D64;LATIN SUBSCRIPT SMALL LETTER U;Lm;0;L; 0075;;;;N;;;;; +1D65;LATIN SUBSCRIPT SMALL LETTER V;Lm;0;L; 0076;;;;N;;;;; +1D66;GREEK SUBSCRIPT SMALL LETTER BETA;Lm;0;L; 03B2;;;;N;;;;; +1D67;GREEK SUBSCRIPT SMALL LETTER GAMMA;Lm;0;L; 03B3;;;;N;;;;; +1D68;GREEK SUBSCRIPT SMALL LETTER RHO;Lm;0;L; 03C1;;;;N;;;;; +1D69;GREEK SUBSCRIPT SMALL LETTER PHI;Lm;0;L; 03C6;;;;N;;;;; +1D6A;GREEK SUBSCRIPT SMALL LETTER CHI;Lm;0;L; 03C7;;;;N;;;;; 1D6B;LATIN SMALL LETTER UE;Ll;0;L;;;;;N;;;;; 1D6C;LATIN SMALL LETTER B WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; 1D6D;LATIN SMALL LETTER D WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; @@ -8827,7 +8897,9 @@ 27C8;REVERSE SOLIDUS PRECEDING SUBSET;Sm;0;ON;;;;;Y;;;;; 27C9;SUPERSET PRECEDING SOLIDUS;Sm;0;ON;;;;;Y;;;;; 27CA;VERTICAL BAR WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;; +27CB;MATHEMATICAL RISING DIAGONAL;Sm;0;ON;;;;;Y;;;;; 27CC;LONG DIVISION;Sm;0;ON;;;;;Y;;;;; +27CD;MATHEMATICAL FALLING DIAGONAL;Sm;0;ON;;;;;Y;;;;; 27CE;SQUARED LOGICAL AND;Sm;0;ON;;;;;N;;;;; 27CF;SQUARED LOGICAL OR;Sm;0;ON;;;;;N;;;;; 27D0;WHITE DIAMOND WITH CENTRED DOT;Sm;0;ON;;;;;N;;;;; @@ -9855,7 +9927,7 @@ 2C79;LATIN SMALL LETTER TURNED R WITH TAIL;Ll;0;L;;;;;N;;;;; 2C7A;LATIN SMALL LETTER O WITH LOW RING INSIDE;Ll;0;L;;;;;N;;;;; 2C7B;LATIN LETTER SMALL CAPITAL TURNED E;Ll;0;L;;;;;N;;;;; -2C7C;LATIN SUBSCRIPT SMALL LETTER J;Ll;0;L; 006A;;;;N;;;;; +2C7C;LATIN SUBSCRIPT SMALL LETTER J;Lm;0;L; 006A;;;;N;;;;; 2C7D;MODIFIER LETTER CAPITAL V;Lm;0;L; 0056;;;;N;;;;; 2C7E;LATIN CAPITAL LETTER S WITH SWASH TAIL;Lu;0;L;;;;;N;;;;023F; 2C7F;LATIN CAPITAL LETTER Z WITH SWASH TAIL;Lu;0;L;;;;;N;;;;0240; @@ -9973,6 +10045,8 @@ 2CEF;COPTIC COMBINING NI ABOVE;Mn;230;NSM;;;;;N;;;;; 2CF0;COPTIC COMBINING SPIRITUS ASPER;Mn;230;NSM;;;;;N;;;;; 2CF1;COPTIC COMBINING SPIRITUS LENIS;Mn;230;NSM;;;;;N;;;;; +2CF2;COPTIC CAPITAL LETTER BOHAIRIC KHEI;Lu;0;L;;;;;N;;;;2CF3; +2CF3;COPTIC SMALL LETTER BOHAIRIC KHEI;Ll;0;L;;;;;N;;;2CF2;;2CF2 2CF9;COPTIC OLD NUBIAN FULL STOP;Po;0;ON;;;;;N;;;;; 2CFA;COPTIC OLD NUBIAN DIRECT QUESTION MARK;Po;0;ON;;;;;N;;;;; 2CFB;COPTIC OLD NUBIAN INDIRECT QUESTION MARK;Po;0;ON;;;;;N;;;;; @@ -10018,6 +10092,8 @@ 2D23;GEORGIAN SMALL LETTER WE;Ll;0;L;;;;;N;;;10C3;;10C3 2D24;GEORGIAN SMALL LETTER HAR;Ll;0;L;;;;;N;;;10C4;;10C4 2D25;GEORGIAN SMALL LETTER HOE;Ll;0;L;;;;;N;;;10C5;;10C5 +2D27;GEORGIAN SMALL LETTER YN;Ll;0;L;;;;;N;;;10C7;;10C7 +2D2D;GEORGIAN SMALL LETTER AEN;Ll;0;L;;;;;N;;;10CD;;10CD 2D30;TIFINAGH LETTER YA;Lo;0;L;;;;;N;;;;; 2D31;TIFINAGH LETTER YAB;Lo;0;L;;;;;N;;;;; 2D32;TIFINAGH LETTER YABH;Lo;0;L;;;;;N;;;;; @@ -10072,6 +10148,8 @@ 2D63;TIFINAGH LETTER YAZ;Lo;0;L;;;;;N;;;;; 2D64;TIFINAGH LETTER TAWELLEMET YAZ;Lo;0;L;;;;;N;;;;; 2D65;TIFINAGH LETTER YAZZ;Lo;0;L;;;;;N;;;;; +2D66;TIFINAGH LETTER YE;Lo;0;L;;;;;N;;;;; +2D67;TIFINAGH LETTER YO;Lo;0;L;;;;;N;;;;; 2D6F;TIFINAGH MODIFIER LETTER LABIALIZATION MARK;Lm;0;L; 2D61;;;;N;;;;; 2D70;TIFINAGH SEPARATOR MARK;Po;0;L;;;;;N;;;;; 2D7F;TIFINAGH CONSONANT JOINER;Mn;9;NSM;;;;;N;;;;; @@ -10236,6 +10314,16 @@ 2E2F;VERTICAL TILDE;Lm;0;ON;;;;;N;;;;; 2E30;RING POINT;Po;0;ON;;;;;N;;;;; 2E31;WORD SEPARATOR MIDDLE DOT;Po;0;ON;;;;;N;;;;; +2E32;TURNED COMMA;Po;0;ON;;;;;N;;;;; +2E33;RAISED DOT;Po;0;ON;;;;;N;;;;; +2E34;RAISED COMMA;Po;0;ON;;;;;N;;;;; +2E35;TURNED SEMICOLON;Po;0;ON;;;;;N;;;;; +2E36;DAGGER WITH LEFT GUARD;Po;0;ON;;;;;N;;;;; +2E37;DAGGER WITH RIGHT GUARD;Po;0;ON;;;;;N;;;;; +2E38;TURNED DAGGER;Po;0;ON;;;;;N;;;;; +2E39;TOP HALF SECTION SIGN;Po;0;ON;;;;;N;;;;; +2E3A;TWO-EM DASH;Pd;0;ON;;;;;N;;;;; +2E3B;THREE-EM DASH;Pd;0;ON;;;;;N;;;;; 2E80;CJK RADICAL REPEAT;So;0;ON;;;;;N;;;;; 2E81;CJK RADICAL CLIFF;So;0;ON;;;;;N;;;;; 2E82;CJK RADICAL SECOND ONE;So;0;ON;;;;;N;;;;; @@ -10623,8 +10711,8 @@ 302B;IDEOGRAPHIC RISING TONE MARK;Mn;228;NSM;;;;;N;;;;; 302C;IDEOGRAPHIC DEPARTING TONE MARK;Mn;232;NSM;;;;;N;;;;; 302D;IDEOGRAPHIC ENTERING TONE MARK;Mn;222;NSM;;;;;N;;;;; -302E;HANGUL SINGLE DOT TONE MARK;Mn;224;NSM;;;;;N;;;;; -302F;HANGUL DOUBLE DOT TONE MARK;Mn;224;NSM;;;;;N;;;;; +302E;HANGUL SINGLE DOT TONE MARK;Mc;224;L;;;;;N;;;;; +302F;HANGUL DOUBLE DOT TONE MARK;Mc;224;L;;;;;N;;;;; 3030;WAVY DASH;Pd;0;ON;;;;;N;;;;; 3031;VERTICAL KANA REPEAT MARK;Lm;0;L;;;;;N;;;;; 3032;VERTICAL KANA REPEAT WITH VOICED SOUND MARK;Lm;0;L;;;;;N;;;;; @@ -11131,14 +11219,14 @@ 3245;CIRCLED IDEOGRAPH KINDERGARTEN;So;0;L; 5E7C;;;;N;;;;; 3246;CIRCLED IDEOGRAPH SCHOOL;So;0;L; 6587;;;;N;;;;; 3247;CIRCLED IDEOGRAPH KOTO;So;0;L; 7B8F;;;;N;;;;; -3248;CIRCLED NUMBER TEN ON BLACK SQUARE;So;0;L;;;;;N;;;;; -3249;CIRCLED NUMBER TWENTY ON BLACK SQUARE;So;0;L;;;;;N;;;;; -324A;CIRCLED NUMBER THIRTY ON BLACK SQUARE;So;0;L;;;;;N;;;;; -324B;CIRCLED NUMBER FORTY ON BLACK SQUARE;So;0;L;;;;;N;;;;; -324C;CIRCLED NUMBER FIFTY ON BLACK SQUARE;So;0;L;;;;;N;;;;; -324D;CIRCLED NUMBER SIXTY ON BLACK SQUARE;So;0;L;;;;;N;;;;; -324E;CIRCLED NUMBER SEVENTY ON BLACK SQUARE;So;0;L;;;;;N;;;;; -324F;CIRCLED NUMBER EIGHTY ON BLACK SQUARE;So;0;L;;;;;N;;;;; +3248;CIRCLED NUMBER TEN ON BLACK SQUARE;No;0;L;;;;10;N;;;;; +3249;CIRCLED NUMBER TWENTY ON BLACK SQUARE;No;0;L;;;;20;N;;;;; +324A;CIRCLED NUMBER THIRTY ON BLACK SQUARE;No;0;L;;;;30;N;;;;; +324B;CIRCLED NUMBER FORTY ON BLACK SQUARE;No;0;L;;;;40;N;;;;; +324C;CIRCLED NUMBER FIFTY ON BLACK SQUARE;No;0;L;;;;50;N;;;;; +324D;CIRCLED NUMBER SIXTY ON BLACK SQUARE;No;0;L;;;;60;N;;;;; +324E;CIRCLED NUMBER SEVENTY ON BLACK SQUARE;No;0;L;;;;70;N;;;;; +324F;CIRCLED NUMBER EIGHTY ON BLACK SQUARE;No;0;L;;;;80;N;;;;; 3250;PARTNERSHIP SIGN;So;0;ON; 0050 0054 0045;;;;N;;;;; 3251;CIRCLED NUMBER TWENTY ONE;No;0;ON; 0032 0031;;;21;N;;;;; 3252;CIRCLED NUMBER TWENTY TWO;No;0;ON; 0032 0032;;;22;N;;;;; @@ -11637,7 +11725,7 @@ 4DFE;HEXAGRAM FOR AFTER COMPLETION;So;0;ON;;;;;N;;;;; 4DFF;HEXAGRAM FOR BEFORE COMPLETION;So;0;ON;;;;;N;;;;; 4E00;;Lo;0;L;;;;;N;;;;; -9FCB;;Lo;0;L;;;;;N;;;;; +9FCC;;Lo;0;L;;;;;N;;;;; A000;YI SYLLABLE IT;Lo;0;L;;;;;N;;;;; A001;YI SYLLABLE IX;Lo;0;L;;;;;N;;;;; A002;YI SYLLABLE I;Lo;0;L;;;;;N;;;;; @@ -13258,6 +13346,14 @@ A670;COMBINING CYRILLIC TEN MILLIONS SIGN;Me;0;NSM;;;;;N;;;;; A671;COMBINING CYRILLIC HUNDRED MILLIONS SIGN;Me;0;NSM;;;;;N;;;;; A672;COMBINING CYRILLIC THOUSAND MILLIONS SIGN;Me;0;NSM;;;;;N;;;;; A673;SLAVONIC ASTERISK;Po;0;ON;;;;;N;;;;; +A674;COMBINING CYRILLIC LETTER UKRAINIAN IE;Mn;230;NSM;;;;;N;;;;; +A675;COMBINING CYRILLIC LETTER I;Mn;230;NSM;;;;;N;;;;; +A676;COMBINING CYRILLIC LETTER YI;Mn;230;NSM;;;;;N;;;;; +A677;COMBINING CYRILLIC LETTER U;Mn;230;NSM;;;;;N;;;;; +A678;COMBINING CYRILLIC LETTER HARD SIGN;Mn;230;NSM;;;;;N;;;;; +A679;COMBINING CYRILLIC LETTER YERU;Mn;230;NSM;;;;;N;;;;; +A67A;COMBINING CYRILLIC LETTER SOFT SIGN;Mn;230;NSM;;;;;N;;;;; +A67B;COMBINING CYRILLIC LETTER OMEGA;Mn;230;NSM;;;;;N;;;;; A67C;COMBINING CYRILLIC KAVYKA;Mn;230;NSM;;;;;N;;;;; A67D;COMBINING CYRILLIC PAYEROK;Mn;230;NSM;;;;;N;;;;; A67E;CYRILLIC KAVYKA;Po;0;ON;;;;;N;;;;; @@ -13286,6 +13382,7 @@ A694;CYRILLIC CAPITAL LETTER HWE;Lu;0;L;;;;;N;;;;A695; A695;CYRILLIC SMALL LETTER HWE;Ll;0;L;;;;;N;;;A694;;A694 A696;CYRILLIC CAPITAL LETTER SHWE;Lu;0;L;;;;;N;;;;A697; A697;CYRILLIC SMALL LETTER SHWE;Ll;0;L;;;;;N;;;A696;;A696 +A69F;COMBINING CYRILLIC LETTER IOTIFIED E;Mn;230;NSM;;;;;N;;;;; A6A0;BAMUM LETTER A;Lo;0;L;;;;;N;;;;; A6A1;BAMUM LETTER KA;Lo;0;L;;;;;N;;;;; A6A2;BAMUM LETTER U;Lo;0;L;;;;;N;;;;; @@ -13519,6 +13616,8 @@ A78D;LATIN CAPITAL LETTER TURNED H;Lu;0;L;;;;;N;;;;0265; A78E;LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT;Ll;0;L;;;;;N;;;;; A790;LATIN CAPITAL LETTER N WITH DESCENDER;Lu;0;L;;;;;N;;;;A791; A791;LATIN SMALL LETTER N WITH DESCENDER;Ll;0;L;;;;;N;;;A790;;A790 +A792;LATIN CAPITAL LETTER C WITH BAR;Lu;0;L;;;;;N;;;;A793; +A793;LATIN SMALL LETTER C WITH BAR;Ll;0;L;;;;;N;;;A792;;A792 A7A0;LATIN CAPITAL LETTER G WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A1; A7A1;LATIN SMALL LETTER G WITH OBLIQUE STROKE;Ll;0;L;;;;;N;;;A7A0;;A7A0 A7A2;LATIN CAPITAL LETTER K WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A3; @@ -13529,6 +13628,9 @@ A7A6;LATIN CAPITAL LETTER R WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A7; A7A7;LATIN SMALL LETTER R WITH OBLIQUE STROKE;Ll;0;L;;;;;N;;;A7A6;;A7A6 A7A8;LATIN CAPITAL LETTER S WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A9; A7A9;LATIN SMALL LETTER S WITH OBLIQUE STROKE;Ll;0;L;;;;;N;;;A7A8;;A7A8 +A7AA;LATIN CAPITAL LETTER H WITH HOOK;Lu;0;L;;;;;N;;;;0266; +A7F8;MODIFIER LETTER CAPITAL H WITH STROKE;Lm;0;L; 0126;;;;N;;;;; +A7F9;MODIFIER LETTER SMALL LIGATURE OE;Lm;0;L; 0153;;;;N;;;;; A7FA;LATIN LETTER SMALL CAPITAL TURNED M;Ll;0;L;;;;;N;;;;; A7FB;LATIN EPIGRAPHIC LETTER REVERSED F;Lo;0;L;;;;;N;;;;; A7FC;LATIN EPIGRAPHIC LETTER REVERSED P;Lo;0;L;;;;;N;;;;; @@ -14142,6 +14244,29 @@ AADC;TAI VIET SYMBOL NUENG;Lo;0;L;;;;;N;;;;; AADD;TAI VIET SYMBOL SAM;Lm;0;L;;;;;N;;;;; AADE;TAI VIET SYMBOL HO HOI;Po;0;L;;;;;N;;;;; AADF;TAI VIET SYMBOL KOI KOI;Po;0;L;;;;;N;;;;; +AAE0;MEETEI MAYEK LETTER E;Lo;0;L;;;;;N;;;;; +AAE1;MEETEI MAYEK LETTER O;Lo;0;L;;;;;N;;;;; +AAE2;MEETEI MAYEK LETTER CHA;Lo;0;L;;;;;N;;;;; +AAE3;MEETEI MAYEK LETTER NYA;Lo;0;L;;;;;N;;;;; +AAE4;MEETEI MAYEK LETTER TTA;Lo;0;L;;;;;N;;;;; +AAE5;MEETEI MAYEK LETTER TTHA;Lo;0;L;;;;;N;;;;; +AAE6;MEETEI MAYEK LETTER DDA;Lo;0;L;;;;;N;;;;; +AAE7;MEETEI MAYEK LETTER DDHA;Lo;0;L;;;;;N;;;;; +AAE8;MEETEI MAYEK LETTER NNA;Lo;0;L;;;;;N;;;;; +AAE9;MEETEI MAYEK LETTER SHA;Lo;0;L;;;;;N;;;;; +AAEA;MEETEI MAYEK LETTER SSA;Lo;0;L;;;;;N;;;;; +AAEB;MEETEI MAYEK VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +AAEC;MEETEI MAYEK VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +AAED;MEETEI MAYEK VOWEL SIGN AAI;Mn;0;NSM;;;;;N;;;;; +AAEE;MEETEI MAYEK VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; +AAEF;MEETEI MAYEK VOWEL SIGN AAU;Mc;0;L;;;;;N;;;;; +AAF0;MEETEI MAYEK CHEIKHAN;Po;0;L;;;;;N;;;;; +AAF1;MEETEI MAYEK AHANG KHUDAM;Po;0;L;;;;;N;;;;; +AAF2;MEETEI MAYEK ANJI;Lo;0;L;;;;;N;;;;; +AAF3;MEETEI MAYEK SYLLABLE REPETITION MARK;Lm;0;L;;;;;N;;;;; +AAF4;MEETEI MAYEK WORD REPETITION MARK;Lm;0;L;;;;;N;;;;; +AAF5;MEETEI MAYEK VOWEL SIGN VISARGA;Mc;0;L;;;;;N;;;;; +AAF6;MEETEI MAYEK VIRAMA;Mn;9;NSM;;;;;N;;;;; AB01;ETHIOPIC SYLLABLE TTHU;Lo;0;L;;;;;N;;;;; AB02;ETHIOPIC SYLLABLE TTHI;Lo;0;L;;;;;N;;;;; AB03;ETHIOPIC SYLLABLE TTHAA;Lo;0;L;;;;;N;;;;; @@ -14614,6 +14739,8 @@ FA2A;CJK COMPATIBILITY IDEOGRAPH-FA2A;Lo;0;L;98EF;;;;N;;;;; FA2B;CJK COMPATIBILITY IDEOGRAPH-FA2B;Lo;0;L;98FC;;;;N;;;;; FA2C;CJK COMPATIBILITY IDEOGRAPH-FA2C;Lo;0;L;9928;;;;N;;;;; FA2D;CJK COMPATIBILITY IDEOGRAPH-FA2D;Lo;0;L;9DB4;;;;N;;;;; +FA2E;CJK COMPATIBILITY IDEOGRAPH-FA2E;Lo;0;L;90DE;;;;N;;;;; +FA2F;CJK COMPATIBILITY IDEOGRAPH-FA2F;Lo;0;L;96B7;;;;N;;;;; FA30;CJK COMPATIBILITY IDEOGRAPH-FA30;Lo;0;L;4FAE;;;;N;;;;; FA31;CJK COMPATIBILITY IDEOGRAPH-FA31;Lo;0;L;50E7;;;;N;;;;; FA32;CJK COMPATIBILITY IDEOGRAPH-FA32;Lo;0;L;514D;;;;N;;;;; @@ -16126,7 +16253,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 100FA;LINEAR B IDEOGRAM VESSEL B305;Lo;0;L;;;;;N;;;;; 10100;AEGEAN WORD SEPARATOR LINE;Po;0;L;;;;;N;;;;; 10101;AEGEAN WORD SEPARATOR DOT;Po;0;ON;;;;;N;;;;; -10102;AEGEAN CHECK MARK;So;0;L;;;;;N;;;;; +10102;AEGEAN CHECK MARK;Po;0;L;;;;;N;;;;; 10107;AEGEAN NUMBER ONE;No;0;L;;;;1;N;;;;; 10108;AEGEAN NUMBER TWO;No;0;L;;;;2;N;;;;; 10109;AEGEAN NUMBER THREE;No;0;L;;;;3;N;;;;; @@ -16845,6 +16972,64 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 10938;LYDIAN LETTER NN;Lo;0;R;;;;;N;;;;; 10939;LYDIAN LETTER C;Lo;0;R;;;;;N;;;;; 1093F;LYDIAN TRIANGULAR MARK;Po;0;R;;;;;N;;;;; +10980;MEROITIC HIEROGLYPHIC LETTER A;Lo;0;R;;;;;N;;;;; +10981;MEROITIC HIEROGLYPHIC LETTER E;Lo;0;R;;;;;N;;;;; +10982;MEROITIC HIEROGLYPHIC LETTER I;Lo;0;R;;;;;N;;;;; +10983;MEROITIC HIEROGLYPHIC LETTER O;Lo;0;R;;;;;N;;;;; +10984;MEROITIC HIEROGLYPHIC LETTER YA;Lo;0;R;;;;;N;;;;; +10985;MEROITIC HIEROGLYPHIC LETTER WA;Lo;0;R;;;;;N;;;;; +10986;MEROITIC HIEROGLYPHIC LETTER BA;Lo;0;R;;;;;N;;;;; +10987;MEROITIC HIEROGLYPHIC LETTER BA-2;Lo;0;R;;;;;N;;;;; +10988;MEROITIC HIEROGLYPHIC LETTER PA;Lo;0;R;;;;;N;;;;; +10989;MEROITIC HIEROGLYPHIC LETTER MA;Lo;0;R;;;;;N;;;;; +1098A;MEROITIC HIEROGLYPHIC LETTER NA;Lo;0;R;;;;;N;;;;; +1098B;MEROITIC HIEROGLYPHIC LETTER NA-2;Lo;0;R;;;;;N;;;;; +1098C;MEROITIC HIEROGLYPHIC LETTER NE;Lo;0;R;;;;;N;;;;; +1098D;MEROITIC HIEROGLYPHIC LETTER NE-2;Lo;0;R;;;;;N;;;;; +1098E;MEROITIC HIEROGLYPHIC LETTER RA;Lo;0;R;;;;;N;;;;; +1098F;MEROITIC HIEROGLYPHIC LETTER RA-2;Lo;0;R;;;;;N;;;;; +10990;MEROITIC HIEROGLYPHIC LETTER LA;Lo;0;R;;;;;N;;;;; +10991;MEROITIC HIEROGLYPHIC LETTER KHA;Lo;0;R;;;;;N;;;;; +10992;MEROITIC HIEROGLYPHIC LETTER HHA;Lo;0;R;;;;;N;;;;; +10993;MEROITIC HIEROGLYPHIC LETTER SA;Lo;0;R;;;;;N;;;;; +10994;MEROITIC HIEROGLYPHIC LETTER SA-2;Lo;0;R;;;;;N;;;;; +10995;MEROITIC HIEROGLYPHIC LETTER SE;Lo;0;R;;;;;N;;;;; +10996;MEROITIC HIEROGLYPHIC LETTER KA;Lo;0;R;;;;;N;;;;; +10997;MEROITIC HIEROGLYPHIC LETTER QA;Lo;0;R;;;;;N;;;;; +10998;MEROITIC HIEROGLYPHIC LETTER TA;Lo;0;R;;;;;N;;;;; +10999;MEROITIC HIEROGLYPHIC LETTER TA-2;Lo;0;R;;;;;N;;;;; +1099A;MEROITIC HIEROGLYPHIC LETTER TE;Lo;0;R;;;;;N;;;;; +1099B;MEROITIC HIEROGLYPHIC LETTER TE-2;Lo;0;R;;;;;N;;;;; +1099C;MEROITIC HIEROGLYPHIC LETTER TO;Lo;0;R;;;;;N;;;;; +1099D;MEROITIC HIEROGLYPHIC LETTER DA;Lo;0;R;;;;;N;;;;; +1099E;MEROITIC HIEROGLYPHIC SYMBOL VIDJ;Lo;0;R;;;;;N;;;;; +1099F;MEROITIC HIEROGLYPHIC SYMBOL VIDJ-2;Lo;0;R;;;;;N;;;;; +109A0;MEROITIC CURSIVE LETTER A;Lo;0;R;;;;;N;;;;; +109A1;MEROITIC CURSIVE LETTER E;Lo;0;R;;;;;N;;;;; +109A2;MEROITIC CURSIVE LETTER I;Lo;0;R;;;;;N;;;;; +109A3;MEROITIC CURSIVE LETTER O;Lo;0;R;;;;;N;;;;; +109A4;MEROITIC CURSIVE LETTER YA;Lo;0;R;;;;;N;;;;; +109A5;MEROITIC CURSIVE LETTER WA;Lo;0;R;;;;;N;;;;; +109A6;MEROITIC CURSIVE LETTER BA;Lo;0;R;;;;;N;;;;; +109A7;MEROITIC CURSIVE LETTER PA;Lo;0;R;;;;;N;;;;; +109A8;MEROITIC CURSIVE LETTER MA;Lo;0;R;;;;;N;;;;; +109A9;MEROITIC CURSIVE LETTER NA;Lo;0;R;;;;;N;;;;; +109AA;MEROITIC CURSIVE LETTER NE;Lo;0;R;;;;;N;;;;; +109AB;MEROITIC CURSIVE LETTER RA;Lo;0;R;;;;;N;;;;; +109AC;MEROITIC CURSIVE LETTER LA;Lo;0;R;;;;;N;;;;; +109AD;MEROITIC CURSIVE LETTER KHA;Lo;0;R;;;;;N;;;;; +109AE;MEROITIC CURSIVE LETTER HHA;Lo;0;R;;;;;N;;;;; +109AF;MEROITIC CURSIVE LETTER SA;Lo;0;R;;;;;N;;;;; +109B0;MEROITIC CURSIVE LETTER ARCHAIC SA;Lo;0;R;;;;;N;;;;; +109B1;MEROITIC CURSIVE LETTER SE;Lo;0;R;;;;;N;;;;; +109B2;MEROITIC CURSIVE LETTER KA;Lo;0;R;;;;;N;;;;; +109B3;MEROITIC CURSIVE LETTER QA;Lo;0;R;;;;;N;;;;; +109B4;MEROITIC CURSIVE LETTER TA;Lo;0;R;;;;;N;;;;; +109B5;MEROITIC CURSIVE LETTER TE;Lo;0;R;;;;;N;;;;; +109B6;MEROITIC CURSIVE LETTER TO;Lo;0;R;;;;;N;;;;; +109B7;MEROITIC CURSIVE LETTER DA;Lo;0;R;;;;;N;;;;; +109BE;MEROITIC CURSIVE LOGOGRAM RMT;Lo;0;R;;;;;N;;;;; +109BF;MEROITIC CURSIVE LOGOGRAM IMN;Lo;0;R;;;;;N;;;;; 10A00;KHAROSHTHI LETTER A;Lo;0;R;;;;;N;;;;; 10A01;KHAROSHTHI VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; 10A02;KHAROSHTHI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; @@ -17338,6 +17523,257 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 110BF;KAITHI DOUBLE SECTION MARK;Po;0;L;;;;;N;;;;; 110C0;KAITHI DANDA;Po;0;L;;;;;N;;;;; 110C1;KAITHI DOUBLE DANDA;Po;0;L;;;;;N;;;;; +110D0;SORA SOMPENG LETTER SAH;Lo;0;L;;;;;N;;;;; +110D1;SORA SOMPENG LETTER TAH;Lo;0;L;;;;;N;;;;; +110D2;SORA SOMPENG LETTER BAH;Lo;0;L;;;;;N;;;;; +110D3;SORA SOMPENG LETTER CAH;Lo;0;L;;;;;N;;;;; +110D4;SORA SOMPENG LETTER DAH;Lo;0;L;;;;;N;;;;; +110D5;SORA SOMPENG LETTER GAH;Lo;0;L;;;;;N;;;;; +110D6;SORA SOMPENG LETTER MAH;Lo;0;L;;;;;N;;;;; +110D7;SORA SOMPENG LETTER NGAH;Lo;0;L;;;;;N;;;;; +110D8;SORA SOMPENG LETTER LAH;Lo;0;L;;;;;N;;;;; +110D9;SORA SOMPENG LETTER NAH;Lo;0;L;;;;;N;;;;; +110DA;SORA SOMPENG LETTER VAH;Lo;0;L;;;;;N;;;;; +110DB;SORA SOMPENG LETTER PAH;Lo;0;L;;;;;N;;;;; +110DC;SORA SOMPENG LETTER YAH;Lo;0;L;;;;;N;;;;; +110DD;SORA SOMPENG LETTER RAH;Lo;0;L;;;;;N;;;;; +110DE;SORA SOMPENG LETTER HAH;Lo;0;L;;;;;N;;;;; +110DF;SORA SOMPENG LETTER KAH;Lo;0;L;;;;;N;;;;; +110E0;SORA SOMPENG LETTER JAH;Lo;0;L;;;;;N;;;;; +110E1;SORA SOMPENG LETTER NYAH;Lo;0;L;;;;;N;;;;; +110E2;SORA SOMPENG LETTER AH;Lo;0;L;;;;;N;;;;; +110E3;SORA SOMPENG LETTER EEH;Lo;0;L;;;;;N;;;;; +110E4;SORA SOMPENG LETTER IH;Lo;0;L;;;;;N;;;;; +110E5;SORA SOMPENG LETTER UH;Lo;0;L;;;;;N;;;;; +110E6;SORA SOMPENG LETTER OH;Lo;0;L;;;;;N;;;;; +110E7;SORA SOMPENG LETTER EH;Lo;0;L;;;;;N;;;;; +110E8;SORA SOMPENG LETTER MAE;Lo;0;L;;;;;N;;;;; +110F0;SORA SOMPENG DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +110F1;SORA SOMPENG DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +110F2;SORA SOMPENG DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +110F3;SORA SOMPENG DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +110F4;SORA SOMPENG DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +110F5;SORA SOMPENG DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +110F6;SORA SOMPENG DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +110F7;SORA SOMPENG DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +110F8;SORA SOMPENG DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +110F9;SORA SOMPENG DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +11100;CHAKMA SIGN CANDRABINDU;Mn;230;NSM;;;;;N;;;;; +11101;CHAKMA SIGN ANUSVARA;Mn;230;NSM;;;;;N;;;;; +11102;CHAKMA SIGN VISARGA;Mn;230;NSM;;;;;N;;;;; +11103;CHAKMA LETTER AA;Lo;0;L;;;;;N;;;;; +11104;CHAKMA LETTER I;Lo;0;L;;;;;N;;;;; +11105;CHAKMA LETTER U;Lo;0;L;;;;;N;;;;; +11106;CHAKMA LETTER E;Lo;0;L;;;;;N;;;;; +11107;CHAKMA LETTER KAA;Lo;0;L;;;;;N;;;;; +11108;CHAKMA LETTER KHAA;Lo;0;L;;;;;N;;;;; +11109;CHAKMA LETTER GAA;Lo;0;L;;;;;N;;;;; +1110A;CHAKMA LETTER GHAA;Lo;0;L;;;;;N;;;;; +1110B;CHAKMA LETTER NGAA;Lo;0;L;;;;;N;;;;; +1110C;CHAKMA LETTER CAA;Lo;0;L;;;;;N;;;;; +1110D;CHAKMA LETTER CHAA;Lo;0;L;;;;;N;;;;; +1110E;CHAKMA LETTER JAA;Lo;0;L;;;;;N;;;;; +1110F;CHAKMA LETTER JHAA;Lo;0;L;;;;;N;;;;; +11110;CHAKMA LETTER NYAA;Lo;0;L;;;;;N;;;;; +11111;CHAKMA LETTER TTAA;Lo;0;L;;;;;N;;;;; +11112;CHAKMA LETTER TTHAA;Lo;0;L;;;;;N;;;;; +11113;CHAKMA LETTER DDAA;Lo;0;L;;;;;N;;;;; +11114;CHAKMA LETTER DDHAA;Lo;0;L;;;;;N;;;;; +11115;CHAKMA LETTER NNAA;Lo;0;L;;;;;N;;;;; +11116;CHAKMA LETTER TAA;Lo;0;L;;;;;N;;;;; +11117;CHAKMA LETTER THAA;Lo;0;L;;;;;N;;;;; +11118;CHAKMA LETTER DAA;Lo;0;L;;;;;N;;;;; +11119;CHAKMA LETTER DHAA;Lo;0;L;;;;;N;;;;; +1111A;CHAKMA LETTER NAA;Lo;0;L;;;;;N;;;;; +1111B;CHAKMA LETTER PAA;Lo;0;L;;;;;N;;;;; +1111C;CHAKMA LETTER PHAA;Lo;0;L;;;;;N;;;;; +1111D;CHAKMA LETTER BAA;Lo;0;L;;;;;N;;;;; +1111E;CHAKMA LETTER BHAA;Lo;0;L;;;;;N;;;;; +1111F;CHAKMA LETTER MAA;Lo;0;L;;;;;N;;;;; +11120;CHAKMA LETTER YYAA;Lo;0;L;;;;;N;;;;; +11121;CHAKMA LETTER YAA;Lo;0;L;;;;;N;;;;; +11122;CHAKMA LETTER RAA;Lo;0;L;;;;;N;;;;; +11123;CHAKMA LETTER LAA;Lo;0;L;;;;;N;;;;; +11124;CHAKMA LETTER WAA;Lo;0;L;;;;;N;;;;; +11125;CHAKMA LETTER SAA;Lo;0;L;;;;;N;;;;; +11126;CHAKMA LETTER HAA;Lo;0;L;;;;;N;;;;; +11127;CHAKMA VOWEL SIGN A;Mn;0;NSM;;;;;N;;;;; +11128;CHAKMA VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +11129;CHAKMA VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; +1112A;CHAKMA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +1112B;CHAKMA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +1112C;CHAKMA VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +1112D;CHAKMA VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +1112E;CHAKMA VOWEL SIGN O;Mn;0;NSM;11131 11127;;;;N;;;;; +1112F;CHAKMA VOWEL SIGN AU;Mn;0;NSM;11132 11127;;;;N;;;;; +11130;CHAKMA VOWEL SIGN OI;Mn;0;NSM;;;;;N;;;;; +11131;CHAKMA O MARK;Mn;0;NSM;;;;;N;;;;; +11132;CHAKMA AU MARK;Mn;0;NSM;;;;;N;;;;; +11133;CHAKMA VIRAMA;Mn;9;NSM;;;;;N;;;;; +11134;CHAKMA MAAYYAA;Mn;9;NSM;;;;;N;;;;; +11136;CHAKMA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +11137;CHAKMA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +11138;CHAKMA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +11139;CHAKMA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +1113A;CHAKMA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +1113B;CHAKMA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +1113C;CHAKMA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +1113D;CHAKMA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +1113E;CHAKMA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +1113F;CHAKMA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +11140;CHAKMA SECTION MARK;Po;0;L;;;;;N;;;;; +11141;CHAKMA DANDA;Po;0;L;;;;;N;;;;; +11142;CHAKMA DOUBLE DANDA;Po;0;L;;;;;N;;;;; +11143;CHAKMA QUESTION MARK;Po;0;L;;;;;N;;;;; +11180;SHARADA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; +11181;SHARADA SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +11182;SHARADA SIGN VISARGA;Mc;0;L;;;;;N;;;;; +11183;SHARADA LETTER A;Lo;0;L;;;;;N;;;;; +11184;SHARADA LETTER AA;Lo;0;L;;;;;N;;;;; +11185;SHARADA LETTER I;Lo;0;L;;;;;N;;;;; +11186;SHARADA LETTER II;Lo;0;L;;;;;N;;;;; +11187;SHARADA LETTER U;Lo;0;L;;;;;N;;;;; +11188;SHARADA LETTER UU;Lo;0;L;;;;;N;;;;; +11189;SHARADA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +1118A;SHARADA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +1118B;SHARADA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +1118C;SHARADA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +1118D;SHARADA LETTER E;Lo;0;L;;;;;N;;;;; +1118E;SHARADA LETTER AI;Lo;0;L;;;;;N;;;;; +1118F;SHARADA LETTER O;Lo;0;L;;;;;N;;;;; +11190;SHARADA LETTER AU;Lo;0;L;;;;;N;;;;; +11191;SHARADA LETTER KA;Lo;0;L;;;;;N;;;;; +11192;SHARADA LETTER KHA;Lo;0;L;;;;;N;;;;; +11193;SHARADA LETTER GA;Lo;0;L;;;;;N;;;;; +11194;SHARADA LETTER GHA;Lo;0;L;;;;;N;;;;; +11195;SHARADA LETTER NGA;Lo;0;L;;;;;N;;;;; +11196;SHARADA LETTER CA;Lo;0;L;;;;;N;;;;; +11197;SHARADA LETTER CHA;Lo;0;L;;;;;N;;;;; +11198;SHARADA LETTER JA;Lo;0;L;;;;;N;;;;; +11199;SHARADA LETTER JHA;Lo;0;L;;;;;N;;;;; +1119A;SHARADA LETTER NYA;Lo;0;L;;;;;N;;;;; +1119B;SHARADA LETTER TTA;Lo;0;L;;;;;N;;;;; +1119C;SHARADA LETTER TTHA;Lo;0;L;;;;;N;;;;; +1119D;SHARADA LETTER DDA;Lo;0;L;;;;;N;;;;; +1119E;SHARADA LETTER DDHA;Lo;0;L;;;;;N;;;;; +1119F;SHARADA LETTER NNA;Lo;0;L;;;;;N;;;;; +111A0;SHARADA LETTER TA;Lo;0;L;;;;;N;;;;; +111A1;SHARADA LETTER THA;Lo;0;L;;;;;N;;;;; +111A2;SHARADA LETTER DA;Lo;0;L;;;;;N;;;;; +111A3;SHARADA LETTER DHA;Lo;0;L;;;;;N;;;;; +111A4;SHARADA LETTER NA;Lo;0;L;;;;;N;;;;; +111A5;SHARADA LETTER PA;Lo;0;L;;;;;N;;;;; +111A6;SHARADA LETTER PHA;Lo;0;L;;;;;N;;;;; +111A7;SHARADA LETTER BA;Lo;0;L;;;;;N;;;;; +111A8;SHARADA LETTER BHA;Lo;0;L;;;;;N;;;;; +111A9;SHARADA LETTER MA;Lo;0;L;;;;;N;;;;; +111AA;SHARADA LETTER YA;Lo;0;L;;;;;N;;;;; +111AB;SHARADA LETTER RA;Lo;0;L;;;;;N;;;;; +111AC;SHARADA LETTER LA;Lo;0;L;;;;;N;;;;; +111AD;SHARADA LETTER LLA;Lo;0;L;;;;;N;;;;; +111AE;SHARADA LETTER VA;Lo;0;L;;;;;N;;;;; +111AF;SHARADA LETTER SHA;Lo;0;L;;;;;N;;;;; +111B0;SHARADA LETTER SSA;Lo;0;L;;;;;N;;;;; +111B1;SHARADA LETTER SA;Lo;0;L;;;;;N;;;;; +111B2;SHARADA LETTER HA;Lo;0;L;;;;;N;;;;; +111B3;SHARADA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +111B4;SHARADA VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +111B5;SHARADA VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +111B6;SHARADA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +111B7;SHARADA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +111B8;SHARADA VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; +111B9;SHARADA VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; +111BA;SHARADA VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; +111BB;SHARADA VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; +111BC;SHARADA VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +111BD;SHARADA VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +111BE;SHARADA VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; +111BF;SHARADA VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; +111C0;SHARADA SIGN VIRAMA;Mc;9;L;;;;;N;;;;; +111C1;SHARADA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; +111C2;SHARADA SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;; +111C3;SHARADA SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;; +111C4;SHARADA OM;Lo;0;L;;;;;N;;;;; +111C5;SHARADA DANDA;Po;0;L;;;;;N;;;;; +111C6;SHARADA DOUBLE DANDA;Po;0;L;;;;;N;;;;; +111C7;SHARADA ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; +111C8;SHARADA SEPARATOR;Po;0;L;;;;;N;;;;; +111D0;SHARADA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +111D1;SHARADA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +111D2;SHARADA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +111D3;SHARADA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +111D4;SHARADA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +111D5;SHARADA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +111D6;SHARADA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +111D7;SHARADA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +111D8;SHARADA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +111D9;SHARADA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +11680;TAKRI LETTER A;Lo;0;L;;;;;N;;;;; +11681;TAKRI LETTER AA;Lo;0;L;;;;;N;;;;; +11682;TAKRI LETTER I;Lo;0;L;;;;;N;;;;; +11683;TAKRI LETTER II;Lo;0;L;;;;;N;;;;; +11684;TAKRI LETTER U;Lo;0;L;;;;;N;;;;; +11685;TAKRI LETTER UU;Lo;0;L;;;;;N;;;;; +11686;TAKRI LETTER E;Lo;0;L;;;;;N;;;;; +11687;TAKRI LETTER AI;Lo;0;L;;;;;N;;;;; +11688;TAKRI LETTER O;Lo;0;L;;;;;N;;;;; +11689;TAKRI LETTER AU;Lo;0;L;;;;;N;;;;; +1168A;TAKRI LETTER KA;Lo;0;L;;;;;N;;;;; +1168B;TAKRI LETTER KHA;Lo;0;L;;;;;N;;;;; +1168C;TAKRI LETTER GA;Lo;0;L;;;;;N;;;;; +1168D;TAKRI LETTER GHA;Lo;0;L;;;;;N;;;;; +1168E;TAKRI LETTER NGA;Lo;0;L;;;;;N;;;;; +1168F;TAKRI LETTER CA;Lo;0;L;;;;;N;;;;; +11690;TAKRI LETTER CHA;Lo;0;L;;;;;N;;;;; +11691;TAKRI LETTER JA;Lo;0;L;;;;;N;;;;; +11692;TAKRI LETTER JHA;Lo;0;L;;;;;N;;;;; +11693;TAKRI LETTER NYA;Lo;0;L;;;;;N;;;;; +11694;TAKRI LETTER TTA;Lo;0;L;;;;;N;;;;; +11695;TAKRI LETTER TTHA;Lo;0;L;;;;;N;;;;; +11696;TAKRI LETTER DDA;Lo;0;L;;;;;N;;;;; +11697;TAKRI LETTER DDHA;Lo;0;L;;;;;N;;;;; +11698;TAKRI LETTER NNA;Lo;0;L;;;;;N;;;;; +11699;TAKRI LETTER TA;Lo;0;L;;;;;N;;;;; +1169A;TAKRI LETTER THA;Lo;0;L;;;;;N;;;;; +1169B;TAKRI LETTER DA;Lo;0;L;;;;;N;;;;; +1169C;TAKRI LETTER DHA;Lo;0;L;;;;;N;;;;; +1169D;TAKRI LETTER NA;Lo;0;L;;;;;N;;;;; +1169E;TAKRI LETTER PA;Lo;0;L;;;;;N;;;;; +1169F;TAKRI LETTER PHA;Lo;0;L;;;;;N;;;;; +116A0;TAKRI LETTER BA;Lo;0;L;;;;;N;;;;; +116A1;TAKRI LETTER BHA;Lo;0;L;;;;;N;;;;; +116A2;TAKRI LETTER MA;Lo;0;L;;;;;N;;;;; +116A3;TAKRI LETTER YA;Lo;0;L;;;;;N;;;;; +116A4;TAKRI LETTER RA;Lo;0;L;;;;;N;;;;; +116A5;TAKRI LETTER LA;Lo;0;L;;;;;N;;;;; +116A6;TAKRI LETTER VA;Lo;0;L;;;;;N;;;;; +116A7;TAKRI LETTER SHA;Lo;0;L;;;;;N;;;;; +116A8;TAKRI LETTER SA;Lo;0;L;;;;;N;;;;; +116A9;TAKRI LETTER HA;Lo;0;L;;;;;N;;;;; +116AA;TAKRI LETTER RRA;Lo;0;L;;;;;N;;;;; +116AB;TAKRI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +116AC;TAKRI SIGN VISARGA;Mc;0;L;;;;;N;;;;; +116AD;TAKRI VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;; +116AE;TAKRI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +116AF;TAKRI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +116B0;TAKRI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +116B1;TAKRI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +116B2;TAKRI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +116B3;TAKRI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +116B4;TAKRI VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; +116B5;TAKRI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;; +116B6;TAKRI SIGN VIRAMA;Mc;9;L;;;;;N;;;;; +116B7;TAKRI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +116C0;TAKRI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +116C1;TAKRI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +116C2;TAKRI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +116C3;TAKRI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +116C4;TAKRI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +116C5;TAKRI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +116C6;TAKRI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +116C7;TAKRI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +116C8;TAKRI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +116C9;TAKRI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; 12000;CUNEIFORM SIGN A;Lo;0;L;;;;;N;;;;; 12001;CUNEIFORM SIGN A TIMES A;Lo;0;L;;;;;N;;;;; 12002;CUNEIFORM SIGN A TIMES BAD;Lo;0;L;;;;;N;;;;; @@ -19960,6 +20396,139 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 16A36;BAMUM LETTER PHASE-F KPA;Lo;0;L;;;;;N;;;;; 16A37;BAMUM LETTER PHASE-F SAMBA;Lo;0;L;;;;;N;;;;; 16A38;BAMUM LETTER PHASE-F VUEQ;Lo;0;L;;;;;N;;;;; +16F00;MIAO LETTER PA;Lo;0;L;;;;;N;;;;; +16F01;MIAO LETTER BA;Lo;0;L;;;;;N;;;;; +16F02;MIAO LETTER YI PA;Lo;0;L;;;;;N;;;;; +16F03;MIAO LETTER PLA;Lo;0;L;;;;;N;;;;; +16F04;MIAO LETTER MA;Lo;0;L;;;;;N;;;;; +16F05;MIAO LETTER MHA;Lo;0;L;;;;;N;;;;; +16F06;MIAO LETTER ARCHAIC MA;Lo;0;L;;;;;N;;;;; +16F07;MIAO LETTER FA;Lo;0;L;;;;;N;;;;; +16F08;MIAO LETTER VA;Lo;0;L;;;;;N;;;;; +16F09;MIAO LETTER VFA;Lo;0;L;;;;;N;;;;; +16F0A;MIAO LETTER TA;Lo;0;L;;;;;N;;;;; +16F0B;MIAO LETTER DA;Lo;0;L;;;;;N;;;;; +16F0C;MIAO LETTER YI TTA;Lo;0;L;;;;;N;;;;; +16F0D;MIAO LETTER YI TA;Lo;0;L;;;;;N;;;;; +16F0E;MIAO LETTER TTA;Lo;0;L;;;;;N;;;;; +16F0F;MIAO LETTER DDA;Lo;0;L;;;;;N;;;;; +16F10;MIAO LETTER NA;Lo;0;L;;;;;N;;;;; +16F11;MIAO LETTER NHA;Lo;0;L;;;;;N;;;;; +16F12;MIAO LETTER YI NNA;Lo;0;L;;;;;N;;;;; +16F13;MIAO LETTER ARCHAIC NA;Lo;0;L;;;;;N;;;;; +16F14;MIAO LETTER NNA;Lo;0;L;;;;;N;;;;; +16F15;MIAO LETTER NNHA;Lo;0;L;;;;;N;;;;; +16F16;MIAO LETTER LA;Lo;0;L;;;;;N;;;;; +16F17;MIAO LETTER LYA;Lo;0;L;;;;;N;;;;; +16F18;MIAO LETTER LHA;Lo;0;L;;;;;N;;;;; +16F19;MIAO LETTER LHYA;Lo;0;L;;;;;N;;;;; +16F1A;MIAO LETTER TLHA;Lo;0;L;;;;;N;;;;; +16F1B;MIAO LETTER DLHA;Lo;0;L;;;;;N;;;;; +16F1C;MIAO LETTER TLHYA;Lo;0;L;;;;;N;;;;; +16F1D;MIAO LETTER DLHYA;Lo;0;L;;;;;N;;;;; +16F1E;MIAO LETTER KA;Lo;0;L;;;;;N;;;;; +16F1F;MIAO LETTER GA;Lo;0;L;;;;;N;;;;; +16F20;MIAO LETTER YI KA;Lo;0;L;;;;;N;;;;; +16F21;MIAO LETTER QA;Lo;0;L;;;;;N;;;;; +16F22;MIAO LETTER QGA;Lo;0;L;;;;;N;;;;; +16F23;MIAO LETTER NGA;Lo;0;L;;;;;N;;;;; +16F24;MIAO LETTER NGHA;Lo;0;L;;;;;N;;;;; +16F25;MIAO LETTER ARCHAIC NGA;Lo;0;L;;;;;N;;;;; +16F26;MIAO LETTER HA;Lo;0;L;;;;;N;;;;; +16F27;MIAO LETTER XA;Lo;0;L;;;;;N;;;;; +16F28;MIAO LETTER GHA;Lo;0;L;;;;;N;;;;; +16F29;MIAO LETTER GHHA;Lo;0;L;;;;;N;;;;; +16F2A;MIAO LETTER TSSA;Lo;0;L;;;;;N;;;;; +16F2B;MIAO LETTER DZZA;Lo;0;L;;;;;N;;;;; +16F2C;MIAO LETTER NYA;Lo;0;L;;;;;N;;;;; +16F2D;MIAO LETTER NYHA;Lo;0;L;;;;;N;;;;; +16F2E;MIAO LETTER TSHA;Lo;0;L;;;;;N;;;;; +16F2F;MIAO LETTER DZHA;Lo;0;L;;;;;N;;;;; +16F30;MIAO LETTER YI TSHA;Lo;0;L;;;;;N;;;;; +16F31;MIAO LETTER YI DZHA;Lo;0;L;;;;;N;;;;; +16F32;MIAO LETTER REFORMED TSHA;Lo;0;L;;;;;N;;;;; +16F33;MIAO LETTER SHA;Lo;0;L;;;;;N;;;;; +16F34;MIAO LETTER SSA;Lo;0;L;;;;;N;;;;; +16F35;MIAO LETTER ZHA;Lo;0;L;;;;;N;;;;; +16F36;MIAO LETTER ZSHA;Lo;0;L;;;;;N;;;;; +16F37;MIAO LETTER TSA;Lo;0;L;;;;;N;;;;; +16F38;MIAO LETTER DZA;Lo;0;L;;;;;N;;;;; +16F39;MIAO LETTER YI TSA;Lo;0;L;;;;;N;;;;; +16F3A;MIAO LETTER SA;Lo;0;L;;;;;N;;;;; +16F3B;MIAO LETTER ZA;Lo;0;L;;;;;N;;;;; +16F3C;MIAO LETTER ZSA;Lo;0;L;;;;;N;;;;; +16F3D;MIAO LETTER ZZA;Lo;0;L;;;;;N;;;;; +16F3E;MIAO LETTER ZZSA;Lo;0;L;;;;;N;;;;; +16F3F;MIAO LETTER ARCHAIC ZZA;Lo;0;L;;;;;N;;;;; +16F40;MIAO LETTER ZZYA;Lo;0;L;;;;;N;;;;; +16F41;MIAO LETTER ZZSYA;Lo;0;L;;;;;N;;;;; +16F42;MIAO LETTER WA;Lo;0;L;;;;;N;;;;; +16F43;MIAO LETTER AH;Lo;0;L;;;;;N;;;;; +16F44;MIAO LETTER HHA;Lo;0;L;;;;;N;;;;; +16F50;MIAO LETTER NASALIZATION;Lo;0;L;;;;;N;;;;; +16F51;MIAO SIGN ASPIRATION;Mc;0;L;;;;;N;;;;; +16F52;MIAO SIGN REFORMED VOICING;Mc;0;L;;;;;N;;;;; +16F53;MIAO SIGN REFORMED ASPIRATION;Mc;0;L;;;;;N;;;;; +16F54;MIAO VOWEL SIGN A;Mc;0;L;;;;;N;;;;; +16F55;MIAO VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +16F56;MIAO VOWEL SIGN AHH;Mc;0;L;;;;;N;;;;; +16F57;MIAO VOWEL SIGN AN;Mc;0;L;;;;;N;;;;; +16F58;MIAO VOWEL SIGN ANG;Mc;0;L;;;;;N;;;;; +16F59;MIAO VOWEL SIGN O;Mc;0;L;;;;;N;;;;; +16F5A;MIAO VOWEL SIGN OO;Mc;0;L;;;;;N;;;;; +16F5B;MIAO VOWEL SIGN WO;Mc;0;L;;;;;N;;;;; +16F5C;MIAO VOWEL SIGN W;Mc;0;L;;;;;N;;;;; +16F5D;MIAO VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +16F5E;MIAO VOWEL SIGN EN;Mc;0;L;;;;;N;;;;; +16F5F;MIAO VOWEL SIGN ENG;Mc;0;L;;;;;N;;;;; +16F60;MIAO VOWEL SIGN OEY;Mc;0;L;;;;;N;;;;; +16F61;MIAO VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +16F62;MIAO VOWEL SIGN IA;Mc;0;L;;;;;N;;;;; +16F63;MIAO VOWEL SIGN IAN;Mc;0;L;;;;;N;;;;; +16F64;MIAO VOWEL SIGN IANG;Mc;0;L;;;;;N;;;;; +16F65;MIAO VOWEL SIGN IO;Mc;0;L;;;;;N;;;;; +16F66;MIAO VOWEL SIGN IE;Mc;0;L;;;;;N;;;;; +16F67;MIAO VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +16F68;MIAO VOWEL SIGN IU;Mc;0;L;;;;;N;;;;; +16F69;MIAO VOWEL SIGN ING;Mc;0;L;;;;;N;;;;; +16F6A;MIAO VOWEL SIGN U;Mc;0;L;;;;;N;;;;; +16F6B;MIAO VOWEL SIGN UA;Mc;0;L;;;;;N;;;;; +16F6C;MIAO VOWEL SIGN UAN;Mc;0;L;;;;;N;;;;; +16F6D;MIAO VOWEL SIGN UANG;Mc;0;L;;;;;N;;;;; +16F6E;MIAO VOWEL SIGN UU;Mc;0;L;;;;;N;;;;; +16F6F;MIAO VOWEL SIGN UEI;Mc;0;L;;;;;N;;;;; +16F70;MIAO VOWEL SIGN UNG;Mc;0;L;;;;;N;;;;; +16F71;MIAO VOWEL SIGN Y;Mc;0;L;;;;;N;;;;; +16F72;MIAO VOWEL SIGN YI;Mc;0;L;;;;;N;;;;; +16F73;MIAO VOWEL SIGN AE;Mc;0;L;;;;;N;;;;; +16F74;MIAO VOWEL SIGN AEE;Mc;0;L;;;;;N;;;;; +16F75;MIAO VOWEL SIGN ERR;Mc;0;L;;;;;N;;;;; +16F76;MIAO VOWEL SIGN ROUNDED ERR;Mc;0;L;;;;;N;;;;; +16F77;MIAO VOWEL SIGN ER;Mc;0;L;;;;;N;;;;; +16F78;MIAO VOWEL SIGN ROUNDED ER;Mc;0;L;;;;;N;;;;; +16F79;MIAO VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; +16F7A;MIAO VOWEL SIGN EI;Mc;0;L;;;;;N;;;;; +16F7B;MIAO VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; +16F7C;MIAO VOWEL SIGN OU;Mc;0;L;;;;;N;;;;; +16F7D;MIAO VOWEL SIGN N;Mc;0;L;;;;;N;;;;; +16F7E;MIAO VOWEL SIGN NG;Mc;0;L;;;;;N;;;;; +16F8F;MIAO TONE RIGHT;Mn;0;NSM;;;;;N;;;;; +16F90;MIAO TONE TOP RIGHT;Mn;0;NSM;;;;;N;;;;; +16F91;MIAO TONE ABOVE;Mn;0;NSM;;;;;N;;;;; +16F92;MIAO TONE BELOW;Mn;0;NSM;;;;;N;;;;; +16F93;MIAO LETTER TONE-2;Lm;0;L;;;;;N;;;;; +16F94;MIAO LETTER TONE-3;Lm;0;L;;;;;N;;;;; +16F95;MIAO LETTER TONE-4;Lm;0;L;;;;;N;;;;; +16F96;MIAO LETTER TONE-5;Lm;0;L;;;;;N;;;;; +16F97;MIAO LETTER TONE-6;Lm;0;L;;;;;N;;;;; +16F98;MIAO LETTER TONE-7;Lm;0;L;;;;;N;;;;; +16F99;MIAO LETTER TONE-8;Lm;0;L;;;;;N;;;;; +16F9A;MIAO LETTER REFORMED TONE-1;Lm;0;L;;;;;N;;;;; +16F9B;MIAO LETTER REFORMED TONE-2;Lm;0;L;;;;;N;;;;; +16F9C;MIAO LETTER REFORMED TONE-4;Lm;0;L;;;;;N;;;;; +16F9D;MIAO LETTER REFORMED TONE-5;Lm;0;L;;;;;N;;;;; +16F9E;MIAO LETTER REFORMED TONE-6;Lm;0;L;;;;;N;;;;; +16F9F;MIAO LETTER REFORMED TONE-8;Lm;0;L;;;;;N;;;;; 1B000;KATAKANA LETTER ARCHAIC E;Lo;0;L;;;;;N;;;;; 1B001;HIRAGANA LETTER ARCHAIC YE;Lo;0;L;;;;;N;;;;; 1D000;BYZANTINE MUSICAL SYMBOL PSILI;So;0;L;;;;;N;;;;; @@ -21599,6 +22168,149 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1D7FD;MATHEMATICAL MONOSPACE DIGIT SEVEN;Nd;0;EN; 0037;7;7;7;N;;;;; 1D7FE;MATHEMATICAL MONOSPACE DIGIT EIGHT;Nd;0;EN; 0038;8;8;8;N;;;;; 1D7FF;MATHEMATICAL MONOSPACE DIGIT NINE;Nd;0;EN; 0039;9;9;9;N;;;;; +1EE00;ARABIC MATHEMATICAL ALEF;Lo;0;AL; 0627;;;;N;;;;; +1EE01;ARABIC MATHEMATICAL BEH;Lo;0;AL; 0628;;;;N;;;;; +1EE02;ARABIC MATHEMATICAL JEEM;Lo;0;AL; 062C;;;;N;;;;; +1EE03;ARABIC MATHEMATICAL DAL;Lo;0;AL; 062F;;;;N;;;;; +1EE05;ARABIC MATHEMATICAL WAW;Lo;0;AL; 0648;;;;N;;;;; +1EE06;ARABIC MATHEMATICAL ZAIN;Lo;0;AL; 0632;;;;N;;;;; +1EE07;ARABIC MATHEMATICAL HAH;Lo;0;AL; 062D;;;;N;;;;; +1EE08;ARABIC MATHEMATICAL TAH;Lo;0;AL; 0637;;;;N;;;;; +1EE09;ARABIC MATHEMATICAL YEH;Lo;0;AL; 064A;;;;N;;;;; +1EE0A;ARABIC MATHEMATICAL KAF;Lo;0;AL; 0643;;;;N;;;;; +1EE0B;ARABIC MATHEMATICAL LAM;Lo;0;AL; 0644;;;;N;;;;; +1EE0C;ARABIC MATHEMATICAL MEEM;Lo;0;AL; 0645;;;;N;;;;; +1EE0D;ARABIC MATHEMATICAL NOON;Lo;0;AL; 0646;;;;N;;;;; +1EE0E;ARABIC MATHEMATICAL SEEN;Lo;0;AL; 0633;;;;N;;;;; +1EE0F;ARABIC MATHEMATICAL AIN;Lo;0;AL; 0639;;;;N;;;;; +1EE10;ARABIC MATHEMATICAL FEH;Lo;0;AL; 0641;;;;N;;;;; +1EE11;ARABIC MATHEMATICAL SAD;Lo;0;AL; 0635;;;;N;;;;; +1EE12;ARABIC MATHEMATICAL QAF;Lo;0;AL; 0642;;;;N;;;;; +1EE13;ARABIC MATHEMATICAL REH;Lo;0;AL; 0631;;;;N;;;;; +1EE14;ARABIC MATHEMATICAL SHEEN;Lo;0;AL; 0634;;;;N;;;;; +1EE15;ARABIC MATHEMATICAL TEH;Lo;0;AL; 062A;;;;N;;;;; +1EE16;ARABIC MATHEMATICAL THEH;Lo;0;AL; 062B;;;;N;;;;; +1EE17;ARABIC MATHEMATICAL KHAH;Lo;0;AL; 062E;;;;N;;;;; +1EE18;ARABIC MATHEMATICAL THAL;Lo;0;AL; 0630;;;;N;;;;; +1EE19;ARABIC MATHEMATICAL DAD;Lo;0;AL; 0636;;;;N;;;;; +1EE1A;ARABIC MATHEMATICAL ZAH;Lo;0;AL; 0638;;;;N;;;;; +1EE1B;ARABIC MATHEMATICAL GHAIN;Lo;0;AL; 063A;;;;N;;;;; +1EE1C;ARABIC MATHEMATICAL DOTLESS BEH;Lo;0;AL; 066E;;;;N;;;;; +1EE1D;ARABIC MATHEMATICAL DOTLESS NOON;Lo;0;AL; 06BA;;;;N;;;;; +1EE1E;ARABIC MATHEMATICAL DOTLESS FEH;Lo;0;AL; 06A1;;;;N;;;;; +1EE1F;ARABIC MATHEMATICAL DOTLESS QAF;Lo;0;AL; 066F;;;;N;;;;; +1EE21;ARABIC MATHEMATICAL INITIAL BEH;Lo;0;AL; 0628;;;;N;;;;; +1EE22;ARABIC MATHEMATICAL INITIAL JEEM;Lo;0;AL; 062C;;;;N;;;;; +1EE24;ARABIC MATHEMATICAL INITIAL HEH;Lo;0;AL; 0647;;;;N;;;;; +1EE27;ARABIC MATHEMATICAL INITIAL HAH;Lo;0;AL; 062D;;;;N;;;;; +1EE29;ARABIC MATHEMATICAL INITIAL YEH;Lo;0;AL; 064A;;;;N;;;;; +1EE2A;ARABIC MATHEMATICAL INITIAL KAF;Lo;0;AL; 0643;;;;N;;;;; +1EE2B;ARABIC MATHEMATICAL INITIAL LAM;Lo;0;AL; 0644;;;;N;;;;; +1EE2C;ARABIC MATHEMATICAL INITIAL MEEM;Lo;0;AL; 0645;;;;N;;;;; +1EE2D;ARABIC MATHEMATICAL INITIAL NOON;Lo;0;AL; 0646;;;;N;;;;; +1EE2E;ARABIC MATHEMATICAL INITIAL SEEN;Lo;0;AL; 0633;;;;N;;;;; +1EE2F;ARABIC MATHEMATICAL INITIAL AIN;Lo;0;AL; 0639;;;;N;;;;; +1EE30;ARABIC MATHEMATICAL INITIAL FEH;Lo;0;AL; 0641;;;;N;;;;; +1EE31;ARABIC MATHEMATICAL INITIAL SAD;Lo;0;AL; 0635;;;;N;;;;; +1EE32;ARABIC MATHEMATICAL INITIAL QAF;Lo;0;AL; 0642;;;;N;;;;; +1EE34;ARABIC MATHEMATICAL INITIAL SHEEN;Lo;0;AL; 0634;;;;N;;;;; +1EE35;ARABIC MATHEMATICAL INITIAL TEH;Lo;0;AL; 062A;;;;N;;;;; +1EE36;ARABIC MATHEMATICAL INITIAL THEH;Lo;0;AL; 062B;;;;N;;;;; +1EE37;ARABIC MATHEMATICAL INITIAL KHAH;Lo;0;AL; 062E;;;;N;;;;; +1EE39;ARABIC MATHEMATICAL INITIAL DAD;Lo;0;AL; 0636;;;;N;;;;; +1EE3B;ARABIC MATHEMATICAL INITIAL GHAIN;Lo;0;AL; 063A;;;;N;;;;; +1EE42;ARABIC MATHEMATICAL TAILED JEEM;Lo;0;AL; 062C;;;;N;;;;; +1EE47;ARABIC MATHEMATICAL TAILED HAH;Lo;0;AL; 062D;;;;N;;;;; +1EE49;ARABIC MATHEMATICAL TAILED YEH;Lo;0;AL; 064A;;;;N;;;;; +1EE4B;ARABIC MATHEMATICAL TAILED LAM;Lo;0;AL; 0644;;;;N;;;;; +1EE4D;ARABIC MATHEMATICAL TAILED NOON;Lo;0;AL; 0646;;;;N;;;;; +1EE4E;ARABIC MATHEMATICAL TAILED SEEN;Lo;0;AL; 0633;;;;N;;;;; +1EE4F;ARABIC MATHEMATICAL TAILED AIN;Lo;0;AL; 0639;;;;N;;;;; +1EE51;ARABIC MATHEMATICAL TAILED SAD;Lo;0;AL; 0635;;;;N;;;;; +1EE52;ARABIC MATHEMATICAL TAILED QAF;Lo;0;AL; 0642;;;;N;;;;; +1EE54;ARABIC MATHEMATICAL TAILED SHEEN;Lo;0;AL; 0634;;;;N;;;;; +1EE57;ARABIC MATHEMATICAL TAILED KHAH;Lo;0;AL; 062E;;;;N;;;;; +1EE59;ARABIC MATHEMATICAL TAILED DAD;Lo;0;AL; 0636;;;;N;;;;; +1EE5B;ARABIC MATHEMATICAL TAILED GHAIN;Lo;0;AL; 063A;;;;N;;;;; +1EE5D;ARABIC MATHEMATICAL TAILED DOTLESS NOON;Lo;0;AL; 06BA;;;;N;;;;; +1EE5F;ARABIC MATHEMATICAL TAILED DOTLESS QAF;Lo;0;AL; 066F;;;;N;;;;; +1EE61;ARABIC MATHEMATICAL STRETCHED BEH;Lo;0;AL; 0628;;;;N;;;;; +1EE62;ARABIC MATHEMATICAL STRETCHED JEEM;Lo;0;AL; 062C;;;;N;;;;; +1EE64;ARABIC MATHEMATICAL STRETCHED HEH;Lo;0;AL; 0647;;;;N;;;;; +1EE67;ARABIC MATHEMATICAL STRETCHED HAH;Lo;0;AL; 062D;;;;N;;;;; +1EE68;ARABIC MATHEMATICAL STRETCHED TAH;Lo;0;AL; 0637;;;;N;;;;; +1EE69;ARABIC MATHEMATICAL STRETCHED YEH;Lo;0;AL; 064A;;;;N;;;;; +1EE6A;ARABIC MATHEMATICAL STRETCHED KAF;Lo;0;AL; 0643;;;;N;;;;; +1EE6C;ARABIC MATHEMATICAL STRETCHED MEEM;Lo;0;AL; 0645;;;;N;;;;; +1EE6D;ARABIC MATHEMATICAL STRETCHED NOON;Lo;0;AL; 0646;;;;N;;;;; +1EE6E;ARABIC MATHEMATICAL STRETCHED SEEN;Lo;0;AL; 0633;;;;N;;;;; +1EE6F;ARABIC MATHEMATICAL STRETCHED AIN;Lo;0;AL; 0639;;;;N;;;;; +1EE70;ARABIC MATHEMATICAL STRETCHED FEH;Lo;0;AL; 0641;;;;N;;;;; +1EE71;ARABIC MATHEMATICAL STRETCHED SAD;Lo;0;AL; 0635;;;;N;;;;; +1EE72;ARABIC MATHEMATICAL STRETCHED QAF;Lo;0;AL; 0642;;;;N;;;;; +1EE74;ARABIC MATHEMATICAL STRETCHED SHEEN;Lo;0;AL; 0634;;;;N;;;;; +1EE75;ARABIC MATHEMATICAL STRETCHED TEH;Lo;0;AL; 062A;;;;N;;;;; +1EE76;ARABIC MATHEMATICAL STRETCHED THEH;Lo;0;AL; 062B;;;;N;;;;; +1EE77;ARABIC MATHEMATICAL STRETCHED KHAH;Lo;0;AL; 062E;;;;N;;;;; +1EE79;ARABIC MATHEMATICAL STRETCHED DAD;Lo;0;AL; 0636;;;;N;;;;; +1EE7A;ARABIC MATHEMATICAL STRETCHED ZAH;Lo;0;AL; 0638;;;;N;;;;; +1EE7B;ARABIC MATHEMATICAL STRETCHED GHAIN;Lo;0;AL; 063A;;;;N;;;;; +1EE7C;ARABIC MATHEMATICAL STRETCHED DOTLESS BEH;Lo;0;AL; 066E;;;;N;;;;; +1EE7E;ARABIC MATHEMATICAL STRETCHED DOTLESS FEH;Lo;0;AL; 06A1;;;;N;;;;; +1EE80;ARABIC MATHEMATICAL LOOPED ALEF;Lo;0;AL; 0627;;;;N;;;;; +1EE81;ARABIC MATHEMATICAL LOOPED BEH;Lo;0;AL; 0628;;;;N;;;;; +1EE82;ARABIC MATHEMATICAL LOOPED JEEM;Lo;0;AL; 062C;;;;N;;;;; +1EE83;ARABIC MATHEMATICAL LOOPED DAL;Lo;0;AL; 062F;;;;N;;;;; +1EE84;ARABIC MATHEMATICAL LOOPED HEH;Lo;0;AL; 0647;;;;N;;;;; +1EE85;ARABIC MATHEMATICAL LOOPED WAW;Lo;0;AL; 0648;;;;N;;;;; +1EE86;ARABIC MATHEMATICAL LOOPED ZAIN;Lo;0;AL; 0632;;;;N;;;;; +1EE87;ARABIC MATHEMATICAL LOOPED HAH;Lo;0;AL; 062D;;;;N;;;;; +1EE88;ARABIC MATHEMATICAL LOOPED TAH;Lo;0;AL; 0637;;;;N;;;;; +1EE89;ARABIC MATHEMATICAL LOOPED YEH;Lo;0;AL; 064A;;;;N;;;;; +1EE8B;ARABIC MATHEMATICAL LOOPED LAM;Lo;0;AL; 0644;;;;N;;;;; +1EE8C;ARABIC MATHEMATICAL LOOPED MEEM;Lo;0;AL; 0645;;;;N;;;;; +1EE8D;ARABIC MATHEMATICAL LOOPED NOON;Lo;0;AL; 0646;;;;N;;;;; +1EE8E;ARABIC MATHEMATICAL LOOPED SEEN;Lo;0;AL; 0633;;;;N;;;;; +1EE8F;ARABIC MATHEMATICAL LOOPED AIN;Lo;0;AL; 0639;;;;N;;;;; +1EE90;ARABIC MATHEMATICAL LOOPED FEH;Lo;0;AL; 0641;;;;N;;;;; +1EE91;ARABIC MATHEMATICAL LOOPED SAD;Lo;0;AL; 0635;;;;N;;;;; +1EE92;ARABIC MATHEMATICAL LOOPED QAF;Lo;0;AL; 0642;;;;N;;;;; +1EE93;ARABIC MATHEMATICAL LOOPED REH;Lo;0;AL; 0631;;;;N;;;;; +1EE94;ARABIC MATHEMATICAL LOOPED SHEEN;Lo;0;AL; 0634;;;;N;;;;; +1EE95;ARABIC MATHEMATICAL LOOPED TEH;Lo;0;AL; 062A;;;;N;;;;; +1EE96;ARABIC MATHEMATICAL LOOPED THEH;Lo;0;AL; 062B;;;;N;;;;; +1EE97;ARABIC MATHEMATICAL LOOPED KHAH;Lo;0;AL; 062E;;;;N;;;;; +1EE98;ARABIC MATHEMATICAL LOOPED THAL;Lo;0;AL; 0630;;;;N;;;;; +1EE99;ARABIC MATHEMATICAL LOOPED DAD;Lo;0;AL; 0636;;;;N;;;;; +1EE9A;ARABIC MATHEMATICAL LOOPED ZAH;Lo;0;AL; 0638;;;;N;;;;; +1EE9B;ARABIC MATHEMATICAL LOOPED GHAIN;Lo;0;AL; 063A;;;;N;;;;; +1EEA1;ARABIC MATHEMATICAL DOUBLE-STRUCK BEH;Lo;0;AL; 0628;;;;N;;;;; +1EEA2;ARABIC MATHEMATICAL DOUBLE-STRUCK JEEM;Lo;0;AL; 062C;;;;N;;;;; +1EEA3;ARABIC MATHEMATICAL DOUBLE-STRUCK DAL;Lo;0;AL; 062F;;;;N;;;;; +1EEA5;ARABIC MATHEMATICAL DOUBLE-STRUCK WAW;Lo;0;AL; 0648;;;;N;;;;; +1EEA6;ARABIC MATHEMATICAL DOUBLE-STRUCK ZAIN;Lo;0;AL; 0632;;;;N;;;;; +1EEA7;ARABIC MATHEMATICAL DOUBLE-STRUCK HAH;Lo;0;AL; 062D;;;;N;;;;; +1EEA8;ARABIC MATHEMATICAL DOUBLE-STRUCK TAH;Lo;0;AL; 0637;;;;N;;;;; +1EEA9;ARABIC MATHEMATICAL DOUBLE-STRUCK YEH;Lo;0;AL; 064A;;;;N;;;;; +1EEAB;ARABIC MATHEMATICAL DOUBLE-STRUCK LAM;Lo;0;AL; 0644;;;;N;;;;; +1EEAC;ARABIC MATHEMATICAL DOUBLE-STRUCK MEEM;Lo;0;AL; 0645;;;;N;;;;; +1EEAD;ARABIC MATHEMATICAL DOUBLE-STRUCK NOON;Lo;0;AL; 0646;;;;N;;;;; +1EEAE;ARABIC MATHEMATICAL DOUBLE-STRUCK SEEN;Lo;0;AL; 0633;;;;N;;;;; +1EEAF;ARABIC MATHEMATICAL DOUBLE-STRUCK AIN;Lo;0;AL; 0639;;;;N;;;;; +1EEB0;ARABIC MATHEMATICAL DOUBLE-STRUCK FEH;Lo;0;AL; 0641;;;;N;;;;; +1EEB1;ARABIC MATHEMATICAL DOUBLE-STRUCK SAD;Lo;0;AL; 0635;;;;N;;;;; +1EEB2;ARABIC MATHEMATICAL DOUBLE-STRUCK QAF;Lo;0;AL; 0642;;;;N;;;;; +1EEB3;ARABIC MATHEMATICAL DOUBLE-STRUCK REH;Lo;0;AL; 0631;;;;N;;;;; +1EEB4;ARABIC MATHEMATICAL DOUBLE-STRUCK SHEEN;Lo;0;AL; 0634;;;;N;;;;; +1EEB5;ARABIC MATHEMATICAL DOUBLE-STRUCK TEH;Lo;0;AL; 062A;;;;N;;;;; +1EEB6;ARABIC MATHEMATICAL DOUBLE-STRUCK THEH;Lo;0;AL; 062B;;;;N;;;;; +1EEB7;ARABIC MATHEMATICAL DOUBLE-STRUCK KHAH;Lo;0;AL; 062E;;;;N;;;;; +1EEB8;ARABIC MATHEMATICAL DOUBLE-STRUCK THAL;Lo;0;AL; 0630;;;;N;;;;; +1EEB9;ARABIC MATHEMATICAL DOUBLE-STRUCK DAD;Lo;0;AL; 0636;;;;N;;;;; +1EEBA;ARABIC MATHEMATICAL DOUBLE-STRUCK ZAH;Lo;0;AL; 0638;;;;N;;;;; +1EEBB;ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN;Lo;0;AL; 063A;;;;N;;;;; +1EEF0;ARABIC MATHEMATICAL OPERATOR MEEM WITH HAH WITH TATWEEL;Sm;0;ON;;;;;N;;;;; +1EEF1;ARABIC MATHEMATICAL OPERATOR HAH WITH DAL;Sm;0;ON;;;;;N;;;;; 1F000;MAHJONG TILE EAST WIND;So;0;ON;;;;;N;;;;; 1F001;MAHJONG TILE SOUTH WIND;So;0;ON;;;;;N;;;;; 1F002;MAHJONG TILE WEST WIND;So;0;ON;;;;;N;;;;; @@ -21902,6 +22614,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1F167;NEGATIVE CIRCLED LATIN CAPITAL LETTER X;So;0;L;;;;;N;;;;; 1F168;NEGATIVE CIRCLED LATIN CAPITAL LETTER Y;So;0;L;;;;;N;;;;; 1F169;NEGATIVE CIRCLED LATIN CAPITAL LETTER Z;So;0;L;;;;;N;;;;; +1F16A;RAISED MC SIGN;So;0;ON; 004D 0043;;;;N;;;;; +1F16B;RAISED MD SIGN;So;0;ON; 004D 0044;;;;N;;;;; 1F170;NEGATIVE SQUARED LATIN CAPITAL LETTER A;So;0;L;;;;;N;;;;; 1F171;NEGATIVE SQUARED LATIN CAPITAL LETTER B;So;0;L;;;;;N;;;;; 1F172;NEGATIVE SQUARED LATIN CAPITAL LETTER C;So;0;L;;;;;N;;;;; @@ -22354,7 +23068,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1F489;SYRINGE;So;0;ON;;;;;N;;;;; 1F48A;PILL;So;0;ON;;;;;N;;;;; 1F48B;KISS MARK;So;0;ON;;;;;N;;;;; -1F48C;LOVE LETTER;So;0;L;;;;;N;;;;; +1F48C;LOVE LETTER;So;0;ON;;;;;N;;;;; 1F48D;RING;So;0;ON;;;;;N;;;;; 1F48E;GEM STONE;So;0;ON;;;;;N;;;;; 1F48F;KISS;So;0;ON;;;;;N;;;;; @@ -22502,7 +23216,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1F521;INPUT SYMBOL FOR LATIN SMALL LETTERS;So;0;ON;;;;;N;;;;; 1F522;INPUT SYMBOL FOR NUMBERS;So;0;ON;;;;;N;;;;; 1F523;INPUT SYMBOL FOR SYMBOLS;So;0;ON;;;;;N;;;;; -1F524;INPUT SYMBOL FOR LATIN LETTERS;So;0;L;;;;;N;;;;; +1F524;INPUT SYMBOL FOR LATIN LETTERS;So;0;ON;;;;;N;;;;; 1F525;FIRE;So;0;ON;;;;;N;;;;; 1F526;ELECTRIC TORCH;So;0;ON;;;;;N;;;;; 1F527;WRENCH;So;0;ON;;;;;N;;;;; @@ -22528,6 +23242,10 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1F53B;DOWN-POINTING RED TRIANGLE;So;0;ON;;;;;N;;;;; 1F53C;UP-POINTING SMALL RED TRIANGLE;So;0;ON;;;;;N;;;;; 1F53D;DOWN-POINTING SMALL RED TRIANGLE;So;0;ON;;;;;N;;;;; +1F540;CIRCLED CROSS POMMEE;So;0;ON;;;;;N;;;;; +1F541;CROSS POMMEE WITH HALF-CIRCLE BELOW;So;0;ON;;;;;N;;;;; +1F542;CROSS POMMEE;So;0;ON;;;;;N;;;;; +1F543;NOTCHED LEFT SEMICIRCLE WITH THREE DOTS;So;0;ON;;;;;N;;;;; 1F550;CLOCK FACE ONE OCLOCK;So;0;ON;;;;;N;;;;; 1F551;CLOCK FACE TWO OCLOCK;So;0;ON;;;;;N;;;;; 1F552;CLOCK FACE THREE OCLOCK;So;0;ON;;;;;N;;;;; @@ -22557,6 +23275,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1F5FD;STATUE OF LIBERTY;So;0;ON;;;;;N;;;;; 1F5FE;SILHOUETTE OF JAPAN;So;0;ON;;;;;N;;;;; 1F5FF;MOYAI;So;0;ON;;;;;N;;;;; +1F600;GRINNING FACE;So;0;ON;;;;;N;;;;; 1F601;GRINNING FACE WITH SMILING EYES;So;0;ON;;;;;N;;;;; 1F602;FACE WITH TEARS OF JOY;So;0;ON;;;;;N;;;;; 1F603;SMILING FACE WITH OPEN MOUTH;So;0;ON;;;;;N;;;;; @@ -22573,30 +23292,42 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; 1F60E;SMILING FACE WITH SUNGLASSES;So;0;ON;;;;;N;;;;; 1F60F;SMIRKING FACE;So;0;ON;;;;;N;;;;; 1F610;NEUTRAL FACE;So;0;ON;;;;;N;;;;; +1F611;EXPRESSIONLESS FACE;So;0;ON;;;;;N;;;;; 1F612;UNAMUSED FACE;So;0;ON;;;;;N;;;;; 1F613;FACE WITH COLD SWEAT;So;0;ON;;;;;N;;;;; 1F614;PENSIVE FACE;So;0;ON;;;;;N;;;;; +1F615;CONFUSED FACE;So;0;ON;;;;;N;;;;; 1F616;CONFOUNDED FACE;So;0;ON;;;;;N;;;;; +1F617;KISSING FACE;So;0;ON;;;;;N;;;;; 1F618;FACE THROWING A KISS;So;0;ON;;;;;N;;;;; +1F619;KISSING FACE WITH SMILING EYES;So;0;ON;;;;;N;;;;; 1F61A;KISSING FACE WITH CLOSED EYES;So;0;ON;;;;;N;;;;; +1F61B;FACE WITH STUCK-OUT TONGUE;So;0;ON;;;;;N;;;;; 1F61C;FACE WITH STUCK-OUT TONGUE AND WINKING EYE;So;0;ON;;;;;N;;;;; 1F61D;FACE WITH STUCK-OUT TONGUE AND TIGHTLY-CLOSED EYES;So;0;ON;;;;;N;;;;; 1F61E;DISAPPOINTED FACE;So;0;ON;;;;;N;;;;; +1F61F;WORRIED FACE;So;0;ON;;;;;N;;;;; 1F620;ANGRY FACE;So;0;ON;;;;;N;;;;; 1F621;POUTING FACE;So;0;ON;;;;;N;;;;; 1F622;CRYING FACE;So;0;ON;;;;;N;;;;; 1F623;PERSEVERING FACE;So;0;ON;;;;;N;;;;; 1F624;FACE WITH LOOK OF TRIUMPH;So;0;ON;;;;;N;;;;; 1F625;DISAPPOINTED BUT RELIEVED FACE;So;0;ON;;;;;N;;;;; +1F626;FROWNING FACE WITH OPEN MOUTH;So;0;ON;;;;;N;;;;; +1F627;ANGUISHED FACE;So;0;ON;;;;;N;;;;; 1F628;FEARFUL FACE;So;0;ON;;;;;N;;;;; 1F629;WEARY FACE;So;0;ON;;;;;N;;;;; 1F62A;SLEEPY FACE;So;0;ON;;;;;N;;;;; 1F62B;TIRED FACE;So;0;ON;;;;;N;;;;; +1F62C;GRIMACING FACE;So;0;ON;;;;;N;;;;; 1F62D;LOUDLY CRYING FACE;So;0;ON;;;;;N;;;;; +1F62E;FACE WITH OPEN MOUTH;So;0;ON;;;;;N;;;;; +1F62F;HUSHED FACE;So;0;ON;;;;;N;;;;; 1F630;FACE WITH OPEN MOUTH AND COLD SWEAT;So;0;ON;;;;;N;;;;; 1F631;FACE SCREAMING IN FEAR;So;0;ON;;;;;N;;;;; 1F632;ASTONISHED FACE;So;0;ON;;;;;N;;;;; 1F633;FLUSHED FACE;So;0;ON;;;;;N;;;;; +1F634;SLEEPING FACE;So;0;ON;;;;;N;;;;; 1F635;DIZZY FACE;So;0;ON;;;;;N;;;;; 1F636;FACE WITHOUT MOUTH;So;0;ON;;;;;N;;;;; 1F637;FACE WITH MEDICAL MASK;So;0;ON;;;;;N;;;;; 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 ae248b136cc..d506d95af8b 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -42,6 +42,10 @@ /* High level class and public functions implementation. */ +#include "mozilla/Assertions.h" +#include "mozilla/Base64.h" +#include "mozilla/Util.h" + #include "xpcprivate.h" #include "XPCWrapper.h" #include "nsBaseHashtable.h" @@ -64,10 +68,6 @@ #include "XPCQuickStubs.h" #include "dombindings.h" -#include "mozilla/Assertions.h" -#include "mozilla/Base64.h" -#include "mozilla/Util.h" - #include "nsWrapperCacheInlines.h" NS_IMPL_THREADSAFE_ISUPPORTS7(nsXPConnect, @@ -166,7 +166,7 @@ nsXPConnect::GetXPConnect() // XPConnect off the main thread. If you're an extension developer hitting // this, you need to change your code. See bug 716167. if (!NS_LIKELY(NS_IsMainThread() || NS_IsCycleCollectorThread())) - JS_Assert("NS_IsMainThread()", __FILE__, __LINE__); + MOZ_Assert("NS_IsMainThread()", __FILE__, __LINE__); if (!gSelf) { if (gOnceAliveNowDead) @@ -480,9 +480,10 @@ struct NoteWeakMapChildrenTracer : public JSTracer }; static void -TraceWeakMappingChild(JSTracer *trc, void *thing, JSGCTraceKind kind) +TraceWeakMappingChild(JSTracer *trc, void **thingp, JSGCTraceKind kind) { - JS_ASSERT(trc->callback == TraceWeakMappingChild); + MOZ_ASSERT(trc->callback == TraceWeakMappingChild); + void *thing = *thingp; NoteWeakMapChildrenTracer *tracer = static_cast(trc); if (kind == JSTRACE_STRING) @@ -513,7 +514,7 @@ TraceWeakMapping(js::WeakMapTracer *trc, JSObject *m, void *k, JSGCTraceKind kkind, void *v, JSGCTraceKind vkind) { - JS_ASSERT(trc->callback == TraceWeakMapping); + MOZ_ASSERT(trc->callback == TraceWeakMapping); NoteWeakMapsTracer *tracer = static_cast(trc); if (vkind == JSTRACE_STRING) return; @@ -524,7 +525,7 @@ TraceWeakMapping(js::WeakMapTracer *trc, JSObject *m, // reason about the liveness of their keys, which in turn requires that // the key can be represented in the cycle collector graph. All existing // uses of weak maps use either objects or scripts as keys, which are okay. - JS_ASSERT(AddToCCKind(kkind)); + MOZ_ASSERT(AddToCCKind(kkind)); // As an emergency fallback for non-debug builds, if the key is not // representable in the cycle collector graph, we treat it as marked. This @@ -721,8 +722,9 @@ xpc_GCThingIsGrayCCThing(void *thing) * re-coloring. */ static void -UnmarkGrayChildren(JSTracer *trc, void *thing, JSGCTraceKind kind) +UnmarkGrayChildren(JSTracer *trc, void **thingp, JSGCTraceKind kind) { + void *thing = *thingp; int stackDummy; if (!JS_CHECK_STACK_SIZE(js::GetContextStackLimit(trc->context), &stackDummy)) { /* @@ -776,9 +778,10 @@ struct TraversalTracer : public JSTracer }; static void -NoteJSChild(JSTracer *trc, void *thing, JSGCTraceKind kind) +NoteJSChild(JSTracer *trc, void **thingp, JSGCTraceKind kind) { TraversalTracer *tracer = static_cast(trc); + void *thing = *thingp; // Don't traverse non-gray objects, unless we want all traces. if (!xpc_IsGrayGCThing(thing) && !tracer->cb.WantAllTraces()) @@ -1009,7 +1012,7 @@ nsXPConnect::GetOutstandingRequests(JSContext* cx) // Ignore the contribution from the XPCCallContext we created for cycle // collection. if (context && cx == context->GetJSContext()) { - JS_ASSERT(n); + MOZ_ASSERT(n); --n; } return n; @@ -1166,7 +1169,7 @@ struct VerifyTraceXPCGlobalCalledTracer }; static void -VerifyTraceXPCGlobalCalled(JSTracer *trc, void *thing, JSGCTraceKind kind) +VerifyTraceXPCGlobalCalled(JSTracer *trc, void **thingp, JSGCTraceKind kind) { // We don't do anything here, we only want to verify that TraceXPCGlobal // was called. @@ -1301,7 +1304,7 @@ nsXPConnect::InitClassesWithNewWrappedGlobal(JSContext * aJSContext, // voodoo to fixup scoping and parenting... - JS_ASSERT(!js::GetObjectParent(globalJSObj)); + MOZ_ASSERT(!js::GetObjectParent(globalJSObj)); JSObject* oldGlobal = JS_GetGlobalObject(aJSContext); if (!oldGlobal || oldGlobal == tempGlobal) diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 404704255d3..3f97e1d1a02 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -46,6 +46,7 @@ #ifndef xpcprivate_h___ #define xpcprivate_h___ +#include "mozilla/Assertions.h" #include "mozilla/Attributes.h" #include @@ -1350,7 +1351,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; @@ -1608,7 +1610,7 @@ public: static XPCWrappedNativeScope *GetNativeScope(JSContext *cx, JSObject *obj) { - JS_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_XPCONNECT_GLOBAL); + MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_XPCONNECT_GLOBAL); const js::Value &v = js::GetObjectSlot(obj, JSCLASS_GLOBAL_SLOT_COUNT); return v.isUndefined() @@ -2034,7 +2036,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; @@ -2076,7 +2081,7 @@ public: private: XPCNativeScriptableFlags mFlags; - XPCNativeScriptableSharedJSClass mJSClass; + XPCWrappedNativeJSClass mJSClass; JSBool mCanBeSlim; }; @@ -3020,7 +3025,10 @@ public: JSBool IsAggregatedToNative() const {return mRoot->mOuter != nsnull;} nsISupports* GetAggregatedNativeObject() const {return mRoot->mOuter;} - void SetIsMainThreadOnly() {JS_ASSERT(mMainThread); mMainThreadOnly = true;} + void SetIsMainThreadOnly() { + MOZ_ASSERT(mMainThread); + mMainThreadOnly = true; + } bool IsMainThreadOnly() const {return mMainThreadOnly;} void TraceJS(JSTracer* trc); @@ -3649,7 +3657,7 @@ public: // XPConnect off the main thread. If you're an extension developer hitting // this, you need to change your code. See bug 716167. if (!NS_LIKELY(NS_IsMainThread() || NS_IsCycleCollectorThread())) - JS_Assert("NS_IsMainThread()", __FILE__, __LINE__); + MOZ_Assert("NS_IsMainThread()", __FILE__, __LINE__); if (cx) { if (js::GetOwnerThread(cx) == sMainJSThread) diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index fac2737d7f0..809e3ad2172 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -989,7 +989,8 @@ ContainerState::PopThebesLayerData() nsRefPtr layer; nsRefPtr imageContainer = data->CanOptimizeImageLayer(); - if (data->mIsSolidColorInVisibleRegion || imageContainer) { + if ((data->mIsSolidColorInVisibleRegion || imageContainer) && + data->mLayer->GetValidRegion().IsEmpty()) { NS_ASSERTION(!(data->mIsSolidColorInVisibleRegion && imageContainer), "Can't be a solid color as well as an image!"); if (imageContainer) { 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..ffe4bc85d19 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) { diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 547d931e0c1..50edad41cc2 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/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 92886094b78..b12f6e32f37 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1575,7 +1575,7 @@ asserts(0-11) == 582146-1.html about:blank == 584699-1.html 584699-1-ref.html == 585598-2.xhtml 585598-2-ref.xhtml == 586400-1.html 586400-1-ref.html -fails-if(Android) fails-if(cocoaWidget) == 586683-1.html 586683-1-ref.html +fuzzy-if(d2d,1,1051) fails-if(Android) fails-if(cocoaWidget) == 586683-1.html 586683-1-ref.html == 589615-1a.xhtml 589615-1-ref.html == 589615-1b.html 589615-1-ref.html == 589672-1.html 589672-1-ref.html @@ -1690,6 +1690,6 @@ needs-focus != 703186-1.html 703186-2.html == 714519-1-q.html 714519-1-ref.html == 714519-2-as.html 714519-2-ref.html == 714519-2-q.html 714519-2-ref.html -fuzzy-if(cocoaWidget,1,170) == 718521.html 718521-ref.html +fuzzy-if(d2d,1,19) fuzzy-if(cocoaWidget,1,170) == 718521.html 718521-ref.html == 720987.html 720987-ref.html == 722923-1.html 722923-1-ref.html 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/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/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index e774246eb3f..16e9c6b1463 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -4331,7 +4331,7 @@ CSSParserImpl::TranslateDimension(nsCSSValue& aValue, // Must be a zero number... NS_ASSERTION(0 == aNumber, "numbers without units must be 0"); if ((VARIANT_LENGTH & aVariantMask) != 0) { - units = eCSSUnit_Point; + units = eCSSUnit_Pixel; type = VARIANT_LENGTH; } else if ((VARIANT_ANGLE & aVariantMask) != 0) { @@ -7765,6 +7765,35 @@ CSSParserImpl::ParseSingleTransform(nsCSSValue& aValue, bool& aIs3D) minElems, maxElems, variantMask, aIs3D)) return false; + // Bug 721136: Normalize the identifier to lowercase, except that things + // like scaleX should have the last character capitalized. This matches + // what other browsers do. + nsContentUtils::ASCIIToLower(mToken.mIdent); + switch (keyword) { + case eCSSKeyword_rotatex: + case eCSSKeyword_scalex: + case eCSSKeyword_skewx: + case eCSSKeyword_translatex: + mToken.mIdent.Replace(mToken.mIdent.Length() - 1, 1, PRUnichar('X')); + break; + + case eCSSKeyword_rotatey: + case eCSSKeyword_scaley: + case eCSSKeyword_skewy: + case eCSSKeyword_translatey: + mToken.mIdent.Replace(mToken.mIdent.Length() - 1, 1, PRUnichar('Y')); + break; + + case eCSSKeyword_rotatez: + case eCSSKeyword_scalez: + case eCSSKeyword_translatez: + mToken.mIdent.Replace(mToken.mIdent.Length() - 1, 1, PRUnichar('Z')); + break; + + default: + break; + } + return ParseFunction(mToken.mIdent, variantMask, minElems, maxElems, aValue); } diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index 5a0f3988447..fd593bd416f 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -83,6 +83,7 @@ #include "nsDisplayList.h" #include "nsDOMCSSDeclaration.h" #include "mozilla/dom/Element.h" +#include "nsGenericElement.h" #include "CSSCalc.h" using namespace mozilla; @@ -161,8 +162,26 @@ nsComputedDOMStyle::Shutdown() } +// If nsComputedDOMStyle 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(nsComputedDOMStyle, mContent) +// nsComputedDOMStyle has only one cycle collected field, so if +// mContent is going to be skipped, the style isn't part of a garbage +// cycle. +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsComputedDOMStyle) + return !tmp->mContent || nsGenericElement::CanSkip(tmp->mContent, true); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsComputedDOMStyle) + return !tmp->mContent || nsGenericElement::CanSkipInCC(tmp->mContent); +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(nsComputedDOMStyle) +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END + // QueryInterface implementation for nsComputedDOMStyle NS_INTERFACE_MAP_BEGIN(nsComputedDOMStyle) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h index a5637befb2b..cd38c9997f3 100644 --- a/layout/style/nsComputedDOMStyle.h +++ b/layout/style/nsComputedDOMStyle.h @@ -62,8 +62,8 @@ class nsComputedDOMStyle : public nsDOMCSSDeclaration, { public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsComputedDOMStyle, - nsICSSDeclaration) + NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS_AMBIGUOUS(nsComputedDOMStyle, + nsICSSDeclaration) NS_IMETHOD Init(nsIDOMElement *aElement, const nsAString& aPseudoElt, 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/Makefile.in b/layout/style/test/Makefile.in index 41dd9cedf3e..86d9b132164 100644 --- a/layout/style/test/Makefile.in +++ b/layout/style/test/Makefile.in @@ -229,6 +229,7 @@ _TEST_FILES = test_acid3_test46.html \ visited_image_loading_frame.html \ visited_image_loading_frame_empty.html \ test_load_events_on_stylesheets.html \ + test_bug721136.html \ $(NULL) _VISITED_REFTEST_FILES = \ diff --git a/layout/style/test/test_bug721136.html b/layout/style/test/test_bug721136.html new file mode 100644 index 00000000000..1dec8e8d24b --- /dev/null +++ b/layout/style/test/test_bug721136.html @@ -0,0 +1,45 @@ + + +Test for Bug 721136 + + +Mozilla Bug 721136 +
+
+
diff --git a/layout/style/test/test_keyframes_rules.html b/layout/style/test/test_keyframes_rules.html index ca6a1f3c23b..f31f057ef2b 100644 --- a/layout/style/test/test_keyframes_rules.html +++ b/layout/style/test/test_keyframes_rules.html @@ -71,12 +71,12 @@ is(bounce.cssRules[0].keyText, "0%", "keyframe rule keyText"); is(bounce.cssRules[1].keyText, "25%", "keyframe rule keyText"); is(bounce.cssRules[2].keyText, "75%, 85%", "keyframe rule keyText"); is(bounce.cssRules[3].keyText, "100%", "keyframe rule keyText"); -is(bounce.cssRules[0].style.marginLeft, "0pt", "keyframe rule style"); +is(bounce.cssRules[0].style.marginLeft, "0px", "keyframe rule style"); is(bounce.cssRules[1].style.marginLeft, "25px", "keyframe rule style"); -is(bounce.cssRules[0].cssText, "0% { margin-left: 0pt; }"); +is(bounce.cssRules[0].cssText, "0% { margin-left: 0px; }"); is(bounce.cssText, "@-moz-keyframes bouncier {\n" + - "0% { margin-left: 0pt; }\n" + + "0% { margin-left: 0px; }\n" + "25% { margin-left: 25px; }\n" + "75%, 85% { margin-left: 90px; }\n" + "100% { margin-left: 100px; }\n" + 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/test/test_units_length.html b/layout/style/test/test_units_length.html index 378a98f3520..6db3f0c7dc6 100644 --- a/layout/style/test/test_units_length.html +++ b/layout/style/test/test_units_length.html @@ -48,6 +48,11 @@ for (var test in tests) { } } +// Bug 393910 +p.setAttribute("style", "margin-left: 0"); +is(p.style.getPropertyValue("margin-left"), "0px", + "0 serializes to 0px"); + 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 67de74f79db..289919a713d 100644 --- a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp +++ b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp @@ -271,12 +271,6 @@ nsSVGForeignObjectFrame::PaintSVG(nsSVGRenderState *aContext, gfx->Multiply(matrixForChildren); - // Transform the dirty rect into the rectangle containing the - // transformed dirty rect. - gfxMatrix invmatrix = matrix.Invert(); - NS_ASSERTION(!invmatrix.IsSingular(), - "inverse of non-singular matrix should be non-singular"); - PRUint32 flags = nsLayoutUtils::PAINT_IN_TRANSFORM; if (aContext->IsPaintingToWindow()) { flags |= nsLayoutUtils::PAINT_TO_WINDOW; @@ -370,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; @@ -501,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..1525f1a2960 100644 --- a/layout/svg/base/src/nsSVGGlyphFrame.cpp +++ b/layout/svg/base/src/nsSVGGlyphFrame.cpp @@ -491,7 +491,7 @@ nsSVGGlyphFrame::UpdateCoveredRegion() if (!extent.IsEmpty()) { mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent, - PresContext()->AppUnitsPerDevPixel()); + PresContext()->AppUnitsPerCSSPixel()); } // See bug 614732 comment 32. diff --git a/layout/svg/base/src/nsSVGImageFrame.cpp b/layout/svg/base/src/nsSVGImageFrame.cpp index 9a2cd3205d6..b0930b26ca4 100644 --- a/layout/svg/base/src/nsSVGImageFrame.cpp +++ b/layout/svg/base/src/nsSVGImageFrame.cpp @@ -52,7 +52,7 @@ using namespace mozilla; class nsSVGImageFrame; -class nsSVGImageListener : public nsStubImageDecoderObserver +class nsSVGImageListener MOZ_FINAL : public nsStubImageDecoderObserver { public: nsSVGImageListener(nsSVGImageFrame *aFrame); @@ -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/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/media/libpng/pngrutil.c b/media/libpng/pngrutil.c index 85386f2ad56..1f7af96df6d 100644 --- a/media/libpng/pngrutil.c +++ b/media/libpng/pngrutil.c @@ -401,8 +401,15 @@ png_decompress_chunk(png_structp png_ptr, int comp_type, { /* Success (maybe) - really uncompress the chunk. */ png_size_t new_size = 0; - png_charp text = png_malloc_warn(png_ptr, - prefix_size + expanded_size + 1); + png_charp text = NULL; + /* Need to check for both truncation (64-bit platforms) and integer + * overflow. + */ + if (prefix_size + expanded_size > prefix_size && + prefix_size + expanded_size < 0xffffffffU) + { + text = png_malloc_warn(png_ptr, prefix_size + expanded_size + 1); + } if (text != NULL) { 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/AboutHomeContent.java b/mobile/android/base/AboutHomeContent.java index bdf6e16c82f..cf6a18ce335 100644 --- a/mobile/android/base/AboutHomeContent.java +++ b/mobile/android/base/AboutHomeContent.java @@ -693,14 +693,14 @@ public class AboutHomeContent extends ScrollView { ImageView thumbnail = (ImageView) view; if (b == null) { - thumbnail.setImageResource(R.drawable.abouthome_topsite_placeholder); + thumbnail.setImageResource(R.drawable.tab_thumbnail_default); } else { try { Bitmap bitmap = BitmapFactory.decodeByteArray(b, 0, b.length); thumbnail.setImageBitmap(bitmap); } catch (OutOfMemoryError oom) { Log.e(LOGTAG, "Unable to load thumbnail bitmap", oom); - thumbnail.setImageResource(R.drawable.abouthome_topsite_placeholder); + thumbnail.setImageResource(R.drawable.tab_thumbnail_default); } } diff --git a/mobile/android/base/AwesomeBarTabs.java b/mobile/android/base/AwesomeBarTabs.java index d8d50d40a27..19524a3d1c3 100644 --- a/mobile/android/base/AwesomeBarTabs.java +++ b/mobile/android/base/AwesomeBarTabs.java @@ -284,7 +284,7 @@ public class AwesomeBarTabs extends TabHost { try { // use reflection to disable auto-requery - Class cls = Class.forName("android.widget.CursorTreeAdapter"); + Class cls = Class.forName("android.widget.CursorTreeAdapter"); Field field = cls.getDeclaredField("mAutoRequery"); field.setAccessible(true); field.set(mBookmarksAdapter, false); diff --git a/mobile/android/base/BrowserToolbar.java b/mobile/android/base/BrowserToolbar.java index ef373e60a44..8708e236de8 100644 --- a/mobile/android/base/BrowserToolbar.java +++ b/mobile/android/base/BrowserToolbar.java @@ -322,6 +322,20 @@ public class BrowserToolbar extends LinearLayout { } } + public void show() { + if (Build.VERSION.SDK_INT >= 11) + GeckoActionBar.show(GeckoApp.mAppContext); + else + setVisibility(View.VISIBLE); + } + + public void hide() { + if (Build.VERSION.SDK_INT >= 11) + GeckoActionBar.hide(GeckoApp.mAppContext); + else + setVisibility(View.GONE); + } + public void refresh() { Tab tab = Tabs.getInstance().getSelectedTab(); if (tab != null) { diff --git a/mobile/android/base/GeckoActionBar.java b/mobile/android/base/GeckoActionBar.java index 9f1db9b7e02..4f707d8b6c6 100644 --- a/mobile/android/base/GeckoActionBar.java +++ b/mobile/android/base/GeckoActionBar.java @@ -66,4 +66,8 @@ public class GeckoActionBar { public static void setDisplayHomeAsUpEnabled(Activity activity, boolean enabled) { activity.getActionBar().setDisplayHomeAsUpEnabled(enabled); } + + public static void show(Activity activity) { + activity.getActionBar().show(); + } } diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index a06f1cfead9..93d47d3dcc7 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -626,25 +626,7 @@ abstract public class GeckoApp int sh = forceBigSceenshot ? mSoftwareLayerClient.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)); } } @@ -1024,13 +1006,13 @@ abstract public class GeckoApp } else if (event.equals("ToggleChrome:Hide")) { mMainHandler.post(new Runnable() { public void run() { - mBrowserToolbar.setVisibility(View.GONE); + mBrowserToolbar.hide(); } }); } else if (event.equals("ToggleChrome:Show")) { mMainHandler.post(new Runnable() { public void run() { - mBrowserToolbar.setVisibility(View.VISIBLE); + mBrowserToolbar.show(); } }); } else if (event.equals("DOMFullScreen:Start")) { @@ -1507,7 +1489,8 @@ abstract public class GeckoApp if (tab == null) return; - tab.removePluginLayer(surface); + Layer layer = tab.removePluginLayer(surface); + hidePluginLayer(layer); } public void showSurface(Surface surface, int x, int y, @@ -1642,9 +1625,13 @@ abstract public class GeckoApp mMainHandler.post(new Runnable() { public void run() { // Hide/show the system notification bar - getWindow().setFlags(fullscreen ? - WindowManager.LayoutParams.FLAG_FULLSCREEN : 0, - WindowManager.LayoutParams.FLAG_FULLSCREEN); + Window window = getWindow(); + window.setFlags(fullscreen ? + WindowManager.LayoutParams.FLAG_FULLSCREEN : 0, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + + if (Build.VERSION.SDK_INT >= 11) + window.getDecorView().setSystemUiVisibility(fullscreen ? 1 : 0); } }); } @@ -2087,7 +2074,7 @@ abstract public class GeckoApp Runnable r = new SessionSnapshotRunnable(null); GeckoAppShell.getHandler().post(r); - GeckoAppShell.sendEventToGecko(GeckoEvent.createPauseEvent()); + GeckoAppShell.sendEventToGecko(GeckoEvent.createPauseEvent(mOwnActivityDepth)); // The user is navigating away from this activity, but nothing // has come to the foreground yet; for Gecko, we may want to // stop repainting, for example. @@ -2107,7 +2094,8 @@ abstract public class GeckoApp { Log.i(LOGTAG, "resume"); if (checkLaunchState(LaunchState.GeckoRunning)) - GeckoAppShell.onResume(); + GeckoAppShell.sendEventToGecko(GeckoEvent.createResumeEvent(mOwnActivityDepth)); + // After an onPause, the activity is back in the foreground. // Undo whatever we did in onPause. super.onResume(); @@ -2156,7 +2144,7 @@ abstract public class GeckoApp // etc., and generally mark the profile as 'clean', and then // dirty it again if we get an onResume. - GeckoAppShell.sendEventToGecko(GeckoEvent.createStoppingEvent()); + GeckoAppShell.sendEventToGecko(GeckoEvent.createStoppingEvent(mOwnActivityDepth)); super.onStop(); } @@ -2173,7 +2161,7 @@ abstract public class GeckoApp Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - onStart"); Log.i(LOGTAG, "start"); - GeckoAppShell.sendEventToGecko(GeckoEvent.createStartEvent()); + GeckoAppShell.sendEventToGecko(GeckoEvent.createStartEvent(mOwnActivityDepth)); super.onStart(); } diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index fff9fac50e3..c83f3f9b869 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 @@ -558,12 +566,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 30701e3b378..bfea49b4edc 100644 --- a/mobile/android/base/GeckoEvent.java +++ b/mobile/android/base/GeckoEvent.java @@ -87,6 +87,8 @@ public class GeckoEvent { private static final int VISITED = 21; 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; @@ -139,16 +141,28 @@ public class GeckoEvent { mType = evType; } - public static GeckoEvent createPauseEvent() { - return new GeckoEvent(ACTIVITY_PAUSING); + public static GeckoEvent createPauseEvent(int activityDepth) { + GeckoEvent event = new GeckoEvent(ACTIVITY_PAUSING); + event.mFlags = activityDepth > 0 ? 1 : 0; + return event; } - public static GeckoEvent createStoppingEvent() { - return new GeckoEvent(ACTIVITY_STOPPING); + public static GeckoEvent createResumeEvent(int activityDepth) { + GeckoEvent event = new GeckoEvent(ACTIVITY_RESUMING); + event.mFlags = activityDepth > 0 ? 1 : 0; + return event; } - public static GeckoEvent createStartEvent() { - return new GeckoEvent(ACTIVITY_START); + public static GeckoEvent createStoppingEvent(int activityDepth) { + GeckoEvent event = new GeckoEvent(ACTIVITY_STOPPING); + event.mFlags = activityDepth > 0 ? 1 : 0; + return event; + } + + public static GeckoEvent createStartEvent(int activityDepth) { + GeckoEvent event = new GeckoEvent(ACTIVITY_START); + event.mFlags = activityDepth > 0 ? 1 : 0; + return event; } public static GeckoEvent createShutdownEvent() { @@ -386,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/Makefile.in b/mobile/android/base/Makefile.in index 08bcb5d9140..70d0d8ab0cb 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -295,8 +295,6 @@ RES_DRAWABLE_BASE = \ res/drawable/abouthome_sync_logo.png \ res/drawable/abouthome_sync_bg.9.png \ res/drawable/abouthome_sync_pressed_bg.9.png \ - res/drawable/abouthome_topsite_placeholder.png \ - res/drawable/abouthome_topsite_shadow.9.png \ res/drawable/awesomebar_tab.9.png \ res/drawable/awesomebar_tab_pressed.9.png \ res/drawable/ic_addons_empty.png \ @@ -345,8 +343,6 @@ RES_DRAWABLE_HDPI = \ res/drawable-hdpi/abouthome_sync_logo.png \ res/drawable-hdpi/abouthome_sync_bg.9.png \ res/drawable-hdpi/abouthome_sync_pressed_bg.9.png \ - res/drawable-hdpi/abouthome_topsite_placeholder.png \ - res/drawable-hdpi/abouthome_topsite_shadow.9.png \ res/drawable-hdpi/awesomebar_tab.9.png \ res/drawable-hdpi/awesomebar_tab_pressed.9.png \ res/drawable-hdpi/ic_addons_empty.png \ @@ -409,8 +405,6 @@ RES_DRAWABLE_XHDPI_V11 = \ res/drawable-xhdpi-v11/abouthome_sync_logo.png \ res/drawable-xhdpi-v11/abouthome_sync_bg.9.png \ res/drawable-xhdpi-v11/abouthome_sync_pressed_bg.9.png \ - res/drawable-xhdpi-v11/abouthome_topsite_placeholder.png \ - res/drawable-xhdpi-v11/abouthome_topsite_shadow.9.png \ res/drawable-xhdpi-v11/awesomebar_tab.9.png \ res/drawable-xhdpi-v11/awesomebar_tab_pressed.9.png \ res/drawable-xhdpi-v11/ic_addons_empty.png \ diff --git a/mobile/android/base/db/AndroidBrowserDB.java b/mobile/android/base/db/AndroidBrowserDB.java index b2edaa9403a..ca52ca37e00 100644 --- a/mobile/android/base/db/AndroidBrowserDB.java +++ b/mobile/android/base/db/AndroidBrowserDB.java @@ -188,23 +188,6 @@ public class AndroidBrowserDB implements BrowserDB.BrowserDBIface { Browser.clearHistory(cr); } - public Cursor getAllBookmarks(ContentResolver cr) { - Cursor c = cr.query(Browser.BOOKMARKS_URI, - new String[] { URL_COLUMN_ID, - BookmarkColumns.URL, - BookmarkColumns.TITLE, - BookmarkColumns.FAVICON }, - // Select all bookmarks with a non-empty URL. When the history - // is empty there appears to be a dummy entry in the database - // which has a title of "Bookmarks" and no URL; the length restriction - // is to avoid picking that up specifically. - Browser.BookmarkColumns.BOOKMARK + " = 1 AND LENGTH(" + Browser.BookmarkColumns.URL + ") > 0", - null, - Browser.BookmarkColumns.TITLE); - - return new AndroidDBCursor(c); - } - public Cursor getMobileBookmarks(ContentResolver cr) { Cursor c = cr.query(null, null, null, null, null); return new AndroidDBCursor(c); diff --git a/mobile/android/base/db/BrowserDB.java b/mobile/android/base/db/BrowserDB.java index 80a80ba015d..922e6e8d7f3 100644 --- a/mobile/android/base/db/BrowserDB.java +++ b/mobile/android/base/db/BrowserDB.java @@ -77,8 +77,6 @@ public class BrowserDB { public void clearHistory(ContentResolver cr); - public Cursor getAllBookmarks(ContentResolver cr); - public Cursor getMobileBookmarks(ContentResolver cr); public Cursor getDesktopBookmarks(ContentResolver cr); @@ -148,10 +146,6 @@ public class BrowserDB { sDb.clearHistory(cr); } - public static Cursor getAllBookmarks(ContentResolver cr) { - return sDb.getAllBookmarks(cr); - } - public static Cursor getMobileBookmarks(ContentResolver cr) { return sDb.getMobileBookmarks(cr); } diff --git a/mobile/android/base/db/LocalBrowserDB.java b/mobile/android/base/db/LocalBrowserDB.java index 60906a18d71..23192071021 100644 --- a/mobile/android/base/db/LocalBrowserDB.java +++ b/mobile/android/base/db/LocalBrowserDB.java @@ -80,14 +80,28 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { private final String mProfile; private long mMobileFolderId; + private final Uri mBookmarksUriWithProfile; + private final Uri mParentsUriWithProfile; + private final Uri mHistoryUriWithProfile; + private final Uri mImagesUriWithProfile; + private final Uri mDeletedHistoryUriWithProfile; + public LocalBrowserDB(String profile) { mProfile = profile; mMobileFolderId = -1; + + mBookmarksUriWithProfile = appendProfile(Bookmarks.CONTENT_URI); + mParentsUriWithProfile = appendProfile(Bookmarks.PARENTS_CONTENT_URI); + mHistoryUriWithProfile = appendProfile(History.CONTENT_URI); + mImagesUriWithProfile = appendProfile(Images.CONTENT_URI); + + mDeletedHistoryUriWithProfile = mHistoryUriWithProfile.buildUpon(). + appendQueryParameter(BrowserContract.PARAM_SHOW_DELETED, "1").build(); } - private Uri appendProfileAndLimit(Uri uri, int limit) { - return uri.buildUpon().appendQueryParameter(BrowserContract.PARAM_PROFILE, mProfile). - appendQueryParameter(BrowserContract.PARAM_LIMIT, String.valueOf(limit)).build(); + private Uri historyUriWithLimit(int limit) { + return mHistoryUriWithProfile.buildUpon().appendQueryParameter(BrowserContract.PARAM_LIMIT, + String.valueOf(limit)).build(); } private Uri appendProfile(Uri uri) { @@ -95,7 +109,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { } private Cursor filterAllSites(ContentResolver cr, String[] projection, CharSequence constraint, int limit, CharSequence urlFilter) { - Cursor c = cr.query(appendProfileAndLimit(History.CONTENT_URI, limit), + Cursor c = cr.query(historyUriWithLimit(limit), projection, (urlFilter != null ? "(" + History.URL + " NOT LIKE ? ) AND " : "" ) + "(" + History.URL + " LIKE ? OR " + History.TITLE + " LIKE ?)", @@ -136,7 +150,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { Cursor cursor = null; try { - cursor = cr.query(appendProfile(History.CONTENT_URI), + cursor = cr.query(mHistoryUriWithProfile, new String[] { History._ID }, null, null, @@ -170,7 +184,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { History.VISITS, // 1 }; - cursor = cr.query(appendProfile(History.CONTENT_URI), + cursor = cr.query(mDeletedHistoryUriWithProfile, projection, History.URL + " = ?", new String[] { uri }, @@ -182,6 +196,9 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { values.put(History.VISITS, cursor.getInt(1) + 1); values.put(History.DATE_LAST_VISITED, now); + // Restore deleted record if possible + values.put(History.IS_DELETED, 0); + Uri historyUri = ContentUris.withAppendedId(History.CONTENT_URI, cursor.getLong(0)); cr.update(appendProfile(historyUri), values, null, null); } else { @@ -196,7 +213,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { values.put(History.DATE_LAST_VISITED, now); values.put(History.TITLE, uri); - cr.insert(appendProfile(History.CONTENT_URI), values); + cr.insert(mHistoryUriWithProfile, values); } } finally { if (cursor != null) @@ -208,7 +225,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { ContentValues values = new ContentValues(); values.put(History.TITLE, title); - cr.update(appendProfile(History.CONTENT_URI), + cr.update(mHistoryUriWithProfile, values, History.URL + " = ?", new String[] { uri }); @@ -219,7 +236,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { int oldVisits = 0; Cursor cursor = null; try { - cursor = cr.query(appendProfile(History.CONTENT_URI), + cursor = cr.query(mHistoryUriWithProfile, new String[] { History.VISITS }, History.URL + " = ?", new String[] { uri }, @@ -240,14 +257,14 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { values.put(History.TITLE, title); } - cr.update(appendProfile(History.CONTENT_URI), + cr.update(mHistoryUriWithProfile, values, History.URL + " = ?", new String[] { uri }); } public Cursor getAllVisitedHistory(ContentResolver cr) { - Cursor c = cr.query(appendProfile(History.CONTENT_URI), + Cursor c = cr.query(mHistoryUriWithProfile, new String[] { History.URL }, History.VISITS + " > 0", null, @@ -257,7 +274,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { } public Cursor getRecentHistory(ContentResolver cr, int limit) { - Cursor c = cr.query(appendProfileAndLimit(History.CONTENT_URI, limit), + Cursor c = cr.query(historyUriWithLimit(limit), new String[] { History._ID, History.URL, History.TITLE, @@ -276,21 +293,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { } public void clearHistory(ContentResolver cr) { - cr.delete(appendProfile(History.CONTENT_URI), null, null); - } - - public Cursor getAllBookmarks(ContentResolver cr) { - Cursor c = cr.query(appendProfile(Bookmarks.CONTENT_URI), - new String[] { Bookmarks._ID, - Bookmarks.URL, - Bookmarks.TITLE, - Bookmarks.FAVICON, - Bookmarks.KEYWORD }, - Bookmarks.IS_FOLDER + " = 0", - null, - Bookmarks.TITLE + " ASC"); - - return new LocalDBCursor(c); + cr.delete(mHistoryUriWithProfile, null, null); } public Cursor getMobileBookmarks(ContentResolver cr) { @@ -304,7 +307,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { private Cursor getBookmarks(ContentResolver cr, boolean mobileBookmarks) { String parentSelection = mobileBookmarks ? " = ?" : " != ?"; long mobileFolderId = getMobileBookmarksFolderId(cr); - Cursor c = cr.query(appendProfile(Bookmarks.CONTENT_URI), + Cursor c = cr.query(mBookmarksUriWithProfile, new String[] { Bookmarks._ID, Bookmarks.URL, Bookmarks.TITLE, @@ -318,7 +321,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { } public boolean isBookmark(ContentResolver cr, String uri) { - Cursor cursor = cr.query(appendProfile(Bookmarks.CONTENT_URI), + Cursor cursor = cr.query(mBookmarksUriWithProfile, new String[] { Bookmarks._ID }, Bookmarks.URL + " = ?", new String[] { uri }, @@ -331,7 +334,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { } public String getUrlForKeyword(ContentResolver cr, String keyword) { - Cursor cursor = cr.query(appendProfile(Bookmarks.CONTENT_URI), + Cursor cursor = cr.query(mBookmarksUriWithProfile, new String[] { Bookmarks.URL }, Bookmarks.KEYWORD + " = ?", new String[] { keyword }, @@ -355,7 +358,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { Cursor c = null; try { - c = cr.query(appendProfile(Bookmarks.CONTENT_URI), + c = cr.query(mBookmarksUriWithProfile, new String[] { Bookmarks._ID }, Bookmarks.GUID + " = ?", new String[] { Bookmarks.MOBILE_FOLDER_GUID }, @@ -379,15 +382,9 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { ContentValues values = new ContentValues(); values.put(Bookmarks.DATE_MODIFIED, System.currentTimeMillis()); - // Inline appendProfile here to avoid building multiple Uri objects. - final Uri updateURI = - Bookmarks.CONTENT_URI.buildUpon() - .appendPath("parents") - .appendQueryParameter(BrowserContract.PARAM_PROFILE, mProfile) - .build(); String where = param + " = ?"; String[] args = new String[] { value }; - int updated = cr.update(updateURI, values, where, args); + int updated = cr.update(mParentsUriWithProfile, values, where, args); debug("Updated " + updated + " rows to new modified time."); } @@ -406,7 +403,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { // Restore deleted record if possible values.put(Bookmarks.IS_DELETED, 0); - Uri contentUri = appendProfile(Bookmarks.CONTENT_URI); + Uri contentUri = mBookmarksUriWithProfile; int updated = cr.update(contentUri, values, Bookmarks.URL + " = ?", @@ -430,7 +427,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { } public void removeBookmark(ContentResolver cr, int id) { - Uri contentUri = appendProfile(Bookmarks.CONTENT_URI); + Uri contentUri = mBookmarksUriWithProfile; // Do this now so that the item still exists! final String idString = String.valueOf(id); @@ -444,7 +441,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { } public void removeBookmarksWithURL(ContentResolver cr, String uri) { - Uri contentUri = appendProfile(Bookmarks.CONTENT_URI); + Uri contentUri = mBookmarksUriWithProfile; // Do this now so that the items still exist! bumpParents(cr, Bookmarks.URL, uri); @@ -457,7 +454,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { } public void registerBookmarkObserver(ContentResolver cr, ContentObserver observer) { - Uri uri = appendProfile(Bookmarks.CONTENT_URI); + Uri uri = mBookmarksUriWithProfile; cr.registerContentObserver(uri, false, observer); } @@ -467,14 +464,14 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { values.put(Bookmarks.URL, uri); values.put(Bookmarks.KEYWORD, keyword); - cr.update(appendProfile(Bookmarks.CONTENT_URI), + cr.update(mBookmarksUriWithProfile, values, Bookmarks.URL + " = ?", new String[] { oldUri }); } public BitmapDrawable getFaviconForUrl(ContentResolver cr, String uri) { - Cursor c = cr.query(appendProfile(Images.CONTENT_URI), + Cursor c = cr.query(mImagesUriWithProfile, new String[] { Images.FAVICON }, Images.URL + " = ?", new String[] { uri }, @@ -511,13 +508,13 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { // Restore deleted record if possible values.put(Images.IS_DELETED, 0); - int updated = cr.update(appendProfile(Images.CONTENT_URI), + int updated = cr.update(mImagesUriWithProfile, values, Images.URL + " = ?", new String[] { uri }); if (updated == 0) - cr.insert(appendProfile(Images.CONTENT_URI), values); + cr.insert(mImagesUriWithProfile, values); } public void updateThumbnailForUrl(ContentResolver cr, String uri, @@ -534,17 +531,17 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { // Restore deleted record if possible values.put(Images.IS_DELETED, 0); - int updated = cr.update(appendProfile(Images.CONTENT_URI), + int updated = cr.update(mImagesUriWithProfile, values, Images.URL + " = ?", new String[] { uri }); if (updated == 0) - cr.insert(appendProfile(Images.CONTENT_URI), values); + cr.insert(mImagesUriWithProfile, values); } public byte[] getThumbnailForUrl(ContentResolver cr, String uri) { - Cursor c = cr.query(appendProfile(Images.CONTENT_URI), + Cursor c = cr.query(mImagesUriWithProfile, new String[] { Images.THUMBNAIL }, Images.URL + " = ?", new String[] { uri }, diff --git a/mobile/android/base/resources/drawable-hdpi/abouthome_topsite_placeholder.png b/mobile/android/base/resources/drawable-hdpi/abouthome_topsite_placeholder.png deleted file mode 100644 index 82452a943b7..00000000000 Binary files a/mobile/android/base/resources/drawable-hdpi/abouthome_topsite_placeholder.png and /dev/null differ diff --git a/mobile/android/base/resources/drawable-hdpi/abouthome_topsite_shadow.9.png b/mobile/android/base/resources/drawable-hdpi/abouthome_topsite_shadow.9.png deleted file mode 100644 index c3afbe3115c..00000000000 Binary files a/mobile/android/base/resources/drawable-hdpi/abouthome_topsite_shadow.9.png and /dev/null differ diff --git a/mobile/android/base/resources/drawable-xhdpi-v11/abouthome_topsite_placeholder.png b/mobile/android/base/resources/drawable-xhdpi-v11/abouthome_topsite_placeholder.png deleted file mode 100644 index 6413a45633e..00000000000 Binary files a/mobile/android/base/resources/drawable-xhdpi-v11/abouthome_topsite_placeholder.png and /dev/null differ diff --git a/mobile/android/base/resources/drawable-xhdpi-v11/abouthome_topsite_shadow.9.png b/mobile/android/base/resources/drawable-xhdpi-v11/abouthome_topsite_shadow.9.png deleted file mode 100644 index bad57a71665..00000000000 Binary files a/mobile/android/base/resources/drawable-xhdpi-v11/abouthome_topsite_shadow.9.png and /dev/null differ diff --git a/mobile/android/base/resources/drawable/abouthome_topsite_placeholder.png b/mobile/android/base/resources/drawable/abouthome_topsite_placeholder.png deleted file mode 100644 index 7ae2b283350..00000000000 Binary files a/mobile/android/base/resources/drawable/abouthome_topsite_placeholder.png and /dev/null differ diff --git a/mobile/android/base/resources/drawable/abouthome_topsite_shadow.9.png b/mobile/android/base/resources/drawable/abouthome_topsite_shadow.9.png deleted file mode 100644 index 478fb92498e..00000000000 Binary files a/mobile/android/base/resources/drawable/abouthome_topsite_shadow.9.png and /dev/null differ diff --git a/mobile/android/base/resources/layout/abouthome_topsite_item.xml b/mobile/android/base/resources/layout/abouthome_topsite_item.xml index 19e3afcc7ca..c4860aa6a61 100644 --- a/mobile/android/base/resources/layout/abouthome_topsite_item.xml +++ b/mobile/android/base/resources/layout/abouthome_topsite_item.xml @@ -10,7 +10,7 @@ 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/base/ui/PanZoomController.java b/mobile/android/base/ui/PanZoomController.java index bfd0b43e7d0..04a182f0d15 100644 --- a/mobile/android/base/ui/PanZoomController.java +++ b/mobile/android/base/ui/PanZoomController.java @@ -297,7 +297,7 @@ public class PanZoomController cancelTouch(); startPanning(event.getX(0), event.getY(0), event.getEventTime()); GeckoApp.mAppContext.hidePlugins(false /* don't hide layers */); - GeckoApp.mAppContext.mAutoCompletePopup.hide(); + GeckoApp.mAutoCompletePopup.hide(); track(event); return true; diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 765291a9069..b25613d66da 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -202,11 +202,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); @@ -557,36 +558,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). @@ -964,10 +935,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") { @@ -1002,7 +969,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 = { @@ -1136,6 +1112,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) { @@ -1225,6 +1210,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)) @@ -1620,16 +1631,6 @@ Tab.prototype = { this.updateTransform(); }, - screenshot: function(aSrc, aDst) { - if (!this.browser || !this.browser.contentWindow) - return; - - 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); - }, - 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); 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/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 15a211ceab1..e13be5299f9 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -1036,6 +1036,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 +1069,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/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/SpdySession.cpp b/netwerk/protocol/http/SpdySession.cpp index d5ae8031b09..b592a6ef423 100644 --- a/netwerk/protocol/http/SpdySession.cpp +++ b/netwerk/protocol/http/SpdySession.cpp @@ -1949,6 +1949,40 @@ SpdySession::Http1xTransactionCount() return 0; } +// used as an enumerator by TakeSubTransactions() +static PLDHashOperator +TakeStream(nsAHttpTransaction *key, + nsAutoPtr &stream, + void *closure) +{ + nsTArray > *list = + static_cast > *>(closure); + + list->AppendElement(key); + + // removing the stream from the hash will delete the stream + // and drop the transaction reference the hash held + return PL_DHASH_REMOVE; +} + +nsresult +SpdySession::TakeSubTransactions( + nsTArray > &outTransactions) +{ + // Generally this cannot be done with spdy as transactions are + // started right away. + + LOG3(("SpdySession::TakeSubTransactions %p\n", this)); + + if (mConcurrentHighWater > 0) + return NS_ERROR_ALREADY_OPENED; + + LOG3((" taking %d\n", mStreamTransactionHash.Count())); + + mStreamTransactionHash.Enumerate(TakeStream, &outTransactions); + return NS_OK; +} + //----------------------------------------------------------------------------- // Pass through methods of nsAHttpConnection //----------------------------------------------------------------------------- diff --git a/netwerk/protocol/http/nsAHttpTransaction.h b/netwerk/protocol/http/nsAHttpTransaction.h index d859cfcf87a..4a7980d2ad4 100644 --- a/netwerk/protocol/http/nsAHttpTransaction.h +++ b/netwerk/protocol/http/nsAHttpTransaction.h @@ -39,6 +39,7 @@ #define nsAHttpTransaction_h__ #include "nsISupports.h" +#include "nsTArray.h" class nsAHttpConnection; class nsAHttpSegmentReader; @@ -101,6 +102,18 @@ public: // abstract object. Pipelines may have multiple, SPDY has 0, // normal http transactions have 1. virtual PRUint32 Http1xTransactionCount() = 0; + + // called to remove the unused sub transactions from an object that can + // handle multiple transactions simultaneously (i.e. pipelines or spdy). + // + // Returns NS_ERROR_NOT_IMPLEMENTED if the object does not implement + // sub-transactions. + // + // Returns NS_ERROR_ALREADY_OPENED if the subtransactions have been + // at least partially written and cannot be moved. + // + virtual nsresult TakeSubTransactions( + nsTArray > &outTransactions) = 0; }; #define NS_DECL_NSAHTTPTRANSACTION \ @@ -118,7 +131,8 @@ public: void Close(nsresult reason); \ void SetSSLConnectFailed(); \ nsHttpRequestHead *RequestHead(); \ - PRUint32 Http1xTransactionCount(); + PRUint32 Http1xTransactionCount(); \ + nsresult TakeSubTransactions(nsTArray > &outTransactions); //----------------------------------------------------------------------------- // nsAHttpSegmentReader diff --git a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp index 3cdece64df8..6acce884ff5 100644 --- a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp +++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp @@ -128,13 +128,16 @@ nsHttpChannelAuthProvider::ProcessAuthentication(PRUint32 httpStatus, rv = mAuthChannel->GetLoadFlags(&loadFlags); if (NS_FAILED(rv)) return rv; - if (loadFlags & nsIRequest::LOAD_ANONYMOUS) { - return NS_ERROR_NOT_AVAILABLE; - } - nsCAutoString challenges; mProxyAuth = (httpStatus == 407); + // Do proxy auth even if we're LOAD_ANONYMOUS + if ((loadFlags & nsIRequest::LOAD_ANONYMOUS) && + (!mProxyAuth || !UsingHttpProxy())) { + LOG(("Skipping authentication for anonymous non-proxy request\n")); + return NS_ERROR_NOT_AVAILABLE; + } + rv = PrepareForAuthentication(mProxyAuth); if (NS_FAILED(rv)) return rv; @@ -199,10 +202,6 @@ nsHttpChannelAuthProvider::AddAuthorizationHeaders() rv = mAuthChannel->GetLoadFlags(&loadFlags); if (NS_FAILED(rv)) return rv; - if (loadFlags & nsIRequest::LOAD_ANONYMOUS) { - return NS_OK; - } - // this getter never fails nsHttpAuthCache *authCache = gHttpHandler->AuthCache(); @@ -214,6 +213,11 @@ nsHttpChannelAuthProvider::AddAuthorizationHeaders() nsnull, // proxy has no path mProxyIdent); + if (loadFlags & nsIRequest::LOAD_ANONYMOUS) { + LOG(("Skipping Authorization header for anonymous load\n")); + return NS_OK; + } + // check if server credentials should be sent nsCAutoString path, scheme; if (NS_SUCCEEDED(GetCurrentPath(path)) && diff --git a/netwerk/protocol/http/nsHttpConnection.cpp b/netwerk/protocol/http/nsHttpConnection.cpp index 51d34110ecd..d47f0401a67 100644 --- a/netwerk/protocol/http/nsHttpConnection.cpp +++ b/netwerk/protocol/http/nsHttpConnection.cpp @@ -170,6 +170,93 @@ nsHttpConnection::Init(nsHttpConnectionInfo *info, return NS_OK; } +void +nsHttpConnection::StartSpdy() +{ + LOG(("nsHttpConnection::StartSpdy [this=%p]\n", this)); + + NS_ABORT_IF_FALSE(!mSpdySession, "mSpdySession should be null"); + + mUsingSpdy = true; + mEverUsedSpdy = true; + + // Setting the connection as reused allows some transactions that fail + // with NS_ERROR_NET_RESET to be restarted and SPDY uses that code + // to handle clean rejections (such as those that arrived after + // a server goaway was generated). + mIsReused = true; + + // If mTransaction is a pipeline object it might represent + // several requests. If so, we need to unpack that and + // pack them all into a new spdy session. + + nsTArray > list; + nsresult rv = mTransaction->TakeSubTransactions(list); + + if (rv == NS_ERROR_ALREADY_OPENED) { + // Has the interface for TakeSubTransactions() changed? + LOG(("TakeSubTranscations somehow called after " + "nsAHttpTransaction began processing\n")); + NS_ABORT_IF_FALSE(false, + "TakeSubTranscations somehow called after " + "nsAHttpTransaction began processing"); + mTransaction->Close(NS_ERROR_ABORT); + return; + } + + if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) { + // Has the interface for TakeSubTransactions() changed? + LOG(("unexpected rv from nnsAHttpTransaction::TakeSubTransactions()")); + NS_ABORT_IF_FALSE(false, + "unexpected result from " + "nsAHttpTransaction::TakeSubTransactions()"); + mTransaction->Close(NS_ERROR_ABORT); + return; + } + + if (NS_FAILED(rv)) { // includes NS_ERROR_NOT_IMPLEMENTED + NS_ABORT_IF_FALSE(list.IsEmpty(), "sub transaction list not empty"); + + // This is ok - treat mTransaction as a single real request. + // Wrap the old http transaction into the new spdy session + // as the first stream. + mSpdySession = new SpdySession(mTransaction, + mSocketTransport, + mPriority); + LOG(("nsHttpConnection::StartSpdy moves single transaction %p " + "into SpdySession %p\n", mTransaction.get(), mSpdySession.get())); + } + else { + NS_ABORT_IF_FALSE(!list.IsEmpty(), "sub transaction list empty"); + + PRInt32 count = list.Length(); + + LOG(("nsHttpConnection::StartSpdy moving transaction list len=%d " + "into SpdySession %p\n", count, mSpdySession.get())); + + for (PRInt32 index = 0; index < count; ++index) { + if (!mSpdySession) { + mSpdySession = new SpdySession(list[index], + mSocketTransport, + mPriority); + } + else { + // AddStream() cannot fail + if (!mSpdySession->AddStream(list[index], mPriority)) { + NS_ABORT_IF_FALSE(false, "SpdySession::AddStream failed"); + LOG(("SpdySession::AddStream failed\n")); + mTransaction->Close(NS_ERROR_ABORT); + return; + } + } + } + } + + mSupportsPipelining = false; // dont use http/1 pipelines with spdy + mTransaction = mSpdySession; + mIdleTimeout = gHttpHandler->SpdyTimeout(); +} + bool nsHttpConnection::EnsureNPNComplete() { @@ -215,31 +302,15 @@ nsHttpConnection::EnsureNPNComplete() goto npnComplete; return false; } - + if (NS_FAILED(rv)) goto npnComplete; LOG(("nsHttpConnection::EnsureNPNComplete %p negotiated to '%s'", this, negotiatedNPN.get())); - - if (negotiatedNPN.Equals(NS_LITERAL_CSTRING("spdy/2"))) { - mUsingSpdy = true; - mEverUsedSpdy = true; - // Setting the connection as reused allows some transactions that fail - // with NS_ERROR_NET_RESET to be restarted and SPDY uses that code - // to handle clean rejections (such as those that arrived after - // a server goaway was generated). - mIsReused = true; - - // Wrap the old http transaction into the new spdy session - // as the first stream - mSpdySession = new SpdySession(mTransaction, - mSocketTransport, - mPriority); - mTransaction = mSpdySession; - mIdleTimeout = gHttpHandler->SpdyTimeout(); - } + if (negotiatedNPN.Equals(NS_LITERAL_CSTRING("spdy/2"))) + StartSpdy(); mozilla::Telemetry::Accumulate(mozilla::Telemetry::SPDY_NPN_CONNECT, mUsingSpdy); diff --git a/netwerk/protocol/http/nsHttpConnection.h b/netwerk/protocol/http/nsHttpConnection.h index 9bc69d47e14..38ea0024750 100644 --- a/netwerk/protocol/http/nsHttpConnection.h +++ b/netwerk/protocol/http/nsHttpConnection.h @@ -180,6 +180,9 @@ private: // redirections void HandleAlternateProtocol(nsHttpResponseHead *); + // Start the Spdy transaction handler when NPN indicates spdy/2 + void StartSpdy(); + // Directly Add a transaction to an active connection for SPDY nsresult AddTransaction(nsAHttpTransaction *, PRInt32); 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/protocol/http/nsHttpPipeline.cpp b/netwerk/protocol/http/nsHttpPipeline.cpp index f58f4a8026a..6eb1908bfee 100644 --- a/netwerk/protocol/http/nsHttpPipeline.cpp +++ b/netwerk/protocol/http/nsHttpPipeline.cpp @@ -364,6 +364,33 @@ nsHttpPipeline::Http1xTransactionCount() return mHttp1xTransactionCount; } +nsresult +nsHttpPipeline::TakeSubTransactions( + nsTArray > &outTransactions) +{ + LOG(("nsHttpPipeline::TakeSubTransactions [this=%p]\n", this)); + + if (mResponseQ.Length() || mRequestIsPartial) + return NS_ERROR_ALREADY_OPENED; + + // request queue could be empty if it was already canceled, in which + // case it is safe to treat this as a case without any + // sub-transactions. + if (!mRequestQ.Length()) + return NS_ERROR_NOT_IMPLEMENTED; + + PRInt32 i, count = mRequestQ.Length(); + for (i = 0; i < count; ++i) { + nsAHttpTransaction *trans = Request(i); + outTransactions.AppendElement(trans); + NS_RELEASE(trans); + } + mRequestQ.Clear(); + + LOG((" took %d\n", count)); + return NS_OK; +} + //----------------------------------------------------------------------------- // nsHttpPipeline::nsAHttpConnection //----------------------------------------------------------------------------- diff --git a/netwerk/protocol/http/nsHttpTransaction.cpp b/netwerk/protocol/http/nsHttpTransaction.cpp index d32c35ff2eb..4571429d0b5 100644 --- a/netwerk/protocol/http/nsHttpTransaction.cpp +++ b/netwerk/protocol/http/nsHttpTransaction.cpp @@ -341,6 +341,13 @@ nsHttpTransaction::Http1xTransactionCount() return 1; } +nsresult +nsHttpTransaction::TakeSubTransactions( + nsTArray > &outTransactions) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + //---------------------------------------------------------------------------- // nsHttpTransaction::nsAHttpTransaction //---------------------------------------------------------------------------- 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/testing/testsuite-targets.mk b/testing/testsuite-targets.mk index 8345d1a154d..1bc40e6ed62 100644 --- a/testing/testsuite-targets.mk +++ b/testing/testsuite-targets.mk @@ -97,20 +97,24 @@ endif mochitest-remote: DM_TRANS?=adb mochitest-remote: - @if test -f ${MOZ_HOST_BIN}/xpcshell && [ "${TEST_DEVICE}" != "usb" -o "$(DM_TRANS)" = "adb" ]; \ - then $(RUN_MOCHITEST_REMOTE); \ - else \ - echo "please prepare your host with environment variables for TEST_DEVICE and MOZ_HOST_BIN"; \ - fi + @if [ ! -f ${MOZ_HOST_BIN}/xpcshell ]; then \ + echo "please prepare your host with the environment variable MOZ_HOST_BIN"; \ + elif [ "${TEST_DEVICE}" = "" -a "$(DM_TRANS)" != "adb" ]; then \ + echo "please prepare your host with the environment variable TEST_DEVICE"; \ + else \ + $(RUN_MOCHITEST_REMOTE); \ + fi mochitest-robotium: robotium-id-map mochitest-robotium: DM_TRANS?=adb mochitest-robotium: - @if test -f ${MOZ_HOST_BIN}/xpcshell && [ "${TEST_DEVICE}" != "usb" -o "$(DM_TRANS)" = "adb" ]; \ - then $(RUN_MOCHITEST_ROBOTIUM); \ - else \ - echo "please prepare your host with environment variables for TEST_DEVICE and MOZ_HOST_BIN"; \ - fi + @if [ ! -f ${MOZ_HOST_BIN}/xpcshell ]; then \ + echo "please prepare your host with the environment variable MOZ_HOST_BIN"; \ + elif [ "${TEST_DEVICE}" = "" -a "$(DM_TRANS)" != "adb" ]; then \ + echo "please prepare your host with the environment variable TEST_DEVICE"; \ + else \ + $(RUN_MOCHITEST_ROBOTIUM); \ + fi mochitest-plain: $(RUN_MOCHITEST) @@ -172,11 +176,15 @@ reftest: reftest-remote: TEST_PATH?=layout/reftests/reftest.list reftest-remote: DM_TRANS?=adb reftest-remote: - @if test -f ${MOZ_HOST_BIN}/xpcshell && [ "${TEST_DEVICE}" != "" -o "$(DM_TRANS)" = "adb" ]; \ - then ln -s $(abspath $(topsrcdir)) _tests/reftest/tests;$(call REMOTE_REFTEST,tests/$(TEST_PATH)); $(CHECK_TEST_ERROR); \ - else \ - echo "please prepare your host with environment variables for TEST_DEVICE and MOZ_HOST_BIN"; \ - fi + @if [ ! -f ${MOZ_HOST_BIN}/xpcshell ]; then \ + echo "please prepare your host with the environment variable MOZ_HOST_BIN"; \ + elif [ "${TEST_DEVICE}" = "" -a "$(DM_TRANS)" != "adb" ]; then \ + echo "please prepare your host with the environment variable TEST_DEVICE"; \ + else \ + ln -s $(abspath $(topsrcdir)) _tests/reftest/tests; \ + $(call REMOTE_REFTEST,tests/$(TEST_PATH)); \ + $(CHECK_TEST_ERROR); \ + fi reftest-ipc: TEST_PATH?=layout/reftests/reftest.list reftest-ipc: diff --git a/toolkit/components/passwordmgr/test/Makefile.in b/toolkit/components/passwordmgr/test/Makefile.in index f8fe444e87e..dd36ea71d50 100644 --- a/toolkit/components/passwordmgr/test/Makefile.in +++ b/toolkit/components/passwordmgr/test/Makefile.in @@ -76,6 +76,7 @@ MOCHI_TESTS = \ test_bug_391514.html \ test_bug_427033.html \ test_bug_444968.html \ + test_bug_627616.html \ test_master_password.html \ test_master_password_cleanup.html \ test_prompt_async.html \ diff --git a/toolkit/components/passwordmgr/test/authenticate.sjs b/toolkit/components/passwordmgr/test/authenticate.sjs index 7c2102fd0db..58da655cf98 100644 --- a/toolkit/components/passwordmgr/test/authenticate.sjs +++ b/toolkit/components/passwordmgr/test/authenticate.sjs @@ -15,24 +15,26 @@ function reallyHandleRequest(request, response) { // Allow the caller to drive how authentication is processed via the query. // Eg, http://localhost:8888/authenticate.sjs?user=foo&realm=bar - var query = request.queryString; + // The extra ? allows the user/pass/realm checks to succeed if the name is + // at the beginning of the query string. + var query = "?" + request.queryString; var expected_user = "", expected_pass = "", realm = "mochitest"; var proxy_expected_user = "", proxy_expected_pass = "", proxy_realm = "mochi-proxy"; - var huge = false, plugin = false; + var huge = false, plugin = false, anonymous = false; var authHeaderCount = 1; // user=xxx - match = /user=([^&]*)/.exec(query); + match = /[^_]user=([^&]*)/.exec(query); if (match) expected_user = match[1]; // pass=xxx - match = /pass=([^&]*)/.exec(query); + match = /[^_]pass=([^&]*)/.exec(query); if (match) expected_pass = match[1]; // realm=xxx - match = /realm=([^&]*)/.exec(query); + match = /[^_]realm=([^&]*)/.exec(query); if (match) realm = match[1]; @@ -66,6 +68,10 @@ function reallyHandleRequest(request, response) { if (match) authHeaderCount = match[1]+0; + // anonymous=1 + match = /anonymous=1/.exec(query); + if (match) + anonymous = true; // Look for an authentication header, if any, in the request. // @@ -74,8 +80,9 @@ function reallyHandleRequest(request, response) { // This test only supports Basic auth. The value sent by the client is // "username:password", obscured with base64 encoding. - var actual_user = "", actual_pass = "", authHeader; + var actual_user = "", actual_pass = "", authHeader, authPresent = false; if (request.hasHeader("Authorization")) { + authPresent = true; authHeader = request.getHeader("Authorization"); match = /Basic (.+)/.exec(authHeader); if (match.length != 2) @@ -115,16 +122,24 @@ function reallyHandleRequest(request, response) { requestProxyAuth = false; } - if (requestProxyAuth) { - response.setStatusLine("1.0", 407, "Proxy authentication required"); - for (i = 0; i < authHeaderCount; ++i) - response.setHeader("Proxy-Authenticate", "basic realm=\"" + proxy_realm + "\"", true); - } else if (requestAuth) { - response.setStatusLine("1.0", 401, "Authentication required"); - for (i = 0; i < authHeaderCount; ++i) - response.setHeader("WWW-Authenticate", "basic realm=\"" + realm + "\"", true); + if (anonymous) { + if (authPresent) { + response.setStatusLine("1.0", 400, "Unexpected authorization header found"); + } else { + response.setStatusLine("1.0", 200, "Authorization header not found"); + } } else { - response.setStatusLine("1.0", 200, "OK"); + if (requestProxyAuth) { + response.setStatusLine("1.0", 407, "Proxy authentication required"); + for (i = 0; i < authHeaderCount; ++i) + response.setHeader("Proxy-Authenticate", "basic realm=\"" + proxy_realm + "\"", true); + } else if (requestAuth) { + response.setStatusLine("1.0", 401, "Authentication required"); + for (i = 0; i < authHeaderCount; ++i) + response.setHeader("WWW-Authenticate", "basic realm=\"" + realm + "\"", true); + } else { + response.setStatusLine("1.0", 200, "OK"); + } } response.setHeader("Content-Type", "application/xhtml+xml", false); diff --git a/toolkit/components/passwordmgr/test/prompt_common.js b/toolkit/components/passwordmgr/test/prompt_common.js index c93d3b131f3..a25ffd17cd1 100644 --- a/toolkit/components/passwordmgr/test/prompt_common.js +++ b/toolkit/components/passwordmgr/test/prompt_common.js @@ -1,8 +1,8 @@ netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); -const Ci = Components.interfaces; +var Ci = Components.interfaces; ok(Ci != null, "Access Ci"); -const Cc = Components.classes; +var Cc = Components.classes; ok(Cc != null, "Access Cc"); var didDialog; diff --git a/toolkit/components/passwordmgr/test/test_bug_627616.html b/toolkit/components/passwordmgr/test/test_bug_627616.html new file mode 100644 index 00000000000..e4f5f0892f5 --- /dev/null +++ b/toolkit/components/passwordmgr/test/test_bug_627616.html @@ -0,0 +1,106 @@ + + + + Test bug 627616 + + + + + + + + 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 17f2763eb44..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,17 +86,27 @@ function run_test() { items = as.getItemsWithAnnotation("test2"); do_check_eq(items.length, 10); - waitForAsyncUpdates(function() { - let stmt = DBConn().createStatement( + waitForConnectionClosed(function() { + let stmt = DBConn(true).createAsyncStatement( "SELECT id FROM moz_annos " - + "UNION " + + "UNION ALL " + "SELECT id FROM moz_items_annos " ); - do_check_false(stmt.executeStep()); + 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(); - - do_test_finished(); }); - - shutdownPlaces(); } diff --git a/toolkit/components/places/tests/head_common.js b/toolkit/components/places/tests/head_common.js index dc585d0b2dc..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"; @@ -112,25 +112,32 @@ function uri(aSpec) NetUtil.newURI(aSpec); * Gets the database connection. If the Places connection is invalid it will * try to create a new connection. * + * @param [optional] aForceNewConnection + * Forces creation of a new connection to the database. When a + * connection is asyncClosed it cannot anymore schedule async statements, + * though connectionReady will keep returning true (Bug 726990). + * * @return The database connection or null if unable to get one. */ let gDBConn; -function DBConn() { - let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase) - .DBConnection; - if (db.connectionReady) - return db; +function DBConn(aForceNewConnection) { + if (!aForceNewConnection) { + let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase) + .DBConnection; + if (db.connectionReady) + return db; + } // If the Places database connection has been closed, create a new connection. - if (!gDBConn) { + if (!gDBConn || aForceNewConnection) { let file = Services.dirsvc.get('ProfD', Ci.nsIFile); file.append("places.sqlite"); - gDBConn = Services.storage.openDatabase(file); + let dbConn = gDBConn = Services.storage.openDatabase(file); // Be sure to cleanly close this connection. - Services.obs.addObserver(function (aSubject, aTopic, aData) { - Services.obs.removeObserver(arguments.callee, aTopic); - gDBConn.asyncClose(); + Services.obs.addObserver(function DBCloseCallback(aSubject, aTopic, aData) { + Services.obs.removeObserver(DBCloseCallback, aTopic); + dbConn.asyncClose(); }, "profile-before-change", false); } @@ -637,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 6846920af8d..b310d5ddbc0 100644 --- a/toolkit/components/places/tests/inline/head_autocomplete.js +++ b/toolkit/components/places/tests/inline/head_autocomplete.js @@ -23,7 +23,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "gHistory", /** * @param aSearches - * Array of AutoCompleteSearch names. + * Array of AutoCompleteSearch names. */ function AutoCompleteInput(aSearches) { this.searches = aSearches; @@ -145,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) { @@ -191,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_autocomplete_functional.js b/toolkit/components/places/tests/inline/test_autocomplete_functional.js new file mode 100644 index 00000000000..6836e3024e9 --- /dev/null +++ b/toolkit/components/places/tests/inline/test_autocomplete_functional.js @@ -0,0 +1,148 @@ +/* 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/. */ + +// Functional tests for inline autocomplete + +add_autocomplete_test([ + "Add urls, check for correct order", + "vis", + "visit2.mozilla.org/", + function () + { + let urls = [{ url: NetUtil.newURI("http://visit1.mozilla.org"), + transition: undefined, + }, + { url: NetUtil.newURI("http://visit2.mozilla.org"), + transition: TRANSITION_TYPED, + }]; + addVisits(urls); + } +]); + +add_autocomplete_test([ + "Add urls, make sure www and http are ignored", + "visit1", + "visit1.mozilla.org/", + function () + { + + let urls = [{ url: NetUtil.newURI("http://www.visit1.mozilla.org"), + transition: undefined, + }, + ]; + addVisits(urls); + } +]); + +add_autocomplete_test([ + "Autocompleting after an existing host completes to the url", + "visit3.mozilla.org/", + "visit3.mozilla.org/", + function () + { + + let urls = [{ url: NetUtil.newURI("http://www.visit3.mozilla.org"), + transition: undefined, + }, + ]; + addVisits(urls); + } +]); + +add_autocomplete_test([ + "Searching for www.me should yield www.me.mozilla.org/", + "www.me", + "www.me.mozilla.org/", + function () + { + + let urls = [{ url: NetUtil.newURI("http://www.me.mozilla.org"), + transition: undefined, + }, + ]; + addVisits(urls); + } +]); + +add_autocomplete_test([ + "With a bookmark and history, the query result should be the bookmark", + "bookmark", + "bookmark1.mozilla.org/", + function () + { + + let urls = [{ url: NetUtil.newURI("http://bookmark1.mozilla.org/foo"), + transition: undefined, + }, + ]; + addBookmark({ url: "http://bookmark1.mozilla.org/", }); + addVisits(urls); + } +]); + +add_autocomplete_test([ + "Check to make sure we get the proper results with full paths", + "smokey", + "smokey.mozilla.org/", + function () + { + + let urls = [{ url: NetUtil.newURI("http://smokey.mozilla.org/foo/bar/baz?bacon=delicious"), + transition: undefined, + }, + { url: NetUtil.newURI("http://smokey.mozilla.org/foo/bar/baz?bacon=smokey"), + transition: undefined, + }, + ]; + addVisits(urls); + } +]); + +add_autocomplete_test([ + "Check to make sure we autocomplete to the following '/'", + "smokey.mozilla.org/fo", + "smokey.mozilla.org/foo/", + function () + { + + let urls = [{ url: NetUtil.newURI("http://smokey.mozilla.org/foo/bar/baz?bacon=delicious"), + transition: undefined, + }, + { url: NetUtil.newURI("http://smokey.mozilla.org/foo/bar/baz?bacon=smokey"), + transition: undefined, + }, + ]; + addVisits(urls); + } +]); + +add_autocomplete_test([ + "Check to make sure we autocomplete after ?", + "smokey.mozilla.org/foo?", + "smokey.mozilla.org/foo?bacon=delicious", + function () + { + + let urls = [{ url: NetUtil.newURI("http://smokey.mozilla.org/foo?bacon=delicious"), + transition: undefined, + }, + ]; + addVisits(urls); + } +]); + +add_autocomplete_test([ + "Check to make sure we autocomplete after #", + "smokey.mozilla.org/foo?bacon=delicious#bar", + "smokey.mozilla.org/foo?bacon=delicious#bar", + function () + { + + let urls = [{ url: NetUtil.newURI("http://smokey.mozilla.org/foo?bacon=delicious#bar"), + transition: undefined, + }, + ]; + addVisits(urls); + } +]); 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 41a0312c6e0..7556fbc7ea9 100644 --- a/toolkit/components/places/tests/inline/xpcshell.ini +++ b/toolkit/components/places/tests/inline/xpcshell.ini @@ -2,5 +2,7 @@ head = head_autocomplete.js 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..360ac7d4a96 100644 --- a/toolkit/components/telemetry/TelemetryHistograms.h +++ b/toolkit/components/telemetry/TelemetryHistograms.h @@ -344,6 +344,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/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/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/content/widgets/videocontrols.css b/toolkit/content/widgets/videocontrols.css index ec38d974b6b..ab9975701b6 100644 --- a/toolkit/content/widgets/videocontrols.css +++ b/toolkit/content/widgets/videocontrols.css @@ -27,7 +27,7 @@ } .controlsSpacer[hideCursor] { - cursor: none; + cursor: none; } /* CSS Transitions 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/mozapps/installer/packager.mk b/toolkit/mozapps/installer/packager.mk index 1b3b37fa7a0..61b5a868297 100644 --- a/toolkit/mozapps/installer/packager.mk +++ b/toolkit/mozapps/installer/packager.mk @@ -342,7 +342,7 @@ UPLOAD_EXTRA_FILES += robocop.apk UPLOAD_EXTRA_FILES += fennec_ids.txt ROBOCOP_PATH = $(call core_abspath,$(_ABS_DIST)/../build/mobile/robocop) INNER_ROBOCOP_PACKAGE= \ - $(PYTHON) $(topsrcdir)/build/mobile/robocop/parse_ids.py -i $(call core_abspath,$(DEPTH)/mobile/android/base/R.java) -o $(call core_abspath,$(DEPTH)/build/mobile/robocop/fennec_ids.txt) && \ + $(PYTHON) $(abspath $(topsrcdir)/build/mobile/robocop/parse_ids.py) -i $(call core_abspath,$(DEPTH)/mobile/android/base/R.java) -o $(call core_abspath,$(DEPTH)/build/mobile/robocop/fennec_ids.txt) && \ $(NSINSTALL) $(call core_abspath,$(DEPTH)/build/mobile/robocop/fennec_ids.txt) $(_ABS_DIST) && \ $(APKBUILDER) $(_ABS_DIST)/robocop-raw.apk -v $(APKBUILDER_FLAGS) -z $(ROBOCOP_PATH)/robocop.ap_ -f $(ROBOCOP_PATH)/classes.dex && \ $(JARSIGNER) $(_ABS_DIST)/robocop-raw.apk && \ diff --git a/toolkit/mozapps/update/common/updatelogging.cpp b/toolkit/mozapps/update/common/updatelogging.cpp index 051a0840a86..285e6730dd7 100644 --- a/toolkit/mozapps/update/common/updatelogging.cpp +++ b/toolkit/mozapps/update/common/updatelogging.cpp @@ -51,7 +51,7 @@ UpdateLog::UpdateLog() : logFP(NULL) { } -void UpdateLog::Init(NS_tchar* sourcePath, NS_tchar* fileName) +void UpdateLog::Init(NS_tchar* sourcePath, const NS_tchar* fileName) { if (logFP) return; diff --git a/toolkit/mozapps/update/common/updatelogging.h b/toolkit/mozapps/update/common/updatelogging.h index e53f9e6bc2e..9d0b4fb999e 100644 --- a/toolkit/mozapps/update/common/updatelogging.h +++ b/toolkit/mozapps/update/common/updatelogging.h @@ -50,7 +50,7 @@ public: return primaryLog; } - void Init(NS_tchar* sourcePath, NS_tchar* fileName); + void Init(NS_tchar* sourcePath, const NS_tchar* fileName); void Finish(); void Flush(); void Printf(const char *fmt, ... ); diff --git a/toolkit/mozapps/update/updater/updater.cpp b/toolkit/mozapps/update/updater/updater.cpp index 68e6bcd27e6..50c22537e2a 100644 --- a/toolkit/mozapps/update/updater/updater.cpp +++ b/toolkit/mozapps/update/updater/updater.cpp @@ -1452,6 +1452,7 @@ WriteStatusApplying() return true; } +#ifdef MOZ_MAINTENANCE_SERVICE /* * Read the update.status file and sets isPendingService to true if * the status is set to pending-service. @@ -1486,7 +1487,9 @@ IsUpdateStatusPending(bool &isPendingService) sizeof(kPendingService) - 1) == 0; return isPending; } +#endif +#ifdef XP_WIN /* * Read the update.status file and sets isSuccess to true if * the status is set to succeeded. @@ -1516,7 +1519,6 @@ IsUpdateStatusSucceeded(bool &isSucceeded) return true; } -#ifdef XP_WIN static void WaitForServiceFinishThread(void *param) { @@ -2555,7 +2557,6 @@ int DoUpdate() ActionList list; NS_tchar *line; bool isFirstAction = true; - bool isComplete = false; while((line = mstrtok(kNL, &rb)) != 0) { // skip comments @@ -2572,7 +2573,6 @@ int DoUpdate() const NS_tchar *type = mstrtok(kQuote, &line); LOG(("UPDATE TYPE " LOG_S "\n", type)); if (NS_tstrcmp(type, NS_T("complete")) == 0) { - isComplete = true; rv = AddPreCompleteActions(&list); if (rv) return rv; diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index af51a6f648a..85860800f5c 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -1967,8 +1967,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) @@ -1989,22 +2008,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 4cd206a9858..84e2de05561 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -156,7 +156,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 3fcadd2cf94..387a9011ee6 100644 --- a/widget/android/AndroidJavaWrappers.cpp +++ b/widget/android/AndroidJavaWrappers.cpp @@ -547,6 +547,19 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj) break; } + case ACTIVITY_STOPPING: + case ACTIVITY_START: + case ACTIVITY_PAUSING: + case ACTIVITY_RESUMING: { + mFlags = jenv->GetIntField(jobj, jFlagsField); + 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 751c5be6e03..3ae573c1a6f 100644 --- a/widget/android/AndroidJavaWrappers.h +++ b/widget/android/AndroidJavaWrappers.h @@ -569,6 +569,8 @@ public: VISITED = 21, NETWORK_CHANGED = 22, PROXIMITY_EVENT = 23, + ACTIVITY_RESUMING = 24, + SCREENSHOT = 25, dummy_java_enum_list_end }; diff --git a/widget/android/AndroidMediaLayer.cpp b/widget/android/AndroidMediaLayer.cpp index 572dadcc46b..d5691c3c29f 100644 --- a/widget/android/AndroidMediaLayer.cpp +++ b/widget/android/AndroidMediaLayer.cpp @@ -45,7 +45,7 @@ namespace mozilla { AndroidMediaLayer::AndroidMediaLayer() - : mInverted(false) { + : mInverted(false), mVisible(true) { } AndroidMediaLayer::~AndroidMediaLayer() { @@ -132,6 +132,8 @@ void AndroidMediaLayer::SetNativeWindowDimensions(void* aWindow, const gfxRect& } void AndroidMediaLayer::UpdatePosition(const gfxRect& aRect, float aZoomLevel) { + if (!mVisible) + return; std::map::iterator it; @@ -152,4 +154,24 @@ void AndroidMediaLayer::UpdatePosition(const gfxRect& aRect, float aZoomLevel) { } } +void AndroidMediaLayer::SetVisible(bool aVisible) { + if (aVisible == mVisible) + return; + + mVisible = aVisible; + if (mVisible) + return; + + // Hide all surfaces + std::map::iterator it; + + if (EnsureContentSurface()) + AndroidBridge::Bridge()->HideSurface(mContentData.surface); + + for (it = mVideoSurfaces.begin(); it != mVideoSurfaces.end(); it++) { + SurfaceData* data = it->second; + AndroidBridge::Bridge()->HideSurface(data->surface); + } +} + } /* mozilla */ diff --git a/widget/android/AndroidMediaLayer.h b/widget/android/AndroidMediaLayer.h index eb402743fc1..a5b01e6e080 100644 --- a/widget/android/AndroidMediaLayer.h +++ b/widget/android/AndroidMediaLayer.h @@ -41,15 +41,17 @@ #include #include #include "gfxRect.h" +#include "nsISupports.h" namespace mozilla { class AndroidMediaLayer { public: - AndroidMediaLayer(); virtual ~AndroidMediaLayer(); + + NS_INLINE_DECL_REFCOUNTING(AndroidMediaLayer) void* GetNativeWindowForContent(); @@ -68,8 +70,15 @@ public: mInverted = aInverted; } + bool IsVisible() { + return mVisible; + } + + void SetVisible(bool aVisible); + private: bool mInverted; + bool mVisible; class SurfaceData { public: diff --git a/widget/android/nsAppShell.cpp b/widget/android/nsAppShell.cpp index 581c21b9272..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: { @@ -370,6 +372,9 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) } case AndroidGeckoEvent::ACTIVITY_STOPPING: { + if (curEvent->Flags() > 0) + break; + nsCOMPtr obsServ = mozilla::services::GetObserverService(); NS_NAMED_LITERAL_STRING(minimize, "heap-minimize"); @@ -395,6 +400,14 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) } case AndroidGeckoEvent::ACTIVITY_PAUSING: { + if (curEvent->Flags() == 0) { + // We aren't transferring to one of our own activities, so set + // background status + nsCOMPtr obsServ = + mozilla::services::GetObserverService(); + obsServ->NotifyObservers(nsnull, "application-background", nsnull); + } + // We really want to send a notification like profile-before-change, // but profile-before-change ends up shutting some things down instead // of flushing data @@ -413,6 +426,9 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) } case AndroidGeckoEvent::ACTIVITY_START: { + if (curEvent->Flags() > 0) + break; + nsCOMPtr obsServ = mozilla::services::GetObserverService(); obsServ->NotifyObservers(nsnull, "application-foreground", nsnull); @@ -420,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: { @@ -483,6 +516,17 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) break; } + case AndroidGeckoEvent::ACTIVITY_RESUMING: { + if (curEvent->Flags() == 0) { + // We didn't return from one of our own activities, so restore + // to foreground status + nsCOMPtr obsServ = + mozilla::services::GetObserverService(); + obsServ->NotifyObservers(nsnull, "application-foreground", nsnull); + } + break; + } + default: nsWindow::OnGlobalAndroidEvent(curEvent); } @@ -547,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/cocoa/nsMenuItemX.h b/widget/cocoa/nsMenuItemX.h index f858c16780c..0cff11e4e92 100644 --- a/widget/cocoa/nsMenuItemX.h +++ b/widget/cocoa/nsMenuItemX.h @@ -90,6 +90,8 @@ public: nsresult DispatchDOMEvent(const nsString &eventName, bool* preventDefaultCalled); void SetupIcon(); + static PRUint32 ConvertGeckoToMacKeyCode(nsAString& aKeyCodeName); + protected: void UncheckRadioSiblings(nsIContent* inCheckedElement); void SetKeyEquiv(); diff --git a/widget/cocoa/nsMenuItemX.mm b/widget/cocoa/nsMenuItemX.mm index 0b8e5fc6043..f1328aeee76 100644 --- a/widget/cocoa/nsMenuItemX.mm +++ b/widget/cocoa/nsMenuItemX.mm @@ -89,6 +89,159 @@ nsMenuItemX::~nsMenuItemX() NS_OBJC_END_TRY_ABORT_BLOCK; } +struct macKeyCodeData { + const char* str; + size_t strlength; + PRUint32 keycode; +}; + +static const macKeyCodeData gMacKeyCodes[] = { + +#define KEYCODE_ENTRY(str, code) {#str, sizeof(#str) - 1, code} + + KEYCODE_ENTRY(VK_CANCEL, 0x001B), + KEYCODE_ENTRY(VK_DELETE, NSBackspaceCharacter), + KEYCODE_ENTRY(VK_BACK, NSBackspaceCharacter), + KEYCODE_ENTRY(VK_BACK_SPACE, NSBackspaceCharacter), + KEYCODE_ENTRY(VK_TAB, NSTabCharacter), + KEYCODE_ENTRY(VK_CLEAR, NSClearLineFunctionKey), + KEYCODE_ENTRY(VK_RETURN, NSEnterCharacter), + KEYCODE_ENTRY(VK_ENTER, NSEnterCharacter), + KEYCODE_ENTRY(VK_SHIFT, 0), + KEYCODE_ENTRY(VK_CONTROL, 0), + KEYCODE_ENTRY(VK_ALT, 0), + KEYCODE_ENTRY(VK_PAUSE, NSPauseFunctionKey), + KEYCODE_ENTRY(VK_CAPS_LOCK, 0), + KEYCODE_ENTRY(VK_ESCAPE, 0), + KEYCODE_ENTRY(VK_SPACE, ' '), + KEYCODE_ENTRY(VK_PAGE_UP, NSPageUpFunctionKey), + KEYCODE_ENTRY(VK_PAGE_DOWN, NSPageDownFunctionKey), + KEYCODE_ENTRY(VK_END, NSEndFunctionKey), + KEYCODE_ENTRY(VK_HOME, NSHomeFunctionKey), + KEYCODE_ENTRY(VK_LEFT, NSLeftArrowFunctionKey), + KEYCODE_ENTRY(VK_UP, NSUpArrowFunctionKey), + KEYCODE_ENTRY(VK_RIGHT, NSRightArrowFunctionKey), + KEYCODE_ENTRY(VK_DOWN, NSDownArrowFunctionKey), + KEYCODE_ENTRY(VK_PRINTSCREEN, NSPrintScreenFunctionKey), + KEYCODE_ENTRY(VK_INSERT, NSInsertFunctionKey), + KEYCODE_ENTRY(VK_HELP, NSHelpFunctionKey), + KEYCODE_ENTRY(VK_0, '0'), + KEYCODE_ENTRY(VK_1, '1'), + KEYCODE_ENTRY(VK_2, '2'), + KEYCODE_ENTRY(VK_3, '3'), + KEYCODE_ENTRY(VK_4, '4'), + KEYCODE_ENTRY(VK_5, '5'), + KEYCODE_ENTRY(VK_6, '6'), + KEYCODE_ENTRY(VK_7, '7'), + KEYCODE_ENTRY(VK_8, '8'), + KEYCODE_ENTRY(VK_9, '9'), + KEYCODE_ENTRY(VK_SEMICOLON, ':'), + KEYCODE_ENTRY(VK_EQUALS, '='), + KEYCODE_ENTRY(VK_A, 'A'), + KEYCODE_ENTRY(VK_B, 'B'), + KEYCODE_ENTRY(VK_C, 'C'), + KEYCODE_ENTRY(VK_D, 'D'), + KEYCODE_ENTRY(VK_E, 'E'), + KEYCODE_ENTRY(VK_F, 'F'), + KEYCODE_ENTRY(VK_G, 'G'), + KEYCODE_ENTRY(VK_H, 'H'), + KEYCODE_ENTRY(VK_I, 'I'), + KEYCODE_ENTRY(VK_J, 'J'), + KEYCODE_ENTRY(VK_K, 'K'), + KEYCODE_ENTRY(VK_L, 'L'), + KEYCODE_ENTRY(VK_M, 'M'), + KEYCODE_ENTRY(VK_N, 'N'), + KEYCODE_ENTRY(VK_O, 'O'), + KEYCODE_ENTRY(VK_P, 'P'), + KEYCODE_ENTRY(VK_Q, 'Q'), + KEYCODE_ENTRY(VK_R, 'R'), + KEYCODE_ENTRY(VK_S, 'S'), + KEYCODE_ENTRY(VK_T, 'T'), + KEYCODE_ENTRY(VK_U, 'U'), + KEYCODE_ENTRY(VK_V, 'V'), + KEYCODE_ENTRY(VK_W, 'W'), + KEYCODE_ENTRY(VK_X, 'X'), + KEYCODE_ENTRY(VK_Y, 'Y'), + KEYCODE_ENTRY(VK_Z, 'Z'), + KEYCODE_ENTRY(VK_CONTEXT_MENU, NSMenuFunctionKey), + KEYCODE_ENTRY(VK_NUMPAD0, '0'), + KEYCODE_ENTRY(VK_NUMPAD1, '1'), + KEYCODE_ENTRY(VK_NUMPAD2, '2'), + KEYCODE_ENTRY(VK_NUMPAD3, '3'), + KEYCODE_ENTRY(VK_NUMPAD4, '4'), + KEYCODE_ENTRY(VK_NUMPAD5, '5'), + KEYCODE_ENTRY(VK_NUMPAD6, '6'), + KEYCODE_ENTRY(VK_NUMPAD7, '7'), + KEYCODE_ENTRY(VK_NUMPAD8, '8'), + KEYCODE_ENTRY(VK_NUMPAD9, '9'), + KEYCODE_ENTRY(VK_MULTIPLY, '*'), + KEYCODE_ENTRY(VK_ADD, '+'), + KEYCODE_ENTRY(VK_SEPARATOR, 0), + KEYCODE_ENTRY(VK_SUBTRACT, '-'), + KEYCODE_ENTRY(VK_DECIMAL, '.'), + KEYCODE_ENTRY(VK_DIVIDE, '/'), + KEYCODE_ENTRY(VK_F1, NSF1FunctionKey), + KEYCODE_ENTRY(VK_F2, NSF2FunctionKey), + KEYCODE_ENTRY(VK_F3, NSF3FunctionKey), + KEYCODE_ENTRY(VK_F4, NSF4FunctionKey), + KEYCODE_ENTRY(VK_F5, NSF5FunctionKey), + KEYCODE_ENTRY(VK_F6, NSF6FunctionKey), + KEYCODE_ENTRY(VK_F7, NSF7FunctionKey), + KEYCODE_ENTRY(VK_F8, NSF8FunctionKey), + KEYCODE_ENTRY(VK_F9, NSF9FunctionKey), + KEYCODE_ENTRY(VK_F10, NSF10FunctionKey), + KEYCODE_ENTRY(VK_F11, NSF11FunctionKey), + KEYCODE_ENTRY(VK_F12, NSF12FunctionKey), + KEYCODE_ENTRY(VK_F13, NSF13FunctionKey), + KEYCODE_ENTRY(VK_F14, NSF14FunctionKey), + KEYCODE_ENTRY(VK_F15, NSF15FunctionKey), + KEYCODE_ENTRY(VK_F16, NSF16FunctionKey), + KEYCODE_ENTRY(VK_F17, NSF17FunctionKey), + KEYCODE_ENTRY(VK_F18, NSF18FunctionKey), + KEYCODE_ENTRY(VK_F19, NSF19FunctionKey), + KEYCODE_ENTRY(VK_F20, NSF20FunctionKey), + KEYCODE_ENTRY(VK_F21, NSF21FunctionKey), + KEYCODE_ENTRY(VK_F22, NSF22FunctionKey), + KEYCODE_ENTRY(VK_F23, NSF23FunctionKey), + KEYCODE_ENTRY(VK_F24, NSF24FunctionKey), + KEYCODE_ENTRY(VK_NUM_LOCK, NSClearLineFunctionKey), + KEYCODE_ENTRY(VK_SCROLL_LOCK, NSScrollLockFunctionKey), + KEYCODE_ENTRY(VK_COMMA, ','), + KEYCODE_ENTRY(VK_PERIOD, '.'), + KEYCODE_ENTRY(VK_SLASH, '/'), + KEYCODE_ENTRY(VK_BACK_QUOTE, '`'), + KEYCODE_ENTRY(VK_OPEN_BRACKET, '['), + KEYCODE_ENTRY(VK_BACK_SLASH, '\\'), + KEYCODE_ENTRY(VK_CLOSE_BRACKET, ']'), + KEYCODE_ENTRY(VK_QUOTE, '\'') + +#undef KEYCODE_ENTRY + +}; + +PRUint32 nsMenuItemX::ConvertGeckoToMacKeyCode(nsAString& aKeyCodeName) +{ + if (aKeyCodeName.IsEmpty()) { + return 0; + } + + nsCAutoString keyCodeName; + keyCodeName.AssignWithConversion(aKeyCodeName); + // We want case-insensitive comparison with data stored as uppercase. + ToUpperCase(keyCodeName); + + PRUint32 keyCodeNameLength = keyCodeName.Length(); + const char* keyCodeNameStr = keyCodeName.get(); + for (PRUint16 i = 0; i < (sizeof(gMacKeyCodes) / sizeof(gMacKeyCodes[0])); ++i) { + if (keyCodeNameLength == gMacKeyCodes[i].strlength && + nsCRT::strcmp(gMacKeyCodes[i].str, keyCodeNameStr) == 0) { + return gMacKeyCodes[i].keycode; + } + } + + return 0; +} + nsresult nsMenuItemX::Create(nsMenuX* aParent, const nsString& aLabel, EMenuItemType aItemType, nsMenuGroupOwnerX* aMenuGroupOwner, nsIContent* aNode) { @@ -274,8 +427,20 @@ void nsMenuItemX::SetKeyEquiv() if (!keyValue.IsEmpty() && mContent->GetCurrentDoc()) { nsIContent *keyContent = mContent->GetCurrentDoc()->GetElementById(keyValue); if (keyContent) { - nsAutoString keyChar(NS_LITERAL_STRING(" ")); - keyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::key, keyChar); + nsAutoString keyChar; + bool hasKey = keyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::key, keyChar); + + if (!hasKey || keyChar.IsEmpty()) { + nsAutoString keyCodeName; + keyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::keycode, keyCodeName); + PRUint32 keycode = ConvertGeckoToMacKeyCode(keyCodeName); + if (keycode) { + keyChar.Assign(keycode); + } + else { + keyChar.Assign(NS_LITERAL_STRING(" ")); + } + } nsAutoString modifiersStr; keyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::modifiers, modifiersStr); 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 eb5e2879581..0f17dfc32b9 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/windows/nsLookAndFeel.cpp b/widget/windows/nsLookAndFeel.cpp index 844411bfaad..6d437c8d7f1 100644 --- a/widget/windows/nsLookAndFeel.cpp +++ b/widget/windows/nsLookAndFeel.cpp @@ -46,6 +46,9 @@ #include "nsStyleConsts.h" #include "nsUXThemeData.h" #include "nsUXThemeConstants.h" +#include "WinUtils.h" + +using namespace mozilla::widget; typedef UINT (CALLBACK *SHAppBarMessagePtr)(DWORD, PAPPBARDATA); SHAppBarMessagePtr gSHAppBarMessage = NULL; @@ -199,7 +202,8 @@ nsLookAndFeel::NativeGetColor(ColorID aID, nscolor &aColor) idx = COLOR_HIGHLIGHT; break; case eColorID__moz_menubarhovertext: - if (!nsUXThemeData::sIsVistaOrLater || !nsUXThemeData::isAppThemed()) + if (WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION || + !nsUXThemeData::isAppThemed()) { idx = nsUXThemeData::sFlatMenus ? COLOR_HIGHLIGHTTEXT : @@ -208,7 +212,8 @@ nsLookAndFeel::NativeGetColor(ColorID aID, nscolor &aColor) } // Fall through case eColorID__moz_menuhovertext: - if (nsUXThemeData::IsAppThemed() && nsUXThemeData::sIsVistaOrLater) + if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION && + nsUXThemeData::IsAppThemed()) { res = ::GetColorFromTheme(eUXMenu, MENU_POPUPITEM, MPI_HOT, TMT_TEXTCOLOR, aColor); @@ -284,7 +289,8 @@ nsLookAndFeel::NativeGetColor(ColorID aID, nscolor &aColor) idx = COLOR_3DFACE; break; case eColorID__moz_win_mediatext: - if (nsUXThemeData::IsAppThemed() && nsUXThemeData::sIsVistaOrLater) { + if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION && + nsUXThemeData::IsAppThemed()) { res = ::GetColorFromTheme(eUXMediaToolbar, TP_BUTTON, TS_NORMAL, TMT_TEXTCOLOR, aColor); if (NS_SUCCEEDED(res)) @@ -294,7 +300,8 @@ nsLookAndFeel::NativeGetColor(ColorID aID, nscolor &aColor) idx = COLOR_WINDOWTEXT; break; case eColorID__moz_win_communicationstext: - if (nsUXThemeData::IsAppThemed() && nsUXThemeData::sIsVistaOrLater) + if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION && + nsUXThemeData::IsAppThemed()) { res = ::GetColorFromTheme(eUXCommunicationsToolbar, TP_BUTTON, TS_NORMAL, TMT_TEXTCOLOR, aColor); @@ -519,11 +526,5 @@ PRUnichar nsLookAndFeel::GetPasswordCharacterImpl() { #define UNICODE_BLACK_CIRCLE_CHAR 0x25cf - static PRUnichar passwordCharacter = 0; - if (!passwordCharacter) { - passwordCharacter = '*'; - if (nsUXThemeData::sIsXPOrLater) - passwordCharacter = UNICODE_BLACK_CIRCLE_CHAR; - } - return passwordCharacter; + return UNICODE_BLACK_CIRCLE_CHAR; } diff --git a/widget/windows/nsNativeThemeWin.cpp b/widget/windows/nsNativeThemeWin.cpp index 2360bd89e2a..be3002e90d5 100644 --- a/widget/windows/nsNativeThemeWin.cpp +++ b/widget/windows/nsNativeThemeWin.cpp @@ -412,7 +412,7 @@ static void OffsetBackgroundRect(RECT& rect, CaptionButton button) { HANDLE nsNativeThemeWin::GetTheme(PRUint8 aWidgetType) { - if (!nsUXThemeData::sIsVistaOrLater) { + if (WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION) { // On XP or earlier, render dropdowns as textfields; // doing it the right way works fine with the MS themes, // but breaks on a lot of custom themes (presumably because MS @@ -431,8 +431,9 @@ nsNativeThemeWin::GetTheme(PRUint8 aWidgetType) case NS_THEME_TEXTFIELD_MULTILINE: return nsUXThemeData::GetTheme(eUXEdit); case NS_THEME_TOOLTIP: - // BUG #161600: XP/2K3 should force a classic treatment of tooltips - return nsUXThemeData::sIsVistaOrLater ? nsUXThemeData::GetTheme(eUXTooltip) : NULL; + // XP/2K3 should force a classic treatment of tooltips + return WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION ? + NULL : nsUXThemeData::GetTheme(eUXTooltip); case NS_THEME_TOOLBOX: return nsUXThemeData::GetTheme(eUXRebar); case NS_THEME_WIN_MEDIA_TOOLBOX: @@ -557,7 +558,7 @@ nsresult nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, PRUint8 aWidgetType, PRInt32& aPart, PRInt32& aState) { - if (!nsUXThemeData::sIsVistaOrLater) { + if (WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION) { // See GetTheme if (aWidgetType == NS_THEME_DROPDOWN) aWidgetType = NS_THEME_TEXTFIELD; @@ -634,7 +635,7 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, PRUint8 aWidgetType, case NS_THEME_TEXTFIELD_MULTILINE: { nsEventStates eventState = GetContentState(aFrame, aWidgetType); - if (nsUXThemeData::sIsVistaOrLater) { + if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) { /* Note: the NOSCROLL type has a rounded corner in each * corner. The more specific HSCROLL, VSCROLL, HVSCROLL types * have side and/or top/bottom edges rendered as straight @@ -701,9 +702,11 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, PRUint8 aWidgetType, // we have to return aPart = -1. aPart = -1; } else if (IsVerticalProgress(stateFrame)) { - aPart = nsUXThemeData::sIsVistaOrLater ? PP_FILLVERT : PP_CHUNKVERT; + aPart = WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION ? + PP_FILLVERT : PP_CHUNKVERT; } else { - aPart = nsUXThemeData::sIsVistaOrLater ? PP_FILL : PP_CHUNK; + aPart = WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION ? + PP_FILL : PP_CHUNK; } aState = TS_NORMAL; @@ -776,7 +779,8 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, PRUint8 aWidgetType, aState += TS_ACTIVE; else if (eventState.HasState(NS_EVENT_STATE_HOVER)) aState += TS_HOVER; - else if (nsUXThemeData::sIsVistaOrLater && parentState.HasState(NS_EVENT_STATE_HOVER)) + else if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION && + parentState.HasState(NS_EVENT_STATE_HOVER)) aState = (aWidgetType - NS_THEME_SCROLLBAR_BUTTON_UP) + SP_BUTTON_IMPLICIT_HOVER_BASE; else aState += TS_NORMAL; @@ -864,7 +868,7 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, PRUint8 aWidgetType, case NS_THEME_SCROLLBAR: case NS_THEME_SCROLLBAR_SMALL: { aState = 0; - if (nsUXThemeData::sIsVistaOrLater) { + if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) { // On vista, they have a part aPart = RP_BACKGROUND; } else { @@ -996,7 +1000,8 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, PRUint8 aWidgetType, aFrame = parentFrame; nsEventStates eventState = GetContentState(aFrame, aWidgetType); - aPart = nsUXThemeData::sIsVistaOrLater ? CBP_DROPMARKER_VISTA : CBP_DROPMARKER; + aPart = WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION ? + CBP_DROPMARKER_VISTA : CBP_DROPMARKER; // For HTML controls with author styling, we should fall // back to the old dropmarker style to avoid clashes with @@ -1016,7 +1021,7 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, PRUint8 aWidgetType, else isOpen = IsOpenButton(aFrame); - if (nsUXThemeData::sIsVistaOrLater) { + if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) { if (isHTML || IsMenuListEditable(aFrame)) { if (isOpen) { /* Hover is propagated, but we need to know whether we're @@ -1487,7 +1492,8 @@ RENDER_AGAIN: // On vista, choose our own colors and draw an XP style half focus rect // for focused checkboxes and a full rect when active. - if (nsUXThemeData::sIsVistaOrLater && aWidgetType == NS_THEME_CHECKBOX) { + if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION && + aWidgetType == NS_THEME_CHECKBOX) { LOGBRUSH lb; lb.lbStyle = BS_SOLID; lb.lbColor = RGB(255,255,255); @@ -1596,7 +1602,8 @@ RENDER_AGAIN: bool indeterminate = IsIndeterminateProgress(stateFrame, eventStates); bool vertical = IsVerticalProgress(stateFrame); - if (indeterminate || nsUXThemeData::sIsVistaOrLater) { + if (indeterminate || + WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) { if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 30)) { NS_WARNING("unable to animate progress widget!"); } @@ -1608,7 +1615,7 @@ RENDER_AGAIN: * indeterminate progress bars. */ PRInt32 overlaySize; - if (nsUXThemeData::sIsVistaOrLater) { + if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) { if (vertical) { overlaySize = indeterminate ? kProgressVerticalIndeterminateOverlaySize : kProgressVerticalOverlaySize; @@ -1652,13 +1659,14 @@ RENDER_AGAIN: PRInt32 overlayPart; if (vertical) { - if (nsUXThemeData::sIsVistaOrLater) { + if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) { overlayPart = indeterminate ? PP_MOVEOVERLAY : PP_MOVEOVERLAYVERT; } else { overlayPart = PP_CHUNKVERT; } } else { - overlayPart = nsUXThemeData::sIsVistaOrLater ? PP_MOVEOVERLAY : PP_CHUNK; + overlayPart = WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION ? + PP_MOVEOVERLAY : PP_CHUNK; } nsUXThemeData::drawThemeBG(theme, hdc, overlayPart, state, &overlayRect, @@ -1823,7 +1831,7 @@ nsNativeThemeWin::GetWidgetPadding(nsDeviceContext* aContext, return true; } - if (nsUXThemeData::sIsVistaOrLater) { + if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) { if (aWidgetType == NS_THEME_TEXTFIELD || aWidgetType == NS_THEME_TEXTFIELD_MULTILINE || aWidgetType == NS_THEME_DROPDOWN) @@ -1916,7 +1924,7 @@ nsNativeThemeWin::GetWidgetOverflow(nsDeviceContext* aContext, * a border only shows up if the widget is being hovered. */ #if 0 - if (nsUXThemeData::sIsVistaOrLater) { + if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) { /* We explicitly draw dropdown buttons in HTML content 1px bigger * up, right, and bottom so that they overlap the dropdown's border * like they're supposed to. @@ -2042,7 +2050,7 @@ nsNativeThemeWin::GetMinimumWidgetSize(nsRenderingContext* aContext, nsIFrame* a *aIsOverridable = false; // on Vista, GetThemePartAndState returns odd values for // scale thumbs, so use a hardcoded size instead. - if (nsUXThemeData::sIsVistaOrLater) { + if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) { if (aWidgetType == NS_THEME_SCALE_THUMB_HORIZONTAL) { aResult->width = 12; aResult->height = 20; @@ -2217,7 +2225,7 @@ nsNativeThemeWin::WidgetStateChanged(nsIFrame* aFrame, PRUint8 aWidgetType, } // On Vista, the scrollbar buttons need to change state when the track has/doesn't have hover - if (!nsUXThemeData::sIsVistaOrLater && + if (WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION && (aWidgetType == NS_THEME_SCROLLBAR_TRACK_VERTICAL || aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL)) { *aShouldRepaint = false; @@ -2226,7 +2234,7 @@ nsNativeThemeWin::WidgetStateChanged(nsIFrame* aFrame, PRUint8 aWidgetType, // We need to repaint the dropdown arrow in vista HTML combobox controls when // the control is closed to get rid of the hover effect. - if (nsUXThemeData::sIsVistaOrLater && + if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION && (aWidgetType == NS_THEME_DROPDOWN || aWidgetType == NS_THEME_DROPDOWN_BUTTON) && IsHTMLContent(aFrame)) { diff --git a/widget/windows/nsUXThemeData.cpp b/widget/windows/nsUXThemeData.cpp index f526a507a13..b4ea9d7761a 100644 --- a/widget/windows/nsUXThemeData.cpp +++ b/widget/windows/nsUXThemeData.cpp @@ -69,10 +69,6 @@ nsUXThemeData::sDwmDLL = NULL; BOOL nsUXThemeData::sFlatMenus = FALSE; -bool -nsUXThemeData::sIsXPOrLater = false; -bool -nsUXThemeData::sIsVistaOrLater = false; bool nsUXThemeData::sTitlebarInfoPopulatedAero = false; bool nsUXThemeData::sTitlebarInfoPopulatedThemed = false; @@ -121,10 +117,6 @@ nsUXThemeData::Initialize() ::ZeroMemory(sThemes, sizeof(sThemes)); NS_ASSERTION(!sThemeDLL, "nsUXThemeData being initialized twice!"); - WinUtils::WinVersion version = WinUtils::GetWindowsVersion(); - sIsXPOrLater = version >= WinUtils::WINXP_VERSION; - sIsVistaOrLater = version >= WinUtils::VISTA_VERSION; - if (GetThemeDLL()) { openTheme = (OpenThemeDataPtr)GetProcAddress(sThemeDLL, "OpenThemeData"); closeTheme = (CloseThemeDataPtr)GetProcAddress(sThemeDLL, "CloseThemeData"); @@ -166,17 +158,9 @@ nsUXThemeData::Invalidate() { sThemes[i] = NULL; } } - if (sIsXPOrLater) { - BOOL useFlat = false; - sFlatMenus = ::SystemParametersInfo(SPI_GETFLATMENU, 0, &useFlat, 0) ? - useFlat : false; - } else { - // Contrary to Microsoft's documentation, SPI_GETFLATMENU will not fail - // on Windows 2000, and it is also possible (though unlikely) for WIN2K - // to be misconfigured in such a way that it would return true, so we - // shall give WIN2K special treatment - sFlatMenus = false; - } + BOOL useFlat = false; + sFlatMenus = ::SystemParametersInfo(SPI_GETFLATMENU, 0, &useFlat, 0) ? + useFlat : false; } HANDLE @@ -193,7 +177,7 @@ nsUXThemeData::GetTheme(nsUXThemeClass cls) { HMODULE nsUXThemeData::GetThemeDLL() { - if (!sThemeDLL && sIsXPOrLater) + if (!sThemeDLL) sThemeDLL = ::LoadLibraryW(kThemeLibraryName); return sThemeDLL; } @@ -201,7 +185,7 @@ nsUXThemeData::GetThemeDLL() { #if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN HMODULE nsUXThemeData::GetDwmDLL() { - if (!sDwmDLL && sIsVistaOrLater) + if (!sDwmDLL && WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) sDwmDLL = ::LoadLibraryW(kDwmLibraryName); return sDwmDLL; } diff --git a/widget/windows/nsUXThemeData.h b/widget/windows/nsUXThemeData.h index 9e9369852e1..1e6026dd812 100644 --- a/widget/windows/nsUXThemeData.h +++ b/widget/windows/nsUXThemeData.h @@ -128,8 +128,6 @@ public: static const PRUnichar kDwmLibraryName[]; #endif static BOOL sFlatMenus; - static bool sIsXPOrLater; - static bool sIsVistaOrLater; static bool sTitlebarInfoPopulatedAero; static bool sTitlebarInfoPopulatedThemed; static SIZE sCommandButtons[4]; diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 9c677d7dbd1..98fcd712d88 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -1273,7 +1273,8 @@ NS_METHOD nsWindow::IsVisible(bool & bState) // transparency. These routines are called on size and move operations. void nsWindow::ClearThemeRegion() { - if (nsUXThemeData::sIsVistaOrLater && !HasGlass() && + if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION && + !HasGlass() && (mWindowType == eWindowType_popup && !IsPopupWithTitleBar() && (mPopupType == ePopupTypeTooltip || mPopupType == ePopupTypePanel))) { SetWindowRgn(mWnd, NULL, false); @@ -1287,7 +1288,8 @@ void nsWindow::SetThemeRegion() // so default constants are used for part and state. At some point we might need part and // state values from nsNativeThemeWin's GetThemePartAndState, but currently windows that // change shape based on state haven't come up. - if (nsUXThemeData::sIsVistaOrLater && !HasGlass() && + if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION && + !HasGlass() && (mWindowType == eWindowType_popup && !IsPopupWithTitleBar() && (mPopupType == ePopupTypeTooltip || mPopupType == ePopupTypePanel))) { HRGN hRgn = nsnull; @@ -6357,7 +6359,7 @@ nsWindow::InitMouseWheelScrollData() if (!::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &sMouseWheelScrollChars, 0)) { - NS_ASSERTION(!nsUXThemeData::sIsVistaOrLater, + NS_ASSERTION(WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION, "Failed to get SPI_GETWHEELSCROLLCHARS"); sMouseWheelScrollChars = 1; } else if (sMouseWheelScrollChars > WHEEL_DELTA) { diff --git a/widget/xpwidgets/nsBaseWidget.h b/widget/xpwidgets/nsBaseWidget.h index 4f9647134d7..9e48cd2122e 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/build/Makefile.in b/xpcom/build/Makefile.in index d7f00ba2ad8..dc3ee3d10ca 100644 --- a/xpcom/build/Makefile.in +++ b/xpcom/build/Makefile.in @@ -79,7 +79,6 @@ SHARED_LIBRARY_LIBS = \ ../io/$(LIB_PREFIX)xpcomio_s.$(LIB_SUFFIX) \ ../components/$(LIB_PREFIX)xpcomcomponents_s.$(LIB_SUFFIX) \ ../threads/$(LIB_PREFIX)xpcomthreads_s.$(LIB_SUFFIX) \ - ../proxy/src/$(LIB_PREFIX)xpcomproxy_s.$(LIB_SUFFIX) \ ../base/$(LIB_PREFIX)xpcombase_s.$(LIB_SUFFIX) \ ../reflect/xptcall/src/$(LIB_PREFIX)xptcall.$(LIB_SUFFIX) \ ../reflect/xptcall/src/$(LIB_PREFIX)xptcmd.$(LIB_SUFFIX) \ diff --git a/xpcom/build/nsXPCOMCIDInternal.h b/xpcom/build/nsXPCOMCIDInternal.h index bc2185fdfea..be5d17ff3ba 100644 --- a/xpcom/build/nsXPCOMCIDInternal.h +++ b/xpcom/build/nsXPCOMCIDInternal.h @@ -71,12 +71,6 @@ */ #define NS_THREADPOOL_CONTRACTID "@mozilla.org/thread-pool;1" -/** - * The global proxy object manager. This component is a singleton. - * @implement nsIProxyObjectManager - */ -#define NS_XPCOMPROXY_CONTRACTID "@mozilla.org/xpcomproxy;1" - /** * The contract id for the nsIXULAppInfo service. */ diff --git a/xpcom/glue/nsCycleCollectionParticipant.h b/xpcom/glue/nsCycleCollectionParticipant.h index 7bcefbebd33..50a55f8da96 100644 --- a/xpcom/glue/nsCycleCollectionParticipant.h +++ b/xpcom/glue/nsCycleCollectionParticipant.h @@ -299,6 +299,10 @@ public: #define NS_CYCLE_COLLECTION_UPCAST(obj, clazz) \ NS_CYCLE_COLLECTION_CLASSNAME(clazz)::Upcast(obj) +/////////////////////////////////////////////////////////////////////////////// +// Helpers for implementing CanSkip methods +/////////////////////////////////////////////////////////////////////////////// + #define NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(_class) \ NS_IMETHODIMP_(bool) \ NS_CYCLE_COLLECTION_CLASSNAME(_class)::CanSkipReal(void *p, \ @@ -310,6 +314,7 @@ public: _class *tmp = Downcast(s); #define NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END \ + (void)tmp; \ return false; \ } @@ -323,6 +328,7 @@ public: _class *tmp = Downcast(s); #define NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END \ + (void)tmp; \ return false; \ } @@ -336,6 +342,7 @@ public: _class *tmp = Downcast(s); #define NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END \ + (void)tmp; \ return false; \ } @@ -614,6 +621,24 @@ NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE #define NS_DECL_CYCLE_COLLECTION_CLASS(_class) \ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(_class, _class) +// Cycle collector helper for ambiguous classes that can sometimes be skipped. +#define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS_AMBIGUOUS(_class, _base) \ +class NS_CYCLE_COLLECTION_INNERCLASS \ + : public nsXPCOMCycleCollectionParticipant \ +{ \ +public: \ + NS_CYCLE_COLLECTION_INNERCLASS () : nsXPCOMCycleCollectionParticipant(true) {} \ + NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base) \ +protected: \ + NS_IMETHOD_(bool) CanSkipReal(void *p, bool aRemovingAllowed); \ + NS_IMETHOD_(bool) CanSkipInCCReal(void *p); \ + NS_IMETHOD_(bool) CanSkipThisReal(void *p); \ +}; \ +NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE + +#define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS(_class) \ + NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS_AMBIGUOUS(_class, _class) + // Cycle collector helper for classes that don't want to unlink anything. // Note: if this is used a lot it might make sense to have a base class that // doesn't do anything in Root/Unlink/Unroot. 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()