diff options
45 files changed, 0 insertions, 18061 deletions
diff --git a/embedding/android/GeckoSmsManager.java b/embedding/android/GeckoSmsManager.java deleted file mode 100644 index c22a89ea0..000000000 --- a/embedding/android/GeckoSmsManager.java +++ /dev/null @@ -1,983 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import java.util.ArrayList; -import java.util.Iterator; - -import android.util.Log; - -import android.app.PendingIntent; -import android.app.Activity; - -import android.database.Cursor; - -import android.content.Intent; -import android.content.IntentFilter; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.ContentUris; - -import android.net.Uri; - -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; - -import android.telephony.SmsManager; -import android.telephony.SmsMessage; - -import static android.telephony.SmsMessage.MessageClass; - -/** - * This class is returning unique ids for PendingIntent requestCode attribute. - * There are only |Integer.MAX_VALUE - Integer.MIN_VALUE| unique IDs available, - * and they wrap around. - */ -class PendingIntentUID -{ - static private int sUID = Integer.MIN_VALUE; - - static public int generate() { return sUID++; } -} - -/** - * The envelope class contains all information that are needed to keep track of - * a sent SMS. - */ -class Envelope -{ - enum SubParts { - SENT_PART, - DELIVERED_PART - } - - protected int mId; - protected int mMessageId; - protected long mMessageTimestamp; - - /** - * Number of sent/delivered remaining parts. - * @note The array has much slots as SubParts items. - */ - protected int[] mRemainingParts; - - /** - * Whether sending/delivering is currently failing. - * @note The array has much slots as SubParts items. - */ - protected boolean[] mFailing; - - /** - * Error type (only for sent). - */ - protected int mError; - - public Envelope(int aId, int aParts) { - mId = aId; - mMessageId = -1; - mMessageTimestamp = 0; - mError = GeckoSmsManager.kNoError; - - int size = Envelope.SubParts.values().length; - mRemainingParts = new int[size]; - mFailing = new boolean[size]; - - for (int i=0; i<size; ++i) { - mRemainingParts[i] = aParts; - mFailing[i] = false; - } - } - - public void decreaseRemainingParts(Envelope.SubParts aType) { - --mRemainingParts[aType.ordinal()]; - - if (mRemainingParts[SubParts.SENT_PART.ordinal()] > - mRemainingParts[SubParts.DELIVERED_PART.ordinal()]) { - Log.e("GeckoSmsManager", "Delivered more parts than we sent!?"); - } - } - - public boolean arePartsRemaining(Envelope.SubParts aType) { - return mRemainingParts[aType.ordinal()] != 0; - } - - public void markAsFailed(Envelope.SubParts aType) { - mFailing[aType.ordinal()] = true; - } - - public boolean isFailing(Envelope.SubParts aType) { - return mFailing[aType.ordinal()]; - } - - public int getMessageId() { - return mMessageId; - } - - public void setMessageId(int aMessageId) { - mMessageId = aMessageId; - } - - public long getMessageTimestamp() { - return mMessageTimestamp; - } - - public void setMessageTimestamp(long aMessageTimestamp) { - mMessageTimestamp = aMessageTimestamp; - } - - public int getError() { - return mError; - } - - public void setError(int aError) { - mError = aError; - } -} - -/** - * Postman class is a singleton that manages Envelope instances. - */ -class Postman -{ - public static final int kUnknownEnvelopeId = -1; - - private static final Postman sInstance = new Postman(); - - private ArrayList<Envelope> mEnvelopes = new ArrayList<Envelope>(1); - - private Postman() {} - - public static Postman getInstance() { - return sInstance; - } - - public int createEnvelope(int aParts) { - /* - * We are going to create the envelope in the first empty slot in the array - * list. If there is no empty slot, we create a new one. - */ - int size = mEnvelopes.size(); - - for (int i=0; i<size; ++i) { - if (mEnvelopes.get(i) == null) { - mEnvelopes.set(i, new Envelope(i, aParts)); - return i; - } - } - - mEnvelopes.add(new Envelope(size, aParts)); - return size; - } - - public Envelope getEnvelope(int aId) { - if (aId < 0 || mEnvelopes.size() <= aId) { - Log.e("GeckoSmsManager", "Trying to get an unknown Envelope!"); - return null; - } - - Envelope envelope = mEnvelopes.get(aId); - if (envelope == null) { - Log.e("GeckoSmsManager", "Trying to get an empty Envelope!"); - } - - return envelope; - } - - public void destroyEnvelope(int aId) { - if (aId < 0 || mEnvelopes.size() <= aId) { - Log.e("GeckoSmsManager", "Trying to destroy an unknown Envelope!"); - return; - } - - if (mEnvelopes.set(aId, null) == null) { - Log.e("GeckoSmsManager", "Trying to destroy an empty Envelope!"); - } - } -} - -class SmsIOThread extends Thread { - private final static SmsIOThread sInstance = new SmsIOThread(); - - private Handler mHandler; - - public static SmsIOThread getInstance() { - return sInstance; - } - - public boolean execute(Runnable r) { - return mHandler.post(r); - } - - public void run() { - Looper.prepare(); - - mHandler = new Handler(); - - Looper.loop(); - } -} - -class MessagesListManager -{ - private static final MessagesListManager sInstance = new MessagesListManager(); - - public static MessagesListManager getInstance() { - return sInstance; - } - - private ArrayList<Cursor> mCursors = new ArrayList<Cursor>(0); - - public int add(Cursor aCursor) { - int size = mCursors.size(); - - for (int i=0; i<size; ++i) { - if (mCursors.get(i) == null) { - mCursors.set(i, aCursor); - return i; - } - } - - mCursors.add(aCursor); - return size; - } - - public Cursor get(int aId) { - if (aId < 0 || mCursors.size() <= aId) { - Log.e("GeckoSmsManager", "Trying to get an unknown list!"); - return null; - } - - Cursor cursor = mCursors.get(aId); - if (cursor == null) { - Log.e("GeckoSmsManager", "Trying to get an empty list!"); - } - - return cursor; - } - - public void remove(int aId) { - if (aId < 0 || mCursors.size() <= aId) { - Log.e("GeckoSmsManager", "Trying to destroy an unknown list!"); - return; - } - - Cursor cursor = mCursors.set(aId, null); - if (cursor == null) { - Log.e("GeckoSmsManager", "Trying to destroy an empty list!"); - return; - } - - cursor.close(); - } - - public void clear() { - for (int i=0; i<mCursors.size(); ++i) { - Cursor c = mCursors.get(i); - if (c != null) { - c.close(); - } - } - - mCursors.clear(); - } -} - -public class GeckoSmsManager - extends BroadcastReceiver - implements ISmsManager -{ - public final static String ACTION_SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED"; - public final static String ACTION_SMS_SENT = "org.mozilla.gecko.SMS_SENT"; - public final static String ACTION_SMS_DELIVERED = "org.mozilla.gecko.SMS_DELIVERED"; - - /* - * Make sure that the following error codes are in sync with the ones - * defined in dom/mobilemessage/interfaces/nsISmsRequestManager.idl. They are owned - * owned by the interface. - */ - public final static int kNoError = 0; - public final static int kNoSignalError = 1; - public final static int kNotFoundError = 2; - public final static int kUnknownError = 3; - public final static int kInternalError = 4; - public final static int kNoSimCardError = 5; - public final static int kRadioDisabledError = 6; - - private final static int kMaxMessageSize = 160; - - private final static Uri kSmsContentUri = Uri.parse("content://sms"); - private final static Uri kSmsSentContentUri = Uri.parse("content://sms/sent"); - - private final static int kSmsTypeInbox = 1; - private final static int kSmsTypeSentbox = 2; - - /* - * Keep the following state codes in syng with |DeliveryState| in: - * dom/mobilemessage/src/Types.h - */ - private final static int kDeliveryStateSent = 0; - private final static int kDeliveryStateReceived = 1; - private final static int kDeliveryStateSending = 2; - private final static int kDeliveryStateError = 3; - private final static int kDeliveryStateUnknown = 4; - private final static int kDeliveryStateNotDownloaded = 5; - private final static int kDeliveryStateEndGuard = 6; - - /* - * Keep the following status codes in sync with |DeliveryStatus| in: - * dom/mobilemessage/src/Types.h - */ - private final static int kDeliveryStatusNotApplicable = 0; - private final static int kDeliveryStatusSuccess = 1; - private final static int kDeliveryStatusPending = 2; - private final static int kDeliveryStatusError = 3; - - /* - * android.provider.Telephony.Sms.STATUS_*. Duplicated because they're not - * part of Android public API. - */ - private final static int kInternalDeliveryStatusNone = -1; - private final static int kInternalDeliveryStatusComplete = 0; - private final static int kInternalDeliveryStatusPending = 32; - private final static int kInternalDeliveryStatusFailed = 64; - - /* - * Keep the following values in sync with |MessageClass| in: - * dom/mobilemessage/src/Types.h - */ - private final static int kMessageClassNormal = 0; - private final static int kMessageClassClass0 = 1; - private final static int kMessageClassClass1 = 2; - private final static int kMessageClassClass2 = 3; - private final static int kMessageClassClass3 = 4; - - private final static String[] kRequiredMessageRows = new String[] { "_id", "address", "body", "date", "type", "status" }; - - public GeckoSmsManager() { - SmsIOThread.getInstance().start(); - } - - public void start() { - IntentFilter smsFilter = new IntentFilter(); - smsFilter.addAction(GeckoSmsManager.ACTION_SMS_RECEIVED); - smsFilter.addAction(GeckoSmsManager.ACTION_SMS_SENT); - smsFilter.addAction(GeckoSmsManager.ACTION_SMS_DELIVERED); - - GeckoApp.mAppContext.registerReceiver(this, smsFilter); - } - - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(ACTION_SMS_RECEIVED)) { - // TODO: Try to find the receiver number to be able to populate - // SmsMessage.receiver. - // TODO: Get the id and the date from the stock app saved message. - // Using the stock app saved message require us to wait for it to - // be saved which can lead to race conditions. - - Bundle bundle = intent.getExtras(); - - if (bundle == null) { - return; - } - - Object[] pdus = (Object[]) bundle.get("pdus"); - - for (int i=0; i<pdus.length; ++i) { - SmsMessage msg = SmsMessage.createFromPdu((byte[])pdus[i]); - - GeckoAppShell.notifySmsReceived(msg.getDisplayOriginatingAddress(), - msg.getDisplayMessageBody(), - getGeckoMessageClass(msg.getMessageClass()), - System.currentTimeMillis()); - } - - return; - } - - if (intent.getAction().equals(ACTION_SMS_SENT) || - intent.getAction().equals(ACTION_SMS_DELIVERED)) { - Bundle bundle = intent.getExtras(); - - if (bundle == null || !bundle.containsKey("envelopeId") || - !bundle.containsKey("number") || !bundle.containsKey("message") || - !bundle.containsKey("requestId")) { - Log.e("GeckoSmsManager", "Got an invalid ACTION_SMS_SENT/ACTION_SMS_DELIVERED!"); - return; - } - - int envelopeId = bundle.getInt("envelopeId"); - Postman postman = Postman.getInstance(); - - Envelope envelope = postman.getEnvelope(envelopeId); - if (envelope == null) { - Log.e("GeckoSmsManager", "Got an invalid envelope id (or Envelope has been destroyed)!"); - return; - } - - Envelope.SubParts part = intent.getAction().equals(ACTION_SMS_SENT) - ? Envelope.SubParts.SENT_PART - : Envelope.SubParts.DELIVERED_PART; - envelope.decreaseRemainingParts(part); - - - if (getResultCode() != Activity.RESULT_OK) { - switch (getResultCode()) { - case SmsManager.RESULT_ERROR_NULL_PDU: - envelope.setError(kInternalError); - break; - case SmsManager.RESULT_ERROR_NO_SERVICE: - case SmsManager.RESULT_ERROR_RADIO_OFF: - envelope.setError(kNoSignalError); - break; - case SmsManager.RESULT_ERROR_GENERIC_FAILURE: - default: - envelope.setError(kUnknownError); - break; - } - envelope.markAsFailed(part); - Log.i("GeckoSmsManager", "SMS part sending failed!"); - } - - if (envelope.arePartsRemaining(part)) { - return; - } - - if (envelope.isFailing(part)) { - if (part == Envelope.SubParts.SENT_PART) { - GeckoAppShell.notifySmsSendFailed(envelope.getError(), - bundle.getInt("requestId")); - Log.i("GeckoSmsManager", "SMS sending failed!"); - } else { - GeckoAppShell.notifySmsDelivery(envelope.getMessageId(), - kDeliveryStatusError, - bundle.getString("number"), - bundle.getString("message"), - envelope.getMessageTimestamp()); - Log.i("GeckoSmsManager", "SMS delivery failed!"); - } - } else { - if (part == Envelope.SubParts.SENT_PART) { - String number = bundle.getString("number"); - String message = bundle.getString("message"); - long timestamp = System.currentTimeMillis(); - - int id = saveSentMessage(number, message, timestamp); - - GeckoAppShell.notifySmsSent(id, number, message, timestamp, - bundle.getInt("requestId")); - - envelope.setMessageId(id); - envelope.setMessageTimestamp(timestamp); - - Log.i("GeckoSmsManager", "SMS sending was successfull!"); - } else { - GeckoAppShell.notifySmsDelivery(envelope.getMessageId(), - kDeliveryStatusSuccess, - bundle.getString("number"), - bundle.getString("message"), - envelope.getMessageTimestamp()); - Log.i("GeckoSmsManager", "SMS succesfully delivered!"); - } - } - - // Destroy the envelope object only if the SMS has been sent and delivered. - if (!envelope.arePartsRemaining(Envelope.SubParts.SENT_PART) && - !envelope.arePartsRemaining(Envelope.SubParts.DELIVERED_PART)) { - postman.destroyEnvelope(envelopeId); - } - - return; - } - } - - public void send(String aNumber, String aMessage, int aRequestId) { - int envelopeId = Postman.kUnknownEnvelopeId; - - try { - SmsManager sm = SmsManager.getDefault(); - - Intent sentIntent = new Intent(ACTION_SMS_SENT); - Intent deliveredIntent = new Intent(ACTION_SMS_DELIVERED); - - Bundle bundle = new Bundle(); - bundle.putString("number", aNumber); - bundle.putString("message", aMessage); - bundle.putInt("requestId", aRequestId); - - if (aMessage.length() <= kMaxMessageSize) { - envelopeId = Postman.getInstance().createEnvelope(1); - bundle.putInt("envelopeId", envelopeId); - - sentIntent.putExtras(bundle); - deliveredIntent.putExtras(bundle); - - /* - * There are a few things to know about getBroadcast and pending intents: - * - the pending intents are in a shared pool maintained by the system; - * - each pending intent is identified by a token; - * - when a new pending intent is created, if it has the same token as - * another intent in the pool, one of them has to be removed. - * - * To prevent having a hard time because of this situation, we give a - * unique id to all pending intents we are creating. This unique id is - * generated by GetPendingIntentUID(). - */ - PendingIntent sentPendingIntent = - PendingIntent.getBroadcast(GeckoApp.mAppContext, - PendingIntentUID.generate(), sentIntent, - PendingIntent.FLAG_CANCEL_CURRENT); - - PendingIntent deliveredPendingIntent = - PendingIntent.getBroadcast(GeckoApp.mAppContext, - PendingIntentUID.generate(), deliveredIntent, - PendingIntent.FLAG_CANCEL_CURRENT); - - sm.sendTextMessage(aNumber, "", aMessage, - sentPendingIntent, deliveredPendingIntent); - } else { - ArrayList<String> parts = sm.divideMessage(aMessage); - envelopeId = Postman.getInstance().createEnvelope(parts.size()); - bundle.putInt("envelopeId", envelopeId); - - sentIntent.putExtras(bundle); - deliveredIntent.putExtras(bundle); - - ArrayList<PendingIntent> sentPendingIntents = - new ArrayList<PendingIntent>(parts.size()); - ArrayList<PendingIntent> deliveredPendingIntents = - new ArrayList<PendingIntent>(parts.size()); - - for (int i=0; i<parts.size(); ++i) { - sentPendingIntents.add( - PendingIntent.getBroadcast(GeckoApp.mAppContext, - PendingIntentUID.generate(), sentIntent, - PendingIntent.FLAG_CANCEL_CURRENT) - ); - - deliveredPendingIntents.add( - PendingIntent.getBroadcast(GeckoApp.mAppContext, - PendingIntentUID.generate(), deliveredIntent, - PendingIntent.FLAG_CANCEL_CURRENT) - ); - } - - sm.sendMultipartTextMessage(aNumber, "", parts, sentPendingIntents, - deliveredPendingIntents); - } - } catch (Exception e) { - Log.e("GeckoSmsManager", "Failed to send an SMS: ", e); - - if (envelopeId != Postman.kUnknownEnvelopeId) { - Postman.getInstance().destroyEnvelope(envelopeId); - } - - GeckoAppShell.notifySmsSendFailed(kUnknownError, aRequestId); - } - } - - public int saveSentMessage(String aRecipient, String aBody, long aDate) { - try { - ContentValues values = new ContentValues(); - values.put("address", aRecipient); - values.put("body", aBody); - values.put("date", aDate); - // Always 'PENDING' because we always request status report. - values.put("status", kInternalDeliveryStatusPending); - - ContentResolver cr = GeckoApp.mAppContext.getContentResolver(); - Uri uri = cr.insert(kSmsSentContentUri, values); - - long id = ContentUris.parseId(uri); - - // The DOM API takes a 32bits unsigned int for the id. It's unlikely that - // we happen to need more than that but it doesn't cost to check. - if (id > Integer.MAX_VALUE) { - throw new IdTooHighException(); - } - - return (int)id; - } catch (IdTooHighException e) { - Log.e("GeckoSmsManager", "The id we received is higher than the higher allowed value."); - return -1; - } catch (Exception e) { - Log.e("GeckoSmsManager", "Something went wrong when trying to write a sent message: " + e); - return -1; - } - } - - public void getMessage(int aMessageId, int aRequestId) { - class GetMessageRunnable implements Runnable { - private int mMessageId; - private int mRequestId; - - GetMessageRunnable(int aMessageId, int aRequestId) { - mMessageId = aMessageId; - mRequestId = aRequestId; - } - - @Override - public void run() { - Cursor cursor = null; - - try { - ContentResolver cr = GeckoApp.mAppContext.getContentResolver(); - Uri message = ContentUris.withAppendedId(kSmsContentUri, mMessageId); - - cursor = cr.query(message, kRequiredMessageRows, null, null, null); - if (cursor == null || cursor.getCount() == 0) { - throw new NotFoundException(); - } - - if (cursor.getCount() != 1) { - throw new TooManyResultsException(); - } - - cursor.moveToFirst(); - - if (cursor.getInt(cursor.getColumnIndex("_id")) != mMessageId) { - throw new UnmatchingIdException(); - } - - int type = cursor.getInt(cursor.getColumnIndex("type")); - int deliveryStatus; - String sender = ""; - String receiver = ""; - - if (type == kSmsTypeInbox) { - deliveryStatus = kDeliveryStatusSuccess; - sender = cursor.getString(cursor.getColumnIndex("address")); - } else if (type == kSmsTypeSentbox) { - deliveryStatus = getGeckoDeliveryStatus(cursor.getInt(cursor.getColumnIndex("status"))); - receiver = cursor.getString(cursor.getColumnIndex("address")); - } else { - throw new InvalidTypeException(); - } - - GeckoAppShell.notifyGetSms(cursor.getInt(cursor.getColumnIndex("_id")), - deliveryStatus, - receiver, sender, - cursor.getString(cursor.getColumnIndex("body")), - cursor.getLong(cursor.getColumnIndex("date")), - mRequestId); - } catch (NotFoundException e) { - Log.i("GeckoSmsManager", "Message id " + mMessageId + " not found"); - GeckoAppShell.notifyGetSmsFailed(kNotFoundError, mRequestId); - } catch (UnmatchingIdException e) { - Log.e("GeckoSmsManager", "Requested message id (" + mMessageId + - ") is different from the one we got."); - GeckoAppShell.notifyGetSmsFailed(kUnknownError, mRequestId); - } catch (TooManyResultsException e) { - Log.e("GeckoSmsManager", "Get too many results for id " + mMessageId); - GeckoAppShell.notifyGetSmsFailed(kUnknownError, mRequestId); - } catch (InvalidTypeException e) { - Log.i("GeckoSmsManager", "Message has an invalid type, we ignore it."); - GeckoAppShell.notifyGetSmsFailed(kNotFoundError, mRequestId); - } catch (Exception e) { - Log.e("GeckoSmsManager", "Error while trying to get message: " + e); - GeckoAppShell.notifyGetSmsFailed(kUnknownError, mRequestId); - } finally { - if (cursor != null) { - cursor.close(); - } - } - } - } - - if (!SmsIOThread.getInstance().execute(new GetMessageRunnable(aMessageId, aRequestId))) { - Log.e("GeckoSmsManager", "Failed to add GetMessageRunnable to the SmsIOThread"); - GeckoAppShell.notifyGetSmsFailed(kUnknownError, aRequestId); - } - } - - public void deleteMessage(int aMessageId, int aRequestId) { - class DeleteMessageRunnable implements Runnable { - private int mMessageId; - private int mRequestId; - - DeleteMessageRunnable(int aMessageId, int aRequestId) { - mMessageId = aMessageId; - mRequestId = aRequestId; - } - - @Override - public void run() { - try { - ContentResolver cr = GeckoApp.mAppContext.getContentResolver(); - Uri message = ContentUris.withAppendedId(kSmsContentUri, mMessageId); - - int count = cr.delete(message, null, null); - - if (count > 1) { - throw new TooManyResultsException(); - } - - GeckoAppShell.notifySmsDeleted(count == 1, mRequestId); - } catch (TooManyResultsException e) { - Log.e("GeckoSmsManager", "Delete more than one message? " + e); - GeckoAppShell.notifySmsDeleteFailed(kUnknownError, mRequestId); - } catch (Exception e) { - Log.e("GeckoSmsManager", "Error while trying to delete a message: " + e); - GeckoAppShell.notifySmsDeleteFailed(kUnknownError, mRequestId); - } - } - } - - if (!SmsIOThread.getInstance().execute(new DeleteMessageRunnable(aMessageId, aRequestId))) { - Log.e("GeckoSmsManager", "Failed to add GetMessageRunnable to the SmsIOThread"); - GeckoAppShell.notifySmsDeleteFailed(kUnknownError, aRequestId); - } - } - - public void createMessageList(long aStartDate, long aEndDate, String[] aNumbers, int aNumbersCount, int aDeliveryState, boolean aReverse, int aRequestId) { - class CreateMessageListRunnable implements Runnable { - private long mStartDate; - private long mEndDate; - private String[] mNumbers; - private int mNumbersCount; - private int mDeliveryState; - private boolean mReverse; - private int mRequestId; - - CreateMessageListRunnable(long aStartDate, long aEndDate, String[] aNumbers, int aNumbersCount, int aDeliveryState, boolean aReverse, int aRequestId) { - mStartDate = aStartDate; - mEndDate = aEndDate; - mNumbers = aNumbers; - mNumbersCount = aNumbersCount; - mDeliveryState = aDeliveryState; - mReverse = aReverse; - mRequestId = aRequestId; - } - - @Override - public void run() { - Cursor cursor = null; - boolean closeCursor = true; - - try { - // TODO: should use the |selectionArgs| argument in |ContentResolver.query()|. - ArrayList<String> restrictions = new ArrayList<String>(); - - if (mStartDate != 0) { - restrictions.add("date >= " + mStartDate); - } - - if (mEndDate != 0) { - restrictions.add("date <= " + mEndDate); - } - - if (mNumbersCount > 0) { - String numberRestriction = "address IN ('" + mNumbers[0] + "'"; - - for (int i=1; i<mNumbersCount; ++i) { - numberRestriction += ", '" + mNumbers[i] + "'"; - } - numberRestriction += ")"; - - restrictions.add(numberRestriction); - } - - if (mDeliveryState == kDeliveryStateUnknown) { - restrictions.add("type IN ('" + kSmsTypeSentbox + "', '" + kSmsTypeInbox + "')"); - } else if (mDeliveryState == kDeliveryStateSent) { - restrictions.add("type = " + kSmsTypeSentbox); - } else if (mDeliveryState == kDeliveryStateReceived) { - restrictions.add("type = " + kSmsTypeInbox); - } else { - throw new UnexpectedDeliveryStateException(); - } - - String restrictionText = restrictions.size() > 0 ? restrictions.get(0) : ""; - - for (int i=1; i<restrictions.size(); ++i) { - restrictionText += " AND " + restrictions.get(i); - } - - ContentResolver cr = GeckoApp.mAppContext.getContentResolver(); - cursor = cr.query(kSmsContentUri, kRequiredMessageRows, restrictionText, null, - mReverse ? "date DESC" : "date ASC"); - - if (cursor.getCount() == 0) { - GeckoAppShell.notifyNoMessageInList(mRequestId); - return; - } - - cursor.moveToFirst(); - - int type = cursor.getInt(cursor.getColumnIndex("type")); - int deliveryStatus; - String sender = ""; - String receiver = ""; - - if (type == kSmsTypeInbox) { - deliveryStatus = kDeliveryStatusSuccess; - sender = cursor.getString(cursor.getColumnIndex("address")); - } else if (type == kSmsTypeSentbox) { - deliveryStatus = getGeckoDeliveryStatus(cursor.getInt(cursor.getColumnIndex("status"))); - receiver = cursor.getString(cursor.getColumnIndex("address")); - } else { - throw new UnexpectedDeliveryStateException(); - } - - int listId = MessagesListManager.getInstance().add(cursor); - closeCursor = false; - GeckoAppShell.notifyListCreated(listId, - cursor.getInt(cursor.getColumnIndex("_id")), - deliveryStatus, - receiver, sender, - cursor.getString(cursor.getColumnIndex("body")), - cursor.getLong(cursor.getColumnIndex("date")), - mRequestId); - } catch (UnexpectedDeliveryStateException e) { - Log.e("GeckoSmsManager", "Unexcepted delivery state type: " + e); - GeckoAppShell.notifyReadingMessageListFailed(kUnknownError, mRequestId); - } catch (Exception e) { - Log.e("GeckoSmsManager", "Error while trying to create a message list cursor: " + e); - GeckoAppShell.notifyReadingMessageListFailed(kUnknownError, mRequestId); - } finally { - // Close the cursor if MessagesListManager isn't taking care of it. - // We could also just check if it is in the MessagesListManager list but - // that would be less efficient. - if (cursor != null && closeCursor) { - cursor.close(); - } - } - } - } - - if (!SmsIOThread.getInstance().execute(new CreateMessageListRunnable(aStartDate, aEndDate, aNumbers, aNumbersCount, aDeliveryState, aReverse, aRequestId))) { - Log.e("GeckoSmsManager", "Failed to add CreateMessageListRunnable to the SmsIOThread"); - GeckoAppShell.notifyReadingMessageListFailed(kUnknownError, aRequestId); - } - } - - public void getNextMessageInList(int aListId, int aRequestId) { - class GetNextMessageInListRunnable implements Runnable { - private int mListId; - private int mRequestId; - - GetNextMessageInListRunnable(int aListId, int aRequestId) { - mListId = aListId; - mRequestId = aRequestId; - } - - @Override - public void run() { - try { - Cursor cursor = MessagesListManager.getInstance().get(mListId); - - if (!cursor.moveToNext()) { - MessagesListManager.getInstance().remove(mListId); - GeckoAppShell.notifyNoMessageInList(mRequestId); - return; - } - - int type = cursor.getInt(cursor.getColumnIndex("type")); - int deliveryStatus; - String sender = ""; - String receiver = ""; - - if (type == kSmsTypeInbox) { - deliveryStatus = kDeliveryStatusSuccess; - sender = cursor.getString(cursor.getColumnIndex("address")); - } else if (type == kSmsTypeSentbox) { - deliveryStatus = getGeckoDeliveryStatus(cursor.getInt(cursor.getColumnIndex("status"))); - receiver = cursor.getString(cursor.getColumnIndex("address")); - } else { - throw new UnexpectedDeliveryStateException(); - } - - int listId = MessagesListManager.getInstance().add(cursor); - GeckoAppShell.notifyGotNextMessage(cursor.getInt(cursor.getColumnIndex("_id")), - deliveryStatus, - receiver, sender, - cursor.getString(cursor.getColumnIndex("body")), - cursor.getLong(cursor.getColumnIndex("date")), - mRequestId); - } catch (UnexpectedDeliveryStateException e) { - Log.e("GeckoSmsManager", "Unexcepted delivery state type: " + e); - GeckoAppShell.notifyReadingMessageListFailed(kUnknownError, mRequestId); - } catch (Exception e) { - Log.e("GeckoSmsManager", "Error while trying to get the next message of a list: " + e); - GeckoAppShell.notifyReadingMessageListFailed(kUnknownError, mRequestId); - } - } - } - - if (!SmsIOThread.getInstance().execute(new GetNextMessageInListRunnable(aListId, aRequestId))) { - Log.e("GeckoSmsManager", "Failed to add GetNextMessageInListRunnable to the SmsIOThread"); - GeckoAppShell.notifyReadingMessageListFailed(kUnknownError, aRequestId); - } - } - - public void clearMessageList(int aListId) { - MessagesListManager.getInstance().remove(aListId); - } - - public void stop() { - GeckoApp.mAppContext.unregisterReceiver(this); - } - - public void shutdown() { - SmsIOThread.getInstance().interrupt(); - MessagesListManager.getInstance().clear(); - } - - private int getGeckoDeliveryStatus(int aDeliveryStatus) { - if (aDeliveryStatus == kInternalDeliveryStatusNone) { - return kDeliveryStatusNotApplicable; - } - if (aDeliveryStatus >= kInternalDeliveryStatusFailed) { - return kDeliveryStatusError; - } - if (aDeliveryStatus >= kInternalDeliveryStatusPending) { - return kDeliveryStatusPending; - } - return kDeliveryStatusSuccess; - } - - private int getGeckoMessageClass(MessageClass aMessageClass) { - switch (aMessageClass) { - case UNKNOWN: - return kMessageClassNormal; - case CLASS_0: - return kMessageClassClass0; - case CLASS_1: - return kMessageClassClass1; - case CLASS_2: - return kMessageClassClass2; - case CLASS_3: - return kMessageClassClass3; - } - } - - class IdTooHighException extends Exception { - private static final long serialVersionUID = 395697882128640L; - } - - class InvalidTypeException extends Exception { - private static final long serialVersionUID = 23359904803795434L; - } - - class NotFoundException extends Exception { - private static final long serialVersionUID = 266226999371957426L; - } - - class TooManyResultsException extends Exception { - private static final long serialVersionUID = 48899777673841920L; - } - - class UnexpectedDeliveryStateException extends Exception { - private static final long serialVersionUID = 5044567998961920L; - } - - class UnmatchingIdException extends Exception { - private static final long serialVersionUID = 1935649715512128L; - } -} diff --git a/gfx/layers/ipc/GeckoContentController.h b/gfx/layers/ipc/GeckoContentController.h deleted file mode 100644 index 7f59d47db..000000000 --- a/gfx/layers/ipc/GeckoContentController.h +++ /dev/null @@ -1,70 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set sw=4 ts=8 et tw=80 : */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_layers_GeckoContentController_h -#define mozilla_layers_GeckoContentController_h - -#include "FrameMetrics.h" -#include "nsISupportsImpl.h" - -class Task; - -namespace mozilla { -namespace layers { - -class GeckoContentController { -public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GeckoContentController) - - /** - * Requests a paint of the given FrameMetrics |aFrameMetrics| from Gecko. - * Implementations per-platform are responsible for actually handling this. - */ - virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) = 0; - - /** - * Requests handling of a double tap. |aPoint| is in CSS pixels, relative to - * the current scroll offset. This should eventually round-trip back to - * AsyncPanZoomController::ZoomToRect with the dimensions that we want to zoom - * to. - */ - virtual void HandleDoubleTap(const CSSIntPoint& aPoint) = 0; - - /** - * Requests handling a single tap. |aPoint| is in CSS pixels, relative to the - * current scroll offset. This should simulate and send to content a mouse - * button down, then mouse button up at |aPoint|. - */ - virtual void HandleSingleTap(const CSSIntPoint& aPoint) = 0; - - /** - * Requests handling a long tap. |aPoint| is in CSS pixels, relative to the - * current scroll offset. - */ - virtual void HandleLongTap(const CSSIntPoint& aPoint) = 0; - - /** - * Requests sending a mozbrowserasyncscroll domevent to embedder. - * |aContentRect| is in CSS pixels, relative to the current cssPage. - * |aScrollableSize| is the current content width/height in CSS pixels. - */ - virtual void SendAsyncScrollDOMEvent(const CSSRect &aContentRect, - const CSSSize &aScrollableSize) = 0; - - /** - * Schedules a runnable to run on the controller/UI thread at some time - * in the future. - */ - virtual void PostDelayedTask(Task* aTask, int aDelayMs) = 0; - - GeckoContentController() {} - virtual ~GeckoContentController() {} -}; - -} -} - -#endif // mozilla_layers_GeckoContentController_h diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp deleted file mode 100644 index b01062ffd..000000000 --- a/ipc/glue/GeckoChildProcessHost.cpp +++ /dev/null @@ -1,801 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "GeckoChildProcessHost.h" - -#include "base/command_line.h" -#include "base/path_service.h" -#include "base/string_util.h" -#include "chrome/common/chrome_switches.h" -#include "chrome/common/process_watcher.h" -#ifdef MOZ_WIDGET_COCOA -#include "chrome/common/mach_ipc_mac.h" -#include "base/rand_util.h" -#include "nsILocalFileMac.h" -#endif - -#include "prprf.h" -#include "prenv.h" - -#if defined(OS_LINUX) -# define XP_LINUX 1 -#endif - -#include "nsDirectoryServiceDefs.h" -#include "nsIFile.h" - -#include "mozilla/ipc/BrowserProcessSubThread.h" -#include "mozilla/Omnijar.h" -#include <sys/stat.h> - -#ifdef XP_WIN -#include "nsIWinTaskbar.h" -#define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1" -#endif - -using mozilla::MonitorAutoLock; -using mozilla::ipc::GeckoChildProcessHost; - -#ifdef ANDROID -// Like its predecessor in nsExceptionHandler.cpp, this is -// the magic number of a file descriptor remapping we must -// preserve for the child process. -static const int kMagicAndroidSystemPropFd = 5; -#endif - -static const bool kLowRightsSubprocesses = - // We currently only attempt to drop privileges on gonk, because we - // have no plugins or extensions to worry about breaking. -#ifdef MOZ_WIDGET_GONK - true -#else - false -#endif - ; - -static bool -ShouldHaveDirectoryService() -{ - return GeckoProcessType_Default == XRE_GetProcessType(); -} - -template<> -struct RunnableMethodTraits<GeckoChildProcessHost> -{ - static void RetainCallee(GeckoChildProcessHost* obj) { } - static void ReleaseCallee(GeckoChildProcessHost* obj) { } -}; - -/*static*/ -base::ChildPrivileges -GeckoChildProcessHost::DefaultChildPrivileges() -{ - return (kLowRightsSubprocesses ? - base::PRIVILEGES_UNPRIVILEGED : base::PRIVILEGES_INHERIT); -} - -GeckoChildProcessHost::GeckoChildProcessHost(GeckoProcessType aProcessType, - ChildPrivileges aPrivileges) - : ChildProcessHost(RENDER_PROCESS), // FIXME/cjones: we should own this enum - mProcessType(aProcessType), - mPrivileges(aPrivileges), - mMonitor("mozilla.ipc.GeckChildProcessHost.mMonitor"), - mProcessState(CREATING_CHANNEL), - mDelegate(nullptr), - mChildProcessHandle(0) -#if defined(MOZ_WIDGET_COCOA) - , mChildTask(MACH_PORT_NULL) -#endif -{ - MOZ_COUNT_CTOR(GeckoChildProcessHost); - - MessageLoop* ioLoop = XRE_GetIOMessageLoop(); - ioLoop->PostTask(FROM_HERE, - NewRunnableMethod(this, - &GeckoChildProcessHost::InitializeChannel)); -} - -GeckoChildProcessHost::~GeckoChildProcessHost() - -{ - AssertIOThread(); - - MOZ_COUNT_DTOR(GeckoChildProcessHost); - - if (mChildProcessHandle > 0) - ProcessWatcher::EnsureProcessTerminated(mChildProcessHandle -#if defined(NS_BUILD_REFCNT_LOGGING) - , false // don't "force" -#endif - ); - -#if defined(MOZ_WIDGET_COCOA) - if (mChildTask != MACH_PORT_NULL) - mach_port_deallocate(mach_task_self(), mChildTask); -#endif -} - -void GetPathToBinary(FilePath& exePath) -{ - if (ShouldHaveDirectoryService()) { - nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID)); - NS_ASSERTION(directoryService, "Expected XPCOM to be available"); - if (directoryService) { - nsCOMPtr<nsIFile> greDir; - nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(greDir)); - if (NS_SUCCEEDED(rv)) { -#ifdef OS_WIN - nsString path; - greDir->GetPath(path); -#else - nsCString path; - greDir->GetNativePath(path); -#endif - exePath = FilePath(path.get()); -#ifdef MOZ_WIDGET_COCOA - // We need to use an App Bundle on OS X so that we can hide - // the dock icon. See Bug 557225. - exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_BUNDLE); -#endif - } - } - } - - if (exePath.empty()) { -#ifdef OS_WIN - exePath = FilePath::FromWStringHack(CommandLine::ForCurrentProcess()->program()); -#else - exePath = FilePath(CommandLine::ForCurrentProcess()->argv()[0]); -#endif - exePath = exePath.DirName(); - } - - exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_NAME); -} - -#ifdef MOZ_WIDGET_COCOA -class AutoCFTypeObject { -public: - AutoCFTypeObject(CFTypeRef object) - { - mObject = object; - } - ~AutoCFTypeObject() - { - ::CFRelease(mObject); - } -private: - CFTypeRef mObject; -}; -#endif - -nsresult GeckoChildProcessHost::GetArchitecturesForBinary(const char *path, uint32_t *result) -{ - *result = 0; - -#ifdef MOZ_WIDGET_COCOA - CFURLRef url = ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, - (const UInt8*)path, - strlen(path), - false); - if (!url) { - return NS_ERROR_FAILURE; - } - AutoCFTypeObject autoPluginContainerURL(url); - - CFArrayRef pluginContainerArchs = ::CFBundleCopyExecutableArchitecturesForURL(url); - if (!pluginContainerArchs) { - return NS_ERROR_FAILURE; - } - AutoCFTypeObject autoPluginContainerArchs(pluginContainerArchs); - - CFIndex pluginArchCount = ::CFArrayGetCount(pluginContainerArchs); - for (CFIndex i = 0; i < pluginArchCount; i++) { - CFNumberRef currentArch = static_cast<CFNumberRef>(::CFArrayGetValueAtIndex(pluginContainerArchs, i)); - int currentArchInt = 0; - if (!::CFNumberGetValue(currentArch, kCFNumberIntType, ¤tArchInt)) { - continue; - } - switch (currentArchInt) { - case kCFBundleExecutableArchitectureI386: - *result |= base::PROCESS_ARCH_I386; - break; - case kCFBundleExecutableArchitectureX86_64: - *result |= base::PROCESS_ARCH_X86_64; - break; - case kCFBundleExecutableArchitecturePPC: - *result |= base::PROCESS_ARCH_PPC; - break; - default: - break; - } - } - - return (*result ? NS_OK : NS_ERROR_FAILURE); -#else - return NS_ERROR_NOT_IMPLEMENTED; -#endif -} - -uint32_t GeckoChildProcessHost::GetSupportedArchitecturesForProcessType(GeckoProcessType type) -{ -#ifdef MOZ_WIDGET_COCOA - if (type == GeckoProcessType_Plugin) { - // Cache this, it shouldn't ever change. - static uint32_t pluginContainerArchs = 0; - if (pluginContainerArchs == 0) { - FilePath exePath; - GetPathToBinary(exePath); - nsresult rv = GetArchitecturesForBinary(exePath.value().c_str(), &pluginContainerArchs); - NS_ASSERTION(NS_SUCCEEDED(rv) && pluginContainerArchs != 0, "Getting architecture of plugin container failed!"); - if (NS_FAILED(rv) || pluginContainerArchs == 0) { - pluginContainerArchs = base::GetCurrentProcessArchitecture(); - } - } - return pluginContainerArchs; - } -#endif - - return base::GetCurrentProcessArchitecture(); -} - -void -GeckoChildProcessHost::PrepareLaunch() -{ -#ifdef XP_WIN - InitWindowsGroupID(); -#endif -} - -#ifdef XP_WIN -void GeckoChildProcessHost::InitWindowsGroupID() -{ - // On Win7+, pass the application user model to the child, so it can - // register with it. This insures windows created by the container - // properly group with the parent app on the Win7 taskbar. - nsCOMPtr<nsIWinTaskbar> taskbarInfo = - do_GetService(NS_TASKBAR_CONTRACTID); - if (taskbarInfo) { - bool isSupported = false; - taskbarInfo->GetAvailable(&isSupported); - nsAutoString appId; - if (isSupported && NS_SUCCEEDED(taskbarInfo->GetDefaultGroupId(appId))) { - mGroupId.Append(appId); - } else { - mGroupId.AssignLiteral("-"); - } - } -} -#endif - -bool -GeckoChildProcessHost::SyncLaunch(std::vector<std::string> aExtraOpts, int aTimeoutMs, base::ProcessArchitecture arch) -{ - PrepareLaunch(); - - PRIntervalTime timeoutTicks = (aTimeoutMs > 0) ? - PR_MillisecondsToInterval(aTimeoutMs) : PR_INTERVAL_NO_TIMEOUT; - MessageLoop* ioLoop = XRE_GetIOMessageLoop(); - NS_ASSERTION(MessageLoop::current() != ioLoop, "sync launch from the IO thread NYI"); - - ioLoop->PostTask(FROM_HERE, - NewRunnableMethod(this, - &GeckoChildProcessHost::PerformAsyncLaunch, - aExtraOpts, arch)); - // NB: this uses a different mechanism than the chromium parent - // class. - MonitorAutoLock lock(mMonitor); - PRIntervalTime waitStart = PR_IntervalNow(); - PRIntervalTime current; - - // We'll receive several notifications, we need to exit when we - // have either successfully launched or have timed out. - while (mProcessState < PROCESS_CONNECTED) { - lock.Wait(timeoutTicks); - - if (timeoutTicks != PR_INTERVAL_NO_TIMEOUT) { - current = PR_IntervalNow(); - PRIntervalTime elapsed = current - waitStart; - if (elapsed > timeoutTicks) { - break; - } - timeoutTicks = timeoutTicks - elapsed; - waitStart = current; - } - } - - return mProcessState == PROCESS_CONNECTED; -} - -bool -GeckoChildProcessHost::AsyncLaunch(std::vector<std::string> aExtraOpts) -{ - PrepareLaunch(); - - MessageLoop* ioLoop = XRE_GetIOMessageLoop(); - ioLoop->PostTask(FROM_HERE, - NewRunnableMethod(this, - &GeckoChildProcessHost::PerformAsyncLaunch, - aExtraOpts, base::GetCurrentProcessArchitecture())); - - // This may look like the sync launch wait, but we only delay as - // long as it takes to create the channel. - MonitorAutoLock lock(mMonitor); - while (mProcessState < CHANNEL_INITIALIZED) { - lock.Wait(); - } - - return true; -} - -bool -GeckoChildProcessHost::LaunchAndWaitForProcessHandle(StringVector aExtraOpts) -{ - PrepareLaunch(); - - MessageLoop* ioLoop = XRE_GetIOMessageLoop(); - ioLoop->PostTask(FROM_HERE, - NewRunnableMethod(this, - &GeckoChildProcessHost::PerformAsyncLaunch, - aExtraOpts, base::GetCurrentProcessArchitecture())); - - MonitorAutoLock lock(mMonitor); - while (mProcessState < PROCESS_CREATED) { - lock.Wait(); - } - MOZ_ASSERT(mProcessState == PROCESS_ERROR || mChildProcessHandle); - - return mProcessState < PROCESS_ERROR; -} - -void -GeckoChildProcessHost::InitializeChannel() -{ - CreateChannel(); - - MonitorAutoLock lock(mMonitor); - mProcessState = CHANNEL_INITIALIZED; - lock.Notify(); -} - -void -GeckoChildProcessHost::Join() -{ - AssertIOThread(); - - if (!mChildProcessHandle) { - return; - } - - // If this fails, there's nothing we can do. - base::KillProcess(mChildProcessHandle, 0, /*wait*/true); - mChildProcessHandle = 0; -} - -int32_t GeckoChildProcessHost::mChildCounter = 0; - -// -// Wrapper function for handling GECKO_SEPARATE_NSPR_LOGS -// -bool -GeckoChildProcessHost::PerformAsyncLaunch(std::vector<std::string> aExtraOpts, base::ProcessArchitecture arch) -{ - // If separate NSPR log files are not requested, we're done. - const char* origLogName = PR_GetEnv("NSPR_LOG_FILE"); - const char* separateLogs = PR_GetEnv("GECKO_SEPARATE_NSPR_LOGS"); - if (!origLogName || !separateLogs || !*separateLogs || - *separateLogs == '0' || *separateLogs == 'N' || *separateLogs == 'n') { - return PerformAsyncLaunchInternal(aExtraOpts, arch); - } - - // We currently have no portable way to launch child with environment - // different than parent. So temporarily change NSPR_LOG_FILE so child - // inherits value we want it to have. (NSPR only looks at NSPR_LOG_FILE at - // startup, so it's 'safe' to play with the parent's environment this way.) - nsAutoCString setChildLogName("NSPR_LOG_FILE="); - setChildLogName.Append(origLogName); - - // remember original value so we can restore it. - // - buffer needs to be permanently allocated for PR_SetEnv() - // - Note: this code is not called re-entrantly, nor are restoreOrigLogName - // or mChildCounter touched by any other thread, so this is safe. - static char* restoreOrigLogName = 0; - if (!restoreOrigLogName) - restoreOrigLogName = strdup(setChildLogName.get()); - - // Append child-specific postfix to name - setChildLogName.AppendLiteral(".child-"); - setChildLogName.AppendInt(++mChildCounter); - - // Passing temporary to PR_SetEnv is ok here because env gets copied - // by exec, etc., to permanent storage in child when process launched. - PR_SetEnv(setChildLogName.get()); - bool retval = PerformAsyncLaunchInternal(aExtraOpts, arch); - - // Revert to original value - PR_SetEnv(restoreOrigLogName); - - return retval; -} - -void -#if defined(XP_WIN) -AddAppDirToCommandLine(CommandLine& aCmdLine) -#else -AddAppDirToCommandLine(std::vector<std::string>& aCmdLine) -#endif -{ - // Content processes need access to application resources, so pass - // the full application directory path to the child process. - if (ShouldHaveDirectoryService()) { - nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID)); - NS_ASSERTION(directoryService, "Expected XPCOM to be available"); - if (directoryService) { - nsCOMPtr<nsIFile> appDir; - // NS_XPCOM_CURRENT_PROCESS_DIR really means the app dir, not the - // current process dir. - nsresult rv = directoryService->Get(NS_XPCOM_CURRENT_PROCESS_DIR, - NS_GET_IID(nsIFile), - getter_AddRefs(appDir)); - if (NS_SUCCEEDED(rv)) { - nsAutoCString path; - appDir->GetNativePath(path); -#if defined(XP_WIN) - aCmdLine.AppendLooseValue(UTF8ToWide("-appdir")); - aCmdLine.AppendLooseValue(UTF8ToWide(path.get())); -#else - aCmdLine.push_back("-appdir"); - aCmdLine.push_back(path.get()); -#endif - } - } - } -} - -bool -GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExtraOpts, base::ProcessArchitecture arch) -{ - // We rely on the fact that InitializeChannel() has already been processed - // on the IO thread before this point is reached. - if (!GetChannel()) { - return false; - } - - base::ProcessHandle process; - - // send the child the PID so that it can open a ProcessHandle back to us. - // probably don't want to do this in the long run - char pidstring[32]; - PR_snprintf(pidstring, sizeof(pidstring) - 1, - "%ld", base::Process::Current().pid()); - - const char* const childProcessType = - XRE_ChildProcessTypeToString(mProcessType); - -//-------------------------------------------------- -#if defined(OS_POSIX) - // For POSIX, we have to be extremely anal about *not* using - // std::wstring in code compiled with Mozilla's -fshort-wchar - // configuration, because chromium is compiled with -fno-short-wchar - // and passing wstrings from one config to the other is unsafe. So - // we split the logic here. - -#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_BSD) - base::environment_map newEnvVars; - ChildPrivileges privs = mPrivileges; - if (privs == base::PRIVILEGES_DEFAULT) { - privs = DefaultChildPrivileges(); - } - // XPCOM may not be initialized in some subprocesses. We don't want - // to initialize XPCOM just for the directory service, especially - // since LD_LIBRARY_PATH is already set correctly in subprocesses - // (meaning that we don't need to set that up in the environment). - if (ShouldHaveDirectoryService()) { - nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID)); - NS_ASSERTION(directoryService, "Expected XPCOM to be available"); - if (directoryService) { - nsCOMPtr<nsIFile> greDir; - nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(greDir)); - if (NS_SUCCEEDED(rv)) { - nsCString path; - greDir->GetNativePath(path); -# if defined(OS_LINUX) || defined(OS_BSD) -# if defined(MOZ_WIDGET_ANDROID) - path += "/lib"; -# endif // MOZ_WIDGET_ANDROID - const char *ld_library_path = PR_GetEnv("LD_LIBRARY_PATH"); - nsCString new_ld_lib_path; - if (ld_library_path && *ld_library_path) { - new_ld_lib_path.Assign(path.get()); - new_ld_lib_path.AppendLiteral(":"); - new_ld_lib_path.Append(ld_library_path); - newEnvVars["LD_LIBRARY_PATH"] = new_ld_lib_path.get(); - } else { - newEnvVars["LD_LIBRARY_PATH"] = path.get(); - } -# elif OS_MACOSX - newEnvVars["DYLD_LIBRARY_PATH"] = path.get(); - // XXX DYLD_INSERT_LIBRARIES should only be set when launching a plugin - // process, and has no effect on other subprocesses (the hooks in - // libplugin_child_interpose.dylib become noops). But currently it - // gets set when launching any kind of subprocess. - // - // Trigger "dyld interposing" for the dylib that contains - // plugin_child_interpose.mm. This allows us to hook OS calls in the - // plugin process (ones that don't work correctly in a background - // process). Don't break any other "dyld interposing" that has already - // been set up by whatever may have launched the browser. - const char* prevInterpose = PR_GetEnv("DYLD_INSERT_LIBRARIES"); - nsCString interpose; - if (prevInterpose) { - interpose.Assign(prevInterpose); - interpose.AppendLiteral(":"); - } - interpose.Append(path.get()); - interpose.AppendLiteral("/libplugin_child_interpose.dylib"); - newEnvVars["DYLD_INSERT_LIBRARIES"] = interpose.get(); -# endif // OS_LINUX - } - } - } -#endif // OS_LINUX || OS_MACOSX - - FilePath exePath; - GetPathToBinary(exePath); - -#ifdef MOZ_WIDGET_ANDROID - // The java wrapper unpacks this for us but can't make it executable - chmod(exePath.value().c_str(), 0700); -#endif // MOZ_WIDGET_ANDROID - -#ifdef ANDROID - // Remap the Android property workspace to a well-known int, - // and update the environment to reflect the new value for the - // child process. - const char *apws = getenv("ANDROID_PROPERTY_WORKSPACE"); - if (apws) { - int fd = atoi(apws); - mFileMap.push_back(std::pair<int, int>(fd, kMagicAndroidSystemPropFd)); - - char buf[32]; - char *szptr = strchr(apws, ','); - - snprintf(buf, sizeof(buf), "%d%s", kMagicAndroidSystemPropFd, szptr); - newEnvVars["ANDROID_PROPERTY_WORKSPACE"] = buf; - } -#endif // ANDROID - -#ifdef MOZ_WIDGET_GONK - if (const char *ldPreloadPath = getenv("LD_PRELOAD")) { - newEnvVars["LD_PRELOAD"] = ldPreloadPath; - } -#endif // MOZ_WIDGET_GONK - - // remap the IPC socket fd to a well-known int, as the OS does for - // STDOUT_FILENO, for example - int srcChannelFd, dstChannelFd; - channel().GetClientFileDescriptorMapping(&srcChannelFd, &dstChannelFd); - mFileMap.push_back(std::pair<int,int>(srcChannelFd, dstChannelFd)); - - // no need for kProcessChannelID, the child process inherits the - // other end of the socketpair() from us - - std::vector<std::string> childArgv; - - childArgv.push_back(exePath.value()); - - childArgv.insert(childArgv.end(), aExtraOpts.begin(), aExtraOpts.end()); - - if (Omnijar::IsInitialized()) { - // Make sure that child processes can find the omnijar - // See XRE_InitCommandLine in nsAppRunner.cpp - nsAutoCString path; - nsCOMPtr<nsIFile> file = Omnijar::GetPath(Omnijar::GRE); - if (file && NS_SUCCEEDED(file->GetNativePath(path))) { - childArgv.push_back("-greomni"); - childArgv.push_back(path.get()); - } - file = Omnijar::GetPath(Omnijar::APP); - if (file && NS_SUCCEEDED(file->GetNativePath(path))) { - childArgv.push_back("-appomni"); - childArgv.push_back(path.get()); - } - } - - // Add the application directory path (-appdir path) - AddAppDirToCommandLine(childArgv); - - childArgv.push_back(pidstring); - -#ifdef MOZ_WIDGET_COCOA - // Add a mach port to the command line so the child can communicate its - // 'task_t' back to the parent. - // - // Put a random number into the channel name, so that a compromised renderer - // can't pretend being the child that's forked off. - std::string mach_connection_name = StringPrintf("org.mozilla.machname.%d", - base::RandInt(0, std::numeric_limits<int>::max())); - childArgv.push_back(mach_connection_name.c_str()); -#endif - - childArgv.push_back(childProcessType); - - base::LaunchApp(childArgv, mFileMap, -#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_BSD) - newEnvVars, privs, -#endif - false, &process, arch); - -#ifdef MOZ_WIDGET_COCOA - // Wait for the child process to send us its 'task_t' data. - const int kTimeoutMs = 10000; - - MachReceiveMessage child_message; - ReceivePort parent_recv_port(mach_connection_name.c_str()); - kern_return_t err = parent_recv_port.WaitForMessage(&child_message, kTimeoutMs); - if (err != KERN_SUCCESS) { - std::string errString = StringPrintf("0x%x %s", err, mach_error_string(err)); - LOG(ERROR) << "parent WaitForMessage() failed: " << errString; - return false; - } - - task_t child_task = child_message.GetTranslatedPort(0); - if (child_task == MACH_PORT_NULL) { - LOG(ERROR) << "parent GetTranslatedPort(0) failed."; - return false; - } - - if (child_message.GetTranslatedPort(1) == MACH_PORT_NULL) { - LOG(ERROR) << "parent GetTranslatedPort(1) failed."; - return false; - } - MachPortSender parent_sender(child_message.GetTranslatedPort(1)); - - MachSendMessage parent_message(/* id= */0); - if (!parent_message.AddDescriptor(bootstrap_port)) { - LOG(ERROR) << "parent AddDescriptor(" << bootstrap_port << ") failed."; - return false; - } - - err = parent_sender.SendMessage(parent_message, kTimeoutMs); - if (err != KERN_SUCCESS) { - std::string errString = StringPrintf("0x%x %s", err, mach_error_string(err)); - LOG(ERROR) << "parent SendMessage() failed: " << errString; - return false; - } -#endif - -//-------------------------------------------------- -#elif defined(OS_WIN) - - FilePath exePath; - GetPathToBinary(exePath); - - CommandLine cmdLine(exePath.ToWStringHack()); - cmdLine.AppendSwitchWithValue(switches::kProcessChannelID, channel_id()); - - for (std::vector<std::string>::iterator it = aExtraOpts.begin(); - it != aExtraOpts.end(); - ++it) { - cmdLine.AppendLooseValue(UTF8ToWide(*it)); - } - - if (Omnijar::IsInitialized()) { - // Make sure the child process can find the omnijar - // See XRE_InitCommandLine in nsAppRunner.cpp - nsAutoString path; - nsCOMPtr<nsIFile> file = Omnijar::GetPath(Omnijar::GRE); - if (file && NS_SUCCEEDED(file->GetPath(path))) { - cmdLine.AppendLooseValue(UTF8ToWide("-greomni")); - cmdLine.AppendLooseValue(path.get()); - } - file = Omnijar::GetPath(Omnijar::APP); - if (file && NS_SUCCEEDED(file->GetPath(path))) { - cmdLine.AppendLooseValue(UTF8ToWide("-appomni")); - cmdLine.AppendLooseValue(path.get()); - } - } - - // Add the application directory path (-appdir path) - AddAppDirToCommandLine(cmdLine); - - // XXX Command line params past this point are expected to be at - // the end of the command line string, and in a specific order. - // See XRE_InitChildProcess in nsEmbedFunction. - - // Win app model id - cmdLine.AppendLooseValue(std::wstring(mGroupId.get())); - - // Process id - cmdLine.AppendLooseValue(UTF8ToWide(pidstring)); - - // Process type - cmdLine.AppendLooseValue(UTF8ToWide(childProcessType)); - - base::LaunchApp(cmdLine, false, false, &process); - -#else -# error Sorry -#endif - - if (!process) { - MonitorAutoLock lock(mMonitor); - mProcessState = PROCESS_ERROR; - lock.Notify(); - return false; - } - // NB: on OS X, we block much longer than we need to in order to - // reach this call, waiting for the child process's task_t. The - // best way to fix that is to refactor this file, hard. - SetHandle(process); -#if defined(MOZ_WIDGET_COCOA) - mChildTask = child_task; -#endif - - OpenPrivilegedHandle(base::GetProcId(process)); - { - MonitorAutoLock lock(mMonitor); - mProcessState = PROCESS_CREATED; - lock.Notify(); - } - - return true; -} - -void -GeckoChildProcessHost::OpenPrivilegedHandle(base::ProcessId aPid) -{ - if (mChildProcessHandle) { - MOZ_ASSERT(aPid == base::GetProcId(mChildProcessHandle)); - return; - } - if (!base::OpenPrivilegedProcessHandle(aPid, &mChildProcessHandle)) { - NS_RUNTIMEABORT("can't open handle to child process"); - } -} - -void -GeckoChildProcessHost::OnChannelConnected(int32_t peer_pid) -{ - OpenPrivilegedHandle(peer_pid); - { - MonitorAutoLock lock(mMonitor); - mProcessState = PROCESS_CONNECTED; - lock.Notify(); - } -} - -void -GeckoChildProcessHost::OnMessageReceived(const IPC::Message& aMsg) -{ - // We never process messages ourself, just save them up for the next - // listener. - mQueue.push(aMsg); -} - -void -GeckoChildProcessHost::OnChannelError() -{ - // FIXME/bug 773925: save up this error for the next listener. -} - -void -GeckoChildProcessHost::GetQueuedMessages(std::queue<IPC::Message>& queue) -{ - // If this is called off the IO thread, bad things will happen. - DCHECK(MessageLoopForIO::current()); - swap(queue, mQueue); - // We expect the next listener to take over processing of our queue. -} - -void -GeckoChildProcessHost::OnWaitableEventSignaled(base::WaitableEvent *event) -{ - if (mDelegate) { - mDelegate->OnWaitableEventSignaled(event); - } - ChildProcessHost::OnWaitableEventSignaled(event); -} diff --git a/ipc/glue/GeckoChildProcessHost.h b/ipc/glue/GeckoChildProcessHost.h deleted file mode 100644 index 46c70307a..000000000 --- a/ipc/glue/GeckoChildProcessHost.h +++ /dev/null @@ -1,171 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef __IPC_GLUE_GECKOCHILDPROCESSHOST_H__ -#define __IPC_GLUE_GECKOCHILDPROCESSHOST_H__ - -#include "base/file_path.h" -#include "base/process_util.h" -#include "base/scoped_ptr.h" -#include "base/waitable_event.h" -#include "chrome/common/child_process_host.h" - -#include "mozilla/Monitor.h" - -#include "nsXULAppAPI.h" // for GeckoProcessType -#include "nsString.h" - -namespace mozilla { -namespace ipc { - -class GeckoChildProcessHost : public ChildProcessHost -{ -protected: - typedef mozilla::Monitor Monitor; - typedef std::vector<std::string> StringVector; - -public: - typedef base::ChildPrivileges ChildPrivileges; - typedef base::ProcessHandle ProcessHandle; - - static ChildPrivileges DefaultChildPrivileges(); - - GeckoChildProcessHost(GeckoProcessType aProcessType, - ChildPrivileges aPrivileges=base::PRIVILEGES_DEFAULT); - - ~GeckoChildProcessHost(); - - static nsresult GetArchitecturesForBinary(const char *path, uint32_t *result); - - static uint32_t GetSupportedArchitecturesForProcessType(GeckoProcessType type); - - // Block until the IPC channel for our subprocess is initialized, - // but no longer. The child process may or may not have been - // created when this method returns. - bool AsyncLaunch(StringVector aExtraOpts=StringVector()); - - // Block until the IPC channel for our subprocess is initialized and - // the OS process is created. The subprocess may or may not have - // connected back to us when this method returns. - // - // NB: on POSIX, this method is relatively cheap, and doesn't - // require disk IO. On win32 however, it requires at least the - // analogue of stat(). This difference induces a semantic - // difference in this method: on POSIX, when we return, we know the - // subprocess has been created, but we don't know whether its - // executable image can be loaded. On win32, we do know that when - // we return. But we don't know if dynamic linking succeeded on - // either platform. - bool LaunchAndWaitForProcessHandle(StringVector aExtraOpts=StringVector()); - - // Block until the child process has been created and it connects to - // the IPC channel, meaning it's fully initialized. (Or until an - // error occurs.) - bool SyncLaunch(StringVector aExtraOpts=StringVector(), - int32_t timeoutMs=0, - base::ProcessArchitecture arch=base::GetCurrentProcessArchitecture()); - - bool PerformAsyncLaunch(StringVector aExtraOpts=StringVector(), - base::ProcessArchitecture arch=base::GetCurrentProcessArchitecture()); - - virtual void OnChannelConnected(int32_t peer_pid); - virtual void OnMessageReceived(const IPC::Message& aMsg); - virtual void OnChannelError(); - virtual void GetQueuedMessages(std::queue<IPC::Message>& queue); - - void InitializeChannel(); - - virtual bool CanShutdown() { return true; } - - virtual void OnWaitableEventSignaled(base::WaitableEvent *event); - - IPC::Channel* GetChannel() { - return channelp(); - } - - base::WaitableEvent* GetShutDownEvent() { - return GetProcessEvent(); - } - - ProcessHandle GetChildProcessHandle() { - return mChildProcessHandle; - } - -#ifdef XP_MACOSX - task_t GetChildTask() { - return mChildTask; - } -#endif - - /** - * Must run on the IO thread. Cause the OS process to exit and - * ensure its OS resources are cleaned up. - */ - void Join(); - -protected: - GeckoProcessType mProcessType; - ChildPrivileges mPrivileges; - Monitor mMonitor; - FilePath mProcessPath; - // This value must be accessed while holding mMonitor. - enum { - // This object has been constructed, but the OS process has not - // yet. - CREATING_CHANNEL = 0, - // The IPC channel for our subprocess has been created, but the OS - // process has still not been created. - CHANNEL_INITIALIZED, - // The OS process has been created, but it hasn't yet connected to - // our IPC channel. - PROCESS_CREATED, - // The process is launched and connected to our IPC channel. All - // is well. - PROCESS_CONNECTED, - PROCESS_ERROR - } mProcessState; - - static int32_t mChildCounter; - - void PrepareLaunch(); - -#ifdef XP_WIN - void InitWindowsGroupID(); - nsString mGroupId; -#endif - -#if defined(OS_POSIX) - base::file_handle_mapping_vector mFileMap; -#endif - - base::WaitableEventWatcher::Delegate* mDelegate; - - ProcessHandle mChildProcessHandle; -#if defined(OS_MACOSX) - task_t mChildTask; -#endif - -private: - DISALLOW_EVIL_CONSTRUCTORS(GeckoChildProcessHost); - - // Does the actual work for AsyncLaunch, on the IO thread. - bool PerformAsyncLaunchInternal(std::vector<std::string>& aExtraOpts, - base::ProcessArchitecture arch); - - void OpenPrivilegedHandle(base::ProcessId aPid); - - // In between launching the subprocess and handing off its IPC - // channel, there's a small window of time in which *we* might still - // be the channel listener, and receive messages. That's bad - // because we have no idea what to do with those messages. So queue - // them here until we hand off the eventual listener. - // - // FIXME/cjones: this strongly indicates bad design. Shame on us. - std::queue<IPC::Message> mQueue; -}; - -} /* namespace ipc */ -} /* namespace mozilla */ - -#endif /* __IPC_GLUE_GECKOCHILDPROCESSHOST_H__ */ diff --git a/mobile/android/base/GeckoAccessibility.java b/mobile/android/base/GeckoAccessibility.java deleted file mode 100644 index 5f133fcda..000000000 --- a/mobile/android/base/GeckoAccessibility.java +++ /dev/null @@ -1,379 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import org.mozilla.gecko.gfx.LayerView; -import org.mozilla.gecko.util.ThreadUtils; -import org.mozilla.gecko.util.UiAsyncTask; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import android.app.ActivityManager; -import android.app.ActivityManager.RunningServiceInfo; -import android.content.Context; -import android.graphics.Rect; -import android.os.Build; -import android.os.Bundle; -import android.util.Log; -import android.view.View; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.AccessibilityNodeInfo; -import android.view.accessibility.AccessibilityNodeProvider; - -import com.googlecode.eyesfree.braille.selfbraille.SelfBrailleClient; -import com.googlecode.eyesfree.braille.selfbraille.WriteData; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; - -public class GeckoAccessibility { - private static final String LOGTAG = "GeckoAccessibility"; - private static final int VIRTUAL_CURSOR_PREVIOUS = 1; - private static final int VIRTUAL_CURSOR_POSITION = 2; - private static final int VIRTUAL_CURSOR_NEXT = 3; - - private static boolean sEnabled = false; - // Used to store the JSON message and populate the event later in the code path. - private static JSONObject sEventMessage = null; - private static AccessibilityNodeInfo sVirtualCursorNode = null; - - private static SelfBrailleClient sSelfBrailleClient = null; - - private static final HashSet<String> sServiceWhitelist = - new HashSet<String>(Arrays.asList(new String[] { - "com.google.android.marvin.talkback.TalkBackService", // Google Talkback screen reader - "com.mot.readout.ScreenReader", // Motorola screen reader - "info.spielproject.spiel.SpielService", // Spiel screen reader - "es.codefactory.android.app.ma.MAAccessibilityService" // Codefactory Mobile Accessibility screen reader - })); - - public static void updateAccessibilitySettings (final GeckoApp app) { - new UiAsyncTask<Void, Void, Void>(ThreadUtils.getBackgroundHandler()) { - @Override - public Void doInBackground(Void... args) { - JSONObject ret = new JSONObject(); - sEnabled = false; - AccessibilityManager accessibilityManager = - (AccessibilityManager) app.getSystemService(Context.ACCESSIBILITY_SERVICE); - if (accessibilityManager.isEnabled()) { - ActivityManager activityManager = - (ActivityManager) app.getSystemService(Context.ACTIVITY_SERVICE); - List<RunningServiceInfo> runningServices = activityManager.getRunningServices(Integer.MAX_VALUE); - - for (RunningServiceInfo runningServiceInfo : runningServices) { - sEnabled = sServiceWhitelist.contains(runningServiceInfo.service.getClassName()); - if (sEnabled) - break; - } - if (sEnabled && sSelfBrailleClient == null && - Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - sSelfBrailleClient = new SelfBrailleClient(GeckoAppShell.getContext(), false); - } - } - - try { - ret.put("enabled", sEnabled); - } catch (Exception ex) { - Log.e(LOGTAG, "Error building JSON arguments for Accessibility:Settings:", ex); - } - - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Accessibility:Settings", - ret.toString())); - return null; - } - - @Override - public void onPostExecute(Void args) { - // Disable the dynamic toolbar when enabling accessibility. - // These features tend not to interact well. - app.setAccessibilityEnabled(sEnabled); - } - }.execute(); - } - - private static void populateEventFromJSON (AccessibilityEvent event, JSONObject message) { - final JSONArray textArray = message.optJSONArray("text"); - if (textArray != null) { - for (int i = 0; i < textArray.length(); i++) - event.getText().add(textArray.optString(i)); - } - - event.setContentDescription(message.optString("description")); - event.setEnabled(message.optBoolean("enabled", true)); - event.setChecked(message.optBoolean("checked")); - event.setPassword(message.optBoolean("password")); - event.setAddedCount(message.optInt("addedCount", -1)); - event.setRemovedCount(message.optInt("removedCount", -1)); - event.setFromIndex(message.optInt("fromIndex", -1)); - event.setItemCount(message.optInt("itemCount", -1)); - event.setCurrentItemIndex(message.optInt("currentItemIndex", -1)); - event.setBeforeText(message.optString("beforeText")); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - event.setToIndex(message.optInt("toIndex", -1)); - event.setScrollable(message.optBoolean("scrollable")); - event.setScrollX(message.optInt("scrollX", -1)); - event.setScrollY(message.optInt("scrollY", -1)); - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { - event.setMaxScrollX(message.optInt("maxScrollX", -1)); - event.setMaxScrollY(message.optInt("maxScrollY", -1)); - } - } - - private static void sendDirectAccessibilityEvent(int eventType, JSONObject message) { - final AccessibilityEvent accEvent = AccessibilityEvent.obtain(eventType); - accEvent.setClassName(GeckoAccessibility.class.getName()); - accEvent.setPackageName(GeckoAppShell.getContext().getPackageName()); - populateEventFromJSON(accEvent, message); - AccessibilityManager accessibilityManager = - (AccessibilityManager) GeckoAppShell.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); - try { - accessibilityManager.sendAccessibilityEvent(accEvent); - } catch (IllegalStateException e) { - // Accessibility is off. - } - } - - public static void sendAccessibilityEvent (final JSONObject message) { - if (!sEnabled) - return; - - final int eventType = message.optInt("eventType", -1); - if (eventType < 0) { - Log.e(LOGTAG, "No accessibility event type provided"); - return; - } - - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { - // Before Jelly Bean we send events directly from here while spoofing the source by setting - // the package and class name manually. - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - sendDirectAccessibilityEvent(eventType, message); - } - }); - } else { - // In Jelly Bean we populate an AccessibilityNodeInfo with the minimal amount of data to have - // it work with TalkBack. - final LayerView view = GeckoAppShell.getLayerView(); - if (view == null) - return; - - if (sVirtualCursorNode == null) - sVirtualCursorNode = AccessibilityNodeInfo.obtain(view, VIRTUAL_CURSOR_POSITION); - sVirtualCursorNode.setEnabled(message.optBoolean("enabled", true)); - sVirtualCursorNode.setClickable(message.optBoolean("clickable")); - sVirtualCursorNode.setCheckable(message.optBoolean("checkable")); - sVirtualCursorNode.setChecked(message.optBoolean("checked")); - sVirtualCursorNode.setPassword(message.optBoolean("password")); - - final JSONArray textArray = message.optJSONArray("text"); - StringBuilder sb = new StringBuilder(); - if (textArray != null && textArray.length() > 0) { - sb.append(textArray.optString(0)); - for (int i = 1; i < textArray.length(); i++) { - sb.append(" ").append(textArray.optString(i)); - } - } - sVirtualCursorNode.setText(sb.toString()); - sVirtualCursorNode.setContentDescription(message.optString("description")); - - JSONObject bounds = message.optJSONObject("bounds"); - if (bounds != null) { - Rect relativeBounds = new Rect(bounds.optInt("left"), bounds.optInt("top"), - bounds.optInt("right"), bounds.optInt("bottom")); - sVirtualCursorNode.setBoundsInParent(relativeBounds); - int[] locationOnScreen = new int[2]; - view.getLocationOnScreen(locationOnScreen); - Rect screenBounds = new Rect(relativeBounds); - screenBounds.offset(locationOnScreen[0], locationOnScreen[1]); - sVirtualCursorNode.setBoundsInScreen(screenBounds); - } - - final String brailleText = message.optString("brailleText"); - if (!brailleText.isEmpty()) { - sendBrailleText(view, brailleText); - } - - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - // If this is an accessibility focus, a lot of internal voodoo happens so we perform an - // accessibility focus action on the view, and it in turn sends the right events. - switch (eventType) { - case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: - sEventMessage = message; - view.performAccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); - break; - case AccessibilityEvent.TYPE_ANNOUNCEMENT: - case AccessibilityEvent.TYPE_VIEW_SCROLLED: - sEventMessage = null; - final AccessibilityEvent accEvent = AccessibilityEvent.obtain(eventType); - view.onInitializeAccessibilityEvent(accEvent); - populateEventFromJSON(accEvent, message); - view.getParent().requestSendAccessibilityEvent(view, accEvent); - break; - default: - sEventMessage = message; - view.sendAccessibilityEvent(eventType); - break; - } - } - }); - - } - } - - private static void sendBrailleText(final View view, final String text) { - AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(view, VIRTUAL_CURSOR_POSITION); - WriteData data = WriteData.forInfo(info); - data.setText(text); - // Set the focus blink - data.setSelectionStart(0); - data.setSelectionEnd(0); - sSelfBrailleClient.write(data); - } - - public static void setDelegate(LayerView layerview) { - // Only use this delegate in Jelly Bean. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - layerview.setAccessibilityDelegate(new GeckoAccessibilityDelegate()); - layerview.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); - } - } - - public static void onLayerViewFocusChanged(LayerView layerview, boolean gainFocus) { - if (sEnabled) - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Accessibility:Focus", - gainFocus ? "true" : "false")); - } - - public static class GeckoAccessibilityDelegate extends View.AccessibilityDelegate { - AccessibilityNodeProvider mAccessibilityNodeProvider; - - @Override - public void onPopulateAccessibilityEvent (View host, AccessibilityEvent event) { - super.onPopulateAccessibilityEvent(host, event); - if (sEventMessage != null) { - populateEventFromJSON(event, sEventMessage); - // No matter where the a11y focus is requested, we always force it back to the current vc position. - event.setSource(host, VIRTUAL_CURSOR_POSITION); - } - // We save the hover enter event so that we could reuse it for a subsequent accessibility focus event. - if (event.getEventType() != AccessibilityEvent.TYPE_VIEW_HOVER_ENTER) - sEventMessage = null; - } - - @Override - public AccessibilityNodeProvider getAccessibilityNodeProvider(final View host) { - if (mAccessibilityNodeProvider == null) - // The accessibility node structure for web content consists of 3 LayerView child nodes: - // 1. VIRTUAL_CURSOR_PREVIOUS: Represents the virtual cursor position that is previous to the - // current one. - // 2. VIRTUAL_CURSOR_POSITION: Represents the current position of the virtual cursor. - // 3. VIRTUAL_CURSOR_NEXT: Represents the next virtual cursor position. - mAccessibilityNodeProvider = new AccessibilityNodeProvider() { - @Override - public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualDescendantId) { - AccessibilityNodeInfo info = (virtualDescendantId == VIRTUAL_CURSOR_POSITION && sVirtualCursorNode != null) ? - AccessibilityNodeInfo.obtain(sVirtualCursorNode) : - AccessibilityNodeInfo.obtain(host, virtualDescendantId); - - switch (virtualDescendantId) { - case View.NO_ID: - // This is the parent LayerView node, populate it with children. - onInitializeAccessibilityNodeInfo(host, info); - info.addChild(host, VIRTUAL_CURSOR_PREVIOUS); - info.addChild(host, VIRTUAL_CURSOR_POSITION); - info.addChild(host, VIRTUAL_CURSOR_NEXT); - break; - default: - info.setParent(host); - info.setSource(host, virtualDescendantId); - info.setVisibleToUser(true); - info.setPackageName(GeckoAppShell.getContext().getPackageName()); - info.setClassName(host.getClass().getName()); - info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS); - info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); - info.addAction(AccessibilityNodeInfo.ACTION_CLICK); - info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY); - info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY); - info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER | - AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD | - AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH); - break; - } - return info; - } - - @Override - public boolean performAction (int virtualViewId, int action, Bundle arguments) { - if (action == AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS) { - // The accessibility focus is permanently on the middle node, VIRTUAL_CURSOR_POSITION. - // When accessibility focus is requested on one of its siblings we move the virtual cursor - // either forward or backward depending on which sibling was selected. - - switch (virtualViewId) { - case VIRTUAL_CURSOR_PREVIOUS: - GeckoAppShell. - sendEventToGecko(GeckoEvent.createBroadcastEvent("Accessibility:PreviousObject", null)); - return true; - case VIRTUAL_CURSOR_NEXT: - GeckoAppShell. - sendEventToGecko(GeckoEvent.createBroadcastEvent("Accessibility:NextObject", null)); - return true; - default: - break; - } - } else if (action == AccessibilityNodeInfo.ACTION_CLICK && virtualViewId == VIRTUAL_CURSOR_POSITION) { - GeckoAppShell. - sendEventToGecko(GeckoEvent.createBroadcastEvent("Accessibility:ActivateObject", null)); - return true; - } else if (action == AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY && - virtualViewId == VIRTUAL_CURSOR_POSITION) { - // XXX: Self brailling gives this action with a bogus argument instead of an actual click action - int granularity = arguments.getInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT); - if (granularity < 0) { - GeckoAppShell. - sendEventToGecko(GeckoEvent.createBroadcastEvent("Accessibility:ActivateObject", null)); - } else { - JSONObject movementData = new JSONObject(); - try { - movementData.put("direction", "Next"); - movementData.put("granularity", granularity); - } catch (JSONException e) { - return true; - } - GeckoAppShell. - sendEventToGecko(GeckoEvent.createBroadcastEvent("Accessibility:MoveCaret", movementData.toString())); - } - return true; - } else if (action == AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY && - virtualViewId == VIRTUAL_CURSOR_POSITION) { - JSONObject movementData = new JSONObject(); - try { - movementData.put("direction", "Previous"); - movementData.put("granularity", arguments.getInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT)); - } catch (JSONException e) { - return true; - } - GeckoAppShell. - sendEventToGecko(GeckoEvent.createBroadcastEvent("Accessibility:MoveCaret", movementData.toString())); - return true; - } - return host.performAccessibilityAction(action, arguments); - } - }; - - return mAccessibilityNodeProvider; - } - } -} diff --git a/mobile/android/base/GeckoActivity.java b/mobile/android/base/GeckoActivity.java deleted file mode 100644 index 762cde8e7..000000000 --- a/mobile/android/base/GeckoActivity.java +++ /dev/null @@ -1,100 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import android.content.ComponentName; -import android.content.Intent; -import android.support.v4.app.FragmentActivity; - -interface GeckoActivityStatus { - public boolean isGeckoActivityOpened(); - public boolean isFinishing(); // typically from android.app.Activity -}; - -public class GeckoActivity extends FragmentActivity implements GeckoActivityStatus { - // has this activity recently started another Gecko activity? - private boolean mGeckoActivityOpened = false; - - @Override - public void onPause() { - super.onPause(); - - if (getApplication() instanceof GeckoApplication) { - ((GeckoApplication) getApplication()).onActivityPause(this); - } - } - - @Override - public void onResume() { - super.onResume(); - - if (getApplication() instanceof GeckoApplication) { - ((GeckoApplication) getApplication()).onActivityResume(this); - mGeckoActivityOpened = false; - } - } - - @Override - public void onCreate(android.os.Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (AppConstants.MOZ_ANDROID_ANR_REPORTER) { - ANRReporter.register(getApplicationContext()); - } - } - - @Override - public void onDestroy() { - if (AppConstants.MOZ_ANDROID_ANR_REPORTER) { - ANRReporter.unregister(); - } - super.onDestroy(); - } - - @Override - public void startActivity(Intent intent) { - mGeckoActivityOpened = checkIfGeckoActivity(intent); - super.startActivity(intent); - } - - @Override - public void startActivityForResult(Intent intent, int request) { - mGeckoActivityOpened = checkIfGeckoActivity(intent); - super.startActivityForResult(intent, request); - } - - private static boolean checkIfGeckoActivity(Intent intent) { - // Whenever we call our own activity, the component and its package name is set. - // If we call an activity from another package, or an open intent (leaving android to resolve) - // component has a different package name or it is null. - ComponentName component = intent.getComponent(); - return (component != null && - AppConstants.ANDROID_PACKAGE_NAME.equals(component.getPackageName())); - } - - @Override - public boolean isGeckoActivityOpened() { - return mGeckoActivityOpened; - } - - public boolean isApplicationInBackground() { - return ((GeckoApplication) getApplication()).isApplicationInBackground(); - } - - @Override - public void onLowMemory() { - MemoryMonitor.getInstance().onLowMemory(); - super.onLowMemory(); - } - - @Override - public void onTrimMemory(int level) { - MemoryMonitor.getInstance().onTrimMemory(level); - super.onTrimMemory(level); - } - - public LightweightTheme getLightweightTheme() { - return ((GeckoApplication) getApplication()).getLightweightTheme(); - } -} diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java deleted file mode 100644 index df436f5f8..000000000 --- a/mobile/android/base/GeckoApp.java +++ /dev/null @@ -1,2466 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import org.mozilla.gecko.DataReportingNotification; -import org.mozilla.gecko.db.BrowserDB; -import org.mozilla.gecko.gfx.BitmapUtils; -import org.mozilla.gecko.gfx.Layer; -import org.mozilla.gecko.gfx.LayerView; -import org.mozilla.gecko.gfx.PluginLayer; -import org.mozilla.gecko.menu.GeckoMenu; -import org.mozilla.gecko.menu.GeckoMenuInflater; -import org.mozilla.gecko.menu.MenuPanel; -// import org.mozilla.gecko.health.BrowserHealthRecorder; -// import org.mozilla.gecko.health.BrowserHealthRecorder.SessionInformation; -import org.mozilla.gecko.updater.UpdateService; -import org.mozilla.gecko.updater.UpdateServiceHelper; -import org.mozilla.gecko.util.EventDispatcher; -import org.mozilla.gecko.util.GeckoEventListener; -import org.mozilla.gecko.util.GeckoEventResponder; -import org.mozilla.gecko.util.HardwareUtils; -import org.mozilla.gecko.util.ThreadUtils; -import org.mozilla.gecko.util.UiAsyncTask; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.Notification; -import android.app.PendingIntent; -import android.app.WallpaperManager; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.res.Configuration; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Point; -import android.graphics.Rect; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.location.Location; -import android.location.LocationListener; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.os.PowerManager; -import android.os.StrictMode; -import android.preference.PreferenceManager; -import android.provider.ContactsContract; - -import android.text.TextUtils; -import android.util.AttributeSet; -import android.util.Base64; -import android.util.Log; -import android.util.SparseBooleanArray; -import android.view.Display; -import android.view.Gravity; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.OrientationEventListener; -import android.view.Surface; -import android.view.SurfaceHolder; -import android.view.SurfaceView; -import android.view.TextureView; -import android.view.View; -import android.view.ViewGroup; -import android.view.Window; -import android.view.WindowManager; -import android.widget.AbsoluteLayout; -import android.widget.FrameLayout; -import android.widget.ListView; -import android.widget.RelativeLayout; -import android.widget.SimpleAdapter; -import android.widget.TextView; -import android.widget.Toast; - -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -abstract public class GeckoApp - extends GeckoActivity - implements GeckoEventListener, SensorEventListener, LocationListener, - Tabs.OnTabsChangedListener, GeckoEventResponder, - GeckoMenu.Callback, GeckoMenu.MenuPresenter, - ContextGetter, GeckoAppShell.GeckoInterface -{ - private static final String LOGTAG = "GeckoApp"; - - private static enum StartupAction { - NORMAL, /* normal application start */ - URL, /* launched with a passed URL */ - PREFETCH /* launched with a passed URL that we prefetch */ - } - - public static final String ACTION_ALERT_CALLBACK = "org.mozilla.gecko.ACTION_ALERT_CALLBACK"; - public static final String ACTION_WEBAPP_PREFIX = "org.mozilla.gecko.WEBAPP"; - public static final String ACTION_DEBUG = "org.mozilla.gecko.DEBUG"; - public static final String ACTION_BOOKMARK = "org.mozilla.gecko.BOOKMARK"; - public static final String ACTION_LOAD = "org.mozilla.gecko.LOAD"; - public static final String ACTION_LAUNCH_SETTINGS = "org.mozilla.gecko.SETTINGS"; - public static final String ACTION_INIT_PW = "org.mozilla.gecko.INIT_PW"; - public static final String SAVED_STATE_INTENT_HANDLED = "intentHandled"; - public static final String SAVED_STATE_IN_BACKGROUND = "inBackground"; - public static final String SAVED_STATE_PRIVATE_SESSION = "privateSession"; - - public static final String PREFS_NAME = "GeckoApp"; - public static final String PREFS_OOM_EXCEPTION = "OOMException"; - public static final String PREFS_WAS_STOPPED = "wasStopped"; - public static final String PREFS_CRASHED = "crashed"; - public static final String PREFS_VERSION_CODE = "versionCode"; - - static public final int RESTORE_NONE = 0; - static public final int RESTORE_NORMAL = 1; - static public final int RESTORE_CRASH = 2; - - protected RelativeLayout mMainLayout; - protected RelativeLayout mGeckoLayout; - public View getView() { return mGeckoLayout; } - private View mCameraView; - private OrientationEventListener mCameraOrientationEventListener; - public List<GeckoAppShell.AppStateListener> mAppStateListeners; - private static GeckoApp sAppContext; - protected MenuPanel mMenuPanel; - protected Menu mMenu; - private static GeckoThread sGeckoThread; - private GeckoProfile mProfile; - public static int mOrientation; - protected boolean mIsRestoringActivity; - private boolean mIntentHandled; - private String mCurrentResponse = ""; - public static boolean sIsUsingCustomProfile = false; - - private PromptService mPromptService; - private TextSelection mTextSelection; - - protected DoorHangerPopup mDoorHangerPopup; - protected FormAssistPopup mFormAssistPopup; - protected TabsPanel mTabsPanel; - - // Handles notification messages from javascript - protected NotificationHelper mNotificationHelper; - - protected LayerView mLayerView; - private AbsoluteLayout mPluginContainer; - - private FullScreenHolder mFullScreenPluginContainer; - private View mFullScreenPluginView; - - private HashMap<String, PowerManager.WakeLock> mWakeLocks = new HashMap<String, PowerManager.WakeLock>(); - - protected int mRestoreMode = RESTORE_NONE; - protected boolean mInitialized = false; - - private String mPrivateBrowsingSession; - -// private volatile BrowserHealthRecorder mHealthRecorder = null; - - abstract public int getLayout(); - abstract public boolean hasTabsSideBar(); - abstract protected String getDefaultProfileName(); - - private static final String RESTARTER_ACTION = "org.mozilla.gecko.restart"; - private static final String RESTARTER_CLASS = "org.mozilla.gecko.Restarter"; - - @SuppressWarnings("serial") - class SessionRestoreException extends Exception { - public SessionRestoreException(Exception e) { - super(e); - } - - public SessionRestoreException(String message) { - super(message); - } - } - - void toggleChrome(final boolean aShow) { } - - void focusChrome() { } - - public Context getContext() { - return sAppContext; - } - - public Activity getActivity() { - return this; - } - - public LocationListener getLocationListener() { - return this; - } - - public SensorEventListener getSensorEventListener() { - return this; - } - - public static SharedPreferences getAppSharedPreferences() { - return GeckoApp.sAppContext.getSharedPreferences(PREFS_NAME, 0); - } - - public View getCameraView() { - return mCameraView; - } - - public void addAppStateListener(GeckoAppShell.AppStateListener listener) { - mAppStateListeners.add(listener); - } - - public void removeAppStateListener(GeckoAppShell.AppStateListener listener) { - mAppStateListeners.remove(listener); - } - - public FormAssistPopup getFormAssistPopup() { - return mFormAssistPopup; - } - - @Override - public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) { - // When a tab is closed, it is always unselected first. - // When a tab is unselected, another tab is always selected first. - switch(msg) { - case UNSELECTED: - hidePlugins(tab); - break; - - case LOCATION_CHANGE: - // We only care about location change for the selected tab. - if (!Tabs.getInstance().isSelectedTab(tab)) - break; - // Fall through... - case SELECTED: - invalidateOptionsMenu(); - if (mFormAssistPopup != null) - mFormAssistPopup.hide(); - break; - - case LOADED: - // Sync up the layer view and the tab if the tab is - // currently displayed. - LayerView layerView = mLayerView; - if (layerView != null && Tabs.getInstance().isSelectedTab(tab)) - layerView.setBackgroundColor(tab.getBackgroundColor()); - break; - - case DESKTOP_MODE_CHANGE: - if (Tabs.getInstance().isSelectedTab(tab)) - invalidateOptionsMenu(); - break; - } - } - - public void refreshChrome() { } - - @Override - public void invalidateOptionsMenu() { - if (mMenu == null) - return; - - onPrepareOptionsMenu(mMenu); - - if (Build.VERSION.SDK_INT >= 11) - super.invalidateOptionsMenu(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - mMenu = menu; - - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.gecko_app_menu, mMenu); - return true; - } - - @Override - public MenuInflater getMenuInflater() { - if (Build.VERSION.SDK_INT >= 11) - return new GeckoMenuInflater(this); - else - return super.getMenuInflater(); - } - - public MenuPanel getMenuPanel() { - return mMenuPanel; - } - - @Override - public boolean onMenuItemSelected(MenuItem item) { - return onOptionsItemSelected(item); - } - - @Override - public void openMenu() { - openOptionsMenu(); - } - - @Override - public void showMenu(View menu) { - // Hide the menu only if we are showing the MenuPopup. - if (!HardwareUtils.hasMenuButton()) - closeMenu(); - - mMenuPanel.removeAllViews(); - mMenuPanel.addView(menu); - - openOptionsMenu(); - } - - @Override - public void closeMenu() { - closeOptionsMenu(); - } - - @Override - public View onCreatePanelView(int featureId) { - if (Build.VERSION.SDK_INT >= 11 && featureId == Window.FEATURE_OPTIONS_PANEL) { - if (mMenuPanel == null) { - mMenuPanel = new MenuPanel(this, null); - } else { - // Prepare the panel everytime before showing the menu. - onPreparePanel(featureId, mMenuPanel, mMenu); - } - - return mMenuPanel; - } - - return super.onCreatePanelView(featureId); - } - - @Override - public boolean onCreatePanelMenu(int featureId, Menu menu) { - if (Build.VERSION.SDK_INT >= 11 && featureId == Window.FEATURE_OPTIONS_PANEL) { - if (mMenuPanel == null) { - mMenuPanel = (MenuPanel) onCreatePanelView(featureId); - } - - GeckoMenu gMenu = new GeckoMenu(this, null); - gMenu.setCallback(this); - gMenu.setMenuPresenter(this); - menu = gMenu; - mMenuPanel.addView(gMenu); - - return onCreateOptionsMenu(menu); - } - - return super.onCreatePanelMenu(featureId, menu); - } - - @Override - public boolean onPreparePanel(int featureId, View view, Menu menu) { - if (Build.VERSION.SDK_INT >= 11 && featureId == Window.FEATURE_OPTIONS_PANEL) - return onPrepareOptionsMenu(menu); - - return super.onPreparePanel(featureId, view, menu); - } - - @Override - public boolean onMenuOpened(int featureId, Menu menu) { - // exit full-screen mode whenever the menu is opened - if (mLayerView != null && mLayerView.isFullScreen()) { - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FullScreen:Exit", null)); - } - - if (Build.VERSION.SDK_INT >= 11 && featureId == Window.FEATURE_OPTIONS_PANEL) { - if (mMenu == null) { - onCreatePanelMenu(featureId, menu); - onPreparePanel(featureId, mMenuPanel, mMenu); - } - - // Scroll custom menu to the top - if (mMenuPanel != null) - mMenuPanel.scrollTo(0, 0); - - return true; - } - - return super.onMenuOpened(featureId, menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.quit: - if (GeckoThread.checkAndSetLaunchState(GeckoThread.LaunchState.GeckoRunning, GeckoThread.LaunchState.GeckoExiting)) { - GeckoAppShell.notifyGeckoOfEvent(GeckoEvent.createBroadcastEvent("Browser:Quit", null)); - } else { - System.exit(0); - } - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - @Override - public void onOptionsMenuClosed(Menu menu) { - if (Build.VERSION.SDK_INT >= 11) { - mMenuPanel.removeAllViews(); - mMenuPanel.addView((GeckoMenu) mMenu); - } - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - // Handle hardware menu key presses separately so that we can show a custom menu in some cases. - if (keyCode == KeyEvent.KEYCODE_MENU) { - openOptionsMenu(); - return true; - } - - return super.onKeyDown(keyCode, event); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - - if (outState == null) - outState = new Bundle(); - - outState.putBoolean(SAVED_STATE_IN_BACKGROUND, isApplicationInBackground()); - outState.putString(SAVED_STATE_PRIVATE_SESSION, mPrivateBrowsingSession); - - // Bug 896992 - Replace intent action with ACTION_MAIN on restart. - if (mIntentHandled) { - outState.putBoolean(SAVED_STATE_INTENT_HANDLED, true); - } - } - - void handleFaviconRequest(final String url) { - (new UiAsyncTask<Void, Void, String>(ThreadUtils.getBackgroundHandler()) { - @Override - public String doInBackground(Void... params) { - return Favicons.getInstance().getFaviconUrlForPageUrl(url); - } - - @Override - public void onPostExecute(String faviconUrl) { - JSONObject args = new JSONObject(); - - if (faviconUrl != null) { - try { - args.put("url", url); - args.put("faviconUrl", faviconUrl); - } catch (JSONException e) { - Log.w(LOGTAG, "Error building JSON favicon arguments.", e); - } - } - - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Reader:FaviconReturn", args.toString())); - } - }).execute(); - } - - void handleClearHistory() { - BrowserDB.clearHistory(getContentResolver()); - } - - public void addTab() { } - - public void addPrivateTab() { } - - public void showNormalTabs() { } - - public void showPrivateTabs() { } - - public void showRemoteTabs() { } - - private void showTabs(TabsPanel.Panel panel) { } - - public void hideTabs() { } - - /** - * Close the tab UI indirectly (not as the result of a direct user - * action). This does not force the UI to close; for example in Firefox - * tablet mode it will remain open unless the user explicitly closes it. - * - * @return True if the tab UI was hidden. - */ - public boolean autoHideTabs() { return false; } - - public boolean areTabsShown() { return false; } - - @Override - public void handleMessage(String event, JSONObject message) { - try { - if (event.equals("Toast:Show")) { - final String msg = message.getString("message"); - final String duration = message.getString("duration"); - handleShowToast(msg, duration); - } else if (event.equals("log")) { - // generic log listener - final String msg = message.getString("msg"); - Log.d(LOGTAG, "Log: " + msg); - } else if (event.equals("Reader:FaviconRequest")) { - final String url = message.getString("url"); - handleFaviconRequest(url); - } else if (event.equals("Reader:GoToReadingList")) { - showReadingList(); - } else if (event.equals("Gecko:Ready")) { - geckoConnected(); - } else if (event.equals("ToggleChrome:Hide")) { - toggleChrome(false); - } else if (event.equals("ToggleChrome:Show")) { - toggleChrome(true); - } else if (event.equals("ToggleChrome:Focus")) { - focusChrome(); - } else if (event.equals("DOMFullScreen:Start")) { - // Local ref to layerView for thread safety - LayerView layerView = mLayerView; - if (layerView != null) { - layerView.setFullScreen(true); - } - } else if (event.equals("DOMFullScreen:Stop")) { - // Local ref to layerView for thread safety - LayerView layerView = mLayerView; - if (layerView != null) { - layerView.setFullScreen(false); - } - } else if (event.equals("Permissions:Data")) { - String host = message.getString("host"); - JSONArray permissions = message.getJSONArray("permissions"); - showSiteSettingsDialog(host, permissions); - } else if (event.equals("Tab:ViewportMetadata")) { - int tabId = message.getInt("tabID"); - Tab tab = Tabs.getInstance().getTab(tabId); - if (tab == null) - return; - tab.setZoomConstraints(new ZoomConstraints(message)); - tab.setIsRTL(message.getBoolean("isRTL")); - // Sync up the layer view and the tab if the tab is currently displayed. - LayerView layerView = mLayerView; - if (layerView != null && Tabs.getInstance().isSelectedTab(tab)) { - layerView.setZoomConstraints(tab.getZoomConstraints()); - layerView.setIsRTL(tab.getIsRTL()); - } - } else if (event.equals("Session:StatePurged")) { - onStatePurged(); - } else if (event.equals("Bookmark:Insert")) { - final String url = message.getString("url"); - final String title = message.getString("title"); - final Context context = this; - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - Toast.makeText(context, R.string.bookmark_added, Toast.LENGTH_SHORT).show(); - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - BrowserDB.addBookmark(getContentResolver(), title, url); - } - }); - } - }); - } else if (event.equals("Accessibility:Event")) { - GeckoAccessibility.sendAccessibilityEvent(message); - } else if (event.equals("Accessibility:Ready")) { - GeckoAccessibility.updateAccessibilitySettings(this); - } else if (event.equals("Shortcut:Remove")) { - final String url = message.getString("url"); - final String origin = message.getString("origin"); - final String title = message.getString("title"); - final String type = message.getString("shortcutType"); - GeckoAppShell.removeShortcut(title, url, origin, type); - } else if (event.equals("WebApps:Open")) { - String manifestURL = message.getString("manifestURL"); - String origin = message.getString("origin"); - Intent intent = GeckoAppShell.getWebAppIntent(manifestURL, origin, "", null); - if (intent == null) - return; - startActivity(intent); - } else if (event.equals("WebApps:Install")) { - String name = message.getString("name"); - String manifestURL = message.getString("manifestURL"); - String iconURL = message.getString("iconURL"); - String origin = message.getString("origin"); - // preInstallWebapp will return a File object pointing to the profile directory of the webapp - mCurrentResponse = GeckoAppShell.preInstallWebApp(name, manifestURL, origin).toString(); - GeckoAppShell.postInstallWebApp(name, manifestURL, origin, iconURL); - } else if (event.equals("WebApps:PreInstall")) { - String name = message.getString("name"); - String manifestURL = message.getString("manifestURL"); - String origin = message.getString("origin"); - // preInstallWebapp will return a File object pointing to the profile directory of the webapp - mCurrentResponse = GeckoAppShell.preInstallWebApp(name, manifestURL, origin).toString(); - } else if (event.equals("WebApps:PostInstall")) { - String name = message.getString("name"); - String manifestURL = message.getString("manifestURL"); - String iconURL = message.getString("iconURL"); - String origin = message.getString("origin"); - GeckoAppShell.postInstallWebApp(name, manifestURL, origin, iconURL); - } else if (event.equals("WebApps:Uninstall")) { - String origin = message.getString("origin"); - GeckoAppShell.uninstallWebApp(origin); - } else if (event.equals("Share:Text")) { - String text = message.getString("text"); - GeckoAppShell.openUriExternal(text, "text/plain", "", "", Intent.ACTION_SEND, ""); - } else if (event.equals("Share:Image")) { - String src = message.getString("url"); - String type = message.getString("mime"); - GeckoAppShell.shareImage(src, type); - } else if (event.equals("Wallpaper:Set")) { - String src = message.getString("url"); - setImageAsWallpaper(src); - } else if (event.equals("Sanitize:ClearHistory")) { - handleClearHistory(); - } else if (event.equals("Update:Check")) { - startService(new Intent(UpdateServiceHelper.ACTION_CHECK_FOR_UPDATE, null, this, UpdateService.class)); - } else if (event.equals("Update:Download")) { - startService(new Intent(UpdateServiceHelper.ACTION_DOWNLOAD_UPDATE, null, this, UpdateService.class)); - } else if (event.equals("Update:Install")) { - startService(new Intent(UpdateServiceHelper.ACTION_APPLY_UPDATE, null, this, UpdateService.class)); - } else if (event.equals("PrivateBrowsing:Data")) { - // null strings return "null" (http://code.google.com/p/android/issues/detail?id=13830) - if (message.isNull("session")) { - mPrivateBrowsingSession = null; - } else { - mPrivateBrowsingSession = message.getString("session"); - } - } else if (event.equals("Contact:Add")) { - if (!message.isNull("email")) { - Uri contactUri = Uri.parse(message.getString("email")); - Intent i = new Intent(ContactsContract.Intents.SHOW_OR_CREATE_CONTACT, contactUri); - startActivity(i); - } else if (!message.isNull("phone")) { - Uri contactUri = Uri.parse(message.getString("phone")); - Intent i = new Intent(ContactsContract.Intents.SHOW_OR_CREATE_CONTACT, contactUri); - startActivity(i); - } else { - // something went wrong. - Log.e(LOGTAG, "Received Contact:Add message with no email nor phone number"); - } - } - } catch (Exception e) { - Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e); - } - } - - public String getResponse(JSONObject origMessage) { - String res = mCurrentResponse; - mCurrentResponse = ""; - return res; - } - - void onStatePurged() { } - - /** - * @param aPermissions - * Array of JSON objects to represent site permissions. - * Example: { type: "offline-app", setting: "Store Offline Data", value: "Allow" } - */ - private void showSiteSettingsDialog(String aHost, JSONArray aPermissions) { - final AlertDialog.Builder builder = new AlertDialog.Builder(this); - - View customTitleView = getLayoutInflater().inflate(R.layout.site_setting_title, null); - ((TextView) customTitleView.findViewById(R.id.title)).setText(R.string.site_settings_title); - ((TextView) customTitleView.findViewById(R.id.host)).setText(aHost); - builder.setCustomTitle(customTitleView); - - // If there are no permissions to clear, show the user a message about that. - // In the future, we want to disable the menu item if there are no permissions to clear. - if (aPermissions.length() == 0) { - builder.setMessage(R.string.site_settings_no_settings); - } else { - - ArrayList <HashMap<String, String>> itemList = new ArrayList <HashMap<String, String>>(); - for (int i = 0; i < aPermissions.length(); i++) { - try { - JSONObject permObj = aPermissions.getJSONObject(i); - HashMap<String, String> map = new HashMap<String, String>(); - map.put("setting", permObj.getString("setting")); - map.put("value", permObj.getString("value")); - itemList.add(map); - } catch (JSONException e) { - Log.w(LOGTAG, "Exception populating settings items.", e); - } - } - - // setMultiChoiceItems doesn't support using an adapter, so we're creating a hack with - // setSingleChoiceItems and changing the choiceMode below when we create the dialog - builder.setSingleChoiceItems(new SimpleAdapter( - GeckoApp.this, - itemList, - R.layout.site_setting_item, - new String[] { "setting", "value" }, - new int[] { R.id.setting, R.id.value } - ), -1, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { } - }); - - builder.setPositiveButton(R.string.site_settings_clear, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - ListView listView = ((AlertDialog) dialog).getListView(); - SparseBooleanArray checkedItemPositions = listView.getCheckedItemPositions(); - - // An array of the indices of the permissions we want to clear - JSONArray permissionsToClear = new JSONArray(); - for (int i = 0; i < checkedItemPositions.size(); i++) - if (checkedItemPositions.get(i)) - permissionsToClear.put(i); - - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent( - "Permissions:Clear", permissionsToClear.toString())); - } - }); - } - - builder.setNegativeButton(R.string.site_settings_cancel, new DialogInterface.OnClickListener(){ - @Override - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - } - }); - - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - Dialog dialog = builder.create(); - dialog.show(); - - ListView listView = ((AlertDialog) dialog).getListView(); - if (listView != null) { - listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); - int listSize = listView.getAdapter().getCount(); - for (int i = 0; i < listSize; i++) - listView.setItemChecked(i, true); - } - } - }); - } - - public void showToast(final int resId, final int duration) { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - Toast.makeText(GeckoApp.this, resId, duration).show(); - } - }); - } - - void handleShowToast(final String message, final String duration) { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - Toast toast; - if (duration.equals("long")) - toast = Toast.makeText(GeckoApp.this, message, Toast.LENGTH_LONG); - else - toast = Toast.makeText(GeckoApp.this, message, Toast.LENGTH_SHORT); - toast.show(); - } - }); - } - - private void addFullScreenPluginView(View view) { - if (mFullScreenPluginView != null) { - Log.w(LOGTAG, "Already have a fullscreen plugin view"); - return; - } - - setFullScreen(true); - - view.setWillNotDraw(false); - if (view instanceof SurfaceView) { - ((SurfaceView) view).setZOrderOnTop(true); - } - - mFullScreenPluginContainer = new FullScreenHolder(this); - - FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams( - ViewGroup.LayoutParams.FILL_PARENT, - ViewGroup.LayoutParams.FILL_PARENT, - Gravity.CENTER); - mFullScreenPluginContainer.addView(view, layoutParams); - - - FrameLayout decor = (FrameLayout)getWindow().getDecorView(); - decor.addView(mFullScreenPluginContainer, layoutParams); - - mFullScreenPluginView = view; - } - - public void addPluginView(final View view, final Rect rect, final boolean isFullScreen) { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - Tabs tabs = Tabs.getInstance(); - Tab tab = tabs.getSelectedTab(); - - if (isFullScreen) { - addFullScreenPluginView(view); - return; - } - - PluginLayer layer = (PluginLayer) tab.getPluginLayer(view); - if (layer == null) { - layer = new PluginLayer(view, rect, mLayerView.getRenderer().getMaxTextureSize()); - tab.addPluginLayer(view, layer); - } else { - layer.reset(rect); - layer.setVisible(true); - } - - mLayerView.addLayer(layer); - } - }); - } - - private void removeFullScreenPluginView(View view) { - if (mFullScreenPluginView == null) { - Log.w(LOGTAG, "Don't have a fullscreen plugin view"); - return; - } - - if (mFullScreenPluginView != view) { - Log.w(LOGTAG, "Passed view is not the current full screen view"); - return; - } - - mFullScreenPluginContainer.removeView(mFullScreenPluginView); - - // We need do do this on the next iteration in order to avoid - // a deadlock, see comment below in FullScreenHolder - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - mLayerView.show(); - } - }); - - FrameLayout decor = (FrameLayout)getWindow().getDecorView(); - decor.removeView(mFullScreenPluginContainer); - - mFullScreenPluginView = null; - - GeckoScreenOrientationListener.getInstance().unlockScreenOrientation(); - setFullScreen(false); - } - - public void removePluginView(final View view, final boolean isFullScreen) { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - Tabs tabs = Tabs.getInstance(); - Tab tab = tabs.getSelectedTab(); - - if (isFullScreen) { - removeFullScreenPluginView(view); - return; - } - - PluginLayer layer = (PluginLayer) tab.removePluginLayer(view); - if (layer != null) { - layer.destroy(); - } - } - }); - } - - private void setImageAsWallpaper(final String aSrc) { - final String progText = getString(R.string.wallpaper_progress); - final String successText = getString(R.string.wallpaper_success); - final String failureText = getString(R.string.wallpaper_fail); - final String fileName = aSrc.substring(aSrc.lastIndexOf("/") + 1); - final PendingIntent emptyIntent = PendingIntent.getActivity(this, 0, new Intent(), 0); - final AlertNotification notification = new AlertNotification(this, fileName.hashCode(), - R.drawable.alert_download, fileName, progText, System.currentTimeMillis(), null); - notification.setLatestEventInfo(this, fileName, progText, emptyIntent ); - notification.flags |= Notification.FLAG_ONGOING_EVENT; - notification.show(); - new UiAsyncTask<Void, Void, Boolean>(ThreadUtils.getBackgroundHandler()) { - - @Override - protected Boolean doInBackground(Void... params) { - WallpaperManager mgr = WallpaperManager.getInstance(GeckoApp.this); - if (mgr == null) { - return false; - } - - // Determine the ideal width and height of the wallpaper - // for the device - - int idealWidth = mgr.getDesiredMinimumWidth(); - int idealHeight = mgr.getDesiredMinimumHeight(); - - // Sometimes WallpaperManager's getDesiredMinimum*() methods - // can return 0 if a Remote Exception occurs when calling the - // Wallpaper Service. So if that fails, we are calculating - // the ideal width and height from the device's display - // resolution (excluding the decorated area) - - if (idealWidth <= 0 || idealHeight <= 0) { - int orientation; - Display defaultDisplay = getWindowManager().getDefaultDisplay(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { - orientation = defaultDisplay.getRotation(); - } else { - orientation = defaultDisplay.getOrientation(); - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - Point size = new Point(); - defaultDisplay.getSize(size); - // The ideal wallpaper width is always twice the size of - // display width - if (orientation == Surface.ROTATION_0 || orientation == Surface.ROTATION_270) { - idealWidth = size.x * 2; - idealHeight = size.y; - } else { - idealWidth = size.y; - idealHeight = size.x * 2; - } - } else { - if (orientation == Surface.ROTATION_0 || orientation == Surface.ROTATION_270) { - idealWidth = defaultDisplay.getWidth() * 2; - idealHeight = defaultDisplay.getHeight(); - } else { - idealWidth = defaultDisplay.getHeight(); - idealHeight = defaultDisplay.getWidth() * 2; - } - } - } - - boolean isDataURI = aSrc.startsWith("data:"); - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - Bitmap image = null; - InputStream is = null; - ByteArrayOutputStream os = null; - try{ - if (isDataURI) { - int dataStart = aSrc.indexOf(','); - byte[] buf = Base64.decode(aSrc.substring(dataStart+1), Base64.DEFAULT); - BitmapUtils.decodeByteArray(buf, options); - options.inSampleSize = getBitmapSampleSize(options, idealWidth, idealHeight); - options.inJustDecodeBounds = false; - image = BitmapUtils.decodeByteArray(buf, options); - } else { - int byteRead; - byte[] buf = new byte[4192]; - os = new ByteArrayOutputStream(); - URL url = new URL(aSrc); - is = url.openStream(); - - // Cannot read from same stream twice. Also, InputStream from - // URL does not support reset. So converting to byte array - - while((byteRead = is.read(buf)) != -1) { - os.write(buf, 0, byteRead); - } - byte[] imgBuffer = os.toByteArray(); - BitmapUtils.decodeByteArray(imgBuffer, options); - options.inSampleSize = getBitmapSampleSize(options, idealWidth, idealHeight); - options.inJustDecodeBounds = false; - image = BitmapUtils.decodeByteArray(imgBuffer, options); - } - if(image != null) { - mgr.setBitmap(image); - return true; - } else { - return false; - } - } catch(OutOfMemoryError ome) { - Log.e(LOGTAG, "Out of Memory when converting to byte array", ome); - return false; - } catch(IOException ioe) { - Log.e(LOGTAG, "I/O Exception while setting wallpaper", ioe); - return false; - } finally { - if(is != null) { - try { - is.close(); - } catch(IOException ioe) { - Log.w(LOGTAG, "I/O Exception while closing stream", ioe); - } - } - if(os != null) { - try { - os.close(); - } catch(IOException ioe) { - Log.w(LOGTAG, "I/O Exception while closing stream", ioe); - } - } - } - } - - @Override - protected void onPostExecute(Boolean success) { - notification.cancel(); - notification.flags = 0; - notification.flags |= Notification.FLAG_AUTO_CANCEL; - if(!success) { - notification.tickerText = failureText; - notification.setLatestEventInfo(GeckoApp.this, fileName, failureText, emptyIntent); - } else { - notification.tickerText = successText; - notification.setLatestEventInfo(GeckoApp.this, fileName, successText, emptyIntent); - } - notification.show(); - } - }.execute(); - } - - private int getBitmapSampleSize(BitmapFactory.Options options, int idealWidth, int idealHeight) { - int width = options.outWidth; - int height = options.outHeight; - int inSampleSize = 1; - if (height > idealHeight || width > idealWidth) { - if (width > height) { - inSampleSize = Math.round((float)height / (float)idealHeight); - } else { - inSampleSize = Math.round((float)width / (float)idealWidth); - } - } - return inSampleSize; - } - - private void hidePluginLayer(Layer layer) { - LayerView layerView = mLayerView; - layerView.removeLayer(layer); - layerView.requestRender(); - } - - private void showPluginLayer(Layer layer) { - LayerView layerView = mLayerView; - layerView.addLayer(layer); - layerView.requestRender(); - } - - public void requestRender() { - mLayerView.requestRender(); - } - - public void hidePlugins(Tab tab) { - for (Layer layer : tab.getPluginLayers()) { - if (layer instanceof PluginLayer) { - ((PluginLayer) layer).setVisible(false); - } - - hidePluginLayer(layer); - } - - requestRender(); - } - - public void showPlugins() { - Tabs tabs = Tabs.getInstance(); - Tab tab = tabs.getSelectedTab(); - - showPlugins(tab); - } - - public void showPlugins(Tab tab) { - for (Layer layer : tab.getPluginLayers()) { - showPluginLayer(layer); - - if (layer instanceof PluginLayer) { - ((PluginLayer) layer).setVisible(true); - } - } - - requestRender(); - } - - public void setFullScreen(final boolean fullscreen) { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - // Hide/show the system notification bar - Window window = getWindow(); - window.setFlags(fullscreen ? - WindowManager.LayoutParams.FLAG_FULLSCREEN : 0, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - - if (Build.VERSION.SDK_INT >= 11) - window.getDecorView().setSystemUiVisibility(fullscreen ? 1 : 0); - } - }); - } - - /** - * Called when the activity is first created. - * - * Here we initialize all of our profile settings, Firefox Health Report, - * and other one-shot constructions. - **/ - @Override - public void onCreate(Bundle savedInstanceState) - { - GeckoAppShell.registerGlobalExceptionHandler(); - - // Enable Android Strict Mode for developers' local builds (the "default" channel). - if ("default".equals(AppConstants.MOZ_UPDATE_CHANNEL)) { - enableStrictMode(); - } - - String args = getIntent().getStringExtra("args"); - - String profileName = null; - String profilePath = null; - if (args != null) { - if (args.contains("-P")) { - Pattern p = Pattern.compile("(?:-P\\s*)(\\w*)(\\s*)"); - Matcher m = p.matcher(args); - if (m.find()) { - profileName = m.group(1); - } - } - - if (args.contains("-profile")) { - Pattern p = Pattern.compile("(?:-profile\\s*)(\\S*)(\\s*)"); - Matcher m = p.matcher(args); - if (m.find()) { - profilePath = m.group(1); - } - if (profileName == null) { - profileName = getDefaultProfileName(); - if (profileName == null) - profileName = "default"; - } - GeckoApp.sIsUsingCustomProfile = true; - } - - if (profileName != null || profilePath != null) { - mProfile = GeckoProfile.get(this, profileName, profilePath); - } - } - - BrowserDB.initialize(getProfile().getName()); - ((GeckoApplication)getApplication()).initialize(); - - sAppContext = this; - GeckoAppShell.setContextGetter(this); - GeckoAppShell.setGeckoInterface(this); - ThreadUtils.setUiThread(Thread.currentThread(), new Handler()); - - Tabs.getInstance().attachToActivity(this); - Favicons.getInstance().attachToContext(this); - - // When we detect a locale change, we need to restart Gecko, which - // actually means restarting the entire application. This logic should - // actually be handled elsewhere since GeckoApp may not be alive to - // handle this event if "Don't keep activities" is enabled (filed as - // bug 889082). - if (((GeckoApplication)getApplication()).needsRestart()) { - doRestart(); - System.exit(0); - return; - } - - if (sGeckoThread != null) { - // This happens when the GeckoApp activity is destroyed by Android - // without killing the entire application (see Bug 769269). - mIsRestoringActivity = true; - } - - // Fix for Bug 830557 on Tegra boards running Froyo. - // This fix must be done before doing layout. - // Assume the bug is fixed in Gingerbread and up. - if (Build.VERSION.SDK_INT < 9) { - try { - Class<?> inputBindResultClass = - Class.forName("com.android.internal.view.InputBindResult"); - java.lang.reflect.Field creatorField = - inputBindResultClass.getField("CREATOR"); - Log.i(LOGTAG, "froyo startup fix: " + String.valueOf(creatorField.get(null))); - } catch (Exception e) { - Log.w(LOGTAG, "froyo startup fix failed", e); - } - } - - LayoutInflater.from(this).setFactory(this); - - super.onCreate(savedInstanceState); - - mOrientation = getResources().getConfiguration().orientation; - - setContentView(getLayout()); - - // Set up Gecko layout. - mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout); - mMainLayout = (RelativeLayout) findViewById(R.id.main_layout); - - // Set up tabs panel. - mTabsPanel = (TabsPanel) findViewById(R.id.tabs_panel); - mNotificationHelper = new NotificationHelper(this); - - // Check if the last run was exited due to a normal kill while - // we were in the background, or a more harsh kill while we were - // active. - mRestoreMode = getSessionRestoreState(savedInstanceState); - if (mRestoreMode == RESTORE_NORMAL && savedInstanceState != null) { - boolean wasInBackground = - savedInstanceState.getBoolean(SAVED_STATE_IN_BACKGROUND, false); - - if (savedInstanceState.getBoolean(SAVED_STATE_INTENT_HANDLED, false)) { - Intent thisIntent = getIntent(); - // Bug 896992 - This intent has already been handled, clear the intent action. - thisIntent.setAction(Intent.ACTION_MAIN); - setIntent(thisIntent); - - // Persist this flag for reincarnations of this Activity Intent. - mIntentHandled = true; - } - - mPrivateBrowsingSession = savedInstanceState.getString(SAVED_STATE_PRIVATE_SESSION); - } - - // Perform background initialization. - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - final SharedPreferences prefs = GeckoApp.getAppSharedPreferences(); - -// SessionInformation previousSession = SessionInformation.fromSharedPrefs(prefs); - - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean(GeckoApp.PREFS_OOM_EXCEPTION, false); - - // Put a flag to check if we got a normal `onSaveInstanceState` - // on exit, or if we were suddenly killed (crash or native OOM). - editor.putBoolean(GeckoApp.PREFS_WAS_STOPPED, false); - - editor.commit(); - - // The lifecycle of mHealthRecorder is "shortly after onCreate" - // through "onDestroy" -- essentially the same as the lifecycle - // of the activity itself. - final String profilePath = getProfile().getDir().getAbsolutePath(); - final EventDispatcher dispatcher = GeckoAppShell.getEventDispatcher(); -// Log.i(LOGTAG, "Creating BrowserHealthRecorder."); -// mHealthRecorder = new BrowserHealthRecorder(GeckoApp.this, profilePath, dispatcher, -// previousSession); - } - }); - - GeckoAppShell.setNotificationClient(makeNotificationClient()); - } - - protected void initializeChrome() { - mDoorHangerPopup = new DoorHangerPopup(this, null); - mPluginContainer = (AbsoluteLayout) findViewById(R.id.plugin_container); - mFormAssistPopup = (FormAssistPopup) findViewById(R.id.form_assist_popup); - - if (mCameraView == null) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - mCameraView = new SurfaceView(this); - ((SurfaceView)mCameraView).getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); - } else { - mCameraView = new TextureView(this); - } - } - - if (mLayerView == null) { - LayerView layerView = (LayerView) findViewById(R.id.layer_view); - layerView.initializeView(GeckoAppShell.getEventDispatcher()); - mLayerView = layerView; - GeckoAppShell.setLayerView(layerView); - // bind the GeckoEditable instance to the new LayerView - GeckoAppShell.notifyIMEContext(GeckoEditableListener.IME_STATE_DISABLED, "", "", ""); - } - } - - /** - * Loads the initial tab at Fennec startup. - * - * If Fennec was opened with an external URL, that URL will be loaded. - * Otherwise, unless there was a session restore, the default URL - * (about:home) be loaded. - * - * @param url External URL to load, or null to load the default URL - */ - protected void loadStartupTab(String url) { - if (url == null) { - if (mRestoreMode == RESTORE_NONE) { - // Show about:home if we aren't restoring previous session and - // there's no external URL - Tab tab = Tabs.getInstance().loadUrl("about:home", Tabs.LOADURL_NEW_TAB); - } - } else { - // If given an external URL, load it - int flags = Tabs.LOADURL_NEW_TAB | Tabs.LOADURL_USER_ENTERED | Tabs.LOADURL_EXTERNAL; - Tabs.getInstance().loadUrl(url, flags); - } - } - - private void initialize() { - mInitialized = true; - - invalidateOptionsMenu(); - - Intent intent = getIntent(); - String action = intent.getAction(); - - String passedUri = null; - String uri = getURIFromIntent(intent); - if (uri != null && uri.length() > 0) { - passedUri = uri; - } - - final boolean isExternalURL = passedUri != null && !passedUri.equals("about:home"); - StartupAction startupAction; - if (isExternalURL) { - startupAction = StartupAction.URL; - } else { - startupAction = StartupAction.NORMAL; - } - - // Start migrating as early as possible, can do this in - // parallel with Gecko load. - checkMigrateProfile(); - - Uri data = intent.getData(); - if (data != null && "http".equals(data.getScheme())) { - startupAction = StartupAction.PREFETCH; - ThreadUtils.postToBackgroundThread(new PrefetchRunnable(data.toString())); - } - - Tabs.registerOnTabsChangedListener(this); - - initializeChrome(); - - // If we are doing a restore, read the session data and send it to Gecko - String restoreMessage = null; - if (mRestoreMode != RESTORE_NONE && !mIsRestoringActivity) { - try { - // restoreSessionTabs() will create simple tab stubs with the - // URL and title for each page, but we also need to restore - // session history. restoreSessionTabs() will inject the IDs - // of the tab stubs into the JSON data (which holds the session - // history). This JSON data is then sent to Gecko so session - // history can be restored for each tab. - restoreMessage = restoreSessionTabs(isExternalURL); - } catch (SessionRestoreException e) { - // If restore failed, do a normal startup - Log.e(LOGTAG, "An error occurred during restore", e); - mRestoreMode = RESTORE_NONE; - } - } - - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Session:Restore", restoreMessage)); - - if (!mIsRestoringActivity) { - loadStartupTab(isExternalURL ? passedUri : null); - } - - if (mRestoreMode == RESTORE_NORMAL) { - // If we successfully did an OOM restore, we now have tab stubs - // from the last session. Any future tabs should be animated. - Tabs.getInstance().notifyListeners(null, Tabs.TabEvents.RESTORED); - } else { - // Move the session file if it exists - getProfile().moveSessionFile(); - } - - if (mRestoreMode == RESTORE_NONE) { - Tabs.getInstance().notifyListeners(null, Tabs.TabEvents.RESTORED); - } - - if (!mIsRestoringActivity) { - sGeckoThread = new GeckoThread(intent, passedUri); - ThreadUtils.setGeckoThread(sGeckoThread); - } - if (!ACTION_DEBUG.equals(action) && - GeckoThread.checkAndSetLaunchState(GeckoThread.LaunchState.Launching, GeckoThread.LaunchState.Launched)) { - sGeckoThread.start(); - } else if (ACTION_DEBUG.equals(action) && - GeckoThread.checkAndSetLaunchState(GeckoThread.LaunchState.Launching, GeckoThread.LaunchState.WaitForDebugger)) { - ThreadUtils.getUiHandler().postDelayed(new Runnable() { - @Override - public void run() { - GeckoThread.setLaunchState(GeckoThread.LaunchState.Launching); - sGeckoThread.start(); - } - }, 1000 * 5 /* 5 seconds */); - } - - // Check if launched from data reporting notification. - if (ACTION_LAUNCH_SETTINGS.equals(action)) { - mIntentHandled = true; - Intent settingsIntent = new Intent(GeckoApp.this, GeckoPreferences.class); - // Copy extras. - settingsIntent.putExtras(intent); - startActivity(settingsIntent); - } - - //app state callbacks - mAppStateListeners = new LinkedList<GeckoAppShell.AppStateListener>(); - - //register for events - registerEventListener("log"); - registerEventListener("Reader:ListCountRequest"); - registerEventListener("Reader:Added"); - registerEventListener("Reader:Removed"); - registerEventListener("Reader:Share"); - registerEventListener("Reader:FaviconRequest"); - registerEventListener("Reader:GoToReadingList"); - registerEventListener("onCameraCapture"); - registerEventListener("Menu:Add"); - registerEventListener("Menu:Remove"); - registerEventListener("Menu:Update"); - registerEventListener("Gecko:Ready"); - registerEventListener("Toast:Show"); - registerEventListener("DOMFullScreen:Start"); - registerEventListener("DOMFullScreen:Stop"); - registerEventListener("ToggleChrome:Hide"); - registerEventListener("ToggleChrome:Show"); - registerEventListener("ToggleChrome:Focus"); - registerEventListener("Permissions:Data"); - registerEventListener("Tab:ViewportMetadata"); - registerEventListener("Session:StatePurged"); - registerEventListener("Bookmark:Insert"); - registerEventListener("Accessibility:Event"); - registerEventListener("Accessibility:Ready"); - registerEventListener("Shortcut:Remove"); - registerEventListener("WebApps:Open"); - registerEventListener("WebApps:PreInstall"); - registerEventListener("WebApps:PostInstall"); - registerEventListener("WebApps:Install"); - registerEventListener("WebApps:Uninstall"); - registerEventListener("Share:Text"); - registerEventListener("Share:Image"); - registerEventListener("Wallpaper:Set"); - registerEventListener("Sanitize:ClearHistory"); - registerEventListener("Update:Check"); - registerEventListener("Update:Download"); - registerEventListener("Update:Install"); - registerEventListener("PrivateBrowsing:Data"); - registerEventListener("Contact:Add"); - - if (SmsManager.getInstance() != null) { - SmsManager.getInstance().start(); - } - - mPromptService = new PromptService(this); - - mTextSelection = new TextSelection((TextSelectionHandle) findViewById(R.id.start_handle), - (TextSelectionHandle) findViewById(R.id.middle_handle), - (TextSelectionHandle) findViewById(R.id.end_handle), - GeckoAppShell.getEventDispatcher(), - this); - - PrefsHelper.getPref("app.update.autodownload", new PrefsHelper.PrefHandlerBase() { - @Override public void prefValue(String pref, String value) { - UpdateServiceHelper.registerForUpdates(GeckoApp.this, value); - } - }); - - ThreadUtils.getBackgroundHandler().postDelayed(new Runnable() { - @Override - public void run() { - // Sync settings need Gecko to be loaded, so - // no hurry in starting this. - checkMigrateSync(); - - // Kick off our background services that upload health reports. - // We do this by invoking the broadcast receiver, which uses the - // system alarm infrastructure to perform tasks at intervals. - GeckoPreferences.broadcastHealthReportUploadPref(GeckoApp.this); - - /* - XXXX see bug 635342 - We want to disable this code if possible. It is about 145ms in runtime - SharedPreferences settings = getPreferences(Activity.MODE_PRIVATE); - String localeCode = settings.getString(getPackageName() + ".locale", ""); - if (localeCode != null && localeCode.length() > 0) - GeckoAppShell.setSelectedLocale(localeCode); - */ - - if (!GeckoThread.checkLaunchState(GeckoThread.LaunchState.Launched)) { - return; - } - } - }, 50); - - if (mIsRestoringActivity) { - GeckoThread.setLaunchState(GeckoThread.LaunchState.GeckoRunning); - Tab selectedTab = Tabs.getInstance().getSelectedTab(); - if (selectedTab != null) - Tabs.getInstance().notifyListeners(selectedTab, Tabs.TabEvents.SELECTED); - geckoConnected(); - GeckoAppShell.setLayerClient(mLayerView.getLayerClient()); - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Viewport:Flush", null)); - } - - if (ACTION_ALERT_CALLBACK.equals(action)) { - processAlertCallback(intent); - } - } - - private String restoreSessionTabs(final boolean isExternalURL) throws SessionRestoreException { - try { - String sessionString = getProfile().readSessionFile(false); - if (sessionString == null) { - throw new SessionRestoreException("Could not read from session file"); - } - - // If we are doing an OOM restore, parse the session data and - // stub the restored tabs immediately. This allows the UI to be - // updated before Gecko has restored. - if (mRestoreMode == RESTORE_NORMAL) { - final JSONArray tabs = new JSONArray(); - SessionParser parser = new SessionParser() { - @Override - public void onTabRead(SessionTab sessionTab) { - JSONObject tabObject = sessionTab.getTabObject(); - - int flags = Tabs.LOADURL_NEW_TAB; - flags |= ((isExternalURL || !sessionTab.isSelected()) ? Tabs.LOADURL_DELAY_LOAD : 0); - flags |= (tabObject.optBoolean("desktopMode") ? Tabs.LOADURL_DESKTOP : 0); - flags |= (tabObject.optBoolean("isPrivate") ? Tabs.LOADURL_PRIVATE : 0); - - Tab tab = Tabs.getInstance().loadUrl(sessionTab.getUrl(), flags); - tab.updateTitle(sessionTab.getTitle()); - - try { - tabObject.put("tabId", tab.getId()); - } catch (JSONException e) { - Log.e(LOGTAG, "JSON error", e); - } - tabs.put(tabObject); - } - }; - - if (mPrivateBrowsingSession == null) { - parser.parse(sessionString); - } else { - parser.parse(sessionString, mPrivateBrowsingSession); - } - - if (tabs.length() > 0) { - sessionString = new JSONObject().put("windows", new JSONArray().put(new JSONObject().put("tabs", tabs))).toString(); - } else { - throw new SessionRestoreException("No tabs could be read from session file"); - } - } - - JSONObject restoreData = new JSONObject(); - restoreData.put("normalRestore", mRestoreMode == RESTORE_NORMAL); - restoreData.put("sessionString", sessionString); - return restoreData.toString(); - - } catch (JSONException e) { - throw new SessionRestoreException(e); - } - } - - public GeckoProfile getProfile() { - // fall back to default profile if we didn't load a specific one - if (mProfile == null) { - mProfile = GeckoProfile.get(this); - } - return mProfile; - } - - protected int getSessionRestoreState(Bundle savedInstanceState) { - final SharedPreferences prefs = GeckoApp.getAppSharedPreferences(); - int restoreMode = RESTORE_NONE; - - // If the version has changed, the user has done an upgrade, so restore - // previous tabs. - final int versionCode = getVersionCode(); - if (prefs.getInt(PREFS_VERSION_CODE, 0) != versionCode) { - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - prefs.edit() - .putInt(PREFS_VERSION_CODE, versionCode) - .commit(); - } - }); - - restoreMode = RESTORE_NORMAL; - } - - // We record crashes in the crash reporter. If sessionstore.js - // exists, but we didn't flag a crash in the crash reporter, we - // were probably just force killed by the user, so we shouldn't do - // a restore... - if (prefs.getBoolean(PREFS_CRASHED, false)) { - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - prefs.edit() - .putBoolean(PREFS_CRASHED, false) - .commit(); - } - }); - - restoreMode = RESTORE_CRASH; - } - - // ...except for when the user chooses to. - if (savedInstanceState != null || - PreferenceManager.getDefaultSharedPreferences(this).getBoolean(GeckoPreferences.PREFS_RESTORE_SESSION, false)) { - restoreMode = RESTORE_NORMAL; - } - - return restoreMode; - } - - /** - * Enable Android StrictMode checks (for supported OS versions). - * http://developer.android.com/reference/android/os/StrictMode.html - */ - private void enableStrictMode() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) { - return; - } - - Log.d(LOGTAG, "Enabling Android StrictMode"); - - StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() - .detectAll() - .penaltyLog() - .build()); - - StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() - .detectAll() - .penaltyLog() - .build()); - } - - public void enableCameraView() { - // Start listening for orientation events - mCameraOrientationEventListener = new OrientationEventListener(this) { - @Override - public void onOrientationChanged(int orientation) { - if (mAppStateListeners != null) { - for (GeckoAppShell.AppStateListener listener: mAppStateListeners) { - listener.onOrientationChanged(); - } - } - } - }; - mCameraOrientationEventListener.enable(); - - // Try to make it fully transparent. - if (mCameraView instanceof SurfaceView) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - mCameraView.setAlpha(0.0f); - } - } else if (mCameraView instanceof TextureView) { - mCameraView.setAlpha(0.0f); - } - RelativeLayout mCameraLayout = (RelativeLayout) findViewById(R.id.camera_layout); - // Some phones (eg. nexus S) need at least a 8x16 preview size - mCameraLayout.addView(mCameraView, - new AbsoluteLayout.LayoutParams(8, 16, 0, 0)); - } - - public void disableCameraView() { - if (mCameraOrientationEventListener != null) { - mCameraOrientationEventListener.disable(); - mCameraOrientationEventListener = null; - } - RelativeLayout mCameraLayout = (RelativeLayout) findViewById(R.id.camera_layout); - mCameraLayout.removeView(mCameraView); - } - - public String getDefaultUAString() { - return HardwareUtils.isTablet() ? AppConstants.USER_AGENT_FENNEC_TABLET : - AppConstants.USER_AGENT_FENNEC_MOBILE; - } - - public String getUAStringForHost(String host) { - // With our standard UA String, we get a 200 response code and - // client-side redirect from t.co. This bot-like UA gives us a - // 301 response code - if ("t.co".equals(host)) { - return AppConstants.USER_AGENT_BOT_LIKE; - } - return getDefaultUAString(); - } - - class PrefetchRunnable implements Runnable { - private String mPrefetchUrl; - - PrefetchRunnable(String prefetchUrl) { - mPrefetchUrl = prefetchUrl; - } - - @Override - public void run() { - HttpURLConnection connection = null; - try { - URL url = new URL(mPrefetchUrl); - // data url should have an http scheme - connection = (HttpURLConnection) url.openConnection(); - connection.setRequestProperty("User-Agent", getUAStringForHost(url.getHost())); - connection.setInstanceFollowRedirects(false); - connection.setRequestMethod("GET"); - connection.connect(); - } catch (Exception e) { - Log.e(LOGTAG, "Exception prefetching URL", e); - } finally { - if (connection != null) - connection.disconnect(); - } - } - } - - private void processAlertCallback(Intent intent) { - String alertName = ""; - String alertCookie = ""; - Uri data = intent.getData(); - if (data != null) { - alertName = data.getQueryParameter("name"); - if (alertName == null) - alertName = ""; - alertCookie = data.getQueryParameter("cookie"); - if (alertCookie == null) - alertCookie = ""; - } - handleNotification(ACTION_ALERT_CALLBACK, alertName, alertCookie); - - if (intent.hasExtra(NotificationHelper.NOTIFICATION_ID)) { - String id = intent.getStringExtra(NotificationHelper.NOTIFICATION_ID); - mNotificationHelper.hideNotification(id); - } - } - - @Override - protected void onNewIntent(Intent intent) { - if (GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoExiting)) { - // We're exiting and shouldn't try to do anything else just incase - // we're hung for some reason we'll force the process to exit - System.exit(0); - return; - } - - // if we were previously OOM killed, we can end up here when launching - // from external shortcuts, so set this as the intent for initialization - if (!mInitialized) { - setIntent(intent); - return; - } - - // don't perform any actions if launching from recent apps - if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) - return; - - final String action = intent.getAction(); - - if (Intent.ACTION_MAIN.equals(action)) { - GeckoAppShell.sendEventToGecko(GeckoEvent.createURILoadEvent("")); - } else if (ACTION_LOAD.equals(action)) { - String uri = intent.getDataString(); - Tabs.getInstance().loadUrl(uri); - } else if (Intent.ACTION_VIEW.equals(action)) { - String uri = intent.getDataString(); - GeckoAppShell.sendEventToGecko(GeckoEvent.createURILoadEvent(uri)); - } else if (action != null && action.startsWith(ACTION_WEBAPP_PREFIX)) { - String uri = getURIFromIntent(intent); - GeckoAppShell.sendEventToGecko(GeckoEvent.createWebappLoadEvent(uri)); - } else if (ACTION_BOOKMARK.equals(action)) { - String uri = getURIFromIntent(intent); - GeckoAppShell.sendEventToGecko(GeckoEvent.createBookmarkLoadEvent(uri)); - } else if (Intent.ACTION_SEARCH.equals(action)) { - String uri = getURIFromIntent(intent); - GeckoAppShell.sendEventToGecko(GeckoEvent.createURILoadEvent(uri)); - } else if (ACTION_ALERT_CALLBACK.equals(action)) { - processAlertCallback(intent); - } else if (ACTION_LAUNCH_SETTINGS.equals(action)) { - mIntentHandled = true; - // Check if launched from data reporting notification. - Intent settingsIntent = new Intent(GeckoApp.this, GeckoPreferences.class); - // Copy extras. - settingsIntent.putExtras(intent); - startActivity(settingsIntent); - } - } - - /* - * Handles getting a uri from and intent in a way that is backwards - * compatable with our previous implementations - */ - protected String getURIFromIntent(Intent intent) { - final String action = intent.getAction(); - if (ACTION_ALERT_CALLBACK.equals(action)) - return null; - - String uri = intent.getDataString(); - if (uri != null) - return uri; - - if ((action != null && action.startsWith(ACTION_WEBAPP_PREFIX)) || ACTION_BOOKMARK.equals(action)) { - uri = intent.getStringExtra("args"); - if (uri != null && uri.startsWith("--url=")) { - uri.replace("--url=", ""); - } - } - return uri; - } - - @Override - public void onResume() - { - // After an onPause, the activity is back in the foreground. - // Undo whatever we did in onPause. - super.onResume(); - - int newOrientation = getResources().getConfiguration().orientation; - - if (mOrientation != newOrientation) { - mOrientation = newOrientation; - refreshChrome(); - } - - GeckoScreenOrientationListener.getInstance().start(); - - // User may have enabled/disabled accessibility. - GeckoAccessibility.updateAccessibilitySettings(this); - - if (mAppStateListeners != null) { - for (GeckoAppShell.AppStateListener listener: mAppStateListeners) { - listener.onResume(); - } - } - - // We use two times: a pseudo-unique wall-clock time to identify the - // current session across power cycles, and the elapsed realtime to - // track the duration of the session. - final long now = System.currentTimeMillis(); - final long realTime = android.os.SystemClock.elapsedRealtime(); - -/* ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - // Now construct the new session on BrowserHealthRecorder's behalf. We do this here - // so it can benefit from a single near-startup prefs commit. - SessionInformation currentSession = new SessionInformation(now, realTime); - - SharedPreferences prefs = GeckoApp.getAppSharedPreferences(); - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean(GeckoApp.PREFS_WAS_STOPPED, false); - currentSession.recordBegin(editor); - editor.commit(); - - final BrowserHealthRecorder rec = mHealthRecorder; - if (rec != null) { - rec.setCurrentSession(currentSession); - } else { - Log.w(LOGTAG, "Can't record session: rec is null."); - } - } - }); */ - } - - @Override - public void onWindowFocusChanged(boolean hasFocus) { - super.onWindowFocusChanged(hasFocus); - - if (!mInitialized && hasFocus) { - initialize(); - getWindow().setBackgroundDrawable(null); - } - } - - @Override - public void onPause() - { - // final BrowserHealthRecorder rec = mHealthRecorder; - - // In some way it's sad that Android will trigger StrictMode warnings - // here as the whole point is to save to disk while the activity is not - // interacting with the user. - /* ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - SharedPreferences prefs = GeckoApp.getAppSharedPreferences(); - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean(GeckoApp.PREFS_WAS_STOPPED, true); - if (rec != null) { - rec.recordSessionEnd("P", editor); - } - editor.commit(); - } - }); */ - - GeckoScreenOrientationListener.getInstance().stop(); - - if (mAppStateListeners != null) { - for(GeckoAppShell.AppStateListener listener: mAppStateListeners) { - listener.onPause(); - } - } - - super.onPause(); - } - - @Override - public void onRestart() - { - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - SharedPreferences prefs = GeckoApp.getAppSharedPreferences(); - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean(GeckoApp.PREFS_WAS_STOPPED, false); - editor.commit(); - } - }); - - super.onRestart(); - } - - @Override - public void onDestroy() - { - unregisterEventListener("log"); - unregisterEventListener("Reader:ListCountRequest"); - unregisterEventListener("Reader:Added"); - unregisterEventListener("Reader:Removed"); - unregisterEventListener("Reader:Share"); - unregisterEventListener("Reader:FaviconRequest"); - unregisterEventListener("Reader:GoToReadingList"); - unregisterEventListener("onCameraCapture"); - unregisterEventListener("Menu:Add"); - unregisterEventListener("Menu:Remove"); - unregisterEventListener("Menu:Update"); - unregisterEventListener("Gecko:Ready"); - unregisterEventListener("Toast:Show"); - unregisterEventListener("DOMFullScreen:Start"); - unregisterEventListener("DOMFullScreen:Stop"); - unregisterEventListener("ToggleChrome:Hide"); - unregisterEventListener("ToggleChrome:Show"); - unregisterEventListener("ToggleChrome:Focus"); - unregisterEventListener("Permissions:Data"); - unregisterEventListener("Tab:ViewportMetadata"); - unregisterEventListener("Session:StatePurged"); - unregisterEventListener("Bookmark:Insert"); - unregisterEventListener("Accessibility:Event"); - unregisterEventListener("Accessibility:Ready"); - unregisterEventListener("Shortcut:Remove"); - unregisterEventListener("WebApps:Open"); - unregisterEventListener("WebApps:PreInstall"); - unregisterEventListener("WebApps:PostInstall"); - unregisterEventListener("WebApps:Install"); - unregisterEventListener("WebApps:Uninstall"); - unregisterEventListener("Share:Text"); - unregisterEventListener("Share:Image"); - unregisterEventListener("Wallpaper:Set"); - unregisterEventListener("Sanitize:ClearHistory"); - unregisterEventListener("Update:Check"); - unregisterEventListener("Update:Download"); - unregisterEventListener("Update:Install"); - unregisterEventListener("PrivateBrowsing:Data"); - unregisterEventListener("Contact:Add"); - - deleteTempFiles(); - - if (mLayerView != null) - mLayerView.destroy(); - if (mDoorHangerPopup != null) - mDoorHangerPopup.destroy(); - if (mFormAssistPopup != null) - mFormAssistPopup.destroy(); - if (mPromptService != null) - mPromptService.destroy(); - if (mTextSelection != null) - mTextSelection.destroy(); - if (mNotificationHelper != null) - mNotificationHelper.destroy(); - - Tabs.getInstance().detachFromActivity(this); - - if (SmsManager.getInstance() != null) { - SmsManager.getInstance().stop(); - if (isFinishing()) - SmsManager.getInstance().shutdown(); - } - -/* final BrowserHealthRecorder rec = mHealthRecorder; - mHealthRecorder = null; - if (rec != null) { - // Closing a BrowserHealthRecorder could incur a write. - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - rec.close(); - } - }); - } */ - - super.onDestroy(); - - Tabs.unregisterOnTabsChangedListener(this); - } - - protected void registerEventListener(String event) { - GeckoAppShell.getEventDispatcher().registerEventListener(event, this); - } - - protected void unregisterEventListener(String event) { - GeckoAppShell.getEventDispatcher().unregisterEventListener(event, this); - } - - // Get a temporary directory, may return null - public static File getTempDirectory() { - File dir = sAppContext.getExternalFilesDir("temp"); - return dir; - } - - // Delete any files in our temporary directory - public static void deleteTempFiles() { - File dir = getTempDirectory(); - if (dir == null) - return; - File[] files = dir.listFiles(); - if (files == null) - return; - for (File file : files) { - file.delete(); - } - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - - if (mOrientation != newConfig.orientation) { - mOrientation = newConfig.orientation; - if (mFormAssistPopup != null) - mFormAssistPopup.hide(); - refreshChrome(); - } - } - - public String getContentProcessName() { - return AppConstants.MOZ_CHILD_PROCESS_NAME; - } - - /* - * Only one factory can be set on the inflater; however, we want to use two - * factories (GeckoViewsFactory and the FragmentActivity factory). - * Overriding onCreateView() here allows us to dispatch view creation to - * both factories. - */ - @Override - public View onCreateView(String name, Context context, AttributeSet attrs) { - View view = GeckoViewsFactory.getInstance().onCreateView(name, context, attrs); - if (view == null) { - view = super.onCreateView(name, context, attrs); - } - return view; - } - - public void addEnvToIntent(Intent intent) { - Map<String,String> envMap = System.getenv(); - Set<Map.Entry<String,String>> envSet = envMap.entrySet(); - Iterator<Map.Entry<String,String>> envIter = envSet.iterator(); - int c = 0; - while (envIter.hasNext()) { - Map.Entry<String,String> entry = envIter.next(); - intent.putExtra("env" + c, entry.getKey() + "=" - + entry.getValue()); - c++; - } - } - - public void doRestart() { - doRestart(RESTARTER_ACTION); - } - - public void doRestart(String action) { - Log.d(LOGTAG, "doRestart(\"" + action + "\")"); - try { - Intent intent = new Intent(action); - intent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, RESTARTER_CLASS); - /* TODO: addEnvToIntent(intent); */ - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_MULTIPLE_TASK); - Log.d(LOGTAG, "Restart intent: " + intent.toString()); - GeckoAppShell.killAnyZombies(); - startActivity(intent); - } catch (Exception e) { - Log.e(LOGTAG, "Error effecting restart.", e); - } - finish(); - // Give the restart process time to start before we die - GeckoAppShell.waitForAnotherGeckoProc(); - } - - public void handleNotification(String action, String alertName, String alertCookie) { - // If Gecko isn't running yet, we ignore the notification. Note that - // even if Gecko is running but it was restarted since the notification - // was created, the notification won't be handled (bug 849653). - if (GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) { - GeckoAppShell.handleNotification(action, alertName, alertCookie); - } - } - - private void checkMigrateProfile() { - final File profileDir = getProfile().getDir(); - - if (profileDir != null) { - final GeckoApp app = GeckoApp.sAppContext; - - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - ProfileMigrator profileMigrator = new ProfileMigrator(app); - - // Do a migration run on the first start after an upgrade. - if (!GeckoApp.sIsUsingCustomProfile && - !profileMigrator.hasMigrationRun()) { - // Show the "Setting up Fennec" screen if this takes - // a while. - - // Create a "final" holder for the setup screen so that we can - // create it in startCallback and still find a reference to it - // in stopCallback. (We must create it on the UI thread to fix - // bug 788216). Note that synchronization is not a problem here - // since it is only ever touched on the UI thread. - final SetupScreen[] setupScreenHolder = new SetupScreen[1]; - - final Runnable startCallback = new Runnable() { - @Override - public void run() { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - setupScreenHolder[0] = new SetupScreen(app); - setupScreenHolder[0].show(); - } - }); - } - }; - - final Runnable stopCallback = new Runnable() { - @Override - public void run() { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - SetupScreen screen = setupScreenHolder[0]; - // screen will never be null if this code runs, but - // stranger things have happened... - if (screen != null) { - screen.dismiss(); - } - } - }); - } - }; - - profileMigrator.setLongOperationCallbacks(startCallback, - stopCallback); - profileMigrator.launchPlaces(profileDir); - finishProfileMigration(); - } - }} - ); - } - } - - protected void finishProfileMigration() { - } - - private void checkMigrateSync() { - final File profileDir = getProfile().getDir(); - if (!GeckoApp.sIsUsingCustomProfile && profileDir != null) { - final GeckoApp app = GeckoApp.sAppContext; - ProfileMigrator profileMigrator = new ProfileMigrator(app); - if (!profileMigrator.hasSyncMigrated()) { - profileMigrator.launchSyncPrefs(); - } - } - } - - public PromptService getPromptService() { - return mPromptService; - } - - public void showReadingList() { - Intent intent = new Intent(getBaseContext(), AwesomeBar.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_NO_HISTORY); - intent.putExtra(AwesomeBar.TARGET_KEY, AwesomeBar.Target.CURRENT_TAB.toString()); - intent.putExtra(AwesomeBar.READING_LIST_KEY, true); - - int requestCode = GeckoAppShell.sActivityHelper.makeRequestCodeForAwesomebar(); - startActivityForResult(intent, requestCode); - } - - @Override - public void onBackPressed() { - if (autoHideTabs()) { - return; - } - - if (mDoorHangerPopup != null && mDoorHangerPopup.isShowing()) { - mDoorHangerPopup.dismiss(); - return; - } - - if (mFullScreenPluginView != null) { - GeckoAppShell.onFullScreenPluginHidden(mFullScreenPluginView); - removeFullScreenPluginView(mFullScreenPluginView); - return; - } - - if (mLayerView != null && mLayerView.isFullScreen()) { - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FullScreen:Exit", null)); - return; - } - - Tabs tabs = Tabs.getInstance(); - Tab tab = tabs.getSelectedTab(); - if (tab == null) { - moveTaskToBack(true); - return; - } - - if (tab.doBack()) - return; - - if (tab.isExternal()) { - moveTaskToBack(true); - tabs.closeTab(tab); - return; - } - - int parentId = tab.getParentId(); - Tab parent = tabs.getTab(parentId); - if (parent != null) { - // The back button should always return to the parent (not a sibling). - tabs.closeTab(tab, parent); - return; - } - - moveTaskToBack(true); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (!GeckoAppShell.sActivityHelper.handleActivityResult(requestCode, resultCode, data)) { - super.onActivityResult(requestCode, resultCode, data); - } - } - - public AbsoluteLayout getPluginContainer() { return mPluginContainer; } - - // Accelerometer. - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - } - - @Override - public void onSensorChanged(SensorEvent event) { - GeckoAppShell.sendEventToGecko(GeckoEvent.createSensorEvent(event)); - } - - // Geolocation. - @Override - public void onLocationChanged(Location location) { - // No logging here: user-identifying information. - GeckoAppShell.sendEventToGecko(GeckoEvent.createLocationEvent(location)); - } - - @Override - public void onProviderDisabled(String provider) - { - } - - @Override - public void onProviderEnabled(String provider) - { - } - - @Override - public void onStatusChanged(String provider, int status, Bundle extras) - { - } - - // Called when a Gecko Hal WakeLock is changed - public void notifyWakeLockChanged(String topic, String state) { - PowerManager.WakeLock wl = mWakeLocks.get(topic); - if (state.equals("locked-foreground") && wl == null) { - PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); - - if (CPU.equals(topic)) { - wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, topic); - } else if (SCREEN.equals(topic)) { - wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, topic); - } - - if (wl != null) { - wl.acquire(); - mWakeLocks.put(topic, wl); - } - } else if (!state.equals("locked-foreground") && wl != null) { - wl.release(); - mWakeLocks.remove(topic); - } - } - - public void notifyCheckUpdateResult(String result) { - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Update:CheckResult", result)); - } - - protected void geckoConnected() { - mLayerView.geckoConnected(); - } - - public void setAccessibilityEnabled(boolean enabled) { - } - - public static class MainLayout extends RelativeLayout { - private TouchEventInterceptor mTouchEventInterceptor; - private MotionEventInterceptor mMotionEventInterceptor; - - public MainLayout(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public void setTouchEventInterceptor(TouchEventInterceptor interceptor) { - mTouchEventInterceptor = interceptor; - } - - public void setMotionEventInterceptor(MotionEventInterceptor interceptor) { - mMotionEventInterceptor = interceptor; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent event) { - if (mTouchEventInterceptor != null && mTouchEventInterceptor.onInterceptTouchEvent(this, event)) { - return true; - } - return super.onInterceptTouchEvent(event); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (mTouchEventInterceptor != null && mTouchEventInterceptor.onTouch(this, event)) { - return true; - } - return super.onTouchEvent(event); - } - - @Override - public boolean onGenericMotionEvent(MotionEvent event) { - if (mMotionEventInterceptor != null && mMotionEventInterceptor.onInterceptMotionEvent(this, event)) { - return true; - } - return super.onGenericMotionEvent(event); - } - - @Override - public void setDrawingCacheEnabled(boolean enabled) { - // Instead of setting drawing cache in the view itself, we simply - // enable drawing caching on its children. This is mainly used in - // animations (see PropertyAnimator) - super.setChildrenDrawnWithCacheEnabled(enabled); - } - } - - private class FullScreenHolder extends FrameLayout { - - public FullScreenHolder(Context ctx) { - super(ctx); - } - - @Override - public void addView(View view, int index) { - /** - * This normally gets called when Flash adds a separate SurfaceView - * for the video. It is unhappy if we have the LayerView underneath - * it for some reason so we need to hide that. Hiding the LayerView causes - * its surface to be destroyed, which causes a pause composition - * event to be sent to Gecko. We synchronously wait for that to be - * processed. Simultaneously, however, Flash is waiting on a mutex so - * the post() below is an attempt to avoid a deadlock. - */ - super.addView(view, index); - - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - mLayerView.hide(); - } - }); - } - - /** - * The methods below are simply copied from what Android WebKit does. - * It wasn't ever called in my testing, but might as well - * keep it in case it is for some reason. The methods - * all return true because we don't want any events - * leaking out from the fullscreen view. - */ - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if (event.isSystem()) { - return super.onKeyDown(keyCode, event); - } - mFullScreenPluginView.onKeyDown(keyCode, event); - return true; - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - if (event.isSystem()) { - return super.onKeyUp(keyCode, event); - } - mFullScreenPluginView.onKeyUp(keyCode, event); - return true; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - return true; - } - - @Override - public boolean onTrackballEvent(MotionEvent event) { - mFullScreenPluginView.onTrackballEvent(event); - return true; - } - } - - protected NotificationClient makeNotificationClient() { - // Don't use a notification service; we may be killed in the background - // during downloads. - return new AppNotificationClient(getApplicationContext()); - } - - private int getVersionCode() { - int versionCode = 0; - try { - versionCode = getPackageManager().getPackageInfo(getPackageName(), 0).versionCode; - } catch (NameNotFoundException e) { - Log.wtf(LOGTAG, getPackageName() + " not found", e); - } - return versionCode; - } -} diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java deleted file mode 100644 index 9107eab7a..000000000 --- a/mobile/android/base/GeckoAppShell.java +++ /dev/null @@ -1,2500 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import org.mozilla.gecko.gfx.BitmapUtils; -import org.mozilla.gecko.gfx.GeckoLayerClient; -import org.mozilla.gecko.gfx.GfxInfoThread; -import org.mozilla.gecko.gfx.LayerView; -import org.mozilla.gecko.gfx.PanZoomController; -import org.mozilla.gecko.mozglue.GeckoLoader; -import org.mozilla.gecko.util.EventDispatcher; -import org.mozilla.gecko.util.GeckoEventListener; -import org.mozilla.gecko.util.HardwareUtils; -import org.mozilla.gecko.util.ThreadUtils; - -import android.app.Activity; -import android.app.ActivityManager; -import android.app.PendingIntent; -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; -import android.content.pm.Signature; -import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.ImageFormat; -import android.graphics.Paint; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.SurfaceTexture; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.hardware.Sensor; -import android.hardware.SensorManager; -import android.hardware.SensorEventListener; -import android.location.Criteria; -import android.location.Location; -import android.location.LocationListener; -import android.location.LocationManager; -import android.media.MediaScannerConnection; -import android.media.MediaScannerConnection.MediaScannerConnectionClient; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.net.Uri; -import android.os.Build; -import android.os.Looper; -import android.os.Message; -import android.os.MessageQueue; -import android.os.SystemClock; -import android.os.Vibrator; -import android.provider.Settings; -import android.text.TextUtils; -import android.util.Base64; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.ContextThemeWrapper; -import android.view.HapticFeedbackConstants; -import android.view.Surface; -import android.view.SurfaceView; -import android.view.TextureView; -import android.view.View; -import android.view.inputmethod.InputMethodManager; -import android.webkit.MimeTypeMap; -import android.widget.AbsoluteLayout; -import android.widget.Toast; - -import java.io.BufferedReader; -import java.io.Closeable; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.net.Proxy; -import java.net.ProxySelector; -import java.net.URI; -import java.net.URL; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.StringTokenizer; -import java.util.TreeMap; - -public class GeckoAppShell -{ - private static final String LOGTAG = "GeckoAppShell"; - - // static members only - private GeckoAppShell() { } - - static private LinkedList<GeckoEvent> gPendingEvents = - new LinkedList<GeckoEvent>(); - - static private boolean gRestartScheduled = false; - - static private GeckoEditableListener mEditableListener = null; - - static private final HashMap<String, String> - mAlertCookies = new HashMap<String, String>(); - - /* Keep in sync with constants found here: - http://mxr.mozilla.org/mozilla-central/source/uriloader/base/nsIWebProgressListener.idl - */ - static public final int WPL_STATE_START = 0x00000001; - static public final int WPL_STATE_STOP = 0x00000010; - static public final int WPL_STATE_IS_DOCUMENT = 0x00020000; - static public final int WPL_STATE_IS_NETWORK = 0x00040000; - - public static final String SHORTCUT_TYPE_WEBAPP = "webapp"; - public static final String SHORTCUT_TYPE_BOOKMARK = "bookmark"; - - static private final boolean LOGGING = false; - - static private int sDensityDpi = 0; - - private static final EventDispatcher sEventDispatcher = new EventDispatcher(); - - /* Default colors. */ - private static final float[] DEFAULT_LAUNCHER_ICON_HSV = { 32.0f, 1.0f, 1.0f }; - - /* Is the value in sVibrationEndTime valid? */ - private static boolean sVibrationMaybePlaying = false; - - /* Time (in System.nanoTime() units) when the currently-playing vibration - * is scheduled to end. This value is valid only when - * sVibrationMaybePlaying is true. */ - private static long sVibrationEndTime = 0; - - /* Default value of how fast we should hint the Android sensors. */ - private static int sDefaultSensorHint = 100; - - private static Sensor gAccelerometerSensor = null; - private static Sensor gLinearAccelerometerSensor = null; - private static Sensor gGyroscopeSensor = null; - private static Sensor gOrientationSensor = null; - private static Sensor gProximitySensor = null; - private static Sensor gLightSensor = null; - - private static volatile boolean mLocationHighAccuracy; - - public static ActivityHandlerHelper sActivityHelper = new ActivityHandlerHelper(); - static NotificationClient sNotificationClient; - - /* The Android-side API: API methods that Android calls */ - - // Initialization methods - public static native void nativeInit(); - - // helper methods - // public static native void setSurfaceView(GeckoSurfaceView sv); - public static native void setLayerClient(GeckoLayerClient client); - public static native void onResume(); - public static void callObserver(String observerKey, String topic, String data) { - sendEventToGecko(GeckoEvent.createCallObserverEvent(observerKey, topic, data)); - } - public static void removeObserver(String observerKey) { - sendEventToGecko(GeckoEvent.createRemoveObserverEvent(observerKey)); - } - public static native Message getNextMessageFromQueue(MessageQueue queue); - public static native void onSurfaceTextureFrameAvailable(Object surfaceTexture, int id); - - public static void registerGlobalExceptionHandler() { - Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { - @Override - public void uncaughtException(Thread thread, Throwable e) { - // If the uncaught exception was rethrown, walk the exception `cause` chain to find - // the original exception so Socorro can correctly collate related crash reports. - Throwable cause; - while ((cause = e.getCause()) != null) { - e = cause; - } - - try { - Log.e(LOGTAG, ">>> REPORTING UNCAUGHT EXCEPTION FROM THREAD " - + thread.getId() + " (\"" + thread.getName() + "\")", e); - - if (e instanceof OutOfMemoryError) { - SharedPreferences prefs = - getContext().getSharedPreferences(GeckoApp.PREFS_NAME, 0); - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean(GeckoApp.PREFS_OOM_EXCEPTION, true); - editor.commit(); - } - } finally { - reportJavaCrash(getStackTraceString(e)); - } - } - }); - } - - private static String getStackTraceString(Throwable e) { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - e.printStackTrace(pw); - pw.flush(); - return sw.toString(); - } - - private static native void reportJavaCrash(String stackTrace); - - public static void notifyUriVisited(String uri) { - sendEventToGecko(GeckoEvent.createVisitedEvent(uri)); - } - - public static native void processNextNativeEvent(boolean mayWait); - - public static native void notifyBatteryChange(double aLevel, boolean aCharging, double aRemainingTime); - - public static native void scheduleComposite(); - - // Resuming the compositor is a synchronous request, so be - // careful of possible deadlock. Resuming the compositor will also cause - // a composition, so there is no need to schedule a composition after - // resuming. - public static native void scheduleResumeComposition(int width, int height); - - public static native float computeRenderIntegrity(); - - public static native SurfaceBits getSurfaceBits(Surface surface); - - public static native void onFullScreenPluginHidden(View view); - - private static final class GeckoMediaScannerClient implements MediaScannerConnectionClient { - private final String mFile; - private final String mMimeType; - private MediaScannerConnection mScanner; - - public static void startScan(Context context, String file, String mimeType) { - new GeckoMediaScannerClient(context, file, mimeType); - } - - private GeckoMediaScannerClient(Context context, String file, String mimeType) { - mFile = file; - mMimeType = mimeType; - mScanner = new MediaScannerConnection(context, this); - mScanner.connect(); - } - - @Override - public void onMediaScannerConnected() { - mScanner.scanFile(mFile, mMimeType); - } - - @Override - public void onScanCompleted(String path, Uri uri) { - if(path.equals(mFile)) { - mScanner.disconnect(); - mScanner = null; - } - } - } - - private static LayerView sLayerView; - - public static void setLayerView(LayerView lv) { - sLayerView = lv; - } - - public static LayerView getLayerView() { - return sLayerView; - } - - public static void runGecko(String apkPath, String args, String url, String type) { - Looper.prepare(); - - // run gecko -- it will spawn its own thread - GeckoAppShell.nativeInit(); - - if (sLayerView != null) - GeckoAppShell.setLayerClient(sLayerView.getLayerClient()); - - // First argument is the .apk path - String combinedArgs = apkPath + " -greomni " + apkPath; - if (args != null) - combinedArgs += " " + args; - if (url != null) - combinedArgs += " -url " + url; - if (type != null) - combinedArgs += " " + type; - - DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); - combinedArgs += " -width " + metrics.widthPixels + " -height " + metrics.heightPixels; - - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - geckoLoaded(); - } - }); - - // and go - GeckoLoader.nativeRun(combinedArgs); - } - - // Called on the UI thread after Gecko loads. - private static void geckoLoaded() { - GeckoEditable editable = new GeckoEditable(); - // install the gecko => editable listener - mEditableListener = editable; - } - - static void sendPendingEventsToGecko() { - try { - while (!gPendingEvents.isEmpty()) { - GeckoEvent e = gPendingEvents.removeFirst(); - notifyGeckoOfEvent(e); - } - } catch (NoSuchElementException e) {} - } - - /* This method is referenced by Robocop via reflection. */ - public static void sendEventToGecko(GeckoEvent e) { - if (GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) { - notifyGeckoOfEvent(e); - } else { - gPendingEvents.addLast(e); - } - } - - // Tell the Gecko event loop that an event is available. - public static native void notifyGeckoOfEvent(GeckoEvent event); - - /* - * The Gecko-side API: API methods that Gecko calls - */ - public static void notifyIME(int type) { - if (mEditableListener != null) { - mEditableListener.notifyIME(type); - } - } - - public static void notifyIMEContext(int state, String typeHint, - String modeHint, String actionHint) { - if (mEditableListener != null) { - mEditableListener.notifyIMEContext(state, typeHint, - modeHint, actionHint); - } - } - - public static void notifyIMEChange(String text, int start, int end, int newEnd) { - if (newEnd < 0) { // Selection change - mEditableListener.onSelectionChange(start, end); - } else { // Text change - mEditableListener.onTextChange(text, start, end, newEnd); - } - } - - private static final Object sEventAckLock = new Object(); - private static boolean sWaitingForEventAck; - - // Block the current thread until the Gecko event loop is caught up - public static void sendEventToGeckoSync(GeckoEvent e) { - e.setAckNeeded(true); - - long time = SystemClock.uptimeMillis(); - boolean isUiThread = ThreadUtils.isOnUiThread(); - - synchronized (sEventAckLock) { - if (sWaitingForEventAck) { - // should never happen since we always leave it as false when we exit this function. - Log.e(LOGTAG, "geckoEventSync() may have been called twice concurrently!", new Exception()); - // fall through for graceful handling - } - - sendEventToGecko(e); - sWaitingForEventAck = true; - while (true) { - try { - sEventAckLock.wait(1000); - } catch (InterruptedException ie) { - } - if (!sWaitingForEventAck) { - // response received - break; - } - long waited = SystemClock.uptimeMillis() - time; - Log.d(LOGTAG, "Gecko event sync taking too long: " + waited + "ms"); - } - } - } - - // Signal the Java thread that it's time to wake up - public static void acknowledgeEvent() { - synchronized (sEventAckLock) { - sWaitingForEventAck = false; - sEventAckLock.notifyAll(); - } - } - - private static float getLocationAccuracy(Location location) { - float radius = location.getAccuracy(); - return (location.hasAccuracy() && radius > 0) ? radius : 1001; - } - - private static Location getLastKnownLocation(LocationManager lm) { - Location lastKnownLocation = null; - List<String> providers = lm.getAllProviders(); - - for (String provider : providers) { - Location location = lm.getLastKnownLocation(provider); - if (location == null) { - continue; - } - - if (lastKnownLocation == null) { - lastKnownLocation = location; - continue; - } - - long timeDiff = location.getTime() - lastKnownLocation.getTime(); - if (timeDiff > 0 || - (timeDiff == 0 && - getLocationAccuracy(location) < getLocationAccuracy(lastKnownLocation))) { - lastKnownLocation = location; - } - } - - return lastKnownLocation; - } - - public static void enableLocation(final boolean enable) { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - LocationManager lm = getLocationManager(getContext()); - if (lm == null) { - return; - } - - if (enable) { - Location lastKnownLocation = getLastKnownLocation(lm); - if (lastKnownLocation != null) { - getGeckoInterface().getLocationListener().onLocationChanged(lastKnownLocation); - } - - Criteria criteria = new Criteria(); - criteria.setSpeedRequired(false); - criteria.setBearingRequired(false); - criteria.setAltitudeRequired(false); - if (mLocationHighAccuracy) { - criteria.setAccuracy(Criteria.ACCURACY_FINE); - criteria.setCostAllowed(true); - criteria.setPowerRequirement(Criteria.POWER_HIGH); - } else { - criteria.setAccuracy(Criteria.ACCURACY_COARSE); - criteria.setCostAllowed(false); - criteria.setPowerRequirement(Criteria.POWER_LOW); - } - - String provider = lm.getBestProvider(criteria, true); - if (provider == null) - return; - - Looper l = Looper.getMainLooper(); - lm.requestLocationUpdates(provider, 100, (float).5, getGeckoInterface().getLocationListener(), l); - } else { - lm.removeUpdates(getGeckoInterface().getLocationListener()); - } - } - }); - } - - private static LocationManager getLocationManager(Context context) { - try { - return (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); - } catch (NoSuchFieldError e) { - // Some Tegras throw exceptions about missing the CONTROL_LOCATION_UPDATES permission, - // which allows enabling/disabling location update notifications from the cell radio. - // CONTROL_LOCATION_UPDATES is not for use by normal applications, but we might be - // hitting this problem if the Tegras are confused about missing cell radios. - Log.e(LOGTAG, "LOCATION_SERVICE not found?!", e); - return null; - } - } - - public static void enableLocationHighAccuracy(final boolean enable) { - mLocationHighAccuracy = enable; - } - - public static void enableSensor(int aSensortype) { - GeckoInterface gi = getGeckoInterface(); - if (gi == null) - return; - SensorManager sm = (SensorManager) - getContext().getSystemService(Context.SENSOR_SERVICE); - - switch(aSensortype) { - case GeckoHalDefines.SENSOR_ORIENTATION: - if(gOrientationSensor == null) - gOrientationSensor = sm.getDefaultSensor(Sensor.TYPE_ORIENTATION); - if (gOrientationSensor != null) - sm.registerListener(gi.getSensorEventListener(), gOrientationSensor, sDefaultSensorHint); - break; - - case GeckoHalDefines.SENSOR_ACCELERATION: - if(gAccelerometerSensor == null) - gAccelerometerSensor = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); - if (gAccelerometerSensor != null) - sm.registerListener(gi.getSensorEventListener(), gAccelerometerSensor, sDefaultSensorHint); - break; - - case GeckoHalDefines.SENSOR_PROXIMITY: - if(gProximitySensor == null ) - gProximitySensor = sm.getDefaultSensor(Sensor.TYPE_PROXIMITY); - if (gProximitySensor != null) - sm.registerListener(gi.getSensorEventListener(), gProximitySensor, SensorManager.SENSOR_DELAY_NORMAL); - break; - - case GeckoHalDefines.SENSOR_LIGHT: - if(gLightSensor == null) - gLightSensor = sm.getDefaultSensor(Sensor.TYPE_LIGHT); - if (gLightSensor != null) - sm.registerListener(gi.getSensorEventListener(), gLightSensor, SensorManager.SENSOR_DELAY_NORMAL); - break; - - case GeckoHalDefines.SENSOR_LINEAR_ACCELERATION: - if(gLinearAccelerometerSensor == null) - gLinearAccelerometerSensor = sm.getDefaultSensor(10 /* API Level 9 - TYPE_LINEAR_ACCELERATION */); - if (gLinearAccelerometerSensor != null) - sm.registerListener(gi.getSensorEventListener(), gLinearAccelerometerSensor, sDefaultSensorHint); - break; - - case GeckoHalDefines.SENSOR_GYROSCOPE: - if(gGyroscopeSensor == null) - gGyroscopeSensor = sm.getDefaultSensor(Sensor.TYPE_GYROSCOPE); - if (gGyroscopeSensor != null) - sm.registerListener(gi.getSensorEventListener(), gGyroscopeSensor, sDefaultSensorHint); - break; - default: - Log.w(LOGTAG, "Error! Can't enable unknown SENSOR type " + aSensortype); - } - } - - public static void disableSensor(int aSensortype) { - GeckoInterface gi = getGeckoInterface(); - if (gi == null) - return; - - SensorManager sm = (SensorManager) - getContext().getSystemService(Context.SENSOR_SERVICE); - - switch (aSensortype) { - case GeckoHalDefines.SENSOR_ORIENTATION: - if (gOrientationSensor != null) - sm.unregisterListener(gi.getSensorEventListener(), gOrientationSensor); - break; - - case GeckoHalDefines.SENSOR_ACCELERATION: - if (gAccelerometerSensor != null) - sm.unregisterListener(gi.getSensorEventListener(), gAccelerometerSensor); - break; - - case GeckoHalDefines.SENSOR_PROXIMITY: - if (gProximitySensor != null) - sm.unregisterListener(gi.getSensorEventListener(), gProximitySensor); - break; - - case GeckoHalDefines.SENSOR_LIGHT: - if (gLightSensor != null) - sm.unregisterListener(gi.getSensorEventListener(), gLightSensor); - break; - - case GeckoHalDefines.SENSOR_LINEAR_ACCELERATION: - if (gLinearAccelerometerSensor != null) - sm.unregisterListener(gi.getSensorEventListener(), gLinearAccelerometerSensor); - break; - - case GeckoHalDefines.SENSOR_GYROSCOPE: - if (gGyroscopeSensor != null) - sm.unregisterListener(gi.getSensorEventListener(), gGyroscopeSensor); - break; - default: - Log.w(LOGTAG, "Error! Can't disable unknown SENSOR type " + aSensortype); - } - } - - public static void moveTaskToBack() { - if (getGeckoInterface() != null) - getGeckoInterface().getActivity().moveTaskToBack(true); - } - - public static void returnIMEQueryResult(String result, int selectionStart, int selectionLength) { - // This method may be called from JNI to report Gecko's current selection indexes, but - // Native Fennec doesn't care because the Java code already knows the selection indexes. - } - - static void onXreExit() { - // The launch state can only be Launched or GeckoRunning at this point - GeckoThread.setLaunchState(GeckoThread.LaunchState.GeckoExiting); - if (getGeckoInterface() != null) { - if (gRestartScheduled) { - getGeckoInterface().doRestart(); - } else { - getGeckoInterface().getActivity().finish(); - } - } - - Log.d(LOGTAG, "Killing via System.exit()"); - System.exit(0); - } - - static void scheduleRestart() { - gRestartScheduled = true; - } - - public static File preInstallWebApp(String aTitle, String aURI, String aUniqueURI) { - int index = WebAppAllocator.getInstance(getContext()).findAndAllocateIndex(aUniqueURI, aTitle, (String) null); - GeckoProfile profile = GeckoProfile.get(getContext(), "webapp" + index); - return profile.getDir(); - } - - public static void postInstallWebApp(String aTitle, String aURI, String aUniqueURI, String aIconURL) { - WebAppAllocator allocator = WebAppAllocator.getInstance(getContext()); - int index = allocator.getIndexForApp(aUniqueURI); - assert index != -1 && aIconURL != null; - allocator.updateAppAllocation(aUniqueURI, index, BitmapUtils.getBitmapFromDataURI(aIconURL)); - createShortcut(aTitle, aURI, aUniqueURI, aIconURL, "webapp"); - } - - public static Intent getWebAppIntent(String aURI, String aUniqueURI, String aTitle, Bitmap aIcon) { - int index; - if (aIcon != null && !TextUtils.isEmpty(aTitle)) - index = WebAppAllocator.getInstance(getContext()).findAndAllocateIndex(aUniqueURI, aTitle, aIcon); - else - index = WebAppAllocator.getInstance(getContext()).getIndexForApp(aUniqueURI); - - if (index == -1) - return null; - - return getWebAppIntent(index, aURI); - } - - public static Intent getWebAppIntent(int aIndex, String aURI) { - Intent intent = new Intent(); - intent.setAction(GeckoApp.ACTION_WEBAPP_PREFIX + aIndex); - intent.setData(Uri.parse(aURI)); - intent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, - AppConstants.ANDROID_PACKAGE_NAME + ".WebApps$WebApp" + aIndex); - return intent; - } - - // "Installs" an application by creating a shortcut - // This is the entry point from AndroidBridge.h - static void createShortcut(String aTitle, String aURI, String aIconData, String aType) { - if ("webapp".equals(aType)) { - Log.w(LOGTAG, "createShortcut with no unique URI should not be used for aType = webapp!"); - } - - createShortcut(aTitle, aURI, aURI, aIconData, aType); - } - - // internal, for non-webapps - static void createShortcut(String aTitle, String aURI, Bitmap aBitmap, String aType) { - createShortcut(aTitle, aURI, aURI, aBitmap, aType); - } - - // internal, for webapps - static void createShortcut(String aTitle, String aURI, String aUniqueURI, String aIconData, String aType) { - createShortcut(aTitle, aURI, aUniqueURI, BitmapUtils.getBitmapFromDataURI(aIconData), aType); - } - - public static void createShortcut(final String aTitle, final String aURI, final String aUniqueURI, - final Bitmap aIcon, final String aType) - { - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - // the intent to be launched by the shortcut - Intent shortcutIntent; - if (aType.equalsIgnoreCase(SHORTCUT_TYPE_WEBAPP)) { - shortcutIntent = getWebAppIntent(aURI, aUniqueURI, aTitle, aIcon); - } else { - shortcutIntent = new Intent(); - shortcutIntent.setAction(GeckoApp.ACTION_BOOKMARK); - shortcutIntent.setData(Uri.parse(aURI)); - shortcutIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, - AppConstants.BROWSER_INTENT_CLASS); - } - - Intent intent = new Intent(); - intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); - if (aTitle != null) - intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, aTitle); - else - intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, aURI); - intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, getLauncherIcon(aIcon, aType)); - - // Do not allow duplicate items - intent.putExtra("duplicate", false); - - intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); - getContext().sendBroadcast(intent); - } - }); - } - - public static void removeShortcut(final String aTitle, final String aURI, final String aType) { - removeShortcut(aTitle, aURI, null, aType); - } - - public static void removeShortcut(final String aTitle, final String aURI, final String aUniqueURI, final String aType) { - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - // the intent to be launched by the shortcut - Intent shortcutIntent; - if (aType.equalsIgnoreCase(SHORTCUT_TYPE_WEBAPP)) { - int index = WebAppAllocator.getInstance(getContext()).getIndexForApp(aUniqueURI); - shortcutIntent = getWebAppIntent(aURI, aUniqueURI, "", null); - if (shortcutIntent == null) - return; - } else { - shortcutIntent = new Intent(); - shortcutIntent.setAction(GeckoApp.ACTION_BOOKMARK); - shortcutIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, - AppConstants.BROWSER_INTENT_CLASS); - shortcutIntent.setData(Uri.parse(aURI)); - } - - Intent intent = new Intent(); - intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); - if (aTitle != null) - intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, aTitle); - else - intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, aURI); - - intent.setAction("com.android.launcher.action.UNINSTALL_SHORTCUT"); - getContext().sendBroadcast(intent); - } - }); - } - - public static void uninstallWebApp(final String uniqueURI) { - // On uninstall, we need to do a couple of things: - // 1. nuke the running app process. - // 2. nuke the profile that was assigned to that webapp - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - int index = WebAppAllocator.getInstance(getContext()).releaseIndexForApp(uniqueURI); - - // if -1, nothing to do; we didn't think it was installed anyway - if (index == -1) - return; - - // kill the app if it's running - String targetProcessName = getContext().getPackageName(); - targetProcessName = targetProcessName + ":" + targetProcessName + ".WebApp" + index; - - ActivityManager am = (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE); - List<ActivityManager.RunningAppProcessInfo> procs = am.getRunningAppProcesses(); - if (procs != null) { - for (ActivityManager.RunningAppProcessInfo proc : procs) { - if (proc.processName.equals(targetProcessName)) { - android.os.Process.killProcess(proc.pid); - break; - } - } - } - - // then nuke the profile - GeckoProfile.removeProfile(getContext(), "webapp" + index); - } - }); - } - - static public int getPreferredIconSize() { - if (android.os.Build.VERSION.SDK_INT >= 11) { - ActivityManager am = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE); - return am.getLauncherLargeIconSize(); - } else { - switch (getDpi()) { - case DisplayMetrics.DENSITY_MEDIUM: - return 48; - case DisplayMetrics.DENSITY_XHIGH: - return 96; - case DisplayMetrics.DENSITY_HIGH: - default: - return 72; - } - } - } - - static private Bitmap getLauncherIcon(Bitmap aSource, String aType) { - final int kOffset = 6; - final int kRadius = 5; - int size = getPreferredIconSize(); - int insetSize = aSource != null ? size * 2 / 3 : size; - - Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - - if (aType.equalsIgnoreCase(SHORTCUT_TYPE_WEBAPP)) { - Rect iconBounds = new Rect(0, 0, size, size); - canvas.drawBitmap(aSource, null, iconBounds, null); - return bitmap; - } - - // draw a base color - Paint paint = new Paint(); - if (aSource == null) { - // If we aren't drawing a favicon, just use an orange color. - paint.setColor(Color.HSVToColor(DEFAULT_LAUNCHER_ICON_HSV)); - canvas.drawRoundRect(new RectF(kOffset, kOffset, size - kOffset, size - kOffset), kRadius, kRadius, paint); - } else { - // otherwise use the dominant color from the icon + a layer of transparent white to lighten it somewhat - int color = BitmapUtils.getDominantColor(aSource); - paint.setColor(color); - canvas.drawRoundRect(new RectF(kOffset, kOffset, size - kOffset, size - kOffset), kRadius, kRadius, paint); - paint.setColor(Color.argb(100, 255, 255, 255)); - canvas.drawRoundRect(new RectF(kOffset, kOffset, size - kOffset, size - kOffset), kRadius, kRadius, paint); - } - - // draw the overlay - Bitmap overlay = BitmapUtils.decodeResource(getContext(), R.drawable.home_bg); - canvas.drawBitmap(overlay, null, new Rect(0, 0, size, size), null); - - // draw the favicon - if (aSource == null) - aSource = BitmapUtils.decodeResource(getContext(), R.drawable.home_star); - - // by default, we scale the icon to this size - int sWidth = insetSize / 2; - int sHeight = sWidth; - - if (aSource.getWidth() > insetSize || aSource.getHeight() > insetSize) { - // however, if the icon is larger than our minimum, we allow it to be drawn slightly larger - // (but not necessarily at its full resolution) - sWidth = Math.min(size / 3, aSource.getWidth() / 2); - sHeight = Math.min(size / 3, aSource.getHeight() / 2); - } - - int halfSize = size / 2; - canvas.drawBitmap(aSource, - null, - new Rect(halfSize - sWidth, - halfSize - sHeight, - halfSize + sWidth, - halfSize + sHeight), - null); - - return bitmap; - } - - static String[] getHandlersForMimeType(String aMimeType, String aAction) { - Intent intent = getIntentForActionString(aAction); - if (aMimeType != null && aMimeType.length() > 0) - intent.setType(aMimeType); - return getHandlersForIntent(intent); - } - - static String[] getHandlersForURL(String aURL, String aAction) { - // aURL may contain the whole URL or just the protocol - Uri uri = aURL.indexOf(':') >= 0 ? Uri.parse(aURL) : new Uri.Builder().scheme(aURL).build(); - - Intent intent = getOpenURIIntent(getContext(), uri.toString(), "", - TextUtils.isEmpty(aAction) ? Intent.ACTION_VIEW : aAction, ""); - - return getHandlersForIntent(intent); - } - - static boolean hasHandlersForIntent(Intent intent) { - PackageManager pm = getContext().getPackageManager(); - List<ResolveInfo> list = pm.queryIntentActivities(intent, 0); - return !list.isEmpty(); - } - - static String[] getHandlersForIntent(Intent intent) { - PackageManager pm = getContext().getPackageManager(); - List<ResolveInfo> list = pm.queryIntentActivities(intent, 0); - int numAttr = 4; - String[] ret = new String[list.size() * numAttr]; - for (int i = 0; i < list.size(); i++) { - ResolveInfo resolveInfo = list.get(i); - ret[i * numAttr] = resolveInfo.loadLabel(pm).toString(); - if (resolveInfo.isDefault) - ret[i * numAttr + 1] = "default"; - else - ret[i * numAttr + 1] = ""; - ret[i * numAttr + 2] = resolveInfo.activityInfo.applicationInfo.packageName; - ret[i * numAttr + 3] = resolveInfo.activityInfo.name; - } - return ret; - } - - static Intent getIntentForActionString(String aAction) { - // Default to the view action if no other action as been specified. - if (TextUtils.isEmpty(aAction)) { - return new Intent(Intent.ACTION_VIEW); - } - return new Intent(aAction); - } - - static String getExtensionFromMimeType(String aMimeType) { - return MimeTypeMap.getSingleton().getExtensionFromMimeType(aMimeType); - } - - static String getMimeTypeFromExtensions(String aFileExt) { - MimeTypeMap mtm = MimeTypeMap.getSingleton(); - StringTokenizer st = new StringTokenizer(aFileExt, ".,; "); - String type = null; - String subType = null; - while (st.hasMoreElements()) { - String ext = st.nextToken(); - String mt = mtm.getMimeTypeFromExtension(ext); - if (mt == null) - continue; - int slash = mt.indexOf('/'); - String tmpType = mt.substring(0, slash); - if (!tmpType.equalsIgnoreCase(type)) - type = type == null ? tmpType : "*"; - String tmpSubType = mt.substring(slash + 1); - if (!tmpSubType.equalsIgnoreCase(subType)) - subType = subType == null ? tmpSubType : "*"; - } - if (type == null) - type = "*"; - if (subType == null) - subType = "*"; - return type + "/" + subType; - } - - static void safeStreamClose(Closeable stream) { - try { - if (stream != null) - stream.close(); - } catch (IOException e) {} - } - - static void shareImage(String aSrc, String aType) { - - Intent intent = new Intent(Intent.ACTION_SEND); - boolean isDataURI = aSrc.startsWith("data:"); - OutputStream os = null; - File dir = GeckoApp.getTempDirectory(); - - if (dir == null) { - showImageShareFailureToast(); - return; - } - - GeckoApp.deleteTempFiles(); - - try { - // Create a temporary file for the image - File imageFile = File.createTempFile("image", - "." + aType.replace("image/",""), - dir); - os = new FileOutputStream(imageFile); - - if (isDataURI) { - // We are dealing with a Data URI - int dataStart = aSrc.indexOf(','); - byte[] buf = Base64.decode(aSrc.substring(dataStart+1), Base64.DEFAULT); - os.write(buf); - } else { - // We are dealing with a URL - InputStream is = null; - try { - URL url = new URL(aSrc); - is = url.openStream(); - byte[] buf = new byte[2048]; - int length; - - while ((length = is.read(buf)) != -1) { - os.write(buf, 0, length); - } - } finally { - safeStreamClose(is); - } - } - intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(imageFile)); - - // If we were able to determine the image type, send that in the intent. Otherwise, - // use a generic type. - if (aType.startsWith("image/")) { - intent.setType(aType); - } else { - intent.setType("image/*"); - } - } catch (IOException e) { - if (!isDataURI) { - // If we failed, at least send through the URL link - intent.putExtra(Intent.EXTRA_TEXT, aSrc); - intent.setType("text/plain"); - } else { - showImageShareFailureToast(); - return; - } - } finally { - safeStreamClose(os); - } - getContext().startActivity(Intent.createChooser(intent, - getContext().getResources().getString(R.string.share_title))); - } - - // Don't fail silently, tell the user that we weren't able to share the image - private static final void showImageShareFailureToast() { - Toast toast = Toast.makeText(getContext(), - getContext().getResources().getString(R.string.share_image_failed), - Toast.LENGTH_SHORT); - toast.show(); - } - - static boolean isUriSafeForScheme(Uri aUri) { - // Bug 794034 - We don't want to pass MWI or USSD codes to the - // dialer, and ensure the Uri class doesn't parse a URI - // containing a fragment ('#') - final String scheme = aUri.getScheme(); - if ("tel".equals(scheme) || "sms".equals(scheme)) { - final String number = aUri.getSchemeSpecificPart(); - if (number.contains("#") || number.contains("*") || aUri.getFragment() != null) { - return false; - } - } - return true; - } - - /** - * Given the inputs to <code>getOpenURIIntent</code>, plus an optional - * package name and class name, create and fire an intent to open the - * provided URI. - * - * @param targetURI the string spec of the URI to open. - * @param mimeType an optional MIME type string. - * @param action an Android action specifier, such as - * <code>Intent.ACTION_SEND</code>. - * @param title the title to use in <code>ACTION_SEND</code> intents. - * @return true if the activity started successfully; false otherwise. - */ - static boolean openUriExternal(String targetURI, - String mimeType, - String packageName, - String className, - String action, - String title) { - final Context context = getContext(); - final Intent intent = getOpenURIIntent(context, targetURI, - mimeType, action, title); - - if (intent == null) { - return false; - } - - if (packageName.length() > 0 && className.length() > 0) { - intent.setClassName(packageName, className); - } - - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - try { - context.startActivity(intent); - return true; - } catch (ActivityNotFoundException e) { - return false; - } - } - - /** - * Return a <code>Uri</code> instance which is equivalent to <code>u</code>, - * but with a guaranteed-lowercase scheme as if the API level 16 method - * <code>u.normalizeScheme</code> had been called. - * - * @param u the <code>Uri</code> to normalize. - * @return a <code>Uri</code>, which might be <code>u</code>. - */ - static Uri normalizeUriScheme(final Uri u) { - final String scheme = u.getScheme(); - final String lower = scheme.toLowerCase(Locale.US); - if (lower.equals(scheme)) { - return u; - } - - // Otherwise, return a new URI with a normalized scheme. - return u.buildUpon().scheme(lower).build(); - } - - /** - * Given a URI, a MIME type, and a title, - * produce a share intent which can be used to query all activities - * than can open the specified URI. - * - * @param context a <code>Context</code> instance. - * @param targetURI the string spec of the URI to open. - * @param mimeType an optional MIME type string. - * @param title the title to use in <code>ACTION_SEND</code> intents. - * @return an <code>Intent</code>, or <code>null</code> if none could be - * produced. - */ - static Intent getShareIntent(final Context context, - final String targetURI, - final String mimeType, - final String title) { - Intent shareIntent = getIntentForActionString(Intent.ACTION_SEND); - shareIntent.putExtra(Intent.EXTRA_TEXT, targetURI); - shareIntent.putExtra(Intent.EXTRA_SUBJECT, title); - - // Note that EXTRA_TITLE is intended to be used for share dialog - // titles. Common usage (e.g., Pocket) suggests that it's sometimes - // interpreted as an alternate to EXTRA_SUBJECT, so we include it. - shareIntent.putExtra(Intent.EXTRA_TITLE, title); - - if (mimeType != null && mimeType.length() > 0) { - shareIntent.setType(mimeType); - } - - return shareIntent; - } - - /** - * Given a URI, a MIME type, an Android intent "action", and a title, - * produce an intent which can be used to start an activity to open - * the specified URI. - * - * @param context a <code>Context</code> instance. - * @param targetURI the string spec of the URI to open. - * @param mimeType an optional MIME type string. - * @param action an Android action specifier, such as - * <code>Intent.ACTION_SEND</code>. - * @param title the title to use in <code>ACTION_SEND</code> intents. - * @return an <code>Intent</code>, or <code>null</code> if none could be - * produced. - */ - static Intent getOpenURIIntent(final Context context, - final String targetURI, - final String mimeType, - final String action, - final String title) { - - if (action.equalsIgnoreCase(Intent.ACTION_SEND)) { - Intent shareIntent = getShareIntent(context, targetURI, mimeType, title); - return Intent.createChooser(shareIntent, - context.getResources().getString(R.string.share_title)); - } - - final Uri uri = normalizeUriScheme(Uri.parse(targetURI)); - if (mimeType.length() > 0) { - Intent intent = getIntentForActionString(action); - intent.setDataAndType(uri, mimeType); - return intent; - } - - if (!isUriSafeForScheme(uri)) { - return null; - } - - final String scheme = uri.getScheme(); - - final Intent intent; - - // Compute our most likely intent, then check to see if there are any - // custom handlers that would apply. - // Start with the original URI. If we end up modifying it, we'll - // overwrite it. - final Intent likelyIntent = getIntentForActionString(action); - likelyIntent.setData(uri); - - if ("vnd.youtube".equals(scheme) && !hasHandlersForIntent(likelyIntent)) { - // Special-case YouTube to use our own player if no system handler - // exists. - intent = new Intent(VideoPlayer.VIDEO_ACTION); - intent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, - "org.mozilla.gecko.VideoPlayer"); - intent.setData(uri); - } else { - intent = likelyIntent; - } - - // Have a special handling for SMS, as the message body - // is not extracted from the URI automatically. - if (!"sms".equals(scheme)) { - return intent; - } - - final String query = uri.getEncodedQuery(); - if (TextUtils.isEmpty(query)) { - return intent; - } - - final String[] fields = query.split("&"); - boolean foundBody = false; - String resultQuery = ""; - for (String field : fields) { - if (foundBody || !field.startsWith("body=")) { - resultQuery = resultQuery.concat(resultQuery.length() > 0 ? "&" + field : field); - continue; - } - - // Found the first body param. Put it into the intent. - final String body = Uri.decode(field.substring(5)); - intent.putExtra("sms_body", body); - foundBody = true; - } - - if (!foundBody) { - // No need to rewrite the URI, then. - return intent; - } - - // Form a new URI without the body field in the query part, and - // push that into the new Intent. - final String newQuery = resultQuery.length() > 0 ? "?" + resultQuery : ""; - final Uri pruned = uri.buildUpon().encodedQuery(newQuery).build(); - intent.setData(pruned); - - return intent; - } - - public static void setNotificationClient(NotificationClient client) { - if (sNotificationClient == null) { - sNotificationClient = client; - } else { - Log.d(LOGTAG, "Notification client already set"); - } - } - - public static void showAlertNotification(String aImageUrl, String aAlertTitle, String aAlertText, - String aAlertCookie, String aAlertName) { - // The intent to launch when the user clicks the expanded notification - String app = getContext().getClass().getName(); - Intent notificationIntent = new Intent(GeckoApp.ACTION_ALERT_CALLBACK); - notificationIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, app); - notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - - int notificationID = aAlertName.hashCode(); - - // Put the strings into the intent as an URI "alert:?name=<alertName>&app=<appName>&cookie=<cookie>" - Uri.Builder b = new Uri.Builder(); - Uri dataUri = b.scheme("alert").path(Integer.toString(notificationID)) - .appendQueryParameter("name", aAlertName) - .appendQueryParameter("cookie", aAlertCookie) - .build(); - notificationIntent.setData(dataUri); - PendingIntent contentIntent = PendingIntent.getActivity( - getContext(), 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT); - - mAlertCookies.put(aAlertName, aAlertCookie); - callObserver(aAlertName, "alertshow", aAlertCookie); - - sNotificationClient.add(notificationID, aImageUrl, aAlertTitle, aAlertText, contentIntent); - } - - public static void alertsProgressListener_OnProgress(String aAlertName, long aProgress, long aProgressMax, String aAlertText) { - int notificationID = aAlertName.hashCode(); - sNotificationClient.update(notificationID, aProgress, aProgressMax, aAlertText); - } - - public static void closeNotification(String aAlertName) { - String alertCookie = mAlertCookies.get(aAlertName); - if (alertCookie != null) { - callObserver(aAlertName, "alertfinished", alertCookie); - mAlertCookies.remove(aAlertName); - } - - removeObserver(aAlertName); - - int notificationID = aAlertName.hashCode(); - sNotificationClient.remove(notificationID); - } - - public static void handleNotification(String aAction, String aAlertName, String aAlertCookie) { - int notificationID = aAlertName.hashCode(); - - if (GeckoApp.ACTION_ALERT_CALLBACK.equals(aAction)) { - callObserver(aAlertName, "alertclickcallback", aAlertCookie); - - if (sNotificationClient.isOngoing(notificationID)) { - // When clicked, keep the notification if it displays progress - return; - } - } - - // Also send a notification to the observer service - // New listeners should register for these notifications since they will be called even if - // Gecko has been killed and restared between when your notification was shown and when the - // user clicked on it. - sendEventToGecko(GeckoEvent.createBroadcastEvent("Notification:Clicked", aAlertCookie)); - closeNotification(aAlertName); - } - - public static int getDpi() { - if (sDensityDpi == 0) { - sDensityDpi = getContext().getResources().getDisplayMetrics().densityDpi; - } - - return sDensityDpi; - } - - public static void setFullScreen(boolean fullscreen) { - if (getGeckoInterface() != null) - getGeckoInterface().setFullScreen(fullscreen); - } - - public static String showFilePickerForExtensions(String aExtensions) { - if (getGeckoInterface() != null) - return sActivityHelper.showFilePicker(getGeckoInterface().getActivity(), getMimeTypeFromExtensions(aExtensions)); - return ""; - } - - public static String showFilePickerForMimeType(String aMimeType) { - if (getGeckoInterface() != null) - return sActivityHelper.showFilePicker(getGeckoInterface().getActivity(), aMimeType); - return ""; - } - - public static void performHapticFeedback(boolean aIsLongPress) { - // Don't perform haptic feedback if a vibration is currently playing, - // because the haptic feedback will nuke the vibration. - if (!sVibrationMaybePlaying || System.nanoTime() >= sVibrationEndTime) { - LayerView layerView = getLayerView(); - layerView.performHapticFeedback(aIsLongPress ? - HapticFeedbackConstants.LONG_PRESS : - HapticFeedbackConstants.VIRTUAL_KEY); - } - } - - private static Vibrator vibrator() { - LayerView layerView = getLayerView(); - return (Vibrator) layerView.getContext().getSystemService(Context.VIBRATOR_SERVICE); - } - - public static void vibrate(long milliseconds) { - sVibrationEndTime = System.nanoTime() + milliseconds * 1000000; - sVibrationMaybePlaying = true; - vibrator().vibrate(milliseconds); - } - - public static void vibrate(long[] pattern, int repeat) { - // If pattern.length is even, the last element in the pattern is a - // meaningless delay, so don't include it in vibrationDuration. - long vibrationDuration = 0; - int iterLen = pattern.length - (pattern.length % 2 == 0 ? 1 : 0); - for (int i = 0; i < iterLen; i++) { - vibrationDuration += pattern[i]; - } - - sVibrationEndTime = System.nanoTime() + vibrationDuration * 1000000; - sVibrationMaybePlaying = true; - vibrator().vibrate(pattern, repeat); - } - - public static void cancelVibrate() { - sVibrationMaybePlaying = false; - sVibrationEndTime = 0; - vibrator().cancel(); - } - - public static void showInputMethodPicker() { - InputMethodManager imm = (InputMethodManager) - getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.showInputMethodPicker(); - } - - public static void setKeepScreenOn(final boolean on) { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - // TODO - } - }); - } - - public static void notifyDefaultPrevented(final boolean defaultPrevented) { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - LayerView view = getLayerView(); - PanZoomController controller = (view == null ? null : view.getPanZoomController()); - if (controller != null) { - controller.notifyDefaultActionPrevented(defaultPrevented); - } - } - }); - } - - public static boolean isNetworkLinkUp() { - ConnectivityManager cm = (ConnectivityManager) - getContext().getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo info = cm.getActiveNetworkInfo(); - if (info == null || !info.isConnected()) - return false; - return true; - } - - public static boolean isNetworkLinkKnown() { - ConnectivityManager cm = (ConnectivityManager) - getContext().getSystemService(Context.CONNECTIVITY_SERVICE); - if (cm.getActiveNetworkInfo() == null) - return false; - return true; - } - - public static void setSelectedLocale(String localeCode) { - /* Bug 713464: This method is still called from Gecko side. - Earlier we had an option to run Firefox in a language other than system's language. - However, this is not supported as of now. - Gecko resets the locale to en-US by calling this function with an empty string. - This affects GeckoPreferences activity in multi-locale builds. - - //We're not using this, not need to save it (see bug 635342) - SharedPreferences settings = - getContext().getPreferences(Activity.MODE_PRIVATE); - settings.edit().putString(getContext().getPackageName() + ".locale", - localeCode).commit(); - Locale locale; - int index; - if ((index = localeCode.indexOf('-')) != -1 || - (index = localeCode.indexOf('_')) != -1) { - String langCode = localeCode.substring(0, index); - String countryCode = localeCode.substring(index + 1); - locale = new Locale(langCode, countryCode); - } else { - locale = new Locale(localeCode); - } - Locale.setDefault(locale); - - Resources res = getContext().getBaseContext().getResources(); - Configuration config = res.getConfiguration(); - config.locale = locale; - res.updateConfiguration(config, res.getDisplayMetrics()); - */ - } - - public static int[] getSystemColors() { - // attrsAppearance[] must correspond to AndroidSystemColors structure in android/AndroidBridge.h - final int[] attrsAppearance = { - android.R.attr.textColor, - android.R.attr.textColorPrimary, - android.R.attr.textColorPrimaryInverse, - android.R.attr.textColorSecondary, - android.R.attr.textColorSecondaryInverse, - android.R.attr.textColorTertiary, - android.R.attr.textColorTertiaryInverse, - android.R.attr.textColorHighlight, - android.R.attr.colorForeground, - android.R.attr.colorBackground, - android.R.attr.panelColorForeground, - android.R.attr.panelColorBackground - }; - - int[] result = new int[attrsAppearance.length]; - - final ContextThemeWrapper contextThemeWrapper = - new ContextThemeWrapper(getContext(), android.R.style.TextAppearance); - - final TypedArray appearance = contextThemeWrapper.getTheme().obtainStyledAttributes(attrsAppearance); - - if (appearance != null) { - for (int i = 0; i < appearance.getIndexCount(); i++) { - int idx = appearance.getIndex(i); - int color = appearance.getColor(idx, 0); - result[idx] = color; - } - appearance.recycle(); - } - - return result; - } - - public static void killAnyZombies() { - GeckoProcessesVisitor visitor = new GeckoProcessesVisitor() { - @Override - public boolean callback(int pid) { - if (pid != android.os.Process.myPid()) - android.os.Process.killProcess(pid); - return true; - } - }; - - EnumerateGeckoProcesses(visitor); - } - - public static boolean checkForGeckoProcs() { - - class GeckoPidCallback implements GeckoProcessesVisitor { - public boolean otherPidExist = false; - @Override - public boolean callback(int pid) { - if (pid != android.os.Process.myPid()) { - otherPidExist = true; - return false; - } - return true; - } - } - GeckoPidCallback visitor = new GeckoPidCallback(); - EnumerateGeckoProcesses(visitor); - return visitor.otherPidExist; - } - - interface GeckoProcessesVisitor{ - boolean callback(int pid); - } - - private static void EnumerateGeckoProcesses(GeckoProcessesVisitor visiter) { - int pidColumn = -1; - int userColumn = -1; - - try { - // run ps and parse its output - java.lang.Process ps = Runtime.getRuntime().exec("ps"); - BufferedReader in = new BufferedReader(new InputStreamReader(ps.getInputStream()), - 2048); - - String headerOutput = in.readLine(); - - // figure out the column offsets. We only care about the pid and user fields - StringTokenizer st = new StringTokenizer(headerOutput); - - int tokenSoFar = 0; - while (st.hasMoreTokens()) { - String next = st.nextToken(); - if (next.equalsIgnoreCase("PID")) - pidColumn = tokenSoFar; - else if (next.equalsIgnoreCase("USER")) - userColumn = tokenSoFar; - tokenSoFar++; - } - - // alright, the rest are process entries. - String psOutput = null; - while ((psOutput = in.readLine()) != null) { - String[] split = psOutput.split("\\s+"); - if (split.length <= pidColumn || split.length <= userColumn) - continue; - int uid = android.os.Process.getUidForName(split[userColumn]); - if (uid == android.os.Process.myUid() && - !split[split.length - 1].equalsIgnoreCase("ps")) { - int pid = Integer.parseInt(split[pidColumn]); - boolean keepGoing = visiter.callback(pid); - if (keepGoing == false) - break; - } - } - in.close(); - } - catch (Exception e) { - Log.w(LOGTAG, "Failed to enumerate Gecko processes.", e); - } - } - - public static void waitForAnotherGeckoProc(){ - int countdown = 40; - while (!checkForGeckoProcs() && --countdown > 0) { - try { - Thread.sleep(100); - } catch (InterruptedException ie) {} - } - } - public static String getAppNameByPID(int pid) { - BufferedReader cmdlineReader = null; - String path = "/proc/" + pid + "/cmdline"; - try { - File cmdlineFile = new File(path); - if (!cmdlineFile.exists()) - return ""; - cmdlineReader = new BufferedReader(new FileReader(cmdlineFile)); - return cmdlineReader.readLine().trim(); - } catch (Exception ex) { - return ""; - } finally { - if (null != cmdlineReader) { - try { - cmdlineReader.close(); - } catch (Exception e) {} - } - } - } - - public static void listOfOpenFiles() { - int pidColumn = -1; - int nameColumn = -1; - - try { - String filter = GeckoProfile.get(getContext()).getDir().toString(); - Log.i(LOGTAG, "[OPENFILE] Filter: " + filter); - - // run lsof and parse its output - java.lang.Process lsof = Runtime.getRuntime().exec("lsof"); - BufferedReader in = new BufferedReader(new InputStreamReader(lsof.getInputStream()), 2048); - - String headerOutput = in.readLine(); - StringTokenizer st = new StringTokenizer(headerOutput); - int token = 0; - while (st.hasMoreTokens()) { - String next = st.nextToken(); - if (next.equalsIgnoreCase("PID")) - pidColumn = token; - else if (next.equalsIgnoreCase("NAME")) - nameColumn = token; - token++; - } - - // alright, the rest are open file entries. - Map<Integer, String> pidNameMap = new TreeMap<Integer, String>(); - String output = null; - while ((output = in.readLine()) != null) { - String[] split = output.split("\\s+"); - if (split.length <= pidColumn || split.length <= nameColumn) - continue; - Integer pid = new Integer(split[pidColumn]); - String name = pidNameMap.get(pid); - if (name == null) { - name = getAppNameByPID(pid.intValue()); - pidNameMap.put(pid, name); - } - String file = split[nameColumn]; - if (!TextUtils.isEmpty(name) && !TextUtils.isEmpty(file) && file.startsWith(filter)) - Log.i(LOGTAG, "[OPENFILE] " + name + "(" + split[pidColumn] + ") : " + file); - } - in.close(); - } catch (Exception e) { } - } - - public static void scanMedia(String aFile, String aMimeType) { - Context context = getContext(); - GeckoMediaScannerClient.startScan(context, aFile, aMimeType); - } - - public static byte[] getIconForExtension(String aExt, int iconSize) { - try { - if (iconSize <= 0) - iconSize = 16; - - if (aExt != null && aExt.length() > 1 && aExt.charAt(0) == '.') - aExt = aExt.substring(1); - - PackageManager pm = getContext().getPackageManager(); - Drawable icon = getDrawableForExtension(pm, aExt); - if (icon == null) { - // Use a generic icon - icon = pm.getDefaultActivityIcon(); - } - - Bitmap bitmap = ((BitmapDrawable)icon).getBitmap(); - if (bitmap.getWidth() != iconSize || bitmap.getHeight() != iconSize) - bitmap = Bitmap.createScaledBitmap(bitmap, iconSize, iconSize, true); - - ByteBuffer buf = ByteBuffer.allocate(iconSize * iconSize * 4); - bitmap.copyPixelsToBuffer(buf); - - return buf.array(); - } - catch (Exception e) { - Log.w(LOGTAG, "getIconForExtension failed.", e); - return null; - } - } - - private static Drawable getDrawableForExtension(PackageManager pm, String aExt) { - Intent intent = new Intent(Intent.ACTION_VIEW); - MimeTypeMap mtm = MimeTypeMap.getSingleton(); - String mimeType = mtm.getMimeTypeFromExtension(aExt); - if (mimeType != null && mimeType.length() > 0) - intent.setType(mimeType); - else - return null; - - List<ResolveInfo> list = pm.queryIntentActivities(intent, 0); - if (list.size() == 0) - return null; - - ResolveInfo resolveInfo = list.get(0); - - if (resolveInfo == null) - return null; - - ActivityInfo activityInfo = resolveInfo.activityInfo; - - return activityInfo.loadIcon(pm); - } - - public static boolean getShowPasswordSetting() { - try { - int showPassword = - Settings.System.getInt(getContext().getContentResolver(), - Settings.System.TEXT_SHOW_PASSWORD, 1); - return (showPassword > 0); - } - catch (Exception e) { - return true; - } - } - - public static void addPluginView(View view, - int x, int y, - int w, int h, - boolean isFullScreen) { - if (getGeckoInterface() != null) - getGeckoInterface().addPluginView(view, new Rect(x, y, x + w, y + h), isFullScreen); - } - - public static void removePluginView(View view, boolean isFullScreen) { - if (getGeckoInterface() != null) - getGeckoInterface().removePluginView(view, isFullScreen); - } - - /** - * A plugin that wish to be loaded in the WebView must provide this permission - * in their AndroidManifest.xml. - */ - public static final String PLUGIN_ACTION = "android.webkit.PLUGIN"; - public static final String PLUGIN_PERMISSION = "android.webkit.permission.PLUGIN"; - - private static final String PLUGIN_SYSTEM_LIB = "/system/lib/plugins/"; - - private static final String PLUGIN_TYPE = "type"; - private static final String TYPE_NATIVE = "native"; - static public ArrayList<PackageInfo> mPackageInfoCache = new ArrayList<PackageInfo>(); - - // Returns null if plugins are blocked on the device. - static String[] getPluginDirectories() { - - // An awful hack to detect Tegra devices. Easiest way to do it without spinning up a EGL context. - boolean isTegra = (new File("/system/lib/hw/gralloc.tegra.so")).exists() || - (new File("/system/lib/hw/gralloc.tegra3.so")).exists(); - if (isTegra) { - // disable Flash on Tegra ICS with CM9 and other custom firmware (bug 736421) - File vfile = new File("/proc/version"); - FileReader vreader = null; - try { - if (vfile.canRead()) { - vreader = new FileReader(vfile); - String version = new BufferedReader(vreader).readLine(); - if (version.indexOf("CM9") != -1 || - version.indexOf("cyanogen") != -1 || - version.indexOf("Nova") != -1) - { - Log.w(LOGTAG, "Blocking plugins because of Tegra 2 + unofficial ICS bug (bug 736421)"); - return null; - } - } - } catch (IOException ex) { - // nothing - } finally { - try { - if (vreader != null) { - vreader.close(); - } - } catch (IOException ex) { - // nothing - } - } - - // disable on KitKat - if (Build.VERSION.SDK_INT >= 19) { - Log.w(LOGTAG, "Blocking plugins because of Tegra OpenGL deadlocks"); - return null; - } - } - - ArrayList<String> directories = new ArrayList<String>(); - PackageManager pm = getContext().getPackageManager(); - List<ResolveInfo> plugins = pm.queryIntentServices(new Intent(PLUGIN_ACTION), - PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); - - synchronized(mPackageInfoCache) { - - // clear the list of existing packageInfo objects - mPackageInfoCache.clear(); - - - for (ResolveInfo info : plugins) { - - // retrieve the plugin's service information - ServiceInfo serviceInfo = info.serviceInfo; - if (serviceInfo == null) { - Log.w(LOGTAG, "Ignoring bad plugin."); - continue; - } - - // Blacklist HTC's flash lite. - // See bug #704516 - We're not quite sure what Flash Lite does, - // but loading it causes Flash to give errors and fail to draw. - if (serviceInfo.packageName.equals("com.htc.flashliteplugin")) { - Log.w(LOGTAG, "Skipping HTC's flash lite plugin"); - continue; - } - - - // Retrieve information from the plugin's manifest. - PackageInfo pkgInfo; - try { - pkgInfo = pm.getPackageInfo(serviceInfo.packageName, - PackageManager.GET_PERMISSIONS - | PackageManager.GET_SIGNATURES); - } catch (Exception e) { - Log.w(LOGTAG, "Can't find plugin: " + serviceInfo.packageName); - continue; - } - - if (pkgInfo == null) { - Log.w(LOGTAG, "Not loading plugin: " + serviceInfo.packageName + ". Could not load package information."); - continue; - } - - /* - * find the location of the plugin's shared library. The default - * is to assume the app is either a user installed app or an - * updated system app. In both of these cases the library is - * stored in the app's data directory. - */ - String directory = pkgInfo.applicationInfo.dataDir + "/lib"; - final int appFlags = pkgInfo.applicationInfo.flags; - final int updatedSystemFlags = ApplicationInfo.FLAG_SYSTEM | - ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; - - // preloaded system app with no user updates - if ((appFlags & updatedSystemFlags) == ApplicationInfo.FLAG_SYSTEM) { - directory = PLUGIN_SYSTEM_LIB + pkgInfo.packageName; - } - - // check if the plugin has the required permissions - String permissions[] = pkgInfo.requestedPermissions; - if (permissions == null) { - Log.w(LOGTAG, "Not loading plugin: " + serviceInfo.packageName + ". Does not have required permission."); - continue; - } - boolean permissionOk = false; - for (String permit : permissions) { - if (PLUGIN_PERMISSION.equals(permit)) { - permissionOk = true; - break; - } - } - if (!permissionOk) { - Log.w(LOGTAG, "Not loading plugin: " + serviceInfo.packageName + ". Does not have required permission (2)."); - continue; - } - - // check to ensure the plugin is properly signed - Signature signatures[] = pkgInfo.signatures; - if (signatures == null) { - Log.w(LOGTAG, "Not loading plugin: " + serviceInfo.packageName + ". Not signed."); - continue; - } - - // determine the type of plugin from the manifest - if (serviceInfo.metaData == null) { - Log.e(LOGTAG, "The plugin '" + serviceInfo.name + "' has no defined type."); - continue; - } - - String pluginType = serviceInfo.metaData.getString(PLUGIN_TYPE); - if (!TYPE_NATIVE.equals(pluginType)) { - Log.e(LOGTAG, "Unrecognized plugin type: " + pluginType); - continue; - } - - try { - Class<?> cls = getPluginClass(serviceInfo.packageName, serviceInfo.name); - - //TODO implement any requirements of the plugin class here! - boolean classFound = true; - - if (!classFound) { - Log.e(LOGTAG, "The plugin's class' " + serviceInfo.name + "' does not extend the appropriate class."); - continue; - } - - } catch (NameNotFoundException e) { - Log.e(LOGTAG, "Can't find plugin: " + serviceInfo.packageName); - continue; - } catch (ClassNotFoundException e) { - Log.e(LOGTAG, "Can't find plugin's class: " + serviceInfo.name); - continue; - } - - // if all checks have passed then make the plugin available - mPackageInfoCache.add(pkgInfo); - directories.add(directory); - } - } - - return directories.toArray(new String[directories.size()]); - } - - static String getPluginPackage(String pluginLib) { - - if (pluginLib == null || pluginLib.length() == 0) { - return null; - } - - synchronized(mPackageInfoCache) { - for (PackageInfo pkgInfo : mPackageInfoCache) { - if (pluginLib.contains(pkgInfo.packageName)) { - return pkgInfo.packageName; - } - } - } - - return null; - } - - static Class<?> getPluginClass(String packageName, String className) - throws NameNotFoundException, ClassNotFoundException { - Context pluginContext = getContext().createPackageContext(packageName, - Context.CONTEXT_INCLUDE_CODE | - Context.CONTEXT_IGNORE_SECURITY); - ClassLoader pluginCL = pluginContext.getClassLoader(); - return pluginCL.loadClass(className); - } - - public static Class<?> loadPluginClass(String className, String libName) { - if (getGeckoInterface() == null) - return null; - try { - final String packageName = getPluginPackage(libName); - final int contextFlags = Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY; - final Context pluginContext = getContext().createPackageContext(packageName, contextFlags); - return pluginContext.getClassLoader().loadClass(className); - } catch (java.lang.ClassNotFoundException cnfe) { - Log.w(LOGTAG, "Couldn't find plugin class " + className, cnfe); - return null; - } catch (android.content.pm.PackageManager.NameNotFoundException nnfe) { - Log.w(LOGTAG, "Couldn't find package.", nnfe); - return null; - } - } - - private static ContextGetter sContextGetter; - - public static Context getContext() { - return sContextGetter.getContext(); - } - - public static void setContextGetter(ContextGetter cg) { - sContextGetter = cg; - } - - public interface AppStateListener { - public void onPause(); - public void onResume(); - public void onOrientationChanged(); - } - - public interface GeckoInterface { - public GeckoProfile getProfile(); - public PromptService getPromptService(); - public Activity getActivity(); - public String getDefaultUAString(); - public LocationListener getLocationListener(); - public SensorEventListener getSensorEventListener(); - public void doRestart(); - public void setFullScreen(boolean fullscreen); - public void addPluginView(View view, final Rect rect, final boolean isFullScreen); - public void removePluginView(final View view, final boolean isFullScreen); - public void enableCameraView(); - public void disableCameraView(); - public void addAppStateListener(AppStateListener listener); - public void removeAppStateListener(AppStateListener listener); - public View getCameraView(); - public void notifyWakeLockChanged(String topic, String state); - public FormAssistPopup getFormAssistPopup(); - public boolean areTabsShown(); - public AbsoluteLayout getPluginContainer(); - public void notifyCheckUpdateResult(String result); - public boolean hasTabsSideBar(); - public void invalidateOptionsMenu(); - }; - - private static GeckoInterface sGeckoInterface; - - public static GeckoInterface getGeckoInterface() { - return sGeckoInterface; - } - - public static void setGeckoInterface(GeckoInterface aGeckoInterface) { - sGeckoInterface = aGeckoInterface; - } - - public static android.hardware.Camera sCamera = null; - - static native void cameraCallbackBridge(byte[] data); - - static int kPreferedFps = 25; - static byte[] sCameraBuffer = null; - - static int[] initCamera(String aContentType, int aCamera, int aWidth, int aHeight) { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - try { - if (getGeckoInterface() != null) - getGeckoInterface().enableCameraView(); - } catch (Exception e) {} - } - }); - - // [0] = 0|1 (failure/success) - // [1] = width - // [2] = height - // [3] = fps - int[] result = new int[4]; - result[0] = 0; - - if (Build.VERSION.SDK_INT >= 9) { - if (android.hardware.Camera.getNumberOfCameras() == 0) - return result; - } - - try { - // no front/back camera before API level 9 - if (Build.VERSION.SDK_INT >= 9) - sCamera = android.hardware.Camera.open(aCamera); - else - sCamera = android.hardware.Camera.open(); - - android.hardware.Camera.Parameters params = sCamera.getParameters(); - params.setPreviewFormat(ImageFormat.NV21); - - // use the preview fps closest to 25 fps. - int fpsDelta = 1000; - try { - Iterator<Integer> it = params.getSupportedPreviewFrameRates().iterator(); - while (it.hasNext()) { - int nFps = it.next(); - if (Math.abs(nFps - kPreferedFps) < fpsDelta) { - fpsDelta = Math.abs(nFps - kPreferedFps); - params.setPreviewFrameRate(nFps); - } - } - } catch(Exception e) { - params.setPreviewFrameRate(kPreferedFps); - } - - // set up the closest preview size available - Iterator<android.hardware.Camera.Size> sit = params.getSupportedPreviewSizes().iterator(); - int sizeDelta = 10000000; - int bufferSize = 0; - while (sit.hasNext()) { - android.hardware.Camera.Size size = sit.next(); - if (Math.abs(size.width * size.height - aWidth * aHeight) < sizeDelta) { - sizeDelta = Math.abs(size.width * size.height - aWidth * aHeight); - params.setPreviewSize(size.width, size.height); - bufferSize = size.width * size.height; - } - } - - try { - if (getGeckoInterface() != null) { - View cameraView = getGeckoInterface().getCameraView(); - if (cameraView instanceof SurfaceView) { - sCamera.setPreviewDisplay(((SurfaceView)cameraView).getHolder()); - } else if (cameraView instanceof TextureView) { - sCamera.setPreviewTexture(((TextureView)cameraView).getSurfaceTexture()); - } - } - } catch(IOException e) { - Log.w(LOGTAG, "Error setPreviewXXX:", e); - } catch(RuntimeException e) { - Log.w(LOGTAG, "Error setPreviewXXX:", e); - } - - sCamera.setParameters(params); - sCameraBuffer = new byte[(bufferSize * 12) / 8]; - sCamera.addCallbackBuffer(sCameraBuffer); - sCamera.setPreviewCallbackWithBuffer(new android.hardware.Camera.PreviewCallback() { - @Override - public void onPreviewFrame(byte[] data, android.hardware.Camera camera) { - cameraCallbackBridge(data); - if (sCamera != null) - sCamera.addCallbackBuffer(sCameraBuffer); - } - }); - sCamera.startPreview(); - params = sCamera.getParameters(); - result[0] = 1; - result[1] = params.getPreviewSize().width; - result[2] = params.getPreviewSize().height; - result[3] = params.getPreviewFrameRate(); - } catch(RuntimeException e) { - Log.w(LOGTAG, "initCamera RuntimeException.", e); - result[0] = result[1] = result[2] = result[3] = 0; - } - return result; - } - - static synchronized void closeCamera() { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - try { - if (getGeckoInterface() != null) - getGeckoInterface().disableCameraView(); - } catch (Exception e) {} - } - }); - if (sCamera != null) { - sCamera.stopPreview(); - sCamera.release(); - sCamera = null; - sCameraBuffer = null; - } - } - - /** - * Adds a listener for a gecko event. - * This method is thread-safe and may be called at any time. In particular, calling it - * with an event that is currently being processed has the properly-defined behaviour that - * any added listeners will not be invoked on the event currently being processed, but - * will be invoked on future events of that type. - * - * This method is referenced by Robocop via reflection. - */ - public static void registerEventListener(String event, GeckoEventListener listener) { - sEventDispatcher.registerEventListener(event, listener); - } - - static EventDispatcher getEventDispatcher() { - return sEventDispatcher; - } - - /** - * Remove a previously-registered listener for a gecko event. - * This method is thread-safe and may be called at any time. In particular, calling it - * with an event that is currently being processed has the properly-defined behaviour that - * any removed listeners will still be invoked on the event currently being processed, but - * will not be invoked on future events of that type. - * - * This method is referenced by Robocop via reflection. - */ - public static void unregisterEventListener(String event, GeckoEventListener listener) { - sEventDispatcher.unregisterEventListener(event, listener); - } - - /* - * Battery API related methods. - */ - public static void enableBatteryNotifications() { - GeckoBatteryManager.enableNotifications(); - } - - public static String handleGeckoMessage(String message) { - return sEventDispatcher.dispatchEvent(message); - } - - public static void disableBatteryNotifications() { - GeckoBatteryManager.disableNotifications(); - } - - public static double[] getCurrentBatteryInformation() { - return GeckoBatteryManager.getCurrentInformation(); - } - - static void checkUriVisited(String uri) { // invoked from native JNI code - GlobalHistory.getInstance().checkUriVisited(uri); - } - - static void markUriVisited(final String uri) { // invoked from native JNI code - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - GlobalHistory.getInstance().add(uri); - } - }); - } - - static void setUriTitle(final String uri, final String title) { // invoked from native JNI code - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - GlobalHistory.getInstance().update(uri, title); - } - }); - } - - static void hideProgressDialog() { - // unused stub - } - - /* - * WebSMS related methods. - */ - public static void sendMessage(String aNumber, String aMessage, int aRequestId) { - if (SmsManager.getInstance() == null) { - return; - } - - SmsManager.getInstance().send(aNumber, aMessage, aRequestId); - } - - public static void getMessage(int aMessageId, int aRequestId) { - if (SmsManager.getInstance() == null) { - return; - } - - SmsManager.getInstance().getMessage(aMessageId, aRequestId); - } - - public static void deleteMessage(int aMessageId, int aRequestId) { - if (SmsManager.getInstance() == null) { - return; - } - - SmsManager.getInstance().deleteMessage(aMessageId, aRequestId); - } - - public static void createMessageList(long aStartDate, long aEndDate, String[] aNumbers, int aNumbersCount, int aDeliveryState, boolean aReverse, int aRequestId) { - if (SmsManager.getInstance() == null) { - return; - } - - SmsManager.getInstance().createMessageList(aStartDate, aEndDate, aNumbers, aNumbersCount, aDeliveryState, aReverse, aRequestId); - } - - public static void getNextMessageInList(int aListId, int aRequestId) { - if (SmsManager.getInstance() == null) { - return; - } - - SmsManager.getInstance().getNextMessageInList(aListId, aRequestId); - } - - public static void clearMessageList(int aListId) { - if (SmsManager.getInstance() == null) { - return; - } - - SmsManager.getInstance().clearMessageList(aListId); - } - - /* Called by JNI from AndroidBridge, and by reflection from tests/BaseTest.java.in */ - public static boolean isTablet() { - return HardwareUtils.isTablet(); - } - - public static void viewSizeChanged() { - LayerView v = getLayerView(); - if (v != null && v.isIMEEnabled()) { - sendEventToGecko(GeckoEvent.createBroadcastEvent( - "ScrollTo:FocusedInput", "")); - } - } - - public static double[] getCurrentNetworkInformation() { - return GeckoNetworkManager.getInstance().getCurrentInformation(); - } - - public static void enableNetworkNotifications() { - GeckoNetworkManager.getInstance().enableNotifications(); - } - - public static void disableNetworkNotifications() { - GeckoNetworkManager.getInstance().disableNotifications(); - } - - // values taken from android's Base64 - public static final int BASE64_DEFAULT = 0; - public static final int BASE64_URL_SAFE = 8; - - /** - * taken from http://www.source-code.biz/base64coder/java/Base64Coder.java.txt and modified (MIT License) - */ - // Mapping table from 6-bit nibbles to Base64 characters. - private static final byte[] map1 = new byte[64]; - private static final byte[] map1_urlsafe; - static { - int i=0; - for (byte c='A'; c<='Z'; c++) map1[i++] = c; - for (byte c='a'; c<='z'; c++) map1[i++] = c; - for (byte c='0'; c<='9'; c++) map1[i++] = c; - map1[i++] = '+'; map1[i++] = '/'; - map1_urlsafe = map1.clone(); - map1_urlsafe[62] = '-'; map1_urlsafe[63] = '_'; - } - - // Mapping table from Base64 characters to 6-bit nibbles. - private static final byte[] map2 = new byte[128]; - static { - for (int i=0; i<map2.length; i++) map2[i] = -1; - for (int i=0; i<64; i++) map2[map1[i]] = (byte)i; - map2['-'] = (byte)62; map2['_'] = (byte)63; - } - - final static byte EQUALS_ASCII = (byte) '='; - - /** - * Encodes a byte array into Base64 format. - * No blanks or line breaks are inserted in the output. - * @param in An array containing the data bytes to be encoded. - * @return A character array containing the Base64 encoded data. - */ - public static byte[] encodeBase64(byte[] in, int flags) { - if (Build.VERSION.SDK_INT >=Build.VERSION_CODES.FROYO) - return Base64.encode(in, flags | Base64.NO_WRAP); - int oDataLen = (in.length*4+2)/3; // output length without padding - int oLen = ((in.length+2)/3)*4; // output length including padding - byte[] out = new byte[oLen]; - int ip = 0; - int iEnd = in.length; - int op = 0; - byte[] toMap = ((flags & BASE64_URL_SAFE) == 0 ? map1 : map1_urlsafe); - while (ip < iEnd) { - int i0 = in[ip++] & 0xff; - int i1 = ip < iEnd ? in[ip++] & 0xff : 0; - int i2 = ip < iEnd ? in[ip++] & 0xff : 0; - int o0 = i0 >>> 2; - int o1 = ((i0 & 3) << 4) | (i1 >>> 4); - int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); - int o3 = i2 & 0x3F; - out[op++] = toMap[o0]; - out[op++] = toMap[o1]; - out[op] = op < oDataLen ? toMap[o2] : EQUALS_ASCII; op++; - out[op] = op < oDataLen ? toMap[o3] : EQUALS_ASCII; op++; - } - return out; - } - - /** - * Decodes a byte array from Base64 format. - * No blanks or line breaks are allowed within the Base64 encoded input data. - * @param in A character array containing the Base64 encoded data. - * @param iOff Offset of the first character in <code>in</code> to be processed. - * @param iLen Number of characters to process in <code>in</code>, starting at <code>iOff</code>. - * @return An array containing the decoded data bytes. - * @throws IllegalArgumentException If the input is not valid Base64 encoded data. - */ - public static byte[] decodeBase64(byte[] in, int flags) { - if (Build.VERSION.SDK_INT >=Build.VERSION_CODES.FROYO) - return Base64.decode(in, flags); - int iOff = 0; - int iLen = in.length; - if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4."); - while (iLen > 0 && in[iOff+iLen-1] == '=') iLen--; - int oLen = (iLen*3) / 4; - byte[] out = new byte[oLen]; - int ip = iOff; - int iEnd = iOff + iLen; - int op = 0; - while (ip < iEnd) { - int i0 = in[ip++]; - int i1 = in[ip++]; - int i2 = ip < iEnd ? in[ip++] : 'A'; - int i3 = ip < iEnd ? in[ip++] : 'A'; - if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) - throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); - int b0 = map2[i0]; - int b1 = map2[i1]; - int b2 = map2[i2]; - int b3 = map2[i3]; - if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) - throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); - int o0 = ( b0 <<2) | (b1>>>4); - int o1 = ((b1 & 0xf)<<4) | (b2>>>2); - int o2 = ((b2 & 3)<<6) | b3; - out[op++] = (byte)o0; - if (op<oLen) out[op++] = (byte)o1; - if (op<oLen) out[op++] = (byte)o2; } - return out; - } - - public static byte[] decodeBase64(String s, int flags) { - return decodeBase64(s.getBytes(), flags); - } - - public static short getScreenOrientation() { - return GeckoScreenOrientationListener.getInstance().getScreenOrientation(); - } - - public static void enableScreenOrientationNotifications() { - GeckoScreenOrientationListener.getInstance().enableNotifications(); - } - - public static void disableScreenOrientationNotifications() { - GeckoScreenOrientationListener.getInstance().disableNotifications(); - } - - public static void lockScreenOrientation(int aOrientation) { - GeckoScreenOrientationListener.getInstance().lockScreenOrientation(aOrientation); - } - - public static void unlockScreenOrientation() { - GeckoScreenOrientationListener.getInstance().unlockScreenOrientation(); - } - - public static boolean pumpMessageLoop() { - MessageQueue mq = Looper.myQueue(); - Message msg = getNextMessageFromQueue(mq); - if (msg == null) - return false; - if (msg.getTarget() == null) - Looper.myLooper().quit(); - else - msg.getTarget().dispatchMessage(msg); - - try { - msg.recycle(); - } catch (IllegalStateException e) { - // There is nothing we can do here so just eat it - } - return true; - } - - static native void notifyFilePickerResult(String filePath, long id); - - public static void showFilePickerAsync(String aMimeType, final long id) { - sActivityHelper.showFilePickerAsync(getGeckoInterface().getActivity(), aMimeType, new ActivityHandlerHelper.FileResultHandler() { - public void gotFile(String filename) { - GeckoAppShell.notifyFilePickerResult(filename, id); - } - }); - } - - public static void notifyWakeLockChanged(String topic, String state) { - if (getGeckoInterface() != null) - getGeckoInterface().notifyWakeLockChanged(topic, state); - } - - public static String getGfxInfoData() { - return GfxInfoThread.getData(); - } - - public static void registerSurfaceTextureFrameListener(Object surfaceTexture, final int id) { - ((SurfaceTexture)surfaceTexture).setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() { - @Override - public void onFrameAvailable(SurfaceTexture surfaceTexture) { - GeckoAppShell.onSurfaceTextureFrameAvailable(surfaceTexture, id); - } - }); - } - - public static void unregisterSurfaceTextureFrameListener(Object surfaceTexture) { - ((SurfaceTexture)surfaceTexture).setOnFrameAvailableListener(null); - } - - public static boolean unlockProfile() { - // Try to kill any zombie Fennec's that might be running - GeckoAppShell.killAnyZombies(); - - // Then force unlock this profile - if (getGeckoInterface() != null) { - GeckoProfile profile = getGeckoInterface().getProfile(); - File lock = profile.getFile(".parentlock"); - return lock.exists() && lock.delete(); - } - return false; - } - - public static String getProxyForURI(String spec, String scheme, String host, int port) { - URI uri = null; - try { - uri = new URI(spec); - } catch(java.net.URISyntaxException uriEx) { - try { - uri = new URI(scheme, null, host, port, null, null, null); - } catch(java.net.URISyntaxException uriEx2) { - Log.d("GeckoProxy", "Failed to create uri from spec", uriEx); - Log.d("GeckoProxy", "Failed to create uri from parts", uriEx2); - } - } - if (uri != null) { - ProxySelector ps = ProxySelector.getDefault(); - if (ps != null) { - List<Proxy> proxies = ps.select(uri); - if (proxies != null && !proxies.isEmpty()) { - Proxy proxy = proxies.get(0); - if (!Proxy.NO_PROXY.equals(proxy)) { - final String proxyStr; - switch (proxy.type()) { - case HTTP: - proxyStr = "PROXY " + proxy.address().toString(); - break; - case SOCKS: - proxyStr = "SOCKS " + proxy.address().toString(); - break; - case DIRECT: - default: - proxyStr = "DIRECT"; - break; - } - return proxyStr; - } - } - } - } - return "DIRECT"; - } -} diff --git a/mobile/android/base/GeckoApplication.java b/mobile/android/base/GeckoApplication.java deleted file mode 100644 index fa6d44b3d..000000000 --- a/mobile/android/base/GeckoApplication.java +++ /dev/null @@ -1,112 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import org.mozilla.gecko.db.BrowserContract; -import org.mozilla.gecko.db.BrowserDB; -import org.mozilla.gecko.mozglue.GeckoLoader; -import org.mozilla.gecko.util.Clipboard; -import org.mozilla.gecko.util.HardwareUtils; -import org.mozilla.gecko.util.ThreadUtils; - -import android.app.Application; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; - -public class GeckoApplication extends Application { - - private boolean mInited; - private boolean mInBackground; - private boolean mPausedGecko; - private boolean mNeedsRestart; - - private LightweightTheme mLightweightTheme; - - protected void initialize() { - if (mInited) - return; - - // workaround for http://code.google.com/p/android/issues/detail?id=20915 - try { - Class.forName("android.os.AsyncTask"); - } catch (ClassNotFoundException e) {} - - mLightweightTheme = new LightweightTheme(this); - - GeckoConnectivityReceiver.getInstance().init(getApplicationContext()); - GeckoBatteryManager.getInstance().init(getApplicationContext()); - GeckoBatteryManager.getInstance().start(); - GeckoNetworkManager.getInstance().init(getApplicationContext()); - MemoryMonitor.getInstance().init(getApplicationContext()); - - BroadcastReceiver receiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - mNeedsRestart = true; - } - }; - registerReceiver(receiver, new IntentFilter(Intent.ACTION_LOCALE_CHANGED)); - - mInited = true; - } - - protected void onActivityPause(GeckoActivityStatus activity) { - mInBackground = true; - - if ((activity.isFinishing() == false) && - (activity.isGeckoActivityOpened() == false)) { - // Notify Gecko that we are pausing; the cache service will be - // shutdown, closing the disk cache cleanly. If the android - // low memory killer subsequently kills us, the disk cache will - // be left in a consistent state, avoiding costly cleanup and - // re-creation. - GeckoAppShell.sendEventToGecko(GeckoEvent.createAppBackgroundingEvent()); - mPausedGecko = true; - - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - BrowserDB.expireHistory(getContentResolver(), - BrowserContract.ExpirePriority.NORMAL); - } - }); - } - GeckoConnectivityReceiver.getInstance().stop(); - GeckoNetworkManager.getInstance().stop(); - } - - protected void onActivityResume(GeckoActivityStatus activity) { - if (mPausedGecko) { - GeckoAppShell.sendEventToGecko(GeckoEvent.createAppForegroundingEvent()); - mPausedGecko = false; - } - GeckoConnectivityReceiver.getInstance().start(); - GeckoNetworkManager.getInstance().start(); - - mInBackground = false; - } - - protected boolean needsRestart() { - return mNeedsRestart; - } - - @Override - public void onCreate() { - HardwareUtils.init(getApplicationContext()); - Clipboard.init(getApplicationContext()); - GeckoLoader.loadMozGlue(getApplicationContext()); - super.onCreate(); - } - - public boolean isApplicationInBackground() { - return mInBackground; - } - - public LightweightTheme getLightweightTheme() { - return mLightweightTheme; - } -} diff --git a/mobile/android/base/GeckoBatteryManager.java b/mobile/android/base/GeckoBatteryManager.java deleted file mode 100644 index 85e7bd22c..000000000 --- a/mobile/android/base/GeckoBatteryManager.java +++ /dev/null @@ -1,193 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.BatteryManager; -import android.os.Build; -import android.os.SystemClock; -import android.util.Log; - -public class GeckoBatteryManager extends BroadcastReceiver { - private static final String LOGTAG = "GeckoBatteryManager"; - - // Those constants should be keep in sync with the ones in: - // dom/battery/Constants.h - private final static double kDefaultLevel = 1.0; - private final static boolean kDefaultCharging = true; - private final static double kDefaultRemainingTime = 0.0; - private final static double kUnknownRemainingTime = -1.0; - - private static long sLastLevelChange = 0; - private static boolean sNotificationsEnabled = false; - private static double sLevel = kDefaultLevel; - private static boolean sCharging = kDefaultCharging; - private static double sRemainingTime = kDefaultRemainingTime; - - private static GeckoBatteryManager sInstance = new GeckoBatteryManager(); - - private IntentFilter mFilter; - private Context mApplicationContext; - private boolean mIsEnabled; - - public static GeckoBatteryManager getInstance() { - return sInstance; - } - - private GeckoBatteryManager() { - mFilter = new IntentFilter(); - mFilter.addAction(Intent.ACTION_BATTERY_CHANGED); - } - - public void init(Context context) { - mApplicationContext = context.getApplicationContext(); - } - - public synchronized void start() { - if (!mIsEnabled) { - // registerReceiver will return null if registering fails - if (mApplicationContext.registerReceiver(this, mFilter) == null) { - Log.e(LOGTAG, "Registering receiver failed"); - } else { - mIsEnabled = true; - } - } - } - - public synchronized void stop() { - if (mIsEnabled) { - mApplicationContext.unregisterReceiver(this); - mIsEnabled = false; - } - } - - @Override - public void onReceive(Context context, Intent intent) { - if (!intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) { - Log.e(LOGTAG, "Got an unexpected intent!"); - return; - } - - boolean previousCharging = isCharging(); - double previousLevel = getLevel(); - - // NOTE: it might not be common (in 2012) but technically, Android can run - // on a device that has no battery so we want to make sure it's not the case - // before bothering checking for battery state. - // However, the Galaxy Nexus phone advertizes itself as battery-less which - // force us to special-case the logic. - // See the Google bug: https://code.google.com/p/android/issues/detail?id=22035 - if (intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, false) || - Build.MODEL.equals("Galaxy Nexus")) { - int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); - if (plugged == -1) { - sCharging = kDefaultCharging; - Log.e(LOGTAG, "Failed to get the plugged status!"); - } else { - // Likely, if plugged > 0, it's likely plugged and charging but the doc - // isn't clear about that. - sCharging = plugged != 0; - } - - if (sCharging != previousCharging) { - sRemainingTime = kUnknownRemainingTime; - // The new remaining time is going to take some time to show up but - // it's the best way to show a not too wrong value. - sLastLevelChange = 0; - } - - // We need two doubles because sLevel is a double. - double current = (double)intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); - double max = (double)intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); - if (current == -1 || max == -1) { - Log.e(LOGTAG, "Failed to get battery level!"); - sLevel = kDefaultLevel; - } else { - sLevel = current / max; - } - - if (sLevel == 1.0 && sCharging) { - sRemainingTime = kDefaultRemainingTime; - } else if (sLevel != previousLevel) { - // Estimate remaining time. - if (sLastLevelChange != 0) { - // Use elapsedRealtime() because we want to track time across device sleeps. - long currentTime = SystemClock.elapsedRealtime(); - long dt = (currentTime - sLastLevelChange) / 1000; - double dLevel = sLevel - previousLevel; - - if (sCharging) { - if (dLevel < 0) { - Log.w(LOGTAG, "When charging, level should increase!"); - sRemainingTime = kUnknownRemainingTime; - } else { - sRemainingTime = Math.round(dt / dLevel * (1.0 - sLevel)); - } - } else { - if (dLevel > 0) { - Log.w(LOGTAG, "When discharging, level should decrease!"); - sRemainingTime = kUnknownRemainingTime; - } else { - sRemainingTime = Math.round(dt / -dLevel * sLevel); - } - } - - sLastLevelChange = currentTime; - } else { - // That's the first time we got an update, we can't do anything. - sLastLevelChange = SystemClock.elapsedRealtime(); - } - } - } else { - sLevel = kDefaultLevel; - sCharging = kDefaultCharging; - sRemainingTime = kDefaultRemainingTime; - } - - /* - * We want to inform listeners if the following conditions are fulfilled: - * - we have at least one observer; - * - the charging state or the level has changed. - * - * Note: no need to check for a remaining time change given that it's only - * updated if there is a level change or a charging change. - * - * The idea is to prevent doing all the way to the DOM code in the child - * process to finally not send an event. - */ - if (sNotificationsEnabled && - (previousCharging != isCharging() || previousLevel != getLevel())) { - GeckoAppShell.notifyBatteryChange(getLevel(), isCharging(), getRemainingTime()); - } - } - - public static boolean isCharging() { - return sCharging; - } - - public static double getLevel() { - return sLevel; - } - - public static double getRemainingTime() { - return sRemainingTime; - } - - public static void enableNotifications() { - sNotificationsEnabled = true; - } - - public static void disableNotifications() { - sNotificationsEnabled = false; - } - - public static double[] getCurrentInformation() { - return new double[] { getLevel(), isCharging() ? 1.0 : 0.0, getRemainingTime() }; - } -} diff --git a/mobile/android/base/GeckoConnectivityReceiver.java b/mobile/android/base/GeckoConnectivityReceiver.java deleted file mode 100644 index 8c9d1b19c..000000000 --- a/mobile/android/base/GeckoConnectivityReceiver.java +++ /dev/null @@ -1,82 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.util.Log; - -public class GeckoConnectivityReceiver extends BroadcastReceiver { - /* - * Keep the below constants in sync with - * http://mxr.mozilla.org/mozilla-central/source/netwerk/base/public/nsINetworkLinkService.idl - */ - private static final String LINK_DATA_UP = "up"; - private static final String LINK_DATA_DOWN = "down"; - private static final String LINK_DATA_UNKNOWN = "unknown"; - - private static final String LOGTAG = "GeckoConnectivityReceiver"; - - private static GeckoConnectivityReceiver sInstance = new GeckoConnectivityReceiver(); - - private IntentFilter mFilter; - private Context mApplicationContext; - private boolean mIsEnabled; - - public static GeckoConnectivityReceiver getInstance() { - return sInstance; - } - - private GeckoConnectivityReceiver() { - mFilter = new IntentFilter(); - mFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); - } - - public void init(Context context) { - mApplicationContext = context.getApplicationContext(); - } - - public synchronized void start() { - if (!mIsEnabled) { - // registerReceiver will return null if registering fails - if (mApplicationContext.registerReceiver(this, mFilter) == null) { - Log.e(LOGTAG, "Registering receiver failed"); - } else { - mIsEnabled = true; - } - } - } - - public synchronized void stop() { - if (mIsEnabled) { - mApplicationContext.unregisterReceiver(this); - mIsEnabled = false; - } - } - - @Override - public void onReceive(Context context, Intent intent) { - ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo info = cm.getActiveNetworkInfo(); - - String status; - if (info == null) { - status = LINK_DATA_UNKNOWN; - } else if (!info.isConnected()) { - status = LINK_DATA_DOWN; - } else { - status = LINK_DATA_UP; - } - - if (GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) { - GeckoAppShell.sendEventToGecko(GeckoEvent.createNetworkLinkChangeEvent(status)); - } - } -} diff --git a/mobile/android/base/GeckoEditable.java b/mobile/android/base/GeckoEditable.java deleted file mode 100644 index 887b6bb81..000000000 --- a/mobile/android/base/GeckoEditable.java +++ /dev/null @@ -1,1207 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import org.mozilla.gecko.gfx.InputConnectionHandler; -import org.mozilla.gecko.gfx.LayerView; -import org.mozilla.gecko.util.ThreadUtils; - -import android.os.Build; -import android.os.Handler; -import android.os.Looper; -import android.text.Editable; -import android.text.InputFilter; -import android.text.Selection; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.SpannableStringBuilder; -import android.text.Spanned; -import android.text.TextPaint; -import android.text.TextUtils; -import android.text.style.CharacterStyle; -import android.util.Log; -import android.view.KeyCharacterMap; -import android.view.KeyEvent; - -import java.lang.reflect.Field; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.Semaphore; - -// interface for the IC thread -interface GeckoEditableClient { - void sendEvent(GeckoEvent event); - Editable getEditable(); - void setUpdateGecko(boolean update); - void setSuppressKeyUp(boolean suppress); - Handler getInputConnectionHandler(); - boolean setInputConnectionHandler(Handler handler); -} - -/* interface for the Editable to listen to the Gecko thread - and also for the IC thread to listen to the Editable */ -interface GeckoEditableListener { - // IME notification type for notifyIME(), corresponding to NotificationToIME enum in Gecko - final int NOTIFY_IME_REPLY_EVENT = -1; - final int NOTIFY_IME_OF_FOCUS = 1; - final int NOTIFY_IME_OF_BLUR = 2; - final int NOTIFY_IME_TO_COMMIT_COMPOSITION = 4; - final int NOTIFY_IME_TO_CANCEL_COMPOSITION = 5; - // IME enabled state for notifyIMEContext() - final int IME_STATE_DISABLED = 0; - final int IME_STATE_ENABLED = 1; - final int IME_STATE_PASSWORD = 2; - final int IME_STATE_PLUGIN = 3; - - void notifyIME(int type); - void notifyIMEContext(int state, String typeHint, - String modeHint, String actionHint); - void onSelectionChange(int start, int end); - void onTextChange(String text, int start, int oldEnd, int newEnd); -} - -/* - GeckoEditable implements only some functions of Editable - The field mText contains the actual underlying - SpannableStringBuilder/Editable that contains our text. -*/ -final class GeckoEditable - implements InvocationHandler, Editable, - GeckoEditableClient, GeckoEditableListener { - - private static final boolean DEBUG = false; - private static final String LOGTAG = "GeckoEditable"; - - // Filters to implement Editable's filtering functionality - private InputFilter[] mFilters; - - private final SpannableStringBuilder mText; - private final SpannableStringBuilder mChangedText; - private final Editable mProxy; - private final ActionQueue mActionQueue; - - // mIcRunHandler is the Handler that currently runs Gecko-to-IC Runnables - // mIcPostHandler is the Handler to post Gecko-to-IC Runnables to - // The two can be different when switching from one handler to another - private Handler mIcRunHandler; - private Handler mIcPostHandler; - - private GeckoEditableListener mListener; - private int mSavedSelectionStart; - private volatile int mGeckoUpdateSeqno; - private int mIcUpdateSeqno; - private int mLastIcUpdateSeqno; - private boolean mUpdateGecko; - private boolean mFocused; - private volatile boolean mSuppressKeyUp; - - /* An action that alters the Editable - - Each action corresponds to a Gecko event. While the Gecko event is being sent to the Gecko - thread, the action stays on top of mActions queue. After the Gecko event is processed and - replied, the action is removed from the queue - */ - private static final class Action { - // For input events (keypress, etc.); use with IME_SYNCHRONIZE - static final int TYPE_EVENT = 0; - // For Editable.replace() call; use with IME_REPLACE_TEXT - static final int TYPE_REPLACE_TEXT = 1; - /* For Editable.setSpan(Selection...) call; use with IME_SYNCHRONIZE - Note that we don't use this with IME_SET_SELECTION because we don't want to update the - Gecko selection at the point of this action. The Gecko selection is updated only after - IC has updated its selection (during IME_SYNCHRONIZE reply) */ - static final int TYPE_SET_SELECTION = 2; - // For Editable.setSpan() call; use with IME_SYNCHRONIZE - static final int TYPE_SET_SPAN = 3; - // For Editable.removeSpan() call; use with IME_SYNCHRONIZE - static final int TYPE_REMOVE_SPAN = 4; - // For focus events (in notifyIME); use with IME_ACKNOWLEDGE_FOCUS - static final int TYPE_ACKNOWLEDGE_FOCUS = 5; - // For switching handler; use with IME_SYNCHRONIZE - static final int TYPE_SET_HANDLER = 6; - - final int mType; - int mStart; - int mEnd; - CharSequence mSequence; - Object mSpanObject; - int mSpanFlags; - boolean mShouldUpdate; - Handler mHandler; - - Action(int type) { - mType = type; - } - - static Action newReplaceText(CharSequence text, int start, int end) { - if (start < 0 || start > end) { - throw new IllegalArgumentException( - "invalid replace text offsets: " + start + " to " + end); - } - final Action action = new Action(TYPE_REPLACE_TEXT); - action.mSequence = text; - action.mStart = start; - action.mEnd = end; - return action; - } - - static Action newSetSelection(int start, int end) { - // start == -1 when the start offset should remain the same - // end == -1 when the end offset should remain the same - if (start < -1 || end < -1) { - throw new IllegalArgumentException( - "invalid selection offsets: " + start + " to " + end); - } - final Action action = new Action(TYPE_SET_SELECTION); - action.mStart = start; - action.mEnd = end; - return action; - } - - static Action newSetSpan(Object object, int start, int end, int flags) { - if (start < 0 || start > end) { - throw new IllegalArgumentException( - "invalid span offsets: " + start + " to " + end); - } - final Action action = new Action(TYPE_SET_SPAN); - action.mSpanObject = object; - action.mStart = start; - action.mEnd = end; - action.mSpanFlags = flags; - return action; - } - - static Action newSetHandler(Handler handler) { - final Action action = new Action(TYPE_SET_HANDLER); - action.mHandler = handler; - return action; - } - } - - /* Queue of editing actions sent to Gecko thread that - the Gecko thread has not responded to yet */ - private final class ActionQueue { - private final ConcurrentLinkedQueue<Action> mActions; - private final Semaphore mActionsActive; - private KeyCharacterMap mKeyMap; - - ActionQueue() { - mActions = new ConcurrentLinkedQueue<Action>(); - mActionsActive = new Semaphore(1); - } - - void offer(Action action) { - if (DEBUG) { - assertOnIcThread(); - Log.d(LOGTAG, "offer: Action(" + - getConstantName(Action.class, "TYPE_", action.mType) + ")"); - } - /* Events don't need update because they generate text/selection - notifications which will do the updating for us */ - if (action.mType != Action.TYPE_EVENT && - action.mType != Action.TYPE_ACKNOWLEDGE_FOCUS && - action.mType != Action.TYPE_SET_HANDLER) { - action.mShouldUpdate = mUpdateGecko; - } - if (mActions.isEmpty()) { - mActionsActive.acquireUninterruptibly(); - mActions.offer(action); - } else synchronized(this) { - // tryAcquire here in case Gecko thread has just released it - mActionsActive.tryAcquire(); - mActions.offer(action); - } - switch (action.mType) { - case Action.TYPE_EVENT: - case Action.TYPE_SET_SELECTION: - case Action.TYPE_SET_SPAN: - case Action.TYPE_REMOVE_SPAN: - case Action.TYPE_SET_HANDLER: - GeckoAppShell.sendEventToGecko(GeckoEvent.createIMEEvent( - GeckoEvent.ImeAction.IME_SYNCHRONIZE)); - break; - case Action.TYPE_REPLACE_TEXT: - // try key events first - sendCharKeyEvents(action); - GeckoAppShell.sendEventToGecko(GeckoEvent.createIMEReplaceEvent( - action.mStart, action.mEnd, action.mSequence.toString())); - break; - case Action.TYPE_ACKNOWLEDGE_FOCUS: - GeckoAppShell.sendEventToGecko(GeckoEvent.createIMEEvent( - GeckoEvent.ImeAction.IME_ACKNOWLEDGE_FOCUS)); - break; - } - ++mIcUpdateSeqno; - } - - private KeyEvent [] synthesizeKeyEvents(CharSequence cs) { - try { - if (mKeyMap == null) { - mKeyMap = KeyCharacterMap.load( - Build.VERSION.SDK_INT < 11 ? KeyCharacterMap.ALPHA : - KeyCharacterMap.VIRTUAL_KEYBOARD); - } - } catch (Exception e) { - // KeyCharacterMap.UnavailableExcepton is not found on Gingerbread; - // besides, it seems like HC and ICS will throw something other than - // KeyCharacterMap.UnavailableExcepton; so use a generic Exception here - return null; - } - KeyEvent [] keyEvents = mKeyMap.getEvents(cs.toString().toCharArray()); - if (keyEvents == null || keyEvents.length == 0) { - return null; - } - return keyEvents; - } - - private void sendCharKeyEvents(Action action) { - if (action.mSequence.length() == 0 || - (action.mSequence instanceof Spannable && - ((Spannable)action.mSequence).nextSpanTransition( - -1, Integer.MAX_VALUE, null) < Integer.MAX_VALUE)) { - // Spans are not preserved when we use key events, - // so we need the sequence to not have any spans - return; - } - KeyEvent [] keyEvents = synthesizeKeyEvents(action.mSequence); - if (keyEvents == null) { - return; - } - for (KeyEvent event : keyEvents) { - if (KeyEvent.isModifierKey(event.getKeyCode())) { - continue; - } - if (event.getAction() == KeyEvent.ACTION_UP && mSuppressKeyUp) { - continue; - } - if (DEBUG) { - Log.d(LOGTAG, "sending: " + event); - } - GeckoAppShell.sendEventToGecko(GeckoEvent.createIMEKeyEvent(event)); - } - } - - void poll() { - if (DEBUG) { - ThreadUtils.assertOnGeckoThread(); - } - if (mActions.isEmpty()) { - throw new IllegalStateException("empty actions queue"); - } - mActions.poll(); - // Don't bother locking if queue is not empty yet - if (mActions.isEmpty()) { - synchronized(this) { - if (mActions.isEmpty()) { - mActionsActive.release(); - } - } - } - } - - Action peek() { - if (DEBUG) { - ThreadUtils.assertOnGeckoThread(); - } - if (mActions.isEmpty()) { - throw new IllegalStateException("empty actions queue"); - } - return mActions.peek(); - } - - void syncWithGecko() { - if (DEBUG) { - assertOnIcThread(); - } - if (mFocused && !mActions.isEmpty()) { - if (DEBUG) { - Log.d(LOGTAG, "syncWithGecko blocking on thread " + - Thread.currentThread().getName()); - } - mActionsActive.acquireUninterruptibly(); - mActionsActive.release(); - } else if (DEBUG && !mFocused) { - Log.d(LOGTAG, "skipped syncWithGecko (no focus)"); - } - } - - boolean isEmpty() { - return mActions.isEmpty(); - } - } - - GeckoEditable() { - mActionQueue = new ActionQueue(); - mSavedSelectionStart = -1; - mUpdateGecko = true; - - mText = new SpannableStringBuilder(); - mChangedText = new SpannableStringBuilder(); - - final Class<?>[] PROXY_INTERFACES = { Editable.class }; - mProxy = (Editable)Proxy.newProxyInstance( - Editable.class.getClassLoader(), - PROXY_INTERFACES, this); - - LayerView v = GeckoAppShell.getLayerView(); - mListener = GeckoInputConnection.create(v, this); - - mIcRunHandler = mIcPostHandler = ThreadUtils.getUiHandler(); - } - - private boolean onIcThread() { - return mIcRunHandler.getLooper() == Looper.myLooper(); - } - - private void assertOnIcThread() { - ThreadUtils.assertOnThread(mIcRunHandler.getLooper().getThread()); - } - - private void geckoPostToIc(Runnable runnable) { - mIcPostHandler.post(runnable); - } - - private void geckoUpdateGecko(final boolean force) { - /* We do not increment the seqno here, but only check it, because geckoUpdateGecko is a - request for update. If we incremented the seqno here, geckoUpdateGecko would have - prevented other updates from occurring */ - final int seqnoWhenPosted = mGeckoUpdateSeqno; - - geckoPostToIc(new Runnable() { - @Override - public void run() { - mActionQueue.syncWithGecko(); - if (seqnoWhenPosted == mGeckoUpdateSeqno) { - icUpdateGecko(force); - } - } - }); - } - - private Object getField(Object obj, String field, Object def) { - try { - return obj.getClass().getField(field).get(obj); - } catch (Exception e) { - return def; - } - } - - private void icUpdateGecko(boolean force) { - - if (!force && mIcUpdateSeqno == mLastIcUpdateSeqno) { - if (DEBUG) { - Log.d(LOGTAG, "icUpdateGecko() skipped"); - } - return; - } - mLastIcUpdateSeqno = mIcUpdateSeqno; - mActionQueue.syncWithGecko(); - - if (DEBUG) { - Log.d(LOGTAG, "icUpdateGecko()"); - } - - final int selStart = mText.getSpanStart(Selection.SELECTION_START); - final int selEnd = mText.getSpanEnd(Selection.SELECTION_END); - int composingStart = mText.length(); - int composingEnd = 0; - Object[] spans = mText.getSpans(0, composingStart, Object.class); - - for (Object span : spans) { - if ((mText.getSpanFlags(span) & Spanned.SPAN_COMPOSING) != 0) { - composingStart = Math.min(composingStart, mText.getSpanStart(span)); - composingEnd = Math.max(composingEnd, mText.getSpanEnd(span)); - } - } - if (DEBUG) { - Log.d(LOGTAG, " range = " + composingStart + "-" + composingEnd); - Log.d(LOGTAG, " selection = " + selStart + "-" + selEnd); - } - if (composingStart >= composingEnd) { - if (selStart >= 0 && selEnd >= 0) { - GeckoAppShell.sendEventToGecko( - GeckoEvent.createIMESelectEvent(selStart, selEnd)); - } else { - GeckoAppShell.sendEventToGecko(GeckoEvent.createIMEEvent( - GeckoEvent.ImeAction.IME_REMOVE_COMPOSITION)); - } - return; - } - - if (selEnd >= composingStart && selEnd <= composingEnd) { - GeckoAppShell.sendEventToGecko(GeckoEvent.createIMERangeEvent( - selEnd - composingStart, selEnd - composingStart, - GeckoEvent.IME_RANGE_CARETPOSITION, 0, 0, false, 0, 0, 0)); - } - int rangeStart = composingStart; - TextPaint tp = new TextPaint(); - TextPaint emptyTp = new TextPaint(); - // set initial foreground color to 0, because we check for tp.getColor() == 0 - // below to decide whether to pass a foreground color to Gecko - emptyTp.setColor(0); - do { - int rangeType, rangeStyles = 0, rangeLineStyle = GeckoEvent.IME_RANGE_LINE_NONE; - boolean rangeBoldLine = false; - int rangeForeColor = 0, rangeBackColor = 0, rangeLineColor = 0; - int rangeEnd = mText.nextSpanTransition(rangeStart, composingEnd, Object.class); - - if (selStart > rangeStart && selStart < rangeEnd) { - rangeEnd = selStart; - } else if (selEnd > rangeStart && selEnd < rangeEnd) { - rangeEnd = selEnd; - } - CharacterStyle[] styleSpans = - mText.getSpans(rangeStart, rangeEnd, CharacterStyle.class); - - if (DEBUG) { - Log.d(LOGTAG, " found " + styleSpans.length + " spans @ " + - rangeStart + "-" + rangeEnd); - } - - if (styleSpans.length == 0) { - rangeType = (selStart == rangeStart && selEnd == rangeEnd) - ? GeckoEvent.IME_RANGE_SELECTEDRAWTEXT - : GeckoEvent.IME_RANGE_RAWINPUT; - } else { - rangeType = (selStart == rangeStart && selEnd == rangeEnd) - ? GeckoEvent.IME_RANGE_SELECTEDCONVERTEDTEXT - : GeckoEvent.IME_RANGE_CONVERTEDTEXT; - tp.set(emptyTp); - for (CharacterStyle span : styleSpans) { - span.updateDrawState(tp); - } - int tpUnderlineColor = 0; - float tpUnderlineThickness = 0.0f; - // These TextPaint fields only exist on Android ICS+ and are not in the SDK - if (Build.VERSION.SDK_INT >= 14) { - tpUnderlineColor = (Integer)getField(tp, "underlineColor", 0); - tpUnderlineThickness = (Float)getField(tp, "underlineThickness", 0.0f); - } - if (tpUnderlineColor != 0) { - rangeStyles |= GeckoEvent.IME_RANGE_UNDERLINE | GeckoEvent.IME_RANGE_LINECOLOR; - rangeLineColor = tpUnderlineColor; - // Approximately translate underline thickness to what Gecko understands - if (tpUnderlineThickness <= 0.5f) { - rangeLineStyle = GeckoEvent.IME_RANGE_LINE_DOTTED; - } else { - rangeLineStyle = GeckoEvent.IME_RANGE_LINE_SOLID; - if (tpUnderlineThickness >= 2.0f) { - rangeBoldLine = true; - } - } - } else if (tp.isUnderlineText()) { - rangeStyles |= GeckoEvent.IME_RANGE_UNDERLINE; - rangeLineStyle = GeckoEvent.IME_RANGE_LINE_SOLID; - } - if (tp.getColor() != 0) { - rangeStyles |= GeckoEvent.IME_RANGE_FORECOLOR; - rangeForeColor = tp.getColor(); - } - if (tp.bgColor != 0) { - rangeStyles |= GeckoEvent.IME_RANGE_BACKCOLOR; - rangeBackColor = tp.bgColor; - } - } - GeckoAppShell.sendEventToGecko(GeckoEvent.createIMERangeEvent( - rangeStart - composingStart, rangeEnd - composingStart, - rangeType, rangeStyles, rangeLineStyle, rangeBoldLine, - rangeForeColor, rangeBackColor, rangeLineColor)); - rangeStart = rangeEnd; - - if (DEBUG) { - Log.d(LOGTAG, " added " + rangeType + - " : " + Integer.toHexString(rangeStyles) + - " : " + Integer.toHexString(rangeForeColor) + - " : " + Integer.toHexString(rangeBackColor)); - } - } while (rangeStart < composingEnd); - - GeckoAppShell.sendEventToGecko(GeckoEvent.createIMECompositionEvent( - composingStart, composingEnd)); - } - - // GeckoEditableClient interface - - @Override - public void sendEvent(final GeckoEvent event) { - if (DEBUG) { - Log.d(LOGTAG, "sendEvent(" + event + ")"); - } - if (!onIcThread()) { - // Events may get dispatched to the main thread; - // reroute to our IC thread instead - mIcRunHandler.post(new Runnable() { - @Override - public void run() { - sendEvent(event); - } - }); - return; - } - /* - We are actually sending two events to Gecko here, - 1. Event from the event parameter (key event, etc.) - 2. Sync event from the mActionQueue.offer call - The first event is a normal GeckoEvent that does not reply back to us, - the second sync event will have a reply, during which we see that there is a pending - event-type action, and update the selection/composition/etc. accordingly. - */ - GeckoAppShell.sendEventToGecko(event); - mActionQueue.offer(new Action(Action.TYPE_EVENT)); - } - - @Override - public Editable getEditable() { - if (!onIcThread()) { - // Android may be holding an old InputConnection; ignore - if (DEBUG) { - Log.i(LOGTAG, "getEditable() called on non-IC thread"); - } - return null; - } - return mProxy; - } - - @Override - public void setUpdateGecko(boolean update) { - if (!onIcThread()) { - // Android may be holding an old InputConnection; ignore - if (DEBUG) { - Log.i(LOGTAG, "setUpdateGecko() called on non-IC thread"); - } - return; - } - if (update) { - icUpdateGecko(false); - } - mUpdateGecko = update; - } - - @Override - public void setSuppressKeyUp(boolean suppress) { - if (DEBUG) { - // only used by key event handler - ThreadUtils.assertOnUiThread(); - } - // Suppress key up event generated as a result of - // translating characters to key events - mSuppressKeyUp = suppress; - } - - @Override - public Handler getInputConnectionHandler() { - // Can be called from either UI thread or IC thread; - // care must be taken to avoid race conditions - return mIcRunHandler; - } - - @Override - public boolean setInputConnectionHandler(Handler handler) { - if (handler == mIcPostHandler) { - return true; - } - if (!mFocused) { - return false; - } - if (DEBUG) { - assertOnIcThread(); - } - // There are three threads at this point: Gecko thread, old IC thread, and new IC - // thread, and we want to safely switch from old IC thread to new IC thread. - // We first send a TYPE_SET_HANDLER action to the Gecko thread; this ensures that - // the Gecko thread is stopped at a known point. At the same time, the old IC - // thread blocks on the action; this ensures that the old IC thread is stopped at - // a known point. Finally, inside the Gecko thread, we post a Runnable to the old - // IC thread; this Runnable switches from old IC thread to new IC thread. We - // switch IC thread on the old IC thread to ensure any pending Runnables on the - // old IC thread are processed before we switch over. Inside the Gecko thread, we - // also post a Runnable to the new IC thread; this Runnable blocks until the - // switch is complete; this ensures that the new IC thread won't accept - // InputConnection calls until after the switch. - mActionQueue.offer(Action.newSetHandler(handler)); - mActionQueue.syncWithGecko(); - return true; - } - - private void geckoSetIcHandler(final Handler newHandler) { - geckoPostToIc(new Runnable() { // posting to old IC thread - @Override - public void run() { - synchronized (newHandler) { - mIcRunHandler = newHandler; - newHandler.notify(); - } - } - }); - - // At this point, all future Runnables should be posted to the new IC thread, but - // we don't switch mIcRunHandler yet because there may be pending Runnables on the - // old IC thread still waiting to run. - mIcPostHandler = newHandler; - - geckoPostToIc(new Runnable() { // posting to new IC thread - @Override - public void run() { - synchronized (newHandler) { - while (mIcRunHandler != newHandler) { - try { - newHandler.wait(); - } catch (InterruptedException e) { - } - } - } - } - }); - } - - // GeckoEditableListener interface - - private void geckoActionReply() { - if (DEBUG) { - // GeckoEditableListener methods should all be called from the Gecko thread - ThreadUtils.assertOnGeckoThread(); - } - final Action action = mActionQueue.peek(); - - if (DEBUG) { - Log.d(LOGTAG, "reply: Action(" + - getConstantName(Action.class, "TYPE_", action.mType) + ")"); - } - switch (action.mType) { - case Action.TYPE_SET_SELECTION: - final int len = mText.length(); - final int curStart = Selection.getSelectionStart(mText); - final int curEnd = Selection.getSelectionEnd(mText); - // start == -1 when the start offset should remain the same - // end == -1 when the end offset should remain the same - final int selStart = Math.min(action.mStart < 0 ? curStart : action.mStart, len); - final int selEnd = Math.min(action.mEnd < 0 ? curEnd : action.mEnd, len); - - if (selStart < action.mStart || selEnd < action.mEnd) { - Log.w(LOGTAG, "IME sync error: selection out of bounds"); - } - Selection.setSelection(mText, selStart, selEnd); - geckoPostToIc(new Runnable() { - @Override - public void run() { - mActionQueue.syncWithGecko(); - final int start = Selection.getSelectionStart(mText); - final int end = Selection.getSelectionEnd(mText); - if (selStart == start && selEnd == end) { - // There has not been another new selection in the mean time that - // made this notification out-of-date - mListener.onSelectionChange(start, end); - } - } - }); - break; - case Action.TYPE_SET_SPAN: - mText.setSpan(action.mSpanObject, action.mStart, action.mEnd, action.mSpanFlags); - break; - case Action.TYPE_SET_HANDLER: - geckoSetIcHandler(action.mHandler); - break; - } - if (action.mShouldUpdate) { - geckoUpdateGecko(false); - } - } - - @Override - public void notifyIME(final int type) { - if (DEBUG) { - // GeckoEditableListener methods should all be called from the Gecko thread - ThreadUtils.assertOnGeckoThread(); - // NOTIFY_IME_REPLY_EVENT is logged separately, inside geckoActionReply() - if (type != NOTIFY_IME_REPLY_EVENT) { - Log.d(LOGTAG, "notifyIME(" + - getConstantName(GeckoEditableListener.class, "NOTIFY_IME_", type) + - ")"); - } - } - if (type == NOTIFY_IME_REPLY_EVENT) { - try { - if (mFocused) { - // When mFocused is false, the reply is for a stale action, - // and we should not do anything - geckoActionReply(); - } else if (DEBUG) { - Log.d(LOGTAG, "discarding stale reply"); - } - } finally { - // Ensure action is always removed from queue - // even if stale action results in exception in geckoActionReply - mActionQueue.poll(); - } - return; - } - geckoPostToIc(new Runnable() { - @Override - public void run() { - if (type == NOTIFY_IME_OF_BLUR) { - mFocused = false; - } else if (type == NOTIFY_IME_OF_FOCUS) { - mFocused = true; - // Unmask events on the Gecko side - mActionQueue.offer(new Action(Action.TYPE_ACKNOWLEDGE_FOCUS)); - } - // Make sure there are no other things going on. If we sent - // GeckoEvent.IME_ACKNOWLEDGE_FOCUS, this line also makes us - // wait for Gecko to update us on the newly focused content - mActionQueue.syncWithGecko(); - mListener.notifyIME(type); - } - }); - } - - @Override - public void notifyIMEContext(final int state, final String typeHint, - final String modeHint, final String actionHint) { - // Because we want to be able to bind GeckoEditable to the newest LayerView instance, - // this can be called from the Java IC thread in addition to the Gecko thread. - if (DEBUG) { - Log.d(LOGTAG, "notifyIMEContext(" + - getConstantName(GeckoEditableListener.class, "IME_STATE_", state) + - ", \"" + typeHint + "\", \"" + modeHint + "\", \"" + actionHint + "\")"); - } - geckoPostToIc(new Runnable() { - @Override - public void run() { - // Make sure there are no other things going on - mActionQueue.syncWithGecko(); - // Set InputConnectionHandler in notifyIMEContext because - // GeckoInputConnection.notifyIMEContext calls restartInput() which will invoke - // InputConnectionHandler.onCreateInputConnection - LayerView v = GeckoAppShell.getLayerView(); - if (v != null) { - mListener = GeckoInputConnection.create(v, GeckoEditable.this); - v.setInputConnectionHandler((InputConnectionHandler)mListener); - mListener.notifyIMEContext(state, typeHint, modeHint, actionHint); - } - } - }); - } - - @Override - public void onSelectionChange(final int start, final int end) { - if (DEBUG) { - // GeckoEditableListener methods should all be called from the Gecko thread - ThreadUtils.assertOnGeckoThread(); - Log.d(LOGTAG, "onSelectionChange(" + start + ", " + end + ")"); - } - if (start < 0 || start > mText.length() || end < 0 || end > mText.length()) { - throw new IllegalArgumentException("invalid selection notification range: " + - start + " to " + end + ", length: " + mText.length()); - } - final int seqnoWhenPosted = ++mGeckoUpdateSeqno; - - /* An event (keypress, etc.) has potentially changed the selection, - synchronize the selection here. There is not a race with the IC thread - because the IC thread should be blocked on the event action */ - if (!mActionQueue.isEmpty() && - mActionQueue.peek().mType == Action.TYPE_EVENT) { - Selection.setSelection(mText, start, end); - return; - } - - geckoPostToIc(new Runnable() { - @Override - public void run() { - mActionQueue.syncWithGecko(); - /* check to see there has not been another action that potentially changed the - selection. If so, we can skip this update because we know there is another - update right after this one that will replace the effect of this update */ - if (mGeckoUpdateSeqno == seqnoWhenPosted) { - /* In this case, Gecko's selection has changed and it's notifying us to change - Java's selection. In the normal case, whenever Java's selection changes, - we go back and set Gecko's selection as well. However, in this case, - since Gecko's selection is already up-to-date, we skip this step. */ - boolean oldUpdateGecko = mUpdateGecko; - mUpdateGecko = false; - Selection.setSelection(mProxy, start, end); - mUpdateGecko = oldUpdateGecko; - } - } - }); - } - - private void geckoReplaceText(int start, int oldEnd, CharSequence newText) { - // Don't use replace() because Gingerbread has a bug where if the replaced text - // has the same spans as the original text, the spans will end up being deleted - mText.delete(start, oldEnd); - mText.insert(start, newText); - } - - @Override - public void onTextChange(final String text, final int start, - final int unboundedOldEnd, final int unboundedNewEnd) { - if (DEBUG) { - // GeckoEditableListener methods should all be called from the Gecko thread - ThreadUtils.assertOnGeckoThread(); - Log.d(LOGTAG, "onTextChange(\"" + text + "\", " + start + ", " + - unboundedOldEnd + ", " + unboundedNewEnd + ")"); - } - if (start < 0 || start > unboundedOldEnd) { - throw new IllegalArgumentException("invalid text notification range: " + - start + " to " + unboundedOldEnd); - } - /* For the "end" parameters, Gecko can pass in a large - number to denote "end of the text". Fix that here */ - final int oldEnd = unboundedOldEnd > mText.length() ? mText.length() : unboundedOldEnd; - // new end should always match text - if (start != 0 && unboundedNewEnd != (start + text.length())) { - throw new IllegalArgumentException("newEnd does not match text: " + - unboundedNewEnd + " vs " + (start + text.length())); - } - final int newEnd = start + text.length(); - - /* Text changes affect the selection as well, and we may not receive another selection - update as a result of selection notification masking on the Gecko side; therefore, - in order to prevent previous stale selection notifications from occurring, we need - to increment the seqno here as well */ - ++mGeckoUpdateSeqno; - - mChangedText.clearSpans(); - mChangedText.replace(0, mChangedText.length(), text); - // Preserve as many spans as possible - TextUtils.copySpansFrom(mText, start, Math.min(oldEnd, newEnd), - Object.class, mChangedText, 0); - - if (!mActionQueue.isEmpty()) { - final Action action = mActionQueue.peek(); - if (action.mType == Action.TYPE_REPLACE_TEXT && - start <= action.mStart && - action.mStart + action.mSequence.length() <= newEnd) { - - // actionNewEnd is the new end of the original replacement action - final int actionNewEnd = action.mStart + action.mSequence.length(); - int selStart = Selection.getSelectionStart(mText); - int selEnd = Selection.getSelectionEnd(mText); - - // Replace old spans with new spans - mChangedText.replace(action.mStart - start, actionNewEnd - start, - action.mSequence); - geckoReplaceText(start, oldEnd, mChangedText); - - // delete/insert above might have moved our selection to somewhere else - // this happens when the Gecko text change covers a larger range than - // the original replacement action. Fix selection here - if (selStart >= start && selStart <= oldEnd) { - selStart = selStart < action.mStart ? selStart : - selStart < action.mEnd ? actionNewEnd : - selStart + actionNewEnd - action.mEnd; - mText.setSpan(Selection.SELECTION_START, selStart, selStart, - Spanned.SPAN_POINT_POINT); - } - if (selEnd >= start && selEnd <= oldEnd) { - selEnd = selEnd < action.mStart ? selEnd : - selEnd < action.mEnd ? actionNewEnd : - selEnd + actionNewEnd - action.mEnd; - mText.setSpan(Selection.SELECTION_END, selEnd, selEnd, - Spanned.SPAN_POINT_POINT); - } - } else { - geckoReplaceText(start, oldEnd, mChangedText); - } - } else { - geckoReplaceText(start, oldEnd, mChangedText); - } - geckoPostToIc(new Runnable() { - @Override - public void run() { - mListener.onTextChange(text, start, oldEnd, newEnd); - } - }); - } - - // InvocationHandler interface - - static String getConstantName(Class<?> cls, String prefix, Object value) { - for (Field fld : cls.getDeclaredFields()) { - try { - if (fld.getName().startsWith(prefix) && - fld.get(null).equals(value)) { - return fld.getName(); - } - } catch (IllegalAccessException e) { - } - } - return String.valueOf(value); - } - - static StringBuilder debugAppend(StringBuilder sb, Object obj) { - if (obj == null) { - sb.append("null"); - } else if (obj instanceof GeckoEditable) { - sb.append("GeckoEditable"); - } else if (Proxy.isProxyClass(obj.getClass())) { - debugAppend(sb, Proxy.getInvocationHandler(obj)); - } else if (obj instanceof CharSequence) { - sb.append("\"").append(obj.toString().replace('\n', '\u21b2')).append("\""); - } else if (obj.getClass().isArray()) { - sb.append(obj.getClass().getComponentType().getSimpleName()).append("[") - .append(java.lang.reflect.Array.getLength(obj)).append("]"); - } else { - sb.append(obj.toString()); - } - return sb; - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) - throws Throwable { - Object target; - final Class<?> methodInterface = method.getDeclaringClass(); - if (DEBUG) { - // Editable methods should all be called from the IC thread - assertOnIcThread(); - } - if (methodInterface == Editable.class || - methodInterface == Appendable.class || - methodInterface == Spannable.class) { - // Method alters the Editable; route calls to our implementation - target = this; - } else { - // Method queries the Editable; must sync with Gecko first - // then call on the inner Editable itself - mActionQueue.syncWithGecko(); - target = mText; - } - Object ret; - try { - ret = method.invoke(target, args); - } catch (InvocationTargetException e) { - // Bug 817386 - // Most likely Gecko has changed the text while GeckoInputConnection is - // trying to access the text. If we pass through the exception here, Fennec - // will crash due to a lack of exception handler. Log the exception and - // return an empty value instead. - if (!(e.getCause() instanceof IndexOutOfBoundsException)) { - // Only handle IndexOutOfBoundsException for now, - // as other exceptions might signal other bugs - throw e; - } - Log.w(LOGTAG, "Exception in GeckoEditable." + method.getName(), e.getCause()); - Class<?> retClass = method.getReturnType(); - if (retClass == Character.TYPE) { - ret = '\0'; - } else if (retClass == Integer.TYPE) { - ret = 0; - } else if (retClass == String.class) { - ret = ""; - } else { - ret = null; - } - } - if (DEBUG) { - StringBuilder log = new StringBuilder(method.getName()); - log.append("("); - for (Object arg : args) { - debugAppend(log, arg).append(", "); - } - if (args.length > 0) { - log.setLength(log.length() - 2); - } - if (method.getReturnType().equals(Void.TYPE)) { - log.append(")"); - } else { - debugAppend(log.append(") = "), ret); - } - Log.d(LOGTAG, log.toString()); - } - return ret; - } - - // Spannable interface - - @Override - public void removeSpan(Object what) { - if (what == Selection.SELECTION_START || - what == Selection.SELECTION_END) { - Log.w(LOGTAG, "selection removed with removeSpan()"); - } - if (mText.getSpanStart(what) >= 0) { // only remove if it's there - // Okay to remove immediately - mText.removeSpan(what); - mActionQueue.offer(new Action(Action.TYPE_REMOVE_SPAN)); - } - } - - @Override - public void setSpan(Object what, int start, int end, int flags) { - if (what == Selection.SELECTION_START) { - if ((flags & Spanned.SPAN_INTERMEDIATE) != 0) { - // We will get the end offset next, just save the start for now - mSavedSelectionStart = start; - } else { - mActionQueue.offer(Action.newSetSelection(start, -1)); - } - } else if (what == Selection.SELECTION_END) { - mActionQueue.offer(Action.newSetSelection(mSavedSelectionStart, end)); - mSavedSelectionStart = -1; - } else { - mActionQueue.offer(Action.newSetSpan(what, start, end, flags)); - } - } - - // Appendable interface - - @Override - public Editable append(CharSequence text) { - return replace(mProxy.length(), mProxy.length(), text, 0, text.length()); - } - - @Override - public Editable append(CharSequence text, int start, int end) { - return replace(mProxy.length(), mProxy.length(), text, start, end); - } - - @Override - public Editable append(char text) { - return replace(mProxy.length(), mProxy.length(), String.valueOf(text), 0, 1); - } - - // Editable interface - - @Override - public InputFilter[] getFilters() { - return mFilters; - } - - @Override - public void setFilters(InputFilter[] filters) { - mFilters = filters; - } - - @Override - public void clearSpans() { - /* XXX this clears the selection spans too, - but there is no way to clear the corresponding selection in Gecko */ - Log.w(LOGTAG, "selection cleared with clearSpans()"); - mText.clearSpans(); - } - - @Override - public Editable replace(int st, int en, - CharSequence source, int start, int end) { - - CharSequence text = source; - if (start < 0 || start > end || end > text.length()) { - throw new IllegalArgumentException("invalid replace offsets: " + - start + " to " + end + ", length: " + text.length()); - } - if (start != 0 || end != text.length()) { - text = text.subSequence(start, end); - } - if (mFilters != null) { - // Filter text before sending the request to Gecko - for (int i = 0; i < mFilters.length; ++i) { - final CharSequence cs = mFilters[i].filter( - text, 0, text.length(), mProxy, st, en); - if (cs != null) { - text = cs; - } - } - } - if (text == source) { - // Always create a copy - text = new SpannableString(source); - } - mActionQueue.offer(Action.newReplaceText(text, - Math.min(st, en), Math.max(st, en))); - return mProxy; - } - - @Override - public void clear() { - replace(0, mProxy.length(), "", 0, 0); - } - - @Override - public Editable delete(int st, int en) { - return replace(st, en, "", 0, 0); - } - - @Override - public Editable insert(int where, CharSequence text, - int start, int end) { - return replace(where, where, text, start, end); - } - - @Override - public Editable insert(int where, CharSequence text) { - return replace(where, where, text, 0, text.length()); - } - - @Override - public Editable replace(int st, int en, CharSequence text) { - return replace(st, en, text, 0, text.length()); - } - - /* GetChars interface */ - - @Override - public void getChars(int start, int end, char[] dest, int destoff) { - /* overridden Editable interface methods in GeckoEditable must not be called directly - outside of GeckoEditable. Instead, the call must go through mProxy, which ensures - that Java is properly synchronized with Gecko */ - throw new UnsupportedOperationException("method must be called through mProxy"); - } - - /* Spanned interface */ - - @Override - public int getSpanEnd(Object tag) { - throw new UnsupportedOperationException("method must be called through mProxy"); - } - - @Override - public int getSpanFlags(Object tag) { - throw new UnsupportedOperationException("method must be called through mProxy"); - } - - @Override - public int getSpanStart(Object tag) { - throw new UnsupportedOperationException("method must be called through mProxy"); - } - - @Override - public <T> T[] getSpans(int start, int end, Class<T> type) { - throw new UnsupportedOperationException("method must be called through mProxy"); - } - - @Override - @SuppressWarnings("rawtypes") // nextSpanTransition uses raw Class in its Android declaration - public int nextSpanTransition(int start, int limit, Class type) { - throw new UnsupportedOperationException("method must be called through mProxy"); - } - - /* CharSequence interface */ - - @Override - public char charAt(int index) { - throw new UnsupportedOperationException("method must be called through mProxy"); - } - - @Override - public int length() { - throw new UnsupportedOperationException("method must be called through mProxy"); - } - - @Override - public CharSequence subSequence(int start, int end) { - throw new UnsupportedOperationException("method must be called through mProxy"); - } - - @Override - public String toString() { - throw new UnsupportedOperationException("method must be called through mProxy"); - } -} - diff --git a/mobile/android/base/GeckoEvent.java b/mobile/android/base/GeckoEvent.java deleted file mode 100644 index b503ddbd7..000000000 --- a/mobile/android/base/GeckoEvent.java +++ /dev/null @@ -1,694 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import org.mozilla.gecko.gfx.DisplayPortMetrics; -import org.mozilla.gecko.gfx.ImmutableViewportMetrics; - -import android.content.res.Resources; -import android.graphics.Point; -import android.graphics.PointF; -import android.graphics.Rect; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorManager; -import android.location.Address; -import android.location.Location; -import android.os.Build; -import android.os.SystemClock; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.KeyEvent; -import android.view.MotionEvent; - -import java.nio.ByteBuffer; - -/* We're not allowed to hold on to most events given to us - * so we save the parts of the events we want to use in GeckoEvent. - * Fields have different meanings depending on the event type. - */ - -/* This class is referenced by Robocop via reflection; use care when - * modifying the signature. - */ -public class GeckoEvent { - private static final String LOGTAG = "GeckoEvent"; - - // Make sure to keep these values in sync with the enum in - // AndroidGeckoEvent in widget/android/AndroidJavaWrapper.h - private enum NativeGeckoEvent { - NATIVE_POKE(0), - KEY_EVENT(1), - MOTION_EVENT(2), - SENSOR_EVENT(3), - LOCATION_EVENT(5), - IME_EVENT(6), - DRAW(7), - SIZE_CHANGED(8), - APP_BACKGROUNDING(9), - APP_FOREGROUNDING(10), - LOAD_URI(12), - NOOP(15), - BROADCAST(19), - VIEWPORT(20), - VISITED(21), - NETWORK_CHANGED(22), - THUMBNAIL(25), - SCREENORIENTATION_CHANGED(27), - COMPOSITOR_CREATE(28), - COMPOSITOR_PAUSE(29), - COMPOSITOR_RESUME(30), - NATIVE_GESTURE_EVENT(31), - IME_KEY_EVENT(32), - CALL_OBSERVER(33), - REMOVE_OBSERVER(34), - LOW_MEMORY(35), - NETWORK_LINK_CHANGE(36); - - public final int value; - - private NativeGeckoEvent(int value) { - this.value = value; - } - } - - /** - * The DomKeyLocation enum encapsulates the DOM KeyboardEvent's constants. - * @see https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent#Key_location_constants - */ - public enum DomKeyLocation { - DOM_KEY_LOCATION_STANDARD(0), - DOM_KEY_LOCATION_LEFT(1), - DOM_KEY_LOCATION_RIGHT(2), - DOM_KEY_LOCATION_NUMPAD(3), - DOM_KEY_LOCATION_MOBILE(4), - DOM_KEY_LOCATION_JOYSTICK(5); - - public final int value; - - private DomKeyLocation(int value) { - this.value = value; - } - } - - // Encapsulation of common IME actions. - public enum ImeAction { - IME_SYNCHRONIZE(0), - IME_REPLACE_TEXT(1), - IME_SET_SELECTION(2), - IME_ADD_COMPOSITION_RANGE(3), - IME_UPDATE_COMPOSITION(4), - IME_REMOVE_COMPOSITION(5), - IME_ACKNOWLEDGE_FOCUS(6); - - public final int value; - - private ImeAction(int value) { - this.value = value; - } - } - - public static final int IME_RANGE_CARETPOSITION = 1; - public static final int IME_RANGE_RAWINPUT = 2; - public static final int IME_RANGE_SELECTEDRAWTEXT = 3; - public static final int IME_RANGE_CONVERTEDTEXT = 4; - public static final int IME_RANGE_SELECTEDCONVERTEDTEXT = 5; - - public static final int IME_RANGE_LINE_NONE = 0; - public static final int IME_RANGE_LINE_DOTTED = 1; - public static final int IME_RANGE_LINE_DASHED = 2; - public static final int IME_RANGE_LINE_SOLID = 3; - public static final int IME_RANGE_LINE_DOUBLE = 4; - public static final int IME_RANGE_LINE_WAVY = 5; - - public static final int IME_RANGE_UNDERLINE = 1; - public static final int IME_RANGE_FORECOLOR = 2; - public static final int IME_RANGE_BACKCOLOR = 4; - public static final int IME_RANGE_LINECOLOR = 8; - - public static final int ACTION_MAGNIFY_START = 11; - public static final int ACTION_MAGNIFY = 12; - public static final int ACTION_MAGNIFY_END = 13; - - private final int mType; - private int mAction; - private boolean mAckNeeded; - private long mTime; - private Point[] mPoints; - private int[] mPointIndicies; - private int mPointerIndex; // index of the point that has changed - private float[] mOrientations; - private float[] mPressures; - private Point[] mPointRadii; - private Rect mRect; - private double mX; - private double mY; - private double mZ; - - private int mMetaState; - private int mFlags; - private int mKeyCode; - private int mUnicodeChar; - private int mBaseUnicodeChar; // mUnicodeChar without meta states applied - private int mRepeatCount; - private int mCount; - private int mStart; - private int mEnd; - private String mCharacters; - private String mCharactersExtra; - private String mData; - private int mRangeType; - private int mRangeStyles; - private int mRangeLineStyle; - private boolean mRangeBoldLine; - private int mRangeForeColor; - private int mRangeBackColor; - private int mRangeLineColor; - private Location mLocation; - private Address mAddress; - private DomKeyLocation mDomKeyLocation; - - private double mBandwidth; - private boolean mCanBeMetered; - - private int mNativeWindow; - - private short mScreenOrientation; - - private ByteBuffer mBuffer; - - private int mWidth; - private int mHeight; - - private GeckoEvent(NativeGeckoEvent event) { - mType = event.value; - } - - public static GeckoEvent createAppBackgroundingEvent() { - return new GeckoEvent(NativeGeckoEvent.APP_BACKGROUNDING); - } - - public static GeckoEvent createAppForegroundingEvent() { - return new GeckoEvent(NativeGeckoEvent.APP_FOREGROUNDING); - } - - public static GeckoEvent createNoOpEvent() { - return new GeckoEvent(NativeGeckoEvent.NOOP); - } - - public static GeckoEvent createKeyEvent(KeyEvent k, int metaState) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.KEY_EVENT); - event.initKeyEvent(k, metaState); - return event; - } - - public static GeckoEvent createCompositorCreateEvent(int width, int height) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.COMPOSITOR_CREATE); - event.mWidth = width; - event.mHeight = height; - return event; - } - - public static GeckoEvent createCompositorPauseEvent() { - return new GeckoEvent(NativeGeckoEvent.COMPOSITOR_PAUSE); - } - - public static GeckoEvent createCompositorResumeEvent() { - return new GeckoEvent(NativeGeckoEvent.COMPOSITOR_RESUME); - } - - private void initKeyEvent(KeyEvent k, int metaState) { - mAction = k.getAction(); - mTime = k.getEventTime(); - // Normally we expect k.getMetaState() to reflect the current meta-state; however, - // some software-generated key events may not have k.getMetaState() set, e.g. key - // events from Swype. Therefore, it's necessary to combine the key's meta-states - // with the meta-states that we keep separately in KeyListener - mMetaState = k.getMetaState() | metaState; - mFlags = k.getFlags(); - mKeyCode = k.getKeyCode(); - mUnicodeChar = k.getUnicodeChar(mMetaState); - // e.g. for Ctrl+A, Android returns 0 for mUnicodeChar, - // but Gecko expects 'a', so we return that in mBaseUnicodeChar - mBaseUnicodeChar = k.getUnicodeChar(0); - mRepeatCount = k.getRepeatCount(); - mCharacters = k.getCharacters(); - mDomKeyLocation = isJoystickButton(mKeyCode) ? DomKeyLocation.DOM_KEY_LOCATION_JOYSTICK - : DomKeyLocation.DOM_KEY_LOCATION_MOBILE; - } - - /** - * This method tests if a key is one of the described in: - * https://bugzilla.mozilla.org/show_bug.cgi?id=756504#c0 - * @param keyCode int with the key code (Android key constant from KeyEvent) - * @return true if the key is one of the listed above, false otherwise. - */ - private static boolean isJoystickButton(int keyCode) { - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_CENTER: - case KeyEvent.KEYCODE_DPAD_LEFT: - case KeyEvent.KEYCODE_DPAD_RIGHT: - case KeyEvent.KEYCODE_DPAD_DOWN: - case KeyEvent.KEYCODE_DPAD_UP: - return true; - default: - if (Build.VERSION.SDK_INT >= 12) { - return KeyEvent.isGamepadButton(keyCode); - } - return GeckoEvent.isGamepadButton(keyCode); - } - } - - /** - * This method is a replacement for the the KeyEvent.isGamepadButton method to be - * compatible with Build.VERSION.SDK_INT < 12. This is an implementantion of the - * same method isGamepadButton available after SDK 12. - * @param keyCode int with the key code (Android key constant from KeyEvent). - * @return True if the keycode is a gamepad button, such as {@link #KEYCODE_BUTTON_A}. - */ - private static boolean isGamepadButton(int keyCode) { - switch (keyCode) { - case KeyEvent.KEYCODE_BUTTON_A: - case KeyEvent.KEYCODE_BUTTON_B: - case KeyEvent.KEYCODE_BUTTON_C: - case KeyEvent.KEYCODE_BUTTON_X: - case KeyEvent.KEYCODE_BUTTON_Y: - case KeyEvent.KEYCODE_BUTTON_Z: - case KeyEvent.KEYCODE_BUTTON_L1: - case KeyEvent.KEYCODE_BUTTON_R1: - case KeyEvent.KEYCODE_BUTTON_L2: - case KeyEvent.KEYCODE_BUTTON_R2: - case KeyEvent.KEYCODE_BUTTON_THUMBL: - case KeyEvent.KEYCODE_BUTTON_THUMBR: - case KeyEvent.KEYCODE_BUTTON_START: - case KeyEvent.KEYCODE_BUTTON_SELECT: - case KeyEvent.KEYCODE_BUTTON_MODE: - case KeyEvent.KEYCODE_BUTTON_1: - case KeyEvent.KEYCODE_BUTTON_2: - case KeyEvent.KEYCODE_BUTTON_3: - case KeyEvent.KEYCODE_BUTTON_4: - case KeyEvent.KEYCODE_BUTTON_5: - case KeyEvent.KEYCODE_BUTTON_6: - case KeyEvent.KEYCODE_BUTTON_7: - case KeyEvent.KEYCODE_BUTTON_8: - case KeyEvent.KEYCODE_BUTTON_9: - case KeyEvent.KEYCODE_BUTTON_10: - case KeyEvent.KEYCODE_BUTTON_11: - case KeyEvent.KEYCODE_BUTTON_12: - case KeyEvent.KEYCODE_BUTTON_13: - case KeyEvent.KEYCODE_BUTTON_14: - case KeyEvent.KEYCODE_BUTTON_15: - case KeyEvent.KEYCODE_BUTTON_16: - return true; - default: - return false; - } - } - - public static GeckoEvent createNativeGestureEvent(int action, PointF pt, double size) { - try { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.NATIVE_GESTURE_EVENT); - event.mAction = action; - event.mCount = 1; - event.mPoints = new Point[1]; - - PointF geckoPoint = new PointF(pt.x, pt.y); - geckoPoint = GeckoAppShell.getLayerView().convertViewPointToLayerPoint(geckoPoint); - - if (geckoPoint == null) { - // This could happen if Gecko isn't ready yet. - return null; - } - - event.mPoints[0] = new Point(Math.round(geckoPoint.x), Math.round(geckoPoint.y)); - - event.mX = size; - event.mTime = System.currentTimeMillis(); - return event; - } catch (Exception e) { - // This can happen if Gecko isn't ready yet - return null; - } - } - - /** - * Creates a GeckoEvent that contains the data from the MotionEvent. - * The keepInViewCoordinates parameter can be set to false to convert from the Java - * coordinate system (device pixels relative to the LayerView) to a coordinate system - * relative to gecko's coordinate system (CSS pixels relative to gecko scroll position). - */ - public static GeckoEvent createMotionEvent(MotionEvent m, boolean keepInViewCoordinates) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.MOTION_EVENT); - event.initMotionEvent(m, keepInViewCoordinates); - return event; - } - - private void initMotionEvent(MotionEvent m, boolean keepInViewCoordinates) { - mAction = m.getActionMasked(); - mTime = (System.currentTimeMillis() - SystemClock.elapsedRealtime()) + m.getEventTime(); - mMetaState = m.getMetaState(); - - switch (mAction) { - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_POINTER_UP: - case MotionEvent.ACTION_POINTER_DOWN: - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_MOVE: - case MotionEvent.ACTION_HOVER_ENTER: - case MotionEvent.ACTION_HOVER_MOVE: - case MotionEvent.ACTION_HOVER_EXIT: { - mCount = m.getPointerCount(); - mPoints = new Point[mCount]; - mPointIndicies = new int[mCount]; - mOrientations = new float[mCount]; - mPressures = new float[mCount]; - mPointRadii = new Point[mCount]; - mPointerIndex = m.getActionIndex(); - for (int i = 0; i < mCount; i++) { - addMotionPoint(i, i, m, keepInViewCoordinates); - } - break; - } - default: { - mCount = 0; - mPointerIndex = -1; - mPoints = new Point[mCount]; - mPointIndicies = new int[mCount]; - mOrientations = new float[mCount]; - mPressures = new float[mCount]; - mPointRadii = new Point[mCount]; - } - } - } - - private void addMotionPoint(int index, int eventIndex, MotionEvent event, boolean keepInViewCoordinates) { - try { - PointF geckoPoint = new PointF(event.getX(eventIndex), event.getY(eventIndex)); - if (!keepInViewCoordinates) { - geckoPoint = GeckoAppShell.getLayerView().convertViewPointToLayerPoint(geckoPoint); - } - - mPoints[index] = new Point(Math.round(geckoPoint.x), Math.round(geckoPoint.y)); - mPointIndicies[index] = event.getPointerId(eventIndex); - // getToolMajor, getToolMinor and getOrientation are API Level 9 features - if (Build.VERSION.SDK_INT >= 9) { - double radians = event.getOrientation(eventIndex); - mOrientations[index] = (float) Math.toDegrees(radians); - // w3c touchevents spec does not allow orientations == 90 - // this shifts it to -90, which will be shifted to zero below - if (mOrientations[index] == 90) - mOrientations[index] = -90; - - // w3c touchevent radius are given by an orientation between 0 and 90 - // the radius is found by removing the orientation and measuring the x and y - // radius of the resulting ellipse - // for android orientations >= 0 and < 90, the major axis should correspond to - // just reporting the y radius as the major one, and x as minor - // however, for a radius < 0, we have to shift the orientation by adding 90, and - // reverse which radius is major and minor - if (mOrientations[index] < 0) { - mOrientations[index] += 90; - mPointRadii[index] = new Point((int)event.getToolMajor(eventIndex)/2, - (int)event.getToolMinor(eventIndex)/2); - } else { - mPointRadii[index] = new Point((int)event.getToolMinor(eventIndex)/2, - (int)event.getToolMajor(eventIndex)/2); - } - } else { - float size = event.getSize(eventIndex); - Resources resources = GeckoAppShell.getContext().getResources(); - DisplayMetrics displaymetrics = resources.getDisplayMetrics(); - size = size*Math.min(displaymetrics.heightPixels, displaymetrics.widthPixels); - mPointRadii[index] = new Point((int)size,(int)size); - mOrientations[index] = 0; - } - mPressures[index] = event.getPressure(eventIndex); - } catch (Exception ex) { - Log.e(LOGTAG, "Error creating motion point " + index, ex); - mPointRadii[index] = new Point(0, 0); - mPoints[index] = new Point(0, 0); - } - } - - private static int HalSensorAccuracyFor(int androidAccuracy) { - switch (androidAccuracy) { - case SensorManager.SENSOR_STATUS_UNRELIABLE: - return GeckoHalDefines.SENSOR_ACCURACY_UNRELIABLE; - case SensorManager.SENSOR_STATUS_ACCURACY_LOW: - return GeckoHalDefines.SENSOR_ACCURACY_LOW; - case SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM: - return GeckoHalDefines.SENSOR_ACCURACY_MED; - case SensorManager.SENSOR_STATUS_ACCURACY_HIGH: - return GeckoHalDefines.SENSOR_ACCURACY_HIGH; - } - return GeckoHalDefines.SENSOR_ACCURACY_UNKNOWN; - } - - public static GeckoEvent createSensorEvent(SensorEvent s) { - int sensor_type = s.sensor.getType(); - GeckoEvent event = null; - - switch(sensor_type) { - - case Sensor.TYPE_ACCELEROMETER: - event = new GeckoEvent(NativeGeckoEvent.SENSOR_EVENT); - event.mFlags = GeckoHalDefines.SENSOR_ACCELERATION; - event.mMetaState = HalSensorAccuracyFor(s.accuracy); - event.mX = s.values[0]; - event.mY = s.values[1]; - event.mZ = s.values[2]; - break; - - case 10 /* Requires API Level 9, so just use the raw value - Sensor.TYPE_LINEAR_ACCELEROMETER*/ : - event = new GeckoEvent(NativeGeckoEvent.SENSOR_EVENT); - event.mFlags = GeckoHalDefines.SENSOR_LINEAR_ACCELERATION; - event.mMetaState = HalSensorAccuracyFor(s.accuracy); - event.mX = s.values[0]; - event.mY = s.values[1]; - event.mZ = s.values[2]; - break; - - case Sensor.TYPE_ORIENTATION: - event = new GeckoEvent(NativeGeckoEvent.SENSOR_EVENT); - event.mFlags = GeckoHalDefines.SENSOR_ORIENTATION; - event.mMetaState = HalSensorAccuracyFor(s.accuracy); - event.mX = s.values[0]; - event.mY = s.values[1]; - event.mZ = s.values[2]; - break; - - case Sensor.TYPE_GYROSCOPE: - event = new GeckoEvent(NativeGeckoEvent.SENSOR_EVENT); - event.mFlags = GeckoHalDefines.SENSOR_GYROSCOPE; - event.mMetaState = HalSensorAccuracyFor(s.accuracy); - event.mX = Math.toDegrees(s.values[0]); - event.mY = Math.toDegrees(s.values[1]); - event.mZ = Math.toDegrees(s.values[2]); - break; - - case Sensor.TYPE_PROXIMITY: - event = new GeckoEvent(NativeGeckoEvent.SENSOR_EVENT); - event.mFlags = GeckoHalDefines.SENSOR_PROXIMITY; - event.mMetaState = HalSensorAccuracyFor(s.accuracy); - event.mX = s.values[0]; - event.mY = 0; - event.mZ = s.sensor.getMaximumRange(); - break; - - case Sensor.TYPE_LIGHT: - event = new GeckoEvent(NativeGeckoEvent.SENSOR_EVENT); - event.mFlags = GeckoHalDefines.SENSOR_LIGHT; - event.mMetaState = HalSensorAccuracyFor(s.accuracy); - event.mX = s.values[0]; - break; - } - return event; - } - - public static GeckoEvent createLocationEvent(Location l) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.LOCATION_EVENT); - event.mLocation = l; - return event; - } - - public static GeckoEvent createIMEEvent(ImeAction action) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.IME_EVENT); - event.mAction = action.value; - return event; - } - - public static GeckoEvent createIMEKeyEvent(KeyEvent k) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.IME_KEY_EVENT); - event.initKeyEvent(k, 0); - return event; - } - - public static GeckoEvent createIMEReplaceEvent(int start, int end, - String text) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.IME_EVENT); - event.mAction = ImeAction.IME_REPLACE_TEXT.value; - event.mStart = start; - event.mEnd = end; - event.mCharacters = text; - return event; - } - - public static GeckoEvent createIMESelectEvent(int start, int end) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.IME_EVENT); - event.mAction = ImeAction.IME_SET_SELECTION.value; - event.mStart = start; - event.mEnd = end; - return event; - } - - public static GeckoEvent createIMECompositionEvent(int start, int end) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.IME_EVENT); - event.mAction = ImeAction.IME_UPDATE_COMPOSITION.value; - event.mStart = start; - event.mEnd = end; - return event; - } - - public static GeckoEvent createIMERangeEvent(int start, - int end, int rangeType, - int rangeStyles, - int rangeLineStyle, - boolean rangeBoldLine, - int rangeForeColor, - int rangeBackColor, - int rangeLineColor) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.IME_EVENT); - event.mAction = ImeAction.IME_ADD_COMPOSITION_RANGE.value; - event.mStart = start; - event.mEnd = end; - event.mRangeType = rangeType; - event.mRangeStyles = rangeStyles; - event.mRangeLineStyle = rangeLineStyle; - event.mRangeBoldLine = rangeBoldLine; - event.mRangeForeColor = rangeForeColor; - event.mRangeBackColor = rangeBackColor; - event.mRangeLineColor = rangeLineColor; - return event; - } - - public static GeckoEvent createDrawEvent(Rect rect) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.DRAW); - event.mRect = rect; - return event; - } - - public static GeckoEvent createSizeChangedEvent(int w, int h, int screenw, int screenh) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.SIZE_CHANGED); - event.mPoints = new Point[2]; - event.mPoints[0] = new Point(w, h); - event.mPoints[1] = new Point(screenw, screenh); - return event; - } - - public static GeckoEvent createBroadcastEvent(String subject, String data) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.BROADCAST); - event.mCharacters = subject; - event.mCharactersExtra = data; - return event; - } - - public static GeckoEvent createViewportEvent(ImmutableViewportMetrics metrics, DisplayPortMetrics displayPort) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.VIEWPORT); - event.mCharacters = "Viewport:Change"; - StringBuffer sb = new StringBuffer(256); - sb.append("{ \"x\" : ").append(metrics.viewportRectLeft) - .append(", \"y\" : ").append(metrics.viewportRectTop) - .append(", \"zoom\" : ").append(metrics.zoomFactor) - .append(", \"fixedMarginLeft\" : ").append(metrics.marginLeft) - .append(", \"fixedMarginTop\" : ").append(metrics.marginTop) - .append(", \"fixedMarginRight\" : ").append(metrics.marginRight) - .append(", \"fixedMarginBottom\" : ").append(metrics.marginBottom) - .append(", \"displayPort\" :").append(displayPort.toJSON()) - .append('}'); - event.mCharactersExtra = sb.toString(); - return event; - } - - public static GeckoEvent createURILoadEvent(String uri) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.LOAD_URI); - event.mCharacters = uri; - event.mCharactersExtra = ""; - return event; - } - - public static GeckoEvent createWebappLoadEvent(String uri) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.LOAD_URI); - event.mCharacters = uri; - event.mCharactersExtra = "-webapp"; - return event; - } - - public static GeckoEvent createBookmarkLoadEvent(String uri) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.LOAD_URI); - event.mCharacters = uri; - event.mCharactersExtra = "-bookmark"; - return event; - } - - public static GeckoEvent createVisitedEvent(String data) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.VISITED); - event.mCharacters = data; - return event; - } - - public static GeckoEvent createNetworkEvent(double bandwidth, boolean canBeMetered) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.NETWORK_CHANGED); - event.mBandwidth = bandwidth; - event.mCanBeMetered = canBeMetered; - return event; - } - - public static GeckoEvent createThumbnailEvent(int tabId, int bufw, int bufh, ByteBuffer buffer) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.THUMBNAIL); - event.mPoints = new Point[1]; - event.mPoints[0] = new Point(bufw, bufh); - event.mMetaState = tabId; - event.mBuffer = buffer; - return event; - } - - public static GeckoEvent createScreenOrientationEvent(short aScreenOrientation) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.SCREENORIENTATION_CHANGED); - event.mScreenOrientation = aScreenOrientation; - return event; - } - - public static GeckoEvent createCallObserverEvent(String observerKey, String topic, String data) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.CALL_OBSERVER); - event.mCharacters = observerKey; - event.mCharactersExtra = topic; - event.mData = data; - return event; - } - - public static GeckoEvent createRemoveObserverEvent(String observerKey) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.REMOVE_OBSERVER); - event.mCharacters = observerKey; - return event; - } - - public static GeckoEvent createLowMemoryEvent(int level) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.LOW_MEMORY); - event.mMetaState = level; - return event; - } - - public static GeckoEvent createNetworkLinkChangeEvent(String status) { - GeckoEvent event = new GeckoEvent(NativeGeckoEvent.NETWORK_LINK_CHANGE); - event.mCharacters = status; - return event; - } - - public void setAckNeeded(boolean ackNeeded) { - mAckNeeded = ackNeeded; - } -} diff --git a/mobile/android/base/GeckoHalDefines.java b/mobile/android/base/GeckoHalDefines.java deleted file mode 100644 index 3ebe47028..000000000 --- a/mobile/android/base/GeckoHalDefines.java +++ /dev/null @@ -1,25 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -public class GeckoHalDefines -{ - /* - * Keep these values consistent with |SensorType| in Hal.h - */ - public static final int SENSOR_ORIENTATION = 0; - public static final int SENSOR_ACCELERATION = 1; - public static final int SENSOR_PROXIMITY = 2; - public static final int SENSOR_LINEAR_ACCELERATION = 3; - public static final int SENSOR_GYROSCOPE = 4; - public static final int SENSOR_LIGHT = 5; - - public static final int SENSOR_ACCURACY_UNKNOWN = -1; - public static final int SENSOR_ACCURACY_UNRELIABLE = 0; - public static final int SENSOR_ACCURACY_LOW = 1; - public static final int SENSOR_ACCURACY_MED = 2; - public static final int SENSOR_ACCURACY_HIGH = 3; -}; diff --git a/mobile/android/base/GeckoInputConnection.java b/mobile/android/base/GeckoInputConnection.java deleted file mode 100644 index fcc45f8da..000000000 --- a/mobile/android/base/GeckoInputConnection.java +++ /dev/null @@ -1,1032 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import org.mozilla.gecko.gfx.InputConnectionHandler; -import org.mozilla.gecko.util.Clipboard; -import org.mozilla.gecko.util.GamepadUtils; -import org.mozilla.gecko.util.ThreadUtils; - -import android.R; -import android.content.Context; -import android.os.Build; -import android.os.Handler; -import android.os.Looper; -import android.os.SystemClock; -import android.text.Editable; -import android.text.InputType; -import android.text.Selection; -import android.text.SpannableString; -import android.text.method.KeyListener; -import android.text.method.TextKeyListener; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.KeyEvent; -import android.view.View; -import android.view.inputmethod.BaseInputConnection; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.ExtractedText; -import android.view.inputmethod.ExtractedTextRequest; -import android.view.inputmethod.InputConnection; -import android.view.inputmethod.InputMethodManager; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.concurrent.SynchronousQueue; - -class GeckoInputConnection - extends BaseInputConnection - implements InputConnectionHandler, GeckoEditableListener { - - private static final boolean DEBUG = false; - protected static final String LOGTAG = "GeckoInputConnection"; - - private static final int INLINE_IME_MIN_DISPLAY_SIZE = 480; - - private static Handler sBackgroundHandler; - - private static class InputThreadUtils { - // We only want one UI editable around to keep synchronization simple, - // so we make InputThreadUtils a singleton - public static final InputThreadUtils sInstance = new InputThreadUtils(); - - private Editable mUiEditable; - private Object mUiEditableReturn; - private Exception mUiEditableException; - private final SynchronousQueue<Runnable> mIcRunnableSync; - private final Runnable mIcSignalRunnable; - - private InputThreadUtils() { - mIcRunnableSync = new SynchronousQueue<Runnable>(); - mIcSignalRunnable = new Runnable() { - @Override public void run() { - } - }; - } - - private void runOnIcThread(Handler icHandler, final Runnable runnable) { - if (DEBUG) { - ThreadUtils.assertOnUiThread(); - Log.d(LOGTAG, "runOnIcThread() on thread " + - icHandler.getLooper().getThread().getName()); - } - Runnable runner = new Runnable() { - @Override public void run() { - try { - Runnable queuedRunnable = mIcRunnableSync.take(); - if (DEBUG && queuedRunnable != runnable) { - throw new IllegalThreadStateException("sync error"); - } - queuedRunnable.run(); - } catch (InterruptedException e) { - } - } - }; - try { - // if we are not inside waitForUiThread(), runner will call the runnable - icHandler.post(runner); - // runnable will be called by either runner from above or waitForUiThread() - mIcRunnableSync.put(runnable); - } catch (InterruptedException e) { - } finally { - // if waitForUiThread() already called runnable, runner should not call it again - icHandler.removeCallbacks(runner); - } - } - - public void endWaitForUiThread() { - if (DEBUG) { - ThreadUtils.assertOnUiThread(); - Log.d(LOGTAG, "endWaitForUiThread()"); - } - try { - mIcRunnableSync.put(mIcSignalRunnable); - } catch (InterruptedException e) { - } - } - - public void waitForUiThread(Handler icHandler) { - if (DEBUG) { - ThreadUtils.assertOnThread(icHandler.getLooper().getThread()); - Log.d(LOGTAG, "waitForUiThread() blocking on thread " + - icHandler.getLooper().getThread().getName()); - } - try { - Runnable runnable = null; - do { - runnable = mIcRunnableSync.take(); - runnable.run(); - } while (runnable != mIcSignalRunnable); - } catch (InterruptedException e) { - } - } - - public void runOnIcThread(final Handler uiHandler, - final GeckoEditableClient client, - final Runnable runnable) { - final Handler icHandler = client.getInputConnectionHandler(); - if (icHandler.getLooper() == uiHandler.getLooper()) { - // IC thread is UI thread; safe to run directly - runnable.run(); - return; - } - runOnIcThread(icHandler, runnable); - } - - public Editable getEditableForUiThread(final Handler uiHandler, - final GeckoEditableClient client) { - if (DEBUG) { - ThreadUtils.assertOnThread(uiHandler.getLooper().getThread()); - } - final Handler icHandler = client.getInputConnectionHandler(); - if (icHandler.getLooper() == uiHandler.getLooper()) { - // IC thread is UI thread; safe to use Editable directly - return client.getEditable(); - } - // IC thread is not UI thread; we need to return a proxy Editable in order - // to safely use the Editable from the UI thread - if (mUiEditable != null) { - return mUiEditable; - } - final InvocationHandler invokeEditable = new InvocationHandler() { - @Override public Object invoke(final Object proxy, - final Method method, - final Object[] args) throws Throwable { - if (DEBUG) { - ThreadUtils.assertOnThread(uiHandler.getLooper().getThread()); - Log.d(LOGTAG, "UiEditable." + method.getName() + "() blocking"); - } - synchronized (icHandler) { - // Now we are on UI thread - mUiEditableReturn = null; - mUiEditableException = null; - // Post a Runnable that calls the real Editable and saves any - // result/exception. Then wait on the Runnable to finish - runOnIcThread(icHandler, new Runnable() { - @Override public void run() { - synchronized (icHandler) { - try { - mUiEditableReturn = method.invoke( - client.getEditable(), args); - } catch (Exception e) { - mUiEditableException = e; - } - if (DEBUG) { - Log.d(LOGTAG, "UiEditable." + method.getName() + - "() returning"); - } - icHandler.notify(); - } - } - }); - // let InterruptedException propagate - icHandler.wait(); - if (mUiEditableException != null) { - throw mUiEditableException; - } - return mUiEditableReturn; - } - } - }; - mUiEditable = (Editable) Proxy.newProxyInstance(Editable.class.getClassLoader(), - new Class<?>[] { Editable.class }, invokeEditable); - return mUiEditable; - } - } - - // Managed only by notifyIMEContext; see comments in notifyIMEContext - private int mIMEState; - private String mIMETypeHint = ""; - private String mIMEModeHint = ""; - private String mIMEActionHint = ""; - - private String mCurrentInputMethod = ""; - - private final GeckoEditableClient mEditableClient; - protected int mBatchEditCount; - private ExtractedTextRequest mUpdateRequest; - private final ExtractedText mUpdateExtract = new ExtractedText(); - private boolean mBatchSelectionChanged; - private boolean mBatchTextChanged; - private long mLastRestartInputTime; - private final InputConnection mKeyInputConnection; - - public static GeckoEditableListener create(View targetView, - GeckoEditableClient editable) { - if (DEBUG) - return DebugGeckoInputConnection.create(targetView, editable); - else - return new GeckoInputConnection(targetView, editable); - } - - protected GeckoInputConnection(View targetView, - GeckoEditableClient editable) { - super(targetView, true); - mEditableClient = editable; - mIMEState = IME_STATE_DISABLED; - // InputConnection that sends keys for plugins, which don't have full editors - mKeyInputConnection = new BaseInputConnection(targetView, false); - } - - @Override - public synchronized boolean beginBatchEdit() { - mBatchEditCount++; - mEditableClient.setUpdateGecko(false); - return true; - } - - @Override - public synchronized boolean endBatchEdit() { - if (mBatchEditCount > 0) { - mBatchEditCount--; - if (mBatchEditCount == 0) { - if (mBatchTextChanged) { - notifyTextChange(); - mBatchTextChanged = false; - } - if (mBatchSelectionChanged) { - Editable editable = getEditable(); - notifySelectionChange(Selection.getSelectionStart(editable), - Selection.getSelectionEnd(editable)); - mBatchSelectionChanged = false; - } - mEditableClient.setUpdateGecko(true); - } - } else { - Log.w(LOGTAG, "endBatchEdit() called, but mBatchEditCount == 0?!"); - } - return true; - } - - @Override - public Editable getEditable() { - return mEditableClient.getEditable(); - } - - @Override - public boolean performContextMenuAction(int id) { - Editable editable = getEditable(); - if (editable == null) { - return false; - } - int selStart = Selection.getSelectionStart(editable); - int selEnd = Selection.getSelectionEnd(editable); - - switch (id) { - case R.id.selectAll: - setSelection(0, editable.length()); - break; - case R.id.cut: - // If selection is empty, we'll select everything - if (selStart == selEnd) { - // Fill the clipboard - Clipboard.setText(editable); - editable.clear(); - } else { - Clipboard.setText( - editable.toString().substring( - Math.min(selStart, selEnd), - Math.max(selStart, selEnd))); - editable.delete(selStart, selEnd); - } - break; - case R.id.paste: - commitText(Clipboard.getText(), 1); - break; - case R.id.copy: - // Copy the current selection or the empty string if nothing is selected. - String copiedText = selStart == selEnd ? "" : - editable.toString().substring( - Math.min(selStart, selEnd), - Math.max(selStart, selEnd)); - Clipboard.setText(copiedText); - break; - } - return true; - } - - @Override - public ExtractedText getExtractedText(ExtractedTextRequest req, int flags) { - if (req == null) - return null; - - if ((flags & GET_EXTRACTED_TEXT_MONITOR) != 0) - mUpdateRequest = req; - - Editable editable = getEditable(); - if (editable == null) { - return null; - } - int selStart = Selection.getSelectionStart(editable); - int selEnd = Selection.getSelectionEnd(editable); - - ExtractedText extract = new ExtractedText(); - extract.flags = 0; - extract.partialStartOffset = -1; - extract.partialEndOffset = -1; - extract.selectionStart = selStart; - extract.selectionEnd = selEnd; - extract.startOffset = 0; - if ((req.flags & GET_TEXT_WITH_STYLES) != 0) { - extract.text = new SpannableString(editable); - } else { - extract.text = editable.toString(); - } - return extract; - } - - private static View getView() { - return GeckoAppShell.getLayerView(); - } - - private static InputMethodManager getInputMethodManager() { - View view = getView(); - if (view == null) { - return null; - } - Context context = view.getContext(); - return InputMethods.getInputMethodManager(context); - } - - private static void showSoftInput() { - final InputMethodManager imm = getInputMethodManager(); - if (imm != null) { - final View v = getView(); - imm.showSoftInput(v, 0); - } - } - - private static void hideSoftInput() { - final InputMethodManager imm = getInputMethodManager(); - if (imm != null) { - final View v = getView(); - imm.hideSoftInputFromWindow(v.getWindowToken(), 0); - } - } - - private void tryRestartInput() { - // Coalesce restartInput calls because InputMethodManager.restartInput() - // is expensive and successive calls to it can lock up the keyboard - if (SystemClock.uptimeMillis() < mLastRestartInputTime + 200) { - return; - } - restartInput(); - } - - private void restartInput() { - - mLastRestartInputTime = SystemClock.uptimeMillis(); - - final InputMethodManager imm = getInputMethodManager(); - if (imm == null) { - return; - } - final View v = getView(); - // InputMethodManager has internal logic to detect if we are restarting input - // in an already focused View, which is the case here because all content text - // fields are inside one LayerView. When this happens, InputMethodManager will - // tell the input method to soft reset instead of hard reset. Stock latin IME - // on Android 4.2+ has a quirk that when it soft resets, it does not clear the - // composition. The following workaround tricks the IME into clearing the - // composition when soft resetting. - if (InputMethods.needsSoftResetWorkaround(mCurrentInputMethod)) { - // Fake a selection change, because the IME clears the composition when - // the selection changes, even if soft-resetting. Offsets here must be - // different from the previous selection offsets, and -1 seems to be a - // reasonable, deterministic value - notifySelectionChange(-1, -1); - } - imm.restartInput(v); - } - - private void resetInputConnection() { - if (mBatchEditCount != 0) { - Log.w(LOGTAG, "resetting with mBatchEditCount = " + mBatchEditCount); - mBatchEditCount = 0; - } - mBatchSelectionChanged = false; - mBatchTextChanged = false; - - // Do not reset mIMEState here; see comments in notifyIMEContext - } - - @Override - public void onTextChange(String text, int start, int oldEnd, int newEnd) { - - if (mUpdateRequest == null) { - return; - } - - if (mBatchEditCount > 0) { - // Delay notification until after the batch edit - mBatchTextChanged = true; - return; - } - notifyTextChange(); - } - - private void notifyTextChange() { - - final InputMethodManager imm = getInputMethodManager(); - final View v = getView(); - final Editable editable = getEditable(); - if (imm == null || v == null || editable == null) { - return; - } - mUpdateExtract.flags = 0; - // Update the entire Editable range - mUpdateExtract.partialStartOffset = -1; - mUpdateExtract.partialEndOffset = -1; - mUpdateExtract.selectionStart = - Selection.getSelectionStart(editable); - mUpdateExtract.selectionEnd = - Selection.getSelectionEnd(editable); - mUpdateExtract.startOffset = 0; - if ((mUpdateRequest.flags & GET_TEXT_WITH_STYLES) != 0) { - mUpdateExtract.text = new SpannableString(editable); - } else { - mUpdateExtract.text = editable.toString(); - } - imm.updateExtractedText(v, mUpdateRequest.token, - mUpdateExtract); - } - - @Override - public void onSelectionChange(int start, int end) { - - if (mBatchEditCount > 0) { - // Delay notification until after the batch edit - mBatchSelectionChanged = true; - return; - } - notifySelectionChange(start, end); - } - - private void notifySelectionChange(int start, int end) { - - final InputMethodManager imm = getInputMethodManager(); - final View v = getView(); - final Editable editable = getEditable(); - if (imm == null || v == null || editable == null) { - return; - } - imm.updateSelection(v, start, end, getComposingSpanStart(editable), - getComposingSpanEnd(editable)); - } - - private static synchronized Handler getBackgroundHandler() { - if (sBackgroundHandler != null) { - return sBackgroundHandler; - } - // Don't use GeckoBackgroundThread because Gecko thread may block waiting on - // GeckoBackgroundThread. If we were to use GeckoBackgroundThread, due to IME, - // GeckoBackgroundThread may end up also block waiting on Gecko thread and a - // deadlock occurs - Thread backgroundThread = new Thread(new Runnable() { - @Override - public void run() { - Looper.prepare(); - synchronized (GeckoInputConnection.class) { - sBackgroundHandler = new Handler(); - GeckoInputConnection.class.notify(); - } - Looper.loop(); - sBackgroundHandler = null; - } - }, LOGTAG); - backgroundThread.setDaemon(true); - backgroundThread.start(); - while (sBackgroundHandler == null) { - try { - // wait for new thread to set sBackgroundHandler - GeckoInputConnection.class.wait(); - } catch (InterruptedException e) { - } - } - return sBackgroundHandler; - } - - private boolean canReturnCustomHandler() { - if (mIMEState == IME_STATE_DISABLED) { - return false; - } - for (StackTraceElement frame : Thread.currentThread().getStackTrace()) { - // We only return our custom Handler to InputMethodManager's InputConnection - // proxy. For all other purposes, we return the regular Handler. - // InputMethodManager retrieves the Handler for its InputConnection proxy - // inside its method startInputInner(), so we check for that here. This is - // valid from Android 2.2 to at least Android 4.2. If this situation ever - // changes, we gracefully fall back to using the regular Handler. - if ("startInputInner".equals(frame.getMethodName()) && - "android.view.inputmethod.InputMethodManager".equals(frame.getClassName())) { - // only return our own Handler to InputMethodManager - return true; - } - } - return false; - } - - @Override - public Handler getHandler(Handler defHandler) { - if (!canReturnCustomHandler()) { - return defHandler; - } - // getBackgroundHandler() is synchronized and requires locking, - // but if we already have our handler, we don't have to lock - final Handler newHandler = sBackgroundHandler != null - ? sBackgroundHandler - : getBackgroundHandler(); - if (mEditableClient.setInputConnectionHandler(newHandler)) { - return newHandler; - } - // Setting new IC handler failed; return old IC handler - return mEditableClient.getInputConnectionHandler(); - } - - @Override - public InputConnection onCreateInputConnection(EditorInfo outAttrs) { - if (mIMEState == IME_STATE_DISABLED) { - return null; - } - - outAttrs.inputType = InputType.TYPE_CLASS_TEXT; - outAttrs.imeOptions = EditorInfo.IME_ACTION_NONE; - outAttrs.actionLabel = null; - - if (mIMEState == IME_STATE_PASSWORD || - "password".equalsIgnoreCase(mIMETypeHint)) - outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_PASSWORD; - else if (mIMEState == IME_STATE_PLUGIN) - outAttrs.inputType = InputType.TYPE_NULL; // "send key events" mode - else if (mIMETypeHint.equalsIgnoreCase("url")) - outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_URI; - else if (mIMETypeHint.equalsIgnoreCase("email")) - outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS; - else if (mIMETypeHint.equalsIgnoreCase("search")) - outAttrs.imeOptions = EditorInfo.IME_ACTION_SEARCH; - else if (mIMETypeHint.equalsIgnoreCase("tel")) - outAttrs.inputType = InputType.TYPE_CLASS_PHONE; - else if (mIMETypeHint.equalsIgnoreCase("number") || - mIMETypeHint.equalsIgnoreCase("range")) - outAttrs.inputType = InputType.TYPE_CLASS_NUMBER - | InputType.TYPE_NUMBER_FLAG_SIGNED - | InputType.TYPE_NUMBER_FLAG_DECIMAL; - else if (mIMETypeHint.equalsIgnoreCase("week") || - mIMETypeHint.equalsIgnoreCase("month")) - outAttrs.inputType = InputType.TYPE_CLASS_DATETIME - | InputType.TYPE_DATETIME_VARIATION_DATE; - else if (mIMEModeHint.equalsIgnoreCase("numeric")) - outAttrs.inputType = InputType.TYPE_CLASS_NUMBER | - InputType.TYPE_NUMBER_FLAG_SIGNED | - InputType.TYPE_NUMBER_FLAG_DECIMAL; - else if (mIMEModeHint.equalsIgnoreCase("digit")) - outAttrs.inputType = InputType.TYPE_CLASS_NUMBER; - else { - // TYPE_TEXT_FLAG_IME_MULTI_LINE flag makes the fullscreen IME line wrap - outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_AUTO_CORRECT | - InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE; - if (mIMETypeHint.equalsIgnoreCase("textarea") || - mIMETypeHint.length() == 0) { - // empty mIMETypeHint indicates contentEditable/designMode documents - outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE; - } - if (mIMEModeHint.equalsIgnoreCase("uppercase")) - outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS; - else if (mIMEModeHint.equalsIgnoreCase("titlecase")) - outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_WORDS; - else if (mIMEModeHint.equalsIgnoreCase("autocapitalized")) - outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES; - // lowercase mode is the default - } - - if (mIMEActionHint.equalsIgnoreCase("go")) - outAttrs.imeOptions = EditorInfo.IME_ACTION_GO; - else if (mIMEActionHint.equalsIgnoreCase("done")) - outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE; - else if (mIMEActionHint.equalsIgnoreCase("next")) - outAttrs.imeOptions = EditorInfo.IME_ACTION_NEXT; - else if (mIMEActionHint.equalsIgnoreCase("search")) - outAttrs.imeOptions = EditorInfo.IME_ACTION_SEARCH; - else if (mIMEActionHint.equalsIgnoreCase("send")) - outAttrs.imeOptions = EditorInfo.IME_ACTION_SEND; - else if (mIMEActionHint.length() > 0) { - if (DEBUG) - Log.w(LOGTAG, "Unexpected mIMEActionHint=\"" + mIMEActionHint + "\""); - outAttrs.actionLabel = mIMEActionHint; - } - - Context context = GeckoAppShell.getContext(); - DisplayMetrics metrics = context.getResources().getDisplayMetrics(); - if (Math.min(metrics.widthPixels, metrics.heightPixels) > INLINE_IME_MIN_DISPLAY_SIZE) { - // prevent showing full-screen keyboard only when the screen is tall enough - // to show some reasonable amount of the page (see bug 752709) - outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI - | EditorInfo.IME_FLAG_NO_FULLSCREEN; - } - - if (DEBUG) { - Log.d(LOGTAG, "mapped IME states to: inputType = " + - Integer.toHexString(outAttrs.inputType) + ", imeOptions = " + - Integer.toHexString(outAttrs.imeOptions)); - } - - String prevInputMethod = mCurrentInputMethod; - mCurrentInputMethod = InputMethods.getCurrentInputMethod(context); - if (DEBUG) { - Log.d(LOGTAG, "IME: CurrentInputMethod=" + mCurrentInputMethod); - } - - // If the user has changed IMEs, then notify input method observers. - if (!mCurrentInputMethod.equals(prevInputMethod) && GeckoAppShell.getGeckoInterface() != null) { - FormAssistPopup popup = GeckoAppShell.getGeckoInterface().getFormAssistPopup(); - if (popup != null) { - popup.onInputMethodChanged(mCurrentInputMethod); - } - } - - if (mIMEState == IME_STATE_PLUGIN) { - // Since we are using a temporary string as the editable, the selection is at 0 - outAttrs.initialSelStart = 0; - outAttrs.initialSelEnd = 0; - return mKeyInputConnection; - } - Editable editable = getEditable(); - outAttrs.initialSelStart = Selection.getSelectionStart(editable); - outAttrs.initialSelEnd = Selection.getSelectionEnd(editable); - return this; - } - - private boolean replaceComposingSpanWithSelection() { - final Editable content = getEditable(); - if (content == null) { - return false; - } - int a = getComposingSpanStart(content), - b = getComposingSpanEnd(content); - if (a != -1 && b != -1) { - if (DEBUG) { - Log.d(LOGTAG, "removing composition at " + a + "-" + b); - } - removeComposingSpans(content); - Selection.setSelection(content, a, b); - } - return true; - } - - @Override - public boolean commitText(CharSequence text, int newCursorPosition) { - if (InputMethods.shouldCommitCharAsKey(mCurrentInputMethod) && - text.length() == 1 && newCursorPosition > 0) { - if (DEBUG) { - Log.d(LOGTAG, "committing \"" + text + "\" as key"); - } - // mKeyInputConnection is a BaseInputConnection that commits text as keys; - // but we first need to replace any composing span with a selection, - // so that the new key events will generate characters to replace - // text from the old composing span - return replaceComposingSpanWithSelection() && - mKeyInputConnection.commitText(text, newCursorPosition); - } - return super.commitText(text, newCursorPosition); - } - - @Override - public boolean setSelection(int start, int end) { - if (start < 0 || end < 0) { - // Some keyboards (e.g. Samsung) can call setSelection with - // negative offsets. In that case we ignore the call, similar to how - // BaseInputConnection.setSelection ignores offsets that go past the length. - return true; - } - return super.setSelection(start, end); - } - - @Override - public boolean sendKeyEvent(KeyEvent event) { - // BaseInputConnection.sendKeyEvent() dispatches the key event to the main thread. - // In order to ensure events are processed in the proper order, we must block the - // IC thread until the main thread finishes processing the key event - super.sendKeyEvent(event); - final View v = getView(); - if (v == null) { - return false; - } - final Handler icHandler = mEditableClient.getInputConnectionHandler(); - final Handler mainHandler = v.getRootView().getHandler(); - if (icHandler.getLooper() != mainHandler.getLooper()) { - // We are on separate IC thread but the event is queued on the main thread; - // wait on IC thread until the main thread processes our posted Runnable. At - // that point the key event has already been processed. - mainHandler.post(new Runnable() { - @Override public void run() { - InputThreadUtils.sInstance.endWaitForUiThread(); - } - }); - InputThreadUtils.sInstance.waitForUiThread(icHandler); - } - return false; // seems to always return false - } - - @Override - public boolean onKeyPreIme(int keyCode, KeyEvent event) { - return false; - } - - private boolean shouldProcessKey(int keyCode, KeyEvent event) { - switch (keyCode) { - case KeyEvent.KEYCODE_MENU: - case KeyEvent.KEYCODE_BACK: - case KeyEvent.KEYCODE_VOLUME_UP: - case KeyEvent.KEYCODE_VOLUME_DOWN: - case KeyEvent.KEYCODE_SEARCH: - return false; - } - return true; - } - - private boolean shouldSkipKeyListener(int keyCode, KeyEvent event) { - if (mIMEState == IME_STATE_DISABLED || - mIMEState == IME_STATE_PLUGIN) { - return true; - } - // Preserve enter and tab keys for the browser - if (keyCode == KeyEvent.KEYCODE_ENTER || - keyCode == KeyEvent.KEYCODE_TAB) { - return true; - } - // BaseKeyListener returns false even if it handled these keys for us, - // so we skip the key listener entirely and handle these ourselves - if (keyCode == KeyEvent.KEYCODE_DEL || - keyCode == KeyEvent.KEYCODE_FORWARD_DEL) { - return true; - } - return false; - } - - private KeyEvent translateKey(int keyCode, KeyEvent event) { - switch (keyCode) { - case KeyEvent.KEYCODE_ENTER: - if ((event.getFlags() & KeyEvent.FLAG_EDITOR_ACTION) != 0 && - mIMEActionHint.equalsIgnoreCase("next")) { - return new KeyEvent(event.getAction(), KeyEvent.KEYCODE_TAB); - } - break; - } - return event; - } - - private boolean processKey(int keyCode, KeyEvent event, boolean down) { - if (GamepadUtils.isSonyXperiaGamepadKeyEvent(event)) { - event = GamepadUtils.translateSonyXperiaGamepadKeys(keyCode, event); - keyCode = event.getKeyCode(); - } - - if (keyCode > KeyEvent.getMaxKeyCode() || - !shouldProcessKey(keyCode, event)) { - return false; - } - event = translateKey(keyCode, event); - keyCode = event.getKeyCode(); - - View view = getView(); - if (view == null) { - mEditableClient.sendEvent(GeckoEvent.createKeyEvent(event, 0)); - return true; - } - - // KeyListener returns true if it handled the event for us. KeyListener is only - // safe to use on the UI thread; therefore we need to pass a proxy Editable to it - KeyListener keyListener = TextKeyListener.getInstance(); - Handler uiHandler = view.getRootView().getHandler(); - Editable uiEditable = InputThreadUtils.sInstance. - getEditableForUiThread(uiHandler, mEditableClient); - boolean skip = shouldSkipKeyListener(keyCode, event); - if (down) { - mEditableClient.setSuppressKeyUp(true); - } - if (skip || - (down && !keyListener.onKeyDown(view, uiEditable, keyCode, event)) || - (!down && !keyListener.onKeyUp(view, uiEditable, keyCode, event))) { - mEditableClient.sendEvent( - GeckoEvent.createKeyEvent(event, TextKeyListener.getMetaState(uiEditable))); - if (skip && down) { - // Usually, the down key listener call above adjusts meta states for us. - // However, if we skip that call above, we have to manually adjust meta - // states so the meta states remain consistent - TextKeyListener.adjustMetaAfterKeypress(uiEditable); - } - } - if (down) { - mEditableClient.setSuppressKeyUp(false); - } - return true; - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - return processKey(keyCode, event, true); - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - return processKey(keyCode, event, false); - } - - @Override - public boolean onKeyMultiple(int keyCode, int repeatCount, final KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_UNKNOWN) { - // KEYCODE_UNKNOWN means the characters are in KeyEvent.getCharacters() - View view = getView(); - if (view != null) { - InputThreadUtils.sInstance.runOnIcThread( - view.getRootView().getHandler(), mEditableClient, - new Runnable() { - @Override public void run() { - // Don't call GeckoInputConnection.commitText because it can - // post a key event back to onKeyMultiple, causing a loop - GeckoInputConnection.super.commitText(event.getCharacters(), 1); - } - }); - } - return true; - } - while ((repeatCount--) != 0) { - if (!processKey(keyCode, event, true) || - !processKey(keyCode, event, false)) { - return false; - } - } - return true; - } - - @Override - public boolean onKeyLongPress(int keyCode, KeyEvent event) { - View v = getView(); - switch (keyCode) { - case KeyEvent.KEYCODE_MENU: - InputMethodManager imm = getInputMethodManager(); - imm.toggleSoftInputFromWindow(v.getWindowToken(), - InputMethodManager.SHOW_FORCED, 0); - return true; - default: - break; - } - return false; - } - - @Override - public boolean isIMEEnabled() { - // make sure this picks up PASSWORD and PLUGIN states as well - return mIMEState != IME_STATE_DISABLED; - } - - @Override - public void notifyIME(int type) { - switch (type) { - - case NOTIFY_IME_TO_CANCEL_COMPOSITION: - // Set composition to empty and end composition - setComposingText("", 0); - // Fall through - - case NOTIFY_IME_TO_COMMIT_COMPOSITION: - // Commit and end composition - finishComposingText(); - tryRestartInput(); - break; - - case NOTIFY_IME_OF_FOCUS: - case NOTIFY_IME_OF_BLUR: - // Showing/hiding vkb is done in notifyIMEContext - resetInputConnection(); - break; - - default: - if (DEBUG) { - throw new IllegalArgumentException("Unexpected NOTIFY_IME=" + type); - } - break; - } - } - - @Override - public void notifyIMEContext(int state, String typeHint, String modeHint, String actionHint) { - // For some input type we will use a widget to display the ui, for those we must not - // display the ime. We can display a widget for date and time types and, if the sdk version - // is 11 or greater, for datetime/month/week as well. - if (typeHint != null && - (typeHint.equalsIgnoreCase("date") || - typeHint.equalsIgnoreCase("time") || - (Build.VERSION.SDK_INT >= 11 && (typeHint.equalsIgnoreCase("datetime") || - typeHint.equalsIgnoreCase("month") || - typeHint.equalsIgnoreCase("week") || - typeHint.equalsIgnoreCase("datetime-local"))))) { - state = IME_STATE_DISABLED; - } - - // mIMEState and the mIME*Hint fields should only be changed by notifyIMEContext, - // and not reset anywhere else. Usually, notifyIMEContext is called right after a - // focus or blur, so resetting mIMEState during the focus or blur seems harmless. - // However, this behavior is not guaranteed. Gecko may call notifyIMEContext - // independent of focus change; that is, a focus change may not be accompanied by - // a notifyIMEContext call. So if we reset mIMEState inside focus, there may not - // be another notifyIMEContext call to set mIMEState to a proper value (bug 829318) - /* When IME is 'disabled', IME processing is disabled. - In addition, the IME UI is hidden */ - mIMEState = state; - mIMETypeHint = (typeHint == null) ? "" : typeHint; - mIMEModeHint = (modeHint == null) ? "" : modeHint; - mIMEActionHint = (actionHint == null) ? "" : actionHint; - - // These fields are reset here and will be updated when restartInput is called below - mUpdateRequest = null; - mCurrentInputMethod = ""; - - View v = getView(); - if (v == null || !v.hasFocus()) { - // When using Find In Page, we can still receive notifyIMEContext calls due to the - // selection changing when highlighting. However in this case we don't want to reset/ - // show/hide the keyboard because the find box has the focus and is taking input from - // the keyboard. - return; - } - restartInput(); - if (mIMEState == IME_STATE_DISABLED) { - hideSoftInput(); - } else { - showSoftInput(); - } - } -} - -final class DebugGeckoInputConnection - extends GeckoInputConnection - implements InvocationHandler { - - private InputConnection mProxy; - private StringBuilder mCallLevel; - - private DebugGeckoInputConnection(View targetView, - GeckoEditableClient editable) { - super(targetView, editable); - mCallLevel = new StringBuilder(); - } - - public static GeckoEditableListener create(View targetView, - GeckoEditableClient editable) { - final Class[] PROXY_INTERFACES = { InputConnection.class, - InputConnectionHandler.class, - GeckoEditableListener.class }; - DebugGeckoInputConnection dgic = - new DebugGeckoInputConnection(targetView, editable); - dgic.mProxy = (InputConnection)Proxy.newProxyInstance( - GeckoInputConnection.class.getClassLoader(), - PROXY_INTERFACES, dgic); - return (GeckoEditableListener)dgic.mProxy; - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) - throws Throwable { - - StringBuilder log = new StringBuilder(mCallLevel); - log.append("> ").append(method.getName()).append("("); - for (Object arg : args) { - // translate argument values to constant names - if ("notifyIME".equals(method.getName()) && arg == args[0]) { - log.append(GeckoEditable.getConstantName( - GeckoEditableListener.class, "NOTIFY_IME_", arg)); - } else if ("notifyIMEContext".equals(method.getName()) && arg == args[0]) { - log.append(GeckoEditable.getConstantName( - GeckoEditableListener.class, "IME_STATE_", arg)); - } else { - GeckoEditable.debugAppend(log, arg); - } - log.append(", "); - } - if (args.length > 0) { - log.setLength(log.length() - 2); - } - log.append(")"); - Log.d(LOGTAG, log.toString()); - - mCallLevel.append(' '); - Object ret = method.invoke(this, args); - if (ret == this) { - ret = mProxy; - } - mCallLevel.setLength(Math.max(0, mCallLevel.length() - 1)); - - log.setLength(mCallLevel.length()); - log.append("< ").append(method.getName()); - if (!method.getReturnType().equals(Void.TYPE)) { - GeckoEditable.debugAppend(log.append(": "), ret); - } - Log.d(LOGTAG, log.toString()); - return ret; - } -} diff --git a/mobile/android/base/GeckoJavaSampler.java b/mobile/android/base/GeckoJavaSampler.java deleted file mode 100644 index 130d5643f..000000000 --- a/mobile/android/base/GeckoJavaSampler.java +++ /dev/null @@ -1,184 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import android.util.Log; -import java.lang.Thread; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -public class GeckoJavaSampler { - private static final String LOGTAG = "JavaSampler"; - private static Thread sSamplingThread = null; - private static SamplingThread sSamplingRunnable = null; - private static Thread sMainThread = null; - - // Use the same timer primitive as the profiler - // to get a perfect sample syncing. - private static native double getProfilerTime(); - - private static class Sample { - public Frame[] mFrames; - public double mTime; - public Sample(StackTraceElement[] aStack) { - mFrames = new Frame[aStack.length]; - mTime = getProfilerTime(); - for (int i = 0; i < aStack.length; i++) { - mFrames[aStack.length - 1 - i] = new Frame(); - mFrames[aStack.length - 1 - i].fileName = aStack[i].getFileName(); - mFrames[aStack.length - 1 - i].lineNo = aStack[i].getLineNumber(); - mFrames[aStack.length - 1 - i].methodName = aStack[i].getMethodName(); - mFrames[aStack.length - 1 - i].className = aStack[i].getClassName(); - } - } - } - private static class Frame { - public String fileName; - public int lineNo; - public String methodName; - public String className; - } - - private static class SamplingThread implements Runnable { - private final int mInterval; - private final int mSampleCount; - - private boolean mPauseSampler = false; - private boolean mStopSampler = false; - - private Map<Integer,Sample[]> mSamples = new HashMap<Integer,Sample[]>(); - private int mSamplePos; - - public SamplingThread(final int aInterval, final int aSampleCount) { - // If we sample faster then 10ms we get to many missed samples - mInterval = Math.max(10, aInterval); - mSampleCount = aSampleCount; - } - - public void run() { - synchronized (GeckoJavaSampler.class) { - mSamples.put(0, new Sample[mSampleCount]); - mSamplePos = 0; - - // Find the main thread - Set<Thread> threadSet = Thread.getAllStackTraces().keySet(); - for (Thread t : threadSet) { - if (t.getName().compareToIgnoreCase("main") == 0) { - sMainThread = t; - break; - } - } - - if (sMainThread == null) { - Log.e(LOGTAG, "Main thread not found"); - return; - } - } - - while (true) { - try { - Thread.sleep(mInterval); - } catch (InterruptedException e) { - e.printStackTrace(); - } - synchronized (GeckoJavaSampler.class) { - if (!mPauseSampler) { - StackTraceElement[] bt = sMainThread.getStackTrace(); - mSamples.get(0)[mSamplePos] = new Sample(bt); - mSamplePos = (mSamplePos+1) % mSamples.get(0).length; - } - if (mStopSampler) { - break; - } - } - } - } - - private Sample getSample(int aThreadId, int aSampleId) { - if (aThreadId < mSamples.size() && aSampleId < mSamples.get(aThreadId).length && - mSamples.get(aThreadId)[aSampleId] != null) { - int startPos = 0; - if (mSamples.get(aThreadId)[mSamplePos] != null) { - startPos = mSamplePos; - } - int readPos = (startPos + aSampleId) % mSamples.get(aThreadId).length; - return mSamples.get(aThreadId)[readPos]; - } - return null; - } - } - - public synchronized static String getThreadName(int aThreadId) { - if (aThreadId == 0 && sMainThread != null) { - return sMainThread.getName(); - } - return null; - } - - private synchronized static Sample getSample(int aThreadId, int aSampleId) { - return sSamplingRunnable.getSample(aThreadId, aSampleId); - } - public synchronized static double getSampleTime(int aThreadId, int aSampleId) { - Sample sample = getSample(aThreadId, aSampleId); - if (sample != null) { - System.out.println("Sample: " + sample.mTime); - return sample.mTime; - } - return 0; - } - public synchronized static String getFrameName(int aThreadId, int aSampleId, int aFrameId) { - Sample sample = getSample(aThreadId, aSampleId); - if (sample != null && aFrameId < sample.mFrames.length) { - Frame frame = sample.mFrames[aFrameId]; - if (frame == null) { - return null; - } - return frame.className + "." + frame.methodName + "()"; - } - return null; - } - - public static void start(int aInterval, int aSamples) { - synchronized (GeckoJavaSampler.class) { - sSamplingRunnable = new SamplingThread(aInterval, aSamples); - sSamplingThread = new Thread(sSamplingRunnable, "Java Sampler"); - sSamplingThread.start(); - } - } - - public static void pause() { - synchronized (GeckoJavaSampler.class) { - sSamplingRunnable.mPauseSampler = true; - } - } - - public static void unpause() { - synchronized (GeckoJavaSampler.class) { - sSamplingRunnable.mPauseSampler = false; - } - } - - public static void stop() { - synchronized (GeckoJavaSampler.class) { - if (sSamplingThread == null) { - return; - } - - sSamplingRunnable.mStopSampler = true; - try { - sSamplingThread.join(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - sSamplingThread = null; - sSamplingRunnable = null; - } - } -} - - - diff --git a/mobile/android/base/GeckoMessageReceiver.java b/mobile/android/base/GeckoMessageReceiver.java deleted file mode 100644 index 2c31d6c61..000000000 --- a/mobile/android/base/GeckoMessageReceiver.java +++ /dev/null @@ -1,19 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -public class GeckoMessageReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (GeckoApp.ACTION_INIT_PW.equals(action)) { - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Passwords:Init", null)); - } - } -} diff --git a/mobile/android/base/GeckoNetworkManager.java b/mobile/android/base/GeckoNetworkManager.java deleted file mode 100644 index 5ec493463..000000000 --- a/mobile/android/base/GeckoNetworkManager.java +++ /dev/null @@ -1,297 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.telephony.TelephonyManager; -import android.util.Log; - -/* - * A part of the work of GeckoNetworkManager is to give an estimation of the - * download speed of the current connection. For known to be fast connection, we - * simply use a predefined value (we don't care about being precise). For mobile - * connections, we sort them in groups (generations) and estimate the average - * real life download speed of that specific generation. This value comes from - * researches (eg. Wikipedia articles) or is simply an arbitrary estimation. - * Precision isn't important, we mostly need an order of magnitude. - * - * Each group is composed with networks represented by the constant from - * Android's ConnectivityManager and the description comming from the same - * class. - * - * 2G (15 bk/s): - * int NETWORK_TYPE_IDEN Current network is iDen - * int NETWORK_TYPE_CDMA Current network is CDMA: Either IS95A or IS95B - * - * 2.5G (60 kb/s) - * int NETWORK_TYPE_GPRS Current network is GPRS - * int NETWORK_TYPE_1xRTT Current network is 1xRTT - * - * 2.75G (200 kb/s) - * int NETWORK_TYPE_EDGE Current network is EDGE - * - * 3G (300 kb/s) - * int NETWORK_TYPE_UMTS Current network is UMTS - * int NETWORK_TYPE_EVDO_0 Current network is EVDO revision 0 - * - * 3.5G (7 Mb/s) - * int NETWORK_TYPE_HSPA Current network is HSPA - * int NETWORK_TYPE_HSDPA Current network is HSDPA - * int NETWORK_TYPE_HSUPA Current network is HSUPA - * int NETWORK_TYPE_EVDO_A Current network is EVDO revision A - * int NETWORK_TYPE_EVDO_B Current network is EVDO revision B - * int NETWORK_TYPE_EHRPD Current network is eHRPD - * - * 3.75G (20 Mb/s) - * int NETWORK_TYPE_HSPAP Current network is HSPA+ - * - * 3.9G (50 Mb/s) - * int NETWORK_TYPE_LTE Current network is LTE - */ - -public class GeckoNetworkManager extends BroadcastReceiver { - private static final String LOGTAG = "GeckoNetworkManager"; - - static private final GeckoNetworkManager sInstance = new GeckoNetworkManager(); - - static private final double kDefaultBandwidth = -1.0; - static private final boolean kDefaultCanBeMetered = false; - - static private final double kMaxBandwidth = 20.0; - - static private final double kNetworkSpeedEthernet = 20.0; // 20 Mb/s - static private final double kNetworkSpeedWifi = 20.0; // 20 Mb/s - static private final double kNetworkSpeedWiMax = 40.0; // 40 Mb/s - static private final double kNetworkSpeed_2_G = 15.0 / 1024.0; // 15 kb/s - static private final double kNetworkSpeed_2_5_G = 60.0 / 1024.0; // 60 kb/s - static private final double kNetworkSpeed_2_75_G = 200.0 / 1024.0; // 200 kb/s - static private final double kNetworkSpeed_3_G = 300.0 / 1024.0; // 300 kb/s - static private final double kNetworkSpeed_3_5_G = 7.0; // 7 Mb/s - static private final double kNetworkSpeed_3_75_G = 20.0; // 20 Mb/s - static private final double kNetworkSpeed_3_9_G = 50.0; // 50 Mb/s - - private enum NetworkType { - NETWORK_NONE, - NETWORK_ETHERNET, - NETWORK_WIFI, - NETWORK_WIMAX, - NETWORK_2_G, // 2G - NETWORK_2_5_G, // 2.5G - NETWORK_2_75_G, // 2.75G - NETWORK_3_G, // 3G - NETWORK_3_5_G, // 3.5G - NETWORK_3_75_G, // 3.75G - NETWORK_3_9_G, // 3.9G - NETWORK_UNKNOWN - } - - private Context mApplicationContext; - private NetworkType mNetworkType = NetworkType.NETWORK_NONE; - private IntentFilter mNetworkFilter = new IntentFilter(); - // Whether the manager should be listening to Network Information changes. - private boolean mShouldBeListening = false; - // Whether the manager should notify Gecko that a change in Network - // Information happened. - private boolean mShouldNotify = false; - - public static GeckoNetworkManager getInstance() { - return sInstance; - } - - @Override - public void onReceive(Context aContext, Intent aIntent) { - updateNetworkType(); - } - - public void init(Context context) { - mApplicationContext = context.getApplicationContext(); - mNetworkFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); - mNetworkType = getNetworkType(); - } - - public void start() { - mShouldBeListening = true; - updateNetworkType(); - - if (mShouldNotify) { - startListening(); - } - } - - private void startListening() { - mApplicationContext.registerReceiver(sInstance, mNetworkFilter); - } - - public void stop() { - mShouldBeListening = false; - - if (mShouldNotify) { - stopListening(); - } - } - - private void stopListening() { - mApplicationContext.unregisterReceiver(sInstance); - } - - private void updateNetworkType() { - NetworkType previousNetworkType = mNetworkType; - mNetworkType = getNetworkType(); - - if (mNetworkType == previousNetworkType || !mShouldNotify) { - return; - } - - GeckoAppShell.sendEventToGecko(GeckoEvent.createNetworkEvent( - getNetworkSpeed(mNetworkType), - isNetworkUsuallyMetered(mNetworkType))); - } - - public double[] getCurrentInformation() { - return new double[] { getNetworkSpeed(mNetworkType), - isNetworkUsuallyMetered(mNetworkType) ? 1.0 : 0.0 }; - } - - public void enableNotifications() { - // We set mShouldNotify *after* calling updateNetworkType() to make sure we - // don't notify an eventual change in mNetworkType. - updateNetworkType(); - mShouldNotify = true; - - if (mShouldBeListening) { - startListening(); - } - } - - public void disableNotifications() { - mShouldNotify = false; - - if (mShouldBeListening) { - stopListening(); - } - } - - private static NetworkType getNetworkType() { - ConnectivityManager cm = - (ConnectivityManager)sInstance.mApplicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); - if (cm == null) { - Log.e(LOGTAG, "Connectivity service does not exist"); - return NetworkType.NETWORK_NONE; - } - - NetworkInfo ni = cm.getActiveNetworkInfo(); - if (ni == null) { - return NetworkType.NETWORK_NONE; - } - - switch (ni.getType()) { - case ConnectivityManager.TYPE_ETHERNET: - return NetworkType.NETWORK_ETHERNET; - case ConnectivityManager.TYPE_WIFI: - return NetworkType.NETWORK_WIFI; - case ConnectivityManager.TYPE_WIMAX: - return NetworkType.NETWORK_WIMAX; - case ConnectivityManager.TYPE_MOBILE: - break; // We will handle sub-types after the switch. - default: - Log.w(LOGTAG, "Ignoring the current network type."); - return NetworkType.NETWORK_UNKNOWN; - } - - TelephonyManager tm = - (TelephonyManager)sInstance.mApplicationContext.getSystemService(Context.TELEPHONY_SERVICE); - if (tm == null) { - Log.e(LOGTAG, "Telephony service does not exist"); - return NetworkType.NETWORK_UNKNOWN; - } - - switch (tm.getNetworkType()) { - case TelephonyManager.NETWORK_TYPE_IDEN: - case TelephonyManager.NETWORK_TYPE_CDMA: - return NetworkType.NETWORK_2_G; - case TelephonyManager.NETWORK_TYPE_GPRS: - case TelephonyManager.NETWORK_TYPE_1xRTT: - return NetworkType.NETWORK_2_5_G; - case TelephonyManager.NETWORK_TYPE_EDGE: - return NetworkType.NETWORK_2_75_G; - case TelephonyManager.NETWORK_TYPE_UMTS: - case TelephonyManager.NETWORK_TYPE_EVDO_0: - return NetworkType.NETWORK_3_G; - case TelephonyManager.NETWORK_TYPE_HSPA: - case TelephonyManager.NETWORK_TYPE_HSDPA: - case TelephonyManager.NETWORK_TYPE_HSUPA: - case TelephonyManager.NETWORK_TYPE_EVDO_A: - case TelephonyManager.NETWORK_TYPE_EVDO_B: - case TelephonyManager.NETWORK_TYPE_EHRPD: - return NetworkType.NETWORK_3_5_G; - case TelephonyManager.NETWORK_TYPE_HSPAP: - return NetworkType.NETWORK_3_75_G; - case TelephonyManager.NETWORK_TYPE_LTE: - return NetworkType.NETWORK_3_9_G; - case TelephonyManager.NETWORK_TYPE_UNKNOWN: - default: - Log.w(LOGTAG, "Connected to an unknown mobile network!"); - return NetworkType.NETWORK_UNKNOWN; - } - } - - private static double getNetworkSpeed(NetworkType aType) { - switch (aType) { - case NETWORK_NONE: - return 0.0; - case NETWORK_ETHERNET: - return kNetworkSpeedEthernet; - case NETWORK_WIFI: - return kNetworkSpeedWifi; - case NETWORK_WIMAX: - return kNetworkSpeedWiMax; - case NETWORK_2_G: - return kNetworkSpeed_2_G; - case NETWORK_2_5_G: - return kNetworkSpeed_2_5_G; - case NETWORK_2_75_G: - return kNetworkSpeed_2_75_G; - case NETWORK_3_G: - return kNetworkSpeed_3_G; - case NETWORK_3_5_G: - return kNetworkSpeed_3_5_G; - case NETWORK_3_75_G: - return kNetworkSpeed_3_75_G; - case NETWORK_3_9_G: - return kNetworkSpeed_3_9_G; - case NETWORK_UNKNOWN: - default: - return kDefaultBandwidth; - } - } - - private static boolean isNetworkUsuallyMetered(NetworkType aType) { - switch (aType) { - case NETWORK_NONE: - case NETWORK_UNKNOWN: - case NETWORK_ETHERNET: - case NETWORK_WIFI: - case NETWORK_WIMAX: - return false; - case NETWORK_2_G: - case NETWORK_2_5_G: - case NETWORK_2_75_G: - case NETWORK_3_G: - case NETWORK_3_5_G: - case NETWORK_3_75_G: - case NETWORK_3_9_G: - return true; - default: - Log.e(LOGTAG, "Got an unexpected network type!"); - return false; - } - } -} diff --git a/mobile/android/base/GeckoPreferenceFragment.java b/mobile/android/base/GeckoPreferenceFragment.java deleted file mode 100644 index 8545618ec..000000000 --- a/mobile/android/base/GeckoPreferenceFragment.java +++ /dev/null @@ -1,45 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import android.preference.Preference; -import android.preference.PreferenceCategory; -import android.preference.PreferenceFragment; -import android.preference.PreferenceScreen; -import android.os.Bundle; -import android.util.Log; - -/* A simple implementation of PreferenceFragment for large screen devices - * This will strip category headers (so that they aren't shown to the user twice) - * as well as initializing Gecko prefs when a fragment is shown. -*/ -public class GeckoPreferenceFragment extends PreferenceFragment { - - private static final String LOGTAG = "GeckoPreferenceFragment"; - private int mPrefsRequestId = 0; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - String resource = getArguments().getString("resource"); - int res = getActivity().getResources().getIdentifier(resource, - "xml", - getActivity().getPackageName()); - addPreferencesFromResource(res); - - PreferenceScreen screen = getPreferenceScreen(); - setPreferenceScreen(screen); - mPrefsRequestId = ((GeckoPreferences)getActivity()).setupPreferences(screen); - } - - @Override - public void onDestroy() { - super.onDestroy(); - if (mPrefsRequestId > 0) { - PrefsHelper.removeObserver(mPrefsRequestId); - } - } -} diff --git a/mobile/android/base/GeckoPreferences.java b/mobile/android/base/GeckoPreferences.java deleted file mode 100644 index 0986be50c..000000000 --- a/mobile/android/base/GeckoPreferences.java +++ /dev/null @@ -1,729 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import org.mozilla.gecko.background.common.GlobalConstants; -import org.mozilla.gecko.background.healthreport.HealthReportConstants; -import org.mozilla.gecko.util.GeckoEventListener; -import org.mozilla.gecko.GeckoPreferenceFragment; -import org.mozilla.gecko.util.ThreadUtils; - -import org.json.JSONArray; -import org.json.JSONObject; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.Fragment; -import android.app.NotificationManager; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Build; -import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.EditTextPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceChangeListener; -import android.preference.PreferenceActivity; -import android.preference.PreferenceGroup; -import android.preference.PreferenceManager; -import android.preference.PreferenceScreen; -import android.preference.TwoStatePreference; -import android.text.Editable; -import android.text.InputType; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.util.Log; -import android.view.MenuItem; -import android.view.View; -import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.Toast; - -import java.util.ArrayList; -import java.util.List; - -public class GeckoPreferences - extends PreferenceActivity - implements OnPreferenceChangeListener, GeckoEventListener, GeckoActivityStatus -{ - private static final String LOGTAG = "GeckoPreferences"; - - private static final String NON_PREF_PREFIX = "android.not_a_preference."; - public static final String INTENT_EXTRA_RESOURCES = "resource"; - public static String PREFS_HEALTHREPORT_UPLOAD_ENABLED = NON_PREF_PREFIX + "healthreport.uploadEnabled"; - - private static boolean sIsCharEncodingEnabled = false; - private boolean mInitialized = false; - private int mPrefsRequestId = 0; - - // These match keys in resources/xml/preferences.xml.in. - private static String PREFS_DATA_REPORTING_PREFERENCES = NON_PREF_PREFIX + "datareporting.preferences"; - private static String PREFS_TELEMETRY_ENABLED = "datareporting.telemetry.enabled"; - private static String PREFS_MENU_CHAR_ENCODING = "browser.menu.showCharacterEncoding"; - private static String PREFS_MP_ENABLED = "privacy.masterpassword.enabled"; - private static String PREFS_UPDATER_AUTODOWNLOAD = "app.update.autodownload"; - private static String PREFS_HEALTHREPORT_LINK = NON_PREF_PREFIX + "healthreport.link"; - - public static String PREFS_RESTORE_SESSION = NON_PREF_PREFIX + "restoreSession"; - - // These values are chosen to be distinct from other Activity constants. - private static int REQUEST_CODE_PREF_SCREEN = 5; - private static int RESULT_CODE_EXIT_SETTINGS = 6; - - @Override - protected void onCreate(Bundle savedInstanceState) { - // For fragment-capable devices, display the default fragment if no explicit fragment to show. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && - !getIntent().hasExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT)) { - setupTopLevelFragmentIntent(); - } - - super.onCreate(savedInstanceState); - - // Use setResourceToOpen to specify these extras. - Bundle intentExtras = getIntent().getExtras(); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { - if (intentExtras != null && intentExtras.containsKey(INTENT_EXTRA_RESOURCES)) { - String resourceName = intentExtras.getString(INTENT_EXTRA_RESOURCES); - int resource = getResources().getIdentifier(resourceName, "xml", getPackageName()); - addPreferencesFromResource(resource); - } else { - addPreferencesFromResource(R.xml.preferences); - } - } - - registerEventListener("Sanitize:Finished"); - - if (Build.VERSION.SDK_INT >= 14) - getActionBar().setHomeButtonEnabled(true); - - // If launched from notification, explicitly cancel the notification. - if (intentExtras != null && intentExtras.containsKey(DataReportingNotification.ALERT_NAME_DATAREPORTING_NOTIFICATION)) { - NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE); - notificationManager.cancel(DataReportingNotification.ALERT_NAME_DATAREPORTING_NOTIFICATION.hashCode()); - } - } - - /** - * Set intent to display top-level settings fragment. - */ - private void setupTopLevelFragmentIntent() { - Intent intent = getIntent(); - // Check intent to determine settings screen to display. - Bundle intentExtras = intent.getExtras(); - Bundle fragmentArgs = new Bundle(); - // Add resource argument to fragment if it exists. - if (intentExtras != null && intentExtras.containsKey(INTENT_EXTRA_RESOURCES)) { - String resource = intentExtras.getString(INTENT_EXTRA_RESOURCES); - fragmentArgs.putString(INTENT_EXTRA_RESOURCES, resource); - } else { - // Use top-level settings screen. - if (!onIsMultiPane()) { - fragmentArgs.putString(INTENT_EXTRA_RESOURCES, "preferences"); - } else { - fragmentArgs.putString(INTENT_EXTRA_RESOURCES, "preferences_general"); - } - } - - // Build fragment intent. - intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, GeckoPreferenceFragment.class.getName()); - intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, fragmentArgs); - } - - @Override - public void onBuildHeaders(List<Header> target) { - if (onIsMultiPane()) - loadHeadersFromResource(R.xml.preference_headers, target); - } - - @Override - public void onWindowFocusChanged(boolean hasFocus) { - if (!hasFocus || mInitialized) - return; - - mInitialized = true; - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { - PreferenceScreen screen = getPreferenceScreen(); - mPrefsRequestId = setupPreferences(screen); - } - } - - @Override - protected void onDestroy() { - super.onDestroy(); - unregisterEventListener("Sanitize:Finished"); - if (mPrefsRequestId > 0) { - PrefsHelper.removeObserver(mPrefsRequestId); - } - } - - @Override - public void onPause() { - super.onPause(); - - if (getApplication() instanceof GeckoApplication) { - ((GeckoApplication) getApplication()).onActivityPause(this); - } - } - - @Override - public void onResume() { - super.onResume(); - - if (getApplication() instanceof GeckoApplication) { - ((GeckoApplication) getApplication()).onActivityResume(this); - } - } - - @Override - public void startActivity(Intent intent) { - // For settings, we want to be able to pass results up the chain - // of preference screens so Settings can behave as a single unit. - // Specifically, when we open a link, we want to back out of all - // the settings screens. - // We need to start nested PreferenceScreens withStartActivityForResult(). - // Android doesn't let us do that (see Preference.onClick), so we're overriding here. - startActivityForResult(intent, REQUEST_CODE_PREF_SCREEN); - } - - @Override - public void startWithFragment(String fragmentName, Bundle args, - Fragment resultTo, int resultRequestCode, int titleRes, int shortTitleRes) { - // Overriding because we want to use startActivityForResult for Fragment intents. - Intent intent = onBuildStartFragmentIntent(fragmentName, args, titleRes, shortTitleRes); - if (resultTo == null) { - startActivityForResult(intent, REQUEST_CODE_PREF_SCREEN); - } else { - resultTo.startActivityForResult(intent, resultRequestCode); - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (REQUEST_CODE_PREF_SCREEN == requestCode && RESULT_CODE_EXIT_SETTINGS == resultCode) { - // Pass this result up to the parent activity. - setResult(RESULT_CODE_EXIT_SETTINGS); - finish(); - } - } - - @Override - public void handleMessage(String event, JSONObject message) { - try { - if (event.equals("Sanitize:Finished")) { - boolean success = message.getBoolean("success"); - final int stringRes = success ? R.string.private_data_success : R.string.private_data_fail; - final Context context = this; - ThreadUtils.postToUiThread(new Runnable () { - @Override - public void run() { - Toast.makeText(context, stringRes, Toast.LENGTH_SHORT).show(); - } - }); - } - } catch (Exception e) { - Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e); - } - } - - /** - * Initialize all of the preferences (native of Gecko ones) for this screen. - * - * @param prefs The android.preference.PreferenceGroup to initialize - * @return The integer id for the PrefsHelper.PrefHandlerBase listener added - * to monitor changes to Gecko prefs. - */ - public int setupPreferences(PreferenceGroup prefs) { - ArrayList<String> list = new ArrayList<String>(); - setupPreferences(prefs, list); - return getGeckoPreferences(prefs, list); - } - - /** - * Recursively loop through a PreferenceGroup. Initialize native Android prefs, - * and build a list of Gecko preferences in the passed in prefs array - * - * @param preferences The android.preference.PreferenceGroup to initialize - * @param prefs An ArrayList to fill with Gecko preferences that need to be - * initialized - * @return The integer id for the PrefsHelper.PrefHandlerBase listener added - * to monitor changes to Gecko prefs. - */ - private void setupPreferences(PreferenceGroup preferences, ArrayList<String> prefs) { - for (int i = 0; i < preferences.getPreferenceCount(); i++) { - Preference pref = preferences.getPreference(i); - String key = pref.getKey(); - if (pref instanceof PreferenceGroup) { - // If no datareporting is enabled, remove UI. - if (PREFS_DATA_REPORTING_PREFERENCES.equals(key)) { - if (!AppConstants.MOZ_DATA_REPORTING) { - preferences.removePreference(pref); - i--; - continue; - } - } - setupPreferences((PreferenceGroup) pref, prefs); - } else { - pref.setOnPreferenceChangeListener(this); - if (!AppConstants.MOZ_UPDATER && - PREFS_UPDATER_AUTODOWNLOAD.equals(key)) { - preferences.removePreference(pref); - i--; - continue; - } else if (!AppConstants.MOZ_TELEMETRY_REPORTING && - PREFS_TELEMETRY_ENABLED.equals(key)) { - preferences.removePreference(pref); - i--; - continue; - } else if (!AppConstants.MOZ_SERVICES_HEALTHREPORT && - (PREFS_HEALTHREPORT_UPLOAD_ENABLED.equals(key) || - PREFS_HEALTHREPORT_LINK.equals(key))) { - preferences.removePreference(pref); - i--; - continue; - } - - // Some Preference UI elements are not actually preferences, - // but they require a key to work correctly. For example, - // "Clear private data" requires a key for its state to be - // saved when the orientation changes. It uses the - // "android.not_a_preference.privacy.clear" key - which doesn't - // exist in Gecko - to satisfy this requirement. - if (key != null && !key.startsWith(NON_PREF_PREFIX)) { - prefs.add(key); - } - } - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - finish(); - return true; - } - - return super.onOptionsItemSelected(item); - } - - final private int DIALOG_CREATE_MASTER_PASSWORD = 0; - final private int DIALOG_REMOVE_MASTER_PASSWORD = 1; - - public static void setCharEncodingState(boolean enabled) { - sIsCharEncodingEnabled = enabled; - } - - public static boolean getCharEncodingState() { - return sIsCharEncodingEnabled; - } - - /** - * Broadcast an intent with <code>pref</code>, <code>branch</code>, and - * <code>enabled</code> extras. This is intended to represent the - * notification of a preference value to observers. - * - * The broadcast will be sent only to receivers registered with the - * (Fennec-specific) per-Android package permission. - */ - public static void broadcastPrefAction(final Context context, - final String action, - final String pref, - final boolean value) { - final Intent intent = new Intent(action); - intent.setAction(action); - intent.putExtra("pref", pref); - intent.putExtra("branch", GeckoApp.PREFS_NAME); - intent.putExtra("enabled", value); - - // There is a race here, but GeckoProfile returns the default profile - // when Gecko is not explicitly running for a different profile. In a - // multi-profile world, this will need to be updated (possibly to - // broadcast settings for all profiles). See Bug 882182. - GeckoProfile profile = GeckoProfile.get(context); - if (profile != null) { - intent.putExtra("profileName", profile.getName()); - intent.putExtra("profilePath", profile.getDir().getAbsolutePath()); - } - - Log.d(LOGTAG, "Broadcast: " + action + ", " + pref + ", " + GeckoApp.PREFS_NAME + ", " + value); - context.sendBroadcast(intent, GlobalConstants.PER_ANDROID_PACKAGE_PERMISSION); - } - - /** - * Broadcast the provided value as the value of the - * <code>PREFS_HEALTHREPORT_UPLOAD_ENABLED</code> pref. - */ - public static void broadcastHealthReportUploadPref(final Context context, final boolean value) { - broadcastPrefAction(context, - HealthReportConstants.ACTION_HEALTHREPORT_UPLOAD_PREF, - PREFS_HEALTHREPORT_UPLOAD_ENABLED, - value); - } - - /** - * Broadcast the current value of the - * <code>PREFS_HEALTHREPORT_UPLOAD_ENABLED</code> pref. - */ - public static void broadcastHealthReportUploadPref(final Context context) { - final boolean value = getBooleanPref(context, PREFS_HEALTHREPORT_UPLOAD_ENABLED, true); - broadcastHealthReportUploadPref(context, value); - } - - /** - * Return the value of the named preference in the default preferences file. - * - * This corresponds to the storage that backs preferences.xml. - * @param context a <code>Context</code>; the - * <code>PreferenceActivity</code> will suffice, but this - * method is intended to be called from other contexts - * within the application, not just this <code>Activity</code>. - * @param name the name of the preference to retrieve. - * @param def the default value to return if the preference is not present. - * @return the value of the preference, or the default. - */ - public static boolean getBooleanPref(final Context context, final String name, boolean def) { - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - return prefs.getBoolean(name, def); - } - - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - String prefName = preference.getKey(); - if (PREFS_MP_ENABLED.equals(prefName)) { - showDialog((Boolean) newValue ? DIALOG_CREATE_MASTER_PASSWORD : DIALOG_REMOVE_MASTER_PASSWORD); - return false; - } else if (PREFS_MENU_CHAR_ENCODING.equals(prefName)) { - setCharEncodingState(((String) newValue).equals("true")); - } else if (PREFS_UPDATER_AUTODOWNLOAD.equals(prefName)) { - org.mozilla.gecko.updater.UpdateServiceHelper.registerForUpdates(GeckoAppShell.getContext(), (String) newValue); - } else if (PREFS_HEALTHREPORT_UPLOAD_ENABLED.equals(prefName)) { - // The healthreport pref only lives in Android, so we do not persist - // to Gecko, but we do broadcast intent to the health report - // background uploader service, which will start or stop the - // repeated background upload attempts. - broadcastHealthReportUploadPref(GeckoAppShell.getContext(), ((Boolean) newValue).booleanValue()); - return true; - } else if (PREFS_RESTORE_SESSION.equals(prefName)) { - // Do nothing; this pref will be read at startup. - return true; - } - - if (!TextUtils.isEmpty(prefName)) { - PrefsHelper.setPref(prefName, newValue); - } - if (preference instanceof ListPreference) { - // We need to find the entry for the new value - int newIndex = ((ListPreference) preference).findIndexOfValue((String) newValue); - CharSequence newEntry = ((ListPreference) preference).getEntries()[newIndex]; - ((ListPreference) preference).setSummary(newEntry); - } else if (preference instanceof LinkPreference) { - setResult(RESULT_CODE_EXIT_SETTINGS); - finish(); - } else if (preference instanceof FontSizePreference) { - final FontSizePreference fontSizePref = (FontSizePreference) preference; - fontSizePref.setSummary(fontSizePref.getSavedFontSizeName()); - } - return true; - } - - private EditText getTextBox(int aHintText) { - EditText input = new EditText(GeckoAppShell.getContext()); - int inputtype = InputType.TYPE_CLASS_TEXT; - inputtype |= InputType.TYPE_TEXT_VARIATION_PASSWORD | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; - input.setInputType(inputtype); - - String hint = getResources().getString(aHintText); - input.setHint(aHintText); - return input; - } - - private class PasswordTextWatcher implements TextWatcher { - EditText input1 = null; - EditText input2 = null; - AlertDialog dialog = null; - - PasswordTextWatcher(EditText aInput1, EditText aInput2, AlertDialog aDialog) { - input1 = aInput1; - input2 = aInput2; - dialog = aDialog; - } - - @Override - public void afterTextChanged(Editable s) { - if (dialog == null) - return; - - String text1 = input1.getText().toString(); - String text2 = input2.getText().toString(); - boolean disabled = TextUtils.isEmpty(text1) || TextUtils.isEmpty(text2) || !text1.equals(text2); - dialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(!disabled); - } - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { } - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { } - } - - private class EmptyTextWatcher implements TextWatcher { - EditText input = null; - AlertDialog dialog = null; - - EmptyTextWatcher(EditText aInput, AlertDialog aDialog) { - input = aInput; - dialog = aDialog; - } - - @Override - public void afterTextChanged(Editable s) { - if (dialog == null) - return; - - String text = input.getText().toString(); - boolean disabled = TextUtils.isEmpty(text); - dialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(!disabled); - } - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { } - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { } - } - - @Override - protected Dialog onCreateDialog(int id) { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - LinearLayout linearLayout = new LinearLayout(this); - linearLayout.setOrientation(LinearLayout.VERTICAL); - AlertDialog dialog = null; - switch(id) { - case DIALOG_CREATE_MASTER_PASSWORD: - final EditText input1 = getTextBox(R.string.masterpassword_password); - final EditText input2 = getTextBox(R.string.masterpassword_confirm); - linearLayout.addView(input1); - linearLayout.addView(input2); - - builder.setTitle(R.string.masterpassword_create_title) - .setView((View) linearLayout) - .setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - JSONObject jsonPref = new JSONObject(); - try { - jsonPref.put("name", PREFS_MP_ENABLED); - jsonPref.put("type", "string"); - jsonPref.put("value", input1.getText().toString()); - - GeckoEvent event = GeckoEvent.createBroadcastEvent("Preferences:Set", jsonPref.toString()); - GeckoAppShell.sendEventToGecko(event); - } catch(Exception ex) { - Log.e(LOGTAG, "Error setting master password", ex); - } - return; - } - }) - .setNegativeButton(R.string.button_cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - return; - } - }); - dialog = builder.create(); - dialog.setOnShowListener(new DialogInterface.OnShowListener() { - @Override - public void onShow(DialogInterface dialog) { - input1.setText(""); - input2.setText(""); - input1.requestFocus(); - } - }); - - PasswordTextWatcher watcher = new PasswordTextWatcher(input1, input2, dialog); - input1.addTextChangedListener((TextWatcher) watcher); - input2.addTextChangedListener((TextWatcher) watcher); - - break; - case DIALOG_REMOVE_MASTER_PASSWORD: - final EditText input = getTextBox(R.string.masterpassword_password); - linearLayout.addView(input); - - builder.setTitle(R.string.masterpassword_remove_title) - .setView((View) linearLayout) - .setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - PrefsHelper.setPref(PREFS_MP_ENABLED, input.getText().toString()); - } - }) - .setNegativeButton(R.string.button_cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - return; - } - }); - dialog = builder.create(); - dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { - @Override - public void onDismiss(DialogInterface dialog) { - input.setText(""); - } - }); - dialog.setOnShowListener(new DialogInterface.OnShowListener() { - @Override - public void onShow(DialogInterface dialog) { - input.setText(""); - } - }); - input.addTextChangedListener(new EmptyTextWatcher(input, dialog)); - break; - default: - return null; - } - - return dialog; - } - - // Initialize preferences by requesting the preference values from Gecko - private int getGeckoPreferences(final PreferenceGroup screen, ArrayList<String> prefs) { - JSONArray jsonPrefs = new JSONArray(prefs); - - return PrefsHelper.getPrefs(jsonPrefs, new PrefsHelper.PrefHandlerBase() { - private Preference getField(String prefName) { - return screen.findPreference(prefName); - } - - // Handle v14 TwoStatePreference with backwards compatibility. - class CheckBoxPrefSetter { - public void setBooleanPref(Preference preference, boolean value) { - if ((preference instanceof CheckBoxPreference) && - ((CheckBoxPreference) preference).isChecked() != value) { - ((CheckBoxPreference) preference).setChecked(value); - } - } - } - - class TwoStatePrefSetter extends CheckBoxPrefSetter { - @Override - public void setBooleanPref(Preference preference, boolean value) { - if ((preference instanceof TwoStatePreference) && - ((TwoStatePreference) preference).isChecked() != value) { - ((TwoStatePreference) preference).setChecked(value); - } - } - } - - @Override - public void prefValue(String prefName, final boolean value) { - final Preference pref = getField(prefName); - final CheckBoxPrefSetter prefSetter; - if (Build.VERSION.SDK_INT < 14) { - prefSetter = new CheckBoxPrefSetter(); - } else { - prefSetter = new TwoStatePrefSetter(); - } - ThreadUtils.postToUiThread(new Runnable() { - public void run() { - prefSetter.setBooleanPref(pref, value); - } - }); - } - - @Override - public void prefValue(String prefName, final String value) { - final Preference pref = getField(prefName); - if (pref instanceof EditTextPreference) { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - ((EditTextPreference) pref).setText(value); - } - }); - } else if (pref instanceof ListPreference) { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - ((ListPreference) pref).setValue(value); - // Set the summary string to the current entry - CharSequence selectedEntry = ((ListPreference) pref).getEntry(); - ((ListPreference) pref).setSummary(selectedEntry); - } - }); - } else if (pref instanceof FontSizePreference) { - final FontSizePreference fontSizePref = (FontSizePreference) pref; - fontSizePref.setSavedFontSize(value); - final String fontSizeName = fontSizePref.getSavedFontSizeName(); - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - fontSizePref.setSummary(fontSizeName); // Ex: "Small". - } - }); - } - } - - @Override - public boolean isObserver() { - return true; - } - - @Override - public void finish() { - // enable all preferences once we have them from gecko - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - screen.setEnabled(true); - } - }); - } - }); - } - - private void registerEventListener(String event) { - GeckoAppShell.getEventDispatcher().registerEventListener(event, this); - } - - private void unregisterEventListener(String event) { - GeckoAppShell.getEventDispatcher().unregisterEventListener(event, this); - } - - @Override - public boolean isGeckoActivityOpened() { - return false; - } - - /** - * Given an Intent instance, add extras to specify which settings section to - * open. - * - * resource should be a valid Android XML resource identifier. - * - * The mechanism to open a section differs based on Android version. - */ - public static void setResourceToOpen(final Intent intent, final String resource) { - if (intent == null) { - throw new IllegalArgumentException("intent must not be null"); - } - if (resource == null) { - return; - } - - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { - intent.putExtra("resource", resource); - } else { - intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, GeckoPreferenceFragment.class.getName()); - - Bundle fragmentArgs = new Bundle(); - fragmentArgs.putString("resource", resource); - intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, fragmentArgs); - } - } -} diff --git a/mobile/android/base/GeckoProfile.java b/mobile/android/base/GeckoProfile.java deleted file mode 100644 index f40264c6f..000000000 --- a/mobile/android/base/GeckoProfile.java +++ /dev/null @@ -1,399 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import org.mozilla.gecko.util.INIParser; -import org.mozilla.gecko.util.INISection; - -import android.content.Context; -import android.text.TextUtils; -import android.util.Log; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.nio.charset.Charset; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Hashtable; - -public final class GeckoProfile { - private static final String LOGTAG = "GeckoProfile"; - - private static HashMap<String, GeckoProfile> sProfileCache = new HashMap<String, GeckoProfile>(); - private static String sDefaultProfileName = null; - - private final Context mContext; - private final String mName; - private File mMozDir; - private File mDir; - - static private INIParser getProfilesINI(Context context) { - File filesDir = context.getFilesDir(); - File mozillaDir = new File(filesDir, "mozilla"); - File profilesIni = new File(mozillaDir, "profiles.ini"); - return new INIParser(profilesIni); - } - - public static GeckoProfile get(Context context) { - if (context instanceof GeckoApp) - return get(context, ((GeckoApp)context).getDefaultProfileName()); - - return get(context, ""); - } - - public static GeckoProfile get(Context context, String profileName) { - synchronized (sProfileCache) { - GeckoProfile profile = sProfileCache.get(profileName); - if (profile != null) - return profile; - } - return get(context, profileName, null); - } - - public static GeckoProfile get(Context context, String profileName, String profilePath) { - if (context == null) { - throw new IllegalArgumentException("context must be non-null"); - } - - // if no profile was passed in, look for the default profile listed in profiles.ini - // if that doesn't exist, look for a profile called 'default' - if (TextUtils.isEmpty(profileName) && TextUtils.isEmpty(profilePath)) { - profileName = GeckoProfile.findDefaultProfile(context); - if (profileName == null) - profileName = "default"; - } - - // actually try to look up the profile - synchronized (sProfileCache) { - GeckoProfile profile = sProfileCache.get(profileName); - if (profile == null) { - profile = new GeckoProfile(context, profileName, profilePath); - sProfileCache.put(profileName, profile); - } else { - profile.setDir(profilePath); - } - return profile; - } - } - - public static File ensureMozillaDirectory(Context context) throws IOException { - synchronized (context) { - File filesDir = context.getFilesDir(); - File mozDir = new File(filesDir, "mozilla"); - if (! mozDir.exists()) { - if (! mozDir.mkdirs()) { - throw new IOException("Unable to create mozilla directory at " + mozDir.getAbsolutePath()); - } - } - return mozDir; - } - } - - public static boolean removeProfile(Context context, String profileName) { - return new GeckoProfile(context, profileName).remove(); - } - - private GeckoProfile(Context context, String profileName) { - mContext = context; - mName = profileName; - } - - private GeckoProfile(Context context, String profileName, String profilePath) { - mContext = context; - mName = profileName; - setDir(profilePath); - } - - private void setDir(String profilePath) { - if (!TextUtils.isEmpty(profilePath)) { - File dir = new File(profilePath); - if (dir.exists() && dir.isDirectory()) { - mDir = dir; - } else { - Log.w(LOGTAG, "requested profile directory missing: " + profilePath); - } - } - } - - public String getName() { - return mName; - } - - public synchronized File getDir() { - if (mDir != null) { - return mDir; - } - - try { - // Check for old profiles that may need migration. - ProfileMigrator profileMigrator = new ProfileMigrator(mContext); - if (!GeckoApp.sIsUsingCustomProfile && - !profileMigrator.isProfileMoved()) { - Log.i(LOGTAG, "New installation or update, checking for old profiles."); - profileMigrator.launchMoveProfile(); - } - - // now check if a profile with this name that already exists - File mozillaDir = ensureMozillaDirectory(mContext); - mDir = findProfileDir(mozillaDir); - if (mDir == null) { - // otherwise create it - mDir = createProfileDir(mozillaDir); - } else { - Log.d(LOGTAG, "Found profile dir: " + mDir.getAbsolutePath()); - } - } catch (IOException ioe) { - Log.e(LOGTAG, "Error getting profile dir", ioe); - } - return mDir; - } - - public File getFile(String aFile) { - File f = getDir(); - if (f == null) - return null; - - return new File(f, aFile); - } - - public File getFilesDir() { - return mContext.getFilesDir(); - } - - /** - * Moves the session file to the backup session file. - * - * sessionstore.js should hold the current session, and sessionstore.bak - * should hold the previous session (where it is used to read the "tabs - * from last time"). Normally, sessionstore.js is moved to sessionstore.bak - * on a clean quit, but this doesn't happen if Fennec crashed. Thus, this - * method should be called after a crash so sessionstore.bak correctly - * holds the previous session. - */ - public void moveSessionFile() { - File sessionFile = getFile("sessionstore.js"); - if (sessionFile != null && sessionFile.exists()) { - File sessionFileBackup = getFile("sessionstore.bak"); - sessionFile.renameTo(sessionFileBackup); - } - } - - /** - * Get the string from a session file. - * - * The session can either be read from sessionstore.js or sessionstore.bak. - * In general, sessionstore.js holds the current session, and - * sessionstore.bak holds the previous session. - * - * @param readBackup if true, the session is read from sessionstore.bak; - * otherwise, the session is read from sessionstore.js - * - * @return the session string - */ - public String readSessionFile(boolean readBackup) { - File sessionFile = getFile(readBackup ? "sessionstore.bak" : "sessionstore.js"); - - try { - if (sessionFile != null && sessionFile.exists()) { - return readFile(sessionFile); - } - } catch (IOException ioe) { - Log.e(LOGTAG, "Unable to read session file", ioe); - } - return null; - } - - public String readFile(String filename) throws IOException { - File dir = getDir(); - if (dir == null) { - throw new IOException("No profile directory found"); - } - File target = new File(dir, filename); - return readFile(target); - } - - private String readFile(File target) throws IOException { - FileReader fr = new FileReader(target); - try { - StringBuffer sb = new StringBuffer(); - char[] buf = new char[8192]; - int read = fr.read(buf); - while (read >= 0) { - sb.append(buf, 0, read); - read = fr.read(buf); - } - return sb.toString(); - } finally { - fr.close(); - } - } - - private boolean remove() { - try { - File mozillaDir = ensureMozillaDirectory(mContext); - mDir = findProfileDir(mozillaDir); - if (mDir == null) - return false; - - INIParser parser = getProfilesINI(mContext); - - Hashtable<String, INISection> sections = parser.getSections(); - for (Enumeration<INISection> e = sections.elements(); e.hasMoreElements();) { - INISection section = e.nextElement(); - String name = section.getStringProperty("Name"); - - if (name == null || !name.equals(mName)) - continue; - - if (section.getName().startsWith("Profile")) { - // ok, we have stupid Profile#-named things. Rename backwards. - try { - int sectionNumber = Integer.parseInt(section.getName().substring("Profile".length())); - String curSection = "Profile" + sectionNumber; - String nextSection = "Profile" + (sectionNumber+1); - - sections.remove(curSection); - - while (sections.containsKey(nextSection)) { - parser.renameSection(nextSection, curSection); - sectionNumber++; - - curSection = nextSection; - nextSection = "Profile" + (sectionNumber+1); - } - } catch (NumberFormatException nex) { - // uhm, malformed Profile thing; we can't do much. - Log.e(LOGTAG, "Malformed section name in profiles.ini: " + section.getName()); - return false; - } - } else { - // this really shouldn't be the case, but handle it anyway - parser.removeSection(mName); - return true; - } - } - - parser.write(); - return true; - } catch (IOException ex) { - Log.w(LOGTAG, "Failed to remove profile " + mName + ":\n" + ex); - return false; - } - } - - public static String findDefaultProfile(Context context) { - // Have we read the default profile from the INI already? - // Changing the default profile requires a restart, so we don't - // need to worry about runtime changes. - if (sDefaultProfileName != null) { - return sDefaultProfileName; - } - - // Open profiles.ini to find the correct path - INIParser parser = getProfilesINI(context); - - for (Enumeration<INISection> e = parser.getSections().elements(); e.hasMoreElements();) { - INISection section = e.nextElement(); - if (section.getIntProperty("Default") == 1) { - sDefaultProfileName = section.getStringProperty("Name"); - return sDefaultProfileName; - } - } - - return null; - } - - private File findProfileDir(File mozillaDir) { - // Open profiles.ini to find the correct path - INIParser parser = getProfilesINI(mContext); - - for (Enumeration<INISection> e = parser.getSections().elements(); e.hasMoreElements();) { - INISection section = e.nextElement(); - String name = section.getStringProperty("Name"); - if (name != null && name.equals(mName)) { - if (section.getIntProperty("IsRelative") == 1) { - return new File(mozillaDir, section.getStringProperty("Path")); - } - return new File(section.getStringProperty("Path")); - } - } - - return null; - } - - private static String saltProfileName(String name) { - String allowedChars = "abcdefghijklmnopqrstuvwxyz0123456789"; - StringBuffer salt = new StringBuffer(16); - for (int i = 0; i < 8; i++) { - salt.append(allowedChars.charAt((int)(Math.random() * allowedChars.length()))); - } - salt.append('.'); - salt.append(name); - return salt.toString(); - } - - private File createProfileDir(File mozillaDir) throws IOException { - INIParser parser = getProfilesINI(mContext); - - // Salt the name of our requested profile - String saltedName = saltProfileName(mName); - File profileDir = new File(mozillaDir, saltedName); - while (profileDir.exists()) { - saltedName = saltProfileName(mName); - profileDir = new File(mozillaDir, saltedName); - } - - // Attempt to create the salted profile dir - if (! profileDir.mkdirs()) { - throw new IOException("Unable to create profile at " + profileDir.getAbsolutePath()); - } - Log.d(LOGTAG, "Created new profile dir at " + profileDir.getAbsolutePath()); - - // Now update profiles.ini - // If this is the first time its created, we also add a General section - // look for the first profile number that isn't taken yet - int profileNum = 0; - while (parser.getSection("Profile" + profileNum) != null) { - profileNum++; - } - - INISection profileSection = new INISection("Profile" + profileNum); - profileSection.setProperty("Name", mName); - profileSection.setProperty("IsRelative", 1); - profileSection.setProperty("Path", saltedName); - - if (parser.getSection("General") == null) { - INISection generalSection = new INISection("General"); - generalSection.setProperty("StartWithLastProfile", 1); - parser.addSection(generalSection); - - // only set as default if this is the first profile we're creating - profileSection.setProperty("Default", 1); - } - - parser.addSection(profileSection); - parser.write(); - - // Write out profile creation time, mirroring the logic in nsToolkitProfileService. - try { - FileOutputStream stream = new FileOutputStream(profileDir.getAbsolutePath() + File.separator + "times.json"); - OutputStreamWriter writer = new OutputStreamWriter(stream, Charset.forName("UTF-8")); - try { - writer.append("{\"created\": " + System.currentTimeMillis() + "}\n"); - } finally { - writer.close(); - } - } catch (Exception e) { - // Best-effort. - Log.w(LOGTAG, "Couldn't write times.json.", e); - } - - return profileDir; - } -} diff --git a/mobile/android/base/GeckoScreenOrientationListener.java b/mobile/android/base/GeckoScreenOrientationListener.java deleted file mode 100644 index e4ceecad3..000000000 --- a/mobile/android/base/GeckoScreenOrientationListener.java +++ /dev/null @@ -1,215 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import android.content.Context; -import android.content.pm.ActivityInfo; -import android.util.Log; -import android.view.OrientationEventListener; -import android.view.Surface; - -import java.util.Arrays; -import java.util.List; - -import android.app.Activity; - -public class GeckoScreenOrientationListener { - private static final String LOGTAG = "GeckoScreenOrientationListener"; - - static class OrientationEventListenerImpl extends OrientationEventListener { - public OrientationEventListenerImpl(Context c) { - super(c); - } - - @Override - public void onOrientationChanged(int aOrientation) { - GeckoScreenOrientationListener.getInstance().updateScreenOrientation(); - } - } - - static private GeckoScreenOrientationListener sInstance = null; - - // Make sure that any change in dom/base/ScreenOrientation.h happens here too. - static public final short eScreenOrientation_None = 0; - static public final short eScreenOrientation_PortraitPrimary = 1; // PR_BIT(0) - static public final short eScreenOrientation_PortraitSecondary = 2; // PR_BIT(1) - static public final short eScreenOrientation_LandscapePrimary = 4; // PR_BIT(2) - static public final short eScreenOrientation_LandscapeSecondary = 8; // PR_BIT(3) - - static private final short DEFAULT_ORIENTATION = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; - - private short mOrientation; - private OrientationEventListenerImpl mListener = null; - - // Whether the listener should be listening to changes. - private boolean mShouldBeListening = false; - // Whether the listener should notify Gecko that a change happened. - private boolean mShouldNotify = false; - // The default orientation to use if nothing is specified - private short mDefaultOrientation; - - private static final String DEFAULT_ORIENTATION_PREF = "app.orientation.default"; - - private GeckoScreenOrientationListener() { - mListener = new OrientationEventListenerImpl(GeckoAppShell.getContext()); - - PrefsHelper.getPref(DEFAULT_ORIENTATION_PREF, new PrefsHelper.PrefHandlerBase() { - @Override public void prefValue(String pref, String value) { - mDefaultOrientation = orientationFromStringArray(value); - unlockScreenOrientation(); - } - }); - - mDefaultOrientation = DEFAULT_ORIENTATION; - } - - public static GeckoScreenOrientationListener getInstance() { - if (sInstance == null) { - sInstance = new GeckoScreenOrientationListener(); - } - - return sInstance; - } - - public void start() { - mShouldBeListening = true; - updateScreenOrientation(); - - if (mShouldNotify) { - startListening(); - } - } - - public void stop() { - mShouldBeListening = false; - - if (mShouldNotify) { - stopListening(); - } - } - - public void enableNotifications() { - updateScreenOrientation(); - mShouldNotify = true; - - if (mShouldBeListening) { - startListening(); - } - } - - public void disableNotifications() { - mShouldNotify = false; - - if (mShouldBeListening) { - stopListening(); - } - } - - private void startListening() { - mListener.enable(); - } - - private void stopListening() { - mListener.disable(); - } - - private short orientationFromStringArray(String val) { - List<String> orientations = Arrays.asList(val.split(",")); - // if nothing is listed, return unspecified - if (orientations.size() == 0) - return DEFAULT_ORIENTATION; - - // we dont' support multiple orientations yet. To avoid developer confusion, - // just take the first one listed - return orientationFromString(orientations.get(0)); - } - - private short orientationFromString(String val) { - if ("portrait".equals(val)) - return (short)ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; - else if ("landscape".equals(val)) - return (short)ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; - else if ("portrait-primary".equals(val)) - return (short)ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; - else if ("portrait-secondary".equals(val)) - return (short)ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; - else if ("landscape-primary".equals(val)) - return (short)ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; - else if ("landscape-secondary".equals(val)) - return (short)ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; - return DEFAULT_ORIENTATION; - } - - private void updateScreenOrientation() { - Context context = GeckoAppShell.getContext(); - int rotation = mDefaultOrientation; - if (context instanceof Activity) { - rotation = ((Activity)context).getWindowManager().getDefaultDisplay().getRotation(); - } - short previousOrientation = mOrientation; - - if (rotation == Surface.ROTATION_0) { - mOrientation = eScreenOrientation_PortraitPrimary; - } else if (rotation == Surface.ROTATION_180) { - mOrientation = eScreenOrientation_PortraitSecondary; - } else if (rotation == Surface.ROTATION_270) { - mOrientation = eScreenOrientation_LandscapeSecondary; - } else if (rotation == Surface.ROTATION_90) { - mOrientation = eScreenOrientation_LandscapePrimary; - } else { - Log.e(LOGTAG, "Unexpected value received! (" + rotation + ")"); - return; - } - - if (mShouldNotify && mOrientation != previousOrientation) { - GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenOrientationEvent(mOrientation)); - } - } - - public short getScreenOrientation() { - return mOrientation; - } - - public void lockScreenOrientation(int aOrientation) { - int orientation = 0; - - switch (aOrientation) { - case eScreenOrientation_PortraitPrimary: - orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; - break; - case eScreenOrientation_PortraitSecondary: - orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; - break; - case eScreenOrientation_PortraitPrimary | eScreenOrientation_PortraitSecondary: - orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; - break; - case eScreenOrientation_LandscapePrimary: - orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; - break; - case eScreenOrientation_LandscapeSecondary: - orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; - break; - case eScreenOrientation_LandscapePrimary | eScreenOrientation_LandscapeSecondary: - orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; - break; - default: - Log.e(LOGTAG, "Unexpected value received! (" + aOrientation + ")"); - return; - } - if (GeckoAppShell.getContext() instanceof Activity) - ((Activity)GeckoAppShell.getContext()).setRequestedOrientation(orientation); - updateScreenOrientation(); - } - - public void unlockScreenOrientation() { - if (!(GeckoAppShell.getContext() instanceof Activity)) - return; - if (((Activity)GeckoAppShell.getContext()).getRequestedOrientation() == mDefaultOrientation) - return; - - ((Activity)GeckoAppShell.getContext()).setRequestedOrientation(mDefaultOrientation); - updateScreenOrientation(); - } -} diff --git a/mobile/android/base/GeckoSmsManager.java b/mobile/android/base/GeckoSmsManager.java deleted file mode 100644 index 0ab9963ef..000000000 --- a/mobile/android/base/GeckoSmsManager.java +++ /dev/null @@ -1,996 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import android.app.Activity; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.telephony.SmsManager; -import android.telephony.SmsMessage; -import android.util.Log; - -import static android.telephony.SmsMessage.MessageClass; - -import java.util.ArrayList; - -/** - * This class is returning unique ids for PendingIntent requestCode attribute. - * There are only |Integer.MAX_VALUE - Integer.MIN_VALUE| unique IDs available, - * and they wrap around. - */ -class PendingIntentUID -{ - static private int sUID = Integer.MIN_VALUE; - - static public int generate() { return sUID++; } -} - -/** - * The envelope class contains all information that are needed to keep track of - * a sent SMS. - */ -class Envelope -{ - enum SubParts { - SENT_PART, - DELIVERED_PART - } - - protected int mId; - protected int mMessageId; - protected long mMessageTimestamp; - - /** - * Number of sent/delivered remaining parts. - * @note The array has much slots as SubParts items. - */ - protected int[] mRemainingParts; - - /** - * Whether sending/delivering is currently failing. - * @note The array has much slots as SubParts items. - */ - protected boolean[] mFailing; - - /** - * Error type (only for sent). - */ - protected int mError; - - public Envelope(int aId, int aParts) { - mId = aId; - mMessageId = -1; - mMessageTimestamp = 0; - mError = GeckoSmsManager.kNoError; - - int size = Envelope.SubParts.values().length; - mRemainingParts = new int[size]; - mFailing = new boolean[size]; - - for (int i=0; i<size; ++i) { - mRemainingParts[i] = aParts; - mFailing[i] = false; - } - } - - public void decreaseRemainingParts(Envelope.SubParts aType) { - --mRemainingParts[aType.ordinal()]; - - if (mRemainingParts[SubParts.SENT_PART.ordinal()] > - mRemainingParts[SubParts.DELIVERED_PART.ordinal()]) { - Log.e("GeckoSmsManager", "Delivered more parts than we sent!?"); - } - } - - public boolean arePartsRemaining(Envelope.SubParts aType) { - return mRemainingParts[aType.ordinal()] != 0; - } - - public void markAsFailed(Envelope.SubParts aType) { - mFailing[aType.ordinal()] = true; - } - - public boolean isFailing(Envelope.SubParts aType) { - return mFailing[aType.ordinal()]; - } - - public int getMessageId() { - return mMessageId; - } - - public void setMessageId(int aMessageId) { - mMessageId = aMessageId; - } - - public long getMessageTimestamp() { - return mMessageTimestamp; - } - - public void setMessageTimestamp(long aMessageTimestamp) { - mMessageTimestamp = aMessageTimestamp; - } - - public int getError() { - return mError; - } - - public void setError(int aError) { - mError = aError; - } -} - -/** - * Postman class is a singleton that manages Envelope instances. - */ -class Postman -{ - public static final int kUnknownEnvelopeId = -1; - - private static final Postman sInstance = new Postman(); - - private ArrayList<Envelope> mEnvelopes = new ArrayList<Envelope>(1); - - private Postman() {} - - public static Postman getInstance() { - return sInstance; - } - - public int createEnvelope(int aParts) { - /* - * We are going to create the envelope in the first empty slot in the array - * list. If there is no empty slot, we create a new one. - */ - int size = mEnvelopes.size(); - - for (int i=0; i<size; ++i) { - if (mEnvelopes.get(i) == null) { - mEnvelopes.set(i, new Envelope(i, aParts)); - return i; - } - } - - mEnvelopes.add(new Envelope(size, aParts)); - return size; - } - - public Envelope getEnvelope(int aId) { - if (aId < 0 || mEnvelopes.size() <= aId) { - Log.e("GeckoSmsManager", "Trying to get an unknown Envelope!"); - return null; - } - - Envelope envelope = mEnvelopes.get(aId); - if (envelope == null) { - Log.e("GeckoSmsManager", "Trying to get an empty Envelope!"); - } - - return envelope; - } - - public void destroyEnvelope(int aId) { - if (aId < 0 || mEnvelopes.size() <= aId) { - Log.e("GeckoSmsManager", "Trying to destroy an unknown Envelope!"); - return; - } - - if (mEnvelopes.set(aId, null) == null) { - Log.e("GeckoSmsManager", "Trying to destroy an empty Envelope!"); - } - } -} - -class SmsIOThread extends Thread { - private final static SmsIOThread sInstance = new SmsIOThread(); - - private Handler mHandler; - - public static SmsIOThread getInstance() { - return sInstance; - } - - public boolean execute(Runnable r) { - return mHandler.post(r); - } - - @Override - public void run() { - Looper.prepare(); - - mHandler = new Handler(); - - Looper.loop(); - } -} - -class MessagesListManager -{ - private static final MessagesListManager sInstance = new MessagesListManager(); - - public static MessagesListManager getInstance() { - return sInstance; - } - - private ArrayList<Cursor> mCursors = new ArrayList<Cursor>(0); - - public int add(Cursor aCursor) { - int size = mCursors.size(); - - for (int i=0; i<size; ++i) { - if (mCursors.get(i) == null) { - mCursors.set(i, aCursor); - return i; - } - } - - mCursors.add(aCursor); - return size; - } - - public Cursor get(int aId) { - if (aId < 0 || mCursors.size() <= aId) { - Log.e("GeckoSmsManager", "Trying to get an unknown list!"); - return null; - } - - Cursor cursor = mCursors.get(aId); - if (cursor == null) { - Log.e("GeckoSmsManager", "Trying to get an empty list!"); - } - - return cursor; - } - - public void remove(int aId) { - if (aId < 0 || mCursors.size() <= aId) { - Log.e("GeckoSmsManager", "Trying to destroy an unknown list!"); - return; - } - - Cursor cursor = mCursors.set(aId, null); - if (cursor == null) { - Log.e("GeckoSmsManager", "Trying to destroy an empty list!"); - return; - } - - cursor.close(); - } - - public void clear() { - for (int i=0; i<mCursors.size(); ++i) { - Cursor c = mCursors.get(i); - if (c != null) { - c.close(); - } - } - - mCursors.clear(); - } -} - -public class GeckoSmsManager - extends BroadcastReceiver - implements ISmsManager -{ - public final static String ACTION_SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED"; - public final static String ACTION_SMS_SENT = "org.mozilla.gecko.SMS_SENT"; - public final static String ACTION_SMS_DELIVERED = "org.mozilla.gecko.SMS_DELIVERED"; - - /* - * Make sure that the following error codes are in sync with |ErrorType| in: - * dom/mobilemessage/src/Types.h - * The error code are owned by the DOM. - */ - public final static int kNoError = 0; - public final static int kNoSignalError = 1; - public final static int kNotFoundError = 2; - public final static int kUnknownError = 3; - public final static int kInternalError = 4; - - private final static int kMaxMessageSize = 160; - - private final static Uri kSmsContentUri = Uri.parse("content://sms"); - private final static Uri kSmsSentContentUri = Uri.parse("content://sms/sent"); - - private final static int kSmsTypeInbox = 1; - private final static int kSmsTypeSentbox = 2; - - /* - * Keep the following state codes in syng with |DeliveryState| in: - * dom/mobilemessage/src/Types.h - */ - private final static int kDeliveryStateSent = 0; - private final static int kDeliveryStateReceived = 1; - private final static int kDeliveryStateSending = 2; - private final static int kDeliveryStateError = 3; - private final static int kDeliveryStateUnknown = 4; - private final static int kDeliveryStateNotDownloaded = 5; - private final static int kDeliveryStateEndGuard = 6; - - /* - * Keep the following status codes in sync with |DeliveryStatus| in: - * dom/mobilemessage/src/Types.h - */ - private final static int kDeliveryStatusNotApplicable = 0; - private final static int kDeliveryStatusSuccess = 1; - private final static int kDeliveryStatusPending = 2; - private final static int kDeliveryStatusError = 3; - - /* - * android.provider.Telephony.Sms.STATUS_*. Duplicated because they're not - * part of Android public API. - */ - private final static int kInternalDeliveryStatusNone = -1; - private final static int kInternalDeliveryStatusComplete = 0; - private final static int kInternalDeliveryStatusPending = 32; - private final static int kInternalDeliveryStatusFailed = 64; - - /* - * Keep the following values in sync with |MessageClass| in: - * dom/mobilemessage/src/Types.h - */ - private final static int kMessageClassNormal = 0; - private final static int kMessageClassClass0 = 1; - private final static int kMessageClassClass1 = 2; - private final static int kMessageClassClass2 = 3; - private final static int kMessageClassClass3 = 4; - - private final static String[] kRequiredMessageRows = new String[] { "_id", "address", "body", "date", "type", "status" }; - - public GeckoSmsManager() { - SmsIOThread.getInstance().start(); - } - - @Override - public void start() { - IntentFilter smsFilter = new IntentFilter(); - smsFilter.addAction(GeckoSmsManager.ACTION_SMS_RECEIVED); - smsFilter.addAction(GeckoSmsManager.ACTION_SMS_SENT); - smsFilter.addAction(GeckoSmsManager.ACTION_SMS_DELIVERED); - - GeckoAppShell.getContext().registerReceiver(this, smsFilter); - } - - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(ACTION_SMS_RECEIVED)) { - // TODO: Try to find the receiver number to be able to populate - // SmsMessage.receiver. - // TODO: Get the id and the date from the stock app saved message. - // Using the stock app saved message require us to wait for it to - // be saved which can lead to race conditions. - - Bundle bundle = intent.getExtras(); - - if (bundle == null) { - return; - } - - Object[] pdus = (Object[]) bundle.get("pdus"); - - for (int i=0; i<pdus.length; ++i) { - SmsMessage msg = SmsMessage.createFromPdu((byte[])pdus[i]); - - notifySmsReceived(msg.getDisplayOriginatingAddress(), - msg.getDisplayMessageBody(), - getGeckoMessageClass(msg.getMessageClass()), - System.currentTimeMillis()); - } - - return; - } - - if (intent.getAction().equals(ACTION_SMS_SENT) || - intent.getAction().equals(ACTION_SMS_DELIVERED)) { - Bundle bundle = intent.getExtras(); - - if (bundle == null || !bundle.containsKey("envelopeId") || - !bundle.containsKey("number") || !bundle.containsKey("message") || - !bundle.containsKey("requestId")) { - Log.e("GeckoSmsManager", "Got an invalid ACTION_SMS_SENT/ACTION_SMS_DELIVERED!"); - return; - } - - int envelopeId = bundle.getInt("envelopeId"); - Postman postman = Postman.getInstance(); - - Envelope envelope = postman.getEnvelope(envelopeId); - if (envelope == null) { - Log.e("GeckoSmsManager", "Got an invalid envelope id (or Envelope has been destroyed)!"); - return; - } - - Envelope.SubParts part = intent.getAction().equals(ACTION_SMS_SENT) - ? Envelope.SubParts.SENT_PART - : Envelope.SubParts.DELIVERED_PART; - envelope.decreaseRemainingParts(part); - - - if (getResultCode() != Activity.RESULT_OK) { - switch (getResultCode()) { - case SmsManager.RESULT_ERROR_NULL_PDU: - envelope.setError(kInternalError); - break; - case SmsManager.RESULT_ERROR_NO_SERVICE: - case SmsManager.RESULT_ERROR_RADIO_OFF: - envelope.setError(kNoSignalError); - break; - case SmsManager.RESULT_ERROR_GENERIC_FAILURE: - default: - envelope.setError(kUnknownError); - break; - } - envelope.markAsFailed(part); - Log.i("GeckoSmsManager", "SMS part sending failed!"); - } - - if (envelope.arePartsRemaining(part)) { - return; - } - - if (envelope.isFailing(part)) { - if (part == Envelope.SubParts.SENT_PART) { - notifySmsSendFailed(envelope.getError(), bundle.getInt("requestId")); - Log.i("GeckoSmsManager", "SMS sending failed!"); - } else { - notifySmsDelivery(envelope.getMessageId(), - kDeliveryStatusError, - bundle.getString("number"), - bundle.getString("message"), - envelope.getMessageTimestamp()); - Log.i("GeckoSmsManager", "SMS delivery failed!"); - } - } else { - if (part == Envelope.SubParts.SENT_PART) { - String number = bundle.getString("number"); - String message = bundle.getString("message"); - long timestamp = System.currentTimeMillis(); - - int id = saveSentMessage(number, message, timestamp); - - notifySmsSent(id, number, message, timestamp, - bundle.getInt("requestId")); - - envelope.setMessageId(id); - envelope.setMessageTimestamp(timestamp); - - Log.i("GeckoSmsManager", "SMS sending was successfull!"); - } else { - notifySmsDelivery(envelope.getMessageId(), - kDeliveryStatusSuccess, - bundle.getString("number"), - bundle.getString("message"), - envelope.getMessageTimestamp()); - Log.i("GeckoSmsManager", "SMS successfully delivered!"); - } - } - - // Destroy the envelope object only if the SMS has been sent and delivered. - if (!envelope.arePartsRemaining(Envelope.SubParts.SENT_PART) && - !envelope.arePartsRemaining(Envelope.SubParts.DELIVERED_PART)) { - postman.destroyEnvelope(envelopeId); - } - - return; - } - } - - @Override - public void send(String aNumber, String aMessage, int aRequestId) { - int envelopeId = Postman.kUnknownEnvelopeId; - - try { - SmsManager sm = SmsManager.getDefault(); - - Intent sentIntent = new Intent(ACTION_SMS_SENT); - Intent deliveredIntent = new Intent(ACTION_SMS_DELIVERED); - - Bundle bundle = new Bundle(); - bundle.putString("number", aNumber); - bundle.putString("message", aMessage); - bundle.putInt("requestId", aRequestId); - - if (aMessage.length() <= kMaxMessageSize) { - envelopeId = Postman.getInstance().createEnvelope(1); - bundle.putInt("envelopeId", envelopeId); - - sentIntent.putExtras(bundle); - deliveredIntent.putExtras(bundle); - - /* - * There are a few things to know about getBroadcast and pending intents: - * - the pending intents are in a shared pool maintained by the system; - * - each pending intent is identified by a token; - * - when a new pending intent is created, if it has the same token as - * another intent in the pool, one of them has to be removed. - * - * To prevent having a hard time because of this situation, we give a - * unique id to all pending intents we are creating. This unique id is - * generated by GetPendingIntentUID(). - */ - PendingIntent sentPendingIntent = - PendingIntent.getBroadcast(GeckoAppShell.getContext(), - PendingIntentUID.generate(), sentIntent, - PendingIntent.FLAG_CANCEL_CURRENT); - - PendingIntent deliveredPendingIntent = - PendingIntent.getBroadcast(GeckoAppShell.getContext(), - PendingIntentUID.generate(), deliveredIntent, - PendingIntent.FLAG_CANCEL_CURRENT); - - sm.sendTextMessage(aNumber, "", aMessage, - sentPendingIntent, deliveredPendingIntent); - } else { - ArrayList<String> parts = sm.divideMessage(aMessage); - envelopeId = Postman.getInstance().createEnvelope(parts.size()); - bundle.putInt("envelopeId", envelopeId); - - sentIntent.putExtras(bundle); - deliveredIntent.putExtras(bundle); - - ArrayList<PendingIntent> sentPendingIntents = - new ArrayList<PendingIntent>(parts.size()); - ArrayList<PendingIntent> deliveredPendingIntents = - new ArrayList<PendingIntent>(parts.size()); - - for (int i=0; i<parts.size(); ++i) { - sentPendingIntents.add( - PendingIntent.getBroadcast(GeckoAppShell.getContext(), - PendingIntentUID.generate(), sentIntent, - PendingIntent.FLAG_CANCEL_CURRENT) - ); - - deliveredPendingIntents.add( - PendingIntent.getBroadcast(GeckoAppShell.getContext(), - PendingIntentUID.generate(), deliveredIntent, - PendingIntent.FLAG_CANCEL_CURRENT) - ); - } - - sm.sendMultipartTextMessage(aNumber, "", parts, sentPendingIntents, - deliveredPendingIntents); - } - } catch (Exception e) { - Log.e("GeckoSmsManager", "Failed to send an SMS: ", e); - - if (envelopeId != Postman.kUnknownEnvelopeId) { - Postman.getInstance().destroyEnvelope(envelopeId); - } - - notifySmsSendFailed(kUnknownError, aRequestId); - } - } - - public int saveSentMessage(String aRecipient, String aBody, long aDate) { - try { - ContentValues values = new ContentValues(); - values.put("address", aRecipient); - values.put("body", aBody); - values.put("date", aDate); - // Always 'PENDING' because we always request status report. - values.put("status", kInternalDeliveryStatusPending); - - ContentResolver cr = GeckoAppShell.getContext().getContentResolver(); - Uri uri = cr.insert(kSmsSentContentUri, values); - - long id = ContentUris.parseId(uri); - - // The DOM API takes a 32bits unsigned int for the id. It's unlikely that - // we happen to need more than that but it doesn't cost to check. - if (id > Integer.MAX_VALUE) { - throw new IdTooHighException(); - } - - return (int)id; - } catch (IdTooHighException e) { - Log.e("GeckoSmsManager", "The id we received is higher than the higher allowed value."); - return -1; - } catch (Exception e) { - Log.e("GeckoSmsManager", "Something went wrong when trying to write a sent message", e); - return -1; - } - } - - @Override - public void getMessage(int aMessageId, int aRequestId) { - class GetMessageRunnable implements Runnable { - private int mMessageId; - private int mRequestId; - - GetMessageRunnable(int aMessageId, int aRequestId) { - mMessageId = aMessageId; - mRequestId = aRequestId; - } - - @Override - public void run() { - Cursor cursor = null; - - try { - ContentResolver cr = GeckoAppShell.getContext().getContentResolver(); - Uri message = ContentUris.withAppendedId(kSmsContentUri, mMessageId); - - cursor = cr.query(message, kRequiredMessageRows, null, null, null); - if (cursor == null || cursor.getCount() == 0) { - throw new NotFoundException(); - } - - if (cursor.getCount() != 1) { - throw new TooManyResultsException(); - } - - cursor.moveToFirst(); - - if (cursor.getInt(cursor.getColumnIndex("_id")) != mMessageId) { - throw new UnmatchingIdException(); - } - - int type = cursor.getInt(cursor.getColumnIndex("type")); - int deliveryStatus; - String sender = ""; - String receiver = ""; - - if (type == kSmsTypeInbox) { - deliveryStatus = kDeliveryStatusSuccess; - sender = cursor.getString(cursor.getColumnIndex("address")); - } else if (type == kSmsTypeSentbox) { - deliveryStatus = getGeckoDeliveryStatus(cursor.getInt(cursor.getColumnIndex("status"))); - receiver = cursor.getString(cursor.getColumnIndex("address")); - } else { - throw new InvalidTypeException(); - } - - notifyGetSms(cursor.getInt(cursor.getColumnIndex("_id")), - deliveryStatus, - receiver, sender, - cursor.getString(cursor.getColumnIndex("body")), - cursor.getLong(cursor.getColumnIndex("date")), - mRequestId); - } catch (NotFoundException e) { - Log.i("GeckoSmsManager", "Message id " + mMessageId + " not found"); - notifyGetSmsFailed(kNotFoundError, mRequestId); - } catch (UnmatchingIdException e) { - Log.e("GeckoSmsManager", "Requested message id (" + mMessageId + - ") is different from the one we got."); - notifyGetSmsFailed(kUnknownError, mRequestId); - } catch (TooManyResultsException e) { - Log.e("GeckoSmsManager", "Get too many results for id " + mMessageId); - notifyGetSmsFailed(kUnknownError, mRequestId); - } catch (InvalidTypeException e) { - Log.i("GeckoSmsManager", "Message has an invalid type, we ignore it."); - notifyGetSmsFailed(kNotFoundError, mRequestId); - } catch (Exception e) { - Log.e("GeckoSmsManager", "Error while trying to get message", e); - notifyGetSmsFailed(kUnknownError, mRequestId); - } finally { - if (cursor != null) { - cursor.close(); - } - } - } - } - - if (!SmsIOThread.getInstance().execute(new GetMessageRunnable(aMessageId, aRequestId))) { - Log.e("GeckoSmsManager", "Failed to add GetMessageRunnable to the SmsIOThread"); - notifyGetSmsFailed(kUnknownError, aRequestId); - } - } - - @Override - public void deleteMessage(int aMessageId, int aRequestId) { - class DeleteMessageRunnable implements Runnable { - private int mMessageId; - private int mRequestId; - - DeleteMessageRunnable(int aMessageId, int aRequestId) { - mMessageId = aMessageId; - mRequestId = aRequestId; - } - - @Override - public void run() { - try { - ContentResolver cr = GeckoAppShell.getContext().getContentResolver(); - Uri message = ContentUris.withAppendedId(kSmsContentUri, mMessageId); - - int count = cr.delete(message, null, null); - - if (count > 1) { - throw new TooManyResultsException(); - } - - notifySmsDeleted(count == 1, mRequestId); - } catch (TooManyResultsException e) { - Log.e("GeckoSmsManager", "Delete more than one message?", e); - notifySmsDeleteFailed(kUnknownError, mRequestId); - } catch (Exception e) { - Log.e("GeckoSmsManager", "Error while trying to delete a message", e); - notifySmsDeleteFailed(kUnknownError, mRequestId); - } - } - } - - if (!SmsIOThread.getInstance().execute(new DeleteMessageRunnable(aMessageId, aRequestId))) { - Log.e("GeckoSmsManager", "Failed to add GetMessageRunnable to the SmsIOThread"); - notifySmsDeleteFailed(kUnknownError, aRequestId); - } - } - - @Override - public void createMessageList(long aStartDate, long aEndDate, String[] aNumbers, int aNumbersCount, int aDeliveryState, boolean aReverse, int aRequestId) { - class CreateMessageListRunnable implements Runnable { - private long mStartDate; - private long mEndDate; - private String[] mNumbers; - private int mNumbersCount; - private int mDeliveryState; - private boolean mReverse; - private int mRequestId; - - CreateMessageListRunnable(long aStartDate, long aEndDate, String[] aNumbers, int aNumbersCount, int aDeliveryState, boolean aReverse, int aRequestId) { - mStartDate = aStartDate; - mEndDate = aEndDate; - mNumbers = aNumbers; - mNumbersCount = aNumbersCount; - mDeliveryState = aDeliveryState; - mReverse = aReverse; - mRequestId = aRequestId; - } - - @Override - public void run() { - Cursor cursor = null; - boolean closeCursor = true; - - try { - // TODO: should use the |selectionArgs| argument in |ContentResolver.query()|. - ArrayList<String> restrictions = new ArrayList<String>(); - - if (mStartDate != 0) { - restrictions.add("date >= " + mStartDate); - } - - if (mEndDate != 0) { - restrictions.add("date <= " + mEndDate); - } - - if (mNumbersCount > 0) { - String numberRestriction = "address IN ('" + mNumbers[0] + "'"; - - for (int i=1; i<mNumbersCount; ++i) { - numberRestriction += ", '" + mNumbers[i] + "'"; - } - numberRestriction += ")"; - - restrictions.add(numberRestriction); - } - - if (mDeliveryState == kDeliveryStateUnknown) { - restrictions.add("type IN ('" + kSmsTypeSentbox + "', '" + kSmsTypeInbox + "')"); - } else if (mDeliveryState == kDeliveryStateSent) { - restrictions.add("type = " + kSmsTypeSentbox); - } else if (mDeliveryState == kDeliveryStateReceived) { - restrictions.add("type = " + kSmsTypeInbox); - } else { - throw new UnexpectedDeliveryStateException(); - } - - String restrictionText = restrictions.size() > 0 ? restrictions.get(0) : ""; - - for (int i=1; i<restrictions.size(); ++i) { - restrictionText += " AND " + restrictions.get(i); - } - - ContentResolver cr = GeckoAppShell.getContext().getContentResolver(); - cursor = cr.query(kSmsContentUri, kRequiredMessageRows, restrictionText, null, - mReverse ? "date DESC" : "date ASC"); - - if (cursor.getCount() == 0) { - notifyNoMessageInList(mRequestId); - return; - } - - cursor.moveToFirst(); - - int type = cursor.getInt(cursor.getColumnIndex("type")); - int deliveryStatus; - String sender = ""; - String receiver = ""; - - if (type == kSmsTypeInbox) { - deliveryStatus = kDeliveryStatusSuccess; - sender = cursor.getString(cursor.getColumnIndex("address")); - } else if (type == kSmsTypeSentbox) { - deliveryStatus = getGeckoDeliveryStatus(cursor.getInt(cursor.getColumnIndex("status"))); - receiver = cursor.getString(cursor.getColumnIndex("address")); - } else { - throw new UnexpectedDeliveryStateException(); - } - - int listId = MessagesListManager.getInstance().add(cursor); - closeCursor = false; - notifyListCreated(listId, - cursor.getInt(cursor.getColumnIndex("_id")), - deliveryStatus, - receiver, sender, - cursor.getString(cursor.getColumnIndex("body")), - cursor.getLong(cursor.getColumnIndex("date")), - mRequestId); - } catch (UnexpectedDeliveryStateException e) { - Log.e("GeckoSmsManager", "Unexcepted delivery state type", e); - notifyReadingMessageListFailed(kUnknownError, mRequestId); - } catch (Exception e) { - Log.e("GeckoSmsManager", "Error while trying to create a message list cursor", e); - notifyReadingMessageListFailed(kUnknownError, mRequestId); - } finally { - // Close the cursor if MessagesListManager isn't taking care of it. - // We could also just check if it is in the MessagesListManager list but - // that would be less efficient. - if (cursor != null && closeCursor) { - cursor.close(); - } - } - } - } - - if (!SmsIOThread.getInstance().execute(new CreateMessageListRunnable(aStartDate, aEndDate, aNumbers, aNumbersCount, aDeliveryState, aReverse, aRequestId))) { - Log.e("GeckoSmsManager", "Failed to add CreateMessageListRunnable to the SmsIOThread"); - notifyReadingMessageListFailed(kUnknownError, aRequestId); - } - } - - @Override - public void getNextMessageInList(int aListId, int aRequestId) { - class GetNextMessageInListRunnable implements Runnable { - private int mListId; - private int mRequestId; - - GetNextMessageInListRunnable(int aListId, int aRequestId) { - mListId = aListId; - mRequestId = aRequestId; - } - - @Override - public void run() { - try { - Cursor cursor = MessagesListManager.getInstance().get(mListId); - - if (!cursor.moveToNext()) { - MessagesListManager.getInstance().remove(mListId); - notifyNoMessageInList(mRequestId); - return; - } - - int type = cursor.getInt(cursor.getColumnIndex("type")); - int deliveryStatus; - String sender = ""; - String receiver = ""; - - if (type == kSmsTypeInbox) { - deliveryStatus = kDeliveryStatusSuccess; - sender = cursor.getString(cursor.getColumnIndex("address")); - } else if (type == kSmsTypeSentbox) { - deliveryStatus = getGeckoDeliveryStatus(cursor.getInt(cursor.getColumnIndex("status"))); - receiver = cursor.getString(cursor.getColumnIndex("address")); - } else { - throw new UnexpectedDeliveryStateException(); - } - - int listId = MessagesListManager.getInstance().add(cursor); - notifyGotNextMessage(cursor.getInt(cursor.getColumnIndex("_id")), - deliveryStatus, - receiver, sender, - cursor.getString(cursor.getColumnIndex("body")), - cursor.getLong(cursor.getColumnIndex("date")), - mRequestId); - } catch (UnexpectedDeliveryStateException e) { - Log.e("GeckoSmsManager", "Unexcepted delivery state type", e); - notifyReadingMessageListFailed(kUnknownError, mRequestId); - } catch (Exception e) { - Log.e("GeckoSmsManager", "Error while trying to get the next message of a list", e); - notifyReadingMessageListFailed(kUnknownError, mRequestId); - } - } - } - - if (!SmsIOThread.getInstance().execute(new GetNextMessageInListRunnable(aListId, aRequestId))) { - Log.e("GeckoSmsManager", "Failed to add GetNextMessageInListRunnable to the SmsIOThread"); - notifyReadingMessageListFailed(kUnknownError, aRequestId); - } - } - - @Override - public void clearMessageList(int aListId) { - MessagesListManager.getInstance().remove(aListId); - } - - @Override - public void stop() { - GeckoAppShell.getContext().unregisterReceiver(this); - } - - @Override - public void shutdown() { - SmsIOThread.getInstance().interrupt(); - MessagesListManager.getInstance().clear(); - } - - private int getGeckoDeliveryStatus(int aDeliveryStatus) { - if (aDeliveryStatus == kInternalDeliveryStatusNone) { - return kDeliveryStatusNotApplicable; - } - if (aDeliveryStatus >= kInternalDeliveryStatusFailed) { - return kDeliveryStatusError; - } - if (aDeliveryStatus >= kInternalDeliveryStatusPending) { - return kDeliveryStatusPending; - } - return kDeliveryStatusSuccess; - } - - private int getGeckoMessageClass(MessageClass aMessageClass) { - switch (aMessageClass) { - case CLASS_0: - return kMessageClassClass0; - case CLASS_1: - return kMessageClassClass1; - case CLASS_2: - return kMessageClassClass2; - case CLASS_3: - return kMessageClassClass3; - default: - return kMessageClassNormal; - } - } - - class IdTooHighException extends Exception { - private static final long serialVersionUID = 29935575131092050L; - } - - class InvalidTypeException extends Exception { - private static final long serialVersionUID = 47436856832535912L; - } - - class NotFoundException extends Exception { - private static final long serialVersionUID = 1940676816633984L; - } - - class TooManyResultsException extends Exception { - private static final long serialVersionUID = 51883196784325305L; - } - - class UnexpectedDeliveryStateException extends Exception { - private static final long serialVersionUID = 494122763684005716L; - } - - class UnmatchingIdException extends Exception { - private static final long serialVersionUID = 158467542575633280L; - } - - private static native void notifySmsReceived(String aSender, String aBody, int aMessageClass, long aTimestamp); - private static native void notifySmsSent(int aId, String aReceiver, String aBody, long aTimestamp, int aRequestId); - private static native void notifySmsDelivery(int aId, int aDeliveryStatus, String aReceiver, String aBody, long aTimestamp); - private static native void notifySmsSendFailed(int aError, int aRequestId); - private static native void notifyGetSms(int aId, int aDeliveryStatus, String aReceiver, String aSender, String aBody, long aTimestamp, int aRequestId); - private static native void notifyGetSmsFailed(int aError, int aRequestId); - private static native void notifySmsDeleted(boolean aDeleted, int aRequestId); - private static native void notifySmsDeleteFailed(int aError, int aRequestId); - private static native void notifyNoMessageInList(int aRequestId); - private static native void notifyListCreated(int aListId, int aMessageId, int aDeliveryStatus, String aReceiver, String aSender, String aBody, long aTimestamp, int aRequestId); - private static native void notifyGotNextMessage(int aMessageId, int aDeliveryStatus, String aReceiver, String aSender, String aBody, long aTimestamp, int aRequestId); - private static native void notifyReadingMessageListFailed(int aError, int aRequestId); -} diff --git a/mobile/android/base/GeckoThread.java b/mobile/android/base/GeckoThread.java deleted file mode 100644 index 830281a10..000000000 --- a/mobile/android/base/GeckoThread.java +++ /dev/null @@ -1,149 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import org.mozilla.gecko.mozglue.GeckoLoader; -import org.mozilla.gecko.util.GeckoEventListener; - -import org.json.JSONObject; - -import android.content.Intent; -import android.content.Context; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.os.SystemClock; -import android.util.Log; -import android.app.Activity; - - -import java.util.Locale; - -public class GeckoThread extends Thread implements GeckoEventListener { - private static final String LOGTAG = "GeckoThread"; - - public enum LaunchState { - Launching, - WaitForDebugger, - Launched, - GeckoRunning, - GeckoExiting - }; - - private static LaunchState sLaunchState = LaunchState.Launching; - - private Intent mIntent; - private final String mUri; - - GeckoThread(Intent intent, String uri) { - mIntent = intent; - mUri = uri; - setName("Gecko"); - GeckoAppShell.getEventDispatcher().registerEventListener("Gecko:Ready", this); - } - - private String initGeckoEnvironment() { - // At some point while loading the gecko libs our default locale gets set - // so just save it to locale here and reset it as default after the join - Locale locale = Locale.getDefault(); - - if (locale.toString().equalsIgnoreCase("zh_hk")) { - locale = Locale.TRADITIONAL_CHINESE; - Locale.setDefault(locale); - } - - Context app = GeckoAppShell.getContext(); - String resourcePath = ""; - Resources res = null; - String[] pluginDirs = null; - try { - pluginDirs = GeckoAppShell.getPluginDirectories(); - } catch (Exception e) { - Log.w(LOGTAG, "Caught exception getting plugin dirs.", e); - } - - if (app instanceof Activity) { - Activity activity = (Activity)app; - resourcePath = activity.getApplication().getPackageResourcePath(); - res = activity.getBaseContext().getResources(); - GeckoLoader.setupGeckoEnvironment(activity, pluginDirs, GeckoProfile.get(app).getFilesDir().getPath()); - } - GeckoLoader.loadSQLiteLibs(app, resourcePath); - GeckoLoader.loadNSSLibs(app, resourcePath); - GeckoLoader.loadGeckoLibs(app, resourcePath); - - Locale.setDefault(locale); - - Configuration config = res.getConfiguration(); - config.locale = locale; - res.updateConfiguration(config, res.getDisplayMetrics()); - - return resourcePath; - } - - private String getTypeFromAction(String action) { - if (action != null && action.startsWith(GeckoApp.ACTION_WEBAPP_PREFIX)) { - return "-webapp"; - } - if (GeckoApp.ACTION_BOOKMARK.equals(action)) { - return "-bookmark"; - } - return null; - } - - private String addCustomProfileArg(String args) { - String profile = GeckoAppShell.getGeckoInterface() == null || GeckoApp.sIsUsingCustomProfile ? "" : (" -P " + GeckoAppShell.getGeckoInterface().getProfile().getName()); - return (args != null ? args : "") + profile; - } - - @Override - public void run() { - String path = initGeckoEnvironment(); - - Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - runGecko"); - - String args = addCustomProfileArg(mIntent.getStringExtra("args")); - String type = getTypeFromAction(mIntent.getAction()); - mIntent = null; - - // and then fire us up - Log.i(LOGTAG, "RunGecko - args = " + args); - GeckoAppShell.runGecko(path, args, mUri, type); - } - - @Override - public void handleMessage(String event, JSONObject message) { - if ("Gecko:Ready".equals(event)) { - GeckoAppShell.getEventDispatcher().unregisterEventListener(event, this); - setLaunchState(LaunchState.GeckoRunning); - GeckoAppShell.sendPendingEventsToGecko(); - } - } - - public static boolean checkLaunchState(LaunchState checkState) { - synchronized (sLaunchState) { - return sLaunchState == checkState; - } - } - - static void setLaunchState(LaunchState setState) { - synchronized (sLaunchState) { - sLaunchState = setState; - } - } - - /** - * Set the launch state to <code>setState</code> and return true if the current launch - * state is <code>checkState</code>; otherwise do nothing and return false. - */ - static boolean checkAndSetLaunchState(LaunchState checkState, LaunchState setState) { - synchronized (sLaunchState) { - if (sLaunchState != checkState) - return false; - sLaunchState = setState; - return true; - } - } -} diff --git a/mobile/android/base/GeckoUpdateReceiver.java b/mobile/android/base/GeckoUpdateReceiver.java deleted file mode 100644 index f73c42e40..000000000 --- a/mobile/android/base/GeckoUpdateReceiver.java +++ /dev/null @@ -1,25 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import org.mozilla.gecko.updater.UpdateServiceHelper; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -public class GeckoUpdateReceiver extends BroadcastReceiver -{ - @Override - public void onReceive(Context context, Intent intent) { - if (UpdateServiceHelper.ACTION_CHECK_UPDATE_RESULT.equals(intent.getAction())) { - String result = intent.getStringExtra("result"); - if (GeckoAppShell.getGeckoInterface() != null && result != null) { - GeckoAppShell.getGeckoInterface().notifyCheckUpdateResult(result); - } - } - } -} diff --git a/mobile/android/base/GeckoView.java b/mobile/android/base/GeckoView.java deleted file mode 100644 index 414e85369..000000000 --- a/mobile/android/base/GeckoView.java +++ /dev/null @@ -1,96 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import org.mozilla.gecko.db.BrowserDB; -import org.mozilla.gecko.gfx.LayerView; -import org.mozilla.gecko.util.GeckoEventListener; -import org.mozilla.gecko.util.ThreadUtils; - -import org.json.JSONObject; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.net.Uri; -import android.os.Bundle; -import android.util.AttributeSet; -import android.util.Log; -import android.view.SurfaceHolder; -import android.view.SurfaceView; -import android.os.Handler; - -public class GeckoView extends LayerView - implements GeckoEventListener, ContextGetter { - static GeckoThread sGeckoThread; - - public GeckoView(Context context, AttributeSet attrs) { - super(context, attrs); - - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GeckoView); - String url = a.getString(R.styleable.GeckoView_url); - a.recycle(); - - Intent intent; - if (url == null) { - intent = new Intent(Intent.ACTION_MAIN); - } else { - intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - GeckoAppShell.sendEventToGecko(GeckoEvent.createURILoadEvent(url)); - } - GeckoAppShell.setContextGetter(this); - if (context instanceof Activity) { - Tabs tabs = Tabs.getInstance(); - tabs.attachToActivity((Activity) context); - } - GeckoProfile profile = GeckoProfile.get(context); - BrowserDB.initialize(profile.getName()); - GeckoAppShell.registerEventListener("Gecko:Ready", this); - - sGeckoThread = new GeckoThread(intent, url); - ThreadUtils.setGeckoThread(sGeckoThread); - ThreadUtils.setUiThread(Thread.currentThread(), new Handler()); - initializeView(GeckoAppShell.getEventDispatcher()); - if (GeckoThread.checkAndSetLaunchState(GeckoThread.LaunchState.Launching, GeckoThread.LaunchState.Launched)) { - GeckoAppShell.setLayerView(this); - sGeckoThread.start(); - } - } - - @Override - public void onWindowFocusChanged(boolean hasFocus) { - super.onWindowFocusChanged(hasFocus); - - if (hasFocus) { - setBackgroundDrawable(null); - } - } - - public void loadUrl(String uri) { - Tabs.getInstance().loadUrl(uri); - } - - public void handleMessage(String event, JSONObject message) { - if (event.equals("Gecko:Ready")) { - GeckoThread.setLaunchState(GeckoThread.LaunchState.GeckoRunning); - Tab selectedTab = Tabs.getInstance().getSelectedTab(); - if (selectedTab != null) - Tabs.getInstance().notifyListeners(selectedTab, Tabs.TabEvents.SELECTED); - geckoConnected(); - GeckoAppShell.setLayerClient(getLayerClient()); - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Viewport:Flush", null)); - show(); - requestRender(); - } - } - - public static void setGeckoInterface(GeckoAppShell.GeckoInterface aGeckoInterface) { - GeckoAppShell.setGeckoInterface(aGeckoInterface); - } -} diff --git a/mobile/android/base/GeckoView.java.frag b/mobile/android/base/GeckoView.java.frag deleted file mode 100644 index c44f53dd4..000000000 --- a/mobile/android/base/GeckoView.java.frag +++ /dev/null @@ -1,126 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import android.content.Context; -import android.content.res.TypedArray; -import android.util.AttributeSet; -import android.widget.@VIEWTYPE@; - -public class Gecko@VIEWTYPE@ extends @VIEWTYPE@ - implements LightweightTheme.OnChangeListener { - private GeckoActivity mActivity; - private static final int[] STATE_PRIVATE_MODE = { R.attr.state_private }; - private static final int[] STATE_LIGHT = { R.attr.state_light }; - private static final int[] STATE_DARK = { R.attr.state_dark }; - - private boolean mIsPrivate = false; - private boolean mIsLight = false; - private boolean mIsDark = false; - private boolean mAutoUpdateTheme = true; - - public Gecko@VIEWTYPE@(Context context, AttributeSet attrs) { - super(context, attrs); - mActivity = (GeckoActivity) context; - - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LightweightTheme); - mAutoUpdateTheme = a.getBoolean(R.styleable.LightweightTheme_autoUpdateTheme, true); - a.recycle(); - } - - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - - if (mAutoUpdateTheme) - mActivity.getLightweightTheme().addListener(this); - } - - @Override - public void onDetachedFromWindow() { - super.onDetachedFromWindow(); - - if (mAutoUpdateTheme) - mActivity.getLightweightTheme().removeListener(this); - } - - @Override - public int[] onCreateDrawableState(int extraSpace) { - final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); - - if (mIsPrivate) - mergeDrawableStates(drawableState, STATE_PRIVATE_MODE); - else if (mIsLight) - mergeDrawableStates(drawableState, STATE_LIGHT); - else if (mIsDark) - mergeDrawableStates(drawableState, STATE_DARK); - - return drawableState; - } - - @Override - public void onLightweightThemeChanged() { - if (mAutoUpdateTheme && mActivity.getLightweightTheme().isEnabled()) - setTheme(mActivity.getLightweightTheme().isLightTheme()); - } - - @Override - public void onLightweightThemeReset() { - if (mAutoUpdateTheme) - resetTheme(); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - onLightweightThemeChanged(); - } - - public boolean isPrivateMode() { - return mIsPrivate; - } - - public void setPrivateMode(boolean isPrivate) { - if (mIsPrivate != isPrivate) { - mIsPrivate = isPrivate; - refreshDrawableState(); - } - } - - public void setTheme(boolean isLight) { - // Set the theme only if it is different from existing theme. - if ((isLight && mIsLight != isLight) || - (!isLight && mIsDark == isLight)) { - if (isLight) { - mIsLight = true; - mIsDark = false; - } else { - mIsLight = false; - mIsDark = true; - } - - refreshDrawableState(); - } - } - - public void resetTheme() { - if (mIsLight || mIsDark) { - mIsLight = false; - mIsDark = false; - refreshDrawableState(); - } - } - - public void setAutoUpdateTheme(boolean autoUpdateTheme) { - if (mAutoUpdateTheme != autoUpdateTheme) { - mAutoUpdateTheme = autoUpdateTheme; - - if (mAutoUpdateTheme) - mActivity.getLightweightTheme().addListener(this); - else - mActivity.getLightweightTheme().removeListener(this); - } - } -} diff --git a/mobile/android/base/GeckoViewsFactory.java b/mobile/android/base/GeckoViewsFactory.java deleted file mode 100644 index 899e10807..000000000 --- a/mobile/android/base/GeckoViewsFactory.java +++ /dev/null @@ -1,133 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import org.mozilla.gecko.gfx.LayerView; -import org.mozilla.gecko.menu.GeckoMenu; -import org.mozilla.gecko.menu.MenuItemActionBar; -import org.mozilla.gecko.menu.MenuItemDefault; -import org.mozilla.gecko.widget.AboutHomeView; -import org.mozilla.gecko.widget.AddonsSection; -import org.mozilla.gecko.widget.FaviconView; -import org.mozilla.gecko.widget.IconTabWidget; -import org.mozilla.gecko.widget.LastTabsSection; -import org.mozilla.gecko.widget.LinkTextView; -import org.mozilla.gecko.widget.PromoBox; -import org.mozilla.gecko.widget.RemoteTabsSection; -import org.mozilla.gecko.widget.TabRow; -import org.mozilla.gecko.widget.ThumbnailView; -import org.mozilla.gecko.widget.TopSitesView; - -import android.content.Context; -import android.text.TextUtils; -import android.util.AttributeSet; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; - -import java.lang.reflect.Constructor; -import java.util.HashMap; -import java.util.Map; - -public final class GeckoViewsFactory implements LayoutInflater.Factory { - private static final String LOGTAG = "GeckoViewsFactory"; - - private static final String GECKO_VIEW_IDENTIFIER = "org.mozilla.gecko."; - private static final int GECKO_VIEW_IDENTIFIER_LENGTH = GECKO_VIEW_IDENTIFIER.length(); - - private static final String GECKO_IDENTIFIER = "Gecko."; - private static final int GECKO_IDENTIFIER_LENGTH = GECKO_IDENTIFIER.length(); - - private final Map<String, Constructor<? extends View>> mFactoryMap; - - private GeckoViewsFactory() { - // initialize the hashmap to a capacity that is a prime number greater than - // (size * 4/3). The size is the number of items we expect to put in it, and - // 4/3 is the inverse of the default load factor. - mFactoryMap = new HashMap<String, Constructor<? extends View>>(53); - Class<Context> arg1Class = Context.class; - Class<AttributeSet> arg2Class = AttributeSet.class; - try { - mFactoryMap.put("AboutHomeView", AboutHomeView.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("AddonsSection", AddonsSection.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("LastTabsSection", LastTabsSection.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("PromoBox", PromoBox.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("RemoteTabsSection", RemoteTabsSection.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("TopSitesView", TopSitesView.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("AwesomeBarTabs", AwesomeBarTabs.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("AwesomeBarTabs$BackgroundLayout", AwesomeBarTabs.BackgroundLayout.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("BackButton", BackButton.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("BrowserToolbarBackground", BrowserToolbarBackground.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("CheckableLinearLayout", CheckableLinearLayout.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("FormAssistPopup", FormAssistPopup.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("ForwardButton", ForwardButton.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("GeckoApp$MainLayout", GeckoApp.MainLayout.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("LinkTextView", LinkTextView.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("menu.MenuItemActionBar", MenuItemActionBar.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("menu.MenuItemDefault", MenuItemDefault.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("menu.GeckoMenu$DefaultActionItemBar", GeckoMenu.DefaultActionItemBar.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("FindInPageBar", FindInPageBar.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("IconTabWidget", IconTabWidget.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("RemoteTabs", RemoteTabs.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("ShapedButton", ShapedButton.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("TabRow", TabRow.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("TabsPanel", TabsPanel.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("TabsPanel$TabsListContainer", TabsPanel.TabsListContainer.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("TabsPanel$TabsPanelToolbar", TabsPanel.TabsPanelToolbar.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("TabsTray", TabsTray.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("ThumbnailView", ThumbnailView.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("TextSelectionHandle", TextSelectionHandle.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("gfx.LayerView", LayerView.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("AllCapsTextView", AllCapsTextView.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("Button", GeckoButton.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("EditText", GeckoEditText.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("FrameLayout", GeckoFrameLayout.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("ImageButton", GeckoImageButton.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("ImageView", GeckoImageView.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("LinearLayout", GeckoLinearLayout.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("RelativeLayout", GeckoRelativeLayout.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("TextSwitcher", GeckoTextSwitcher.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("TextView", GeckoTextView.class.getConstructor(arg1Class, arg2Class)); - mFactoryMap.put("FaviconView", FaviconView.class.getConstructor(arg1Class, arg2Class)); - } catch (NoSuchMethodException nsme) { - Log.e(LOGTAG, "Unable to initialize views factory", nsme); - } - } - - // Making this a singleton class. - private static final GeckoViewsFactory INSTANCE = new GeckoViewsFactory(); - - public static GeckoViewsFactory getInstance() { - return INSTANCE; - } - - @Override - public View onCreateView(String name, Context context, AttributeSet attrs) { - if (!TextUtils.isEmpty(name)) { - String viewName = null; - - if (name.startsWith(GECKO_VIEW_IDENTIFIER)) - viewName = name.substring(GECKO_VIEW_IDENTIFIER_LENGTH); - else if (name.startsWith(GECKO_IDENTIFIER)) - viewName = name.substring(GECKO_IDENTIFIER_LENGTH); - else - return null; - - Constructor<? extends View> constructor = mFactoryMap.get(viewName); - if (constructor != null) { - try { - return constructor.newInstance(context, attrs); - } catch (Exception e) { - Log.e(LOGTAG, "Unable to instantiate view " + name, e); - return null; - } - } - - Log.d(LOGTAG, "Warning: unknown custom view: " + name); - } - - return null; - } -} diff --git a/mobile/android/base/gfx/GeckoLayerClient.java b/mobile/android/base/gfx/GeckoLayerClient.java deleted file mode 100644 index 034dfeafa..000000000 --- a/mobile/android/base/gfx/GeckoLayerClient.java +++ /dev/null @@ -1,978 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.gfx; - -import org.mozilla.gecko.BrowserApp; -import org.mozilla.gecko.GeckoAppShell; -import org.mozilla.gecko.GeckoEvent; -import org.mozilla.gecko.Tab; -import org.mozilla.gecko.Tabs; -import org.mozilla.gecko.ZoomConstraints; -import org.mozilla.gecko.util.EventDispatcher; -import org.mozilla.gecko.util.FloatUtils; -import org.mozilla.gecko.util.ThreadUtils; - -import android.content.Context; -import android.graphics.PointF; -import android.graphics.RectF; -import android.os.SystemClock; -import android.util.DisplayMetrics; -import android.util.Log; - -public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget -{ - private static final String LOGTAG = "GeckoLayerClient"; - - private LayerRenderer mLayerRenderer; - private boolean mLayerRendererInitialized; - - private Context mContext; - private IntSize mScreenSize; - private IntSize mWindowSize; - private DisplayPortMetrics mDisplayPort; - - private boolean mRecordDrawTimes; - private final DrawTimingQueue mDrawTimingQueue; - - private VirtualLayer mRootLayer; - - /* The Gecko viewport as per the UI thread. Must be touched only on the UI thread. - * If any events being sent to Gecko that are relative to the Gecko viewport position, - * they must (a) be relative to this viewport, and (b) be sent on the UI thread to - * avoid races. As long as these two conditions are satisfied, and the events being - * sent to Gecko are processed in FIFO order, the events will properly be relative - * to the Gecko viewport position. Note that if Gecko updates its viewport independently, - * we get notified synchronously and also update this on the UI thread. - */ - private ImmutableViewportMetrics mGeckoViewport; - - /* - * The viewport metrics being used to draw the current frame. This is only - * accessed by the compositor thread, and so needs no synchronisation. - */ - private ImmutableViewportMetrics mFrameMetrics; - - /* Used by robocop for testing purposes */ - private DrawListener mDrawListener; - - /* Used as temporaries by syncViewportInfo */ - private final ViewTransform mCurrentViewTransform; - private final RectF mCurrentViewTransformMargins; - - /* Used as the return value of progressiveUpdateCallback */ - private final ProgressiveUpdateData mProgressiveUpdateData; - private DisplayPortMetrics mProgressiveUpdateDisplayPort; - private boolean mLastProgressiveUpdateWasLowPrecision; - private boolean mProgressiveUpdateWasInDanger; - - private boolean mForceRedraw; - - /* The current viewport metrics. - * This is volatile so that we can read and write to it from different threads. - * We avoid synchronization to make getting the viewport metrics from - * the compositor as cheap as possible. The viewport is immutable so - * we don't need to worry about anyone mutating it while we're reading from it. - * Specifically: - * 1) reading mViewportMetrics from any thread is fine without synchronization - * 2) writing to mViewportMetrics requires synchronizing on the layer controller object - * 3) whenver reading multiple fields from mViewportMetrics without synchronization (i.e. in - * case 1 above) you should always frist grab a local copy of the reference, and then use - * that because mViewportMetrics might get reassigned in between reading the different - * fields. */ - private volatile ImmutableViewportMetrics mViewportMetrics; - private OnMetricsChangedListener mViewportChangeListener; - - private ZoomConstraints mZoomConstraints; - - private boolean mGeckoIsReady; - - private final PanZoomController mPanZoomController; - private final LayerMarginsAnimator mMarginsAnimator; - private LayerView mView; - - /* This flag is true from the time that browser.js detects a first-paint is about to start, - * to the time that we receive the first-paint composite notification from the compositor. - * Note that there is a small race condition with this; if there are two paints that both - * have the first-paint flag set, and the second paint happens concurrently with the - * composite for the first paint, then this flag may be set to true prematurely. Fixing this - * is possible but risky; see https://bugzilla.mozilla.org/show_bug.cgi?id=797615#c751 - */ - private volatile boolean mContentDocumentIsDisplayed; - - public GeckoLayerClient(Context context, LayerView view, EventDispatcher eventDispatcher) { - // we can fill these in with dummy values because they are always written - // to before being read - mContext = context; - mScreenSize = new IntSize(0, 0); - mWindowSize = new IntSize(0, 0); - mDisplayPort = new DisplayPortMetrics(); - mRecordDrawTimes = true; - mDrawTimingQueue = new DrawTimingQueue(); - mCurrentViewTransform = new ViewTransform(0, 0, 1); - mCurrentViewTransformMargins = new RectF(); - mProgressiveUpdateData = new ProgressiveUpdateData(); - mProgressiveUpdateDisplayPort = new DisplayPortMetrics(); - mLastProgressiveUpdateWasLowPrecision = false; - mProgressiveUpdateWasInDanger = false; - - mForceRedraw = true; - DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); - mViewportMetrics = new ImmutableViewportMetrics(displayMetrics) - .setViewportSize(view.getWidth(), view.getHeight()); - mFrameMetrics = mViewportMetrics; - mZoomConstraints = new ZoomConstraints(false); - - mPanZoomController = PanZoomController.Factory.create(this, view, eventDispatcher); - mMarginsAnimator = new LayerMarginsAnimator(this, view); - mView = view; - mView.setListener(this); - mContentDocumentIsDisplayed = true; - } - - /** Attaches to root layer so that Gecko appears. */ - public void notifyGeckoReady() { - mGeckoIsReady = true; - - mRootLayer = new VirtualLayer(new IntSize(mView.getWidth(), mView.getHeight())); - mLayerRenderer = mView.getRenderer(); - - sendResizeEventIfNecessary(true); - - DisplayPortCalculator.initPrefs(); - - // Gecko being ready is one of the two conditions (along with having an available - // surface) that cause us to create the compositor. So here, now that we know gecko - // is ready, call createCompositor() to see if we can actually do the creation. - // This needs to run on the UI thread so that the surface validity can't change on - // us while we're in the middle of creating the compositor. - mView.post(new Runnable() { - @Override - public void run() { - mView.getGLController().createCompositor(); - } - }); - } - - public void destroy() { - mPanZoomController.destroy(); - mMarginsAnimator.destroy(); - } - - /** - * Returns true if this client is fine with performing a redraw operation or false if it - * would prefer that the action didn't take place. - */ - private boolean getRedrawHint() { - if (mForceRedraw) { - mForceRedraw = false; - return true; - } - - if (!mPanZoomController.getRedrawHint()) { - return false; - } - - return DisplayPortCalculator.aboutToCheckerboard(mViewportMetrics, - mPanZoomController.getVelocityVector(), mDisplayPort); - } - - Layer getRoot() { - return mGeckoIsReady ? mRootLayer : null; - } - - public LayerView getView() { - return mView; - } - - public FloatSize getViewportSize() { - return mViewportMetrics.getSize(); - } - - /** - * The view calls this function to indicate that the viewport changed size. It must hold the - * monitor while calling it. - * - * TODO: Refactor this to use an interface. Expose that interface only to the view and not - * to the layer client. That way, the layer client won't be tempted to call this, which might - * result in an infinite loop. - */ - void setViewportSize(int width, int height) { - mViewportMetrics = mViewportMetrics.setViewportSize(width, height); - - if (mGeckoIsReady) { - // here we send gecko a resize message. The code in browser.js is responsible for - // picking up on that resize event, modifying the viewport as necessary, and informing - // us of the new viewport. - sendResizeEventIfNecessary(true); - // the following call also sends gecko a message, which will be processed after the resize - // message above has updated the viewport. this message ensures that if we have just put - // focus in a text field, we scroll the content so that the text field is in view. - GeckoAppShell.viewSizeChanged(); - } - } - - PanZoomController getPanZoomController() { - return mPanZoomController; - } - - LayerMarginsAnimator getLayerMarginsAnimator() { - return mMarginsAnimator; - } - - /* Informs Gecko that the screen size has changed. */ - private void sendResizeEventIfNecessary(boolean force) { - DisplayMetrics metrics = mContext.getResources().getDisplayMetrics(); - - IntSize newScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels); - IntSize newWindowSize = new IntSize(mView.getWidth(), mView.getHeight()); - - boolean screenSizeChanged = !mScreenSize.equals(newScreenSize); - boolean windowSizeChanged = !mWindowSize.equals(newWindowSize); - - if (!force && !screenSizeChanged && !windowSizeChanged) { - return; - } - - mScreenSize = newScreenSize; - mWindowSize = newWindowSize; - - if (screenSizeChanged) { - Log.d(LOGTAG, "Screen-size changed to " + mScreenSize); - } - - if (windowSizeChanged) { - Log.d(LOGTAG, "Window-size changed to " + mWindowSize); - } - - GeckoEvent event = GeckoEvent.createSizeChangedEvent(mWindowSize.width, mWindowSize.height, - mScreenSize.width, mScreenSize.height); - GeckoAppShell.sendEventToGecko(event); - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Window:Resize", "")); - } - - /** Sets the current page rect. You must hold the monitor while calling this. */ - private void setPageRect(RectF rect, RectF cssRect) { - // Since the "rect" is always just a multiple of "cssRect" we don't need to - // check both; this function assumes that both "rect" and "cssRect" are relative - // the zoom factor in mViewportMetrics. - if (mViewportMetrics.getCssPageRect().equals(cssRect)) - return; - - mViewportMetrics = mViewportMetrics.setPageRect(rect, cssRect); - - // Page size is owned by the layer client, so no need to notify it of - // this change. - - post(new Runnable() { - @Override - public void run() { - mPanZoomController.pageRectUpdated(); - mView.requestRender(); - } - }); - } - - /** - * Derives content document fixed position margins/fixed layer margins from - * the view margins in the given metrics object. - */ - private void getFixedMargins(ImmutableViewportMetrics metrics, RectF fixedMargins) { - fixedMargins.left = 0; - fixedMargins.top = 0; - fixedMargins.right = 0; - fixedMargins.bottom = 0; - - // The maximum margins are determined by the scrollable area of the page. - float maxMarginWidth = Math.max(0, metrics.getPageWidth() - metrics.getWidthWithoutMargins()); - float maxMarginHeight = Math.max(0, metrics.getPageHeight() - metrics.getHeightWithoutMargins()); - - PointF offset = metrics.getMarginOffset(); - RectF overscroll = metrics.getOverscroll(); - if (offset.x >= 0) { - fixedMargins.right = Math.max(0, Math.min(offset.x - overscroll.right, maxMarginWidth)); - } else { - fixedMargins.left = Math.max(0, Math.min(-offset.x - overscroll.left, maxMarginWidth)); - } - if (offset.y >= 0) { - fixedMargins.bottom = Math.max(0, Math.min(offset.y - overscroll.bottom, maxMarginHeight)); - } else { - fixedMargins.top = Math.max(0, Math.min(-offset.y - overscroll.top, maxMarginHeight)); - } - - // Adjust for overscroll. If we're overscrolled on one side, add that - // distance to the margins of the other side (limiting to the maximum - // margin size calculated above). - if (overscroll.left > 0) { - fixedMargins.right = Math.min(maxMarginWidth - fixedMargins.left, - fixedMargins.right + overscroll.left); - } else if (overscroll.right > 0) { - fixedMargins.left = Math.min(maxMarginWidth - fixedMargins.right, - fixedMargins.left + overscroll.right); - } - if (overscroll.top > 0) { - fixedMargins.bottom = Math.min(maxMarginHeight - fixedMargins.top, - fixedMargins.bottom + overscroll.top); - } else if (overscroll.bottom > 0) { - fixedMargins.top = Math.min(maxMarginHeight - fixedMargins.bottom, - fixedMargins.top + overscroll.bottom); - } - } - - private void adjustViewport(DisplayPortMetrics displayPort) { - ImmutableViewportMetrics metrics = getViewportMetrics(); - ImmutableViewportMetrics clampedMetrics = metrics.clamp(); - - RectF margins = new RectF(); - getFixedMargins(metrics, margins); - clampedMetrics = clampedMetrics.setMargins( - margins.left, margins.top, margins.right, margins.bottom); - - if (displayPort == null) { - displayPort = DisplayPortCalculator.calculate(metrics, mPanZoomController.getVelocityVector()); - } - - mDisplayPort = displayPort; - mGeckoViewport = clampedMetrics; - - if (mRecordDrawTimes) { - mDrawTimingQueue.add(displayPort); - } - - GeckoAppShell.sendEventToGecko(GeckoEvent.createViewportEvent(clampedMetrics, displayPort)); - } - - /** Aborts any pan/zoom animation that is currently in progress. */ - private void abortPanZoomAnimation() { - if (mPanZoomController != null) { - post(new Runnable() { - @Override - public void run() { - mPanZoomController.abortAnimation(); - } - }); - } - } - - /** - * The different types of Viewport messages handled. All viewport events - * expect a display-port to be returned, but can handle one not being - * returned. - */ - private enum ViewportMessageType { - UPDATE, // The viewport has changed and should be entirely updated - PAGE_SIZE // The viewport's page-size has changed - } - - /** Viewport message handler. */ - private DisplayPortMetrics handleViewportMessage(ImmutableViewportMetrics messageMetrics, ViewportMessageType type) { - synchronized (getLock()) { - ImmutableViewportMetrics newMetrics; - ImmutableViewportMetrics oldMetrics = getViewportMetrics(); - - switch (type) { - default: - case UPDATE: - // Keep the old viewport size - newMetrics = messageMetrics.setViewportSize(oldMetrics.getWidth(), oldMetrics.getHeight()); - if (!oldMetrics.fuzzyEquals(newMetrics)) { - abortPanZoomAnimation(); - } - break; - case PAGE_SIZE: - // adjust the page dimensions to account for differences in zoom - // between the rendered content (which is what Gecko tells us) - // and our zoom level (which may have diverged). - float scaleFactor = oldMetrics.zoomFactor / messageMetrics.zoomFactor; - newMetrics = oldMetrics.setPageRect(RectUtils.scale(messageMetrics.getPageRect(), scaleFactor), messageMetrics.getCssPageRect()); - break; - } - - // Update the Gecko-side viewport metrics. Make sure to do this - // before modifying the metrics below. - final ImmutableViewportMetrics geckoMetrics = newMetrics.clamp(); - post(new Runnable() { - @Override - public void run() { - mGeckoViewport = geckoMetrics; - } - }); - - setViewportMetrics(newMetrics, type == ViewportMessageType.UPDATE); - mDisplayPort = DisplayPortCalculator.calculate(getViewportMetrics(), null); - } - return mDisplayPort; - } - - /* This is invoked by JNI on the gecko thread */ - DisplayPortMetrics getDisplayPort(boolean pageSizeUpdate, boolean isBrowserContentDisplayed, int tabId, ImmutableViewportMetrics metrics) { - Tabs tabs = Tabs.getInstance(); - if (tabs.isSelectedTab(tabs.getTab(tabId)) && isBrowserContentDisplayed) { - // for foreground tabs, send the viewport update unless the document - // displayed is different from the content document. In that case, just - // calculate the display port. - return handleViewportMessage(metrics, pageSizeUpdate ? ViewportMessageType.PAGE_SIZE : ViewportMessageType.UPDATE); - } else { - // for background tabs, request a new display port calculation, so that - // when we do switch to that tab, we have the correct display port and - // don't need to draw twice (once to allow the first-paint viewport to - // get to java, and again once java figures out the display port). - return DisplayPortCalculator.calculate(metrics, null); - } - } - - /* This is invoked by JNI on the gecko thread */ - void contentDocumentChanged() { - mContentDocumentIsDisplayed = false; - } - - /* This is invoked by JNI on the gecko thread */ - boolean isContentDocumentDisplayed() { - return mContentDocumentIsDisplayed; - } - - // This is called on the Gecko thread to determine if we're still interested - // in the update of this display-port to continue. We can return true here - // to abort the current update and continue with any subsequent ones. This - // is useful for slow-to-render pages when the display-port starts lagging - // behind enough that continuing to draw it is wasted effort. - public ProgressiveUpdateData progressiveUpdateCallback(boolean aHasPendingNewThebesContent, - float x, float y, float width, float height, - float resolution, boolean lowPrecision) { - // Reset the checkerboard risk flag when switching to low precision - // rendering. - if (lowPrecision && !mLastProgressiveUpdateWasLowPrecision) { - // Skip low precision rendering until we're at risk of checkerboarding. - if (!mProgressiveUpdateWasInDanger) { - mProgressiveUpdateData.abort = true; - return mProgressiveUpdateData; - } - mProgressiveUpdateWasInDanger = false; - } - mLastProgressiveUpdateWasLowPrecision = lowPrecision; - - // Grab a local copy of the last display-port sent to Gecko and the - // current viewport metrics to avoid races when accessing them. - DisplayPortMetrics displayPort = mDisplayPort; - ImmutableViewportMetrics viewportMetrics = mViewportMetrics; - mProgressiveUpdateData.setViewport(viewportMetrics); - mProgressiveUpdateData.abort = false; - - // Always abort updates if the resolution has changed. There's no use - // in drawing at the incorrect resolution. - if (!FloatUtils.fuzzyEquals(resolution, viewportMetrics.zoomFactor)) { - Log.d(LOGTAG, "Aborting draw due to resolution change"); - mProgressiveUpdateData.abort = true; - return mProgressiveUpdateData; - } - - // Store the high precision displayport for comparison when doing low - // precision updates. - if (!lowPrecision) { - if (!FloatUtils.fuzzyEquals(resolution, mProgressiveUpdateDisplayPort.resolution) || - !FloatUtils.fuzzyEquals(x, mProgressiveUpdateDisplayPort.getLeft()) || - !FloatUtils.fuzzyEquals(y, mProgressiveUpdateDisplayPort.getTop()) || - !FloatUtils.fuzzyEquals(x + width, mProgressiveUpdateDisplayPort.getRight()) || - !FloatUtils.fuzzyEquals(y + height, mProgressiveUpdateDisplayPort.getBottom())) { - mProgressiveUpdateDisplayPort = - new DisplayPortMetrics(x, y, x+width, y+height, resolution); - } - } - - // If we're not doing low precision draws and we're about to - // checkerboard, enable low precision drawing. - if (!lowPrecision && !mProgressiveUpdateWasInDanger) { - if (DisplayPortCalculator.aboutToCheckerboard(viewportMetrics, - mPanZoomController.getVelocityVector(), mProgressiveUpdateDisplayPort)) { - mProgressiveUpdateWasInDanger = true; - } - } - - // XXX All sorts of rounding happens inside Gecko that becomes hard to - // account exactly for. Given we align the display-port to tile - // boundaries (and so they rarely vary by sub-pixel amounts), just - // check that values are within a couple of pixels of the - // display-port bounds. - - // Never abort drawing if we can't be sure we've sent a more recent - // display-port. If we abort updating when we shouldn't, we can end up - // with blank regions on the screen and we open up the risk of entering - // an endless updating cycle. - if (Math.abs(displayPort.getLeft() - mProgressiveUpdateDisplayPort.getLeft()) <= 2 && - Math.abs(displayPort.getTop() - mProgressiveUpdateDisplayPort.getTop()) <= 2 && - Math.abs(displayPort.getBottom() - mProgressiveUpdateDisplayPort.getBottom()) <= 2 && - Math.abs(displayPort.getRight() - mProgressiveUpdateDisplayPort.getRight()) <= 2) { - return mProgressiveUpdateData; - } - - // Abort updates when the display-port no longer contains the visible - // area of the page (that is, the viewport cropped by the page - // boundaries). - // XXX This makes the assumption that we never let the visible area of - // the page fall outside of the display-port. - if (Math.max(viewportMetrics.viewportRectLeft, viewportMetrics.pageRectLeft) + 1 < x || - Math.max(viewportMetrics.viewportRectTop, viewportMetrics.pageRectTop) + 1 < y || - Math.min(viewportMetrics.viewportRectRight, viewportMetrics.pageRectRight) - 1 > x + width || - Math.min(viewportMetrics.viewportRectBottom, viewportMetrics.pageRectBottom) - 1 > y + height) { - Log.d(LOGTAG, "Aborting update due to viewport not in display-port"); - mProgressiveUpdateData.abort = true; - - // Enable low-precision drawing, as we're likely to be in danger if - // this situation has been encountered. - mProgressiveUpdateWasInDanger = true; - - return mProgressiveUpdateData; - } - - // Abort drawing stale low-precision content if there's a more recent - // display-port in the pipeline. - if (lowPrecision && !aHasPendingNewThebesContent) { - mProgressiveUpdateData.abort = true; - } - return mProgressiveUpdateData; - } - - void setZoomConstraints(ZoomConstraints constraints) { - mZoomConstraints = constraints; - } - - void setIsRTL(boolean aIsRTL) { - synchronized (getLock()) { - ImmutableViewportMetrics newMetrics = getViewportMetrics().setIsRTL(aIsRTL); - setViewportMetrics(newMetrics, false); - } - } - - /** This function is invoked by Gecko via JNI; be careful when modifying signature. - * The compositor invokes this function just before compositing a frame where the document - * is different from the document composited on the last frame. In these cases, the viewport - * information we have in Java is no longer valid and needs to be replaced with the new - * viewport information provided. setPageRect will never be invoked on the same frame that - * this function is invoked on; and this function will always be called prior to syncViewportInfo. - */ - public void setFirstPaintViewport(float offsetX, float offsetY, float zoom, - float cssPageLeft, float cssPageTop, float cssPageRight, float cssPageBottom) { - synchronized (getLock()) { - ImmutableViewportMetrics currentMetrics = getViewportMetrics(); - - Tab tab = Tabs.getInstance().getSelectedTab(); - - RectF cssPageRect = new RectF(cssPageLeft, cssPageTop, cssPageRight, cssPageBottom); - RectF pageRect = RectUtils.scaleAndRound(cssPageRect, zoom); - - final ImmutableViewportMetrics newMetrics = currentMetrics - .setViewportOrigin(offsetX, offsetY) - .setZoomFactor(zoom) - .setPageRect(pageRect, cssPageRect) - .setIsRTL(tab.getIsRTL()); - // Since we have switched to displaying a different document, we need to update any - // viewport-related state we have lying around. This includes mGeckoViewport and - // mViewportMetrics. Usually this information is updated via handleViewportMessage - // while we remain on the same document. - post(new Runnable() { - @Override - public void run() { - mGeckoViewport = newMetrics; - } - }); - - setViewportMetrics(newMetrics); - - mView.setBackgroundColor(tab.getBackgroundColor()); - setZoomConstraints(tab.getZoomConstraints()); - - // At this point, we have just switched to displaying a different document than we - // we previously displaying. This means we need to abort any panning/zooming animations - // that are in progress and send an updated display port request to browser.js as soon - // as possible. The call to PanZoomController.abortAnimation accomplishes this by calling the - // forceRedraw function, which sends the viewport to gecko. The display port request is - // actually a full viewport update, which is fine because if browser.js has somehow moved to - // be out of sync with this first-paint viewport, then we force them back in sync. - abortPanZoomAnimation(); - - // Indicate that the document is about to be composited so the - // LayerView background can be removed. - if (mView.getPaintState() == LayerView.PAINT_START) { - mView.setPaintState(LayerView.PAINT_BEFORE_FIRST); - } - } - DisplayPortCalculator.resetPageState(); - mDrawTimingQueue.reset(); - - mContentDocumentIsDisplayed = true; - } - - /** This function is invoked by Gecko via JNI; be careful when modifying signature. - * The compositor invokes this function whenever it determines that the page rect - * has changed (based on the information it gets from layout). If setFirstPaintViewport - * is invoked on a frame, then this function will not be. For any given frame, this - * function will be invoked before syncViewportInfo. - */ - public void setPageRect(float cssPageLeft, float cssPageTop, float cssPageRight, float cssPageBottom) { - synchronized (getLock()) { - RectF cssPageRect = new RectF(cssPageLeft, cssPageTop, cssPageRight, cssPageBottom); - float ourZoom = getViewportMetrics().zoomFactor; - setPageRect(RectUtils.scale(cssPageRect, ourZoom), cssPageRect); - // Here the page size of the document has changed, but the document being displayed - // is still the same. Therefore, we don't need to send anything to browser.js; any - // changes we need to make to the display port will get sent the next time we call - // adjustViewport(). - } - } - - /** This function is invoked by Gecko via JNI; be careful when modifying signature. - * The compositor invokes this function on every frame to figure out what part of the - * page to display, and to inform Java of the current display port. Since it is called - * on every frame, it needs to be ultra-fast. - * It avoids taking any locks or allocating any objects. We keep around a - * mCurrentViewTransform so we don't need to allocate a new ViewTransform - * everytime we're called. NOTE: we might be able to return a ImmutableViewportMetrics - * which would avoid the copy into mCurrentViewTransform. - */ - public ViewTransform syncViewportInfo(int x, int y, int width, int height, float resolution, boolean layersUpdated) { - // getViewportMetrics is thread safe so we don't need to synchronize. - // We save the viewport metrics here, so we later use it later in - // createFrame (which will be called by nsWindow::DrawWindowUnderlay on - // the native side, by the compositor). The viewport - // metrics can change between here and there, as it's accessed outside - // of the compositor thread. - mFrameMetrics = getViewportMetrics(); - - mCurrentViewTransform.x = mFrameMetrics.viewportRectLeft; - mCurrentViewTransform.y = mFrameMetrics.viewportRectTop; - mCurrentViewTransform.scale = mFrameMetrics.zoomFactor; - - // Adjust the fixed layer margins so that overscroll subtracts from them. - getFixedMargins(mFrameMetrics, mCurrentViewTransformMargins); - mCurrentViewTransform.fixedLayerMarginLeft = mCurrentViewTransformMargins.left; - mCurrentViewTransform.fixedLayerMarginTop = mCurrentViewTransformMargins.top; - mCurrentViewTransform.fixedLayerMarginRight = mCurrentViewTransformMargins.right; - mCurrentViewTransform.fixedLayerMarginBottom = mCurrentViewTransformMargins.bottom; - - // Offset the view transform so that it renders in the correct place. - PointF offset = mFrameMetrics.getMarginOffset(); - mCurrentViewTransform.offsetX = offset.x; - mCurrentViewTransform.offsetY = offset.y; - - mRootLayer.setPositionAndResolution( - Math.round(x + mCurrentViewTransform.offsetX), - Math.round(y + mCurrentViewTransform.offsetY), - Math.round(x + width + mCurrentViewTransform.offsetX), - Math.round(y + height + mCurrentViewTransform.offsetY), - resolution); - - if (layersUpdated && mRecordDrawTimes) { - // If we got a layers update, that means a draw finished. Check to see if the area drawn matches - // one of our requested displayports; if it does calculate the draw time and notify the - // DisplayPortCalculator - DisplayPortMetrics drawn = new DisplayPortMetrics(x, y, x + width, y + height, resolution); - long time = mDrawTimingQueue.findTimeFor(drawn); - if (time >= 0) { - long now = SystemClock.uptimeMillis(); - time = now - time; - mRecordDrawTimes = DisplayPortCalculator.drawTimeUpdate(time, width * height); - } - } - - if (layersUpdated && mDrawListener != null) { - /* Used by robocop for testing purposes */ - mDrawListener.drawFinished(); - } - - return mCurrentViewTransform; - } - - /* Invoked by JNI from the compositor thread */ - public ViewTransform syncFrameMetrics(float offsetX, float offsetY, float zoom, - float cssPageLeft, float cssPageTop, float cssPageRight, float cssPageBottom, - boolean layersUpdated, int x, int y, int width, int height, float resolution, - boolean isFirstPaint) - { - if (isFirstPaint) { - setFirstPaintViewport(offsetX, offsetY, zoom, - cssPageLeft, cssPageTop, cssPageRight, cssPageBottom); - } - - return syncViewportInfo(x, y, width, height, resolution, layersUpdated); - } - - /** This function is invoked by Gecko via JNI; be careful when modifying signature. */ - public LayerRenderer.Frame createFrame() { - // Create the shaders and textures if necessary. - if (!mLayerRendererInitialized) { - mLayerRenderer.checkMonitoringEnabled(); - mLayerRenderer.createDefaultProgram(); - mLayerRendererInitialized = true; - } - - return mLayerRenderer.createFrame(mFrameMetrics); - } - - /** This function is invoked by Gecko via JNI; be careful when modifying signature. */ - public void activateProgram() { - mLayerRenderer.activateDefaultProgram(); - } - - /** This function is invoked by Gecko via JNI; be careful when modifying signature. */ - public void deactivateProgram() { - mLayerRenderer.deactivateDefaultProgram(); - } - - private void geometryChanged(DisplayPortMetrics displayPort) { - /* Let Gecko know if the screensize has changed */ - sendResizeEventIfNecessary(false); - if (getRedrawHint()) { - adjustViewport(displayPort); - } - } - - /** Implementation of LayerView.Listener */ - @Override - public void renderRequested() { - try { - GeckoAppShell.scheduleComposite(); - } catch (UnsupportedOperationException uoe) { - // In some very rare cases this gets called before libxul is loaded, - // so catch and ignore the exception that will throw. See bug 837821 - Log.d(LOGTAG, "Dropping renderRequested call before libxul load."); - } - } - - /** Implementation of LayerView.Listener */ - @Override - public void sizeChanged(int width, int height) { - // We need to make sure a draw happens synchronously at this point, - // but resizing the surface before the SurfaceView has resized will - // cause a visible jump. - mView.getGLController().resumeCompositor(mWindowSize.width, mWindowSize.height); - } - - /** Implementation of LayerView.Listener */ - @Override - public void surfaceChanged(int width, int height) { - setViewportSize(width, height); - } - - /** Implementation of PanZoomTarget */ - @Override - public ImmutableViewportMetrics getViewportMetrics() { - return mViewportMetrics; - } - - /** Implementation of PanZoomTarget */ - @Override - public ZoomConstraints getZoomConstraints() { - return mZoomConstraints; - } - - /** Implementation of PanZoomTarget */ - @Override - public boolean isFullScreen() { - return mView.isFullScreen(); - } - - /** Implementation of PanZoomTarget */ - @Override - public RectF getMaxMargins() { - return mMarginsAnimator.getMaxMargins(); - } - - /** Implementation of PanZoomTarget */ - @Override - public void setAnimationTarget(ImmutableViewportMetrics metrics) { - if (mGeckoIsReady) { - // We know what the final viewport of the animation is going to be, so - // immediately request a draw of that area by setting the display port - // accordingly. This way we should have the content pre-rendered by the - // time the animation is done. - DisplayPortMetrics displayPort = DisplayPortCalculator.calculate(metrics, null); - adjustViewport(displayPort); - } - } - - /** Implementation of PanZoomTarget - * You must hold the monitor while calling this. - */ - @Override - public void setViewportMetrics(ImmutableViewportMetrics metrics) { - setViewportMetrics(metrics, true); - } - - /* - * You must hold the monitor while calling this. - */ - private void setViewportMetrics(ImmutableViewportMetrics metrics, boolean notifyGecko) { - // This class owns the viewport size and the fixed layer margins; don't let other pieces - // of code clobber either of them. The only place the viewport size should ever be - // updated is in GeckoLayerClient.setViewportSize, and the only place the margins should - // ever be updated is in GeckoLayerClient.setFixedLayerMargins; both of these assign to - // mViewportMetrics directly. - metrics = metrics.setViewportSize(mViewportMetrics.getWidth(), mViewportMetrics.getHeight()); - metrics = metrics.setMarginsFrom(mViewportMetrics); - mViewportMetrics = metrics; - - viewportMetricsChanged(notifyGecko); - } - - /* - * You must hold the monitor while calling this. - */ - private void viewportMetricsChanged(boolean notifyGecko) { - if (mViewportChangeListener != null) { - mViewportChangeListener.onMetricsChanged(mViewportMetrics); - } - - mView.requestRender(); - if (notifyGecko && mGeckoIsReady) { - geometryChanged(null); - } - setShadowVisibility(); - } - - /* - * Updates the viewport metrics, overriding the viewport size and margins - * which are normally retained when calling setViewportMetrics. - * You must hold the monitor while calling this. - */ - void forceViewportMetrics(ImmutableViewportMetrics metrics, boolean notifyGecko, boolean forceRedraw) { - if (forceRedraw) { - mForceRedraw = true; - } - mViewportMetrics = metrics; - viewportMetricsChanged(notifyGecko); - } - - /** Implementation of PanZoomTarget - * Scroll the viewport by a certain amount. This will take viewport margins - * and margin animation into account. If margins are currently animating, - * this will just go ahead and modify the viewport origin, otherwise the - * delta will be applied to the margins and the remainder will be applied to - * the viewport origin. - * - * You must hold the monitor while calling this. - */ - @Override - public void scrollBy(float dx, float dy) { - // Set mViewportMetrics manually so the margin changes take. - mViewportMetrics = mMarginsAnimator.scrollBy(mViewportMetrics, dx, dy); - viewportMetricsChanged(true); - } - - /** Implementation of PanZoomTarget - * Notification that a subdocument has been scrolled by a certain amount. - * This is used here to make sure that the margins are still accessible - * during subdocument scrolling. - * - * You must hold the monitor while calling this. - */ - @Override - public void onSubdocumentScrollBy(float dx, float dy) { - ImmutableViewportMetrics newMarginsMetrics = - mMarginsAnimator.scrollBy(mViewportMetrics, dx, dy); - mViewportMetrics = mViewportMetrics.setMarginsFrom(newMarginsMetrics); - viewportMetricsChanged(true); - } - - /** Implementation of PanZoomTarget */ - @Override - public void panZoomStopped() { - if (mViewportChangeListener != null) { - mViewportChangeListener.onPanZoomStopped(); - } - } - - public interface OnMetricsChangedListener { - public void onMetricsChanged(ImmutableViewportMetrics viewport); - public void onPanZoomStopped(); - } - - private void setShadowVisibility() { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - if (BrowserApp.mBrowserToolbar == null) { - return; - } - ImmutableViewportMetrics m = mViewportMetrics; - BrowserApp.mBrowserToolbar.setShadowVisibility(m.viewportRectTop >= m.pageRectTop); - } - }); - } - - /** Implementation of PanZoomTarget */ - @Override - public void forceRedraw(DisplayPortMetrics displayPort) { - mForceRedraw = true; - if (mGeckoIsReady) { - geometryChanged(displayPort); - } - } - - /** Implementation of PanZoomTarget */ - @Override - public boolean post(Runnable action) { - return mView.post(action); - } - - /** Implementation of PanZoomTarget */ - @Override - public boolean postDelayed(Runnable action, long delayMillis) { - return mView.postDelayed(action, delayMillis); - } - - /** Implementation of PanZoomTarget */ - @Override - public Object getLock() { - return this; - } - - /** Implementation of PanZoomTarget - * Converts a point from layer view coordinates to layer coordinates. In other words, given a - * point measured in pixels from the top left corner of the layer view, returns the point in - * pixels measured from the last scroll position we sent to Gecko, in CSS pixels. Assuming the - * events being sent to Gecko are processed in FIFO order, this calculation should always be - * correct. - */ - @Override - public PointF convertViewPointToLayerPoint(PointF viewPoint) { - if (!mGeckoIsReady) { - return null; - } - - ImmutableViewportMetrics viewportMetrics = mViewportMetrics; - PointF origin = viewportMetrics.getOrigin(); - PointF offset = viewportMetrics.getMarginOffset(); - origin.offset(-offset.x, -offset.y); - float zoom = viewportMetrics.zoomFactor; - ImmutableViewportMetrics geckoViewport = mGeckoViewport; - PointF geckoOrigin = geckoViewport.getOrigin(); - float geckoZoom = geckoViewport.zoomFactor; - - // viewPoint + origin - offset gives the coordinate in device pixels from the top-left corner of the page. - // Divided by zoom, this gives us the coordinate in CSS pixels from the top-left corner of the page. - // geckoOrigin / geckoZoom is where Gecko thinks it is (scrollTo position) in CSS pixels from - // the top-left corner of the page. Subtracting the two gives us the offset of the viewPoint from - // the current Gecko coordinate in CSS pixels. - PointF layerPoint = new PointF( - ((viewPoint.x + origin.x) / zoom) - (geckoOrigin.x / geckoZoom), - ((viewPoint.y + origin.y) / zoom) - (geckoOrigin.y / geckoZoom)); - - return layerPoint; - } - - public void setOnMetricsChangedListener(OnMetricsChangedListener listener) { - mViewportChangeListener = listener; - } - - /** Used by robocop for testing purposes. Not for production use! */ - public void setDrawListener(DrawListener listener) { - mDrawListener = listener; - } - - /** Used by robocop for testing purposes. Not for production use! */ - public static interface DrawListener { - public void drawFinished(); - } -} diff --git a/mobile/android/base/menu/GeckoMenu.java b/mobile/android/base/menu/GeckoMenu.java deleted file mode 100644 index 025e5bf9f..000000000 --- a/mobile/android/base/menu/GeckoMenu.java +++ /dev/null @@ -1,629 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.menu; - -import org.mozilla.gecko.R; - -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.util.AttributeSet; -import android.view.ActionProvider; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.SubMenu; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.BaseAdapter; -import android.widget.LinearLayout; -import android.widget.ListView; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class GeckoMenu extends ListView - implements Menu, - AdapterView.OnItemClickListener, - GeckoMenuItem.OnShowAsActionChangedListener { - private static final String LOGTAG = "GeckoMenu"; - - /* - * A callback for a menu item selected event. - */ - public static interface Callback { - // Called when a menu item is selected, with the actual menu item as the argument. - public boolean onMenuItemSelected(MenuItem item); - } - - /* - * An interface for a presenter to show the menu. - * Either an Activity or a View can be a presenter, that can watch for events - * and show/hide menu. - */ - public static interface MenuPresenter { - // Open the menu. - public void openMenu(); - - // Show the actual view contaning the menu items. This can either be a parent or sub-menu. - public void showMenu(View menu); - - // Close the menu. - public void closeMenu(); - } - - /* - * An interface for a presenter of action-items. - * Either an Activity or a View can be a presenter, that can watch for events - * and add/remove action-items. If not ActionItemBarPresenter, the menu uses a - * DefaultActionItemBar, that shows the action-items as a header over list-view. - */ - public static interface ActionItemBarPresenter { - // Add an action-item. - public void addActionItem(View actionItem); - - // Remove an action-item. - public void removeActionItem(View actionItem); - } - - protected static final int NO_ID = 0; - - // List of all menu items. - private List<GeckoMenuItem> mItems; - - // Map of items in action-bar and their views. - private Map<GeckoMenuItem, View> mActionItems; - - // Reference to a callback for menu events. - private Callback mCallback; - - // Reference to menu presenter. - private MenuPresenter mMenuPresenter; - - // Reference to action-items bar in action-bar. - private ActionItemBarPresenter mActionItemBarPresenter; - - // Adapter to hold the list of menu items. - private MenuItemsAdapter mAdapter; - - public GeckoMenu(Context context) { - this(context, null); - } - - public GeckoMenu(Context context, AttributeSet attrs) { - this(context, attrs, android.R.attr.listViewStyle); - } - - public GeckoMenu(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, - LayoutParams.FILL_PARENT)); - - // Attach an adapter. - mAdapter = new MenuItemsAdapter(); - setAdapter(mAdapter); - setOnItemClickListener(this); - - mItems = new ArrayList<GeckoMenuItem>(); - mActionItems = new HashMap<GeckoMenuItem, View>(); - - mActionItemBarPresenter = (DefaultActionItemBar) LayoutInflater.from(context).inflate(R.layout.menu_action_bar, null); - } - - @Override - public MenuItem add(CharSequence title) { - GeckoMenuItem menuItem = new GeckoMenuItem(this, NO_ID, 0, title); - addItem(menuItem); - return menuItem; - } - - @Override - public MenuItem add(int groupId, int itemId, int order, int titleRes) { - GeckoMenuItem menuItem = new GeckoMenuItem(this, itemId, order, titleRes); - addItem(menuItem); - return menuItem; - } - - @Override - public MenuItem add(int titleRes) { - GeckoMenuItem menuItem = new GeckoMenuItem(this, NO_ID, 0, titleRes); - addItem(menuItem); - return menuItem; - } - - @Override - public MenuItem add(int groupId, int itemId, int order, CharSequence title) { - GeckoMenuItem menuItem = new GeckoMenuItem(this, itemId, order, title); - addItem(menuItem); - return menuItem; - } - - private void addItem(GeckoMenuItem menuItem) { - menuItem.setOnShowAsActionChangedListener(this); - mAdapter.addMenuItem(menuItem); - mItems.add(menuItem); - } - - private void addActionItem(final GeckoMenuItem menuItem) { - menuItem.setOnShowAsActionChangedListener(this); - - if (mActionItems.size() == 0 && - mActionItemBarPresenter instanceof DefaultActionItemBar) { - // Reset the adapter before adding the header view to a list. - setAdapter(null); - addHeaderView((DefaultActionItemBar) mActionItemBarPresenter); - setAdapter(mAdapter); - } - - View actionView = menuItem.getActionView(); - actionView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - handleMenuItemClick(menuItem); - } - }); - - mActionItems.put(menuItem, actionView); - mActionItemBarPresenter.addActionItem(actionView); - mItems.add(menuItem); - } - - @Override - public int addIntentOptions(int groupId, int itemId, int order, ComponentName caller, Intent[] specifics, Intent intent, int flags, MenuItem[] outSpecificItems) { - return 0; - } - - @Override - public SubMenu addSubMenu(int groupId, int itemId, int order, CharSequence title) { - MenuItem menuItem = add(groupId, itemId, order, title); - return addSubMenu(menuItem); - } - - @Override - public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) { - MenuItem menuItem = add(groupId, itemId, order, titleRes); - return addSubMenu(menuItem); - } - - @Override - public SubMenu addSubMenu(CharSequence title) { - MenuItem menuItem = add(title); - return addSubMenu(menuItem); - } - - @Override - public SubMenu addSubMenu(int titleRes) { - MenuItem menuItem = add(titleRes); - return addSubMenu(menuItem); - } - - private SubMenu addSubMenu(MenuItem menuItem) { - GeckoSubMenu subMenu = new GeckoSubMenu(getContext()); - subMenu.setMenuItem(menuItem); - subMenu.setCallback(mCallback); - subMenu.setMenuPresenter(mMenuPresenter); - ((GeckoMenuItem) menuItem).setSubMenu(subMenu); - return subMenu; - } - - @Override - public void clear() { - for (GeckoMenuItem menuItem : mItems) { - if (menuItem.hasSubMenu()) { - menuItem.getSubMenu().clear(); - } - } - - mAdapter.clear(); - - mItems.clear(); - mActionItems.clear(); - } - - @Override - public void close() { - if (mMenuPresenter != null) - mMenuPresenter.closeMenu(); - } - - private void showMenu(View viewForMenu) { - if (mMenuPresenter != null) - mMenuPresenter.showMenu(viewForMenu); - } - - @Override - public MenuItem findItem(int id) { - for (GeckoMenuItem menuItem : mItems) { - if (menuItem.getItemId() == id) { - return menuItem; - } else if (menuItem.hasSubMenu()) { - if (!menuItem.hasActionProvider()) { - SubMenu subMenu = menuItem.getSubMenu(); - MenuItem item = subMenu.findItem(id); - if (item != null) - return item; - } - } - } - return null; - } - - @Override - public MenuItem getItem(int index) { - if (index < mItems.size()) - return mItems.get(index); - - return null; - } - - @Override - public boolean hasVisibleItems() { - for (GeckoMenuItem menuItem : mItems) { - if (menuItem.isVisible()) - return true; - } - - return false; - } - - @Override - public boolean isShortcutKey(int keyCode, KeyEvent event) { - return true; - } - - @Override - public boolean performIdentifierAction(int id, int flags) { - return false; - } - - @Override - public boolean performShortcut(int keyCode, KeyEvent event, int flags) { - return false; - } - - @Override - public void removeGroup(int groupId) { - } - - @Override - public void removeItem(int id) { - GeckoMenuItem item = (GeckoMenuItem) findItem(id); - if (item == null) - return; - - if (mActionItems.containsKey(item)) { - if (mActionItemBarPresenter != null) - mActionItemBarPresenter.removeActionItem(mActionItems.get(item)); - - mActionItems.remove(item); - mItems.remove(item); - - if (mActionItems.size() == 0 && - mActionItemBarPresenter instanceof DefaultActionItemBar) { - // Reset the adapter before removing the header view from a list. - setAdapter(null); - removeHeaderView((DefaultActionItemBar) mActionItemBarPresenter); - setAdapter(mAdapter); - } - - return; - } - - mAdapter.removeMenuItem(item); - mItems.remove(item); - } - - @Override - public void setGroupCheckable(int group, boolean checkable, boolean exclusive) { - } - - @Override - public void setGroupEnabled(int group, boolean enabled) { - } - - @Override - public void setGroupVisible(int group, boolean visible) { - } - - @Override - public void setQwertyMode(boolean isQwerty) { - } - - @Override - public int size() { - return mItems.size(); - } - - @Override - public boolean hasActionItemBar() { - return (mActionItemBarPresenter != null); - } - - @Override - public void onShowAsActionChanged(GeckoMenuItem item, boolean isActionItem) { - removeItem(item.getItemId()); - - if (isActionItem) - addActionItem(item); - else - addItem(item); - } - - public void onItemChanged(GeckoMenuItem item) { - if (item.isActionItem()) { - final MenuItemActionBar actionView = (MenuItemActionBar) mActionItems.get(item); - if (actionView != null) { - // The update could be coming from the background thread. - // Post a runnable on the UI thread of the view for it to update. - final GeckoMenuItem menuItem = item; - actionView.post(new Runnable() { - @Override - public void run() { - if (menuItem.isVisible()) { - actionView.setVisibility(View.VISIBLE); - actionView.initialize(menuItem); - } else { - actionView.setVisibility(View.GONE); - } - } - }); - } - } else { - mAdapter.notifyDataSetChanged(); - } - } - - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - // We might be showing headers. Account them while using the position. - position -= getHeaderViewsCount(); - - GeckoMenuItem item = mAdapter.getItem(position); - handleMenuItemClick(item); - } - - private void handleMenuItemClick(GeckoMenuItem item) { - if (!item.isEnabled()) - return; - - if (item.invoke()) { - close(); - } else if (item.hasSubMenu()) { - // Refresh the submenu for the provider. - ActionProvider provider = item.getActionProvider(); - if (provider != null) { - GeckoSubMenu subMenu = new GeckoSubMenu(getContext()); - provider.onPrepareSubMenu(subMenu); - item.setSubMenu(subMenu); - } - - // Show the submenu. - GeckoSubMenu subMenu = (GeckoSubMenu) item.getSubMenu(); - showMenu(subMenu); - } else { - close(); - mCallback.onMenuItemSelected(item); - } - } - - public Callback getCallback() { - return mCallback; - } - - public MenuPresenter getMenuPresenter() { - return mMenuPresenter; - } - - public void setCallback(Callback callback) { - mCallback = callback; - - // Update the submenus just in case this changes on the fly. - for (GeckoMenuItem menuItem : mItems) { - if (menuItem.hasSubMenu()) { - GeckoSubMenu subMenu = (GeckoSubMenu) menuItem.getSubMenu(); - subMenu.setCallback(mCallback); - } - } - } - - public void setMenuPresenter(MenuPresenter presenter) { - mMenuPresenter = presenter; - - // Update the submenus just in case this changes on the fly. - for (GeckoMenuItem menuItem : mItems) { - if (menuItem.hasSubMenu()) { - GeckoSubMenu subMenu = (GeckoSubMenu) menuItem.getSubMenu(); - subMenu.setMenuPresenter(mMenuPresenter); - } - } - } - - public void setActionItemBarPresenter(ActionItemBarPresenter presenter) { - mActionItemBarPresenter = presenter; - } - - // Action Items are added to the header view by default. - // URL bar can register itself as a presenter, in case it has a different place to show them. - public static class DefaultActionItemBar extends LinearLayout - implements ActionItemBarPresenter { - public DefaultActionItemBar(Context context) { - super(context); - } - - public DefaultActionItemBar(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - public void addActionItem(View actionItem) { - LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(actionItem.getLayoutParams()); - params.weight = 1.0f; - actionItem.setLayoutParams(params); - addView(actionItem); - } - - @Override - public void removeActionItem(View actionItem) { - removeView(actionItem); - } - } - - // Adapter to bind menu items to the list. - private class MenuItemsAdapter extends BaseAdapter { - private static final int VIEW_TYPE_DEFAULT = 0; - private static final int VIEW_TYPE_ACTION_MODE = 1; - - private List<GeckoMenuItem> mItems; - - public MenuItemsAdapter() { - mItems = new ArrayList<GeckoMenuItem>(); - } - - @Override - public int getCount() { - if (mItems == null) - return 0; - - int visibleCount = 0; - for (GeckoMenuItem item : mItems) { - if (item.isVisible()) - visibleCount++; - } - - return visibleCount; - } - - @Override - public GeckoMenuItem getItem(int position) { - for (GeckoMenuItem item : mItems) { - if (item.isVisible()) { - position--; - - if (position < 0) - return item; - } - } - - return null; - } - - @Override - public long getItemId(int position) { - return position; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - GeckoMenuItem item = getItem(position); - GeckoMenuItem.Layout view = null; - - // Try to re-use the view. - if (convertView == null && getItemViewType(position) == VIEW_TYPE_DEFAULT) { - view = new MenuItemDefault(parent.getContext(), null); - } else { - view = (GeckoMenuItem.Layout) convertView; - } - - if (view == null || view instanceof MenuItemActionView) { - // Always get from the menu item. - // This will ensure that the default activity is refreshed. - view = (MenuItemActionView) item.getActionView(); - - // ListView will not perform an item click if the row has a focusable view in it. - // Hence, forward the click event on the menu item in the action-view to the ListView. - final View actionView = (View) view; - final int pos = position; - final long id = getItemId(position); - ((MenuItemActionView) view).setMenuItemClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - GeckoMenu listView = GeckoMenu.this; - listView.performItemClick(actionView, pos + listView.getHeaderViewsCount(), id); - } - }); - } - - // Initialize the view. - view.initialize(item); - return (View) view; - } - - @Override - public int getItemViewType(int position) { - return getItem(position).getActionProvider() == null ? VIEW_TYPE_DEFAULT : VIEW_TYPE_ACTION_MODE; - } - - @Override - public int getViewTypeCount() { - return 2; - } - - @Override - public boolean hasStableIds() { - return false; - } - - @Override - public boolean areAllItemsEnabled() { - for (GeckoMenuItem item : mItems) { - if (!item.isEnabled()) - return false; - } - - return true; - } - - @Override - public boolean isEnabled(int position) { - return getItem(position).isEnabled(); - } - - public void addMenuItem(GeckoMenuItem menuItem) { - if (mItems.contains(menuItem)) - return; - - // Insert it in proper order. - int index = 0; - for (GeckoMenuItem item : mItems) { - if (item.getOrder() > menuItem.getOrder()) { - mItems.add(index, menuItem); - notifyDataSetChanged(); - return; - } else { - index++; - } - } - - // Add the menuItem at the end. - mItems.add(menuItem); - notifyDataSetChanged(); - } - - public void removeMenuItem(GeckoMenuItem menuItem) { - // Remove it from the list. - mItems.remove(menuItem); - notifyDataSetChanged(); - } - - public void clear() { - mItems.clear(); - notifyDataSetChanged(); - } - - public GeckoMenuItem getMenuItem(int id) { - for (GeckoMenuItem item : mItems) { - if (item.getItemId() == id) - return item; - } - - return null; - } - } -} diff --git a/mobile/android/base/menu/GeckoMenuInflater.java b/mobile/android/base/menu/GeckoMenuInflater.java deleted file mode 100644 index 9c2b52d68..000000000 --- a/mobile/android/base/menu/GeckoMenuInflater.java +++ /dev/null @@ -1,158 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.menu; - -import org.mozilla.gecko.R; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import android.content.Context; -import android.content.res.TypedArray; -import android.content.res.XmlResourceParser; -import android.os.Build; -import android.util.AttributeSet; -import android.util.Xml; -import android.view.InflateException; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.SubMenu; - -import java.io.IOException; - -public class GeckoMenuInflater extends MenuInflater { - private static final String LOGTAG = "GeckoMenuInflater"; - - private static final String TAG_MENU = "menu"; - private static final String TAG_ITEM = "item"; - private static final int NO_ID = 0; - - private Context mContext; - - private boolean isSubMenu; - - // Private class to hold the parsed menu item. - private class ParsedItem { - public int id; - public int order; - public CharSequence title; - public int iconRes; - public boolean checkable; - public boolean checked; - public boolean visible; - public boolean enabled; - public int showAsAction; - } - - public GeckoMenuInflater(Context context) { - super(context); - mContext = context; - - isSubMenu = false; - } - - @Override - public void inflate(int menuRes, Menu menu) { - - // This does not check for a well-formed XML. - - XmlResourceParser parser = null; - try { - parser = mContext.getResources().getXml(menuRes); - AttributeSet attrs = Xml.asAttributeSet(parser); - - ParsedItem item = null; - SubMenu subMenu = null; - MenuItem menuItem = null; - - String tag; - int eventType = parser.getEventType(); - - do { - tag = parser.getName(); - - switch (eventType) { - case XmlPullParser.START_TAG: - if (tag.equals(TAG_ITEM)) { - // Parse the menu item. - item = new ParsedItem(); - parseItem(item, attrs); - } else if (tag.equals(TAG_MENU)) { - if (item != null) { - // Start parsing the sub menu. - isSubMenu = true; - subMenu = menu.addSubMenu(NO_ID, item.id, item.order, item.title); - menuItem = subMenu.getItem(); - - // Set the menu item in main menu. - setValues(item, menuItem); - } - } - break; - - case XmlPullParser.END_TAG: - if (parser.getName().equals(TAG_ITEM)) { - if (isSubMenu && subMenu == null) { - isSubMenu = false; - } else { - // Add the item. - if (subMenu == null) - menuItem = menu.add(NO_ID, item.id, item.order, item.title); - else - menuItem = subMenu.add(NO_ID, item.id, item.order, item.title); - - setValues(item, menuItem); - } - } else if (tag.equals(TAG_MENU)) { - // End of sub menu. - subMenu = null; - } - break; - } - - eventType = parser.next(); - - } while (eventType != XmlPullParser.END_DOCUMENT); - - } catch (XmlPullParserException e) { - throw new InflateException("Error inflating menu XML", e); - } catch (IOException e) { - throw new InflateException("Error inflating menu XML", e); - } finally { - if (parser != null) - parser.close(); - } - } - - public void parseItem(ParsedItem item, AttributeSet attrs) { - TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.MenuItem); - - item.id = a.getResourceId(R.styleable.MenuItem_android_id, NO_ID); - item.order = a.getInt(R.styleable.MenuItem_android_orderInCategory, 0); - item.title = a.getText(R.styleable.MenuItem_android_title); - item.iconRes = a.getResourceId(R.styleable.MenuItem_android_icon, 0); - item.checkable = a.getBoolean(R.styleable.MenuItem_android_checkable, false); - item.checked = a.getBoolean(R.styleable.MenuItem_android_checked, false); - item.visible = a.getBoolean(R.styleable.MenuItem_android_visible, true); - item.enabled = a.getBoolean(R.styleable.MenuItem_android_enabled, true); - - if (Build.VERSION.SDK_INT >= 11) - item.showAsAction = a.getInt(R.styleable.MenuItem_android_showAsAction, 0); - - a.recycle(); - } - - public void setValues(ParsedItem item, MenuItem menuItem) { - menuItem.setChecked(item.checked) - .setVisible(item.visible) - .setEnabled(item.enabled) - .setCheckable(item.checkable) - .setIcon(item.iconRes); - - if (Build.VERSION.SDK_INT >= 11) - menuItem.setShowAsAction(item.showAsAction); - } -} diff --git a/mobile/android/base/menu/GeckoMenuItem.java b/mobile/android/base/menu/GeckoMenuItem.java deleted file mode 100644 index 44d1e4b19..000000000 --- a/mobile/android/base/menu/GeckoMenuItem.java +++ /dev/null @@ -1,357 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.menu; - -import org.mozilla.gecko.widget.GeckoActionProvider; - -import android.content.Intent; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.view.ActionProvider; -import android.view.ContextMenu; -import android.view.MenuItem; -import android.view.SubMenu; -import android.view.View; - -public class GeckoMenuItem implements MenuItem { - private static final String LOGTAG = "GeckoMenuItem"; - - // A View that can show a MenuItem should be able to initialize from - // the properties of the MenuItem. - public static interface Layout { - public void initialize(GeckoMenuItem item); - } - - public static interface OnShowAsActionChangedListener { - public boolean hasActionItemBar(); - public void onShowAsActionChanged(GeckoMenuItem item, boolean isActionItem); - } - - private int mId; - private int mOrder; - private View mActionView; - private boolean mActionItem = false; - private CharSequence mTitle; - private CharSequence mTitleCondensed; - private boolean mCheckable = false; - private boolean mChecked = false; - private boolean mVisible = true; - private boolean mEnabled = true; - private Drawable mIcon; - private int mIconRes; - private ActionProvider mActionProvider; - private GeckoMenu mMenu; - private GeckoSubMenu mSubMenu; - private MenuItem.OnMenuItemClickListener mMenuItemClickListener = null; - private OnShowAsActionChangedListener mShowAsActionChangedListener; - - public GeckoMenuItem(GeckoMenu menu, int id, int order, int titleRes) { - mMenu = menu; - mId = id; - mOrder = order; - setTitle(titleRes); - } - - public GeckoMenuItem(GeckoMenu menu, int id, int order, CharSequence title) { - mMenu = menu; - mId = id; - mOrder = order; - setTitle(title); - } - - @Override - public boolean collapseActionView() { - return false; - } - - @Override - public boolean expandActionView() { - return false; - } - - public boolean hasActionProvider() { - if (Build.VERSION.SDK_INT < 14) { - return false; - } - - return (mActionProvider != null); - } - - @Override - public ActionProvider getActionProvider() { - return mActionProvider; - } - - @Override - public View getActionView() { - if (mActionProvider != null && mActionProvider instanceof GeckoActionProvider) { - return ((GeckoActionProvider) mActionProvider).getView(); - } - - return mActionView; - } - - @Override - public char getAlphabeticShortcut() { - return 0; - } - - @Override - public int getGroupId() { - return 0; - } - - @Override - public Drawable getIcon() { - if (mIcon == null) { - if (mIconRes != 0) - return mMenu.getResources().getDrawable(mIconRes); - else - return null; - } else { - return mIcon; - } - } - - @Override - public Intent getIntent() { - return null; - } - - @Override - public int getItemId() { - return mId; - } - - @Override - public ContextMenu.ContextMenuInfo getMenuInfo() { - return null; - } - - @Override - public char getNumericShortcut() { - return 0; - } - - @Override - public int getOrder() { - return mOrder; - } - - @Override - public SubMenu getSubMenu() { - return mSubMenu; - } - - @Override - public CharSequence getTitle() { - return mTitle; - } - - @Override - public CharSequence getTitleCondensed() { - return mTitleCondensed; - } - - @Override - public boolean hasSubMenu() { - if (mActionProvider != null) - return mActionProvider.hasSubMenu(); - - return (mSubMenu != null); - } - - public boolean isActionItem() { - return mActionItem; - } - - @Override - public boolean isActionViewExpanded() { - return false; - } - - @Override - public boolean isCheckable() { - return mCheckable; - } - - @Override - public boolean isChecked() { - return mChecked; - } - - @Override - public boolean isEnabled() { - return mEnabled; - } - - @Override - public boolean isVisible() { - return mVisible; - } - - @Override - public MenuItem setActionProvider(ActionProvider actionProvider) { - mActionProvider = actionProvider; - if (mActionProvider != null && mActionProvider instanceof GeckoActionProvider) { - GeckoActionProvider provider = (GeckoActionProvider) mActionProvider; - provider.setOnTargetSelectedListener(new GeckoActionProvider.OnTargetSelectedListener() { - @Override - public void onTargetSelected() { - mMenu.close(); - } - }); - } - - return this; - } - - @Override - public MenuItem setActionView(int resId) { - return this; - } - - @Override - public MenuItem setActionView(View view) { - return this; - } - - @Override - public MenuItem setAlphabeticShortcut(char alphaChar) { - return this; - } - - @Override - public MenuItem setCheckable(boolean checkable) { - mCheckable = checkable; - mMenu.onItemChanged(this); - return this; - } - - @Override - public MenuItem setChecked(boolean checked) { - mChecked = checked; - mMenu.onItemChanged(this); - return this; - } - - @Override - public MenuItem setEnabled(boolean enabled) { - mEnabled = enabled; - mMenu.onItemChanged(this); - return this; - } - - @Override - public MenuItem setIcon(Drawable icon) { - mIcon = icon; - mMenu.onItemChanged(this); - return this; - } - - @Override - public MenuItem setIcon(int iconRes) { - mIconRes = iconRes; - mMenu.onItemChanged(this); - return this; - } - - @Override - public MenuItem setIntent(Intent intent) { - return this; - } - - @Override - public MenuItem setNumericShortcut(char numericChar) { - return this; - } - - @Override - public MenuItem setOnActionExpandListener(MenuItem.OnActionExpandListener listener) { - return this; - } - - @Override - public MenuItem setOnMenuItemClickListener(MenuItem.OnMenuItemClickListener menuItemClickListener) { - mMenuItemClickListener = menuItemClickListener; - return this; - } - - @Override - public MenuItem setShortcut(char numericChar, char alphaChar) { - return this; - } - - @Override - public void setShowAsAction(int actionEnum) { - if (mShowAsActionChangedListener == null) - return; - - if (mActionItem == (actionEnum > 0)) - return; - - if (actionEnum > 0) { - if (!mShowAsActionChangedListener.hasActionItemBar()) - return; - - // Change the type to just an icon - MenuItemActionBar actionView = new MenuItemActionBar(mMenu.getContext(), null); - actionView.initialize(this); - mActionView = actionView; - - mActionItem = (actionEnum > 0); - } - - mShowAsActionChangedListener.onShowAsActionChanged(this, mActionItem); - } - - @Override - public MenuItem setShowAsActionFlags(int actionEnum) { - return this; - } - - public MenuItem setSubMenu(GeckoSubMenu subMenu) { - mSubMenu = subMenu; - return this; - } - - @Override - public MenuItem setTitle(CharSequence title) { - mTitle = title; - mMenu.onItemChanged(this); - return this; - } - - @Override - public MenuItem setTitle(int title) { - mTitle = mMenu.getResources().getString(title); - mMenu.onItemChanged(this); - return this; - } - - @Override - public MenuItem setTitleCondensed(CharSequence title) { - mTitleCondensed = title; - return this; - } - - @Override - public MenuItem setVisible(boolean visible) { - mVisible = visible; - mMenu.onItemChanged(this); - return this; - } - - public boolean invoke() { - if (mMenuItemClickListener != null) - return mMenuItemClickListener.onMenuItemClick(this); - else - return false; - } - - public void setOnShowAsActionChangedListener(OnShowAsActionChangedListener listener) { - mShowAsActionChangedListener = listener; - } -} diff --git a/mobile/android/base/menu/GeckoSubMenu.java b/mobile/android/base/menu/GeckoSubMenu.java deleted file mode 100644 index 554cdd177..000000000 --- a/mobile/android/base/menu/GeckoSubMenu.java +++ /dev/null @@ -1,81 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.menu; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; -import android.view.MenuItem; -import android.view.SubMenu; -import android.view.View; - -public class GeckoSubMenu extends GeckoMenu - implements SubMenu { - private static final String LOGTAG = "GeckoSubMenu"; - - // MenuItem associated with this submenu. - private MenuItem mMenuItem; - - public GeckoSubMenu(Context context) { - super(context); - } - - public GeckoSubMenu(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public GeckoSubMenu(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - public void clearHeader() { - } - - public SubMenu setMenuItem(MenuItem item) { - mMenuItem = item; - return this; - } - - @Override - public MenuItem getItem() { - return mMenuItem; - } - - @Override - public SubMenu setHeaderIcon(Drawable icon) { - return this; - } - - @Override - public SubMenu setHeaderIcon(int iconRes) { - return this; - } - - @Override - public SubMenu setHeaderTitle(CharSequence title) { - return this; - } - - @Override - public SubMenu setHeaderTitle(int titleRes) { - return this; - } - - @Override - public SubMenu setHeaderView(View view) { - return this; - } - - @Override - public SubMenu setIcon(Drawable icon) { - return this; - } - - @Override - public SubMenu setIcon(int iconRes) { - return this; - } -} diff --git a/mobile/android/base/mozglue/GeckoLoader.java.in b/mobile/android/base/mozglue/GeckoLoader.java.in deleted file mode 100644 index 575faea76..000000000 --- a/mobile/android/base/mozglue/GeckoLoader.java.in +++ /dev/null @@ -1,284 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.mozglue; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.os.Build; -import android.os.Environment; -import android.util.Log; - -import java.io.File; -import java.text.DecimalFormat; -import java.text.DecimalFormatSymbols; -import java.text.NumberFormat; -import java.util.Locale; - -public final class GeckoLoader { - private static final String LOGTAG = "GeckoLoader"; - - private static File sCacheFile; - private static File sGREDir; - - private static final Object sLibLoadingLock = new Object(); - // Must hold sLibLoadingLock while accessing the following boolean variables. - private static boolean sSQLiteLibsLoaded; - private static boolean sNSSLibsLoaded; - private static boolean sMozGlueLoaded; - private static boolean sLibsSetup; - - private GeckoLoader() { - // prevent instantiation - } - - public static File getCacheDir(Context context) { - if (sCacheFile == null) { - sCacheFile = context.getCacheDir(); - } - return sCacheFile; - } - - public static File getGREDir(Context context) { - if (sGREDir == null) { - sGREDir = new File(context.getApplicationInfo().dataDir); - } - return sGREDir; - } - - private static void setupPluginEnvironment(Context context, String[] pluginDirs) { - // setup plugin path directories - try { - // Check to see if plugins were blocked. - if (pluginDirs == null) { - putenv("MOZ_PLUGINS_BLOCKED=1"); - putenv("MOZ_PLUGIN_PATH="); - return; - } - - StringBuffer pluginSearchPath = new StringBuffer(); - for (int i = 0; i < pluginDirs.length; i++) { - pluginSearchPath.append(pluginDirs[i]); - pluginSearchPath.append(":"); - } - putenv("MOZ_PLUGIN_PATH="+pluginSearchPath); - - File pluginDataDir = context.getDir("plugins", 0); - putenv("ANDROID_PLUGIN_DATADIR=" + pluginDataDir.getPath()); - - File pluginPrivateDataDir = context.getDir("plugins_private", 0); - putenv("ANDROID_PLUGIN_DATADIR_PRIVATE=" + pluginPrivateDataDir.getPath()); - - } catch (Exception ex) { - Log.w(LOGTAG, "Caught exception getting plugin dirs.", ex); - } - } - - private static void setupDownloadEnvironment(Context context) { - try { - File downloadDir = null; - File updatesDir = null; - if (Build.VERSION.SDK_INT >= 8) { - downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); - updatesDir = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS); - } - if (downloadDir == null) { - downloadDir = new File(Environment.getExternalStorageDirectory().getPath(), "download"); - } - if (updatesDir == null) { - updatesDir = downloadDir; - } - putenv("DOWNLOADS_DIRECTORY=" + downloadDir.getPath()); - putenv("UPDATES_DIRECTORY=" + updatesDir.getPath()); - } - catch (Exception e) { - Log.w(LOGTAG, "No download directory found.", e); - } - } - - private static void delTree(File file) { - if (file.isDirectory()) { - File children[] = file.listFiles(); - for (File child : children) { - delTree(child); - } - } - file.delete(); - } - - private static File getTmpDir(Context context) { - File tmpDir = context.getDir("tmpdir", Context.MODE_PRIVATE); - // check if the old tmp dir is there - File oldDir = new File(tmpDir.getParentFile(), "app_tmp"); - if (oldDir.exists()) { - delTree(oldDir); - } - return tmpDir; - } - - public static void setupGeckoEnvironment(Activity context, String[] pluginDirs, String profilePath) { - // if we have an intent (we're being launched by an activity) - // read in any environmental variables from it here - Intent intent = context.getIntent(); - if (intent != null) { - String env = intent.getStringExtra("env0"); - Log.d(LOGTAG, "Gecko environment env0: " + env); - for (int c = 1; env != null; c++) { - putenv(env); - env = intent.getStringExtra("env" + c); - Log.d(LOGTAG, "env" + c + ": " + env); - } - } - - setupPluginEnvironment(context, pluginDirs); - setupDownloadEnvironment(context); - - // profile home path - putenv("HOME=" + profilePath); - - // setup the tmp path - File f = getTmpDir(context); - if (!f.exists()) { - f.mkdirs(); - } - putenv("TMPDIR=" + f.getPath()); - - // setup the downloads path - f = Environment.getDownloadCacheDirectory(); - putenv("EXTERNAL_STORAGE=" + f.getPath()); - - // setup the app-specific cache path - f = context.getCacheDir(); - putenv("CACHE_DIRECTORY=" + f.getPath()); - - /* We really want to use this code, but it requires bumping up the SDK to 17 so for now - we will use reflection. See https://bugzilla.mozilla.org/show_bug.cgi?id=811763#c11 - - if (Build.VERSION.SDK_INT >= 17) { - android.os.UserManager um = (android.os.UserManager)context.getSystemService(Context.USER_SERVICE); - if (um != null) { - putenv("MOZ_ANDROID_USER_SERIAL_NUMBER=" + um.getSerialNumberForUser(android.os.Process.myUserHandle())); - } else { - Log.d(LOGTAG, "Unable to obtain user manager service on a device with SDK version " + Build.VERSION.SDK_INT); - } - } - */ - try { - Object userManager = context.getSystemService("user"); - if (userManager != null) { - // if userManager is non-null that means we're running on 4.2+ and so the rest of this - // should just work - Object userHandle = android.os.Process.class.getMethod("myUserHandle", (Class[])null).invoke(null); - Object userSerial = userManager.getClass().getMethod("getSerialNumberForUser", userHandle.getClass()).invoke(userManager, userHandle); - putenv("MOZ_ANDROID_USER_SERIAL_NUMBER=" + userSerial.toString()); - } - } catch (Exception e) { - // Guard against any unexpected failures - Log.d(LOGTAG, "Unable to set the user serial number", e); - } - - setupLocaleEnvironment(); - } - - private static void loadLibsSetup(Context context) { - synchronized (sLibLoadingLock) { - if (sLibsSetup) { - return; - } - sLibsSetup = true; - } - - // The package data lib directory isn't placed in ld.so's - // search path, so we have to manually load libraries that - // libxul will depend on. Not ideal. - - File cacheFile = getCacheDir(context); - putenv("GRE_HOME=" + getGREDir(context).getPath()); - - // setup the libs cache - String linkerCache = System.getenv("MOZ_LINKER_CACHE"); - if (linkerCache == null) { - linkerCache = cacheFile.getPath(); - putenv("MOZ_LINKER_CACHE=" + linkerCache); - } - -#ifdef MOZ_LINKER_EXTRACT - putenv("MOZ_LINKER_EXTRACT=1"); - // Ensure that the cache dir is world-writable - File cacheDir = new File(linkerCache); - if (cacheDir.isDirectory()) { - cacheDir.setWritable(true, false); - cacheDir.setExecutable(true, false); - cacheDir.setReadable(true, false); - } -#endif - } - - public static void loadSQLiteLibs(Context context, String apkName) { - synchronized (sLibLoadingLock) { - if (sSQLiteLibsLoaded) { - return; - } - sSQLiteLibsLoaded = true; - } - - loadMozGlue(context); - // the extract libs parameter is being removed in bug 732069 - loadLibsSetup(context); - loadSQLiteLibsNative(apkName, false); - } - - public static void loadNSSLibs(Context context, String apkName) { - synchronized (sLibLoadingLock) { - if (sNSSLibsLoaded) { - return; - } - sNSSLibsLoaded = true; - } - - loadMozGlue(context); - loadLibsSetup(context); - loadNSSLibsNative(apkName, false); - } - - public static void loadMozGlue(Context context) { - synchronized (sLibLoadingLock) { - if (sMozGlueLoaded) { - return; - } - sMozGlueLoaded = true; - } - - System.loadLibrary("mozglue"); - } - - public static void loadGeckoLibs(Context context, String apkName) { - loadLibsSetup(context); - loadGeckoLibsNative(apkName); - } - - private static void setupLocaleEnvironment() { - putenv("LANG=" + Locale.getDefault().toString()); - NumberFormat nf = NumberFormat.getInstance(); - if (nf instanceof DecimalFormat) { - DecimalFormat df = (DecimalFormat)nf; - DecimalFormatSymbols dfs = df.getDecimalFormatSymbols(); - - putenv("LOCALE_DECIMAL_POINT=" + dfs.getDecimalSeparator()); - putenv("LOCALE_THOUSANDS_SEP=" + dfs.getGroupingSeparator()); - putenv("LOCALE_GROUPING=" + (char)df.getGroupingSize()); - } - } - - // These methods are implemented in mozglue/android/nsGeckoUtils.cpp - private static native void putenv(String map); - - // These methods are implemented in mozglue/android/APKOpen.cpp - public static native void nativeRun(String args); - private static native void loadGeckoLibsNative(String apkName); - private static native void loadSQLiteLibsNative(String apkName, boolean shouldExtract); - private static native void loadNSSLibsNative(String apkName, boolean shouldExtract); -} diff --git a/mobile/android/base/resources/layout/gecko_app.xml b/mobile/android/base/resources/layout/gecko_app.xml deleted file mode 100644 index f9c35aac5..000000000 --- a/mobile/android/base/resources/layout/gecko_app.xml +++ /dev/null @@ -1,64 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="fill_parent"> - - <org.mozilla.gecko.TabsPanel android:id="@+id/tabs_panel" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - android:background="@color/background_tabs" - android:visibility="invisible"/> - - <view class="org.mozilla.gecko.GeckoApp$MainLayout" - android:id="@+id/main_layout" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - android:background="@android:color/transparent"> - - <RelativeLayout android:id="@+id/gecko_layout" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - android:layout_above="@+id/find_in_page"> - - <include layout="@layout/shared_ui_components"/> - - </RelativeLayout> - - <org.mozilla.gecko.FindInPageBar android:id="@+id/find_in_page" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_alignParentBottom="true" - style="@style/FindBar" - android:visibility="gone"/> - - <RelativeLayout android:id="@+id/camera_layout" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:layout_alignParentRight="true" - android:layout_alignParentBottom="true"> - </RelativeLayout> - - <include layout="@layout/browser_toolbar" - android:layout_width="fill_parent" - android:layout_height="@dimen/browser_toolbar_height"/> - </view> - - <LinearLayout android:id="@+id/toast" - style="@style/Toast"> - - <TextView android:id="@+id/toast_message" - style="@style/ToastMessage" /> - - <ImageView android:id="@+id/toast_divider" - style="@style/ToastDivider" /> - - <Button android:id="@+id/toast_button" - style="@style/ToastButton" /> - - </LinearLayout> - -</RelativeLayout> diff --git a/mobile/android/base/util/GeckoBackgroundThread.java b/mobile/android/base/util/GeckoBackgroundThread.java deleted file mode 100644 index f7873fe73..000000000 --- a/mobile/android/base/util/GeckoBackgroundThread.java +++ /dev/null @@ -1,55 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.util; - -import android.os.Handler; -import android.os.Looper; - -import java.util.concurrent.SynchronousQueue; - -final class GeckoBackgroundThread extends Thread { - private static final String LOOPER_NAME = "GeckoBackgroundThread"; - - // Guarded by 'this'. - private static Handler sHandler = null; - private SynchronousQueue<Handler> mHandlerQueue = new SynchronousQueue<Handler>(); - - // Singleton, so private constructor. - private GeckoBackgroundThread() { - super(); - } - - @Override - public void run() { - setName(LOOPER_NAME); - Looper.prepare(); - try { - mHandlerQueue.put(new Handler()); - } catch (InterruptedException ie) {} - - Looper.loop(); - } - - // Get a Handler for a looper thread, or create one if it doesn't yet exist. - /*package*/ static synchronized Handler getHandler() { - if (sHandler == null) { - GeckoBackgroundThread lt = new GeckoBackgroundThread(); - ThreadUtils.setBackgroundThread(lt); - lt.start(); - try { - sHandler = lt.mHandlerQueue.take(); - } catch (InterruptedException ie) {} - } - return sHandler; - } - - /*package*/ static void post(Runnable runnable) { - Handler handler = getHandler(); - if (handler == null) { - throw new IllegalStateException("No handler! Must have been interrupted. Not posting."); - } - handler.post(runnable); - } -} diff --git a/mobile/android/base/util/GeckoEventListener.java b/mobile/android/base/util/GeckoEventListener.java deleted file mode 100644 index ba37f0541..000000000 --- a/mobile/android/base/util/GeckoEventListener.java +++ /dev/null @@ -1,15 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.util; - -import org.json.JSONObject; - -/* This class is referenced by Robocop via reflection; use care when - * modifying the signature. - */ -public interface GeckoEventListener { - void handleMessage(String event, JSONObject message); -} diff --git a/mobile/android/base/util/GeckoEventResponder.java b/mobile/android/base/util/GeckoEventResponder.java deleted file mode 100644 index dc4561561..000000000 --- a/mobile/android/base/util/GeckoEventResponder.java +++ /dev/null @@ -1,16 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * ***** BEGIN LICENSE BLOCK ***** - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. - * - * ***** END LICENSE BLOCK ***** */ - -package org.mozilla.gecko.util; - -import org.json.JSONObject; - -public interface GeckoEventResponder extends GeckoEventListener { - String getResponse(JSONObject response); -} diff --git a/mobile/android/base/util/GeckoJarReader.java b/mobile/android/base/util/GeckoJarReader.java deleted file mode 100644 index 448af8bba..000000000 --- a/mobile/android/base/util/GeckoJarReader.java +++ /dev/null @@ -1,171 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.util; - -import org.mozilla.gecko.mozglue.NativeZip; - -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.util.Log; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URL; -import java.util.Stack; - -/* Reads out of a multiple level deep jar file such as - * jar:jar:file:///data/app/org.mozilla.fennec.apk!/omni.ja!/chrome/chrome/content/branding/favicon32.png - */ -public final class GeckoJarReader { - private static final String LOGTAG = "GeckoJarReader"; - - private GeckoJarReader() {} - - public static Bitmap getBitmap(Resources resources, String url) { - BitmapDrawable drawable = getBitmapDrawable(resources, url); - return (drawable != null) ? drawable.getBitmap() : null; - } - - public static BitmapDrawable getBitmapDrawable(Resources resources, String url) { - Stack<String> jarUrls = parseUrl(url); - InputStream inputStream = null; - BitmapDrawable bitmap = null; - - NativeZip zip = null; - try { - // Load the initial jar file as a zip - zip = getZipFile(jarUrls.pop()); - inputStream = getStream(zip, jarUrls, url); - if (inputStream != null) { - bitmap = new BitmapDrawable(resources, inputStream); - } - } catch (IOException ex) { - Log.e(LOGTAG, "Exception ", ex); - } finally { - if (inputStream != null) { - try { - inputStream.close(); - } catch(IOException ex) { - Log.e(LOGTAG, "Error closing stream", ex); - } - } - if (zip != null) { - zip.close(); - } - } - - return bitmap; - } - - public static String getText(String url) { - Stack<String> jarUrls = parseUrl(url); - - NativeZip zip = null; - BufferedReader reader = null; - String text = null; - try { - zip = getZipFile(jarUrls.pop()); - InputStream input = getStream(zip, jarUrls, url); - if (input != null) { - reader = new BufferedReader(new InputStreamReader(input)); - text = reader.readLine(); - } - } catch (IOException ex) { - Log.e(LOGTAG, "Exception ", ex); - } finally { - if (reader != null) { - try { - reader.close(); - } catch(IOException ex) { - Log.e(LOGTAG, "Error closing reader", ex); - } - } - if (zip != null) { - zip.close(); - } - } - - return text; - } - - private static NativeZip getZipFile(String url) throws IOException { - URL fileUrl = new URL(url); - return new NativeZip(fileUrl.getPath()); - } - - // Public for testing only. - public static InputStream getStream(String url) { - Stack<String> jarUrls = parseUrl(url); - try { - NativeZip zip = getZipFile(jarUrls.pop()); - return getStream(zip, jarUrls, url); - } catch (Exception ex) { - // Some JNI code throws IllegalArgumentException on a bad file name; - // swallow the error and return null. We could also see legitimate - // IOExceptions here. - return null; - } - } - - private static InputStream getStream(NativeZip zip, Stack<String> jarUrls, String origUrl) { - InputStream inputStream = null; - - // loop through children jar files until we reach the innermost one - while (!jarUrls.empty()) { - String fileName = jarUrls.pop(); - - if (inputStream != null) { - // intermediate NativeZips and InputStreams will be garbage collected. - try { - zip = new NativeZip(inputStream); - } catch (IllegalArgumentException e) { - String description = "!!! BUG 849589 !!! origUrl=" + origUrl; - Log.e(LOGTAG, description, e); - throw new IllegalArgumentException(description); - } - } - - inputStream = zip.getInputStream(fileName); - if (inputStream == null) { - Log.d(LOGTAG, "No Entry for " + fileName); - return null; - } - } - - return inputStream; - } - - /* Returns a stack of strings breaking the url up into pieces. Each piece - * is assumed to point to a jar file except for the final one. Callers should - * pass in the url to parse, and null for the parent parameter (used for recursion) - * For example, jar:jar:file:///data/app/org.mozilla.fennec.apk!/omni.ja!/chrome/chrome/content/branding/favicon32.png - * will return: - * file:///data/app/org.mozilla.fennec.apk - * omni.ja - * chrome/chrome/content/branding/favicon32.png - */ - private static Stack<String> parseUrl(String url) { - return parseUrl(url, null); - } - - private static Stack<String> parseUrl(String url, Stack<String> results) { - if (results == null) { - results = new Stack<String>(); - } - - if (url.startsWith("jar:")) { - int jarEnd = url.lastIndexOf("!"); - String subStr = url.substring(4, jarEnd); - results.push(url.substring(jarEnd+2)); // remove the !/ characters - return parseUrl(subStr, results); - } else { - results.push(url); - return results; - } - } -} diff --git a/mobile/android/base/widget/GeckoActionProvider.java b/mobile/android/base/widget/GeckoActionProvider.java deleted file mode 100644 index 750613d8b..000000000 --- a/mobile/android/base/widget/GeckoActionProvider.java +++ /dev/null @@ -1,140 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.widget; - -import org.mozilla.gecko.menu.MenuItemActionView; - -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.graphics.drawable.Drawable; -import android.view.ActionProvider; -import android.view.MenuItem; -import android.view.MenuItem.OnMenuItemClickListener; -import android.view.SubMenu; -import android.view.View; -import android.view.View.OnClickListener; - -public class GeckoActionProvider extends ActionProvider { - - /** - * A listener to know when a target was selected. - * When setting a provider, the activity can listen to this, - * to close the menu. - */ - public interface OnTargetSelectedListener { - public void onTargetSelected(); - } - - private final Context mContext; - - public static final String DEFAULT_HISTORY_FILE_NAME = "history.xml"; - - // History file. - private String mHistoryFileName = DEFAULT_HISTORY_FILE_NAME; - - private OnTargetSelectedListener mOnTargetListener; - - private final Callbacks mCallbacks = new Callbacks(); - - public GeckoActionProvider(Context context) { - super(context); - mContext = context; - } - - @Override - public View onCreateActionView() { - // Create the view and set its data model. - ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mHistoryFileName); - MenuItemActionView view = new MenuItemActionView(mContext, null); - view.setActionButtonClickListener(mCallbacks); - - if (dataModel.getHistorySize() > 0) { - PackageManager packageManager = mContext.getPackageManager(); - ResolveInfo defaultActivity = dataModel.getDefaultActivity(); - view.setActionButton(defaultActivity == null ? null : defaultActivity.loadIcon(packageManager)); - } - - return view; - } - - public View getView() { - return onCreateActionView(); - } - - @Override - public boolean hasSubMenu() { - return true; - } - - @Override - public void onPrepareSubMenu(SubMenu subMenu) { - // Clear since the order of items may change. - subMenu.clear(); - - ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mHistoryFileName); - PackageManager packageManager = mContext.getPackageManager(); - - // Populate the sub-menu with a sub set of the activities. - final int count = dataModel.getActivityCount(); - for (int i = 0; i < count; i++) { - ResolveInfo activity = dataModel.getActivity(i); - subMenu.add(0, i, i, activity.loadLabel(packageManager)) - .setIcon(activity.loadIcon(packageManager)) - .setOnMenuItemClickListener(mCallbacks); - } - } - - public void setHistoryFileName(String historyFile) { - mHistoryFileName = historyFile; - } - - public Intent getIntent() { - ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mHistoryFileName); - return dataModel.getIntent(); - } - - public void setIntent(Intent intent) { - ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mHistoryFileName); - dataModel.setIntent(intent); - } - - public void setOnTargetSelectedListener(OnTargetSelectedListener listener) { - mOnTargetListener = listener; - } - - /** - * Listener for handling default activity / menu item clicks. - */ - private class Callbacks implements OnMenuItemClickListener, - OnClickListener { - private void chooseActivity(int index) { - if (mOnTargetListener != null) - mOnTargetListener.onTargetSelected(); - - ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mHistoryFileName); - Intent launchIntent = dataModel.chooseActivity(index); - if (launchIntent != null) { - launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); - mContext.startActivity(launchIntent); - } - } - - @Override - public boolean onMenuItemClick(MenuItem item) { - chooseActivity(item.getItemId()); - return true; - } - - @Override - public void onClick(View view) { - ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mHistoryFileName); - chooseActivity(dataModel.getActivityIndex(dataModel.getDefaultActivity())); - } - } -} - diff --git a/mobile/android/base/widget/GeckoPopupMenu.java b/mobile/android/base/widget/GeckoPopupMenu.java deleted file mode 100644 index 9814ec455..000000000 --- a/mobile/android/base/widget/GeckoPopupMenu.java +++ /dev/null @@ -1,172 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.widget; - -import org.mozilla.gecko.menu.GeckoMenu; -import org.mozilla.gecko.menu.GeckoMenuInflater; -import org.mozilla.gecko.menu.MenuPanel; -import org.mozilla.gecko.menu.MenuPopup; - -import android.content.Context; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; - -/** - * A PopupMenu that uses the custom GeckoMenu. This menu is - * usually tied to an anchor, and show as a dropdrown from the anchor. - */ -public class GeckoPopupMenu implements GeckoMenu.Callback, - GeckoMenu.MenuPresenter { - - // An interface for listeners for dismissal. - public static interface OnDismissListener { - public boolean onDismiss(GeckoMenu menu); - } - - // An interface for listeners for menu item click events. - public static interface OnMenuItemClickListener { - public boolean onMenuItemClick(MenuItem item); - } - - private View mAnchor; - - private MenuPopup mMenuPopup; - private MenuPanel mMenuPanel; - - private GeckoMenu mMenu; - private GeckoMenuInflater mMenuInflater; - - private OnDismissListener mDismissListener; - private OnMenuItemClickListener mClickListener; - - public GeckoPopupMenu(Context context) { - initialize(context, null); - } - - public GeckoPopupMenu(Context context, View anchor) { - initialize(context, anchor); - } - - /** - * This method creates an empty menu and attaches the necessary listeners. - * If an anchor is supplied, it is stored as well. - */ - private void initialize(Context context, View anchor) { - mMenu = new GeckoMenu(context, null); - mMenu.setCallback(this); - mMenu.setMenuPresenter(this); - mMenuInflater = new GeckoMenuInflater(context); - - mMenuPopup = new MenuPopup(context); - mMenuPanel = new MenuPanel(context, null); - - setAnchor(anchor); - } - - /** - * Returns the menu that is current being shown. - * - * @return The menu being shown. - */ - public Menu getMenu() { - return mMenu; - } - - /** - * Returns the menu inflater that was used to create the menu. - * - * @return The menu inflater used. - */ - public MenuInflater getMenuInflater() { - return mMenuInflater; - } - - /** - * Inflates a menu resource to the menu using the menu inflater. - * - * @param menuRes The menu resource to be inflated. - */ - public void inflate(int menuRes) { - mMenuInflater.inflate(menuRes, mMenu); - - mMenuPanel.addView(mMenu); - mMenuPopup.setPanelView(mMenuPanel); - } - - /** - * Set a different anchor after the menu is inflated. - * - * @param anchor The new anchor for the popup. - */ - public void setAnchor(View anchor) { - mAnchor = anchor; - } - - public void setOnDismissListener(OnDismissListener listener) { - mDismissListener = listener; - } - - public void setOnMenuItemClickListener(OnMenuItemClickListener listener) { - mClickListener = listener; - } - - /** - * Show the inflated menu. - */ - public void show() { - if (!mMenuPopup.isShowing()) - mMenuPopup.showAsDropDown(mAnchor); - } - - /** - * Hide the inflated menu. - */ - public void dismiss() { - if (mMenuPopup.isShowing()) { - mMenuPopup.dismiss(); - - if (mDismissListener != null) - mDismissListener.onDismiss(mMenu); - } - } - - /** - * Show/hide the arrow pointing to the anchor. - * - * @param show Show/hide the arrow. - */ - public void showArrowToAnchor(boolean show) { - mMenuPopup.showArrowToAnchor(show); - } - - @Override - public boolean onMenuItemSelected(MenuItem item) { - if (mClickListener != null) - return mClickListener.onMenuItemClick(item); - - return false; - } - - @Override - public void openMenu() { - show(); - } - - @Override - public void showMenu(View menu) { - mMenuPanel.removeAllViews(); - mMenuPanel.addView(menu); - - openMenu(); - } - - @Override - public void closeMenu() { - dismiss(); - } -} diff --git a/mozglue/android/nsGeckoUtils.cpp b/mozglue/android/nsGeckoUtils.cpp deleted file mode 100644 index 0a5e327a7..000000000 --- a/mozglue/android/nsGeckoUtils.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include <jni.h> - -#include <stdlib.h> -#include <fcntl.h> -#include "APKOpen.h" -#include "Zip.h" -#include "mozilla/RefPtr.h" - -extern "C" -__attribute__ ((visibility("default"))) -void JNICALL -Java_org_mozilla_gecko_mozglue_GeckoLoader_putenv(JNIEnv *jenv, jclass, jstring map) -{ - const char* str; - // XXX: java doesn't give us true UTF8, we should figure out something - // better to do here - str = jenv->GetStringUTFChars(map, NULL); - if (str == NULL) - return; - putenv(strdup(str)); - jenv->ReleaseStringUTFChars(map, str); -} - -extern "C" -__attribute__ ((visibility("default"))) -jobject JNICALL -Java_org_mozilla_gecko_mozglue_DirectBufferAllocator_nativeAllocateDirectBuffer(JNIEnv *jenv, jclass, jlong size) -{ - jobject buffer = NULL; - void* mem = malloc(size); - if (mem) { - buffer = jenv->NewDirectByteBuffer(mem, size); - if (!buffer) - free(mem); - } - return buffer; -} - -extern "C" -__attribute__ ((visibility("default"))) -void JNICALL -Java_org_mozilla_gecko_mozglue_DirectBufferAllocator_nativeFreeDirectBuffer(JNIEnv *jenv, jclass, jobject buf) -{ - free(jenv->GetDirectBufferAddress(buf)); -} - -extern "C" -__attribute__ ((visibility("default"))) -jlong JNICALL -Java_org_mozilla_gecko_mozglue_NativeZip_getZip(JNIEnv *jenv, jclass, jstring path) -{ - const char* str; - str = jenv->GetStringUTFChars(path, NULL); - if (!str || !*str) { - if (str) - jenv->ReleaseStringUTFChars(path, str); - JNI_Throw(jenv, "java/lang/IllegalArgumentException", "Invalid path"); - return 0; - } - mozilla::RefPtr<Zip> zip = ZipCollection::GetZip(str); - jenv->ReleaseStringUTFChars(path, str); - if (!zip) { - JNI_Throw(jenv, "java/lang/IllegalArgumentException", "Invalid path or invalid zip"); - return 0; - } - zip->AddRef(); - return (jlong) zip.get(); -} - -extern "C" -__attribute__ ((visibility("default"))) -jlong JNICALL -Java_org_mozilla_gecko_mozglue_NativeZip_getZipFromByteBuffer(JNIEnv *jenv, jclass, jobject buffer) -{ - void *buf = jenv->GetDirectBufferAddress(buffer); - size_t size = jenv->GetDirectBufferCapacity(buffer); - mozilla::RefPtr<Zip> zip = Zip::Create(buf, size); - if (!zip) { - JNI_Throw(jenv, "java/lang/IllegalArgumentException", "Invalid zip"); - return 0; - } - zip->AddRef(); - return (jlong) zip.get(); -} - - extern "C" -__attribute__ ((visibility("default"))) -void JNICALL -Java_org_mozilla_gecko_mozglue_NativeZip__1release(JNIEnv *jenv, jclass, jlong obj) -{ - Zip *zip = (Zip *)obj; - zip->Release(); -} - -extern "C" -__attribute__ ((visibility("default"))) -jobject JNICALL -Java_org_mozilla_gecko_mozglue_NativeZip__1getInputStream(JNIEnv *jenv, jobject jzip, jlong obj, jstring path) -{ - Zip *zip = (Zip *)obj; - const char* str; - str = jenv->GetStringUTFChars(path, NULL); - - Zip::Stream stream; - bool res = zip->GetStream(str, &stream); - jenv->ReleaseStringUTFChars(path, str); - if (!res) { - return NULL; - } - jobject buf = jenv->NewDirectByteBuffer(const_cast<void *>(stream.GetBuffer()), stream.GetSize()); - if (!buf) { - JNI_Throw(jenv, "java/lang/RuntimeException", "Failed to create ByteBuffer"); - return NULL; - } - jclass nativeZip = jenv->GetObjectClass(jzip); - jmethodID method = jenv->GetMethodID(nativeZip, "createInputStream", "(Ljava/nio/ByteBuffer;I)Ljava/io/InputStream;"); - // Since this function is only expected to be called from Java, it is safe - // to skip exception checking for the method call below, as long as no - // other Native -> Java call doesn't happen before returning to Java. - return jenv->CallObjectMethod(jzip, method, buf, (jint) stream.GetType()); -} diff --git a/tools/profiler/GeckoProfiler.h b/tools/profiler/GeckoProfiler.h deleted file mode 100644 index d5d28ad2c..000000000 --- a/tools/profiler/GeckoProfiler.h +++ /dev/null @@ -1,166 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* *************** SPS Sampler Information **************** - * - * SPS is an always on profiler that takes fast and low overheads samples - * of the program execution using only userspace functionity for portability. - * The goal of this module is to provide performance data in a generic - * cross platform way without requiring custom tools or kernel support. - * - * Non goals: Support features that are platform specific or replace - * platform specific profilers. - * - * Samples are collected to form a timeline with optional timeline event (markers) - * used for filtering. - * - * SPS collects samples in a platform independant way by using a speudo stack abstraction - * of the real program stack by using 'sample stack frames'. When a sample is collected - * all active sample stack frames and the program counter are recorded. - */ - -/* *************** SPS Sampler File Format **************** - * - * Simple new line seperated tag format: - * S -> BOF tags EOF - * tags -> tag tags - * tag -> CHAR - STRING - * - * Tags: - * 's' - Sample tag followed by the first stack frame followed by 0 or more 'c' tags. - * 'c' - Continue Sample tag gives remaining tag element. If a 'c' tag is seen without - * a preceding 's' tag it should be ignored. This is to support the behavior - * of circular buffers. - * If the 'stackwalk' feature is enabled this tag will have the format - * 'l-<library name>@<hex address>' and will expect an external tool to translate - * the tag into something readable through a symbolication processing step. - * 'm' - Timeline marker. Zero or more may appear before a 's' tag. - * 'l' - Information about the program counter library and address. Post processing - * can include function and source line. If built with leaf data enabled - * this tag will describe the last 'c' tag. - * 'r' - Responsiveness tag following an 's' tag. Gives an indication on how well the - * application is responding to the event loop. Lower is better. - * 't' - Elapse time since recording started. - * - */ - -#ifndef SAMPLER_H -#define SAMPLER_H - -#include "jsfriendapi.h" -#include "mozilla/NullPtr.h" -#include "mozilla/TimeStamp.h" - -#ifndef MOZ_ENABLE_PROFILER_SPS - -// Insert a RAII in this scope to active a pseudo label. Any samples collected -// in this scope will contain this annotation. For dynamic strings use -// PROFILER_LABEL_PRINTF. Arguments must be string literals. -#define PROFILER_LABEL(name_space, info) do {} while (0) - -// Format a dynamic string as a pseudo label. These labels will a considerable -// storage size in the circular buffer compared to regular labels. This function -// can be used to annotate custom information such as URL for the resource being -// decoded or the size of the paint. -#define PROFILER_LABEL_PRINTF(name_space, info, format, ...) do {} while (0) - -// Insert a marker in the profile timeline. This is useful to delimit something -// important happening such as the first paint. Unlike profiler_label that are -// only recorded if a sample is collected while it is active, marker will always -// be collected. -#define PROFILER_MARKER(info) do {} while (0) - -// Main thread specilization to avoid TLS lookup for performance critical use. -#define PROFILER_MAIN_THREAD_LABEL(name_space, info) do {} while (0) -#define PROFILER_MAIN_THREAD_LABEL_PRINTF(name_space, info, format, ...) do {} while (0) - -// Initilize the profiler TLS, signal handlers on linux. If MOZ_PROFILER_STARTUP -// is set the profiler will be started. This call must happen before any other -// sampler calls. Particularly sampler_label/sampler_marker. -static inline void profiler_init(void* stackTop) {}; - -// Clean up the profiler module, stopping it if required. This function may -// also save a shutdown profile if requested. No profiler calls should happen -// after this point and all pseudo labels should have been popped. -static inline void profiler_shutdown() {}; - -// Start the profiler with the selected options. The samples will be -// recorded in a circular buffer. -// "aProfileEntries" is an abstract size indication of how big -// the profile's circular buffer should be. Multiply by 4 -// words to get the cost. -// "aInterval" the sampling interval. The profiler will do its -// best to sample at this interval. The profiler visualization -// should represent the actual sampling accuracy. -static inline void profiler_start(int aProfileEntries, int aInterval, - const char** aFeatures, uint32_t aFeatureCount, - const char** aThreadNameFilters, uint32_t aFilterCount) {} - -// Stop the profiler and discard the profile. Call 'profiler_save' before this -// to retrieve the profile. -static inline void profiler_stop() {} - -static inline bool profiler_is_active() { return false; } - -// Internal-only. Used by the event tracer. -static inline void profiler_responsiveness(const mozilla::TimeStamp& aTime) {} - -// Internal-only. Used by the event tracer. -static inline double* profiler_get_responsiveness() { return nullptr; } - -// Internal-only. -static inline void profiler_set_frame_number(int frameNumber) {} - -// Get the profile encoded as a JSON string. -static inline char* profiler_get_profile() { return nullptr; } - -// Get the profile encoded as a JSON object. -static inline JSObject* profiler_get_profile_jsobject(JSContext* aCx) { return nullptr; } - -// Get the features supported by the profiler that are accepted by profiler_init. -// Returns a null terminated char* array. -static inline char** profiler_get_features() { return nullptr; } - -// Print the current location to the console. This functill will do it best effort -// to show the profiler's combined js/c++ if the profiler is running. Note that -// printing the location require symbolicating which is very slow. -static inline void profiler_print_location() {} - -// Discard the profile, throw away the profile and notify 'profiler-locked'. -// This function is to be used when entering private browsing to prevent -// the profiler from collecting sensitive data. -static inline void profiler_lock() {} - -// Re-enable the profiler and notify 'profiler-unlocked'. -static inline void profiler_unlock() {} - -static inline void profiler_register_thread(const char* name, void* stackTop) {} -static inline void profiler_unregister_thread() {} - -// Call by the JSRuntime's operation callback. This is used to enable -// profiling on auxilerary threads. -static inline void profiler_js_operation_callback() {} - -static inline double profiler_time() { return 0; } - -static inline bool profiler_in_privacy_mode() { return false; } - -#else - -#include "GeckoProfilerImpl.h" - -#endif - -class GeckoProfilerInitRAII { -public: - GeckoProfilerInitRAII(void* stackTop) { - profiler_init(stackTop); - } - ~GeckoProfilerInitRAII() { - profiler_shutdown(); - } -}; - -#endif // ifndef SAMPLER_H diff --git a/tools/profiler/GeckoProfilerFunc.h b/tools/profiler/GeckoProfilerFunc.h deleted file mode 100644 index 831520f1d..000000000 --- a/tools/profiler/GeckoProfilerFunc.h +++ /dev/null @@ -1,71 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef PROFILER_FUNCS_H -#define PROFILER_FUNCS_H - -#include "mozilla/NullPtr.h" -#include "mozilla/StandardInteger.h" -#include "mozilla/TimeStamp.h" -#include "jsfriendapi.h" - -using mozilla::TimeStamp; -using mozilla::TimeDuration; - -// Returns a handle to pass on exit. This can check that we are popping the -// correct callstack. -inline void* mozilla_sampler_call_enter(const char *aInfo, void *aFrameAddress = NULL, - bool aCopy = false, uint32_t line = 0); -inline void mozilla_sampler_call_exit(void* handle); -inline void mozilla_sampler_add_marker(const char *aInfo); - -void mozilla_sampler_start(int aEntries, int aInterval, - const char** aFeatures, uint32_t aFeatureCount, - const char** aThreadNameFilters, uint32_t aFilterCount); - -void mozilla_sampler_stop(); - -bool mozilla_sampler_is_active(); - -void mozilla_sampler_responsiveness(const TimeStamp& time); - -void mozilla_sampler_frame_number(int frameNumber); - -const double* mozilla_sampler_get_responsiveness(); - -void mozilla_sampler_save(); - -char* mozilla_sampler_get_profile(); - -JSObject *mozilla_sampler_get_profile_data(JSContext *aCx); - -const char** mozilla_sampler_get_features(); - -void mozilla_sampler_init(void* stackTop); - -void mozilla_sampler_shutdown(); - -void mozilla_sampler_print_location1(); -void mozilla_sampler_print_location2(); - -// Lock the profiler. When locked the profiler is (1) stopped, -// (2) profile data is cleared, (3) profiler-locked is fired. -// This is used to lock down the profiler during private browsing -void mozilla_sampler_lock(); - -// Unlock the profiler, leaving it stopped and fires profiler-unlocked. -void mozilla_sampler_unlock(); - -// Register/unregister threads with the profiler -bool mozilla_sampler_register_thread(const char* name, void* stackTop); -void mozilla_sampler_unregister_thread(); - -double mozilla_sampler_time(); - -/* Returns true if env var SPS_NEW is set to anything, else false. */ -extern bool sps_version2(); - -#endif - diff --git a/tools/profiler/GeckoProfilerImpl.h b/tools/profiler/GeckoProfilerImpl.h deleted file mode 100644 index 02e53ba8b..000000000 --- a/tools/profiler/GeckoProfilerImpl.h +++ /dev/null @@ -1,350 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef TOOLS_SPS_SAMPLER_H_ -#define TOOLS_SPS_SAMPLER_H_ - -#include <stdlib.h> -#include <signal.h> -#include <stdarg.h> -#include <algorithm> -#include "mozilla/ThreadLocal.h" -#include "mozilla/Assertions.h" -#include "mozilla/TimeStamp.h" -#include "mozilla/Util.h" -#include "nsAlgorithm.h" -#include "nscore.h" -#include "jsfriendapi.h" -#include "GeckoProfilerFunc.h" -#include "PseudoStack.h" -#include "nsISupports.h" - -/* QT has a #define for the word "slots" and jsfriendapi.h has a struct with - * this variable name, causing compilation problems. Alleviate this for now by - * removing this #define */ -#ifdef MOZ_WIDGET_QT -#undef slots -#endif - -// Make sure that we can use std::min here without the Windows headers messing with us. -#ifdef min -#undef min -#endif - -struct PseudoStack; -class TableTicker; -class JSCustomObject; - -extern mozilla::ThreadLocal<PseudoStack *> tlsPseudoStack; -extern mozilla::ThreadLocal<TableTicker *> tlsTicker; -extern bool stack_key_initialized; - -#ifndef SAMPLE_FUNCTION_NAME -# ifdef __GNUC__ -# define SAMPLE_FUNCTION_NAME __FUNCTION__ -# elif defined(_MSC_VER) -# define SAMPLE_FUNCTION_NAME __FUNCTION__ -# else -# define SAMPLE_FUNCTION_NAME __func__ // defined in C99, supported in various C++ compilers. Just raw function name. -# endif -#endif - -static inline -void profiler_init(void* stackTop) -{ - mozilla_sampler_init(stackTop); -} - -static inline -void profiler_shutdown() -{ - mozilla_sampler_shutdown(); -} - -static inline -void profiler_start(int aProfileEntries, int aInterval, - const char** aFeatures, uint32_t aFeatureCount, - const char** aThreadNameFilters, uint32_t aFilterCount) -{ - mozilla_sampler_start(aProfileEntries, aInterval, aFeatures, aFeatureCount, aThreadNameFilters, aFilterCount); -} - -static inline -void profiler_stop() -{ - mozilla_sampler_stop(); -} - -static inline -bool profiler_is_active() -{ - return mozilla_sampler_is_active(); -} - -static inline -void profiler_responsiveness(const TimeStamp& aTime) -{ - mozilla_sampler_responsiveness(aTime); -} - -static inline -const double* profiler_get_responsiveness() -{ - return mozilla_sampler_get_responsiveness(); -} - -static inline -void profiler_set_frame_number(int frameNumber) -{ - return mozilla_sampler_frame_number(frameNumber); -} - -static inline -char* profiler_get_profile() -{ - return mozilla_sampler_get_profile(); -} - -static inline -JSObject* profiler_get_profile_jsobject(JSContext* aCx) -{ - return mozilla_sampler_get_profile_data(aCx); -} - -static inline -const char** profiler_get_features() -{ - return mozilla_sampler_get_features(); -} - -static inline -void profiler_print_location() -{ - if (!sps_version2()) { - return mozilla_sampler_print_location1(); - } else { - return mozilla_sampler_print_location2(); - } -} - -static inline -void profiler_lock() -{ - return mozilla_sampler_lock(); -} - -static inline -void profiler_unlock() -{ - return mozilla_sampler_unlock(); -} - -static inline -void profiler_register_thread(const char* name, void* stackTop) -{ - mozilla_sampler_register_thread(name, stackTop); -} - -static inline -void profiler_unregister_thread() -{ - mozilla_sampler_unregister_thread(); -} - -static inline -void profiler_js_operation_callback() -{ - PseudoStack *stack = tlsPseudoStack.get(); - if (!stack) { - return; - } - - stack->jsOperationCallback(); -} - -static inline -double profiler_time() -{ - return mozilla_sampler_time(); -} - -static inline -bool profiler_in_privacy_mode() -{ - PseudoStack *stack = tlsPseudoStack.get(); - if (!stack) { - return false; - } - return stack->mPrivacyMode; -} - -// we want the class and function name but can't easily get that using preprocessor macros -// __func__ doesn't have the class name and __PRETTY_FUNCTION__ has the parameters - -#define SAMPLER_APPEND_LINE_NUMBER_PASTE(id, line) id ## line -#define SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, line) SAMPLER_APPEND_LINE_NUMBER_PASTE(id, line) -#define SAMPLER_APPEND_LINE_NUMBER(id) SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, __LINE__) - -#define PROFILER_LABEL(name_space, info) mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__) -#define PROFILER_LABEL_PRINTF(name_space, info, ...) mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__, __VA_ARGS__) -#define PROFILER_MARKER(info) mozilla_sampler_add_marker(info) -#define PROFILER_MAIN_THREAD_LABEL(name_space, info) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__) -#define PROFILER_MAIN_THREAD_LABEL_PRINTF(name_space, info, ...) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__, __VA_ARGS__) -#define PROFILER_MAIN_THREAD_MARKER(info) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla_sampler_add_marker(info) - - -/* FIXME/bug 789667: memory constraints wouldn't much of a problem for - * this small a sample buffer size, except that serializing the - * profile data is extremely, unnecessarily memory intensive. */ -#ifdef MOZ_WIDGET_GONK -# define PLATFORM_LIKELY_MEMORY_CONSTRAINED -#endif - -#if !defined(PLATFORM_LIKELY_MEMORY_CONSTRAINED) && !defined(ARCH_ARMV6) -# define PROFILE_DEFAULT_ENTRY 1000000 -#else -# define PROFILE_DEFAULT_ENTRY 100000 -#endif - -#if defined(PLATFORM_LIKELY_MEMORY_CONSTRAINED) -/* A 1ms sampling interval has been shown to be a large perf hit - * (10fps) on memory-contrained (low-end) platforms, and additionally - * to yield different results from the profiler. Where this is the - * important case, b2g, there are also many gecko processes which - * magnify these effects. */ -# define PROFILE_DEFAULT_INTERVAL 10 -#elif defined(ANDROID) -// We use a lower frequency on Android, in order to make things work -// more smoothly on phones. This value can be adjusted later with -// some libunwind optimizations. -// In one sample measurement on Galaxy Nexus, out of about 700 backtraces, -// 60 of them took more than 25ms, and the average and standard deviation -// were 6.17ms and 9.71ms respectively. - -// For now since we don't support stackwalking let's use 1ms since it's fast -// enough. -#define PROFILE_DEFAULT_INTERVAL 1 -#else -#define PROFILE_DEFAULT_INTERVAL 1 -#endif -#define PROFILE_DEFAULT_FEATURES NULL -#define PROFILE_DEFAULT_FEATURE_COUNT 0 - -namespace mozilla { - -class MOZ_STACK_CLASS SamplerStackFrameRAII { -public: - // we only copy the strings at save time, so to take multiple parameters we'd need to copy them then. - SamplerStackFrameRAII(const char *aInfo, uint32_t line) { - mHandle = mozilla_sampler_call_enter(aInfo, this, false, line); - } - ~SamplerStackFrameRAII() { - mozilla_sampler_call_exit(mHandle); - } -private: - void* mHandle; -}; - -static const int SAMPLER_MAX_STRING = 128; -class MOZ_STACK_CLASS SamplerStackFramePrintfRAII { -public: - // we only copy the strings at save time, so to take multiple parameters we'd need to copy them then. - SamplerStackFramePrintfRAII(const char *aDefault, uint32_t line, const char *aFormat, ...) { - if (profiler_is_active() && !profiler_in_privacy_mode()) { - va_list args; - va_start(args, aFormat); - char buff[SAMPLER_MAX_STRING]; - - // We have to use seperate printf's because we're using - // the vargs. -#if _MSC_VER - _vsnprintf(buff, SAMPLER_MAX_STRING, aFormat, args); - _snprintf(mDest, SAMPLER_MAX_STRING, "%s %s", aDefault, buff); -#else - vsnprintf(buff, SAMPLER_MAX_STRING, aFormat, args); - snprintf(mDest, SAMPLER_MAX_STRING, "%s %s", aDefault, buff); -#endif - mHandle = mozilla_sampler_call_enter(mDest, this, true, line); - va_end(args); - } else { - mHandle = mozilla_sampler_call_enter(aDefault, NULL, false, line); - } - } - ~SamplerStackFramePrintfRAII() { - mozilla_sampler_call_exit(mHandle); - } -private: - char mDest[SAMPLER_MAX_STRING]; - void* mHandle; -}; - -} //mozilla - -inline PseudoStack* mozilla_get_pseudo_stack(void) -{ - if (!stack_key_initialized) - return NULL; - return tlsPseudoStack.get(); -} - -inline void* mozilla_sampler_call_enter(const char *aInfo, void *aFrameAddress, - bool aCopy, uint32_t line) -{ - // check if we've been initialized to avoid calling pthread_getspecific - // with a null tlsStack which will return undefined results. - if (!stack_key_initialized) - return NULL; - - PseudoStack *stack = tlsPseudoStack.get(); - // we can't infer whether 'stack' has been initialized - // based on the value of stack_key_intiailized because - // 'stack' is only intialized when a thread is being - // profiled. - if (!stack) { - return stack; - } - stack->push(aInfo, aFrameAddress, aCopy, line); - - // The handle is meant to support future changes - // but for now it is simply use to save a call to - // pthread_getspecific on exit. It also supports the - // case where the sampler is initialized between - // enter and exit. - return stack; -} - -inline void mozilla_sampler_call_exit(void *aHandle) -{ - if (!aHandle) - return; - - PseudoStack *stack = (PseudoStack*)aHandle; - stack->pop(); -} - -inline void mozilla_sampler_add_marker(const char *aMarker) -{ - if (!stack_key_initialized) - return; - - // Don't insert a marker if we're not profiling to avoid - // the heap copy (malloc). - if (!profiler_is_active()) { - return; - } - - // Don't add a marker if we don't want to include personal information - if (profiler_in_privacy_mode()) { - return; - } - - PseudoStack *stack = tlsPseudoStack.get(); - if (!stack) { - return; - } - stack->addMarker(aMarker); -} - -#endif /* ndef TOOLS_SPS_SAMPLER_H_ */ |