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 @@ -
+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
+ +
+ ++ ++ + 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 @@ + + + + +
+ +
+ ++ ++ + 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
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
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&networkPanel.requestURL;: | ++ |
---|---|
&networkPanel.requestMethod;: | ++ |
&networkPanel.statusCode;: | ++ |