Merge mozilla-central to build-system.

This commit is contained in:
Mitchell Field 2011-03-11 11:05:04 +11:00
Родитель 5f9978cbb7 46e431003f
Коммит e19aeb897a
46 изменённых файлов: 795 добавлений и 298 удалений

Просмотреть файл

@ -705,6 +705,88 @@ function test_renotify_installed() {
},
function test_cancel_restart() {
// If the XPI is already cached then the HTTP observer won't see the request
var cacheService = Cc["@mozilla.org/network/cache-service;1"].
getService(Ci.nsICacheService);
try {
cacheService.evictEntries(Components.interfaces.nsICache.STORE_ANYWHERE);
} catch(ex) {}
// Must be registered before any request starts
var observerService = Cc["@mozilla.org/network/http-activity-distributor;1"].
getService(Ci.nsIHttpActivityDistributor);
observerService.addObserver({
observeActivity: function(aChannel, aType, aSubtype, aTimestamp, aSizeData,
aStringData) {
aChannel.QueryInterface(Ci.nsIChannel);
// Wait for the first event for the download
if (aChannel.URI.spec != TESTROOT + "unsigned.xpi" ||
aType != Ci.nsIHttpActivityObserver.ACTIVITY_TYPE_HTTP_TRANSACTION ||
aSubtype != Ci.nsIHttpActivityObserver.ACTIVITY_SUBTYPE_REQUEST_HEADER)
return;
observerService.removeObserver(this);
info("Replacing channel");
aChannel.QueryInterface(Ci.nsITraceableChannel);
var listener = aChannel.setNewListener({
onStartRequest: function(aRequest, aContext) {
listener.onStartRequest(aRequest, aContext);
},
onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, aCount) {
listener.onDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount);
},
onStopRequest: function(aRequest, aContext, aStatusCode) {
listener.onStopRequest(aRequest, aContext, aStatusCode);
// Request should have been cancelled
is(aStatusCode, Components.results.NS_BINDING_ABORTED, "Should have seen a cancelled request");
// Notification should have changed to cancelled
let notification = PopupNotifications.panel.childNodes[0];
is(notification.id, "addon-install-cancelled-notification", "Should have seen the cancelled 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();
Services.perms.remove("example.com", "install");
wait_for_notification_close(runNextTest);
gBrowser.removeTab(gBrowser.selectedTab);
});
});
aWindow.document.documentElement.acceptDialog();
});
// Restart the download
info("Restarting download");
EventUtils.synthesizeMouse(notification.button, 20, 10, {});
// Should be back to a progress notification
ok(PopupNotifications.isPanelOpen, "Notification should still be open");
is(PopupNotifications.panel.childNodes.length, 1, "Should be only one notification");
notification = PopupNotifications.panel.childNodes[0];
is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
}
});
}
});
// Wait for the progress notification
wait_for_notification(function(aPanel) {
let notification = aPanel.childNodes[0];
@ -724,48 +806,8 @@ function test_cancel_restart() {
let button = document.getAnonymousElementByAttribute(notification, "anonid", "cancel");
// Cancel the download
info("Cancelling 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");
// 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();
Services.perms.remove("example.com", "install");
wait_for_notification_close(runNextTest);
gBrowser.removeTab(gBrowser.selectedTab);
});
});
aWindow.document.documentElement.acceptDialog();
});
// Restart the download
EventUtils.synthesizeMouse(notification.button, 20, 10, {});
// Should be back to a progress notification
ok(PopupNotifications.isPanelOpen, "Notification should still be open");
is(PopupNotifications.panel.childNodes.length, 1, "Should be only one notification");
notification = aPanel.childNodes[0];
is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
});
});
var pm = Services.perms;

Просмотреть файл

@ -184,16 +184,23 @@ function test() {
waitForExplicitFinish();
gTestWin = openDialog(location, "", "chrome,all,dialog=no", "about:blank");
gTestWin.addEventListener("load", function (event) {
info("Window loaded.");
gTestWin.removeEventListener("load", arguments.callee, false);
Services.obs.addObserver(function(aSubject, aTopic, aData) {
if (aSubject != gTestWin)
return;
Services.obs.removeObserver(arguments.callee, "browser-delayed-startup-finished");
info("Browser window opened");
waitForFocus(function() {
info("Setting up browser...");
setupTestBrowserWindow();
info("Running tests...");
executeSoon(runNextTest);
}, gTestWin.content, true);
}, false);
info("Browser window focused");
waitForFocus(function() {
info("Setting up browser...");
setupTestBrowserWindow();
info("Running tests...");
executeSoon(runNextTest);
}, gTestWin.content, true);
}, gTestWin);
}, "browser-delayed-startup-finished", false);
}
// Click handler used to steal click events.

Просмотреть файл

@ -1,3 +1,6 @@
// timer has to be alive so it can't be eaten by the GC.
var timer;
function handleRequest(request, response)
{
response.setHeader("Cache-Control", "no-cache", false);
@ -5,7 +8,7 @@ function handleRequest(request, response)
// The "stray" open comment at the end of the write is important!
response.write("document.write(\"<script charset='utf-8' src='script-2_bug597345.js'></script><!--\")");
response.processAsync();
var timer = Components.classes["@mozilla.org/timer;1"]
timer = Components.classes["@mozilla.org/timer;1"]
.createInstance(Components.interfaces.nsITimer);
timer.initWithCallback(function() {
response.finish();

Просмотреть файл

@ -22,9 +22,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=421602
SimpleTest.waitForExplicitFinish();
var img1loaded = false;
var img2loaded = false;
var img1errored = false;
var img2errored = false;
function gc() {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
@ -36,10 +34,13 @@ function gc() {
// Our test image
function loadTestImage() {
var img1 = new Image();
img1.onload = function() { img1loaded = true; }
img1.onload = function() {
img1loaded = true;
finishTest();
}
img1.onerror = function() {
is(img2errored, false, "Image 2 should not error before image 1");
img1errored = true;
finishTest();
}
img1.src = window.location.href + "?image1=true";
}
@ -48,17 +49,9 @@ loadTestImage();
// Probably overkill to gc() more than once, but let's be safe
gc(); gc(); gc();
// And now our "wrap the test up" image.
var img2 = new Image();
img2.onload = function() { img2loaded = true; }
img2.onerror = function() { img2errored = true; finishTest(); }
img2.src = window.location.href + "?image2=true";
function finishTest() {
is(img1errored, true, "Image 1 should error");
is(img2errored, true, "Image 2 should error");
is(img1loaded, false, "Image 1 should not load");
is(img2loaded, false, "Image 2 should not load");
SimpleTest.finish();
}
</script>

Просмотреть файл

@ -217,7 +217,7 @@ private:
void ChangeState(State aState);
// Create and initialize audio stream using current audio parameters.
void OpenAudioStream();
void OpenAudioStream(nsAutoMonitor& aMonitor);
// Shut down and dispose audio stream.
void CloseAudioStream();
@ -598,7 +598,7 @@ nsWaveStateMachine::Run()
case STATE_PLAYING: {
if (!mAudioStream) {
OpenAudioStream();
OpenAudioStream(monitor);
if (!mAudioStream) {
ChangeState(STATE_ERROR);
break;
@ -907,17 +907,27 @@ nsWaveStateMachine::ChangeState(State aState)
}
void
nsWaveStateMachine::OpenAudioStream()
nsWaveStateMachine::OpenAudioStream(nsAutoMonitor& aMonitor)
{
mAudioStream = nsAudioStream::AllocateStream();
if (!mAudioStream) {
NS_ABORT_IF_FALSE(mMetadataValid,
"Attempting to initialize audio stream with invalid metadata");
nsRefPtr<nsAudioStream> audioStream = nsAudioStream::AllocateStream();
if (!audioStream) {
LOG(PR_LOG_ERROR, ("Could not create audio stream"));
} else {
NS_ABORT_IF_FALSE(mMetadataValid,
"Attempting to initialize audio stream with invalid metadata");
mAudioStream->Init(mChannels, mSampleRate, mSampleFormat);
mAudioStream->SetVolume(mInitialVolume);
return;
}
// Drop the monitor while initializing the stream because remote
// audio streams wait on a synchronous event running on the main
// thread, and holding the decoder monitor while waiting for this
// can result in deadlocks.
aMonitor.Exit();
audioStream->Init(mChannels, mSampleRate, mSampleFormat);
aMonitor.Enter();
mAudioStream = audioStream;
mAudioStream->SetVolume(mInitialVolume);
}
void

Просмотреть файл

@ -636,7 +636,7 @@ abstract public class GeckoApp
Uri uri = data.getData();
String mimeType = cr.getType(uri);
String fileExt = "." +
mimeType.substring(mimeType.lastIndexOf('/') + 1);
GeckoAppShell.getExtensionFromMimeType(mimeType);
File file =
File.createTempFile("tmp_" +
(int)Math.floor(1000 * Math.random()),

Просмотреть файл

@ -44,7 +44,7 @@ import java.nio.channels.*;
import java.text.*;
import java.util.*;
import java.util.zip.*;
import java.util.concurrent.locks.*;
import java.util.concurrent.*;
import android.os.*;
import android.app.*;
@ -58,6 +58,7 @@ import android.graphics.*;
import android.widget.*;
import android.hardware.*;
import android.location.*;
import android.webkit.MimeTypeMap;
import android.util.*;
import android.net.Uri;
@ -104,6 +105,34 @@ public class GeckoAppShell
public static native void loadLibs(String apkName, boolean shouldExtract);
public static native void onChangeNetworkLinkStatus(String status);
// A looper thread, accessed by GeckoAppShell.getHandler
private static class LooperThread extends Thread {
public SynchronousQueue<Handler> mHandlerQueue =
new SynchronousQueue<Handler>();
public void run() {
Looper.prepare();
try {
mHandlerQueue.put(new Handler());
} catch (InterruptedException ie) {}
Looper.loop();
}
}
private static Handler sHandler = null;
// Get a Handler for a looper thread, or create one if it doesn't exist yet
public static Handler getHandler() {
if (sHandler == null) {
LooperThread lt = new LooperThread();
lt.start();
try {
sHandler = lt.mHandlerQueue.take();
} catch (InterruptedException ie) {}
}
return sHandler;
}
public static File getCacheDir() {
if (sCacheFile == null)
sCacheFile = GeckoApp.mAppContext.getCacheDir();
@ -428,8 +457,24 @@ public class GeckoAppShell
switch (type) {
case NOTIFY_IME_RESETINPUTSTATE:
GeckoApp.surfaceView.inputConnection.finishComposingText();
IMEStateUpdater.resetIME();
// Composition event is already fired from widget.
// So reset IME flags.
GeckoApp.surfaceView.inputConnection.reset();
// Don't use IMEStateUpdater for reset.
// Because IME may not work showSoftInput()
// after calling restartInput() immediately.
// So we have to call showSoftInput() delay.
InputMethodManager imm = (InputMethodManager)
GeckoApp.surfaceView.getContext().getSystemService(
Context.INPUT_METHOD_SERVICE);
if (imm == null) {
// no way to reset IME status directly
IMEStateUpdater.resetIME();
} else {
imm.restartInput(GeckoApp.surfaceView);
}
// keep current enabled state
IMEStateUpdater.enableIME();
break;
@ -439,7 +484,6 @@ public class GeckoAppShell
break;
case NOTIFY_IME_FOCUSCHANGE:
GeckoApp.surfaceView.mIMEFocus = state != 0;
IMEStateUpdater.resetIME();
break;
}
@ -477,35 +521,26 @@ public class GeckoAppShell
imm, text, start, end, newEnd);
}
private static final ReentrantLock mGeckoSyncLock = new ReentrantLock();
private static final Condition mGeckoSyncCond = mGeckoSyncLock.newCondition();
private static boolean mGeckoSyncAcked;
private static CountDownLatch sGeckoPendingAcks = null;
// Block the current thread until the Gecko event loop is caught up
public static void geckoEventSync() {
synchronized public static void geckoEventSync() {
sGeckoPendingAcks = new CountDownLatch(1);
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.GECKO_EVENT_SYNC));
mGeckoSyncLock.lock();
mGeckoSyncAcked = false;
while (!mGeckoSyncAcked) {
while (sGeckoPendingAcks.getCount() != 0) {
try {
mGeckoSyncCond.await();
} catch (InterruptedException e) {
break;
}
sGeckoPendingAcks.await();
} catch (InterruptedException e) {}
}
mGeckoSyncLock.unlock();
sGeckoPendingAcks = null;
}
// Signal the Java thread that it's time to wake up
public static void acknowledgeEventSync() {
mGeckoSyncLock.lock();
mGeckoSyncAcked = true;
try {
mGeckoSyncCond.signal();
} finally {
mGeckoSyncLock.unlock();
}
CountDownLatch tmp = sGeckoPendingAcks;
if (tmp != null)
tmp.countDown();
}
public static void enableAccelerometer(boolean enable) {
@ -572,9 +607,8 @@ public class GeckoAppShell
} else {
Log.i("GeckoAppJava", "we're done, good bye");
GeckoApp.mAppContext.finish();
System.exit(0);
}
System.exit(0);
}
static void scheduleRestart() {
Log.i("GeckoAppJava", "scheduling restart");
@ -643,9 +677,12 @@ public class GeckoAppShell
return new Intent(Intent.ACTION_VIEW);
}
static String getExtensionFromMimeType(String aMimeType) {
return MimeTypeMap.getSingleton().getExtensionFromMimeType(aMimeType);
}
static String getMimeTypeFromExtensions(String aFileExt) {
android.webkit.MimeTypeMap mtm =
android.webkit.MimeTypeMap.getSingleton();
MimeTypeMap mtm = MimeTypeMap.getSingleton();
StringTokenizer st = new StringTokenizer(aFileExt, "., ");
String type = null;
String subType = null;
@ -694,20 +731,38 @@ public class GeckoAppShell
}
}
static SynchronousQueue<String> sClipboardQueue =
new SynchronousQueue<String>();
// On some devices, access to the clipboard service needs to happen
// on a thread with a looper, so dispatch this to our looper thread
// Note: the main looper won't work because it may be blocked on the
// gecko thread, which is most likely this thread
static String getClipboardText() {
Context context = GeckoApp.surfaceView.getContext();
ClipboardManager cm = (ClipboardManager)
context.getSystemService(Context.CLIPBOARD_SERVICE);
if (!cm.hasText())
return null;
return cm.getText().toString();
getHandler().post(new Runnable() {
public void run() {
Context context = GeckoApp.surfaceView.getContext();
ClipboardManager cm = (ClipboardManager)
context.getSystemService(Context.CLIPBOARD_SERVICE);
try {
sClipboardQueue.put(cm.hasText() ? cm.getText().toString() : "");
} catch (InterruptedException ie) {}
}});
try {
String ret = sClipboardQueue.take();
return ret == "" ? null : ret;
} catch (InterruptedException ie) {}
return null;
}
static void setClipboardText(String text) {
Context context = GeckoApp.surfaceView.getContext();
ClipboardManager cm = (ClipboardManager)
context.getSystemService(Context.CLIPBOARD_SERVICE);
cm.setText(text);
static void setClipboardText(final String text) {
getHandler().post(new Runnable() {
public void run() {
Context context = GeckoApp.surfaceView.getContext();
ClipboardManager cm = (ClipboardManager)
context.getSystemService(Context.CLIPBOARD_SERVICE);
cm.setText(text);
}});
}
public static void showAlertNotification(String aImageUrl, String aAlertTitle, String aAlertText,

Просмотреть файл

@ -494,6 +494,7 @@ class GeckoSurfaceView
// KeyListener returns true if it handled the event for us.
if (mIMEState == IME_STATE_DISABLED ||
keyCode == KeyEvent.KEYCODE_ENTER ||
(event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) != 0 ||
!mKeyListener.onKeyDown(this, mEditable, keyCode, event))
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
return true;
@ -511,6 +512,7 @@ class GeckoSurfaceView
}
if (mIMEState == IME_STATE_DISABLED ||
keyCode == KeyEvent.KEYCODE_ENTER ||
(event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) != 0 ||
!mKeyListener.onKeyUp(this, mEditable, keyCode, event))
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
return true;
@ -582,7 +584,6 @@ class GeckoSurfaceView
KeyListener mKeyListener;
Editable mEditable;
Editable.Factory mEditableFactory;
boolean mIMEFocus;
int mIMEState;
String mIMETypeHint;
String mIMEActionHint;

Просмотреть файл

@ -166,6 +166,8 @@ missing-cairo-clip-init.diff: Missing cairo_clip_init call in cairo_gstate_show_
fix-cairo-win32-print-gdi-error.diff: Don't use fwprintf with char* format. Flush stderr so that all error messages appears before exit.
pixman-image-transform.patch: Reset the transform on pixman images when using them as destinations.
==== pixman patches ====
pixman-android-cpu-detect.patch: Add CPU detection support for Android, where we can't reliably access /proc/self/auxv.

Просмотреть файл

@ -1143,6 +1143,17 @@ _cairo_image_surface_composite (cairo_operator_t op,
if (unlikely (status))
goto CLEANUP_SURFACES;
/* we sometimes get destinations with transforms.
* we're not equiped to deal with this */
{
static const pixman_transform_t id = {
{{ pixman_fixed_1, 0, 0 },
{ 0, pixman_fixed_1, 0 },
{ 0, 0, pixman_fixed_1 }}
};
pixman_image_set_transform (dst->pixman_image, &id);
}
if (mask) {
status = _cairo_image_surface_set_attributes (mask, &mask_attr,
dst_x + width / 2.,

Просмотреть файл

@ -0,0 +1,52 @@
# HG changeset patch
# User Jeff Muizelaar <jmuizelaar@mozilla.com>
# Date 1299543337 18000
# Node ID 57f411f16517fa3abc31b6b081dd31420c4d9b45
# Parent e56ecd8b3a68c158025207c5fd081d043e28f5ce
Bug 637828. Reset the transform on the dest image. r=joe
We can get into a situation where the destination image has a transform
because we use it as source. The transform set when the image is a source
sticks around and when we use it as a destination pixman gets confused.
It seems like the code at fault here is really pixman. I think that pixman
should probably not be using a transformed fetch on the destination image under
any circumstances.
For example, in this case we're fetching destination pixels from a different
part of the image than we're storing them to. I can't see any reason for
wanting this behaviour.
However, reseting the transform seemed like the easiest solution.
diff --git a/gfx/cairo/cairo/src/cairo-image-surface.c b/gfx/cairo/cairo/src/cairo-image-surface.c
--- a/gfx/cairo/cairo/src/cairo-image-surface.c
+++ b/gfx/cairo/cairo/src/cairo-image-surface.c
@@ -1138,16 +1138,27 @@ _cairo_image_surface_composite (cairo_op
return status;
status = _cairo_image_surface_set_attributes (src, &src_attr,
dst_x + width / 2.,
dst_y + height / 2.);
if (unlikely (status))
goto CLEANUP_SURFACES;
+ /* we sometimes get destinations with transforms.
+ * we're not equiped to deal with this */
+ {
+ static const pixman_transform_t id = {
+ {{ pixman_fixed_1, 0, 0 },
+ { 0, pixman_fixed_1, 0 },
+ { 0, 0, pixman_fixed_1 }}
+ };
+ pixman_image_set_transform (dst->pixman_image, &id);
+ }
+
if (mask) {
status = _cairo_image_surface_set_attributes (mask, &mask_attr,
dst_x + width / 2.,
dst_y + height / 2.);
if (unlikely (status))
goto CLEANUP_SURFACES;
pixman_image_composite (_pixman_operator (op),

Просмотреть файл

@ -40,6 +40,7 @@
#include "gfxContext.h"
#include "gfxPlatform.h"
#include "gfxUtils.h"
#include "nsIDeviceContext.h"
namespace mozilla {
namespace layers {
@ -168,6 +169,43 @@ ThebesLayerBuffer::GetContextForQuadrantUpdate(const nsIntRect& aBounds,
return ctx.forget();
}
// Move the pixels in aBuffer specified by |aSourceRect| to |aDest|.
// |aSourceRect| and |aDest| are in the space of |aBuffer|, but
// unscaled by the resolution. This helper does the scaling.
static void
MovePixels(gfxASurface* aBuffer,
const nsIntRect& aSourceRect, const nsIntPoint& aDest,
float aXResolution, float aYResolution)
{
gfxRect src(aSourceRect.x, aSourceRect.y, aSourceRect.width, aSourceRect.height);
gfxRect dest(aDest.x, aDest.y, aSourceRect.width, aSourceRect.height);
src.Scale(aXResolution, aYResolution);
dest.Scale(aXResolution, aYResolution);
#ifdef DEBUG
// If we're doing a self-copy, enforce that the rects we're copying
// were computed in order to round to device pixels. If the rects
// we're moving *weren't* computed to round, then glitches like
// seaming are likely. Assume that the precision of these
// computations is 1 app unit, and toss in a fudge factor of 2.0.
static const gfxFloat kPrecision =
1.0 / gfxFloat(nsIDeviceContext::AppUnitsPerCSSPixel());
// FIXME/bug 637852: we've decided to live with transient glitches
// during fast-panning for the time being.
NS_WARN_IF_FALSE(
src.WithinEpsilonOfIntegerPixels(2.0 * kPrecision * aXResolution) &&
dest.WithinEpsilonOfIntegerPixels(2.0 * kPrecision * aXResolution),
"Rects don't round to device pixels within precision; glitches likely to follow");
#endif
src.Round();
dest.Round();
aBuffer->MovePixels(nsIntRect(src.pos.x, src.pos.y,
src.size.width, src.size.height),
nsIntPoint(dest.pos.x, dest.pos.y));
}
static void
WrapRotationAxis(PRInt32* aRotationPoint, PRInt32 aSize)
{
@ -206,10 +244,12 @@ ThebesLayerBuffer::BeginPaint(ThebesLayer* aLayer, ContentType aContentType,
if (mBufferRect.Contains(neededRegion.GetBounds())) {
// We don't need to adjust mBufferRect.
destBufferRect = mBufferRect;
} else {
} else if (neededRegion.GetBounds().Size() <= mBufferRect.Size()) {
// The buffer's big enough but doesn't contain everything that's
// going to be visible. We'll move it.
destBufferRect = nsIntRect(neededRegion.GetBounds().TopLeft(), mBufferRect.Size());
} else {
destBufferRect = neededRegion.GetBounds();
}
} else {
destBufferRect = neededRegion.GetBounds();
@ -251,6 +291,9 @@ ThebesLayerBuffer::BeginPaint(ThebesLayer* aLayer, ContentType aContentType,
break;
}
NS_ASSERTION(destBufferRect.Contains(neededRegion.GetBounds()),
"Destination rect doesn't contain what we need to paint");
result.mRegionToDraw.Sub(neededRegion, validRegion);
if (result.mRegionToDraw.IsEmpty())
return result;
@ -287,9 +330,15 @@ ThebesLayerBuffer::BeginPaint(ThebesLayer* aLayer, ContentType aContentType,
(drawBounds.y < yBoundary && yBoundary < drawBounds.YMost()) ||
(newRotation != nsIntPoint(0,0) && !canHaveRotation)) {
// The stuff we need to redraw will wrap around an edge of the
// buffer, so we will need to do a self-copy
if (mBuffer->SupportsSelfCopy() && mBufferRotation == nsIntPoint(0,0)) {
destBuffer = mBuffer;
// buffer, so move the pixels we can keep into a position that
// lets us redraw in just one quadrant.
if (mBufferRotation == nsIntPoint(0,0)) {
nsIntRect srcRect(nsIntPoint(0, 0), mBufferRect.Size());
nsIntPoint dest = mBufferRect.TopLeft() - destBufferRect.TopLeft();
MovePixels(mBuffer, srcRect, dest, curXRes, curYRes);
// Don't set destBuffer; we special-case self-copies, and
// just did the necessary work above.
mBufferRect = destBufferRect;
} else {
// We can't do a real self-copy because the buffer is rotated.
// So allocate a new buffer for the destination.

Просмотреть файл

@ -48,6 +48,7 @@
#include "cairo.h"
#include "yuv_convert.h"
#include "ycbcr_to_rgb565.h"
#include "gfxPlatform.h"
@ -146,14 +147,13 @@ BasicPlanarYCbCrImage::SetData(const Data& aData)
// YCbCr to RGB conversion rather than on the RGB data when rendered.
PRBool prescale = mScaleHint.width > 0 && mScaleHint.height > 0;
if (format == gfxASurface::ImageFormatRGB16_565) {
#ifndef HAVE_SCALE_YCBCR_TO_RGB565
// yuv2rgb16 with scale function not yet available
prescale = PR_FALSE;
#endif
#ifndef HAVE_YCBCR_TO_RGB565
// yuv2rgb16 function not yet available for non-arm
format = gfxASurface::ImageFormatRGB24;
#endif
if (have_ycbcr_to_rgb565()) {
// yuv2rgb16 with scale function not yet available for NEON
prescale = PR_FALSE;
} else {
// yuv2rgb16 function not yet available for non-NEON
format = gfxASurface::ImageFormatRGB24;
}
}
gfxIntSize size(prescale ? mScaleHint.width : aData.mPicSize.width,
prescale ? mScaleHint.height : aData.mPicSize.height);
@ -204,6 +204,7 @@ BasicPlanarYCbCrImage::SetData(const Data& aData)
}
} else { // no prescale
if (format == gfxASurface::ImageFormatRGB16_565) {
NS_ASSERTION(have_ycbcr_to_rgb565(), "Cannot convert YCbCr to RGB565");
gfx::ConvertYCbCrToRGB565(aData.mYChannel,
aData.mCbChannel,
aData.mCrChannel,

Просмотреть файл

@ -426,6 +426,7 @@ protected:
virtual void
PaintBuffer(gfxContext* aContext,
const nsIntRegion& aRegionToDraw,
const nsIntRegion& aExtendedRegionToDraw,
const nsIntRegion& aRegionToInvalidate,
LayerManager::DrawThebesLayerCallback aCallback,
void* aCallbackData)
@ -434,14 +435,14 @@ protected:
BasicManager()->SetTransactionIncomplete();
return;
}
aCallback(this, aContext, aRegionToDraw, aRegionToInvalidate,
aCallback(this, aContext, aExtendedRegionToDraw, aRegionToInvalidate,
aCallbackData);
// Everything that's visible has been validated. Do this instead of just
// OR-ing with aRegionToDraw, since that can lead to a very complex region
// here (OR doesn't automatically simplify to the simplest possible
// representation of a region.)
nsIntRegion tmp;
tmp.Or(mVisibleRegion, aRegionToDraw);
tmp.Or(mVisibleRegion, aExtendedRegionToDraw);
mValidRegion.Or(mValidRegion, tmp);
}
@ -586,7 +587,8 @@ BasicThebesLayer::PaintThebes(gfxContext* aContext,
PRUint32 flags = 0;
gfxMatrix transform;
if (!GetEffectiveTransform().Is2D(&transform) ||
transform.HasNonIntegerTranslation()) {
transform.HasNonIntegerTranslation() ||
MustRetainContent() /*<=> has shadow layer*/) {
flags |= ThebesLayerBuffer::PAINT_WILL_RESAMPLE;
}
Buffer::PaintState state =
@ -599,12 +601,13 @@ BasicThebesLayer::PaintThebes(gfxContext* aContext,
// from RGB to RGBA, because we might need to repaint with
// subpixel AA)
state.mRegionToInvalidate.And(state.mRegionToInvalidate, mVisibleRegion);
state.mRegionToDraw.ExtendForScaling(paintXRes, paintYRes);
nsIntRegion extendedDrawRegion = state.mRegionToDraw;
extendedDrawRegion.ExtendForScaling(paintXRes, paintYRes);
mXResolution = paintXRes;
mYResolution = paintYRes;
SetAntialiasingFlags(this, state.mContext);
PaintBuffer(state.mContext,
state.mRegionToDraw, state.mRegionToInvalidate,
state.mRegionToDraw, extendedDrawRegion, state.mRegionToInvalidate,
aCallback, aCallbackData);
Mutated();
} else {
@ -1795,6 +1798,7 @@ private:
NS_OVERRIDE virtual void
PaintBuffer(gfxContext* aContext,
const nsIntRegion& aRegionToDraw,
const nsIntRegion& aExtendedRegionToDraw,
const nsIntRegion& aRegionToInvalidate,
LayerManager::DrawThebesLayerCallback aCallback,
void* aCallbackData);
@ -1854,11 +1858,13 @@ BasicShadowableThebesLayer::SetBackBufferAndAttrs(const ThebesBuffer& aBuffer,
void
BasicShadowableThebesLayer::PaintBuffer(gfxContext* aContext,
const nsIntRegion& aRegionToDraw,
const nsIntRegion& aExtendedRegionToDraw,
const nsIntRegion& aRegionToInvalidate,
LayerManager::DrawThebesLayerCallback aCallback,
void* aCallbackData)
{
Base::PaintBuffer(aContext, aRegionToDraw, aRegionToInvalidate,
Base::PaintBuffer(aContext,
aRegionToDraw, aExtendedRegionToDraw, aRegionToInvalidate,
aCallback, aCallbackData);
if (!HasShadow()) {
return;
@ -1875,7 +1881,8 @@ BasicShadowableThebesLayer::PaintBuffer(gfxContext* aContext,
updatedRegion = aRegionToDraw;
}
NS_ASSERTION(mBuffer.BufferRect().Contains(aRegionToDraw.GetBounds()),
"Update outside of buffer rect!");
NS_ABORT_IF_FALSE(IsSurfaceDescriptorValid(mBackBuffer),
"should have a back buffer by now");
BasicManager()->PaintedThebesBuffer(BasicManager()->Hold(this),

Просмотреть файл

@ -157,7 +157,7 @@ ReadbackManagerD3D10::~ReadbackManagerD3D10()
::CloseHandle(mTaskSemaphore);
::CloseHandle(mTaskThread);
} else {
NS_WARNING("ReadbackManager: Task thread did not shutdown in 5 seconds. Leaking.");
NS_RUNTIMEABORT("ReadbackManager: Task thread did not shutdown in 5 seconds.");
}
}
@ -227,6 +227,9 @@ ReadbackManagerD3D10::ProcessTasks()
}
::EnterCriticalSection(&mTaskMutex);
if (mPendingReadbackTasks.Length() == 0) {
NS_RUNTIMEABORT("Trying to read from an empty array, bad bad bad");
}
ReadbackTask *nextReadbackTask = mPendingReadbackTasks[0].forget();
mPendingReadbackTasks.RemoveElementAt(0);
::LeaveCriticalSection(&mTaskMutex);

Просмотреть файл

@ -40,9 +40,11 @@
#include "nsMemory.h"
#include "gfxASurface.h"
#include "gfxContext.h"
#include "gfxImageSurface.h"
#include "nsRect.h"
#include "cairo.h"
#ifdef CAIRO_HAS_WIN32_SURFACE
@ -474,6 +476,24 @@ gfxASurface::BytePerPixelFromFormat(gfxImageFormat format)
return 0;
}
void
gfxASurface::MovePixels(const nsIntRect& aSourceRect,
const nsIntPoint& aDestTopLeft)
{
gfxIntSize size = GetSize();
nsIntRect dest(aDestTopLeft, aSourceRect.Size());
// Assume that our cairo backend already knows how to properly
// self-copy. gfxASurface subtypes whose backend can't self-copy
// need their own implementations, or their backends need to be
// fixed.
nsRefPtr<gfxContext> ctx = new gfxContext(this);
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
nsIntPoint srcOrigin = dest.TopLeft() - aSourceRect.TopLeft();
ctx->SetSource(this, gfxPoint(srcOrigin.x, srcOrigin.y));
ctx->Rectangle(gfxRect(dest.x, dest.y, dest.width, dest.height));
ctx->Fill();
}
/** Memory reporting **/
static const char *sSurfaceNamesForSurfaceType[] = {

Просмотреть файл

@ -48,6 +48,8 @@ typedef struct _cairo_user_data_key cairo_user_data_key_t;
typedef void (*thebes_destroy_func_t) (void *data);
class gfxImageSurface;
struct nsIntPoint;
struct nsIntRect;
/**
* A surface is something you can draw on. Instantiate a subclass of this
@ -218,7 +220,18 @@ public:
return empty;
}
virtual PRBool SupportsSelfCopy() { return PR_TRUE; }
/**
* Move the pixels in |aSourceRect| to |aDestTopLeft|. Like with
* memmove(), |aSourceRect| and the rectangle defined by
* |aDestTopLeft| are allowed to overlap, and the effect is
* equivalent to copying |aSourceRect| to a scratch surface and
* then back to |aDestTopLeft|.
*
* |aSourceRect| and the destination rectangle defined by
* |aDestTopLeft| are clipped to this surface's bounds.
*/
virtual void MovePixels(const nsIntRect& aSourceRect,
const nsIntPoint& aDestTopLeft);
/**
* Mark the surface as being allowed/not allowed to be used as a source.

Просмотреть файл

@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -417,6 +417,26 @@ gfxAndroidPlatform::AppendFacesFromFontFile(const char *aFileName, FontNameCache
}
}
void
gfxAndroidPlatform::FindFontsInDirectory(const char *aDirectory, FontNameCache* aFontCache)
{
DIR *d = opendir(aDirectory);
struct dirent *ent = NULL;
while(d && (ent = readdir(d)) != NULL) {
int namelen = strlen(ent->d_name);
if (namelen > 4 &&
strcasecmp(ent->d_name + namelen - 4, ".ttf") == 0)
{
nsCString s(aDirectory);
s.Append("/fonts/");
s.Append(nsDependentCString(ent->d_name));
AppendFacesFromFontFile(nsPromiseFlatCString(s).get(),
aFontCache, &mFontList);
}
}
}
void
gfxAndroidPlatform::GetFontList(InfallibleTArray<FontListEntry>* retValue)
{
@ -431,22 +451,15 @@ gfxAndroidPlatform::GetFontList(InfallibleTArray<FontListEntry>* retValue)
*retValue = mFontList;
return;
}
FontNameCache fnc;
DIR *d = opendir("/system/fonts");
struct dirent *ent = NULL;
while(d && (ent = readdir(d)) != NULL) {
int namelen = strlen(ent->d_name);
if (namelen > 4 &&
strcasecmp(ent->d_name + namelen - 4, ".ttf") == 0)
{
nsCString s("/system/fonts");
s.Append("/");
s.Append(nsDependentCString(ent->d_name));
AppendFacesFromFontFile(nsPromiseFlatCString(s).get(),
&fnc, &mFontList);
}
}
// Check in both /system and $ANDROID_ROOT
FontNameCache fnc;
const char *systemDirectory = "/system";
FindFontsInDirectory(systemDirectory, &fnc);
char *androidRoot = PR_GetEnv("ANDROID_ROOT");
if (androidRoot && strcmp(androidRoot, systemDirectory))
FindFontsInDirectory(androidRoot, &fnc);
*retValue = mFontList;
}

Просмотреть файл

@ -107,6 +107,7 @@ public:
protected:
void AppendFacesFromFontFile(const char *aFileName, FontNameCache* aFontCache, InfallibleTArray<FontListEntry>* retValue);
void FindFontsInDirectory(const char *aDirectory, FontNameCache* aFontCache);
typedef nsDataHashtable<nsStringHashKey, nsRefPtr<FontFamily> > FontTable;

Просмотреть файл

@ -254,3 +254,70 @@ gfxImageSurface::GetAsImageSurface()
nsRefPtr<gfxImageSurface> surface = this;
return surface.forget();
}
void
gfxImageSurface::MovePixels(const nsIntRect& aSourceRect,
const nsIntPoint& aDestTopLeft)
{
const nsIntRect bounds(0, 0, mSize.width, mSize.height);
nsIntPoint offset = aDestTopLeft - aSourceRect.TopLeft();
nsIntRect clippedSource = aSourceRect;
clippedSource.IntersectRect(clippedSource, bounds);
nsIntRect clippedDest = clippedSource + offset;
clippedDest.IntersectRect(clippedDest, bounds);
const nsIntRect dest = clippedDest;
const nsIntRect source = dest - offset;
// NB: this relies on IntersectRect() and operator+/- preserving
// x/y for empty rectangles
NS_ABORT_IF_FALSE(bounds.Contains(dest) && bounds.Contains(source) &&
aSourceRect.Contains(source) &&
nsIntRect(aDestTopLeft, aSourceRect.Size()).Contains(dest) &&
source.Size() == dest.Size() &&
offset == (dest.TopLeft() - source.TopLeft()),
"Messed up clipping, crash or corruption will follow");
if (source.IsEmpty() || source == dest) {
return;
}
long naturalStride = ComputeStride(mSize, mFormat);
if (mStride == naturalStride && dest.width == bounds.width) {
// Fast path: this is a vertical shift of some rows in a
// "normal" image surface. We can directly memmove and
// hopefully stay in SIMD land.
unsigned char* dst = mData + dest.y * mStride;
const unsigned char* src = mData + source.y * mStride;
size_t nBytes = dest.height * mStride;
memmove(dst, src, nBytes);
return;
}
// Slow(er) path: have to move row-by-row.
const PRInt32 bpp = BytePerPixelFromFormat(mFormat);
const size_t nRowBytes = dest.width * bpp;
// dstRow points at the first pixel within the current destination
// row, and similarly for srcRow. endSrcRow is one row beyond the
// last row we need to copy. stride is either +mStride or
// -mStride, depending on which direction we're copying.
unsigned char* dstRow;
unsigned char* srcRow;
unsigned char* endSrcRow; // NB: this may point outside the image
long stride;
if (dest.y > source.y) {
// We're copying down from source to dest, so walk backwards
// starting from the last rows to avoid stomping pixels we
// need.
stride = -mStride;
dstRow = mData + dest.x * bpp + (dest.YMost() - 1) * mStride;
srcRow = mData + source.x * bpp + (source.YMost() - 1) * mStride;
endSrcRow = mData + source.x * bpp + (source.y - 1) * mStride;
} else {
stride = mStride;
dstRow = mData + dest.x * bpp + dest.y * mStride;
srcRow = mData + source.x * bpp + source.y * mStride;
endSrcRow = mData + source.x * bpp + source.YMost() * mStride;
}
for (; srcRow != endSrcRow; dstRow += stride, srcRow += stride) {
memmove(dstRow, srcRow, nRowBytes);
}
}

Просмотреть файл

@ -108,7 +108,10 @@ public:
virtual already_AddRefed<gfxImageSurface> GetAsImageSurface();
virtual PRBool SupportsSelfCopy() { return PR_FALSE; }
/** See gfxASurface.h. */
NS_OVERRIDE
virtual void MovePixels(const nsIntRect& aSourceRect,
const nsIntPoint& aDestTopLeft);
protected:
gfxImageSurface();

Просмотреть файл

@ -87,6 +87,22 @@ gfxRect::Contains(const gfxPoint& aPoint) const
aPoint.y >= Y() && aPoint.y <= YMost();
}
static PRBool
WithinEpsilonOfInteger(gfxFloat aX, gfxFloat aEpsilon)
{
return fabs(NS_round(aX) - aX) <= fabs(aEpsilon);
}
PRBool
gfxRect::WithinEpsilonOfIntegerPixels(gfxFloat aEpsilon) const
{
NS_ASSERTION(-0.5 < aEpsilon && aEpsilon < 0.5, "Nonsense epsilon value");
return (WithinEpsilonOfInteger(pos.x, aEpsilon) &&
WithinEpsilonOfInteger(pos.y, aEpsilon) &&
WithinEpsilonOfInteger(size.width, aEpsilon) &&
WithinEpsilonOfInteger(size.height, aEpsilon));
}
void
gfxRect::Round()
{

Просмотреть файл

@ -114,7 +114,14 @@ struct THEBES_API gfxRect {
gfxRect Union(const gfxRect& aRect) const;
PRBool Contains(const gfxRect& aRect) const;
PRBool Contains(const gfxPoint& aPoint) const;
// XXX figure out what methods (intersect, union, etc) we use and add them.
/**
* Return true if all components of this rect are within
* aEpsilon of integer coordinates, defined as
* |round(coord) - coord| <= |aEpsilon|
* for x,y,width,height.
*/
PRBool WithinEpsilonOfIntegerPixels(gfxFloat aEpsilon) const;
gfxPoint TopLeft() { return pos; }
gfxPoint BottomRight() { return gfxPoint(XMost(), YMost()); }

Просмотреть файл

@ -15,6 +15,7 @@ DEFINES += -D_IMPL_NS_GFX
EXPORTS = chromium_types.h \
yuv_convert.h \
yuv_row.h \
ycbcr_to_rgb565.h \
$(NULL)
CPPSRCS = yuv_convert.cpp \

Просмотреть файл

@ -98,18 +98,6 @@ XPCCallContext::Init(XPCContext::LangType callerLanguage,
jsval *argv,
jsval *rval)
{
// Mark our internal string wrappers as not used. Make sure we do
// this before any early returns, as the destructor will assert
// based on this.
StringWrapperEntry *se =
reinterpret_cast<StringWrapperEntry*>(&mStringWrapperData);
PRUint32 i;
for(i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i)
{
se[i].mInUse = PR_FALSE;
}
if(!mXPC)
return;
@ -424,15 +412,9 @@ XPCCallContext::~XPCCallContext()
}
#ifdef DEBUG
for(PRUint32 i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i)
{
StringWrapperEntry *se =
reinterpret_cast<StringWrapperEntry*>(&mStringWrapperData);
PRUint32 i;
for(i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i)
{
NS_ASSERTION(!se[i].mInUse, "Uh, string wrapper still in use!");
}
NS_ASSERTION(!mScratchStrings[i].mInUse, "Uh, string wrapper still in use!");
}
#endif
@ -443,13 +425,9 @@ XPCCallContext::~XPCCallContext()
XPCReadableJSStringWrapper *
XPCCallContext::NewStringWrapper(const PRUnichar *str, PRUint32 len)
{
StringWrapperEntry *se =
reinterpret_cast<StringWrapperEntry*>(&mStringWrapperData);
PRUint32 i;
for(i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i)
for(PRUint32 i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i)
{
StringWrapperEntry& ent = se[i];
StringWrapperEntry& ent = mScratchStrings[i];
if(!ent.mInUse)
{
@ -457,7 +435,7 @@ XPCCallContext::NewStringWrapper(const PRUnichar *str, PRUint32 len)
// Construct the string using placement new.
return new (&ent.mString) XPCReadableJSStringWrapper(str, len);
return new (ent.mString.addr()) XPCReadableJSStringWrapper(str, len);
}
}
@ -469,20 +447,16 @@ XPCCallContext::NewStringWrapper(const PRUnichar *str, PRUint32 len)
void
XPCCallContext::DeleteString(nsAString *string)
{
StringWrapperEntry *se =
reinterpret_cast<StringWrapperEntry*>(&mStringWrapperData);
PRUint32 i;
for(i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i)
for(PRUint32 i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i)
{
StringWrapperEntry& ent = se[i];
if(string == &ent.mString)
StringWrapperEntry& ent = mScratchStrings[i];
if(string == ent.mString.addr())
{
// One of our internal strings is no longer in use, mark
// it as such and destroy the string.
ent.mInUse = PR_FALSE;
ent.mString.~XPCReadableJSStringWrapper();
ent.mString.addr()->~XPCReadableJSStringWrapper();
return;
}

Просмотреть файл

@ -1254,23 +1254,18 @@ private:
// String wrapper entry, holds a string, and a boolean that tells
// whether the string is in use or not.
//
// NB: The string is not stored by value so that we avoid the cost of
// construction/destruction.
struct StringWrapperEntry
{
StringWrapperEntry()
: mInUse(PR_FALSE)
{
}
StringWrapperEntry() : mInUse(PR_FALSE) { }
XPCReadableJSStringWrapper mString;
js::AlignedStorage2<XPCReadableJSStringWrapper> mString;
PRBool mInUse;
};
// Reserve space for XPCCCX_STRING_CACHE_SIZE string wrapper
// entries for use on demand. It's important to not make this be
// string class members since we don't want to pay the cost of
// calling the constructors and destructors when the strings
// aren't being used.
char mStringWrapperData[sizeof(StringWrapperEntry) * XPCCCX_STRING_CACHE_SIZE];
StringWrapperEntry mScratchStrings[XPCCCX_STRING_CACHE_SIZE];
};
class XPCLazyCallContext

Просмотреть файл

@ -6082,7 +6082,8 @@ void PresShell::SetRenderingState(const RenderingState& aState)
nsIPresShell* rootPresShell = rootPresContext->GetPresShell();
nsIFrame* rootFrame = rootPresShell->FrameManager()->GetRootFrame();
if (rootFrame) {
rootFrame->InvalidateFrameSubtree();
rootFrame->InvalidateWithFlags(rootFrame->GetVisualOverflowRectRelativeToSelf(),
nsIFrame::INVALIDATE_NO_THEBES_LAYERS);
}
}
}

Просмотреть файл

@ -25,6 +25,12 @@ const IMAGE_DATA =
0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82,
];
/**
* The timer is needed when a delay is set. We need it to be out of the method
* so it is not eaten alive by the GC.
*/
var timer;
function handleRequest(request, response) {
var query = {};
request.queryString.split('&').forEach(function (val) {
@ -40,16 +46,20 @@ function handleRequest(request, response) {
stream.writeByteArray(IMAGE_DATA, IMAGE_DATA.length);
}
if ("delay" in query) {
response.processAsync();
const nsITimer = Components.interfaces.nsITimer;
var timer = Components.classes["@mozilla.org/timer;1"].createInstance(nsITimer);
timer.initWithCallback(function() {
imageWrite();
response.finish();
}, query["delay"], nsITimer.TYPE_ONE_SHOT);
} else {
// If there is no delay, we write the image and leave.
if (!("delay" in query)) {
imageWrite();
return;
}
// If there is a delay, we create a timer which, when it fires, will write
// image and leave.
response.processAsync();
const nsITimer = Components.interfaces.nsITimer;
timer = Components.classes["@mozilla.org/timer;1"].createInstance(nsITimer);
timer.initWithCallback(function() {
imageWrite();
response.finish();
}, query["delay"], nsITimer.TYPE_ONE_SHOT);
}

Просмотреть файл

@ -273,7 +273,8 @@ static inline PRInt32 _MD_ATOMIC_SET(PRInt32 *ptr, PRInt32 nv)
#else
#define _PR_NO_LARGE_FILES
#endif
#if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1)
#if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) \
|| defined(ANDROID)
#define _PR_INET6
#define _PR_HAVE_INET_NTOP
#define _PR_HAVE_GETHOSTBYNAME2
@ -284,6 +285,7 @@ static inline PRInt32 _MD_ATOMIC_SET(PRInt32 *ptr, PRInt32 nv)
#define _PR_HAVE_SYSV_SEMAPHORES
#define PR_HAVE_SYSV_NAMED_SHARED_MEMORY
#endif
/* Android has gethostbyname_r but not gethostbyaddr_r or gethostbyname2_r. */
#if (__GLIBC__ >= 2) && defined(_PR_PTHREADS)
#define _PR_HAVE_GETHOST_R
#define _PR_HAVE_GETHOST_R_INT

Просмотреть файл

@ -1360,7 +1360,8 @@ PR_LoadStaticLibrary(const char *name, const PRStaticLinkTable *slt)
PR_IMPLEMENT(char *)
PR_GetLibraryFilePathname(const char *name, PRFuncPtr addr)
{
#if defined(USE_DLFCN) && !defined(ANDROID) && (defined(SOLARIS) || defined(FREEBSD) \
#if defined(USE_DLFCN) && !defined(ANDROID) \
&& (defined(SOLARIS) || defined(FREEBSD) \
|| defined(LINUX) || defined(__GNU__) || defined(__GLIBC__) \
|| defined(DARWIN))
Dl_info dli;

Просмотреть файл

@ -95,8 +95,8 @@ PRLock *_pr_dnsLock = NULL;
#if defined(SOLARIS) || (defined(BSDI) && defined(_REENTRANT)) \
|| (defined(LINUX) && defined(_REENTRANT) \
&& !(defined(__GLIBC__) && __GLIBC__ >= 2)) \
&& !defined(ANDROID)
&& !(defined(__GLIBC__) && __GLIBC__ >= 2) \
&& !defined(ANDROID))
#define _PR_HAVE_GETPROTO_R
#define _PR_HAVE_GETPROTO_R_POINTER
#endif

Просмотреть файл

@ -48,6 +48,7 @@ Components.utils.import("resource://gre/modules/ctypes.jsm");
const ALGORITHM = Ci.IWeaveCrypto.AES_256_CBC;
const KEYSIZE_AES_256 = 32;
const KEY_DERIVATION_ITERATIONS = 4096; // PKCS#5 recommends at least 1000.
const INITIAL_BUFFER_SIZE = 1024;
function WeaveCrypto() {
this.init();
@ -88,12 +89,29 @@ WeaveCrypto.prototype = {
this.initNSS();
this.initAlgorithmSettings(); // Depends on NSS.
this.initIVSECItem();
this.initSharedInts();
this.initBuffers(INITIAL_BUFFER_SIZE);
} catch (e) {
this.log("init failed: " + e);
throw e;
}
},
// Avoid allocating new temporary ints on every run of _commonCrypt.
_commonCryptSignedOutputSize: null,
_commonCryptSignedOutputSizeAddr: null,
_commonCryptUnsignedOutputSize: null,
_commonCryptUnsignedOutputSizeAddr: null,
initSharedInts: function initSharedInts() {
let signed = new ctypes.int();
let unsigned = new ctypes.unsigned_int();
this._commonCryptSignedOutputSize = signed;
this._commonCryptUnsignedOutputSize = unsigned;
this._commonCryptSignedOutputSizeAddr = signed.address();
this._commonCryptUnsignedOutputSizeAddr = unsigned.address();
},
/**
* Set a bunch of NSS values once, at init-time. These are:
* - .blockSize
@ -365,21 +383,68 @@ WeaveCrypto.prototype = {
// IWeaveCrypto interfaces
//
_sharedInputBuffer: null,
_sharedInputBufferInts: null,
_sharedInputBufferSize: 0,
_sharedOutputBuffer: null,
_sharedOutputBufferSize: 0,
_randomByteBuffer: null,
_randomByteBufferAddr: null,
_randomByteBufferSize: 0,
_getInputBuffer: function _getInputBuffer(size) {
if (size > this._sharedInputBufferSize) {
let b = new ctypes.ArrayType(ctypes.unsigned_char, size)();
this._sharedInputBuffer = b;
this._sharedInputBufferInts = ctypes.cast(b, ctypes.uint8_t.array(size));
this._sharedInputBufferSize = size;
}
return this._sharedInputBuffer;
},
_getOutputBuffer: function _getOutputBuffer(size) {
if (size > this._sharedOutputBufferSize) {
let b = new ctypes.ArrayType(ctypes.unsigned_char, size)();
this._sharedOutputBuffer = b;
this._sharedOutputBufferSize = size;
}
return this._sharedOutputBuffer;
},
_getRandomByteBuffer: function _getRandomByteBuffer(size) {
if (size > this._randomByteBufferSize) {
let b = new ctypes.ArrayType(ctypes.unsigned_char, size)();
this._randomByteBuffer = b;
this._randomByteBufferAddr = b.address();
this._randomByteBufferSize = size;
}
return this._randomByteBuffer;
},
initBuffers: function initBuffers(initialSize) {
this._getInputBuffer(initialSize);
this._getOutputBuffer(initialSize);
this._getRandomByteBuffer(this.ivLength);
},
encrypt : function(clearTextUCS2, symmetricKey, iv) {
this.log("encrypt() called");
// js-ctypes autoconverts to a UTF8 buffer, but also includes a null
// at the end which we don't want. Cast to make the length 1 byte shorter.
// at the end which we don't want. Decrement length to skip it.
let inputBuffer = new ctypes.ArrayType(ctypes.unsigned_char)(clearTextUCS2);
inputBuffer = ctypes.cast(inputBuffer, ctypes.unsigned_char.array(inputBuffer.length - 1));
let inputBufferSize = inputBuffer.length - 1;
// When using CBC padding, the output size is the input size rounded
// up to the nearest block. If the input size is exactly on a block
// boundary, the output is 1 extra block long.
let outputBufferSize = inputBuffer.length + this.blockSize;
let outputBuffer = new ctypes.ArrayType(ctypes.unsigned_char, outputBufferSize)();
let outputBufferSize = inputBufferSize + this.blockSize;
let outputBuffer = this._getOutputBuffer(outputBufferSize);
outputBuffer = this._commonCrypt(inputBuffer, outputBuffer, symmetricKey, iv, this.nss.CKA_ENCRYPT);
outputBuffer = this._commonCrypt(inputBuffer, inputBufferSize,
outputBuffer, outputBufferSize,
symmetricKey, iv, this.nss.CKA_ENCRYPT);
return this.encodeBase64(outputBuffer.address(), outputBuffer.length);
},
@ -395,16 +460,15 @@ WeaveCrypto.prototype = {
// We can't have js-ctypes create the buffer directly from the string
// (as in encrypt()), because we do _not_ want it to do UTF8
// conversion... We've got random binary data in the input's low byte.
//
//
// Compress a JS string (2-byte chars) into a normal C string (1-byte chars).
let len = inputUCS2.length;
let input = new ctypes.ArrayType(ctypes.unsigned_char, len)();
let ints = ctypes.cast(input, ctypes.uint8_t.array(len));
this.byteCompressInts(inputUCS2, ints, len);
let input = this._getInputBuffer(len);
this.byteCompressInts(inputUCS2, this._sharedInputBufferInts, len);
let outputBuffer = new ctypes.ArrayType(ctypes.unsigned_char, input.length)();
outputBuffer = this._commonCrypt(input, outputBuffer, symmetricKey, iv, this.nss.CKA_DECRYPT);
let outputBuffer = this._commonCrypt(input, len,
this._getOutputBuffer(len), len,
symmetricKey, iv, this.nss.CKA_DECRYPT);
// outputBuffer contains UTF-8 data, let js-ctypes autoconvert that to a JS string.
// XXX Bug 573842: wrap the string from ctypes to get a new string, so
@ -412,13 +476,14 @@ WeaveCrypto.prototype = {
return "" + outputBuffer.readString() + "";
},
_commonCrypt : function (input, output, symmetricKey, iv, operation) {
_commonCrypt : function (input, inputLength, output, outputLength, symmetricKey, iv, operation) {
this.log("_commonCrypt() called");
iv = atob(iv);
// We never want an IV longer than the block size, which is 16 bytes
// for AES.
// for AES. Neither do we want one smaller; throw in that case.
if (iv.length < this.blockSize)
throw "IV too short; must be " + this.blockSize + " bytes.";
if (iv.length > this.blockSize)
iv = iv.slice(0, this.blockSize);
@ -436,24 +501,21 @@ WeaveCrypto.prototype = {
if (ctx.isNull())
throw Components.Exception("couldn't create context for symkey", Cr.NS_ERROR_FAILURE);
let maxOutputSize = output.length;
let tmpOutputSize = new ctypes.int(); // Note 1: NSS uses a signed int here...
if (this.nss.PK11_CipherOp(ctx, output, tmpOutputSize.address(), maxOutputSize, input, input.length))
let maxOutputSize = outputLength;
if (this.nss.PK11_CipherOp(ctx, output, this._commonCryptSignedOutputSize.address(), maxOutputSize, input, inputLength))
throw Components.Exception("cipher operation failed", Cr.NS_ERROR_FAILURE);
let actualOutputSize = tmpOutputSize.value;
let actualOutputSize = this._commonCryptSignedOutputSize.value;
let finalOutput = output.addressOfElement(actualOutputSize);
maxOutputSize -= actualOutputSize;
// PK11_DigestFinal sure sounds like the last step for *hashing*, but it
// just seems to be an odd name -- NSS uses this to finish the current
// cipher operation. You'd think it would be called PK11_CipherOpFinal...
let tmpOutputSize2 = new ctypes.unsigned_int(); // Note 2: ...but an unsigned here!
if (this.nss.PK11_DigestFinal(ctx, finalOutput, tmpOutputSize2.address(), maxOutputSize))
if (this.nss.PK11_DigestFinal(ctx, finalOutput, this._commonCryptUnsignedOutputSizeAddr, maxOutputSize))
throw Components.Exception("cipher finalize failed", Cr.NS_ERROR_FAILURE);
actualOutputSize += tmpOutputSize2.value;
actualOutputSize += this._commonCryptUnsignedOutputSize.value;
let newOutput = ctypes.cast(output, ctypes.unsigned_char.array(actualOutputSize));
return newOutput;
} catch (e) {
@ -510,11 +572,11 @@ WeaveCrypto.prototype = {
this.log("generateRandomBytes() called");
// Temporary buffer to hold the generated data.
let scratch = new ctypes.ArrayType(ctypes.unsigned_char, byteCount)();
let scratch = this._getRandomByteBuffer(byteCount);
if (this.nss.PK11_GenerateRandom(scratch, byteCount))
throw Components.Exception("PK11_GenrateRandom failed", Cr.NS_ERROR_FAILURE);
return this.encodeBase64(scratch.address(), scratch.length);
return this.encodeBase64(this._randomByteBufferAddr, byteCount);
},
//
@ -577,18 +639,13 @@ WeaveCrypto.prototype = {
* Compress a JS string into a C uint8 array. count is the number of
* elements in the destination array. If the array is smaller than the
* string, the string is effectively truncated. If the string is smaller
* than the array, the array is 0-padded.
* than the array, the array is not 0-padded.
*/
byteCompressInts : function byteCompressInts (jsString, intArray, count) {
let len = jsString.length;
let end = Math.min(len, count);
for (let i = 0; i < end; i++)
intArray[i] = jsString.charCodeAt(i) % 256; // convert to bytes
// Must zero-pad.
for (let i = len; i < count; i++)
intArray[i] = 0;
intArray[i] = jsString.charCodeAt(i) & 0xFF; // convert to bytes.
},
// Expand a normal C string (1-byte chars) into a JS string (2-byte chars)

Просмотреть файл

@ -103,8 +103,8 @@ function test_SECItem_byteCompressInts() {
// Fill it too short.
cryptoSvc.byteCompressInts("MMM", intData, 8);
for (let i = 0; i < 8; ++i)
do_check_eq(intData[i], [77, 77, 77, 0, 0, 0, 0, 0, 0][i]);
for (let i = 0; i < 3; ++i)
do_check_eq(intData[i], [77, 77, 77][i]);
// Fill it too much. Doesn't buffer overrun.
cryptoSvc.byteCompressInts("NNNNNNNNNNNNNNNN", intData, 8);
@ -138,6 +138,17 @@ function test_encrypt_decrypt() {
key = "St1tFCor7vQEJNug/465dQ==";
iv = "oLjkfrLIOnK2bDRvW4kXYA==";
_("Testing small IV.");
mySecret = "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=";
shortiv = "YWJj"; // "abc": Less than 16.
let err;
try {
cryptoSvc.encrypt(mySecret, key, shortiv);
} catch (ex) {
err = ex;
}
do_check_true(!!err);
// Test small input sizes
mySecret = "";
cipherText = cryptoSvc.encrypt(mySecret, key, iv);

Просмотреть файл

@ -35,10 +35,10 @@ function run_test() {
do_check_eq(salt2.length, 12);
do_check_neq(salt, salt2);
salt = cryptoSvc.generateRandomBytes(16);
do_check_eq(salt.length, 24);
salt = cryptoSvc.generateRandomBytes(1024);
do_check_eq(salt.length, 1368);
salt = cryptoSvc.generateRandomBytes(16);
do_check_eq(salt.length, 24);
// Test random key generation

Просмотреть файл

@ -192,12 +192,23 @@ function Store(name) {
this._log = Log4Moz.repository.getLogger("Store." + name);
let level = Svc.Prefs.get("log.logger.engine." + this.name, "Debug");
this._log.level = Log4Moz.Level[level];
Utils.lazy2(this, "_timer", function() {
return Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
});
}
Store.prototype = {
_sleep: function _sleep(delay) {
let cb = Utils.makeSyncCallback();
this._timer.initWithCallback({notify: cb}, delay,
Ci.nsITimer.TYPE_ONE_SHOT);
Utils.waitForSyncCallback(cb);
},
applyIncomingBatch: function applyIncomingBatch(records) {
let failed = [];
records.forEach(function (record) {
for each (let record in records) {
try {
this.applyIncoming(record);
} catch (ex) {
@ -205,7 +216,7 @@ Store.prototype = {
this._log.warn("Encountered exception: " + Utils.exceptionStr(ex));
failed.push(record.id);
}
}, this);
};
return failed;
},
@ -404,10 +415,6 @@ Engine.prototype = {
function SyncEngine(name) {
Engine.call(this, name || "SyncEngine");
this.loadToFetch();
Utils.lazy2(this, "_timer", function() {
return Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
});
}
SyncEngine.prototype = {
__proto__: Engine.prototype,
@ -499,13 +506,6 @@ SyncEngine.prototype = {
return record;
},
_sleep: function _sleep(delay) {
let cb = Utils.makeSyncCallback();
this._timer.initWithCallback({notify: cb}, delay,
Ci.nsITimer.TYPE_ONE_SHOT);
Utils.waitForSyncCallback(cb);
},
// Any setup that needs to happen at the beginning of each sync.
_syncStartup: function SyncEngine__syncStartup() {
@ -675,7 +675,7 @@ SyncEngine.prototype = {
if (applyBatch.length == self.applyIncomingBatchSize) {
doApplyBatch.call(self);
}
self._sleep(0);
self._store._sleep(0);
};
// Only bother getting data from the server if there's new things
@ -909,7 +909,7 @@ SyncEngine.prototype = {
if ((++count % MAX_UPLOAD_RECORDS) == 0)
doUpload((count - MAX_UPLOAD_RECORDS) + " - " + count + " out");
this._sleep(0);
this._store._sleep(0);
}
// Final upload

Просмотреть файл

@ -182,6 +182,11 @@ FormStore.prototype = {
}, this);
},
applyIncoming: function applyIncoming(record) {
Store.prototype.applyIncoming.call(this, record);
this._sleep(0); // Yield back to main thread after synchronous operation.
},
getAllIDs: function FormStore_getAllIDs() {
let guids = {};
for each (let {name, value} in FormWrapper.getAllEntries())
@ -202,7 +207,7 @@ FormStore.prototype = {
let entry = FormWrapper.getEntry(id);
if (entry != null) {
record.name = entry.name;
record.value = entry.value
record.value = entry.value;
}
else
record.deleted = true;

Просмотреть файл

@ -98,7 +98,8 @@ PasswordEngine.prototype = {
return;
let logins = Svc.Login.findLogins({}, login.hostname, login.formSubmitURL,
login.httpRealm);
login.httpRealm);
this._store._sleep(0); // Yield back to main thread after synchronous operation.
// Look for existing logins that match the hostname but ignore the password
for each (let local in logins)
@ -155,13 +156,14 @@ PasswordStore.prototype = {
prop.setPropertyAsAUTF8String("guid", id);
let logins = Svc.Login.searchLogins({}, prop);
this._sleep(0); // Yield back to main thread after synchronous operation.
if (logins.length > 0) {
this._log.trace(logins.length + " items matching " + id + " found.");
return logins[0];
} else {
this._log.trace("No items matching " + id + " found. Ignoring");
}
return false;
return null;
},
applyIncomingBatch: function applyIncomingBatch(records) {
@ -174,6 +176,11 @@ PasswordStore.prototype = {
}, this);
},
applyIncoming: function applyIncoming(record) {
Store.prototype.applyIncoming.call(this, record);
this._sleep(0); // Yield back to main thread after synchronous operation.
},
getAllIDs: function PasswordStore__getAllIDs() {
let items = {};
let logins = Svc.Login.getAllLogins({});

Просмотреть файл

@ -1140,7 +1140,8 @@ let Utils = {
let fos = Cc["@mozilla.org/network/safe-file-output-stream;1"]
.createInstance(Ci.nsIFileOutputStream);
fos.init(file, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, PERMS_FILE, 0);
fos.init(file, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, PERMS_FILE,
fos.DEFER_OPEN || 0);
let is = this._utf8Converter.convertToInputStream(out);
NetUtil.asyncCopy(is, fos, function (result) {
if (typeof callback == "function") {

Просмотреть файл

@ -82,7 +82,7 @@ function test_toFetch() {
engine.toFetch = toFetch;
do_check_eq(engine.toFetch, toFetch);
// toFetch is written asynchronously
engine._sleep(0);
engine._store._sleep(0);
let fakefile = syncTesting.fakeFilesystem.fakeContents[filename];
do_check_eq(fakefile, JSON.stringify(toFetch));

Просмотреть файл

@ -1 +1 @@
1.7b1pre
1.7

Просмотреть файл

@ -605,9 +605,11 @@ function writeInstallRDFForExtension(aData, aDir, aId, aExtraFile) {
function setExtensionModifiedTime(aExt, aTime) {
aExt.lastModifiedTime = aTime;
if (aExt.isDirectory()) {
aExt = aExt.clone();
aExt.append("install.rdf");
aExt.lastModifiedTime = aTime;
let entries = aExt.directoryEntries
.QueryInterface(AM_Ci.nsIDirectoryEnumerator);
while (entries.hasMoreElements())
setExtensionModifiedTime(entries.nextFile, aTime);
entries.close();
}
}

Просмотреть файл

@ -4,9 +4,10 @@
// This verifies that add-ons can be installed from XPI files
// The maximum allowable time since install. If an add-on claims to have been
// installed longer ago than this the the test will fail.
const MAX_INSTALL_TIME = 10000;
// Maximum error in file modification times. Some file systems don't store
// modification times exactly. As long as we are closer than this then it
// still passes.
const MAX_TIME_DIFFERENCE = 3000;
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/NetUtil.jsm");
@ -138,6 +139,12 @@ function check_test_1() {
do_check_true(iconFile.exists());
}
// Make the pending install have a sensible date
let updateDate = Date.now();
let extURI = pendingAddons[0].getResourceURI("");
let ext = extURI.QueryInterface(AM_Ci.nsIFileURL).file;
setExtensionModifiedTime(ext, updateDate);
// The pending add-on cannot be disabled or enabled.
do_check_false(hasFlag(pendingAddons[0].permissions, AddonManager.PERM_CAN_ENABLE));
do_check_false(hasFlag(pendingAddons[0].permissions, AddonManager.PERM_CAN_DISABLE));
@ -159,14 +166,13 @@ function check_test_1() {
do_check_eq(a1.sourceURI.spec,
Services.io.newFileURI(do_get_addon("test_install1")).spec);
// Should have been installed sometime in the last two second.
let difference = Date.now() - a1.installDate.getTime();
if (difference > MAX_INSTALL_TIME)
do_throw("Add-on was installed " + difference + "ms ago");
if (difference < 0)
do_throw("Add-on was installed " + difference + "ms in the future");
let difference = a1.installDate.getTime() - updateDate;
if (Math.abs(difference) > MAX_TIME_DIFFERENCE)
do_throw("Add-on install time was out by " + difference + "ms");
do_check_eq(a1.installDate.getTime(), a1.updateDate.getTime());
difference = a1.updateDate.getTime() - updateDate;
if (Math.abs(difference) > MAX_TIME_DIFFERENCE)
do_throw("Add-on update time was out by " + difference + "ms");
do_check_true(a1.hasResource("install.rdf"));
do_check_false(a1.hasResource("foo.bar"));
@ -249,7 +255,13 @@ function run_test_3(install) {
install.install();
}
function check_test_3() {
function check_test_3(aInstall) {
// Make the pending install have a sensible date
let updateDate = Date.now();
let extURI = aInstall.addon.getResourceURI("");
let ext = extURI.QueryInterface(AM_Ci.nsIFileURL).file;
setExtensionModifiedTime(ext, updateDate);
ensure_test_completed();
AddonManager.getAddonByID("addon2@tests.mozilla.org", function(olda2) {
do_check_eq(olda2, null);
@ -269,14 +281,14 @@ function check_test_3() {
do_check_eq(a2.sourceURI.spec,
"http://localhost:4444/addons/test_install2_1.xpi");
// Should have been installed sometime in the last two second.
let difference = Date.now() - a2.installDate.getTime();
if (difference > MAX_INSTALL_TIME)
do_throw("Add-on was installed " + difference + "ms ago");
if (difference < 0)
do_throw("Add-on was installed " + difference + "ms in the future");
let difference = a2.installDate.getTime() - updateDate;
if (Math.abs(difference) > MAX_TIME_DIFFERENCE)
do_throw("Add-on install time was out by " + difference + "ms");
difference = a2.updateDate.getTime() - updateDate;
if (Math.abs(difference) > MAX_TIME_DIFFERENCE)
do_throw("Add-on update time was out by " + difference + "ms");
do_check_eq(a2.installDate.getTime(), a2.updateDate.getTime());
gInstallDate = a2.installDate.getTime();
run_test_4();

Просмотреть файл

@ -25,6 +25,16 @@ function download_progress(addon, value, maxValue) {
}
function finish_test(count) {
function wait_for_online() {
info("Checking if the browser is still offline...");
var request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
createInstance(Ci.nsIXMLHttpRequest);
request.open("GET", TESTROOT + "empty.xpi", true);
request.onerror = wait_for_online;
request.onload = Harness.finish;
request.send(null);
}
is(count, 0, "No add-ons should have been installed");
try {
Services.prefs.setBoolPref("browser.offline", false);
@ -35,5 +45,5 @@ function finish_test(count) {
Services.perms.remove("example.com", "install");
gBrowser.removeCurrentTab();
Harness.finish();
wait_for_online();
}

Просмотреть файл

@ -88,6 +88,10 @@ nsMIMEInfoAndroid::GetMimeInfoForMimeType(const nsACString& aMimeType,
if (systemDefault)
info->mPrefApp = systemDefault;
nsCAutoString fileExt;
bridge->GetExtensionFromMimeType(nsDependentCString(aMimeType), fileExt);
info->SetPrimaryExtension(fileExt);
PRUint32 len;
info->mHandlerApps->GetLength(&len);
if (len == 1) {
@ -107,8 +111,10 @@ nsMIMEInfoAndroid::GetMimeInfoForFileExt(const nsACString& aFileExt,
if (mozilla::AndroidBridge::Bridge())
mozilla::AndroidBridge::Bridge()->
GetMimeTypeFromExtensions(aFileExt, mimeType);
return GetMimeInfoForMimeType(mimeType, aMimeInfo);
nsresult rv = GetMimeInfoForMimeType(mimeType, aMimeInfo);
NS_ENSURE_SUCCESS(rv, rv);
return (*aMimeInfo)->SetPrimaryExtension(aFileExt);
}
/**
@ -138,6 +144,12 @@ nsMIMEInfoAndroid::GetMimeInfoForURL(const nsACString &aURL,
mimeinfo->mPrefApp = systemDefault;
nsCAutoString fileExt;
nsCAutoString mimeType;
mimeinfo->GetType(mimeType);
bridge->GetExtensionFromMimeType(mimeType, fileExt);
mimeinfo->SetPrimaryExtension(fileExt);
PRUint32 len;
mimeinfo->mHandlerApps->GetLength(&len);
if (len == 1) {

Просмотреть файл

@ -119,6 +119,7 @@ AndroidBridge::Init(JNIEnv *jEnv,
jGetHandlersForURL = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getHandlersForURL", "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;");
jOpenUriExternal = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "openUriExternal", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z");
jGetMimeTypeFromExtensions = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getMimeTypeFromExtensions", "(Ljava/lang/String;)Ljava/lang/String;");
jGetExtensionFromMimeType = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getExtensionFromMimeType", "(Ljava/lang/String;)Ljava/lang/String;");
jMoveTaskToBack = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "moveTaskToBack", "()V");
jGetClipboardText = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getClipboardText", "()Ljava/lang/String;");
jSetClipboardText = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "setClipboardText", "(Ljava/lang/String;)V");
@ -454,13 +455,30 @@ AndroidBridge::GetMimeTypeFromExtensions(const nsACString& aFileExt, nsCString&
AutoLocalJNIFrame jniFrame;
NS_ConvertUTF8toUTF16 wFileExt(aFileExt);
jstring jstrExt = mJNIEnv->NewString(wFileExt.get(), wFileExt.Length());
jstring jstrType = static_cast<jstring>(mJNIEnv->CallStaticObjectMethod(mGeckoAppShellClass,
jGetMimeTypeFromExtensions,
jstrExt));
jstring jstrType = static_cast<jstring>(
mJNIEnv->CallStaticObjectMethod(mGeckoAppShellClass,
jGetMimeTypeFromExtensions,
jstrExt));
nsJNIString jniStr(jstrType);
aMimeType.Assign(NS_ConvertUTF16toUTF8(jniStr.get()));
}
void
AndroidBridge::GetExtensionFromMimeType(const nsCString& aMimeType, nsACString& aFileExt)
{
ALOG_BRIDGE("AndroidBridge::GetExtensionFromMimeType");
AutoLocalJNIFrame jniFrame;
NS_ConvertUTF8toUTF16 wMimeType(aMimeType);
jstring jstrType = mJNIEnv->NewString(wMimeType.get(), wMimeType.Length());
jstring jstrExt = static_cast<jstring>(
mJNIEnv->CallStaticObjectMethod(mGeckoAppShellClass,
jGetExtensionFromMimeType,
jstrType));
nsJNIString jniStr(jstrExt);
aFileExt.Assign(NS_ConvertUTF16toUTF8(jniStr.get()));
}
void
AndroidBridge::MoveTaskToBack()
{

Просмотреть файл

@ -145,6 +145,7 @@ public:
const nsAString& aTitle = EmptyString());
void GetMimeTypeFromExtensions(const nsACString& aFileExt, nsCString& aMimeType);
void GetExtensionFromMimeType(const nsCString& aMimeType, nsACString& aFileExt);
void MoveTaskToBack();
@ -260,6 +261,7 @@ protected:
jmethodID jGetHandlersForURL;
jmethodID jOpenUriExternal;
jmethodID jGetMimeTypeFromExtensions;
jmethodID jGetExtensionFromMimeType;
jmethodID jMoveTaskToBack;
jmethodID jGetClipboardText;
jmethodID jSetClipboardText;