diff --git a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/browser.js b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/browser.js index 3fe5e164ab1..514f0748415 100644 --- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/browser.js +++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/browser.js @@ -152,7 +152,7 @@ var TestPilotMenuUtils; * whether they opened with Firefox on startup or were opened later. */ TestPilotWindowHandlers.setUpToolbarFeedbackButton(); - if (TestPilotSetup.startupComplete) { + if (TestPilotSetup && TestPilotSetup.startupComplete) { TestPilotSetup.onWindowLoad(window); } else { let observerSvc = Cc["@mozilla.org/observer-service;1"] diff --git a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/debug.html b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/debug.html index fbb8cda5cfd..9257fbd3f92 100644 --- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/debug.html +++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/debug.html @@ -171,6 +171,34 @@ task.changeStatus(newStatus, false); } + function showIndexFileDropdown() { + var prefService = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch); + var prefName = "extensions.testpilot.indexFileName"; + var selector = document.getElementById("index-file-selector"); + if (prefService.getCharPref(prefName) == "index.json") { + selector.selectedIndex = 0; + } else { + selector.selectedIndex = 1; + } + } + + function setSelectedIndexFile() { + var prefService = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch); + var prefName = "extensions.testpilot.indexFileName"; + var selector = document.getElementById("index-file-selector"); + var i = selector.selectedIndex; + prefService.setCharPref( prefName, selector.options[i].value ); + + // DELETE CACHED INDEX FILE + var file = Components.classes["@mozilla.org/file/directory_service;1"]. + getService(Components.interfaces.nsIProperties). + get("ProfD", Components.interfaces.nsIFile); + file.append("TestPilotExperimentFiles"); + file.append("index.json"); + if (file.exists()) { + file.remove(false); + } + } @@ -180,7 +208,7 @@ - +

Current Status = . @@ -208,6 +236,11 @@ or set it to +Index file: +

diff --git a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/experiment-page.js b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/experiment-page.js index 6f99dcf15e6..5400171c40b 100644 --- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/experiment-page.js +++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/experiment-page.js @@ -60,7 +60,7 @@ var stringBundle; function uploadData() { Components.utils.import("resource://testpilot/modules/setup.js"); - let eid = parseInt(getUrlParam("eid")); + let eid = getUrlParam("eid"); let task = TestPilotSetup.getTaskById(eid); // If always-submit-checkbox is checked, set the pref @@ -97,7 +97,7 @@ var stringBundle; function deleteData() { Components.utils.import("resource://testpilot/modules/setup.js"); Components.utils.import("resource://testpilot/modules/tasks.js"); - let eid = parseInt(getUrlParam("eid")); + let eid = getUrlParam("eid"); let task = TestPilotSetup.getTaskById(eid); task.dataStore.wipeAllData(); // reload the URL after wiping all data. @@ -150,7 +150,7 @@ var stringBundle; const nsIFilePicker = Components.interfaces.nsIFilePicker; let filePicker = Components.classes["@mozilla.org/filepicker;1"]. createInstance(nsIFilePicker); - let eid = parseInt(getUrlParam("eid")); + let eid = getUrlParam("eid"); let task = TestPilotSetup.getTaskById(eid); filePicker.init(window, null, nsIFilePicker.modeSave); @@ -268,7 +268,7 @@ var stringBundle; function onQuitPageLoad() { Components.utils.import("resource://testpilot/modules/setup.js"); setStrings(PAGE_TYPE_QUIT); - let eid = parseInt(getUrlParam("eid")); + let eid = getUrlParam("eid"); let task = TestPilotSetup.getTaskById(eid); let header = document.getElementById("about-quit-title"); header.innerHTML = @@ -285,7 +285,7 @@ var stringBundle; function quitExperiment() { Components.utils.import("resource://testpilot/modules/setup.js"); Components.utils.import("resource://testpilot/modules/tasks.js"); - let eid = parseInt(getUrlParam("eid")); + let eid = getUrlParam("eid"); let reason = document.getElementById("reason-for-quit").value; let task = TestPilotSetup.getTaskById(eid); task.optOut(reason, function(success) { @@ -306,7 +306,7 @@ var stringBundle; function updateRecurSettings() { Components.utils.import("resource://testpilot/modules/setup.js"); - let eid = parseInt(getUrlParam("eid")); + let eid = getUrlParam("eid"); let experiment = TestPilotSetup.getTaskById(eid); let recurSelector = document.getElementById("recur-selector"); let newValue = recurSelector.options[recurSelector.selectedIndex].value; @@ -367,9 +367,7 @@ var stringBundle; var contentDiv = document.getElementById("experiment-specific-text"); var dataPrivacyDiv = document.getElementById("data-privacy-text"); // Get experimentID from the GET args of page - // TODO no reason actually to do parseInt here -- all it accomplishes - // is preventing us from using non-numeric study IDs. - var eid = parseInt(getUrlParam("eid")); + var eid = getUrlParam("eid"); var experiment = TestPilotSetup.getTaskById(eid); if (!experiment) { // Possible that experiments aren't done loading yet. Try again in diff --git a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/survey-generator.js b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/survey-generator.js index 3d42e8d4a0c..6dfb15c2de1 100644 --- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/survey-generator.js +++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/survey-generator.js @@ -1,3 +1,40 @@ +/* ***** 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 Test Pilot. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jono X + * Raymond Lee + * + * 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 ***** */ + const MULTIPLE_CHOICE = 0; const CHECK_BOXES_WITH_FREE_ENTRY = 1; const SCALE = 2; @@ -58,9 +95,16 @@ function onBuiltinSurveyLoad() { } function drawSurveyForm(task, contentDiv) { - let oldAnswers = task.oldAnswers; let surveyQuestions = task.surveyQuestions; + /* Fill form fields with old survey answers if available -- + * but not if the survey version has changed since you stored them!! + * (bug 576482) */ + let oldAnswers = null; + if (task.oldAnswers && (task.version == task.oldAnswers["version_number"])) { + oldAnswers = task.oldAnswers["answers"]; + } + let submitButton = document.getElementById("survey-submit"); submitButton.setAttribute("style", ""); let changeButton = document.getElementById("change-answers"); diff --git a/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/remote-experiment-loader.js b/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/remote-experiment-loader.js index 7577388d334..cdfdacf49d6 100644 --- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/remote-experiment-loader.js +++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/remote-experiment-loader.js @@ -185,7 +185,7 @@ exports.RemoteExperimentLoader.prototype = { } else { this._fileGetter = downloadFile; } - this._logger.trace("About to instantiate preferences store."); + this._logger.trace("About to instantiate jar store."); this._jarStore = new JarStore(); this._experimentFileNames = []; let self = this; @@ -324,11 +324,17 @@ exports.RemoteExperimentLoader.prototype = { // TODO a bad thing that can go wrong: If we have a net connection but the index file // has not changed, we currently don't try to download anything... + // The logic is bad because executeCachedIndexFile is called in two different + // cases: the one with no network, and the one with network but unchanged file. // Another bad thing: If there's a jar download that's corrupt or unreadable or has - // the wrong permissions or something, we need to kill it and download a new one. + // the wrong permissions or something, we need to kill it and download a new one. + // Should also try to download a new jar if any required modules are missing + // (Which is currently the case!) - // WTF every jar file I'm downloading appears as 0 bytes with __x__x___ permissions! + // (module "about_firefox.js" is not found; there is no about_firefox.jar on disk, + // indicating it didn't download, and we're not trying again because index-dev is + // unmodified. Hmmm.) _cachedIndexNsiFile: null, get cachedIndexNsiFile() { diff --git a/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/tasks.js b/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/tasks.js index a9d9364a300..38d543810de 100644 --- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/tasks.js +++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/tasks.js @@ -120,6 +120,10 @@ var TestPilotTask = { return this._id; }, + get version() { + return this._versionNumber; + }, + get taskType() { return null; }, @@ -248,15 +252,16 @@ var TestPilotTask = { } }; -function TestPilotExperiment(expInfo, dataStore, handlers, webContent) { +function TestPilotExperiment(expInfo, dataStore, handlers, webContent, dateOverrideFunc) { // All four of these are objects defined in the remote experiment file - this._init(expInfo, dataStore, handlers, webContent); + this._init(expInfo, dataStore, handlers, webContent, dateOverrideFunc); } TestPilotExperiment.prototype = { _init: function TestPilotExperiment__init(expInfo, dataStore, handlers, - webContent) { + webContent, + dateOverrideFunc) { /* expInfo is a dictionary defined in the remote experiment code, which * should have the following properties: * startDate (string representation of date) @@ -270,6 +275,14 @@ TestPilotExperiment.prototype = { * recursAutomatically (boolean) * recurrenceInterval (number of days) * versionNumber (int) */ + + // dateOverrideFunc: For unit testing. Optional. If provided, will be called + // instead of Date.now() for determining the current time. + if (dateOverrideFunc) { + this._now = dateOverrideFunc; + } else { + this._now = Date.now; + } this._taskInit(expInfo.testId, expInfo.testName, expInfo.testInfoUrl, expInfo.summary, expInfo.thumbnail); this._webContent = webContent; @@ -293,8 +306,8 @@ TestPilotExperiment.prototype = { this._startDate = Date.parse(expInfo.startDate); Application.prefs.setValue(prefName, expInfo.startDate); } else { - this._startDate = Date.now(); - Application.prefs.setValue(prefName, (new Date()).toString()); + this._startDate = this._now(); + Application.prefs.setValue(prefName, (new Date(this._startDate)).toString()); } } @@ -580,7 +593,7 @@ TestPilotExperiment.prototype = { checkDate: function TestPilotExperiment_checkDate() { // This method handles all date-related status changes and should be // called periodically. - let currentDate = Date.now(); + let currentDate = this._now(); // Reset automatically recurring tests: if (this._recursAutomatically && @@ -774,7 +787,7 @@ TestPilotExperiment.prototype = { self._uploadRetryTimer.cancel(); // Stop retrying - it worked! } self.changeStatus(TaskConstants.STATUS_SUBMITTED); - self._dateForDataDeletion = Date.now() + TIME_FOR_DATA_DELETION; + self._dateForDataDeletion = self._now() + TIME_FOR_DATA_DELETION; self._expirationDateForDataSubmission = null; callback(true); } else { @@ -919,8 +932,7 @@ TestPilotBuiltinSurvey.prototype = { return null; } else { this._logger.info("Trying to json.parse this: " + surveyResults); - let surveyJson = sanitizeJSONStrings( JSON.parse(surveyResults) ); - return surveyJson["answers"]; + return sanitizeJSONStrings( JSON.parse(surveyResults) ); } }, diff --git a/browser/app/profile/extensions/testpilot@labs.mozilla.com/skin/all/css/screen-standalone.css b/browser/app/profile/extensions/testpilot@labs.mozilla.com/skin/all/css/screen-standalone.css index 78b2b38c9b3..4ba0a4e6ae8 100644 --- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/skin/all/css/screen-standalone.css +++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/skin/all/css/screen-standalone.css @@ -55,7 +55,7 @@ body { border-radius: 0.5em; -webkit-border-radius: 0.5em; background: rgba(220, 240, 247, 0.8) url('chrome://testpilot/skin/images/callout.png') no-repeat top center; - //display: inline; + /* display: inline; */ } #container { @@ -124,7 +124,7 @@ body { rgba(255, 255, 255, 1) 0 1px, rgba(133, 153, 166, 0.3) 0px 1px 8.5px; background-color: #e7eaec; - //display: inline; + /* display: inline; */ } .home_button { @@ -143,7 +143,7 @@ body { rgba(255, 255, 255, 1) 0 1px, rgba(133, 153, 166, 0.3) 0px 1px 8.5px; background-color: #e7eaec; - //display: inline; + /* display: inline; */ } .callout { @@ -158,7 +158,7 @@ body { inset rgba(185, 221, 234, 0.2) 0 -10px 8.5px, inset rgba(185, 221, 234, 1) 0 0px 1px, inset rgba(255, 255, 255, 0.2) 0 10px 8.5px; - //display: inline; + /* display: inline; */ } #data-privacy-text { @@ -169,7 +169,7 @@ body { .home_callout { font-size: 16px; vertical-align: middle; - width: 280px; + /* width: 280px;*/ padding: 8px 24px; margin: 8px auto; color: rgba(0, 0, 0, 0.8); @@ -180,7 +180,7 @@ body { inset rgba(185, 221, 234, 0.2) 0 -10px 8.5px, inset rgba(185, 221, 234, 1) 0 0px 1px, inset rgba(255, 255, 255, 0.2) 0 10px 8.5px; - //display: inline; + /* display: inline; */ } .home_callout_continue { @@ -197,7 +197,7 @@ body { inset rgba(185, 221, 234, 0.2) 0 -10px 8.5px, inset rgba(185, 221, 234, 1) 0 0px 1px, inset rgba(255, 255, 255, 0.2) 0 10px 8.5px; - //display: inline; + /* display: inline; */ } .home_callout_continue a {color: #6c9735; text-decoration: none;} diff --git a/browser/app/profile/extensions/testpilot@labs.mozilla.com/skin/linux/feedback.css b/browser/app/profile/extensions/testpilot@labs.mozilla.com/skin/linux/feedback.css index 6e309f5ac33..43ed112fd28 100644 --- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/skin/linux/feedback.css +++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/skin/linux/feedback.css @@ -1,7 +1,3 @@ -#feedback-menu-button { - -moz-appearance: button; -} - #pilot-notification-popup { -moz-appearance: none; background-color: Menu; diff --git a/browser/app/profile/extensions/testpilot@labs.mozilla.com/tests/test_data_store.js b/browser/app/profile/extensions/testpilot@labs.mozilla.com/tests/test_data_store.js index a63838ed88b..7e1ca748b1a 100644 --- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/tests/test_data_store.js +++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/tests/test_data_store.js @@ -1,5 +1,7 @@ EXPORTED_SYMBOLS = ["runAllTests"]; var Cu = Components.utils; +var Cc = Components.classes; +var Ci = Components.interfaces; var testsRun = 0; var testsPassed = 0; @@ -314,6 +316,221 @@ function testRemoteLoaderIndexCache() { cheapAssertEqual(remoteLoader._loadCachedIndexFile(), data); } + +function StubDataStore(fileName, tableName, columns) { +} +StubDataStore.prototype = { + storeEvent: function(uiEvent, callback) { + callback(true); + }, + getJSONRows: function(callback) { + callback([]); + }, + getAllDataAsJSON: function(useDisplayValues, callback) { + callback([]); + }, + wipeAllData: function(callback) { + callback(true); + }, + nukeTable: function() { + }, + haveData: function(callback) { + callback(true); + }, + getHumanReadableColumnNames: function() { + }, + getPropertyNames: function() { + } +}; + +function StubWebContent() { +} +StubWebContent.prototype = { + inProgressHtml: "", + completedHtml: "", + upcomingHtml: "", + canceledHtml: "", + remainDataHtml: "", + dataExpiredHtml: "", + deletedRemainDataHtml: "", + inProgressDataPrivacyHtml: "", + completedDataPrivacyHtml: "", + canceledDataPrivacyHtml: "", + dataExpiredDataPrivacyHtml: "", + remainDataPrivacyHtml: "", + deletedRemainDataPrivacyHtml: "", + onPageLoad: function(experiment, document, graphUtils) { + } +}; + +function StubHandlers() { +} +StubHandlers.prototype = { + onNewWindow: function(window) { + }, + onWindowClosed: function(window) { + }, + onAppStartup: function() { + }, + onAppShutdown: function() { + }, + onExperimentStartup: function(store) { + }, + onExperimentShutdown: function() { + }, + onEnterPrivateBrowsing: function() { + }, + onExitPrivateBrowsing: function() { + }, + uninstallAll: function() { + } +}; + + +function testRecurringStudyStateChange() { + + Cu.import("resource://testpilot/modules/tasks.js"); + + let expInfo = { + startDate: null, + duration: 7, + testName: "Unit Test Recurring Study", + testId: "unit_test_recur_study", + testInfoUrl: "https://testpilot.mozillalabs.com/", + summary: "Be sure to wipe all prefs and the store in the setup/teardown", + thumbnail: "", + optInRequired: false, + recursAutomatically: true, + recurrenceInterval: 30, + versionNumber: 1 + }; + + dump("Looking for prefs to delete...\n"); + let prefService = Cc["@mozilla.org/preferences-service;1"] + .getService(Ci.nsIPrefService) + .QueryInterface(Ci.nsIPrefBranch2); + let prefStem = "extensions.testpilot"; + let prefNames = prefService.getChildList(prefStem); + for each (let prefName in prefNames) { + if (prefName.indexOf("unit_test_recur_study") != -1) { + dump("Clearing pref " + prefName + "\n"); + prefService.clearUserPref(prefName); + } + } + + const START_DATE = 1292629441000; + let stubDate = START_DATE; + + let stubDateFunction = function() { + return stubDate; + }; + + let advanceDate = function(days) { + stubDate += days * 24 * 60 * 60 * 1000; + }; + + let dataStore = new StubDataStore(); + let handlers = new StubHandlers(); + let webContent = new StubWebContent(); + let task = new TestPilotExperiment(expInfo, dataStore, handlers, webContent, + stubDateFunction); + + // for this test, show popup on new study is set to true... + prefService.setBoolPref("extensions.testpilot.popup.showOnNewStudy", true); + // TODO another test with this turned off. + + cheapAssertEqual(task.id, "unit_test_recur_study", "id should be id"); + cheapAssertEqual(task.version, 1, "version should be version"); + cheapAssertEqual(task.title, "Unit Test Recurring Study", "title should be title"); + cheapAssertEqual(task.taskType, TaskConstants.TYPE_EXPERIMENT, "Should be experiment"); + // Ensure that end date is 7 days past the start date + cheapAssertEqual(task.startDate, START_DATE, "Start date is wrong"); + cheapAssertEqual(task.endDate, START_DATE + 7 * 24 * 60 * 60 * 1000, "End date is wrong"); + + cheapAssertEqual(task.status, TaskConstants.STATUS_NEW, "Status should be new"); + // advance time a little and check date... b/c showOnNewStudy is true, it won't start: + advanceDate(1); + task.checkDate(); + cheapAssertEqual(task.status, TaskConstants.STATUS_NEW, "Status should still be new"); + // Now we fake the user action needed to advance it from NEW to STARTING: + task.changeStatus(TaskConstants.STATUS_STARTING); + cheapAssertEqual(task.status, TaskConstants.STATUS_STARTING, "Status should now be starting"); + // when we check date again, it should advance from starting to in-progress: + task.checkDate(); + cheapAssertEqual(task.status, TaskConstants.STATUS_IN_PROGRESS, "Status should be in progress"); + + // Go 7 days into the future + advanceDate(7); + task.checkDate(); + cheapAssertEqual(task.status, TaskConstants.STATUS_FINISHED, "Status should be finished"); + + /* So how is the buggy bug happening? It seems like: + * + * - Data is posted successfully + * - Task status set to 6 + * - (Reschedule is happening somewhere silently in here, which is screwing things + * up maybe?) + * - Reload everything + * - It's past its end date, so the task status gets switched to Finished. + */ + + // TODO ideally figure out how to fake an upload - until then, just set status to 6. + task.changeStatus(TaskConstants.STATUS_SUBMITTED); + cheapAssertEqual(task.status, TaskConstants.STATUS_SUBMITTED, "Should be submitted."); + + // Delete and recreate task, make sure it recovers its status and start/end + // dates correctly + task = null; + let task2 = new TestPilotExperiment(expInfo, dataStore, handlers, webContent, + stubDateFunction); + cheapAssertEqual(task2.status, TaskConstants.STATUS_SUBMITTED, "Status should be submitted!"); + // It should remain submitted; it should have rescheduled itself to 30 days ahead. + cheapAssertEqual(task2.startDate, START_DATE + 30 * 24 * 60 * 60 * 1000, + "Start date is wrong"); + cheapAssertEqual(task2.endDate, START_DATE + 37 * 24 * 60 * 60 * 1000, + "End date is wrong"); + + + + // test task._reschedule()? + // basically test everything that can happen in checkDate: + /* - reset of automatically recurring test (with NEVER_SUBMIT on and with it off) when + * we pass the (new) start date. + * - test that we jumped ahead to STARTING if optInRequired is false... and that + * we don't jump ahead to starting if it's true! And the popup.showOnNewStudy pref + * should have the same effect!! + * + * - When a test finishes (status is less than finished and current date passes end date) + * then it should switch to finish. If it recurs automatically, it should then + * reschedule; if study-specific recur pref is ALWAYS_SUBMIT (OR if global + * extensions.testpilot.alwaysSubmitData is true) it should then submit, but if it's + * NEVER_SUBMIT it should then cancel and wipe data. If none of these are true + * it should just reschedule but remain on status = finished. + * + * If it ends and doesn't recur automatically, then it should autosubmit if + * extensions.testpilot.alwaysSubmitData is true. + * + * At this point, a data deletion date and an expiration date for data submission + * should be set (unless dat was already wiped as a result of NEVER_SUBMIT). + * + * + * - Data expiration (this clause looks maybe wrong in the code - why is there another + * autosubmit command here?) + * If study is finished but not submitted and we pass expiration date for data + * submission, cancel and wipe data. (Expired should really be a status code!!) + * If study is submitted and we pass the date for data deletion, then wipe the data. + * + * */ + // Trying to upload when status is already submitted or greater should be a no-op. + // Successful submit should advance us to SUBMITTED and generate/store a time for + // data deletion. + + /* getWebContent and getDataPrivacyContent should give us back the right strings + * based on all the state. */ + +} + + function runAllTests() { testTheDataStore(); testFirefoxVersionCheck(); @@ -321,10 +538,12 @@ function runAllTests() { //testTheCuddlefishPreferencesFilesystem(); //testRemoteLoader(); testRemoteLoaderIndexCache(); + testRecurringStudyStateChange(); dump("TESTING COMPLETE. " + testsPassed + " out of " + testsRun + " tests passed."); } + //exports.runAllTests = runAllTests; diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index 0a82ec3cc12..8a952ddee2a 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -474,6 +474,10 @@ window[chromehidden~="toolbar"] toolbar:not(.toolbar-primary):not(.chromeclass-m -moz-binding: url("chrome://browser/content/urlbarBindings.xml#geolocation-notification"); } +#addon-progress-notification { + -moz-binding: url("chrome://browser/content/urlbarBindings.xml#addon-progress-notification"); +} + /* override hidden="true" for the status bar compatibility shim in case it was persisted for the real status bar */ #status-bar { diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 408581c345d..a9f8881cd31 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -705,6 +705,23 @@ const gXPInstallObserver = { PopupNotifications.show(browser, notificationID, messageString, anchorID, action, null, options); break; + case "addon-install-started": + function needsDownload(aInstall) { + return aInstall.state != AddonManager.STATE_DOWNLOADED; + } + // If all installs have already been downloaded then there is no need to + // show the download progress + if (!installInfo.installs.some(needsDownload)) + return; + notificationID = "addon-progress"; + messageString = gNavigatorBundle.getString("addonDownloading"); + messageString = PluralForm.get(installInfo.installs.length, messageString); + options.installs = installInfo.installs; + options.contentWindow = browser.contentWindow; + options.sourceURI = browser.currentURI; + PopupNotifications.show(browser, notificationID, messageString, anchorID, + null, null, options); + break; case "addon-install-failed": // TODO This isn't terribly ideal for the multiple failure case installInfo.installs.forEach(function(aInstall) { @@ -1384,6 +1401,7 @@ function prepareForStartup() { function delayedStartup(isLoadingBlank, mustLoadSidebar) { Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false); Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false); + Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false); Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false); Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false); Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false); @@ -1626,6 +1644,7 @@ function BrowserShutdown() Services.obs.removeObserver(gSessionHistoryObserver, "browser:purge-session-history"); Services.obs.removeObserver(gXPInstallObserver, "addon-install-disabled"); + Services.obs.removeObserver(gXPInstallObserver, "addon-install-started"); Services.obs.removeObserver(gXPInstallObserver, "addon-install-blocked"); Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed"); Services.obs.removeObserver(gXPInstallObserver, "addon-install-complete"); diff --git a/browser/base/content/test/browser_bug553455.js b/browser/base/content/test/browser_bug553455.js index 939d3d537bd..5f3b01dd0ec 100644 --- a/browser/base/content/test/browser_bug553455.js +++ b/browser/base/content/test/browser_bug553455.js @@ -25,6 +25,7 @@ function wait_for_notification(aCallback) { PopupNotifications.panel.addEventListener("popupshown", function() { PopupNotifications.panel.removeEventListener("popupshown", arguments.callee, false); info("Saw notification"); + is(PopupNotifications.panel.childNodes.length, 1, "Should be only one notification"); aCallback(PopupNotifications.panel); }, false); } @@ -112,6 +113,10 @@ function test_blocked_install() { // Click on Allow EventUtils.synthesizeMouse(notification.button, 20, 10, {}); + // Notification should have changed to progress notification + notification = aPanel.childNodes[0]; + is(notification.id, "addon-progress-notification", "Should have seen the progress notification"); + // Wait for the install confirmation dialog wait_for_install_dialog(function(aWindow) { // Wait for the complete notification @@ -119,7 +124,7 @@ function test_blocked_install() { let notification = aPanel.childNodes[0]; is(notification.id, "addon-install-complete-notification", "Should have seen the install complete"); is(notification.button.label, "Restart Now", "Should have seen the right button"); - is(notification.getAttribute("label"), + is(notification.getAttribute("label"), "XPI Test will be installed after you restart " + gApp + ".", "Should have seen the right message"); @@ -144,28 +149,34 @@ function test_blocked_install() { }, function test_whitelisted_install() { - // Wait for the install confirmation dialog - wait_for_install_dialog(function(aWindow) { - // Wait for the complete notification - wait_for_notification(function(aPanel) { - let notification = aPanel.childNodes[0]; - is(notification.id, "addon-install-complete-notification", "Should have seen the install complete"); - is(notification.button.label, "Restart Now", "Should have seen the right button"); - is(notification.getAttribute("label"), - "XPI Test will be installed after you restart " + gApp + ".", - "Should have seen the right message"); + // Wait for the progress notification + wait_for_notification(function(aPanel) { + let notification = aPanel.childNodes[0]; + is(notification.id, "addon-progress-notification", "Should have seen the progress notification"); - AddonManager.getAllInstalls(function(aInstalls) { - is(aInstalls.length, 1, "Should be one pending install"); - aInstalls[0].cancel(); + // Wait for the install confirmation dialog + wait_for_install_dialog(function(aWindow) { + // Wait for the complete notification + wait_for_notification(function(aPanel) { + let notification = aPanel.childNodes[0]; + is(notification.id, "addon-install-complete-notification", "Should have seen the install complete"); + is(notification.button.label, "Restart Now", "Should have seen the right button"); + is(notification.getAttribute("label"), + "XPI Test will be installed after you restart " + gApp + ".", + "Should have seen the right message"); - gBrowser.removeTab(gBrowser.selectedTab); - Services.perms.remove("example.com", "install"); - runNextTest(); + AddonManager.getAllInstalls(function(aInstalls) { + is(aInstalls.length, 1, "Should be one pending install"); + aInstalls[0].cancel(); + + gBrowser.removeTab(gBrowser.selectedTab); + Services.perms.remove("example.com", "install"); + runNextTest(); + }); }); - }); - aWindow.document.documentElement.acceptDialog(); + aWindow.document.documentElement.acceptDialog(); + }); }); var pm = Services.perms; @@ -179,18 +190,24 @@ function test_whitelisted_install() { }, function test_failed_download() { - // Wait for the failed notification + // Wait for the progress notification wait_for_notification(function(aPanel) { let notification = aPanel.childNodes[0]; - is(notification.id, "addon-install-failed-notification", "Should have seen the install fail"); - is(notification.getAttribute("label"), - "The add-on could not be downloaded because of a connection failure " + - "on example.com.", - "Should have seen the right message"); + is(notification.id, "addon-progress-notification", "Should have seen the progress notification"); - gBrowser.removeTab(gBrowser.selectedTab); - Services.perms.remove("example.com", "install"); - runNextTest(); + // Wait for the failed notification + wait_for_notification(function(aPanel) { + let notification = aPanel.childNodes[0]; + is(notification.id, "addon-install-failed-notification", "Should have seen the install fail"); + is(notification.getAttribute("label"), + "The add-on could not be downloaded because of a connection failure " + + "on example.com.", + "Should have seen the right message"); + + gBrowser.removeTab(gBrowser.selectedTab); + Services.perms.remove("example.com", "install"); + runNextTest(); + }); }); var pm = Services.perms; @@ -204,18 +221,24 @@ function test_failed_download() { }, function test_corrupt_file() { - // Wait for the failed notification + // Wait for the progress notification wait_for_notification(function(aPanel) { let notification = aPanel.childNodes[0]; - is(notification.id, "addon-install-failed-notification", "Should have seen the install fail"); - is(notification.getAttribute("label"), - "The add-on downloaded from example.com could not be installed " + - "because it appears to be corrupt.", - "Should have seen the right message"); + is(notification.id, "addon-progress-notification", "Should have seen the progress notification"); - gBrowser.removeTab(gBrowser.selectedTab); - Services.perms.remove("example.com", "install"); - runNextTest(); + // Wait for the failed notification + wait_for_notification(function(aPanel) { + let notification = aPanel.childNodes[0]; + is(notification.id, "addon-install-failed-notification", "Should have seen the install fail"); + is(notification.getAttribute("label"), + "The add-on downloaded from example.com could not be installed " + + "because it appears to be corrupt.", + "Should have seen the right message"); + + gBrowser.removeTab(gBrowser.selectedTab); + Services.perms.remove("example.com", "install"); + runNextTest(); + }); }); var pm = Services.perms; @@ -229,18 +252,24 @@ function test_corrupt_file() { }, function test_incompatible() { - // Wait for the failed notification + // Wait for the progress notification wait_for_notification(function(aPanel) { let notification = aPanel.childNodes[0]; - is(notification.id, "addon-install-failed-notification", "Should have seen the install fail"); - is(notification.getAttribute("label"), - "XPI Test could not be installed because it is not compatible with " + - gApp + " " + gVersion + ".", - "Should have seen the right message"); + is(notification.id, "addon-progress-notification", "Should have seen the progress notification"); - gBrowser.removeTab(gBrowser.selectedTab); - Services.perms.remove("example.com", "install"); - runNextTest(); + // Wait for the failed notification + wait_for_notification(function(aPanel) { + let notification = aPanel.childNodes[0]; + is(notification.id, "addon-install-failed-notification", "Should have seen the install fail"); + is(notification.getAttribute("label"), + "XPI Test could not be installed because it is not compatible with " + + gApp + " " + gVersion + ".", + "Should have seen the right message"); + + gBrowser.removeTab(gBrowser.selectedTab); + Services.perms.remove("example.com", "install"); + runNextTest(); + }); }); var pm = Services.perms; @@ -254,31 +283,37 @@ function test_incompatible() { }, function test_restartless() { - // Wait for the install confirmation dialog - wait_for_install_dialog(function(aWindow) { - // Wait for the complete notification - wait_for_notification(function(aPanel) { - let notification = aPanel.childNodes[0]; - is(notification.id, "addon-install-complete-notification", "Should have seen the install complete"); - is(notification.button.label, "Open Add-ons Manager", "Should have seen the right button"); - is(notification.getAttribute("label"), - "XPI Test has been installed successfully.", - "Should have seen the right message"); + // Wait for the progress notification + wait_for_notification(function(aPanel) { + let notification = aPanel.childNodes[0]; + is(notification.id, "addon-progress-notification", "Should have seen the progress notification"); - AddonManager.getAllInstalls(function(aInstalls) { - is(aInstalls.length, 0, "Should be no pending installs"); + // Wait for the install confirmation dialog + wait_for_install_dialog(function(aWindow) { + // Wait for the complete notification + wait_for_notification(function(aPanel) { + let notification = aPanel.childNodes[0]; + is(notification.id, "addon-install-complete-notification", "Should have seen the install complete"); + is(notification.button.label, "Open Add-ons Manager", "Should have seen the right button"); + is(notification.getAttribute("label"), + "XPI Test has been installed successfully.", + "Should have seen the right message"); - AddonManager.getAddonByID("restartless-xpi@tests.mozilla.org", function(aAddon) { - aAddon.uninstall(); + AddonManager.getAllInstalls(function(aInstalls) { + is(aInstalls.length, 0, "Should be no pending installs"); - gBrowser.removeTab(gBrowser.selectedTab); - Services.perms.remove("example.com", "install"); - runNextTest(); + AddonManager.getAddonByID("restartless-xpi@tests.mozilla.org", function(aAddon) { + aAddon.uninstall(); + + gBrowser.removeTab(gBrowser.selectedTab); + Services.perms.remove("example.com", "install"); + runNextTest(); + }); }); }); - }); - aWindow.document.documentElement.acceptDialog(); + aWindow.document.documentElement.acceptDialog(); + }); }); var pm = Services.perms; @@ -292,32 +327,38 @@ function test_restartless() { }, function test_multiple() { - // Wait for the install confirmation dialog - wait_for_install_dialog(function(aWindow) { - // Wait for the complete notification - wait_for_notification(function(aPanel) { - let notification = aPanel.childNodes[0]; - is(notification.id, "addon-install-complete-notification", "Should have seen the install complete"); - is(notification.button.label, "Restart Now", "Should have seen the right button"); - is(notification.getAttribute("label"), - "2 add-ons will be installed after you restart " + gApp + ".", - "Should have seen the right message"); + // Wait for the progress notification + wait_for_notification(function(aPanel) { + let notification = aPanel.childNodes[0]; + is(notification.id, "addon-progress-notification", "Should have seen the progress notification"); - AddonManager.getAllInstalls(function(aInstalls) { - is(aInstalls.length, 1, "Should be one pending install"); - aInstalls[0].cancel(); + // Wait for the install confirmation dialog + wait_for_install_dialog(function(aWindow) { + // Wait for the complete notification + wait_for_notification(function(aPanel) { + let notification = aPanel.childNodes[0]; + is(notification.id, "addon-install-complete-notification", "Should have seen the install complete"); + is(notification.button.label, "Restart Now", "Should have seen the right button"); + is(notification.getAttribute("label"), + "2 add-ons will be installed after you restart " + gApp + ".", + "Should have seen the right message"); - AddonManager.getAddonByID("restartless-xpi@tests.mozilla.org", function(aAddon) { - aAddon.uninstall(); + AddonManager.getAllInstalls(function(aInstalls) { + is(aInstalls.length, 1, "Should be one pending install"); + aInstalls[0].cancel(); - gBrowser.removeTab(gBrowser.selectedTab); - Services.perms.remove("example.com", "install"); - runNextTest(); + AddonManager.getAddonByID("restartless-xpi@tests.mozilla.org", function(aAddon) { + aAddon.uninstall(); + + gBrowser.removeTab(gBrowser.selectedTab); + Services.perms.remove("example.com", "install"); + runNextTest(); + }); }); }); - }); - aWindow.document.documentElement.acceptDialog(); + aWindow.document.documentElement.acceptDialog(); + }); }); var pm = Services.perms; @@ -332,27 +373,33 @@ function test_multiple() { }, function test_url() { - // Wait for the install confirmation dialog - wait_for_install_dialog(function(aWindow) { - // Wait for the complete notification - wait_for_notification(function(aPanel) { - let notification = aPanel.childNodes[0]; - is(notification.id, "addon-install-complete-notification", "Should have seen the install complete"); - is(notification.button.label, "Restart Now", "Should have seen the right button"); - is(notification.getAttribute("label"), - "XPI Test will be installed after you restart " + gApp + ".", - "Should have seen the right message"); + // Wait for the progress notification + wait_for_notification(function(aPanel) { + let notification = aPanel.childNodes[0]; + is(notification.id, "addon-progress-notification", "Should have seen the progress notification"); - AddonManager.getAllInstalls(function(aInstalls) { - is(aInstalls.length, 1, "Should be one pending install"); - aInstalls[0].cancel(); + // Wait for the install confirmation dialog + wait_for_install_dialog(function(aWindow) { + // Wait for the complete notification + wait_for_notification(function(aPanel) { + let notification = aPanel.childNodes[0]; + is(notification.id, "addon-install-complete-notification", "Should have seen the install complete"); + is(notification.button.label, "Restart Now", "Should have seen the right button"); + is(notification.getAttribute("label"), + "XPI Test will be installed after you restart " + gApp + ".", + "Should have seen the right message"); - gBrowser.removeTab(gBrowser.selectedTab); - runNextTest(); + AddonManager.getAllInstalls(function(aInstalls) { + is(aInstalls.length, 1, "Should be one pending install"); + aInstalls[0].cancel(); + + gBrowser.removeTab(gBrowser.selectedTab); + runNextTest(); + }); }); - }); - aWindow.document.documentElement.acceptDialog(); + aWindow.document.documentElement.acceptDialog(); + }); }); gBrowser.selectedTab = gBrowser.addTab(); @@ -391,17 +438,22 @@ function test_wronghost() { gBrowser.removeEventListener("load", arguments.callee, true); - // Wait for the complete notification + // Wait for the progress notification wait_for_notification(function(aPanel) { let notification = aPanel.childNodes[0]; - is(notification.id, "addon-install-failed-notification", "Should have seen the install fail"); - is(notification.getAttribute("label"), - "The add-on downloaded from example.com could not be installed " + - "because it appears to be corrupt.", - "Should have seen the right message"); + is(notification.id, "addon-progress-notification", "Should have seen the progress notification"); + // Wait for the complete notification + wait_for_notification(function(aPanel) { + let notification = aPanel.childNodes[0]; + is(notification.id, "addon-install-failed-notification", "Should have seen the install fail"); + is(notification.getAttribute("label"), + "The add-on downloaded from example.com could not be installed " + + "because it appears to be corrupt.", + "Should have seen the right message"); - gBrowser.removeTab(gBrowser.selectedTab); - runNextTest(); + gBrowser.removeTab(gBrowser.selectedTab); + runNextTest(); + }); }); gBrowser.loadURI(TESTROOT + "corrupt.xpi"); @@ -410,44 +462,50 @@ function test_wronghost() { }, function test_reload() { - // Wait for the install confirmation dialog - wait_for_install_dialog(function(aWindow) { - // Wait for the complete notification - wait_for_notification(function(aPanel) { - let notification = aPanel.childNodes[0]; - is(notification.id, "addon-install-complete-notification", "Should have seen the install complete"); - is(notification.button.label, "Restart Now", "Should have seen the right button"); - is(notification.getAttribute("label"), - "XPI Test will be installed after you restart " + gApp + ".", - "Should have seen the right message"); + // Wait for the progress notification + wait_for_notification(function(aPanel) { + let notification = aPanel.childNodes[0]; + is(notification.id, "addon-progress-notification", "Should have seen the progress notification"); - function test_fail() { - ok(false, "Reloading should not have hidden the notification"); - } + // Wait for the install confirmation dialog + wait_for_install_dialog(function(aWindow) { + // Wait for the complete notification + wait_for_notification(function(aPanel) { + let notification = aPanel.childNodes[0]; + is(notification.id, "addon-install-complete-notification", "Should have seen the install complete"); + is(notification.button.label, "Restart Now", "Should have seen the right button"); + is(notification.getAttribute("label"), + "XPI Test will be installed after you restart " + gApp + ".", + "Should have seen the right message"); - PopupNotifications.panel.addEventListener("popuphiding", test_fail, false); + function test_fail() { + ok(false, "Reloading should not have hidden the notification"); + } - gBrowser.addEventListener("load", function() { - if (gBrowser.currentURI.spec != TESTROOT2 + "enabled.html") - return; + PopupNotifications.panel.addEventListener("popuphiding", test_fail, false); - gBrowser.removeEventListener("load", arguments.callee, true); + gBrowser.addEventListener("load", function() { + if (gBrowser.currentURI.spec != TESTROOT2 + "enabled.html") + return; - PopupNotifications.panel.removeEventListener("popuphiding", test_fail, false); + gBrowser.removeEventListener("load", arguments.callee, true); - AddonManager.getAllInstalls(function(aInstalls) { - is(aInstalls.length, 1, "Should be one pending install"); - aInstalls[0].cancel(); + PopupNotifications.panel.removeEventListener("popuphiding", test_fail, false); - gBrowser.removeTab(gBrowser.selectedTab); - Services.perms.remove("example.com", "install"); - runNextTest(); - }); - }, true); - gBrowser.loadURI(TESTROOT2 + "enabled.html"); + AddonManager.getAllInstalls(function(aInstalls) { + is(aInstalls.length, 1, "Should be one pending install"); + aInstalls[0].cancel(); + + gBrowser.removeTab(gBrowser.selectedTab); + Services.perms.remove("example.com", "install"); + runNextTest(); + }); + }, true); + gBrowser.loadURI(TESTROOT2 + "enabled.html"); + }); + + aWindow.document.documentElement.acceptDialog(); }); - - aWindow.document.documentElement.acceptDialog(); }); var pm = Services.perms; @@ -461,35 +519,41 @@ function test_reload() { }, function test_theme() { - // Wait for the install confirmation dialog - wait_for_install_dialog(function(aWindow) { - // Wait for the complete notification - wait_for_notification(function(aPanel) { - let notification = aPanel.childNodes[0]; - is(notification.id, "addon-install-complete-notification", "Should have seen the install complete"); - is(notification.button.label, "Restart Now", "Should have seen the right button"); - is(notification.getAttribute("label"), - "Theme Test will be installed after you restart " + gApp + ".", - "Should have seen the right message"); + // Wait for the progress notification + wait_for_notification(function(aPanel) { + let notification = aPanel.childNodes[0]; + is(notification.id, "addon-progress-notification", "Should have seen the progress notification"); - gBrowser.removeTab(gBrowser.selectedTab); - Services.perms.remove("example.com", "install"); + // Wait for the install confirmation dialog + wait_for_install_dialog(function(aWindow) { + // Wait for the complete notification + wait_for_notification(function(aPanel) { + let notification = aPanel.childNodes[0]; + is(notification.id, "addon-install-complete-notification", "Should have seen the install complete"); + is(notification.button.label, "Restart Now", "Should have seen the right button"); + is(notification.getAttribute("label"), + "Theme Test will be installed after you restart " + gApp + ".", + "Should have seen the right message"); - AddonManager.getAddonByID("{972ce4c6-7e08-4474-a285-3208198ce6fd}", function(aAddon) { - ok(aAddon.userDisabled, "Should be switching away from the default theme."); - // Undo the pending theme switch - aAddon.userDisabled = false; + gBrowser.removeTab(gBrowser.selectedTab); + Services.perms.remove("example.com", "install"); - AddonManager.getAddonByID("theme-xpi@tests.mozilla.org", function(aAddon) { - isnot(aAddon, null, "Test theme will have been installed"); - aAddon.uninstall(); + AddonManager.getAddonByID("{972ce4c6-7e08-4474-a285-3208198ce6fd}", function(aAddon) { + ok(aAddon.userDisabled, "Should be switching away from the default theme."); + // Undo the pending theme switch + aAddon.userDisabled = false; - runNextTest(); + AddonManager.getAddonByID("theme-xpi@tests.mozilla.org", function(aAddon) { + isnot(aAddon, null, "Test theme will have been installed"); + aAddon.uninstall(); + + runNextTest(); + }); }); }); - }); - aWindow.document.documentElement.acceptDialog(); + aWindow.document.documentElement.acceptDialog(); + }); }); var pm = Services.perms; @@ -544,49 +608,121 @@ function test_renotify_blocked() { }, function test_renotify_installed() { - // Wait for the install confirmation dialog - wait_for_install_dialog(function(aWindow) { - // Wait for the complete notification - wait_for_notification(function(aPanel) { - let notification = aPanel.childNodes[0]; - is(notification.id, "addon-install-complete-notification", "Should have seen the install complete"); + // Wait for the progress notification + wait_for_notification(function(aPanel) { + let notification = aPanel.childNodes[0]; + is(notification.id, "addon-progress-notification", "Should have seen the progress notification"); - // Dismiss the notification - aPanel.addEventListener("popuphidden", function () { - aPanel.removeEventListener("popuphidden", arguments.callee, false); + // Wait for the install confirmation dialog + wait_for_install_dialog(function(aWindow) { + // Wait for the complete notification + wait_for_notification(function(aPanel) { + let notification = aPanel.childNodes[0]; + is(notification.id, "addon-install-complete-notification", "Should have seen the install complete"); - // Install another - executeSoon(function () { - // Wait for the install confirmation dialog - wait_for_install_dialog(function(aWindow) { - info("Timeouts after this probably mean bug 589954 regressed"); + // Dismiss the notification + aPanel.addEventListener("popuphidden", function () { + aPanel.removeEventListener("popuphidden", arguments.callee, false); - // Wait for the complete notification + // Install another + executeSoon(function () { + // Wait for the progress notification wait_for_notification(function(aPanel) { let notification = aPanel.childNodes[0]; - is(notification.id, "addon-install-complete-notification", "Should have seen the second install complete"); + is(notification.id, "addon-progress-notification", "Should have seen the progress notification"); - AddonManager.getAllInstalls(function(aInstalls) { - is(aInstalls.length, 1, "Should be one pending installs"); - aInstalls[0].cancel(); + // Wait for the install confirmation dialog + wait_for_install_dialog(function(aWindow) { + info("Timeouts after this probably mean bug 589954 regressed"); - gBrowser.removeTab(gBrowser.selectedTab); - runNextTest(); + // Wait for the complete notification + wait_for_notification(function(aPanel) { + let notification = aPanel.childNodes[0]; + is(notification.id, "addon-install-complete-notification", "Should have seen the second install complete"); + + AddonManager.getAllInstalls(function(aInstalls) { + is(aInstalls.length, 1, "Should be one pending installs"); + aInstalls[0].cancel(); + + gBrowser.removeTab(gBrowser.selectedTab); + Services.perms.remove("example.com", "install"); + runNextTest(); + }); + }); + + aWindow.document.documentElement.acceptDialog(); }); }); - aWindow.document.documentElement.acceptDialog(); + gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers); }); + }, false); - gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers); - }); - }, false); - - // hide the panel (this simulates the user dismissing it) - aPanel.hidePopup(); + // hide the panel (this simulates the user dismissing it) + aPanel.hidePopup(); + }); + + aWindow.document.documentElement.acceptDialog(); }); + }); - aWindow.document.documentElement.acceptDialog(); + var pm = Services.perms; + pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION); + + var triggers = encodeURIComponent(JSON.stringify({ + "XPI": "unsigned.xpi" + })); + gBrowser.selectedTab = gBrowser.addTab(); + gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers); +}, + +function test_cancel_restart() { + // Wait for the progress notification + wait_for_notification(function(aPanel) { + let notification = aPanel.childNodes[0]; + is(notification.id, "addon-progress-notification", "Should have seen the progress notification"); + let button = document.getAnonymousElementByAttribute(notification, "anonid", "cancel"); + + // Cancel the download + EventUtils.synthesizeMouse(button, 2, 2, {}); + + // Downloads cannot be restarted synchronously, bug 611755 + executeSoon(function() { + // Notification should have changed to cancelled + notification = aPanel.childNodes[0]; + is(notification.id, "addon-install-cancelled-notification", "Should have seen the cancelled notification"); + + // Restart the download + EventUtils.synthesizeMouse(notification.button, 20, 10, {}); + + // Should be back to a progress notification + notification = aPanel.childNodes[0]; + is(notification.id, "addon-progress-notification", "Should have seen the progress notification"); + + // Wait for the install confirmation dialog + wait_for_install_dialog(function(aWindow) { + // Wait for the complete notification + wait_for_notification(function(aPanel) { + let notification = aPanel.childNodes[0]; + is(notification.id, "addon-install-complete-notification", "Should have seen the install complete"); + is(notification.button.label, "Restart Now", "Should have seen the right button"); + is(notification.getAttribute("label"), + "XPI Test will be installed after you restart " + gApp + ".", + "Should have seen the right message"); + + AddonManager.getAllInstalls(function(aInstalls) { + is(aInstalls.length, 1, "Should be one pending install"); + aInstalls[0].cancel(); + + gBrowser.removeTab(gBrowser.selectedTab); + Services.perms.remove("example.com", "install"); + runNextTest(); + }); + }); + + aWindow.document.documentElement.acceptDialog(); + }); + }); }); var pm = Services.perms; diff --git a/browser/base/content/test/browser_popupNotification.js b/browser/base/content/test/browser_popupNotification.js index 52c4e063bd1..9391f7fc48e 100644 --- a/browser/base/content/test/browser_popupNotification.js +++ b/browser/base/content/test/browser_popupNotification.js @@ -21,6 +21,7 @@ * Contributor(s): * Gavin Sharp * Sylvain Pasche + * Drew Willcoxon * * 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 @@ -92,13 +93,20 @@ function runNextTest() { info("[Test #" + gTestIndex + "] popup shown"); nextTest.onShown(this); }); - + + // We allow multiple onHidden functions to be defined in an array. They're + // called in the order they appear. + let onHiddenArray = nextTest.onHidden instanceof Array ? + nextTest.onHidden : + [nextTest.onHidden]; doOnPopupEvent("popuphidden", function () { - info("[Test #" + gTestIndex + "] popup hidden"); - nextTest.onHidden(this); - - goNext(); - }); + let onHidden = onHiddenArray.shift(); + info("[Test #" + gTestIndex + "] popup hidden (" + onHiddenArray.length + " hides remaining)"); + onHidden.call(nextTest, this); + if (!onHiddenArray.length) + goNext(); + }, onHiddenArray.length); + info("[Test #" + gTestIndex + "] added listeners; panel state: " + PopupNotifications.isPanelOpen); } @@ -106,12 +114,16 @@ function runNextTest() { nextTest.run(); } -function doOnPopupEvent(eventName, callback) { +function doOnPopupEvent(eventName, callback, numExpected) { gActiveListeners[eventName] = function (event) { if (event.target != PopupNotifications.panel) return; - PopupNotifications.panel.removeEventListener(eventName, gActiveListeners[eventName], false); - delete gActiveListeners[eventName]; + if (typeof(numExpected) === "number") + numExpected--; + if (!numExpected) { + PopupNotifications.panel.removeEventListener(eventName, gActiveListeners[eventName], false); + delete gActiveListeners[eventName]; + } callback.call(PopupNotifications.panel); } @@ -335,11 +347,15 @@ var tests = [ "geo anchor shouldn't be visible"); dismissNotification(popup); }, - onHidden: function (popup) { - // Remove the first notification - this.firstNotification.remove(); - ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); - } + onHidden: [ + function (popup) { + // Remove the first notification + this.firstNotification.remove(); + ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); + }, + // The removal triggers another popuphidden event. + function (popup) {} + ], }, // Test optional params { // Test #10 diff --git a/browser/base/content/urlbarBindings.xml b/browser/base/content/urlbarBindings.xml index f025c90d2a9..3bd48a69909 100644 --- a/browser/base/content/urlbarBindings.xml +++ b/browser/base/content/urlbarBindings.xml @@ -1100,6 +1100,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + document.getAnonymousElementByAttribute(this, "anonid", "progressmeter"); + + + document.getAnonymousElementByAttribute(this, "anonid", "progresstext"); + + + document.getAnonymousElementByAttribute(this, "anonid", "cancel"); + + + let utils = {}; + Components.utils.import("resource://gre/modules/DownloadUtils.jsm", utils); + utils.DownloadUtils; + + + + + + + + + + + + + + + + + + = 0) + maxProgress += aInstall.maxProgress; + if (aInstall.state < AddonManager.STATE_DOWNLOADED) + downloadingCount++; + }); + + if (downloadingCount == 0) { + this.destroy(); + PopupNotifications.remove(this.notification); + } + else { + this.setProgress(progress, maxProgress); + } + ]]> + + + + + + + + + + + + + + + + + + + + menupopup { width: 16px; min-height: 16px; max-height: 16px; - padding: 0; } .bookmark-item > .toolbarbutton-icon[label]:not([label=""]), @@ -323,12 +322,11 @@ toolbar:not([mode="icons"]) #restore-button { min-width: 0; } -.toolbarbutton-1 > .toolbarbutton-icon, -.toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon, -#restore-button > .toolbarbutton-icon { - padding: 0; - height: 20px; - width: 20px; +.toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-icon, +.toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon { + margin: 2px; + height: 16px; + width: 16px; } .toolbarbutton-1[disabled="true"] > .toolbarbutton-icon, @@ -1762,7 +1760,6 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker { .tab-close-button > .toolbarbutton-icon { -moz-margin-end: 0px !important; - padding: 0; } .tab-close-button { @@ -1849,12 +1846,6 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker { padding: 0 1px; } -.tabs-newtab-button > .toolbarbutton-icon, -:-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1 > .toolbarbutton-icon, -:-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon { - padding: 0; -} - .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled="true"]):hover, .tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled="true"]):hover, :-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1:not([type="menu-button"]):not([disabled]):not([open]):hover, @@ -2065,6 +2056,8 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker { } .popup-notification-icon[popupid="xpinstall-disabled"], +.popup-notification-icon[popupid="addon-progress"], +.popup-notification-icon[popupid="addon-install-cancelled"], .popup-notification-icon[popupid="addon-install-blocked"], .popup-notification-icon[popupid="addon-install-failed"], .popup-notification-icon[popupid="addon-install-complete"] { @@ -2073,6 +2066,37 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker { height: 32px; } +.addon-progress-description { + width: 350px; + max-width: 350px; +} + +.popup-progress-label, +.popup-progress-meter { + -moz-margin-start: 0; + -moz-margin-end: 0; +} + +.popup-progress-cancel { + -moz-appearance: none; + min-height: 16px; + min-width: 16px; + max-height: 16px; + max-width: 16px; + padding: 0; + margin: 0 1px 0 1px; + list-style-image: url(chrome://mozapps/skin/downloads/buttons.png); + -moz-image-region: rect(0px, 16px, 16px, 0px); +} + +.popup-progress-cancel:hover { + -moz-image-region: rect(0px, 32px, 16px, 16px); +} + +.popup-progress-cancel:active { + -moz-image-region: rect(0px, 48px, 16px, 32px); +} + #indexedDB-notification-icon { list-style-image: url(chrome://global/skin/icons/question-16.png); } diff --git a/browser/themes/pinstripe/browser/places/organizer.css b/browser/themes/pinstripe/browser/places/organizer.css index 1a4195849cc..101c511c3ce 100644 --- a/browser/themes/pinstripe/browser/places/organizer.css +++ b/browser/themes/pinstripe/browser/places/organizer.css @@ -111,7 +111,7 @@ } #placesToolbar > toolbarbutton > .toolbarbutton-icon { - margin: 0 4px; + margin: 1px 4px; } #placesToolbar > toolbarbutton > .toolbarbutton-text { diff --git a/browser/themes/winstripe/browser/browser.css b/browser/themes/winstripe/browser/browser.css index d6714502faa..e297d2e3b6a 100644 --- a/browser/themes/winstripe/browser/browser.css +++ b/browser/themes/winstripe/browser/browser.css @@ -1645,14 +1645,6 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action- -moz-transition: none; } -.tabs-newtab-button > .toolbarbutton-icon, -#TabsToolbar > #new-tab-button > .toolbarbutton-icon, -#TabsToolbar > toolbarpaletteitem > #new-tab-button > .toolbarbutton-icon, -#alltabs-button > .toolbarbutton-icon { - width: auto; - height: auto; -} - .tabs-newtab-button > .toolbarbutton-icon { margin-top: -1px; margin-bottom: -1px; @@ -1882,6 +1874,8 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] { } .popup-notification-icon[popupid="xpinstall-disabled"], +.popup-notification-icon[popupid="addon-progress"], +.popup-notification-icon[popupid="addon-install-cancelled"], .popup-notification-icon[popupid="addon-install-blocked"], .popup-notification-icon[popupid="addon-install-failed"], .popup-notification-icon[popupid="addon-install-complete"] { @@ -1890,6 +1884,37 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] { height: 32px; } +.addon-progress-description { + width: 350px; + max-width: 350px; +} + +.popup-progress-label, +.popup-progress-meter { + -moz-margin-start: 0; + -moz-margin-end: 0; +} + +.popup-progress-cancel { + -moz-appearance: none; + background: transparent; + border: none; + padding: 0; + margin: 0; + min-height: 0; + min-width: 0; + list-style-image: url(chrome://mozapps/skin/downloads/downloadButtons.png); + -moz-image-region: rect(0px, 32px, 16px, 16px); +} + +.popup-progress-cancel:hover { + -moz-image-region: rect(16px, 32px, 32px, 16px); +} + +.popup-progress-cancel:active { + -moz-image-region: rect(32px, 32px, 48px, 16px); +} + .popup-notification-icon[popupid="indexedDB-permissions-prompt"], .popup-notification-icon[popupid="indexedDB-quota-prompt"] { list-style-image: url(chrome://global/skin/icons/question-64.png); diff --git a/build/automation.py.in b/build/automation.py.in index 837d068b2ba..5795b6bb1df 100644 --- a/build/automation.py.in +++ b/build/automation.py.in @@ -867,7 +867,7 @@ user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless t # create certificate database for the profile certificateStatus = self.fillCertificateDB(profileDir, certPath, utilityPath, xrePath) if certificateStatus != 0: - self.log.info("TEST-UNEXPECTED FAIL | automation.py | Certificate integration failed") + self.log.info("TEST-UNEXPECTED-FAIL | automation.py | Certificate integration failed") return certificateStatus # start ssltunnel to provide https:// URLs capability @@ -926,9 +926,11 @@ user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless t def installExtension(self, extensionSource, profileDir, extensionID): if (not os.path.exists(extensionSource)): self.log.info("INFO | automation.py | Cannot install extension no source at: %s", extensionSource) + return if (not os.path.exists(profileDir)): self.log.info("INFO | automation.py | Cannot install extension invalid profileDir at: %s", profileDir) + return # See if we have an XPI or a directory if (os.path.isfile(extensionSource)): diff --git a/caps/src/nsScriptSecurityManager.cpp b/caps/src/nsScriptSecurityManager.cpp index fd486326f4a..87326e2957a 100644 --- a/caps/src/nsScriptSecurityManager.cpp +++ b/caps/src/nsScriptSecurityManager.cpp @@ -2177,6 +2177,9 @@ nsScriptSecurityManager::GetFunctionObjectPrincipal(JSContext *cx, nsresult *rv) { NS_PRECONDITION(rv, "Null out param"); + + *rv = NS_OK; + if (!JS_ObjectIsFunction(cx, obj)) { // Protect against pseudo-functions (like SJOWs). @@ -2189,8 +2192,6 @@ nsScriptSecurityManager::GetFunctionObjectPrincipal(JSContext *cx, JSFunction *fun = GET_FUNCTION_PRIVATE(cx, obj); JSScript *script = JS_GetFunctionScript(cx, fun); - *rv = NS_OK; - if (!script) { // A native function: skip it in order to find its scripted caller. diff --git a/content/base/src/nsGenericElement.cpp b/content/base/src/nsGenericElement.cpp index 7c3d4dc727f..1ee7db7d499 100644 --- a/content/base/src/nsGenericElement.cpp +++ b/content/base/src/nsGenericElement.cpp @@ -5354,10 +5354,7 @@ nsGenericElement::PostHandleEventForLinks(nsEventChainPostVisitor& aVisitor) case NS_UI_ACTIVATE: { - nsCOMPtr targetContent; - aVisitor.mPresContext->EventStateManager()-> - GetEventTargetContent(aVisitor.mEvent, getter_AddRefs(targetContent)); - if (targetContent == this) { + if (aVisitor.mEvent->originalTarget == this) { nsAutoString target; GetLinkTarget(target); nsContentUtils::TriggerLink(this, aVisitor.mPresContext, absURI, target, diff --git a/content/base/test/Makefile.in b/content/base/test/Makefile.in index c75157594fc..0a2e83f8a22 100644 --- a/content/base/test/Makefile.in +++ b/content/base/test/Makefile.in @@ -453,6 +453,8 @@ _TEST_FILES2 = \ test_treewalker_nextsibling.xml \ test_bug614058.html \ test_bug590771.html \ + test_bug622117.html \ + test_bug622246.html \ $(NULL) # This test fails on the Mac for some reason diff --git a/content/base/test/test_bug622117.html b/content/base/test/test_bug622117.html new file mode 100644 index 00000000000..ef2601195fd --- /dev/null +++ b/content/base/test/test_bug622117.html @@ -0,0 +1,44 @@ + + + + + Test for Bug 622117 + + + + + + +Mozilla Bug 622117 +

+ +

+ +
+
+
+ + diff --git a/content/base/test/test_bug622246.html b/content/base/test/test_bug622246.html new file mode 100644 index 00000000000..72ae7ce1d98 --- /dev/null +++ b/content/base/test/test_bug622246.html @@ -0,0 +1,44 @@ + + + + + Test for Bug 622246 + + + + + + +Mozilla Bug 622246 +

+ +

+ +
+
+
+ + diff --git a/content/base/test/test_ws_basic_tests.html b/content/base/test/test_ws_basic_tests.html index 6beaaee1fa4..4c3386b5513 100644 --- a/content/base/test/test_ws_basic_tests.html +++ b/content/base/test/test_ws_basic_tests.html @@ -57,6 +57,7 @@ function testWebSocket () { } ws.onclose = function(e) { is(results.length, 0, "All the messages should have been processed!"); + ok(e.wasClean, "Connection closed cleanly"); testWebSocket2(); } ws.onerror = function(e) { @@ -83,6 +84,7 @@ function testWebSocket2() { } ws.onclose = function(e) { is(messageCount, testCount, "Didn't receive all the messages!"); + ok(e.wasClean, "Connection closed cleanly"); testWebSocket3(); } ws.onerror = function(e) { @@ -113,6 +115,7 @@ function testWebSocket3() { } ws.onclose = function(e) { is(messageCount, testCount, "Didn't receive all the messages!"); + ok(e.wasClean, "Connection closed cleanly"); testWebSocket4(); } ws.onerror = function(e) { @@ -132,6 +135,7 @@ function testWebSocket3() { function testWebSocket4() { ws = new WebSocket("ws://mochi.test:8888/tests/content/base/test/file_ws_basic_tests", "test"); + // String length = (10,000 - 1) * 23 = 229,977 = almost 225 KiB. var longString = new Array(10000).join("-huge websocket message"); ws.onopen = function(e) { is(this, ws, "'this' should point to the WebSocket. (1)"); @@ -139,7 +143,7 @@ function testWebSocket4() { } ws.onclose = function(e) { is(this, ws, "'this' should point to the WebSocket. (2)"); - //ok(e.wasClean, "Connection should have closed cleanly."); + ok(e.wasClean, "Connection closed cleanly"); testWebSocket5(); } ws.onerror = function(e) { @@ -148,7 +152,9 @@ function testWebSocket4() { } ws.onmessage = function(e) { is(this, ws, "'this' should point to the WebSocket. (3)"); - is(e.data, longString, "Didn't get the huge message back!"); + // Do not use |is(e.data, longString, "...");| that results in a _very_ long line. + is(e.data.length, longString.length, "Length of received message"); + ok(e.data == longString, "Content of received message"); document.getElementById('log').textContent += "\nReceived the huge message"; this.close(); } @@ -160,7 +166,7 @@ function testWebSocket5() { this.close(); } ws.onclose = function(e) { - //ok(e.wasClean, "Connection should have closed cleanly."); + ok(e.wasClean, "Connection closed cleanly"); is(this.bufferedAmount, 0, "Shouldn't have anything buffered"); var msg = "some data"; this.send(msg); diff --git a/content/canvas/src/WebGLContextGL.cpp b/content/canvas/src/WebGLContextGL.cpp index e80bc1f3d32..9c28d9c8818 100644 --- a/content/canvas/src/WebGLContextGL.cpp +++ b/content/canvas/src/WebGLContextGL.cpp @@ -2980,7 +2980,8 @@ WebGLContext::ConvertImage(size_t width, size_t height, size_t srcStride, size_t const PRUint8* src_end = src + height * srcStride; PRUint8* dst_row = mPixelStoreFlipY ? dst + (height-1) * dstStride : dst; - ptrdiff_t dst_delta = mPixelStoreFlipY ? -dstStride : dstStride; + ptrdiff_t dstStrideSigned(dstStride); + ptrdiff_t dst_delta = mPixelStoreFlipY ? -dstStrideSigned : dstStrideSigned; while(src_row != src_end) { memcpy(dst_row, src_row, row_size); diff --git a/content/canvas/src/WebGLContextNotSupported.cpp b/content/canvas/src/WebGLContextNotSupported.cpp index 0e4c16bc4d2..b3f9b2940b9 100644 --- a/content/canvas/src/WebGLContextNotSupported.cpp +++ b/content/canvas/src/WebGLContextNotSupported.cpp @@ -43,7 +43,7 @@ DUMMY(NS_NewCanvasRenderingContextWebGL, nsIDOMWebGLRenderingContext) -DOMCI_DATA(CanvasRenderingContextWebGL, void) +DOMCI_DATA(WebGLRenderingContext, void) DOMCI_DATA(WebGLBuffer, void) DOMCI_DATA(WebGLTexture, void) DOMCI_DATA(WebGLProgram, void) @@ -51,3 +51,4 @@ DOMCI_DATA(WebGLShader, void) DOMCI_DATA(WebGLFramebuffer, void) DOMCI_DATA(WebGLRenderbuffer, void) DOMCI_DATA(WebGLUniformLocation, void) +DOMCI_DATA(WebGLActiveInfo, void) diff --git a/content/media/nsAudioStream.cpp b/content/media/nsAudioStream.cpp index 3c26e238757..bd9fa17d925 100644 --- a/content/media/nsAudioStream.cpp +++ b/content/media/nsAudioStream.cpp @@ -301,10 +301,12 @@ void nsAudioStream::ShutdownLibrary() { } - nsIThread * nsAudioStream::GetThread() { + if (!mAudioPlaybackThread) { + NS_NewThread(getter_AddRefs(mAudioPlaybackThread)); + } return mAudioPlaybackThread; } @@ -318,6 +320,23 @@ nsAudioStream* nsAudioStream::AllocateStream() return new nsAudioStreamLocal(); } +class AsyncShutdownPlaybackThread : public nsRunnable +{ +public: + AsyncShutdownPlaybackThread(nsIThread* aThread) : mThread(aThread) {} + NS_IMETHODIMP Run() { return mThread->Shutdown(); } +private: + nsCOMPtr mThread; +}; + +nsAudioStream::~nsAudioStream() +{ + if (mAudioPlaybackThread) { + nsCOMPtr event = new AsyncShutdownPlaybackThread(mAudioPlaybackThread); + NS_DispatchToMainThread(event); + } +} + nsAudioStreamLocal::nsAudioStreamLocal() : mVolume(1.0), mAudioHandle(0), @@ -327,12 +346,6 @@ nsAudioStreamLocal::nsAudioStreamLocal() : mPaused(PR_FALSE), mInError(PR_FALSE) { -#ifdef MOZ_IPC - // We only need this thread in the main process. - if (XRE_GetProcessType() == GeckoProcessType_Default) { - NS_NewThread(getter_AddRefs(mAudioPlaybackThread)); - } -#endif } nsAudioStreamLocal::~nsAudioStreamLocal() diff --git a/content/media/nsAudioStream.h b/content/media/nsAudioStream.h index 58aea775865..6283d2f3dca 100644 --- a/content/media/nsAudioStream.h +++ b/content/media/nsAudioStream.h @@ -54,6 +54,8 @@ public: FORMAT_FLOAT32 }; + virtual ~nsAudioStream(); + // Initialize Audio Library. Some Audio backends require initializing the // library before using it. static void InitLibrary(); @@ -64,7 +66,7 @@ public: // Thread, usually for MOZ_IPC handling, that is shared between audio streams. // This may return null in the child process - virtual nsIThread *GetThread(); + nsIThread *GetThread(); // AllocateStream will return either a local stream or a remoted stream // depending on where you call it from. If MOZ_IPC is enabled, and you diff --git a/content/smil/test/db_smilCSSFromBy.js b/content/smil/test/db_smilCSSFromBy.js index 4836d48c527..d4724261a5d 100644 --- a/content/smil/test/db_smilCSSFromBy.js +++ b/content/smil/test/db_smilCSSFromBy.js @@ -61,7 +61,7 @@ var _fromByTestLists = toComp: "40px"}), ], lengthNoUnitsSVG: [ - new AnimTestcaseFromBy("0", "50", { fromComp: "0px", // 0 acts like 0px + new AnimTestcaseFromBy("0", "50", { fromComp: "0", midComp: "25", toComp: "50"}), new AnimTestcaseFromBy("30", "10", { fromComp: "30", @@ -69,9 +69,9 @@ var _fromByTestLists = toComp: "40"}), ], lengthPx: [ - new AnimTestcaseFromBy("0", "8px", { fromComp: "0px", // 0 acts like 0px - midComp: "4px", - toComp: "8px"}), + new AnimTestcaseFromBy("0px", "8px", { fromComp: "0px", + midComp: "4px", + toComp: "8px"}), new AnimTestcaseFromBy("1px", "10px", { midComp: "6px", toComp: "11px"}), ], opacity: [ diff --git a/content/smil/test/db_smilCSSFromTo.js b/content/smil/test/db_smilCSSFromTo.js index db8a36a4a86..d05bf4042d4 100644 --- a/content/smil/test/db_smilCSSFromTo.js +++ b/content/smil/test/db_smilCSSFromTo.js @@ -117,21 +117,21 @@ var _fromToTestLists = { toComp: "80px"}), ], lengthNoUnitsSVG: [ - new AnimTestcaseFromTo("0", "20", { fromComp: "0px", // 0 acts like 0px + new AnimTestcaseFromTo("0", "20", { fromComp: "0", midComp: "10", toComp: "20"}), new AnimTestcaseFromTo("50", "0", { fromComp: "50", midComp: "25", - toComp: "0px"}), // 0 acts like 0px + toComp: "0"}), new AnimTestcaseFromTo("30", "80", { fromComp: "30", midComp: "55", toComp: "80"}), ], lengthPx: [ - new AnimTestcaseFromTo("0", "12px", { fromComp: "0px", // 0 acts like 0px - midComp: "6px"}), - new AnimTestcaseFromTo("16px", "0", { midComp: "8px", - toComp: "0px"}), // 0 acts like 0px + new AnimTestcaseFromTo("0px", "12px", { fromComp: "0px", + midComp: "6px"}), + new AnimTestcaseFromTo("16px", "0px", { midComp: "8px", + toComp: "0px"}), new AnimTestcaseFromTo("10px", "20px", { midComp: "15px"}), new AnimTestcaseFromTo("41px", "1px", { midComp: "21px"}), ], diff --git a/content/smil/test/db_smilCSSPaced.js b/content/smil/test/db_smilCSSPaced.js index ce429eb3a8a..0f746e4d3b4 100644 --- a/content/smil/test/db_smilCSSPaced.js +++ b/content/smil/test/db_smilCSSPaced.js @@ -112,7 +112,7 @@ var _pacedTestLists = new AnimTestcasePaced("2; 0; 4", { comp0: "2", comp1_6: "1", - comp1_3: "0px", // 0 acts like 0px + comp1_3: "0", comp2_3: "2", comp1: "4" }), @@ -125,8 +125,8 @@ var _pacedTestLists = }), ], lengthPx : [ - new AnimTestcasePaced("0; 2px; 6px", - { comp0: "0px", // 0 acts like 0px + new AnimTestcasePaced("0px; 2px; 6px", + { comp0: "0px", comp1_6: "1px", comp1_3: "2px", comp2_3: "4px", diff --git a/content/svg/content/src/SVGAnimatedLengthList.cpp b/content/svg/content/src/SVGAnimatedLengthList.cpp index 9ac91382432..2870f593af3 100644 --- a/content/svg/content/src/SVGAnimatedLengthList.cpp +++ b/content/svg/content/src/SVGAnimatedLengthList.cpp @@ -85,7 +85,7 @@ SVGAnimatedLengthList::ClearBaseValue(PRUint32 aAttrEnum) DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this); if (domWrapper) { // We must send this notification *before* changing mBaseVal! (See above.) - domWrapper->InternalAnimValListWillChangeTo(SVGLengthList()); + domWrapper->InternalBaseValListWillChangeTo(SVGLengthList()); } mBaseVal.Clear(); // Caller notifies diff --git a/content/svg/content/src/SVGAnimatedNumberList.cpp b/content/svg/content/src/SVGAnimatedNumberList.cpp index 0171869ba74..05f761607ec 100644 --- a/content/svg/content/src/SVGAnimatedNumberList.cpp +++ b/content/svg/content/src/SVGAnimatedNumberList.cpp @@ -85,7 +85,7 @@ SVGAnimatedNumberList::ClearBaseValue(PRUint32 aAttrEnum) DOMSVGAnimatedNumberList::GetDOMWrapperIfExists(this); if (domWrapper) { // We must send this notification *before* changing mBaseVal! (See above.) - domWrapper->InternalAnimValListWillChangeTo(SVGNumberList()); + domWrapper->InternalBaseValListWillChangeTo(SVGNumberList()); } mBaseVal.Clear(); // Caller notifies diff --git a/content/svg/content/src/SVGPathData.cpp b/content/svg/content/src/SVGPathData.cpp index e8f16ccd894..67cab0228f8 100644 --- a/content/svg/content/src/SVGPathData.cpp +++ b/content/svg/content/src/SVGPathData.cpp @@ -554,9 +554,9 @@ SVGPathData::GetMarkerPositioningData(nsTArray *aMarks) const case nsIDOMSVGPathSeg::PATHSEG_ARC_ABS: case nsIDOMSVGPathSeg::PATHSEG_ARC_REL: { - float rx = mData[i]; - float ry = mData[i+1]; - float angle = mData[i+2]; + double rx = mData[i]; + double ry = mData[i+1]; + double angle = mData[i+2]; PRBool largeArcFlag = mData[i+3] != 0.0f; PRBool sweepFlag = mData[i+4] != 0.0f; if (segType == nsIDOMSVGPathSeg::PATHSEG_ARC_ABS) { @@ -595,46 +595,47 @@ SVGPathData::GetMarkerPositioningData(nsTArray *aMarks) const // F.6.5.1: angle = angle * M_PI/180.0; - float x1p = cos(angle) * (segStart.x - segEnd.x) / 2.0 - + sin(angle) * (segStart.y - segEnd.y) / 2.0; - float y1p = -sin(angle) * (segStart.x - segEnd.x) / 2.0 - + cos(angle) *(segStart.y - segEnd.y) / 2.0; + double x1p = cos(angle) * (segStart.x - segEnd.x) / 2.0 + + sin(angle) * (segStart.y - segEnd.y) / 2.0; + double y1p = -sin(angle) * (segStart.x - segEnd.x) / 2.0 + + cos(angle) * (segStart.y - segEnd.y) / 2.0; // This is the root in F.6.5.2 and the numerator under that root: - float root; - float numerator = rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p; + double root; + double numerator = rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p; - if (numerator < 0.0) { - // F.6.6 step 3 - |numerator < 0.0| is equivalent to the result of - // F.6.6.2 (lamedh) being greater than one. What we have here is radii - // that do not reach between segStart and segEnd, so we need to correct - // them. - float lamedh = 1.0 - numerator/(rx*rx*ry*ry); // equiv to eqn F.6.6.2 - float s = sqrt(lamedh); + if (numerator >= 0.0) { + root = sqrt(numerator/(rx*rx*y1p*y1p + ry*ry*x1p*x1p)); + if (largeArcFlag == sweepFlag) + root = -root; + } else { + // F.6.6 step 3 - |numerator < 0.0|. This is equivalent to the result + // of F.6.6.2 (lamedh) being greater than one. What we have here is + // ellipse radii that are too small for the ellipse to reach between + // segStart and segEnd. We scale the radii up uniformly so that the + // ellipse is just big enough to fit (i.e. to the point where there is + // exactly one solution). + + double lamedh = 1.0 - numerator/(rx*rx*ry*ry); // equiv to eqn F.6.6.2 + double s = sqrt(lamedh); rx *= s; // F.6.6.3 ry *= s; - // rx and ry changed, so we have to recompute numerator - numerator = rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p; - NS_ABORT_IF_FALSE(numerator >= 0, - "F.6.6.3 should prevent this. Will sqrt(-num)!"); + root = 0.0; } - root = sqrt(numerator/(rx*rx*y1p*y1p + ry*ry*x1p*x1p)); - if (largeArcFlag == sweepFlag) - root = -root; - float cxp = root * rx * y1p / ry; // F.6.5.2 - float cyp = -root * ry * x1p / rx; + double cxp = root * rx * y1p / ry; // F.6.5.2 + double cyp = -root * ry * x1p / rx; - float theta, delta; - theta = CalcVectorAngle(1.0, 0.0, (x1p-cxp)/rx, (y1p-cyp)/ry); // F.6.5.5 - delta = CalcVectorAngle((x1p-cxp)/rx, (y1p-cyp)/ry, - (-x1p-cxp)/rx, (-y1p-cyp)/ry); // F.6.5.6 + double theta, delta; + theta = CalcVectorAngle(1.0, 0.0, (x1p-cxp)/rx, (y1p-cyp)/ry); // F.6.5.5 + delta = CalcVectorAngle((x1p-cxp)/rx, (y1p-cyp)/ry, + (-x1p-cxp)/rx, (-y1p-cyp)/ry); // F.6.5.6 if (!sweepFlag && delta > 0) delta -= 2.0 * M_PI; else if (sweepFlag && delta < 0) delta += 2.0 * M_PI; - float tx1, ty1, tx2, ty2; + double tx1, ty1, tx2, ty2; tx1 = -cos(angle)*rx*sin(theta) - sin(angle)*ry*cos(theta); ty1 = -sin(angle)*rx*sin(theta) + cos(angle)*ry*cos(theta); tx2 = -cos(angle)*rx*sin(theta+delta) - sin(angle)*ry*cos(theta+delta); diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index b20ecd4fd04..22e387a06da 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -2276,12 +2276,20 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_MAP_ENTRY(nsIDOMLocation) DOM_CLASSINFO_MAP_END - DOM_CLASSINFO_MAP_BEGIN(Navigator, nsIDOMNavigator) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigator) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorGeolocation) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorDesktopNotification) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMClientInformation) - DOM_CLASSINFO_MAP_END + if (nsNavigator::HasDesktopNotificationSupport()) { + DOM_CLASSINFO_MAP_BEGIN(Navigator, nsIDOMNavigator) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigator) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorGeolocation) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorDesktopNotification) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMClientInformation) + DOM_CLASSINFO_MAP_END + } else { + DOM_CLASSINFO_MAP_BEGIN(Navigator, nsIDOMNavigator) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigator) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorGeolocation) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMClientInformation) + DOM_CLASSINFO_MAP_END + } DOM_CLASSINFO_MAP_BEGIN(Plugin, nsIDOMPlugin) DOM_CLASSINFO_MAP_ENTRY(nsIDOMPlugin) diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index e9b271039ea..6264c35f078 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -7966,7 +7966,7 @@ nsGlobalWindow::GetLocalStorage(nsIDOMStorage ** aLocalStorage) } NS_IMETHODIMP -nsGlobalWindow::GetMoz_indexedDB(nsIIDBFactory** _retval) +nsGlobalWindow::GetMozIndexedDB(nsIIDBFactory** _retval) { if (!mIndexedDB) { mIndexedDB = indexedDB::IDBFactory::Create(); @@ -10722,6 +10722,12 @@ nsNavigator::RefreshMIMEArray() return rv; } +bool +nsNavigator::HasDesktopNotificationSupport() +{ + return nsContentUtils::GetBoolPref("notification.feature.enabled", PR_FALSE); +} + //***************************************************************************** // nsNavigator::nsIDOMClientInformation //***************************************************************************** diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 54b2355f848..194fcbedcf5 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -1056,6 +1056,8 @@ public: void LoadingNewDocument(); nsresult RefreshMIMEArray(); + static bool HasDesktopNotificationSupport(); + protected: nsRefPtr mMimeTypes; nsRefPtr mPlugins; diff --git a/dom/indexedDB/test/Makefile.in b/dom/indexedDB/test/Makefile.in index ae586d5282a..1e10513c12f 100644 --- a/dom/indexedDB/test/Makefile.in +++ b/dom/indexedDB/test/Makefile.in @@ -101,6 +101,7 @@ BROWSER_TEST_FILES = \ browser_permissionsPrompt.html \ browser_permissionsPromptAllow.js \ browser_permissionsPromptDeny.js \ + browser_privateBrowsing.js \ browser_quotaPrompt.html \ browser_quotaPromptAllow.js \ browser_quotaPromptDeny.js \ diff --git a/dom/indexedDB/test/bfcache_iframe1.html b/dom/indexedDB/test/bfcache_iframe1.html index b138e6058db..5b11ea126d8 100644 --- a/dom/indexedDB/test/bfcache_iframe1.html +++ b/dom/indexedDB/test/bfcache_iframe1.html @@ -2,7 +2,7 @@ diff --git a/dom/tests/mochitest/notification/test_basic_notification_click.html b/dom/tests/mochitest/notification/test_basic_notification_click.html index 333a12aab51..50b5a3adb03 100644 --- a/dom/tests/mochitest/notification/test_basic_notification_click.html +++ b/dom/tests/mochitest/notification/test_basic_notification_click.html @@ -21,32 +21,38 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=573588
 
 
diff --git a/dom/tests/mochitest/notification/test_leak_windowClose.html b/dom/tests/mochitest/notification/test_leak_windowClose.html index 81eda4ad1f4..1944aff1588 100644 --- a/dom/tests/mochitest/notification/test_leak_windowClose.html +++ b/dom/tests/mochitest/notification/test_leak_windowClose.html @@ -10,16 +10,22 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=605309 diff --git a/embedding/android/GeckoApp.java b/embedding/android/GeckoApp.java index 17d0009b498..ce332cdb0e0 100644 --- a/embedding/android/GeckoApp.java +++ b/embedding/android/GeckoApp.java @@ -123,9 +123,11 @@ abstract public class GeckoApp try { unpackComponents(); } catch (FileNotFoundException fnfe) { + Log.e("GeckoApp", "error unpacking components", fnfe); showErrorDialog(getString(R.string.error_loading_file)); return false; } catch (IOException ie) { + Log.e("GeckoApp", "error unpacking components", ie); String msg = ie.getMessage(); if (msg.equalsIgnoreCase("No space left on device")) showErrorDialog(getString(R.string.no_space_to_start_error)); @@ -412,7 +414,7 @@ abstract public class GeckoApp Log.i("GeckoAppJava", intent.toString()); startActivity(intent); } catch (Exception e) { - Log.i("GeckoAppJava", e.toString()); + Log.i("GeckoAppJava", "error doing restart", e); } finish(); } @@ -425,10 +427,16 @@ abstract public class GeckoApp Log.i("GeckoAppJava", "Checking for an update"); int statusCode = 8; // UNEXPECTED_ERROR + File downloadDir = null; + if (Build.VERSION.SDK_INT >= 8) + downloadDir = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS); + else + downloadDir = new File(Environment.getExternalStorageDirectory().getPath(), "download"); - String updateDir = Environment.getExternalStorageDirectory().getPath() + "/downloads/updates/0/"; - File updateFile = new File(updateDir + "update.apk"); - File statusFile = new File(updateDir + "update.status"); + File updateDir = new File(new File(downloadDir, "updates"),"0"); + + File updateFile = new File(updateDir, "update.apk"); + File statusFile = new File(updateDir, "update.status"); if (!statusFile.exists() || !readUpdateStatus(statusFile).equals("pending")) return; @@ -453,7 +461,7 @@ abstract public class GeckoApp statusCode = 7; // WRITE_ERROR } } catch (Exception e) { - Log.i("GeckoAppJava", e.toString()); + Log.i("GeckoAppJava", "error launching installer to update", e); } // Update the status file @@ -466,7 +474,7 @@ abstract public class GeckoApp outStream.write(buf, 0, buf.length); outStream.close(); } catch (Exception e) { - Log.i("GeckoAppJava", e.toString()); + Log.i("GeckoAppJava", "error writing status file", e); } if (statusCode == 0) @@ -480,7 +488,7 @@ abstract public class GeckoApp status = reader.readLine(); reader.close(); } catch (Exception e) { - Log.i("GeckoAppJava", e.toString()); + Log.i("GeckoAppJava", "error reading update status", e); } return status; } @@ -500,7 +508,7 @@ abstract public class GeckoApp try { filePickerResult = mFilePickerResult.take(); } catch (InterruptedException e) { - Log.i("GeckoApp", "error: " + e); + Log.i("GeckoApp", "showing file picker ", e); } return filePickerResult; @@ -535,13 +543,13 @@ abstract public class GeckoApp fos.close(); filePickerResult = file.getAbsolutePath(); }catch (Exception e) { - Log.e("GeckoApp", "error : "+ e); + Log.e("GeckoApp", "showing file picker", e); } } try { mFilePickerResult.put(filePickerResult); } catch (InterruptedException e) { - Log.i("GeckoApp", "error: " + e); + Log.i("GeckoApp", "error returning file picker result", e); } } } diff --git a/embedding/android/GeckoAppShell.java b/embedding/android/GeckoAppShell.java index e3e2b7382de..e23cb92439a 100644 --- a/embedding/android/GeckoAppShell.java +++ b/embedding/android/GeckoAppShell.java @@ -118,8 +118,13 @@ class GeckoAppShell GeckoAppShell.putenv("TMPDIR=" + f.getPath()); f = Environment.getDownloadCacheDirectory(); - GeckoAppShell.putenv("EXTERNAL_STORAGE" + f.getPath()); - + GeckoAppShell.putenv("EXTERNAL_STORAGE=" + f.getPath()); + File downloadDir = null; + if (Build.VERSION.SDK_INT >= 8) + downloadDir = GeckoApp.mAppContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS); + else + downloadDir = new File(Environment.getExternalStorageDirectory().getPath(), "download"); + GeckoAppShell.putenv("DOWNLOADS_DIRECTORY=" + downloadDir.getPath()); GeckoAppShell.putenv("LANG=" + Locale.getDefault().toString()); loadLibs(apkName); diff --git a/embedding/android/GeckoInputConnection.java b/embedding/android/GeckoInputConnection.java index 988130e723c..7e02e8cb9d2 100644 --- a/embedding/android/GeckoInputConnection.java +++ b/embedding/android/GeckoInputConnection.java @@ -1,4 +1,4 @@ -/* -*- Mode: Java; tab-width: 20; indent-tabs-mode: nil; -*- +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -109,7 +109,7 @@ public class GeckoInputConnection try { mQueryResult.take(); } catch (InterruptedException e) { - Log.e("GeckoAppJava", "IME: deleteSurroundingText interrupted"); + Log.e("GeckoAppJava", "IME: deleteSurroundingText interrupted", e); return false; } delStart = mSelectionStart > leftLength ? @@ -194,7 +194,7 @@ public class GeckoInputConnection try { text = mQueryResult.take(); } catch (InterruptedException e) { - Log.e("GeckoAppJava", "IME: performContextMenuAction interrupted"); + Log.e("GeckoAppJava", "IME: performContextMenuAction interrupted", e); return false; } @@ -224,7 +224,7 @@ public class GeckoInputConnection try { text = mQueryResult.take(); } catch (InterruptedException e) { - Log.e("GeckoAppJava", "IME: performContextMenuAction interrupted"); + Log.e("GeckoAppJava", "IME: performContextMenuAction interrupted", e); return false; } } @@ -251,7 +251,7 @@ public class GeckoInputConnection try { mQueryResult.take(); } catch (InterruptedException e) { - Log.e("GeckoAppJava", "IME: getExtractedText interrupted"); + Log.e("GeckoAppJava", "IME: getExtractedText interrupted", e); return null; } extract.selectionStart = mSelectionStart; @@ -268,7 +268,7 @@ public class GeckoInputConnection return extract; } catch (InterruptedException e) { - Log.e("GeckoAppJava", "IME: getExtractedText interrupted"); + Log.e("GeckoAppJava", "IME: getExtractedText interrupted", e); return null; } } @@ -282,7 +282,7 @@ public class GeckoInputConnection try { mQueryResult.take(); } catch (InterruptedException e) { - Log.e("GeckoAppJava", "IME: getTextBefore/AfterCursor interrupted"); + Log.e("GeckoAppJava", "IME: getTextBefore/AfterCursor interrupted", e); return null; } @@ -305,7 +305,7 @@ public class GeckoInputConnection try { return mQueryResult.take(); } catch (InterruptedException e) { - Log.e("GeckoAppJava", "IME: getTextBefore/AfterCursor: Interrupted!"); + Log.e("GeckoAppJava", "IME: getTextBefore/AfterCursor: Interrupted!", e); return null; } } @@ -331,7 +331,7 @@ public class GeckoInputConnection try { mQueryResult.take(); } catch (InterruptedException e) { - Log.e("GeckoAppJava", "IME: setComposingText interrupted"); + Log.e("GeckoAppJava", "IME: setComposingText interrupted", e); return false; } // Make sure we are in a composition diff --git a/embedding/android/GeckoSurfaceView.java b/embedding/android/GeckoSurfaceView.java index 5b64b3520fc..acbb7e3a85d 100644 --- a/embedding/android/GeckoSurfaceView.java +++ b/embedding/android/GeckoSurfaceView.java @@ -175,7 +175,7 @@ class GeckoSurfaceView try { bb = mSyncBuf.take(); } catch (InterruptedException ie) { - Log.e("GeckoAppJava", "Threw exception while getting sync buf: " + ie); + Log.e("GeckoAppJava", "Threw exception while getting sync buf: ", ie); } if (bb != null && bb.capacity() == (width * height * 2)) { mSoftwareBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.RGB_565); @@ -289,7 +289,7 @@ class GeckoSurfaceView try { mSyncBuf.put(buffer); } catch (InterruptedException ie) { - Log.e("GeckoAppJava", "Threw exception while getting sync buf: " + ie); + Log.e("GeckoAppJava", "Threw exception while getting sync buf: ", ie); } return; } diff --git a/embedding/android/NotificationHandler.java.in b/embedding/android/NotificationHandler.java.in index 6338f3db5b0..4bfb9507d80 100644 --- a/embedding/android/NotificationHandler.java.in +++ b/embedding/android/NotificationHandler.java.in @@ -1,4 +1,4 @@ -/* -*- Mode: Java; tab-width: 20; indent-tabs-mode: nil; -*- +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -94,7 +94,7 @@ public class NotificationHandler try { context.startActivity(appIntent); } catch (ActivityNotFoundException e) { - Log.e("GeckoAppJava", "NotificationHandler Exception: " + e.getMessage()); + Log.e("GeckoAppJava", "NotificationHandler Exception: ", e); } } } diff --git a/gfx/cairo/cairo/src/cairo-pattern.c b/gfx/cairo/cairo/src/cairo-pattern.c index e988b367ed5..048ad667ff9 100644 --- a/gfx/cairo/cairo/src/cairo-pattern.c +++ b/gfx/cairo/cairo/src/cairo-pattern.c @@ -2945,7 +2945,7 @@ cairo_pattern_get_surface (cairo_pattern_t *pattern, return pattern->status; if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) - return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + return CAIRO_STATUS_PATTERN_TYPE_MISMATCH; if (surface) *surface = spat->surface; diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c index 6b0f9529ad2..07d3e01e236 100644 --- a/gfx/cairo/cairo/src/cairo-quartz-surface.c +++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c @@ -2478,6 +2478,7 @@ _cairo_quartz_surface_show_glyphs (void *abstract_surface, cairo_bool_t isClipping = FALSE; cairo_bool_t didForceFontSmoothing = FALSE; + cairo_antialias_t effective_antialiasing; if (IS_EMPTY(surface)) return CAIRO_STATUS_SUCCESS; @@ -2519,6 +2520,12 @@ _cairo_quartz_surface_show_glyphs (void *abstract_surface, CGContextSetFont (state.context, cgfref); CGContextSetFontSize (state.context, 1.0); + effective_antialiasing = scaled_font->options.antialias; + if (effective_antialiasing == CAIRO_ANTIALIAS_SUBPIXEL && + !surface->base.permit_subpixel_antialiasing) { + effective_antialiasing = CAIRO_ANTIALIAS_GRAY; + } + switch (scaled_font->options.antialias) { case CAIRO_ANTIALIAS_SUBPIXEL: CGContextSetShouldAntialias (state.context, TRUE); diff --git a/gfx/cairo/cairo/src/cairo-surface-private.h b/gfx/cairo/cairo/src/cairo-surface-private.h index 994df0e595c..e569f18cbf5 100644 --- a/gfx/cairo/cairo/src/cairo-surface-private.h +++ b/gfx/cairo/cairo/src/cairo-surface-private.h @@ -63,6 +63,7 @@ struct _cairo_surface { unsigned finished : 1; unsigned is_clear : 1; unsigned has_font_options : 1; + unsigned permit_subpixel_antialiasing : 1; cairo_user_data_array_t user_data; cairo_user_data_array_t mime_data; diff --git a/gfx/cairo/cairo/src/cairo-surface.c b/gfx/cairo/cairo/src/cairo-surface.c index 4a1d6a0a715..c3f6f03a7ef 100644 --- a/gfx/cairo/cairo/src/cairo-surface.c +++ b/gfx/cairo/cairo/src/cairo-surface.c @@ -54,7 +54,8 @@ const cairo_surface_t name = { \ 0, /* unique id */ \ FALSE, /* finished */ \ TRUE, /* is_clear */ \ - FALSE, /* has_font_options */ \ + FALSE, /* has_font_options */ \ + FALSE, /* permit_subpixel_antialiasing */ \ { 0, 0, 0, NULL, }, /* user_data */ \ { 0, 0, 0, NULL, }, /* mime_data */ \ { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, /* device_transform */ \ @@ -347,6 +348,8 @@ _cairo_surface_init (cairo_surface_t *surface, surface->unique_id = _cairo_surface_allocate_unique_id (); surface->finished = FALSE; surface->is_clear = FALSE; + surface->has_font_options = FALSE; + surface->permit_subpixel_antialiasing = TRUE; _cairo_user_data_array_init (&surface->user_data); _cairo_user_data_array_init (&surface->mime_data); @@ -362,8 +365,6 @@ _cairo_surface_init (cairo_surface_t *surface, _cairo_array_init (&surface->snapshots, sizeof (cairo_surface_t *)); surface->snapshot_of = NULL; - - surface->has_font_options = FALSE; } static void @@ -377,6 +378,8 @@ _cairo_surface_copy_similar_properties (cairo_surface_t *surface, _cairo_surface_set_font_options (surface, &options); } + surface->permit_subpixel_antialiasing = other->permit_subpixel_antialiasing; + cairo_surface_set_fallback_resolution (surface, other->x_fallback_resolution, other->y_fallback_resolution); @@ -2487,6 +2490,57 @@ cairo_surface_has_show_text_glyphs (cairo_surface_t *surface) } slim_hidden_def (cairo_surface_has_show_text_glyphs); +/** + * cairo_surface_set_subpixel_antialiasing: + * @surface: a #cairo_surface_t + * + * Sets whether the surface permits subpixel antialiasing. By default, + * surfaces permit subpixel antialiasing. + * + * Enabling subpixel antialiasing for CONTENT_COLOR_ALPHA surfaces generally + * requires that the pixels in the areas under a subpixel antialiasing + * operation already be opaque. + * + * Since: 1.12 + **/ +void +cairo_surface_set_subpixel_antialiasing (cairo_surface_t *surface, + cairo_subpixel_antialiasing_t enabled) +{ + if (surface->status) + return; + + if (surface->finished) { + _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); + return; + } + + surface->permit_subpixel_antialiasing = + enabled == CAIRO_SUBPIXEL_ANTIALIASING_ENABLED; +} +slim_hidden_def (cairo_surface_set_subpixel_antialiasing); + +/** + * cairo_surface_get_subpixel_antialiasing: + * @surface: a #cairo_surface_t + * + * Gets whether the surface supports subpixel antialiasing. By default, + * CAIRO_CONTENT_COLOR surfaces support subpixel antialiasing but other + * surfaces do not. + * + * Since: 1.12 + **/ +cairo_subpixel_antialiasing_t +cairo_surface_get_subpixel_antialiasing (cairo_surface_t *surface) +{ + if (surface->status) + return CAIRO_SUBPIXEL_ANTIALIASING_DISABLED; + + return surface->permit_subpixel_antialiasing ? + CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED; +} +slim_hidden_def (cairo_surface_get_subpixel_antialiasing); + /* Note: the backends may modify the contents of the glyph array as long as * they do not return %CAIRO_INT_STATUS_UNSUPPORTED. This makes it possible to * avoid copying the array again and again, and edit it in-place. diff --git a/gfx/cairo/cairo/src/cairo-tee-surface.c b/gfx/cairo/cairo/src/cairo-tee-surface.c index 1111fa1b49f..de8c2307fe9 100644 --- a/gfx/cairo/cairo/src/cairo-tee-surface.c +++ b/gfx/cairo/cairo/src/cairo-tee-surface.c @@ -191,6 +191,33 @@ _cairo_tee_surface_get_font_options (void *abstract_surface, _cairo_surface_wrapper_get_font_options (&surface->master, options); } +static const cairo_pattern_t * +_cairo_tee_surface_match_source (cairo_tee_surface_t *surface, + const cairo_pattern_t *source, + int index, + cairo_surface_wrapper_t *dest, + cairo_surface_pattern_t *temp) +{ + cairo_surface_t *s; + cairo_status_t status = cairo_pattern_get_surface ((cairo_pattern_t *)source, &s); + if (status == CAIRO_STATUS_SUCCESS && + cairo_surface_get_type (s) == CAIRO_SURFACE_TYPE_TEE) { + cairo_surface_t *tee_surf = cairo_tee_surface_index (s, index); + if (tee_surf->status == CAIRO_STATUS_SUCCESS && + tee_surf->backend == dest->target->backend) { + status = _cairo_pattern_init_copy (&temp->base, source); + if (status == CAIRO_STATUS_SUCCESS) { + cairo_surface_destroy (temp->surface); + temp->surface = tee_surf; + cairo_surface_reference (temp->surface); + return &temp->base; + } + } + } + + return source; +} + static cairo_int_status_t _cairo_tee_surface_paint (void *abstract_surface, cairo_operator_t op, @@ -201,15 +228,25 @@ _cairo_tee_surface_paint (void *abstract_surface, cairo_surface_wrapper_t *slaves; int n, num_slaves; cairo_status_t status; + const cairo_pattern_t *matched_source; + cairo_surface_pattern_t temp; - status = _cairo_surface_wrapper_paint (&surface->master, op, source, clip); + matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp); + status = _cairo_surface_wrapper_paint (&surface->master, op, matched_source, clip); + if (matched_source == &temp.base) { + _cairo_pattern_fini (&temp.base); + } if (unlikely (status)) return status; num_slaves = _cairo_array_num_elements (&surface->slaves); slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { - status = _cairo_surface_wrapper_paint (&slaves[n], op, source, clip); + matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp); + status = _cairo_surface_wrapper_paint (&slaves[n], op, matched_source, clip); + if (matched_source == &temp.base) { + _cairo_pattern_fini (&temp.base); + } if (unlikely (status)) return status; } @@ -228,17 +265,27 @@ _cairo_tee_surface_mask (void *abstract_surface, cairo_surface_wrapper_t *slaves; int n, num_slaves; cairo_status_t status; + const cairo_pattern_t *matched_source; + cairo_surface_pattern_t temp; + matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp); status = _cairo_surface_wrapper_mask (&surface->master, - op, source, mask, clip); + op, matched_source, mask, clip); + if (matched_source == &temp.base) { + _cairo_pattern_fini (&temp.base); + } if (unlikely (status)) return status; num_slaves = _cairo_array_num_elements (&surface->slaves); slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { + matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp); status = _cairo_surface_wrapper_mask (&slaves[n], - op, source, mask, clip); + op, matched_source, mask, clip); + if (matched_source == &temp.base) { + _cairo_pattern_fini (&temp.base); + } if (unlikely (status)) return status; } @@ -262,25 +309,35 @@ _cairo_tee_surface_stroke (void *abstract_surface, cairo_surface_wrapper_t *slaves; int n, num_slaves; cairo_status_t status; + const cairo_pattern_t *matched_source; + cairo_surface_pattern_t temp; + matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp); status = _cairo_surface_wrapper_stroke (&surface->master, - op, source, + op, matched_source, path, style, ctm, ctm_inverse, tolerance, antialias, clip); + if (matched_source == &temp.base) { + _cairo_pattern_fini (&temp.base); + } if (unlikely (status)) return status; num_slaves = _cairo_array_num_elements (&surface->slaves); slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { + matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp); status = _cairo_surface_wrapper_stroke (&slaves[n], - op, source, + op, matched_source, path, style, ctm, ctm_inverse, tolerance, antialias, clip); + if (matched_source == &temp.base) { + _cairo_pattern_fini (&temp.base); + } if (unlikely (status)) return status; } @@ -302,23 +359,33 @@ _cairo_tee_surface_fill (void *abstract_surface, cairo_surface_wrapper_t *slaves; int n, num_slaves; cairo_status_t status; + const cairo_pattern_t *matched_source; + cairo_surface_pattern_t temp; + matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp); status = _cairo_surface_wrapper_fill (&surface->master, - op, source, + op, matched_source, path, fill_rule, tolerance, antialias, clip); + if (matched_source == &temp.base) { + _cairo_pattern_fini (&temp.base); + } if (unlikely (status)) return status; num_slaves = _cairo_array_num_elements (&surface->slaves); slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { + matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp); status = _cairo_surface_wrapper_fill (&slaves[n], - op, source, + op, matched_source, path, fill_rule, tolerance, antialias, clip); + if (matched_source == &temp.base) { + _cairo_pattern_fini (&temp.base); + } if (unlikely (status)) return status; } @@ -351,6 +418,8 @@ _cairo_tee_surface_show_text_glyphs (void *abstract_surface, int n, num_slaves; cairo_status_t status; cairo_glyph_t *glyphs_copy; + const cairo_pattern_t *matched_source; + cairo_surface_pattern_t temp; /* XXX: This copying is ugly. */ glyphs_copy = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); @@ -358,14 +427,18 @@ _cairo_tee_surface_show_text_glyphs (void *abstract_surface, return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs); + matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp); status = _cairo_surface_wrapper_show_text_glyphs (&surface->master, op, - source, + matched_source, utf8, utf8_len, glyphs_copy, num_glyphs, clusters, num_clusters, cluster_flags, scaled_font, clip); + if (matched_source == &temp.base) { + _cairo_pattern_fini (&temp.base); + } if (unlikely (status)) goto CLEANUP; @@ -373,14 +446,18 @@ _cairo_tee_surface_show_text_glyphs (void *abstract_surface, slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs); + matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp); status = _cairo_surface_wrapper_show_text_glyphs (&slaves[n], op, - source, + matched_source, utf8, utf8_len, glyphs_copy, num_glyphs, clusters, num_clusters, cluster_flags, scaled_font, clip); + if (matched_source == &temp.base) { + _cairo_pattern_fini (&temp.base); + } if (unlikely (status)) goto CLEANUP; } diff --git a/gfx/cairo/cairo/src/cairo-win32-font.c b/gfx/cairo/cairo/src/cairo-win32-font.c index 445dbfbf347..3d5489b29e8 100644 --- a/gfx/cairo/cairo/src/cairo-win32-font.c +++ b/gfx/cairo/cairo/src/cairo-win32-font.c @@ -1385,6 +1385,7 @@ _cairo_win32_scaled_font_show_glyphs (void *abstract_font, if (_cairo_surface_is_win32 (generic_surface) && surface->format == CAIRO_FORMAT_RGB24 && + (generic_surface->permit_subpixel_antialiasing || scaled_font->quality != CLEARTYPE_QUALITY) && op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque_solid (pattern)) { @@ -1416,6 +1417,8 @@ _cairo_win32_scaled_font_show_glyphs (void *abstract_font, cairo_win32_surface_t *tmp_surface; cairo_surface_t *mask_surface; cairo_surface_pattern_t mask; + cairo_bool_t use_subpixel_antialiasing = + scaled_font->quality == CLEARTYPE_QUALITY && generic_surface->permit_subpixel_antialiasing; RECT r; tmp_surface = (cairo_win32_surface_t *)cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, width, height); @@ -1437,7 +1440,7 @@ _cairo_win32_scaled_font_show_glyphs (void *abstract_font, return status; } - if (scaled_font->quality == CLEARTYPE_QUALITY) { + if (use_subpixel_antialiasing) { /* For ClearType, we need a 4-channel mask. If we are compositing on * a surface with alpha, we need to compute the alpha channel of * the mask (we just copy the green channel). But for a destination @@ -1465,7 +1468,7 @@ _cairo_win32_scaled_font_show_glyphs (void *abstract_font, _cairo_pattern_init_for_surface (&mask, mask_surface); cairo_surface_destroy (mask_surface); - if (scaled_font->quality == CLEARTYPE_QUALITY) + if (use_subpixel_antialiasing) mask.base.has_component_alpha = TRUE; status = _cairo_surface_composite (op, pattern, diff --git a/gfx/cairo/cairo/src/cairo-xlib-surface.c b/gfx/cairo/cairo/src/cairo-xlib-surface.c index 91531d60a8d..80ce023be50 100644 --- a/gfx/cairo/cairo/src/cairo-xlib-surface.c +++ b/gfx/cairo/cairo/src/cairo-xlib-surface.c @@ -3575,6 +3575,7 @@ typedef struct _cairo_xlib_font_glyphset_info { typedef struct _cairo_xlib_surface_font_private { cairo_scaled_font_t *scaled_font; + cairo_scaled_font_t *grayscale_font; cairo_xlib_hook_t close_display_hook; cairo_xlib_display_t *display; cairo_xlib_font_glyphset_info_t glyphset_info[NUM_GLYPHSETS]; @@ -3604,6 +3605,10 @@ _cairo_xlib_surface_remove_scaled_font (cairo_xlib_display_t *display, Display *dpy; int i; + if (font_private->grayscale_font) { + cairo_scaled_font_destroy (font_private->grayscale_font); + } + dpy = _cairo_xlib_display_get_dpy (display); for (i = 0; i < NUM_GLYPHSETS; i++) { cairo_xlib_font_glyphset_info_t *glyphset_info; @@ -3634,6 +3639,7 @@ _cairo_xlib_surface_font_init (Display *dpy, return _cairo_error (CAIRO_STATUS_NO_MEMORY); font_private->scaled_font = scaled_font; + font_private->grayscale_font = NULL; status = _cairo_xlib_display_get (dpy, &font_private->display); if (unlikely (status)) { free (font_private); @@ -3676,6 +3682,10 @@ _cairo_xlib_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) cairo_xlib_display_t *display; int i; + if (font_private->grayscale_font) { + cairo_scaled_font_destroy (font_private->grayscale_font); + } + display = font_private->display; _cairo_xlib_remove_close_display_hook (display, &font_private->close_display_hook); @@ -4422,6 +4432,52 @@ _cairo_xlib_surface_owns_font (cairo_xlib_surface_t *dst, return TRUE; } +/* Gets a grayscale version of scaled_font. The grayscale version is cached + * in our surface_private data. + */ +static cairo_scaled_font_t * +_cairo_xlib_get_grayscale_font (cairo_xlib_surface_t *dst, + cairo_scaled_font_t *scaled_font) +{ + cairo_xlib_surface_font_private_t *font_private = scaled_font->surface_private; + cairo_bool_t needs_font; + + if (font_private == NULL) { + cairo_status_t status = _cairo_xlib_surface_font_init (dst->dpy, scaled_font); + if (unlikely (status)) + return _cairo_scaled_font_create_in_error (status); + font_private = scaled_font->surface_private; + } + + CAIRO_MUTEX_LOCK (scaled_font->mutex); + needs_font = !font_private->grayscale_font; + CAIRO_MUTEX_UNLOCK (scaled_font->mutex); + + if (needs_font) { + cairo_font_options_t options; + cairo_scaled_font_t *new_font; + + options = scaled_font->options; + options.antialias = CAIRO_ANTIALIAS_GRAY; + new_font = cairo_scaled_font_create (scaled_font->font_face, + &scaled_font->font_matrix, + &scaled_font->ctm, &options); + + CAIRO_MUTEX_LOCK (scaled_font->mutex); + if (!font_private->grayscale_font) { + font_private->grayscale_font = new_font; + new_font = NULL; + } + CAIRO_MUTEX_UNLOCK (scaled_font->mutex); + + if (new_font) { + cairo_scaled_font_destroy (new_font); + } + } + + return font_private->grayscale_font; +} + static cairo_int_status_t _cairo_xlib_surface_show_glyphs (void *abstract_dst, cairo_operator_t op, @@ -4480,6 +4536,11 @@ _cairo_xlib_surface_show_glyphs (void *abstract_dst, if (! _cairo_xlib_surface_owns_font (dst, scaled_font)) return UNSUPPORTED ("unowned font"); + if (!dst->base.permit_subpixel_antialiasing && + scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL) { + scaled_font = _cairo_xlib_get_grayscale_font (dst, scaled_font); + } + X_DEBUG ((dst->dpy, "show_glyphs (dst=%x)", (unsigned int) dst->drawable)); if (clip_region != NULL && diff --git a/gfx/cairo/cairo/src/cairo.h b/gfx/cairo/cairo/src/cairo.h index 905b831ba3e..b149bd7ecea 100644 --- a/gfx/cairo/cairo/src/cairo.h +++ b/gfx/cairo/cairo/src/cairo.h @@ -2106,6 +2106,25 @@ cairo_surface_show_page (cairo_surface_t *surface); cairo_public cairo_bool_t cairo_surface_has_show_text_glyphs (cairo_surface_t *surface); +/** + * _cairo_subpixel_antialiasing_t: + * @CAIRO_SUBPIXEL_ANTIALIASING_ENABLED: subpixel antialiasing is enabled + * for this surface. + * @CAIRO_SUBPIXEL_ANTIALIASING_DISABLED: subpixel antialiasing is disabled + * for this surface. + */ +typedef enum _cairo_subpixel_antialiasing_t { + CAIRO_SUBPIXEL_ANTIALIASING_ENABLED, + CAIRO_SUBPIXEL_ANTIALIASING_DISABLED +} cairo_subpixel_antialiasing_t; + +cairo_public void +cairo_surface_set_subpixel_antialiasing (cairo_surface_t *surface, + cairo_subpixel_antialiasing_t enabled); + +cairo_public cairo_subpixel_antialiasing_t +cairo_surface_get_subpixel_antialiasing (cairo_surface_t *surface); + /* Image-surface functions */ /** diff --git a/gfx/cairo/cairo/src/cairoint.h b/gfx/cairo/cairo/src/cairoint.h index 63612e63ae3..e0127e8c90f 100644 --- a/gfx/cairo/cairo/src/cairoint.h +++ b/gfx/cairo/cairo/src/cairoint.h @@ -2755,6 +2755,8 @@ slim_hidden_proto (cairo_surface_get_font_options); slim_hidden_proto (cairo_surface_get_mime_data); slim_hidden_proto (cairo_surface_get_type); slim_hidden_proto (cairo_surface_has_show_text_glyphs); +slim_hidden_proto (cairo_surface_set_subpixel_antialiasing); +slim_hidden_proto (cairo_surface_get_subpixel_antialiasing); slim_hidden_proto (cairo_surface_mark_dirty_rectangle); slim_hidden_proto_no_warn (cairo_surface_reference); slim_hidden_proto (cairo_surface_set_device_offset); diff --git a/gfx/cairo/libpixman/src/Makefile.in b/gfx/cairo/libpixman/src/Makefile.in index 4f26babefa0..9fc04143ce5 100644 --- a/gfx/cairo/libpixman/src/Makefile.in +++ b/gfx/cairo/libpixman/src/Makefile.in @@ -158,7 +158,7 @@ ifdef USE_ARM_NEON_GCC CSRCS += pixman-arm-neon.c SSRCS += pixman-arm-neon-asm.S DEFINES += -DUSE_ARM_NEON -ARM_NEON_CFLAGS = -mfloat-abi=softfp -mfpu=neon +ARM_NEON_CFLAGS = -mfpu=neon endif EXPORTS = pixman.h pixman-version.h diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h index 9cde8dfdeff..176107d2e7c 100644 --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -232,10 +232,12 @@ class THEBES_API LayerManager { public: enum LayersBackend { - LAYERS_BASIC = 0, + LAYERS_NONE = 0, + LAYERS_BASIC, LAYERS_OPENGL, LAYERS_D3D9, - LAYERS_D3D10 + LAYERS_D3D10, + LAYERS_LAST }; LayerManager() : mDestroyed(PR_FALSE), mSnapEffectiveTransforms(PR_TRUE) @@ -629,6 +631,20 @@ public: // quality. PRBool CanUseOpaqueSurface(); + enum SurfaceMode { + SURFACE_OPAQUE, + SURFACE_SINGLE_CHANNEL_ALPHA, + SURFACE_COMPONENT_ALPHA + }; + SurfaceMode GetSurfaceMode() + { + if (CanUseOpaqueSurface()) + return SURFACE_OPAQUE; + if (mContentFlags & CONTENT_COMPONENT_ALPHA) + return SURFACE_COMPONENT_ALPHA; + return SURFACE_SINGLE_CHANNEL_ALPHA; + } + /** * This setter can be used anytime. The user data for all keys is * initially null. Ownership pases to the layer manager. @@ -935,12 +951,19 @@ public: */ PRBool HasMultipleChildren(); + /** + * Returns true if this container supports children with component alpha. + * Should only be called while painting a child of this layer. + */ + PRBool SupportsComponentAlphaChildren() { return mSupportsComponentAlphaChildren; } + protected: ContainerLayer(LayerManager* aManager, void* aImplData) : Layer(aManager, aImplData), mFirstChild(nsnull), mLastChild(nsnull), - mUseIntermediateSurface(PR_FALSE) + mUseIntermediateSurface(PR_FALSE), + mSupportsComponentAlphaChildren(PR_FALSE) { mContentFlags = 0; // Clear NO_TEXT, NO_TEXT_OVER_TRANSPARENT } @@ -962,6 +985,7 @@ protected: Layer* mLastChild; FrameMetrics mFrameMetrics; PRPackedBool mUseIntermediateSurface; + PRPackedBool mSupportsComponentAlphaChildren; }; /** diff --git a/gfx/layers/basic/BasicLayers.cpp b/gfx/layers/basic/BasicLayers.cpp index 687cc1b8e05..1f994dc31f1 100644 --- a/gfx/layers/basic/BasicLayers.cpp +++ b/gfx/layers/basic/BasicLayers.cpp @@ -162,6 +162,7 @@ public: ContainerLayer(aManager, static_cast(this)) { MOZ_COUNT_CTOR(BasicContainerLayer); + mSupportsComponentAlphaChildren = PR_TRUE; } virtual ~BasicContainerLayer(); @@ -454,6 +455,19 @@ IntersectWithClip(const nsIntRegion& aRegion, gfxContext* aContext) return result; } +static void +SetAntialiasingFlags(Layer* aLayer, gfxContext* aTarget) +{ + nsRefPtr surface = aTarget->CurrentSurface(); + if (surface->GetContentType() != gfxASurface::CONTENT_COLOR_ALPHA) { + // Destination doesn't have alpha channel; no need to set any special flags + return; + } + + surface->SetSubpixelAntialiasingEnabled( + !(aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA)); +} + void BasicThebesLayer::Paint(gfxContext* aContext, LayerManager::DrawThebesLayerCallback aCallback, @@ -488,8 +502,9 @@ BasicThebesLayer::Paint(gfxContext* aContext, target->Save(); gfxUtils::ClipToRegionSnapped(target, toDraw); if (opacity != 1.0) { - target->PushGroup(contentType); + target->PushGroupAndCopyBackground(contentType); } + SetAntialiasingFlags(this, target); aCallback(this, target, toDraw, nsIntRegion(), aCallbackData); if (opacity != 1.0) { target->PopGroupToSource(); @@ -515,6 +530,7 @@ BasicThebesLayer::Paint(gfxContext* aContext, state.mRegionToInvalidate.And(state.mRegionToInvalidate, mVisibleRegion); mXResolution = paintXRes; mYResolution = paintYRes; + SetAntialiasingFlags(this, state.mContext); PaintBuffer(state.mContext, state.mRegionToDraw, state.mRegionToInvalidate, aCallback, aCallbackData); @@ -1299,6 +1315,22 @@ BasicLayerManager::PaintLayer(Layer* aLayer, effectiveTransform.Is2D(&transform); mTarget->SetMatrix(transform); + PRBool pushedTargetOpaqueRect = PR_FALSE; + const nsIntRegion& visibleRegion = aLayer->GetEffectiveVisibleRegion(); + nsRefPtr currentSurface = mTarget->CurrentSurface(); + const gfxRect& targetOpaqueRect = currentSurface->GetOpaqueRect(); + + // Try to annotate currentSurface with a region of pixels that have been + // (or will be) painted opaque, if no such region is currently set. + if (targetOpaqueRect.IsEmpty() && visibleRegion.GetNumRects() == 1 && + (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) && + !transform.HasNonAxisAlignedTransform()) { + const nsIntRect& bounds = visibleRegion.GetBounds(); + currentSurface->SetOpaqueRect( + mTarget->UserToDevice(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height))); + pushedTargetOpaqueRect = PR_TRUE; + } + if (needsGroup) { // If we need to call PushGroup, we should clip to the smallest possible // area first to minimize the size of the temporary surface. @@ -1306,7 +1338,7 @@ BasicLayerManager::PaintLayer(Layer* aLayer, gfxASurface::gfxContentType type = aLayer->CanUseOpaqueSurface() ? gfxASurface::CONTENT_COLOR : gfxASurface::CONTENT_COLOR_ALPHA; - mTarget->PushGroup(type); + mTarget->PushGroupAndCopyBackground(type); } /* Only paint ourself, or our children - This optimization relies on this! */ @@ -1337,6 +1369,10 @@ BasicLayerManager::PaintLayer(Layer* aLayer, mTarget->Paint(aLayer->GetEffectiveOpacity()); } + if (pushedTargetOpaqueRect) { + currentSurface->SetOpaqueRect(gfxRect(0, 0, 0, 0)); + } + if (needsSaveRestore) { mTarget->Restore(); } else { @@ -1696,11 +1732,20 @@ BasicShadowableThebesLayer::CreateBuffer(Buffer::ContentType aType, // XXX error handling SurfaceDescriptor tmpFront; - if (!BasicManager()->AllocDoubleBuffer(gfxIntSize(aSize.width, aSize.height), - aType, - &tmpFront, - &mBackBuffer)) - NS_RUNTIMEABORT("creating ThebesLayer 'back buffer' failed!"); + if (BasicManager()->ShouldDoubleBuffer()) { + if (!BasicManager()->AllocDoubleBuffer(gfxIntSize(aSize.width, aSize.height), + aType, + &tmpFront, + &mBackBuffer)) { + NS_RUNTIMEABORT("creating ThebesLayer 'back buffer' failed!"); + } + } else { + if (!BasicManager()->AllocBuffer(gfxIntSize(aSize.width, aSize.height), + aType, + &mBackBuffer)) { + NS_RUNTIMEABORT("creating ThebesLayer 'back buffer' failed!"); + } + } NS_ABORT_IF_FALSE(!mIsNewBuffer, "Bad! Did we create a buffer twice without painting?"); @@ -1775,8 +1820,6 @@ BasicShadowableImageLayer::Paint(gfxContext* aContext, return; if (oldSize != mSize) { - NS_ASSERTION(oldSize == gfxIntSize(-1, -1), "video changed size?"); - if (mBackSurface) { BasicManager()->ShadowLayerForwarder::DestroySharedSurface(mBackSurface); mBackSurface = nsnull; @@ -1992,7 +2035,7 @@ public: MOZ_COUNT_DTOR(BasicShadowThebesLayer); } - virtual void SetFrontBuffer(const ThebesBuffer& aNewFront, + virtual void SetFrontBuffer(const OptionalThebesBuffer& aNewFront, const nsIntRegion& aValidRegion, float aXResolution, float aYResolution); @@ -2057,22 +2100,27 @@ private: }; void -BasicShadowThebesLayer::SetFrontBuffer(const ThebesBuffer& aNewFront, +BasicShadowThebesLayer::SetFrontBuffer(const OptionalThebesBuffer& aNewFront, const nsIntRegion& aValidRegion, float aXResolution, float aYResolution) { mValidRegion = mOldValidRegion = aValidRegion; mXResolution = mOldXResolution = aXResolution; mYResolution = mOldYResolution = aYResolution; + + NS_ABORT_IF_FALSE(OptionalThebesBuffer::Tnull_t != aNewFront.type(), + "aNewFront must be valid here!"); + + const ThebesBuffer newFront = aNewFront.get_ThebesBuffer(); nsRefPtr newFrontBuffer = - BasicManager()->OpenDescriptor(aNewFront.buffer()); + BasicManager()->OpenDescriptor(newFront.buffer()); nsRefPtr unused; nsIntRect unusedRect; nsIntPoint unusedRotation; - mFrontBuffer.Swap(newFrontBuffer, aNewFront.rect(), aNewFront.rotation(), + mFrontBuffer.Swap(newFrontBuffer, newFront.rect(), newFront.rotation(), getter_AddRefs(unused), &unusedRect, &unusedRotation); - mFrontBufferDescriptor = aNewFront.buffer(); + mFrontBufferDescriptor = newFront.buffer(); } void diff --git a/gfx/layers/d3d10/ImageLayerD3D10.cpp b/gfx/layers/d3d10/ImageLayerD3D10.cpp index 0d4ea23fe8d..3991662d0ec 100644 --- a/gfx/layers/d3d10/ImageLayerD3D10.cpp +++ b/gfx/layers/d3d10/ImageLayerD3D10.cpp @@ -457,9 +457,24 @@ CairoImageD3D10::SetData(const CairoImage::Data &aData) already_AddRefed CairoImageD3D10::GetAsSurface() { + nsRefPtr surfTexture; + + // Make a copy of the texture since our current texture is not suitable for + // drawing with Direct2D because it is immutable and cannot be bound as a + // render target. + D3D10_TEXTURE2D_DESC texDesc; + mTexture->GetDesc(&texDesc); + texDesc.Usage = D3D10_USAGE_DEFAULT; + texDesc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; + texDesc.MiscFlags = D3D10_RESOURCE_MISC_GDI_COMPATIBLE; + + mDevice->CreateTexture2D(&texDesc, NULL, getter_AddRefs(surfTexture)); + + mDevice->CopyResource(surfTexture, mTexture); + nsRefPtr surf = - new gfxD2DSurface(mTexture, mHasAlpha ? gfxASurface::CONTENT_COLOR_ALPHA : - gfxASurface::CONTENT_COLOR); + new gfxD2DSurface(surfTexture, mHasAlpha ? gfxASurface::CONTENT_COLOR_ALPHA : + gfxASurface::CONTENT_COLOR); return surf.forget(); } diff --git a/gfx/layers/d3d9/CanvasLayerD3D9.cpp b/gfx/layers/d3d9/CanvasLayerD3D9.cpp index a3b70a17286..4a73726ec43 100644 --- a/gfx/layers/d3d9/CanvasLayerD3D9.cpp +++ b/gfx/layers/d3d9/CanvasLayerD3D9.cpp @@ -75,17 +75,6 @@ CanvasLayerD3D9::Initialize(const Data& aData) mBounds.SetRect(0, 0, aData.mSize.width, aData.mSize.height); - if (mSurface && mSurface->GetType() == gfxASurface::SurfaceTypeD2D) { - void *data = mSurface->GetData(&gKeyD3D9Texture); - if (data) { - mTexture = static_cast(data); - mIsInteropTexture = true; - return; - } - } - - mIsInteropTexture = false; - CreateTexture(); } @@ -98,14 +87,6 @@ CanvasLayerD3D9::Updated(const nsIntRect& aRect) return; } -#ifdef CAIRO_HAS_D2D_SURFACE - if (mIsInteropTexture) { - mSurface->Flush(); - cairo_d2d_finish_device(gfxWindowsPlatform::GetPlatform()->GetD2DDevice()); - return; - } -#endif - if (mGLContext) { // WebGL reads entire surface. D3DLOCKED_RECT r; diff --git a/gfx/layers/d3d9/CanvasLayerD3D9.h b/gfx/layers/d3d9/CanvasLayerD3D9.h index 6178165f13b..9434a39c884 100644 --- a/gfx/layers/d3d9/CanvasLayerD3D9.h +++ b/gfx/layers/d3d9/CanvasLayerD3D9.h @@ -84,9 +84,6 @@ protected: PRUint32 mCanvasFramebuffer; - // Indicates whether our texture was obtained through D2D interop. - bool mIsInteropTexture; - PRPackedBool mDataIsPremultiplied; PRPackedBool mNeedsYFlip; }; diff --git a/gfx/layers/d3d9/ContainerLayerD3D9.cpp b/gfx/layers/d3d9/ContainerLayerD3D9.cpp index dabefe65ed4..cada6336136 100644 --- a/gfx/layers/d3d9/ContainerLayerD3D9.cpp +++ b/gfx/layers/d3d9/ContainerLayerD3D9.cpp @@ -153,6 +153,16 @@ GetNextSiblingD3D9(LayerD3D9* aLayer) : nsnull; } +static PRBool +HasOpaqueAncestorLayer(Layer* aLayer) +{ + for (Layer* l = aLayer->GetParent(); l; l = l->GetParent()) { + if (l->GetContentFlags() & Layer::CONTENT_OPAQUE) + return PR_TRUE; + } + return PR_FALSE; +} + void ContainerLayerD3D9::RenderLayer() { @@ -166,6 +176,7 @@ ContainerLayerD3D9::RenderLayer() nsIntRect visibleRect = mVisibleRegion.GetBounds(); PRBool useIntermediate = UseIntermediateSurface(); + mSupportsComponentAlphaChildren = PR_FALSE; gfxMatrix contTransform; if (useIntermediate) { device()->GetRenderTarget(0, getter_AddRefs(previousRenderTarget)); @@ -176,7 +187,36 @@ ContainerLayerD3D9::RenderLayer() nsRefPtr renderSurface; renderTexture->GetSurfaceLevel(0, getter_AddRefs(renderSurface)); device()->SetRenderTarget(0, renderSurface); - device()->Clear(0, 0, D3DCLEAR_TARGET, D3DCOLOR_RGBA(0, 0, 0, 0), 0, 0); + + if (mVisibleRegion.GetNumRects() == 1 && (GetContentFlags() & CONTENT_OPAQUE)) { + // don't need a background, we're going to paint all opaque stuff + mSupportsComponentAlphaChildren = PR_TRUE; + } else { + const gfx3DMatrix& transform3D = GetEffectiveTransform(); + gfxMatrix transform; + // If we have an opaque ancestor layer, then we can be sure that + // all the pixels we draw into are either opaque already or will be + // covered by something opaque. Otherwise copying up the background is + // not safe. + HRESULT hr = E_FAIL; + if (HasOpaqueAncestorLayer(this) && + transform3D.Is2D(&transform) && !transform.HasNonIntegerTranslation()) { + // Copy background up from below + RECT dest = { 0, 0, visibleRect.width, visibleRect.height }; + RECT src = dest; + ::OffsetRect(&src, + visibleRect.x + PRInt32(transform.x0), + visibleRect.y + PRInt32(transform.y0)); + hr = device()-> + StretchRect(previousRenderTarget, &src, renderSurface, &dest, D3DTEXF_NONE); + } + if (hr == S_OK) { + mSupportsComponentAlphaChildren = PR_TRUE; + } else { + device()->Clear(0, 0, D3DCLEAR_TARGET, D3DCOLOR_RGBA(0, 0, 0, 0), 0, 0); + } + } + device()->GetVertexShaderConstantF(CBvRenderTargetOffset, previousRenderTargetOffset, 1); renderTargetOffset[0] = (float)visibleRect.x; renderTargetOffset[1] = (float)visibleRect.y; @@ -200,6 +240,8 @@ ContainerLayerD3D9::RenderLayer() #endif GetEffectiveTransform().Is2D(&contTransform); NS_ASSERTION(is2d, "Transform must be 2D"); + mSupportsComponentAlphaChildren = (GetContentFlags() & CONTENT_OPAQUE) || + (mParent && mParent->SupportsComponentAlphaChildren()); } /* diff --git a/gfx/layers/d3d9/DeviceManagerD3D9.cpp b/gfx/layers/d3d9/DeviceManagerD3D9.cpp index 3c2731f098e..979ae082110 100644 --- a/gfx/layers/d3d9/DeviceManagerD3D9.cpp +++ b/gfx/layers/d3d9/DeviceManagerD3D9.cpp @@ -363,6 +363,20 @@ DeviceManagerD3D9::Init() return false; } + hr = mDevice->CreatePixelShader((DWORD*)ComponentPass1ShaderPS, + getter_AddRefs(mComponentPass1PS)); + + if (FAILED(hr)) { + return false; + } + + hr = mDevice->CreatePixelShader((DWORD*)ComponentPass2ShaderPS, + getter_AddRefs(mComponentPass2PS)); + + if (FAILED(hr)) { + return false; + } + hr = mDevice->CreatePixelShader((DWORD*)YCbCrShaderPS, getter_AddRefs(mYCbCrPS)); @@ -483,6 +497,14 @@ DeviceManagerD3D9::SetShaderMode(ShaderMode aMode) mDevice->SetVertexShader(mLayerVS); mDevice->SetPixelShader(mRGBAPS); break; + case COMPONENTLAYERPASS1: + mDevice->SetVertexShader(mLayerVS); + mDevice->SetPixelShader(mComponentPass1PS); + break; + case COMPONENTLAYERPASS2: + mDevice->SetVertexShader(mLayerVS); + mDevice->SetPixelShader(mComponentPass2PS); + break; case YCBCRLAYER: mDevice->SetVertexShader(mLayerVS); mDevice->SetPixelShader(mYCbCrPS); diff --git a/gfx/layers/d3d9/DeviceManagerD3D9.h b/gfx/layers/d3d9/DeviceManagerD3D9.h index 2f25dd83970..418cf8df230 100644 --- a/gfx/layers/d3d9/DeviceManagerD3D9.h +++ b/gfx/layers/d3d9/DeviceManagerD3D9.h @@ -142,6 +142,8 @@ public: enum ShaderMode { RGBLAYER, RGBALAYER, + COMPONENTLAYERPASS1, + COMPONENTLAYERPASS2, YCBCRLAYER, SOLIDCOLORLAYER }; @@ -205,6 +207,12 @@ private: /* Pixel shader used for RGBA textures */ nsRefPtr mRGBAPS; + /* Pixel shader used for component alpha textures (pass 1) */ + nsRefPtr mComponentPass1PS; + + /* Pixel shader used for component alpha textures (pass 2) */ + nsRefPtr mComponentPass2PS; + /* Pixel shader used for RGB textures */ nsRefPtr mYCbCrPS; diff --git a/gfx/layers/d3d9/LayerManagerD3D9.cpp b/gfx/layers/d3d9/LayerManagerD3D9.cpp index 088746b2bd6..7a091baae01 100644 --- a/gfx/layers/d3d9/LayerManagerD3D9.cpp +++ b/gfx/layers/d3d9/LayerManagerD3D9.cpp @@ -46,10 +46,6 @@ #include "nsIGfxInfo.h" #include "nsServiceManagerUtils.h" -#ifdef CAIRO_HAS_D2D_SURFACE -#include "gfxD2DSurface.h" -#endif - namespace mozilla { namespace layers { @@ -210,51 +206,11 @@ LayerManagerD3D9::CreateImageContainer() return container.forget(); } -cairo_user_data_key_t gKeyD3D9Texture; - void ReleaseTexture(void *texture) { static_cast(texture)->Release(); } -already_AddRefed -LayerManagerD3D9::CreateOptimalSurface(const gfxIntSize &aSize, - gfxASurface::gfxImageFormat aFormat) -{ -#ifdef CAIRO_HAS_D2D_SURFACE - if ((aFormat != gfxASurface::ImageFormatRGB24 && - aFormat != gfxASurface::ImageFormatARGB32) || - gfxWindowsPlatform::GetPlatform()->GetRenderMode() != - gfxWindowsPlatform::RENDER_DIRECT2D || - !deviceManager()->IsD3D9Ex()) { - return LayerManager::CreateOptimalSurface(aSize, aFormat); - } - - nsRefPtr texture; - - HANDLE sharedHandle = 0; - device()->CreateTexture(aSize.width, aSize.height, 1, - D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, - D3DPOOL_DEFAULT, getter_AddRefs(texture), &sharedHandle); - - nsRefPtr surface = - new gfxD2DSurface(sharedHandle, aFormat == gfxASurface::ImageFormatRGB24 ? - gfxASurface::CONTENT_COLOR : gfxASurface::CONTENT_COLOR_ALPHA); - - if (!surface || surface->CairoStatus()) { - return LayerManager::CreateOptimalSurface(aSize, aFormat); - } - - surface->SetData(&gKeyD3D9Texture, - texture.forget().get(), - ReleaseTexture); - - return surface.forget(); -#else - return LayerManager::CreateOptimalSurface(aSize, aFormat); -#endif -} - void LayerManagerD3D9::ReportFailure(const nsACString &aMsg, HRESULT aCode) { diff --git a/gfx/layers/d3d9/LayerManagerD3D9.h b/gfx/layers/d3d9/LayerManagerD3D9.h index c476cca03d7..9f84d03db4e 100644 --- a/gfx/layers/d3d9/LayerManagerD3D9.h +++ b/gfx/layers/d3d9/LayerManagerD3D9.h @@ -51,8 +51,6 @@ namespace mozilla { namespace layers { -extern cairo_user_data_key_t gKeyD3D9Texture; - class LayerD3D9; class ThebesLayerD3D9; @@ -151,10 +149,6 @@ public: virtual already_AddRefed CreateImageContainer(); - virtual already_AddRefed - CreateOptimalSurface(const gfxIntSize &aSize, - gfxASurface::gfxImageFormat imageFormat); - virtual LayersBackend GetBackendType() { return LAYERS_D3D9; } virtual void GetBackendName(nsAString& name) { name.AssignLiteral("Direct3D 9"); } bool DeviceWasRemoved() { return deviceManager()->DeviceWasRemoved(); } diff --git a/gfx/layers/d3d9/LayerManagerD3D9Shaders.h b/gfx/layers/d3d9/LayerManagerD3D9Shaders.h index dfa1573360e..fe3525410eb 100644 --- a/gfx/layers/d3d9/LayerManagerD3D9Shaders.h +++ b/gfx/layers/d3d9/LayerManagerD3D9Shaders.h @@ -229,6 +229,228 @@ const BYTE RGBAShaderPS[] = // // Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 // +// fxc LayerManagerD3D9Shaders.hlsl -EComponentPass1Shader -nologo -Tps_2_0 +// -FhtmpShaderHeader -VnComponentPass1ShaderPS +// +// +// Parameters: +// +// float fLayerOpacity; +// sampler2D s2D; +// sampler2D s2DWhite; +// +// +// Registers: +// +// Name Reg Size +// ------------- ----- ---- +// fLayerOpacity c0 1 +// s2D s0 1 +// s2DWhite s1 1 +// + + ps_2_0 + def c1, 1, 0, 0, 0 + dcl t0.xy + dcl_2d s0 + dcl_2d s1 + texld r0, t0, s0 + texld r1, t0, s1 + add r0.xyz, r0, -r1 + add r0.xyz, r0, c1.x + mul r0.xyz, r0, c0.x + mov r1.xyz, r0 + mov r1.w, r0.y + mov oC0, r1 + +// approximately 8 instruction slots used (2 texture, 6 arithmetic) +#endif + +const BYTE ComponentPass1ShaderPS[] = +{ + 0, 2, 255, 255, 254, 255, + 57, 0, 67, 84, 65, 66, + 28, 0, 0, 0, 175, 0, + 0, 0, 0, 2, 255, 255, + 3, 0, 0, 0, 28, 0, + 0, 0, 0, 1, 0, 0, + 168, 0, 0, 0, 88, 0, + 0, 0, 2, 0, 0, 0, + 1, 0, 0, 0, 104, 0, + 0, 0, 0, 0, 0, 0, + 120, 0, 0, 0, 3, 0, + 0, 0, 1, 0, 0, 0, + 124, 0, 0, 0, 0, 0, + 0, 0, 140, 0, 0, 0, + 3, 0, 1, 0, 1, 0, + 0, 0, 152, 0, 0, 0, + 0, 0, 0, 0, 102, 76, + 97, 121, 101, 114, 79, 112, + 97, 99, 105, 116, 121, 0, + 171, 171, 0, 0, 3, 0, + 1, 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 115, 50, 68, 0, 4, 0, + 12, 0, 1, 0, 1, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 115, 50, 68, 87, + 104, 105, 116, 101, 0, 171, + 171, 171, 4, 0, 12, 0, + 1, 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 112, 115, 95, 50, 95, 48, + 0, 77, 105, 99, 114, 111, + 115, 111, 102, 116, 32, 40, + 82, 41, 32, 72, 76, 83, + 76, 32, 83, 104, 97, 100, + 101, 114, 32, 67, 111, 109, + 112, 105, 108, 101, 114, 32, + 57, 46, 50, 57, 46, 57, + 53, 50, 46, 51, 49, 49, + 49, 0, 81, 0, 0, 5, + 1, 0, 15, 160, 0, 0, + 128, 63, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 31, 0, 0, 2, + 0, 0, 0, 128, 0, 0, + 3, 176, 31, 0, 0, 2, + 0, 0, 0, 144, 0, 8, + 15, 160, 31, 0, 0, 2, + 0, 0, 0, 144, 1, 8, + 15, 160, 66, 0, 0, 3, + 0, 0, 15, 128, 0, 0, + 228, 176, 0, 8, 228, 160, + 66, 0, 0, 3, 1, 0, + 15, 128, 0, 0, 228, 176, + 1, 8, 228, 160, 2, 0, + 0, 3, 0, 0, 7, 128, + 0, 0, 228, 128, 1, 0, + 228, 129, 2, 0, 0, 3, + 0, 0, 7, 128, 0, 0, + 228, 128, 1, 0, 0, 160, + 5, 0, 0, 3, 0, 0, + 7, 128, 0, 0, 228, 128, + 0, 0, 0, 160, 1, 0, + 0, 2, 1, 0, 7, 128, + 0, 0, 228, 128, 1, 0, + 0, 2, 1, 0, 8, 128, + 0, 0, 85, 128, 1, 0, + 0, 2, 0, 8, 15, 128, + 1, 0, 228, 128, 255, 255, + 0, 0 +}; +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +// fxc LayerManagerD3D9Shaders.hlsl -EComponentPass2Shader -nologo -Tps_2_0 +// -FhtmpShaderHeader -VnComponentPass2ShaderPS +// +// +// Parameters: +// +// float fLayerOpacity; +// sampler2D s2D; +// sampler2D s2DWhite; +// +// +// Registers: +// +// Name Reg Size +// ------------- ----- ---- +// fLayerOpacity c0 1 +// s2D s0 1 +// s2DWhite s1 1 +// + + ps_2_0 + def c1, 1, 0, 0, 0 + dcl t0.xy + dcl_2d s0 + dcl_2d s1 + texld r0, t0, s1 + texld r1, t0, s0 + add r0.x, -r0.y, r1.y + add r1.w, r0.x, c1.x + mul r0, r1, c0.x + mov oC0, r0 + +// approximately 6 instruction slots used (2 texture, 4 arithmetic) +#endif + +const BYTE ComponentPass2ShaderPS[] = +{ + 0, 2, 255, 255, 254, 255, + 57, 0, 67, 84, 65, 66, + 28, 0, 0, 0, 175, 0, + 0, 0, 0, 2, 255, 255, + 3, 0, 0, 0, 28, 0, + 0, 0, 0, 1, 0, 0, + 168, 0, 0, 0, 88, 0, + 0, 0, 2, 0, 0, 0, + 1, 0, 0, 0, 104, 0, + 0, 0, 0, 0, 0, 0, + 120, 0, 0, 0, 3, 0, + 0, 0, 1, 0, 0, 0, + 124, 0, 0, 0, 0, 0, + 0, 0, 140, 0, 0, 0, + 3, 0, 1, 0, 1, 0, + 0, 0, 152, 0, 0, 0, + 0, 0, 0, 0, 102, 76, + 97, 121, 101, 114, 79, 112, + 97, 99, 105, 116, 121, 0, + 171, 171, 0, 0, 3, 0, + 1, 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 115, 50, 68, 0, 4, 0, + 12, 0, 1, 0, 1, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 115, 50, 68, 87, + 104, 105, 116, 101, 0, 171, + 171, 171, 4, 0, 12, 0, + 1, 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 112, 115, 95, 50, 95, 48, + 0, 77, 105, 99, 114, 111, + 115, 111, 102, 116, 32, 40, + 82, 41, 32, 72, 76, 83, + 76, 32, 83, 104, 97, 100, + 101, 114, 32, 67, 111, 109, + 112, 105, 108, 101, 114, 32, + 57, 46, 50, 57, 46, 57, + 53, 50, 46, 51, 49, 49, + 49, 0, 81, 0, 0, 5, + 1, 0, 15, 160, 0, 0, + 128, 63, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 31, 0, 0, 2, + 0, 0, 0, 128, 0, 0, + 3, 176, 31, 0, 0, 2, + 0, 0, 0, 144, 0, 8, + 15, 160, 31, 0, 0, 2, + 0, 0, 0, 144, 1, 8, + 15, 160, 66, 0, 0, 3, + 0, 0, 15, 128, 0, 0, + 228, 176, 1, 8, 228, 160, + 66, 0, 0, 3, 1, 0, + 15, 128, 0, 0, 228, 176, + 0, 8, 228, 160, 2, 0, + 0, 3, 0, 0, 1, 128, + 0, 0, 85, 129, 1, 0, + 85, 128, 2, 0, 0, 3, + 1, 0, 8, 128, 0, 0, + 0, 128, 1, 0, 0, 160, + 5, 0, 0, 3, 0, 0, + 15, 128, 1, 0, 228, 128, + 0, 0, 0, 160, 1, 0, + 0, 2, 0, 8, 15, 128, + 0, 0, 228, 128, 255, 255, + 0, 0 +}; +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// // fxc LayerManagerD3D9Shaders.hlsl -ERGBShader -nologo -Tps_2_0 // -FhtmpShaderHeader -VnRGBShaderPS // diff --git a/gfx/layers/d3d9/LayerManagerD3D9Shaders.hlsl b/gfx/layers/d3d9/LayerManagerD3D9Shaders.hlsl index 87596b8573c..c39c2ea5330 100644 --- a/gfx/layers/d3d9/LayerManagerD3D9Shaders.hlsl +++ b/gfx/layers/d3d9/LayerManagerD3D9Shaders.hlsl @@ -8,6 +8,7 @@ rect vLayerQuad; texture tex0; sampler s2D; +sampler s2DWhite; sampler s2DY; sampler s2DCb; sampler s2DCr; @@ -57,6 +58,22 @@ VS_OUTPUT LayerQuadVS(const VS_INPUT aVertex) return outp; } +float4 ComponentPass1Shader(const VS_OUTPUT aVertex) : COLOR +{ + float4 src = tex2D(s2D, aVertex.vTexCoords); + float4 alphas = 1.0 - tex2D(s2DWhite, aVertex.vTexCoords) + src; + alphas.a = alphas.g; + return alphas * fLayerOpacity; +} + +float4 ComponentPass2Shader(const VS_OUTPUT aVertex) : COLOR +{ + float4 src = tex2D(s2D, aVertex.vTexCoords); + float4 alphas = 1.0 - tex2D(s2DWhite, aVertex.vTexCoords) + src; + src.a = alphas.g; + return src * fLayerOpacity; +} + float4 RGBAShader(const VS_OUTPUT aVertex) : COLOR { return tex2D(s2D, aVertex.vTexCoords) * fLayerOpacity; diff --git a/gfx/layers/d3d9/ThebesLayerD3D9.cpp b/gfx/layers/d3d9/ThebesLayerD3D9.cpp index b265168581f..89d8f015d89 100644 --- a/gfx/layers/d3d9/ThebesLayerD3D9.cpp +++ b/gfx/layers/d3d9/ThebesLayerD3D9.cpp @@ -39,9 +39,8 @@ #include "gfxPlatform.h" #include "gfxWindowsPlatform.h" -#ifdef CAIRO_HAS_D2D_SURFACE -#include "gfxD2DSurface.h" -#endif +#include "gfxTeeSurface.h" +#include "gfxUtils.h" namespace mozilla { namespace layers { @@ -49,7 +48,6 @@ namespace layers { ThebesLayerD3D9::ThebesLayerD3D9(LayerManagerD3D9 *aManager) : ThebesLayer(aManager, NULL) , LayerD3D9(aManager) - , mD2DSurfaceInitialized(false) { mImplData = static_cast(this); aManager->deviceManager()->mLayersWithResources.AppendElement(this); @@ -70,69 +68,22 @@ ThebesLayerD3D9::~ThebesLayerD3D9() #define RETENTION_THRESHOLD 16384 void -ThebesLayerD3D9::SetVisibleRegion(const nsIntRegion &aRegion) +ThebesLayerD3D9::InvalidateRegion(const nsIntRegion &aRegion) { - if (aRegion.IsEqual(mVisibleRegion)) { - return; - } - - nsIntRegion oldVisibleRegion = mVisibleRegion; - ThebesLayer::SetVisibleRegion(aRegion); - - if (!mTexture) { - // If we don't need to retain content initialize lazily. This is good also - // because we might get mIsOpaqueSurface set later than the first call to - // SetVisibleRegion. - return; - } - - D3DFORMAT fmt = (CanUseOpaqueSurface() && !mD2DSurface) ? - D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8; - - D3DSURFACE_DESC desc; - mTexture->GetLevelDesc(0, &desc); - - if (fmt != desc.Format) { - // The new format isn't compatible with the old texture, toss out the old - // texture. - mTexture = nsnull; - } - - VerifyContentType(); - - nsRefPtr oldTexture = mTexture; - - nsIntRect oldBounds = oldVisibleRegion.GetBounds(); - nsIntRect newBounds = mVisibleRegion.GetBounds(); - - CreateNewTexture(gfxIntSize(newBounds.width, newBounds.height)); - - // Old visible region will become the region that is covered by both the - // old and the new visible region. - oldVisibleRegion.And(oldVisibleRegion, mVisibleRegion); - // No point in retaining parts which were not valid. - oldVisibleRegion.And(oldVisibleRegion, mValidRegion); - - nsIntRect largeRect = oldVisibleRegion.GetLargestRectangle(); - - // If we had no hardware texture before or have no retained area larger than - // the retention threshold, we're not retaining and are done here. If our - // texture creation failed this can mean a device reset is pending and we - // should silently ignore the failure. In the future when device failures - // are properly handled we should test for the type of failure and gracefully - // handle different failures. See bug 569081. - if (!oldTexture || !mTexture || - largeRect.width * largeRect.height < RETENTION_THRESHOLD) { - mValidRegion.SetEmpty(); - return; - } + mValidRegion.Sub(mValidRegion, aRegion); +} +void +ThebesLayerD3D9::CopyRegion(IDirect3DTexture9* aSrc, const nsIntPoint &aSrcOffset, + IDirect3DTexture9* aDest, const nsIntPoint &aDestOffset, + const nsIntRegion &aCopyRegion, nsIntRegion* aValidRegion) +{ nsRefPtr srcSurface, dstSurface; - oldTexture->GetSurfaceLevel(0, getter_AddRefs(srcSurface)); - mTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + aSrc->GetSurfaceLevel(0, getter_AddRefs(srcSurface)); + aDest->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); nsIntRegion retainedRegion; - nsIntRegionRectIterator iter(oldVisibleRegion); + nsIntRegionRectIterator iter(aCopyRegion); const nsIntRect *r; while ((r = iter.Next())) { if (r->width * r->height > RETENTION_THRESHOLD) { @@ -140,13 +91,13 @@ ThebesLayerD3D9::SetVisibleRegion(const nsIntRegion &aRegion) // Calculate the retained rectangle's position on the old and the new // surface. - oldRect.left = r->x - oldBounds.x; - oldRect.top = r->y - oldBounds.y; + oldRect.left = r->x - aSrcOffset.x; + oldRect.top = r->y - aSrcOffset.y; oldRect.right = oldRect.left + r->width; oldRect.bottom = oldRect.top + r->height; - newRect.left = r->x - newBounds.x; - newRect.top = r->y - newBounds.y; + newRect.left = r->x - aDestOffset.x; + newRect.top = r->y - aDestOffset.y; newRect.right = newRect.left + r->width; newRect.bottom = newRect.top + r->height; @@ -161,81 +112,61 @@ ThebesLayerD3D9::SetVisibleRegion(const nsIntRegion &aRegion) } // Areas which were valid and were retained are still valid - mValidRegion.And(mValidRegion, retainedRegion); + aValidRegion->And(*aValidRegion, retainedRegion); } - -void -ThebesLayerD3D9::InvalidateRegion(const nsIntRegion &aRegion) +static PRUint64 RectArea(const nsIntRect& aRect) { - mValidRegion.Sub(mValidRegion, aRegion); + return aRect.width*PRUint64(aRect.height); } void -ThebesLayerD3D9::RenderLayer() +ThebesLayerD3D9::UpdateTextures(SurfaceMode aMode) { - if (mVisibleRegion.IsEmpty()) { - return; - } - nsIntRect visibleRect = mVisibleRegion.GetBounds(); - // We differentiate between these formats since D3D9 will only allow us to - // call GetDC on an opaque surface. - D3DFORMAT fmt = (CanUseOpaqueSurface() && !mD2DSurface) ? - D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8; + if (HaveTextures(aMode)) { + if (mTextureRect != visibleRect) { + nsRefPtr oldTexture = mTexture; + nsRefPtr oldTextureOnWhite = mTextureOnWhite; - if (mTexture) { - D3DSURFACE_DESC desc; - mTexture->GetLevelDesc(0, &desc); + NS_ASSERTION(mTextureRect.Contains(mValidRegion.GetBounds()), + "How can we have valid data outside the texture?"); + nsIntRegion retainRegion; + // The region we want to retain is the valid data that is inside + // the new visible region + retainRegion.And(mValidRegion, mVisibleRegion); - if (fmt != desc.Format) { - // The new format isn't compatible with the old texture, toss out the old - // texture. - mTexture = nsnull; - mValidRegion.SetEmpty(); + CreateNewTextures(gfxIntSize(visibleRect.width, visibleRect.height), aMode); + + // If our texture creation failed this can mean a device reset is pending and we + // should silently ignore the failure. In the future when device failures + // are properly handled we should test for the type of failure and gracefully + // handle different failures. See bug 569081. + if (!HaveTextures(aMode)) { + mValidRegion.SetEmpty(); + } else { + CopyRegion(oldTexture, mTextureRect.TopLeft(), mTexture, visibleRect.TopLeft(), + retainRegion, &mValidRegion); + if (aMode == SURFACE_COMPONENT_ALPHA) { + CopyRegion(oldTextureOnWhite, mTextureRect.TopLeft(), mTextureOnWhite, visibleRect.TopLeft(), + retainRegion, &mValidRegion); + } + } + + mTextureRect = visibleRect; } - } - - VerifyContentType(); - - if (!mTexture) { - CreateNewTexture(gfxIntSize(visibleRect.width, visibleRect.height)); + } else { + CreateNewTextures(gfxIntSize(visibleRect.width, visibleRect.height), aMode); + mTextureRect = visibleRect; - if (!mTexture) { - NS_WARNING("Failed to create texture for thebes layer - not drawing."); - return; - } - - mValidRegion.SetEmpty(); + NS_ASSERTION(mValidRegion.IsEmpty(), "Someone forgot to empty the region"); } +} - if (!mValidRegion.IsEqual(mVisibleRegion)) { - /* We use the bounds of the visible region because we draw the bounds of - * this region when we draw this entire texture. We have to make sure that - * the areas that aren't filled with content get their background drawn. - * This is an issue for opaque surfaces, which otherwise won't get their - * background painted. - */ - nsIntRegion region; - region.Sub(mVisibleRegion, mValidRegion); - - DrawRegion(region); - - mValidRegion = mVisibleRegion; - } - - SetShaderTransformAndOpacity(); - -#ifdef CAIRO_HAS_D2D_SURFACE - if (mD2DSurface && CanUseOpaqueSurface()) { - mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBLAYER); - } else -#endif - mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBALAYER); - - device()->SetTexture(0, mTexture); - +void +ThebesLayerD3D9::RenderVisibleRegion() +{ nsIntRegionRectIterator iter(mVisibleRegion); const nsIntRect *iterRect; @@ -249,13 +180,72 @@ ThebesLayerD3D9::RenderLayer() device()->SetVertexShaderConstantF(CBvTextureCoords, ShaderConstantRect( - (float)(iterRect->x - visibleRect.x) / (float)visibleRect.width, - (float)(iterRect->y - visibleRect.y) / (float)visibleRect.height, - (float)iterRect->width / (float)visibleRect.width, - (float)iterRect->height / (float)visibleRect.height), 1); + (float)(iterRect->x - mTextureRect.x) / (float)mTextureRect.width, + (float)(iterRect->y - mTextureRect.y) / (float)mTextureRect.height, + (float)iterRect->width / (float)mTextureRect.width, + (float)iterRect->height / (float)mTextureRect.height), 1); device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); } +} + +void +ThebesLayerD3D9::RenderLayer() +{ + if (mVisibleRegion.IsEmpty()) { + return; + } + + SurfaceMode mode = GetSurfaceMode(); + if (mode == SURFACE_COMPONENT_ALPHA && + (!mParent || !mParent->SupportsComponentAlphaChildren())) { + mode = SURFACE_SINGLE_CHANNEL_ALPHA; + } + VerifyContentType(mode); + UpdateTextures(mode); + if (!HaveTextures(mode)) { + NS_WARNING("Texture creation failed"); + return; + } + + if (!mValidRegion.IsEqual(mVisibleRegion)) { + /* We use the bounds of the visible region because we draw the bounds of + * this region when we draw this entire texture. We have to make sure that + * the areas that aren't filled with content get their background drawn. + * This is an issue for opaque surfaces, which otherwise won't get their + * background painted. + */ + nsIntRegion region; + region.Sub(mVisibleRegion, mValidRegion); + DrawRegion(region, mode); + + mValidRegion = mVisibleRegion; + } + + SetShaderTransformAndOpacity(); + + if (mode == SURFACE_COMPONENT_ALPHA) { + mD3DManager->SetShaderMode(DeviceManagerD3D9::COMPONENTLAYERPASS1); + device()->SetTexture(0, mTexture); + device()->SetTexture(1, mTextureOnWhite); + device()->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO); + device()->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR); + RenderVisibleRegion(); + + mD3DManager->SetShaderMode(DeviceManagerD3D9::COMPONENTLAYERPASS2); + device()->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); + device()->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); + RenderVisibleRegion(); + + // Restore defaults + device()->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); + device()->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + device()->SetTexture(1, NULL); + } else { + mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBALAYER); + device()->SetTexture(0, mTexture); + RenderVisibleRegion(); + } // Set back to default. device()->SetVertexShaderConstantF(CBvTextureCoords, @@ -267,6 +257,8 @@ void ThebesLayerD3D9::CleanResources() { mTexture = nsnull; + mTextureOnWhite = nsnull; + mValidRegion.SetEmpty(); } void @@ -289,107 +281,159 @@ ThebesLayerD3D9::IsEmpty() } void -ThebesLayerD3D9::VerifyContentType() +ThebesLayerD3D9::VerifyContentType(SurfaceMode aMode) { -#ifdef CAIRO_HAS_D2D_SURFACE - if (mD2DSurface) { - gfxASurface::gfxContentType type = CanUseOpaqueSurface() ? - gfxASurface::CONTENT_COLOR : gfxASurface::CONTENT_COLOR_ALPHA; + if (!mTexture) + return; - if (type != mD2DSurface->GetContentType()) { - // We could choose to recreate only the D2D surface, but since we can't - // use retention the synchronisation overhead probably isn't worth it. - mD2DSurface = nsnull; - mTexture = nsnull; + D3DSURFACE_DESC desc; + mTexture->GetLevelDesc(0, &desc); + + switch (aMode) { + case SURFACE_OPAQUE: + if (desc.Format == D3DFMT_X8R8G8B8 && !mTextureOnWhite) + return; + break; + + case SURFACE_SINGLE_CHANNEL_ALPHA: + if (desc.Format == D3DFMT_A8R8G8B8 && !mTextureOnWhite) + return; + break; + + case SURFACE_COMPONENT_ALPHA: + if (mTextureOnWhite) { + NS_ASSERTION(desc.Format == D3DFMT_X8R8G8B8, "Wrong format for component alpha texture"); + return; } + break; } -#endif + + // The new format isn't compatible with the old texture(s), toss out the old + // texture(s). + mTexture = nsnull; + mTextureOnWhite = nsnull; + mValidRegion.SetEmpty(); +} + +class OpaqueRenderer { +public: + OpaqueRenderer(const nsIntRegion& aUpdateRegion) : + mUpdateRegion(aUpdateRegion), mDC(NULL) {} + already_AddRefed Begin(LayerD3D9* aLayer); + void End(); + IDirect3DTexture9* GetTexture() { return mTmpTexture; } + +private: + const nsIntRegion& mUpdateRegion; + nsRefPtr mTmpTexture; + nsRefPtr mSurface; + HDC mDC; +}; + +already_AddRefed +OpaqueRenderer::Begin(LayerD3D9* aLayer) +{ + nsIntRect bounds = mUpdateRegion.GetBounds(); + + HRESULT hr = aLayer->device()-> + CreateTexture(bounds.width, bounds.height, 1, 0, D3DFMT_X8R8G8B8, + D3DPOOL_SYSTEMMEM, getter_AddRefs(mTmpTexture), NULL); + + if (FAILED(hr)) { + aLayer->ReportFailure(NS_LITERAL_CSTRING("Failed to create temporary texture in system memory."), hr); + return nsnull; + } + + hr = mTmpTexture->GetSurfaceLevel(0, getter_AddRefs(mSurface)); + + if (FAILED(hr)) { + // Uh-oh, bail. + NS_WARNING("Failed to get texture surface level."); + return nsnull; + } + + hr = mSurface->GetDC(&mDC); + if (FAILED(hr)) { + NS_WARNING("Failed to get device context for texture surface."); + return nsnull; + } + + nsRefPtr result = new gfxWindowsSurface(mDC); + return result.forget(); } void -ThebesLayerD3D9::DrawRegion(const nsIntRegion &aRegion) +OpaqueRenderer::End() +{ + mSurface->ReleaseDC(mDC); +} + +static void +FillSurface(gfxASurface* aSurface, const nsIntRegion& aRegion, + const nsIntPoint& aOffset, const gfxRGBA& aColor) +{ + nsRefPtr ctx = new gfxContext(aSurface); + ctx->Translate(-gfxPoint(aOffset.x, aOffset.y)); + gfxUtils::ClipToRegion(ctx, aRegion); + ctx->SetColor(aColor); + ctx->Paint(); +} + +void +ThebesLayerD3D9::DrawRegion(const nsIntRegion &aRegion, SurfaceMode aMode) { HRESULT hr; nsIntRect visibleRect = mVisibleRegion.GetBounds(); nsRefPtr context; -#ifdef CAIRO_HAS_D2D_SURFACE - if (mD2DSurface) { - context = new gfxContext(mD2DSurface); - nsIntRegionRectIterator iter(aRegion); - - context->Translate(gfxPoint(-visibleRect.x, -visibleRect.y)); - context->NewPath(); - const nsIntRect *iterRect; - while ((iterRect = iter.Next())) { - context->Rectangle(gfxRect(iterRect->x, iterRect->y, iterRect->width, iterRect->height)); - } - context->Clip(); - if (!mD2DSurfaceInitialized || - mD2DSurface->GetContentType() != gfxASurface::CONTENT_COLOR) { - context->SetOperator(gfxContext::OPERATOR_CLEAR); - context->Paint(); - context->SetOperator(gfxContext::OPERATOR_OVER); - mD2DSurfaceInitialized = true; - } - - LayerManagerD3D9::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo(); - cbInfo.Callback(this, context, aRegion, nsIntRegion(), cbInfo.CallbackData); - mD2DSurface->Flush(); - - // XXX - This call is quite expensive, we may want to consider doing our - // drawing in a seperate 'validation' iteration. And then flushing once for - // all the D2D surfaces we might have drawn, before doing our D3D9 rendering - // loop. - cairo_d2d_finish_device(gfxWindowsPlatform::GetPlatform()->GetD2DDevice()); - return; - } -#endif - - D3DFORMAT fmt = CanUseOpaqueSurface() ? D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8; - nsIntRect bounds = aRegion.GetBounds(); - - gfxASurface::gfxImageFormat imageFormat = gfxASurface::ImageFormatARGB32; nsRefPtr destinationSurface; - + nsIntRect bounds = aRegion.GetBounds(); nsRefPtr tmpTexture; - hr = device()->CreateTexture(bounds.width, bounds.height, 1, - 0, fmt, - D3DPOOL_SYSTEMMEM, getter_AddRefs(tmpTexture), NULL); + OpaqueRenderer opaqueRenderer(aRegion); + OpaqueRenderer opaqueRendererOnWhite(aRegion); - if (FAILED(hr)) { - ReportFailure(NS_LITERAL_CSTRING("Failed to create temporary texture in system memory."), hr); - return; - } + switch (aMode) + { + case SURFACE_OPAQUE: + destinationSurface = opaqueRenderer.Begin(this); + break; - nsRefPtr surf; - HDC dc; - if (CanUseOpaqueSurface()) { - hr = tmpTexture->GetSurfaceLevel(0, getter_AddRefs(surf)); + case SURFACE_SINGLE_CHANNEL_ALPHA: { + hr = device()->CreateTexture(bounds.width, bounds.height, 1, + 0, D3DFMT_A8R8G8B8, + D3DPOOL_SYSTEMMEM, getter_AddRefs(tmpTexture), NULL); - if (FAILED(hr)) { - // Uh-oh, bail. - NS_WARNING("Failed to get texture surface level."); - return; + if (FAILED(hr)) { + ReportFailure(NS_LITERAL_CSTRING("Failed to create temporary texture in system memory."), hr); + return; + } + + // XXX - We may consider retaining a SYSTEMMEM texture texture the size + // of our DEFAULT texture and then use UpdateTexture and add dirty rects + // to update in a single call. + nsRefPtr dest = new gfxWindowsSurface( + gfxIntSize(bounds.width, bounds.height), gfxASurface::ImageFormatARGB32); + // If the contents of this layer don't require component alpha in the + // end of rendering, it's safe to enable Cleartype since all the Cleartype + // glyphs must be over (or under) opaque pixels. + dest->SetSubpixelAntialiasingEnabled(!(mContentFlags & CONTENT_COMPONENT_ALPHA)); + destinationSurface = dest.forget(); + break; } - hr = surf->GetDC(&dc); - - if (FAILED(hr)) { - NS_WARNING("Failed to get device context for texture surface."); - return; + case SURFACE_COMPONENT_ALPHA: { + nsRefPtr onBlack = opaqueRenderer.Begin(this); + nsRefPtr onWhite = opaqueRendererOnWhite.Begin(this); + FillSurface(onBlack, aRegion, bounds.TopLeft(), gfxRGBA(0.0, 0.0, 0.0, 1.0)); + FillSurface(onWhite, aRegion, bounds.TopLeft(), gfxRGBA(1.0, 1.0, 1.0, 1.0)); + gfxASurface* surfaces[2] = { onBlack.get(), onWhite.get() }; + destinationSurface = new gfxTeeSurface(surfaces, NS_ARRAY_LENGTH(surfaces)); + // Using this surface as a source will likely go horribly wrong, since + // only the onBlack surface will really be used, so alpha information will + // be incorrect. + destinationSurface->SetAllowUseAsSource(PR_FALSE); + break; } - - destinationSurface = new gfxWindowsSurface(dc); - } else { - // XXX - We may consider retaining a SYSTEMMEM texture texture the size - // of our DEFAULT texture and then use UpdateTexture and add dirty rects - // to update in a single call. - destinationSurface = - gfxPlatform::GetPlatform()-> - CreateOffscreenSurface(gfxIntSize(bounds.width, - bounds.height), - gfxASurface::ContentFromFormat(imageFormat)); } context = new gfxContext(destinationSurface); @@ -397,86 +441,96 @@ ThebesLayerD3D9::DrawRegion(const nsIntRegion &aRegion) LayerManagerD3D9::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo(); cbInfo.Callback(this, context, aRegion, nsIntRegion(), cbInfo.CallbackData); - if (CanUseOpaqueSurface()) { - surf->ReleaseDC(dc); - } else { - D3DLOCKED_RECT r; - tmpTexture->LockRect(0, &r, NULL, 0); + nsAutoTArray srcTextures; + nsAutoTArray destTextures; + switch (aMode) + { + case SURFACE_OPAQUE: + opaqueRenderer.End(); + srcTextures.AppendElement(opaqueRenderer.GetTexture()); + destTextures.AppendElement(mTexture); + break; - nsRefPtr imgSurface = - new gfxImageSurface((unsigned char *)r.pBits, - gfxIntSize(bounds.width, - bounds.height), - r.Pitch, - imageFormat); + case SURFACE_SINGLE_CHANNEL_ALPHA: { + D3DLOCKED_RECT r; + tmpTexture->LockRect(0, &r, NULL, 0); - context = new gfxContext(imgSurface); - context->SetSource(destinationSurface); - context->SetOperator(gfxContext::OPERATOR_SOURCE); - context->Paint(); + nsRefPtr imgSurface = + new gfxImageSurface((unsigned char *)r.pBits, + gfxIntSize(bounds.width, + bounds.height), + r.Pitch, + gfxASurface::ImageFormatARGB32); - imgSurface = NULL; + context = new gfxContext(imgSurface); + context->SetSource(destinationSurface); + context->SetOperator(gfxContext::OPERATOR_SOURCE); + context->Paint(); - tmpTexture->UnlockRect(0); + imgSurface = NULL; + + tmpTexture->UnlockRect(0); + + srcTextures.AppendElement(tmpTexture); + destTextures.AppendElement(mTexture); + break; + } + + case SURFACE_COMPONENT_ALPHA: { + opaqueRenderer.End(); + opaqueRendererOnWhite.End(); + srcTextures.AppendElement(opaqueRenderer.GetTexture()); + destTextures.AppendElement(mTexture); + srcTextures.AppendElement(opaqueRendererOnWhite.GetTexture()); + destTextures.AppendElement(mTextureOnWhite); + break; + } } + NS_ASSERTION(srcTextures.Length() == destTextures.Length(), "Mismatched lengths"); - nsRefPtr srcSurface; - nsRefPtr dstSurface; + for (PRUint32 i = 0; i < srcTextures.Length(); ++i) { + nsRefPtr srcSurface; + nsRefPtr dstSurface; - mTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); - tmpTexture->GetSurfaceLevel(0, getter_AddRefs(srcSurface)); + destTextures[i]->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + srcTextures[i]->GetSurfaceLevel(0, getter_AddRefs(srcSurface)); - nsIntRegionRectIterator iter(aRegion); - const nsIntRect *iterRect; - while ((iterRect = iter.Next())) { - RECT rect; - rect.left = iterRect->x - bounds.x; - rect.top = iterRect->y - bounds.y; - rect.right = rect.left + iterRect->width; - rect.bottom = rect.top + iterRect->height; - POINT point; - point.x = iterRect->x - visibleRect.x; - point.y = iterRect->y - visibleRect.y; - device()->UpdateSurface(srcSurface, &rect, dstSurface, &point); + nsIntRegionRectIterator iter(aRegion); + const nsIntRect *iterRect; + while ((iterRect = iter.Next())) { + RECT rect; + rect.left = iterRect->x - bounds.x; + rect.top = iterRect->y - bounds.y; + rect.right = rect.left + iterRect->width; + rect.bottom = rect.top + iterRect->height; + POINT point; + point.x = iterRect->x - visibleRect.x; + point.y = iterRect->y - visibleRect.y; + device()->UpdateSurface(srcSurface, &rect, dstSurface, &point); + } } } void -ThebesLayerD3D9::CreateNewTexture(const gfxIntSize &aSize) +ThebesLayerD3D9::CreateNewTextures(const gfxIntSize &aSize, + SurfaceMode aMode) { - if (aSize.width == 0 | aSize.height == 0) { + if (aSize.width == 0 || aSize.height == 0) { // Nothing to do. return; } mTexture = nsnull; - PRBool canUseOpaqueSurface = CanUseOpaqueSurface(); -#ifdef CAIRO_HAS_D2D_SURFACE - if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() == - gfxWindowsPlatform::RENDER_DIRECT2D) { - if (mD3DManager->deviceManager()->IsD3D9Ex()) { - // We should have D3D9Ex where we have D2D. - HANDLE sharedHandle = 0; - device()->CreateTexture(aSize.width, aSize.height, 1, - D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, - D3DPOOL_DEFAULT, getter_AddRefs(mTexture), &sharedHandle); - - mD2DSurfaceInitialized = false; - mD2DSurface = new gfxD2DSurface(sharedHandle, canUseOpaqueSurface ? - gfxASurface::CONTENT_COLOR : gfxASurface::CONTENT_COLOR_ALPHA); - - // If there's an error, go on and do what we always do. - if (mD2DSurface->CairoStatus()) { - mD2DSurface = nsnull; - mTexture = nsnull; - } - } - } -#endif - if (!mTexture) { + mTextureOnWhite = nsnull; + device()->CreateTexture(aSize.width, aSize.height, 1, + D3DUSAGE_RENDERTARGET, + aMode != SURFACE_SINGLE_CHANNEL_ALPHA ? D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8, + D3DPOOL_DEFAULT, getter_AddRefs(mTexture), NULL); + if (aMode == SURFACE_COMPONENT_ALPHA) { device()->CreateTexture(aSize.width, aSize.height, 1, - D3DUSAGE_RENDERTARGET, canUseOpaqueSurface ? D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8, - D3DPOOL_DEFAULT, getter_AddRefs(mTexture), NULL); + D3DUSAGE_RENDERTARGET, + D3DFMT_X8R8G8B8, + D3DPOOL_DEFAULT, getter_AddRefs(mTextureOnWhite), NULL); } } diff --git a/gfx/layers/d3d9/ThebesLayerD3D9.h b/gfx/layers/d3d9/ThebesLayerD3D9.h index d3e93b262b8..a3b9d342c3f 100644 --- a/gfx/layers/d3d9/ThebesLayerD3D9.h +++ b/gfx/layers/d3d9/ThebesLayerD3D9.h @@ -52,9 +52,6 @@ public: ThebesLayerD3D9(LayerManagerD3D9 *aManager); virtual ~ThebesLayerD3D9(); - /* Layer implementation */ - void SetVisibleRegion(const nsIntRegion& aRegion); - /* ThebesLayer implementation */ void InvalidateRegion(const nsIntRegion& aRegion); @@ -70,20 +67,43 @@ private: * D3D9 texture */ nsRefPtr mTexture; + /* + * D3D9 texture for render-on-white when doing component alpha + */ + nsRefPtr mTextureOnWhite; + /** + * Visible region bounds used when we drew the contents of the textures + */ + nsIntRect mTextureRect; - /* Checks if our D2D surface has the right content type */ - void VerifyContentType(); + bool HaveTextures(SurfaceMode aMode) + { + return mTexture && (aMode != SURFACE_COMPONENT_ALPHA || mTextureOnWhite); + } - /* This contains the D2D surface if we have one */ - nsRefPtr mD2DSurface; + /* Checks if our surface has the right content type */ + void VerifyContentType(SurfaceMode aMode); - bool mD2DSurfaceInitialized; + /* Ensures we have the necessary texture object(s) and that they correspond + * to mVisibleRegion.GetBounds(). This creates new texture objects as + * necessary and also copies existing valid texture data if necessary. + */ + void UpdateTextures(SurfaceMode aMode); + + /* Render the rectangles of mVisibleRegion with D3D9 using the currently + * bound textures, target, shaders, etc. + */ + void RenderVisibleRegion(); /* Have a region of our layer drawn */ - void DrawRegion(const nsIntRegion &aRegion); + void DrawRegion(const nsIntRegion &aRegion, SurfaceMode aMode); /* Create a new texture */ - void CreateNewTexture(const gfxIntSize &aSize); + void CreateNewTextures(const gfxIntSize &aSize, SurfaceMode aMode); + + void CopyRegion(IDirect3DTexture9* aSrc, const nsIntPoint &aSrcOffset, + IDirect3DTexture9* aDest, const nsIntPoint &aDestOffset, + const nsIntRegion &aCopyRegion, nsIntRegion* aValidRegion); }; } /* layers */ diff --git a/gfx/layers/d3d9/genshaders.sh b/gfx/layers/d3d9/genshaders.sh index 383041ad03d..d657bba9e2b 100644 --- a/gfx/layers/d3d9/genshaders.sh +++ b/gfx/layers/d3d9/genshaders.sh @@ -4,6 +4,10 @@ fxc LayerManagerD3D9Shaders.hlsl -ELayerQuadVS -nologo -Fh$tempfile -VnLayerQuad cat $tempfile >> LayerManagerD3D9Shaders.h fxc LayerManagerD3D9Shaders.hlsl -ERGBAShader -nologo -Tps_2_0 -Fh$tempfile -VnRGBAShaderPS cat $tempfile >> LayerManagerD3D9Shaders.h +fxc LayerManagerD3D9Shaders.hlsl -EComponentPass1Shader -nologo -Tps_2_0 -Fh$tempfile -VnComponentPass1ShaderPS +cat $tempfile >> LayerManagerD3D9Shaders.h +fxc LayerManagerD3D9Shaders.hlsl -EComponentPass2Shader -nologo -Tps_2_0 -Fh$tempfile -VnComponentPass2ShaderPS +cat $tempfile >> LayerManagerD3D9Shaders.h fxc LayerManagerD3D9Shaders.hlsl -ERGBShader -nologo -Tps_2_0 -Fh$tempfile -VnRGBShaderPS cat $tempfile >> LayerManagerD3D9Shaders.h fxc LayerManagerD3D9Shaders.hlsl -EYCbCrShader -nologo -Tps_2_0 -Fh$tempfile -VnYCbCrShaderPS diff --git a/gfx/layers/ipc/PLayers.ipdl b/gfx/layers/ipc/PLayers.ipdl index 2187336ad43..aa0732d8db3 100644 --- a/gfx/layers/ipc/PLayers.ipdl +++ b/gfx/layers/ipc/PLayers.ipdl @@ -54,6 +54,7 @@ using mozilla::GraphicsFilterType; using mozilla::layers::FrameMetrics; using mozilla::layers::SurfaceDescriptorX11; using mozilla::null_t; +using mozilla::LayersBackend; /** * The layers protocol is spoken between thread contexts that manage @@ -91,7 +92,7 @@ union OptionalThebesBuffer { ThebesBuffer; null_t; }; struct OpCreateThebesBuffer { PLayer layer; - ThebesBuffer initialFront; + OptionalThebesBuffer initialFront; nsIntRegion frontValidRegion; float xResolution; float yResolution; @@ -243,6 +244,9 @@ parent: sync Update(Edit[] cset) returns (EditReply[] reply); + sync GetParentType() + returns (LayersBackend backend); + async __delete__(); }; diff --git a/gfx/layers/ipc/ShadowLayerUtilsX11.cpp b/gfx/layers/ipc/ShadowLayerUtilsX11.cpp index 3c6fbf3adf9..ae161aa1a70 100644 --- a/gfx/layers/ipc/ShadowLayerUtilsX11.cpp +++ b/gfx/layers/ipc/ShadowLayerUtilsX11.cpp @@ -102,6 +102,15 @@ ShadowLayerForwarder::PlatformAllocDoubleBuffer(const gfxIntSize& aSize, gfxASurface::gfxContentType aContent, SurfaceDescriptor* aFrontBuffer, SurfaceDescriptor* aBackBuffer) +{ + return (PlatformAllocBuffer(aSize, aContent, aFrontBuffer) && + PlatformAllocBuffer(aSize, aContent, aBackBuffer)); +} + +PRBool +ShadowLayerForwarder::PlatformAllocBuffer(const gfxIntSize& aSize, + gfxASurface::gfxContentType aContent, + SurfaceDescriptor* aBuffer) { if (!UsingXCompositing()) { // If we're not using X compositing, we're probably compositing on @@ -111,23 +120,18 @@ ShadowLayerForwarder::PlatformAllocDoubleBuffer(const gfxIntSize& aSize, } gfxPlatform* platform = gfxPlatform::GetPlatform(); - nsRefPtr front = platform->CreateOffscreenSurface(aSize, aContent); - nsRefPtr back = platform->CreateOffscreenSurface(aSize, aContent); - if (!front || !back || - front->GetType() != gfxASurface::SurfaceTypeXlib || - back->GetType() != gfxASurface::SurfaceTypeXlib) { + nsRefPtr buffer = platform->CreateOffscreenSurface(aSize, aContent); + if (!buffer || + buffer->GetType() != gfxASurface::SurfaceTypeXlib) { NS_ERROR("creating Xlib front/back surfaces failed!"); return PR_FALSE; } - gfxXlibSurface* frontX = static_cast(front.get()); - gfxXlibSurface* backX = static_cast(back.get()); + gfxXlibSurface* bufferX = static_cast(buffer.get()); // Release Pixmap ownership to the layers model - frontX->ReleasePixmap(); - backX->ReleasePixmap(); + bufferX->ReleasePixmap(); - *aFrontBuffer = SurfaceDescriptorX11(frontX); - *aBackBuffer = SurfaceDescriptorX11(backX); + *aBuffer = SurfaceDescriptorX11(bufferX); return PR_TRUE; } diff --git a/gfx/layers/ipc/ShadowLayers.cpp b/gfx/layers/ipc/ShadowLayers.cpp index 75942cfb01a..eb63a3adfca 100644 --- a/gfx/layers/ipc/ShadowLayers.cpp +++ b/gfx/layers/ipc/ShadowLayers.cpp @@ -123,7 +123,9 @@ struct AutoTxnEnd { Transaction* mTxn; }; -ShadowLayerForwarder::ShadowLayerForwarder() : mShadowManager(NULL) +ShadowLayerForwarder::ShadowLayerForwarder() + : mShadowManager(NULL) + , mParentBackend(LayerManager::LAYERS_NONE) { mTxn = new Transaction(); } @@ -189,10 +191,14 @@ ShadowLayerForwarder::CreatedThebesBuffer(ShadowableLayer* aThebes, const nsIntRect& aBufferRect, const SurfaceDescriptor& aTempFrontBuffer) { + OptionalThebesBuffer buffer = null_t(); + if (SurfaceDescriptor::T__None != aTempFrontBuffer.type()) { + buffer = ThebesBuffer(aTempFrontBuffer, + aBufferRect, + nsIntPoint(0, 0)); + } mTxn->AddEdit(OpCreateThebesBuffer(NULL, Shadow(aThebes), - ThebesBuffer(aTempFrontBuffer, - aBufferRect, - nsIntPoint(0, 0)), + buffer, aFrontValidRegion, aXResolution, aYResolution)); @@ -372,6 +378,18 @@ ShadowLayerForwarder::EndTransaction(InfallibleTArray* aReplies) return PR_TRUE; } +LayersBackend +ShadowLayerForwarder::GetParentBackendType() +{ + if (mParentBackend == LayerManager::LAYERS_NONE) { + LayersBackend backend; + if (mShadowManager->SendGetParentType(&backend)) { + mParentBackend = backend; + } + } + return mParentBackend; +} + static gfxASurface::gfxImageFormat OptimalFormatFor(gfxASurface::gfxContentType aContent) { @@ -413,20 +431,8 @@ ShadowLayerForwarder::AllocDoubleBuffer(const gfxIntSize& aSize, gfxSharedImageSurface** aFrontBuffer, gfxSharedImageSurface** aBackBuffer) { - NS_ABORT_IF_FALSE(HasShadowManager(), "no manager to forward to"); - - gfxASurface::gfxImageFormat format = OptimalFormatFor(aContent); - SharedMemory::SharedMemoryType shmemType = OptimalShmemType(); - - nsRefPtr front = new gfxSharedImageSurface(); - nsRefPtr back = new gfxSharedImageSurface(); - if (!front->InitUnsafe(mShadowManager, aSize, format, shmemType) || - !back->InitUnsafe(mShadowManager, aSize, format, shmemType)) - return PR_FALSE; - - *aFrontBuffer = NULL; *aBackBuffer = NULL; - front.swap(*aFrontBuffer); back.swap(*aBackBuffer); - return PR_TRUE; + return AllocBuffer(aSize, aContent, aFrontBuffer) && + AllocBuffer(aSize, aContent, aBackBuffer); } void @@ -435,6 +441,26 @@ ShadowLayerForwarder::DestroySharedSurface(gfxSharedImageSurface* aSurface) mShadowManager->DeallocShmem(aSurface->GetShmem()); } +PRBool +ShadowLayerForwarder::AllocBuffer(const gfxIntSize& aSize, + gfxASurface::gfxContentType aContent, + gfxSharedImageSurface** aBuffer) +{ + NS_ABORT_IF_FALSE(HasShadowManager(), "no manager to forward to"); + + gfxASurface::gfxImageFormat format = OptimalFormatFor(aContent); + SharedMemory::SharedMemoryType shmemType = OptimalShmemType(); + + nsRefPtr back = + gfxSharedImageSurface::CreateUnsafe(mShadowManager, aSize, format, shmemType); + if (!back) + return PR_FALSE; + + *aBuffer = nsnull; + back.swap(*aBuffer); + return PR_TRUE; +} + PRBool ShadowLayerForwarder::AllocDoubleBuffer(const gfxIntSize& aSize, gfxASurface::gfxContentType aContent, @@ -462,6 +488,29 @@ ShadowLayerForwarder::AllocDoubleBuffer(const gfxIntSize& aSize, return PR_TRUE; } +PRBool +ShadowLayerForwarder::AllocBuffer(const gfxIntSize& aSize, + gfxASurface::gfxContentType aContent, + SurfaceDescriptor* aBuffer) +{ + PRBool tryPlatformSurface = PR_TRUE; +#ifdef DEBUG + tryPlatformSurface = !PR_GetEnv("MOZ_LAYERS_FORCE_SHMEM_SURFACES"); +#endif + if (tryPlatformSurface && + PlatformAllocBuffer(aSize, aContent, aBuffer)) { + return PR_TRUE; + } + + nsRefPtr buffer; + if (!AllocBuffer(aSize, aContent, + getter_AddRefs(buffer))) + return PR_FALSE; + + *aBuffer = buffer->GetShmem(); + return PR_TRUE; +} + /*static*/ already_AddRefed ShadowLayerForwarder::OpenDescriptor(const SurfaceDescriptor& aSurface) { @@ -472,7 +521,7 @@ ShadowLayerForwarder::OpenDescriptor(const SurfaceDescriptor& aSurface) switch (aSurface.type()) { case SurfaceDescriptor::TShmem: { - surf = new gfxSharedImageSurface(aSurface.get_Shmem()); + surf = gfxSharedImageSurface::Open(aSurface.get_Shmem()); return surf.forget(); } default: @@ -546,6 +595,14 @@ ShadowLayerForwarder::PlatformAllocDoubleBuffer(const gfxIntSize&, return PR_FALSE; } +PRBool +ShadowLayerForwarder::PlatformAllocBuffer(const gfxIntSize&, + gfxASurface::gfxContentType, + SurfaceDescriptor*) +{ + return PR_FALSE; +} + /*static*/ already_AddRefed ShadowLayerForwarder::PlatformOpenDescriptor(const SurfaceDescriptor&) { diff --git a/gfx/layers/ipc/ShadowLayers.h b/gfx/layers/ipc/ShadowLayers.h index 885f7daad7e..b67873c9b98 100644 --- a/gfx/layers/ipc/ShadowLayers.h +++ b/gfx/layers/ipc/ShadowLayers.h @@ -111,6 +111,8 @@ class Transaction; class ShadowLayerForwarder { public: + typedef LayerManager::LayersBackend LayersBackend; + virtual ~ShadowLayerForwarder(); /** @@ -285,6 +287,10 @@ public: gfxSharedImageSurface** aBackBuffer); void DestroySharedSurface(gfxSharedImageSurface* aSurface); + PRBool AllocBuffer(const gfxIntSize& aSize, + gfxASurface::gfxContentType aContent, + gfxSharedImageSurface** aBuffer); + /** * In the absence of platform-specific buffers these fall back to * Shmem/gfxSharedImageSurface. @@ -294,6 +300,10 @@ public: SurfaceDescriptor* aFrontBuffer, SurfaceDescriptor* aBackBuffer); + PRBool AllocBuffer(const gfxIntSize& aSize, + gfxASurface::gfxContentType aContent, + SurfaceDescriptor* aBuffer); + static already_AddRefed OpenDescriptor(const SurfaceDescriptor& aSurface); @@ -305,6 +315,14 @@ public: */ PLayerChild* ConstructShadowFor(ShadowableLayer* aLayer); + LayersBackend GetParentBackendType(); + + /* + * No need to use double buffer in system memory with GPU rendering, + * texture used as front buffer. + */ + bool ShouldDoubleBuffer() { return GetParentBackendType() == LayerManager::LAYERS_BASIC; } + protected: ShadowLayerForwarder(); @@ -316,6 +334,10 @@ private: SurfaceDescriptor* aFrontBuffer, SurfaceDescriptor* aBackBuffer); + PRBool PlatformAllocBuffer(const gfxIntSize& aSize, + gfxASurface::gfxContentType aContent, + SurfaceDescriptor* aBuffer); + static already_AddRefed PlatformOpenDescriptor(const SurfaceDescriptor& aDescriptor); @@ -324,6 +346,7 @@ private: static void PlatformSyncBeforeUpdate(); Transaction* mTxn; + LayersBackend mParentBackend; }; @@ -468,7 +491,7 @@ public: * Override the front buffer and its valid region with the specified * values. This is called when a new buffer has been created. */ - virtual void SetFrontBuffer(const ThebesBuffer& aNewFront, + virtual void SetFrontBuffer(const OptionalThebesBuffer& aNewFront, const nsIntRegion& aValidRegion, float aXResolution, float aYResolution) = 0; diff --git a/gfx/layers/ipc/ShadowLayersParent.cpp b/gfx/layers/ipc/ShadowLayersParent.cpp index 5719ee3c2cf..9412c60fcea 100644 --- a/gfx/layers/ipc/ShadowLayersParent.cpp +++ b/gfx/layers/ipc/ShadowLayersParent.cpp @@ -207,7 +207,7 @@ ShadowLayersParent::RecvUpdate(const InfallibleTArray& cset, ShadowCanvasLayer* canvas = static_cast( AsShadowLayer(ocb)->AsLayer()); nsRefPtr front = - new gfxSharedImageSurface(ocb.initialFront()); + gfxSharedImageSurface::Open(ocb.initialFront()); CanvasLayer::Data data; data.mSurface = front; data.mSize = ocb.size(); @@ -223,7 +223,9 @@ ShadowLayersParent::RecvUpdate(const InfallibleTArray& cset, ShadowImageLayer* image = static_cast( AsShadowLayer(ocb)->AsLayer()); - image->Init(new gfxSharedImageSurface(ocb.initialFront()), ocb.size()); + nsRefPtr surf = + gfxSharedImageSurface::Open(ocb.initialFront()); + image->Init(surf, ocb.size()); break; } @@ -396,8 +398,9 @@ ShadowLayersParent::RecvUpdate(const InfallibleTArray& cset, ShadowCanvasLayer* canvas = static_cast(shadow->AsLayer()); - nsRefPtr newBack = - canvas->Swap(new gfxSharedImageSurface(op.newFrontBuffer())); + nsRefPtr newFront = + gfxSharedImageSurface::Open(op.newFrontBuffer()); + nsRefPtr newBack = canvas->Swap(newFront); canvas->Updated(op.updated()); replyv.push_back(OpBufferSwap(shadow, NULL, @@ -413,8 +416,9 @@ ShadowLayersParent::RecvUpdate(const InfallibleTArray& cset, ShadowImageLayer* image = static_cast(shadow->AsLayer()); - nsRefPtr newBack = - image->Swap(new gfxSharedImageSurface(op.newFrontBuffer())); + nsRefPtr newFront = + gfxSharedImageSurface::Open(op.newFrontBuffer()); + nsRefPtr newBack = image->Swap(newFront); replyv.push_back(OpBufferSwap(shadow, NULL, newBack->GetShmem())); @@ -444,6 +448,13 @@ ShadowLayersParent::RecvUpdate(const InfallibleTArray& cset, return true; } +bool +ShadowLayersParent::RecvGetParentType(LayersBackend* aBackend) +{ + *aBackend = layer_manager()->GetBackendType(); + return true; +} + PLayerParent* ShadowLayersParent::AllocPLayer() { diff --git a/gfx/layers/ipc/ShadowLayersParent.h b/gfx/layers/ipc/ShadowLayersParent.h index c7036ba0724..647080f205b 100644 --- a/gfx/layers/ipc/ShadowLayersParent.h +++ b/gfx/layers/ipc/ShadowLayersParent.h @@ -72,6 +72,8 @@ protected: NS_OVERRIDE virtual bool RecvUpdate(const EditArray& cset, EditReplyArray* reply); + NS_OVERRIDE virtual bool RecvGetParentType(LayersBackend* aBackend); + NS_OVERRIDE virtual PLayerParent* AllocPLayer(); NS_OVERRIDE virtual bool DeallocPLayer(PLayerParent* actor); diff --git a/gfx/layers/opengl/ThebesLayerOGL.cpp b/gfx/layers/opengl/ThebesLayerOGL.cpp index 6c769b793ea..98f87889f94 100644 --- a/gfx/layers/opengl/ThebesLayerOGL.cpp +++ b/gfx/layers/opengl/ThebesLayerOGL.cpp @@ -195,15 +195,16 @@ ThebesLayerBufferOGL::RenderTo(const nsIntPoint& aOffset, float xres = mLayer->GetXResolution(); float yres = mLayer->GetYResolution(); + program->Activate(); + program->SetLayerOpacity(mLayer->GetEffectiveOpacity()); + program->SetLayerTransform(mLayer->GetEffectiveTransform()); + program->SetRenderOffset(aOffset); + program->SetTextureUnit(0); + nsIntRegionRectIterator iter(mLayer->GetEffectiveVisibleRegion()); while (const nsIntRect *iterRect = iter.Next()) { nsIntRect quadRect = *iterRect; - program->Activate(); program->SetLayerQuadRect(quadRect); - program->SetLayerOpacity(mLayer->GetEffectiveOpacity()); - program->SetLayerTransform(mLayer->GetEffectiveTransform()); - program->SetRenderOffset(aOffset); - program->SetTextureUnit(0); DEBUG_GL_ERROR_CHECK(gl()); quadRect.MoveBy(-GetOriginOffset()); @@ -616,6 +617,12 @@ void ShadowBufferOGL::Upload(gfxASurface* aUpdate, const nsIntRegion& aUpdated, const nsIntRect& aRect, const nsIntPoint& aRotation) { + gfxIntSize size = aUpdate->GetSize(); + if (GetSize() != nsIntSize(size.width, size.height)) { + CreateTexture(aUpdate->GetContentType(), + nsIntSize(size.width, size.height)); + } + nsIntRegion destRegion(aUpdated); // aUpdated is in screen coordinates. Move it so that the layer's // top-left is 0,0 @@ -658,7 +665,7 @@ ShadowThebesLayerOGL::~ShadowThebesLayerOGL() {} void -ShadowThebesLayerOGL::SetFrontBuffer(const ThebesBuffer& aNewFront, +ShadowThebesLayerOGL::SetFrontBuffer(const OptionalThebesBuffer& aNewFront, const nsIntRegion& aValidRegion, float aXResolution, float aYResolution) { @@ -670,14 +677,8 @@ ShadowThebesLayerOGL::SetFrontBuffer(const ThebesBuffer& aNewFront, mBuffer = new ShadowBufferOGL(this); } - nsRefPtr surf = ShadowLayerForwarder::OpenDescriptor(aNewFront.buffer()); - gfxIntSize size = surf->GetSize(); - mBuffer->CreateTexture(surf->GetContentType(), - nsIntSize(size.width, size.height)); - - - - mDeadweight = aNewFront.buffer(); + NS_ASSERTION(OptionalThebesBuffer::Tnull_t == aNewFront.type(), + "Only one system-memory buffer expected"); } void @@ -706,9 +707,6 @@ void ShadowThebesLayerOGL::DestroyFrontBuffer() { mBuffer = nsnull; - if (SurfaceDescriptor::T__None != mDeadweight.type()) { - mOGLManager->DestroySharedSurface(&mDeadweight, mAllocator); - } } void diff --git a/gfx/layers/opengl/ThebesLayerOGL.h b/gfx/layers/opengl/ThebesLayerOGL.h index e604209ee92..fd8978e11a3 100644 --- a/gfx/layers/opengl/ThebesLayerOGL.h +++ b/gfx/layers/opengl/ThebesLayerOGL.h @@ -95,7 +95,7 @@ public: virtual ~ShadowThebesLayerOGL(); // ShadowThebesLayer impl - virtual void SetFrontBuffer(const ThebesBuffer& aNewFront, + virtual void SetFrontBuffer(const OptionalThebesBuffer& aNewFront, const nsIntRegion& aValidRegion, float aXResolution, float aYResolution); virtual void @@ -114,12 +114,6 @@ public: private: nsRefPtr mBuffer; - - - // XXX FIXME TEMP: hold on to this so that we can free it in DestroyFrontBuffer() - SurfaceDescriptor mDeadweight; - - }; #endif // MOZ_IPC diff --git a/gfx/src/nsRect.h b/gfx/src/nsRect.h index e262f0d9d4f..5c557aa320f 100644 --- a/gfx/src/nsRect.h +++ b/gfx/src/nsRect.h @@ -292,8 +292,9 @@ struct NS_GFX nsIntRect { PRBool Contains(const nsIntRect& aRect) const { - return (PRBool) ((aRect.x >= x) && (aRect.y >= y) && - (aRect.XMost() <= XMost()) && (aRect.YMost() <= YMost())); + return aRect.IsEmpty() || + (PRBool) ((aRect.x >= x) && (aRect.y >= y) && + (aRect.XMost() <= XMost()) && (aRect.YMost() <= YMost())); } PRBool Contains(PRInt32 aX, PRInt32 aY) const { diff --git a/gfx/src/nsRegion.cpp b/gfx/src/nsRegion.cpp index bc246e7cad2..6d7d7540f07 100644 --- a/gfx/src/nsRegion.cpp +++ b/gfx/src/nsRegion.cpp @@ -1339,12 +1339,22 @@ nsIntRegion nsRegion::ToOutsidePixels (nscoord aAppUnitsPerPixel) const return result; } -// This algorithm works in three phases: +// A cell's "value" is a pair consisting of +// a) the area of the subrectangle it corresponds to, if it's in +// aContainingRect and in the region, 0 otherwise +// b) the area of the subrectangle it corresponds to, if it's in the region, +// 0 otherwise +// Addition, subtraction and identity are defined on these values in the +// obvious way. Partial order is lexicographic. +// A "large negative value" is defined with large negative numbers for both +// fields of the pair. This negative value has the property that adding any +// number of non-negative values to it always results in a negative value. +// +// The GetLargestRectangle algorithm works in three phases: // 1) Convert the region into a grid by adding vertical/horizontal lines for // each edge of each rectangle in the region. // 2) For each rectangle in the region, for each cell it contains, set that -// cells's value to the area of the subrectangle it corresponds to. Cells -// that are not contained by any rectangle have the value 0. +// cells's value as described above. // 3) Calculate the submatrix with the largest sum such that none of its cells // contain any 0s (empty regions). The rectangle represented by the // submatrix is the largest rectangle in the region. @@ -1370,7 +1380,7 @@ nsIntRegion nsRegion::ToOutsidePixels (nscoord aAppUnitsPerPixel) const // S = array(m+1,n+1) // S[0][i] = 0 for i in [0,n] // S[j][0] = 0 for j in [0,m] -// S[j][i] = (if A[j-1][i-1] = 0 then some large negative number else A[j-1][i-1]) +// S[j][i] = (if A[j-1][i-1] = 0 then some large negative value else A[j-1][i-1]) // + S[j-1][n] + S[j][i-1] - S[j-1][i-1] // // // top, bottom, left, right, area @@ -1452,13 +1462,52 @@ namespace { const PRInt64 kVeryLargeNegativeNumber = 0xffff000000000000ll; + struct SizePair { + PRInt64 mSizeContainingRect; + PRInt64 mSize; + + SizePair() : mSizeContainingRect(0), mSize(0) {} + + static SizePair VeryLargeNegative() { + SizePair result; + result.mSize = result.mSizeContainingRect = kVeryLargeNegativeNumber; + return result; + } + SizePair& operator=(const SizePair& aOther) { + mSizeContainingRect = aOther.mSizeContainingRect; + mSize = aOther.mSize; + return *this; + } + PRBool operator<(const SizePair& aOther) const { + if (mSizeContainingRect < aOther.mSizeContainingRect) + return PR_TRUE; + if (mSizeContainingRect > aOther.mSizeContainingRect) + return PR_FALSE; + return mSize < aOther.mSize; + } + PRBool operator>(const SizePair& aOther) const { + return aOther.operator<(*this); + } + SizePair operator+(const SizePair& aOther) const { + SizePair result = *this; + result.mSizeContainingRect += aOther.mSizeContainingRect; + result.mSize += aOther.mSize; + return result; + } + SizePair operator-(const SizePair& aOther) const { + SizePair result = *this; + result.mSizeContainingRect -= aOther.mSizeContainingRect; + result.mSize -= aOther.mSize; + return result; + } + }; + // Returns the sum and indices of the subarray with the maximum sum of the // given array (A,n), assuming the array is already in prefix sum form. - PRInt64 MaxSum1D(const nsTArray &A, PRInt32 n, - PRInt32 *minIdx, PRInt32 *maxIdx) { + SizePair MaxSum1D(const nsTArray &A, PRInt32 n, + PRInt32 *minIdx, PRInt32 *maxIdx) { // The min/max indicies of the largest subarray found so far - PRInt64 min = 0, - max = 0; + SizePair min, max; PRInt32 currentMinIdx = 0; *minIdx = 0; @@ -1467,7 +1516,7 @@ namespace { // Because we're given the array in prefix sum form, we know the first // element is 0 for(PRInt32 i = 1; i < n; i++) { - PRInt64 cand = A[i] - min; + SizePair cand = A[i] - min; if (cand > max) { max = cand; *minIdx = currentMinIdx; @@ -1483,11 +1532,13 @@ namespace { } } -nsRect nsRegion::GetLargestRectangle () const { +nsRect nsRegion::GetLargestRectangle (const nsRect& aContainingRect) const { nsRect bestRect; - if (!mRectCount) + if (mRectCount <= 1) { + bestRect = mBoundRect; return bestRect; + } AxisPartition xaxis, yaxis; @@ -1500,6 +1551,12 @@ nsRect nsRegion::GetLargestRectangle () const { yaxis.InsertCoord(currentRect->y); yaxis.InsertCoord(currentRect->YMost()); } + if (!aContainingRect.IsEmpty()) { + xaxis.InsertCoord(aContainingRect.x); + xaxis.InsertCoord(aContainingRect.XMost()); + yaxis.InsertCoord(aContainingRect.y); + yaxis.InsertCoord(aContainingRect.YMost()); + } // Step 2: Fill out the grid with the areas // Note: due to the ordering of rectangles in the region, it is not always @@ -1507,9 +1564,8 @@ nsRect nsRegion::GetLargestRectangle () const { PRInt32 matrixHeight = yaxis.GetNumStops() - 1; PRInt32 matrixWidth = xaxis.GetNumStops() - 1; PRInt32 matrixSize = matrixHeight * matrixWidth; - nsTArray areas(matrixSize); + nsTArray areas(matrixSize); areas.SetLength(matrixSize); - memset(areas.Elements(), 0, matrixSize * sizeof(PRInt64)); iter.Reset(); while ((currentRect = iter.Next())) { @@ -1522,7 +1578,11 @@ nsRect nsRegion::GetLargestRectangle () const { nscoord height = yaxis.StopSize(y); for (PRInt32 x = xstart; x < xend; x++) { nscoord width = xaxis.StopSize(x); - areas[y*matrixWidth+x] = width*PRInt64(height); + PRInt64 size = width*PRInt64(height); + if (currentRect->Intersects(aContainingRect)) { + areas[y*matrixWidth+x].mSizeContainingRect = size; + } + areas[y*matrixWidth+x].mSize = size; } } } @@ -1532,21 +1592,17 @@ nsRect nsRegion::GetLargestRectangle () const { // First get the prefix sum array PRInt32 m = matrixHeight + 1; PRInt32 n = matrixWidth + 1; - nsTArray pareas(m*n); + nsTArray pareas(m*n); pareas.SetLength(m*n); - // Zero out the first row - for (PRInt32 x = 0; x < n; x++) - pareas[x] = 0; for (PRInt32 y = 1; y < m; y++) { - // Zero out the left column - pareas[y*n] = 0; for (PRInt32 x = 1; x < n; x++) { - PRInt64 area = areas[(y-1)*matrixWidth+x-1]; - if (!area) - area = kVeryLargeNegativeNumber; - area += pareas[ y*n+x-1] - + pareas[(y-1)*n+x ] - - pareas[(y-1)*n+x-1]; + SizePair area = areas[(y-1)*matrixWidth+x-1]; + if (!area.mSize) { + area = SizePair::VeryLargeNegative(); + } + area = area + pareas[ y*n+x-1] + + pareas[(y-1)*n+x ] + - pareas[(y-1)*n+x-1]; pareas[y*n+x] = area; } } @@ -1554,18 +1610,19 @@ nsRect nsRegion::GetLargestRectangle () const { // No longer need the grid areas.SetLength(0); - PRInt64 bestArea = 0; + SizePair bestArea; struct { PRInt32 left, top, right, bottom; } bestRectIndices = { 0, 0, 0, 0 }; for (PRInt32 m1 = 0; m1 < m; m1++) { for (PRInt32 m2 = m1+1; m2 < m; m2++) { - nsTArray B; + nsTArray B; B.SetLength(n); - for (PRInt32 i = 0; i < n; i++) + for (PRInt32 i = 0; i < n; i++) { B[i] = pareas[m2*n+i] - pareas[m1*n+i]; + } PRInt32 minIdx, maxIdx; - PRInt64 area = MaxSum1D(B, n, &minIdx, &maxIdx); + SizePair area = MaxSum1D(B, n, &minIdx, &maxIdx); if (area > bestArea) { bestRectIndices.left = minIdx; bestRectIndices.top = m1; @@ -1589,6 +1646,32 @@ void nsRegion::SimplifyOutward (PRUint32 aMaxRects) { NS_ASSERTION(aMaxRects >= 1, "Invalid max rect count"); + if (mRectCount <= aMaxRects) + return; + + // Try combining rects in horizontal bands into a single rect + RgnRect* pRect = mRectListHead.next; + while (pRect != &mRectListHead) + { + // Combine with the following rectangle if they have the same YMost + // or if they overlap vertically. This ensures that all overlapping + // rectangles are merged, preserving the invariant that rectangles + // don't overlap. + // The goal here is to try to keep groups of rectangles that are vertically + // discontiguous as separate rectangles in the final region. This is + // simple and fast to implement and page contents tend to vary more + // vertically than horizontally (which is why our rectangles are stored + // sorted by y-coordinate, too). + while (pRect->next != &mRectListHead && + pRect->YMost () >= pRect->next->y) + { + pRect->UnionRect(*pRect, *pRect->next); + delete Remove (pRect->next); + } + + pRect = pRect->next; + } + if (mRectCount <= aMaxRects) return; diff --git a/gfx/src/nsRegion.h b/gfx/src/nsRegion.h index 83ad6231b6a..6e70fb39508 100644 --- a/gfx/src/nsRegion.h +++ b/gfx/src/nsRegion.h @@ -184,7 +184,13 @@ public: nsRegion ConvertAppUnitsRoundOut (PRInt32 aFromAPP, PRInt32 aToAPP) const; nsRegion ConvertAppUnitsRoundIn (PRInt32 aFromAPP, PRInt32 aToAPP) const; nsIntRegion ToOutsidePixels (nscoord aAppUnitsPerPixel) const; - nsRect GetLargestRectangle () const; + /** + * Gets the largest rectangle contained in the region. + * @param aContainingRect if non-empty, we choose a rectangle that + * maximizes the area intersecting with aContainingRect (and break ties by + * then choosing the largest rectangle overall) + */ + nsRect GetLargestRectangle (const nsRect& aContainingRect = nsRect()) const; /** * Make sure the region has at most aMaxRects by adding area to it @@ -421,7 +427,10 @@ public: PRUint32 GetNumRects () const { return mImpl.GetNumRects (); } nsIntRect GetBounds () const { return FromRect (mImpl.GetBounds ()); } nsRegion ToAppUnits (nscoord aAppUnitsPerPixel) const; - nsIntRect GetLargestRectangle () const { return FromRect (mImpl.GetLargestRectangle()); } + nsIntRect GetLargestRectangle (const nsIntRect& aContainingRect = nsIntRect()) const + { + return FromRect (mImpl.GetLargestRectangle( ToRect(aContainingRect) )); + } /** * Make sure the region has at most aMaxRects by adding area to it diff --git a/gfx/tests/TestRegion.cpp b/gfx/tests/TestRegion.cpp index 14fadc7d009..e9724aa71b1 100644 --- a/gfx/tests/TestRegion.cpp +++ b/gfx/tests/TestRegion.cpp @@ -131,6 +131,24 @@ class TestLargestRegion { } return success; } + static PRBool TestContainsSpecifiedRect() { + nsRegion r(nsRect(0, 0, 100, 100)); + r.Or(r, nsRect(0, 300, 50, 50)); + if (r.GetLargestRectangle(nsRect(0, 300, 10, 10)) != nsRect(0, 300, 50, 50)) { + fail("Chose wrong rectangle"); + return PR_FALSE; + } + return PR_TRUE; + } + static PRBool TestContainsSpecifiedOverflowingRect() { + nsRegion r(nsRect(0, 0, 100, 100)); + r.Or(r, nsRect(0, 300, 50, 50)); + if (r.GetLargestRectangle(nsRect(0, 290, 10, 20)) != nsRect(0, 300, 50, 50)) { + fail("Chose wrong rectangle"); + return PR_FALSE; + } + return PR_TRUE; + } public: static PRBool Test() { if (!TestSingleRect(nsRect(0, 52, 720, 480)) || @@ -143,6 +161,10 @@ public: return PR_FALSE; if (!TwoRectTest()) return PR_FALSE; + if (!TestContainsSpecifiedRect()) + return PR_FALSE; + if (!TestContainsSpecifiedOverflowingRect()) + return PR_FALSE; passed("TestLargestRegion"); return PR_TRUE; } diff --git a/gfx/thebes/GLContextProviderCGL.mm b/gfx/thebes/GLContextProviderCGL.mm index d138fb16cce..56669982ec5 100644 --- a/gfx/thebes/GLContextProviderCGL.mm +++ b/gfx/thebes/GLContextProviderCGL.mm @@ -298,6 +298,7 @@ public: ~TextureImageCGL() { if (mPixelBuffer) { + mGLContext->MakeCurrent(); mGLContext->fDeleteBuffers(1, &mPixelBuffer); } } @@ -306,6 +307,7 @@ protected: already_AddRefed GetSurfaceForUpdate(const gfxIntSize& aSize, ImageFormat aFmt) { + mGLContext->MakeCurrent(); if (!mGLContext->IsExtensionSupported(GLContext::ARB_pixel_buffer_object)) { return gfxPlatform::GetPlatform()-> CreateOffscreenSurface(aSize, gfxASurface::ContentFromFormat(aFmt)); @@ -339,6 +341,7 @@ protected: bool FinishedSurfaceUpdate() { if (mPixelBuffer) { + mGLContext->MakeCurrent(); mGLContext->fUnmapBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER); return true; } @@ -348,6 +351,7 @@ protected: void FinishedSurfaceUpload() { if (mPixelBuffer) { + mGLContext->MakeCurrent(); mGLContext->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0); } } diff --git a/gfx/thebes/Makefile.in b/gfx/thebes/Makefile.in index 44eaa6bd4d3..73ef37c6603 100644 --- a/gfx/thebes/Makefile.in +++ b/gfx/thebes/Makefile.in @@ -32,9 +32,11 @@ EXPORTS = \ gfxPoint.h \ gfxRect.h \ gfxSkipChars.h \ + gfxTeeSurface.h \ gfxTypes.h \ gfxTextRunCache.h \ gfxTextRunWordCache.h \ + gfxUnicodeProperties.h \ gfxUtils.h \ gfxUserFontSet.h \ GLDefs.h \ @@ -181,6 +183,7 @@ CPPSRCS = \ gfxPlatformFontList.cpp \ gfxRect.cpp \ gfxSkipChars.cpp \ + gfxTeeSurface.cpp \ gfxTextRunCache.cpp \ gfxTextRunWordCache.cpp \ gfxUserFontSet.cpp \ diff --git a/gfx/thebes/gfxASurface.cpp b/gfx/thebes/gfxASurface.cpp index 83a5e6409b8..ace89bf5842 100644 --- a/gfx/thebes/gfxASurface.cpp +++ b/gfx/thebes/gfxASurface.cpp @@ -221,6 +221,9 @@ gfxASurface::Init(cairo_surface_t* surface, PRBool existingSurface) mFloatingRefs = 0; } else { mFloatingRefs = 1; + if (cairo_surface_get_content(surface) != CAIRO_CONTENT_COLOR) { + cairo_surface_set_subpixel_antialiasing(surface, CAIRO_SUBPIXEL_ANTIALIASING_DISABLED); + } } } @@ -429,6 +432,23 @@ gfxASurface::FormatFromContent(gfxASurface::gfxContentType type) } } +void +gfxASurface::SetSubpixelAntialiasingEnabled(PRBool aEnabled) +{ + if (!mSurfaceValid) + return; + cairo_surface_set_subpixel_antialiasing(mSurface, + aEnabled ? CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED); +} + +PRBool +gfxASurface::GetSubpixelAntialiasingEnabled() +{ + if (!mSurfaceValid) + return PR_FALSE; + return cairo_surface_get_subpixel_antialiasing(mSurface) == CAIRO_SUBPIXEL_ANTIALIASING_ENABLED; +} + PRInt32 gfxASurface::BytePerPixelFromFormat(gfxImageFormat format) { diff --git a/gfx/thebes/gfxASurface.h b/gfx/thebes/gfxASurface.h index 06dcb5a22a1..4e13db3952d 100644 --- a/gfx/thebes/gfxASurface.h +++ b/gfx/thebes/gfxASurface.h @@ -177,6 +177,9 @@ public: static gfxContentType ContentFromFormat(gfxImageFormat format); static gfxImageFormat FormatFromContent(gfxContentType format); + void SetSubpixelAntialiasingEnabled(PRBool aEnabled); + PRBool GetSubpixelAntialiasingEnabled(); + /** * Record number of bytes for given surface type. Use positive bytes * for allocations and negative bytes for deallocations. @@ -199,10 +202,35 @@ public: virtual const gfxIntSize GetSize() const { return gfxIntSize(-1, -1); } + void SetOpaqueRect(const gfxRect& aRect) { + if (aRect.IsEmpty()) { + mOpaqueRect = nsnull; + } else if (mOpaqueRect) { + *mOpaqueRect = aRect; + } else { + mOpaqueRect = new gfxRect(aRect); + } + } + const gfxRect& GetOpaqueRect() { + if (mOpaqueRect) + return *mOpaqueRect; + static const gfxRect empty(0, 0, 0, 0); + return empty; + } + virtual PRBool SupportsSelfCopy() { return PR_TRUE; } + /** + * Mark the surface as being allowed/not allowed to be used as a source. + * This currently has no effect other than triggering assertions in some + * cases. + */ + void SetAllowUseAsSource(PRBool aAllow) { mAllowUseAsSource = aAllow; } + PRBool GetAllowUseAsSource() { return mAllowUseAsSource; } + protected: - gfxASurface() : mSurface(nsnull), mFloatingRefs(0), mBytesRecorded(0), mSurfaceValid(PR_FALSE) + gfxASurface() : mSurface(nsnull), mFloatingRefs(0), mBytesRecorded(0), + mSurfaceValid(PR_FALSE), mAllowUseAsSource(PR_TRUE) { MOZ_COUNT_CTOR(gfxASurface); } @@ -210,6 +238,9 @@ protected: static gfxASurface* GetSurfaceWrapper(cairo_surface_t *csurf); static void SetSurfaceWrapper(cairo_surface_t *csurf, gfxASurface *asurf); + // NB: Init() *must* be called from within subclass's + // constructors. It's unsafe to call it after the ctor finishes; + // leaks and use-after-frees are possible. void Init(cairo_surface_t *surface, PRBool existingSurface = PR_FALSE); virtual ~gfxASurface() @@ -220,6 +251,7 @@ protected: } cairo_surface_t *mSurface; + nsAutoPtr mOpaqueRect; private: static void SurfaceDestroyFunc(void *data); @@ -229,6 +261,7 @@ private: protected: PRPackedBool mSurfaceValid; + PRPackedBool mAllowUseAsSource; }; /** diff --git a/gfx/thebes/gfxContext.cpp b/gfx/thebes/gfxContext.cpp index 5c488f44319..f271ea2cbf5 100644 --- a/gfx/thebes/gfxContext.cpp +++ b/gfx/thebes/gfxContext.cpp @@ -54,7 +54,7 @@ #include "gfxASurface.h" #include "gfxPattern.h" #include "gfxPlatform.h" - +#include "gfxTeeSurface.h" gfxContext::gfxContext(gfxASurface *surface) : mSurface(surface) @@ -692,6 +692,7 @@ gfxContext::GetDeviceColor(gfxRGBA& c) void gfxContext::SetSource(gfxASurface *surface, const gfxPoint& offset) { + NS_ASSERTION(surface->GetAllowUseAsSource(), "Surface not allowed to be used as source!"); cairo_set_source_surface(mCairo, surface->CairoSurface(), offset.x, offset.y); } @@ -746,6 +747,56 @@ gfxContext::PushGroup(gfxASurface::gfxContentType content) cairo_push_group_with_content(mCairo, (cairo_content_t) content); } +static gfxRect +GetRoundOutDeviceClipExtents(gfxContext* aCtx) +{ + gfxContextMatrixAutoSaveRestore save(aCtx); + aCtx->IdentityMatrix(); + gfxRect r = aCtx->GetClipExtents(); + r.RoundOut(); + return r; +} + +static void +CopySurface(gfxASurface* aSrc, gfxASurface* aDest) +{ + cairo_t *cr = cairo_create(aDest->CairoSurface()); + cairo_set_source_surface(cr, aSrc->CairoSurface(), 0, 0); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_paint(cr); + cairo_destroy(cr); +} + +void +gfxContext::PushGroupAndCopyBackground(gfxASurface::gfxContentType content) +{ + if (content == gfxASurface::CONTENT_COLOR_ALPHA) { + nsRefPtr s = CurrentSurface(); + if (s->GetContentType() == gfxASurface::CONTENT_COLOR || + s->GetOpaqueRect().Contains(GetRoundOutDeviceClipExtents(this))) { + cairo_push_group_with_content(mCairo, CAIRO_CONTENT_COLOR); + nsRefPtr d = CurrentSurface(); + + if (d->GetType() == gfxASurface::SurfaceTypeTee) { + NS_ASSERTION(s->GetType() == gfxASurface::SurfaceTypeTee, "Mismatched types"); + nsAutoTArray,2> ss; + nsAutoTArray,2> ds; + static_cast(s.get())->GetSurfaces(&ss); + static_cast(d.get())->GetSurfaces(&ds); + NS_ASSERTION(ss.Length() == ds.Length(), "Mismatched lengths"); + for (PRUint32 i = 0; i < ss.Length(); ++i) { + CopySurface(ss[i], ds[i]); + } + } else { + CopySurface(s, d); + } + d->SetOpaqueRect(s->GetOpaqueRect()); + return; + } + } + cairo_push_group_with_content(mCairo, (cairo_content_t) content); +} + already_AddRefed gfxContext::PopGroup() { diff --git a/gfx/thebes/gfxContext.h b/gfx/thebes/gfxContext.h index 884b975d57d..01fd4805cf6 100644 --- a/gfx/thebes/gfxContext.h +++ b/gfx/thebes/gfxContext.h @@ -595,6 +595,18 @@ public: * Groups */ void PushGroup(gfxASurface::gfxContentType content = gfxASurface::CONTENT_COLOR); + /** + * Like PushGroup, but if the current surface is CONTENT_COLOR and + * content is CONTENT_COLOR_ALPHA, makes the pushed surface CONTENT_COLOR + * instead and copies the contents of the current surface to the pushed + * surface. This is good for pushing opacity groups, since blending the + * group back to the current surface with some alpha applied will give + * the correct results and using an opaque pushed surface gives better + * quality and performance. + * This API really only makes sense if you do a PopGroupToSource and + * immediate Paint with OPERATOR_OVER. + */ + void PushGroupAndCopyBackground(gfxASurface::gfxContentType content = gfxASurface::CONTENT_COLOR); already_AddRefed PopGroup(); void PopGroupToSource(); @@ -779,4 +791,27 @@ private: gfxMatrix mMatrix; }; + +class THEBES_API gfxContextAutoDisableSubpixelAntialiasing { +public: + gfxContextAutoDisableSubpixelAntialiasing(gfxContext *aContext, PRBool aDisable) + { + if (aDisable) { + mSurface = aContext->CurrentSurface(); + mSubpixelAntialiasingEnabled = mSurface->GetSubpixelAntialiasingEnabled(); + mSurface->SetSubpixelAntialiasingEnabled(PR_FALSE); + } + } + ~gfxContextAutoDisableSubpixelAntialiasing() + { + if (mSurface) { + mSurface->SetSubpixelAntialiasingEnabled(mSubpixelAntialiasingEnabled); + } + } + +private: + nsRefPtr mSurface; + PRPackedBool mSubpixelAntialiasingEnabled; +}; + #endif /* GFX_CONTEXT_H */ diff --git a/gfx/thebes/gfxDWriteFonts.cpp b/gfx/thebes/gfxDWriteFonts.cpp index a57867d9af4..84ec9a77de8 100644 --- a/gfx/thebes/gfxDWriteFonts.cpp +++ b/gfx/thebes/gfxDWriteFonts.cpp @@ -68,6 +68,51 @@ GetCairoAntialiasOption(gfxFont::AntialiasOption anAntialiasOption) } } +// Code to determine whether Windows is set to use ClearType font smoothing; +// based on private functions in cairo-win32-font.c + +#ifndef SPI_GETFONTSMOOTHINGTYPE +#define SPI_GETFONTSMOOTHINGTYPE 0x200a +#endif +#ifndef FE_FONTSMOOTHINGCLEARTYPE +#define FE_FONTSMOOTHINGCLEARTYPE 2 +#endif + +static bool +HasClearType() +{ + OSVERSIONINFO versionInfo; + versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + return (GetVersionEx(&versionInfo) && + (versionInfo.dwMajorVersion > 5 || + (versionInfo.dwMajorVersion == 5 && + versionInfo.dwMinorVersion >= 1))); // XP or newer +} + +static bool +UsingClearType() +{ + BOOL fontSmoothing; + if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fontSmoothing, 0) || + !fontSmoothing) + { + return false; + } + + if (!HasClearType()) { + return false; + } + + UINT type; + if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &type, 0) && + type == FE_FONTSMOOTHINGCLEARTYPE) + { + return true; + } + return false; +} + //////////////////////////////////////////////////////////////////////////////// // gfxDWriteFont gfxDWriteFont::gfxDWriteFont(gfxFontEntry *aFontEntry, @@ -79,6 +124,7 @@ gfxDWriteFont::gfxDWriteFont(gfxFontEntry *aFontEntry, , mCairoScaledFont(nsnull) , mNeedsOblique(PR_FALSE) , mNeedsBold(aNeedsBold) + , mUsingClearType(PR_FALSE) { gfxDWriteFontEntry *fe = static_cast(aFontEntry); @@ -101,6 +147,12 @@ gfxDWriteFont::gfxDWriteFont(gfxFontEntry *aFontEntry, return; } + if ((anAAOption == gfxFont::kAntialiasDefault && UsingClearType()) || + anAAOption == gfxFont::kAntialiasSubpixel) + { + mUsingClearType = PR_TRUE; + } + ComputeMetrics(); if (FontCanSupportHarfBuzz()) { @@ -427,3 +479,29 @@ gfxDWriteFont::GetFontTable(PRUint32 aTag) return nsnull; } + +PRInt32 +gfxDWriteFont::GetHintedGlyphWidth(gfxContext *aCtx, PRUint16 aGID) +{ + if (!mGlyphWidths.IsInitialized()) { + mGlyphWidths.Init(200); + } + + PRInt32 width; + if (mGlyphWidths.Get(aGID, &width)) { + return width; + } + + DWRITE_GLYPH_METRICS glyphMetrics; + HRESULT hr = mFontFace->GetGdiCompatibleGlyphMetrics( + GetAdjustedSize(), 1.0f, nsnull, TRUE, + &aGID, 1, &glyphMetrics, FALSE); + + if (NS_SUCCEEDED(hr)) { + width = NS_lround(glyphMetrics.advanceWidth * mFUnitsConvFactor) << 16; + mGlyphWidths.Put(aGID, width); + return width; + } + + return -1; +} diff --git a/gfx/thebes/gfxDWriteFonts.h b/gfx/thebes/gfxDWriteFonts.h index 2a7502398af..9334caeac98 100644 --- a/gfx/thebes/gfxDWriteFonts.h +++ b/gfx/thebes/gfxDWriteFonts.h @@ -79,6 +79,12 @@ public: // use DWrite API to get direct access to system font data virtual hb_blob_t *GetFontTable(PRUint32 aTag); + virtual PRBool ProvidesHintedWidths() const { + return !mUsingClearType; + } + + virtual PRInt32 GetHintedGlyphWidth(gfxContext *aCtx, PRUint16 aGID); + protected: virtual void CreatePlatformShaper(); @@ -95,8 +101,13 @@ protected: cairo_scaled_font_t *mCairoScaledFont; gfxFont::Metrics mMetrics; - PRBool mNeedsOblique; - PRBool mNeedsBold; + + // cache of glyph widths in 16.16 fixed-point pixels + nsDataHashtable mGlyphWidths; + + PRPackedBool mNeedsOblique; + PRPackedBool mNeedsBold; + PRPackedBool mUsingClearType; }; #endif diff --git a/gfx/thebes/gfxDWriteShaper.cpp b/gfx/thebes/gfxDWriteShaper.cpp index f4973696f5b..007366d930f 100644 --- a/gfx/thebes/gfxDWriteShaper.cpp +++ b/gfx/thebes/gfxDWriteShaper.cpp @@ -175,24 +175,49 @@ trymoreglyphs: continue; } - hr = analyzer->GetGlyphPlacements(aString + range.start, - clusters.Elements(), - textProperties.Elements(), - range.Length(), - indices.Elements(), - glyphProperties.Elements(), - actualGlyphs, - font->GetFontFace(), - font->GetAdjustedSize(), - FALSE, - FALSE, - &runHead->mScript, - NULL, - NULL, - NULL, - 0, - advances.Elements(), - glyphOffsets.Elements()); + if (mFont->ProvidesHintedWidths()) { + hr = analyzer->GetGdiCompatibleGlyphPlacements( + aString + range.start, + clusters.Elements(), + textProperties.Elements(), + range.Length(), + indices.Elements(), + glyphProperties.Elements(), + actualGlyphs, + font->GetFontFace(), + font->GetAdjustedSize(), + 1.0, + nsnull, + TRUE, + FALSE, + FALSE, + &runHead->mScript, + NULL, + NULL, + NULL, + 0, + advances.Elements(), + glyphOffsets.Elements()); + } else { + hr = analyzer->GetGlyphPlacements(aString + range.start, + clusters.Elements(), + textProperties.Elements(), + range.Length(), + indices.Elements(), + glyphProperties.Elements(), + actualGlyphs, + font->GetFontFace(), + font->GetAdjustedSize(), + FALSE, + FALSE, + &runHead->mScript, + NULL, + NULL, + NULL, + 0, + advances.Elements(), + glyphOffsets.Elements()); + } if (FAILED(hr)) { NS_WARNING("Analyzer failed to get glyph placements."); result = PR_FALSE; diff --git a/gfx/thebes/gfxImageSurface.cpp b/gfx/thebes/gfxImageSurface.cpp index 427d7ecd76c..66036fbc4a1 100644 --- a/gfx/thebes/gfxImageSurface.cpp +++ b/gfx/thebes/gfxImageSurface.cpp @@ -149,24 +149,24 @@ gfxImageSurface::~gfxImageSurface() free(mData); } -long -gfxImageSurface::ComputeStride() const +/*static*/ long +gfxImageSurface::ComputeStride(const gfxIntSize& aSize, gfxImageFormat aFormat) { long stride; - if (mFormat == ImageFormatARGB32) - stride = mSize.width * 4; - else if (mFormat == ImageFormatRGB24) - stride = mSize.width * 4; - else if (mFormat == ImageFormatRGB16_565) - stride = mSize.width * 2; - else if (mFormat == ImageFormatA8) - stride = mSize.width; - else if (mFormat == ImageFormatA1) { - stride = (mSize.width + 7) / 8; + if (aFormat == ImageFormatARGB32) + stride = aSize.width * 4; + else if (aFormat == ImageFormatRGB24) + stride = aSize.width * 4; + else if (aFormat == ImageFormatRGB16_565) + stride = aSize.width * 2; + else if (aFormat == ImageFormatA8) + stride = aSize.width; + else if (aFormat == ImageFormatA1) { + stride = (aSize.width + 7) / 8; } else { NS_WARNING("Unknown format specified to gfxImageSurface!"); - stride = mSize.width * 4; + stride = aSize.width * 4; } stride = ((stride + 3) / 4) * 4; diff --git a/gfx/thebes/gfxImageSurface.h b/gfx/thebes/gfxImageSurface.h index 3a484101202..7a600ff002b 100644 --- a/gfx/thebes/gfxImageSurface.h +++ b/gfx/thebes/gfxImageSurface.h @@ -115,7 +115,9 @@ protected: void InitWithData(unsigned char *aData, const gfxIntSize& aSize, long aStride, gfxImageFormat aFormat); void InitFromSurface(cairo_surface_t *csurf); - long ComputeStride() const; + long ComputeStride() const { return ComputeStride(mSize, mFormat); } + + static long ComputeStride(const gfxIntSize&, gfxImageFormat); gfxIntSize mSize; PRBool mOwnsData; diff --git a/gfx/thebes/gfxQtPlatform.cpp b/gfx/thebes/gfxQtPlatform.cpp index 800073368e2..dc074dccf39 100644 --- a/gfx/thebes/gfxQtPlatform.cpp +++ b/gfx/thebes/gfxQtPlatform.cpp @@ -79,13 +79,7 @@ #include "nsIPrefBranch.h" #include "nsIPrefService.h" -// Because the QPainter backend has some problems with glyphs rendering -// it is better to use image or xlib cairo backends by default -#if (MOZ_PLATFORM_MAEMO == 6) -#define DEFAULT_RENDER_MODE RENDER_BUFFERED -#else #define DEFAULT_RENDER_MODE RENDER_DIRECT -#endif static QPaintEngine::Type sDefaultQtPaintEngineType = QPaintEngine::X11; gfxFontconfigUtils *gfxQtPlatform::sFontconfigUtils = nsnull; diff --git a/gfx/thebes/gfxSharedImageSurface.cpp b/gfx/thebes/gfxSharedImageSurface.cpp index a462d22403e..d0f5a41345b 100644 --- a/gfx/thebes/gfxSharedImageSurface.cpp +++ b/gfx/thebes/gfxSharedImageSurface.cpp @@ -42,86 +42,82 @@ #define MOZ_ALIGN_WORD(x) (((x) + 3) & ~3) -using mozilla::ipc::SharedMemory; +using namespace mozilla::ipc; static const cairo_user_data_key_t SHM_KEY = {0}; -typedef struct _SharedImageInfo -{ +struct SharedImageInfo { PRInt32 width; PRInt32 height; PRInt32 format; -} SharedImageInfo; +}; static SharedImageInfo* -GetShmInfoPtr(const mozilla::ipc::Shmem &aShmem) +GetShmInfoPtr(const Shmem& aShmem) { return reinterpret_cast (aShmem.get() + aShmem.Size() - sizeof(SharedImageInfo)); } -size_t -gfxSharedImageSurface::GetAlignedSize() -{ - return MOZ_ALIGN_WORD(sizeof(SharedImageInfo) + mSize.height * mStride); -} - -bool -gfxSharedImageSurface::InitSurface(PRBool aUpdateShmemInfo) -{ - if (!CheckSurfaceSize(mSize)) - return false; - - cairo_surface_t *surface = - cairo_image_surface_create_for_data(mShmem.get(), - (cairo_format_t)mFormat, - mSize.width, - mSize.height, - mStride); - - if (!surface) - return false; - - cairo_surface_set_user_data(surface, - &SHM_KEY, - this, NULL); - - if (aUpdateShmemInfo) { - SharedImageInfo *shmInfo = GetShmInfoPtr(mShmem); - shmInfo->width = mSize.width; - shmInfo->height = mSize.height; - shmInfo->format = mFormat; - } - - InitFromSurface(surface); - return true; -} - gfxSharedImageSurface::~gfxSharedImageSurface() { } -gfxSharedImageSurface::gfxSharedImageSurface() -{ -} - -gfxSharedImageSurface::gfxSharedImageSurface(const mozilla::ipc::Shmem &aShmem) -{ - mShmem = aShmem; - SharedImageInfo *shmInfo = GetShmInfoPtr(aShmem); - mSize.width = shmInfo->width; - mSize.height = shmInfo->height; - mFormat = (gfxImageFormat)shmInfo->format; - mStride = ComputeStride(); - - if (!InitSurface(PR_FALSE)) - NS_RUNTIMEABORT("Shared memory is bad"); -} - -PRBool -gfxSharedImageSurface::IsSharedImage(gfxASurface *aSurface) +/*static*/ PRBool +gfxSharedImageSurface::IsSharedImage(gfxASurface* aSurface) { return (aSurface && aSurface->GetType() == gfxASurface::SurfaceTypeImage && aSurface->GetData(&SHM_KEY)); } + +gfxSharedImageSurface::gfxSharedImageSurface(const gfxIntSize& aSize, + gfxImageFormat aFormat, + const Shmem& aShmem) +{ + mSize = aSize; + mFormat = aFormat; + mStride = ComputeStride(aSize, aFormat); + mShmem = aShmem; + cairo_surface_t *surface = + cairo_image_surface_create_for_data(mShmem.get(), + (cairo_format_t)mFormat, + mSize.width, + mSize.height, + mStride); + if (surface) { + cairo_surface_set_user_data(surface, &SHM_KEY, this, NULL); + } + Init(surface); +} + +void +gfxSharedImageSurface::WriteShmemInfo() +{ + SharedImageInfo* shmInfo = GetShmInfoPtr(mShmem); + shmInfo->width = mSize.width; + shmInfo->height = mSize.height; + shmInfo->format = mFormat; +} + +/*static*/ size_t +gfxSharedImageSurface::GetAlignedSize(const gfxIntSize& aSize, long aStride) +{ + return MOZ_ALIGN_WORD(sizeof(SharedImageInfo) + aSize.height * aStride); +} + +/*static*/ already_AddRefed +gfxSharedImageSurface::Open(const Shmem& aShmem) +{ + SharedImageInfo* shmInfo = GetShmInfoPtr(aShmem); + gfxIntSize size(shmInfo->width, shmInfo->height); + if (!CheckSurfaceSize(size)) + return nsnull; + + nsRefPtr s = + new gfxSharedImageSurface(size, + (gfxImageFormat)shmInfo->format, + aShmem); + // We didn't create this Shmem and so don't free it on errors + return (s->CairoStatus() != 0) ? nsnull : s.forget(); +} diff --git a/gfx/thebes/gfxSharedImageSurface.h b/gfx/thebes/gfxSharedImageSurface.h index a5474a09b9b..7f80c56e812 100644 --- a/gfx/thebes/gfxSharedImageSurface.h +++ b/gfx/thebes/gfxSharedImageSurface.h @@ -50,77 +50,93 @@ class THEBES_API gfxSharedImageSurface : public gfxImageSurface { typedef mozilla::ipc::Shmem Shmem; public: - /** - * Init must be called after ctor - */ - gfxSharedImageSurface(); + virtual ~gfxSharedImageSurface(); /** - * Create shared image from external Shmem - * Shmem must be initialized by this class - */ - gfxSharedImageSurface(const Shmem &aShmem); - - ~gfxSharedImageSurface(); - - /** - * Initialize shared image surface - * @param aAllocator The pointer to protocol class which has AllocShmem method - * @param aSize The size of the buffer - * @param aFormat Format of the data - * @see gfxImageFormat + * Return a new gfxSharedImageSurface around a shmem segment newly + * allocated by this function. |aAllocator| is the object used to + * allocate the new shmem segment. Null is returned if creating + * the surface failed. + * + * NB: the *caller* is responsible for freeing the Shmem allocated + * by this function. */ template - bool Init(ShmemAllocator *aAllocator, - const gfxIntSize& aSize, - gfxImageFormat aFormat, - SharedMemory::SharedMemoryType aShmType = SharedMemory::TYPE_BASIC) + static already_AddRefed + Create(ShmemAllocator* aAllocator, + const gfxIntSize& aSize, + gfxImageFormat aFormat, + SharedMemory::SharedMemoryType aShmType = SharedMemory::TYPE_BASIC) { - return Init(aAllocator, aSize, aFormat, aShmType); + return Create(aAllocator, aSize, aFormat, aShmType); } + /** + * Return a new gfxSharedImageSurface that wraps a shmem segment + * already created by the Create() above. Bad things will happen + * if an attempt is made to wrap any other shmem segment. Null is + * returned if creating the surface failed. + */ + static already_AddRefed + Open(const Shmem& aShmem); + template - bool InitUnsafe(ShmemAllocator *aAllocator, - const gfxIntSize& aSize, - gfxImageFormat aFormat, - SharedMemory::SharedMemoryType aShmType = SharedMemory::TYPE_BASIC) + static already_AddRefed + CreateUnsafe(ShmemAllocator* aAllocator, + const gfxIntSize& aSize, + gfxImageFormat aFormat, + SharedMemory::SharedMemoryType aShmType = SharedMemory::TYPE_BASIC) { - return Init(aAllocator, aSize, aFormat, aShmType); + return Create(aAllocator, aSize, aFormat, aShmType); } - /* Gives Shmem data, which can be passed to IPDL interfaces */ Shmem& GetShmem() { return mShmem; } - // This can be used for recognizing normal gfxImageSurface as SharedImage static PRBool IsSharedImage(gfxASurface *aSurface); private: + gfxSharedImageSurface(const gfxIntSize&, gfxImageFormat, const Shmem&); + + void WriteShmemInfo(); + + static size_t GetAlignedSize(const gfxIntSize&, long aStride); + template - bool Init(ShmemAllocator *aAllocator, - const gfxIntSize& aSize, - gfxImageFormat aFormat, - SharedMemory::SharedMemoryType aShmType) + static already_AddRefed + Create(ShmemAllocator* aAllocator, + const gfxIntSize& aSize, + gfxImageFormat aFormat, + SharedMemory::SharedMemoryType aShmType) { - mSize = aSize; - mFormat = aFormat; - mStride = ComputeStride(); + if (!CheckSurfaceSize(aSize)) + return nsnull; + + Shmem shmem; + long stride = ComputeStride(aSize, aFormat); + size_t size = GetAlignedSize(aSize, stride); if (!Unsafe) { - if (!aAllocator->AllocShmem(GetAlignedSize(), - aShmType, &mShmem)) - return false; + if (!aAllocator->AllocShmem(size, aShmType, &shmem)) + return nsnull; } else { - if (!aAllocator->AllocUnsafeShmem(GetAlignedSize(), - aShmType, &mShmem)) - return false; + if (!aAllocator->AllocUnsafeShmem(size, aShmType, &shmem)) + return nsnull; } - return InitSurface(PR_TRUE); + nsRefPtr s = + new gfxSharedImageSurface(aSize, aFormat, shmem); + if (s->CairoStatus() != 0) { + aAllocator->DeallocShmem(shmem); + return nsnull; + } + s->WriteShmemInfo(); + return s.forget(); } - size_t GetAlignedSize(); - bool InitSurface(PRBool aUpdateShmemInfo); - Shmem mShmem; + + // Calling these is very bad, disallow it + gfxSharedImageSurface(const gfxSharedImageSurface&); + gfxSharedImageSurface& operator=(const gfxSharedImageSurface&); }; #endif /* GFX_SHARED_IMAGESURFACE_H */ diff --git a/gfx/thebes/gfxTeeSurface.cpp b/gfx/thebes/gfxTeeSurface.cpp new file mode 100644 index 00000000000..ecb2d36e62c --- /dev/null +++ b/gfx/thebes/gfxTeeSurface.cpp @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** 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 Thebes code. + * + * The Initial Developer of the Original Code is Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Robert O'Callahan + * + * 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 "gfxTeeSurface.h" + +#include "cairo.h" + +gfxTeeSurface::gfxTeeSurface(cairo_surface_t *csurf) +{ + Init(csurf, PR_TRUE); +} + +gfxTeeSurface::gfxTeeSurface(gfxASurface **aSurfaces, PRInt32 aSurfaceCount) +{ + NS_ASSERTION(aSurfaceCount > 0, "Must have a least one surface"); + cairo_surface_t *csurf = cairo_tee_surface_create(aSurfaces[0]->CairoSurface()); + Init(csurf, PR_FALSE); + + for (PRInt32 i = 1; i < aSurfaceCount; ++i) { + cairo_tee_surface_add(csurf, aSurfaces[i]->CairoSurface()); + } +} + +const gfxIntSize +gfxTeeSurface::GetSize() const +{ + nsRefPtr master = Wrap(cairo_tee_surface_index(mSurface, 0)); + return master->GetSize(); +} + +void +gfxTeeSurface::GetSurfaces(nsTArray >* aSurfaces) +{ + for (PRInt32 i = 0; ; ++i) { + cairo_surface_t *csurf = cairo_tee_surface_index(mSurface, i); + if (cairo_surface_status(csurf)) + break; + nsRefPtr *elem = aSurfaces->AppendElement(); + if (!elem) + return; + *elem = Wrap(csurf); + } +} diff --git a/gfx/thebes/gfxTeeSurface.h b/gfx/thebes/gfxTeeSurface.h new file mode 100644 index 00000000000..cb3981faafc --- /dev/null +++ b/gfx/thebes/gfxTeeSurface.h @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** 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 Thebes code. + * + * The Initial Developer of the Original Code is Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Robert O'Callahan + * + * 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 GFX_TEESURFACE_H +#define GFX_TEESURFACE_H + +#include "gfxASurface.h" +#include "nsTArray.h" + +/** + * Wraps a cairo_tee_surface. The first surface in the surface list is the + * primary surface, which answers all surface queries (including size). + * All drawing is performed on all the surfaces. + */ +class THEBES_API gfxTeeSurface : public gfxASurface { +public: + gfxTeeSurface(cairo_surface_t *csurf); + gfxTeeSurface(gfxASurface **aSurfaces, PRInt32 aSurfaceCount); + + virtual const gfxIntSize GetSize() const; + + void GetSurfaces(nsTArray > *aSurfaces); +}; + +#endif /* GFX_TEESURFACE_H */ diff --git a/gfx/thebes/gfxUnicodeProperties.h b/gfx/thebes/gfxUnicodeProperties.h index b2574af6404..2224f2e129e 100644 --- a/gfx/thebes/gfxUnicodeProperties.h +++ b/gfx/thebes/gfxUnicodeProperties.h @@ -39,8 +39,9 @@ #define GFX_UNICODEPROPERTIES_H #include "prtypes.h" +#include "gfxTypes.h" -class gfxUnicodeProperties +class THEBES_API gfxUnicodeProperties { public: static PRUint32 GetMirroredChar(PRUint32 aCh); diff --git a/ipc/glue/IPCMessageUtils.h b/ipc/glue/IPCMessageUtils.h index 88afbd03559..23f0855760b 100644 --- a/ipc/glue/IPCMessageUtils.h +++ b/ipc/glue/IPCMessageUtils.h @@ -51,6 +51,7 @@ #include "nsRect.h" #include "nsRegion.h" #include "gfxASurface.h" +#include "Layers.h" #ifdef _MSC_VER #pragma warning( disable : 4800 ) @@ -65,10 +66,13 @@ namespace base { class FileDescriptor { }; } #endif +using mozilla::layers::LayerManager; + namespace mozilla { typedef gfxPattern::GraphicsFilter GraphicsFilterType; typedef gfxASurface::gfxSurfaceType gfxSurfaceType; +typedef LayerManager::LayersBackend LayersBackend; // XXX there are out of place and might be generally useful. Could // move to nscore.h or something. @@ -498,6 +502,35 @@ struct ParamTraits } }; + template<> +struct ParamTraits +{ + typedef mozilla::LayersBackend paramType; + + static void Write(Message* msg, const paramType& param) + { + if (LayerManager::LAYERS_NONE < param && + param < LayerManager::LAYERS_LAST) { + WriteParam(msg, int32(param)); + return; + } + NS_RUNTIMEABORT("surface type not reached"); + } + + static bool Read(const Message* msg, void** iter, paramType* result) + { + int32 type; + if (!ReadParam(msg, iter, &type)) + return false; + + if (LayerManager::LAYERS_NONE < type && + type < LayerManager::LAYERS_LAST) { + *result = paramType(type); + return true; + } + return false; + } +}; template<> struct ParamTraits diff --git a/ipc/ipdl/test/cxx/app/Makefile.in b/ipc/ipdl/test/cxx/app/Makefile.in index bfb502d7652..11e064fb839 100644 --- a/ipc/ipdl/test/cxx/app/Makefile.in +++ b/ipc/ipdl/test/cxx/app/Makefile.in @@ -47,6 +47,7 @@ PROGRAM = $(MODULE)$(BIN_SUFFIX) NSDISTMODE = copy LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre +LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/base CPPSRCS = \ TestIPDL.cpp \ diff --git a/js/jetpack/JetpackActorCommon.cpp b/js/jetpack/JetpackActorCommon.cpp index de979243a37..164ac1a7d8f 100644 --- a/js/jetpack/JetpackActorCommon.cpp +++ b/js/jetpack/JetpackActorCommon.cpp @@ -523,8 +523,9 @@ JetpackActorCommon::RecList::remove(jsval v) if (node->value() == v) { prev->down = node->down; delete node; - } - node = (prev = node)->down; + } else + prev = node; + node = prev->down; } } diff --git a/js/src/configure.in b/js/src/configure.in index f4c8c0314fa..2f229e6f352 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -642,35 +642,31 @@ case "$target" in if test "$_CC_MAJOR_VERSION" != "$_CXX_MAJOR_VERSION"; then AC_MSG_ERROR([The major versions of \$CC and \$CXX do not match.]) fi - if test "$_CC_MAJOR_VERSION" = "13"; then - _CC_SUITE=7 - elif test "$_CC_MAJOR_VERSION" = "14"; then + + if test "$_CC_MAJOR_VERSION" = "14"; then + dnl Require VC8SP1 or newer. + dnl VC8 is 14.00.50727.42, VC8SP1 is 14.00.50727.762. + if test "$_CC_RELEASE" -lt 50727 -o \ + \( "$_CC_RELEASE" -eq 50727 -a "$_CC_BUILD" -lt 762 \); then + AC_MSG_ERROR([This version ($CC_VERSION) of the MSVC compiler is unsupported. You probably need to install Service Pack 1 of Visual Studio 2005. See https://developer.mozilla.org/en/Windows_Build_Prerequisites.]) + fi + _CC_SUITE=8 CXXFLAGS="$CXXFLAGS -Zc:wchar_t-" - dnl -DYNAMICBASE is only supported on VC8SP1 or newer, - dnl so be very specific here! - dnl VC8 is 14.00.50727.42, VC8SP1 is 14.00.50727.762 - if test $_CC_RELEASE -gt 50727; then - _USE_DYNAMICBASE=1 - elif test $_CC_BUILD -ge 762; then - _USE_DYNAMICBASE=1 - fi AC_DEFINE(_CRT_SECURE_NO_DEPRECATE) AC_DEFINE(_CRT_NONSTDC_NO_DEPRECATE) elif test "$_CC_MAJOR_VERSION" = "15"; then _CC_SUITE=9 CXXFLAGS="$CXXFLAGS -Zc:wchar_t-" - _USE_DYNAMICBASE=1 AC_DEFINE(_CRT_SECURE_NO_WARNINGS) AC_DEFINE(_CRT_NONSTDC_NO_WARNINGS) elif test "$_CC_MAJOR_VERSION" = "16"; then _CC_SUITE=10 CXXFLAGS="$CXXFLAGS -Zc:wchar_t-" - _USE_DYNAMICBASE=1 AC_DEFINE(_CRT_SECURE_NO_WARNINGS) AC_DEFINE(_CRT_NONSTDC_NO_WARNINGS) else - AC_MSG_ERROR([This version of the MSVC compiler, $CC_VERSION , is unsupported.]) + AC_MSG_ERROR([This version ($CC_VERSION) of the MSVC compiler is unsupported. See https://developer.mozilla.org/en/Windows_Build_Prerequisites.]) fi _MOZ_RTTI_FLAGS_ON='-GR' @@ -681,33 +677,32 @@ case "$target" in if test -n "$WIN32_REDIST_DIR"; then WIN32_REDIST_DIR=`cd "$WIN32_REDIST_DIR" && pwd` fi - - # bug #249782 - # ensure that mt.exe is Microsoft (R) Manifest Tool and not magnetic tape manipulation utility (or something else) - if test "$_CC_SUITE" -ge "8"; then - changequote(,) - _MSMT_VER_FILTER='s|.* \([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*|\1|p' - changequote([,]) - MSMT_TOOL=`mt 2>&1|grep 'Microsoft (R) Manifest Tool'` - if test -n "$MSMT_TOOL"; then - MSMANIFEST_TOOL_VERSION=`echo ${MSMT_TOOL}|sed -ne "$_MSMT_VER_FILTER"` - if test -z "$MSMANIFEST_TOOL_VERSION"; then - AC_MSG_WARN([Unknown version of the Microsoft (R) Manifest Tool.]) - fi - MSMANIFEST_TOOL=1 - unset MSMT_TOOL - else - AC_MSG_ERROR([Microsoft (R) Manifest Tool must be in your \$PATH.]) - fi + dnl Ensure that mt.exe is 'Microsoft (R) Manifest Tool', + dnl not something else like "magnetic tape manipulation utility". + MSMT_TOOL=`mt 2>&1|grep 'Microsoft (R) Manifest Tool'` + if test -z "$MSMT_TOOL"; then + AC_MSG_ERROR([Microsoft (R) Manifest Tool must be in your \$PATH.]) fi + changequote(,) + _MSMT_VER_FILTER='s|.* \([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*|\1|p' + changequote([,]) + MSMANIFEST_TOOL_VERSION=`echo ${MSMT_TOOL}|sed -ne "$_MSMT_VER_FILTER"` + if test -z "$MSMANIFEST_TOOL_VERSION"; then + AC_MSG_WARN([Unknown version of the Microsoft (R) Manifest Tool.]) + fi + + MSMANIFEST_TOOL=1 + unset MSMT_TOOL + # Check linker version _LD_FULL_VERSION=`"${LD}" -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"` _LD_MAJOR_VERSION=`echo ${_LD_FULL_VERSION} | $AWK -F\. '{ print $1 }'` if test "$_LD_MAJOR_VERSION" != "$_CC_SUITE"; then AC_MSG_ERROR([The linker major version, $_LD_FULL_VERSION, does not match the compiler suite version, $_CC_SUITE.]) fi + INCREMENTAL_LINKER=1 # Identify which version of the SDK we're building with @@ -2420,9 +2415,7 @@ ia64*-hpux*) dnl XXX: should be -LTCG:PGOPTIMIZE, but that fails on libxul. dnl Probably also a compiler bug, but what can you do? PROFILE_USE_LDFLAGS="-LTCG:PGUPDATE" - if test -n "$_USE_DYNAMICBASE"; then - LDFLAGS="$LDFLAGS -DYNAMICBASE" - fi + LDFLAGS="$LDFLAGS -DYNAMICBASE" fi fi AC_DEFINE(HAVE_SNPRINTF) @@ -2481,7 +2474,7 @@ ia64*-hpux*) case "$host_os" in cygwin*|msvc*|mks*) - AC_MSG_WARN([Using a cygwin build environment is unsupported. Configure cannot check for the presence of necessary headers. Please upgrade to MozillaBuild; see http://developer.mozilla.org/en/docs/Windows_Build_Prerequisites]) + AC_MSG_WARN([Using a cygwin build environment is unsupported. Configure cannot check for the presence of necessary headers. Please upgrade to MozillaBuild; see https://developer.mozilla.org/en/Windows_Build_Prerequisites.]) ;; esac diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index e85d9f92bf5..e42eb531a5e 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -186,7 +186,8 @@ protected: void Accumulate(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem, const nsIntRect& aVisibleRect, - const nsIntRect& aDrawRect); + const nsIntRect& aDrawRect, + const FrameLayerBuilder::Clip& aClip); nsIFrame* GetActiveScrolledRoot() { return mActiveScrolledRoot; } /** @@ -307,6 +308,7 @@ protected: already_AddRefed FindThebesLayerFor(nsDisplayItem* aItem, const nsIntRect& aVisibleRect, const nsIntRect& aDrawRect, + const FrameLayerBuilder::Clip& aClip, nsIFrame* aActiveScrolledRoot); ThebesLayerData* GetTopThebesLayerData() { @@ -895,11 +897,43 @@ ContainerState::PopThebesLayerData() mThebesLayerDataStack.RemoveElementAt(lastIndex); } +static PRBool +SuppressComponentAlpha(nsDisplayListBuilder* aBuilder, + nsDisplayItem* aItem, + const nsRect& aComponentAlphaBounds) +{ + const nsRegion* windowTransparentRegion = aBuilder->GetFinalTransparentRegion(); + if (!windowTransparentRegion || windowTransparentRegion->IsEmpty()) + return PR_FALSE; + + // Suppress component alpha for items in the toplevel window that are over + // the window translucent area + nsIFrame* f = aItem->GetUnderlyingFrame(); + nsIFrame* ref = aBuilder->ReferenceFrame(); + if (f->PresContext() != ref->PresContext()) + return PR_FALSE; + + for (nsIFrame* t = f; t; t = t->GetParent()) { + if (t->IsTransformed()) + return PR_FALSE; + } + + return windowTransparentRegion->Intersects(aComponentAlphaBounds); +} + +static PRBool +WindowHasTransparency(nsDisplayListBuilder* aBuilder) +{ + const nsRegion* windowTransparentRegion = aBuilder->GetFinalTransparentRegion(); + return windowTransparentRegion && !windowTransparentRegion->IsEmpty(); +} + void ContainerState::ThebesLayerData::Accumulate(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem, const nsIntRect& aVisibleRect, - const nsIntRect& aDrawRect) + const nsIntRect& aDrawRect, + const FrameLayerBuilder::Clip& aClip) { nscolor uniformColor; if (aItem->IsUniform(aBuilder, &uniformColor)) { @@ -924,20 +958,39 @@ ContainerState::ThebesLayerData::Accumulate(nsDisplayListBuilder* aBuilder, mDrawRegion.SimplifyOutward(4); PRBool forceTransparentSurface = PR_FALSE; - if (aItem->IsOpaque(aBuilder, &forceTransparentSurface)) { - // We don't use SimplifyInward here since it's not defined exactly - // what it will discard. For our purposes the most important case - // is a large opaque background at the bottom of z-order (e.g., - // a canvas background), so we need to make sure that the first rect - // we see doesn't get discarded. - nsIntRegion tmp; - tmp.Or(mOpaqueRegion, aDrawRect); - if (tmp.GetNumRects() <= 4) { - mOpaqueRegion = tmp; + nsRegion opaque = aItem->GetOpaqueRegion(aBuilder, &forceTransparentSurface); + if (!opaque.IsEmpty()) { + nsRegionRectIterator iter(opaque); + nscoord appUnitsPerDevPixel = AppUnitsPerDevPixel(aItem); + for (const nsRect* r = iter.Next(); r; r = iter.Next()) { + // We don't use SimplifyInward here since it's not defined exactly + // what it will discard. For our purposes the most important case + // is a large opaque background at the bottom of z-order (e.g., + // a canvas background), so we need to make sure that the first rect + // we see doesn't get discarded. + nsIntRect rect = aClip.ApproximateIntersect(*r).ToNearestPixels(appUnitsPerDevPixel); + nsIntRegion tmp; + tmp.Or(mOpaqueRegion, rect); + // Opaque display items in chrome documents whose window is partially + // transparent are always added to the opaque region. This helps ensure + // that we get as much subpixel-AA as possible in the chrome. + if (tmp.GetNumRects() <= 4 || + (WindowHasTransparency(aBuilder) && + aItem->GetUnderlyingFrame()->PresContext()->IsChrome())) { + mOpaqueRegion = tmp; + } } - } else if (aItem->HasText()) { - if (!mOpaqueRegion.Contains(aVisibleRect)) { - mNeedComponentAlpha = PR_TRUE; + } + nsRect componentAlpha = aItem->GetComponentAlphaBounds(aBuilder); + componentAlpha.IntersectRect(componentAlpha, aItem->GetVisibleRect()); + if (!componentAlpha.IsEmpty()) { + nscoord appUnitsPerDevPixel = AppUnitsPerDevPixel(aItem); + if (!mOpaqueRegion.Contains(componentAlpha.ToOutsidePixels(appUnitsPerDevPixel))) { + if (SuppressComponentAlpha(aBuilder, aItem, componentAlpha)) { + aItem->DisableComponentAlpha(); + } else { + mNeedComponentAlpha = PR_TRUE; + } } } mForceTransparentSurface = mForceTransparentSurface || forceTransparentSurface; @@ -947,6 +1000,7 @@ already_AddRefed ContainerState::FindThebesLayerFor(nsDisplayItem* aItem, const nsIntRect& aVisibleRect, const nsIntRect& aDrawRect, + const FrameLayerBuilder::Clip& aClip, nsIFrame* aActiveScrolledRoot) { PRInt32 i; @@ -1001,7 +1055,7 @@ ContainerState::FindThebesLayerFor(nsDisplayItem* aItem, layer = thebesLayerData->mLayer; } - thebesLayerData->Accumulate(mBuilder, aItem, aVisibleRect, aDrawRect); + thebesLayerData->Accumulate(mBuilder, aItem, aVisibleRect, aDrawRect, aClip); return layer.forget(); } @@ -1148,7 +1202,7 @@ ContainerState::ProcessDisplayItems(const nsDisplayList& aList, } nsRefPtr thebesLayer = - FindThebesLayerFor(item, itemVisibleRect, itemDrawRect, + FindThebesLayerFor(item, itemVisibleRect, itemDrawRect, aClip, activeScrolledRoot); InvalidateForLayerChange(item, thebesLayer); @@ -1787,4 +1841,20 @@ FrameLayerBuilder::Clip::ApplyTo(gfxContext* aContext, } } +nsRect +FrameLayerBuilder::Clip::ApproximateIntersect(const nsRect& aRect) const +{ + nsRect r = aRect; + if (mHaveClipRect) { + r.IntersectRect(r, mClipRect); + } + for (PRUint32 i = 0, iEnd = mRoundedClipRects.Length(); + i < iEnd; ++i) { + const Clip::RoundedRect &rr = mRoundedClipRects[i]; + nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(rr.mRect, rr.mRadii, r); + r = rgn.GetLargestRectangle(); + } + return r; +} + } // namespace mozilla diff --git a/layout/base/FrameLayerBuilder.h b/layout/base/FrameLayerBuilder.h index 5fc3b34f767..e956ad988ef 100644 --- a/layout/base/FrameLayerBuilder.h +++ b/layout/base/FrameLayerBuilder.h @@ -317,6 +317,11 @@ public: // or clearing of other clips must be done by the caller. void ApplyTo(gfxContext* aContext, nsPresContext* aPresContext); + // Return a rectangle contained in the intersection of aRect with this + // clip region. Tries to return the largest possible rectangle, but may + // not succeed. + nsRect ApproximateIntersect(const nsRect& aRect) const; + bool operator==(const Clip& aOther) const { return mHaveClipRect == aOther.mHaveClipRect && (!mHaveClipRect || mClipRect == aOther.mClipRect) && diff --git a/layout/base/nsBidi.cpp b/layout/base/nsBidi.cpp index 175a1a24036..dd99ce9e2b4 100644 --- a/layout/base/nsBidi.cpp +++ b/layout/base/nsBidi.cpp @@ -1116,15 +1116,15 @@ void nsBidi::AdjustWSLevels() } } } -#ifdef FULL_BIDI_ENGINE - -/* -------------------------------------------------------------------------- */ nsresult nsBidi::GetDirection(nsBidiDirection* aDirection) { *aDirection = mDirection; return NS_OK; } +#ifdef FULL_BIDI_ENGINE + +/* -------------------------------------------------------------------------- */ nsresult nsBidi::GetLength(PRInt32* aLength) { diff --git a/layout/base/nsBidi.h b/layout/base/nsBidi.h index f9462aa4b81..4d2dc4276d3 100644 --- a/layout/base/nsBidi.h +++ b/layout/base/nsBidi.h @@ -510,6 +510,17 @@ public: */ nsresult SetPara(const PRUnichar *aText, PRInt32 aLength, nsBidiLevel aParaLevel, nsBidiLevel *aEmbeddingLevels); + /** + * Get the directionality of the text. + * + * @param aDirection receives a NSBIDI_XXX value that indicates if the entire text + * represented by this object is unidirectional, + * and which direction, or if it is mixed-directional. + * + * @see nsBidiDirection + */ + nsresult GetDirection(nsBidiDirection* aDirection); + #ifdef FULL_BIDI_ENGINE /** * SetLine sets an nsBidi to @@ -546,17 +557,6 @@ public: */ nsresult SetLine(nsIBidi* aParaBidi, PRInt32 aStart, PRInt32 aLimit); - /** - * Get the directionality of the text. - * - * @param aDirection receives a NSBIDI_XXX value that indicates if the entire text - * represented by this object is unidirectional, - * and which direction, or if it is mixed-directional. - * - * @see nsBidiDirection - */ - nsresult GetDirection(nsBidiDirection* aDirection); - /** * Get the length of the text. * diff --git a/layout/base/nsBidiPresUtils.cpp b/layout/base/nsBidiPresUtils.cpp index 3823f017ced..fd69e29166d 100644 --- a/layout/base/nsBidiPresUtils.cpp +++ b/layout/base/nsBidiPresUtils.cpp @@ -55,6 +55,7 @@ #include "nsPlaceholderFrame.h" #include "nsContainerFrame.h" #include "nsFirstLetterFrame.h" +#include "gfxUnicodeProperties.h" using namespace mozilla; @@ -1683,6 +1684,139 @@ nsresult nsBidiPresUtils::ProcessTextForRenderingContext(const PRUnichar* aMode, aPosResolve, aPosResolveCount, aWidth); } +/* static */ +void nsBidiPresUtils::WriteReverse(const PRUnichar* aSrc, + PRUint32 aSrcLength, + PRUnichar* aDest) +{ + const PRUnichar* src = aSrc + aSrcLength; + PRUnichar* dest = aDest; + PRUint32 UTF32Char; + + while (--src >= aSrc) { + if (NS_IS_LOW_SURROGATE(*src)) { + if (src > aSrc && NS_IS_HIGH_SURROGATE(*(src - 1))) { + UTF32Char = SURROGATE_TO_UCS4(*(src - 1), *src); + --src; + } else { + UTF32Char = UCS2_REPLACEMENT_CHAR; + } + } else if (NS_IS_HIGH_SURROGATE(*src)) { + // paired high surrogates are handled above, so this is a lone high surrogate + UTF32Char = UCS2_REPLACEMENT_CHAR; + } else { + UTF32Char = *src; + } + + UTF32Char = gfxUnicodeProperties::GetMirroredChar(UTF32Char); + + if (IS_IN_BMP(UTF32Char)) { + *(dest++) = UTF32Char; + } else { + *(dest++) = H_SURROGATE(UTF32Char); + *(dest++) = L_SURROGATE(UTF32Char); + } + } + + NS_ASSERTION(dest - aDest == aSrcLength, "Whole string not copied"); +} + +/* static */ +PRBool nsBidiPresUtils::WriteLogicalToVisual(const PRUnichar* aSrc, + PRUint32 aSrcLength, + PRUnichar* aDest, + nsBidiLevel aBaseDirection, + nsBidi* aBidiEngine) +{ + const PRUnichar* src = aSrc; + nsresult rv = aBidiEngine->SetPara(src, aSrcLength, aBaseDirection, nsnull); + if (NS_FAILED(rv)) { + return PR_FALSE; + } + + nsBidiDirection dir; + rv = aBidiEngine->GetDirection(&dir); + // NSBIDI_LTR returned from GetDirection means the whole text is LTR + if (NS_FAILED(rv) || dir == NSBIDI_LTR) { + return PR_FALSE; + } + + PRInt32 runCount; + rv = aBidiEngine->CountRuns(&runCount); + if (NS_FAILED(rv)) { + return PR_FALSE; + } + + PRInt32 runIndex, start, length; + PRUnichar* dest = aDest; + + for (runIndex = 0; runIndex < runCount; ++runIndex) { + rv = aBidiEngine->GetVisualRun(runIndex, &start, &length, &dir); + if (NS_FAILED(rv)) { + return PR_FALSE; + } + + src = aSrc + start; + + if (dir == NSBIDI_RTL) { + WriteReverse(src, length, dest); + dest += length; + } else { + do { + NS_ASSERTION(src >= aSrc && src < aSrc + aSrcLength, + "logical index out of range"); + NS_ASSERTION(dest < aDest + aSrcLength, "visual index out of range"); + *(dest++) = *(src++); + } while (--length); + } + } + + NS_ASSERTION(dest - aDest == aSrcLength, "whole string not copied"); + return PR_TRUE; +} + +void nsBidiPresUtils::CopyLogicalToVisual(const nsAString& aSource, + nsAString& aDest, + nsBidiLevel aBaseDirection, + PRBool aOverride) +{ + aDest.SetLength(0); + PRUint32 srcLength = aSource.Length(); + if (srcLength == 0) + return; + if (!EnsureStringLength(aDest, srcLength)) { + return; + } + nsAString::const_iterator fromBegin, fromEnd; + nsAString::iterator toBegin; + aSource.BeginReading(fromBegin); + aSource.EndReading(fromEnd); + aDest.BeginWriting(toBegin); + + if (aOverride) { + if (aBaseDirection == NSBIDI_RTL) { + // no need to use the converter -- just copy the string in reverse order + WriteReverse(fromBegin.get(), srcLength, toBegin.get()); + } else { + // if aOverride && aBaseDirection == NSBIDI_LTR, fall through to the + // simple copy + aDest.SetLength(0); + } + } else { + if (!WriteLogicalToVisual(fromBegin.get(), srcLength, toBegin.get(), + aBaseDirection, mBidiEngine)) { + aDest.SetLength(0); + } + } + + if (aDest.IsEmpty()) { + // Either there was an error or the source is unidirectional + // left-to-right. In either case, just copy source to dest. + CopyUnicodeTo(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd), + aDest); + } +} + PRUint32 nsBidiPresUtils::EstimateMemoryUsed() { PRUint32 size = 0; diff --git a/layout/base/nsBidiPresUtils.h b/layout/base/nsBidiPresUtils.h index c0b9382a2f4..e8e24c83091 100644 --- a/layout/base/nsBidiPresUtils.h +++ b/layout/base/nsBidiPresUtils.h @@ -317,6 +317,24 @@ public: PRInt32 aPosResolveCount, nscoord* aWidth); + /** + * Make a copy of a string, converting from logical to visual order + * + * @param aSource the source string + * @param aDest the destination string + * @param aBaseDirection the base direction of the string + * (NSBIDI_LTR or NSBIDI_RTL to force the base direction; + * NSBIDI_DEFAULT_LTR or NSBIDI_DEFAULT_RTL to let the bidi engine + * determine the direction from rules P2 and P3 of the bidi algorithm. + * @see nsBidi::GetPara + * @param aOverride if TRUE, the text has a bidi override, according to + * the direction in aDir + */ + void CopyLogicalToVisual(const nsAString& aSource, + nsAString& aDest, + nsBidiLevel aBaseDirection, + PRBool aOverride); + /** * Guess at how much memory is being used by this nsBidiPresUtils instance, * including memory used by nsBidi. @@ -477,6 +495,17 @@ private: void StripBidiControlCharacters(PRUnichar* aText, PRInt32& aTextLength) const; + + static PRBool WriteLogicalToVisual(const PRUnichar* aSrc, + PRUint32 aSrcLength, + PRUnichar* aDest, + nsBidiLevel aBaseDirection, + nsBidi* aBidiEngine); + + static void WriteReverse(const PRUnichar* aSrc, + PRUint32 aSrcLength, + PRUnichar* aDest); + nsAutoString mBuffer; nsTArray mLogicalFrames; nsTArray mVisualFrames; diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index 8c43912e2d9..efd130be42d 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -2134,6 +2134,51 @@ nsCSSRendering::PaintGradient(nsPresContext* aPresContext, } } +/** + * A struct representing all the information needed to paint a background + * image to some target, taking into account all CSS background-* properties. + * See PrepareBackgroundLayer. + */ +struct BackgroundLayerState { + /** + * @param aFlags some combination of nsCSSRendering::PAINTBG_* flags + */ + BackgroundLayerState(nsIFrame* aForFrame, const nsStyleImage* aImage, PRUint32 aFlags) + : mImageRenderer(aForFrame, aImage, aFlags) {} + + /** + * The ImageRenderer that will be used to draw the background. + */ + ImageRenderer mImageRenderer; + /** + * A rectangle that one copy of the image tile is mapped onto. Same + * coordinate system as aBorderArea/aBGClipRect passed into + * PrepareBackgroundLayer. + */ + nsRect mDestArea; + /** + * The actual rectangle that should be filled with (complete or partial) + * image tiles. Same coordinate system as aBorderArea/aBGClipRect passed into + * PrepareBackgroundLayer. + */ + nsRect mFillArea; + /** + * The anchor point that should be snapped to a pixel corner. Same + * coordinate system as aBorderArea/aBGClipRect passed into + * PrepareBackgroundLayer. + */ + nsPoint mAnchor; +}; + +static BackgroundLayerState +PrepareBackgroundLayer(nsPresContext* aPresContext, + nsIFrame* aForFrame, + PRUint32 aFlags, + const nsRect& aBorderArea, + const nsRect& aBGClipRect, + const nsStyleBackground& aBackground, + const nsStyleBackground::Layer& aLayer); + void nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, nsIRenderingContext& aRenderingContext, @@ -2304,9 +2349,13 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, } } if (!dirtyRectGfx.IsEmpty()) { - PaintBackgroundLayer(aPresContext, aRenderingContext, aForFrame, aFlags, - dirtyRect, aBorderArea, bgClipArea, *bg, - layer); + BackgroundLayerState state = PrepareBackgroundLayer(aPresContext, aForFrame, + aFlags, aBorderArea, bgClipArea, *bg, layer); + if (!state.mFillArea.IsEmpty()) { + state.mImageRenderer.Draw(aPresContext, aRenderingContext, + state.mDestArea, state.mFillArea, + state.mAnchor + aBorderArea.TopLeft(), dirtyRect); + } } } } @@ -2333,16 +2382,14 @@ ScaleDimension(const nsStyleBackground::Size::Dimension& aDimension, } } -static void -PaintBackgroundLayer(nsPresContext* aPresContext, - nsIRenderingContext& aRenderingContext, - nsIFrame* aForFrame, - PRUint32 aFlags, - const nsRect& aDirtyRect, // intersected with aBGClipRect - const nsRect& aBorderArea, - const nsRect& aBGClipRect, - const nsStyleBackground& aBackground, - const nsStyleBackground::Layer& aLayer) +static BackgroundLayerState +PrepareBackgroundLayer(nsPresContext* aPresContext, + nsIFrame* aForFrame, + PRUint32 aFlags, + const nsRect& aBorderArea, + const nsRect& aBGClipRect, + const nsStyleBackground& aBackground, + const nsStyleBackground::Layer& aLayer) { /* * The background properties we need to keep in mind when drawing background @@ -2400,12 +2447,14 @@ PaintBackgroundLayer(nsPresContext* aPresContext, */ PRUint32 irFlags = 0; - if (aFlags & nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES) + if (aFlags & nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES) { irFlags |= ImageRenderer::FLAG_SYNC_DECODE_IMAGES; - ImageRenderer imageRenderer(aForFrame, &aLayer.mImage, irFlags); - if (!imageRenderer.PrepareImage()) { + } + + BackgroundLayerState state(aForFrame, &aLayer.mImage, irFlags); + if (!state.mImageRenderer.PrepareImage()) { // There's no image or it's not ready to be painted. - return; + return state; } // Compute background origin area relative to aBorderArea now as we may need @@ -2469,7 +2518,7 @@ PaintBackgroundLayer(nsPresContext* aPresContext, // // relative to aBorderArea.TopLeft() (which is where the top-left // of aForFrame's border-box will be rendered) - nsPoint imageTopLeft, anchor; + nsPoint imageTopLeft; if (NS_STYLE_BG_ATTACHMENT_FIXED == aLayer.mAttachment) { aPresContext->SetHasFixedBackgroundFrame(); @@ -2514,9 +2563,9 @@ PaintBackgroundLayer(nsPresContext* aPresContext, } } - nsSize imageSize = imageRenderer.ComputeSize(bgPositioningArea.Size()); + nsSize imageSize = state.mImageRenderer.ComputeSize(bgPositioningArea.Size()); if (imageSize.width <= 0 || imageSize.height <= 0) - return; + return state; // Scale the image as specified for background-size and as required for // proper background positioning when background-position is defined with @@ -2564,27 +2613,38 @@ PaintBackgroundLayer(nsPresContext* aPresContext, // Compute the position of the background now that the background's size is // determined. ComputeBackgroundAnchorPoint(aLayer, bgPositioningArea.Size(), imageSize, - &imageTopLeft, &anchor); + &imageTopLeft, &state.mAnchor); imageTopLeft += bgPositioningArea.TopLeft(); - anchor += bgPositioningArea.TopLeft(); + state.mAnchor += bgPositioningArea.TopLeft(); - nsRect destArea(imageTopLeft + aBorderArea.TopLeft(), imageSize); - nsRect fillArea = destArea; + state.mDestArea = nsRect(imageTopLeft + aBorderArea.TopLeft(), imageSize); + state.mFillArea = state.mDestArea; PRIntn repeat = aLayer.mRepeat; PR_STATIC_ASSERT(NS_STYLE_BG_REPEAT_XY == (NS_STYLE_BG_REPEAT_X | NS_STYLE_BG_REPEAT_Y)); if (repeat & NS_STYLE_BG_REPEAT_X) { - fillArea.x = bgClipRect.x; - fillArea.width = bgClipRect.width; + state.mFillArea.x = bgClipRect.x; + state.mFillArea.width = bgClipRect.width; } if (repeat & NS_STYLE_BG_REPEAT_Y) { - fillArea.y = bgClipRect.y; - fillArea.height = bgClipRect.height; + state.mFillArea.y = bgClipRect.y; + state.mFillArea.height = bgClipRect.height; } - fillArea.IntersectRect(fillArea, bgClipRect); + state.mFillArea.IntersectRect(state.mFillArea, bgClipRect); + return state; +} - imageRenderer.Draw(aPresContext, aRenderingContext, destArea, fillArea, - anchor + aBorderArea.TopLeft(), aDirtyRect); +nsRect +nsCSSRendering::GetBackgroundLayerRect(nsPresContext* aPresContext, + nsIFrame* aForFrame, + const nsRect& aBorderArea, + const nsStyleBackground& aBackground, + const nsStyleBackground::Layer& aLayer) +{ + BackgroundLayerState state = + PrepareBackgroundLayer(aPresContext, aForFrame, 0, aBorderArea, + aBorderArea, aBackground, aLayer); + return state.mFillArea; } static void diff --git a/layout/base/nsCSSRendering.h b/layout/base/nsCSSRendering.h index fdcc2061522..ff7718187af 100644 --- a/layout/base/nsCSSRendering.h +++ b/layout/base/nsCSSRendering.h @@ -256,6 +256,17 @@ struct nsCSSRendering { PRUint32 aFlags, nsRect* aBGClipRect = nsnull); + /** + * Returns the rectangle covered by the given background layer image, taking + * into account background positioning, sizing, and repetition, but not + * clipping. + */ + static nsRect GetBackgroundLayerRect(nsPresContext* aPresContext, + nsIFrame* aForFrame, + const nsRect& aBorderArea, + const nsStyleBackground& aBackground, + const nsStyleBackground::Layer& aLayer); + /** * Called by the presShell when painting is finished, so we can clear our * inline background data cache. diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 2c3ba8175d1..9737495b112 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -73,6 +73,7 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame, : mReferenceFrame(aReferenceFrame), mIgnoreScrollFrame(nsnull), mCurrentTableItem(nsnull), + mFinalTransparentRegion(nsnull), mMode(aMode), mBuildCaret(aBuildCaret), mIgnoreSuppression(PR_FALSE), @@ -169,15 +170,32 @@ nsDisplayListBuilder::GetBackgroundPaintFlags() { return flags; } +static PRUint64 RegionArea(const nsRegion& aRegion) +{ + PRUint64 area = 0; + nsRegionRectIterator iter(aRegion); + const nsRect* r; + while ((r = iter.Next()) != nsnull) { + area += PRUint64(r->width)*r->height; + } + return area; +} + void nsDisplayListBuilder::SubtractFromVisibleRegion(nsRegion* aVisibleRegion, const nsRegion& aRegion) { + if (aRegion.IsEmpty()) + return; + nsRegion tmp; tmp.Sub(*aVisibleRegion, aRegion); // Don't let *aVisibleRegion get too complex, but don't let it fluff out // to its bounds either, which can be very bad (see bug 516740). - if (GetAccurateVisibleRegions() || tmp.GetNumRects() <= 15) { + // Do let aVisibleRegion get more complex if by doing so we reduce its + // area by at least half. + if (GetAccurateVisibleRegions() || tmp.GetNumRects() <= 15 || + RegionArea(tmp) <= RegionArea(*aVisibleRegion)/2) { *aVisibleRegion = tmp; } } @@ -309,19 +327,19 @@ nsDisplayList::ComputeVisibilityForRoot(nsDisplayListBuilder* aBuilder, return ComputeVisibilityForSublist(aBuilder, aVisibleRegion, r.GetBounds()); } -static PRBool +static nsRegion TreatAsOpaque(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder, PRBool* aTransparentBackground) { - if (aItem->IsOpaque(aBuilder, aTransparentBackground)) - return PR_TRUE; + nsRegion opaque = aItem->GetOpaqueRegion(aBuilder, aTransparentBackground); if (aBuilder->IsForPluginGeometry()) { // Treat all chrome items as opaque nsIFrame* f = aItem->GetUnderlyingFrame(); - if (f && f->PresContext()->IsChrome()) - return PR_TRUE; + if (f && f->PresContext()->IsChrome()) { + opaque = aItem->GetBounds(aBuilder); + } } - return PR_FALSE; + return opaque; } PRBool @@ -361,10 +379,9 @@ nsDisplayList::ComputeVisibilityForSublist(nsDisplayListBuilder* aBuilder, if (item->ComputeVisibility(aBuilder, aVisibleRegion)) { anyVisible = PR_TRUE; PRBool transparentBackground = PR_FALSE; - if (TreatAsOpaque(item, aBuilder, &transparentBackground)) { - // Subtract opaque item from the visible region - aBuilder->SubtractFromVisibleRegion(aVisibleRegion, nsRegion(bounds)); - } + nsRegion opaque = TreatAsOpaque(item, aBuilder, &transparentBackground); + // Subtract opaque item from the visible region + aBuilder->SubtractFromVisibleRegion(aVisibleRegion, opaque); forceTransparentSurface = forceTransparentSurface || transparentBackground; } AppendToBottom(item); @@ -685,9 +702,8 @@ PRBool nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder, return PR_FALSE; PRBool forceTransparentBackground; - if (TreatAsOpaque(this, aBuilder, &forceTransparentBackground)) { - aVisibleRegion->Sub(*aVisibleRegion, bounds); - } + nsRegion opaque = TreatAsOpaque(this, aBuilder, &forceTransparentBackground); + aBuilder->SubtractFromVisibleRegion(aVisibleRegion, opaque); return PR_TRUE; } @@ -833,25 +849,8 @@ RoundedBorderIntersectsRect(nsIFrame* aFrame, static PRBool RoundedRectContainsRect(const nsRect& aRoundedRect, const nscoord aRadii[8], const nsRect& aContainedRect) { - // rectFullHeight and rectFullWidth together will approximately contain - // the total area of the frame minus the rounded corners. - nsRect rectFullHeight = aRoundedRect; - nscoord xDiff = NS_MAX(aRadii[NS_CORNER_TOP_LEFT_X], aRadii[NS_CORNER_BOTTOM_LEFT_X]); - rectFullHeight.x += xDiff; - rectFullHeight.width -= NS_MAX(aRadii[NS_CORNER_TOP_RIGHT_X], - aRadii[NS_CORNER_BOTTOM_RIGHT_X]) + xDiff; - if (rectFullHeight.Contains(aContainedRect)) - return PR_TRUE; - - nsRect rectFullWidth = aRoundedRect; - nscoord yDiff = NS_MAX(aRadii[NS_CORNER_TOP_LEFT_Y], aRadii[NS_CORNER_TOP_RIGHT_Y]); - rectFullWidth.y += yDiff; - rectFullWidth.height -= NS_MAX(aRadii[NS_CORNER_BOTTOM_LEFT_Y], - aRadii[NS_CORNER_BOTTOM_RIGHT_Y]) + yDiff; - if (rectFullWidth.Contains(aContainedRect)) - return PR_TRUE; - - return PR_FALSE; + nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(aRoundedRect, aRadii, aContainedRect); + return rgn.Contains(aContainedRect); } void @@ -887,9 +886,48 @@ nsDisplayBackground::ComputeVisibility(nsDisplayListBuilder* aBuilder, nsCSSRendering::FindBackground(mFrame->PresContext(), mFrame, &bgSC); } -PRBool -nsDisplayBackground::IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface) { +nsRegion +nsDisplayBackground::GetInsideClipRegion(PRUint8 aClip, const nsRect& aRect) +{ + nsRegion result; + if (aRect.IsEmpty()) + return result; + + nscoord radii[8]; + nsRect clipRect; + PRBool haveRadii; + switch (aClip) { + case NS_STYLE_BG_CLIP_BORDER: + haveRadii = mFrame->GetBorderRadii(radii); + clipRect = nsRect(ToReferenceFrame(), mFrame->GetSize()); + break; + case NS_STYLE_BG_CLIP_PADDING: + haveRadii = mFrame->GetPaddingBoxBorderRadii(radii); + clipRect = mFrame->GetPaddingRect() - mFrame->GetPosition() + ToReferenceFrame(); + break; + case NS_STYLE_BG_CLIP_CONTENT: + haveRadii = mFrame->GetContentBoxBorderRadii(radii); + clipRect = mFrame->GetContentRect() - mFrame->GetPosition() + ToReferenceFrame(); + break; + default: + NS_NOTREACHED("Unknown clip type"); + return result; + } + + if (haveRadii) { + result = nsLayoutUtils::RoundedRectIntersectRect(clipRect, radii, aRect); + } else { + nsRect r; + r.IntersectRect(clipRect, aRect); + result = r; + } + return result; +} + +nsRegion +nsDisplayBackground::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface) { + nsRegion result; if (aForceTransparentSurface) { *aForceTransparentSurface = PR_FALSE; } @@ -900,27 +938,43 @@ nsDisplayBackground::IsOpaque(nsDisplayListBuilder* aBuilder, *aForceTransparentSurface = disp->mAppearance == NS_THEME_WIN_BORDERLESS_GLASS || disp->mAppearance == NS_THEME_WIN_GLASS; } - return mThemeTransparency == nsITheme::eOpaque; + if (mThemeTransparency == nsITheme::eOpaque) { + result = GetBounds(aBuilder); + } + return result; } nsStyleContext* bgSC; + nsPresContext* presContext = mFrame->PresContext(); if (!nsCSSRendering::FindBackground(mFrame->PresContext(), mFrame, &bgSC)) - return PR_FALSE; + return result; const nsStyleBackground* bg = bgSC->GetStyleBackground(); - const nsStyleBackground::Layer& bottomLayer = bg->BottomLayer(); - // bottom layer's clip is used for the color - if (bottomLayer.mClip != NS_STYLE_BG_CLIP_BORDER || - nsLayoutUtils::HasNonZeroCorner(mFrame->GetStyleBorder()->mBorderRadius)) - return PR_FALSE; - + nsRect borderBox = nsRect(ToReferenceFrame(), mFrame->GetSize()); if (NS_GET_A(bg->mBackgroundColor) == 255 && - !nsCSSRendering::IsCanvasFrame(mFrame)) - return PR_TRUE; + !nsCSSRendering::IsCanvasFrame(mFrame)) { + result = GetInsideClipRegion(bottomLayer.mClip, borderBox); + } - return bottomLayer.mRepeat == NS_STYLE_BG_REPEAT_XY && - bottomLayer.mImage.IsOpaque(); + // For policies other than EACH_BOX, don't try to optimize here, since + // this could easily lead to O(N^2) behavior inside InlineBackgroundData, + // which expects frames to be sent to it in content order, not reverse + // content order which we'll produce here. + // Of course, if there's only one frame in the flow, it doesn't matter. + if (bg->mBackgroundInlinePolicy == NS_STYLE_BG_INLINE_POLICY_EACH_BOX || + (!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) { + NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) { + const nsStyleBackground::Layer& layer = bg->mLayers[i]; + if (layer.mImage.IsOpaque()) { + nsRect r = nsCSSRendering::GetBackgroundLayerRect(presContext, mFrame, + borderBox, *bg, layer); + result.Or(result, GetInsideClipRegion(layer.mClip, r)); + } + } + } + + return result; } PRBool @@ -1282,13 +1336,17 @@ nsDisplayWrapList::ComputeVisibility(nsDisplayListBuilder* aBuilder, mVisibleRect); } -PRBool -nsDisplayWrapList::IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface) { +nsRegion +nsDisplayWrapList::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface) { if (aForceTransparentSurface) { *aForceTransparentSurface = PR_FALSE; } - return mList.IsOpaque(); + nsRegion result; + if (mList.IsOpaque()) { + result = GetBounds(aBuilder); + } + return result; } PRBool nsDisplayWrapList::IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) { @@ -1333,14 +1391,13 @@ PRBool nsDisplayWrapList::ChildrenCanBeInactive(nsDisplayListBuilder* aBuilder, return PR_TRUE; } -PRBool nsDisplayWrapList::HasText() +nsRect nsDisplayWrapList::GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) { + nsRect bounds; for (nsDisplayItem* i = mList.GetBottom(); i; i = i->GetAbove()) { - if (i->HasText()) { - return PR_TRUE; - } + bounds.UnionRect(bounds, i->GetComponentAlphaBounds(aBuilder)); } - return PR_FALSE; + return bounds; } static nsresult @@ -1426,14 +1483,14 @@ nsDisplayOpacity::~nsDisplayOpacity() { } #endif -PRBool nsDisplayOpacity::IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface) { +nsRegion nsDisplayOpacity::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface) { if (aForceTransparentSurface) { *aForceTransparentSurface = PR_FALSE; } // We are never opaque, if our opacity was < 1 then we wouldn't have // been created. - return PR_FALSE; + return nsRegion(); } // nsDisplayOpacity uses layers for rendering @@ -1600,13 +1657,14 @@ nsDisplayClipRoundedRect::~nsDisplayClipRoundedRect() } #endif -PRBool nsDisplayClipRoundedRect::IsOpaque(nsDisplayListBuilder* aBuilder, +nsRegion +nsDisplayClipRoundedRect::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, PRBool* aForceTransparentSurface) { if (aForceTransparentSurface) { *aForceTransparentSurface = PR_FALSE; } - return PR_FALSE; + return nsRegion(); } void @@ -2020,8 +2078,8 @@ nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder *aBuilder) * mStoredList.GetVisibleRect().Contains(untransformedVisible), then it * certainly contains the actual (non-axis-aligned) untransformed rect. */ -PRBool nsDisplayTransform::IsOpaque(nsDisplayListBuilder *aBuilder, - PRBool* aForceTransparentSurface) +nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder *aBuilder, + PRBool* aForceTransparentSurface) { if (aForceTransparentSurface) { *aForceTransparentSurface = PR_FALSE; @@ -2029,14 +2087,17 @@ PRBool nsDisplayTransform::IsOpaque(nsDisplayListBuilder *aBuilder, const nsStyleDisplay* disp = mFrame->GetStyleDisplay(); nsRect untransformedVisible = UntransformRect(mVisibleRect, mFrame, ToReferenceFrame()); - return disp->mTransform.GetMainMatrixEntry(1) == 0.0f && - disp->mTransform.GetMainMatrixEntry(2) == 0.0f && - mStoredList.GetVisibleRect().Contains(untransformedVisible) && - mStoredList.IsOpaque(aBuilder); + nsRegion result; + if (disp->mTransform.GetMainMatrixEntry(1) == 0.0f && + disp->mTransform.GetMainMatrixEntry(2) == 0.0f && + mStoredList.GetOpaqueRegion(aBuilder).Contains(untransformedVisible)) { + result = mVisibleRect; + } + return result; } /* The transform is uniform if it fills the entire bounding rect and the - * wrapped list is uniform. See IsOpaque for discussion of why this + * wrapped list is uniform. See GetOpaqueRegion for discussion of why this * works. */ PRBool nsDisplayTransform::IsUniform(nsDisplayListBuilder *aBuilder, nscolor* aColor) @@ -2160,13 +2221,13 @@ nsDisplaySVGEffects::~nsDisplaySVGEffects() } #endif -PRBool nsDisplaySVGEffects::IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface) +nsRegion nsDisplaySVGEffects::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface) { if (aForceTransparentSurface) { *aForceTransparentSurface = PR_FALSE; } - return PR_FALSE; + return nsRegion(); } void diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 6f69065ac56..ac98e31a043 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -151,10 +151,14 @@ public: */ PRBool IsForEventDelivery() { return mMode == EVENT_DELIVERY; } /** - * @return PR_TRUE if the display list is being build to compute geometry + * @return PR_TRUE if the display list is being built to compute geometry * for plugins. */ PRBool IsForPluginGeometry() { return mMode == PLUGIN_GEOMETRY; } + /** + * @return PR_TRUE if the display list is being built for painting. + */ + PRBool IsForPainting() { return mMode == PAINTING; } /** * @return PR_TRUE if "painting is suppressed" during page load and we * should paint only the background of the document. @@ -334,6 +338,19 @@ public: */ FrameLayerBuilder* LayerBuilder() { return &mLayerBuilder; } + /** + * Get the area of the final transparent region. + */ + const nsRegion* GetFinalTransparentRegion() { return mFinalTransparentRegion; } + /** + * Record the area of the final transparent region after all visibility + * calculations were performed. + */ + void SetFinalTransparentRegion(const nsRegion& aFinalTransparentRegion) + { + mFinalTransparentRegion = &aFinalTransparentRegion; + } + /** * Returns true if we need to descend into this frame when building * the display list, even though it doesn't intersect the dirty @@ -418,6 +435,7 @@ private: nsAutoTArray mPresShellStates; nsAutoTArray mFramesMarkedForDisplay; nsDisplayTableItem* mCurrentTableItem; + const nsRegion* mFinalTransparentRegion; Mode mMode; PRPackedBool mBuildCaret; PRPackedBool mIgnoreSuppression; @@ -541,16 +559,16 @@ public: return nsRect(ToReferenceFrame(), GetUnderlyingFrame()->GetSize()); } /** - * @return PR_TRUE if the item is definitely opaque --- i.e., paints - * every pixel within its bounds opaquely + * @return a region of the item that is opaque --- every pixel painted + * with an opaque color. */ - virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface = nsnull) + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface = nsnull) { if (aForceTransparentSurface) { *aForceTransparentSurface = PR_FALSE; } - return PR_FALSE; + return nsRegion(); } /** * If this returns true, then aColor is set to the uniform color @@ -623,14 +641,12 @@ public: * On entry, aVisibleRegion contains the region (relative to ReferenceFrame()) * which may be visible. If the display item opaquely covers an area, it * can remove that area from aVisibleRegion before returning. - * nsDisplayList::ComputeVisibility automatically subtracts the bounds - * of items that return true from IsOpaque(), and automatically - * removes items whose bounds do not intersect the visible area, - * so implementations of nsDisplayItem::ComputeVisibility do not - * need to do these things. + * nsDisplayList::ComputeVisibility automatically subtracts the region + * returned by GetOpaqueRegion, and automatically removes items whose bounds + * do not intersect the visible area, so implementations of + * nsDisplayItem::ComputeVisibility do not need to do these things. * nsDisplayList::ComputeVisibility will already have set mVisibleRect on - * this item to the intersection of *aVisibleRegion (unioned with - * *aVisibleRegionBeforeMove, if that's non-null) and this item's bounds. + * this item to the intersection of *aVisibleRegion and this item's bounds. * We rely on that, so this should only be called by * nsDisplayList::ComputeVisibility or nsDisplayItem::RecomputeVisibility. * @@ -694,10 +710,17 @@ public: } /** - * Checks if this display item (or any children) contains text that might - * be rendered with subpixel antialiasing. + * Checks if this display item (or any children) contains content that might + * be rendered with component alpha (e.g. subpixel antialiasing). Returns the + * bounds of the area that needs component alpha, or an empty rect if nothing + * in the item does. */ - virtual PRBool HasText() { return PR_FALSE; } + virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) { return nsRect(); } + + /** + * Disable usage of component alpha. Currently only relevant for items that have text. + */ + virtual void DisableComponentAlpha() {} protected: friend class nsDisplayList; @@ -1169,13 +1192,12 @@ public: } NS_DISPLAY_DECL_NAME(mName, mType) - virtual PRBool HasText() { - if (mType == nsDisplayItem::TYPE_HEADER_FOOTER) { - return PR_TRUE; - } else { - return PR_FALSE; - } + virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) { + if (mType == nsDisplayItem::TYPE_HEADER_FOOTER) + return GetBounds(aBuilder); + return nsRect(); } + protected: PaintCallback mPaint; #ifdef DEBUG @@ -1338,12 +1360,16 @@ public: virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) { return mBounds; } - virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aOutTransparentBackground = nsnull) { + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aOutTransparentBackground = nsnull) { if (aOutTransparentBackground) { *aOutTransparentBackground = PR_FALSE; } - return (NS_GET_A(mColor) == 255); + nsRegion result; + if (NS_GET_A(mColor) == 255) { + result = GetBounds(aBuilder); + } + return result; } virtual PRBool IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) @@ -1378,8 +1404,8 @@ public: HitTestState* aState, nsTArray *aOutFrames); virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion); - virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface = nsnull); + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface = nsnull); virtual PRBool IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame); virtual PRBool IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor); @@ -1388,6 +1414,8 @@ public: virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx); NS_DISPLAY_DECL_NAME("Background", TYPE_BACKGROUND) protected: + nsRegion GetInsideClipRegion(PRUint8 aClip, const nsRect& aRect); + /* Used to cache mFrame->IsThemed() since it isn't a cheap call */ PRPackedBool mIsThemed; nsITheme::Transparency mThemeTransparency; @@ -1514,8 +1542,8 @@ public: virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, HitTestState* aState, nsTArray *aOutFrames); virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder); - virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface = nsnull); + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface = nsnull); virtual PRBool IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor); virtual PRBool IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame); @@ -1528,7 +1556,7 @@ public: } NS_DISPLAY_DECL_NAME("WrapList", TYPE_WRAP_LIST) - virtual PRBool HasText(); + virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder); virtual nsDisplayList* GetList() { return &mList; } @@ -1598,8 +1626,8 @@ public: virtual ~nsDisplayOpacity(); #endif - virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface = nsnull); + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface = nsnull); virtual already_AddRefed BuildLayer(nsDisplayListBuilder* aBuilder, LayerManager* aManager); virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder, @@ -1696,8 +1724,8 @@ public: virtual ~nsDisplayClipRoundedRect(); #endif - virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface = nsnull); + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface = nsnull); virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, HitTestState* aState, nsTArray *aOutFrames); virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, @@ -1766,8 +1794,8 @@ public: virtual ~nsDisplaySVGEffects(); #endif - virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface = nsnull); + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface = nsnull); virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, HitTestState* aState, nsTArray *aOutFrames); virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) { @@ -1816,7 +1844,12 @@ public: NS_DISPLAY_DECL_NAME("nsDisplayTransform", TYPE_TRANSFORM); - virtual PRBool HasText() { return mStoredList.HasText(); } + virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) + { + if (mStoredList.GetComponentAlphaBounds(aBuilder).IsEmpty()) + return nsRect(); + return GetBounds(aBuilder); + } #ifdef NS_DEBUG nsDisplayWrapList* GetStoredList() { return &mStoredList; } @@ -1825,8 +1858,8 @@ public: virtual void HitTest(nsDisplayListBuilder *aBuilder, const nsRect& aRect, HitTestState *aState, nsTArray *aOutFrames); virtual nsRect GetBounds(nsDisplayListBuilder *aBuilder); - virtual PRBool IsOpaque(nsDisplayListBuilder *aBuilder, - PRBool* aForceTransparentSurface = nsnull); + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder *aBuilder, + PRBool* aForceTransparentSurface = nsnull); virtual PRBool IsUniform(nsDisplayListBuilder *aBuilder, nscolor* aColor); virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder, LayerManager* aManager); diff --git a/layout/base/nsLayoutDebugger.cpp b/layout/base/nsLayoutDebugger.cpp index 037974b7558..0dd2a3e3332 100644 --- a/layout/base/nsLayoutDebugger.cpp +++ b/layout/base/nsLayoutDebugger.cpp @@ -43,9 +43,12 @@ #include "nsILayoutDebugger.h" #include "nsFrame.h" #include "nsDisplayList.h" +#include "FrameLayerBuilder.h" #include +using namespace mozilla::layers; + #ifdef NS_DEBUG class nsLayoutDebugger : public nsILayoutDebugger { public: @@ -178,12 +181,24 @@ PrintDisplayListTo(nsDisplayListBuilder* aBuilder, const nsDisplayList& aList, nscolor color; nsRect vis = i->GetVisibleRect(); nsDisplayList* list = i->GetList(); - fprintf(aOutput, "%s %p(%s) (%d,%d,%d,%d)(%d,%d,%d,%d)%s%s\n", + nsRegion opaque; + if (!list || list->DidComputeVisibility()) { + opaque = i->GetOpaqueRegion(aBuilder); + } + fprintf(aOutput, "%s %p(%s) (%d,%d,%d,%d)(%d,%d,%d,%d)%s%s", i->Name(), (void*)f, NS_ConvertUTF16toUTF8(fName).get(), rect.x, rect.y, rect.width, rect.height, vis.x, vis.y, vis.width, vis.height, - ((!list || list->DidComputeVisibility()) && i->IsOpaque(aBuilder)) ? " opaque" : "", + opaque.IsEmpty() ? "" : " opaque", i->IsUniform(aBuilder, &color) ? " uniform" : ""); + if (f) { + PRUint32 key = i->GetPerFrameKey(); + Layer* layer = aBuilder->LayerBuilder()->GetOldLayerFor(f, key); + if (layer) { + fprintf(aOutput, " layer=%p", layer); + } + } + fputc('\n', aOutput); if (list) { PrintDisplayListTo(aBuilder, *list, aIndent + 4, aOutput); } diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 33f658f2019..004554c52dd 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -923,6 +923,35 @@ nsLayoutUtils::RoundGfxRectToAppRect(const gfxRect &aRect, float aFactor) nscoord(scaledRect.size.width), nscoord(scaledRect.size.height)); } + +nsRegion +nsLayoutUtils::RoundedRectIntersectRect(const nsRect& aRoundedRect, + const nscoord aRadii[8], + const nsRect& aContainedRect) +{ + // rectFullHeight and rectFullWidth together will approximately contain + // the total area of the frame minus the rounded corners. + nsRect rectFullHeight = aRoundedRect; + nscoord xDiff = NS_MAX(aRadii[NS_CORNER_TOP_LEFT_X], aRadii[NS_CORNER_BOTTOM_LEFT_X]); + rectFullHeight.x += xDiff; + rectFullHeight.width -= NS_MAX(aRadii[NS_CORNER_TOP_RIGHT_X], + aRadii[NS_CORNER_BOTTOM_RIGHT_X]) + xDiff; + nsRect r1; + r1.IntersectRect(rectFullHeight, aContainedRect); + + nsRect rectFullWidth = aRoundedRect; + nscoord yDiff = NS_MAX(aRadii[NS_CORNER_TOP_LEFT_Y], aRadii[NS_CORNER_TOP_RIGHT_Y]); + rectFullWidth.y += yDiff; + rectFullWidth.height -= NS_MAX(aRadii[NS_CORNER_BOTTOM_LEFT_Y], + aRadii[NS_CORNER_BOTTOM_RIGHT_Y]) + yDiff; + nsRect r2; + r2.IntersectRect(rectFullWidth, aContainedRect); + + nsRegion result; + result.Or(r1, r2); + return result; +} + nsRect nsLayoutUtils::MatrixTransformRect(const nsRect &aBounds, const gfxMatrix &aMatrix, float aFactor) @@ -1399,21 +1428,11 @@ nsLayoutUtils::PaintFrame(nsIRenderingContext* aRenderingContext, nsIFrame* aFra list.ComputeVisibilityForRoot(&builder, &visibleRegion); -#ifdef DEBUG - if (gDumpPaintList) { - fprintf(stderr, "Painting --- after optimization:\n"); - nsFrame::PrintDisplayList(&builder, list); - } -#endif - PRUint32 flags = nsDisplayList::PAINT_DEFAULT; if (aFlags & PAINT_WIDGET_LAYERS) { flags |= nsDisplayList::PAINT_USE_WIDGET_LAYERS; - nsIWidget *widget = aFrame->GetNearestWidget(); - PRInt32 pixelRatio = presContext->AppUnitsPerDevPixel(); - nsIntRegion visibleWindowRegion(visibleRegion.ToOutsidePixels(pixelRatio)); - nsIntRegion dirtyWindowRegion(aDirtyRegion.ToOutsidePixels(pixelRatio)); + nsIWidget *widget = aFrame->GetNearestWidget(); if (willFlushRetainedLayers) { // The caller wanted to paint from retained layers, but set up // the paint in such a way that we can't use them. We're going @@ -1426,6 +1445,10 @@ nsLayoutUtils::PaintFrame(nsIRenderingContext* aRenderingContext, nsIFrame* aFra } else if (widget && !(aFlags & PAINT_DOCUMENT_RELATIVE)) { // XXX we should simplify this API now that dirtyWindowRegion always // covers the entire window + PRInt32 pixelRatio = presContext->AppUnitsPerDevPixel(); + nsIntRegion visibleWindowRegion(visibleRegion.ToOutsidePixels(pixelRatio)); + nsIntRegion dirtyWindowRegion(aDirtyRegion.ToOutsidePixels(pixelRatio)); + builder.SetFinalTransparentRegion(visibleRegion); widget->UpdatePossiblyTransparentRegion(dirtyWindowRegion, visibleWindowRegion); } } @@ -1434,6 +1457,9 @@ nsLayoutUtils::PaintFrame(nsIRenderingContext* aRenderingContext, nsIFrame* aFra #ifdef DEBUG if (gDumpPaintList) { + fprintf(stderr, "Painting --- after optimization:\n"); + nsFrame::PrintDisplayList(&builder, list); + fprintf(stderr, "Painting --- retained layer tree:\n"); builder.LayerBuilder()->DumpRetainedLayerTree(); } @@ -1621,7 +1647,8 @@ nsLayoutUtils::GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo) { nsRect nsLayoutUtils::GetTextShadowRectsUnion(const nsRect& aTextAndDecorationsRect, - nsIFrame* aFrame) + nsIFrame* aFrame, + PRUint32 aFlags) { const nsStyleText* textStyle = aFrame->GetStyleText(); if (!textStyle->mTextShadow) @@ -1630,12 +1657,15 @@ nsLayoutUtils::GetTextShadowRectsUnion(const nsRect& aTextAndDecorationsRect, nsRect resultRect = aTextAndDecorationsRect; PRInt32 A2D = aFrame->PresContext()->AppUnitsPerDevPixel(); for (PRUint32 i = 0; i < textStyle->mTextShadow->Length(); ++i) { - nsRect tmpRect(aTextAndDecorationsRect); nsCSSShadowItem* shadow = textStyle->mTextShadow->ShadowAt(i); + nsMargin blur = nsContextBoxBlur::GetBlurRadiusMargin(shadow->mRadius, A2D); + if ((aFlags & EXCLUDE_BLUR_SHADOWS) && blur != nsMargin(0, 0, 0, 0)) + continue; + + nsRect tmpRect(aTextAndDecorationsRect); tmpRect.MoveBy(nsPoint(shadow->mXOffset, shadow->mYOffset)); - tmpRect.Inflate( - nsContextBoxBlur::GetBlurRadiusMargin(shadow->mRadius, A2D)); + tmpRect.Inflate(blur); resultRect.UnionRect(resultRect, tmpRect); } diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 893766d8124..0136fd3ee77 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -512,6 +512,15 @@ public: */ static nsRect RoundGfxRectToAppRect(const gfxRect &aRect, float aFactor); + /** + * Returns a subrectangle of aContainedRect that is entirely inside the rounded + * rect. Complex cases are handled conservatively by returning a smaller + * rect than necessary. + */ + static nsRegion RoundedRectIntersectRect(const nsRect& aRoundedRect, + const nscoord aRadii[8], + const nsRect& aContainedRect); + enum { PAINT_IN_TRANSFORM = 0x01, PAINT_SYNC_DECODE_IMAGES = 0x02, @@ -651,13 +660,17 @@ public: */ static nsRect GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo); + enum { + EXCLUDE_BLUR_SHADOWS = 0x01 + }; /** * Takes a text-shadow array from the style properties of a given nsIFrame and * computes the union of those shadows along with the given initial rect. * If there are no shadows, the initial rect is returned. */ static nsRect GetTextShadowRectsUnion(const nsRect& aTextAndDecorationsRect, - nsIFrame* aFrame); + nsIFrame* aFrame, + PRUint32 aFlags = 0); /** * Get the font metrics corresponding to the frame's style data. diff --git a/layout/generic/nsBulletFrame.cpp b/layout/generic/nsBulletFrame.cpp index a858a37240f..a8ab52dfcfb 100644 --- a/layout/generic/nsBulletFrame.cpp +++ b/layout/generic/nsBulletFrame.cpp @@ -197,6 +197,10 @@ public: } #endif + virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) + { + return mFrame->GetVisualOverflowRect() + ToReferenceFrame(); + } virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, HitTestState* aState, nsTArray *aOutFrames) { aOutFrames->AppendElement(mFrame); @@ -205,7 +209,10 @@ public: nsIRenderingContext* aCtx); NS_DISPLAY_DECL_NAME("Bullet", TYPE_BULLET) - virtual PRBool HasText() { return PR_TRUE; } + virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) + { + return GetBounds(aBuilder); + } }; void nsDisplayBullet::Paint(nsDisplayListBuilder* aBuilder, diff --git a/layout/generic/nsCanvasFrame.h b/layout/generic/nsCanvasFrame.h index 12f55cc16ff..2674a76b0d0 100644 --- a/layout/generic/nsCanvasFrame.h +++ b/layout/generic/nsCanvasFrame.h @@ -177,14 +177,15 @@ public: return NS_GET_A(mExtraBackgroundColor) > 0 || nsDisplayBackground::ComputeVisibility(aBuilder, aVisibleRegion); } - virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface = nsnull) + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface = nsnull) { if (aForceTransparentSurface) { *aForceTransparentSurface = PR_FALSE; } - return NS_GET_A(mExtraBackgroundColor) == 255 || - nsDisplayBackground::IsOpaque(aBuilder); + if (NS_GET_A(mExtraBackgroundColor) == 255) + return nsRegion(GetBounds(aBuilder)); + return nsDisplayBackground::GetOpaqueRegion(aBuilder); } virtual PRBool IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) { diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index d906cb8525a..a509f086436 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -1312,8 +1312,7 @@ public: virtual void NotifyExpired(nsGfxScrollFrameInner *aObject) { RemoveObject(aObject); - aObject->mScrollingActive = PR_FALSE; - aObject->mOuter->InvalidateFrameSubtree(); + aObject->MarkInactive(); } }; @@ -1347,8 +1346,7 @@ nsGfxScrollFrameInner::nsGfxScrollFrameInner(nsContainerFrame* aOuter, mVerticalOverflow(PR_FALSE), mPostedReflowCallback(PR_FALSE), mMayHaveDirtyFixedChildren(PR_FALSE), - mUpdateScrollbarAttributes(PR_FALSE), - mScrollingActive(PR_FALSE) + mUpdateScrollbarAttributes(PR_FALSE) { // lookup if we're allowed to overlap the content from the look&feel object PRBool canOverlap; @@ -1356,6 +1354,7 @@ nsGfxScrollFrameInner::nsGfxScrollFrameInner(nsContainerFrame* aOuter, presContext->LookAndFeel()-> GetMetric(nsILookAndFeel::eMetric_ScrollbarsCanOverlapContent, canOverlap); mScrollbarsCanOverlapContent = canOverlap; + mScrollingActive = IsAlwaysActive(); } nsGfxScrollFrameInner::~nsGfxScrollFrameInner() @@ -1530,7 +1529,7 @@ static void AdjustViews(nsIFrame* aFrame) } static PRBool -CanScrollWithBlitting(nsIFrame* aFrame, nsIFrame* aDisplayRoot) +CanScrollWithBlitting(nsIFrame* aFrame) { for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) { @@ -1543,7 +1542,10 @@ CanScrollWithBlitting(nsIFrame* aFrame, nsIFrame* aDisplayRoot) return PR_FALSE; } #endif - if (f == aDisplayRoot) + nsIScrollableFrame* sf = do_QueryFrame(f); + if (sf && nsLayoutUtils::HasNonZeroCorner(f->GetStyleBorder()->mBorderRadius)) + return PR_FALSE; + if (nsLayoutUtils::IsPopup(f)) break; } return PR_TRUE; @@ -1609,9 +1611,13 @@ PRBool nsGfxScrollFrameInner::IsAlwaysActive() const return mIsRoot && mOuter->PresContext()->IsRootContentDocument(); } -PRBool nsGfxScrollFrameInner::IsScrollingActive() const +void nsGfxScrollFrameInner::MarkInactive() { - return mScrollingActive || IsAlwaysActive(); + if (IsAlwaysActive() || !mScrollingActive) + return; + + mScrollingActive = PR_FALSE; + mOuter->InvalidateFrameSubtree(); } void nsGfxScrollFrameInner::MarkActive() @@ -1643,14 +1649,21 @@ void nsGfxScrollFrameInner::ScrollVisual() // We need to call this after fixing up the view positions // to be consistent with the frame hierarchy. PRUint32 flags = nsIFrame::INVALIDATE_REASON_SCROLL_REPAINT; - nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(mOuter); - if (IsScrollingActive() && CanScrollWithBlitting(mOuter, displayRoot)) { - flags |= nsIFrame::INVALIDATE_NO_THEBES_LAYERS; + PRBool canScrollWithBlitting = CanScrollWithBlitting(mOuter); + if (IsScrollingActive()) { + if (!canScrollWithBlitting) { + MarkInactive(); + } else { + flags |= nsIFrame::INVALIDATE_NO_THEBES_LAYERS; + } + } + if (canScrollWithBlitting) { + MarkActive(); } - MarkActive(); mOuter->InvalidateWithFlags(mScrollPort, flags); if (flags & nsIFrame::INVALIDATE_NO_THEBES_LAYERS) { + nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(mOuter); nsRect update = GetScrollPortRect() + mOuter->GetOffsetToCrossDoc(displayRoot); update = update.ConvertAppUnitsRoundOut( @@ -1782,6 +1795,9 @@ nsGfxScrollFrameInner::BuildDisplayList(nsDisplayListBuilder* aBuilder, if (aBuilder->IsPaintingToWindow()) { mScrollPosAtLastPaint = GetScrollPosition(); + if (IsScrollingActive() && !CanScrollWithBlitting(mOuter)) { + MarkInactive(); + } } if (aBuilder->GetIgnoreScrollFrame() == mOuter) { diff --git a/layout/generic/nsGfxScrollFrame.h b/layout/generic/nsGfxScrollFrame.h index 2003bb4ce3c..e49bcb1aa9d 100644 --- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -223,7 +223,7 @@ public: nsMargin GetDesiredScrollbarSizes(nsBoxLayoutState* aState); PRBool IsLTR() const; PRBool IsScrollbarOnRight() const; - PRBool IsScrollingActive() const; + PRBool IsScrollingActive() const { return mScrollingActive; } // adjust the scrollbar rectangle aRect to account for any visible resizer. // aHasResizer specifies if there is a content resizer, however this method // will also check if a widget resizer is present as well. @@ -239,6 +239,7 @@ public: PRBool IsAlwaysActive() const; void MarkActive(); + void MarkInactive(); nsExpirationState* GetExpirationState() { return &mActivityExpirationState; } // owning references to the nsIAnonymousContentCreator-built content diff --git a/layout/generic/nsHTMLCanvasFrame.cpp b/layout/generic/nsHTMLCanvasFrame.cpp index cb4ae47213b..0fdba89b610 100644 --- a/layout/generic/nsHTMLCanvasFrame.cpp +++ b/layout/generic/nsHTMLCanvasFrame.cpp @@ -77,14 +77,18 @@ public: NS_DISPLAY_DECL_NAME("nsDisplayCanvas", TYPE_CANVAS) - virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface = nsnull) { + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface = nsnull) { if (aForceTransparentSurface) { *aForceTransparentSurface = PR_FALSE; } nsIFrame* f = GetUnderlyingFrame(); nsHTMLCanvasElement *canvas = CanvasElementFromContent(f->GetContent()); - return canvas->GetIsOpaque(); + nsRegion result; + if (canvas->GetIsOpaque()) { + result = GetBounds(aBuilder); + } + return result; } virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) { diff --git a/layout/generic/nsObjectFrame.cpp b/layout/generic/nsObjectFrame.cpp index 24902d5aebb..01fbdcbae26 100644 --- a/layout/generic/nsObjectFrame.cpp +++ b/layout/generic/nsObjectFrame.cpp @@ -1290,13 +1290,14 @@ nsDisplayPlugin::ComputeVisibility(nsDisplayListBuilder* aBuilder, return nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion); } -PRBool -nsDisplayPlugin::IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface) +nsRegion +nsDisplayPlugin::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface) { if (aForceTransparentSurface) { *aForceTransparentSurface = PR_FALSE; } + nsRegion result; nsObjectFrame* f = static_cast(mFrame); if (!aBuilder->IsForPluginGeometry()) { nsIWidget* widget = f->GetWidget(); @@ -1310,11 +1311,14 @@ nsDisplayPlugin::IsOpaque(nsDisplayListBuilder* aBuilder, // Something has clipped us unexpectedly. Perhaps there is a translucent // chrome element overlaying us that forced us to be clipped away. Treat // us as non-opaque since we may have holes. - return PR_FALSE; + return result; } } } - return f->IsOpaque(); + if (f->IsOpaque()) { + result = GetBounds(aBuilder); + } + return result; } void @@ -1469,7 +1473,7 @@ nsObjectFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, nsDisplayList replacedContent; - if (mInstanceOwner && mInstanceOwner->UseLayers()) { + if (aBuilder->IsForPainting() && mInstanceOwner && mInstanceOwner->UseLayers()) { NPWindow* window = nsnull; mInstanceOwner->GetWindow(window); PRBool isVisible = window && window->width > 0 && window->height > 0; @@ -2523,11 +2527,9 @@ DoStopPlugin(nsPluginInstanceOwner *aInstanceOwner, PRBool aDelayedStop) aInstanceOwner->HidePluginWindow(); #endif - inst->Stop(); - nsCOMPtr pluginHost = do_GetService(MOZ_PLUGIN_HOST_CONTRACTID); - if (pluginHost) - pluginHost->StopPluginInstance(inst); + NS_ASSERTION(pluginHost, "Without a pluginHost, how can we have an instance to destroy?"); + pluginHost->StopPluginInstance(inst); // the frame is going away along with its widget so tell the // window to forget its widget too diff --git a/layout/generic/nsObjectFrame.h b/layout/generic/nsObjectFrame.h index cc84f43cf8c..bf92e1c7053 100644 --- a/layout/generic/nsObjectFrame.h +++ b/layout/generic/nsObjectFrame.h @@ -316,8 +316,8 @@ public: #endif virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder); - virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface = nsnull); + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface = nsnull); virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx); virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, diff --git a/layout/generic/nsTextFrameThebes.cpp b/layout/generic/nsTextFrameThebes.cpp index 57349f9c1b9..78deab9432d 100644 --- a/layout/generic/nsTextFrameThebes.cpp +++ b/layout/generic/nsTextFrameThebes.cpp @@ -4039,7 +4039,8 @@ nsTextFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) class nsDisplayText : public nsDisplayItem { public: nsDisplayText(nsDisplayListBuilder* aBuilder, nsTextFrame* aFrame) : - nsDisplayItem(aBuilder, aFrame) { + nsDisplayItem(aBuilder, aFrame), + mDisableSubpixelAA(PR_FALSE) { MOZ_COUNT_CTOR(nsDisplayText); } #ifdef NS_BUILD_REFCNT_LOGGING @@ -4061,7 +4062,14 @@ public: nsIRenderingContext* aCtx); NS_DISPLAY_DECL_NAME("Text", TYPE_TEXT) - virtual PRBool HasText() { return PR_TRUE; } + virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) + { + return GetBounds(aBuilder); + } + + virtual void DisableComponentAlpha() { mDisableSubpixelAA = PR_TRUE; } + + PRPackedBool mDisableSubpixelAA; }; void @@ -4073,8 +4081,11 @@ nsDisplayText::Paint(nsDisplayListBuilder* aBuilder, nsRect extraVisible = mVisibleRect; nscoord appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel(); extraVisible.Inflate(appUnitsPerDevPixel, appUnitsPerDevPixel); - static_cast(mFrame)-> - PaintText(aCtx, ToReferenceFrame(), extraVisible); + nsTextFrame* f = static_cast(mFrame); + + gfxContextAutoDisableSubpixelAntialiasing disable(aCtx->ThebesContext(), + mDisableSubpixelAA); + f->PaintText(aCtx, ToReferenceFrame(), extraVisible); } NS_IMETHODIMP diff --git a/layout/generic/nsVideoFrame.cpp b/layout/generic/nsVideoFrame.cpp index f9d50484b38..062bfb320b3 100644 --- a/layout/generic/nsVideoFrame.cpp +++ b/layout/generic/nsVideoFrame.cpp @@ -375,10 +375,10 @@ public: NS_DISPLAY_DECL_NAME("Video", TYPE_VIDEO) - // It would be great if we could override IsOpaque to return false here, + // It would be great if we could override GetOpaqueRegion to return nonempty here, // but it's probably not safe to do so in general. Video frames are // updated asynchronously from decoder threads, and it's possible that - // we might have an opaque video frame when IsOpaque is called, but + // we might have an opaque video frame when GetOpaqueRegion is called, but // when we come to paint, the video frame is transparent or has gone // away completely (e.g. because of a decoder error). The problem would // be especially acute if we have off-main-thread rendering. diff --git a/layout/mathml/nsMathMLChar.cpp b/layout/mathml/nsMathMLChar.cpp index a7590acceab..59a74b95238 100644 --- a/layout/mathml/nsMathMLChar.cpp +++ b/layout/mathml/nsMathMLChar.cpp @@ -2016,7 +2016,10 @@ public: NS_DISPLAY_DECL_NAME("MathMLCharForeground", TYPE_MATHML_CHAR_FOREGROUND) - virtual PRBool HasText() { return PR_TRUE; } + virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) + { + return GetBounds(aBuilder); + } private: nsMathMLChar* mChar; diff --git a/layout/reftests/bidi/bidiSVG-01-ref.svg b/layout/reftests/bidi/bidiSVG-01-ref.svg new file mode 100644 index 00000000000..c84d5374c48 --- /dev/null +++ b/layout/reftests/bidi/bidiSVG-01-ref.svg @@ -0,0 +1,3 @@ + + Bidi in SVG + diff --git a/layout/reftests/bidi/bidiSVG-01.svg b/layout/reftests/bidi/bidiSVG-01.svg new file mode 100644 index 00000000000..c7abbf14754 --- /dev/null +++ b/layout/reftests/bidi/bidiSVG-01.svg @@ -0,0 +1,3 @@ + + ‮GVS ni idiB‬ + diff --git a/layout/reftests/bidi/bidiSVG-02-ref.svg b/layout/reftests/bidi/bidiSVG-02-ref.svg new file mode 100644 index 00000000000..ea11dc429eb --- /dev/null +++ b/layout/reftests/bidi/bidiSVG-02-ref.svg @@ -0,0 +1,3 @@ + + ‭left-to-right text תירבע םע‬ + diff --git a/layout/reftests/bidi/bidiSVG-02.svg b/layout/reftests/bidi/bidiSVG-02.svg new file mode 100644 index 00000000000..33372f1ad83 --- /dev/null +++ b/layout/reftests/bidi/bidiSVG-02.svg @@ -0,0 +1,3 @@ + + left-to-right text עם עברית + diff --git a/layout/reftests/bidi/bidiSVG-03-ref.svg b/layout/reftests/bidi/bidiSVG-03-ref.svg new file mode 100644 index 00000000000..b11f37079e0 --- /dev/null +++ b/layout/reftests/bidi/bidiSVG-03-ref.svg @@ -0,0 +1,3 @@ + + in right-to-left element תירבע םע left-to-right text + diff --git a/layout/reftests/bidi/bidiSVG-03.svg b/layout/reftests/bidi/bidiSVG-03.svg new file mode 100644 index 00000000000..355bc5ac75b --- /dev/null +++ b/layout/reftests/bidi/bidiSVG-03.svg @@ -0,0 +1,3 @@ + + left-to-right text עם עברית in right-to-left element + diff --git a/layout/reftests/bidi/bidiSVG-04-ref.svg b/layout/reftests/bidi/bidiSVG-04-ref.svg new file mode 100644 index 00000000000..368ea26d2f9 --- /dev/null +++ b/layout/reftests/bidi/bidiSVG-04-ref.svg @@ -0,0 +1,3 @@ + + ب>ا + diff --git a/layout/reftests/bidi/bidiSVG-04.svg b/layout/reftests/bidi/bidiSVG-04.svg new file mode 100644 index 00000000000..ff5c65ad85d --- /dev/null +++ b/layout/reftests/bidi/bidiSVG-04.svg @@ -0,0 +1,3 @@ + + ا<ب + diff --git a/layout/reftests/bidi/bidiSVG-05-ref.svg b/layout/reftests/bidi/bidiSVG-05-ref.svg new file mode 100644 index 00000000000..29d5707b4ed --- /dev/null +++ b/layout/reftests/bidi/bidiSVG-05-ref.svg @@ -0,0 +1,3 @@ + + 𐤌𐤋𐤔𐤅𐤓𐤉 + diff --git a/layout/reftests/bidi/bidiSVG-05.svg b/layout/reftests/bidi/bidiSVG-05.svg new file mode 100644 index 00000000000..d8e31574728 --- /dev/null +++ b/layout/reftests/bidi/bidiSVG-05.svg @@ -0,0 +1,3 @@ + + 𐤉𐤓𐤅𐤔𐤋𐤌 + diff --git a/layout/reftests/bidi/reftest.list b/layout/reftests/bidi/reftest.list index e728f2e3d6b..c3ded4ec9a6 100644 --- a/layout/reftests/bidi/reftest.list +++ b/layout/reftests/bidi/reftest.list @@ -9,6 +9,11 @@ == bidi-005.html bidi-005-ref.html == bidi-006.html bidi-006-ref.html == bidi-006-j.html bidi-006-ref.html +== bidiSVG-01.svg bidiSVG-01-ref.svg +== bidiSVG-02.svg bidiSVG-02-ref.svg +== bidiSVG-03.svg bidiSVG-03-ref.svg +== bidiSVG-04.svg bidiSVG-04-ref.svg +== bidiSVG-05.svg bidiSVG-05-ref.svg random-if(layersGPUAccelerated) == visualmarquee.html marquee-ref.html random-if(layersGPUAccelerated) == logicalmarquee.html marquee-ref.html == visualmarquee.html logicalmarquee.html diff --git a/layout/reftests/border-radius/reftest.list b/layout/reftests/border-radius/reftest.list index 7e59dc18e02..07b27bbd952 100644 --- a/layout/reftests/border-radius/reftest.list +++ b/layout/reftests/border-radius/reftest.list @@ -73,3 +73,5 @@ fails-if(cocoaWidget) == intersecting-clipping-1-refc.html intersecting-clipping # Test for bad corner joins. == corner-joins-1.xhtml corner-joins-1-ref.xhtml random-if(winWidget) HTTP(..) == corner-joins-2.xhtml corner-joins-2-ref.xhtml + +random == scroll-1.html scroll-1-ref.html # see bug 602892 diff --git a/layout/reftests/border-radius/scroll-1-ref.html b/layout/reftests/border-radius/scroll-1-ref.html new file mode 100644 index 00000000000..c9f2ea45d36 --- /dev/null +++ b/layout/reftests/border-radius/scroll-1-ref.html @@ -0,0 +1,8 @@ + + + +
+
+
+ + diff --git a/layout/reftests/border-radius/scroll-1.html b/layout/reftests/border-radius/scroll-1.html new file mode 100644 index 00000000000..bcfeaa270a1 --- /dev/null +++ b/layout/reftests/border-radius/scroll-1.html @@ -0,0 +1,23 @@ + + + + + + +
+
+
+
+
+ + diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 75a40d38090..eafe0b8297b 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -4359,6 +4359,18 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue, } } } + // Check VARIANT_NUMBER and VARIANT_INTEGER before VARIANT_LENGTH or + // VARIANT_ZERO_ANGLE. + if (((aVariantMask & VARIANT_NUMBER) != 0) && + (eCSSToken_Number == tk->mType)) { + aValue.SetFloatValue(tk->mNumber, eCSSUnit_Number); + return PR_TRUE; + } + if (((aVariantMask & VARIANT_INTEGER) != 0) && + (eCSSToken_Number == tk->mType) && tk->mIntegerValid) { + aValue.SetIntValue(tk->mInteger, eCSSUnit_Integer); + return PR_TRUE; + } if (((aVariantMask & (VARIANT_LENGTH | VARIANT_ANGLE | VARIANT_FREQUENCY | VARIANT_TIME)) != 0 && eCSSToken_Dimension == tk->mType) || @@ -4377,16 +4389,6 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue, aValue.SetPercentValue(tk->mNumber); return PR_TRUE; } - if (((aVariantMask & VARIANT_NUMBER) != 0) && - (eCSSToken_Number == tk->mType)) { - aValue.SetFloatValue(tk->mNumber, eCSSUnit_Number); - return PR_TRUE; - } - if (((aVariantMask & VARIANT_INTEGER) != 0) && - (eCSSToken_Number == tk->mType) && tk->mIntegerValid) { - aValue.SetIntValue(tk->mInteger, eCSSUnit_Integer); - return PR_TRUE; - } if (mNavQuirkMode && !IsParsingCompoundProperty()) { // NONSTANDARD: Nav interprets unitless numbers as px if (((aVariantMask & VARIANT_LENGTH) != 0) && (eCSSToken_Number == tk->mType)) { @@ -7131,7 +7133,13 @@ CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, PRInt32& aVariantMask) } // ... or just a value UngetToken(); - if (!ParseVariant(aValue, aVariantMask, nsnull)) { + // Always pass VARIANT_NUMBER to ParseVariant so that unitless zero + // always gets picked up + if (!ParseVariant(aValue, aVariantMask | VARIANT_NUMBER, nsnull)) { + return PR_FALSE; + } + // ...and do the VARIANT_NUMBER check ourselves. + if (!(aVariantMask & VARIANT_NUMBER) && aValue.GetUnit() == eCSSUnit_Number) { return PR_FALSE; } // If we did the value parsing, we need to adjust aVariantMask to diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index f8c6de7a639..bd73f8bd12f 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -1373,6 +1373,16 @@ nsStyleGradient::nsStyleGradient(void) { } +PRBool +nsStyleGradient::IsOpaque() +{ + for (PRUint32 i = 0; i < mStops.Length(); i++) { + if (NS_GET_A(mStops[i].mColor) < 255) + return PR_FALSE; + } + return PR_TRUE; +} + // -------------------- // nsStyleImage // @@ -1608,10 +1618,8 @@ nsStyleImage::IsOpaque() const if (!IsComplete()) return PR_FALSE; - if (mType == eStyleImageType_Gradient) { - // We could check if every stop color of the gradient is non-transparent. - return PR_FALSE; - } + if (mType == eStyleImageType_Gradient) + return mGradient->IsOpaque(); if (mType == eStyleImageType_Element) return PR_FALSE; diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 07ddf871882..a050c5e7c30 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -173,6 +173,8 @@ public: return !(*this == aOther); }; + PRBool IsOpaque(); + NS_INLINE_DECL_REFCOUNTING(nsStyleGradient) private: @@ -278,7 +280,7 @@ struct nsStyleImage { // rect is non-trivial since each side value can be specified with // percentage unit, which can not be evaluated until the source image size // is available. Therefore, we currently postpone the evaluation of crop - // rect until the actual rendering time --- alternatively until IsOpaque() + // rect until the actual rendering time --- alternatively until GetOpaqueRegion() // is called. return mType == eStyleImageType_Null; } diff --git a/layout/style/nsTransitionManager.cpp b/layout/style/nsTransitionManager.cpp index 2bae1036cb5..de0a9412b7c 100644 --- a/layout/style/nsTransitionManager.cpp +++ b/layout/style/nsTransitionManager.cpp @@ -122,9 +122,9 @@ ElementPropertyTransition::ValuePortionFor(TimeStamp aRefreshTime) const // When duration is zero, we can still have a transition when delay // is nonzero. mStartTime already incorporates delay. if (aRefreshTime >= mStartTime) { - timePortion = 0.0; - } else { timePortion = 1.0; + } else { + timePortion = 0.0; } } else { timePortion = (aRefreshTime - mStartTime).ToSeconds() / duration; diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index 18c041ad546..8ca9c5403a1 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -146,7 +146,7 @@ var gCSSProperties = { "-moz-calc(2px)", "-moz-calc(-2px)", "-moz-calc(0em)", - "-moz-calc(0)", + "-moz-calc(0px)", "-moz-calc(5em)", "-moz-calc(3*25px)", "-moz-calc(25px*3)", @@ -324,7 +324,7 @@ var gCSSProperties = { "-moz-calc(2px)", "-moz-calc(-2px)", "-moz-calc(0em)", - "-moz-calc(0)", + "-moz-calc(0px)", "-moz-calc(5em)", "-moz-calc(3*25px)", "-moz-calc(25px*3)", @@ -413,7 +413,7 @@ var gCSSProperties = { other_values: [ "2px", "4em", "-moz-calc(2px)", "-moz-calc(-2px)", - "-moz-calc(0)", + "-moz-calc(0px)", "-moz-calc(0pt)", "-moz-calc(5em)", "-moz-calc(3*25px)", @@ -529,6 +529,22 @@ var gCSSProperties = { "-moz-calc(2em / (4 / 3))", "-moz-calc(4 * (2em / 3))", + // Valid cases with unitless zero (which is never + // a length). + "-moz-calc(0 * 2em)", + "-moz-calc(2em * 0)", + "-moz-calc(3em + 0 * 2em)", + "-moz-calc(3em + 2em * 0)", + "-moz-calc((0 + 2) * 2em)", + "-moz-calc((2 + 0) * 2em)", + // And test zero lengths while we're here. + "-moz-calc(2 * 0px)", + "-moz-calc(0 * 0px)", + "-moz-calc(2 * 0em)", + "-moz-calc(0 * 0em)", + "-moz-calc(0px * 0)", + "-moz-calc(0px * 2)", + ], invalid_values: [ "20", "-1px", "red", "50%", /* invalid calc() values */ @@ -560,6 +576,20 @@ var gCSSProperties = { "-moz-calc((4 * 3) / 2em)", "-moz-calc(4 * (3 / 2em))", "-moz-calc(4 / (3 * 2em))", + + // Tests for handling of unitless zero, which cannot + // be a length inside calc(). + "-moz-calc(0)", + "-moz-calc(0 + 2em)", + "-moz-calc(2em + 0)", + "-moz-calc(0 * 2)", + "-moz-calc(2 * 0)", + "-moz-calc(1 * (2em + 0))", + "-moz-calc((2em + 0))", + "-moz-calc((2em + 0) * 1)", + "-moz-calc(1 * (0 + 2em))", + "-moz-calc((0 + 2em))", + "-moz-calc((0 + 2em) * 1)", ] }, "-moz-column-rule-style": { @@ -1354,7 +1384,7 @@ var gCSSProperties = { "-moz-calc(2px)", "-moz-calc(-2px)", "-moz-calc(0em)", - "-moz-calc(0)", + "-moz-calc(0px)", "-moz-calc(5em)", "-moz-calc(3*25px)", "-moz-calc(25px*3)", @@ -1416,7 +1446,7 @@ var gCSSProperties = { "-moz-calc(2px)", "-moz-calc(-2px)", "-moz-calc(0em)", - "-moz-calc(0)", + "-moz-calc(0px)", "-moz-calc(5em)", "-moz-calc(3*25px)", "-moz-calc(25px*3)", @@ -1461,7 +1491,7 @@ var gCSSProperties = { "-moz-calc(2px)", "-moz-calc(-2px)", "-moz-calc(0em)", - "-moz-calc(0)", + "-moz-calc(0px)", "-moz-calc(5em)", "-moz-calc(3*25px)", "-moz-calc(25px*3)", @@ -1524,7 +1554,7 @@ var gCSSProperties = { "-moz-calc(2px)", "-moz-calc(-2px)", "-moz-calc(0em)", - "-moz-calc(0)", + "-moz-calc(0px)", "-moz-calc(5em)", "-moz-calc(3*25px)", "-moz-calc(25px*3)", @@ -2039,7 +2069,7 @@ var gCSSProperties = { other_values: [ "30px", "50%", "0", "-moz-calc(2px)", "-moz-calc(-2px)", - "-moz-calc(0)", + "-moz-calc(0px)", "-moz-calc(50%)", "-moz-calc(3*25px)", "-moz-calc(25px*3)", @@ -2056,7 +2086,7 @@ var gCSSProperties = { other_values: [ "30px", "50%", "0", "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available", "-moz-calc(2px)", "-moz-calc(-2px)", - "-moz-calc(0)", + "-moz-calc(0px)", "-moz-calc(50%)", "-moz-calc(3*25px)", "-moz-calc(25px*3)", @@ -2161,7 +2191,7 @@ var gCSSProperties = { other_values: [ "thin", "thick", "1px", "2em", "-moz-calc(2px)", "-moz-calc(-2px)", - "-moz-calc(0)", + "-moz-calc(0px)", "-moz-calc(0px)", "-moz-calc(5em)", "-moz-calc(3*25px)", @@ -2203,7 +2233,7 @@ var gCSSProperties = { inherited: false, type: CSS_TYPE_TRUE_SHORTHAND, subproperties: [ "padding-top", "padding-right", "padding-bottom", "padding-left" ], - initial_values: [ "0", "0px 0 0em", "0% 0px 0em 0pt", "-moz-calc(0) -moz-calc(0em) -moz-calc(-2px) -moz-calc(-1%)" ], + initial_values: [ "0", "0px 0 0em", "0% 0px 0em 0pt", "-moz-calc(0px) -moz-calc(0em) -moz-calc(-2px) -moz-calc(-1%)" ], other_values: [ "3px 0", "2em 4px 2pt", "1em 2em 3px 4px" ], invalid_values: [] }, diff --git a/layout/style/test/test_transitions.html b/layout/style/test/test_transitions.html index 5bb5476d47f..af76f2b811f 100644 --- a/layout/style/test/test_transitions.html +++ b/layout/style/test/test_transitions.html @@ -228,6 +228,22 @@ for (var d = -4; d <= 4; ++d) { delay_tests[d] = p; } +// Test transition-delay values of -4s through 4s on a 4s transition +// with duration of zero. +var delay_zero_tests = {}; +for (var d = -4; d <= 4; ++d) { + var p = document.createElement("p"); + var delay = d + "s"; + var t = document.createTextNode("transition-delay: " + delay); + p.appendChild(t); + p.style.marginLeft = "0px"; + p.style.MozTransition = "0s margin-left linear " + delay; + div.appendChild(p); + is(getComputedStyle(p, "").marginLeft, "0px", + "should be zero before changing value"); + delay_zero_tests[d] = p; +} + // Test that changing the value on an already-running transition to the // value it currently happens to have resets the transition. function make_reset_test(transition, description) @@ -406,6 +422,10 @@ for (var d in delay_tests) { var p = delay_tests[d]; p.style.marginLeft = "100px"; } +for (var d in delay_zero_tests) { + var p = delay_zero_tests[d]; + p.style.marginLeft = "100px"; +} reset_test.style.marginLeft = "100px"; reset_test_reference.style.marginLeft = "100px"; for (var i in descendant_tests) { @@ -585,6 +605,29 @@ for (var i = 1; i <= 8; ++i) { add_future_call(i, check_delay_test); } +function check_delay_zero_test(time) +{ + for (var d in delay_zero_tests) { + var p = delay_zero_tests[d]; + + time_range = [ px_to_num(earlyrefcs.textIndent) / 125, + px_to_num(laterefcs.textIndent) / 125 ]; + var m = getComputedStyle(p, "").marginLeft; + var desc = "delay_zero test for delay " + d + "s"; + if (time_range[0] < d && time_range[1] < d) { + is(m, "0px", desc); + } else if ((time_range[0] > d && time_range[1] > d) || + (d == 0 && time == 0)) { + is(m, "100px", desc); + } + } +} + +check_delay_zero_test(0); +for (var i = 1; i <= 8; ++i) { + add_future_call(i, check_delay_zero_test); +} + function reset_reset_test(time) { reset_test.style.marginLeft = "0px"; diff --git a/layout/style/test/test_value_computation.html b/layout/style/test/test_value_computation.html index 55f379bd52c..e9c09ebacf7 100644 --- a/layout/style/test/test_value_computation.html +++ b/layout/style/test/test_value_computation.html @@ -43,6 +43,10 @@ var gBadComputed = { // These values are treated as auto. "page-break-after": [ "avoid" ], "page-break-before": [ "avoid" ], + + // This is the only SVG-length property (i.e., length allowing + // unitless lengths) whose initial value is zero. + "stroke-dashoffset": [ "0" ], }; var gBadComputedNoFrame = { @@ -68,7 +72,7 @@ var gBadComputedNoFrame = { "margin-top": [ "0%", "-moz-calc(0% + 0px)" ], "min-height": [ "-moz-calc(-1%)" ], "min-width": [ "-moz-calc(-1%)" ], - "padding": [ "0% 0px 0em 0pt", "-moz-calc(0) -moz-calc(0em) -moz-calc(-2px) -moz-calc(-1%)" ], + "padding": [ "0% 0px 0em 0pt", "-moz-calc(0px) -moz-calc(0em) -moz-calc(-2px) -moz-calc(-1%)" ], "padding-bottom": [ "0%", "-moz-calc(0% + 0px)", "-moz-calc(-1%)" ], "padding-left": [ "0%", "-moz-calc(0% + 0px)", "-moz-calc(-1%)" ], "padding-right": [ "0%", "-moz-calc(0% + 0px)", "-moz-calc(-1%)" ], diff --git a/layout/svg/base/src/nsSVGGlyphFrame.cpp b/layout/svg/base/src/nsSVGGlyphFrame.cpp index 88fc2207d48..2bde6669303 100644 --- a/layout/svg/base/src/nsSVGGlyphFrame.cpp +++ b/layout/svg/base/src/nsSVGGlyphFrame.cpp @@ -39,6 +39,7 @@ #include "nsSVGTextFrame.h" #include "nsILookAndFeel.h" #include "nsTextFragment.h" +#include "nsBidiPresUtils.h" #include "nsSVGUtils.h" #include "SVGLengthList.h" #include "nsIDOMSVGLength.h" @@ -1548,6 +1549,50 @@ nsSVGGlyphFrame::EnsureTextRun(float *aDrawScale, float *aMetricsScale, if (!GetCharacterData(text)) return PR_FALSE; + nsBidiPresUtils* bidiUtils = presContext->GetBidiUtils(); + if (bidiUtils) { + nsAutoString visualText; + + /* + * XXXsmontagu: The SVG spec says: + * + * http://www.w3.org/TR/SVG11/text.html#DirectionProperty + * "For the 'direction' property to have any effect, the 'unicode-bidi' + * property's value must be embed or bidi-override." + * + * The SVGTiny spec, on the other hand, says + * + * http://www.w3.org/TR/SVGTiny12/text.html#DirectionProperty + * "For the 'direction' property to have any effect on an element that + * does not by itself establish a new text chunk (such as the 'tspan' + * element in SVG 1.2 Tiny), the 'unicode-bidi' property's value must + * be embed or bidi-override." + * + * Note that this is different from HTML/CSS, where setting the 'dir' + * attribute on an inline element automatically sets unicode-bidi: embed + * + * Our current implementation of bidi in SVG does not distinguish between + * different text elements, but treats every text container frame as a + * new text chunk, so we always set the base direction according to the + * direction property + * + * See also XXXsmontagu comments in nsSVGTextFrame::UpdateGlyphPositioning + */ + + // Get the unicodeBidi property from the parent, because it doesn't + // inherit + PRBool bidiOverride = (mParent->GetStyleTextReset()->mUnicodeBidi == + NS_STYLE_UNICODE_BIDI_OVERRIDE); + nsBidiLevel baseDirection = + GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL ? + NSBIDI_RTL : NSBIDI_LTR; + bidiUtils->CopyLogicalToVisual(text, visualText, + baseDirection, bidiOverride); + if (!visualText.IsEmpty()) { + text = visualText; + } + } + gfxMatrix m; if (aForceGlobalTransform || !(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) { diff --git a/layout/svg/base/src/nsSVGTextFrame.cpp b/layout/svg/base/src/nsSVGTextFrame.cpp index ce73a1f8c1b..7cc6bca2b98 100644 --- a/layout/svg/base/src/nsSVGTextFrame.cpp +++ b/layout/svg/base/src/nsSVGTextFrame.cpp @@ -341,6 +341,39 @@ nsSVGTextFrame::UpdateGlyphPositioning(PRBool aForceGlobalTransform) PRUint8 anchor = firstFragment->GetTextAnchor(); + /** + * XXXsmontagu: The SVG spec is very vague as to how 'text-anchor' + * interacts with bidirectional text. It says: + * + * "For scripts that are inherently right to left such as Hebrew and + * Arabic [text-anchor: start] is equivalent to right alignment." + * and + * "For scripts that are inherently right to left such as Hebrew and + * Arabic, [text-anchor: end] is equivalent to left alignment. + * + * It's not clear how this should be implemented in terms of defined + * properties, i.e. how one should determine that a particular element + * contains a script that is inherently right to left. + * + * The code below follows http://www.w3.org/TR/SVGTiny12/text.html#TextAnchorProperty + * and swaps the values of text-anchor: end and text-anchor: start + * whenever the 'direction' property is rtl. + * + * This is probably the "right" thing to do, but other browsers don't do it, + * so I am leaving it inside #if 0 for now for interoperability. + * + * See also XXXsmontagu comments in nsSVGGlyphFrame::EnsureTextRun + */ +#if 0 + if (GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { + if (anchor == NS_STYLE_TEXT_ANCHOR_END) { + anchor = NS_STYLE_TEXT_ANCHOR_START; + } else if (anchor == NS_STYLE_TEXT_ANCHOR_START) { + anchor = NS_STYLE_TEXT_ANCHOR_END; + } + } +#endif + float chunkLength = 0.0f; if (anchor != NS_STYLE_TEXT_ANCHOR_START) { // need to get the total chunk length diff --git a/layout/svg/crashtests/610594-1.html b/layout/svg/crashtests/610594-1.html new file mode 100644 index 00000000000..ee48e762ccb --- /dev/null +++ b/layout/svg/crashtests/610594-1.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/layout/svg/crashtests/620034-1.html b/layout/svg/crashtests/620034-1.html new file mode 100644 index 00000000000..bfffd3ffac5 --- /dev/null +++ b/layout/svg/crashtests/620034-1.html @@ -0,0 +1,15 @@ + + diff --git a/layout/svg/crashtests/crashtests.list b/layout/svg/crashtests/crashtests.list index c894598021e..c171e2a28e3 100644 --- a/layout/svg/crashtests/crashtests.list +++ b/layout/svg/crashtests/crashtests.list @@ -97,7 +97,10 @@ load 587336-1.html load 590291-1.svg load 601999-1.html load 605626-1.svg +load 610594-1.html load 610954-1.html load 612662-1.svg load 612662-2.svg +load 620034-1.html + load 621598-1.svg diff --git a/layout/tools/layout-debug/Makefile.in b/layout/tools/layout-debug/Makefile.in index 05d9547aeb5..0584926df88 100644 --- a/layout/tools/layout-debug/Makefile.in +++ b/layout/tools/layout-debug/Makefile.in @@ -46,17 +46,4 @@ include $(DEPTH)/config/autoconf.mk MODULE = layout_debug DIRS = src ui tests -# If we have a XULRunner-enabled tree, then build as a standalone XUL app. -ifdef MOZ_XULRUNNER - -export XPI_NAME = layoutdebug -PREF_JS_EXPORTS = $(srcdir)/layoutdebug-prefs.js -DIST_FILES = application.ini -DIST_CHROME_FILES = chrome.manifest - -BUILD_ID = $(shell $(PYTHON) $(topsrcdir)/config/printconfigsetting.py $(LIBXUL_DIST)/bin/platform.ini Build BuildID) -DEFINES += -DBUILD_ID=$(BUILD_ID) - -endif # MOZ_XULRUNNER - include $(topsrcdir)/config/rules.mk diff --git a/layout/tools/reftest/reftest.js b/layout/tools/reftest/reftest.js index c74a3aa3721..201dc595412 100644 --- a/layout/tools/reftest/reftest.js +++ b/layout/tools/reftest/reftest.js @@ -949,8 +949,7 @@ function shouldWaitForPendingPaints() { return gCurrentCanvas && gWindowUtils.isMozAfterPaintPending; } -function shouldWaitForReftestWaitRemoval() { - var contentRootElement = gBrowser.contentDocument.documentElement; +function shouldWaitForReftestWaitRemoval(contentRootElement) { // use getAttribute because className works differently in HTML and SVG return contentRootElement && contentRootElement.hasAttribute('class') && @@ -971,10 +970,9 @@ const STATE_WAITING_TO_FINISH = 2; const STATE_COMPLETED = 3; function WaitForTestEnd() { - var contentRootElement = gBrowser.contentDocument.documentElement; - - var stopAfterPaintReceived = false; var currentDoc = gBrowser.contentDocument; + var contentRootElement = currentDoc ? currentDoc.documentElement : null; + var stopAfterPaintReceived = false; var state = STATE_WAITING_TO_FIRE_INVALIDATE_EVENT; function FlushRendering() { @@ -1043,7 +1041,9 @@ function WaitForTestEnd() { function RemoveListeners() { // OK, we can end the test now. window.removeEventListener("MozAfterPaint", AfterPaintListener, false); - contentRootElement.removeEventListener("DOMAttrModified", AttrModifiedListener, false); + if (contentRootElement) { + contentRootElement.removeEventListener("DOMAttrModified", AttrModifiedListener, false); + } gExplicitPendingPaintsCompleteHook = null; gTimeoutHook = null; // Make sure we're in the COMPLETED state just in case @@ -1079,12 +1079,12 @@ function WaitForTestEnd() { } state = STATE_WAITING_FOR_REFTEST_WAIT_REMOVAL; - var hasReftestWait = shouldWaitForReftestWaitRemoval(); + var hasReftestWait = shouldWaitForReftestWaitRemoval(contentRootElement); // Notify the test document that now is a good time to test some invalidation var notification = document.createEvent("Events"); notification.initEvent("MozReftestInvalidate", true, false); contentRootElement.dispatchEvent(notification); - if (hasReftestWait && !shouldWaitForReftestWaitRemoval()) { + if (hasReftestWait && !shouldWaitForReftestWaitRemoval(contentRootElement)) { // MozReftestInvalidate handler removed reftest-wait. // We expect something to have been invalidated... FlushRendering(); @@ -1099,7 +1099,7 @@ function WaitForTestEnd() { case STATE_WAITING_FOR_REFTEST_WAIT_REMOVAL: LogInfo("MakeProgress: STATE_WAITING_FOR_REFTEST_WAIT_REMOVAL"); - if (shouldWaitForReftestWaitRemoval()) { + if (shouldWaitForReftestWaitRemoval(contentRootElement)) { gFailureReason = "timed out waiting for reftest-wait to be removed"; LogInfo("MakeProgress: waiting for reftest-wait to be removed"); return; @@ -1138,8 +1138,13 @@ function WaitForTestEnd() { } } + LogInfo("WaitForTestEnd: Adding listeners"); window.addEventListener("MozAfterPaint", AfterPaintListener, false); - contentRootElement.addEventListener("DOMAttrModified", AttrModifiedListener, false); + // If contentRootElement is null then shouldWaitForReftestWaitRemoval will + // always return false so we don't need a listener anyway + if (contentRootElement) { + contentRootElement.addEventListener("DOMAttrModified", AttrModifiedListener, false); + } gExplicitPendingPaintsCompleteHook = ExplicitPaintsCompleteListener; gTimeoutHook = RemoveListeners; @@ -1152,24 +1157,24 @@ function WaitForTestEnd() { function OnDocumentLoad(event) { - if (event.target != gBrowser.contentDocument) + var currentDoc = gBrowser.contentDocument; + if (event.target != currentDoc) // Ignore load events for subframes. return; if (gClearingForAssertionCheck && - gBrowser.contentDocument.location.href == BLANK_URL_FOR_CLEARING) { + currentDoc.location.href == BLANK_URL_FOR_CLEARING) { DoAssertionCheck(); return; } - if (gBrowser.contentDocument.location.href != gCurrentURL) { + if (currentDoc.location.href != gCurrentURL) { LogInfo("OnDocumentLoad fired for previous document"); // Ignore load events for previous documents. return; } - var contentRootElement = gBrowser.contentDocument.documentElement; - + var contentRootElement = currentDoc ? currentDoc.documentElement : null; setupZoom(contentRootElement); function AfterOnLoadScripts() { @@ -1193,7 +1198,8 @@ function OnDocumentLoad(event) } } - if (shouldWaitForReftestWaitRemoval() || shouldWaitForExplicitPaintWaiters()) { + if (shouldWaitForReftestWaitRemoval(contentRootElement) || + shouldWaitForExplicitPaintWaiters()) { // Go into reftest-wait mode immediately after painting has been // unsuppressed, after the onload event has finished dispatching. gFailureReason = "timed out waiting for test to complete (trying to get into WaitForTestEnd)"; diff --git a/layout/xul/base/public/nsIMenuBoxObject.idl b/layout/xul/base/public/nsIMenuBoxObject.idl index 1768427f3b9..599ed27f589 100644 --- a/layout/xul/base/public/nsIMenuBoxObject.idl +++ b/layout/xul/base/public/nsIMenuBoxObject.idl @@ -41,7 +41,7 @@ interface nsIDOMElement; interface nsIDOMKeyEvent; -[scriptable, uuid(F5099746-5049-4e81-A03E-945D5110FEE2)] +[scriptable, uuid(3931F141-D640-48AB-A792-719D62CF1736)] interface nsIMenuBoxObject : nsISupports { void openMenu(in boolean openFlag); @@ -49,6 +49,9 @@ interface nsIMenuBoxObject : nsISupports attribute nsIDOMElement activeChild; boolean handleKeyPress(in nsIDOMKeyEvent keyEvent); + + // true if the menu or menubar was opened via a keypress. + readonly attribute boolean openedWithKey; }; %{C++ diff --git a/layout/xul/base/src/nsMenuBarFrame.cpp b/layout/xul/base/src/nsMenuBarFrame.cpp index 2ce02a11026..02965e590a9 100644 --- a/layout/xul/base/src/nsMenuBarFrame.cpp +++ b/layout/xul/base/src/nsMenuBarFrame.cpp @@ -151,6 +151,7 @@ nsMenuBarFrame::SetActive(PRBool aActiveFlag) InstallKeyboardNavigator(); } else { + mActiveByKeyboard = PR_FALSE; RemoveKeyboardNavigator(); } diff --git a/layout/xul/base/src/nsMenuBarFrame.h b/layout/xul/base/src/nsMenuBarFrame.h index 54ac4e748e8..34e13169750 100644 --- a/layout/xul/base/src/nsMenuBarFrame.h +++ b/layout/xul/base/src/nsMenuBarFrame.h @@ -103,6 +103,9 @@ public: // needs to be closed. nsMenuFrame* ToggleMenuActiveState(); + PRBool IsActiveByKeyboard() { return mActiveByKeyboard; } + void SetActiveByKeyboard() { mActiveByKeyboard = PR_TRUE; } + // indicate that a menu on the menubar was closed. Returns true if the caller // may deselect the menuitem. virtual PRBool MenuClosed(); @@ -137,6 +140,10 @@ protected: PRPackedBool mStayActive; PRPackedBool mIsActive; // Whether or not the menu bar is active (a menu item is highlighted or shown). + + // whether the menubar was made active via the keyboard. + PRPackedBool mActiveByKeyboard; + // The current menu that is active (highlighted), which may not be open. This will // be null if no menu is active. nsMenuFrame* mCurrentMenu; diff --git a/layout/xul/base/src/nsMenuBarListener.cpp b/layout/xul/base/src/nsMenuBarListener.cpp index 3ef2e8af240..835ba2fdea3 100644 --- a/layout/xul/base/src/nsMenuBarListener.cpp +++ b/layout/xul/base/src/nsMenuBarListener.cpp @@ -179,6 +179,9 @@ nsMenuBarListener::KeyUp(nsIDOMEvent* aKeyEvent) { // The access key was down and is now up, and no other // keys were pressed in between. + if (!mMenuBarFrame->IsActive()) { + mMenuBarFrame->SetActiveByKeyboard(); + } ToggleMenuActiveState(); } mAccessKeyDown = PR_FALSE; @@ -257,6 +260,7 @@ nsMenuBarListener::KeyPress(nsIDOMEvent* aKeyEvent) // so, we'll know the menu got activated. nsMenuFrame* result = mMenuBarFrame->FindMenuWithShortcut(keyEvent); if (result) { + mMenuBarFrame->SetActiveByKeyboard(); mMenuBarFrame->SetActive(PR_TRUE); result->OpenMenu(PR_TRUE); @@ -275,6 +279,7 @@ nsMenuBarListener::KeyPress(nsIDOMEvent* aKeyEvent) if ((GetModifiers(keyEvent) & ~MODIFIER_CONTROL) == 0) { // The F10 key just went down by itself or with ctrl pressed. // In Windows, both of these activate the menu bar. + mMenuBarFrame->SetActiveByKeyboard(); ToggleMenuActiveState(); aKeyEvent->StopPropagation(); diff --git a/layout/xul/base/src/nsMenuBoxObject.cpp b/layout/xul/base/src/nsMenuBoxObject.cpp index d86dd0758af..12d2ececac9 100644 --- a/layout/xul/base/src/nsMenuBoxObject.cpp +++ b/layout/xul/base/src/nsMenuBoxObject.cpp @@ -41,6 +41,7 @@ #include "nsIFrame.h" #include "nsGUIEvent.h" #include "nsIDOMNSUIEvent.h" +#include "nsMenuBarFrame.h" #include "nsMenuBarListener.h" #include "nsMenuFrame.h" #include "nsMenuPopupFrame.h" @@ -157,6 +158,28 @@ NS_IMETHODIMP nsMenuBoxObject::HandleKeyPress(nsIDOMKeyEvent* aKeyEvent, PRBool* } } +NS_IMETHODIMP +nsMenuBoxObject::GetOpenedWithKey(PRBool* aOpenedWithKey) +{ + *aOpenedWithKey = PR_FALSE; + + nsIFrame* frame = GetFrame(PR_FALSE); + if (!frame || frame->GetType() != nsGkAtoms::menuFrame) + return NS_OK; + + frame = frame->GetParent(); + while (frame) { + if (frame->GetType() == nsGkAtoms::menuBarFrame) { + *aOpenedWithKey = (static_cast(frame))->IsActiveByKeyboard(); + return NS_OK; + } + frame = frame->GetParent(); + } + + return NS_OK; +} + + // Creation Routine /////////////////////////////////////////////////////////////////////// nsresult diff --git a/layout/xul/base/src/nsTextBoxFrame.cpp b/layout/xul/base/src/nsTextBoxFrame.cpp index a9f4df1c6c5..767fddaaf32 100644 --- a/layout/xul/base/src/nsTextBoxFrame.cpp +++ b/layout/xul/base/src/nsTextBoxFrame.cpp @@ -84,10 +84,6 @@ #define CROP_START "start" #define CROP_END "end" -// It's not clear to me whether nsLeafBoxFrame also uses some of the -// nsBoxFrame bits, so use NS_STATE_BOX_CHILD_RESERVED to be safe. -#define NS_STATE_NEED_LAYOUT NS_STATE_BOX_CHILD_RESERVED - class nsAccessKeyInfo { public: @@ -120,7 +116,6 @@ nsTextBoxFrame::AttributeChanged(PRInt32 aNameSpaceID, nsIAtom* aAttribute, PRInt32 aModType) { - mState |= NS_STATE_NEED_LAYOUT; PRBool aResize; PRBool aRedraw; @@ -147,7 +142,6 @@ nsTextBoxFrame::nsTextBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext): nsLeafBoxFrame(aShell, aContext), mAccessKeyInfo(nsnull), mCropType(CropRight), mNeedsReflowCallback(PR_FALSE) { - mState |= NS_STATE_NEED_LAYOUT; MarkIntrinsicWidthsDirty(); } @@ -164,7 +158,6 @@ nsTextBoxFrame::Init(nsIContent* aContent, { nsTextBoxFrameSuper::Init(aContent, aParent, aPrevInFlow); - mState |= NS_STATE_NEED_LAYOUT; PRBool aResize; PRBool aRedraw; UpdateAttributes(nsnull, aResize, aRedraw); /* update all */ @@ -333,7 +326,9 @@ class nsDisplayXULTextBox : public nsDisplayItem { public: nsDisplayXULTextBox(nsDisplayListBuilder* aBuilder, nsTextBoxFrame* aFrame) : - nsDisplayItem(aBuilder, aFrame) { + nsDisplayItem(aBuilder, aFrame), + mDisableSubpixelAA(PR_FALSE) + { MOZ_COUNT_CTOR(nsDisplayXULTextBox); } #ifdef NS_BUILD_REFCNT_LOGGING @@ -347,13 +342,19 @@ public: virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder); NS_DISPLAY_DECL_NAME("XULTextBox", TYPE_XUL_TEXT_BOX) - virtual PRBool HasText() { return PR_TRUE; } + virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder); + + virtual void DisableComponentAlpha() { mDisableSubpixelAA = PR_TRUE; } + + PRPackedBool mDisableSubpixelAA; }; void nsDisplayXULTextBox::Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx) { + gfxContextAutoDisableSubpixelAntialiasing disable(aCtx->ThebesContext(), + mDisableSubpixelAA); static_cast(mFrame)-> PaintTitle(*aCtx, mVisibleRect, ToReferenceFrame()); } @@ -363,6 +364,13 @@ nsDisplayXULTextBox::GetBounds(nsDisplayListBuilder* aBuilder) { return mFrame->GetVisualOverflowRect() + ToReferenceFrame(); } +nsRect +nsDisplayXULTextBox::GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) +{ + return static_cast(mFrame)->GetComponentAlphaBounds() + + ToReferenceFrame(); +} + NS_IMETHODIMP nsTextBoxFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, @@ -386,7 +394,7 @@ nsTextBoxFrame::PaintTitle(nsIRenderingContext& aRenderingContext, if (mTitle.IsEmpty()) return; - nsRect textRect(CalcTextRect(aRenderingContext, aPt)); + nsRect textRect = mTextDrawRect + aPt; // Paint the text shadow before doing any foreground stuff const nsStyleText* textStyle = GetStyleText(); @@ -617,25 +625,6 @@ void nsTextBoxFrame::PaintOneShadow(gfxContext* aCtx, aCtx->Restore(); } -void -nsTextBoxFrame::LayoutTitle(nsPresContext* aPresContext, - nsIRenderingContext& aRenderingContext, - const nsRect& aRect) -{ - // and do caculations if our size changed - if ((mState & NS_STATE_NEED_LAYOUT)) { - - // determine (cropped) title which fits in aRect.width and its width - CalculateTitleForWidth(aPresContext, aRenderingContext, aRect.width); - - // determine if and at which position to put the underline - UpdateAccessIndex(); - - // ok layout complete - mState &= ~NS_STATE_NEED_LAYOUT; - } -} - void nsTextBoxFrame::CalculateUnderline(nsIRenderingContext& aRenderingContext) { @@ -657,28 +646,28 @@ nsTextBoxFrame::CalculateUnderline(nsIRenderingContext& aRenderingContext) } } -void +nscoord nsTextBoxFrame::CalculateTitleForWidth(nsPresContext* aPresContext, nsIRenderingContext& aRenderingContext, nscoord aWidth) { if (mTitle.IsEmpty()) - return; + return 0; nsLayoutUtils::SetFontFromStyle(&aRenderingContext, GetStyleContext()); // see if the text will completely fit in the width given - mTitleWidth = nsLayoutUtils::GetStringWidth(this, &aRenderingContext, - mTitle.get(), mTitle.Length()); + nscoord titleWidth = nsLayoutUtils::GetStringWidth(this, &aRenderingContext, + mTitle.get(), mTitle.Length()); - if (mTitleWidth <= aWidth) { + if (titleWidth <= aWidth) { mCroppedTitle = mTitle; #ifdef IBMBIDI if (HasRTLChars(mTitle)) { mState |= NS_FRAME_IS_BIDI; } #endif // IBMBIDI - return; // fits, done. + return titleWidth; // fits, done. } const nsDependentString& kEllipsis = nsContentUtils::GetLocalizedEllipsis(); @@ -688,19 +677,18 @@ nsTextBoxFrame::CalculateTitleForWidth(nsPresContext* aPresContext, // see if the width is even smaller than the ellipsis // if so, clear the text (XXX set as many '.' as we can?). aRenderingContext.SetTextRunRTL(PR_FALSE); - aRenderingContext.GetWidth(kEllipsis, mTitleWidth); + aRenderingContext.GetWidth(kEllipsis, titleWidth); - if (mTitleWidth > aWidth) { + if (titleWidth > aWidth) { mCroppedTitle.SetLength(0); - mTitleWidth = 0; - return; + return 0; } // if the ellipsis fits perfectly, no use in trying to insert - if (mTitleWidth == aWidth) - return; + if (titleWidth == aWidth) + return titleWidth; - aWidth -= mTitleWidth; + aWidth -= titleWidth; // XXX: This whole block should probably take surrogates into account // XXX and clusters! @@ -730,7 +718,7 @@ nsTextBoxFrame::CalculateTitleForWidth(nsPresContext* aPresContext, } if (i == 0) - return; + return titleWidth; // insert what character we can in. nsAutoString title( mTitle ); @@ -760,7 +748,7 @@ nsTextBoxFrame::CalculateTitleForWidth(nsPresContext* aPresContext, } if (i == length-1) - return; + return titleWidth; nsAutoString copy; mTitle.Right(copy, length-1-i); @@ -830,8 +818,8 @@ nsTextBoxFrame::CalculateTitleForWidth(nsPresContext* aPresContext, break; } - mTitleWidth = nsLayoutUtils::GetStringWidth(this, &aRenderingContext, - mCroppedTitle.get(), mCroppedTitle.Length()); + return nsLayoutUtils::GetStringWidth(this, &aRenderingContext, + mCroppedTitle.get(), mCroppedTitle.Length()); } #define OLD_ELLIPSIS NS_LITERAL_STRING("...") @@ -952,10 +940,10 @@ nsTextBoxFrame::DoLayout(nsBoxLayoutState& aBoxLayoutState) mNeedsReflowCallback = PR_FALSE; } - mState |= NS_STATE_NEED_LAYOUT; - nsresult rv = nsLeafBoxFrame::DoLayout(aBoxLayoutState); + CalcDrawRect(*aBoxLayoutState.GetRenderingContext()); + const nsStyleText* textStyle = GetStyleText(); if (textStyle->mTextShadow) { nsRect bounds(nsPoint(0, 0), GetSize()); @@ -963,14 +951,21 @@ nsTextBoxFrame::DoLayout(nsBoxLayoutState& aBoxLayoutState) // Our scrollable overflow is our bounds; our visual overflow may // extend beyond that. nsPoint origin(0,0); - nsRect textRect = CalcTextRect(*aBoxLayoutState.GetRenderingContext(), origin); nsRect &vis = overflow.VisualOverflow(); - vis.UnionRect(vis, nsLayoutUtils::GetTextShadowRectsUnion(textRect, this)); + vis.UnionRect(vis, nsLayoutUtils::GetTextShadowRectsUnion(mTextDrawRect, this)); FinishAndStoreOverflow(overflow, GetSize()); } + return rv; } +nsRect +nsTextBoxFrame::GetComponentAlphaBounds() +{ + return nsLayoutUtils::GetTextShadowRectsUnion(mTextDrawRect, this, + nsLayoutUtils::EXCLUDE_BLUR_SHADOWS); +} + PRBool nsTextBoxFrame::ComputesOwnOverflowArea() { @@ -1014,20 +1009,25 @@ nsTextBoxFrame::CalcTextSize(nsBoxLayoutState& aBoxLayoutState) } } -nsRect -nsTextBoxFrame::CalcTextRect(nsIRenderingContext &aRenderingContext, const nsPoint &aTextOrigin) +void +nsTextBoxFrame::CalcDrawRect(nsIRenderingContext &aRenderingContext) { - nsRect textRect(aTextOrigin, GetSize()); + nsRect textRect(nsPoint(0, 0), GetSize()); nsMargin borderPadding; GetBorderAndPadding(borderPadding); textRect.Deflate(borderPadding); + // determine (cropped) title and underline position nsPresContext* presContext = PresContext(); - LayoutTitle(presContext, aRenderingContext, textRect); + // determine (cropped) title which fits in aRect.width and its width + nscoord titleWidth = + CalculateTitleForWidth(presContext, aRenderingContext, textRect.width); + // determine if and at which position to put the underline + UpdateAccessIndex(); // make the rect as small as our (cropped) text. nscoord outerWidth = textRect.width; - textRect.width = mTitleWidth; + textRect.width = titleWidth; // Align our text within the overall rect by checking our text-align property. const nsStyleVisibility* vis = GetStyleVisibility(); @@ -1042,7 +1042,8 @@ nsTextBoxFrame::CalcTextRect(nsIRenderingContext &aRenderingContext, const nsPoi vis->mDirection == NS_STYLE_DIRECTION_LTR)) { textRect.x += (outerWidth - textRect.width); } - return textRect; + + mTextDrawRect = textRect; } /** diff --git a/layout/xul/base/src/nsTextBoxFrame.h b/layout/xul/base/src/nsTextBoxFrame.h index 74c4063d4d0..f0aa64c870d 100644 --- a/layout/xul/base/src/nsTextBoxFrame.h +++ b/layout/xul/base/src/nsTextBoxFrame.h @@ -88,6 +88,8 @@ public: const nsRect& aDirtyRect, nsPoint aPt); + nsRect GetComponentAlphaBounds(); + virtual PRBool ComputesOwnOverflowArea(); protected: @@ -107,13 +109,13 @@ protected: void CalcTextSize(nsBoxLayoutState& aBoxLayoutState); - nsRect CalcTextRect(nsIRenderingContext &aRenderingContext, const nsPoint &aTextOrigin); + void CalcDrawRect(nsIRenderingContext &aRenderingContext); nsTextBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext); - void CalculateTitleForWidth(nsPresContext* aPresContext, - nsIRenderingContext& aRenderingContext, - nscoord aWidth); + nscoord CalculateTitleForWidth(nsPresContext* aPresContext, + nsIRenderingContext& aRenderingContext, + nscoord aWidth); void GetTextSize(nsPresContext* aPresContext, nsIRenderingContext& aRenderingContext, @@ -142,10 +144,10 @@ private: nsString mCroppedTitle; nsString mAccessKey; nsSize mTextSize; + nsRect mTextDrawRect; nsAccessKeyInfo* mAccessKeyInfo; CroppingStyle mCropType; - nscoord mTitleWidth; nscoord mAscent; PRPackedBool mNeedsRecalc; PRPackedBool mNeedsReflowCallback; diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 04ccb6f2088..30286153a0f 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -3278,3 +3278,6 @@ pref("extensions.alwaysUnpack", false); pref("network.buffer.cache.count", 24); pref("network.buffer.cache.size", 32768); + +// Desktop Notification +pref("notification.feature.enabled", false); diff --git a/modules/plugin/base/src/nsNPAPIPluginInstance.h b/modules/plugin/base/src/nsNPAPIPluginInstance.h index a0b47a14889..f233d6403f4 100644 --- a/modules/plugin/base/src/nsNPAPIPluginInstance.h +++ b/modules/plugin/base/src/nsNPAPIPluginInstance.h @@ -109,6 +109,9 @@ public: bool IsRunning() { return RUNNING == mRunning; } + bool HasStartedDestroying() { + return mRunning >= DESTROYING; + } // Indicates whether the plugin is running normally or being shut down bool CanFireNotifications() { diff --git a/modules/plugin/base/src/nsPluginHost.cpp b/modules/plugin/base/src/nsPluginHost.cpp index 84c5838028d..1a60951f235 100644 --- a/modules/plugin/base/src/nsPluginHost.cpp +++ b/modules/plugin/base/src/nsPluginHost.cpp @@ -1218,7 +1218,7 @@ nsPluginHost::TagForPlugin(nsNPAPIPlugin* aPlugin) } } // a plugin should never exist without a corresponding tag - NS_ASSERTION(PR_FALSE, "TagForPlugin has failed"); + NS_ERROR("TagForPlugin has failed"); return nsnull; } @@ -3216,9 +3216,11 @@ nsPluginHost::StopPluginInstance(nsIPluginInstance* aInstance) PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHost::StopPluginInstance called instance=%p\n",aInstance)); - aInstance->Stop(); - nsNPAPIPluginInstance* instance = static_cast(aInstance); + if (instance->HasStartedDestroying()) + return NS_OK; + + aInstance->Stop(); // if the plugin does not want to be 'cached' just remove it PRBool doCache = PR_TRUE; diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp index c9be9815f2b..f7096eb4112 100644 --- a/parser/html/nsHtml5TreeOpExecutor.cpp +++ b/parser/html/nsHtml5TreeOpExecutor.cpp @@ -203,7 +203,7 @@ nsHtml5TreeOpExecutor::FlushPendingNotifications(mozFlushType aType) { if (aType >= Flush_InterruptibleLayout) { // Bug 577508 / 253951 - nsContentSink::StartLayout(PR_FALSE); + nsContentSink::StartLayout(PR_TRUE); } } diff --git a/testing/mochitest/tests/SimpleTest/SimpleTest.js b/testing/mochitest/tests/SimpleTest/SimpleTest.js index 8fcf1b9b087..8f97c22dac8 100644 --- a/testing/mochitest/tests/SimpleTest/SimpleTest.js +++ b/testing/mochitest/tests/SimpleTest/SimpleTest.js @@ -258,6 +258,8 @@ SimpleTest.showReport = function() { addNode(SPAN(null, " ")); addNode(toggleTodo); addNode(SimpleTest.report()); + // Add a separator from the test content. + addNode(HR()); }; /** diff --git a/testing/xpcshell/runxpcshelltests.py b/testing/xpcshell/runxpcshelltests.py index 9e9d5ac8aea..3d75dde4c81 100644 --- a/testing/xpcshell/runxpcshelltests.py +++ b/testing/xpcshell/runxpcshelltests.py @@ -172,7 +172,11 @@ class XPCShellTests(object): elif sys.platform == 'osx' or sys.platform == "darwin": self.env["DYLD_LIBRARY_PATH"] = self.xrePath else: # unix or linux? - self.env["LD_LIBRARY_PATH"] = self.xrePath + if not "LD_LIBRARY_PATH" in self.env or self.env["LD_LIBRARY_PATH"] is None: + self.env["LD_LIBRARY_PATH"] = self.xrePath + else: + self.env["LD_LIBRARY_PATH"] = ":".join([self.xrePath, self.env["LD_LIBRARY_PATH"]]) + return self.env def buildXpcsRunArgs(self): diff --git a/toolkit/components/console/hudservice/HUDService.jsm b/toolkit/components/console/hudservice/HUDService.jsm index 6f02c0b6ff9..31c6065e82b 100644 --- a/toolkit/components/console/hudservice/HUDService.jsm +++ b/toolkit/components/console/hudservice/HUDService.jsm @@ -215,6 +215,9 @@ const MINIMUM_PAGE_HEIGHT = 50; // The default console height, as a ratio from the content window inner height. const DEFAULT_CONSOLE_HEIGHT = 0.33; +// Constant used when checking the typeof objects. +const TYPEOF_FUNCTION = "function"; + const ERRORS = { LOG_MESSAGE_MISSING_ARGS: "Missing arguments: aMessage, aConsoleNode and aMessageNode are required.", CANNOT_GET_HUD: "Cannot getHeads Up Display with provided ID", @@ -715,25 +718,27 @@ NetworkPanel.prototype = /** * The following code creates the HTML: - * - * ${line}: - * ${aList[line]}
- * + * + * ${line}: + * ${aList[line]} + * * and adds it to parent. */ + let row = doc.createElement("tr"); let textNode = doc.createTextNode(key + ":"); - let span = doc.createElement("span"); - span.setAttribute("class", "property-name"); - span.appendChild(textNode); - parent.appendChild(span); + let th = doc.createElement("th"); + th.setAttribute("scope", "row"); + th.setAttribute("class", "property-name"); + th.appendChild(textNode); + row.appendChild(th); textNode = doc.createTextNode(sortedList[key]); - span = doc.createElement("span"); - span.setAttribute("class", "property-value"); - span.appendChild(textNode); - parent.appendChild(span); + let td = doc.createElement("td"); + td.setAttribute("class", "property-value"); + td.appendChild(textNode); + row.appendChild(td); - parent.appendChild(doc.createElement("br")); + parent.appendChild(row); } }, @@ -1966,7 +1971,7 @@ HUD_SERVICE.prototype = let panel = netPanel.panel; panel.openPopup(aNode, "after_pointer", 0, 0, false, false); - panel.sizeTo(350, 400); + panel.sizeTo(450, 500); aHttpActivity.panels.push(Cu.getWeakReference(netPanel)); return netPanel; }, @@ -2362,23 +2367,29 @@ HUD_SERVICE.prototype = return sequencer(aInt); }, + // See jsapi.h (JSErrorReport flags): + // http://mxr.mozilla.org/mozilla-central/source/js/src/jsapi.h#3429 scriptErrorFlags: { - 0: "error", - 1: "warn", - 2: "exception", - 4: "error", // strict error - 5: "warn", // strict warning + 0: "error", // JSREPORT_ERROR + 1: "warn", // JSREPORT_WARNING + 2: "exception", // JSREPORT_EXCEPTION + 4: "error", // JSREPORT_STRICT | JSREPORT_ERROR + 5: "warn", // JSREPORT_STRICT | JSREPORT_WARNING + 8: "error", // JSREPORT_STRICT_MODE_ERROR + 13: "warn", // JSREPORT_STRICT_MODE_ERROR | JSREPORT_WARNING | JSREPORT_ERROR }, /** * replacement strings (L10N) */ scriptMsgLogLevel: { - 0: "typeError", - 1: "typeWarning", - 2: "typeException", - 4: "typeError", // strict error - 5: "typeStrict", // strict warning + 0: "typeError", // JSREPORT_ERROR + 1: "typeWarning", // JSREPORT_WARNING + 2: "typeException", // JSREPORT_EXCEPTION + 4: "typeError", // JSREPORT_STRICT | JSREPORT_ERROR + 5: "typeStrict", // JSREPORT_STRICT | JSREPORT_WARNING + 8: "typeError", // JSREPORT_STRICT_MODE_ERROR + 13: "typeWarning", // JSREPORT_STRICT_MODE_ERROR | JSREPORT_WARNING | JSREPORT_ERROR }, /** @@ -3055,6 +3066,10 @@ HeadsUpDisplay.prototype = { let menuPopup = this.makeXULNode("menupopup"); let id = this.hudId + "-output-contextmenu"; menuPopup.setAttribute("id", id); + menuPopup.addEventListener("popupshowing", function() { + saveBodiesItem.setAttribute("checked", + HUDService.saveRequestAndResponseBodies); + }, true); let saveBodiesItem = this.makeXULNode("menuitem"); saveBodiesItem.setAttribute("label", this.getStr("saveBodies.label")); @@ -3682,6 +3697,11 @@ function JSTermHelper(aJSTerm) aJSTerm.console.error(HUDService.getStr("helperFuncUnsupportedTypeError")); return; } + else if (typeof aObject === TYPEOF_FUNCTION) { + aJSTerm.writeOutput(aObject + "\n", CATEGORY_OUTPUT, SEVERITY_LOG); + return; + } + let output = []; let pairs = namesAndValuesOf(unwrap(aObject)); diff --git a/toolkit/components/console/hudservice/NetworkPanel.xhtml b/toolkit/components/console/hudservice/NetworkPanel.xhtml index 24933cf3dc4..645819bf318 100644 --- a/toolkit/components/console/hudservice/NetworkPanel.xhtml +++ b/toolkit/components/console/hudservice/NetworkPanel.xhtml @@ -29,6 +29,7 @@ - Contributor(s): - Joe Walker - Julian Viereck + - Mihai Șucan - - 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 @@ -51,34 +52,46 @@ - + + + + + + + + + + + + + +

&networkPanel.requestHeaders;

-
+
@@ -87,21 +100,21 @@ &networkPanel.responseHeaders; Δ -
+