зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to build-system.
This commit is contained in:
Коммит
e19aeb897a
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче