diff --git a/dom/plugins/base/android/ANPCanvas.cpp b/dom/plugins/base/android/ANPCanvas.cpp deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/dom/plugins/base/android/ANPPaint.cpp b/dom/plugins/base/android/ANPPaint.cpp deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/dom/plugins/base/android/ANPPath.cpp b/dom/plugins/base/android/ANPPath.cpp deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/dom/plugins/base/android/ANPTypeface.cpp b/dom/plugins/base/android/ANPTypeface.cpp deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/embedding/android/CrashReporter.java.in b/embedding/android/CrashReporter.java.in deleted file mode 100644 index dd1f6350e70f..000000000000 --- a/embedding/android/CrashReporter.java.in +++ /dev/null @@ -1,336 +0,0 @@ -/* -*- Mode: Java; tab-width: 20; indent-tabs-mode: nil; -*- - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Android code. - * - * The Initial Developer of the Original Code is Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Brad Lassey - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#filter substitution -package @ANDROID_PACKAGE_NAME@; - - -import android.app.*; -import android.content.*; -import android.os.*; -import android.util.*; -import android.view.*; -import android.view.View.*; -import android.widget.*; - -import org.mozilla.gecko.*; -import java.util.*; -import java.io.*; -import java.net.*; -import java.nio.channels.*; - -public class CrashReporter extends Activity -{ - static final String kMiniDumpPathKey = "upload_file_minidump"; - static final String kPageURLKey = "URL"; - static final String kNotesKey = "Notes"; - Handler mHandler = null; - ProgressDialog mProgressDialog; - File mPendingMinidumpFile; - File mPendingExtrasFile; - HashMap mExtrasStringMap; - - boolean moveFile(File inFile, File outFile) - { - Log.i("GeckoCrashReporter", "moving " + inFile + " to " + outFile); - if (inFile.renameTo(outFile)) - return true; - try { - outFile.createNewFile(); - Log.i("GeckoCrashReporter", "couldn't rename minidump file"); - // so copy it instead - FileChannel inChannel = new FileInputStream(inFile).getChannel(); - FileChannel outChannel = new FileOutputStream(outFile).getChannel(); - long transferred = inChannel.transferTo(0, inChannel.size(), outChannel); - inChannel.close(); - outChannel.close(); - - if (transferred > 0) - inFile.delete(); - } catch (Exception e) { - Log.e("GeckoCrashReporter", - "exception while copying minidump file: ", e); - return false; - } - return true; - } - - void doFinish() { - if (mHandler != null) { - mHandler.post(new Runnable(){ - public void run() { - finish(); - }}); - } - } - - @Override - public void finish() - { - mProgressDialog.dismiss(); - super.finish(); - } - - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - // mHandler is created here so runnables can be run on the main thread - mHandler = new Handler(); - setContentView(R.layout.crash_reporter); - mProgressDialog = new ProgressDialog(CrashReporter.this); - mProgressDialog.setMessage(getString(R.string.sending_crash_report)); - - final Button restartButton = (Button) findViewById(R.id.restart); - final Button closeButton = (Button) findViewById(R.id.close); - String passedMinidumpPath = getIntent().getStringExtra("minidumpPath"); - File passedMinidumpFile = new File(passedMinidumpPath); - File pendingDir = - new File("/data/data/@ANDROID_PACKAGE_NAME@/files/mozilla/Crash Reports/pending"); - pendingDir.mkdirs(); - mPendingMinidumpFile = new File(pendingDir, passedMinidumpFile.getName()); - moveFile(passedMinidumpFile, mPendingMinidumpFile); - - File extrasFile = new File(passedMinidumpPath.replaceAll(".dmp", ".extra")); - mPendingExtrasFile = new File(pendingDir, extrasFile.getName()); - moveFile(extrasFile, mPendingExtrasFile); - - mExtrasStringMap = new HashMap(); - readStringsFromFile(mPendingExtrasFile.getPath(), mExtrasStringMap); - } - - void backgroundSendReport() - { - final CheckBox sendReportCheckbox = (CheckBox) findViewById(R.id.send_report); - if (!sendReportCheckbox.isChecked()) { - doFinish(); - return; - } - - mProgressDialog.show(); - new Thread(new Runnable() { - public void run() { - sendReport(mPendingMinidumpFile, mExtrasStringMap, mPendingExtrasFile); - }}).start(); - } - - public void onCloseClick(View v) - { - backgroundSendReport(); - } - - public void onRestartClick(View v) - { - doRestart(); - backgroundSendReport(); - } - - boolean readStringsFromFile(String filePath, Map stringMap) - { - try { - BufferedReader reader = new BufferedReader( - new FileReader(filePath)); - return readStringsFromReader(reader, stringMap); - } catch (Exception e) { - Log.e("GeckoCrashReporter", "exception while reading strings: ", e); - return false; - } - } - - boolean readStringsFromReader(BufferedReader reader, Map stringMap) - throws java.io.IOException - { - String line; - while ((line = reader.readLine()) != null) { - int equalsPos = -1; - if ((equalsPos = line.indexOf('=')) != -1) { - String key = line.substring(0, equalsPos); - String val = unescape(line.substring(equalsPos + 1)); - stringMap.put(key, val); - } - } - reader.close(); - return true; - } - - String generateBoundary() - { - // Generate some random numbers to fill out the boundary - int r0 = (int)((double)Integer.MAX_VALUE * Math.random()); - int r1 = (int)((double)Integer.MAX_VALUE * Math.random()); - - return String.format("---------------------------%08X%08X", r0, r1); - } - - void sendPart(OutputStream os, String boundary, String name, String data) - { - try { - os.write(("--" + boundary + "\r\n" + - "Content-Disposition: form-data; name=\"" + - name + "\"\r\n\r\n" + - data + "\r\n").getBytes()); - } catch (Exception ex) { - Log.e("GeckoCrashReporter", "Exception when sending \"" + name + "\"", ex); - } - } - - void sendFile(OutputStream os, String boundary, String name, File file) - throws IOException - { - os.write(("--" + boundary + "\r\n" + - "Content-Disposition: form-data; " + - "name=\"" + name + "\"; " + - "filename=\"" + file.getName() + "\"\r\n" + - "Content-Type: application/octet-stream\r\n" + - "\r\n").getBytes()); - FileChannel fc = - new FileInputStream(file).getChannel(); - fc.transferTo(0, fc.size(), Channels.newChannel(os)); - fc.close(); - } - - void sendReport(File minidumpFile, Map extras, - File extrasFile) - { - Log.i("GeckoCrashReport", "sendReport: " + minidumpFile.getPath()); - final CheckBox includeURLCheckbox = (CheckBox) findViewById(R.id.include_url); - - String spec = extras.get("ServerURL"); - if (spec == null) - doFinish(); - - Log.i("GeckoCrashReport", "server url: " + spec); - try { - URL url = new URL(spec); - HttpURLConnection conn = (HttpURLConnection)url.openConnection(); - conn.setRequestMethod("POST"); - String boundary = generateBoundary(); - conn.setDoOutput(true); - conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); - - OutputStream os = conn.getOutputStream(); - Iterator keys = extras.keySet().iterator(); - while (keys.hasNext()) { - String key = keys.next(); - if (key.equals(kPageURLKey)) { - if (includeURLCheckbox.isChecked()) - sendPart(os, boundary, key, extras.get(key)); - } else if (!key.equals("ServerURL") && !key.equals(kNotesKey)) { - sendPart(os, boundary, key, extras.get(key)); - } - } - - // Add some extra information to notes so its displayed by - // crash-stats.mozilla.org. Remove this when bug 607942 is fixed. - String notes = extras.containsKey(kNotesKey) ? extras.get(kNotesKey) + - "\n" : ""; - if (@MOZ_MIN_CPU_VERSION@ < 7) - notes += "nothumb Build\n"; - notes += Build.MANUFACTURER + " "; - notes += Build.MODEL + "\n"; - notes += Build.FINGERPRINT; - sendPart(os, boundary, kNotesKey, notes); - - sendPart(os, boundary, "Min_ARM_Version", "@MOZ_MIN_CPU_VERSION@"); - sendPart(os, boundary, "Android_Manufacturer", Build.MANUFACTURER); - sendPart(os, boundary, "Android_Model", Build.MODEL); - sendPart(os, boundary, "Android_Board", Build.BOARD); - sendPart(os, boundary, "Android_Brand", Build.BRAND); - sendPart(os, boundary, "Android_Device", Build.DEVICE); - sendPart(os, boundary, "Android_Display", Build.DISPLAY); - sendPart(os, boundary, "Android_Fingerprint", Build.FINGERPRINT); - sendPart(os, boundary, "Android_CPU_ABI", Build.CPU_ABI); - if (Build.VERSION.SDK_INT >= 8) { - try { - sendPart(os, boundary, "Android_CPU_ABI2", Build.CPU_ABI2); - sendPart(os, boundary, "Android_Hardware", Build.HARDWARE); - } catch (Exception ex) { - Log.e("GeckoCrashReporter", "Exception while sending SDK version 8 keys", ex); - } - } - sendPart(os, boundary, "Android_Version", Build.VERSION.SDK_INT + " (" + Build.VERSION.CODENAME + ")"); - - sendFile(os, boundary, kMiniDumpPathKey, minidumpFile); - os.write(("\r\n--" + boundary + "--\r\n").getBytes()); - os.flush(); - os.close(); - BufferedReader br = new BufferedReader( - new InputStreamReader(conn.getInputStream())); - HashMap responseMap = new HashMap(); - readStringsFromReader(br, responseMap); - - if (conn.getResponseCode() == conn.HTTP_OK) { - File submittedDir = new File( - "/data/data/@ANDROID_PACKAGE_NAME@/files/mozilla/Crash Reports/submitted"); - submittedDir.mkdirs(); - minidumpFile.delete(); - extrasFile.delete(); - String crashid = responseMap.get("CrashID"); - File file = new File(submittedDir, crashid + ".txt"); - FileOutputStream fos = new FileOutputStream(file); - fos.write("Crash ID: ".getBytes()); - fos.write(crashid.getBytes()); - fos.close(); - } - } catch (IOException e) { - Log.e("GeckoCrashReporter", "exception during send: ", e); - } - - doFinish(); - } - - void doRestart() - { - try { - String action = "android.intent.action.MAIN"; - Intent intent = new Intent(action); - intent.setClassName("@ANDROID_PACKAGE_NAME@", - "@ANDROID_PACKAGE_NAME@.App"); - Log.i("GeckoCrashReporter", intent.toString()); - startActivity(intent); - } catch (Exception e) { - Log.e("GeckoCrashReporter", "error while trying to restart", e); - } - } - - public String unescape(String string) - { - return string.replaceAll("\\\\", "\\").replaceAll("\\n", "\n") - .replaceAll("\\t", "\t"); - } -} - diff --git a/embedding/android/GeckoApp.java b/embedding/android/GeckoApp.java deleted file mode 100644 index 1fc150e2bb4a..000000000000 --- a/embedding/android/GeckoApp.java +++ /dev/null @@ -1,881 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Android code. - * - * The Initial Developer of the Original Code is Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2009-2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Vladimir Vukicevic - * Matt Brubeck - * Vivien Nicolas - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -package org.mozilla.gecko; - -import java.io.*; -import java.util.*; -import java.util.zip.*; -import java.nio.*; -import java.nio.channels.FileChannel; -import java.util.concurrent.*; -import java.lang.reflect.*; - -import android.os.*; -import android.app.*; -import android.text.*; -import android.view.*; -import android.view.inputmethod.*; -import android.content.*; -import android.content.res.*; -import android.graphics.*; -import android.widget.*; -import android.hardware.*; - -import android.util.*; -import android.net.*; -import android.database.*; -import android.provider.*; -import android.content.pm.*; -import android.content.pm.PackageManager.*; -import dalvik.system.*; - -abstract public class GeckoApp - extends Activity -{ - private static final String LOG_FILE_NAME = "GeckoApp"; - - public static final String ACTION_ALERT_CLICK = "org.mozilla.gecko.ACTION_ALERT_CLICK"; - public static final String ACTION_ALERT_CLEAR = "org.mozilla.gecko.ACTION_ALERT_CLEAR"; - public static final String ACTION_WEBAPP = "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 AbsoluteLayout mainLayout; - public static GeckoSurfaceView surfaceView; - public static SurfaceView cameraView; - public static GeckoApp mAppContext; - public static boolean mFullscreen = false; - public static File sGREDir = null; - static Thread mLibLoadThread = null; - public Handler mMainHandler; - private IntentFilter mConnectivityFilter; - private BroadcastReceiver mConnectivityReceiver; - private BroadcastReceiver mBatteryReceiver; - private BroadcastReceiver mSmsReceiver; - - enum LaunchState {PreLaunch, Launching, WaitForDebugger, - Launched, GeckoRunning, GeckoExiting}; - private static LaunchState sLaunchState = LaunchState.PreLaunch; - private static boolean sTryCatchAttached = false; - - - static boolean checkLaunchState(LaunchState checkState) { - synchronized(sLaunchState) { - return sLaunchState == checkState; - } - } - - static void setLaunchState(LaunchState setState) { - synchronized(sLaunchState) { - sLaunchState = setState; - } - } - - // if mLaunchState is equal to checkState this sets mLaunchState to setState - // and return true. Otherwise we return false. - static boolean checkAndSetLaunchState(LaunchState checkState, LaunchState setState) { - synchronized(sLaunchState) { - if (sLaunchState != checkState) - return false; - sLaunchState = setState; - return true; - } - } - - void showErrorDialog(String message) - { - new AlertDialog.Builder(this) - .setMessage(message) - .setCancelable(false) - .setPositiveButton(R.string.exit_label, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, - int id) - { - GeckoApp.this.finish(); - System.exit(0); - } - }).show(); - } - - public static final String PLUGIN_ACTION = "android.webkit.PLUGIN"; - - /** - * A plugin that wish to be loaded in the WebView must provide this permission - * in their AndroidManifest.xml. - */ - public static final String PLUGIN_PERMISSION = "android.webkit.permission.PLUGIN"; - - private static final String LOGTAG = "PluginManager"; - - private static final String PLUGIN_SYSTEM_LIB = "/system/lib/plugins/"; - - private static final String PLUGIN_TYPE = "type"; - private static final String TYPE_NATIVE = "native"; - public ArrayList mPackageInfoCache = new ArrayList(); - - String[] getPluginDirectories() { - - ArrayList directories = new ArrayList(); - PackageManager pm = this.mAppContext.getPackageManager(); - List 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, "Ignore bad plugin"); - continue; - } - - Log.w(LOGTAG, "Loading plugin: " + serviceInfo.packageName); - - - // 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, "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, "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, "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, "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 type defined"); - 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()]); - } - - Class getPluginClass(String packageName, String className) - throws NameNotFoundException, ClassNotFoundException { - Context pluginContext = this.mAppContext.createPackageContext(packageName, - Context.CONTEXT_INCLUDE_CODE | - Context.CONTEXT_IGNORE_SECURITY); - ClassLoader pluginCL = pluginContext.getClassLoader(); - return pluginCL.loadClass(className); - } - - // Returns true when the intent is going to be handled by gecko launch - boolean launch(Intent intent) - { - if (!checkAndSetLaunchState(LaunchState.Launching, LaunchState.Launched)) - return false; - - if (intent == null) - intent = getIntent(); - final Intent i = intent; - new Thread() { - public void run() { - try { - if (mLibLoadThread != null) - mLibLoadThread.join(); - } catch (InterruptedException ie) {} - - // Show the URL we are about to load, if the intent has one - if (Intent.ACTION_VIEW.equals(i.getAction())) { - surfaceView.mSplashURL = i.getDataString(); - } - surfaceView.drawSplashScreen(); - - // unpack files in the components directory - try { - unpackComponents(); - } catch (FileNotFoundException fnfe) { - Log.e(LOG_FILE_NAME, "error unpacking components", fnfe); - Looper.prepare(); - showErrorDialog(getString(R.string.error_loading_file)); - Looper.loop(); - return; - } catch (IOException ie) { - Log.e(LOG_FILE_NAME, "error unpacking components", ie); - String msg = ie.getMessage(); - Looper.prepare(); - if (msg != null && msg.equalsIgnoreCase("No space left on device")) - showErrorDialog(getString(R.string.no_space_to_start_error)); - else - showErrorDialog(getString(R.string.error_loading_file)); - Looper.loop(); - return; - } - - // and then fire us up - try { - String env = i.getStringExtra("env0"); - GeckoAppShell.runGecko(getApplication().getPackageResourcePath(), - i.getStringExtra("args"), - i.getDataString()); - } catch (Exception e) { - Log.e(LOG_FILE_NAME, "top level exception", e); - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - e.printStackTrace(pw); - pw.flush(); - GeckoAppShell.reportJavaCrash(sw.toString()); - } - } - }.start(); - return true; - } - - /** Called when the activity is first created. */ - @Override - public void onCreate(Bundle savedInstanceState) - { - mAppContext = this; - mMainHandler = new Handler(); - - if (!sTryCatchAttached) { - sTryCatchAttached = true; - mMainHandler.post(new Runnable() { - public void run() { - try { - Looper.loop(); - } catch (Exception e) { - Log.e(LOG_FILE_NAME, "top level exception", e); - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - e.printStackTrace(pw); - pw.flush(); - GeckoAppShell.reportJavaCrash(sw.toString()); - } - // resetting this is kinda pointless, but oh well - sTryCatchAttached = false; - } - }); - } - - SharedPreferences settings = getPreferences(Activity.MODE_PRIVATE); - String localeCode = settings.getString(getPackageName() + ".locale", ""); - if (localeCode != null && localeCode.length() > 0) - GeckoAppShell.setSelectedLocale(localeCode); - - Log.i(LOG_FILE_NAME, "create"); - super.onCreate(savedInstanceState); - - if (sGREDir == null) - sGREDir = new File(this.getApplicationInfo().dataDir); - - getWindow().setFlags(mFullscreen ? - WindowManager.LayoutParams.FLAG_FULLSCREEN : 0, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - - if (cameraView == null) { - cameraView = new SurfaceView(this); - cameraView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); - } - - if (surfaceView == null) - surfaceView = new GeckoSurfaceView(this); - else - mainLayout.removeAllViews(); - - mainLayout = new AbsoluteLayout(this); - mainLayout.addView(surfaceView, - new AbsoluteLayout.LayoutParams(AbsoluteLayout.LayoutParams.MATCH_PARENT, // level 8 - AbsoluteLayout.LayoutParams.MATCH_PARENT, - 0, - 0)); - - setContentView(mainLayout, - new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, - ViewGroup.LayoutParams.FILL_PARENT)); - - mConnectivityFilter = new IntentFilter(); - mConnectivityFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); - mConnectivityReceiver = new GeckoConnectivityReceiver(); - - IntentFilter batteryFilter = new IntentFilter(); - batteryFilter.addAction(Intent.ACTION_BATTERY_CHANGED); - mBatteryReceiver = new GeckoBatteryManager(); - registerReceiver(mBatteryReceiver, batteryFilter); - - IntentFilter smsFilter = new IntentFilter(); - smsFilter.addAction("android.provider.Telephony.SMS_RECEIVED"); - mSmsReceiver = new GeckoSmsManager(); - registerReceiver(mSmsReceiver, smsFilter); - - if (!checkAndSetLaunchState(LaunchState.PreLaunch, - LaunchState.Launching)) - return; - - checkAndLaunchUpdate(); - mLibLoadThread = new Thread(new Runnable() { - public void run() { - // 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(); - GeckoAppShell.loadGeckoLibs( - getApplication().getPackageResourcePath()); - Locale.setDefault(locale); - Resources res = getBaseContext().getResources(); - Configuration config = res.getConfiguration(); - config.locale = locale; - res.updateConfiguration(config, res.getDisplayMetrics()); - }}); - mLibLoadThread.start(); - } - - public void enableCameraView() { - // Some phones (eg. nexus S) need at least a 8x16 preview size - mainLayout.addView(cameraView, new AbsoluteLayout.LayoutParams(8, 16, 0, 0)); - } - - public void disableCameraView() { - mainLayout.removeView(cameraView); - } - - @Override - protected void onNewIntent(Intent intent) { - if (checkLaunchState(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; - } - final String action = intent.getAction(); - if (ACTION_DEBUG.equals(action) && - checkAndSetLaunchState(LaunchState.Launching, LaunchState.WaitForDebugger)) { - - mMainHandler.postDelayed(new Runnable() { - public void run() { - Log.i(LOG_FILE_NAME, "Launching from debug intent after 5s wait"); - setLaunchState(LaunchState.Launching); - launch(null); - } - }, 1000 * 5 /* 5 seconds */); - Log.i(LOG_FILE_NAME, "Intent : ACTION_DEBUG - waiting 5s before launching"); - return; - } - if (checkLaunchState(LaunchState.WaitForDebugger) || launch(intent)) - return; - - if (Intent.ACTION_MAIN.equals(action)) { - Log.i(LOG_FILE_NAME, "Intent : ACTION_MAIN"); - GeckoAppShell.sendEventToGecko(new GeckoEvent("")); - } - else if (Intent.ACTION_VIEW.equals(action)) { - String uri = intent.getDataString(); - GeckoAppShell.sendEventToGecko(new GeckoEvent(uri)); - Log.i(LOG_FILE_NAME,"onNewIntent: "+uri); - } - else if (ACTION_WEBAPP.equals(action)) { - String uri = intent.getStringExtra("args"); - GeckoAppShell.sendEventToGecko(new GeckoEvent(uri)); - Log.i(LOG_FILE_NAME,"Intent : WEBAPP - " + uri); - } - else if (ACTION_BOOKMARK.equals(action)) { - String args = intent.getStringExtra("args"); - GeckoAppShell.sendEventToGecko(new GeckoEvent(args)); - Log.i(LOG_FILE_NAME,"Intent : BOOKMARK - " + args); - } - } - - @Override - public void onPause() - { - Log.i(LOG_FILE_NAME, "pause"); - GeckoAppShell.sendEventToGecko(new GeckoEvent(GeckoEvent.ACTIVITY_PAUSING)); - // The user is navigating away from this activity, but nothing - // has come to the foreground yet; for Gecko, we may want to - // stop repainting, for example. - - // Whatever we do here should be fast, because we're blocking - // the next activity from showing up until we finish. - - // onPause will be followed by either onResume or onStop. - super.onPause(); - - unregisterReceiver(mConnectivityReceiver); - } - - @Override - public void onResume() - { - Log.i(LOG_FILE_NAME, "resume"); - if (checkLaunchState(LaunchState.GeckoRunning)) - GeckoAppShell.onResume(); - // After an onPause, the activity is back in the foreground. - // Undo whatever we did in onPause. - super.onResume(); - - // Just in case. Normally we start in onNewIntent - if (checkLaunchState(LaunchState.PreLaunch) || - checkLaunchState(LaunchState.Launching)) - onNewIntent(getIntent()); - - registerReceiver(mConnectivityReceiver, mConnectivityFilter); - } - - @Override - public void onStop() - { - Log.i(LOG_FILE_NAME, "stop"); - // We're about to be stopped, potentially in preparation for - // being destroyed. We're killable after this point -- as I - // understand it, in extreme cases the process can be terminated - // without going through onDestroy. - // - // We might also get an onRestart after this; not sure what - // that would mean for Gecko if we were to kill it here. - // Instead, what we should do here is save prefs, session, - // etc., and generally mark the profile as 'clean', and then - // dirty it again if we get an onResume. - - GeckoAppShell.sendEventToGecko(new GeckoEvent(GeckoEvent.ACTIVITY_STOPPING)); - super.onStop(); - GeckoAppShell.putChildInBackground(); - } - - @Override - public void onRestart() - { - Log.i(LOG_FILE_NAME, "restart"); - GeckoAppShell.putChildInForeground(); - super.onRestart(); - } - - @Override - public void onStart() - { - Log.i(LOG_FILE_NAME, "start"); - GeckoAppShell.sendEventToGecko(new GeckoEvent(GeckoEvent.ACTIVITY_START)); - super.onStart(); - } - - @Override - public void onDestroy() - { - Log.i(LOG_FILE_NAME, "destroy"); - - // Tell Gecko to shutting down; we'll end up calling System.exit() - // in onXreExit. - if (isFinishing()) - GeckoAppShell.sendEventToGecko(new GeckoEvent(GeckoEvent.ACTIVITY_SHUTDOWN)); - - super.onDestroy(); - - unregisterReceiver(mSmsReceiver); - unregisterReceiver(mBatteryReceiver); - } - - @Override - public void onConfigurationChanged(android.content.res.Configuration newConfig) - { - Log.i(LOG_FILE_NAME, "configuration changed"); - // nothing, just ignore - super.onConfigurationChanged(newConfig); - } - - @Override - public void onLowMemory() - { - Log.e(LOG_FILE_NAME, "low memory"); - if (checkLaunchState(LaunchState.GeckoRunning)) - GeckoAppShell.onLowMemory(); - super.onLowMemory(); - } - - abstract public String getPackageName(); - abstract public String getContentProcessName(); - - protected void unpackComponents() - throws IOException, FileNotFoundException - { - File applicationPackage = new File(getApplication().getPackageResourcePath()); - File componentsDir = new File(sGREDir, "components"); - if (componentsDir.lastModified() == applicationPackage.lastModified()) - return; - - componentsDir.mkdir(); - componentsDir.setLastModified(applicationPackage.lastModified()); - - GeckoAppShell.killAnyZombies(); - - ZipFile zip = new ZipFile(applicationPackage); - - byte[] buf = new byte[32768]; - try { - if (unpackFile(zip, buf, null, "removed-files")) - removeFiles(); - } catch (Exception ex) { - // This file may not be there, so just log any errors and move on - Log.w(LOG_FILE_NAME, "error removing files", ex); - } - - // copy any .xpi file into an extensions/ directory - Enumeration zipEntries = zip.entries(); - while (zipEntries.hasMoreElements()) { - ZipEntry entry = zipEntries.nextElement(); - if (entry.getName().startsWith("extensions/") && entry.getName().endsWith(".xpi")) { - Log.i("GeckoAppJava", "installing extension : " + entry.getName()); - unpackFile(zip, buf, entry, entry.getName()); - } - } - } - - void removeFiles() throws IOException { - BufferedReader reader = new BufferedReader( - new FileReader(new File(sGREDir, "removed-files"))); - try { - for (String removedFileName = reader.readLine(); - removedFileName != null; removedFileName = reader.readLine()) { - File removedFile = new File(sGREDir, removedFileName); - if (removedFile.exists()) - removedFile.delete(); - } - } finally { - reader.close(); - } - - } - - private boolean unpackFile(ZipFile zip, byte[] buf, ZipEntry fileEntry, - String name) - throws IOException, FileNotFoundException - { - if (fileEntry == null) - fileEntry = zip.getEntry(name); - if (fileEntry == null) - throw new FileNotFoundException("Can't find " + name + " in " + - zip.getName()); - - File outFile = new File(sGREDir, name); - if (outFile.lastModified() == fileEntry.getTime() && - outFile.length() == fileEntry.getSize()) - return false; - - File dir = outFile.getParentFile(); - if (!dir.exists()) - dir.mkdirs(); - - InputStream fileStream; - fileStream = zip.getInputStream(fileEntry); - - OutputStream outStream = new FileOutputStream(outFile); - - while (fileStream.available() > 0) { - int read = fileStream.read(buf, 0, buf.length); - outStream.write(buf, 0, read); - } - - fileStream.close(); - outStream.close(); - outFile.setLastModified(fileEntry.getTime()); - return true; - } - - public void addEnvToIntent(Intent intent) { - Map envMap = System.getenv(); - Set> envSet = envMap.entrySet(); - Iterator> envIter = envSet.iterator(); - StringBuffer envstr = new StringBuffer(); - int c = 0; - while (envIter.hasNext()) { - Map.Entry entry = envIter.next(); - intent.putExtra("env" + c, entry.getKey() + "=" - + entry.getValue()); - c++; - } - } - - public void doRestart() { - try { - String action = "org.mozilla.gecko.restart"; - Intent intent = new Intent(action); - intent.setClassName(getPackageName(), - getPackageName() + ".Restarter"); - addEnvToIntent(intent); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_MULTIPLE_TASK); - Log.i(LOG_FILE_NAME, intent.toString()); - GeckoAppShell.killAnyZombies(); - startActivity(intent); - } catch (Exception e) { - Log.i(LOG_FILE_NAME, "error doing restart", e); - } - finish(); - // Give the restart process time to start before we die - GeckoAppShell.waitForAnotherGeckoProc(); - } - - public void handleNotification(String action, String alertName, String alertCookie) { - GeckoAppShell.handleNotification(action, alertName, alertCookie); - } - - private void checkAndLaunchUpdate() { - Log.i(LOG_FILE_NAME, "Checking for an update"); - - int statusCode = 8; // UNEXPECTED_ERROR - File baseUpdateDir = null; - if (Build.VERSION.SDK_INT >= 8) - baseUpdateDir = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS); - else - baseUpdateDir = new File(Environment.getExternalStorageDirectory().getPath(), "download"); - - File updateDir = new File(new File(baseUpdateDir, "updates"),"0"); - - File updateFile = new File(updateDir, "update.apk"); - File statusFile = new File(updateDir, "update.status"); - - if (!statusFile.exists() || !readUpdateStatus(statusFile).equals("pending")) - return; - - if (!updateFile.exists()) - return; - - Log.i(LOG_FILE_NAME, "Update is available!"); - - // Launch APK - File updateFileToRun = new File(updateDir, getPackageName() + "-update.apk"); - try { - if (updateFile.renameTo(updateFileToRun)) { - String amCmd = "/system/bin/am start -a android.intent.action.VIEW " + - "-n com.android.packageinstaller/.PackageInstallerActivity -d file://" + - updateFileToRun.getPath(); - Log.i(LOG_FILE_NAME, amCmd); - Runtime.getRuntime().exec(amCmd); - statusCode = 0; // OK - } else { - Log.i(LOG_FILE_NAME, "Cannot rename the update file!"); - statusCode = 7; // WRITE_ERROR - } - } catch (Exception e) { - Log.i(LOG_FILE_NAME, "error launching installer to update", e); - } - - // Update the status file - String status = statusCode == 0 ? "succeeded\n" : "failed: "+ statusCode + "\n"; - - OutputStream outStream; - try { - byte[] buf = status.getBytes("UTF-8"); - outStream = new FileOutputStream(statusFile); - outStream.write(buf, 0, buf.length); - outStream.close(); - } catch (Exception e) { - Log.i(LOG_FILE_NAME, "error writing status file", e); - } - - if (statusCode == 0) - System.exit(0); - } - - private String readUpdateStatus(File statusFile) { - String status = ""; - try { - BufferedReader reader = new BufferedReader(new FileReader(statusFile)); - status = reader.readLine(); - reader.close(); - } catch (Exception e) { - Log.i(LOG_FILE_NAME, "error reading update status", e); - } - return status; - } - - static final int FILE_PICKER_REQUEST = 1; - - private SynchronousQueue mFilePickerResult = new SynchronousQueue(); - public String showFilePicker(String aMimeType) { - Intent intent = new Intent(Intent.ACTION_GET_CONTENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType(aMimeType); - GeckoApp.this. - startActivityForResult( - Intent.createChooser(intent, getString(R.string.choose_file)), - FILE_PICKER_REQUEST); - String filePickerResult = ""; - - try { - while (null == (filePickerResult = mFilePickerResult.poll(1, TimeUnit.MILLISECONDS))) { - Log.i("GeckoApp", "processing events from showFilePicker "); - GeckoAppShell.processNextNativeEvent(); - } - } catch (InterruptedException e) { - Log.i(LOG_FILE_NAME, "showing file picker ", e); - } - - return filePickerResult; - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, - Intent data) { - String filePickerResult = ""; - if (data != null && resultCode == RESULT_OK) { - try { - ContentResolver cr = getContentResolver(); - Uri uri = data.getData(); - Cursor cursor = GeckoApp.mAppContext.getContentResolver().query( - uri, - new String[] { OpenableColumns.DISPLAY_NAME }, - null, - null, - null); - String name = null; - if (cursor != null) { - try { - if (cursor.moveToNext()) { - name = cursor.getString(0); - } - } finally { - cursor.close(); - } - } - String fileName = "tmp_"; - String fileExt = null; - int period; - if (name == null || (period = name.lastIndexOf('.')) == -1) { - String mimeType = cr.getType(uri); - fileExt = "." + GeckoAppShell.getExtensionFromMimeType(mimeType); - } else { - fileExt = name.substring(period); - fileName = name.substring(0, period); - } - File file = File.createTempFile(fileName, fileExt, sGREDir); - - FileOutputStream fos = new FileOutputStream(file); - InputStream is = cr.openInputStream(uri); - byte[] buf = new byte[4096]; - int len = is.read(buf); - while (len != -1) { - fos.write(buf, 0, len); - len = is.read(buf); - } - fos.close(); - filePickerResult = file.getAbsolutePath(); - }catch (Exception e) { - Log.e(LOG_FILE_NAME, "showing file picker", e); - } - } - try { - mFilePickerResult.put(filePickerResult); - } catch (InterruptedException e) { - Log.i(LOG_FILE_NAME, "error returning file picker result", e); - } - } -} diff --git a/embedding/android/GeckoSurfaceView.java b/embedding/android/GeckoSurfaceView.java deleted file mode 100644 index 385c8d1e29ed..000000000000 --- a/embedding/android/GeckoSurfaceView.java +++ /dev/null @@ -1,833 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Android code. - * - * The Initial Developer of the Original Code is Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Vladimir Vukicevic - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -package org.mozilla.gecko; - -import java.io.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.locks.*; -import java.util.concurrent.atomic.*; -import java.util.zip.*; -import java.nio.*; - -import android.os.*; -import android.app.*; -import android.text.*; -import android.text.method.*; -import android.view.*; -import android.view.inputmethod.*; -import android.content.*; -import android.graphics.*; -import android.widget.*; -import android.hardware.*; -import android.location.*; -import android.graphics.drawable.*; -import android.content.res.*; - -import android.util.*; - -/* - * GeckoSurfaceView implements a GL surface view, - * similar to GLSurfaceView. However, since we - * already have a thread for Gecko, we don't really want - * a separate renderer thread that GLSurfaceView provides. - */ -class GeckoSurfaceView - extends SurfaceView - implements SurfaceHolder.Callback, SensorEventListener, LocationListener -{ - private static final String LOG_FILE_NAME = "GeckoSurfaceView"; - - public GeckoSurfaceView(Context context) { - super(context); - - getHolder().addCallback(this); - inputConnection = new GeckoInputConnection(this); - setFocusable(true); - setFocusableInTouchMode(true); - - DisplayMetrics metrics = new DisplayMetrics(); - GeckoApp.mAppContext.getWindowManager(). - getDefaultDisplay().getMetrics(metrics); - mWidth = metrics.widthPixels; - mHeight = metrics.heightPixels; - mBufferWidth = 0; - mBufferHeight = 0; - - mSurfaceLock = new ReentrantLock(); - - mEditableFactory = Editable.Factory.getInstance(); - initEditable(""); - mIMEState = IME_STATE_DISABLED; - mIMETypeHint = ""; - mIMEActionHint = ""; - } - - protected void finalize() throws Throwable { - super.finalize(); - } - - void drawSplashScreen() { - this.drawSplashScreen(getHolder(), mWidth, mHeight); - } - - void drawSplashScreen(SurfaceHolder holder, int width, int height) { - // No splash screen for Honeycomb or greater - if (Build.VERSION.SDK_INT >= 11) { - Log.i(LOG_FILE_NAME, "skipping splash screen"); - return; - } - - Canvas c = holder.lockCanvas(); - if (c == null) { - Log.i(LOG_FILE_NAME, "canvas is null"); - return; - } - - Resources res = getResources(); - - File watchDir = new File(GeckoApp.sGREDir, "components"); - if (watchDir.exists() == false) { - // Just show the simple splash screen for "new profile" startup - c.drawColor(res.getColor(R.color.splash_background)); - Drawable drawable = res.getDrawable(R.drawable.splash); - int w = drawable.getIntrinsicWidth(); - int h = drawable.getIntrinsicHeight(); - int x = (width - w) / 2; - int y = (height - h) / 2 - 16; - drawable.setBounds(x, y, x + w, y + h); - drawable.draw(c); - - Paint p = new Paint(); - p.setTextAlign(Paint.Align.CENTER); - p.setTextSize(32f); - p.setAntiAlias(true); - p.setColor(res.getColor(R.color.splash_msgfont)); - c.drawText(res.getString(R.string.splash_firstrun), width / 2, y + h + 16, p); - } else { - // Show the static UI for normal startup - DisplayMetrics metrics = new DisplayMetrics(); - GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics); - - // Default to DENSITY_HIGH sizes - int toolbarHeight = 80; - int faviconOffset = 25; - float urlHeight = 24f; - int urlOffsetX = 80; - int urlOffsetY = 48; - if (metrics.densityDpi == DisplayMetrics.DENSITY_MEDIUM) { - toolbarHeight = 53; - faviconOffset = 10; - urlHeight = 16f; - urlOffsetX = 53; - urlOffsetY = 32; - } - - c.drawColor(res.getColor(R.color.splash_content)); - Drawable toolbar = res.getDrawable(Build.VERSION.SDK_INT > 8 ? - R.drawable.splash_v9 : - R.drawable.splash_v8); - toolbar.setBounds(0, 0, width, toolbarHeight); - toolbar.draw(c); - - // XUL/CSS always uses 32px width and height for favicon - Drawable favicon = res.getDrawable(R.drawable.favicon32); - favicon.setBounds(faviconOffset, faviconOffset, 32 + faviconOffset, 32 + faviconOffset); - favicon.draw(c); - - if (GeckoSurfaceView.mSplashURL != "") { - TextPaint p = new TextPaint(); - p.setTextAlign(Paint.Align.LEFT); - p.setTextSize(urlHeight); - p.setAntiAlias(true); - p.setColor(res.getColor(R.color.splash_urlfont)); - String url = TextUtils.ellipsize(GeckoSurfaceView.mSplashURL, p, width - urlOffsetX * 2, TextUtils.TruncateAt.END).toString(); - c.drawText(url, urlOffsetX, urlOffsetY, p); - } - } - holder.unlockCanvasAndPost(c); - } - - /* - * Called on main thread - */ - - public void draw(SurfaceHolder holder, ByteBuffer buffer) { - if (buffer == null || buffer.capacity() != (mWidth * mHeight * 2)) - return; - - synchronized (mSoftwareBuffer) { - if (buffer != mSoftwareBuffer || mSoftwareBufferCopy == null) - return; - - Canvas c = holder.lockCanvas(); - if (c == null) - return; - mSoftwareBufferCopy.copyPixelsFromBuffer(buffer); - c.drawBitmap(mSoftwareBufferCopy, 0, 0, null); - holder.unlockCanvasAndPost(c); - } - } - - public void draw(SurfaceHolder holder, Bitmap bitmap) { - if (bitmap == null || - bitmap.getWidth() != mWidth || bitmap.getHeight() != mHeight) - return; - - synchronized (mSoftwareBitmap) { - if (bitmap != mSoftwareBitmap) - return; - - Canvas c = holder.lockCanvas(); - if (c == null) - return; - c.drawBitmap(bitmap, 0, 0, null); - holder.unlockCanvasAndPost(c); - } - } - - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - - // On pre-Honeycomb, force exactly one frame of the previous size - // to render because the surface change is only seen by GLES after we - // have swapped the back buffer (i.e. the buffer size only changes - // after the next swap buffer). We need to make sure Gecko's view - // resizes when Android's buffer resizes. - // In Honeycomb, the buffer size changes immediately, so rendering a - // frame of the previous size is unnecessary (and wrong). - if (mDrawMode == DRAW_GLES_2 && - (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)) { - // When we get a surfaceChange event, we have 0 to n paint events - // waiting in the Gecko event queue. We will make the first - // succeed and the abort the others. - mDrawSingleFrame = true; - if (!mInDrawing) { - // Queue at least one paint event in case none are queued. - GeckoAppShell.scheduleRedraw(); - } - GeckoAppShell.geckoEventSync(); - mDrawSingleFrame = false; - mAbortDraw = false; - } - - if (mShowingSplashScreen) - drawSplashScreen(holder, width, height); - - mSurfaceLock.lock(); - - if (mInDrawing) { - Log.w(LOG_FILE_NAME, "surfaceChanged while mInDrawing is true!"); - } - - boolean invalidSize; - - if (width == 0 || height == 0) { - mSoftwareBitmap = null; - mSoftwareBuffer = null; - mSoftwareBufferCopy = null; - invalidSize = true; - } else { - invalidSize = false; - } - - boolean doSyncDraw = - mDrawMode == DRAW_2D && - !invalidSize && - GeckoApp.checkLaunchState(GeckoApp.LaunchState.GeckoRunning); - mSyncDraw = doSyncDraw; - - mFormat = format; - mWidth = width; - mHeight = height; - mSurfaceValid = true; - - Log.i(LOG_FILE_NAME, "surfaceChanged: fmt: " + format + " dim: " + width + " " + height); - - try { - DisplayMetrics metrics = new DisplayMetrics(); - GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics); - - GeckoEvent e = new GeckoEvent(GeckoEvent.SIZE_CHANGED, width, height, - metrics.widthPixels, metrics.heightPixels); - GeckoAppShell.sendEventToGecko(e); - } finally { - mSurfaceLock.unlock(); - } - - if (doSyncDraw) { - GeckoAppShell.scheduleRedraw(); - - Object syncDrawObject = null; - try { - syncDrawObject = mSyncDraws.take(); - } catch (InterruptedException ie) { - Log.e(LOG_FILE_NAME, "Threw exception while getting sync draw bitmap/buffer: ", ie); - } - if (syncDrawObject != null) { - if (syncDrawObject instanceof Bitmap) - draw(holder, (Bitmap)syncDrawObject); - else - draw(holder, (ByteBuffer)syncDrawObject); - } else { - Log.e("GeckoSurfaceViewJava", "Synchronised draw object is null"); - } - } else if (!mShowingSplashScreen) { - // Make sure a frame is drawn before we return - // otherwise we see artifacts or a black screen - GeckoAppShell.scheduleRedraw(); - GeckoAppShell.geckoEventSync(); - } - } - - public void surfaceCreated(SurfaceHolder holder) { - Log.i(LOG_FILE_NAME, "surface created"); - GeckoEvent e = new GeckoEvent(GeckoEvent.SURFACE_CREATED); - GeckoAppShell.sendEventToGecko(e); - if (mShowingSplashScreen) - drawSplashScreen(); - } - - public void surfaceDestroyed(SurfaceHolder holder) { - Log.i(LOG_FILE_NAME, "surface destroyed"); - mSurfaceValid = false; - mSoftwareBuffer = null; - mSoftwareBufferCopy = null; - mSoftwareBitmap = null; - GeckoEvent e = new GeckoEvent(GeckoEvent.SURFACE_DESTROYED); - if (mDrawMode == DRAW_GLES_2) { - // Ensure GL cleanup occurs before we return. - GeckoAppShell.sendEventToGeckoSync(e); - } else { - GeckoAppShell.sendEventToGecko(e); - } - } - - public Bitmap getSoftwareDrawBitmap() { - if (mSoftwareBitmap == null || - mSoftwareBitmap.getHeight() != mHeight || - mSoftwareBitmap.getWidth() != mWidth) { - mSoftwareBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.RGB_565); - } - - mDrawMode = DRAW_2D; - return mSoftwareBitmap; - } - - public ByteBuffer getSoftwareDrawBuffer() { - // We store pixels in 565 format, so two bytes per pixel (explaining - // the * 2 in the following check/allocation) - if (mSoftwareBuffer == null || - mSoftwareBuffer.capacity() != (mWidth * mHeight * 2)) { - mSoftwareBuffer = ByteBuffer.allocateDirect(mWidth * mHeight * 2); - } - - if (mSoftwareBufferCopy == null || - mSoftwareBufferCopy.getHeight() != mHeight || - mSoftwareBufferCopy.getWidth() != mWidth) { - mSoftwareBufferCopy = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.RGB_565); - } - - mDrawMode = DRAW_2D; - return mSoftwareBuffer; - } - - public Surface getSurface() { - return getHolder().getSurface(); - } - - /* - * Called on Gecko thread - */ - - public static final int DRAW_ERROR = 0; - public static final int DRAW_GLES_2 = 1; - public static final int DRAW_2D = 2; - // Drawing is disable when the surface buffer - // has changed size but we haven't yet processed the - // resize event. - public static final int DRAW_DISABLED = 3; - - public int beginDrawing() { - if (mInDrawing) { - Log.e(LOG_FILE_NAME, "Recursive beginDrawing call!"); - return DRAW_ERROR; - } - - // Once we drawn our first frame after resize we can ignore - // the other draw events until we handle the resize events. - if (mAbortDraw) { - return DRAW_DISABLED; - } - - /* Grab the lock, which we'll hold while we're drawing. - * It gets released in endDrawing(), and is also used in surfaceChanged - * to make sure that we don't change our surface details while - * we're in the middle of drawing (and especially in the middle of - * executing beginDrawing/endDrawing). - * - * We might not need to hold this lock in between - * beginDrawing/endDrawing, and might just be able to make - * surfaceChanged, beginDrawing, and endDrawing synchronized, - * but this way is safer for now. - */ - mSurfaceLock.lock(); - - if (!mSurfaceValid) { - Log.e(LOG_FILE_NAME, "Surface not valid"); - mSurfaceLock.unlock(); - return DRAW_ERROR; - } - - mInDrawing = true; - mDrawMode = DRAW_GLES_2; - return DRAW_GLES_2; - } - - public void endDrawing() { - if (!mInDrawing) { - Log.e(LOG_FILE_NAME, "endDrawing without beginDrawing!"); - return; - } - - if (mDrawSingleFrame) - mAbortDraw = true; - - try { - if (!mSurfaceValid) { - Log.e(LOG_FILE_NAME, "endDrawing with false mSurfaceValid"); - return; - } - } finally { - mInDrawing = false; - - if (!mSurfaceLock.isHeldByCurrentThread()) - Log.e(LOG_FILE_NAME, "endDrawing while mSurfaceLock not held by current thread!"); - - mSurfaceLock.unlock(); - } - } - - /* How this works: - * Whenever we want to draw, we want to be sure that we do not lock - * the canvas unless we're sure we can draw. Locking the canvas clears - * the canvas to black in most cases, causing a black flash. - * At the same time, the surface can resize/disappear at any moment - * unless the canvas is locked. - * Draws originate from a different thread so the surface could change - * at any moment while we try to draw until we lock the canvas. - * - * Also, never try to lock the canvas while holding the surface lock - * unless you're in SurfaceChanged, in which case the canvas was already - * locked. Surface lock -> Canvas lock will lead to AB-BA deadlocks. - */ - public void draw2D(Bitmap bitmap, int width, int height) { - // mSurfaceLock ensures that we get mSyncDraw/mSoftwareBitmap/etc. - // set correctly before determining whether we should do a sync draw - mSurfaceLock.lock(); - try { - if (mSyncDraw) { - if (bitmap != mSoftwareBitmap || width != mWidth || height != mHeight) - return; - mSyncDraw = false; - try { - mSyncDraws.put(bitmap); - } catch (InterruptedException ie) { - Log.e(LOG_FILE_NAME, "Threw exception while getting sync draws queue: ", ie); - } - return; - } - } finally { - mSurfaceLock.unlock(); - } - - draw(getHolder(), bitmap); - } - - public void draw2D(ByteBuffer buffer, int stride) { - mSurfaceLock.lock(); - try { - if (mSyncDraw) { - if (buffer != mSoftwareBuffer || stride != (mWidth * 2)) - return; - mSyncDraw = false; - try { - mSyncDraws.put(buffer); - } catch (InterruptedException ie) { - Log.e(LOG_FILE_NAME, "Threw exception while getting sync bitmaps queue: ", ie); - } - return; - } - } finally { - mSurfaceLock.unlock(); - } - - draw(getHolder(), buffer); - } - - @Override - public boolean onCheckIsTextEditor () { - return false; - } - - @Override - public InputConnection onCreateInputConnection(EditorInfo outAttrs) { - outAttrs.inputType = InputType.TYPE_CLASS_TEXT; - outAttrs.imeOptions = EditorInfo.IME_ACTION_NONE; - outAttrs.actionLabel = null; - mKeyListener = TextKeyListener.getInstance(); - - if (mIMEState == IME_STATE_PASSWORD) - outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_PASSWORD; - 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; - else if (mIMETypeHint.equalsIgnoreCase("datetime") || - mIMETypeHint.equalsIgnoreCase("datetime-local")) - outAttrs.inputType = InputType.TYPE_CLASS_DATETIME | - InputType.TYPE_DATETIME_VARIATION_NORMAL; - else if (mIMETypeHint.equalsIgnoreCase("date")) - outAttrs.inputType = InputType.TYPE_CLASS_DATETIME | - InputType.TYPE_DATETIME_VARIATION_DATE; - else if (mIMETypeHint.equalsIgnoreCase("time")) - outAttrs.inputType = InputType.TYPE_CLASS_DATETIME | - InputType.TYPE_DATETIME_VARIATION_TIME; - - 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 != null && mIMEActionHint.length() != 0) - outAttrs.actionLabel = mIMEActionHint; - - if (mIMELandscapeFS == false) - outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI; - - inputConnection.reset(); - return inputConnection; - } - - public void setEditable(String contents) - { - mEditable.removeSpan(inputConnection); - mEditable.replace(0, mEditable.length(), contents); - mEditable.setSpan(inputConnection, 0, contents.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - Selection.setSelection(mEditable, contents.length()); - } - - public void initEditable(String contents) - { - mEditable = mEditableFactory.newEditable(contents); - mEditable.setSpan(inputConnection, 0, contents.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - Selection.setSelection(mEditable, contents.length()); - } - - // accelerometer - public void onAccuracyChanged(Sensor sensor, int accuracy) - { - } - - public void onSensorChanged(SensorEvent event) - { - GeckoAppShell.sendEventToGecko(new GeckoEvent(event)); - } - - private class GeocoderTask extends AsyncTask { - protected Void doInBackground(Location... location) { - try { - List
addresses = mGeocoder.getFromLocation(location[0].getLatitude(), - location[0].getLongitude(), 1); - // grab the first address. in the future, - // may want to expose multiple, or filter - // for best. - mLastGeoAddress = addresses.get(0); - GeckoAppShell.sendEventToGecko(new GeckoEvent(location[0], mLastGeoAddress)); - } catch (Exception e) { - Log.w(LOG_FILE_NAME, "GeocoderTask "+e); - } - return null; - } - } - - // geolocation - public void onLocationChanged(Location location) - { - if (mGeocoder == null) - mGeocoder = new Geocoder(getContext(), Locale.getDefault()); - - if (mLastGeoAddress == null) { - new GeocoderTask().execute(location); - } - else { - float[] results = new float[1]; - Location.distanceBetween(location.getLatitude(), - location.getLongitude(), - mLastGeoAddress.getLatitude(), - mLastGeoAddress.getLongitude(), - results); - // pfm value. don't want to slam the - // geocoder with very similar values, so - // only call after about 100m - if (results[0] > 100) - new GeocoderTask().execute(location); - } - - GeckoAppShell.sendEventToGecko(new GeckoEvent(location, mLastGeoAddress)); - } - - public void onProviderDisabled(String provider) - { - } - - public void onProviderEnabled(String provider) - { - } - - public void onStatusChanged(String provider, int status, Bundle extras) - { - } - - // event stuff - public boolean onTouchEvent(MotionEvent event) { - GeckoAppShell.sendEventToGecko(new GeckoEvent(event)); - return true; - } - - @Override - public boolean onKeyPreIme(int keyCode, KeyEvent event) { - if (event.isSystem()) - return super.onKeyPreIme(keyCode, event); - - switch (event.getAction()) { - case KeyEvent.ACTION_DOWN: - return processKeyDown(keyCode, event, true); - case KeyEvent.ACTION_UP: - return processKeyUp(keyCode, event, true); - case KeyEvent.ACTION_MULTIPLE: - return onKeyMultiple(keyCode, event.getRepeatCount(), event); - } - return super.onKeyPreIme(keyCode, event); - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - return processKeyDown(keyCode, event, false); - } - - private boolean processKeyDown(int keyCode, KeyEvent event, boolean isPreIme) { - switch (keyCode) { - case KeyEvent.KEYCODE_BACK: - if (event.getRepeatCount() == 0) { - event.startTracking(); - return true; - } else { - return false; - } - case KeyEvent.KEYCODE_MENU: - if (event.getRepeatCount() == 0) { - event.startTracking(); - break; - } else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) { - break; - } - // Ignore repeats for KEYCODE_MENU; they confuse the widget code. - return false; - case KeyEvent.KEYCODE_VOLUME_UP: - case KeyEvent.KEYCODE_VOLUME_DOWN: - case KeyEvent.KEYCODE_SEARCH: - return false; - case KeyEvent.KEYCODE_DEL: - // See comments in GeckoInputConnection.onKeyDel - if (inputConnection != null && - inputConnection.onKeyDel()) { - return true; - } - break; - case KeyEvent.KEYCODE_ENTER: - if ((event.getFlags() & KeyEvent.FLAG_EDITOR_ACTION) != 0 && - mIMEActionHint.equalsIgnoreCase("next")) - event = new KeyEvent(event.getAction(), KeyEvent.KEYCODE_TAB); - break; - default: - break; - } - - if (isPreIme && mIMEState != IME_STATE_DISABLED && - (event.getMetaState() & KeyEvent.META_ALT_ON) == 0) - // Let active IME process pre-IME key events - return false; - - // KeyListener returns true if it handled the event for us. - if (mIMEState == IME_STATE_DISABLED || - keyCode == KeyEvent.KEYCODE_ENTER || - keyCode == KeyEvent.KEYCODE_DEL || - (event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) != 0 || - !mKeyListener.onKeyDown(this, mEditable, keyCode, event)) - GeckoAppShell.sendEventToGecko(new GeckoEvent(event)); - return true; - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - return processKeyUp(keyCode, event, false); - } - - private boolean processKeyUp(int keyCode, KeyEvent event, boolean isPreIme) { - switch (keyCode) { - case KeyEvent.KEYCODE_BACK: - if (!event.isTracking() || event.isCanceled()) - return false; - break; - default: - break; - } - - if (isPreIme && mIMEState != IME_STATE_DISABLED && - (event.getMetaState() & KeyEvent.META_ALT_ON) == 0) - // Let active IME process pre-IME key events - return false; - - if (mIMEState == IME_STATE_DISABLED || - keyCode == KeyEvent.KEYCODE_ENTER || - keyCode == KeyEvent.KEYCODE_DEL || - (event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) != 0 || - !mKeyListener.onKeyUp(this, mEditable, keyCode, event)) - GeckoAppShell.sendEventToGecko(new GeckoEvent(event)); - return true; - } - - @Override - public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { - GeckoAppShell.sendEventToGecko(new GeckoEvent(event)); - return true; - } - - @Override - public boolean onKeyLongPress(int keyCode, KeyEvent event) { - switch (keyCode) { - case KeyEvent.KEYCODE_BACK: - GeckoAppShell.sendEventToGecko(new GeckoEvent(event)); - return true; - case KeyEvent.KEYCODE_MENU: - InputMethodManager imm = (InputMethodManager) - getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.toggleSoftInputFromWindow(getWindowToken(), - imm.SHOW_FORCED, 0); - return true; - default: - break; - } - return false; - } - - // Is this surface valid for drawing into? - boolean mSurfaceValid; - - // Are we actively between beginDrawing/endDrawing? - boolean mInDrawing; - - // Used to finish the current buffer before changing the surface size - boolean mDrawSingleFrame = false; - boolean mAbortDraw = false; - - // Are we waiting for a buffer to draw in surfaceChanged? - boolean mSyncDraw; - - // True if gecko requests a buffer - int mDrawMode; - - static boolean mShowingSplashScreen = true; - static String mSplashURL = ""; - - // let's not change stuff around while we're in the middle of - // starting drawing, ending drawing, or changing surface - // characteristics - ReentrantLock mSurfaceLock; - - // Surface format, from surfaceChanged. Largely - // useless. - int mFormat; - - // the dimensions of the surface - int mWidth; - int mHeight; - - // the dimensions of the buffer we're using for drawing, - // that is the software buffer or the EGLSurface - int mBufferWidth; - int mBufferHeight; - - // IME stuff - public static final int IME_STATE_DISABLED = 0; - public static final int IME_STATE_ENABLED = 1; - public static final int IME_STATE_PASSWORD = 2; - public static final int IME_STATE_PLUGIN = 3; - - GeckoInputConnection inputConnection; - KeyListener mKeyListener; - Editable mEditable; - Editable.Factory mEditableFactory; - int mIMEState; - String mIMETypeHint; - String mIMEActionHint; - boolean mIMELandscapeFS; - - // Software rendering - Bitmap mSoftwareBitmap; - ByteBuffer mSoftwareBuffer; - Bitmap mSoftwareBufferCopy; - - Geocoder mGeocoder; - Address mLastGeoAddress; - - final SynchronousQueue mSyncDraws = new SynchronousQueue(); -} - diff --git a/embedding/android/Makefile.in b/embedding/android/Makefile.in deleted file mode 100644 index 540adaab05cf..000000000000 --- a/embedding/android/Makefile.in +++ /dev/null @@ -1,220 +0,0 @@ -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is the Mozilla browser. -# -# The Initial Developer of the Original Code is -# Mozilla Foundation -# Portions created by the Initial Developer are Copyright (C) 2009-2010 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Vladimir Vukicevic -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk -include $(topsrcdir)/ipc/app/defs.mk - -DIRS = locales - -JAVAFILES = \ - GeckoApp.java \ - GeckoAppShell.java \ - GeckoConnectivityReceiver.java \ - GeckoEvent.java \ - GeckoSurfaceView.java \ - GeckoInputConnection.java \ - AlertNotification.java \ - SurfaceInfo.java \ - GeckoBatteryManager.java \ - GeckoSmsManager.java \ - $(NULL) - -PROCESSEDJAVAFILES = \ - App.java \ - Restarter.java \ - NotificationHandler.java \ - LauncherShortcuts.java \ - $(NULL) - - -ifneq (,$(findstring -march=armv7,$(OS_CFLAGS))) -MIN_CPU_VERSION=7 -else -MIN_CPU_VERSION=5 -endif - -ifeq (,$(ANDROID_VERSION_CODE)) -ANDROID_VERSION_CODE=$(shell $(PYTHON) $(topsrcdir)/toolkit/xre/make-platformini.py --print-buildid | cut -c1-10) -endif - -DEFINES += \ - -DANDROID_PACKAGE_NAME=$(ANDROID_PACKAGE_NAME) \ - -DMOZ_APP_DISPLAYNAME="$(MOZ_APP_DISPLAYNAME)" \ - -DMOZ_APP_NAME=$(MOZ_APP_NAME) \ - -DMOZ_APP_VERSION=$(MOZ_APP_VERSION) \ - -DMOZ_CHILD_PROCESS_NAME=$(MOZ_CHILD_PROCESS_NAME) \ - -DMOZ_MIN_CPU_VERSION=$(MIN_CPU_VERSION) \ - -DMOZ_CRASHREPORTER=$(MOZ_CRASHREPORTER) \ - -DANDROID_VERSION_CODE=$(ANDROID_VERSION_CODE) \ - -DMOZILLA_OFFICIAL=$(MOZILLA_OFFICIAL) \ - $(NULL) - -GARBAGE += \ - AndroidManifest.xml \ - classes.dex \ - $(PROCESSEDJAVAFILES) \ - gecko.ap_ \ - res/values/strings.xml \ - R.java \ - $(NULL) - -GARBAGE_DIRS += classes res - -# Bug 567884 - Need a way to find appropriate icons during packaging -ifeq ($(MOZ_APP_NAME),fennec) -ICON_PATH = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/fennec_48x48.png -ICON_PATH_HDPI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/fennec_72x72.png - -# we released these builds to the public with shared IDs and need to keep them -ifeq (org.mozilla.firefox,$(ANDROID_PACKAGE_NAME)) -DEFINES += -DMOZ_ANDROID_SHARED_ID="org.mozilla.firefox.sharedID" -else ifeq (org.mozilla.firefox_beta,$(ANDROID_PACKAGE_NAME)) -DEFINES += -DMOZ_ANDROID_SHARED_ID="org.mozilla.firefox.sharedID" -else ifeq (org.mozilla.fennec_aurora,$(ANDROID_PACKAGE_NAME)) -DEFINES += -DMOZ_ANDROID_SHARED_ID="org.mozilla.fennec.sharedID" -else ifeq (org.mozilla.fennec,$(ANDROID_PACKAGE_NAME)) -DEFINES += -DMOZ_ANDROID_SHARED_ID="org.mozilla.fennec.sharedID" -endif - -else -ICON_PATH = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/icon48.png -ICON_PATH_HDPI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/icon64.png -DEFINES += -DMOZ_ANDROID_SHARED_ID="$(ANDROID_PACKAGE_NAME).sharedID" -endif - -RES_LAYOUT = \ - res/layout/notification_progress.xml \ - res/layout/notification_progress_text.xml \ - res/layout/notification_icon_text.xml \ - res/layout/launch_app_list.xml \ - res/layout/launch_app_listitem.xml \ - $(NULL) - -RES_VALUES = res/values/colors.xml res/values/themes.xml - -AB_rCD = $(shell echo $(AB_CD) | sed -e s/-/-r/) - -JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar - -DEFAULT_BRANDPATH = $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales/en-US/brand.dtd -DEFAULT_STRINGSPATH = locales/en-US/android_strings.dtd -LOCALIZED_BRANDPATH = $(DEPTH)/dist/bin/chrome/$(AB_CD)/locale/branding/brand.dtd -LOCALIZED_STRINGSPATH = $(DEPTH)/dist/bin/chrome/android-res/res/values-$(AB_CD)/android_strings.dtd - -ifdef MOZ_CRASHREPORTER -PROCESSEDJAVAFILES += CrashReporter.java -MOZ_ANDROID_DRAWABLES += embedding/android/resources/drawable/crash_reporter.png -RES_LAYOUT += res/layout/crash_reporter.xml -endif - -MOZ_ANDROID_DRAWABLES += embedding/android/resources/drawable/desktop_notification.png - -MOZ_ANDROID_DRAWABLES += $(shell if test -e $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources.mn; then cat $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources.mn | tr '\n' ' '; fi) - -include $(topsrcdir)/config/rules.mk - -ifneq ($(AB_CD),en-US) -LOCALIZED_STRINGS_XML = res/values-$(AB_rCD)/strings.xml -endif - -# Override the Java settings with some specific android settings -include $(topsrcdir)/config/android-common.mk - -# Note that we're going to set up a dependency directly between embed_android.dex and the java files -# Instead of on the .class files, since more than one .class file might be produced per .java file -classes.dex: $(JAVAFILES) $(PROCESSEDJAVAFILES) R.java - $(NSINSTALL) -D classes - $(JAVAC) $(JAVAC_FLAGS) -d classes $(addprefix $(srcdir)/,$(JAVAFILES)) $(PROCESSEDJAVAFILES) R.java - $(DX) --dex --output=$@ classes - -AndroidManifest.xml $(PROCESSEDJAVAFILES): % : %.in - $(PYTHON) $(topsrcdir)/config/Preprocessor.py \ - $(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $< > $@ - -res/drawable/icon.png: $(MOZ_APP_ICON) - $(NSINSTALL) -D res/drawable - cp $(ICON_PATH) $@ - -res/drawable-hdpi/icon.png: $(MOZ_APP_ICON) - $(NSINSTALL) -D res/drawable-hdpi - cp $(ICON_PATH_HDPI) $@ - -RES_DRAWABLE = $(addprefix res/drawable/,$(notdir $(MOZ_ANDROID_DRAWABLES))) - -$(RES_DRAWABLE): $(addprefix $(topsrcdir)/,$(MOZ_ANDROID_DRAWABLES)) - $(NSINSTALL) -D res/drawable - $(NSINSTALL) $^ res/drawable/ - -$(RES_LAYOUT): $(subst res/,$(srcdir)/resources/,$(RES_LAYOUT)) - $(NSINSTALL) -D res/layout - $(NSINSTALL) $(srcdir)/resources/layout/* res/layout/ - -$(RES_VALUES): $(subst res/,$(srcdir)/resources/,$(RES_VALUES)) - $(NSINSTALL) -D res/values - $(NSINSTALL) $(srcdir)/resources/values/* res/values/ - -R.java: $(MOZ_APP_ICON) $(RES_LAYOUT) $(RES_DRAWABLE) $(RES_VALUES) res/drawable/icon.png res/drawable-hdpi/icon.png res/values/strings.xml AndroidManifest.xml - $(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -J . --custom-package org.mozilla.gecko - -gecko.ap_: AndroidManifest.xml res/drawable/icon.png res/drawable-hdpi/icon.png $(RES_LAYOUT) $(RES_DRAWABLE) $(RES_VALUES) res/values/strings.xml FORCE - $(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -F $@ - -res/values/strings.xml: $(DEFAULT_BRANDPATH) $(DEFAULT_STRINGSPATH) - mkdir -p res/values - $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) \ - -DBRANDPATH="$(DEFAULT_BRANDPATH)" \ - -DSTRINGSPATH="$(DEFAULT_STRINGSPATH)" \ - $(srcdir)/strings.xml.in \ - > $@ - -res/values-$(AB_rCD)/strings.xml: $(LOCALIZED_BRANDPATH) $(LOCALIZED_STRINGSPATH) - mkdir -p res/values-$(AB_rCD) - $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) \ - -DBRANDPATH="$(call core_abspath,$(LOCALIZED_BRANDPATH))" \ - -DSTRINGSPATH="$(call core_abspath,$(LOCALIZED_STRINGSPATH))" \ - $(srcdir)/strings.xml.in \ - > $@ - -chrome:: $(LOCALIZED_STRINGS_XML) - -libs:: classes.dex - $(INSTALL) classes.dex $(FINAL_TARGET) diff --git a/embedding/android/SurfaceInfo.java b/embedding/android/SurfaceInfo.java deleted file mode 100644 index f823926a2821..000000000000 --- a/embedding/android/SurfaceInfo.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.mozilla.gecko; - -public class SurfaceInfo { - public int format; - public int width; - public int height; -} diff --git a/embedding/android/locales/en-US/android_strings.dtd b/embedding/android/locales/en-US/android_strings.dtd deleted file mode 100644 index 854f67d826a0..000000000000 --- a/embedding/android/locales/en-US/android_strings.dtd +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/embedding/android/resources/layout/crash_reporter.xml b/embedding/android/resources/layout/crash_reporter.xml deleted file mode 100644 index f9178668536c..000000000000 --- a/embedding/android/resources/layout/crash_reporter.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - + + + + + + + + diff --git a/mobile/android/chrome/content/browser.css b/mobile/android/chrome/content/browser.css new file mode 100644 index 000000000000..cdc040c0e30a --- /dev/null +++ b/mobile/android/chrome/content/browser.css @@ -0,0 +1,110 @@ +settings { + -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#settings"); +} + +setting[type="bool"] { + -moz-binding: url("chrome://browser/content/bindings.xml#setting-fulltoggle-bool"); +} + +setting[type="bool"][localized="true"] { + -moz-binding: url("chrome://browser/content/bindings.xml#setting-fulltoggle-localized-bool"); +} + +setting[type="boolint"] { + -moz-binding: url("chrome://browser/content/bindings.xml#setting-fulltoggle-boolint"); +} + +setting[type="integer"] { + -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-integer"); +} + +setting[type="control"] { + -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-control"); +} + +setting[type="string"] { + -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-string"); +} + +setting[type="color"] { + -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-color"); +} + +setting[type="file"], +setting[type="directory"] { + -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-path"); +} + +setting[type="radio"], +setting[type="menulist"] { + -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-multi"); +} + +radio { + -moz-binding: url("chrome://global/content/bindings/radio.xml#radio"); +} + +checkbox { + -moz-binding: url("chrome://browser/content/bindings/checkbox.xml#checkbox-radio"); +} + +menulist { + -moz-binding: url("chrome://browser/content/bindings.xml#menulist"); +} + +.chrome-select-option { + -moz-binding: url("chrome://browser/content/bindings.xml#chrome-select-option"); +} + +/* richlist defaults ------------------------------------------------------- */ +richlistbox[batch] { + -moz-binding: url("chrome://browser/content/bindings.xml#richlistbox-batch"); +} + +richlistitem { + -moz-binding: url("chrome://browser/content/bindings.xml#richlistitem"); +} + +richlistitem[type="error"], +richlistitem[type="warning"] { + -moz-binding: url("chrome://browser/content/bindings/console.xml#error"); +} + +richlistitem[type="message"]{ + -moz-binding: url("chrome://browser/content/bindings/console.xml#message"); +} + +dialog { + -moz-binding: url("chrome://browser/content/bindings/dialog.xml#dialog"); +} + +/* Do not allow these to inherit from the toolkit binding */ +dialog.content-dialog { + -moz-binding: none; +} + +/* Disable context menus in textboxes */ +.textbox-input-box, +.textbox-input-box[spellcheck="true"] { + -moz-binding: url("chrome://browser/content/bindings.xml#input-box"); +} + +textbox { + -moz-binding: url("chrome://browser/content/bindings.xml#textbox"); +} + +textbox[multiline="true"] { + -moz-binding: url("chrome://browser/content/bindings.xml#textarea"); +} + +textbox[type="timed"] { + -moz-binding: url("chrome://browser/content/bindings.xml#timed-textbox"); +} + +textbox[type="search"] { + -moz-binding: url("chrome://browser/content/bindings.xml#search-textbox"); +} + +textbox[type="number"] { + -moz-binding: url("chrome://browser/content/bindings.xml#numberbox"); +} diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js new file mode 100644 index 000000000000..7979a70e3a0c --- /dev/null +++ b/mobile/android/chrome/content/browser.js @@ -0,0 +1,2670 @@ +// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Mobile Browser. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +"use strict"; + +let Cc = Components.classes; +let Ci = Components.interfaces; +let Cu = Components.utils; +let Cr = Components.results; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm") +Cu.import("resource://gre/modules/AddonManager.jsm"); + +XPCOMUtils.defineLazyServiceGetter(this, "URIFixup", + "@mozilla.org/docshell/urifixup;1", "nsIURIFixup"); +XPCOMUtils.defineLazyServiceGetter(this, "DOMUtils", + "@mozilla.org/inspector/dom-utils;1", "inIDOMUtils"); +const kStateActive = 0x00000001; // :active pseudoclass for elements + +// TODO: Take into account ppi in these units? + +// The ratio of velocity that is retained every ms. +const kPanDeceleration = 0.999; + +// The number of ms to consider events over for a swipe gesture. +const kSwipeLength = 500; + +// The number of pixels to move before we consider a drag to be more than +// just a click with jitter. +const kDragThreshold = 10; + +// The number of pixels to move to break out of axis-lock +const kLockBreakThreshold = 100; + +// Minimum speed to move during kinetic panning. 0.015 pixels/ms is roughly +// equivalent to a pixel every 4 frames at 60fps. +const kMinKineticSpeed = 0.015; + +// Maximum kinetic panning speed. 9 pixels/ms is equivalent to 150 pixels per +// frame at 60fps. +const kMaxKineticSpeed = 9; + +// The maximum magnitude of disparity allowed between axes acceleration. If +// it's larger than this, lock the slow-moving axis. +const kAxisLockRatio = 5; + +// The element tag names that are considered to receive input. Mouse-down +// events directed to one of these are allowed to go through. +const kElementsReceivingInput = { + applet: true, + audio: true, + button: true, + embed: true, + input: true, + map: true, + select: true, + textarea: true, + video: true +}; + +const UA_MODE_MOBILE = "mobile"; +const UA_MODE_DESKTOP = "desktop"; + +function dump(a) { + Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).logStringMessage(a); +} + +function getBridge() { + return Cc["@mozilla.org/android/bridge;1"].getService(Ci.nsIAndroidBridge); +} + +function sendMessageToJava(aMessage) { + return getBridge().handleGeckoMessage(JSON.stringify(aMessage)); +} + +#ifdef MOZ_CRASHREPORTER +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter", + "@mozilla.org/xre/app-info;1", "nsICrashReporter"); +#endif + +function resolveGeckoURI(aURI) { + if (aURI.indexOf("chrome://") == 0) { + let registry = Cc['@mozilla.org/chrome/chrome-registry;1'].getService(Ci["nsIChromeRegistry"]); + return registry.convertChromeURL(Services.io.newURI(aURI, null, null)).spec; + } else if (aURI.indexOf("resource://") == 0) { + let handler = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler); + return handler.resolveURI(Services.io.newURI(aURI, null, null)); + } + return aURI; +} + +/** + * Cache of commonly used string bundles. + */ +var Strings = {}; +[ + ["brand", "chrome://branding/locale/brand.properties"], + ["browser", "chrome://browser/locale/browser.properties"] +].forEach(function (aStringBundle) { + let [name, bundle] = aStringBundle; + XPCOMUtils.defineLazyGetter(Strings, name, function() { + return Services.strings.createBundle(bundle); + }); +}); + +var BrowserApp = { + _tabs: [], + _selectedTab: null, + + deck: null, + + startup: function startup() { + window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = new nsBrowserAccess(); + dump("zerdatime " + Date.now() + " - browser chrome startup finished."); + + this.deck = document.getElementById("browsers"); + BrowserEventHandler.init(); + ViewportHandler.init(); + + getBridge().setDrawMetadataProvider(this.getDrawMetadata.bind(this)); + + Services.obs.addObserver(this, "Tab:Add", false); + Services.obs.addObserver(this, "Tab:Load", false); + Services.obs.addObserver(this, "Tab:Select", false); + Services.obs.addObserver(this, "Tab:Close", false); + Services.obs.addObserver(this, "Session:Back", false); + Services.obs.addObserver(this, "Session:Forward", false); + Services.obs.addObserver(this, "Session:Reload", false); + Services.obs.addObserver(this, "Session:Stop", false); + Services.obs.addObserver(this, "SaveAs:PDF", false); + Services.obs.addObserver(this, "Browser:Quit", false); + Services.obs.addObserver(this, "Preferences:Get", false); + Services.obs.addObserver(this, "Preferences:Set", false); + Services.obs.addObserver(this, "ScrollTo:FocusedInput", false); + Services.obs.addObserver(this, "Sanitize:ClearAll", false); + Services.obs.addObserver(this, "PanZoom:PanZoom", false); + Services.obs.addObserver(this, "FullScreen:Exit", false); + Services.obs.addObserver(this, "Viewport:Change", false); + Services.obs.addObserver(this, "AgentMode:Change", false); + + function showFullScreenWarning() { + NativeWindow.toast.show(Strings.browser.GetStringFromName("alertFullScreenToast"), "short"); + } + + window.addEventListener("fullscreen", function() { + sendMessageToJava({ + gecko: { + type: window.fullScreen ? "ToggleChrome:Show" : "ToggleChrome:Hide" + } + }); + + if (!window.fullScreen) + showFullScreenWarning(); + }, false); + + // When a restricted key is pressed in DOM full-screen mode, we should display + // the "Press ESC to exit" warning message. + window.addEventListener("MozShowFullScreenWarning", showFullScreenWarning, true); + + NativeWindow.init(); + Downloads.init(); + OfflineApps.init(); + IndexedDB.init(); + XPInstallObserver.init(); + ConsoleAPI.init(); + + // Init LoginManager + Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager); + + let uri = "about:home"; + if ("arguments" in window && window.arguments[0]) + uri = window.arguments[0]; + + // XXX maybe we don't do this if the launch was kicked off from external + Services.io.offline = false; + let newTab = this.addTab(uri); + newTab.active = true; + + // Broadcast a UIReady message so add-ons know we are finished with startup + let event = document.createEvent("Events"); + event.initEvent("UIReady", true, false); + window.dispatchEvent(event); + + // notify java that gecko has loaded + sendMessageToJava({ + gecko: { + type: "Gecko:Ready" + } + }); + + let telemetryPrompted = false; + try { + telemetryPrompted = Services.prefs.getBoolPref("toolkit.telemetry.prompted"); + } catch (e) { + // optional + } + + if (!telemetryPrompted) { + let buttons = [ + { + label: Strings.browser.GetStringFromName("telemetry.optin.yes"), + callback: function () { + Services.prefs.setBoolPref("toolkit.telemetry.prompted", true); + Services.prefs.setBoolPref("toolkit.telemetry.enabled", true); + } + }, + { + label: Strings.browser.GetStringFromName("telemetry.optin.no"), + callback: function () { + Services.prefs.setBoolPref("toolkit.telemetry.prompted", true); + Services.prefs.setBoolPref("toolkit.telemetry.enabled", false); + } + } + ]; + let brandShortName = Strings.brand.GetStringFromName("brandShortName"); + let message = Strings.browser.formatStringFromName("telemetry.optin.message", [brandShortName], 1); + NativeWindow.doorhanger.show(message, "telemetry-optin", buttons); + } + }, + + shutdown: function shutdown() { + NativeWindow.uninit(); + OfflineApps.uninit(); + IndexedDB.uninit(); + ViewportHandler.uninit(); + XPInstallObserver.uninit(); + ConsoleAPI.uninit(); + }, + + get tabs() { + return this._tabs; + }, + + get selectedTab() { + return this._selectedTab; + }, + + set selectedTab(aTab) { + this._selectedTab = aTab; + if (!aTab) + return; + + aTab.updateViewport(false); + this.deck.selectedPanel = aTab.vbox; + }, + + get selectedBrowser() { + if (this._selectedTab) + return this._selectedTab.browser; + return null; + }, + + getTabForId: function getTabForId(aId) { + let tabs = this._tabs; + for (let i=0; i < tabs.length; i++) { + if (tabs[i].id == aId) + return tabs[i]; + } + return null; + }, + + getTabForBrowser: function getTabForBrowser(aBrowser) { + let tabs = this._tabs; + for (let i = 0; i < tabs.length; i++) { + if (tabs[i].browser == aBrowser) + return tabs[i]; + } + return null; + }, + + getBrowserForWindow: function getBrowserForWindow(aWindow) { + let tabs = this._tabs; + for (let i = 0; i < tabs.length; i++) { + if (tabs[i].browser.contentWindow == aWindow) + return tabs[i].browser; + } + return null; + }, + + getBrowserForDocument: function getBrowserForDocument(aDocument) { + let tabs = this._tabs; + for (let i = 0; i < tabs.length; i++) { + if (tabs[i].browser.contentDocument == aDocument) + return tabs[i].browser; + } + return null; + }, + + loadURI: function loadURI(aURI, aParams) { + let browser = this.selectedBrowser; + if (!browser) + return; + + let flags = aParams.flags || Ci.nsIWebNavigation.LOAD_FLAGS_NONE; + let postData = ("postData" in aParams && aParams.postData) ? aParams.postData.value : null; + let referrerURI = "referrerURI" in aParams ? aParams.referrerURI : null; + let charset = "charset" in aParams ? aParams.charset : null; + browser.loadURIWithFlags(aURI, flags, referrerURI, charset, postData); + }, + + addTab: function addTab(aURI, aParams) { + let newTab = new Tab(aURI, aParams); + this._tabs.push(newTab); + return newTab; + }, + + closeTab: function closeTab(aTab) { + if (aTab == this.selectedTab) + this.selectedTab = null; + + aTab.destroy(); + this._tabs.splice(this._tabs.indexOf(aTab), 1); + }, + + selectTab: function selectTab(aTab) { + if (aTab != null) { + this.selectedTab = aTab; + aTab.active = true; + let message = { + gecko: { + type: "Tab:Selected", + tabID: aTab.id + } + }; + + sendMessageToJava(message); + } + }, + + quit: function quit() { + Cu.reportError("got quit quit message"); + window.QueryInterface(Ci.nsIDOMChromeWindow).minimize(); + window.close(); + }, + + saveAsPDF: function saveAsPDF(aBrowser) { + // Create the final destination file location + let ContentAreaUtils = {}; + Services.scriptloader.loadSubScript("chrome://global/content/contentAreaUtils.js", ContentAreaUtils); + let fileName = ContentAreaUtils.getDefaultFileName(aBrowser.contentTitle, aBrowser.documentURI, null, null); + fileName = fileName.trim() + ".pdf"; + + let dm = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager); + let downloadsDir = dm.defaultDownloadsDirectory; + + let file = downloadsDir.clone(); + file.append(fileName); + file.createUnique(file.NORMAL_FILE_TYPE, parseInt("666", 8)); + + let printSettings = Cc["@mozilla.org/gfx/printsettings-service;1"].getService(Ci.nsIPrintSettingsService).newPrintSettings; + printSettings.printSilent = true; + printSettings.showPrintProgress = false; + printSettings.printBGImages = true; + printSettings.printBGColors = true; + printSettings.printToFile = true; + printSettings.toFileName = file.path; + printSettings.printFrameType = Ci.nsIPrintSettings.kFramesAsIs; + printSettings.outputFormat = Ci.nsIPrintSettings.kOutputFormatPDF; + + //XXX we probably need a preference here, the header can be useful + printSettings.footerStrCenter = ""; + printSettings.footerStrLeft = ""; + printSettings.footerStrRight = ""; + printSettings.headerStrCenter = ""; + printSettings.headerStrLeft = ""; + printSettings.headerStrRight = ""; + + let webBrowserPrint = content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebBrowserPrint); + + let cancelable = { + cancel: function (aReason) { + webBrowserPrint.cancel(); + } + } + let download = dm.addDownload(Ci.nsIDownloadManager.DOWNLOAD_TYPE_DOWNLOAD, + aBrowser.currentURI, + Services.io.newFileURI(file), "", null, + Date.now() * 1000, null, cancelable); + + webBrowserPrint.print(printSettings, download); + }, + + getPreferences: function getPreferences(aPrefNames) { + try { + let json = JSON.parse(aPrefNames); + let prefs = []; + + for each (let prefName in json) { + let pref = { + name: prefName + }; + + try { + switch (Services.prefs.getPrefType(prefName)) { + case Ci.nsIPrefBranch.PREF_BOOL: + pref.type = "bool"; + pref.value = Services.prefs.getBoolPref(prefName); + break; + case Ci.nsIPrefBranch.PREF_INT: + pref.type = "int"; + pref.value = Services.prefs.getIntPref(prefName); + break; + case Ci.nsIPrefBranch.PREF_STRING: + default: + pref.type = "string"; + pref.value = Services.prefs.getComplexValue(prefName, Ci.nsISupportsString).data; + break; + } + } catch (e) { + // preference does not exist; do not send it + continue; + } + + // some preferences use integers or strings instead of booleans for + // indicating enabled/disabled. since the java ui uses the type to + // determine which ui elements to show, we need to normalize these + // preferences to be actual booleans. + switch (prefName) { + case "network.cookie.cookieBehavior": + pref.type = "bool"; + pref.value = pref.value == 0; + break; + case "permissions.default.image": + pref.type = "bool"; + pref.value = pref.value == 1; + break; + case "browser.menu.showCharacterEncoding": + pref.type = "bool"; + pref.value = pref.value == "true"; + break; + } + + prefs.push(pref); + } + + sendMessageToJava({ + gecko: { + type: "Preferences:Data", + preferences: prefs + } + }); + } catch (e) {} + }, + + setPreferences: function setPreferences(aPref) { + let json = JSON.parse(aPref); + + // when sending to java, we normalized special preferences that use + // integers and strings to represent booleans. here, we convert them back + // to their actual types so we can store them. + switch (json.name) { + case "network.cookie.cookieBehavior": + json.type = "int"; + json.value = (json.value ? 0 : 2); + break; + case "permissions.default.image": + json.type = "int"; + json.value = (json.value ? 1 : 2); + break; + case "browser.menu.showCharacterEncoding": + json.type = "string"; + json.value = (json.value ? "true" : "false"); + break; + } + + if (json.type == "bool") + Services.prefs.setBoolPref(json.name, json.value); + else if (json.type == "int") + Services.prefs.setIntPref(json.name, json.value); + else { + let pref = Cc["@mozilla.org/pref-localizedstring;1"].createInstance(Ci.nsIPrefLocalizedString); + pref.data = json.value; + Services.prefs.setComplexValue(json.name, Ci.nsISupportsString, pref); + } + }, + + scrollToFocusedInput: function(aBrowser) { + let doc = aBrowser.contentDocument; + if (!doc) + return; + let focused = doc.activeElement; + if ((focused instanceof HTMLInputElement && focused.mozIsTextField(false)) || (focused instanceof HTMLTextAreaElement)) + focused.scrollIntoView(false); + }, + + getDrawMetadata: function getDrawMetadata() { + return JSON.stringify(this.selectedTab.viewport); + }, + + observe: function(aSubject, aTopic, aData) { + let browser = this.selectedBrowser; + if (!browser) + return; + + if (aTopic == "Session:Back") { + browser.goBack(); + } else if (aTopic == "Session:Forward") { + browser.goForward(); + } else if (aTopic == "Session:Reload") { + browser.reload(); + } else if (aTopic == "Session:Stop") { + browser.stop(); + } else if (aTopic == "Tab:Add") { + let uri = URIFixup.createFixupURI(aData, Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP); + let newTab = this.addTab(uri ? uri.spec : aData); + newTab.active = true; + } else if (aTopic == "Tab:Load") { + let uri = URIFixup.createFixupURI(aData, Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP); + browser.loadURI(uri ? uri.spec : aData); + } else if (aTopic == "Tab:Select") { + this.selectTab(this.getTabForId(parseInt(aData))); + } else if (aTopic == "Tab:Close") { + this.closeTab(this.getTabForId(parseInt(aData))); + } else if (aTopic == "Browser:Quit") { + this.quit(); + } else if (aTopic == "SaveAs:PDF") { + this.saveAsPDF(browser); + } else if (aTopic == "Preferences:Get") { + this.getPreferences(aData); + } else if (aTopic == "Preferences:Set") { + this.setPreferences(aData); + } else if (aTopic == "AgentMode:Change") { + let args = JSON.parse(aData); + let tab = this.getTabForId(args.tabId); + tab.setAgentMode(args.agent); + tab.browser.reload(); + } else if (aTopic == "ScrollTo:FocusedInput") { + this.scrollToFocusedInput(browser); + } else if (aTopic == "Sanitize:ClearAll") { + Sanitizer.sanitize(); + } else if (aTopic == "FullScreen:Exit") { + browser.contentDocument.mozCancelFullScreen(); + } else if (aTopic == "Viewport:Change") { + this.selectedTab.viewport = JSON.parse(aData); + ViewportHandler.onResize(); + } + }, + + get defaultBrowserWidth() { + delete this.defaultBrowserWidth; + let width = Services.prefs.getIntPref("browser.viewport.desktopWidth"); + return this.defaultBrowserWidth = width; + } +}; + +var NativeWindow = { + init: function() { + Services.obs.addObserver(this, "Menu:Clicked", false); + Services.obs.addObserver(this, "Doorhanger:Reply", false); + this.contextmenus.init(); + }, + + uninit: function() { + Services.obs.removeObserver(this, "Menu:Clicked"); + Services.obs.removeObserver(this, "Doorhanger:Reply"); + this.contextmenus.uninit(); + }, + + toast: { + show: function(aMessage, aDuration) { + sendMessageToJava({ + gecko: { + type: "Toast:Show", + message: aMessage, + duration: aDuration + } + }); + } + }, + + menu: { + _callbacks: [], + _menuId: 0, + add: function(aName, aIcon, aCallback) { + sendMessageToJava({ + gecko: { + type: "Menu:Add", + name: aName, + icon: aIcon, + id: this._menuId + } + }); + this._callbacks[this._menuId] = aCallback; + this._menuId++; + return this._menuId - 1; + }, + + remove: function(aId) { + sendMessageToJava({ gecko: {type: "Menu:Remove", id: aId }}); + } + }, + + doorhanger: { + _callbacks: {}, + _callbacksId: 0, + _promptId: 0, + + /** + * @param aOptions + * An options JavaScript object holding additional properties for the + * notification. The following properties are currently supported: + * persistence: An integer. The notification will not automatically + * dismiss for this many page loads. If persistence is set + * to -1, the doorhanger will never automatically dismiss. + * timeout: A time in milliseconds. The notification will not + * automatically dismiss before this time. + */ + show: function(aMessage, aValue, aButtons, aTabID, aOptions) { + aButtons.forEach((function(aButton) { + this._callbacks[this._callbacksId] = { cb: aButton.callback, prompt: this._promptId }; + aButton.callback = this._callbacksId; + this._callbacksId++; + }).bind(this)); + + this._promptId++; + let json = { + gecko: { + type: "Doorhanger:Add", + message: aMessage, + value: aValue, + buttons: aButtons, + // use the current tab if none is provided + tabID: aTabID || BrowserApp.selectedTab.id, + options: aOptions || {} + } + }; + sendMessageToJava(json); + }, + + hide: function(aValue, aTabID) { + sendMessageToJava({ + type: "Doorhanger:Remove", + value: aValue, + tabID: aTabID + }); + } + }, + + observe: function(aSubject, aTopic, aData) { + if (aTopic == "Menu:Clicked") { + if (this.menu._callbacks[aData]) + this.menu._callbacks[aData](); + } else if (aTopic == "Doorhanger:Reply") { + let reply_id = aData; + if (this.doorhanger._callbacks[reply_id]) { + let prompt = this.doorhanger._callbacks[reply_id].prompt; + this.doorhanger._callbacks[reply_id].cb(); + for (let id in this.doorhanger._callbacks) { + if (this.doorhanger._callbacks[id].prompt == prompt) { + delete this.doorhanger._callbacks[id]; + } + } + } + } + }, + contextmenus: { + items: {}, // a list of context menu items that we may show + textContext: null, // saved selector for text input areas + linkContext: null, // saved selector for links + _contextId: 0, // id to assign to new context menu items if they are added + + init: function() { + this.textContext = this.SelectorContext("input[type='text'],input[type='password'],textarea"); + this.linkContext = this.SelectorContext("a:not([href='']),area:not([href='']),link"); + Services.obs.addObserver(this, "Gesture:LongPress", false); + + // TODO: These should eventually move into more appropriate classes + this.add(Strings.browser.GetStringFromName("contextmenu.openInNewTab"), + this.linkContext, + function(aTarget) { + let url = NativeWindow.contextmenus._getLinkURL(aTarget); + BrowserApp.addTab(url, {selected: false}); + }); + + this.add(Strings.browser.GetStringFromName("contextmenu.changeInputMethod"), + this.textContext, + function(aTarget) { + Cc["@mozilla.org/imepicker;1"].getService(Ci.nsIIMEPicker).show(); + }); + + this.add(Strings.browser.GetStringFromName("contextmenu.fullScreen"), + this.SelectorContext("video:not(:-moz-full-screen)"), + function(aTarget) { + aTarget.mozRequestFullScreen(); + }); + }, + + uninit: function() { + Services.obs.removeObserver(this, "Gesture:LongPress"); + }, + + add: function(aName, aSelector, aCallback) { + if (!aName) + throw "Menu items must have a name"; + + let item = { + name: aName, + context: aSelector, + callback: aCallback, + matches: function(aElt) { + return this.context.matches(aElt); + }, + getValue: function() { + return { + label: this.name, + id: this.id + } + } + }; + item.id = this._contextId++; + this.items[item.id] = item; + return item.id; + }, + + remove: function(aId) { + this.items[aId] = null; + }, + + SelectorContext: function(aSelector) { + return { + matches: function(aElt) { + if (aElt.mozMatchesSelector) + return aElt.mozMatchesSelector(aSelector); + return false; + } + } + }, + + _sendToContent: function(aX, aY) { + // initially we look for nearby clickable elements. If we don't find one we fall back to using whatever this click was on + let rootElement = ElementTouchHelper.elementFromPoint(BrowserApp.selectedBrowser.contentWindow, aX, aY); + if (!rootElement) + rootElement = ElementTouchHelper.anyElementFromPoint(BrowserApp.selectedBrowser.contentWindow, aX, aY) + + this.menuitems = null; + let element = rootElement; + if (!element) + return; + + while (element) { + for each (let item in this.items) { + // since we'll have to spin through this for each element, check that + // it is not already in the list + if ((!this.menuitems || !this.menuitems[item.id]) && item.matches(element)) { + if (!this.menuitems) + this.menuitems = {}; + this.menuitems[item.id] = item; + } + } + + if (this.linkContext.matches(element) || this.textContext.matches(element)) + break; + element = element.parentNode; + } + + // only send the contextmenu event to content if we are planning to show a context menu (i.e. not on every long tap) + if (this.menuitems) { + BrowserEventHandler.blockClick = true; + let event = rootElement.ownerDocument.createEvent("MouseEvent"); + event.initMouseEvent("contextmenu", true, true, content, + 0, aX, aY, aX, aY, false, false, false, false, + 0, null); + rootElement.ownerDocument.defaultView.addEventListener("contextmenu", this, false); + rootElement.dispatchEvent(event); + } + }, + + _show: function(aEvent) { + if (aEvent.getPreventDefault()) + return; + + let popupNode = aEvent.originalTarget; + let title = ""; + if ((popupNode instanceof Ci.nsIDOMHTMLAnchorElement && popupNode.href) || + (popupNode instanceof Ci.nsIDOMHTMLAreaElement && popupNode.href)) { + title = this._getLinkURL(popupNode); + } else if (popupNode instanceof Ci.nsIImageLoadingContent && popupNode.currentURI) { + title = popupNode.currentURI.spec; + } else if (popupNode instanceof Ci.nsIDOMHTMLMediaElement) { + title = (popupNode.currentSrc || popupNode.src); + } + + // convert this.menuitems object to an array for sending to native code + let itemArray = []; + for each (let item in this.menuitems) { + itemArray.push(item.getValue()); + } + + let msg = { + gecko: { + type: "Prompt:Show", + title: title, + listitems: itemArray + } + }; + let data = JSON.parse(sendMessageToJava(msg)); + let selectedId = itemArray[data.button].id; + let selectedItem = this.menuitems[selectedId]; + + if (selectedItem && selectedItem.callback) { + while (popupNode) { + if (selectedItem.matches(popupNode)) { + selectedItem.callback.call(selectedItem, popupNode); + break; + } + popupNode = popupNode.parentNode; + } + } + this.menuitems = null; + }, + + handleEvent: function(aEvent) { + aEvent.target.ownerDocument.defaultView.removeEventListener("contextmenu", this, false); + this._show(aEvent); + }, + + observe: function(aSubject, aTopic, aData) { + BrowserEventHandler._cancelTapHighlight(); + let data = JSON.parse(aData); + // content gets first crack at cancelling context menus + this._sendToContent(data.x, data.y); + }, + + // XXX - These are stolen from Util.js, we should remove them if we bring it back + makeURLAbsolute: function makeURLAbsolute(base, url) { + // Note: makeURI() will throw if url is not a valid URI + return this.makeURI(url, null, this.makeURI(base)).spec; + }, + + makeURI: function makeURI(aURL, aOriginCharset, aBaseURI) { + return Services.io.newURI(aURL, aOriginCharset, aBaseURI); + }, + + _getLinkURL: function ch_getLinkURL(aLink) { + let href = aLink.href; + if (href) + return href; + + href = aLink.getAttributeNS(kXLinkNamespace, "href"); + if (!href || !href.match(/\S/)) { + // Without this we try to save as the current doc, + // for example, HTML case also throws if empty + throw "Empty href"; + } + + return Util.makeURLAbsolute(aLink.baseURI, href); + } + } +}; + + +function nsBrowserAccess() { +} + +nsBrowserAccess.prototype = { + openURI: function browser_openURI(aURI, aOpener, aWhere, aContext) { + let isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL); + + dump("nsBrowserAccess::openURI"); + let browser = BrowserApp.selectedBrowser; + if (!browser || isExternal) { + let tab = BrowserApp.addTab("about:blank"); + BrowserApp.selectTab(tab); + browser = tab.browser; + } + + // Why does returning the browser.contentWindow not work here? + Services.io.offline = false; + browser.loadURI(aURI.spec, null, null); + return null; + }, + + openURIInFrame: function browser_openURIInFrame(aURI, aOpener, aWhere, aContext) { + dump("nsBrowserAccess::openURIInFrame"); + return null; + }, + + isTabContentWindow: function(aWindow) { + return BrowserApp.getBrowserForWindow(aWindow) != null; + }, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow]) +}; + + +let gTabIDFactory = 0; + +const kDefaultMetadata = { autoSize: false, allowZoom: true, autoScale: true }; + +// track the last known screen size so that new tabs +// get created with the right size rather than being 1x1 +let gScreenWidth = 1; +let gScreenHeight = 1; + +function Tab(aURL, aParams) { + this.browser = null; + this.vbox = null; + this.id = 0; + this.agentMode = UA_MODE_MOBILE; + this.lastHost = null; + this.create(aURL, aParams); + this._metadata = null; + this._viewport = { x: 0, y: 0, width: gScreenWidth, height: gScreenHeight, offsetX: 0, offsetY: 0, + pageWidth: 1, pageHeight: 1, zoom: 1.0 }; + this.viewportExcess = { x: 0, y: 0 }; +} + +Tab.prototype = { + create: function(aURL, aParams) { + if (this.browser) + return; + + this.vbox = document.createElement("vbox"); + this.vbox.align = "start"; + BrowserApp.deck.appendChild(this.vbox); + + this.browser = document.createElement("browser"); + this.browser.setAttribute("type", "content"); + this.setBrowserSize(980, 480); + this.browser.style.MozTransformOrigin = "0 0"; + this.vbox.appendChild(this.browser); + + // Turn off clipping so we can buffer areas outside of the browser element. + let frameLoader = this.browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader; + frameLoader.clipSubdocument = false; + + this.browser.stop(); + + this.id = ++gTabIDFactory; + let aParams = aParams || { selected: true }; + let message = { + gecko: { + type: "Tab:Added", + tabID: this.id, + uri: aURL, + selected: ("selected" in aParams) ? aParams.selected : true + } + }; + sendMessageToJava(message); + + let flags = Ci.nsIWebProgress.NOTIFY_STATE_ALL | + Ci.nsIWebProgress.NOTIFY_LOCATION | + Ci.nsIWebProgress.NOTIFY_SECURITY; + this.browser.addProgressListener(this, flags); + this.browser.sessionHistory.addSHistoryListener(this); + Services.obs.addObserver(this, "http-on-modify-request", false); + this.browser.loadURI(aURL); + }, + + setAgentMode: function(aMode) { + if (this.agentMode != aMode) { + this.agentMode = aMode; + sendMessageToJava({ + gecko: { + type: "AgentMode:Changed", + agentMode: aMode, + tabId: this.id + } + }); + } + }, + + setHostFromURL: function(aURL) { + let uri = Services.io.newURI(aURL, null, null); + let host = uri.asciiHost; + if (this.lastHost != host) { + this.lastHost = host; + // TODO: remember mobile/desktop selection for each host (bug 705840) + this.setAgentMode(UA_MODE_MOBILE); + } + }, + + destroy: function() { + if (!this.browser) + return; + + this.browser.removeProgressListener(this); + BrowserApp.deck.removeChild(this.vbox); + Services.obs.removeObserver(this, "http-on-modify-request", false); + this.browser = null; + this.vbox = null; + let message = { + gecko: { + type: "Tab:Closed", + tabID: this.id + } + }; + + sendMessageToJava(message); + }, + + set active(aActive) { + if (!this.browser) + return; + + if (aActive) { + this.browser.setAttribute("type", "content-primary"); + this.browser.focus(); + BrowserApp.selectedTab = this; + } else { + this.browser.setAttribute("type", "content"); + } + }, + + get active() { + if (!this.browser) + return false; + return this.browser.getAttribute("type") == "content-primary"; + }, + + set viewport(aViewport) { + // Transform coordinates based on zoom + aViewport.x /= aViewport.zoom; + aViewport.y /= aViewport.zoom; + + // Set scroll position + this.browser.contentWindow.scrollTo(aViewport.x, aViewport.y); + + // If we've been asked to over-scroll, do it via the transformation + // and store it separately to the viewport. + let excessX = aViewport.x - this.browser.contentWindow.scrollX; + let excessY = aViewport.y - this.browser.contentWindow.scrollY; + + this._viewport.width = gScreenWidth = aViewport.width; + this._viewport.height = gScreenHeight = aViewport.height; + + let transformChanged = false; + + if ((aViewport.offsetX != this._viewport.offsetX) || + (excessX != this.viewportExcess.x)) { + this._viewport.offsetX = aViewport.offsetX; + this.viewportExcess.x = excessX; + transformChanged = true; + } + if ((aViewport.offsetY != this._viewport.offsetY) || + (excessY != this.viewportExcess.y)) { + this._viewport.offsetY = aViewport.offsetY; + this.viewportExcess.y = excessY; + transformChanged = true; + } + if (Math.abs(aViewport.zoom - this._viewport.zoom) >= 1e-6) { + this._viewport.zoom = aViewport.zoom; + transformChanged = true; + } + + let hasZoom = (Math.abs(this._viewport.zoom - 1.0) >= 1e-6); + + if (transformChanged) { + let x = this._viewport.offsetX + Math.round(-excessX * this._viewport.zoom); + let y = this._viewport.offsetY + Math.round(-excessY * this._viewport.zoom); + + let transform = + "translate(" + x + "px, " + + y + "px)"; + if (hasZoom) + transform += " scale(" + this._viewport.zoom + ")"; + + this.browser.style.MozTransform = transform; + } + }, + + get viewport() { + // Update the viewport to current dimensions + this._viewport.x = this.browser.contentWindow.scrollX + + this.viewportExcess.x; + this._viewport.y = this.browser.contentWindow.scrollY + + this.viewportExcess.y; + + let doc = this.browser.contentDocument; + let pageWidth = this._viewport.width; + let pageHeight = this._viewport.height; + if (doc != null) { + let body = doc.body || { scrollWidth: pageWidth, scrollHeight: pageHeight }; + let html = doc.documentElement || { scrollWidth: pageWidth, scrollHeight: pageHeight }; + pageWidth = Math.max(body.scrollWidth, html.scrollWidth); + pageHeight = Math.max(body.scrollHeight, html.scrollHeight); + } + + // Transform coordinates based on zoom + this._viewport.x = Math.round(this._viewport.x * this._viewport.zoom); + this._viewport.y = Math.round(this._viewport.y * this._viewport.zoom); + this._viewport.pageWidth = Math.round(pageWidth * this._viewport.zoom); + this._viewport.pageHeight = Math.round(pageHeight * this._viewport.zoom); + + return this._viewport; + }, + + updateViewport: function(aReset) { + let win = this.browser.contentWindow; + let zoom = (aReset ? this.getDefaultZoomLevel() : this._viewport.zoom); + let xpos = (aReset ? win.scrollX * zoom : this._viewport.x); + let ypos = (aReset ? win.scrollY * zoom : this._viewport.y); + this.viewportExcess = { x: 0, y: 0 }; + this.viewport = { x: xpos, y: ypos, + offsetX: 0, offsetY: 0, + width: this._viewport.width, height: this._viewport.height, + pageWidth: 1, pageHeight: 1, + zoom: zoom }; + this.sendViewportUpdate(); + }, + + sendViewportUpdate: function() { + sendMessageToJava({ + gecko: { + type: "Viewport:Update", + viewport: JSON.stringify(this.viewport) + } + }); + }, + + onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) { + if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT) { + // Filter optimization: Only really send DOCUMENT state changes to Java listener + let browser = BrowserApp.getBrowserForWindow(aWebProgress.DOMWindow); + let uri = ""; + if (browser) + uri = browser.currentURI.spec; + + let message = { + gecko: { + type: "Content:StateChange", + tabID: this.id, + uri: uri, + state: aStateFlags + } + }; + + sendMessageToJava(message); + } + }, + + onLocationChange: function(aWebProgress, aRequest, aLocationURI, aFlags) { + let contentWin = aWebProgress.DOMWindow; + if (contentWin != contentWin.top) + return; + + let browser = BrowserApp.getBrowserForWindow(contentWin); + let uri = browser.currentURI.spec; + + let message = { + gecko: { + type: "Content:LocationChange", + tabID: this.id, + uri: uri + } + }; + + sendMessageToJava(message); + + if ((aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) == 0) { + this.updateViewport(true); + } else { + this.sendViewportUpdate(); + } + }, + + onSecurityChange: function(aWebProgress, aRequest, aState) { + let mode = "unknown"; + if (aState & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL) + mode = "identified"; + else if (aState & Ci.nsIWebProgressListener.STATE_SECURE_HIGH) + mode = "verified"; + else if (aState & Ci.nsIWebProgressListener.STATE_IS_BROKEN) + mode = "mixed"; + else + mode = "unknown"; + + let message = { + gecko: { + type: "Content:SecurityChange", + tabID: this.id, + mode: mode + } + }; + + sendMessageToJava(message); + }, + + onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) { + }, + + onStatusChange: function(aBrowser, aWebProgress, aRequest, aStatus, aMessage) { + }, + + _sendHistoryEvent: function(aMessage, aIndex, aUri) { + let message = { + gecko: { + type: "SessionHistory:" + aMessage, + tabID: this.id, + } + }; + if (aIndex != -1) { + message.gecko.index = aIndex; + } + if (aUri != null) { + message.gecko.uri = aUri; + } + sendMessageToJava(message); + }, + + OnHistoryNewEntry: function(aUri) { + this._sendHistoryEvent("New", -1, aUri.spec); + }, + + OnHistoryGoBack: function(aUri) { + this._sendHistoryEvent("Back", -1, null); + return true; + }, + + OnHistoryGoForward: function(aUri) { + this._sendHistoryEvent("Forward", -1, null); + return true; + }, + + OnHistoryReload: function(aUri, aFlags) { + // we don't do anything with this, so don't propagate it + // for now anyway + return true; + }, + + OnHistoryGotoIndex: function(aIndex, aUri) { + this._sendHistoryEvent("Goto", aIndex, null); + return true; + }, + + OnHistoryPurge: function(aNumEntries) { + this._sendHistoryEvent("Purge", -1, null); + return true; + }, + + get metadata() { + return this._metadata || kDefaultMetadata; + }, + + /** Update viewport when the metadata changes. */ + updateViewportMetadata: function updateViewportMetadata(aMetadata) { + if (aMetadata && aMetadata.autoScale) { + let scaleRatio = aMetadata.scaleRatio = ViewportHandler.getScaleRatio(); + + if ("defaultZoom" in aMetadata && aMetadata.defaultZoom > 0) + aMetadata.defaultZoom *= scaleRatio; + if ("minZoom" in aMetadata && aMetadata.minZoom > 0) + aMetadata.minZoom *= scaleRatio; + if ("maxZoom" in aMetadata && aMetadata.maxZoom > 0) + aMetadata.maxZoom *= scaleRatio; + } + this._metadata = aMetadata; + this.updateViewportSize(); + this.updateViewport(true); + }, + + /** Update viewport when the metadata or the window size changes. */ + updateViewportSize: function updateViewportSize() { + let browser = this.browser; + if (!browser) + return; + + let screenW = screen.width; + let screenH = screen.height; + let viewportW, viewportH; + + let metadata = this.metadata; + if (metadata.autoSize) { + if ("scaleRatio" in metadata) { + viewportW = screenW / metadata.scaleRatio; + viewportH = screenH / metadata.scaleRatio; + } else { + viewportW = screenW; + viewportH = screenH; + } + } else { + viewportW = metadata.width; + viewportH = metadata.height; + + // If (scale * width) < device-width, increase the width (bug 561413). + let maxInitialZoom = metadata.defaultZoom || metadata.maxZoom; + if (maxInitialZoom && viewportW) + viewportW = Math.max(viewportW, screenW / maxInitialZoom); + + let validW = viewportW > 0; + let validH = viewportH > 0; + + if (!validW) + viewportW = validH ? (viewportH * (screenW / screenH)) : BrowserApp.defaultBrowserWidth; + if (!validH) + viewportH = viewportW * (screenH / screenW); + } + + // Make sure the viewport height is not shorter than the window when + // the page is zoomed out to show its full width. + let minScale = this.getPageZoomLevel(screenW); + viewportH = Math.max(viewportH, screenH / minScale); + + this.setBrowserSize(viewportW, viewportH); + }, + + getDefaultZoomLevel: function getDefaultZoomLevel() { + let md = this.metadata; + if ("defaultZoom" in md && md.defaultZoom) + return md.defaultZoom; + + let browserWidth = this.browser.getBoundingClientRect().width; + return screen.width / browserWidth; + }, + + getPageZoomLevel: function getPageZoomLevel() { + // This may get called during a Viewport:Change message while the document + // has not loaded yet. + if (!this.browser.contentDocument || !this.browser.contentDocument.body) + return 1.0; + + return screen.width / this.browser.contentDocument.body.clientWidth; + }, + + setBrowserSize: function(aWidth, aHeight) { + this.browser.style.width = aWidth + "px"; + this.browser.style.height = aHeight + "px"; + }, + + getRequestLoadContext: function(aRequest) { + if (aRequest && aRequest.notificationCallbacks) { + try { + return aRequest.notificationCallbacks.getInterface(Ci.nsILoadContext); + } catch (ex) { } + } + + if (aRequest && aRequest.loadGroup && aRequest.loadGroup.notificationCallbacks) { + try { + return aRequest.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext); + } catch (ex) { } + } + + return null; + }, + + getWindowForRequest: function(aRequest) { + let loadContext = this.getRequestLoadContext(aRequest); + if (loadContext) + return loadContext.associatedWindow; + return null; + }, + + observe: function(aSubject, aTopic, aData) { + if (!(aSubject instanceof Ci.nsIHttpChannel)) + return; + + let channel = aSubject.QueryInterface(Ci.nsIHttpChannel); + if (!(channel.loadFlags & Ci.nsIChannel.LOAD_DOCUMENT_URI)) + return; + + let channelWindow = this.getWindowForRequest(channel); + if (channelWindow == this.browser.contentWindow) { + this.setHostFromURL(channel.URI.spec); + if (this.agentMode == UA_MODE_DESKTOP) + channel.setRequestHeader("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:7.0.1) Gecko/20100101 Firefox/7.0.1", false); + } + }, + + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsIWebProgressListener, + Ci.nsISHistoryListener, + Ci.nsIObserver, + Ci.nsISupportsWeakReference + ]) +}; + + +var BrowserEventHandler = { + init: function init() { + window.addEventListener("click", this, true); + window.addEventListener("mousedown", this, true); + window.addEventListener("mouseup", this, true); + window.addEventListener("mousemove", this, true); + + Services.obs.addObserver(this, "Gesture:SingleTap", false); + Services.obs.addObserver(this, "Gesture:ShowPress", false); + Services.obs.addObserver(this, "Gesture:CancelTouch", false); + + BrowserApp.deck.addEventListener("DOMContentLoaded", this, true); + BrowserApp.deck.addEventListener("DOMLinkAdded", this, true); + BrowserApp.deck.addEventListener("DOMTitleChanged", this, true); + BrowserApp.deck.addEventListener("DOMUpdatePageReport", PopupBlockerObserver.onUpdatePageReport, false); + }, + + observe: function(aSubject, aTopic, aData) { + if (aTopic == "Gesture:CancelTouch") { + this._cancelTapHighlight(); + } else if (aTopic == "Gesture:ShowPress") { + let data = JSON.parse(aData); + let closest = ElementTouchHelper.elementFromPoint(BrowserApp.selectedBrowser.contentWindow, + data.x, data.y); + if (!closest) + closest = ElementTouchHelper.anyElementFromPoint(BrowserApp.selectedBrowser.contentWindow, + data.x, data.y); + if (closest) + this._doTapHighlight(closest); + } else if (aTopic == "Gesture:SingleTap") { + let element = this._highlightElement; + if (element && !FormAssistant.handleClick(element)) { + let data = JSON.parse(aData); + [data.x, data.y] = ElementTouchHelper.toScreenCoords(element.ownerDocument.defaultView, data.x, data.y); + + this._sendMouseEvent("mousemove", element, data.x, data.y); + this._sendMouseEvent("mousedown", element, data.x, data.y); + this._sendMouseEvent("mouseup", element, data.x, data.y); + } + this._cancelTapHighlight(); + } + }, + + _highlihtElement: null, + + _doTapHighlight: function _doTapHighlight(aElement) { + DOMUtils.setContentState(aElement, kStateActive); + this._highlightElement = aElement; + }, + + _cancelTapHighlight: function _cancelTapHighlight() { + DOMUtils.setContentState(BrowserApp.selectedBrowser.contentWindow.document.documentElement, kStateActive); + this._highlightElement = null; + }, + + handleEvent: function(aEvent) { + switch (aEvent.type) { + case "DOMContentLoaded": { + let browser = BrowserApp.getBrowserForDocument(aEvent.target); + if (!browser) + return; + + let tab = BrowserApp.getTabForBrowser(browser); + tab.updateViewport(true); + + sendMessageToJava({ + gecko: { + type: "DOMContentLoaded", + tabID: tab.id, + windowID: 0, + uri: browser.currentURI.spec, + title: browser.contentTitle + } + }); + + // Attach a listener to watch for "click" events bubbling up from error + // pages and other similar page. This lets us fix bugs like 401575 which + // require error page UI to do privileged things, without letting error + // pages have any privilege themselves. + if (/^about:/.test(aEvent.originalTarget.documentURI)) { + let browser = BrowserApp.getBrowserForDocument(aEvent.originalTarget); + browser.addEventListener("click", ErrorPageEventHandler, false); + browser.addEventListener("pagehide", function listener() { + browser.removeEventListener("click", ErrorPageEventHandler, false); + browser.removeEventListener("pagehide", listener, true); + }, true); + } + + break; + } + + case "DOMLinkAdded": { + let target = aEvent.originalTarget; + if (!target.href || target.disabled) + return; + + let browser = BrowserApp.getBrowserForDocument(target.ownerDocument); + if (!browser) + return; + let tabID = BrowserApp.getTabForBrowser(browser).id; + + let json = { + type: "DOMLinkAdded", + tabID: tabID, + href: resolveGeckoURI(target.href), + charset: target.ownerDocument.characterSet, + title: target.title, + rel: target.rel + }; + + // rel=icon can also have a sizes attribute + if (target.hasAttribute("sizes")) + json.sizes = target.getAttribute("sizes"); + + sendMessageToJava({ gecko: json }); + break; + } + + case "DOMTitleChanged": { + if (!aEvent.isTrusted) + return; + + let contentWin = aEvent.target.defaultView; + if (contentWin != contentWin.top) + return; + + let browser = BrowserApp.getBrowserForDocument(aEvent.target); + if (!browser) + return; + let tabID = BrowserApp.getTabForBrowser(browser).id; + + sendMessageToJava({ + gecko: { + type: "DOMTitleChanged", + tabID: tabID, + title: aEvent.target.title + } + }); + break; + } + } + }, + + _updateLastPosition: function(x, y, dx, dy) { + this.lastX = x; + this.lastY = y; + this.lastTime = Date.now(); + + this.motionBuffer.push({ dx: dx, dy: dy, time: this.lastTime }); + }, + + _sendMouseEvent: function _sendMouseEvent(aName, aElement, aX, aY, aButton) { + // the element can be out of the aX/aY point because of the touch radius + // if outside, we gracefully move the touch point to the center of the element + if (!(aElement instanceof HTMLHtmlElement)) { + let isTouchClick = true; + let rects = ElementTouchHelper.getContentClientRects(aElement); + for (let i = 0; i < rects.length; i++) { + let rect = rects[i]; + // We might be able to deal with fractional pixels, but mouse events won't. + // Deflate the bounds in by 1 pixel to deal with any fractional scroll offset issues. + let inBounds = + (aX > rect.left + 1 && aX < (rect.left + rect.width - 1)) && + (aY > rect.top + 1 && aY < (rect.top + rect.height - 1)); + if (inBounds) { + isTouchClick = false; + break; + } + } + + if (isTouchClick) { + let rect = {x: rects[0].left, y: rects[0].top, w: rects[0].width, h: rects[0].height}; + if (rect.w == 0 && rect.h == 0) + return; + + let point = { x: rect.x + rect.w/2, y: rect.y + rect.h/2 }; + aX = point.x; + aY = point.y; + } + } + + [aX, aY] = ElementTouchHelper.toBrowserCoords(aElement.ownerDocument.defaultView, aX, aY); + let cwu = aElement.ownerDocument.defaultView.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); + aButton = aButton || 0; + cwu.sendMouseEventToWindow(aName, Math.round(aX), Math.round(aY), aButton, 1, 0, true); + }, + + _findScrollableElement: function(elem, checkElem) { + // Walk the DOM tree until we find a scrollable element + let scrollable = false; + while (elem) { + /* Element is scrollable if its scroll-size exceeds its client size, and: + * - It has overflow 'auto' or 'scroll' + * - It's a textarea + * We don't consider HTML/BODY nodes here, since Java pans those. + */ + if (checkElem) { + if (((elem.scrollHeight > elem.clientHeight) || + (elem.scrollWidth > elem.clientWidth)) && + (elem.style.overflow == 'auto' || + elem.style.overflow == 'scroll' || + elem.localName == 'textarea')) { + scrollable = true; + break; + } + } else { + checkElem = true; + } + + // Propagate up iFrames + if (!elem.parentNode && elem.documentElement && + elem.documentElement.ownerDocument) + elem = elem.documentElement.ownerDocument.defaultView.frameElement; + else + elem = elem.parentNode; + } + + if (!scrollable) + return null; + + return elem; + }, + + _elementReceivesInput: function(aElement) { + return aElement instanceof Element && + kElementsReceivingInput.hasOwnProperty(aElement.tagName.toLowerCase()) || + this._isEditable(aElement); + }, + + _isEditable: function(aElement) { + let canEdit = false; + + if (aElement.isContentEditable || aElement.designMode == "on") { + canEdit = true; + } else if (aElement instanceof HTMLIFrameElement && (aElement.contentDocument.body.isContentEditable || aElement.contentDocument.designMode == "on")) { + canEdit = true; + } else { + canEdit = aElement.ownerDocument && aElement.ownerDocument.designMode == "on"; + } + + return canEdit; + }, + + _scrollElementBy: function(elem, x, y) { + elem.scrollTop = elem.scrollTop + y; + elem.scrollLeft = elem.scrollLeft + x; + }, + + _elementCanScroll: function(elem, x, y) { + let scrollX = true; + let scrollY = true; + + if (x < 0) { + if (elem.scrollLeft <= 0) { + scrollX = false; + } + } else if (elem.scrollLeft >= (elem.scrollWidth - elem.clientWidth)) { + scrollX = false; + } + + if (y < 0) { + if (elem.scrollTop <= 0) { + scrollY = false; + } + } else if (elem.scrollTop >= (elem.scrollHeight - elem.clientHeight)) { + scrollY = false; + } + + return scrollX || scrollY; + } +}; + +const kReferenceDpi = 240; // standard "pixel" size used in some preferences + +const ElementTouchHelper = { + toBrowserCoords: function(aWindow, aX, aY) { + let tab = BrowserApp.selectedTab; + if (aWindow) { + let browser = BrowserApp.getBrowserForWindow(aWindow); + tab = BrowserApp.getTabForBrowser(browser); + } + let viewport = tab.viewport; + return [ + ((aX-tab.viewportExcess.x)*viewport.zoom + viewport.offsetX), + ((aY-tab.viewportExcess.y)*viewport.zoom + viewport.offsetY) + ]; + }, + + toScreenCoords: function(aWindow, aX, aY) { + let tab = BrowserApp.selectedTab; + if (aWindow) { + let browser = BrowserApp.getBrowserForWindow(aWindow); + tab = BrowserApp.getTabForBrowser(browser); + } + let viewport = tab.viewport; + return [ + (aX - viewport.offsetX)/viewport.zoom + tab.viewportExcess.x, + (aY - viewport.offsetY)/viewport.zoom + tab.viewportExcess.y + ]; + }, + + anyElementFromPoint: function(aWindow, aX, aY) { + [aX, aY] = this.toScreenCoords(aWindow, aX, aY); + let cwu = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); + let elem = cwu.elementFromPoint(aX, aY, false, true); + + while (elem && (elem instanceof HTMLIFrameElement || elem instanceof HTMLFrameElement)) { + let rect = elem.getBoundingClientRect(); + aX -= rect.left; + aY -= rect.top; + cwu = elem.contentDocument.defaultView.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); + elem = cwu.elementFromPoint(aX, aY, false, true); + } + + return elem; + }, + + elementFromPoint: function(aWindow, aX, aY) { + [aX, aY] = this.toScreenCoords(aWindow, aX, aY); + // browser's elementFromPoint expect browser-relative client coordinates. + // subtract browser's scroll values to adjust + let cwu = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); + let elem = this.getClosest(cwu, aX, aY); + + // step through layers of IFRAMEs and FRAMES to find innermost element + while (elem && (elem instanceof HTMLIFrameElement || elem instanceof HTMLFrameElement)) { + // adjust client coordinates' origin to be top left of iframe viewport + let rect = elem.getBoundingClientRect(); + aX -= rect.left; + aY -= rect.top; + cwu = elem.contentDocument.defaultView.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); + elem = ElementTouchHelper.getClosest(cwu, aX, aY); + } + + return elem; + }, + + get radius() { + let prefs = Services.prefs; + delete this.radius; + return this.radius = { "top": prefs.getIntPref("browser.ui.touch.top"), + "right": prefs.getIntPref("browser.ui.touch.right"), + "bottom": prefs.getIntPref("browser.ui.touch.bottom"), + "left": prefs.getIntPref("browser.ui.touch.left") + }; + }, + + get weight() { + delete this.weight; + return this.weight = { "visited": Services.prefs.getIntPref("browser.ui.touch.weight.visited") }; + }, + + /* Retrieve the closest element to a point by looking at borders position */ + getClosest: function getClosest(aWindowUtils, aX, aY) { + if (!this.dpiRatio) + this.dpiRatio = aWindowUtils.displayDPI / kReferenceDpi; + + let dpiRatio = this.dpiRatio; + + let target = aWindowUtils.elementFromPoint(aX, aY, + true, /* ignore root scroll frame*/ + false); /* don't flush layout */ + + // if this element is clickable we return quickly + if (this._isElementClickable(target)) + return target; + + let target = null; + let nodes = aWindowUtils.nodesFromRect(aX, aY, this.radius.top * dpiRatio, + this.radius.right * dpiRatio, + this.radius.bottom * dpiRatio, + this.radius.left * dpiRatio, true, false); + + let threshold = Number.POSITIVE_INFINITY; + for (let i = 0; i < nodes.length; i++) { + let current = nodes[i]; + if (!current.mozMatchesSelector || !this._isElementClickable(current)) + continue; + + let rect = current.getBoundingClientRect(); + let distance = this._computeDistanceFromRect(aX, aY, rect); + + // increase a little bit the weight for already visited items + if (current && current.mozMatchesSelector("*:visited")) + distance *= (this.weight.visited / 100); + + if (distance < threshold) { + target = current; + threshold = distance; + } + } + + return target; + }, + + _isElementClickable: function _isElementClickable(aElement) { + const selector = "a,:link,:visited,[role=button],button,input,select,textarea,label"; + for (let elem = aElement; elem; elem = elem.parentNode) { + if (this._hasMouseListener(elem)) + return true; + if (elem.mozMatchesSelector && elem.mozMatchesSelector(selector)) + return true; + } + return false; + }, + + _computeDistanceFromRect: function _computeDistanceFromRect(aX, aY, aRect) { + let x = 0, y = 0; + let xmost = aRect.left + aRect.width; + let ymost = aRect.top + aRect.height; + + // compute horizontal distance from left/right border depending if X is + // before/inside/after the element's rectangle + if (aRect.left < aX && aX < xmost) + x = Math.min(xmost - aX, aX - aRect.left); + else if (aX < aRect.left) + x = aRect.left - aX; + else if (aX > xmost) + x = aX - xmost; + + // compute vertical distance from top/bottom border depending if Y is + // above/inside/below the element's rectangle + if (aRect.top < aY && aY < ymost) + y = Math.min(ymost - aY, aY - aRect.top); + else if (aY < aRect.top) + y = aRect.top - aY; + if (aY > ymost) + y = aY - ymost; + + return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); + }, + + _els: Cc["@mozilla.org/eventlistenerservice;1"].getService(Ci.nsIEventListenerService), + _clickableEvents: ["mousedown", "mouseup", "click"], + _hasMouseListener: function _hasMouseListener(aElement) { + let els = this._els; + let listeners = els.getListenerInfoFor(aElement, {}); + for (let i = 0; i < listeners.length; i++) { + if (this._clickableEvents.indexOf(listeners[i].type) != -1) + return true; + } + return false; + }, + getContentClientRects: function(aElement) { + let offset = {x: 0, y: 0}; + + let nativeRects = aElement.getClientRects(); + // step out of iframes and frames, offsetting scroll values + for (let frame = aElement.ownerDocument.defaultView; frame != content; frame = frame.parent) { + // adjust client coordinates' origin to be top left of iframe viewport + let rect = frame.frameElement.getBoundingClientRect(); + let left = frame.getComputedStyle(frame.frameElement, "").borderLeftWidth; + let top = frame.getComputedStyle(frame.frameElement, "").borderTopWidth; + offset.x += rect.left + parseInt(left); + offset.y += rect.top + parseInt(top); + } + + let result = []; + for (let i = nativeRects.length - 1; i >= 0; i--) { + let r = nativeRects[i]; + result.push({ left: r.left + offset.x, + top: r.top + offset.y, + width: r.width, + height: r.height + }); + } + return result; + } +}; + +var ErrorPageEventHandler = { + handleEvent: function(aEvent) { + switch (aEvent.type) { + case "click": { + // Don't trust synthetic events + if (!aEvent.isTrusted) + return; + + let target = aEvent.originalTarget; + let errorDoc = target.ownerDocument; + + // If the event came from an ssl error page, it is probably either the "Add + // Exception…" or "Get me out of here!" button + if (/^about:certerror\?e=nssBadCert/.test(errorDoc.documentURI)) { + let perm = errorDoc.getElementById("permanentExceptionButton"); + let temp = errorDoc.getElementById("temporaryExceptionButton"); + if (target == temp || target == perm) { + // Handle setting an cert exception and reloading the page + try { + // Add a new SSL exception for this URL + let uri = Services.io.newURI(errorDoc.location.href, null, null); + let sslExceptions = new SSLExceptions(); + + if (target == perm) + sslExceptions.addPermanentException(uri); + else + sslExceptions.addTemporaryException(uri); + } catch (e) { + dump("Failed to set cert exception: " + e + "\n"); + } + errorDoc.location.reload(); + } else if (target == errorDoc.getElementById("getMeOutOfHereButton")) { + errorDoc.location = this.getFallbackSafeURL(); + } + } + break; + } + } + }, + + getFallbackSafeURL: function getFallbackSafeURL() { + // Get the start page from the *default* pref branch, not the user's + let prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).getDefaultBranch(null); + let url = "about:home"; + try { + url = prefs.getComplexValue("browser.startup.homepage", Ci.nsIPrefLocalizedString).data; + // If url is a pipe-delimited set of pages, just take the first one. + if (url.indexOf("|") != -1) + url = url.split("|")[0]; + } catch(e) { + Cu.reportError("Couldn't get homepage pref: " + e); + } + return url; + } +}; + + +var FormAssistant = { + show: function(aList, aElement) { + let data = JSON.parse(sendMessageToJava({ gecko: aList })); + let selected = data.button; + if (!(selected instanceof Array)) { + let temp = []; + for (let i = 0; i < aList.listitems.length; i++) { + temp[i] = (i == selected); + } + selected = temp; + } + this.forOptions(aElement, function(aNode, aIndex) { + aNode.selected = selected[aIndex]; + }); + this.fireOnChange(aElement); + }, + + handleClick: function(aTarget) { + let target = aTarget; + while (target) { + if (this._isSelectElement(target)) { + let list = this.getListForElement(target); + this.show(list, target); + target = null; + return true; + } + if (target) + target = target.parentNode; + } + return false; + }, + + fireOnChange: function(aElement) { + let evt = aElement.ownerDocument.createEvent("Events"); + evt.initEvent("change", true, true, aElement.defaultView, 0, + false, false, + false, false, null); + setTimeout(function() { + aElement.dispatchEvent(evt); + }, 0); + }, + + _isSelectElement: function(aElement) { + return (aElement instanceof HTMLSelectElement); + }, + + _isOptionElement: function(aElement) { + return aElement instanceof HTMLOptionElement; + }, + + _isOptionGroupElement: function(aElement) { + return aElement instanceof HTMLOptGroupElement; + }, + + getListForElement: function(aElement) { + let result = { + type: "Prompt:Show", + multiple: aElement.multiple, + selected: [], + listitems: [] + }; + + if (aElement.multiple) { + result.buttons = [ + { label: Strings.browser.GetStringFromName("selectHelper.closeMultipleSelectDialog") }, + ]; + } + + this.forOptions(aElement, function(aNode, aIndex) { + result.listitems[aIndex] = { + label: aNode.text || aNode.label, + isGroup: this._isOptionGroupElement(aNode), + inGroup: this._isOptionGroupElement(aNode.parentNode), + disabled: aNode.disabled, + id: aIndex + } + result.selected[aIndex] = aNode.selected; + }); + return result; + }, + + forOptions: function(aElement, aFunction) { + let optionIndex = 0; + let children = aElement.children; + // if there are no children in this select, we add a dummy row so that at least something appears + if (children.length == 0) + aFunction.call(this, {label:""}, optionIndex); + for (let i = 0; i < children.length; i++) { + let child = children[i]; + if (this._isOptionGroupElement(child)) { + aFunction.call(this, child, optionIndex); + optionIndex++; + + let subchildren = child.children; + for (let j = 0; j < subchildren.length; j++) { + let subchild = subchildren[j]; + aFunction.call(this, subchild, optionIndex); + optionIndex++; + } + + } else if (this._isOptionElement(child)) { + // This is a regular choice under no group. + aFunction.call(this, child, optionIndex); + optionIndex++; + } + } + } +} + +var XPInstallObserver = { + init: function xpi_init() { + Services.obs.addObserver(XPInstallObserver, "addon-install-blocked", false); + Services.obs.addObserver(XPInstallObserver, "addon-install-started", false); + + AddonManager.addInstallListener(XPInstallObserver); + }, + + uninit: function xpi_uninit() { + Services.obs.removeObserver(XPInstallObserver, "addon-install-blocked"); + Services.obs.removeObserver(XPInstallObserver, "addon-install-started"); + + AddonManager.removeInstallListener(XPInstallObserver); + }, + + observe: function xpi_observer(aSubject, aTopic, aData) { + switch (aTopic) { + case "addon-install-started": + NativeWindow.toast.show(Strings.browser.GetStringFromName("alertAddonsDownloading"), "short"); + break; + case "addon-install-blocked": + let installInfo = aSubject.QueryInterface(Ci.amIWebInstallInfo); + let host = installInfo.originatingURI.host; + + let brandShortName = Strings.brand.GetStringFromName("brandShortName"); + let notificationName, buttons, message; + let strings = Strings.browser; + let enabled = true; + try { + enabled = Services.prefs.getBoolPref("xpinstall.enabled"); + } + catch (e) {} + + if (!enabled) { + notificationName = "xpinstall-disabled"; + if (Services.prefs.prefIsLocked("xpinstall.enabled")) { + message = strings.GetStringFromName("xpinstallDisabledMessageLocked"); + buttons = []; + } else { + message = strings.formatStringFromName("xpinstallDisabledMessage2", [brandShortName, host], 2); + buttons = [{ + label: strings.GetStringFromName("xpinstallDisabledButton"), + callback: function editPrefs() { + Services.prefs.setBoolPref("xpinstall.enabled", true); + return false; + } + }]; + } + } else { + notificationName = "xpinstall"; + message = strings.formatStringFromName("xpinstallPromptWarning2", [brandShortName, host], 2); + + buttons = [{ + label: strings.GetStringFromName("xpinstallPromptAllowButton"), + callback: function() { + // Kick off the install + installInfo.install(); + return false; + } + }]; + } + NativeWindow.doorhanger.show(message, aTopic, buttons); + break; + } + }, + + onInstallEnded: function(aInstall, aAddon) { + let needsRestart = false; + if (aInstall.existingAddon && (aInstall.existingAddon.pendingOperations & AddonManager.PENDING_UPGRADE)) + needsRestart = true; + else if (aAddon.pendingOperations & AddonManager.PENDING_INSTALL) + needsRestart = true; + + if (needsRestart) { + buttons = [{ + label: Strings.browser.GetStringFromName("notificationRestart.button"), + callback: function() { + // Notify all windows that an application quit has been requested + let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool); + Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart"); + + // If nothing aborted, quit the app + if (cancelQuit.data == false) { + let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup); + appStartup.quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit); + } + } + }]; + + let message = Strings.browser.GetStringFromName("notificationRestart.normal"); + NativeWindow.doorhanger.show(message, "addon-app-restart", buttons, BrowserApp.selectedTab.id, { persistence: -1 }); + } else { + let message = Strings.browser.GetStringFromName("alertAddonsInstalledNoRestart"); + NativeWindow.toast.show(message, "short"); + } + }, + + onInstallFailed: function(aInstall) { + NativeWindow.toast.show(Strings.browser.GetStringFromName("alertAddonsFail"), "short"); + }, + + onDownloadProgress: function xpidm_onDownloadProgress(aInstall) {}, + + onDownloadFailed: function(aInstall) { + this.onInstallFailed(aInstall); + }, + + onDownloadCancelled: function(aInstall) {} +}; + +// Blindly copied from Safari documentation for now. +const kViewportMinScale = 0; +const kViewportMaxScale = 10; +const kViewportMinWidth = 200; +const kViewportMaxWidth = 10000; +const kViewportMinHeight = 223; +const kViewportMaxHeight = 10000; + +var ViewportHandler = { + init: function init() { + addEventListener("DOMWindowCreated", this, false); + addEventListener("DOMMetaAdded", this, false); + addEventListener("DOMContentLoaded", this, false); + addEventListener("pageshow", this, false); + addEventListener("resize", this, false); + }, + + uninit: function uninit() { + removeEventListener("DOMWindowCreated", this, false); + removeEventListener("DOMMetaAdded", this, false); + removeEventListener("DOMContentLoaded", this, false); + removeEventListener("pageshow", this, false); + removeEventListener("resize", this, false); + }, + + handleEvent: function handleEvent(aEvent) { + let target = aEvent.originalTarget; + let document = target.ownerDocument || target; + let browser = BrowserApp.getBrowserForDocument(document); + let tab = BrowserApp.getTabForBrowser(browser); + if (!tab) + return; + + switch (aEvent.type) { + case "DOMWindowCreated": + this.resetMetadata(tab); + break; + + case "DOMMetaAdded": + if (target.name == "viewport") + this.updateMetadata(tab); + break; + + case "DOMContentLoaded": + case "pageshow": + this.updateMetadata(tab); + break; + + case "resize": + this.onResize(); + break; + } + }, + + resetMetadata: function resetMetadata(tab) { + tab.updateViewportMetadata(null); + }, + + updateMetadata: function updateMetadata(tab) { + let metadata = this.getViewportMetadata(tab.browser.contentWindow); + tab.updateViewportMetadata(metadata); + }, + + /** + * Returns an object with the page's preferred viewport properties: + * defaultZoom (optional float): The initial scale when the page is loaded. + * minZoom (optional float): The minimum zoom level. + * maxZoom (optional float): The maximum zoom level. + * width (optional int): The CSS viewport width in px. + * height (optional int): The CSS viewport height in px. + * autoSize (boolean): Resize the CSS viewport when the window resizes. + * allowZoom (boolean): Let the user zoom in or out. + * autoScale (boolean): Adjust the viewport properties to account for display density. + */ + getViewportMetadata: function getViewportMetadata(aWindow) { + let doctype = aWindow.document.doctype; + if (doctype && /(WAP|WML|Mobile)/.test(doctype.publicId)) + return { defaultZoom: 1, autoSize: true, allowZoom: true, autoScale: true }; + + let windowUtils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); + let handheldFriendly = windowUtils.getDocumentMetadata("HandheldFriendly"); + if (handheldFriendly == "true") + return { defaultZoom: 1, autoSize: true, allowZoom: true, autoScale: true }; + + if (aWindow.document instanceof XULDocument) + return { defaultZoom: 1, autoSize: true, allowZoom: false, autoScale: false }; + + // viewport details found here + // http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/MetaTags.html + // http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariWebContent/UsingtheViewport/UsingtheViewport.html + + // Note: These values will be NaN if parseFloat or parseInt doesn't find a number. + // Remember that NaN is contagious: Math.max(1, NaN) == Math.min(1, NaN) == NaN. + let scale = parseFloat(windowUtils.getDocumentMetadata("viewport-initial-scale")); + let minScale = parseFloat(windowUtils.getDocumentMetadata("viewport-minimum-scale")); + let maxScale = parseFloat(windowUtils.getDocumentMetadata("viewport-maximum-scale")); + + let widthStr = windowUtils.getDocumentMetadata("viewport-width"); + let heightStr = windowUtils.getDocumentMetadata("viewport-height"); + let width = this.clamp(parseInt(widthStr), kViewportMinWidth, kViewportMaxWidth); + let height = this.clamp(parseInt(heightStr), kViewportMinHeight, kViewportMaxHeight); + + let allowZoomStr = windowUtils.getDocumentMetadata("viewport-user-scalable"); + let allowZoom = !/^(0|no|false)$/.test(allowZoomStr); // WebKit allows 0, "no", or "false" + + scale = this.clamp(scale, kViewportMinScale, kViewportMaxScale); + minScale = this.clamp(minScale, kViewportMinScale, kViewportMaxScale); + maxScale = this.clamp(maxScale, kViewportMinScale, kViewportMaxScale); + + // If initial scale is 1.0 and width is not set, assume width=device-width + let autoSize = (widthStr == "device-width" || + (!widthStr && (heightStr == "device-height" || scale == 1.0))); + + return { + defaultZoom: scale, + minZoom: minScale, + maxZoom: maxScale, + width: width, + height: height, + autoSize: autoSize, + allowZoom: allowZoom, + autoScale: true + }; + }, + + onResize: function onResize() { + for (let i = 0; i < BrowserApp.tabs.length; i++) + BrowserApp.tabs[i].updateViewportSize(); + }, + + clamp: function(num, min, max) { + return Math.max(min, Math.min(max, num)); + }, + + // The device-pixel-to-CSS-px ratio used to adjust meta viewport values. + // This is higher on higher-dpi displays, so pages stay about the same physical size. + getScaleRatio: function getScaleRatio() { + let prefValue = Services.prefs.getIntPref("browser.viewport.scaleRatio"); + if (prefValue > 0) + return prefValue / 100; + + let dpi = this.displayDPI; + if (dpi < 200) // Includes desktop displays, and LDPI and MDPI Android devices + return 1; + else if (dpi < 300) // Includes Nokia N900, and HDPI Android devices + return 1.5; + + // For very high-density displays like the iPhone 4, calculate an integer ratio. + return Math.floor(dpi / 150); + }, + + get displayDPI() { + let utils = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); + delete this.displayDPI; + return this.displayDPI = utils.displayDPI; + } +}; + +/** + * Handler for blocked popups, triggered by DOMUpdatePageReport events in browser.xml + */ +var PopupBlockerObserver = { + onUpdatePageReport: function onUpdatePageReport(aEvent) { + let browser = BrowserApp.selectedBrowser; + if (aEvent.originalTarget != browser) + return; + + if (!browser.pageReport) + return; + + let result = Services.perms.testExactPermission(BrowserApp.selectedBrowser.currentURI, "popup"); + if (result == Ci.nsIPermissionManager.DENY_ACTION) + return; + + // Only show the notification again if we've not already shown it. Since + // notifications are per-browser, we don't need to worry about re-adding + // it. + if (!browser.pageReport.reported) { + if (Services.prefs.getBoolPref("privacy.popups.showBrowserMessage")) { + let brandShortName = Strings.brand.GetStringFromName("brandShortName"); + let message; + let popupCount = browser.pageReport.length; + + let strings = Strings.browser; + if (popupCount > 1) + message = strings.formatStringFromName("popupWarningMultiple", [brandShortName, popupCount], 2); + else + message = strings.formatStringFromName("popupWarning", [brandShortName], 1); + + let buttons = [ + { + label: strings.GetStringFromName("popupButtonAllowOnce"), + callback: function() { PopupBlockerObserver.showPopupsForSite(); } + }, + { + label: strings.GetStringFromName("popupButtonAlwaysAllow2"), + callback: function() { PopupBlockerObserver.allowPopupsForSite(true); } + }, + { + label: strings.GetStringFromName("popupButtonNeverWarn2"), + callback: function() { PopupBlockerObserver.allowPopupsForSite(false); } + } + ]; + + NativeWindow.doorhanger.show(message, "popup-blocked", buttons); + } + // Record the fact that we've reported this blocked popup, so we don't + // show it again. + browser.pageReport.reported = true; + } + }, + + allowPopupsForSite: function allowPopupsForSite(aAllow) { + let currentURI = BrowserApp.selectedBrowser.currentURI; + Services.perms.add(currentURI, "popup", aAllow + ? Ci.nsIPermissionManager.ALLOW_ACTION + : Ci.nsIPermissionManager.DENY_ACTION); + dump("Allowing popups for: " + currentURI); + }, + + showPopupsForSite: function showPopupsForSite() { + let uri = BrowserApp.selectedBrowser.currentURI; + let pageReport = BrowserApp.selectedBrowser.pageReport; + if (pageReport) { + for (let i = 0; i < pageReport.length; ++i) { + let popupURIspec = pageReport[i].popupWindowURI.spec; + + // Sometimes the popup URI that we get back from the pageReport + // isn't useful (for instance, netscape.com's popup URI ends up + // being "http://www.netscape.com", which isn't really the URI of + // the popup they're trying to show). This isn't going to be + // useful to the user, so we won't create a menu item for it. + if (popupURIspec == "" || popupURIspec == "about:blank" || popupURIspec == uri.spec) + continue; + + let popupFeatures = pageReport[i].popupWindowFeatures; + let popupName = pageReport[i].popupWindowName; + + BrowserApp.addTab(popupURIspec); + } + } + } +}; + + +var OfflineApps = { + init: function() { + BrowserApp.deck.addEventListener("MozApplicationManifest", this, false); + }, + + uninit: function() { + BrowserApp.deck.removeEventListener("MozApplicationManifest", this, false); + }, + + handleEvent: function(aEvent) { + if (aEvent.type == "MozApplicationManifest") + this.offlineAppRequested(aEvent.originalTarget.defaultView); + }, + + offlineAppRequested: function(aContentWindow) { + if (!Services.prefs.getBoolPref("browser.offline-apps.notify")) + return; + + let browser = BrowserApp.getBrowserForWindow(aContentWindow); + let tab = BrowserApp.getTabForBrowser(browser); + let currentURI = aContentWindow.document.documentURIObject; + + // Don't bother showing UI if the user has already made a decision + if (Services.perms.testExactPermission(currentURI, "offline-app") != Services.perms.UNKNOWN_ACTION) + return; + + try { + if (Services.prefs.getBoolPref("offline-apps.allow_by_default")) { + // All pages can use offline capabilities, no need to ask the user + return; + } + } catch(e) { + // This pref isn't set by default, ignore failures + } + + let host = currentURI.asciiHost; + let notificationID = "offline-app-requested-" + host; + + let strings = Strings.browser; + let buttons = [{ + label: strings.GetStringFromName("offlineApps.allow"), + callback: function() { + OfflineApps.allowSite(aContentWindow.document); + } + }, + { + label: strings.GetStringFromName("offlineApps.never"), + callback: function() { + OfflineApps.disallowSite(aContentWindow.document); + } + }, + { + label: strings.GetStringFromName("offlineApps.notNow"), + callback: function() { /* noop */ } + }]; + + let message = strings.formatStringFromName("offlineApps.available2", [host], 1); + NativeWindow.doorhanger.show(message, notificationID, buttons, tab.id); + }, + + allowSite: function(aDocument) { + Services.perms.add(aDocument.documentURIObject, "offline-app", Services.perms.ALLOW_ACTION); + + // When a site is enabled while loading, manifest resources will + // start fetching immediately. This one time we need to do it + // ourselves. + this._startFetching(aDocument); + }, + + disallowSite: function(aDocument) { + Services.perms.add(aDocument.documentURIObject, "offline-app", Services.perms.DENY_ACTION); + }, + + _startFetching: function(aDocument) { + if (!aDocument.documentElement) + return; + + let manifest = aDocument.documentElement.getAttribute("manifest"); + if (!manifest) + return; + + let manifestURI = Services.io.newURI(manifest, aDocument.characterSet, aDocument.documentURIObject); + let updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"].getService(Ci.nsIOfflineCacheUpdateService); + updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject, window); + } +}; + +var IndexedDB = { + _permissionsPrompt: "indexedDB-permissions-prompt", + _permissionsResponse: "indexedDB-permissions-response", + + _quotaPrompt: "indexedDB-quota-prompt", + _quotaResponse: "indexedDB-quota-response", + _quotaCancel: "indexedDB-quota-cancel", + + init: function IndexedDB_init() { + Services.obs.addObserver(this, this._permissionsPrompt, false); + Services.obs.addObserver(this, this._quotaPrompt, false); + Services.obs.addObserver(this, this._quotaCancel, false); + }, + + uninit: function IndexedDB_uninit() { + Services.obs.removeObserver(this, this._permissionsPrompt, false); + Services.obs.removeObserver(this, this._quotaPrompt, false); + Services.obs.removeObserver(this, this._quotaCancel, false); + }, + + observe: function IndexedDB_observe(subject, topic, data) { + if (topic != this._permissionsPrompt && + topic != this._quotaPrompt && + topic != this._quotaCancel) { + throw new Error("Unexpected topic!"); + } + + let requestor = subject.QueryInterface(Ci.nsIInterfaceRequestor); + + let contentWindow = requestor.getInterface(Ci.nsIDOMWindow); + let contentDocument = contentWindow.document; + let browser = BrowserApp.getBrowserForWindow(contentWindow); + if (!browser) + return; + + let host = contentDocument.documentURIObject.asciiHost; + + let strings = Strings.browser; + + let message, responseTopic; + if (topic == this._permissionsPrompt) { + message = strings.formatStringFromName("offlineApps.available2", [host], 1); + responseTopic = this._permissionsResponse; + } else if (topic == this._quotaPrompt) { + message = strings.formatStringFromName("indexedDBQuota.wantsTo", [ host, data ], 2); + responseTopic = this._quotaResponse; + } else if (topic == this._quotaCancel) { + responseTopic = this._quotaResponse; + } + + let notificationID = responseTopic + host; + let tab = BrowserApp.getTabForBrowser(browser); + let observer = requestor.getInterface(Ci.nsIObserver); + + if (topic == this._quotaCancel) { + NativeWindow.doorhanger.hide(notificationID, tab.id); + observer.observe(null, responseTopic, Ci.nsIPermissionManager.UNKNOWN_ACTION); + return; + } + + let buttons = [{ + label: strings.GetStringFromName("offlineApps.allow"), + callback: function() { + observer.observe(null, responseTopic, Ci.nsIPermissionManager.ALLOW_ACTION); + } + }, + { + label: strings.GetStringFromName("offlineApps.never"), + callback: function() { + observer.observe(null, responseTopic, Ci.nsIPermissionManager.DENY_ACTION); + } + }, + { + label: strings.GetStringFromName("offlineApps.notNow"), + callback: function() { + observer.observe(null, responseTopic, Ci.nsIPermissionManager.UNKNOWN_ACTION); + } + }]; + + NativeWindow.doorhanger.show(message, notificationID, buttons, tab.id); + } +}; + +var ConsoleAPI = { + init: function init() { + Services.obs.addObserver(this, "console-api-log-event", false); + }, + + uninit: function uninit() { + Services.obs.removeObserver(this, "console-api-log-event", false); + }, + + observe: function observe(aMessage, aTopic, aData) { + aMessage = aMessage.wrappedJSObject; + + let mappedArguments = Array.map(aMessage.arguments, this.formatResult, this); + let joinedArguments = Array.join(mappedArguments, " "); + + if (aMessage.level == "error" || aMessage.level == "warn") { + let flag = (aMessage.level == "error" ? Ci.nsIScriptError.errorFlag : Ci.nsIScriptError.warningFlag); + let consoleMsg = Cc["@mozilla.org/scripterror;1"].createInstance(Ci.nsIScriptError); + consoleMsg.init(joinedArguments, null, null, 0, 0, flag, "content javascript"); + Services.console.logMessage(consoleMsg); + } else if (aMessage.level == "trace") { + let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); + let args = aMessage.arguments; + let filename = this.abbreviateSourceURL(args[0].filename); + let functionName = args[0].functionName || bundle.GetStringFromName("stacktrace.anonymousFunction"); + let lineNumber = args[0].lineNumber; + + let body = bundle.formatStringFromName("stacktrace.outputMessage", [filename, functionName, lineNumber], 3); + body += "\n"; + args.forEach(function(aFrame) { + let functionName = aFrame.functionName || bundle.GetStringFromName("stacktrace.anonymousFunction"); + body += " " + aFrame.filename + " :: " + functionName + " :: " + aFrame.lineNumber + "\n"; + }); + + Services.console.logStringMessage(body); + } else if (aMessage.level == "time" && aMessage.arguments) { + let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); + let body = bundle.formatStringFromName("timer.start", [aMessage.arguments.name], 1); + Services.console.logStringMessage(body); + } else if (aMessage.level == "timeEnd" && aMessage.arguments) { + let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); + let body = bundle.formatStringFromName("timer.end", [aMessage.arguments.name, aMessage.arguments.duration], 2); + Services.console.logStringMessage(body); + } else if (["group", "groupCollapsed", "groupEnd"].indexOf(aMessage.level) != -1) { + // Do nothing yet + } else { + Services.console.logStringMessage(joinedArguments); + } + }, + + getResultType: function getResultType(aResult) { + let type = aResult === null ? "null" : typeof aResult; + if (type == "object" && aResult.constructor && aResult.constructor.name) + type = aResult.constructor.name; + return type.toLowerCase(); + }, + + formatResult: function formatResult(aResult) { + let output = ""; + let type = this.getResultType(aResult); + switch (type) { + case "string": + case "boolean": + case "date": + case "error": + case "number": + case "regexp": + output = aResult.toString(); + break; + case "null": + case "undefined": + output = type; + break; + default: + if (aResult.toSource) { + try { + output = aResult.toSource(); + } catch (ex) { } + } + if (!output || output == "({})") { + output = aResult.toString(); + } + break; + } + + return output; + }, + + abbreviateSourceURL: function abbreviateSourceURL(aSourceURL) { + // Remove any query parameters. + let hookIndex = aSourceURL.indexOf("?"); + if (hookIndex > -1) + aSourceURL = aSourceURL.substring(0, hookIndex); + + // Remove a trailing "/". + if (aSourceURL[aSourceURL.length - 1] == "/") + aSourceURL = aSourceURL.substring(0, aSourceURL.length - 1); + + // Remove all but the last path component. + let slashIndex = aSourceURL.lastIndexOf("/"); + if (slashIndex > -1) + aSourceURL = aSourceURL.substring(slashIndex + 1); + + return aSourceURL; + } +}; diff --git a/mobile/android/chrome/content/browser.xul b/mobile/android/chrome/content/browser.xul new file mode 100644 index 000000000000..30aa819f16ec --- /dev/null +++ b/mobile/android/chrome/content/browser.xul @@ -0,0 +1,15 @@ + + + + + + + diff --git a/mobile/android/chrome/content/cursor.css b/mobile/android/chrome/content/cursor.css new file mode 100644 index 000000000000..1a2038a4d5cf --- /dev/null +++ b/mobile/android/chrome/content/cursor.css @@ -0,0 +1,47 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Mobile Browser. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +@namespace xul url(http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul); +@namespace html url(http://www.w3.org/1999/xhtml); + +xul|*:-moz-system-metric(touch-enabled) { + cursor: none !important; +} + +html|*:-moz-system-metric(touch-enabled) { + cursor: none !important; +} diff --git a/mobile/android/chrome/content/downloads.js b/mobile/android/chrome/content/downloads.js new file mode 100644 index 000000000000..9184832d23f7 --- /dev/null +++ b/mobile/android/chrome/content/downloads.js @@ -0,0 +1,217 @@ +// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Mobile Browser. + * + * The Initial Developer of the Original Code is Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * Alex Pakhotin + * Margaret Leibovic + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const URI_GENERIC_ICON_DOWNLOAD = "drawable://alertdownloads"; + +var Downloads = { + _initialized: false, + _dlmgr: null, + _progressAlert: null, + + _getLocalFile: function dl__getLocalFile(aFileURI) { + // if this is a URL, get the file from that + // XXX it's possible that using a null char-set here is bad + const fileUrl = Services.io.newURI(aFileURI, null, null).QueryInterface(Ci.nsIFileURL); + return fileUrl.file.clone().QueryInterface(Ci.nsILocalFile); + }, + + init: function dl_init() { + if (this._initialized) + return; + this._initialized = true; + + // Monitor downloads and display alerts + var os = Services.obs; + os.addObserver(this, "dl-start", true); + os.addObserver(this, "dl-failed", true); + os.addObserver(this, "dl-done", true); + os.addObserver(this, "dl-blocked", true); + os.addObserver(this, "dl-dirty", true); + os.addObserver(this, "dl-cancel", true); + }, + + openDownload: function dl_openDownload(aFileURI) { + let f = this._getLocalFile(aFileURI); + try { + f.launch(); + } catch (ex) { } + }, + + cancelDownload: function dl_cancelDownload(aDownload) { + this._dlmgr.cancelDownload(aDownload.id); + + let fileURI = aDownload.target.spec; + let f = this._getLocalFile(fileURI); + if (f.exists()) + f.remove(false); + }, + + showAlert: function dl_showAlert(aDownload, aMessage, aTitle, aIcon) { + let self = this; + + // Use this flag to make sure we only show one prompt at a time + let cancelPrompt = false; + + // Callback for tapping on the alert popup + let observer = { + observe: function (aSubject, aTopic, aData) { + if (aTopic == "alertclickcallback") { + if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_FINISHED) { + // Only open the downloaded file if the download is complete + self.openDownload(aDownload.target.spec); + } else if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING && + !cancelPrompt) { + cancelPrompt = true; + // Open a prompt that offers a choice to cancel the download + let title = Strings.browser.GetStringFromName("downloadCancelPromptTitle"); + let message = Strings.browser.GetStringFromName("downloadCancelPromptMessage"); + let flags = Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_YES + + Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_NO; + + let choice = Services.prompt.confirmEx(null, title, message, flags, + null, null, null, null, {}); + if (choice == 0) + self.cancelDownload(aDownload); + cancelPrompt = false; + } + } + } + }; + + if (!aTitle) + aTitle = Strings.browser.GetStringFromName("alertDownloads"); + if (!aIcon) + aIcon = URI_GENERIC_ICON_DOWNLOAD; + + var notifier = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService); + notifier.showAlertNotification(aIcon, aTitle, aMessage, true, "", observer, + aDownload.target.spec.replace("file:", "download:")); + }, + + observe: function dl_observe(aSubject, aTopic, aData) { + let msgKey = ""; + if (aTopic == "dl-start") { + msgKey = "alertDownloadsStart"; + if (!this._progressAlert) { + if (!this._dlmgr) + this._dlmgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager); + this._progressAlert = new AlertDownloadProgressListener(); + this._dlmgr.addListener(this._progressAlert); + + NativeWindow.toast.show(Strings.browser.GetStringFromName("alertDownloadsToast"), "long"); + } + } else if (aTopic == "dl-done") { + msgKey = "alertDownloadsDone"; + } + + if (msgKey) { + let download = aSubject.QueryInterface(Ci.nsIDownload); + this.showAlert(download, Strings.browser.formatStringFromName(msgKey, [download.displayName], 1)); + } + }, + + QueryInterface: function (aIID) { + if (!aIID.equals(Ci.nsIObserver) && + !aIID.equals(Ci.nsISupportsWeakReference) && + !aIID.equals(Ci.nsISupports)) + throw Components.results.NS_ERROR_NO_INTERFACE; + return this; + } +}; + +// AlertDownloadProgressListener is used to display progress in the alert notifications. +function AlertDownloadProgressListener() { } + +AlertDownloadProgressListener.prototype = { + ////////////////////////////////////////////////////////////////////////////// + //// nsIDownloadProgressListener + onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress, aDownload) { + let strings = Strings.browser; + let availableSpace = -1; + try { + // diskSpaceAvailable is not implemented on all systems + let availableSpace = aDownload.targetFile.diskSpaceAvailable; + } catch(ex) { } + let contentLength = aDownload.size; + if (availableSpace > 0 && contentLength > 0 && contentLength > availableSpace) { + Downloads.showAlert(aDownload, strings.GetStringFromName("alertDownloadsNoSpace"), + strings.GetStringFromName("alertDownloadsSize")); + + Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager).cancelDownload(aDownload.id); + } + + if (aDownload.percentComplete == -1) { + // Undetermined progress is not supported yet + return; + } + let alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService); + let progressListener = alertsService.QueryInterface(Ci.nsIAlertsProgressListener); + let notificationName = aDownload.target.spec.replace("file:", "download:"); + progressListener.onProgress(notificationName, aDownload.percentComplete, 100); + }, + + onDownloadStateChange: function(aState, aDownload) { + let state = aDownload.state; + switch (state) { + case Ci.nsIDownloadManager.DOWNLOAD_FAILED: + case Ci.nsIDownloadManager.DOWNLOAD_CANCELED: + case Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_PARENTAL: + case Ci.nsIDownloadManager.DOWNLOAD_DIRTY: + case Ci.nsIDownloadManager.DOWNLOAD_FINISHED: { + let alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService); + let progressListener = alertsService.QueryInterface(Ci.nsIAlertsProgressListener); + let notificationName = aDownload.target.spec.replace("file:", "download:"); + progressListener.onCancel(notificationName); + break; + } + } + }, + + onStateChange: function(aWebProgress, aRequest, aState, aStatus, aDownload) { }, + onSecurityChange: function(aWebProgress, aRequest, aState, aDownload) { }, + + ////////////////////////////////////////////////////////////////////////////// + //// nsISupports + QueryInterface: function (aIID) { + if (!aIID.equals(Ci.nsIDownloadProgressListener) && + !aIID.equals(Ci.nsISupports)) + throw Components.results.NS_ERROR_NO_INTERFACE; + return this; + } +}; diff --git a/mobile/android/chrome/content/exceptions.js b/mobile/android/chrome/content/exceptions.js new file mode 100644 index 000000000000..eb2bcdb58c58 --- /dev/null +++ b/mobile/android/chrome/content/exceptions.js @@ -0,0 +1,158 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Johnathan Nightingale + * Ehsan Akhgari + * Steffen Imhof + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +let Cc = Components.classes; +let Ci = Components.interfaces; + +/** + A class to add exceptions to override SSL certificate problems. The functionality + itself is borrowed from exceptionDialog.js. +*/ +function SSLExceptions() { + this._overrideService = Cc["@mozilla.org/security/certoverride;1"] + .getService(Ci.nsICertOverrideService); +} + + +SSLExceptions.prototype = { + _overrideService: null, + _sslStatus: null, + + getInterface: function SSLE_getInterface(aIID) { + return this.QueryInterface(aIID); + }, + QueryInterface: function SSLE_QueryInterface(aIID) { + if (aIID.equals(Ci.nsIBadCertListener2) || + aIID.equals(Ci.nsISupports)) + return this; + + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + /** + To collect the SSL status we intercept the certificate error here + and store the status for later use. + */ + notifyCertProblem: function SSLE_notifyCertProblem(socketInfo, sslStatus, targetHost) { + this._sslStatus = sslStatus.QueryInterface(Ci.nsISSLStatus); + return true; // suppress error UI + }, + + /** + Returns true if the private browsing mode is currently active + */ + _inPrivateBrowsingMode: function SSLE_inPrivateBrowsingMode() { + try { + var pb = Cc["@mozilla.org/privatebrowsing;1"].getService(Ci.nsIPrivateBrowsingService); + return pb.privateBrowsingEnabled; + } catch (ex) {} + return false; + }, + + /** + Attempt to download the certificate for the location specified to get the SSLState + for the certificate and the errors. + */ + _checkCert: function SSLE_checkCert(aURI) { + this._sslStatus = null; + + var req = new XMLHttpRequest(); + try { + if(aURI) { + req.open("GET", aURI.prePath, false); + req.channel.notificationCallbacks = this; + req.send(null); + } + } catch (e) { + // We *expect* exceptions if there are problems with the certificate + // presented by the site. Log it, just in case, but we can proceed here, + // with appropriate sanity checks + Components.utils.reportError("Attempted to connect to a site with a bad certificate in the add exception dialog. " + + "This results in a (mostly harmless) exception being thrown. " + + "Logged for information purposes only: " + e); + } + + return this._sslStatus; + }, + + /** + Internal method to create an override. + */ + _addOverride: function SSLE_addOverride(aURI, temporary) { + var SSLStatus = this._checkCert(aURI); + var certificate = SSLStatus.serverCert; + + var flags = 0; + + // in private browsing do not store exceptions permanently ever + if (this._inPrivateBrowsingMode()) { + temporary = true; + } + + if(SSLStatus.isUntrusted) + flags |= this._overrideService.ERROR_UNTRUSTED; + if(SSLStatus.isDomainMismatch) + flags |= this._overrideService.ERROR_MISMATCH; + if(SSLStatus.isNotValidAtThisTime) + flags |= this._overrideService.ERROR_TIME; + + this._overrideService.rememberValidityOverride( + aURI.asciiHost, + aURI.port, + certificate, + flags, + temporary); + }, + + /** + Creates a permanent exception to override all overridable errors for + the given URL. + */ + addPermanentException: function SSLE_addPermanentException(aURI) { + this._addOverride(aURI, false); + }, + + /** + Creates a temporary exception to override all overridable errors for + the given URL. + */ + addTemporaryException: function SSLE_addTemporaryException(aURI) { + this._addOverride(aURI, true); + } +}; diff --git a/mobile/android/chrome/content/languages.properties b/mobile/android/chrome/content/languages.properties new file mode 100644 index 000000000000..378a11a84568 --- /dev/null +++ b/mobile/android/chrome/content/languages.properties @@ -0,0 +1,110 @@ +# LOCALIZATION NOTE: do not localize +af=Afrikaans +ak=Akan +ar=عربي +as=অসমীয়া +ast-ES=Asturianu +be=Беларуская +bg=Български +bn-BD=বাংলা (বাংলাদেশ) +bn-IN=বাংলা (ভারত) +br-FR=Brezhoneg +ca=català +ca-valencia=català (valencià) +cs=Čeština +cy=Cymraeg +da=Dansk +de=Deutsch +de-AT=Deutsch (Österreich) +de-CH=Deutsch (Schweiz) +de-DE=Deutsch (Deutschland) +el=Ελληνικά +en-AU=English (Australian) +en-CA=English (Canadian) +en-GB=English (British) +en-NZ=English (New Zealand) +en-US=English (US) +en-ZA=English (South African) +eo=Esperanto +es-AR=Español (de Argentina) +es-CL=Español (de Chile) +es-ES=Español (de España) +es-MX=Español (de México) +et=Eesti keel +eu=Euskara +fa=فارسی +fi=suomi +fr=Français +fur-IT=Furlan +fy-NL=Frysk +ga-IE=Gaeilge +gl=Galego +gu-IN=ગુજરાતી +he=עברית +hi=हिन्दी +hi-IN=हिन्दी (भारत) +hr=Hrvatski +hsb=Hornjoserbsce +hu=Magyar +hy-AM=Հայերեն +id=Bahasa Indonesia +is=íslenska +it=Italiano +ja=日本語 +ka=ქართული +kk=Қазақ +kn=ಕನ್ನಡ +ko=한국어 +ku=Kurdî +la=Latina +lt=lietuvių +lv=Latviešu +mg=Malagasy +mi=Māori (Aotearoa) +mk=Македонски +ml=മലയാളം +mn=Монгол +mr=मराठी +nb-NO=Norsk bokmål +ne-NP=नेपाली +nl=Nederlands +nn-NO=Norsk nynorsk +nr=isiNdebele Sepumalanga +nso=Sepedi +oc=occitan (lengadocian) +or=ଓଡ଼ିଆ +pa-IN=ਪੰਜਾਬੀ +pl=Polski +pt-BR=Português (do Brasil) +pt-PT=Português (Europeu) +rm=rumantsch +ro=română +ru=Русский +rw=Ikinyarwanda +si=සිංහල +sk=slovenčina +sl=slovensko +sq=Shqip +sr=Српски +sr-Latn=Srpski +ss=Siswati +st=Sesotho +sv-SE=Svenska +ta=தமிழ் +ta-IN=தமிழ் (இந்தியா) +ta-LK=தமிழ் (இலங்கை) +te=తెలుగు +th=ไทย +tn=Setswana +tr=Türkçe +ts=Mutsonga +tt-RU=Tatarça +uk=Українська +ur=اُردو +ve=Tshivenḓa +vi=Tiếng Việt +wo=Wolof +xh=isiXhosa +zh-CN=中文 (简体) +zh-TW=正體中文 (繁體) +zu=isiZulu diff --git a/mobile/android/chrome/content/netError.xhtml b/mobile/android/chrome/content/netError.xhtml new file mode 100644 index 000000000000..e5937cbcce6a --- /dev/null +++ b/mobile/android/chrome/content/netError.xhtml @@ -0,0 +1,402 @@ + + + + %htmlDTD; + + %netErrorDTD; + + %globalDTD; +]> + + + + + + + &loadError.label; + + + + + + + + + + +
+
+

&generic.title;

+

&dnsNotFound.title;

+

&fileNotFound.title;

+

&malformedURI.title;

+

&protocolNotFound.title;

+

&connectionFailure.title;

+

&netTimeout.title;

+

&redirectLoop.title;

+

&unknownSocketType.title;

+

&netReset.title;

+

¬Cached.title;

+

&netOffline.title;

+

&netInterrupt.title;

+

&deniedPortAccess.title;

+

&proxyResolveFailure.title;

+

&proxyConnectFailure.title;

+

&contentEncodingError.title;

+

&unsafeContentType.title;

+

&nssFailure2.title;

+

&nssBadCert.title;

+

&cspFrameAncestorBlocked.title;

+

&remoteXUL.title;

+

&corruptedContentError.title;

+
+
+
&generic.longDesc;
+
&dnsNotFound.longDesc2;
+
&fileNotFound.longDesc;
+
&malformedURI.longDesc;
+
&protocolNotFound.longDesc;
+
&connectionFailure.longDesc;
+
&netTimeout.longDesc;
+
&redirectLoop.longDesc;
+
&unknownSocketType.longDesc;
+
&netReset.longDesc;
+
¬Cached.longDesc;
+
&netOffline.longDesc2;
+
&netInterrupt.longDesc;
+
&deniedPortAccess.longDesc;
+
&proxyResolveFailure.longDesc2;
+
&proxyConnectFailure.longDesc;
+
&contentEncodingError.longDesc;
+
&unsafeContentType.longDesc;
+
&nssFailure2.longDesc;
+
&nssBadCert.longDesc2;
+
&cspFrameAncestorBlocked.longDesc;
+
&remoteXUL.longDesc;
+
&corruptedContentError.longDesc;
+
+
+ + +
+

+

+ + +
+ + +
+ + +
+

+

+ + +
+ + +
+ &securityOverride.linkText; + +
+
+ + + + +
+ + + + + + diff --git a/mobile/android/chrome/content/sanitize.js b/mobile/android/chrome/content/sanitize.js new file mode 100644 index 000000000000..db411fbc91d0 --- /dev/null +++ b/mobile/android/chrome/content/sanitize.js @@ -0,0 +1,338 @@ +// -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Firefox Sanitizer. + * + * The Initial Developer of the Original Code is + * Ben Goodger. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ben Goodger + * Giorgio Maone + * Matt Brubeck + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +function Sanitizer() {} +Sanitizer.prototype = { + // warning to the caller: this one may raise an exception (e.g. bug #265028) + clearItem: function (aItemName) + { + if (this.items[aItemName].canClear) + this.items[aItemName].clear(); + }, + + canClearItem: function (aItemName) + { + return this.items[aItemName].canClear; + }, + + _prefDomain: "privacy.item.", + getNameFromPreference: function (aPreferenceName) + { + return aPreferenceName.substr(this._prefDomain.length); + }, + + /** + * Deletes privacy sensitive data in a batch, according to user preferences + * + * @returns null if everything's fine; an object in the form + * { itemName: error, ... } on (partial) failure + */ + sanitize: function () + { + var branch = Services.prefs.getBranch(this._prefDomain); + var errors = null; + for (var itemName in this.items) { + var item = this.items[itemName]; + if ("clear" in item && item.canClear && branch.getBoolPref(itemName)) { + // Some of these clear() may raise exceptions (see bug #265028) + // to sanitize as much as possible, we catch and store them, + // rather than fail fast. + // Callers should check returned errors and give user feedback + // about items that could not be sanitized + try { + item.clear(); + } catch(er) { + if (!errors) + errors = {}; + errors[itemName] = er; + dump("Error sanitizing " + itemName + ": " + er + "\n"); + } + } + } + return errors; + }, + + items: { + cache: { + clear: function () + { + var cacheService = Cc["@mozilla.org/network/cache-service;1"].getService(Ci.nsICacheService); + try { + cacheService.evictEntries(Ci.nsICache.STORE_ANYWHERE); + } catch(er) {} + + let imageCache = Cc["@mozilla.org/image/cache;1"].getService(Ci.imgICache); + try { + imageCache.clearCache(false); // true=chrome, false=content + } catch(er) {} + }, + + get canClear() + { + return true; + } + }, + + cookies: { + clear: function () + { + var cookieMgr = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager); + cookieMgr.removeAll(); + }, + + get canClear() + { + return true; + } + }, + + geolocation: { + clear: function () + { + // clear any network geolocation provider sessions + try { + var branch = Services.prefs.getBranch("geo.wifi.access_token."); + branch.deleteBranch(""); + + branch = Services.prefs.getBranch("geo.request.remember."); + branch.deleteBranch(""); + } catch (e) {dump(e);} + }, + + get canClear() + { + return true; + } + }, + + siteSettings: { + clear: function () + { + // Clear site-specific permissions like "Allow this site to open popups" + Services.perms.removeAll(); + + // Clear site-specific settings like page-zoom level + var cps = Cc["@mozilla.org/content-pref/service;1"].getService(Ci.nsIContentPrefService); + cps.removeGroupedPrefs(); + + // Clear "Never remember passwords for this site", which is not handled by + // the permission manager + var pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager); + var hosts = pwmgr.getAllDisabledHosts({}) + for each (var host in hosts) { + pwmgr.setLoginSavingEnabled(host, true); + } + }, + + get canClear() + { + return true; + } + }, + + offlineApps: { + clear: function () + { + var cacheService = Cc["@mozilla.org/network/cache-service;1"].getService(Ci.nsICacheService); + try { + cacheService.evictEntries(Ci.nsICache.STORE_OFFLINE); + } catch(er) {} + + var storage = Cc["@mozilla.org/dom/storagemanager;1"].getService(Ci.nsIDOMStorageManager); + storage.clearOfflineApps(); + }, + + get canClear() + { + return true; + } + }, + + history: { + clear: function () + { + var globalHistory = Cc["@mozilla.org/browser/global-history;2"].getService(Ci.nsIBrowserHistory); + globalHistory.removeAllPages(); + + try { + Services.obs.notifyObservers(null, "browser:purge-session-history", ""); + } + catch (e) { } + + // Clear last URL of the Open Web Location dialog + try { + Services.prefs.clearUserPref("general.open_location.last_url"); + } + catch (e) { } + }, + + get canClear() + { + // bug 347231: Always allow clearing history due to dependencies on + // the browser:purge-session-history notification. (like error console) + return true; + } + }, + + formdata: { + clear: function () + { + //Clear undo history of all searchBars + var windows = Services.wm.getEnumerator("navigator:browser"); + while (windows.hasMoreElements()) { + var searchBar = windows.getNext().document.getElementById("searchbar"); + if (searchBar) { + searchBar.value = ""; + searchBar.textbox.editor.transactionManager.clear(); + } + } + + var formHistory = Cc["@mozilla.org/satchel/form-history;1"].getService(Ci.nsIFormHistory2); + formHistory.removeAllEntries(); + }, + + get canClear() + { + var formHistory = Cc["@mozilla.org/satchel/form-history;1"].getService(Ci.nsIFormHistory2); + return formHistory.hasEntries; + } + }, + + downloads: { + clear: function () + { + var dlMgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager); + dlMgr.cleanUp(); + }, + + get canClear() + { + var dlMgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager); + return dlMgr.canCleanUp; + } + }, + + passwords: { + clear: function () + { + var pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager); + pwmgr.removeAllLogins(); + }, + + get canClear() + { + var pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager); + var count = pwmgr.countLogins("", "", ""); // count all logins + return (count > 0); + } + }, + + sessions: { + clear: function () + { + // clear all auth tokens + var sdr = Cc["@mozilla.org/security/sdr;1"].getService(Ci.nsISecretDecoderRing); + sdr.logoutAndTeardown(); + + // clear plain HTTP auth sessions + var authMgr = Cc['@mozilla.org/network/http-auth-manager;1'].getService(Ci.nsIHttpAuthManager); + authMgr.clearAll(); + }, + + get canClear() + { + return true; + } + } + } +}; + + +// "Static" members +Sanitizer.prefDomain = "privacy.sanitize."; +Sanitizer.prefShutdown = "sanitizeOnShutdown"; +Sanitizer.prefDidShutdown = "didShutdownSanitize"; + +Sanitizer._prefs = null; +Sanitizer.__defineGetter__("prefs", function() +{ + return Sanitizer._prefs ? Sanitizer._prefs + : Sanitizer._prefs = Cc["@mozilla.org/preferences-service;1"] + .getService(Ci.nsIPrefService) + .getBranch(Sanitizer.prefDomain); +}); + +/** + * Deletes privacy sensitive data in a batch, optionally showing the + * sanitize UI, according to user preferences + * + * @returns null if everything's fine + * an object in the form { itemName: error, ... } on (partial) failure + */ +Sanitizer.sanitize = function() +{ + return new Sanitizer().sanitize(); +}; + +Sanitizer.onStartup = function() +{ + // we check for unclean exit with pending sanitization + Sanitizer._checkAndSanitize(); +}; + +Sanitizer.onShutdown = function() +{ + // we check if sanitization is needed and perform it + Sanitizer._checkAndSanitize(); +}; + +// this is called on startup and shutdown, to perform pending sanitizations +Sanitizer._checkAndSanitize = function() +{ + const prefs = Sanitizer.prefs; + if (prefs.getBoolPref(Sanitizer.prefShutdown) && + !prefs.prefHasUserValue(Sanitizer.prefDidShutdown)) { + // this is a shutdown or a startup after an unclean exit + Sanitizer.sanitize() || // sanitize() returns null on full success + prefs.setBoolPref(Sanitizer.prefDidShutdown, true); + } +}; + + diff --git a/mobile/android/chrome/jar.mn b/mobile/android/chrome/jar.mn new file mode 100644 index 000000000000..0c71e06437fa --- /dev/null +++ b/mobile/android/chrome/jar.mn @@ -0,0 +1,30 @@ +#filter substitution + +chrome.jar: +% content browser %content/ + +* content/about.xhtml (content/about.xhtml) + content/config.xhtml (content/config.xhtml) + content/aboutAddons.xhtml (content/aboutAddons.xhtml) + content/aboutCertError.xhtml (content/aboutCertError.xhtml) + content/aboutHome.xhtml (content/aboutHome.xhtml) +* content/aboutRights.xhtml (content/aboutRights.xhtml) + content/blockedSite.xhtml (content/blockedSite.xhtml) + content/languages.properties (content/languages.properties) +* content/browser.xul (content/browser.xul) +* content/browser.js (content/browser.js) +* content/bindings.xml (content/bindings.xml) + content/bindings/checkbox.xml (content/bindings/checkbox.xml) + content/bindings/console.xml (content/bindings/console.xml) + content/bindings/dialog.xml (content/bindings/dialog.xml) + content/browser.css (content/browser.css) + content/cursor.css (content/cursor.css) +% content branding %content/branding/ + content/sanitize.js (content/sanitize.js) + content/exceptions.js (content/exceptions.js) +* content/downloads.js (content/downloads.js) + content/netError.xhtml (content/netError.xhtml) + +% override chrome://global/content/config.xul chrome://browser/content/config.xhtml +% override chrome://global/content/netError.xhtml chrome://browser/content/netError.xhtml +% override chrome://mozapps/content/extensions/extensions.xul chrome://browser/content/aboutAddons.xhtml diff --git a/mobile/android/chrome/tests/Makefile.in b/mobile/android/chrome/tests/Makefile.in new file mode 100644 index 000000000000..5780f544d803 --- /dev/null +++ b/mobile/android/chrome/tests/Makefile.in @@ -0,0 +1,142 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla. +# +# The Initial Developer of the Original Code is +# the Mozilla Foundation . +# Portions created by the Initial Developer are Copyright (C) 2008 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Finkle +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +relativesrcdir = mobile/chrome/tests +TESTXPI = $(CURDIR)/$(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)/addons +ADDONSRC = $(srcdir)/addons + +include $(DEPTH)/config/autoconf.mk +include $(topsrcdir)/config/rules.mk + +_BROWSER_FILES = \ + head.js \ + remote_autocomplete.js \ + remote_contentpopup.js \ + remote_head.js \ + remote_focus.js \ + remote_forms.js \ + remote_formsZoom.js \ + remote_vkb.js \ + browser_addons.js \ + browser_addons_locales.js \ + browser_appmenu.js \ + browser_autocompletesearch.js \ + $(info browser_awesomescreen.js is disabled because the localepicker is disabled (bug 693524)) \ + browser_blank_01.html \ + browser_blank_02.html \ + browser_blank_03.html \ + browser_bookmarks.js \ + browser_contacts.js \ + browser_dragger.js \ + browser_escape.js \ + browser_find.js \ + browser_focus.html \ + browser_focus.js \ + browser_forms.html \ + $(warning browser_forms.js disabled due to failures) \ + browser_formsZoom.html \ + $(warning browser_formsZoom.js disabled due to failures) \ + $(info browser_history.js is disabled because it is random orange on XUL fennec (bug 700537)) \ + $(info browser_localepicker.js is disabled because the localepicker is disabled (bug 694047)) \ + browser_localepicker_escape.js \ + browser_mainui.js \ + browser_preferences_text.js \ + browser_preferences_fulltoggle.js \ + browser_rect.js \ + $(info browser_rememberPassword.js is disabled because it is random orange on XUL fennec (bug 698387)) \ + browser_scroll.js \ + browser_scroll.html \ + browser_scrollbar.js \ + browser_select.html \ + browser_select.js \ + browser_sessionstore.js \ + $(info browser_tabs.js is disabled because it is random orange on XUL fennec (bug 700537)) \ + $(info browser_tapping.js is disabled because it is random orange on XUL fennec (bug 698387)) \ + $(info browser_tap_content.html is disabled because it is random orange on XUL fennec (bug 698387)) \ + browser_tapping_edit.js \ + browser_tap_contentedit.html \ + browser_test.js \ + browser_vkb.js \ + $(warning browser_viewport.js disabled due to failures) \ + browser_viewport.sjs \ + browser_scrollbar.sjs \ + browser_title.sjs \ + $(info browser_thumbnails.js is disabled because it is random orange on XUL fennec (bug 700537)) \ + browser_install.xml \ + browser_upgrade.rdf\ + browser_localerepository.js \ + browser_localerepository_pref.js \ + browser_localerepository_buildid.js \ + locales_list.sjs \ + mock_autocomplete.json\ + $(NULL) + +ifneq ($(OS_TARGET),Android) +_BROWSER_FILES += \ + browser_autocomplete.html \ + browser_autocomplete.js \ + browser_bookmarks_star.js \ + browser_bookmarks_tags.js \ + browser_click_content.html \ + browser_click_content.js \ + browser_contentpopup.html \ + browser_contentpopup.js \ + browser_navigation.js \ + $(NULL) +ifndef MOZ_PLATFORM_MAEMO +_BROWSER_FILES += browser_sidebars.js +endif +endif + +libs:: $(_BROWSER_FILES) + $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir) + +libs:: + rm -rf $(TESTXPI) + $(NSINSTALL) -D $(TESTXPI) + if [ -d $(ADDONSRC) ]; then \ + $(EXIT_ON_ERROR) \ + for dir in $(ADDONSRC)/*; do \ + base=`basename $$dir` ; \ + (cd $$dir && zip $(TESTXPI)/$$base.xpi *) \ + done \ + fi + diff --git a/mobile/android/chrome/tests/addons/browser_install1_1/bootstrap.js b/mobile/android/chrome/tests/addons/browser_install1_1/bootstrap.js new file mode 100644 index 000000000000..7b86e419a3b3 --- /dev/null +++ b/mobile/android/chrome/tests/addons/browser_install1_1/bootstrap.js @@ -0,0 +1,9 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function install(data, reason) {} +function startup(data, reason) {} +function shutdown(data, reason) {} +function uninstall(data, reason) {} + diff --git a/mobile/android/chrome/tests/addons/browser_install1_1/install.rdf b/mobile/android/chrome/tests/addons/browser_install1_1/install.rdf new file mode 100644 index 000000000000..0825d11aa0cb --- /dev/null +++ b/mobile/android/chrome/tests/addons/browser_install1_1/install.rdf @@ -0,0 +1,24 @@ + + + + + + addon1@tests.mozilla.org + 1.0 + http://example.com/browser/mobile/chrome/tests/browser_upgrade.rdf + true + + + + toolkit@mozilla.org + 0 + * + + + + + Install Tests + + + diff --git a/mobile/android/chrome/tests/addons/browser_install1_2/install.rdf b/mobile/android/chrome/tests/addons/browser_install1_2/install.rdf new file mode 100644 index 000000000000..945afb22b014 --- /dev/null +++ b/mobile/android/chrome/tests/addons/browser_install1_2/install.rdf @@ -0,0 +1,22 @@ + + + + + + addon2@tests.mozilla.org + 2.0 + + + + toolkit@mozilla.org + 0 + * + + + + + Install Tests 2 + + + diff --git a/mobile/android/chrome/tests/addons/browser_install1_3/install.rdf b/mobile/android/chrome/tests/addons/browser_install1_3/install.rdf new file mode 100644 index 000000000000..2c10f4921628 --- /dev/null +++ b/mobile/android/chrome/tests/addons/browser_install1_3/install.rdf @@ -0,0 +1,23 @@ + + + + + + addon1@tests.mozilla.org + 3.0 + http://example.com/browser/mobile/chrome/tests/browser_upgrade.rdf + + + + toolkit@mozilla.org + 0 + * + + + + + Install Tests + + + diff --git a/mobile/android/chrome/tests/addons/browser_locale1/boostrap.js b/mobile/android/chrome/tests/addons/browser_locale1/boostrap.js new file mode 100644 index 000000000000..7b86e419a3b3 --- /dev/null +++ b/mobile/android/chrome/tests/addons/browser_locale1/boostrap.js @@ -0,0 +1,9 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function install(data, reason) {} +function startup(data, reason) {} +function shutdown(data, reason) {} +function uninstall(data, reason) {} + diff --git a/mobile/android/chrome/tests/addons/browser_locale1/chrome.manifest b/mobile/android/chrome/tests/addons/browser_locale1/chrome.manifest new file mode 100644 index 000000000000..a909200d5b02 --- /dev/null +++ b/mobile/android/chrome/tests/addons/browser_locale1/chrome.manifest @@ -0,0 +1,4 @@ +locale mozapps te-st chrome # locale +locale browser te-st chrome # duplicate locale +locale browser te-st-a chrome # second locale +locale branding te-st-3 chrome # wrong component diff --git a/mobile/android/chrome/tests/addons/browser_locale1/install.rdf b/mobile/android/chrome/tests/addons/browser_locale1/install.rdf new file mode 100644 index 000000000000..ba92a4581869 --- /dev/null +++ b/mobile/android/chrome/tests/addons/browser_locale1/install.rdf @@ -0,0 +1,24 @@ + + + + + + locale1@tests.mozilla.org + 1.0 + 8 + true + + + + toolkit@mozilla.org + 0 + * + + + + + Test Locale + + + diff --git a/mobile/android/chrome/tests/browser_addons.js b/mobile/android/chrome/tests/browser_addons.js new file mode 100644 index 000000000000..7b465e2e5cdf --- /dev/null +++ b/mobile/android/chrome/tests/browser_addons.js @@ -0,0 +1,503 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +Components.utils.import("resource://gre/modules/AddonManager.jsm"); +Components.utils.import("resource://gre/modules/AddonUpdateChecker.jsm"); +Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource://gre/modules/NetUtil.jsm"); + +const RELATIVE_DIR = "browser/mobile/chrome/tests/"; +const TESTROOT = "http://example.com/" + RELATIVE_DIR; +const TESTROOT2 = "http://example.org/" + RELATIVE_DIR; +const PREF_LOGGING_ENABLED = "extensions.logging.enabled"; +const PREF_SEARCH_MAXRESULTS = "extensions.getAddons.maxResults"; +const CHROME_NAME = "mochikit"; +const PREF_AUTOUPDATE_DEFAULT = "extensions.update.autoUpdateDefault" +const PREF_GETADDONS_BROWSESEARCHRESULTS = "extensions.getAddons.search.browseURL"; +const PREF_GETADDONS_GETSEARCHRESULTS = "extensions.getAddons.search.url"; +const PREF_GETADDONS_GETRECOMMENDED = "extensions.getAddons.recommended.url"; +const PREF_GETADDONS_BROWSERECOMMENDED = "extensions.getAddons.recommended.browseURL"; +const PREF_GETADDONS_UPDATE = "extensions.update.url"; +const PREF_ADDONS_LOGGING = "extensions.logging.enabled"; +const PREF_ADDONS_SECURITY = "extensions.checkUpdateSecurity"; +const SEARCH_URL = TESTROOT + "browser_details.xml"; +const ADDON_IMG = "chrome://browser/skin/images/alert-addons-30.png"; + +var addons = [{ + id: "addon1@tests.mozilla.org", + name : "Install Tests", + iconURL: "http://example.com/icon.png", + homepageURL: "http://example.com/", + version: "1.0", + description: "Test add-on", + sourceURL: TESTROOT + "addons/browser_install1_1.xpi", + bootstrapped: true, + willFail: false, + updateIndex: 2, +}, +{ + id: "addon2@tests.mozilla.org", + name : "Install Tests 2", + iconURL: "http://example.com/icon.png", + homepageURL: "http://example.com/", + version: "1.0", + description: "Test add-on 2", + sourceURL: TESTROOT + "addons/browser_install1_2.xpi", + bootstrapped: false, + willFail: false, +}, +{ + id: "addon1@tests.mozilla.org", + name : "Install Tests 3", + iconURL: "http://example.com/icon.png", + homepageURL: "http://example.com/", + version: "1.0", + description: "Test add-on 3", + sourceURL: TESTROOT + "addons/browser_install1_3.xpi", + bootstrapped: false, + willFail: false, +}]; + + +var gPendingTests = []; +var gTestsRun = 0; +var gTestStart = null; +var gDate = new Date(2010, 7, 1); +var gApp = Strings.brand.GetStringFromName("brandShortName"); +var gCategoryUtilities; +var gSearchCount = 0; +var gTestURL = TESTROOT + "browser_blank_01.html"; +var gCurrentTab = null; + +function test() { + waitForExplicitFinish(); + requestLongerTimeout(2); + Services.prefs.setCharPref(PREF_GETADDONS_GETRECOMMENDED, TESTROOT + "browser_install.xml"); + Services.prefs.setCharPref(PREF_GETADDONS_BROWSERECOMMENDED, TESTROOT + "browser_install.xml"); + Services.prefs.setCharPref(PREF_GETADDONS_BROWSESEARCHRESULTS, TESTROOT + "browser_install.xml"); + Services.prefs.setCharPref(PREF_GETADDONS_GETSEARCHRESULTS, TESTROOT + "browser_install.xml"); + Services.prefs.setCharPref(PREF_GETADDONS_UPDATE, TESTROOT + "browser_upgrade.rdf"); + Services.prefs.setBoolPref(PREF_ADDONS_SECURITY, false); + Services.prefs.setBoolPref(PREF_ADDONS_LOGGING, true); + run_next_test(); +} + +function end_test() { + close_manager(); + Services.prefs.clearUserPref(PREF_GETADDONS_GETRECOMMENDED); + Services.prefs.clearUserPref(PREF_GETADDONS_BROWSERECOMMENDED); + Services.prefs.clearUserPref(PREF_GETADDONS_BROWSESEARCHRESULTS); + Services.prefs.clearUserPref(PREF_GETADDONS_GETSEARCHRESULTS); + Services.prefs.clearUserPref(PREF_GETADDONS_UPDATE); + Services.prefs.clearUserPref(PREF_ADDONS_SECURITY); + Services.prefs.clearUserPref(PREF_ADDONS_LOGGING); +} + +registerCleanupFunction(end_test); + +function add_test(test) { + gPendingTests.push(test); +} + +function run_next_test() { + if (gTestsRun > 0) + info("Test " + gTestsRun + " took " + (Date.now() - gTestStart) + "ms"); + + if (!gPendingTests.length) { + finish(); + return; + } + + gTestsRun++; + let test = gPendingTests.shift(); + if (test.name) + info("Running test " + gTestsRun + " (" + test.name + ")"); + else + info("Running test " + gTestsRun); + + gTestStart = Date.now(); + test(); +} + +function checkAttribute(aElt, aAttr, aVal) { + ok(aElt.hasAttribute(aAttr), "Element has " + aAttr + " attribute"); + if(aVal) + is(aElt.getAttribute(aAttr), aVal, "Element has " + aAttr + " attribute with value " + aVal); +} + +function installExtension(elt, aListener) { + elt.parentNode.ensureElementIsVisible(elt); + elt.install.addListener(aListener) + + let button = document.getAnonymousElementByAttribute(elt, "class", "addon-install hide-on-install hide-on-restart"); + ok(!!button, "Extension has install button"); + ExtensionsView.installFromRepo(elt); +} + +function isRestartShown(aShown, isUpdate, aCallback) { + let msg = document.getElementById("addons-messages"); + ok(!!msg, "Have message box"); + + let done = function(aNotification) { + is(!!aNotification, aShown, "Restart exists = " + aShown); + if (aShown && aNotification) { + let showsUpdate = aNotification.label.match(/update/i) != null; + // this test regularly fails due to race conditions here + is(showsUpdate, isUpdate, "Restart shows correct message"); + } + msg.removeAllNotifications(true); + aCallback(); + } + + let notification = msg.getNotificationWithValue("restart-app"); + if (!notification && aShown) { + window.addEventListener("AlertActive", function() { + window.removeEventListener("AlertActive", arguments.callee, true); + notification = msg.getNotificationWithValue("restart-app"); + done(notification); + }, true); + } else { + done(notification); + } +} + +function checkInstallAlert(aShown, aCallback) { + checkAlert(null, "xpinstall", null, aShown, function(aNotifyBox, aNotification) { + if (aShown) { + let button = aNotification.childNodes[0]; + ok(!!button, "Notification has button"); + if (button) + button.click(); + } + aNotifyBox.removeAllNotifications(true); + if (aCallback) + aCallback(); + }); +} + +function checkDownloadNotification(aCallback) { + let msg = /download/i; + checkNotification(/Add-ons/, msg, ADDON_IMG, aCallback); +} + +function checkInstallNotification(aRestart, aCallback) { + let msg = null; + if (aRestart) + msg = /restart/i; + checkNotification(/Add-ons/, msg, ADDON_IMG, aCallback); +} + +function checkNotification(aTitle, aMessage, aIcon, aCallback) { + let doTest = function() { + ok(document.getElementById("alerts-container").classList.contains("showing"), "Alert shown"); + let title = document.getElementById("alerts-title").value; + let msg = document.getElementById("alerts-text").textContent; + let img = document.getElementById("alerts-image").getAttribute("src"); + + if (aTitle) + ok(aTitle.test(title), "Correct title alert shown: " + title); + if (aMessage) + ok(aMessage.test(msg), "Correct message shown: " + msg); + if (aIcon) + is(img, aIcon, "Correct image shown: " + aIcon); + + // make sure this is hidden before another test asks about it + AlertsHelper.container.classList.remove("showing"); + AlertsHelper.container.height = 0; + AlertsHelper.container.hidden = true; + aCallback(); + }; + + let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2); + if (sysInfo.get("device")) + aCallback(); + else + waitFor(doTest, function() { return AlertsHelper.container.hidden == false; }); +} + +function checkAlert(aId, aName, aLabel, aShown, aCallback) { + let msg = null; + if (aId) + msg = document.getElementById(aId); + else + msg = window.getNotificationBox(gCurrentTab.browser); + ok(!!msg, "Have notification box"); + + let haveNotification = function(notify) { + is(!!notify, aShown, "Notification alert exists = " + aShown); + if (notify && aLabel) + ok(aLabel.test(notify.label), "Notification shows correct message"); + if (aCallback) + aCallback(msg, notify); + } + + let notification = msg.getNotificationWithValue(aName); + if (!notification && aShown) { + window.addEventListener("AlertActive", function() { + window.removeEventListener("AlertActive", arguments.callee, true); + notification = msg.getNotificationWithValue(aName); + haveNotification(notification); + }, true); + } else { + haveNotification(notification); + } +} + +function checkAddonListing(aAddon, elt, aType) { + ok(!!elt, "Element exists for addon"); + checkAttribute(elt, "id", "urn:mozilla:item:" + aAddon.id); + checkAttribute(elt, "addonID", aAddon.id); + checkAttribute(elt, "typeName", aType); + checkAttribute(elt, "name", aAddon.name); + checkAttribute(elt, "version", aAddon.version); + if (aType == "search") { + checkAttribute(elt, "iconURL", aAddon.iconURL); + checkAttribute(elt, "description", aAddon.description) + checkAttribute(elt, "homepageURL", aAddon.homepageURL); + checkAttribute(elt, "sourceURL", aAddon.sourceURL); + ok(elt.install, "Extension has install property"); + } +} + +function checkUpdate(aSettings) { + let os = Services.obs; + let ul = new updateListener(aSettings); + os.addObserver(ul, "addon-update-ended", false); + + ExtensionsView.updateAll(); +} + +function get_addon_element(aId) { + return document.getElementById("urn:mozilla:item:" + aId); +} + +function open_manager(aView, aCallback) { + BrowserUI.showPanel("addons-container"); + + ExtensionsView.init(); + ExtensionsView.delayedInit(); + + window.addEventListener("ViewChanged", function() { + window.removeEventListener("ViewChanged", arguments.callee, true); + aCallback(); + }, true); +} + +function close_manager(aCallback) { + let prefsButton = document.getElementById("tool-preferences"); + prefsButton.click(); + + ExtensionsView.clearSection(); + ExtensionsView.clearSection("local"); + ExtensionsView._list = null; + ExtensionsView._restartCount = 0; + BrowserUI.hidePanel(); + + if (aCallback) + aCallback(); +} + +function loadUrl(aURL, aCallback, aNewTab) { + if (aNewTab) + gCurrentTab = Browser.addTab(aURL, true); + else + Browser.loadURI(aURL); + + gCurrentTab.browser.messageManager.addMessageListener("pageshow", function(aMessage) { + if (gCurrentTab.browser.currentURI.spec == aURL) { + gCurrentTab.browser.messageManager.removeMessageListener(aMessage.name, arguments.callee); + if (aCallback) + setTimeout(aCallback, 0); + } + }); +} + +function checkInstallPopup(aName, aCallback) { + testPrompt("Installing Add-on", aName, [ {label: "Install", click: true}, + {label: "Cancel", click: false}], + aCallback); +} + +function testPrompt(aTitle, aMessage, aButtons, aCallback) { + function doTest() { + let prompt = document.getElementById("prompt-confirm-dialog"); + ok(!!prompt, "Prompt shown"); + + if (prompt) { + let title = document.getElementById("prompt-confirm-title"); + let message = document.getElementById("prompt-confirm-message"); + is(aTitle, title.textContent, "Correct title shown"); + is(aMessage, message.textContent, "Correct message shown"); + + let buttons = document.querySelectorAll("#prompt-confirm-buttons-box .prompt-button"); + let clickButton = null; + is(buttons.length, aButtons.length, "Prompt has correct number of buttons"); + if (buttons.length == aButtons.length) { + for (let i = 0; i < buttons.length; i++) { + is(buttons[i].label, aButtons[i].label, "Button has correct label"); + if (aButtons[i].click) + clickButton = buttons[i]; + } + } + if (clickButton) + clickButton.click(); + } + if (aCallback) + aCallback(); + } + + if (!document.getElementById("prompt-confirm-dialog")) { + window.addEventListener("DOMWillOpenModalDialog", function() { + window.removeEventListener("DOMWillOpenModalDialog", arguments.callee, true); + // without this timeout, this can cause the prompt service to fail + setTimeout(doTest, 500); + }, true); + } else { + doTest(); + } +} + +// Installs an addon via the urlbar. +function installFromURLBar(aAddon) { + return function() { + AddonManager.addInstallListener({ + onInstallEnded: function (install) { + AddonManager.removeInstallListener(this); + checkInstallNotification(!aAddon.bootstrapped, function() { + open_manager(true, function() { + isRestartShown(!aAddon.bootstrapped, false, function() { + let elt = get_addon_element(aAddon.id); + if (aAddon.bootstrapped) { + checkAddonListing(aAddon, elt, "local"); + let button = document.getAnonymousElementByAttribute(elt, "anonid", "uninstall-button"); + ok(!!button, "Extension has uninstall button"); + + let updateButton = document.getElementById("addons-update-all"); + is(updateButton.disabled, false, "Update button is enabled"); + + ExtensionsView.uninstall(elt); + + elt = get_addon_element(aAddon.id); + ok(!elt, "Addon element removed during uninstall"); + Browser.closeTab(gCurrentTab); + close_manager(run_next_test); + } else { + ok(!elt, "Extension not in list"); + AddonManager.getAllInstalls(function(aInstalls) { + for(let i = 0; i < aInstalls.length; i++) { + aInstalls[i].cancel(); + } + Browser.closeTab(gCurrentTab); + close_manager(run_next_test); + }); + } + }); + }); + }); + } + }); + loadUrl(gTestURL, function() { + loadUrl(aAddon.sourceURL, null, false); + checkInstallAlert(true, function() { + checkDownloadNotification(function() { + checkInstallPopup(aAddon.name, function() { }); + }); + }); + }, true); + }; +} + +// Installs an addon from the addons pref pane, and then +// updates it if requested. Checks to make sure +// restart notifications are shown at the right time +function installFromAddonsPage(aAddon, aDoUpdate) { + return function() { + open_manager(null, function() { + let elt = get_addon_element(aAddon.id); + checkAddonListing(aAddon, elt); + installExtension(elt, new installListener({ + addon: aAddon, + onComplete: function() { + if (aDoUpdate) { + checkUpdate({ + addon: addons[aAddon.updateIndex], + onComplete: function() { + close_manager(); + run_next_test(); + } + }); + } else { + close_manager(); + run_next_test(); + } + } + })); + }); + } +} + +add_test(installFromURLBar(addons[0])); +add_test(installFromURLBar(addons[1])); +add_test(installFromAddonsPage(addons[0], true)); +add_test(installFromAddonsPage(addons[1], false)); + +function installListener(aSettings) { + this.onComplete = aSettings.onComplete; + this.addon = aSettings.addon; +} + +installListener.prototype = { + onNewInstall : function(install) { }, + onDownloadStarted : function(install) { }, + onDownloadProgress : function(install) { }, + onDownloadEnded : function(install) { }, + onDownloadCancelled : function(install) { }, + onDownloadFailed : function(install) { + if(this.addon.willFail) + ok(false, "Install failed"); + }, + onInstallStarted : function(install) { }, + onInstallEnded : function(install, addon) { + let self = this; + isRestartShown(!this.addon.bootstrapped, false, function() { + if(self.onComplete) + self.onComplete(); + }); + }, + onInstallCancelled : function(install) { }, + onInstallFailed : function(install) { + if(this.willFail) + ok(false, "Install failed"); + }, + onExternalInstall : function(install, existing, needsRestart) { }, +}; + +function updateListener(aSettings) { + this.onComplete = aSettings.onComplete; + this.addon = aSettings.addon; +} + +updateListener.prototype = { + observe: function (aSubject, aTopic, aData) { + switch(aTopic) { + case "addon-update-ended" : + let json = aSubject.QueryInterface(Ci.nsISupportsString).data; + let update = JSON.parse(json); + if(update.id == this.addon.id) { + let os = Services.obs; + os.removeObserver(this, "addon-update-ended", false); + + let element = get_addon_element(update.id); + ok(!!element, "Have element for upgrade"); + + let self = this; + isRestartShown(!this.addon.bootstrapped, true, function() { + if(self.onComplete) + self.onComplete(); + }); + } + break; + } + }, +} diff --git a/mobile/android/chrome/tests/browser_addons_locales.js b/mobile/android/chrome/tests/browser_addons_locales.js new file mode 100644 index 000000000000..1d9bc1add664 --- /dev/null +++ b/mobile/android/chrome/tests/browser_addons_locales.js @@ -0,0 +1,64 @@ +var localeList = serverRoot + "locales_list.sjs"; +var PREF_LOCALE_LIST = "extensions.getLocales.get.url"; + +Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource://gre/modules/LocaleRepository.jsm"); +Components.utils.import("resource://gre/modules/AddonManager.jsm"); + +function test() { + waitForExplicitFinish(); + runNextTest(); +} + +function end_test() { + Services.prefs.clearUserPref(PREF_LOCALE_LIST); +} + +registerCleanupFunction(end_test); + +gTests.push({ + addon: null, + desc: "Test the values returned from _getLocalesInAddon", + run: function() { + Services.prefs.setCharPref(PREF_LOCALE_LIST, localeList); + LocaleRepository.getLocales(this.listLoaded.bind(this)); + }, + + listLoaded: function(aLocales) { + is(aLocales.length, 1, "Correct number of locales were found"); + aLocales[0].addon.install.addListener(this); + aLocales[0].addon.install.install(); + }, + + onInstallEnded: function(aInstall, aAddon) { + aInstall.removeListener(this); + this.addon = aAddon; + info("Installed " + aAddon.id); + + try { + ExtensionsView._getLocalesInAddon(aAddon, null); + ok(false, "_getLocalesInAddon should have thrown with a null callback"); + } catch(ex) { + ok(ex, "_getLocalesInAddon requires a callback") + } + + try { + ExtensionsView._getLocalesInAddon(aAddon, "foo"); + ok(false, "_getLocalesInAddons should have thrown without a non-function callback"); + } catch(ex) { + ok(ex, "_getLocalesInAddon requires the callback be a function") + } + + ExtensionsView._getLocalesInAddon(aAddon, this.gotLocales.bind(this)); + }, + + gotLocales: function(aLocales) { + is(aLocales.length, 2, "Correct number of locales were found"); + ok(aLocales.indexOf("te-st") > -1, "te-st locale found"); + ok(aLocales.indexOf("te-st-a") > -1, "te-st-a locale found"); + + // locales can't be restartless yet, so we can't really test the uninstall code + this.addon.install.cancel(); + runNextTest(); + } +}); diff --git a/mobile/android/chrome/tests/browser_appmenu.js b/mobile/android/chrome/tests/browser_appmenu.js new file mode 100644 index 000000000000..972b0ba0d817 --- /dev/null +++ b/mobile/android/chrome/tests/browser_appmenu.js @@ -0,0 +1,139 @@ +let gTests = []; +let gCurrentTest = null; +let Panels = [AllPagesList, HistoryList, BookmarkList]; + +let removedForTestButtons = []; + +function test() { + waitForExplicitFinish(); + + // Make sure we start the test with less than a full menu + let menu = document.getElementById("appmenu"); + while (menu.children.length > 5) + removedForTestButtons.push(menu.removeChild(menu.lastChild)); + + setTimeout(runNextTest, 200); +} + +//------------------------------------------------------------------------------ +// Iterating tests by shifting test out one by one as runNextTest is called. +function runNextTest() { + // Run the next test until all tests completed + if (gTests.length > 0) { + gCurrentTest = gTests.shift(); + info(gCurrentTest.desc); + gCurrentTest.run(); + } else { + // Add back any removed buttons + let menu = document.getElementById("appmenu"); + removedForTestButtons.forEach(function(aButton) { + menu.appendChild(aButton); + }); + + // Close the awesome panel just in case + AwesomeScreen.activePanel = null; + finish(); + } +} + +gTests.push({ +// This test will keep adding buttons and checking the result until there are a +// total of 9 buttons, then it will (one at a time) hide 3 items and check the +// result again. + + desc: "Test for showing the application menu", + newButtons: [], + hidden: 0, + + run: function() { + addEventListener("PopupChanged", gCurrentTest.popupOpened, false) + CommandUpdater.doCommand("cmd_menu"); + }, + + addButton: function() { + info("Adding a new button\n"); + let menu = document.getElementById("appmenu"); + let newButton = menu.children[0].cloneNode(true); + menu.appendChild(newButton); + gCurrentTest.newButtons.push(newButton); + }, + + popupOpened: function() { + removeEventListener("PopupChanged", gCurrentTest.popupOpened, false); + let menu = document.getElementById("appmenu"); + ok(!menu.hidden, "App menu is shown"); + + let more = document.getElementById("appmenu-more-button"); + if (menu.children.length > 6) { + ok(!!more, "More button is shown"); + addEventListener("PopupChanged", gCurrentTest.moreShown, false); + more.click(); + } else { + ok(!more, "More button is hidden"); + addEventListener("PopupChanged", gCurrentTest.popupClosed, false); + EventUtils.synthesizeKey("VK_ESCAPE", {}, window); + } + }, + + popupClosed: function() { + removeEventListener("PopupChanged", gCurrentTest.popupClosed, false); + let menu = document.getElementById("appmenu"); + ok(document.getElementById("appmenu").hidden, "Esc hides menus"); + if (menu.children.length < 9) { + gCurrentTest.addButton(); + gCurrentTest.run(); + } else { + menu.children[gCurrentTest.hidden].hidden = true; + gCurrentTest.hidden++; + addEventListener("PopupChanged", gCurrentTest.menuitemHidden, false) + CommandUpdater.doCommand("cmd_menu"); + } + }, + + moreShown: function(aEvent) { + // AppMenu hiding + if (!aEvent.detail) + return; + + let menu = document.getElementById("appmenu"); + ok(document.getElementById("appmenu").hidden, "Clicking more button hides menu"); + + removeEventListener("PopupChanged", gCurrentTest.moreShown, false); + let listbox = document.getElementById("appmenu-overflow-commands"); + is(listbox.childNodes.length, (menu.childNodes.length - 5), "Menu popup only shows overflow children"); + + addEventListener("PopupChanged", gCurrentTest.popupClosed, false); + EventUtils.synthesizeKey("VK_ESCAPE", {}, window); + }, + + menuitemHidden: function() { + removeEventListener("PopupChanged", gCurrentTest.menuitemHidden, false); + let menu = document.getElementById("appmenu"); + ok(!document.getElementById("appmenu").hidden, "App menu is shown"); + + let more = document.getElementById("appmenu-more-button"); + if (menu.children.length - gCurrentTest.hidden > 6) { + ok(more, "More button is shown"); + addEventListener("PopupChanged", gCurrentTest.popupClosed, false); + } else { + ok(!more, "More button is hidden"); + addEventListener("PopupChanged", gCurrentTest.popupClosedAgain, false); + } + EventUtils.synthesizeKey("VK_ESCAPE", {}, window); + }, + + popupClosedAgain: function() { + removeEventListener("PopupChanged", gCurrentTest.popupClosedAgain, false) + let menu = document.getElementById("appmenu"); + while (gCurrentTest.hidden > 0) { + gCurrentTest.hidden--; + menu.children[gCurrentTest.hidden].hidden = false; + } + + gCurrentTest.newButtons.forEach(function(aButton) { + menu.removeChild(aButton); + }); + + runNextTest(); + } +}); diff --git a/mobile/android/chrome/tests/browser_autocomplete.html b/mobile/android/chrome/tests/browser_autocomplete.html new file mode 100644 index 000000000000..93d424fa627e --- /dev/null +++ b/mobile/android/chrome/tests/browser_autocomplete.html @@ -0,0 +1,34 @@ + + + + + + + + + + +

+ + + + + + + + + + + + +

+ + + + + + +

+ + + diff --git a/mobile/android/chrome/tests/browser_autocomplete.js b/mobile/android/chrome/tests/browser_autocomplete.js new file mode 100644 index 000000000000..5649946722a9 --- /dev/null +++ b/mobile/android/chrome/tests/browser_autocomplete.js @@ -0,0 +1,184 @@ +let testURL = chromeRoot + "browser_autocomplete.html"; +messageManager.loadFrameScript(chromeRoot + "remote_autocomplete.js", true); + +let newTab = null; + +// A queue to order the tests and a handle for each test +var gTests = []; +var gCurrentTest = null; + +function test() { + // This test is async + waitForExplicitFinish(); + + // Ensure the form helper is initialized + try { + FormHelperUI.enabled; + } + catch(e) { + FormHelperUI.init(); + } + + // Need to wait until the page is loaded + messageManager.addMessageListener("pageshow", function(aMessage) { + if (newTab && newTab.browser.currentURI.spec != "about:blank") { + messageManager.removeMessageListener(aMessage.name, arguments.callee); + BrowserUI.closeAutoComplete(true); + setTimeout(runNextTest, 0); + } + }); + + newTab = Browser.addTab(testURL, true); +} + + +//------------------------------------------------------------------------------ +// Iterating tests by shifting test out one by one as runNextTest is called. +function runNextTest() { + // Run the next test until all tests completed + if (gTests.length > 0) { + gCurrentTest = gTests.shift(); + info(gCurrentTest.desc); + gCurrentTest.run(); + } + else { + // Cleanup. All tests are completed at this point + try { + // Add any cleanup code here + + // Close our tab when finished + Browser.closeTab(newTab); + } + finally { + // We must finialize the tests + finish(); + } + } +} + +function waitForAutocomplete(aCallback) { + window.addEventListener("contentpopupshown", function(aEvent) { + window.removeEventListener(aEvent.type, arguments.callee, false); + setTimeout(function() { + aCallback(FormHelperUI._currentElement.list); + }, 0); + }, false); +}; + +let data = [ + { label: "foo", value: "foo" }, + { label: "Somewhat bar", value: "bar" }, + { label: "foobar", value: "_" } +]; + +//------------------------------------------------------------------------------ +// Case: Click on a datalist element and show suggestions +gTests.push({ + desc: "Click on a datalist element and show suggestions", + + run: function() { + waitForAutocomplete(gCurrentTest.checkData); + AsyncTests.waitFor("TestRemoteAutocomplete:Click", + { id: "input-datalist-1" }, function(json) {}); + }, + + // Check that the data returned by the autocomplete handler on the content + // side is correct + checkData: function(aOptions) { + for (let i = 0; i < aOptions.length; i++) { + let option = aOptions[i]; + let valid = data[i]; + + is(option.label, valid.label, "Label should be equal (" + option.label + ", " + valid.label +")"); + is(option.value, valid.value, "Value should be equal (" + option.value + ", " + valid.value +")"); + } + + // Wait until suggestions box has been popupated + waitFor(gCurrentTest.checkUI, function() { + let suggestionsBox = document.getElementById("form-helper-suggestions"); + return suggestionsBox.childNodes.length; + }); + }, + + // Check that the UI reflect the specificity of the data + checkUI: function() { + let suggestionsBox = document.getElementById("form-helper-suggestions"); + let suggestions = suggestionsBox.childNodes; + + for (let i = 0; i < suggestions.length; i++) { + let suggestion = suggestions[i]; + let valid = data[i]; + let label = suggestion.getAttribute("value"); + let value = suggestion.getAttribute("data"); + + is(label, valid.label, "Label should be equal (" + label + ", " + valid.label +")"); + is(value, valid.value, "Value should be equal (" + value + ", " + valid.value +")"); + } + + gCurrentTest.checkUIClick(0); + }, + + // Ensure that clicking on a given datalist element set the right value in + // the input box + checkUIClick: function(aIndex) { + let suggestionsBox = document.getElementById("form-helper-suggestions"); + + let suggestion = suggestionsBox.childNodes[aIndex]; + if (!suggestion) { + gCurrentTest.finish(); + return; + } + + // Use the form helper autocompletion helper + FormHelperUI.doAutoComplete(suggestion); + + AsyncTests.waitFor("TestRemoteAutocomplete:Check", { id: "input-datalist-1" }, function(json) { + is(json.result, suggestion.getAttribute("data"), "The target input value should be set to " + data); + gCurrentTest.checkUIClick(aIndex + 1); + }); + }, + + finish: function() { + // Close the form assistant + FormHelperUI.hide(); + + + AsyncTests.waitFor("TestRemoteAutocomplete:Reset", { id: "input-datalist-1" }, function(json) { + runNextTest(); + }); + } +}); + +//------------------------------------------------------------------------------ +// Case: Check arrows visibility +gTests.push({ + desc: "Check arrows visibility", + + run: function() { + let popup = document.getElementById("form-helper-suggestions-container"); + popup.addEventListener("contentpopupshown", function(aEvent) { + aEvent.target.removeEventListener(aEvent.type, arguments.callee, false); + waitFor(gCurrentTest.checkNoArrows, function() { + return FormHelperUI._open; + }); + }, false); + + AsyncTests.waitFor("TestRemoteAutocomplete:Click", + { id: "input-datalist-3" }, function(json) {}); + }, + + checkNoArrows: function() { + let scrollbox = document.getElementById("form-helper-suggestions"); + todo_is(scrollbox._scrollButtonUp.collapsed, true, "Left button should be collapsed"); + todo_is(scrollbox._scrollButtonDown.collapsed, true, "Right button should be collapsed"); + gCurrentTest.finish(); + }, + + finish: function() { + // Close the form assistant + FormHelperUI.hide(); + + runNextTest(); + } +}); + diff --git a/mobile/android/chrome/tests/browser_autocompletesearch.js b/mobile/android/chrome/tests/browser_autocompletesearch.js new file mode 100644 index 000000000000..e7129eafe8ec --- /dev/null +++ b/mobile/android/chrome/tests/browser_autocompletesearch.js @@ -0,0 +1,108 @@ +Components.utils.import("resource://gre/modules/Services.jsm"); + +let match= [ + ["http://example.com/a", "A", "favicon", "http://example.com/a/favicon.png"], + ["http://example.com/b", "B", "favicon", "http://example.com/b/favicon.png"], + ["http://example.com/c", "C", "favicon", "http://example.com/c/favicon.png"], + ["http://example.com/d", "D", "bookmark", "http://example.com/d/favicon.png"], + ["http://example.com/e", "E", "boolmark", "http://example.com/e/favicon.png"] +]; + +var gAutocomplete = null; +var gProfileDir = null; + +function test() { + waitForExplicitFinish(); + + gProfileDir = Services.dirsvc.get("ProfD", Ci.nsIFile); + + // First we need to remove the existing cache file so we can control the state of the service + let oldCacheFile = gProfileDir.clone(); + oldCacheFile.append("autocomplete.json"); + if (oldCacheFile.exists()) + oldCacheFile.remove(true); + + // Since we removed the cache file, we know the service will need to write out a new + // file. We use that as a trigger to move forward. + Services.obs.addObserver(function (aSubject, aTopic, aData) { + Services.obs.removeObserver(arguments.callee, aTopic, false); + saveMockCache(); + }, "browser:cache-session-history-write-complete", false); + + // This might trigger an init or it may have already happened. That's why we need + // to do some work to control the state. + gAutocomplete = Cc["@mozilla.org/autocomplete/search;1?name=history"].getService(Ci.nsIAutoCompleteSearch); + + // Trigger the new cache to be written out, since the existing was removed + Services.obs.notifyObservers(null, "browser:cache-session-history-reload", ""); +} + +function saveMockCache() { + // Now we write our own mock data cache into the profile + let oldCacheFile = gProfileDir.clone(); + oldCacheFile.append("autocomplete.json"); + if (oldCacheFile.exists()) + oldCacheFile.remove(true); + + let mockCachePath = gTestPath; + info("mock path: " + mockCachePath); + let mockCacheURI = getResolvedURI(mockCachePath); + info("mock URI: " + mockCacheURI.spec); + if (mockCacheURI instanceof Ci.nsIJARURI) { + // Android tests are stored in a JAR file, so we need to extract the mock_autocomplete.json file + info("jar file: " + mockCacheURI.JARFile.spec); + let zReader = Cc["@mozilla.org/libjar/zip-reader;1"].createInstance(Ci.nsIZipReader); + let fileHandler = Cc["@mozilla.org/network/protocol;1?name=file"].getService(Ci.nsIFileProtocolHandler); + let fileName = fileHandler.getFileFromURLSpec(mockCacheURI.JARFile.spec); + zReader.open(fileName); + + let extract = mockCacheURI.spec.split("!")[1]; + extract = extract.substring(1, extract.lastIndexOf("/") + 1); + extract += "mock_autocomplete.json"; + info("extract path: " + extract); + let target = gProfileDir.clone(); + target.append("autocomplete.json"); + info("target path: " + target.path); + zReader.extract(extract, target); + } else { + // Tests are run from a folder, so we can just copy the mock_autocomplete.json file + let mockCacheFile = getChromeDir(mockCacheURI); + info("mock file: " + mockCacheFile.path); + mockCacheFile.append("mock_autocomplete.json"); + mockCacheFile.copyToFollowingLinks(gProfileDir, "autocomplete.json"); + } + + // Listen for when the mock cache has been loaded + Services.obs.addObserver(function (aSubject, aTopic, aData) { + Services.obs.removeObserver(arguments.callee, aTopic, false); + runTest(); + }, "browser:cache-session-history-read-complete", false); + + // Trigger the new mock cache to be loaded + Services.obs.notifyObservers(null, "browser:cache-session-history-reload", ""); +} + +function runTest() { + let cacheFile = gProfileDir.clone(); + cacheFile.append("autocomplete.json"); + ok(cacheFile.exists(), "Mock autocomplete JSON cache file exists"); + + // Compare the mock data, which should be used now that we loaded it into the service + gAutocomplete.startSearch("", "", null, { + onSearchResult: function(search, result) { + is(result.matchCount, 5, "matchCount is correct"); + + for (let i=0; i<5; i++) { + is(result.getValueAt(i), match[i][0], "value matches"); + is(result.getCommentAt(i), match[i][1], "comment matches"); + is(result.getStyleAt(i), match[i][2], "style matches"); + is(result.getImageAt(i), match[i][3], "image matches"); + } + + if (cacheFile.exists()) + cacheFile.remove(true); + + finish(); + } + }); +} diff --git a/mobile/android/chrome/tests/browser_awesomescreen.js b/mobile/android/chrome/tests/browser_awesomescreen.js new file mode 100644 index 000000000000..6283881b1dfc --- /dev/null +++ b/mobile/android/chrome/tests/browser_awesomescreen.js @@ -0,0 +1,470 @@ +/* + * Bug 436069 - Fennec browser-chrome tests to verify correct navigation into the + * differents part of the awesome panel + */ + +let testURL_01 = chromeRoot + "browser_blank_01.html"; + +let gTests = []; +let gCurrentTest = null; +let Panels = [AllPagesList, HistoryList, BookmarkList]; + +function test() { + // The "runNextTest" approach is async, so we need to call "waitForExplicitFinish()" + // We call "finish()" when the tests are finished + waitForExplicitFinish(); + + // Start the tests + setTimeout(runNextTest, 200); +} + +//------------------------------------------------------------------------------ +// Iterating tests by shifting test out one by one as runNextTest is called. +function runNextTest() { + // Run the next test until all tests completed + if (gTests.length > 0) { + gCurrentTest = gTests.shift(); + info(gCurrentTest.desc); + + // Ensure all tests start with hidden awesome screen + AwesomeScreen.activePanel = null; + + gCurrentTest.run(); + } + else { + // Close the awesome panel just in case + AwesomeScreen.activePanel = null; + finish(); + } +} + +function waitForNavigationPanel(aCallback, aWaitForHide) { + let evt = aWaitForHide ? "NavigationPanelHidden" : "NavigationPanelShown"; + info("waitFor " + evt + "(" + Components.stack.caller + ")"); + window.addEventListener(evt, function(aEvent) { + info("receive " + evt); + window.removeEventListener(aEvent.type, arguments.callee, false); + setTimeout(aCallback, 0); + }, false); +} + +//------------------------------------------------------------------------------ +// Case: Test awesome bar collapsed state +gTests.push({ + desc: "Test awesome bar collapsed state", + + run: function() { + waitForNavigationPanel(gCurrentTest.onPopupShown); + AllPagesList.doCommand(); + }, + + onPopupShown: function() { + is(AwesomeScreen.activePanel, AllPagesList, "AllPagesList should be visible"); + ok(!BrowserUI._edit.collapsed, "The urlbar edit element is visible"); + ok(BrowserUI._title.collapsed, "The urlbar title element is not visible"); + + waitForNavigationPanel(gCurrentTest.onPopupHidden, true); + + EventUtils.synthesizeKey("VK_ESCAPE", {type: "keypress"}, window); + }, + + onPopupHidden: function() { + is(AwesomeScreen.activePanel, null, "AllPagesList should be dismissed"); + ok(BrowserUI._edit.collapsed, "The urlbar edit element is not visible"); + ok(!BrowserUI._title.collapsed, "The urlbar title element is visible"); + + runNextTest(); + } +}); + + +//------------------------------------------------------------------------------ +// Case: Test typing a character should dismiss the awesome header +gTests.push({ + desc: "Test typing a character should dismiss the awesome header", + + run: function() { + waitForNavigationPanel(gCurrentTest.onPopupReady); + AllPagesList.doCommand(); + }, + + onPopupReady: function() { + is(AwesomeScreen.activePanel == AllPagesList, true, "AllPagesList should be visible"); + + let awesomeHeader = document.getElementById("awesome-header"); + is(awesomeHeader.hidden, false, "Awesome header should be visible"); + + BrowserUI._edit.addEventListener("onsearchbegin", function(aEvent) { + if (BrowserUI._edit.value == "") + return; + + BrowserUI._edit.removeEventListener(aEvent.type, arguments.callee, true); + let awesomeHeader = document.getElementById("awesome-header"); + is(awesomeHeader.hidden, true, "Awesome header should be hidden"); + gCurrentTest.onKeyPress(); + }, true); + EventUtils.synthesizeKey("A", {}, window); + }, + + onKeyPress: function(aKey, aHidden) { + waitForNavigationPanel(function() { + let awesomeHeader = document.getElementById("awesome-header"); + is(awesomeHeader.hidden, false, "Awesome header should be visible"); + runNextTest(); + }, true); + + EventUtils.synthesizeKey("VK_ESCAPE", {type: "keypress"}, window); + } +}); + +//------------------------------------------------------------------------------ +// Case: Test typing a character should open the awesome bar +gTests.push({ + desc: "Test typing a character should open the All Pages List", + + run: function() { + waitForNavigationPanel(gCurrentTest.onPopupReady); + BookmarkList.doCommand(); + }, + + onPopupReady: function() { + BrowserUI._edit.addEventListener("onsearchbegin", function(aEvent) { + BrowserUI._edit.removeEventListener(aEvent.type, arguments.callee, false); + gCurrentTest.onSearchBegin(); + }, false); + EventUtils.synthesizeKey("I", {}, window); + }, + + onSearchBegin: function() { + let awesomeHeader = document.getElementById("awesome-header"); + is(awesomeHeader.hidden, true, "Awesome header should be hidden"); + is(AwesomeScreen.activePanel == AllPagesList, true, "AllPagesList should be opened on a keydown"); + is(BrowserUI._edit.readOnly, false, "urlbar should not be readonly after an input"); + + waitForNavigationPanel(gCurrentTest.onPopupHidden, true); + EventUtils.synthesizeKey("VK_ESCAPE", {type: "keypress"}, window); + }, + + onPopupHidden: function() { + is(AwesomeScreen.activePanel == null, true, "VK_ESCAPE should have dismissed the awesome panel"); + runNextTest(); + } +}); + +//------------------------------------------------------------------------------ +// Case: Test opening the awesome panel and checking the urlbar readonly state +gTests.push({ + desc: "Test opening the awesome panel and checking the urlbar readonly state", + + run: function() { + is(BrowserUI._edit.readOnly, true, "urlbar input textbox should be readonly"); + + waitForNavigationPanel(gCurrentTest.onPopupReady); + AllPagesList.doCommand(); + }, + + onPopupReady: function() { + is(Elements.urlbarState.getAttribute("mode"), "edit", "bcast_urlbarState mode attribute should be equal to 'edit'"); + + let edit = BrowserUI._edit; + is(edit.readOnly, BrowserUI._isKeyboardFullscreen(), "urlbar input textbox is readonly if keyboard is fullscreen, editable otherwise"); + + let urlString = BrowserUI.getDisplayURI(Browser.selectedBrowser); + if (Util.isURLEmpty(urlString)) + urlString = ""; + + let firstPanel = true; + Panels.forEach(function(aPanel) { + aPanel.doCommand(); + is(AwesomeScreen.activePanel, aPanel, "The panel " + aPanel.panel.id + " should be selected"); + if (firstPanel) { + // First panel will have selected text, if we are in portrait + is(edit.readOnly, BrowserUI._isKeyboardFullscreen(), "urlbar input textbox is readonly if keyboard is fullscreen, editable otherwise"); + } else { + is(edit.readOnly, true, "urlbar input textbox be readonly if not the first panel"); + } + edit.click(); + is(edit.readOnly, false, "urlbar input textbox should not be readonly after a click, in both landscape and portrait"); + is(edit.value, urlString, "urlbar value should be equal to the page uri"); + + firstPanel = false; + }); + + setTimeout(function() { + runNextTest(); + }, 0); + } +}); + +//------------------------------------------------------------------------------ +// Case: Test opening the awesome panel and checking the urlbar selection +gTests.push({ + desc: "Test opening the awesome panel and checking the urlbar selection", + + run: function() { + BrowserUI.closeAutoComplete(true); + this.currentTab = BrowserUI.newTab(testURL_01); + + // Need to wait until the page is loaded + messageManager.addMessageListener("pageshow", + function(aMessage) { + if (gCurrentTest.currentTab.browser.currentURI.spec != "about:blank") { + messageManager.removeMessageListener(aMessage.name, arguments.callee); + setTimeout(gCurrentTest.onPageReady, 0); + } + }); + }, + + onPageReady: function() { + waitForNavigationPanel(gCurrentTest.onPopupReady); + + AllPagesList.doCommand(); + }, + + onPopupReady: function() { + let edit = BrowserUI._edit; + + let firstPanel = true; + Panels.forEach(function(aPanel) { + aPanel.doCommand(); + if (firstPanel && !BrowserUI._isKeyboardFullscreen()) { + // First panel will have selected text, if we are in portrait + ok(edit.selectionStart == 0 && edit.selectionEnd == edit.textLength, "[case 1] urlbar text should be selected on a simple show"); + edit.click(); + // The click is not sync enough for this to work + todo(edit.selectionStart == edit.selectionEnd, "[case 1] urlbar text should not be selected on a click"); + } else { + ok(edit.selectionStart == edit.selectionEnd, "[case 2] urlbar text should not be selected on a simple show"); + edit.click(); + ok(edit.selectionStart == 0 && edit.selectionEnd == edit.textLength, "[case 2] urlbar text should be selected on a click"); + } + firstPanel = false; + }); + + // We are disabling it early, otherwise calling edit.click(); quickly made input.js though this is a double click (sigh) + let oldDoubleClickSelectsAll = Services.prefs.getBoolPref("browser.urlbar.doubleClickSelectsAll"); + Services.prefs.setBoolPref("browser.urlbar.doubleClickSelectsAll", false); + + let oldClickSelectsAll = edit.clickSelectsAll; + edit.clickSelectsAll = false; + firstPanel = true; + Panels.forEach(function(aPanel) { + aPanel.doCommand(); + if (firstPanel && !BrowserUI._isKeyboardFullscreen()) { + // First panel will have selected text, if we are in portrait + ok(edit.selectionStart == 0 && edit.selectionEnd == edit.textLength, "[case 1] urlbar text should be selected on a simple show"); + edit.click(); + // The click is not sync enough for this to work + todo(edit.selectionStart == edit.selectionEnd, "[case 1] urlbar text should not be selected on a click"); + } else { + ok(edit.selectionStart == edit.selectionEnd, "[case 2] urlbar text should not be selected on a simple show"); + edit.click(); + ok(edit.selectionStart == edit.selectionEnd, "[case 2] urlbar text should not be selected on a click"); + } + + firstPanel = false; + }); + + Panels.forEach(function(aPanel) { + aPanel.doCommand(); + ok(edit.selectionStart == edit.selectionEnd, "urlbar text should not be selected on a simple show"); + edit.click(); + edit.click(); + ok(edit.selectionStart == edit.selectionEnd, "urlbar text should not be selected on a double click"); + }); + + Services.prefs.setBoolPref("browser.urlbar.doubleClickSelectsAll", oldDoubleClickSelectsAll); + + Panels.forEach(function(aPanel) { + aPanel.doCommand(); + ok(edit.selectionStart == edit.selectionEnd, "urlbar text should not be selected on a simple show"); + edit.click(); + edit.click(); + ok(edit.selectionStart == 0 && edit.selectionEnd == edit.textLength, "urlbar text should be selected on a double click"); + }); + + edit.clickSelectsAll = oldClickSelectsAll; + + // Ensure the tab is well closed before doing the rest of the code, otherwise + // this cause some bugs with the composition events + let tabCount = Browser.tabs.length; + Browser.closeTab(gCurrentTest.currentTab, { forceClose: true }); + waitFor(runNextTest, function() Browser.tabs.length == tabCount - 1); + } +}); + +// Case: Test context clicks on awesome panel +gTests.push({ + desc: "Test context clicks on awesome panel", + + _panelIndex : 0, + _contextOpts : [ + ["link-openable", "link-shareable"], + ["link-openable", "link-shareable"], + ["edit-bookmark", "link-shareable", "link-openable"], + ], + + clearContextTypes: function clearContextTypes() { + if (ContextHelper.popupState) + ContextHelper.hide(); + }, + + checkContextTypes: function checkContextTypes(aTypes) { + let commandlist = document.getElementById("context-commands"); + + for (let i=0; i -1) { + // command should be visible + if(command.hidden == true) + return false; + } else { + if(command.hidden == false) + return false; + } + } + return true; + }, + + run: function() { + waitForNavigationPanel(gCurrentTest.onPopupReady); + AllPagesList.doCommand(); + }, + + onPopupReady: function() { + let self = this; + if(self._panelIndex < Panels.length) { + let panel = Panels[self._panelIndex]; + panel.doCommand(); + + self.clearContextTypes(); + + EventUtils.synthesizeMouse(panel.panel, panel.panel.width / 2, panel.panel.height / 2, { type: "mousedown" }); + setTimeout(function() { + EventUtils.synthesizeMouse(panel.panel, panel.panel.width / 2, panel.panel.height / 2, { type: "mouseup" }); + ok(self.checkContextTypes(self._contextOpts[self._panelIndex]), "Correct context menu shown for panel"); + self.clearContextTypes(); + + self._panelIndex++; + self.onPopupReady(); + }, 500); + } else { + runNextTest(); + } + } +}); + + +// Case: Test compositionevent +gTests.push({ + desc: "Test sending composition events", + _textValue: null, + get popup() { + delete this.popup; + return this.popup = document.getElementById("popup_autocomplete"); + }, + + get popupHeader() { + delete this.popupHeader; + return this.popupHeader = document.getElementById("awesome-header"); + }, + + get inputField() { + delete this.inputField; + return this.inputField = document.getElementById("urlbar-edit"); + }, + + run: function() { + // Saving value to compare the result before and after the composition event + gCurrentTest._textValue = gCurrentTest.inputField.value; + + window.addEventListener("popupshown", function() { + window.removeEventListener("popupshown", arguments.callee, false); + if (BrowserUI._isKeyboardFullscreen()) + gCurrentTest.inputField.readOnly = false; + setTimeout(gCurrentTest.onPopupReady, 0); + }, false); + AllPagesList.doCommand(); + }, + + _checkState: function() { + ok(gCurrentTest.popup._popupOpen, "AutoComplete popup should be opened"); + is(gCurrentTest.popupHeader.hidden, false, "AutoComplete popup header should be visible"); + is(gCurrentTest.inputField.value, gCurrentTest._textValue, "Value should not have changed"); + }, + + onPopupReady: function() { + gCurrentTest._checkState(); + + window.addEventListener("compositionstart", function() { + window.removeEventListener("compositionstart", arguments.callee, false); + setTimeout(gCurrentTest.onCompositionStart, 0) + }, false); + Browser.windowUtils.sendCompositionEvent("compositionstart", "", ""); + }, + + onCompositionStart: function() { + gCurrentTest._checkState(); + + window.addEventListener("compositionend", function() { + window.removeEventListener("compositionend", arguments.callee, false); + setTimeout(gCurrentTest.onCompositionEnd, 0) + }, false); + Browser.windowUtils.sendCompositionEvent("compositionend", "", ""); + }, + + onCompositionEnd: function() { + /* TODO: This is currently failing (bug 642771) + gCurrentTest._checkState(); + + let isHiddenHeader = function() { + return gCurrentTest.popupHeader.hidden; + } + + // Wait to be sure there the header won't dissapear + // XXX this sucks because it means we'll be stuck 500ms if the test succeed + // but I don't have a better idea about how to do it for now since we don't + // that to happen! + + waitForAndContinue(function() { + gCurrentTest._checkState(); + runNextTest(); + }, isHiddenHeader, Date.now() + 500); + */ + runNextTest(); + } +}); + +// Case: Test context popup dismiss on top of awesome panel +gTests.push({ + desc: "Case: Test context popup dismiss on top of awesome panel", + + run: function() { + waitForNavigationPanel(gCurrentTest.onPopupReady); + AllPagesList.doCommand(); + }, + + onPopupReady: function() { + EventUtils.synthesizeMouse(AllPagesList.panel, AllPagesList.panel.width / 2, + AllPagesList.panel.height / 2, { type: "mousedown" }); + + // Simulate a long tap + setTimeout(function(self) { + EventUtils.synthesizeMouse(AllPagesList.panel, AllPagesList.panel.width / 2, + AllPagesList.panel.height / 2, { type: "mouseup" }); + + let contextContainer = document.getElementById("context-container"); + + ok(!AllPagesList.panel.hidden, "The context popup is still visible after long tap"); + ok(!contextContainer.hidden, "The context popup is visible after long tap"); + + EventUtils.synthesizeMouse(AllPagesList.panel, 0, 0, {}); + + ok(contextContainer.hidden, "The context popup is not visible after tap"); + ok(!AllPagesList.panel.hidden, "The awesome panel is still visible after popup is dismissed"); + + AwesomeScreen.activePanel = null; + runNextTest(); + }, 500, this); + } +}); diff --git a/mobile/android/chrome/tests/browser_blank_01.html b/mobile/android/chrome/tests/browser_blank_01.html new file mode 100644 index 000000000000..8fd14cc1bebd --- /dev/null +++ b/mobile/android/chrome/tests/browser_blank_01.html @@ -0,0 +1,6 @@ + +Browser Blank Page 01 + +

Browser Blank Page 01

+ + diff --git a/mobile/android/chrome/tests/browser_blank_02.html b/mobile/android/chrome/tests/browser_blank_02.html new file mode 100644 index 000000000000..61d6b1f61acc --- /dev/null +++ b/mobile/android/chrome/tests/browser_blank_02.html @@ -0,0 +1,7 @@ + +Browser Blank Page 02 + + +

Browser Blank Page 02

+ + diff --git a/mobile/android/chrome/tests/browser_blank_03.html b/mobile/android/chrome/tests/browser_blank_03.html new file mode 100644 index 000000000000..57b38163dd30 --- /dev/null +++ b/mobile/android/chrome/tests/browser_blank_03.html @@ -0,0 +1,6 @@ + +Browser Blank Page 03 + +

Browser Blank Page 03

+ + diff --git a/mobile/android/chrome/tests/browser_bookmarks.js b/mobile/android/chrome/tests/browser_bookmarks.js new file mode 100644 index 000000000000..f71b597ad197 --- /dev/null +++ b/mobile/android/chrome/tests/browser_bookmarks.js @@ -0,0 +1,304 @@ +/* + * Bug 486490 - Fennec browser-chrome tests to verify correct implementation of chrome + * code in mobile/chrome/content in terms of integration with Places + * component, specifically for bookmark management. + */ + +var testURL_01 = chromeRoot + "browser_blank_01.html"; +var testURL_02 = chromeRoot + "browser_blank_02.html"; + +// A queue to order the tests and a handle for each test +var gTests = []; +var gCurrentTest = null; + +//------------------------------------------------------------------------------ +// Entry point (must be named "test") +function test() { + // The "runNextTest" approach is async, so we need to call "waitForExplicitFinish()" + // We call "finish()" when the tests are finished + waitForExplicitFinish(); + + // Start the tests + runNextTest(); +} + +//------------------------------------------------------------------------------ +// Iterating tests by shifting test out one by one as runNextTest is called. +function runNextTest() { + // Run the next test until all tests completed + if (gTests.length > 0) { + gCurrentTest = gTests.shift(); + info(gCurrentTest.desc); + gCurrentTest.run(); + } + else { + // Cleanup. All tests are completed at this point + try { + PlacesUtils.bookmarks.removeFolderChildren(BookmarkList.panel.mobileRoot); + } + finally { + // We must finialize the tests + finish(); + } + } +} + +function waitForPageShow(aCallback) { + messageManager.addMessageListener("pageshow", function(aMessage) { + if (gCurrentTest._currentTab.browser.currentURI.spec != "about:blank") { + messageManager.removeMessageListener(aMessage.name, arguments.callee); + setTimeout(aCallback, 0); + } + }); +} + +function waitForNavigationPanel(aCallback, aWaitForHide) { + let evt = aWaitForHide ? "NavigationPanelHidden" : "NavigationPanelShown"; + info("waitFor " + evt + "(" + Components.stack.caller + ")"); + window.addEventListener(evt, function(aEvent) { + info("receive " + evt); + window.removeEventListener(aEvent.type, arguments.callee, false); + setTimeout(aCallback, 0); + }, false); +} + +//------------------------------------------------------------------------------ +// Case: Test adding a bookmark with the Star button +gTests.push({ + desc: "Test adding a bookmark with the Star button", + _currentTab: null, + + run: function() { + this._currentTab = Browser.addTab(testURL_01, true); + + // Need to wait until the page is loaded + waitForPageShow(gCurrentTest.onPageReady); + }, + + onPageReady: function() { + let starbutton = document.getElementById("tool-star"); + starbutton.click(); + window.addEventListener("BookmarkCreated", function(aEvent) { + window.removeEventListener(aEvent.type, arguments.callee, false); + let bookmark = PlacesUtils.getMostRecentBookmarkForURI(makeURI(testURL_01)); + ok(bookmark != -1, testURL_01 + " should be added."); + + Browser.closeTab(gCurrentTest._currentTab); + + runNextTest(); + }, false); + } +}); + +//------------------------------------------------------------------------------ +// Case: Test clicking on a bookmark loads the web page +gTests.push({ + desc: "Test clicking on a bookmark loads the web page", + _currentTab: null, + + run: function() { + BrowserUI.closeAutoComplete(true); + this._currentTab = Browser.addTab(testURL_02, true); + + // Need to wait until the page is loaded + waitForPageShow(gCurrentTest.onPageReady); + }, + + onPageReady: function() { + // Wait for the bookmarks to load, then do the test + waitForNavigationPanel(gCurrentTest.onBookmarksReady); + BrowserUI.doCommand("cmd_bookmarks"); + }, + + onBookmarksReady: function() { + let bookmarkitem = document.getAnonymousElementByAttribute(BookmarkList.panel, "uri", testURL_01); + bookmarkitem.control.scrollBoxObject.ensureElementIsVisible(bookmarkitem); + + isnot(bookmarkitem, null, "Found the bookmark"); + is(bookmarkitem.getAttribute("uri"), testURL_01, "Bookmark has the right URL via attribute"); + is(bookmarkitem.spec, testURL_01, "Bookmark has the right URL via property"); + + // Create a listener for the opening bookmark + waitForPageShow(function() { + if (Services.appinfo.OS == "Android") + todo_is(gCurrentTest._currentTab.browser.currentURI.spec, testURL_01, "Opened the right bookmark"); + else + is(gCurrentTest._currentTab.browser.currentURI.spec, testURL_01, "Opened the right bookmark"); + + Browser.closeTab(gCurrentTest._currentTab); + + runNextTest(); + }); + + EventUtils.synthesizeMouse(bookmarkitem, bookmarkitem.width / 2, bookmarkitem.height / 2, {}); + } +}); + +//------------------------------------------------------------------------------ +// Case: Test editing URI of existing bookmark +gTests.push({ + desc: "Test editing URI of existing bookmark", + + run: function() { + // Wait for the bookmarks to load, then do the test + waitForNavigationPanel(gCurrentTest.onBookmarksReady); + BrowserUI.doCommand("cmd_bookmarks"); + }, + + onBookmarksReady: function() { + // Go into edit mode + let bookmark = BookmarkList.panel.items[0]; + bookmark.startEditing(); + + waitFor(gCurrentTest.onEditorReady, function() { return bookmark.isEditing == true; }); + }, + + onEditorReady: function() { + let bookmarkitem = document.getAnonymousElementByAttribute(BookmarkList.panel, "uri", testURL_01); + EventUtils.synthesizeMouse(bookmarkitem, bookmarkitem.width / 2, bookmarkitem.height / 2, {}); + + let uritextbox = document.getAnonymousElementByAttribute(bookmarkitem, "anonid", "uri"); + uritextbox.value = testURL_02; + + let donebutton = document.getAnonymousElementByAttribute(bookmarkitem, "anonid", "done-button"); + donebutton.click(); + + let bookmark = PlacesUtils.getMostRecentBookmarkForURI(makeURI(testURL_01)); + is(bookmark, -1, testURL_01 + " should no longer in bookmark"); + bookmark = PlacesUtils.getMostRecentBookmarkForURI(makeURI(testURL_02)); + isnot(bookmark, -1, testURL_02 + " is in bookmark"); + + AwesomeScreen.activePanel = null; + + runNextTest(); + } +}); + +//------------------------------------------------------------------------------ +// Case: Test editing title of existing bookmark +gTests.push({ + desc: "Test editing title of existing bookmark", + + run: function() { + // Wait for the bookmarks to load, then do the test + waitForNavigationPanel(gCurrentTest.onBookmarksReady); + BrowserUI.doCommand("cmd_bookmarks"); + }, + + onBookmarksReady: function() { + // Go into edit mode + let bookmark = BookmarkList.panel.items[0]; + bookmark.startEditing(); + + waitFor(gCurrentTest.onEditorReady, function() { return bookmark.isEditing == true; }); + }, + + onEditorReady: function() { + let bookmark = PlacesUtils.getMostRecentBookmarkForURI(makeURI(testURL_02)); + is(PlacesUtils.bookmarks.getItemTitle(bookmark), "Browser Blank Page 01", "Title remains the same."); + + let bookmarkitem = document.getAnonymousElementByAttribute(BookmarkList.panel, "uri", testURL_02); + EventUtils.synthesizeMouse(bookmarkitem, bookmarkitem.width / 2, bookmarkitem.height / 2, {}); + + let titletextbox = document.getAnonymousElementByAttribute(bookmarkitem, "anonid", "name"); + let newtitle = "Changed Title"; + titletextbox.value = newtitle; + + let donebutton = document.getAnonymousElementByAttribute(bookmarkitem, "anonid", "done-button"); + donebutton.click(); + + isnot(PlacesUtils.getMostRecentBookmarkForURI(makeURI(testURL_02)), -1, testURL_02 + " is still in bookmark."); + is(PlacesUtils.bookmarks.getItemTitle(bookmark), newtitle, "Title is changed."); + + AwesomeScreen.activePanel = null; + + runNextTest(); + } +}); + +//------------------------------------------------------------------------------ +// Case: Test removing existing bookmark +gTests.push({ + desc: "Test removing existing bookmark", + bookmarkitem: null, + + run: function() { + // Wait for the bookmarks to load, then do the test + waitForNavigationPanel(gCurrentTest.onBookmarksReady); + BrowserUI.doCommand("cmd_bookmarks"); + }, + + onBookmarksReady: function() { + // Go into edit mode + let bookmark = BookmarkList.panel.items[0]; + bookmark.startEditing(); + + waitFor(gCurrentTest.onEditorReady, function() { return bookmark.isEditing == true; }); + }, + + onEditorReady: function() { + let bookmark = document.getAnonymousElementByAttribute(BookmarkList.panel, "uri", testURL_02); + bookmark.remove(); + + let bookmark = PlacesUtils.getMostRecentBookmarkForURI(makeURI(testURL_02)); + ok(bookmark == -1, testURL_02 + " should no longer in bookmark"); + bookmark = PlacesUtils.getMostRecentBookmarkForURI(makeURI(testURL_01)); + ok(bookmark == -1, testURL_01 + " should no longer in bookmark"); + + AwesomeScreen.activePanel = null; + + runNextTest(); + } +}); + +//------------------------------------------------------------------------------ +// Case: Test editing title of desktop folder +gTests.push({ + desc: "Test editing title of desktop folder", + bmId: null, + + run: function() { + // Add a bookmark to the desktop area so the desktop folder is displayed + gCurrentTest.bmId = PlacesUtils.bookmarks + .insertBookmark(PlacesUtils.unfiledBookmarksFolderId, + makeURI(testURL_02), + Ci.nsINavBookmarksService.DEFAULT_INDEX, + testURL_02); + + // Wait for the bookmarks to load, then do the test + waitForNavigationPanel(gCurrentTest.onBookmarksReady); + BrowserUI.doCommand("cmd_bookmarks"); + }, + + onBookmarksReady: function() { + // Go into edit mode + let bookmarksPanel = BookmarkList.panel; + let bookmark = bookmarksPanel.items[0]; + bookmark.startEditing(); + + // Is the "desktop" folder showing? + let first = bookmarksPanel._children.firstChild; + is(first.itemId, bookmarksPanel._desktopFolderId, "Desktop folder is showing"); + + // Is the "desktop" folder in edit mode? + is(first.isEditing, false, "Desktop folder is not in edit mode"); + + // Do not allow the "desktop" folder to be editable by tap + EventUtils.synthesizeMouse(first, first.width / 2, first.height / 2, {}); + + // A tap on the "desktop" folder _should_ open the folder, not put it into edit mode. + // So we need to get the first item again. + first = bookmarksPanel._children.firstChild; + + // It should not be the "desktop" folder + isnot(first.itemId, bookmarksPanel._desktopFolderId, "Desktop folder is not showing after mouse click"); + + // But it should be one of the other readonly bookmark roots + isnot(bookmarksPanel._readOnlyFolders.indexOf(parseInt(first.itemId)), -1, "Desktop subfolder is showing after mouse click"); + + PlacesUtils.bookmarks.removeItem(gCurrentTest.bmId); + + AwesomeScreen.activePanel = null; + runNextTest(); + } +}); diff --git a/mobile/android/chrome/tests/browser_bookmarks_star.js b/mobile/android/chrome/tests/browser_bookmarks_star.js new file mode 100644 index 000000000000..3dfac0eb5aec --- /dev/null +++ b/mobile/android/chrome/tests/browser_bookmarks_star.js @@ -0,0 +1,248 @@ +/* + * Bug 486490 - Fennec browser-chrome tests to verify correct implementation of chrome + * code in mobile/chrome/content in terms of integration with Places + * component, specifically for bookmark management. + */ + +let testURL_01 = chromeRoot + "browser_blank_01.html"; +let testURL_02 = chromeRoot + "browser_blank_02.html"; + +function test() { + BookmarkHelper.logging = true; + + // The "runNextTest" approach is async, so we need to call "waitForExplicitFinish()" + // We call "finish()" when the tests are finished + waitForExplicitFinish(); + + // Start the tests + runNextTest(); +} + +registerCleanupFunction(function() { + BookmarkHelper.logging = false; + PlacesUtils.bookmarks.removeFolderChildren(BookmarkList.panel.mobileRoot); +}); + +//------------------------------------------------------------------------------ +// Case: Test appearance and behavior of the bookmark popup +gTests.push({ + desc: "Test appearance and behavior of the bookmark popup", + _currentTab: null, + + run: function() { + this._currentTab = Browser.addTab(testURL_02, true); + + messageManager.addMessageListener("pageshow", + function(aMessage) { + if (gCurrentTest._currentTab.browser.currentURI.spec != "about:blank") { + messageManager.removeMessageListener(aMessage.name, arguments.callee); + + // Wait a bit until Places is initialized + waitFor(gCurrentTest.onPageLoad, function() { + let mobileRoot = PlacesUtils.annotations.getItemsWithAnnotation("mobile/bookmarksRoot", {})[0]; + return mobileRoot; + }); + } + }); + }, + + onPageLoad: function() { + let starbutton = document.getElementById("tool-star"); + starbutton.click(); + + waitFor(gCurrentTest.onPopupReady, function() { return BookmarkPopup.box.hidden == false; }); + }, + + onPopupReady: function() { + // Let's make it disappear again by clicking the star again + let starbutton = document.getElementById("tool-star"); + starbutton.click(); + + waitFor(gCurrentTest.onPopupGone, function() { return BookmarkPopup.box.hidden == true; }); + }, + + onPopupGone: function() { + // Make sure it's hidden again + is(BookmarkPopup.box.hidden, true, "Bookmark popup should be hidden by clicking star"); + + // Let's make it appear again and continue the test + let starbutton = document.getElementById("tool-star"); + starbutton.click(); + + waitFor(gCurrentTest.onPopupReady2, function() { return BookmarkPopup.box.hidden == false; }); + }, + + onPopupReady2: function() { + // Let's make it disappear again by clicking somewhere + let contentarea = document.getElementById("browsers"); + EventUtils.synthesizeMouse(contentarea, contentarea.clientWidth / 2, contentarea.clientHeight / 2, {}); + + waitFor(gCurrentTest.onPopupGone2, function() { return BookmarkPopup.box.hidden == true; }); + }, + + onPopupGone2: function() { + // Make sure it's hidden again + is(BookmarkPopup.box.hidden, true, "Bookmark popup should be hidden by clicking in content"); + + BookmarkHelper.removeBookmarksForURI(getBrowser().currentURI); + BrowserUI.closeTab(this._currentTab); + + runNextTest(); + } +}); + +//------------------------------------------------------------------------------ +// Case: Test adding tags via star icon +gTests.push({ + desc: "Test adding tags via star icon", + _currentTab: null, + + run: function() { + this._currentTab = Browser.addTab(testURL_02, true); + + messageManager.addMessageListener("pageshow", + function(aMessage) { + if (gCurrentTest._currentTab.browser.currentURI.spec != "about:blank") { + messageManager.removeMessageListener(aMessage.name, arguments.callee); + gCurrentTest.onPageLoad(); + } + }); + }, + + onPageLoad: function() { + let starbutton = document.getElementById("tool-star"); + starbutton.click(); + + waitFor(gCurrentTest.onPopupReady, function() { return BookmarkPopup.box.hidden == false }); + }, + + onPopupReady: function() { + let editbutton = document.getElementById("bookmark-popup-edit"); + editbutton.click(); + + waitFor(gCurrentTest.onEditorReady, function() { + let item = document.getElementById("bookmark-item"); + return item && item.isEditing == true; + }); + }, + + onEditorReady: function() { + let bookmarkitem = document.getElementById("bookmark-item"); + bookmarkitem.tags = "tagone, tag two, tag-three, tag4"; + + let donebutton = document.getAnonymousElementByAttribute(bookmarkitem, "anonid", "done-button"); + donebutton.click(); + + waitFor(gCurrentTest.onEditorDone, function() { return document.getElementById("bookmark-container").hidden == true; }); + }, + + onEditorDone: function() { + let uri = makeURI(testURL_02); + let tagsarray = PlacesUtils.tagging.getTagsForURI(uri, {}); + is(tagsarray.length, 4, "All tags are added."); + + BookmarkHelper.removeBookmarksForURI(uri); + BrowserUI.closeTab(this._currentTab); + + runNextTest(); + } +}); + +//------------------------------------------------------------------------------ +// Case: Test editing uri via star icon +gTests.push({ + desc: "Test editing uri via star icon", + _currentTab: null, + + run: function() { + this._currentTab = Browser.addTab(testURL_02, true); + + messageManager.addMessageListener("pageshow", + function(aMessage) { + if (gCurrentTest._currentTab.browser.currentURI.spec != "about:blank") { + messageManager.removeMessageListener(aMessage.name, arguments.callee); + gCurrentTest.onPageLoad(); + } + }); + }, + + onPageLoad: function() { + let starbutton = document.getElementById("tool-star"); + starbutton.click(); + + waitFor(gCurrentTest.onPopupReady, function() { + return BookmarkPopup.box.hidden == false && + PlacesUtils.getMostRecentBookmarkForURI(makeURI(testURL_02)) != -1; + }); + }, + + onPopupReady: function() { + let editbutton = document.getElementById("bookmark-popup-edit"); + editbutton.click(); + + waitFor(gCurrentTest.onEditorReady, function() { + let item = document.getElementById("bookmark-item"); + return item && item.isEditing == true; + }); + }, + + onEditorReady: function() { + let bookmarkitem = document.getElementById("bookmark-item"); + bookmarkitem.spec = testURL_01; + + let donebutton = document.getAnonymousElementByAttribute(bookmarkitem, "anonid", "done-button"); + donebutton.click(); + + waitFor(gCurrentTest.onEditorDone, function() { return document.getElementById("bookmark-container").hidden == true; }); + }, + + onEditorDone: function() { + isnot(PlacesUtils.getMostRecentBookmarkForURI(makeURI(testURL_01)), -1, testURL_01 + " is now bookmarked"); + is(PlacesUtils.getMostRecentBookmarkForURI(makeURI(testURL_02)), -1, testURL_02 + " is no longer bookmarked"); + + BookmarkHelper.removeBookmarksForURI(makeURI(testURL_02)); + BrowserUI.closeTab(this._currentTab); + + runNextTest(); + } +}); + +//------------------------------------------------------------------------------ +// Case: Test removing existing bookmark via popup +gTests.push({ + desc: "Test removing existing bookmark via popup", + _currentTab: null, + run: function() { + this._currentTab = Browser.addTab(testURL_01, true); + + messageManager.addMessageListener("pageshow", + function(aMessage) { + if (gCurrentTest._currentTab.browser.currentURI.spec != "about:blank") { + messageManager.removeMessageListener(aMessage.name, arguments.callee); + gCurrentTest.onPageLoad(); + } + }); + }, + + onPageLoad: function() { + let starbutton = document.getElementById("tool-star"); + starbutton.click(); + + waitFor(gCurrentTest.onPopupReady, function() { + return BookmarkPopup.box.hidden == false && + PlacesUtils.getMostRecentBookmarkForURI(makeURI(testURL_01)) != -1; + }); + }, + + onPopupReady: function() { + let removebutton = document.getElementById("bookmark-popup-remove"); + removebutton.click(); + + let bookmark = PlacesUtils.getMostRecentBookmarkForURI(makeURI(testURL_01)); + ok(bookmark == -1, testURL_01 + " should no longer in bookmark"); + + BrowserUI.closeTab(this._currentTab); + + runNextTest(); + } +}); diff --git a/mobile/android/chrome/tests/browser_bookmarks_tags.js b/mobile/android/chrome/tests/browser_bookmarks_tags.js new file mode 100644 index 000000000000..43849e3ce25c --- /dev/null +++ b/mobile/android/chrome/tests/browser_bookmarks_tags.js @@ -0,0 +1,192 @@ +/* + * Bug 486490 - Fennec browser-chrome tests to verify correct implementation of chrome + * code in mobile/chrome/content in terms of integration with Places + * component, specifically for bookmark management. + */ + +var testURL_01 = chromeRoot + "browser_blank_01.html"; +var testURL_02 = chromeRoot + "browser_blank_02.html"; + +// A queue to order the tests and a handle for each test +var gTests = []; +var gCurrentTest = null; + +//------------------------------------------------------------------------------ +// Entry point (must be named "test") +function test() { + // The "runNextTest" approach is async, so we need to call "waitForExplicitFinish()" + // We call "finish()" when the tests are finished + waitForExplicitFinish(); + + // Start the tests + runNextTest(); +} + +//------------------------------------------------------------------------------ +// Iterating tests by shifting test out one by one as runNextTest is called. +function runNextTest() { + // Run the next test until all tests completed + if (gTests.length > 0) { + gCurrentTest = gTests.shift(); + info(gCurrentTest.desc); + gCurrentTest.run(); + } + else { + // Cleanup. All tests are completed at this point + try { + PlacesUtils.bookmarks.removeFolderChildren(BookmarkList.panel.mobileRoot); + } + finally { + // We must finialize the tests + finish(); + } + } +} + +//------------------------------------------------------------------------------ +// Case: Test adding tags to bookmark +gTests.push({ + desc: "Test adding tags to a bookmark", + _currentTab: null, + + run: function() { + this._currentTab = Browser.addTab(testURL_02, true); + function handleEvent() { + gCurrentTest._currentTab.browser.removeEventListener("load", handleEvent, true); + gCurrentTest.onPageLoad(); + }; + this._currentTab.browser.addEventListener("load", handleEvent , true); + }, + + onPageLoad: function() { + let starbutton = document.getElementById("tool-star"); + starbutton.click(); + + window.addEventListener("BookmarkCreated", function(aEvent) { + window.removeEventListener(aEvent.type, arguments.callee, false); + let bookmarkItem = PlacesUtils.getMostRecentBookmarkForURI(makeURI(testURL_02)); + ok(bookmarkItem != -1, testURL_02 + " should be added."); + + // Wait for the bookmarks to load, then do the test + window.addEventListener("NavigationPanelShown", gCurrentTest.onBookmarksReady, false); + BrowserUI.doCommand("cmd_bookmarks"); + }, false); + }, + + onBookmarksReady: function() { + window.removeEventListener("NavigationPanelShown", gCurrentTest.onBookmarksReady, false); + + // Go into edit mode + let bookmark = document.getAnonymousElementByAttribute(BookmarkList.panel, "uri", testURL_02); + bookmark.startEditing(); + + waitFor(gCurrentTest.onEditorReady, function() { return bookmark.isEditing == true; }); + }, + + onEditorReady: function() { + let bookmark = document.getAnonymousElementByAttribute(BookmarkList.panel, "uri", testURL_02); + let tagstextbox = document.getAnonymousElementByAttribute(bookmark, "anonid", "tags"); + tagstextbox.value = "tagone, tag two, tag-three, tag4"; + + let donebutton = document.getAnonymousElementByAttribute(bookmark, "anonid", "done-button"); + donebutton.click(); + + let tagsarray = PlacesUtils.tagging.getTagsForURI(makeURI(testURL_02), {}); + is(tagsarray.length, 4, "All tags are associated with specified bookmark"); + + AwesomeScreen.activePanel = null; + + Browser.closeTab(gCurrentTest._currentTab); + + runNextTest(); + } +}); + +//------------------------------------------------------------------------------ +// Case: Test editing tags to bookmark +gTests.push({ + desc: "Test editing tags to bookmark", + + run: function() { + // Wait for the bookmarks to load, then do the test + window.addEventListener("NavigationPanelShown", gCurrentTest.onBookmarksReady, false); + BrowserUI.doCommand("cmd_bookmarks"); + }, + + onBookmarksReady: function() { + window.removeEventListener("NavigationPanelShown", gCurrentTest.onBookmarksReady, false); + + + // Go into edit mode + let bookmark = document.getAnonymousElementByAttribute(BookmarkList.panel, "uri", testURL_02); + bookmark.startEditing(); + + waitFor(gCurrentTest.onEditorReady, function() { return bookmark.isEditing == true; }); + }, + + onEditorReady: function() { + let bookmark = document.getAnonymousElementByAttribute(BookmarkList.panel, "uri", testURL_02); + + let taggeduri = PlacesUtils.tagging.getURIsForTag("tag-three"); + is(taggeduri[0].spec, testURL_02, "Old tag still associated with bookmark"); + + let tagstextbox = document.getAnonymousElementByAttribute(bookmark, "anonid", "tags"); + tagstextbox.value = "tagone, tag two, edited-tag-three, tag4"; + + let donebutton = document.getAnonymousElementByAttribute(bookmark, "anonid", "done-button"); + donebutton.click(); + + let untaggeduri = PlacesUtils.tagging.getURIsForTag("tag-three"); + is(untaggeduri, "", "Old tag is not associated with any bookmark"); + taggeduri = PlacesUtils.tagging.getURIsForTag("edited-tag-three"); + is(taggeduri[0].spec, testURL_02, "New tag is added to bookmark"); + let tagsarray = PlacesUtils.tagging.getTagsForURI(makeURI(testURL_02), {}); + is(tagsarray.length, 4, "Bookmark still has same number of tags"); + + AwesomeScreen.activePanel = null; + + runNextTest(); + } +}); + + +//------------------------------------------------------------------------------ +// Case: Test removing tags from bookmark +gTests.push({ + desc: "Test removing tags from a bookmark", + _currentTab: null, + + run: function() { + // Wait for the bookmarks to load, then do the test + window.addEventListener("NavigationPanelShown", gCurrentTest.onBookmarksReady, false); + BrowserUI.doCommand("cmd_bookmarks"); + }, + + onBookmarksReady: function() { + window.removeEventListener("NavigationPanelShown", gCurrentTest.onBookmarksReady, false); + + // Go into edit mode + let bookmark = document.getAnonymousElementByAttribute(BookmarkList.panel, "uri", testURL_02); + bookmark.startEditing(); + + waitFor(gCurrentTest.onEditorReady, function() { return bookmark.isEditing == true; }); + }, + + onEditorReady: function() { + let bookmark = document.getAnonymousElementByAttribute(BookmarkList.panel, "uri", testURL_02); + + let tagstextbox = document.getAnonymousElementByAttribute(bookmark, "anonid", "tags"); + tagstextbox.value = "tagone, tag two, tag4"; + + let donebutton = document.getAnonymousElementByAttribute(bookmark, "anonid", "done-button"); + donebutton.click(); + + let untaggeduri = PlacesUtils.tagging.getURIsForTag("edited-tag-three"); + is(untaggeduri, "", "Old tag is not associated with any bookmark"); + let tagsarray = PlacesUtils.tagging.getTagsForURI(makeURI(testURL_02), {}); + is(tagsarray.length, 3, "Tag is successfully deleted"); + + AwesomeScreen.activePanel = null; + runNextTest(); + } +}); diff --git a/mobile/android/chrome/tests/browser_click_content.html b/mobile/android/chrome/tests/browser_click_content.html new file mode 100644 index 000000000000..071ce21c24ba --- /dev/null +++ b/mobile/android/chrome/tests/browser_click_content.html @@ -0,0 +1,7 @@ + +Browser Click Page 01 + + + + + diff --git a/mobile/android/chrome/tests/browser_click_content.js b/mobile/android/chrome/tests/browser_click_content.js new file mode 100644 index 000000000000..f06428f90e2d --- /dev/null +++ b/mobile/android/chrome/tests/browser_click_content.js @@ -0,0 +1,128 @@ +let testURL_click = chromeRoot + "browser_click_content.html"; + +let currentTab; +let element; +let clickPosition = { x: null, y: null}; + +//------------------------------------------------------------------------------ +// Entry point (must be named "test") +function test() { + // This test is async + waitForExplicitFinish(); + + // Add new tab + currentTab = Browser.addTab(testURL_click, true); + ok(currentTab, "Tab Opened"); + + // Wait for the tab to load, then do the test + messageManager.addMessageListener("pageshow", function() { + if (currentTab.browser.currentURI.spec == testURL_click) { + messageManager.removeMessageListener("pageshow", arguments.callee); + testClickAndPosition(); + }}); +} + +function clickFired(aEvent) { + let [x, y] = browserViewToClient(aEvent.clientX, aEvent.clientY); + clickPosition.x = x; + clickPosition.y = y; +} + +function testClickAndPosition() { + // Do sanity tests + let uri = currentTab.browser.currentURI.spec; + is(uri, testURL_click, "URL Matches newly created Tab"); + + // Check click + element = currentTab.browser.contentDocument.getElementById("iframe-1"); + element.addEventListener("click", function(aEvent) { + element.removeEventListener("click", arguments.callee, true); + clickFired(aEvent); + is(aEvent.type, "click", "Click fired"); + checkClick(); + }, true); + + EventUtils.synthesizeMouseForContent(element, 1, 1, {}, window); +} + +function checkClick() { + // Check position + element = currentTab.browser.contentDocument.documentElement; + element.addEventListener("click", function(aEvent) { + element.removeEventListener("click", arguments.callee, true); + clickFired(aEvent); + checkPosition(); + }, true); + + let rect = getBoundingContentRect(element); + EventUtils.synthesizeMouse(element, 1, rect.height + 10, {}, window); +} + +function checkPosition() { + let rect = getBoundingContentRect(element); + is(clickPosition.x, 1, "X position is correct"); + is(clickPosition.y, rect.height + 10, "Y position is correct"); + + checkThickBorder(); +} + +function checkThickBorder() { + let frame = currentTab.browser.contentDocument.getElementById("iframe-2"); + let element = frame.contentDocument.getElementsByTagName("input")[0]; + + let frameRect = getBoundingContentRect(frame); + let frameLeftBorder = window.getComputedStyle(frame, "").borderLeftWidth; + let frameTopBorder = window.getComputedStyle(frame, "").borderTopWidth; + + let elementRect = getBoundingContentRect(element); + ok((frameRect.left + parseInt(frameLeftBorder)) < elementRect.left, "X position of nested element ok"); + ok((frameRect.top + parseInt(frameTopBorder)) < elementRect.top, "Y position of nested element ok"); + + close(); +} + +function close() { + // Close the tab + Browser.closeTab(currentTab); + + // We must finialize the tests + finish(); +} + +// XXX copied from chrome/content/content.js +function getBoundingContentRect(aElement) { + if (!aElement) + return new Rect(0, 0, 0, 0); + + let document = aElement.ownerDocument; + while(document.defaultView.frameElement) + document = document.defaultView.frameElement.ownerDocument; + + let content = document.defaultView; + let cwu = content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); + let scrollX = {}, scrollY = {}; + cwu.getScrollXY(false, scrollX, scrollY); + let offset = new Point(scrollX.value, scrollY.value); + let r = aElement.getBoundingClientRect(); + + // step out of iframes and frames, offsetting scroll values + for (let frame = aElement.ownerDocument.defaultView; frame != content; frame = frame.parent) { + // adjust client coordinates' origin to be top left of iframe viewport + let rect = frame.frameElement.getBoundingClientRect(); + let left = frame.getComputedStyle(frame.frameElement, "").borderLeftWidth; + let top = frame.getComputedStyle(frame.frameElement, "").borderTopWidth; + offset.add(rect.left + parseInt(left), rect.top + parseInt(top)); + } + + return new Rect(r.left + offset.x, r.top + offset.y, r.width, r.height); +} + +function browserViewToClient(x, y) { + let container = document.getElementById("browsers"); + let containerBCR = container.getBoundingClientRect(); + + let x0 = Math.round(-containerBCR.left); + let y0 = Math.round(-containerBCR.top); + + return [x - x0, y - y0]; +} diff --git a/mobile/android/chrome/tests/browser_contacts.js b/mobile/android/chrome/tests/browser_contacts.js new file mode 100644 index 000000000000..47d61ac93cf7 --- /dev/null +++ b/mobile/android/chrome/tests/browser_contacts.js @@ -0,0 +1,88 @@ + +// pull in the Contacts service +Components.utils.import("resource:///modules/contacts.jsm"); + +let fac = Cc["@mozilla.org/satchel/form-autocomplete;1"].getService(Ci.nsIFormAutoComplete); +let fh = Cc["@mozilla.org/satchel/form-history;1"].getService(Ci.nsIFormHistory2); + +function test() { + ok(Contacts, "Contacts class exists"); + for (var fname in tests) { + tests[fname](); + } +} + +let MockContactsProvider = { + getContacts: function() { + let contacts = [ + { + fullName: "-Billy One", + emails: [], + phoneNumbers: ["999-888-7777"] + }, + { + fullName: "-Billy Two", + emails: ["billy.two@fake.com", "btwo@work.com"], + phoneNumbers: ["111-222-3333", "123-123-1234"] + }, + { + fullName: "-Joe Schmo", + emails: ["joeschmo@foo.com"], + phoneNumbers: ["555-555-5555"] + } + ]; + + return contacts; + } +}; + +// In case there are real contacts that could mess up our test counts +let preEmailCount = fac.autoCompleteSearch("email", "", null, null).matchCount; +let prePhoneCount = fac.autoCompleteSearch("tel", "", null, null).matchCount; + +Contacts.addProvider(MockContactsProvider); + +let tests = { + testBasicMatch: function() { + // Search for any emails + let results = fac.autoCompleteSearch("email", "", null, null); + is(results.matchCount, 3 + preEmailCount, "Found 3 emails for un-filtered search"); + + // Do some filtered searches + results = fac.autoCompleteSearch("email", "-Billy", null, null); + is(results.matchCount, 2, "Found 2 emails '-Billy'"); + + results = fac.autoCompleteSearch("tel", "-Billy", null, null); + is(results.matchCount, 3, "Found 3 phone numbers '-Billy'"); + + results = fac.autoCompleteSearch("skip", "-Billy", null, null); + is(results.matchCount, 0, "Found nothing for a non-contact field"); + + results = fac.autoCompleteSearch("phone", "-Jo", null, null); + is(results.matchCount, 1, "Found 1 phone number '-Jo'"); + }, + + testMixingData: function() { + // Add a simple value to the non-contact system + fh.addEntry("email", "super.cool@place.com"); + + let results = fac.autoCompleteSearch("email", "", null, null); + is(results.matchCount, 4 + preEmailCount, "Found 4 emails for un-filtered search"); + + let firstEmail = results.getValueAt(0); + is(firstEmail, "super.cool@place.com", "The non-contact entry is first"); + + fh.removeAllEntries(); + }, + + testFakeInputField: function() { + let attributes = ["type", "id", "class"]; + for (let i = 0; i < attributes.length; i++) { + let field = document.createElementNS("http://www.w3.org/1999/xhtml", "html:input"); + field.setAttribute(attributes[i], "tel"); + + let results = fac.autoCompleteSearch("", "-Jo", field, null); + is(results.matchCount, 1 + prePhoneCount, "Found 1 phone number -Jo"); + } + } +}; diff --git a/mobile/android/chrome/tests/browser_contentpopup.html b/mobile/android/chrome/tests/browser_contentpopup.html new file mode 100644 index 000000000000..882759457815 --- /dev/null +++ b/mobile/android/chrome/tests/browser_contentpopup.html @@ -0,0 +1,26 @@ + + + + + + + + + + +

+ + + + + + + + + + + + + + + diff --git a/mobile/android/chrome/tests/browser_contentpopup.js b/mobile/android/chrome/tests/browser_contentpopup.js new file mode 100644 index 000000000000..305a7bf0f78e --- /dev/null +++ b/mobile/android/chrome/tests/browser_contentpopup.js @@ -0,0 +1,89 @@ +let testURL = chromeRoot + "browser_contentpopup.html"; +messageManager.loadFrameScript(chromeRoot + "remote_contentpopup.js", true); + +let newTab = null; + +// A queue to order the tests and a handle for each test +var gTests = []; +var gCurrentTest = null; + +function test() { + // This test is async + waitForExplicitFinish(); + + // Need to wait until the page is loaded + messageManager.addMessageListener("pageshow", function(aMessage) { + if (newTab && newTab.browser.currentURI.spec != "about:blank") { + messageManager.removeMessageListener(aMessage.name, arguments.callee); + BrowserUI.closeAutoComplete(true); + setTimeout(runNextTest, 0); + } + }); + + waitForFirstPaint(function() { + newTab = Browser.addTab(testURL, true); + }); +} + +//------------------------------------------------------------------------------ +// Iterating tests by shifting test out one by one as runNextTest is called. +function runNextTest() { + // Run the next test until all tests completed + if (gTests.length > 0) { + gCurrentTest = gTests.shift(); + info(gCurrentTest.desc); + gCurrentTest.run(); + } + else { + // Cleanup. All tests are completed at this point + try { + // Add any cleanup code here + + // Close our tab when finished + Browser.closeTab(newTab); + } + finally { + // We must finalize the tests + finish(); + } + } +} + +//------------------------------------------------------------------------------ +// Case: Show/Hide the content popup helper +gTests.push({ + desc: "Show/Hide the content popup helper", + + run: function() { + let popup = document.getElementById("form-helper-suggestions-container"); + popup.addEventListener("contentpopupshown", function(aEvent) { + aEvent.target.removeEventListener(aEvent.type, arguments.callee, false); + ok(!popup.hidden, "Content popup should be visible"); + waitFor(gCurrentTest.hidePopup, function() { + return FormHelperUI._open; + }); + }, false); + + AsyncTests.waitFor("TestRemoteAutocomplete:Click", + { id: "input-datalist-1" }, function(json) {}); + }, + + hidePopup: function() { + let popup = document.getElementById("form-helper-suggestions-container"); + popup.addEventListener("contentpopuphidden", function(aEvent) { + popup.removeEventListener("contentpopuphidden", arguments.callee, false); + ok(popup.hidden, "Content popup should be hidden"); + waitFor(gCurrentTest.finish, function() { + return !FormHelperUI._open; + }); + }, false); + + // Close the form assistant + FormHelperUI.hide(); + }, + + finish: function() { + runNextTest(); + } +}); + diff --git a/mobile/android/chrome/tests/browser_dragger.js b/mobile/android/chrome/tests/browser_dragger.js new file mode 100644 index 000000000000..0b873ddc4ee0 --- /dev/null +++ b/mobile/android/chrome/tests/browser_dragger.js @@ -0,0 +1,89 @@ +"use strict"; + +const testURL_01 = chromeRoot + "browser_blank_01.html"; +const testURL_01_Remote = serverRoot + "browser_blank_01.html"; + +function test() { + waitForExplicitFinish(); + runNextTest(); +} + +gTests.push({ + desc: "Test that kinetic panning does not open sidebars.", + tab: null, + + run: function() { + gCurrentTest.tab = Browser.addTab(testURL_01, true); + onMessageOnce(gCurrentTest.tab.browser.messageManager, "Browser:FirstPaint", gCurrentTest.checkPan); + }, + + checkPan: function() { + let browser = gCurrentTest.tab.browser; + let docWidth = browser.contentDocumentWidth * browser.scale; + let winWidth = window.innerWidth; + info("Browser document width is " + docWidth); + info("Window width is " + winWidth); + ok(docWidth <= winWidth, + "Sanity check. Blank document cannot be panned left or right."); + + function dragAndCheck(dx) { + let dragger = Elements.browsers.customDragger; + try { + dragger.dragStart(0, 0, null, null); + dragger.dragMove(dx, 0, null, true); + + let [leftVis, rightVis] = Browser.computeSidebarVisibility(); + is(leftVis, 0, "Left sidebar is not visible"); + is(rightVis, 0, "Right sidebar is not visible"); + } finally { + // Be fail tolerant and hide sidebars in case tests failed. + Browser.hideSidebars(); + dragger.dragStop(); + } + } + + dragAndCheck(-20); + dragAndCheck(20); + + Browser._doCloseTab(gCurrentTest.tab); + runNextTest(); + } +}); + +gTests.push({ + desc: "Test that urlbar cannot be panned in when content is captured.", + tab: null, + + run: function() { + gCurrentTest.tab = Browser.addTab(testURL_01_Remote, true); + Browser.selectedTab = gCurrentTest.tab; + onMessageOnce(gCurrentTest.tab.browser.messageManager, "MozScrolledAreaChanged", gCurrentTest.mouseMove); + }, + + mouseMove: function(json) { + let inputHandler = gCurrentTest.tab.browser.parentNode; + function fireMouseEvent(y, type) { + EventUtils.synthesizeMouse(inputHandler, 0, y, { type: type }); + } + + Browser.hideTitlebar(); + let rect = Elements.browsers.getBoundingClientRect(); + is(rect.top, 0, "Titlebar begins hidden"); + + let dragger = Elements.browsers.customDragger; + try { + dragger.contentCanCapture = true; + dragger.dragStart(0, 0, null, null); + dragger.dragMove(0, 20, null, true); + dragger.dragStop(); + } finally { + dragger.contentCanCapture = false; + } + + rect = Elements.browsers.getBoundingClientRect(); + is(rect.top, 0, "Titlebar is still hidden"); + + Browser._doCloseTab(gCurrentTest.tab); + runNextTest(); + } +}); diff --git a/mobile/android/chrome/tests/browser_escape.js b/mobile/android/chrome/tests/browser_escape.js new file mode 100644 index 000000000000..5b17f0340f5e --- /dev/null +++ b/mobile/android/chrome/tests/browser_escape.js @@ -0,0 +1,128 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Mobile Browser. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Matt Brubeck + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +"use strict"; +const url1 = baseURI + "browser_blank_01.html"; +const url2 = baseURI + "browser_blank_02.html"; +const url3 = baseURI + "browser_blank_03.html"; +let tab1, tab2; + +function test() { + waitForExplicitFinish(); + testGoBack(); + + registerCleanupFunction(closeTabs); +} + +function testGoBack() { + tab1 = Browser.addTab("about:blank", true); + tab2 = Browser.addTab("about:blank", true, tab1); + let browser = tab2.browser; + + // Load each of these URLs, then use escape to step backward through them. + let urls = [url1, url2, url3]; + let i = 0, step = 1; + let expectedURI; + + function loadNextPage() { + expectedURI = urls[i]; + if (step == 1) { + // go forward + Browser.loadURI(expectedURI); + } else { + // go back by pressing the escape key + Browser.selectedBrowser.focus(); + EventUtils.synthesizeKey("VK_ESCAPE", {type: "keypress"}, window); + } + } + + browser.messageManager.addMessageListener("pageshow", function listener(aMessage) { + let uri = browser.currentURI.spec; + if (uri == "about:blank") { + loadNextPage(); + return; + } + is(uri, expectedURI, "Page " + i + " loaded"); + + if (i == urls.length - 1) + step = -1; // start going back when we get to the end + i += step; + + if (i >= 0) { + loadNextPage(); + //setTimeout(loadNextPage, 1000); + } else { + // All done. Go to the next test. + browser.messageManager.removeMessageListener("pageshow", listener); + closeTabs(); + testReturnToOwner(); + } + }); +} + +function testReturnToOwner() { + tab1 = Browser.addTab("about:blank", true); + tab2 = Browser.addTab("about:blank", true, tab1); + is(Browser.selectedTab, tab2, "tab2 is selected"); + EventUtils.sendKey("ESCAPE", window); + is(Browser.selectedTab, tab1, "tab1 is selected"); + closeTabs(); + testContextMenu(); +} + +function testContextMenu() { + ContextHelper.showPopup({ + json: { + types: ['link'] + }, + target: Browser.selectedBrowser + }); + ok(ContextHelper.popupState, "Context menu is shown"); + Browser.selectedBrowser.focus(); + EventUtils.synthesizeKey("VK_ESCAPE", {type: "keypress"}, window); + ok(!ContextHelper.popupState, "Context menu is dismissed"); + finish(); +} + +function closeTabs() { + try { + Browser.closeTab(tab1); + Browser.closeTab(tab2); + } finally { + tab1 = tab2 = null; + } +} diff --git a/mobile/android/chrome/tests/browser_find.js b/mobile/android/chrome/tests/browser_find.js new file mode 100644 index 000000000000..89d0392c1b92 --- /dev/null +++ b/mobile/android/chrome/tests/browser_find.js @@ -0,0 +1,39 @@ +// Tests for the Find In Page UI + +//------------------------------------------------------------------------------ +// Entry point (must be named "test") +function test() { + addEventListener("PopupChanged", popupOpened, false); + CommandUpdater.doCommand("cmd_menu"); +} + +function popupOpened() { + removeEventListener("PopupChanged", popupOpened, false); + + let menu = document.getElementById("appmenu"); + ok(!menu.hidden, "App menu is shown"); + + let navigator = document.getElementById("content-navigator"); + ok(!navigator.isActive, "Toolbar is closed"); + + addEventListener("PopupChanged", findOpened, false); + let item = document.getElementsByClassName("appmenu-findinpage-button")[0]; + item.click(); +} + +function findOpened() { + removeEventListener("PopupChanged", findOpened, false); + + let menu = document.getElementById("appmenu"); + ok(menu.hidden, "App menu is closed"); + + let navigator = document.getElementById("content-navigator"); + ok(navigator.isActive, "Toolbar is open"); + + is(navigator._previousButton.disabled, true, "Previous button should be disabled"); + is(navigator._nextButton.disabled, true, "Previous button should be disabled"); + + EventUtils.synthesizeKey("VK_ESCAPE", {}, window); + ok(menu.hidden, "Site menu is closed"); + ok(!navigator.isActive, "Toolbar is closed"); +} diff --git a/mobile/android/chrome/tests/browser_focus.html b/mobile/android/chrome/tests/browser_focus.html new file mode 100644 index 000000000000..95550379da60 --- /dev/null +++ b/mobile/android/chrome/tests/browser_focus.html @@ -0,0 +1,7 @@ + + + Focus/Activate test + + + + diff --git a/mobile/android/chrome/tests/browser_focus.js b/mobile/android/chrome/tests/browser_focus.js new file mode 100644 index 000000000000..5b53f4339c48 --- /dev/null +++ b/mobile/android/chrome/tests/browser_focus.js @@ -0,0 +1,52 @@ +"use strict"; +let testURL = chromeRoot + "browser_focus.html"; +let newTab = null; + +function test() { + waitForExplicitFinish(); + + registerCleanupFunction(function() { + try { + messageManager.sendAsyncMessage("Test:E10SFocusTestFinished", {}); + Browser.closeTab(newTab); + } finally { + newTab = null; + } + }); + + messageManager.addMessageListener("pageshow", function listener(aMessage) { + if (newTab && newTab.browser.currentURI.spec != "about:blank") { + messageManager.removeMessageListener("pageshow", listener); + setTimeout(onTabLoaded, 0); + } + }); + + newTab = Browser.addTab(testURL, true); +} + +function onTabLoaded() { + // ensure that the is not already focused + newTab.browser.blur(); + messageManager.loadFrameScript(chromeRoot + "remote_focus.js", false); + testFocus(); +} + +function testFocus() { + onMessageOnce(messageManager, "Test:E10SFocusReceived", function() { + ok("Focus in triggered activateRemoteFrame as expected"); + testBlur(); + }); + newTab.browser.focus(); +} + +function testBlur() { + onMessageOnce(messageManager, "Test:E10SBlurReceived", function() { + ok("Blur in triggerered deactivateRemoteFrame as expected"); + endTest(); + }); + newTab.browser.blur(); +} + +function endTest() { + finish(); +} diff --git a/mobile/android/chrome/tests/browser_forms.html b/mobile/android/chrome/tests/browser_forms.html new file mode 100644 index 000000000000..5566bbe5bb52 --- /dev/null +++ b/mobile/android/chrome/tests/browser_forms.html @@ -0,0 +1,51 @@ + + + Browser Form Assistant + + + + + + + + + + text + +
click here
+ + +
+ + + dumb type + + +
div
+ + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/chrome/tests/browser_forms.js b/mobile/android/chrome/tests/browser_forms.js new file mode 100644 index 000000000000..37589eb19b07 --- /dev/null +++ b/mobile/android/chrome/tests/browser_forms.js @@ -0,0 +1,242 @@ +let testURL = chromeRoot + "browser_forms.html"; +messageManager.loadFrameScript(chromeRoot + "remote_forms.js", true); + +let newTab = null; + +function test() { + // This test is async + waitForExplicitFinish(); + + // Need to wait until the page is loaded + messageManager.addMessageListener("pageshow", function(aMessage) { + if (newTab && newTab.browser.currentURI.spec != "about:blank") { + messageManager.removeMessageListener(aMessage.name, arguments.callee); + setTimeout(onTabLoaded, 0); + } + }); + + // Add new tab to hold the page + newTab = Browser.addTab(testURL, true); +} + +function onTabLoaded() { + BrowserUI.closeAutoComplete(true); + testMouseEvents(); +} + +function testMouseEvents() { + // Sending a synthesized event directly on content should not work - we + // don't want web content to be able to open the form helper without the + // user consent, so we have to pass through the canvas tile-container + AsyncTests.waitFor("Test:Click", {}, function(json) { + is(json.result, false, "Form Assistant should stay closed"); + }); + + AsyncTests.waitFor("Test:Focus", { value: "#root" }, function(json) { + is(json.result, false, "Form Assistant should stay closed"); + }); + + AsyncTests.waitFor("Test:FocusRedirect", { value: "*[tabindex='0']" }, function(json) { + is(json.result, false, "Form Assistant should stay closed"); + testOpenUIWithSyncFocus(); + }); +}; + +function waitForFormAssist(aCallback) { + messageManager.addMessageListener("FormAssist:Show", function(aMessage) { + messageManager.removeMessageListener(aMessage.name, arguments.callee); + setTimeout(function() { + ok(FormHelperUI._open, "Form Assistant should be open"); + setTimeout(aCallback, 0); + }); + }); +}; + +function testOpenUIWithSyncFocus() { + AsyncTests.waitFor("Test:Open", { value: "*[tabindex='0']" }, function(json) {}); + waitForFormAssist(testOpenUI); +}; + +function testOpenUI() { + AsyncTests.waitFor("Test:Open", { value: "*[tabindex='0']" }, function(json) {}); + waitForFormAssist(testOpenUIWithFocusRedirect); +}; + +function testOpenUIWithFocusRedirect() { + AsyncTests.waitFor("Test:OpenWithFocusRedirect", { value: "*[tabindex='0']" }, function(json) {}); + waitForFormAssist(testShowUIForSelect); +}; + +function testShowUIForSelect() { + AsyncTests.waitFor("Test:CanShowUI", { value: "#select"}, function(json) { + ok(json.result, "canShowUI for select element'"); + }); + + AsyncTests.waitFor("Test:CanShowUI", { value: "#select", disabled: true }, function(json) { + is(json.result, false, "!canShowUI for disabled select element'"); + }); + + AsyncTests.waitFor("Test:CanShowUI", { value: "#option"}, function(json) { + ok(json.result, "canShowUI for option element'"); + }); + + AsyncTests.waitFor("Test:CanShowUISelect", { value: "#option", disabled: true }, function(json) { + is(json.result, false, "!canShowUI for option element with a disabled parent select element'"); + }); + + AsyncTests.waitFor("Test:CanShowUI", { value: "#option", disabled: true }, function(json) { + is(json.result, false, "!canShowUI for disabled option element'"); + testShowUIForElements(); + }); +} + +function testShowUIForElements() { + AsyncTests.waitFor("Test:CanShowUI", { value: "*[tabindex='1']" }, function(json) { + ok(json.result, "canShowUI for input type='text'"); + }); + + AsyncTests.waitFor("Test:CanShowUI", { value: "*[tabindex='1']", disabled: true }, function(json) { + is(json.result, false, "!canShowUI for disabled input type='text'"); + }); + + AsyncTests.waitFor("Test:CanShowUI", { value: "*[tabindex='2']" }, function(json) { + ok(json.result, "canShowUI for input type='password'"); + }); + + AsyncTests.waitFor("Test:CanShowUI", { value: "*[tabindex='2']", disabled: true }, function(json) { + is(json.result, false, "!canShowUI for disabled input type='password'"); + }); + + AsyncTests.waitFor("Test:CanShowUI", { value: "*[tabindex='8']" }, function(json) { + ok(json.result, "canShowUI for contenteditable div"); + }); + + AsyncTests.waitFor("Test:CanShowUI", { value: "*[tabindex='8']", disabled: true }, function(json) { + is(json.result, false, "!canShowUI for disabled contenteditable div"); + }); + + AsyncTests.waitFor("Test:CanShowUI", { value: "*[tabindex='3']" }, function(json) { + is(json.result, false, "!canShowUI for input type='submit'"); + }); + + AsyncTests.waitFor("Test:CanShowUI", { value: "*[tabindex='4']" }, function(json) { + is(json.result, false, "!canShowUI for input type='file'"); + }); + + AsyncTests.waitFor("Test:CanShowUI", { value: "*[tabindex='5']" }, function(json) { + is(json.result, false, "!canShowUI for input button type='submit'"); + }); + + AsyncTests.waitFor("Test:CanShowUI", { value: "*[tabindex='6']" }, function(json) { + is(json.result, false, "!canShowUI for input div@role='button'"); + }); + + AsyncTests.waitFor("Test:CanShowUI", { value: "*[tabindex='6']" }, function(json) { + is(json.result, false, "!canShowUI for input type='image'"); + }); + + // Open the Form Helper + AsyncTests.waitFor("Test:Open", { value: "*[tabindex='1']" }, function(json) { + ok(json.result, "Form Assistant should be open"); + testTabIndexNavigation(); + }); +}; + +function testTabIndexNavigation() { + AsyncTests.waitFor("Test:Previous", { value: "*[tabindex='0']" }, function(json) { + is(json.result, false, "Focus should not have changed"); + }); + + AsyncTests.waitFor("Test:Next", { value: "*[tabindex='2']" }, function(json) { + is(json.result, true, "Focus should be on element with tab-index : 2"); + }); + + AsyncTests.waitFor("Test:Previous", { value: "*[tabindex='1']" }, function(json) { + is(json.result, true, "Focus should be on element with tab-index : 1"); + }); + + AsyncTests.waitFor("Test:Next"); + AsyncTests.waitFor("Test:Next"); + AsyncTests.waitFor("Test:Next"); + AsyncTests.waitFor("Test:Next"); + AsyncTests.waitFor("Test:Next"); + + AsyncTests.waitFor("Test:Next", { value: "*[tabindex='7']" }, function(json) { + is(json.result, true, "Focus should be on element with tab-index : 7"); + }); + + AsyncTests.waitFor("Test:Next", { value: "*[tabindex='8']" }, function(json) { + is(json.result, true, "Focus should be on element with tab-index : 8"); + }); + + AsyncTests.waitFor("Test:Next", { value: "*[tabindex='0']" }, function(json) { + is(json.result, true, "Focus should be on element with tab-index : 0"); + }); + + let ids = ["next", "select", "dumb", "reset", "checkbox", "radio0", "radio4", "last", "last"]; + for (let i = 0; i < ids.length; i++) { + let id = ids[i]; + AsyncTests.waitFor("Test:Next", { value: "#" + id }, function(json) { + is(json.result, true, "Focus should be on element with #id: " + id + ""); + }); + }; + + FormHelperUI.hide(); + let container = document.getElementById("content-navigator"); + is(container.hidden, true, "Form Assistant should be close"); + + AsyncTests.waitFor("Test:Open", { value: "*[tabindex='0']" }, function(json) { + ok(FormHelperUI._open, "Form Assistant should be open"); + testFocusChanges(); + }); +}; + +function testFocusChanges() { + AsyncTests.waitFor("Test:Focus", { value: "*[tabindex='1']" }, function(json) { + ok(json.result, "Form Assistant should be open"); + }); + + AsyncTests.waitFor("Test:Focus", { value: "#select" }, function(json) { + ok(json.result, "Form Assistant should stay open"); + }); + + AsyncTests.waitFor("Test:Focus", { value: "*[type='hidden']" }, function(json) { + ok(json.result, "Form Assistant should stay open"); + loadNestedIFrames(); + }); +} + +function loadNestedIFrames() { + AsyncTests.waitFor("Test:Iframe", { }, function(json) { + is(json.result, true, "Iframe should have loaded"); + navigateIntoNestedIFrames(); + }); +} + +function navigateIntoNestedIFrames() { + AsyncTests.waitFor("Test:IframeOpen", { }, function(json) { + is(json.result, true, "Form Assistant should have been opened"); + }); + + AsyncTests.waitFor("Test:IframePrevious", { value: 0 }, function(json) { + is(json.result, true, "Focus should not have move"); + }); + + AsyncTests.waitFor("Test:IframeNext", { value: 1 }, function(json) { + is(json.result, true, "Focus should have move"); + }); + + AsyncTests.waitFor("Test:IframeNext", { value: 1 }, function(json) { + is(json.result, true, "Focus should not have move"); + + // Close the form assistant + FormHelperUI.hide(); + + // Close our tab when finished + Browser.closeTab(newTab); + + // We must finalize the tests + finish(); + }); +}; + diff --git a/mobile/android/chrome/tests/browser_formsZoom.html b/mobile/android/chrome/tests/browser_formsZoom.html new file mode 100644 index 000000000000..36f641d0d0dd --- /dev/null +++ b/mobile/android/chrome/tests/browser_formsZoom.html @@ -0,0 +1,66 @@ + + + Browser Zoom Test + + +















+
+
+
+ +
+ + diff --git a/mobile/android/chrome/tests/browser_formsZoom.js b/mobile/android/chrome/tests/browser_formsZoom.js new file mode 100644 index 000000000000..d78a26771690 --- /dev/null +++ b/mobile/android/chrome/tests/browser_formsZoom.js @@ -0,0 +1,214 @@ +let testURL_01 = chromeRoot + "browser_formsZoom.html"; +let testURL_02 = baseURI + "browser_formsZoom.html"; +messageManager.loadFrameScript(baseURI + "remote_formsZoom.js", true); + +// A queue to order the tests and a handle for each test +var gTests = []; +var gCurrentTest = null; + +//------------------------------------------------------------------------------ +// Entry point (must be named "test") +function test() { + // The "runNextTest" approach is async, so we need to call "waitForExplicitFinish()" + // We call "finish()" when the tests are finished + waitForExplicitFinish(); + // Start the tests + runNextTest(); +} + +function waitForPageShow(aPageURL, aCallback) { + messageManager.addMessageListener("pageshow", function(aMessage) { + if (aMessage.target.currentURI.spec == aPageURL) { + messageManager.removeMessageListener("pageshow", arguments.callee); + setTimeout(function() { aCallback(); }, 0); + } + }); +}; + +//------------------------------------------------------------------------------ +// Iterating tests by shifting test out one by one as runNextTest is called. +function runNextTest() { + // Run the next test until all tests completed + if (gTests.length > 0) { + gCurrentTest = gTests.shift(); + info(gCurrentTest.desc); + setTimeout(gCurrentTest.run, 0); + } + else { + // Cleanup. All tests are completed at this point + try { + // Add any cleanup code here + } + finally { + // We must finialize the tests + finish(); + } + } +} + +function waitForZoom(aCallback) { + if (AnimatedZoom.isZooming()) { + let self = this; + window.addEventListener("AnimatedZoomEnd", function() { + window.removeEventListener("AnimatedZoomEnd", arguments.callee, false); + setTimeout(aCallback, 0); + }, false); + } + else setTimeout(aCallback, 0); +} + +function isElementVisible(aElement) { + let elementRect = Rect.fromRect(aElement.rect); + let caretRect = Rect.fromRect(aElement.caretRect); + + let browser = getBrowser(); + let zoomRect = Rect.fromRect(browser.getBoundingClientRect()); + let scroll = browser.getRootView().getPosition(); + let browserRect = new Rect(scroll.x, scroll.y, zoomRect.width, zoomRect.height); + + info("CanZoom: " +Browser.selectedTab.allowZoom); + + info("Browser rect: " + browserRect + " - scale: " + browser.scale); + info("Element rect: " + elementRect + " - caret rect: " + caretRect); + info("Scale element rect: " + elementRect.clone().scale(browser.scale, browser.scale) + " - scale caretRect: " + caretRect.clone().scale(browser.scale, browser.scale)); + info("Resulting zoom rect: " + Browser._getZoomRectForPoint(elementRect.center().x, elementRect.y, browser.scale)); + + let isCaretSyncEnabled = Services.prefs.getBoolPref("formhelper.autozoom.caret"); + if (isCaretSyncEnabled) { + ok(browserRect.contains(caretRect.clone().scale(browser.scale, browser.scale)), "Caret rect should be completely visible"); + } + else { + elementRect = elementRect.clone().scale(browser.scale, browser.scale); + let resultRect = browserRect.intersect(elementRect); + ok(!resultRect.isEmpty() && elementRect.x > browserRect.x && elementRect.y > browserRect.y, "Element should be partially visible"); + } +} + + +//------------------------------------------------------------------------------ +// Case: Loading a page and Zoom into textarea field with caret sync disabled +gTests.push({ + desc: "Loading a page and Zoom into textarea field with caret sync enabled", + elements: ["textarea-1", "textarea-2", "textarea-3", "textarea-4"], + _currentTab: null, + + run: function() { + Services.prefs.setBoolPref("formhelper.autozoom.caret", false); + gCurrentTest._currentTab = BrowserUI.newTab(testURL_01); + + waitForPageShow(testURL_01, function() { gCurrentTest.zoomNext(); }); + }, + + zoomNext: function() { + let id = this.elements.shift(); + if (!id) { + BrowserUI.closeTab(); + runNextTest(); + return; + } + + info("Zooming to " + id); + AsyncTests.waitFor("FormAssist:Show", { id: id }, function(json) { + waitForZoom(function() { + isElementVisible(json.current); + gCurrentTest.zoomNext(); + }); + }); + } +}); + +//------------------------------------------------------------------------------ +// Case: Loading a page and Zoom into textarea field with caret sync enabled +gTests.push({ + desc: "Loading a page and Zoom into textarea field with caret sync enabled", + elements: ["textarea-1", "textarea-2", "textarea-3", "textarea-4"], + _currentTab: null, + + run: function() { + Services.prefs.setBoolPref("formhelper.autozoom.caret", true); + gCurrentTest._currentTab = BrowserUI.newTab(testURL_01); + + waitForPageShow(testURL_01, function() { gCurrentTest.zoomNext(); }); + }, + + zoomNext: function() { + let id = this.elements.shift(); + if (!id) { + BrowserUI.closeTab(); + runNextTest(); + return; + } + + AsyncTests.waitFor("FormAssist:Show", { id: id }, function(json) { + waitForZoom(function() { + isElementVisible(json.current); + gCurrentTest.zoomNext(); + }); + }); + } +}); + +//------------------------------------------------------------------------------ +// Case: Loading a remote page and Zoom into textarea field with caret sync disabled +gTests.push({ + desc: "Loading a remote page and Zoom into textarea field with caret sync disabled", + elements: ["textarea-1", "textarea-2", "textarea-3", "textarea-4"], + _currentTab: null, + + run: function() { + Services.prefs.setBoolPref("formhelper.autozoom.caret", false); + gCurrentTest._currentTab = BrowserUI.newTab(testURL_02); + + waitForPageShow(testURL_02, function() { gCurrentTest.zoomNext(); }); + }, + + zoomNext: function() { + let id = this.elements.shift(); + if (!id) { + BrowserUI.closeTab(); + runNextTest(); + return; + } + + AsyncTests.waitFor("FormAssist:Show", { id: id }, function(json) { + waitForZoom(function() { + isElementVisible(json.current); + gCurrentTest.zoomNext(); + }); + }); + } +}); + +//------------------------------------------------------------------------------ +// Case: Loading a remote page and Zoom into textarea field with caret sync enabled +gTests.push({ + desc: "Loading a remote page and Zoom into textarea field with caret sync enabled", + elements: ["textarea-1", "textarea-2"], + _currentTab: null, + + run: function() { + Services.prefs.setBoolPref("formhelper.autozoom.caret", true); + gCurrentTest._currentTab = BrowserUI.newTab(testURL_02); + + waitForPageShow(testURL_02, function() { gCurrentTest.zoomNext(); }); + }, + + zoomNext: function() { + let id = this.elements.shift(); + if (!id) { + todo(false, "textarea-3 caret should be synced, but for some reason it is not"); + todo(false, "textarea-4 caret should be synced, but for some reason it is not"); + BrowserUI.closeTab(); + runNextTest(); + return; + } + + AsyncTests.waitFor("FormAssist:Show", { id: id }, function(json) { + waitForZoom(function() { + isElementVisible(json.current); + gCurrentTest.zoomNext(); + }); + }); + } +}); + diff --git a/mobile/android/chrome/tests/browser_history.js b/mobile/android/chrome/tests/browser_history.js new file mode 100644 index 000000000000..dd4ad8061cea --- /dev/null +++ b/mobile/android/chrome/tests/browser_history.js @@ -0,0 +1,75 @@ +/* + * Make sure history is being recorded when we visit websites. + */ + +var testURL_01 = baseURI + "browser_blank_01.html"; + +// A queue to order the tests and a handle for each test +var gTests = []; +var gCurrentTest = null; + +//------------------------------------------------------------------------------ +// Entry point (must be named "test") +function test() { + // The "runNextTest" approach is async, so we need to call "waitForExplicitFinish()" + // We call "finish()" when the tests are finished + waitForExplicitFinish(); + + // Start the tests + runNextTest(); +} + +//------------------------------------------------------------------------------ +// Iterating tests by shifting test out one by one as runNextTest is called. +function runNextTest() { + // Run the next test until all tests completed + if (gTests.length > 0) { + gCurrentTest = gTests.shift(); + info(gCurrentTest.desc); + gCurrentTest.run(); + } + else { + // Cleanup. All tests are completed at this point + try { + } + finally { + // We must finialize the tests + finish(); + } + } +} + +/** + * One-time observer callback. + */ +function waitForObserve(name, callback) { + var observerService = Cc["@mozilla.org/observer-service;1"] + .getService(Ci.nsIObserverService); + var observer = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]), + observe: function(subject, topic, data) { + observerService.removeObserver(observer, name); + observer = null; + callback(subject, topic, data); + } + }; + + observerService.addObserver(observer, name, false); +} + +//------------------------------------------------------------------------------ + +gTests.push({ + desc: "Test history being added with page visit", + _currentTab: null, + + run: function() { + this._currentTab = Browser.addTab(testURL_01, true); + waitForObserve("uri-visit-saved", function(subject, topic, data) { + let uri = subject.QueryInterface(Ci.nsIURI); + ok(uri.spec == testURL_01, "URI was saved to history"); + Browser.closeTab(gCurrentTest._currentTab); + runNextTest(); + }); + } +}); diff --git a/mobile/android/chrome/tests/browser_install.xml b/mobile/android/chrome/tests/browser_install.xml new file mode 100644 index 000000000000..3e96873c2afd --- /dev/null +++ b/mobile/android/chrome/tests/browser_install.xml @@ -0,0 +1,58 @@ + + + + Install Tests + Extension + addon1@tests.mozilla.org + 1.0 + http://example.com/icon.png + http://example.com/ + + + Test Creator + http://example.com/creator.html + + + Public + Test add-on + Test add-on + + + Fennec + {a23983c0-fd0e-11dc-95ff-0800200c9a66} + 0 + * + + + ALL + http://example.com/browser/mobile/chrome/tests/addons/browser_install1_1.xpi + + + + Install Tests 2 + Extension + addon2@tests.mozilla.org + 1.0 + http://example.com/icon.png + http://example.com/ + + + Test Creator + http://example.com/creator.html + + + Public + Test add-on 2 + Test add-on 2 + + + Fennec + {a23983c0-fd0e-11dc-95ff-0800200c9a66} + 0 + * + + + ALL + http://example.com/browser/mobile/chrome/tests/addons/browser_install1_2.xpi + + diff --git a/mobile/android/chrome/tests/browser_localepicker.js b/mobile/android/chrome/tests/browser_localepicker.js new file mode 100644 index 000000000000..1acef76c559e --- /dev/null +++ b/mobile/android/chrome/tests/browser_localepicker.js @@ -0,0 +1,282 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +const RELATIVE_DIR = "browser/mobile/chrome/tests/"; +const TESTROOT = "http://example.com/" + RELATIVE_DIR; +const PREF_GET_LOCALES = "extensions.getLocales.get.url"; + +var gAvailable = []; + +var restartObserver = { + observe: function(aSubject, aTopic, aData) { + // cancel restart requests + aSubject.QueryInterface(Ci.nsISupportsPRBool); + aSubject.data = true; + }, + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]) +} + +function test() { + Services.obs.addObserver(restartObserver, "quit-application-requested", false); + waitForExplicitFinish(); + Services.prefs.setCharPref(PREF_GET_LOCALES, TESTROOT + "locales_list.sjs?numvalid=2"); + + let chromeReg = Components.classes["@mozilla.org/chrome/chrome-registry;1"].getService(Components.interfaces.nsIXULChromeRegistry); + chromeReg.QueryInterface(Ci.nsIToolkitChromeRegistry); + let availableLocales = chromeReg.getLocalesForPackage("browser"); + while (availableLocales.hasMore()) + gAvailable.push( availableLocales.getNext() ); + + + // in order to test restart notifications being shown, we much open the settings panel once + let settingsButton = document.getElementById("tool-panel-open"); + settingsButton.click(); + waitForAndContinue(runNextTest, function() { + return !document.getElementById("panel-container").hidden; + }); +} + +function end_test() { + BrowserUI.hidePanel(); + Services.prefs.clearUserPref(PREF_GET_LOCALES); + Services.obs.removeObserver(restartObserver, "quit-application-requested"); +} + +registerCleanupFunction(end_test); + +function CheckListLoad(aList, aLength) { + return function() { + return aList.childNodes.length == aLength; + } +} + +function CheckDeck(aDeck, aPanel) { + return function() { + return aDeck.selectedPanel == aPanel; + } +} + +function LocaleTest(aName, aOptions) { + var install = null; + return { + desc: aName, + win: null, + run: function lt_run() { + this.loadedWindow = this.loadedWindow.bind(this); + this.windowClosed = this.windowClosed.bind(this); + this.win = Services.ww.openWindow(aOptions.opener, "chrome://browser/content/localePicker.xul", "_browser", "chrome,dialog=no,all", null); + this.win.addEventListener("load", this.loadedWindow, false); + }, + + loadedWindow: function lt_loadedWindow() { + this.win.removeEventListener("load", this.loadedWindow, false); + if (aOptions.opener) + setTimeout(this.delayedLoadPicker.bind(this), 0); + else + setTimeout(this.delayedLoadMain.bind(this), 0); + }, + + delayedLoadMain: function lt_delayedLoadMain() { + let deck = this.win.document.getElementById("language-deck"); + let mainPage = this.win.document.getElementById("main-page"); + is(deck.selectedPanel, mainPage, "Deck is showing the main page"); + + if (aOptions.loadLocalesList) { + // load the locales list + let changeButton = this.win.document.getElementById("change-language"); + changeButton.click(); + this.delayedLoadPicker(); + } else { + // click the "Continue in English" button + let continueButton = this.win.document.getElementById("continue-in-button"); + ok(/english/i.test(continueButton.textContent), "Continue button says English"); + this.win.addEventListener("unload", this.windowClosed, false); + continueButton.click(); + } + }, + + delayedLoadPicker: function lt_delayedLoadPicker() { + let deck = this.win.document.getElementById("language-deck"); + let pickerPage = this.win.document.getElementById("picker-page"); + is(deck.selectedPanel, pickerPage, "Deck is showing the picker page"); + + let list = this.win.document.getElementById("language-list"); + // wait till the list shows the number of locales bundled with this build + the 2 from the downloaded list + waitForAndContinue(this.listLoaded.bind(this), CheckListLoad(list, gAvailable.length + 2)); + }, + + listLoaded: function() { + let continueButton = this.win.document.getElementById("continue-button"); + let cancelButton = this.win.document.getElementById("cancel-button"); + ok(/continue/i.test(continueButton.textContent), "Continue button has correct text"); + ok(/cancel/i.test(cancelButton.textContent), "Cancel button has correct text"); + + let list = this.win.document.getElementById("language-list"); + is(list.childNodes.length, gAvailable.length + 2, "List has correct number of children"); + + let nextSelected = null; + let selectedItem = null; + for(var i = 0; i < list.childNodes.length; i++) { + let child = list.childNodes[i]; + if (/english/i.test(child.textContent)) { + ok(child.hasAttribute("selected"), "English is initially selected"); + selectedItem = child; + } else { + ok(!child.hasAttribute("selected"), "Language is not selected"); + if (aOptions.selectAddon && child.textContent == aOptions.selectAddon.name) + nextSelected = child; + } + } + this.testInstallingItem(nextSelected); + }, + + testInstallingItem: function lt_testInstallingItem(aSelect) { + let continueButton = this.win.document.getElementById("continue-button"); + let cancelButton = this.win.document.getElementById("cancel-button"); + + if (aSelect) { + aSelect.click(); + is(continueButton.textContent, aOptions.selectAddon.continueButton, "Continue button says " + aOptions.selectAddon.continueButton); + is(cancelButton.textContent, aOptions.selectAddon.cancelButton, "Cancel button says " + aOptions.selectAddon.cancelButton); + let title = this.win.document.getElementById("picker-title"); + is(title.textContent, aOptions.selectAddon.title, "Title says " + aOptions.selectAddon.title); + continueButton.click(); + + let deck = this.win.document.getElementById("language-deck"); + let installerPage = this.win.document.getElementById("installer-page"); + is(deck.selectedPanel, installerPage, "Deck is showing the installer page"); + + let installingPage = this.win.document.getElementById("installer-page-installing"); + is(installerPage.selectedPanel, installingPage, "Installer is showing installing page"); + let installMsg = this.win.document.getElementById("installing-message"); + is(installMsg.textContent, aOptions.selectAddon.installMessage, "Installer is showing correct message"); + + if (aOptions.selectAddon.willFail) { + let failedPage = this.win.document.getElementById("installer-page-error"); + waitForAndContinue(this.installFailed.bind(this), CheckDeck(installerPage, failedPage)); + } else { + let install = aSelect.locale; + this.win.addEventListener("unload", this.windowClosed, false); + } + } else { + this.cancelList(); + } + }, + + installFailed: function lt_installFailed() { + let continueButton = this.win.document.getElementById("install-continue"); + is(continueButton.textContent, aOptions.selectAddon.installFailed, "Install failed button has correct label"); + continueButton.click(); + + let deck = this.win.document.getElementById("language-deck"); + let pickerPage = this.win.document.getElementById("picker-page"); + is(deck.selectedPanel, pickerPage, "Deck is showing the picker page"); + this.cancelList(); + }, + + cancelList: function lt_cancelList() { + this.win.addEventListener("unload", this.windowClosed, false); + + let cancelButton = this.win.document.getElementById("cancel-button"); + cancelButton.click(); + if (!aOptions.opener) { + // canceling out of the list, should revert back to english ui + let deck = this.win.document.getElementById("language-deck"); + let mainPage = this.win.document.getElementById("main-page"); + is(deck.selectedPanel, mainPage, "Deck is showing the main page again"); + let continueButton = this.win.document.getElementById("continue-in-button"); + ok(/english/i.test(continueButton.textContent), "Cancelling returned the UI to English"); + continueButton.click(); + } + }, + + windowClosed: function lt_windowClosed(aEvent) { + this.checkMainUI(aOptions.selectAddon); + + Services.prefs.clearUserPref("intl.locale.matchOS"); + Services.prefs.clearUserPref("general.useragent.locale"); + window.PreferencesView.hideRestart(); + + if (install) + install.uninstall(); + + runNextTest(); + }, + + checkMainUI: function(aAddon) { + let systemPref = ""; + let userAgentPref = ""; + try { + systemPref = Services.prefs.getBoolPref("intl.locale.matchOS"); + userAgentPref = Services.prefs.getCharPref("general.useragent.locale") + } catch(ex) { } + + let notification = document.getElementById("prefs-messages").getNotificationWithValue("restart-app"); + let showRestart = aAddon ? !aAddon.willFail : false; + is(!!notification, showRestart, "Restart message is " + (showRestart ? "" : "not ") + "shown"); + + // check that locale pref has been updated + let localeName = aAddon ? aAddon.locale : "en-US"; + is(systemPref, false, "Match system locale is false"); + is(userAgentPref, localeName, "User agent locale is " + localeName); + let buttonLabel = aAddon ? aAddon.localeName : "English (US)"; + is(document.getElementById("prefs-uilanguage-button").getAttribute("label"), buttonLabel, "Locale button says " + buttonLabel); + } + } +} + +let invalidInstall = { + name: "Test Locale 0", + installMessage: "INSTALLINGTest Locale 0", + continueButton: "CONTINUE", + cancelButton: "CANCEL", + title: "CHOOSELANGUAGE", + installFailed: "CONTINUE", + locale: "en-US", + localeName: "English (US)", + willFail: true +}; +let validInstall = { + name: "Test Locale 1", + installMessage: "INSTALLINGTest Locale 1", + continueButton: "CONTINUE", + cancelButton: "CANCEL", + title: "CHOOSELANGUAGE", + locale: "test1", + localeName: "test1", + willFail: false +} + +gTests.push(new LocaleTest("Load locale picker with no opener and continue", + { opener: null, + loadLocalesList: false, + selectAddon: null + })); + +gTests.push(new LocaleTest("Load locale picker with no opener and try to install an invalid language", + { opener: null, + loadLocalesList: true, + selectAddon: invalidInstall + })); + +gTests.push(new LocaleTest("Load locale picker with no opener and try to install a valid language", + { opener: null, + loadLocalesList: true, + selectAddon: validInstall + })); + +gTests.push(new LocaleTest("Load locale picker with opener and try to install an invalid language", + { opener: this.window, + loadLocalesList: true, + selectAddon: invalidInstall + })); + +gTests.push(new LocaleTest("Load locale picker with opener and try to install a valid language", + { opener: this.window, + loadLocalesList: true, + selectAddon: validInstall + })); diff --git a/mobile/android/chrome/tests/browser_localepicker_escape.js b/mobile/android/chrome/tests/browser_localepicker_escape.js new file mode 100644 index 000000000000..4968d48c0504 --- /dev/null +++ b/mobile/android/chrome/tests/browser_localepicker_escape.js @@ -0,0 +1,44 @@ +Components.utils.import("resource://gre/modules/Services.jsm"); + +var gWin; + +function test() { + waitForExplicitFinish(); + gWin = Services.ww.openWindow(window, "chrome://browser/content/localePicker.xul", "_browser", "chrome,dialog=no,all", null); + gWin.addEventListener("load", onload, false); +} + +function onload(aEvent) { + gWin.removeEventListener("load", onload, false); + ok(true, "Locale picker is opened."); + setTimeout(afterLoad, 0); +} + +function afterLoad() { + let ui = gWin.LocaleUI; + is(ui.selectedPanel, ui.pickerpage, "Picker page is selected."); + + ui.selectedPanel = ui.mainPage; + is(ui.selectedPanel, ui.mainPage, "Select the main page."); + sendEscape(); + is(ui.selectedPanel, ui.mainPage, "Main page is still selected (escape key does nothing)."); + + ui.selectedPanel = ui.installerPage; + is(ui.selectedPanel, ui.installerPage, "Select the installer page."); + sendEscape(); + is(ui.selectedPanel, ui.pickerpage, "Escape key goes back to the picker page."); + + gWin.addEventListener("unload", windowClosed, false); + sendEscape(); +} + +function windowClosed() { + gWin.removeEventListener("unload", windowClosed, false); + ok(true, "Locale picker is closed."); + finish(); +} + +function sendEscape() { + info("Sending escape key."); + EventUtils.synthesizeKey("VK_ESCAPE", { type: "keypress" }, gWin); +} diff --git a/mobile/android/chrome/tests/browser_localerepository.js b/mobile/android/chrome/tests/browser_localerepository.js new file mode 100644 index 000000000000..63de102580bf --- /dev/null +++ b/mobile/android/chrome/tests/browser_localerepository.js @@ -0,0 +1,39 @@ +var localeList = serverRoot + "locales_list.sjs"; +var PREF_LOCALE_LIST = "extensions.getLocales.get.url"; + +Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource://gre/modules/LocaleRepository.jsm"); + +function test() { + waitForExplicitFinish(); + runNextTest(); +} + +function end_test() { + Services.prefs.clearUserPref(PREF_LOCALE_LIST); +} + +registerCleanupFunction(end_test); + +gTests.push({ + desc: "Test getting a list of compatable locales", + run: function() { + Services.prefs.setCharPref(PREF_LOCALE_LIST, localeList); + LocaleRepository.getLocales(this.listLoaded); + }, + + listLoaded: function(aLocales) { + is(aLocales.length, 1, "Correct number of locales were found"); + isnot(aLocales[0].addon, null, "Locale has an addon"); + is(aLocales[0].xpiURL, "http://www.example.com/browser/mobile/chrome/tests/addons/browser_locale1.xpi", "Locale has correct xpi url"); + is(aLocales[0].xpiHash, null, "Locale has correct hash"); + + is(aLocales[0].addon.id, "langpack-test1@firefox-mobile.mozilla.org", "Locale has correct id"); + is(aLocales[0].addon.name, "Test Locale 1", "Locale has correct name"); + is(aLocales[0].addon.type, "language", "Locale has correct type"); + + is(aLocales[0].addon.targetLocale, "test1", "Locale has correct target locale"); + is(aLocales[0].addon.version, "1.0", "Locale has correct version"); + runNextTest(); + } +}); diff --git a/mobile/android/chrome/tests/browser_localerepository_buildid.js b/mobile/android/chrome/tests/browser_localerepository_buildid.js new file mode 100644 index 000000000000..9a3689c565ae --- /dev/null +++ b/mobile/android/chrome/tests/browser_localerepository_buildid.js @@ -0,0 +1,41 @@ +var localeList = serverRoot + "locales_list.sjs"; +var PREF_LOCALE_LIST = "extensions.getLocales.get.url"; + +Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource://gre/modules/LocaleRepository.jsm"); + +function test() { + waitForExplicitFinish(); + runNextTest(); +} + +function end_test() { + Services.prefs.clearUserPref(PREF_LOCALE_LIST); +} + +registerCleanupFunction(end_test); + +gTests.push({ + desc: "Test dynamically changing extensions.getLocales.get.url", + run: function() { + Services.prefs.setCharPref(PREF_LOCALE_LIST, localeList + "?buildid=%BUILDID_EXPANDED%"); + LocaleRepository.getLocales(this.listLoaded.bind(this), {buildID: "00001122334455"}); + }, + + listLoaded: function(aLocales) { + is(aLocales.length, 1, "Correct number of locales were found"); + is(aLocales[0].addon.name, "0000-11-22-33-44-55", "Buildid was correctly replaced"); + + Services.prefs.setCharPref(PREF_LOCALE_LIST, localeList + "?buildid=%BUILDID_EXPANDED%"); + LocaleRepository.getLocales(this.secondListLoaded.bind(this)); + }, + + secondListLoaded: function(aLocales) { + is(aLocales.length, 1, "Correct number of locales were found"); + + let buildID = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).QueryInterface(Ci.nsIXULRuntime).appBuildID; + is(aLocales[0].addon.name.replace(/-/g, ""), buildID, "Buildid was correctly replaced"); + + runNextTest(); + } +}); diff --git a/mobile/android/chrome/tests/browser_localerepository_pref.js b/mobile/android/chrome/tests/browser_localerepository_pref.js new file mode 100644 index 000000000000..bc94c1280fc9 --- /dev/null +++ b/mobile/android/chrome/tests/browser_localerepository_pref.js @@ -0,0 +1,35 @@ +var localeList = serverRoot + "locales_list.sjs"; +var PREF_LOCALE_LIST = "extensions.getLocales.get.url"; + +Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource://gre/modules/LocaleRepository.jsm"); + +function test() { + waitForExplicitFinish(); + runNextTest(); +} + +function end_test() { + Services.prefs.clearUserPref(PREF_LOCALE_LIST); +} + +registerCleanupFunction(end_test); + +gTests.push({ + desc: "Test dynamically changing extensions.getLocales.get.url", + run: function() { + Services.prefs.setCharPref(PREF_LOCALE_LIST, localeList); + LocaleRepository.getLocales(this.listLoaded.bind(this)); + }, + + listLoaded: function(aLocales) { + is(aLocales.length, 1, "Correct number of locales were found"); + Services.prefs.setCharPref(PREF_LOCALE_LIST, localeList + "?numvalid=2"); + LocaleRepository.getLocales(this.secondListLoaded.bind(this)); + }, + + secondListLoaded: function(aLocales) { + is(aLocales.length, 2, "Correct number of locales were found"); + runNextTest(); + } +}); diff --git a/mobile/android/chrome/tests/browser_mainui.js b/mobile/android/chrome/tests/browser_mainui.js new file mode 100644 index 000000000000..386858ade07d --- /dev/null +++ b/mobile/android/chrome/tests/browser_mainui.js @@ -0,0 +1,27 @@ +// Very basic tests for the main window UI + +const Cc = Components.classes; +const Ci = Components.interfaces; + +//------------------------------------------------------------------------------ +// Entry point (must be named "test") +function test() { + is(window.location.href, "chrome://browser/content/browser.xul", "Main window should be browser.xul"); + + window.focus(); + + let browser = Browser.selectedBrowser; + isnot(browser, null, "Should have a browser"); + + is(browser.currentURI.spec, Browser.selectedTab.browser.currentURI.spec, "selectedBrowser == selectedTab.browser"); + + testContentContainerSize(); +} + +function testContentContainerSize() { + let container = document.getElementById("content-viewport"); + + let rect = container.getBoundingClientRect(); + is(rect.width, window.innerWidth, "Content container is same width as window"); + is(rect.height, window.innerHeight, "Content container is same height as window"); +} diff --git a/mobile/android/chrome/tests/browser_navigation.js b/mobile/android/chrome/tests/browser_navigation.js new file mode 100644 index 000000000000..aa69a0ff7615 --- /dev/null +++ b/mobile/android/chrome/tests/browser_navigation.js @@ -0,0 +1,403 @@ +var testURL_01 = chromeRoot + "browser_blank_01.html"; +var testURL_02 = chromeRoot + "browser_blank_02.html"; + +var titleURL = baseURI + "browser_title.sjs?"; +var pngURL = "data:image/gif;base64,R0lGODlhCwALAIAAAAAA3pn/ZiH5BAEAAAEALAAAAAALAAsAAAIUhA+hkcuO4lmNVindo7qyrIXiGBYAOw=="; + +// A queue to order the tests and a handle for each test +var gTests = []; +var gCurrentTest = null; + +var back = document.getElementById("tool-back"); +var forward = document.getElementById("tool-forward"); + +function pageLoaded(aURL) { + return function() { + let tab = gCurrentTest._currentTab; + return !tab.isLoading() && tab.browser.currentURI.spec == aURL; + } +} + +//------------------------------------------------------------------------------ +// Entry point (must be named "test") +function test() { + // The "runNextTest" approach is async, so we need to call "waitForExplicitFinish()" + // We call "finish()" when the tests are finished + waitForExplicitFinish(); + // Start the tests + runNextTest(); +} + +function waitForPageShow(aPageURL, aCallback) { + messageManager.addMessageListener("pageshow", function(aMessage) { + if (aMessage.target.currentURI.spec == aPageURL) { + messageManager.removeMessageListener("pageshow", arguments.callee); + setTimeout(function() { aCallback(); }, 0); + } + }); +}; + +//------------------------------------------------------------------------------ +// Iterating tests by shifting test out one by one as runNextTest is called. +function runNextTest() { + // Run the next test until all tests completed + if (gTests.length > 0) { + gCurrentTest = gTests.shift(); + info(gCurrentTest.desc); + gCurrentTest.run(); + } + else { + // Cleanup. All tests are completed at this point + try { + // Add any cleanup code here + } + finally { + // We must finialize the tests + finish(); + } + } +} + +//------------------------------------------------------------------------------ +// Case: Loading a page into the URLBar with VK_RETURN +gTests.push({ + desc: "Loading a page into the URLBar with VK_RETURN", + _currentTab: null, + + run: function() { + gCurrentTest._currentTab = BrowserUI.newTab(testURL_01); + + // Wait for the tab to load, then do the test + waitFor(gCurrentTest.onPageReady, pageLoaded(testURL_01)); + }, + + onPageReady: function() { + // Test the mode + let urlIcons = document.getElementById("urlbar-icons"); + is(urlIcons.getAttribute("mode"), "view", "URL Mode is set to 'view'"); + + // Test back button state + is(back.disabled, !gCurrentTest._currentTab.browser.canGoBack, "Back button check"); + + // Test forward button state + is(forward.disabled, !gCurrentTest._currentTab.browser.canGoForward, "Forward button check"); + + // Focus the url edit + let urlbarTitle = document.getElementById("urlbar-title"); + + // Wait for the awesomebar to load, then do the test + window.addEventListener("NavigationPanelShown", function() { + window.removeEventListener("NavigationPanelShown", arguments.callee, false); + setTimeout(gCurrentTest.onFocusReady, 0); + }, false); + EventUtils.synthesizeMouse(urlbarTitle, urlbarTitle.width / 2, urlbarTitle.height / 2, {}); + }, + + onFocusReady: function() { + // Test mode + let urlIcons = document.getElementById("urlbar-icons"); + is(urlIcons.getAttribute("mode"), "edit", "URL Mode is set to 'edit'"); + + // Test back button state + is(back.disabled, !gCurrentTest._currentTab.browser.canGoBack, "Back button check"); + + // Test forward button state + is(forward.disabled, !gCurrentTest._currentTab.browser.canGoForward, "Forward button check"); + + // Check button states (url edit is focused) + let search = document.getElementById("tool-search"); + let searchStyle = window.getComputedStyle(search, null); + is(searchStyle.visibility, "visible", "SEARCH is visible"); + + let stop = document.getElementById("tool-stop"); + let stopStyle = window.getComputedStyle(stop, null); + is(stopStyle.visibility, "collapse", "STOP is hidden"); + + let reload = document.getElementById("tool-reload"); + let reloadStyle = window.getComputedStyle(reload, null); + is(reloadStyle.visibility, "collapse", "RELOAD is hidden"); + + // Send the string and press return + EventUtils.synthesizeString(testURL_02, window); + + // It looks like there is a race condition somewhere that result having + // testURL_01 concatenate with testURL_02 as a urlbar value, so to + // workaround that we're waiting for the readonly state to be fully updated + function URLIsReadWrite() { + return BrowserUI._edit.readOnly == false; + } + + waitFor(function() { + // Wait for the tab to load, then do the test + waitFor(gCurrentTest.onPageFinish, pageLoaded(testURL_02)); + + setTimeout(function() { + is(BrowserUI._edit.value, testURL_02, "URL value should be equal to the string sent via synthesizeString"); + EventUtils.synthesizeKey("VK_RETURN", {}, window); + }, 0); + }, URLIsReadWrite); + }, + + onPageFinish: function() { + let urlIcons = document.getElementById("urlbar-icons"); + is(urlIcons.getAttribute("mode"), "view", "URL Mode is set to 'view'"); + + // Check button states (url edit is not focused) + let search = document.getElementById("tool-search"); + let searchStyle = window.getComputedStyle(search, null); + is(searchStyle.visibility, "collapse", "SEARCH is hidden"); + + let stop = document.getElementById("tool-stop"); + let stopStyle = window.getComputedStyle(stop, null); + is(stopStyle.visibility, "collapse", "STOP is hidden"); + + let reload = document.getElementById("tool-reload"); + let reloadStyle = window.getComputedStyle(reload, null); + is(reloadStyle.visibility, "visible", "RELOAD is visible"); + + let uri = gCurrentTest._currentTab.browser.currentURI.spec; + is(uri, testURL_02, "URL Matches newly created Tab"); + + // Go back in session + gCurrentTest._currentTab.browser.goBack(); + + // Wait for the tab to load, then do the test + waitFor(gCurrentTest.onPageBack, pageLoaded(testURL_01)); + }, + + onPageBack: function() { + // Test back button state + is(back.disabled, !gCurrentTest._currentTab.browser.canGoBack, "Back button check"); + + // Test forward button state + is(forward.disabled, !gCurrentTest._currentTab.browser.canGoForward, "Forward button check"); + + BrowserUI.closeTab(gCurrentTest._currentTab); + runNextTest(); + } +}); + +// Bug 611327 ----------------------------------------------------------------- +// Check for urlbar label value +gTests.push({ + desc: "Check for urlbar label value on different cases", + _currentTab: null, + + run: function() { + gCurrentTest._currentTab = BrowserUI.newTab(titleURL + "no_title"); + waitForPageShow(titleURL + "no_title", gCurrentTest.onPageLoadWithoutTitle); + }, + + onPageLoadWithoutTitle: function() { + let urlbarTitle = document.getElementById("urlbar-title"); + is(urlbarTitle.value, titleURL + "no_title", "The title should be equal to the URL"); + + BrowserUI.closeTab(gCurrentTest._currentTab); + + gCurrentTest._currentTab = BrowserUI.newTab(titleURL + "english_title"); + waitForPageShow(titleURL + "english_title", gCurrentTest.onPageLoadWithTitle); + }, + + onPageLoadWithTitle: function() { + let urlbarTitle = document.getElementById("urlbar-title"); + is(urlbarTitle.value, "English Title Page", "The title should be equal to the page title"); + + BrowserUI.closeTab(gCurrentTest._currentTab); + + gCurrentTest._currentTab = BrowserUI.newTab(titleURL + "dynamic_title"); + messageManager.addMessageListener("pageshow", function(aMessage) { + messageManager.removeMessageListener("pageshow", arguments.callee); + gCurrentTest.onBeforePageTitleChanged(); + }); + + messageManager.addMessageListener("DOMTitleChanged", function(aMessage) { + messageManager.removeMessageListener("DOMTitleChanged", arguments.callee); + urlbarTitle.addEventListener("DOMAttrModified", function(aEvent) { + if (aEvent.attrName == "value") { + urlbarTitle.removeEventListener("DOMAttrModified", arguments.callee, false); + setTimeout(gCurrentTest.onPageTitleChanged, 0); + } + }, false); + }); + }, + + onBeforePageTitleChanged: function() { + let urlbarTitle = document.getElementById("urlbar-title"); + isnot(urlbarTitle.value, "This is not a french title", "The title should not be equal to the new page title yet"); + }, + + onPageTitleChanged: function() { + let urlbarTitle = document.getElementById("urlbar-title"); + is(urlbarTitle.value, "This is not a french title", "The title should be equal to the new page title"); + + BrowserUI.closeTab(gCurrentTest._currentTab); + + gCurrentTest._currentTab = BrowserUI.newTab(titleURL + "redirect"); + waitForPageShow(titleURL + "no_title", gCurrentTest.onPageRedirect); + }, + + onPageRedirect: function() { + let urlbarTitle = document.getElementById("urlbar-title"); + is(urlbarTitle.value, gCurrentTest._currentTab.browser.currentURI.spec, "The title should be equal to the redirected page url"); + + BrowserUI.closeTab(gCurrentTest._currentTab); + + gCurrentTest._currentTab = BrowserUI.newTab(titleURL + "location"); + waitForPageShow(titleURL + "no_title", gCurrentTest.onPageLocation); + }, + + onPageLocation: function() { + let urlbarTitle = document.getElementById("urlbar-title"); + is(urlbarTitle.value, gCurrentTest._currentTab.browser.currentURI.spec, "The title should be equal to the relocate page url"); + + BrowserUI.closeTab(gCurrentTest._currentTab); + + // Wait for the awesomebar to load, then do the test + window.addEventListener("NavigationPanelShown", function() { + window.removeEventListener("NavigationPanelShown", arguments.callee, false); + + setTimeout(function() { + EventUtils.synthesizeString(testURL_02, window); + EventUtils.synthesizeKey("VK_RETURN", {}, window); + + waitForPageShow(testURL_02, gCurrentTest.onUserTypedValue); + }, 0); + + }, false); + + gCurrentTest._currentTab = BrowserUI.newTab("about:blank"); + }, + + onUserTypedValue: function() { + let urlbarTitle = document.getElementById("urlbar-title"); + is(urlbarTitle.value, "Browser Blank Page 02", "The title should be equal to the typed page url title"); + + // about:blank has been closed, so we need to close the last selected one + BrowserUI.closeTab(Browser.selectedTab); + + // Wait for the awesomebar to load, then do the test + window.addEventListener("NavigationPanelShown", function() { + window.removeEventListener("NavigationPanelShown", arguments.callee, false); + + EventUtils.synthesizeString("no_title", window); + + // Wait until the no_title result row is here + let popup = document.getElementById("popup_autocomplete"); + let result = null; + function hasResults() { + result = popup._items.childNodes[0]; + if (result) + return result.getAttribute("value") == (titleURL + "no_title"); + + return false; + }; + waitFor(function() { EventUtils.synthesizeMouse(result, result.width / 2, result.height / 2, {}); }, hasResults); + + urlbarTitle.addEventListener("DOMAttrModified", function(aEvent) { + if (aEvent.attrName == "value") { + urlbarTitle.removeEventListener("DOMAttrModified", arguments.callee, false); + is(urlbarTitle.value, titleURL + "no_title", "The title should be equal to the url of the clicked row"); + } + }, false); + + waitForPageShow(titleURL + "no_title", gCurrentTest.onUserSelectValue); + }, false); + + gCurrentTest._currentTab = BrowserUI.newTab("about:blank"); + }, + + onUserSelectValue: function() { + let urlbarTitle = document.getElementById("urlbar-title"); + is(urlbarTitle.value, Browser.selectedTab.browser.currentURI.spec, "The title should be equal to the clicked page url"); + + // about:blank has been closed, so we need to close the last selected one + BrowserUI.closeTab(Browser.selectedTab); + + //is(urlbarTitle.value, "Browser Blank Page 02", "The title of the second page must be displayed"); + runNextTest(); + } +}); + +// Case: Check for appearance of the favicon +gTests.push({ + desc: "Check for appearance of the favicon", + _currentTab: null, + + run: function() { + gCurrentTest._currentTab = BrowserUI.newTab(testURL_01); + waitForPageShow(testURL_01, gCurrentTest.onPageReady); + }, + + onPageReady: function() { + let favicon = document.getElementById("urlbar-favicon"); + is(favicon.src, "", "The default favicon must be loaded"); + BrowserUI.closeTab(gCurrentTest._currentTab); + + gCurrentTest._currentTab = BrowserUI.newTab(testURL_02); + waitForPageShow(testURL_02, gCurrentTest.onPageFinish); + }, + + onPageFinish: function(){ + let favicon = document.getElementById("urlbar-favicon"); + is(favicon.src, pngURL, "The page favicon must be loaded"); + BrowserUI.closeTab(gCurrentTest._currentTab); + runNextTest(); + } +}); + +// Bug 600707 - Back and forward buttons are updated when navigating within a page +// +// These tests use setTimeout instead of waitFor or addEventListener, because +// in-page navigation does not fire any loading events or progress +// notifications, and happens more or less instantly. +gTests.push({ + desc: "Navigating within a page via URI fragments", + _currentTab: null, + + run: function() { + gCurrentTest._currentTab = BrowserUI.newTab(testURL_01); + waitFor(gCurrentTest.onPageReady, pageLoaded(testURL_01)); + }, + + onPageReady: function() { + ok(back.disabled, "Can't go back"); + ok(forward.disabled, "Can't go forward"); + + messageManager.addMessageListener("Content:LocationChange", gCurrentTest.onFragmentLoaded); + Browser.loadURI(testURL_01 + "#fragment"); + }, + + onFragmentLoaded: function() { + messageManager.removeMessageListener("Content:LocationChange", arguments.callee); + + ok(!back.disabled, "Can go back"); + ok(forward.disabled, "Can't go forward"); + + messageManager.addMessageListener("Content:LocationChange", gCurrentTest.onBack); + CommandUpdater.doCommand("cmd_back"); + }, + + onBack: function() { + messageManager.removeMessageListener("Content:LocationChange", arguments.callee); + + ok(back.disabled, "Can't go back"); + ok(!forward.disabled, "Can go forward"); + + messageManager.addMessageListener("Content:LocationChange", gCurrentTest.onForward); + CommandUpdater.doCommand("cmd_forward"); + }, + + onForward: function() { + messageManager.removeMessageListener("Content:LocationChange", arguments.callee); + + ok(!back.disabled, "Can go back"); + ok(forward.disabled, "Can't go forward"); + + gCurrentTest.finish(); + }, + + finish: function() { + BrowserUI.closeTab(gCurrentTest._currentTab); + runNextTest(); + } +}); diff --git a/mobile/android/chrome/tests/browser_preferences_fulltoggle.js b/mobile/android/chrome/tests/browser_preferences_fulltoggle.js new file mode 100644 index 000000000000..9ce68fbe22f9 --- /dev/null +++ b/mobile/android/chrome/tests/browser_preferences_fulltoggle.js @@ -0,0 +1,67 @@ +// browser-chrome test for fennec preferences to toggle values while clicking on the preference name + +var gTests = []; +var gCurrentTest = null; + +function test() { + // The "runNextTest" approach is async, so we need to call "waitForExplicitFinish()" + // We call "finish()" when the tests are finished + waitForExplicitFinish(); + + // Start the tests + runNextTest(); +} +//------------------------------------------------------------------------------ +// Iterating tests by shifting test out one by one as runNextTest is called. +function runNextTest() { + // Run the next test until all tests completed + if (gTests.length > 0) { + gCurrentTest = gTests.shift(); + info(gCurrentTest.desc); + gCurrentTest.run(); + } + else { + // Cleanup. All tests are completed at this point + finish(); + } +} + +// ----------------------------------------------------------------------------------------- +// Verify preferences and text +gTests.push({ + desc: "Verify full toggle on Preferences", + + run: function(){ + // 1.Click preferences to view prefs + document.getElementById("tool-panel-open").click(); + is(document.getElementById("panel-container").hidden, false, "Preferences should be visible"); + + var contentRegion = document.getElementById("prefs-content"); + + // Check for *Show images* + var imageRegion = document.getAnonymousElementByAttribute(contentRegion, "pref", "permissions.default.image"); + var imageValue = imageRegion.value; + var imageTitle = document.getAnonymousElementByAttribute(imageRegion, "class", "preferences-title"); + var imageButton = document.getAnonymousElementByAttribute(imageRegion, "anonid", "input"); + + var ibEvent = document.createEvent("MouseEvents"); + ibEvent.initEvent("TapSingle", true, false); + imageButton.dispatchEvent(ibEvent); + is(imageRegion.value, !imageValue, "Tapping on input control should change the value"); + + var itEvent = document.createEvent("MouseEvents"); + itEvent.initEvent("TapSingle", true, false); + imageTitle.dispatchEvent(itEvent); + is(imageRegion.value, imageValue, "Tapping on the title should change the value"); + + var irEvent = document.createEvent("MouseEvents"); + irEvent.initEvent("TapSingle", true, false); + imageRegion.dispatchEvent(irEvent); + is(imageRegion.value, !imageValue, "Tapping on the setting should change the value"); + + BrowserUI.hidePanel(); + is(document.getElementById("panel-container").hidden, true, "Preferences panel should be closed"); + runNextTest(); + } +}); + diff --git a/mobile/android/chrome/tests/browser_preferences_text.js b/mobile/android/chrome/tests/browser_preferences_text.js new file mode 100644 index 000000000000..de21f56fd5e0 --- /dev/null +++ b/mobile/android/chrome/tests/browser_preferences_text.js @@ -0,0 +1,131 @@ +// Bug 571866 - --browser-chrome test for fennec preferences and text values + +var gTests = []; +var gCurrentTest = null; +var expected = { + "aboutButton": {"tagName": "button", "element_id": "prefs-about-button"}, + "homepage": {"element_id": "prefs-homepage", + "home_page": "prefs-homepage-default", + "blank_page": "prefs-homepage-none", + "current_page": "prefs-homepage-currentpage"}, + "doneButton": {"tagName": "button"}, + "contentRegion": {"element_id": "prefs-content"}, + "imageRegion": {"pref": "permissions.default.image", "anonid": "input", "localName": "checkbox"}, + "jsRegion": {"pref": "javascript.enabled", "anonid": "input", "localName": "checkbox"}, + "privacyRegion": {"element_id": "prefs-privacy" }, + "cookiesRegion": {"pref": "network.cookie.cookieBehavior", "anonid": "input", "localName": "checkbox"}, + "passwordsRegion": {"pref": "signon.rememberSignons", "anonid": "input", "localName": "checkbox"}, + "clearDataButton": {"element_id": "prefs-clear-data", "tagName": "button"} +}; + +function getPreferencesElements() { + let prefElements = {}; + prefElements.panelOpen = document.getElementById("tool-panel-open"); + prefElements.panelContainer = document.getElementById("panel-container"); + prefElements.homeButton = document.getElementById("prefs-homepage-options"); + prefElements.doneButton = document.getElementById("select-buttons-done"); + prefElements.homePageControl = document.getElementById("prefs-homepage"); + prefElements.selectContainer = document.getElementById("menulist-container"); + return prefElements; +} + +function test() { + // The "runNextTest" approach is async, so we need to call "waitForExplicitFinish()" + // We call "finish()" when the tests are finished + waitForExplicitFinish(); + + // Start the tests + runNextTest(); +} +//------------------------------------------------------------------------------ +// Iterating tests by shifting test out one by one as runNextTest is called. +function runNextTest() { + // Run the next test until all tests completed + if (gTests.length > 0) { + gCurrentTest = gTests.shift(); + info(gCurrentTest.desc); + gCurrentTest.run(); + } + else { + // Cleanup. All tests are completed at this point + finish(); + } +} + +// ----------------------------------------------------------------------------------------- +// Verify preferences and text +gTests.push({ + desc: "Verify Preferences and Text", + + run: function(){ + var prefs = getPreferencesElements(); + // 1.Click preferences to view prefs + prefs.panelOpen.click(); + + // 2. For each prefs *verify text *the button/option type *verify height of each field to be the same + is(prefs.panelContainer.hidden, false, "Preferences should be visible"); + + var prefsList = document.getElementById("prefs-messages"); + + // Check for *About page* + let about = expected.aboutButton; + var aboutRegion = document.getAnonymousElementByAttribute(prefsList, "title", "About Fennec"); + var aboutButton = document.getElementById(about.element_id); + is(aboutButton.tagName, about.tagName, "The About Fennec input must be a button"); + + // Check for *Startpage* + let homepage = expected.homepage; + var homepageRegion = document.getElementById(homepage.element_id); + prefs.homeButton.click(); + + is(prefs.selectContainer.hidden, false, "Homepage select dialog must be visible"); + + EventUtils.synthesizeKey("VK_ESCAPE", {}, window); + is(prefs.selectContainer.hidden, true, "Homepage select dialog must be closed"); + + let content = expected.contentRegion; + var contentRegion = document.getElementById(content.element_id); + + // Check for *Show images* + var images = expected.imageRegion; + var imageRegion = document.getAnonymousElementByAttribute(contentRegion, "pref", images.pref); + var imageButton = document.getAnonymousElementByAttribute(imageRegion, "anonid", images.anonid); + is(imageButton.localName, images.localName, "Show images checkbox check"); + // Checkbox or radiobutton? + + // Check for *Enable javascript* + let js = expected.jsRegion; + var jsRegion = document.getAnonymousElementByAttribute(contentRegion, "pref", js.pref); + var jsButton = document.getAnonymousElementByAttribute(jsRegion, "anonid", js.anonid); + is(jsButton.localName, js.localName, "Enable JavaScript checkbox check"); + // Checkbox or radiobutton? + + let privacyRegion = expected.privacyRegion; + var prefsPrivacy = document.getElementById(privacyRegion.element_id); + + // Check for *Allow cookies* + let cookies = expected.cookiesRegion; + var cookiesRegion = document.getAnonymousElementByAttribute(prefsPrivacy, "pref", cookies.pref); + var cookiesButton = document.getAnonymousElementByAttribute(cookiesRegion, "anonid", cookies.anonid); + is(cookiesButton.localName, cookies.localName, "Allow cookies checkbox check"); + // Checkbox or radiobutton? + + // Check for *Remember password* + let passwords = expected.passwordsRegion; + var passwordsRegion = document.getAnonymousElementByAttribute(prefsPrivacy, "pref", passwords.pref); + var passwordsButton = document.getAnonymousElementByAttribute(passwordsRegion, "anonid", passwords.anonid); + is(passwordsButton.localName, passwords.localName, "Allow cookies checkbox check"); + // Checkbox or radiobutton? + + // Check for *Clear Private Data* + let clearData = expected.clearDataButton; + var clearDataRegion = prefsPrivacy.lastChild; + var clearDataButton = document.getElementById(clearData.element_id); + is(clearDataButton.tagName, clearData.tagName, "Check for Clear Private Data button type"); + + BrowserUI.hidePanel(); + is(document.getElementById("panel-container").hidden, true, "Preferences panel should be closed"); + runNextTest(); + } +}); + diff --git a/mobile/android/chrome/tests/browser_rect.js b/mobile/android/chrome/tests/browser_rect.js new file mode 100644 index 000000000000..fb8be8cc4d25 --- /dev/null +++ b/mobile/android/chrome/tests/browser_rect.js @@ -0,0 +1,107 @@ +function test() { + waitForExplicitFinish(); + + ok(Rect, "Rect class exists"); + + for (let test in tests) { + tests[test](); + } + + finish(); +} + +let tests = { + testGetDimensions: function() { + let r = new Rect(5, 10, 100, 50); + ok(r.left == 5, "rect has correct left value"); + ok(r.top == 10, "rect has correct top value"); + ok(r.right == 105, "rect has correct right value"); + ok(r.bottom == 60, "rect has correct bottom value"); + ok(r.width == 100, "rect has correct width value"); + ok(r.height == 50, "rect has correct height value"); + ok(r.x == 5, "rect has correct x value"); + ok(r.y == 10, "rect has correct y value"); + }, + + testIsEmpty: function() { + let r = new Rect(0, 0, 0, 10); + ok(r.isEmpty(), "rect with nonpositive width is empty"); + let r = new Rect(0, 0, 10, 0); + ok(r.isEmpty(), "rect with nonpositive height is empty"); + let r = new Rect(0, 0, 10, 10); + ok(!r.isEmpty(), "rect with positive dimensions is not empty"); + }, + + testRestrictTo: function() { + let r1 = new Rect(10, 10, 100, 100); + let r2 = new Rect(50, 50, 100, 100); + r1.restrictTo(r2); + ok(r1.equals(new Rect(50, 50, 60, 60)), "intersection is non-empty"); + + let r1 = new Rect(10, 10, 100, 100); + let r2 = new Rect(120, 120, 100, 100); + r1.restrictTo(r2); + ok(r1.isEmpty(), "intersection is empty"); + + let r1 = new Rect(10, 10, 100, 100); + let r2 = new Rect(0, 0, 0, 0); + r1.restrictTo(r2); + ok(r1.isEmpty(), "intersection of rect and empty is empty"); + + let r1 = new Rect(0, 0, 0, 0); + let r2 = new Rect(0, 0, 0, 0); + r1.restrictTo(r2); + ok(r1.isEmpty(), "intersection of empty and empty is empty"); + }, + + testExpandToContain: function() { + let r1 = new Rect(10, 10, 100, 100); + let r2 = new Rect(50, 50, 100, 100); + r1.expandToContain(r2); + ok(r1.equals(new Rect(10, 10, 140, 140)), "correct expandToContain on intersecting rectangles"); + + let r1 = new Rect(10, 10, 100, 100); + let r2 = new Rect(120, 120, 100, 100); + r1.expandToContain(r2); + ok(r1.equals(new Rect(10, 10, 210, 210)), "correct expandToContain on non-intersecting rectangles"); + + let r1 = new Rect(10, 10, 100, 100); + let r2 = new Rect(0, 0, 0, 0); + r1.expandToContain(r2); + ok(r1.equals(new Rect(10, 10, 100, 100)), "expandToContain of rect and empty is rect"); + + let r1 = new Rect(10, 10, 0, 0); + let r2 = new Rect(0, 0, 0, 0); + r1.expandToContain(r2); + ok(r1.isEmpty(), "expandToContain of empty and empty is empty"); + }, + + testSubtract: function testSubtract() { + function equals(rects1, rects2) { + return rects1.length == rects2.length && rects1.every(function(r, i) { + return r.equals(rects2[i]); + }); + } + + let r1 = new Rect(0, 0, 100, 100); + let r2 = new Rect(500, 500, 100, 100); + ok(equals(r1.subtract(r2), [r1]), "subtract area outside of region yields same region"); + + let r1 = new Rect(0, 0, 100, 100); + let r2 = new Rect(-10, -10, 50, 120); + ok(equals(r1.subtract(r2), [new Rect(40, 0, 60, 100)]), "subtracting vertical bar from edge leaves one rect"); + + let r1 = new Rect(0, 0, 100, 100); + let r2 = new Rect(-10, -10, 120, 50); + ok(equals(r1.subtract(r2), [new Rect(0, 40, 100, 60)]), "subtracting horizontal bar from edge leaves one rect"); + + let r1 = new Rect(0, 0, 100, 100); + let r2 = new Rect(40, 40, 20, 20); + ok(equals(r1.subtract(r2), [ + new Rect(0, 0, 40, 100), + new Rect(40, 0, 20, 40), + new Rect(40, 60, 20, 40), + new Rect(60, 0, 40, 100)]), + "subtracting rect in middle leaves union of rects"); + }, +}; diff --git a/mobile/android/chrome/tests/browser_rememberPassword.js b/mobile/android/chrome/tests/browser_rememberPassword.js new file mode 100644 index 000000000000..f2708a4b16e8 --- /dev/null +++ b/mobile/android/chrome/tests/browser_rememberPassword.js @@ -0,0 +1,60 @@ +var testURL_01 = chromeRoot + "browser_blank_01.html"; + +// Tests for the Remember Password UI + +let gCurrentTab = null; +function test() { + waitForExplicitFinish(); + + messageManager.addMessageListener("pageshow", function() { + if (gCurrentTab.browser.currentURI.spec == testURL_01) { + messageManager.removeMessageListener("pageshow", arguments.callee); + pageLoaded(); + } + }); + + gCurrentTab = Browser.addTab(testURL_01, true); +} + +function pageLoaded() { + let iHandler = getIdentityHandler(); + let iPassword = document.getElementById("pageaction-password"); + let lm = getLoginManager(); + let host = gCurrentTab.browser.currentURI.prePath; + let nullSubmit = createLogin(host, host, null); + let nullForm = createLogin(host, null, "realm"); + + lm.removeAllLogins(); + + iHandler.show(); + is(iPassword.hidden, true, "Remember password hidden for no logins"); + iHandler.hide(); + + lm.addLogin(nullSubmit); + iHandler.show(); + is(iPassword.hidden, false, "Remember password shown for form logins"); + iPassword.click(); + is(iPassword.hidden, true, "Remember password hidden after click"); + is(lm.countLogins(host, "", null), 0, "Logins deleted when clicked"); + iHandler.hide(); + + lm.addLogin(nullForm); + iHandler.show(); + is(iPassword.hidden, false, "Remember password shown for protocol logins"); + iPassword.click(); + is(iPassword.hidden, true, "Remember password hidden after click"); + is(lm.countLogins(host, null, ""), 0, "Logins deleted when clicked"); + iHandler.hide(); + + Browser.closeTab(gCurrentTab); + finish(); +} + +function getLoginManager() { + return Components.classes["@mozilla.org/login-manager;1"].getService(Components.interfaces.nsILoginManager); +} + +function createLogin(aHostname, aFormSubmitURL, aRealm) { + let nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1", Components.interfaces.nsILoginInfo, "init"); + return new nsLoginInfo(aHostname, aFormSubmitURL, aRealm, "username", "password", "uname", "pword"); +} diff --git a/mobile/android/chrome/tests/browser_scroll.html b/mobile/android/chrome/tests/browser_scroll.html new file mode 100644 index 000000000000..e0f816a9617c --- /dev/null +++ b/mobile/android/chrome/tests/browser_scroll.html @@ -0,0 +1,16 @@ + + + + scrollTo test + + + + +

scrollTo test

+

The urlbar should scroll out of view after this page loads.

+ + diff --git a/mobile/android/chrome/tests/browser_scroll.js b/mobile/android/chrome/tests/browser_scroll.js new file mode 100644 index 000000000000..5445280589bc --- /dev/null +++ b/mobile/android/chrome/tests/browser_scroll.js @@ -0,0 +1,19 @@ +// Test behavior of window.scrollTo during page load (bug 654122). +"use strict"; + +var gTab; +registerCleanupFunction(function() Browser.closeTab(gTab)); + +const TEST_URL = baseURI + "browser_scroll.html"; + +function test() { + waitForExplicitFinish(); + gTab = Browser.addTab(TEST_URL, true); + onMessageOnce(gTab.browser.messageManager, "Browser:FirstPaint", function() { + executeSoon(function() { + let rect = Elements.browsers.getBoundingClientRect(); + is(rect.top, 0, "Titlebar is hidden."); + finish(); + }); + }); +} diff --git a/mobile/android/chrome/tests/browser_scrollbar.js b/mobile/android/chrome/tests/browser_scrollbar.js new file mode 100644 index 000000000000..008d43c35f96 --- /dev/null +++ b/mobile/android/chrome/tests/browser_scrollbar.js @@ -0,0 +1,179 @@ +let testURL_01 = baseURI + "browser_scrollbar.sjs?"; + +let gCurrentTest = null; +let gTests = []; +let gOpenedTabs = []; // for cleanup + +//------------------------------------------------------------------------------ +// Iterating tests by shifting test out one by one as runNextTest is called. +function runNextTest() { + // Run the next test until all tests completed + if (gTests.length > 0) { + gCurrentTest = gTests.shift(); + info(gCurrentTest.desc); + gCurrentTest.run(); + } + else { + // Close the awesome panel just in case + AwesomeScreen.activePanel = null; + finish(); + } +} + +function waitForPageShow(aPageURL, aCallback) { + messageManager.addMessageListener("pageshow", function(aMessage) { + if (aMessage.target.currentURI.spec == aPageURL) { + messageManager.removeMessageListener("pageshow", arguments.callee); + + setTimeout(aCallback, 0); + } + }); +}; + +//------------------------------------------------------------------------------ +// Entry point (must be named "test") +function test() { + // This test is async + waitForExplicitFinish(); + runNextTest(); +} + +let horizontalScrollbar = document.getElementById("horizontal-scroller"); +let verticalScrollbar = document.getElementById("vertical-scroller"); + +function checkScrollbars(aHorizontalVisible, aVerticalVisible, aHorizontalPosition, aVerticalPosition) { + let browser = getBrowser(); + let width = browser.getBoundingClientRect().width; + let height = browser.getBoundingClientRect().height; + EventUtils.synthesizeMouse(browser, width / 2, height / 4, { type: "mousedown" }); + EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mousemove" }); + + let horizontalVisible = horizontalScrollbar.hasAttribute("panning"), + verticalVisible = verticalScrollbar.hasAttribute("panning"); + is(horizontalVisible, aHorizontalVisible, "The horizontal scrollbar should be " + (aHorizontalVisible ? "visible" : "hidden")); + is(verticalVisible, aVerticalVisible, "The vertical scrollbar should be " + (aVerticalVisible ? "visible" : "hidden")); + + EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mouseup" }); +} + +function checkScrollbarsPosition(aX) { + let browser = getBrowser(); + let width = browser.getBoundingClientRect().width; + let height = browser.getBoundingClientRect().height; + EventUtils.synthesizeMouse(browser, width / 2, height / 4, { type: "mousedown" }); + EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mousemove" }); + + let verticalRect = verticalScrollbar.getBoundingClientRect(); + let margin = parseInt(verticalScrollbar.getAttribute("end")); + let expectedPosition = window.innerWidth - aX - margin; + is(verticalRect.right, expectedPosition, "The vertical scrollbar should be position to " + expectedPosition + " (got " + verticalRect.right + ")"); + + EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mouseup" }); +} + +gTests.push({ + desc: "Testing visibility of scrollbars", + + run: function() { + waitForPageShow(testURL_01 + "blank", gCurrentTest.checkNotScrollable); + gOpenedTabs.push(Browser.addTab(testURL_01 + "blank", true)); + }, + + checkNotScrollable: function() { + checkScrollbars(false, false); + + waitForPageShow(testURL_01 + "horizontal", gCurrentTest.checkHorizontalScrollable); + gOpenedTabs.push(Browser.addTab(testURL_01 + "horizontal", true)); + }, + + checkHorizontalScrollable: function() { + checkScrollbars(true, false); + + waitForPageShow(testURL_01 + "vertical", gCurrentTest.checkVerticalScrollable); + gOpenedTabs.push(Browser.addTab(testURL_01 + "vertical", true)); + }, + + checkVerticalScrollable: function() { + checkScrollbars(false, true); + + waitForPageShow(testURL_01 + "both", gCurrentTest.checkBothScrollable); + gOpenedTabs.push(Browser.addTab(testURL_01 + "both", true)); + }, + + checkBothScrollable: function() { + checkScrollbars(true, true); + Elements.browsers.addEventListener("PanFinished", function(aEvent) { + Elements.browsers.removeEventListener("PanFinished", arguments.callee, false); + setTimeout(function() { + Browser.hideSidebars(); + }, 0); + runNextTest(); + }, false); + } +}); + +gTests.push({ + desc: "Testing position of scrollbars", + + run: function() { + waitForPageShow(testURL_01 + "vertical", gCurrentTest.checkScrollbarsPosition); + gOpenedTabs.push(Browser.addTab(testURL_01 + "vertical", true)); + }, + + checkScrollbarsPosition: function() { + let [,, tabsWidth, controlsWidth] = Browser.computeSidebarVisibility(); + + checkScrollbarsPosition(0); + + // Show the left sidebar and ensure scrollbar is visible + Browser.controlsScrollboxScroller.scrollTo(0, 0); + checkScrollbarsPosition(-tabsWidth); + + // Show the right sidebar and ensure scrollbar is visible + Browser.controlsScrollboxScroller.scrollTo(tabsWidth + controlsWidth, 0); + checkScrollbarsPosition(controlsWidth); + + gCurrentTest.finish(); + }, + + finish: function() { + Elements.browsers.addEventListener("PanFinished", function(aEvent) { + Elements.browsers.removeEventListener("PanFinished", arguments.callee, false); + setTimeout(function() { + Browser.hideSidebars(); + }, 0); + runNextTest(); + }, false); + } +}); + + +gTests.push({ + desc: "Check scrollbar visibility when the touch sequence is cancelled", + + run: function() { + waitForPageShow(testURL_01 + "both", gCurrentTest.checkVisibility); + gOpenedTabs.push(Browser.addTab(testURL_01 + "both", true)); + }, + + checkVisibility: function() { + let browser = getBrowser(); + let width = browser.getBoundingClientRect().width; + let height = browser.getBoundingClientRect().height; + EventUtils.synthesizeMouse(browser, width / 2, height / 4, { type: "mousedown" }); + EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mousemove" }); + + let event = document.createEvent("Events"); + event.initEvent("CancelTouchSequence", true, false); + document.dispatchEvent(event); + + let horizontalVisible = horizontalScrollbar.hasAttribute("panning"), + verticalVisible = verticalScrollbar.hasAttribute("panning"); + is(horizontalVisible, false, "The horizontal scrollbar should be hidden when a canceltouchsequence is fired"); + is(verticalVisible, false, "The vertical scrollbar should be hidden should be hidden when a canceltouchsequence is called"); + + for (let iTab=0; iTab"); + response.write(""); + + let body = "" + query + ""; + switch (query) { + case "blank": + response.write("This is a not a scrollable page"); + break; + case "horizontal": + response.write("This is a horizontally scrollable page"); + body += "\n
Browser scrollbar test
"; + break; + case "vertical": + response.write("This is a vertically scrollable page"); + body += "\n
Browser scrollbar test
"; + break; + case "both": + response.write("This is a scrollable page in both directions"); + body += "\n
Browser scrollbar test
"; + break; + default: + break; + } + response.write("" + body + ""); +} diff --git a/mobile/android/chrome/tests/browser_select.html b/mobile/android/chrome/tests/browser_select.html new file mode 100644 index 000000000000..4de8ead4e201 --- /dev/null +++ b/mobile/android/chrome/tests/browser_select.html @@ -0,0 +1,99 @@ + +Browser Blank Page 01 + + + +
+ onchange: (selectedIndex) [value] +
+ - +
+ + + +
+
+ + +
+ onchange: (selectedIndex) [value] +
+ - +
+
+ + +
+
+ +
OptGroup Combobox:
+ +
+
+ + +
+ +

+
+
+
diff --git a/mobile/android/chrome/tests/browser_select.js b/mobile/android/chrome/tests/browser_select.js
new file mode 100644
index 000000000000..f9aca619ccc4
--- /dev/null
+++ b/mobile/android/chrome/tests/browser_select.js
@@ -0,0 +1,87 @@
+let testURL = chromeRoot + "browser_select.html";
+let new_tab = null;
+
+//------------------------------------------------------------------------------
+// Entry point (must be named "test")
+function test() {
+  // This test is async
+  waitForExplicitFinish();
+
+  // Add new tab to hold the 
+  
+ +
+ +
+ + + diff --git a/mobile/android/chrome/tests/browser_tapping.js b/mobile/android/chrome/tests/browser_tapping.js new file mode 100644 index 000000000000..12c203a9e651 --- /dev/null +++ b/mobile/android/chrome/tests/browser_tapping.js @@ -0,0 +1,306 @@ +/* + * Testing the tapping interactions: + * single tap, double tap & long tap + */ + +let testURL = chromeRoot + "browser_tap_content.html"; + +let gTests = []; +let gCurrentTest = null; +let gCurrentTab; + +const kDoubleClickIntervalPlus = kDoubleClickInterval + 100; + +let gEvents = []; +function dumpEvents(aEvent) { + if (aEvent.target != gCurrentTab.browser.parentNode) + return; + + gEvents.push(aEvent.type); +} + +function clearEvents() { + gEvents = []; +} + +function checkEvents(aEvents) { + if (aEvents.length != gEvents.length) { + info("---- event check: failed length (" + aEvents.length + " != " + gEvents.length + ")\n"); + info("---- expected: [" + aEvents.join(",") + "] actual: [" + gEvents.join(",") + "]\n"); + return false; + } + + for (let i=0; i 0) { + gCurrentTest = gTests.shift(); + info(gCurrentTest.desc); + gCurrentTest.run(); + } + else { + window.removeEventListener("TapSingle", dumpEvents, true); + window.removeEventListener("TapDouble", dumpEvents, true); + window.removeEventListener("TapLong", dumpEvents, true); + + SelectionHelper.enabled = true; + Browser.closeTab(gCurrentTab); + + finish(); + } +} + +//------------------------------------------------------------------------------ +// Case: Test the double tap behavior +gTests.push({ + desc: "Test the double tap behavior", + + run: function() { + let inputHandler = gCurrentTab.browser.parentNode; + let width = inputHandler.getBoundingClientRect().width; + let height = inputHandler.getBoundingClientRect().height; + + // Should fire "TapSingle" + info("Test good single tap"); + clearEvents(); + EventUtils.synthesizeMouse(document.documentElement, width / 2, height / 2, {}); + + // We wait a bit because of the delay allowed for double clicking on device + // where it is not native + setTimeout(function() { + ok(checkEvents(["TapSingle"]), "Fired a good single tap"); + clearEvents(); + gCurrentTest.doubleTapTest(); + }, kDoubleClickIntervalPlus); + }, + + doubleTapTest: function() { + let inputHandler = gCurrentTab.browser.parentNode; + let width = inputHandler.getBoundingClientRect().width; + let height = inputHandler.getBoundingClientRect().height; + + // Should fire "TapDouble" + info("Test good double tap"); + clearEvents(); + EventUtils.synthesizeMouse(document.documentElement, width / 2, height / 2, {}); + EventUtils.synthesizeMouse(document.documentElement, width / 2, height / 2, {}); + + setTimeout(function() { + let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2); + if (sysInfo.get("device")) + todo(checkEvents(["TapDouble"]), "Fired a good double tap"); + else + ok(checkEvents(["TapDouble"]), "Fired a good double tap"); + + clearEvents(); + + gCurrentTest.doubleTapFailTest(); + }, kDoubleClickIntervalPlus); + }, + + doubleTapFailTest: function() { + let inputHandler = gCurrentTab.browser.parentNode; + let width = inputHandler.getBoundingClientRect().width; + let height = inputHandler.getBoundingClientRect().height; + + // Should fire "TapSingle", "TapSingle" + info("Test two single taps in different locations"); + clearEvents(); + EventUtils.synthesizeMouse(document.documentElement, width / 3, height / 3, {}); + EventUtils.synthesizeMouse(document.documentElement, width * 2 / 3, height * 2 / 3, {}); + + setTimeout(function() { + ok(checkEvents(["TapSingle", "TapSingle"]), "Fired two single taps in different places, not a double tap"); + clearEvents(); + + gCurrentTest.tapPanTest(); + }, 500); + }, + + tapPanTest: function() { + let inputHandler = gCurrentTab.browser.parentNode; + let width = inputHandler.getBoundingClientRect().width; + let height = inputHandler.getBoundingClientRect().height; + + info("Test a pan - non-tap event"); + clearEvents(); + EventUtils.synthesizeMouse(document.documentElement, width / 2, height / 3, { type: "mousedown" }); + EventUtils.synthesizeMouse(document.documentElement, width / 2, height * 2 / 3, { type: "mousemove" }); + EventUtils.synthesizeMouse(document.documentElement, width / 2, height * 2 / 3, { type: "mouseup" }); + ok(checkEvents([]), "Fired a pan which should be seen as a non event"); + clearEvents(); + + setTimeout(function() { gCurrentTest.longTapFailTest(); }, 500); + }, + + longTapFailTest: function() { + let inputHandler = gCurrentTab.browser.parentNode; + let width = inputHandler.getBoundingClientRect().width; + let height = inputHandler.getBoundingClientRect().height; + + info("Test a long pan - non-tap event"); + clearEvents(); + EventUtils.synthesizeMouse(document.documentElement, width / 2, height / 3, { type: "mousedown" }); + EventUtils.synthesizeMouse(document.documentElement, width / 2, height * 2 / 3, { type: "mousemove" }); + setTimeout(function() { + EventUtils.synthesizeMouse(document.documentElement, width / 2, height * 2 / 3, { type: "mouseup" }); + ok(checkEvents([]), "Fired a pan + delay which should be seen as a non-event"); + clearEvents(); + + window.addEventListener("PanFinished", function() { + window.removeEventListener("PanFinished", arguments.callee, true); + setTimeout(gCurrentTest.longTapPassTest, 0); + }, true); + }, 500); + }, + + longTapPassTest: function() { + let browser = gCurrentTab.browser; + let inputHandler = browser.parentNode; + let width = inputHandler.getBoundingClientRect().width; + let height = inputHandler.getBoundingClientRect().height; + + window.addEventListener("TapLong", function() { + window.removeEventListener("TapLong", arguments.callee, true); + EventUtils.synthesizeMouse(document.documentElement, width / 2, height / 2, { type: "mouseup" }); + ok(checkEvents(["TapLong"]), "Fired a good long tap"); + clearEvents(); + }, true); + + browser.messageManager.addMessageListener("Browser:ContextMenu", function(aMessage) { + browser.messageManager.removeMessageListener(aMessage.name, arguments.callee); + setTimeout(gCurrentTest.contextPlainLinkTest, 0); + }); + + info("Test a good long pan"); + clearEvents(); + EventUtils.synthesizeMouse(document.documentElement, width / 2, height / 2, { type: "mousedown" }); + }, + + contextPlainLinkTest: function() { + waitForContextMenu(function(aJSON) { + is(aJSON.linkTitle, "A blank page - nothing interesting", "Text content should be the content of the second link"); + ok(checkContextTypes(["link", "link-openable"]), "Plain link context types"); + }, gCurrentTest.contextPlainImageTest); + + let browser = gCurrentTab.browser; + let linkDisabled = browser.contentDocument.getElementById("link-disabled"); + let event = content.document.createEvent("PopupEvents"); + event.initEvent("contextmenu", true, true); + linkDisabled.dispatchEvent(event); + + let link = browser.contentDocument.getElementById("link-single"); + let event = content.document.createEvent("PopupEvents"); + event.initEvent("contextmenu", true, true); + link.dispatchEvent(event); + }, + + contextPlainImageTest: function() { + waitForContextMenu(function() { + ok(checkContextTypes(["image","image-shareable","image-loaded", "content-text"]), "Plain image context types"); + }, gCurrentTest.contextNestedImageTest); + + let browser = gCurrentTab.browser; + let img = browser.contentDocument.getElementById("img-single"); + let event = content.document.createEvent("PopupEvents"); + event.initEvent("contextmenu", true, true); + img.dispatchEvent(event); + }, + + contextNestedImageTest: function() { + waitForContextMenu(function() { + ok(checkContextTypes(["link","image","image-shareable","image-loaded","link-openable"]), "Nested image context types"); + }, runNextTest); + + let browser = gCurrentTab.browser; + let img = browser.contentDocument.getElementById("img-nested"); + let event = content.document.createEvent("PopupEvents"); + event.initEvent("contextmenu", true, true); + img.dispatchEvent(event); + } +}); + diff --git a/mobile/android/chrome/tests/browser_tapping_edit.js b/mobile/android/chrome/tests/browser_tapping_edit.js new file mode 100644 index 000000000000..b1e4c139a476 --- /dev/null +++ b/mobile/android/chrome/tests/browser_tapping_edit.js @@ -0,0 +1,279 @@ +/* + * Testing the context menus on editboxes: + * plain and url + */ + +let testURL = chromeRoot + "browser_tap_contentedit.html"; + +let gTests = []; +let gCurrentTest = null; +let gCurrentTab; + +const kDoubleClickIntervalPlus = kDoubleClickInterval + 100; + +let gEvents = []; +function dumpEvents(aEvent) { + if (aEvent.target != gCurrentTab.browser.parentNode) + return; + + gEvents.push(aEvent.type); +} + +function clearEvents() { + gEvents = []; +} + +function checkEvents(aEvents) { + if (aEvents.length != gEvents.length) { + info("---- event check: failed length (" + aEvents.length + " != " + gEvents.length + ")\n"); + info("---- expected: [" + aEvents.join(",") + "] actual: [" + gEvents.join(",") + "]\n"); + return false; + } + + for (let i=0; i 0) { + gCurrentTest = gTests.shift(); + info(gCurrentTest.desc); + gCurrentTest.run(); + } + else { + let clipboard = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard); + clipboard.emptyClipboard(Ci.nsIClipboard.kGlobalClipboard); + + window.removeEventListener("TapSingle", dumpEvents, true); + window.removeEventListener("TapDouble", dumpEvents, true); + window.removeEventListener("TapLong", dumpEvents, true); + + SelectionHelper.enabled = true; + Browser.closeTab(gCurrentTab); + + finish(); + } +} + +//------------------------------------------------------------------------------ +gTests.push({ + desc: "Test empty plain textbox", + + run: function() { + waitForContextMenu(function(aJSON) { + ok(checkContextTypes(["input-text"]), "Editbox with no text, no selection and no clipboard"); + }, runNextTest); + + let browser = gCurrentTab.browser; + let plainEdit = browser.contentDocument.getElementById("plain-edit"); + plainEdit.readOnly = false; + plainEdit.value = ""; + + // Try very hard to keep "paste" from if the clipboard has data + let clipboard = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard); + clipboard.emptyClipboard(Ci.nsIClipboard.kGlobalClipboard); + plainEdit.readOnly = true; + + let event = content.document.createEvent("PopupEvents"); + event.initEvent("contextmenu", true, true); + plainEdit.dispatchEvent(event); + } +}); + +//------------------------------------------------------------------------------ +gTests.push({ + desc: "Test plain textbox with text fully selected", + + run: function() { + waitForContextMenu(function(aJSON) { + ok(checkContextTypes(["input-text", "copy"]), "Editbox with text and full selection, but no clipboard"); + }, runNextTest); + + let browser = gCurrentTab.browser; + let plainEdit = browser.contentDocument.getElementById("plain-edit"); + plainEdit.readOnly = false; + plainEdit.value = "Every time we fix a bug, Stuart call's the President"; + let plainEdit = plainEdit.QueryInterface(Ci.nsIDOMNSEditableElement); + plainEdit.editor.selectAll(); + + // Try very hard to keep "paste" from if the clipboard has data + let clipboard = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard); + clipboard.emptyClipboard(Ci.nsIClipboard.kGlobalClipboard); + plainEdit.readOnly = true; + + let event = content.document.createEvent("PopupEvents"); + event.initEvent("contextmenu", true, true); + plainEdit.dispatchEvent(event); + } +}); + +//------------------------------------------------------------------------------ +gTests.push({ + desc: "Test plain textbox with text no selection", + + run: function() { + waitForContextMenu(function(aJSON) { + ok(checkContextTypes(["input-text", "copy-all", "select-all"]), "Editbox with text, but no selection and no clipboard"); + }, runNextTest); + + let browser = gCurrentTab.browser; + let plainEdit = browser.contentDocument.getElementById("plain-edit"); + plainEdit.readOnly = false; + plainEdit.value = "Every time we fix a bug, Stuart call's the President"; + plainEdit.selectionStart = 0; + plainEdit.selectionEnd = 0; + + // Try very hard to keep "paste" from if the clipboard has data + let clipboard = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard); + clipboard.emptyClipboard(Ci.nsIClipboard.kGlobalClipboard); + plainEdit.readOnly = true; + + let event = content.document.createEvent("PopupEvents"); + event.initEvent("contextmenu", true, true); + plainEdit.dispatchEvent(event); + } +}); + +//------------------------------------------------------------------------------ +gTests.push({ + desc: "Test plain textbox with text no selection and text on clipboard", + + run: function() { + waitForContextMenu(function(aJSON) { + ok(checkContextTypes(["input-text", "copy-all", "select-all", "paste"]), "Editbox with text and clipboard, but no selection"); + }, runNextTest); + + let browser = gCurrentTab.browser; + let plainEdit = browser.contentDocument.getElementById("plain-edit"); + plainEdit.readOnly = false; + plainEdit.value = "Every time we fix a bug, Stuart call's the President"; + plainEdit.selectionStart = 0; + plainEdit.selectionEnd = 0; + + // Put some data on the clipboard to get "paste" to be active + let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper); + clipboard.copyString("We are testing Firefox"); + + let event = content.document.createEvent("PopupEvents"); + event.initEvent("contextmenu", true, true); + plainEdit.dispatchEvent(event); + } +}); + +//------------------------------------------------------------------------------ +gTests.push({ + desc: "Test that tapping on input box causes mouse events to fire", + + run: function() { + let browser = gCurrentTab.browser; + let plainEdit = browser.contentDocument.getElementById("plain-edit"); + plainEdit.blur(); + plainEdit.value = ''; + + let eventArray = ['mouseover', 'mousedown', 'mouseup', 'click']; + + while (eventArray.length > 0) { + let currentEventType = eventArray.shift(); + browser.contentWindow.addEventListener(currentEventType, function(e) { + browser.contentWindow.removeEventListener(currentEventType, arguments.callee, true); + ok(e.target == plainEdit, e.type + " should fire over input id=" + plainEdit.id); + plainEdit.value += e.type + ' '; + + if (e.type == 'click') { + ok(plainEdit.value == 'mouseover mousedown mouseup click ', + 'Events should fire in this order: mouseover mousedown mouseup click '); + runNextTest(); + } + } , true); + } + + EventUtils.synthesizeMouse(plainEdit, browser.getBoundingClientRect().left + plainEdit.getBoundingClientRect().left + 2, + browser.getBoundingClientRect().top + plainEdit.getBoundingClientRect().top + 2, {}); + } +}); + diff --git a/mobile/android/chrome/tests/browser_test.js b/mobile/android/chrome/tests/browser_test.js new file mode 100644 index 000000000000..c0a987eb4c04 --- /dev/null +++ b/mobile/android/chrome/tests/browser_test.js @@ -0,0 +1,20 @@ +// Tests for the test functions in head.js + +function test() { + waitForExplicitFinish(); + testWaitForAndContinue(); +} + +function testWaitForAndContinue() { + waitForAndContinue(function() { + ok(true, "continues on success") + testWaitForAndContinue2(); + }, function() true); +} + +function testWaitForAndContinue2() { + waitForAndContinue(function() { + ok(true, "continues on failure"); + finish(); + }, function() false); +} diff --git a/mobile/android/chrome/tests/browser_thumbnails.js b/mobile/android/chrome/tests/browser_thumbnails.js new file mode 100644 index 000000000000..59ac82d731e0 --- /dev/null +++ b/mobile/android/chrome/tests/browser_thumbnails.js @@ -0,0 +1,155 @@ +// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Mobile Browser. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Wes Johnston + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +let testURL_blank = baseURI + "browser_blank_01.html"; + +const DEFAULT_WIDTH = 800; + +function testURL(n) { + if (n < 0) + return testURL_blank; + + return baseURI + "browser_viewport.sjs?" + encodeURIComponent(gTestData[n].metadata); +} + +function scaleRatio(n) { + if ("scaleRatio" in gTestData[n]) + return gTestData[n].scaleRatio; + return 150; // Default value matches our main target hardware (N900, Nexus One, etc.) +} + +let currentTab; + +let loadURL = function loadURL(aPageURL) { + BrowserUI.goToURI(aPageURL); +}; + +let gTestData = [ + { metadata: "", width: DEFAULT_WIDTH, scale: 1 }, + { metadata: "width=device-width, initial-scale=1" }, + { metadata: "width=device-width" }, + { metadata: "width=320, initial-scale=1" }, + { metadata: "initial-scale=1.0, user-scalable=no" }, + { metadata: "width=200,height=500" }, + { metadata: "width=2000, minimum-scale=0.75" }, + { metadata: "width=100, maximum-scale=2.0" }, + { metadata: "width=2000, initial-scale=0.75" }, + { metadata: "width=20000, initial-scale=100" }, + { metadata: "XHTML" }, + /* testing opening and switching between pages without a viewport */ + { metadata: "style=width:400px;margin:0px;" }, + { metadata: "style=width:1000px;margin:0px;" }, + { metadata: "style=width:800px;margin:0px;" }, + { metadata: "style=width:800px;height:300px;margin:0px;" }, + { metadata: "style=width:800px;height:1000px;margin:0px;" }, +]; + + + +//------------------------------------------------------------------------------ +// Entry point (must be named "test") +let gCurrentTest = -1; +let secondPass = false; +let oldRendererFactory; +let gDevice = null; +function test() { + oldRendererFactory = rendererFactory; + rendererFactory = newRendererFactory; + + // if we are on desktop, we can run tests in both portrait and landscape modes + // on devices we should just use the current orientation + let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2); + gDevice = sysInfo.get("device"); + if (gDevice != null) + secondPass = true; + + // This test is async + waitForExplicitFinish(); + + currentTab = Browser.addTab("about:blank", true); + ok(currentTab, "Tab Opened"); + + startTest(gCurrentTest); +} + +function startTest(n) { + let url = testURL(gCurrentTest); + info("Testing: " + url) + loadURL(url); +} + +function newRendererFactory(aBrowser, aCanvas) { + let wrapper = {}; + let ctx = aCanvas.MozGetIPCContext("2d"); + let draw = function(browser, aLeft, aTop, aWidth, aHeight, aColor, aFlags) { + is(aLeft, 0,"Thumbnail drawn at x=0"); + is(aTop, 0,"Thumbnail drawn at y=0"); + ok(aWidth <= browser.contentDocumentWidth, "Thumbnail ok width: " + aWidth + " <= " + browser.contentDocumentWidth); + ok(aHeight <= browser.contentDocumentHeight,"Thumbnail ok height:" + aHeight + " <= " + browser.contentDocumentHeight); + finishTest(); + }; + wrapper.checkBrowser = function(browser) { + return browser.contentWindow; + }; + wrapper.drawContent = function(callback) { + callback(ctx, draw); + }; + + return wrapper; +}; + +function finishTest() { + gCurrentTest++; + if (gCurrentTest < gTestData.length) { + startTest(gCurrentTest); + } else if (secondPass) { + + if (gDevice == null) + window.top.resizeTo(480,800); + + Browser.closeTab(currentTab); + rendererFactory = oldRendererFactory; + finish(); + } else { + secondPass = true; + gCurrentTest = -1; + window.top.resizeTo(800,480); + startTest(gCurrentTest); + } +} diff --git a/mobile/android/chrome/tests/browser_title.sjs b/mobile/android/chrome/tests/browser_title.sjs new file mode 100644 index 000000000000..a523c1de5de6 --- /dev/null +++ b/mobile/android/chrome/tests/browser_title.sjs @@ -0,0 +1,67 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Mobile Browser. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Aditya Rao + * Vivien Nicolas <21@vingtetun.org> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +function handleRequest(request, response) { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/html", false); + + let action = ""; + let query = decodeURIComponent(request.queryString || ""); + response.write(""); + + switch (query) { + case "no_title": + break; + case "english_title": + response.write("English Title Page"); + break; + case "dynamic_title": + response.write("This is an english title page"); + response.write(""); + break; + case "redirect": + response.write(""); + break; + case "location": + response.write(""); + break; + default: + break; + } + response.write("" + query + ""); +} diff --git a/mobile/android/chrome/tests/browser_upgrade.rdf b/mobile/android/chrome/tests/browser_upgrade.rdf new file mode 100644 index 000000000000..bfde13200e03 --- /dev/null +++ b/mobile/android/chrome/tests/browser_upgrade.rdf @@ -0,0 +1,45 @@ + + + + + + + +
  • + + 2.0 + + + toolkit@mozilla.org + 0 + * + http://example.com/browser/mobile/chrome/tests/addons/browser_install1_3.xpi + + + +
  • +
    +
    +
    + + + + +
  • + + 1.0 + + + mochikit@mozilla.org + 0 + * + + + +
  • +
    +
    +
    + +
    diff --git a/mobile/android/chrome/tests/browser_viewport.js b/mobile/android/chrome/tests/browser_viewport.js new file mode 100644 index 000000000000..bb95cc2eb6d2 --- /dev/null +++ b/mobile/android/chrome/tests/browser_viewport.js @@ -0,0 +1,209 @@ +// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Mobile Browser. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * Matt Brubeck + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +let testURL_blank = baseURI + "browser_blank_01.html"; + +const DEFAULT_WIDTH = 800; + +function testURL(n) { + return baseURI + "browser_viewport.sjs" + + "?metadata=" + encodeURIComponent(gTestData[n].metadata || "") + + "&style=" + encodeURIComponent(gTestData[n].style || "") + + "&xhtml=" + encodeURIComponent(!!gTestData[n].xhtml); +} + +function scaleRatio(n) { + if ("scaleRatio" in gTestData[n]) + return gTestData[n].scaleRatio; + return 150; // Default value matches our main target hardware (N900, Nexus One, etc.) +} + +let currentTab; + +let loadURL = function loadURL(aPageURL, aCallback, aScale) { + messageManager.addMessageListener("pageshow", function(aMessage) { + if (aMessage.target.currentURI.spec == aPageURL) { + messageManager.removeMessageListener(aMessage.name, arguments.callee); + + waitFor(aCallback, function() { + return !aScale || aScale == aMessage.target.scale; + }); + } + }); + + BrowserUI.goToURI(aPageURL); +}; + +// XXX Tests do not yet run correctly in portrait. +window.resizeTo(800, 480); + +let gTestData = [ + { metadata: "", width: DEFAULT_WIDTH, scale: 1 }, + { metadata: "width=device-width, initial-scale=1", width: 533.33, scale: 1.5 }, + { metadata: "width=device-width", width: 533.33, scale: 1.5 }, + { metadata: "width=device-width, initial-scale=1", scaleRatio: 100, width: 800, scale: 1 }, + { metadata: "width=320, initial-scale=1", width: 533.33, scale: 1.5 }, + { metadata: "initial-scale=1.0, user-scalable=no", width: 533.33, scale: 1.5, disableZoom: true }, + { metadata: "initial-scale=1.0, user-scalable=0", width: 533.33, scale: 1.5, disableZoom: true }, + { metadata: "initial-scale=1.0, user-scalable=false", width: 533.33, scale: 1.5, disableZoom: true }, + { metadata: "initial-scale=1.0, user-scalable=NO", width: 533.33, scale: 1.5, disableZoom: false }, // values are case-sensitive + { metadata: "width=200,height=500", width: 200, scale: 4 }, + { metadata: "width=2000, minimum-scale=0.75", width: 2000, scale: 1.125, minScale: 1.125 }, + { metadata: "width=100, maximum-scale=2.0", width: 266.67, scale: 3, maxScale: 3 }, + { metadata: "width=2000, initial-scale=0.75", width: 2000, scale: 1.125 }, + { metadata: "width=20000, initial-scale=100", width: 10000, scale: 4 }, + { xhtml: true, width: 533.33, scale: 1.5, disableZoom: false }, + /* testing spaces between arguments (bug 572696) */ + { metadata: "width= 2000, minimum-scale=0.75", width: 2000, scale: 1.125 }, + { metadata: "width = 2000, minimum-scale=0.75", width: 2000, scale: 1.125 }, + { metadata: "width = 2000 , minimum-scale=0.75", width: 2000, scale: 1.125 }, + { metadata: "width = 2000 , minimum-scale =0.75", width: 2000, scale: 1.125 }, + { metadata: "width = 2000 , minimum-scale = 0.75", width: 2000, scale: 1.125 }, + { metadata: "width = 2000 , minimum-scale = 0.75", width: 2000, scale: 1.125 }, + /* testing opening and switching between pages without a viewport */ + { style: "width:400px;margin:0px;", width: DEFAULT_WIDTH, scale: 1 }, + { style: "width:2000px;margin:0px;", width: 980, scale: window.innerWidth/2000 }, + { style: "width:800px;margin:0px;", width: DEFAULT_WIDTH, scale: 1 }, +]; + + +//------------------------------------------------------------------------------ +// Entry point (must be named "test") +function test() { + // This test is async + waitForExplicitFinish(); + requestLongerTimeout(2); + + currentTab = Browser.addTab("about:blank", true); + ok(currentTab, "Tab Opened"); + + startTest(0); +} + +function startTest(n) { + info(JSON.stringify(gTestData[n])); + BrowserUI.goToURI(testURL_blank); + loadURL(testURL_blank, verifyBlank(n)); + Services.prefs.setIntPref("browser.viewport.scaleRatio", scaleRatio(n)); +} + +function verifyBlank(n) { + return function() { + // Do sanity tests + let uri = currentTab.browser.currentURI.spec; + is(uri, testURL_blank, "URL Matches blank page " + n); + + waitFor(function() { + loadURL(testURL(n), verifyTest(n), gTestData[n].scale); + }, function() { + return currentTab.browser.contentWindowWidth == DEFAULT_WIDTH; + }); + } +} + +function is_approx(actual, expected, fuzz, description) { + ok(Math.abs(actual - expected) <= fuzz, + description + " [got " + actual + ", expected " + expected + "]"); +} + +function verifyTest(n) { + let assumedWidth = 480; + if (!Util.isPortrait()) + assumedWidth = 800; + + return function() { + is(window.innerWidth, assumedWidth, "Test assumes window width is " + assumedWidth + "px"); + + // Do sanity tests + let uri = currentTab.browser.currentURI.spec; + is(uri, testURL(n), "URL is " + testURL(n)); + + let data = gTestData[n]; + let actualWidth = currentTab.browser.contentWindowWidth; + is_approx(actualWidth, parseFloat(data.width), .01, "Viewport width=" + data.width); + + let zoomLevel = getBrowser().scale; + is_approx(zoomLevel, parseFloat(data.scale), .01, "Viewport scale=" + data.scale); + + // Test zooming + if (data.disableZoom) { + ok(!currentTab.allowZoom, "Zoom disabled"); + + Browser.zoom(-1); + is(getBrowser().scale, zoomLevel, "Zoom in does nothing"); + + Browser.zoom(1); + is(getBrowser().scale, zoomLevel, "Zoom out does nothing"); + } + else { + ok(Browser.selectedTab.allowZoom, "Zoom enabled"); + } + + + if (data.minScale) { + do { // Zoom out until we can't go any farther. + zoomLevel = getBrowser().scale; + Browser.zoom(1); + } while (getBrowser().scale != zoomLevel); + ok(getBrowser().scale >= data.minScale, "Zoom out limited"); + } + + if (data.maxScale) { + do { // Zoom in until we can't go any farther. + zoomLevel = getBrowser().scale; + Browser.zoom(-1); + } while (getBrowser().scale != zoomLevel); + ok(getBrowser().scale <= data.maxScale, "Zoom in limited"); + } + + finishTest(n); + } +} + +function finishTest(n) { + Services.prefs.clearUserPref("browser.viewport.scaleRatio"); + if (n + 1 < gTestData.length) { + startTest(n + 1); + } else { + window.resizeTo(480, 800); + Browser.closeTab(currentTab); + finish(); + } +} diff --git a/mobile/android/chrome/tests/browser_viewport.sjs b/mobile/android/chrome/tests/browser_viewport.sjs new file mode 100644 index 000000000000..e7351a3f6754 --- /dev/null +++ b/mobile/android/chrome/tests/browser_viewport.sjs @@ -0,0 +1,65 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Mobile Browser. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Matt Brubeck + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + + function decodeQuery(query) { + let result = {}; + query.split("&").forEach(function(pair) { + let [key, val] = pair.split("="); + result[key] = decodeURIComponent(val); + }); + return result; + } + +function handleRequest(request, response) { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/html", false); + + let params = decodeQuery(request.queryString || ""); + + if (params.xhtml == "true") { + response.write(""); + response.write(""); + } + response.write("Browser Viewport Test"); + if (params.metadata) + response.write(""); + response.write(" "); +} diff --git a/mobile/android/chrome/tests/browser_vkb.js b/mobile/android/chrome/tests/browser_vkb.js new file mode 100644 index 000000000000..d20efe6c75a2 --- /dev/null +++ b/mobile/android/chrome/tests/browser_vkb.js @@ -0,0 +1,150 @@ +/* + * Check VKB show/hide behavior + */ +let testURL_01 = chromeRoot + "browser_forms.html"; +messageManager.loadFrameScript(chromeRoot + "remote_vkb.js", true); + +/* ============================= Tests Utils =============================== */ +let gTests = []; +let gCurrentTest = null; + +// Iterating tests by shifting test out one by one as runNextTest is called. +function runNextTest() { + // Run the next test until all tests completed + if (gTests.length > 0) { + gCurrentTest = gTests.shift(); + info(gCurrentTest.desc); + gCurrentTest.run(); + } + else { + // Close the awesome panel just in case + AwesomeScreen.activePanel = null; + finish(); + } +} + +function test() { + // The "runNextTest" approach is async, so we need to call "waitForExplicitFinish()" + // We call "finish()" when the tests are finished + waitForExplicitFinish(); + + // Start the tests + if ("gTimeout" in window) + setTimeout(runNextTest, gTimeout); + else + runNextTest(); +} +/* ============================ End Utils ================================== */ + +Components.utils.import("resource://gre/modules/Services.jsm"); +let VKBStateHasChanged = false; +let VKBObserver = { + _enabled: false, + observe: function(aTopic, aSubject, aData) { + if (this._enabled != parseInt(aData)) { + this._enabled = parseInt(aData); + VKBstateHasChanged = true; + } + } +}; +Services.obs.addObserver(VKBObserver, "ime-enabled-state-changed", false); + +function waitForVKBChange(aCallback, aState) { + waitForAndContinue(aCallback, function() { + if (VKBStateHasChanged) { + VKBStateHasChanged = false; + return true; + } + + return VKBStateHasChanged; + }); +} + +let newTab = null; + +function waitForPageShow(aPageURL, aCallback) { + messageManager.addMessageListener("pageshow", function(aMessage) { + if (aMessage.target.currentURI.spec == aPageURL) { + messageManager.removeMessageListener("pageshow", arguments.callee); + setTimeout(function() { aCallback(); }, 0); + } + }); +}; + +function dragElement(element, x1, y1, x2, y2) { + EventUtils.synthesizeMouse(element, x1, y1, { type: "mousedown" }); + EventUtils.synthesizeMouse(element, x2, y2, { type: "mousemove" }); + EventUtils.synthesizeMouse(element, x2, y2, { type: "mouseup" }); +} + +//------------------------------------------------------------------------------ +// Case: Test interactions with a VKB from content +gTests.push({ + desc: "Test interactions with a VKB from content", + run: function() { + waitForPageShow(testURL_01, gCurrentTest.focusContentInputField); + + newTab = Browser.addTab(testURL_01, true); + ok(newTab, "Tab Opened"); + }, + + focusContentInputField: function() { + is(VKBObserver._enabled, false, "Initially the VKB should be closed"); + + AsyncTests.waitFor("Test:FocusRoot", {}, function(json) { + waitForVKBChange(gCurrentTest.showLeftSidebar); + }) + }, + + showLeftSidebar: function() { + is(VKBObserver._enabled, true, "When focusing an input field the VKB should be opened"); + + let browsers = document.getElementById("browsers"); + dragElement(browsers, window.innerWidth / 2, window.innerHeight / 2, 1000, 1000); + waitForVKBChange(gCurrentTest.showRightSidebar); + }, + + showRightSidebar: function() { + is(VKBObserver._enabled, true, "When pannning to the leftSidebar the VKB state should not changed"); + + let browsers = document.getElementById("browsers"); + dragElement(browsers, window.innerWidth / 2, window.innerHeight / 2, -1000, -1000); + waitForVKBChange(gCurrentTest.changeTab); + }, + + changeTab: function() { + is(VKBObserver._enabled, true, "When panning to the right sidebar the VKB state should not changed"); + + let firstTab = document.getElementById("tabs").children.firstChild; + BrowserUI.selectTab(firstTab); + waitForVKBChange(gCurrentTest.prepareOpenRightPanel); + }, + + prepareOpenRightPanel: function() { + is(VKBObserver._enabled, false, "Switching tab should close the VKB"); + + BrowserUI.selectTab(newTab); + + // Give back the focus to the content input and launch and check + // interaction with the right panel + AsyncTests.waitFor("Test:FocusRoot", {}, function(json) { + waitForVKBChange(gCurrentTest.openRightPanel); + }); + }, + + openRightPanel: function() { + is(VKBObserver._enabled, true, "Re-cliking on an input field should re-open the VKB"); + + let toolsButton = document.getElementById("tool-panel-open"); + let rect = toolsButton.getBoundingClientRect(); + EventUtils.synthesizeMouse(toolsButton, rect.width / 2, rect.height / 2, {}); + waitForVKBChange(function() { + is(VKBObserver._enabled, false, "Opening the right panel should close the VKB"); + BrowserUI.hidePanel(); + Browser.hideSidebars(); + BrowserUI.closeTab(newTab); + Services.obs.removeObserver(VKBObserver, "ime-enabled-state-changed"); + runNextTest(); + }); + } +}); diff --git a/mobile/android/chrome/tests/head.js b/mobile/android/chrome/tests/head.js new file mode 100644 index 000000000000..965858ec96f7 --- /dev/null +++ b/mobile/android/chrome/tests/head.js @@ -0,0 +1,122 @@ +/*============================================================================= + Common Helpers functions +=============================================================================*/ + +const kDefaultWait = 2000; +// Wait for a condition and call a supplied callback if condition is met within +// alloted time. If condition is not met, cause a hard failure, stopping the test. +function waitFor(callback, test, timeout) { + if (test()) { + callback(); + return; + } + + timeout = timeout || Date.now(); + if (Date.now() - timeout > kDefaultWait) + throw "waitFor timeout"; + setTimeout(waitFor, 50, callback, test, timeout); +}; + +// Wait for a condition and call a supplied callback if condition is met within +// alloted time. If condition is not met, continue anyway. Use this helper if the +// callback will test for the outcome, but not stop the entire test. +function waitForAndContinue(callback, test, timeout) { + if (test()) { + callback(); + return; + } + + timeout = timeout || Date.now(); + if (Date.now() - timeout > kDefaultWait) { + callback(); + return; + } + setTimeout(waitForAndContinue, 50, callback, test, timeout); +}; + +// Listen for the specified message once, then remove the listener. +function onMessageOnce(aMessageManager, aName, aCallback) { + aMessageManager.addMessageListener(aName, function onMessage(aMessage) { + aMessageManager.removeMessageListener(aName, onMessage); + setTimeout(function() { + aCallback(aMessage); + }, 0); + }); +}; + +// This function is useful for debugging one test where you need to wait for +// application to be ready +function waitForFirstPaint(aCallback) { + function hasFirstPaint() { + let startupInfo = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup).getStartupInfo(); + return ("firstPaint" in startupInfo); + } + + if (!hasFirstPaint()) { + waitFor(aCallback, hasFirstPaint, Date.now() + 3000); + return; + } + + aCallback(); +}; + +function makeURI(spec) { + return Services.io.newURI(spec, null, null); +}; + +EventUtils.synthesizeString = function synthesizeString(aString, aWindow) { + for (let i = 0; i < aString.length; i++) { + EventUtils.synthesizeKey(aString.charAt(i), {}, aWindow); + } +}; + +EventUtils.synthesizeMouseForContent = function synthesizeMouseForContent(aElement, aOffsetX, aOffsetY, aEvent, aWindow) { + let container = document.getElementById("browsers"); + let rect = container.getBoundingClientRect(); + + EventUtils.synthesizeMouse(aElement, rect.left + aOffsetX, rect.top + aOffsetY, aEvent, aWindow); +}; + +let AsyncTests = { + _tests: {}, + waitFor: function(aMessage, aData, aCallback) { + messageManager.addMessageListener(aMessage, this); + if (!this._tests[aMessage]) + this._tests[aMessage] = []; + + this._tests[aMessage].push(aCallback || function() {}); + setTimeout(function() { + Browser.selectedBrowser.messageManager.sendAsyncMessage(aMessage, aData || { }); + }, 0); + }, + + receiveMessage: function(aMessage) { + let test = this._tests[aMessage.name]; + let callback = test.shift(); + if (callback) + callback(aMessage.json); + } +}; + +let gCurrentTest = null; +let gTests = []; + +// Iterating tests by shifting test out one by one as runNextTest is called. +function runNextTest() { + // Run the next test until all tests completed + if (gTests.length > 0) { + gCurrentTest = gTests.shift(); + info(gCurrentTest.desc); + gCurrentTest.run(); + } + else { + finish(); + } +} + +let serverRoot = "http://example.com/browser/mobile/chrome/tests/"; +let baseURI = "http://mochi.test:8888/browser/mobile/chrome/tests/"; + +let chromeRoot = getRootDirectory(gTestPath); +messageManager.loadFrameScript(chromeRoot + "remote_head.js", true); +messageManager.loadFrameScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", true); diff --git a/mobile/android/chrome/tests/locales_list.sjs b/mobile/android/chrome/tests/locales_list.sjs new file mode 100644 index 000000000000..5843a82450c2 --- /dev/null +++ b/mobile/android/chrome/tests/locales_list.sjs @@ -0,0 +1,121 @@ +let fennecID = "{a23983c0-fd0e-11dc-95ff-0800200c9a66}"; +Components.utils.import("resource://gre/modules/Services.jsm"); + +function getLocale(aLocaleParams, aAppParams) { + let l = { + TARGETLOCALE: "test{IDNUMBER}", + NAME: "Test Locale {IDNUMBER}", + VERSION: "1.0", + INSTALL: "http://www.example.com/browser/mobile/chrome/tests/addons/browser_locale{IDNUMBER}.xpi", + TYPENUMBER: 5, + TYPENAME: "Language Pack (Application)", + IDNUMBER: "", + }; + let a = { + APPNAME: "Fennec", + MINVERSION: "4.0", MAXVERSION: "*", + APPID: fennecID + }; + + if (aLocaleParams) { + for (var entry in aLocaleParams) { + l[entry] = aLocaleParams[entry]; + } + } + + if (aAppParams) { + for (var entry in aAppParams) { + a[entry] = aAppParams[entry]; + } + } + + l.app = a; + return l; +} + +let appTemplate = "" + +"{APPNAME}" + + "{MINVERSION}" + + "{MAXVERSION}" + + "{APPID}" + +""; + +let template = ""+ + "{TARGETLOCALE}" + + "{NAME}"+ + "{TYPENAME}"+ + "langpack-{TARGETLOCALE}@firefox-mobile.mozilla.org"+ + "{VERSION}"+ + "Public"+ + "{APPS}"+ + "ALL"+ + "{INSTALL}\n" + + "title=TITLE\n" + + "continueIn=CONTINUEIN%S\n" + + "name=NAME\n" + + "choose=CHOOSE\n" + + "chooseLanguage=CHOOSELANGUAGE\n" + + "cancel=CANCEL\n" + + "continue=CONTINUE\n" + + "installing=INSTALLING%S\n" + + "installerror=INSTALLERROR\n" + + "loading=LOADING" + + ""+ +""; + +function handleRequest(request, response) { + + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/xml", false); + + response.write(""); + response.write(""); + + let locales = []; + let query = decodeURIComponent(request.queryString || "").split("="); + switch(query[0]) { + case "numvalid": + let numValid = parseInt(query[1]); + for (let i = 0; i < numValid; i++) { + locales.push( getLocale({IDNUMBER: i}) ); + } + break; + case "buildid": + locales.push( getLocale({IDNUMBER: 1, NAME: query[1]}) ); + break; + default : + locales.push( getLocale({IDNUMBER: 1}) ); + /* These locales should fail in the LocaleRepository */ + locales.push( getLocale({IDNUMBER: 1}) ); // no duplicate ids + locales.push( getLocale({IDNUMBER: 2, INSTALL: "INVALID_URL"}) ); + locales.push( getLocale({IDNUMBER: 3}, {APPID: "INVALID_ID"}) ); + locales.push( getLocale({IDNUMBER: 3}, {MAXVERSION: "0"}) ); + locales.push( getLocale({IDNUMBER: 4, TARGETLOCALE: ""}) ); + locales.push( getLocale({IDNUMBER: 5, NAME: ""}) ); + locales.push( getLocale({IDNUMBER: 6, VERSION: ""}) ); + locales.push( getLocale({IDNUMBER: 7, TYPENUMBER: ""}) ); + break; + } + + for(var i = 0; i < locales.length; i++) { + let t = template; + t = t.replace(/{TARGETLOCALE}/g, locales[i].TARGETLOCALE); + t = t.replace(/{NAME}/, locales[i].NAME); + t = t.replace(/{VERSION}/, locales[i].VERSION); + t = t.replace(/{INSTALL}/, locales[i].INSTALL); + t = t.replace(/{TYPENUMBER}/, locales[i].TYPENUMBER); + t = t.replace(/{TYPENAME}/, locales[i].TYPENAME); + t = t.replace(/{IDNUMBER}/g, locales[i].IDNUMBER) + + let a = appTemplate; + a = a.replace(/{APPNAME}/, locales[i].app.APPNAME); + a = a.replace(/{MINVERSION}/, locales[i].app.MINVERSION); + a = a.replace(/{MAXVERSION}/, locales[i].app.MAXVERSION); + a = a.replace(/{APPID}/, locales[i].app.APPID); + + t = t.replace(/{APPS}/, a); + response.write(t); + } + + response.write(""); +} diff --git a/mobile/android/chrome/tests/mock_autocomplete.json b/mobile/android/chrome/tests/mock_autocomplete.json new file mode 100644 index 000000000000..ef5e43d6fa55 --- /dev/null +++ b/mobile/android/chrome/tests/mock_autocomplete.json @@ -0,0 +1,22 @@ +{ + "version": 1, + "result": { + "searchResult": 4, + "RESULT_NOMATCH_ONGOING": 5, + "RESULT_SUCCESS_ONGOING": 6, + "searchString": "", + "defaultIndex": 0, + "errorDescription": "", + "matchCount": 5, + "RESULT_IGNORED": 1, + "RESULT_FAILURE": 2, + "RESULT_NOMATCH": 3, + "RESULT_SUCCESS": 4, + "data": [["http://example.com/a", "A", "favicon", "http://example.com/a/favicon.png"], + ["http://example.com/b", "B", "favicon", "http://example.com/b/favicon.png"], + ["http://example.com/c", "C", "favicon", "http://example.com/c/favicon.png"], + ["http://example.com/d", "D", "bookmark", "http://example.com/d/favicon.png"], + ["http://example.com/e", "E", "boolmark", "http://example.com/e/favicon.png"] + ] + } +} diff --git a/mobile/android/chrome/tests/remote_autocomplete.js b/mobile/android/chrome/tests/remote_autocomplete.js new file mode 100644 index 000000000000..991671aa6ef4 --- /dev/null +++ b/mobile/android/chrome/tests/remote_autocomplete.js @@ -0,0 +1,22 @@ +dump("====================== Content Script Loaded =======================\n"); + +let assistant = Content.formAssistant; + +AsyncTests.add("TestRemoteAutocomplete:Click", function(aMessage, aJson) { + let element = content.document.getElementById(aJson.id); + assistant.open(element); + return true; +}); + +AsyncTests.add("TestRemoteAutocomplete:Check", function(aMessage, aJson) { + let element = content.document.getElementById(aJson.id); + return element.value; +}); + +AsyncTests.add("TestRemoteAutocomplete:Reset", function(aMessage, aJson) { + gFocusManager.focusedElement = null; + let element = content.document.getElementById(aJson.id); + element.value = ""; + return true; +}); + diff --git a/mobile/android/chrome/tests/remote_contentpopup.js b/mobile/android/chrome/tests/remote_contentpopup.js new file mode 100644 index 000000000000..f2156d700c5d --- /dev/null +++ b/mobile/android/chrome/tests/remote_contentpopup.js @@ -0,0 +1,18 @@ +dump("====================== Content Script Loaded =======================\n"); + +let assistant = Content.formAssistant; + +AsyncTests.add("TestRemoteAutocomplete:Click", function(aMessage, aJson) { + let element = content.document.getElementById(aJson.id); + assistant.open(element); + assistant._executeDelayed(function(assistant) { + sendAsyncMessage("FormAssist:AutoComplete", assistant._getJSON()); + }); + return true; +}); + +AsyncTests.add("TestRemoteAutocomplete:Check", function(aMessage, aJson) { + let element = content.document.getElementById(aJson.id); + return element.value; +}); + diff --git a/mobile/android/chrome/tests/remote_focus.js b/mobile/android/chrome/tests/remote_focus.js new file mode 100644 index 000000000000..d3c4544c2715 --- /dev/null +++ b/mobile/android/chrome/tests/remote_focus.js @@ -0,0 +1,16 @@ +function focusReceived() { + sendAsyncMessage("Test:E10SFocusReceived"); +} + +function blurReceived() { + sendAsyncMessage("Test:E10SBlurReceived"); +} + +addEventListener("focus", focusReceived, true); +addEventListener("blur", blurReceived, true); + +addMessageListener("Test:E10SFocusTestFinished", function testFinished() { + removeEventListener("focus", focusReceived, true); + removeEventListener("blur", blurReceived, true); + removeMessageListener("Test:E10SFocusTestFinished", testFinished); +}); diff --git a/mobile/android/chrome/tests/remote_forms.js b/mobile/android/chrome/tests/remote_forms.js new file mode 100644 index 000000000000..36779f5ae150 --- /dev/null +++ b/mobile/android/chrome/tests/remote_forms.js @@ -0,0 +1,171 @@ +dump("====================== Content Script Loaded =======================\n"); + +let assistant = Content.formAssistant; + +// Copied from http://mxr.mozilla.org/mozilla-central/source/testing/mochitest/tests/SimpleTest/EventUtils.js +// except the netscape.security.PrivilegeManager.enablePrivilege call +function sendMouseEvent(aEvent, aTarget, aWindow) { + if (['click', 'mousedown', 'mouseup', 'mouseover', 'mouseout'].indexOf(aEvent.type) == -1) { + throw new Error("sendMouseEvent doesn't know about event type '" + aEvent.type + "'"); + } + + if (!aWindow) { + aWindow = window; + } + + if (!(aTarget instanceof Element)) { + aTarget = aWindow.document.getElementById(aTarget); + } + + let event = aWindow.document.createEvent('MouseEvent'); + + let typeArg = aEvent.type; + let canBubbleArg = true; + let cancelableArg = true; + let viewArg = aWindow; + let detailArg = aEvent.detail || (aEvent.type == 'click' || + aEvent.type == 'mousedown' || + aEvent.type == 'mouseup' ? 1 : 0); + let screenXArg = aEvent.screenX || 0; + let screenYArg = aEvent.screenY || 0; + let clientXArg = aEvent.clientX || 0; + let clientYArg = aEvent.clientY || 0; + let ctrlKeyArg = aEvent.ctrlKey || false; + let altKeyArg = aEvent.altKey || false; + let shiftKeyArg = aEvent.shiftKey || false; + let metaKeyArg = aEvent.metaKey || false; + let buttonArg = aEvent.button || 0; + let relatedTargetArg = aEvent.relatedTarget || null; + + event.initMouseEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg, + screenXArg, screenYArg, clientXArg, clientYArg, + ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg, + buttonArg, relatedTargetArg); + + aTarget.dispatchEvent(event); +} + +AsyncTests.add("Test:Click", function(aMessage, aJson) { + sendMouseEvent({type: "click"}, "root", content); + return assistant._open; +}); + +AsyncTests.add("Test:Focus", function(aMessage, aJson) { + let targetElement = content.document.querySelector(aJson.value); + targetElement.focus(); + assistant._executeDelayed(function() { + sendAsyncMessage(aMessage, { result: assistant._open }); + }); +}); + +AsyncTests.add("Test:FocusRedirect", function(aMessage, aJson) { + let element = content.document.querySelector(aJson.value); + element.addEventListener("focus", function(aEvent) { + element.removeEventListener("focus", arguments.callee, false); + content.document.getElementById("root").focus(); + }, false); + element.focus(); + + assistant._executeDelayed(function() { + sendAsyncMessage(aMessage, { result: assistant._open }); + }); +}); + +// It should be only 2 ways to open the FormAssistant, the first one is +// by manually synchronizing the focus to the form helper and the other +// one is by a user click on an authorized element +AsyncTests.add("Test:OpenUIWithSyncFocus", function(aMessage, aJson) { + let element = content.document.querySelector(aJson.value); + + assistant._open = false; + assitant.focusSync = true; + element.focus(); + assistant._executeDelayed(function() { + assistant.focusSync = false; + sendAsyncMessage(aMessage, { result: assistant._open }); + }); +}); + +AsyncTests.add("Test:Open", function(aMessage, aJson) { + let element = content.document.querySelector(aJson.value); + assistant._open = false; + return assistant.open(element); +}); + +AsyncTests.add("Test:OpenWithFocusRedirect", function(aMessage, aJson) { + let element = content.document.querySelector(aJson.value); + assistant._open = false; + assistant.focusSync = true; + assistant.open(element); + assistant._executeDelayed(function() { + assistant.focusSync = false; + sendAsyncMessage(aMessage, { result: assistant._open }); + }); +}); + +AsyncTests.add("Test:CanShowUI", function(aMessage, aJson) { + let element = content.document.querySelector(aJson.value); + element.disabled = aJson.disabled; + assistant._open = false; + let open = assistant.open(element); + element.disabled = false; + return open; +}); + +AsyncTests.add("Test:CanShowUISelect", function(aMessage, aJson) { + let select = content.document.getElementById("select"); + select.disabled = aJson.disabled; + + let element = content.document.querySelector(aJson.value); + assistant._open = false; + let open = assistant.open(element); + select.disabled = false; + return open; +}); + +AsyncTests.add("Test:Previous", function(aMessage, aJson) { + let targetElement = content.document.querySelector(aJson.value); + assistant.currentIndex--; + return (assistant.currentElement == targetElement); +}); + +AsyncTests.add("Test:Next", function(aMessage, aJson) { + let targetElement = content.document.querySelector(aJson.value); + assistant.currentIndex++; + return (assistant.currentElement == targetElement); +}); + +// ============= iframe navigation ================== +let iframe = null; +let iframeInputs = null; +AsyncTests.add("Test:Iframe", function(aMessage, aJson) { + iframe = content.document.createElement("iframe"); + iframe.setAttribute("src", "data:text/html;charset=utf-8,%3Ciframe%20src%3D%22data%3Atext/html%3Bcharset%3Dutf-8%2C%253Cinput%253E%253Cbr%253E%253Cinput%253E%250A%22%3E%3C/iframe%3E"); + iframe.setAttribute("width", "300"); + iframe.setAttribute("height", "100"); + + iframe.addEventListener("load", function() { + iframe.removeEventListener("load", arguments.callee, false); + iframeInputs = iframe.contentDocument + .querySelector("iframe").contentDocument + .getElementsByTagName("input"); + sendAsyncMessage(aMessage, { result: true }); + }, false); + + content.document.body.appendChild(iframe); +}); + +AsyncTests.add("Test:IframeOpen", function(aMessage, aJson) { + return assistant.open(iframeInputs[0]); +}); + +AsyncTests.add("Test:IframePrevious", function(aMessage, aJson) { + assistant.currentIndex--; + return (assistant.currentElement == iframeInputs[aJson.value]); +}); + +AsyncTests.add("Test:IframeNext", function(aMessage, aJson) { + assistant.currentIndex++; + return (assistant.currentElement == iframeInputs[aJson.value]); +}); + diff --git a/mobile/android/chrome/tests/remote_formsZoom.js b/mobile/android/chrome/tests/remote_formsZoom.js new file mode 100644 index 000000000000..74cffac37d9c --- /dev/null +++ b/mobile/android/chrome/tests/remote_formsZoom.js @@ -0,0 +1,9 @@ +dump("====================== Content Script Loaded =======================\n"); + +let assistant = Content.formAssistant; + +AsyncTests.add("FormAssist:Show", function(aMessage, aJson) { + let element = content.document.getElementById(aJson.id); + assistant.open(element); +}); + diff --git a/mobile/android/chrome/tests/remote_head.js b/mobile/android/chrome/tests/remote_head.js new file mode 100644 index 000000000000..157d2e7b8252 --- /dev/null +++ b/mobile/android/chrome/tests/remote_head.js @@ -0,0 +1,35 @@ +// XXX Those constants are here because EventUtils.js need them +window = content.document.defaultView.wrappedJSObject; +Element = Components.interfaces.nsIDOMElement; +netscape = window.netscape; + +let AsyncTests = { + _tests: [], + + add: function(aMessage, aCallback) { + addMessageListener(aMessage, this); + this._tests.push({ name: aMessage, callback: aCallback }); + }, + + receiveMessage: function(aMessage) { + let rv = { }; + let name = aMessage.name; + try { + let tests = this._tests; + for (let i = 0; i < tests.length; i++) { + if (tests[i].name == name) { + rv.result = tests[i].callback(name, aMessage.json); + break; + } + } + // Don't send test callback if rv.result == undefined, this allow to + // use a custom callback + if (rv.result != undefined) + sendAsyncMessage(name, rv); + } + catch(e) { + dump("receiveMessage: " + name + " - " + e + "\n"); + } + } +}; + diff --git a/mobile/android/chrome/tests/remote_vkb.js b/mobile/android/chrome/tests/remote_vkb.js new file mode 100644 index 000000000000..eb6b8283afc6 --- /dev/null +++ b/mobile/android/chrome/tests/remote_vkb.js @@ -0,0 +1,6 @@ +dump("====================== Content Script Loaded =======================\n"); + +AsyncTests.add("Test:FocusRoot", function(aMessage, aJson) { + content.document.getElementById("root").focus(); + return true; +}); diff --git a/mobile/android/components/AboutRedirector.js b/mobile/android/components/AboutRedirector.js new file mode 100644 index 000000000000..28f89b9ee9be --- /dev/null +++ b/mobile/android/components/AboutRedirector.js @@ -0,0 +1,163 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is About:FirstRun. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ryan Flint + * Justin Dolske + * Gavin Sharp + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +const Cc = Components.classes; +const Ci = Components.interfaces; + +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +let modules = { + // about:blank has some bad loading behavior we can avoid, if we use an alias + empty: { + uri: "about:blank", + privileged: false + }, + fennec: { + uri: "chrome://browser/content/about.xhtml", + privileged: true + }, + // about:firefox is an alias for about:fennec + get firefox() this.fennec, + + rights: { +#ifdef MOZ_OFFICIAL_BRANDING + uri: "chrome://browser/content/aboutRights.xhtml", +#else + uri: "chrome://global/content/aboutRights-unbranded.xhtml", +#endif + privileged: false + }, + blocked: { + uri: "chrome://browser/content/blockedSite.xhtml", + privileged: true + }, + certerror: { + uri: "chrome://browser/content/aboutCertError.xhtml", + privileged: true + }, + home: { + uri: "chrome://browser/content/aboutHome.xhtml", + privileged: true + } +} + +function AboutGeneric() {} +AboutGeneric.prototype = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]), + + _getModuleInfo: function (aURI) { + let moduleName = aURI.path.replace(/[?#].*/, "").toLowerCase(); + return modules[moduleName]; + }, + + // nsIAboutModule + getURIFlags: function(aURI) { + return Ci.nsIAboutModule.ALLOW_SCRIPT; + }, + + newChannel: function(aURI) { + let moduleInfo = this._getModuleInfo(aURI); + + var ios = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService); + + var channel = ios.newChannel(moduleInfo.uri, null, null); + + if (!moduleInfo.privileged) { + let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]. + getService(Ci.nsIScriptSecurityManager); + let principal = secMan.getCodebasePrincipal(aURI); + channel.owner = principal; + } + + channel.originalURI = aURI; + + return channel; + } +}; + +function AboutEmpty() {} +AboutEmpty.prototype = { + __proto__: AboutGeneric.prototype, + classID: Components.ID("{433d2d75-5923-49b0-854d-f37267b03dc7}") +} + +function AboutFennec() {} +AboutFennec.prototype = { + __proto__: AboutGeneric.prototype, + classID: Components.ID("{842a6d11-b369-4610-ba66-c3b5217e82be}") +} + +function AboutFirefox() {} +AboutFirefox.prototype = { + __proto__: AboutGeneric.prototype, + classID: Components.ID("{dd40c467-d206-4f22-9215-8fcc74c74e38}") +} + +function AboutRights() {} +AboutRights.prototype = { + __proto__: AboutGeneric.prototype, + classID: Components.ID("{3b988fbf-ec97-4e1c-a5e4-573d999edc9c}") +} + +function AboutCertError() {} +AboutCertError.prototype = { + __proto__: AboutGeneric.prototype, + classID: Components.ID("{972efe64-8ac0-4e91-bdb0-22835d987815}") +} + +function AboutHome() {} +AboutHome.prototype = { + __proto__: AboutGeneric.prototype, + classID: Components.ID("{b071364f-ab68-4669-a9db-33fca168271a}") +} + +function AboutDougt() {} +AboutDougt.prototype = { + __proto__: AboutGeneric.prototype, + classID: Components.ID("{7490b75b-0ed4-4b2f-b80c-75e7f9c75682}") +} + +function AboutBlocked() {} +AboutBlocked.prototype = { + __proto__: AboutGeneric.prototype, + classID: Components.ID("{88fd40b6-c5c2-4120-9238-f2cb9ff98928}") +} + +const components = [AboutEmpty, AboutFennec, AboutRights, + AboutCertError, AboutFirefox, AboutHome, AboutDougt, AboutBlocked]; +const NSGetFactory = XPCOMUtils.generateNSGetFactory(components); diff --git a/mobile/android/components/AddonUpdateService.js b/mobile/android/components/AddonUpdateService.js new file mode 100644 index 000000000000..62c15d9f6981 --- /dev/null +++ b/mobile/android/components/AddonUpdateService.js @@ -0,0 +1,234 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Add-on Update Service. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +XPCOMUtils.defineLazyGetter(this, "AddonManager", function() { + Components.utils.import("resource://gre/modules/AddonManager.jsm"); + return AddonManager; +}); + +XPCOMUtils.defineLazyGetter(this, "AddonRepository", function() { + Components.utils.import("resource://gre/modules/AddonRepository.jsm"); + return AddonRepository; +}); + +XPCOMUtils.defineLazyGetter(this, "NetUtil", function() { + Components.utils.import("resource://gre/modules/NetUtil.jsm"); + return NetUtil; +}); + + +function getPref(func, preference, defaultValue) { + try { + return Services.prefs[func](preference); + } + catch (e) {} + return defaultValue; +} + +// ----------------------------------------------------------------------- +// Add-on auto-update management service +// ----------------------------------------------------------------------- + +const PREF_ADDON_UPDATE_ENABLED = "extensions.autoupdate.enabled"; + +var gNeedsRestart = false; + +function AddonUpdateService() {} + +AddonUpdateService.prototype = { + classDescription: "Add-on auto-update management", + classID: Components.ID("{93c8824c-9b87-45ae-bc90-5b82a1e4d877}"), + + QueryInterface: XPCOMUtils.generateQI([Ci.nsITimerCallback]), + + notify: function aus_notify(aTimer) { + if (aTimer && !getPref("getBoolPref", PREF_ADDON_UPDATE_ENABLED, true)) + return; + + // If we already auto-upgraded and installed new versions, ignore this check + if (gNeedsRestart) + return; + + Services.io.offline = false; + + // Assume we are doing a periodic update check + let reason = AddonManager.UPDATE_WHEN_PERIODIC_UPDATE; + if (!aTimer) + reason = AddonManager.UPDATE_WHEN_USER_REQUESTED; + + AddonManager.getAddonsByTypes(null, function(aAddonList) { + aAddonList.forEach(function(aAddon) { + if (aAddon.permissions & AddonManager.PERM_CAN_UPGRADE) { + let data = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString); + data.data = JSON.stringify({ id: aAddon.id, name: aAddon.name }); + Services.obs.notifyObservers(data, "addon-update-started", null); + + let listener = new UpdateCheckListener(); + aAddon.findUpdates(listener, reason); + } + }); + }); + + RecommendedSearchResults.search(); + } +}; + +// ----------------------------------------------------------------------- +// Add-on update listener. Starts a download for any add-on with a viable +// update waiting +// ----------------------------------------------------------------------- + +function UpdateCheckListener() { + this._status = null; + this._version = null; +} + +UpdateCheckListener.prototype = { + onCompatibilityUpdateAvailable: function(aAddon) { + this._status = "compatibility"; + }, + + onUpdateAvailable: function(aAddon, aInstall) { + this._status = "update"; + this._version = aInstall.version; + aInstall.install(); + }, + + onNoUpdateAvailable: function(aAddon) { + if (!this._status) + this._status = "no-update"; + }, + + onUpdateFinished: function(aAddon, aError) { + let data = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString); + if (this._version) + data.data = JSON.stringify({ id: aAddon.id, name: aAddon.name, version: this._version }); + else + data.data = JSON.stringify({ id: aAddon.id, name: aAddon.name }); + + if (aError) + this._status = "error"; + + Services.obs.notifyObservers(data, "addon-update-ended", this._status); + } +}; + +// ----------------------------------------------------------------------- +// RecommendedSearchResults fetches add-on data and saves it to a cache +// ----------------------------------------------------------------------- + +var RecommendedSearchResults = { + _getFile: function() { + let dirService = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties); + let file = dirService.get("ProfD", Ci.nsILocalFile); + file.append("recommended-addons.json"); + return file; + }, + + _writeFile: function (aFile, aData) { + if (!aData) + return; + + // Initialize the file output stream. + let ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"].createInstance(Ci.nsIFileOutputStream); + ostream.init(aFile, 0x02 | 0x08 | 0x20, 0600, ostream.DEFER_OPEN); + + // Obtain a converter to convert our data to a UTF-8 encoded input stream. + let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter); + converter.charset = "UTF-8"; + + // Asynchronously copy the data to the file. + let istream = converter.convertToInputStream(aData); + NetUtil.asyncCopy(istream, ostream, function(rc) { + if (Components.isSuccessCode(rc)) + Services.obs.notifyObservers(null, "recommended-addons-cache-updated", ""); + }); + }, + + searchSucceeded: function(aAddons, aAddonCount, aTotalResults) { + let self = this; + + // Filter addons already installed + AddonManager.getAllAddons(function(aAllAddons) { + let addons = aAddons.filter(function(addon) { + for (let i = 0; i < aAllAddons.length; i++) + if (addon.id == aAllAddons[i].id) + return false; + + return true; + }); + + let json = { + addons: [] + }; + + // Avoid any NSS costs. Convert https to http. + addons.forEach(function(aAddon){ + json.addons.push({ + id: aAddon.id, + name: aAddon.name, + version: aAddon.version, + description: aAddon.description, + averageRating: aAddon.averageRating, + iconURL: aAddon.iconURL.replace(/^https/, "http") + }) + }); + + let file = self._getFile(); + self._writeFile(file, JSON.stringify(json)); + }); + }, + + searchFailed: function searchFailed() { }, + + search: function() { + const kAddonsMaxDisplay = 2; + + if (AddonRepository.isSearching) + AddonRepository.cancelSearch(); + AddonRepository.retrieveRecommendedAddons(kAddonsMaxDisplay, RecommendedSearchResults); + } +} + +const NSGetFactory = XPCOMUtils.generateNSGetFactory([AddonUpdateService]); + diff --git a/mobile/android/components/AlertsService.js b/mobile/android/components/AlertsService.js new file mode 100644 index 000000000000..c7f7d79661e3 --- /dev/null +++ b/mobile/android/components/AlertsService.js @@ -0,0 +1,59 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Alerts Service. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +// ----------------------------------------------------------------------- +// Alerts Service +// ----------------------------------------------------------------------- + +function AlertsService() { } + +AlertsService.prototype = { + classID: Components.ID("{fe33c107-82a4-41d6-8c64-5353267e04c9}"), + QueryInterface: XPCOMUtils.generateQI([Ci.nsIAlertsService]), + + showAlertNotification: function(aImageUrl, aTitle, aText, aTextClickable, aCookie, aAlertListener, aName) { + let browser = Services.wm.getMostRecentWindow("navigator:browser"); + browser.AlertsHelper.showAlertNotification(aImageUrl, aTitle, aText, aTextClickable, aCookie, aAlertListener); + } +}; + +const NSGetFactory = XPCOMUtils.generateNSGetFactory([AlertsService]); diff --git a/mobile/android/components/BlocklistPrompt.js b/mobile/android/components/BlocklistPrompt.js new file mode 100644 index 000000000000..87ec915a55c4 --- /dev/null +++ b/mobile/android/components/BlocklistPrompt.js @@ -0,0 +1,93 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Alerts Service. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Wes Johnston + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const Ci = Components.interfaces; +const Cu = Components.utils; +const Cc = Components.classes; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +// ----------------------------------------------------------------------- +// BlocklistPrompt Service +// ----------------------------------------------------------------------- + + +function BlocklistPrompt() { } + +BlocklistPrompt.prototype = { + prompt: function(aAddons, aCount) { + let win = Services.wm.getMostRecentWindow("navigator:browser"); + if (win.ExtensionsView.visible) { + win.ExtensionsView.showRestart("blocked"); + } else { + let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); + let notifyBox = win.getNotificationBox(); + let restartCallback = function(aNotification, aDescription) { + // Notify all windows that an application quit has been requested + var cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool); + Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart"); + + // If nothing aborted, quit the app + if (cancelQuit.data == false) { + let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup); + appStartup.quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit); + } + }; + + let buttons = [{accessKey: null, + label: bundle.GetStringFromName("notificationRestart.button"), + callback: restartCallback}]; + notifyBox.appendNotification(bundle.GetStringFromName("notificationRestart.blocked"), + "blocked-add-on", + "", + "PRIORITY_CRITICAL_HIGH", + buttons); + } + // Disable softblocked items automatically + for (let i = 0; i < aAddons.length; i++) { + if (aAddons[i].item instanceof Ci.nsIPluginTag) + addonList[i].item.disabled = true; + else + aAddons[i].item.userDisabled = true; + } + }, + classID: Components.ID("{4e6ea350-b09a-11df-94e2-0800200c9a66}"), + QueryInterface: XPCOMUtils.generateQI([Ci.nsIBlocklistPrompt]) +}; + +const NSGetFactory = XPCOMUtils.generateNSGetFactory([BlocklistPrompt]); + diff --git a/mobile/android/components/BrowserCLH.js b/mobile/android/components/BrowserCLH.js new file mode 100644 index 000000000000..40627dc4cd45 --- /dev/null +++ b/mobile/android/components/BrowserCLH.js @@ -0,0 +1,84 @@ +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + + +function dump(a) { + Cc["@mozilla.org/consoleservice;1"] + .getService(Ci.nsIConsoleService) + .logStringMessage(a); +} + +function openWindow(aParent, aURL, aTarget, aFeatures, aArgs) { + let argString = null; + if (aArgs && !(aArgs instanceof Ci.nsISupportsArray)) { + argString = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString); + argString.data = aArgs; + } + + return Services.ww.openWindow(aParent, aURL, aTarget, aFeatures, argString || aArgs); +} + + +function resolveURIInternal(aCmdLine, aArgument) { + let uri = aCmdLine.resolveURI(aArgument); + if (uri) + return uri; + + try { + let urifixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup); + uri = urifixup.createFixupURI(aArgument, 0); + } catch (e) { + Cu.reportError(e); + } + + return uri; +} + +function BrowserCLH() {} + +BrowserCLH.prototype = { + handle: function fs_handle(aCmdLine) { + let urlParam = "about:home"; + try { + urlParam = aCmdLine.handleFlagWithParam("remote", false); + } catch (e) { + // Optional so not a real error + } + dump("fs_handle: " + urlParam); + + try { + let uri = resolveURIInternal(aCmdLine, urlParam); + if (!uri) + return; + + let browserWin = Services.wm.getMostRecentWindow("navigator:browser"); + if (browserWin) { + browserWin.browserDOMWindow.openURI(uri, + null, + Ci.nsIBrowserDOMWindow.OPEN_CURRENTWINDOW, + Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL); + } else { + browserWin = openWindow(null, "chrome://browser/content/browser.xul", "_blank", "chrome,dialog=no,all", urlParam); + } + + aCmdLine.preventDefault = true; + } catch (x) { + Cc["@mozilla.org/consoleservice;1"] + .getService(Ci.nsIConsoleService) + .logStringMessage("fs_handle exception!: " + x); + } + }, + + // QI + QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]), + + // XPCOMUtils factory + classID: Components.ID("{be623d20-d305-11de-8a39-0800200c9a66}") +}; + +var components = [ BrowserCLH ]; +const NSGetFactory = XPCOMUtils.generateNSGetFactory(components); diff --git a/mobile/android/components/CapturePicker.js b/mobile/android/components/CapturePicker.js new file mode 100644 index 000000000000..78a283348121 --- /dev/null +++ b/mobile/android/components/CapturePicker.js @@ -0,0 +1,140 @@ +/* -*- Mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil; -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is CapturePicker.js + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Kyle Huey + * Fabrice Desré + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cr = Components.results; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +function CapturePicker() { + +} + +CapturePicker.prototype = { + _file: null, + _mode: -1, + _result: -1, + _shown: false, + _title: "", + _type: "", + _window: null, + _done: null, + + // + // nsICapturePicker + // + init: function(aWindow, aTitle, aMode) { + this._window = aWindow; + this._title = aTitle; + this._mode = aMode; + }, + + show: function() { + if (this._shown) + throw Cr.NS_ERROR_UNEXPECTED; + + this._shown = true; + this._file = null; + this._done = false; + + Services.obs.addObserver(this, "cameraCaptureDone", false); + + let msg = { gecko: { type: "onCameraCapture" } }; + Cc["@mozilla.org/android/bridge;1"].getService(Ci.nsIAndroidBridge).handleGeckoMessage(JSON.stringify(msg)); + + // we need to turn all the async messaging into a blocking call + while (!this._done) + Services.tm.currentThread.processNextEvent(true); + + if (this._res.ok) { + this._file = this._res.path; + // delete the file when exiting + let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); + file.initWithPath(this._res.path); + Cc["@mozilla.org/uriloader/external-helper-app-service;1"].getService(Ci.nsPIExternalAppLauncher).deleteTemporaryFileOnExit(file); + } + + return (this._res.ok ? Ci.nsICapturePicker.RETURN_OK : Ci.nsICapturePicker.RETURN_CANCEL); + }, + + observe: function(aObject, aTopic, aData) { + Services.obs.removeObserver(this, "cameraCaptureDone"); + this._done = true; + this._res = JSON.parse(aData); + }, + + modeMayBeAvailable: function(aMode) { + if (aMode != Ci.nsICapturePicker.MODE_STILL) + return false; + return true; + }, + + get file() { + if (this._file) { + let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); + file.initWithPath(this._file); + let utils = this._window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); + return utils.wrapDOMFile(file); + } else { + throw Cr.NS_ERROR_FAILURE; + } + }, + + get type() { + return this._type; + }, + + set type(aNewType) { + if (this._shown) + throw Cr.NS_ERROR_UNEXPECTED; + else + this._type = aNewType; + }, + + // QI + QueryInterface: XPCOMUtils.generateQI([Ci.nsICapturePicker, Ci.nsIObserver]), + + // XPCOMUtils factory + classID: Components.ID("{cb5a47f0-b58c-4fc3-b61a-358ee95f8238}"), +}; + +var components = [ CapturePicker ]; +const NSGetFactory = XPCOMUtils.generateNSGetFactory(components); diff --git a/mobile/android/components/ContentDispatchChooser.js b/mobile/android/components/ContentDispatchChooser.js new file mode 100644 index 000000000000..49025c69537a --- /dev/null +++ b/mobile/android/components/ContentDispatchChooser.js @@ -0,0 +1,70 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Content Dispatch Chooser. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +function ContentDispatchChooser() {} + +ContentDispatchChooser.prototype = +{ + classID: Components.ID("5a072a22-1e66-4100-afc1-07aed8b62fc5"), + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentDispatchChooser]), + + ask: function ask(aHandler, aWindowContext, aURI, aReason) { + let window = null; + try { + if (aWindowContext) + window = aWindowContext.getInterface(Ci.nsIDOMWindow); + } catch (e) { /* it's OK to not have a window */ } + + let bundle = Services.strings.createBundle("chrome://mozapps/locale/handling/handling.properties"); + + let title = bundle.GetStringFromName("protocol.title"); + let message = bundle.GetStringFromName("protocol.description"); + + let open = Services.prompt.confirm(window, title, message); + if (open) + aHandler.launchWithURI(aURI, aWindowContext); + } +}; + +const NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentDispatchChooser]); + diff --git a/mobile/android/components/ContentPermissionPrompt.js b/mobile/android/components/ContentPermissionPrompt.js new file mode 100644 index 000000000000..755447eb2a9b --- /dev/null +++ b/mobile/android/components/ContentPermissionPrompt.js @@ -0,0 +1,171 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Gian-Carlo Pascutto + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const Ci = Components.interfaces; +const Cr = Components.results; +const Cu = Components.utils; +const Cc = Components.classes; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +const kCountBeforeWeRemember = 5; + +function dump(a) { + Cc["@mozilla.org/consoleservice;1"] + .getService(Ci.nsIConsoleService) + .logStringMessage(a); +} + +function setPagePermission(type, uri, allow) { + let pm = Services.perms; + let contentPrefs = Services.contentPrefs; + let contentPrefName = type + ".request.remember"; + + if (!contentPrefs.hasPref(uri, contentPrefName)) + contentPrefs.setPref(uri, contentPrefName, 0); + + let count = contentPrefs.getPref(uri, contentPrefName); + + if (allow == false) + count--; + else + count++; + + contentPrefs.setPref(uri, contentPrefName, count); + if (count == kCountBeforeWeRemember) + pm.add(uri, type, Ci.nsIPermissionManager.ALLOW_ACTION); + else if (count == -kCountBeforeWeRemember) + pm.add(uri, type, Ci.nsIPermissionManager.DENY_ACTION); +} + +const kEntities = { "geolocation": "geolocation", "desktop-notification": "desktopNotification", + "indexedDB": "offlineApps", "indexedDBQuota": "indexedDBQuota", + "openWebappsManage": "openWebappsManage" }; + +function ContentPermissionPrompt() {} + +ContentPermissionPrompt.prototype = { + classID: Components.ID("{C6E8C44D-9F39-4AF7-BCC0-76E38A8310F5}"), + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]), + + handleExistingPermission: function handleExistingPermission(request) { + let result = Services.perms.testExactPermission(request.uri, request.type); + if (result == Ci.nsIPermissionManager.ALLOW_ACTION) { + request.allow(); + return true; + } + if (result == Ci.nsIPermissionManager.DENY_ACTION) { + request.cancel(); + return true; + } + return false; + }, + + getChromeWindow: function getChromeWindow(aWindow) { + let chromeWin = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow) + .QueryInterface(Ci.nsIDOMChromeWindow); + return chromeWin; + }, + + getChromeForRequest: function getChromeForRequest(request) { + if (request.window) { + let requestingWindow = request.window.top; + return this.getChromeWindow(requestingWindow).wrappedJSObject; + } + return request.element.ownerDocument.defaultView; + }, + + getTabForRequest: function getTabForRequest(request) { + let chromeWin = this.getChromeForRequest(request); + if (request.window) { + let browser = chromeWin.BrowserApp.getBrowserForWindow(request.window); + let tabID = chromeWin.BrowserApp.getTabForBrowser(browser).id; + return tabID; + } + // Fix this if e10s is needed again + return null; + }, + + prompt: function(request) { + // returns true if the request was handled + if (this.handleExistingPermission(request)) + return; + + let pm = Services.perms; + let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); + + let entityName = kEntities[request.type]; + + let tabID = this.getTabForRequest(request); + let chromeWin = this.getChromeForRequest(request); + + let buttons = [{ + label: browserBundle.GetStringFromName(entityName + ".allow"), + accessKey: null, + callback: function(notification) { + setPagePermission(request.type, request.uri, true); + request.allow(); + } + }, + { + label: browserBundle.GetStringFromName(entityName + ".dontAllow"), + accessKey: null, + callback: function(notification) { + setPagePermission(request.type, request.uri, false); + request.cancel(); + } + }]; + + let message = browserBundle.formatStringFromName(entityName + ".wantsTo", + [request.uri.host], 1); + + chromeWin.NativeWindow.doorhanger.show(message, + entityName + request.uri.host, + buttons, tabID); + } +}; + + +//module initialization +const NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentPermissionPrompt]); diff --git a/mobile/android/components/DirectoryProvider.js b/mobile/android/components/DirectoryProvider.js new file mode 100644 index 000000000000..684a83f12a83 --- /dev/null +++ b/mobile/android/components/DirectoryProvider.js @@ -0,0 +1,99 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is XPI Dialog Service. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const Cc = Components.classes; +const Ci = Components.interfaces; + +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +// ----------------------------------------------------------------------- +// Directory Provider for special browser folders and files +// ----------------------------------------------------------------------- + +const NS_APP_CACHE_PARENT_DIR = "cachePDir"; +const XRE_UPDATE_ROOT_DIR = "UpdRootD"; +const ENVVAR_UPDATE_DIR = "UPDATES_DIRECTORY"; + +function DirectoryProvider() {} + +DirectoryProvider.prototype = { + classID: Components.ID("{ef0f7a87-c1ee-45a8-8d67-26f586e46a4b}"), + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDirectoryServiceProvider]), + + getFile: function(prop, persistent) { + if (prop == NS_APP_CACHE_PARENT_DIR) { + let dirsvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties); + let profile = dirsvc.get("ProfD", Ci.nsIFile); + + let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2); + let device = sysInfo.get("device"); + switch (device) { +#ifdef MOZ_PLATFORM_MAEMO + case "Nokia N900": + return profile; + + case "Nokia N8xx": + let folder = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); + folder.initWithPath("/media/mmc2/.mozilla/fennec"); + return folder; +#endif + default: + return profile; + } + } else if (prop == XRE_UPDATE_ROOT_DIR) { + let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment); + if (env.exists(ENVVAR_UPDATE_DIR)) { + let path = env.get(ENVVAR_UPDATE_DIR); + if (path) { + let localFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); + localFile.initWithPath(path); + return localFile; + } + } + let dm = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager); + return dm.defaultDownloadsDirectory; + } + + // We are retuning null to show failure instead for throwing an error. The + // interface is called quite a bit and throwing an error is noisy. Returning + // null works with the way the interface is called [see bug 529077] + return null; + } +}; + +const NSGetFactory = XPCOMUtils.generateNSGetFactory([DirectoryProvider]); + diff --git a/mobile/android/components/DownloadManagerUI.js b/mobile/android/components/DownloadManagerUI.js new file mode 100644 index 000000000000..83dd8ab7dd7e --- /dev/null +++ b/mobile/android/components/DownloadManagerUI.js @@ -0,0 +1,78 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Download Manager UI. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const Ci = Components.interfaces; + +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); +Components.utils.import("resource://gre/modules/Services.jsm"); + +// ----------------------------------------------------------------------- +// Download Manager UI +// ----------------------------------------------------------------------- + +function DownloadManagerUI() { } + +DownloadManagerUI.prototype = { + classID: Components.ID("{93db15b1-b408-453e-9a2b-6619e168324a}"), + + show: function show(aWindowContext, aID, aReason) { + if (!aReason) + aReason = Ci.nsIDownloadManagerUI.REASON_USER_INTERACTED; + + let browser = Services.wm.getMostRecentWindow("navigator:browser"); + if (browser) + browser.showDownloadManager(aWindowContext, aID, aReason); + }, + + get visible() { + let browser = Services.wm.getMostRecentWindow("navigator:browser"); + if (browser) { + return browser.DownloadsView.visible; + } + return false; + }, + + getAttention: function getAttention() { + if (this.visible) + this.show(null, null, null); + else + throw Cr.NS_ERROR_UNEXPECTED; + }, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDownloadManagerUI]) +}; + +const NSGetFactory = XPCOMUtils.generateNSGetFactory([DownloadManagerUI]); diff --git a/mobile/android/components/FormAutoComplete.js b/mobile/android/components/FormAutoComplete.js new file mode 100644 index 000000000000..cf142b7bc7c2 --- /dev/null +++ b/mobile/android/components/FormAutoComplete.js @@ -0,0 +1,172 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Form Autocomplete Plus. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * Dan Mills + * Justin Dolske + * Michael Hanson + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +function LOG() { + return; // comment out for verbose debugging + let msg = Array.join(arguments, " "); + dump(msg + "\n"); + Cu.reportError(msg); +} + +// Lazily get the base Form AutoComplete Search +XPCOMUtils.defineLazyGetter(this, "FAC", function() { + return Components.classesByID["{c11c21b2-71c9-4f87-a0f8-5e13f50495fd}"] + .getService(Ci.nsIFormAutoComplete); +}); + +XPCOMUtils.defineLazyGetter(this, "Contacts", function() { + Cu.import("resource:///modules/contacts.jsm"); + return Contacts; +}); + +function FormAutoComplete() { + LOG("new FAC"); +} + +FormAutoComplete.prototype = { + classDescription: "Form AutoComplete Plus", + classID: Components.ID("{cccd414c-3ec2-4cc5-9dc4-36c87cc3c4fe}"), + QueryInterface: XPCOMUtils.generateQI([Ci.nsIFormAutoComplete]), + + // Specify the html5 types that we want and some values to guess + contactTypes: { + email: /^(?:.*(?:e-?mail|recipients?).*|(send_)?to(_b?cc)?)$/i, + tel: /^(?:tel(?:ephone)?|.*phone.*)$/i + }, + + checkQueryType: function checkQueryType(aName, aField) { + // If we have an input field with the desired html5 type, take it! + if (aField && "type" in aField) { + let type = aField.type; + if (type && type in this.contactTypes) + return type; + } + + // Grab properties to check for contact inputs + let props = [aName]; + if (aField) { + let specialProps = [aField["className"], aField["id"]]; + props = props.concat(specialProps.filter(function(aValue) { + return aValue; + })); + } + + // Check the gathered properties for contact-like values + for (let [type, regex] in Iterator(this.contactTypes)) { + if (props.some(function(prop) prop.search(regex) != -1)) + return type; + } + return null; + }, + + findContact: function findContact(aQuery, aType, aResult, aDupCheck) { + // Match the name and show the email for now.. + Contacts.find({ fullName: aQuery }).forEach(function(contact) { + // Might not have an email for some reason... ? + try { + LOG("findContact", "Contact " + contact.fullName); + + let suggestions; + switch (aType) { + case "email": + suggestions = contact.emails; + break; + case "tel": + suggestions = contact.phoneNumbers; + break; + default: + LOG("unknown type!", aType); + return; + } + + for each (let suggestion in suggestions) { + if (aDupCheck[suggestion]) + continue; + aDupCheck[suggestion] = true; + + let data = contact.fullName + " <" + suggestion + ">"; + aResult.appendMatch(suggestion, data, null, "contact"); + } + } + catch(ex) { + LOG("findContact error", ex); + } + }); + }, + + autoCompleteSearch: function autoCompleteSearch(aName, aQuery, aField, aPrev) { + if (!Services.prefs.getBoolPref("browser.formfill.enable")) + return null; + + LOG("autocomplete search", Array.slice(arguments)); + let result = Cc["@mozilla.org/autocomplete/simple-result;1"].createInstance(Ci.nsIAutoCompleteSimpleResult); + result.setSearchString(aQuery); + + // Don't allow duplicates get merged into the final results + let dupCheck = {}; + + // Use the base form autocomplete for non-contact searches + let normal = FAC.autoCompleteSearch(aName, aQuery, aField, aPrev); + if (normal.matchCount > 0) { + for (let i = 0; i < normal.matchCount; i++) { + dupCheck[normal.getValueAt(i)] = true; + result.appendMatch(normal.getValueAt(i), normal.getCommentAt(i), normal.getImageAt(i), normal.getStyleAt(i)); + } + } + + // Do searches for certain input fields + let type = this.checkQueryType(aName, aField); + if (type != null) + this.findContact(aQuery, type, result, dupCheck); + + let resultCode = result.matchCount ? "RESULT_SUCCESS" : "RESULT_NOMATCH"; + result.setSearchResult(Ci.nsIAutoCompleteResult[resultCode]); + return result; + } +}; + +let components = [FormAutoComplete]; +const NSGetFactory = XPCOMUtils.generateNSGetFactory(components); diff --git a/mobile/android/components/HelperAppDialog.js b/mobile/android/components/HelperAppDialog.js new file mode 100644 index 000000000000..c9f1ef0303ac --- /dev/null +++ b/mobile/android/components/HelperAppDialog.js @@ -0,0 +1,252 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is HelperApp Launcher Dialog. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; +const Cr = Components.results; + +const PREF_BD_USEDOWNLOADDIR = "browser.download.useDownloadDir"; +#ifdef ANDROID +const URI_GENERIC_ICON_DOWNLOAD = "drawable://alertdownloads"; +#else +const URI_GENERIC_ICON_DOWNLOAD = "chrome://browser/skin/images/alert-downloads-30.png"; +#endif + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +// ----------------------------------------------------------------------- +// HelperApp Launcher Dialog +// ----------------------------------------------------------------------- + +function HelperAppLauncherDialog() { } + +HelperAppLauncherDialog.prototype = { + classID: Components.ID("{e9d277a0-268a-4ec2-bb8c-10fdf3e44611}"), + QueryInterface: XPCOMUtils.generateQI([Ci.nsIHelperAppLauncherDialog]), + + show: function hald_show(aLauncher, aContext, aReason) { + // Check to see if we can open this file or not + if (aLauncher.MIMEInfo.hasDefaultHandler) { + aLauncher.MIMEInfo.preferredAction = Ci.nsIMIMEInfo.useSystemDefault; + aLauncher.launchWithApplication(null, false); + } else { + let wasClicked = false; + let listener = { + observe: function(aSubject, aTopic, aData) { + if (aTopic == "alertclickcallback") { + wasClicked = true; + let win = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator).getMostRecentWindow("navigator:browser"); + if (win) + win.BrowserUI.showPanel("downloads-container"); + + aLauncher.saveToDisk(null, false); + } else { + if (!wasClicked) + aLauncher.cancel(Cr.NS_BINDING_ABORTED); + } + } + }; + this._notify(aLauncher, listener); + } + }, + + promptForSaveToFile: function hald_promptForSaveToFile(aLauncher, aContext, aDefaultFile, aSuggestedFileExt, aForcePrompt) { + let file = null; + let prefs = Services.prefs; + + if (!aForcePrompt) { + // Check to see if the user wishes to auto save to the default download + // folder without prompting. Note that preference might not be set. + let autodownload = true; + try { + autodownload = prefs.getBoolPref(PREF_BD_USEDOWNLOADDIR); + } catch (e) { } + + if (autodownload) { + // Retrieve the user's default download directory + let dnldMgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager); + let defaultFolder = dnldMgr.userDownloadsDirectory; + + try { + file = this.validateLeafName(defaultFolder, aDefaultFile, aSuggestedFileExt); + } + catch (e) { + } + + // Check to make sure we have a valid directory, otherwise, prompt + if (file) + return file; + } + } + + // Use file picker to show dialog. + let picker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); + let windowTitle = ""; + let parent = aContext.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow); + picker.init(parent, windowTitle, Ci.nsIFilePicker.modeSave); + picker.defaultString = aDefaultFile; + + if (aSuggestedFileExt) { + // aSuggestedFileExtension includes the period, so strip it + picker.defaultExtension = aSuggestedFileExt.substring(1); + } + else { + try { + picker.defaultExtension = aLauncher.MIMEInfo.primaryExtension; + } + catch (e) { } + } + + var wildCardExtension = "*"; + if (aSuggestedFileExt) { + wildCardExtension += aSuggestedFileExt; + picker.appendFilter(aLauncher.MIMEInfo.description, wildCardExtension); + } + + picker.appendFilters(Ci.nsIFilePicker.filterAll); + + // Default to lastDir if it is valid, otherwise use the user's default + // downloads directory. userDownloadsDirectory should always return a + // valid directory, so we can safely default to it. + var dnldMgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager); + picker.displayDirectory = dnldMgr.userDownloadsDirectory; + + // The last directory preference may not exist, which will throw. + try { + let lastDir = prefs.getComplexValue("browser.download.lastDir", Ci.nsILocalFile); + if (isUsableDirectory(lastDir)) + picker.displayDirectory = lastDir; + } + catch (e) { } + + if (picker.show() == Ci.nsIFilePicker.returnCancel) { + // null result means user cancelled. + return null; + } + + // Be sure to save the directory the user chose through the Save As... + // dialog as the new browser.download.dir since the old one + // didn't exist. + file = picker.file; + + if (file) { + try { + // Remove the file so that it's not there when we ensure non-existence later; + // this is safe because for the file to exist, the user would have had to + // confirm that he wanted the file overwritten. + if (file.exists()) + file.remove(false); + } + catch (e) { } + var newDir = file.parent.QueryInterface(Ci.nsILocalFile); + prefs.setComplexValue("browser.download.lastDir", Ci.nsILocalFile, newDir); + file = this.validateLeafName(newDir, file.leafName, null); + } + return file; + }, + + validateLeafName: function hald_validateLeafName(aLocalFile, aLeafName, aFileExt) { + if (!(aLocalFile && this.isUsableDirectory(aLocalFile))) + return null; + + // Remove any leading periods, since we don't want to save hidden files + // automatically. + aLeafName = aLeafName.replace(/^\.+/, ""); + + if (aLeafName == "") + aLeafName = "unnamed" + (aFileExt ? "." + aFileExt : ""); + aLocalFile.append(aLeafName); + + this.makeFileUnique(aLocalFile); + return aLocalFile; + }, + + makeFileUnique: function hald_makeFileUnique(aLocalFile) { + try { + // Note - this code is identical to that in + // toolkit/content/contentAreaUtils.js. + // If you are updating this code, update that code too! We can't share code + // here since this is called in a js component. + var collisionCount = 0; + while (aLocalFile.exists()) { + collisionCount++; + if (collisionCount == 1) { + // Append "(2)" before the last dot in (or at the end of) the filename + // special case .ext.gz etc files so we don't wind up with .tar(2).gz + if (aLocalFile.leafName.match(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i)) + aLocalFile.leafName = aLocalFile.leafName.replace(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i, "(2)$&"); + else + aLocalFile.leafName = aLocalFile.leafName.replace(/(\.[^\.]*)?$/, "(2)$&"); + } + else { + // replace the last (n) in the filename with (n+1) + aLocalFile.leafName = aLocalFile.leafName.replace(/^(.*\()\d+\)/, "$1" + (collisionCount+1) + ")"); + } + } + aLocalFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0600); + } + catch (e) { + dump("*** exception in validateLeafName: " + e + "\n"); + + if (e.result == Cr.NS_ERROR_FILE_ACCESS_DENIED) + throw e; + + if (aLocalFile.leafName == "" || aLocalFile.isDirectory()) { + aLocalFile.append("unnamed"); + if (aLocalFile.exists()) + aLocalFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600); + } + } + }, + + isUsableDirectory: function hald_isUsableDirectory(aDirectory) { + return aDirectory.exists() && aDirectory.isDirectory() && aDirectory.isWritable(); + }, + + _notify: function hald_notify(aLauncher, aCallback) { + let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); + + let notifier = Cc[aCallback ? "@mozilla.org/alerts-service;1" : "@mozilla.org/toaster-alerts-service;1"].getService(Ci.nsIAlertsService); + notifier.showAlertNotification(URI_GENERIC_ICON_DOWNLOAD, + bundle.GetStringFromName("alertDownloads"), + bundle.GetStringFromName("alertCantOpenDownload"), + true, "", aCallback, "downloadopen-fail"); + } +}; + +const NSGetFactory = XPCOMUtils.generateNSGetFactory([HelperAppLauncherDialog]); diff --git a/mobile/android/components/LoginManagerPrompter.js b/mobile/android/components/LoginManagerPrompter.js new file mode 100644 index 000000000000..3928443744a5 --- /dev/null +++ b/mobile/android/components/LoginManagerPrompter.js @@ -0,0 +1,655 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Justin Dolske (original author) + * Ehsan Akhgari + * Wes Johnston + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cr = Components.results; + +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); +Components.utils.import("resource://gre/modules/Services.jsm"); + +/* ==================== LoginManagerPrompter ==================== */ +/* + * LoginManagerPrompter + * + * Implements interfaces for prompting the user to enter/save/change auth info. + * + * nsILoginManagerPrompter: Used by Login Manager for saving/changing logins + * found in HTML forms. + */ +function LoginManagerPrompter() { +} + +LoginManagerPrompter.prototype = { + + classID : Components.ID("97d12931-abe2-11df-94e2-0800200c9a66"), + QueryInterface : XPCOMUtils.generateQI([Ci.nsILoginManagerPrompter]), + + _factory : null, + _window : null, + _debug : false, // mirrors signon.debug + + __pwmgr : null, // Password Manager service + get _pwmgr() { + if (!this.__pwmgr) + this.__pwmgr = Cc["@mozilla.org/login-manager;1"]. + getService(Ci.nsILoginManager); + return this.__pwmgr; + }, + + __promptService : null, // Prompt service for user interaction + get _promptService() { + if (!this.__promptService) + this.__promptService = + Cc["@mozilla.org/embedcomp/prompt-service;1"]. + getService(Ci.nsIPromptService2); + return this.__promptService; + }, + + __strBundle : null, // String bundle for L10N + get _strBundle() { + if (!this.__strBundle) { + var bunService = Cc["@mozilla.org/intl/stringbundle;1"]. + getService(Ci.nsIStringBundleService); + this.__strBundle = bunService.createBundle( + "chrome://passwordmgr/locale/passwordmgr.properties"); + if (!this.__strBundle) + throw "String bundle for Login Manager not present!"; + } + + return this.__strBundle; + }, + + __brandBundle : null, // String bundle for L10N + get _brandBundle() { + if (!this.__brandBundle) { + var bunService = Cc["@mozilla.org/intl/stringbundle;1"]. + getService(Ci.nsIStringBundleService); + this.__brandBundle = bunService.createBundle( + "chrome://branding/locale/brand.properties"); + if (!this.__brandBundle) + throw "Branding string bundle not present!"; + } + + return this.__brandBundle; + }, + + + __ellipsis : null, + get _ellipsis() { + if (!this.__ellipsis) { + this.__ellipsis = "\u2026"; + try { + this.__ellipsis = Services.prefs.getComplexValue( + "intl.ellipsis", Ci.nsIPrefLocalizedString).data; + } catch (e) { } + } + return this.__ellipsis; + }, + + + /* + * log + * + * Internal function for logging debug messages to the Error Console window. + */ + log : function (message) { + if (!this._debug) + return; + + dump("Pwmgr Prompter: " + message + "\n"); + Services.console.logStringMessage("Pwmgr Prompter: " + message); + }, + + + /* ---------- nsILoginManagerPrompter prompts ---------- */ + + + + + /* + * init + * + */ + init : function (aWindow, aFactory) { + this._window = aWindow; + this._factory = aFactory || null; + + var prefBranch = Services.prefs.getBranch("signon."); + this._debug = prefBranch.getBoolPref("debug"); + this.log("===== initialized ====="); + }, + + + /* + * promptToSavePassword + * + */ + promptToSavePassword : function (aLogin) { + var nativeWindow = this._getNativeWindow(); + + if (nativeWindow) + this._showSaveLoginNotification(nativeWindow, aLogin); + else + this._showSaveLoginDialog(aLogin); + }, + + + /* + * _showLoginNotification + * + * Displays a notification doorhanger. + * + */ + _showLoginNotification : function (aNativeWindow, aName, aText, aButtons) { + this.log("Adding new " + aName + " notification bar"); + let notifyWin = this._window.top; + let chromeWin = this._getChromeWindow(notifyWin).wrappedJSObject; + let browser = chromeWin.BrowserApp.getBrowserForWindow(notifyWin); + let tabID = chromeWin.BrowserApp.getTabForBrowser(browser).id; + + // The page we're going to hasn't loaded yet, so we want to persist + // across the first location change. + + // Sites like Gmail perform a funky redirect dance before you end up + // at the post-authentication page. I don't see a good way to + // heuristically determine when to ignore such location changes, so + // we'll try ignoring location changes based on a time interval. + + let options = { + persistence: 1, + timeout: Date.now() + 20000 + } + + aNativeWindow.doorhanger.show(aText, aName, aButtons, tabID, options); + }, + + + /* + * _showSaveLoginNotification + * + * Displays a notification doorhanger (rather than a popup), to allow the user to + * save the specified login. This allows the user to see the results of + * their login, and only save a login which they know worked. + * + */ + _showSaveLoginNotification : function (aNativeWindow, aLogin) { + + // Ugh. We can't use the strings from the popup window, because they + // have the access key marked in the string (eg "Mo&zilla"), along + // with some weird rules for handling access keys that do not occur + // in the string, for L10N. See commonDialog.js's setLabelForNode(). + var neverButtonText = + this._getLocalizedString("notifyBarNeverForSiteButtonText"); + var neverButtonAccessKey = + this._getLocalizedString("notifyBarNeverForSiteButtonAccessKey"); + var rememberButtonText = + this._getLocalizedString("notifyBarRememberButtonText"); + var rememberButtonAccessKey = + this._getLocalizedString("notifyBarRememberButtonAccessKey"); + var notNowButtonText = + this._getLocalizedString("notifyBarNotNowButtonText"); + var notNowButtonAccessKey = + this._getLocalizedString("notifyBarNotNowButtonAccessKey"); + + var brandShortName = + this._brandBundle.GetStringFromName("brandShortName"); + var displayHost = this._getShortDisplayHost(aLogin.hostname); + var notificationText; + if (aLogin.username) { + var displayUser = this._sanitizeUsername(aLogin.username); + notificationText = this._getLocalizedString( + "saveLoginText", + [brandShortName, displayUser, displayHost]); + } else { + notificationText = this._getLocalizedString( + "saveLoginTextNoUsername", + [brandShortName, displayHost]); + } + + // The callbacks in |buttons| have a closure to access the variables + // in scope here; set one to |this._pwmgr| so we can get back to pwmgr + // without a getService() call. + var pwmgr = this._pwmgr; + + + var buttons = [ + // "Remember" button + { + label: rememberButtonText, + accessKey: rememberButtonAccessKey, + popup: null, + callback: function(aNotificationBar, aButton) { + pwmgr.addLogin(aLogin); + } + }, + + // "Never for this site" button + { + label: neverButtonText, + accessKey: neverButtonAccessKey, + popup: null, + callback: function(aNotificationBar, aButton) { + pwmgr.setLoginSavingEnabled(aLogin.hostname, false); + } + }, + + // "Not now" button + { + label: notNowButtonText, + accessKey: notNowButtonAccessKey, + popup: null, + callback: function() { /* NOP */ } + } + ]; + + this._showLoginNotification(aNativeWindow, "password-save", + notificationText, buttons); + }, + + + /* + * _showSaveLoginDialog + * + * Called when we detect a new login in a form submission, + * asks the user what to do. + * + */ + _showSaveLoginDialog : function (aLogin) { + const buttonFlags = Ci.nsIPrompt.BUTTON_POS_1_DEFAULT + + (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) + + (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_1) + + (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_2); + + var brandShortName = + this._brandBundle.GetStringFromName("brandShortName"); + var displayHost = this._getShortDisplayHost(aLogin.hostname); + + var dialogText; + if (aLogin.username) { + var displayUser = this._sanitizeUsername(aLogin.username); + dialogText = this._getLocalizedString( + "saveLoginText", + [brandShortName, displayUser, displayHost]); + } else { + dialogText = this._getLocalizedString( + "saveLoginTextNoUsername", + [brandShortName, displayHost]); + } + var dialogTitle = this._getLocalizedString( + "savePasswordTitle"); + var neverButtonText = this._getLocalizedString( + "promptNeverForSiteButtonText"); + var rememberButtonText = this._getLocalizedString( + "promptRememberButtonText"); + var notNowButtonText = this._getLocalizedString( + "promptNotNowButtonText"); + + this.log("Prompting user to save/ignore login"); + var userChoice = this._promptService.confirmEx(null, + dialogTitle, dialogText, + buttonFlags, rememberButtonText, + notNowButtonText, neverButtonText, + null, {}); + // Returns: + // 0 - Save the login + // 1 - Ignore the login this time + // 2 - Never save logins for this site + if (userChoice == 2) { + this.log("Disabling " + aLogin.hostname + " logins by request."); + this._pwmgr.setLoginSavingEnabled(aLogin.hostname, false); + } else if (userChoice == 0) { + this.log("Saving login for " + aLogin.hostname); + this._pwmgr.addLogin(aLogin); + } else { + // userChoice == 1 --> just ignore the login. + this.log("Ignoring login."); + } + }, + + /* + * promptToChangePassword + * + * Called when we think we detect a password change for an existing + * login, when the form being submitted contains multiple password + * fields. + * + */ + promptToChangePassword : function (aOldLogin, aNewLogin) { + var nativeWindow = this._getNativeWindow(); + + if (nativeWindow) + this._showChangeLoginNotification(nativeWindow, aOldLogin, aNewLogin.password); + else + this._showChangeLoginDialog(aOldLogin, aNewLogin.password); + }, + + /* + * _showChangeLoginNotification + * + * Shows the Change Password notification doorhanger. + * + */ + _showChangeLoginNotification : function (aNativeWindow, aOldLogin, aNewPassword) { + var notificationText; + if (aOldLogin.username) + notificationText = this._getLocalizedString( + "passwordChangeText", + [aOldLogin.username]); + else + notificationText = this._getLocalizedString( + "passwordChangeTextNoUser"); + + var changeButtonText = + this._getLocalizedString("notifyBarChangeButtonText"); + var changeButtonAccessKey = + this._getLocalizedString("notifyBarChangeButtonAccessKey"); + var dontChangeButtonText = + this._getLocalizedString("notifyBarDontChangeButtonText"); + var dontChangeButtonAccessKey = + this._getLocalizedString("notifyBarDontChangeButtonAccessKey"); + + // The callbacks in |buttons| have a closure to access the variables + // in scope here; set one to |this._pwmgr| so we can get back to pwmgr + // without a getService() call. + var self = this; + + var buttons = [ + // "Yes" button + { + label: changeButtonText, + accessKey: changeButtonAccessKey, + popup: null, + callback: function(aNotificationBar, aButton) { + self._updateLogin(aOldLogin, aNewPassword); + } + }, + + // "No" button + { + label: dontChangeButtonText, + accessKey: dontChangeButtonAccessKey, + popup: null, + callback: function(aNotificationBar, aButton) { + // do nothing + } + } + ]; + + this._showLoginNotification(aNativeWindow, "password-change", + notificationText, buttons); + }, + + /* + * _showChangeLoginDialog + * + * Shows the Change Password dialog. + * + */ + _showChangeLoginDialog : function (aOldLogin, aNewPassword) { + const buttonFlags = Ci.nsIPrompt.STD_YES_NO_BUTTONS; + + var dialogText; + if (aOldLogin.username) + dialogText = this._getLocalizedString( + "passwordChangeText", + [aOldLogin.username]); + else + dialogText = this._getLocalizedString( + "passwordChangeTextNoUser"); + + var dialogTitle = this._getLocalizedString( + "passwordChangeTitle"); + + // returns 0 for yes, 1 for no. + var ok = !this._promptService.confirmEx(null, + dialogTitle, dialogText, buttonFlags, + null, null, null, + null, {}); + if (ok) { + this.log("Updating password for user " + aOldLogin.username); + this._updateLogin(aOldLogin, aNewPassword); + } + }, + + + /* + * promptToChangePasswordWithUsernames + * + * Called when we detect a password change in a form submission, but we + * don't know which existing login (username) it's for. Asks the user + * to select a username and confirm the password change. + * + * Note: The caller doesn't know the username for aNewLogin, so this + * function fills in .username and .usernameField with the values + * from the login selected by the user. + * + * Note; XPCOM stupidity: |count| is just |logins.length|. + */ + promptToChangePasswordWithUsernames : function (logins, count, aNewLogin) { + const buttonFlags = Ci.nsIPrompt.STD_YES_NO_BUTTONS; + + var usernames = logins.map(function (l) l.username); + var dialogText = this._getLocalizedString("userSelectText"); + var dialogTitle = this._getLocalizedString("passwordChangeTitle"); + var selectedIndex = { value: null }; + + // If user selects ok, outparam.value is set to the index + // of the selected username. + var ok = this._promptService.select(null, + dialogTitle, dialogText, + usernames.length, usernames, + selectedIndex); + if (ok) { + // Now that we know which login to use, modify its password. + var selectedLogin = logins[selectedIndex.value]; + this.log("Updating password for user " + selectedLogin.username); + this._updateLogin(selectedLogin, aNewLogin.password); + } + }, + + + + + /* ---------- Internal Methods ---------- */ + + + + + /* + * _updateLogin + */ + _updateLogin : function (login, newPassword) { + var now = Date.now(); + var propBag = Cc["@mozilla.org/hash-property-bag;1"]. + createInstance(Ci.nsIWritablePropertyBag); + if (newPassword) { + propBag.setProperty("password", newPassword); + // Explicitly set the password change time here (even though it would + // be changed automatically), to ensure that it's exactly the same + // value as timeLastUsed. + propBag.setProperty("timePasswordChanged", now); + } + propBag.setProperty("timeLastUsed", now); + propBag.setProperty("timesUsedIncrement", 1); + this._pwmgr.modifyLogin(login, propBag); + }, + + /* + * _getChromeWindow + * + * Given a content DOM window, returns the chrome window it's in. + */ + _getChromeWindow: function (aWindow) { + var chromeWin = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell) + .chromeEventHandler.ownerDocument.defaultView; + return chromeWin; + }, + + /* + * _getNativeWindow + * + * Returns the NativeWindow to this prompter, or null if there isn't + * a NativeWindow available. + */ + _getNativeWindow : function () { + let nativeWindow = null; + try { + let notifyWin = this._window.top; + let chromeWin = this._getChromeWindow(notifyWin).wrappedJSObject; + if (chromeWin.NativeWindow) { + nativeWindow = chromeWin.NativeWindow; + } else { + this.log("NativeWindow not available on window"); + } + + } catch (e) { + // If any errors happen, just assume no native window helper. + this.log("No NativeWindow available: " + e) + } + return nativeWindow; + }, + + + /* + * _getLocalizedString + * + * Can be called as: + * _getLocalizedString("key1"); + * _getLocalizedString("key2", ["arg1"]); + * _getLocalizedString("key3", ["arg1", "arg2"]); + * (etc) + * + * Returns the localized string for the specified key, + * formatted if required. + * + */ + _getLocalizedString : function (key, formatArgs) { + if (formatArgs) + return this._strBundle.formatStringFromName( + key, formatArgs, formatArgs.length); + else + return this._strBundle.GetStringFromName(key); + }, + + + /* + * _sanitizeUsername + * + * Sanitizes the specified username, by stripping quotes and truncating if + * it's too long. This helps prevent an evil site from messing with the + * "save password?" prompt too much. + */ + _sanitizeUsername : function (username) { + if (username.length > 30) { + username = username.substring(0, 30); + username += this._ellipsis; + } + return username.replace(/['"]/g, ""); + }, + + + /* + * _getFormattedHostname + * + * The aURI parameter may either be a string uri, or an nsIURI instance. + * + * Returns the hostname to use in a nsILoginInfo object (for example, + * "http://example.com"). + */ + _getFormattedHostname : function (aURI) { + var uri; + if (aURI instanceof Ci.nsIURI) { + uri = aURI; + } else { + uri = Services.io.newURI(aURI, null, null); + } + var scheme = uri.scheme; + + var hostname = scheme + "://" + uri.host; + + // If the URI explicitly specified a port, only include it when + // it's not the default. (We never want "http://foo.com:80") + port = uri.port; + if (port != -1) { + var handler = Services.io.getProtocolHandler(scheme); + if (port != handler.defaultPort) + hostname += ":" + port; + } + + return hostname; + }, + + + /* + * _getShortDisplayHost + * + * Converts a login's hostname field (a URL) to a short string for + * prompting purposes. Eg, "http://foo.com" --> "foo.com", or + * "ftp://www.site.co.uk" --> "site.co.uk". + */ + _getShortDisplayHost: function (aURIString) { + var displayHost; + + var eTLDService = Cc["@mozilla.org/network/effective-tld-service;1"]. + getService(Ci.nsIEffectiveTLDService); + var idnService = Cc["@mozilla.org/network/idn-service;1"]. + getService(Ci.nsIIDNService); + try { + var uri = Services.io.newURI(aURIString, null, null); + var baseDomain = eTLDService.getBaseDomain(uri); + displayHost = idnService.convertToDisplayIDN(baseDomain, {}); + } catch (e) { + this.log("_getShortDisplayHost couldn't process " + aURIString); + } + + if (!displayHost) + displayHost = aURIString; + + return displayHost; + }, + +}; // end of LoginManagerPrompter implementation + + +var component = [LoginManagerPrompter]; +var NSGetFactory = XPCOMUtils.generateNSGetFactory(component); + diff --git a/mobile/android/components/Makefile.in b/mobile/android/components/Makefile.in new file mode 100644 index 000000000000..d444bb398fea --- /dev/null +++ b/mobile/android/components/Makefile.in @@ -0,0 +1,86 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla. +# +# The Initial Developer of the Original Code is +# the Mozilla Foundation . +# Portions created by the Initial Developer are Copyright (C) 2007 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Finkle +# Nino D'Aversa +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = MobileComponents +XPIDL_MODULE = MobileComponents + +# disabled (bug 696203) +#XPIDLSRCS = \ +# SessionStore.idl \ +# $(NULL) + +EXTRA_PP_COMPONENTS = \ + MobileComponents.manifest \ + AboutRedirector.js \ + BrowserCLH.js \ + DirectoryProvider.js\ + HelperAppDialog.js \ + Sidebar.js \ + $(NULL SessionStore.js is disabled - bug 696203) \ + $(NULL) + +EXTRA_COMPONENTS = \ + AlertsService.js \ + ContentPermissionPrompt.js \ + XPIDialogService.js \ + DownloadManagerUI.js \ + PromptService.js \ + ContentDispatchChooser.js \ + AddonUpdateService.js \ + FormAutoComplete.js \ + LoginManagerPrompter.js \ + BlocklistPrompt.js \ + CapturePicker.js \ + $(NULL) + +ifdef MOZ_SAFE_BROWSING +EXTRA_COMPONENTS += SafeBrowsing.js +endif + +ifdef MOZ_UPDATER +EXTRA_COMPONENTS += UpdatePrompt.js +endif + +include $(topsrcdir)/config/rules.mk diff --git a/mobile/android/components/MobileComponents.manifest b/mobile/android/components/MobileComponents.manifest new file mode 100644 index 000000000000..158c1eeca8c1 --- /dev/null +++ b/mobile/android/components/MobileComponents.manifest @@ -0,0 +1,115 @@ +# AboutRedirector.js +component {433d2d75-5923-49b0-854d-f37267b03dc7} AboutRedirector.js +contract @mozilla.org/network/protocol/about;1?what=empty {433d2d75-5923-49b0-854d-f37267b03dc7} +component {842a6d11-b369-4610-ba66-c3b5217e82be} AboutRedirector.js +contract @mozilla.org/network/protocol/about;1?what=fennec {842a6d11-b369-4610-ba66-c3b5217e82be} +component {dd40c467-d206-4f22-9215-8fcc74c74e38} AboutRedirector.js +contract @mozilla.org/network/protocol/about;1?what=firefox {dd40c467-d206-4f22-9215-8fcc74c74e38} +component {3b988fbf-ec97-4e1c-a5e4-573d999edc9c} AboutRedirector.js +contract @mozilla.org/network/protocol/about;1?what=rights {3b988fbf-ec97-4e1c-a5e4-573d999edc9c} +component {972efe64-8ac0-4e91-bdb0-22835d987815} AboutRedirector.js +contract @mozilla.org/network/protocol/about;1?what=certerror {972efe64-8ac0-4e91-bdb0-22835d987815} +component {b071364f-ab68-4669-a9db-33fca168271a} AboutRedirector.js +contract @mozilla.org/network/protocol/about;1?what=home {b071364f-ab68-4669-a9db-33fca168271a} +component {7490b75b-0ed4-4b2f-b80c-75e7f9c75682} AboutRedirector.js +contract @mozilla.org/network/protocol/about;1?what=dougt {7490b75b-0ed4-4b2f-b80c-75e7f9c75682} +#ifdef MOZ_SAFE_BROWSING +component {88fd40b6-c5c2-4120-9238-f2cb9ff98928} AboutRedirector.js +contract @mozilla.org/network/protocol/about;1?what=blocked {88fd40b6-c5c2-4120-9238-f2cb9ff98928} +#endif + +# DirectoryProvider.js +component {ef0f7a87-c1ee-45a8-8d67-26f586e46a4b} DirectoryProvider.js +contract @mozilla.org/browser/directory-provider;1 {ef0f7a87-c1ee-45a8-8d67-26f586e46a4b} +category xpcom-directory-providers browser-directory-provider @mozilla.org/browser/directory-provider;1 + +# Sidebar.js +component {22117140-9c6e-11d3-aaf1-00805f8a4905} Sidebar.js +contract @mozilla.org/sidebar;1 {22117140-9c6e-11d3-aaf1-00805f8a4905} +category JavaScript-global-property sidebar @mozilla.org/sidebar;1 +category JavaScript-global-property external @mozilla.org/sidebar;1 +category wakeup-request Sidebar @mozilla.org/sidebar;1,nsISidebarExternal,getService,Sidebar:AddSearchProvider + +# SessionStore.js - temporarily disabled (bug 696203) +# component {8c1f07d6-cba3-4226-a315-8bd43d67d032} SessionStore.js +# contract @mozilla.org/browser/sessionstore;1 {8c1f07d6-cba3-4226-a315-8bd43d67d032} +# category app-startup SessionStore service,@mozilla.org/browser/sessionstore;1 + +# stylesheets +category agent-style-sheets browser-content-stylesheet chrome://browser/skin/content.css +category agent-style-sheets browser-cursor-stylesheet chrome://browser/content/cursor.css + +# ContentPermissionPrompt.js +component {C6E8C44D-9F39-4AF7-BCC0-76E38A8310F5} ContentPermissionPrompt.js +contract @mozilla.org/content-permission/prompt;1 {C6E8C44D-9F39-4AF7-BCC0-76E38A8310F5} + +# AlertsService.js +component {fe33c107-82a4-41d6-8c64-5353267e04c9} AlertsService.js +#ifndef ANDROID +contract @mozilla.org/system-alerts-service;1 {fe33c107-82a4-41d6-8c64-5353267e04c9} +#endif + +# ToasterAlertsService : alias to AlertsService +contract @mozilla.org/toaster-alerts-service;1 {fe33c107-82a4-41d6-8c64-5353267e04c9} + +# XPIDialogService.js +component {c1242012-27d8-477e-a0f1-0b098ffc329b} XPIDialogService.js +contract @mozilla.org/addons/web-install-prompt;1 {c1242012-27d8-477e-a0f1-0b098ffc329b} + +# DownloadManagerUI.js +component {93db15b1-b408-453e-9a2b-6619e168324a} DownloadManagerUI.js +contract @mozilla.org/download-manager-ui;1 {93db15b1-b408-453e-9a2b-6619e168324a} + +# HelperAppDialog.js +component {e9d277a0-268a-4ec2-bb8c-10fdf3e44611} HelperAppDialog.js +contract @mozilla.org/helperapplauncherdialog;1 {e9d277a0-268a-4ec2-bb8c-10fdf3e44611} + +# PromptService.js +component {9a61149b-2276-4a0a-b79c-be994ad106cf} PromptService.js +contract @mozilla.org/prompter;1 {9a61149b-2276-4a0a-b79c-be994ad106cf} +contract @mozilla.org/embedcomp/prompt-service;1 {9a61149b-2276-4a0a-b79c-be994ad106cf} +category wakeup-request PromptService @mozilla.org/embedcomp/prompt-service;1,nsIPromptService,getService,Prompt:Call + +# BrowserCLH.js +component {be623d20-d305-11de-8a39-0800200c9a66} BrowserCLH.js application={aa3c5121-dab2-40e2-81ca-7ea25febc110} +contract @mozilla.org/browser/browser-clh;1 {be623d20-d305-11de-8a39-0800200c9a66} +category command-line-handler x-browser @mozilla.org/browser/browser-clh;1 + +# ContentDispatchChooser.js +component {5a072a22-1e66-4100-afc1-07aed8b62fc5} ContentDispatchChooser.js +contract @mozilla.org/content-dispatch-chooser;1 {5a072a22-1e66-4100-afc1-07aed8b62fc5} + +# AddonUpdateService.js +component {93c8824c-9b87-45ae-bc90-5b82a1e4d877} AddonUpdateService.js +contract @mozilla.org/browser/addon-update-service;1 {93c8824c-9b87-45ae-bc90-5b82a1e4d877} +category update-timer AddonUpdateService @mozilla.org/browser/addon-update-service;1,getService,auto-addon-background-update-timer,extensions.autoupdate.interval,86400 + +# FormAutoComplete.js +component {cccd414c-3ec2-4cc5-9dc4-36c87cc3c4fe} FormAutoComplete.js +contract @mozilla.org/satchel/form-autocomplete;1 {cccd414c-3ec2-4cc5-9dc4-36c87cc3c4fe} + +# LoginManagerPrompter.js +component {97d12931-abe2-11df-94e2-0800200c9a66} LoginManagerPrompter.js +contract @mozilla.org/login-manager/prompter;1 {97d12931-abe2-11df-94e2-0800200c9a66} + +# BlocklistPrompt.js +component {4e6ea350-b09a-11df-94e2-0800200c9a66} BlocklistPrompt.js +contract @mozilla.org/addons/blocklist-prompt;1 {4e6ea350-b09a-11df-94e2-0800200c9a66} + +#ifdef MOZ_SAFE_BROWSING +# SafeBrowsing.js +component {aadaed90-6c03-42d0-924a-fc61198ff283} SafeBrowsing.js +contract @mozilla.org/safebrowsing/application;1 {aadaed90-6c03-42d0-924a-fc61198ff283} +category app-startup SafeBrowsing service,@mozilla.org/safebrowsing/application;1 +#endif + +#ifdef MOZ_UPDATER +# UpdatePrompt.js +component {88b3eb21-d072-4e3b-886d-f89d8c49fe59} UpdatePrompt.js +contract @mozilla.org/updates/update-prompt;1 {88b3eb21-d072-4e3b-886d-f89d8c49fe59} +#endif + +# CapturePicker.js +component {cb5a47f0-b58c-4fc3-b61a-358ee95f8238} CapturePicker.js +contract @mozilla.org/capturepicker;1 {cb5a47f0-b58c-4fc3-b61a-358ee95f8238} + diff --git a/mobile/android/components/PromptService.js b/mobile/android/components/PromptService.js new file mode 100644 index 000000000000..6f261f272843 --- /dev/null +++ b/mobile/android/components/PromptService.js @@ -0,0 +1,798 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Fabrice Desré , Original author + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +const Ci = Components.interfaces; +const Cc = Components.classes; +const Cr = Components.results; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +// Whitelist of methods we remote - to check against malicious data. +// For example, it would be dangerous to allow content to show auth prompts. +const REMOTABLE_METHODS = { + alert: { outParams: [] }, + alertCheck: { outParams: [4] }, + confirm: { outParams: [] }, + prompt: { outParams: [3, 5] }, + confirmEx: { outParams: [8] }, + confirmCheck: { outParams: [4] }, + select: { outParams: [5] } +}; + +var gPromptService = null; + +function PromptService() { + gPromptService = this; +} + +PromptService.prototype = { + classID: Components.ID("{9a61149b-2276-4a0a-b79c-be994ad106cf}"), + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPromptFactory, Ci.nsIPromptService, Ci.nsIPromptService2]), + + /* ---------- nsIPromptFactory ---------- */ + // XXX Copied from nsPrompter.js. + getPrompt: function getPrompt(domWin, iid) { + let doc = this.getDocument(); + if (!doc) { + let fallback = this._getFallbackService(); + return fallback.getPrompt(domWin, iid); + } + + let p = new Prompt(domWin, doc); + p.QueryInterface(iid); + return p; + }, + + /* ---------- private memebers ---------- */ + + _getFallbackService: function _getFallbackService() { + return Components.classesByID["{7ad1b327-6dfa-46ec-9234-f2a620ea7e00}"] + .getService(Ci.nsIPromptService); + }, + + getDocument: function getDocument() { + let win = Services.wm.getMostRecentWindow("navigator:browser"); + return win ? win.document : null; + }, + + // nsIPromptService and nsIPromptService2 methods proxy to our Prompt class + // if we can show in-document popups, or to the fallback service otherwise. + callProxy: function(aMethod, aArguments) { + let prompt; + let doc = this.getDocument(); + if (!doc) { + let fallback = this._getFallbackService(); + return fallback[aMethod].apply(fallback, aArguments); + } + let domWin = aArguments[0]; + prompt = new Prompt(domWin, doc); + return prompt[aMethod].apply(prompt, Array.prototype.slice.call(aArguments, 1)); + }, + + /* ---------- nsIPromptService ---------- */ + + alert: function() { + return this.callProxy("alert", arguments); + }, + alertCheck: function() { + return this.callProxy("alertCheck", arguments); + }, + confirm: function() { + return this.callProxy("confirm", arguments); + }, + confirmCheck: function() { + return this.callProxy("confirmCheck", arguments); + }, + confirmEx: function() { + return this.callProxy("confirmEx", arguments); + }, + prompt: function() { + return this.callProxy("prompt", arguments); + }, + promptUsernameAndPassword: function() { + return this.callProxy("promptUsernameAndPassword", arguments); + }, + promptPassword: function() { + return this.callProxy("promptPassword", arguments); + }, + select: function() { + return this.callProxy("select", arguments); + }, + + /* ---------- nsIPromptService2 ---------- */ + promptAuth: function() { + return this.callProxy("promptAuth", arguments); + }, + asyncPromptAuth: function() { + return this.callProxy("asyncPromptAuth", arguments); + } +}; + +function Prompt(aDomWin, aDocument) { + this._domWin = aDomWin; + this._doc = aDocument; +} + +Prompt.prototype = { + _domWin: null, + _doc: null, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt, Ci.nsIAuthPrompt, Ci.nsIAuthPrompt2]), + + /* ---------- internal methods ---------- */ + commonPrompt: function commonPrompt(aTitle, aText, aButtons, aCheckMsg, aCheckState, aInputs) { + if (aCheckMsg) + aInputs.push({ type: "checkbox", label: aCheckMsg, checked: aCheckState.value }); + + let msg = { type: "Prompt:Show" }; + if (aTitle) msg.title = aTitle; + if (aText) msg.text = aText; + msg.buttons = aButtons || [ + { label: PromptUtils.getLocaleString("OK") }, + { label: PromptUtils.getLocaleString("Cancel") } + ]; + msg.inputs = aInputs; + return PromptUtils.sendMessageToJava(msg); + }, + + // + // Copied from chrome://global/content/commonDialog.js + // + setLabelForNode: function setLabelForNode(aNode, aLabel) { + // This is for labels which may contain embedded access keys. + // If we end in (&X) where X represents the access key, optionally preceded + // by spaces and/or followed by the ':' character, store the access key and + // remove the access key placeholder + leading spaces from the label. + // Otherwise a character preceded by one but not two &s is the access key. + // Store it and remove the &. + + // Note that if you change the following code, see the comment of + // nsTextBoxFrame::UpdateAccessTitle. + + if (!aLabel) + return; + + var accessKey = null; + if (/ *\(\&([^&])\)(:)?$/.test(aLabel)) { + aLabel = RegExp.leftContext + RegExp.$2; + accessKey = RegExp.$1; + } else if (/^(.*[^&])?\&(([^&]).*$)/.test(aLabel)) { + aLabel = RegExp.$1 + RegExp.$2; + accessKey = RegExp.$3; + } + + // && is the magic sequence to embed an & in your label. + aLabel = aLabel.replace(/\&\&/g, "&"); + if (aNode instanceof Ci.nsIDOMXULLabelElement) { + aNode.setAttribute("value", aLabel); + } else if (aNode instanceof Ci.nsIDOMXULDescriptionElement) { + let text = aNode.ownerDocument.createTextNode(aLabel); + aNode.appendChild(text); + } else { // Set text for other xul elements + aNode.setAttribute("label", aLabel); + } + + // XXXjag bug 325251 + // Need to set this after aNode.setAttribute("value", aLabel); + if (accessKey) + aNode.setAttribute("accesskey", accessKey); + }, + + /* + * ---------- interface disambiguation ---------- + * + * XXX Copied from nsPrompter.js. + * + * nsIPrompt and nsIAuthPrompt share 3 method names with slightly + * different arguments. All but prompt() have the same number of + * arguments, so look at the arg types to figure out how we're being + * called. :-( + */ + prompt: function prompt() { + if (gPromptService.inContentProcess) + return gPromptService.callProxy("prompt", [null].concat(Array.prototype.slice.call(arguments))); + + // also, the nsIPrompt flavor has 5 args instead of 6. + if (typeof arguments[2] == "object") + return this.nsIPrompt_prompt.apply(this, arguments); + else + return this.nsIAuthPrompt_prompt.apply(this, arguments); + }, + + promptUsernameAndPassword: function promptUsernameAndPassword() { + // Both have 6 args, so use types. + if (typeof arguments[2] == "object") + return this.nsIPrompt_promptUsernameAndPassword.apply(this, arguments); + else + return this.nsIAuthPrompt_promptUsernameAndPassword.apply(this, arguments); + }, + + promptPassword: function promptPassword() { + // Both have 5 args, so use types. + if (typeof arguments[2] == "object") + return this.nsIPrompt_promptPassword.apply(this, arguments); + else + return this.nsIAuthPrompt_promptPassword.apply(this, arguments); + }, + + /* ---------- nsIPrompt ---------- */ + + alert: function alert(aTitle, aText) { + this.commonPrompt(aTitle, aText, [{ label: PromptUtils.getLocaleString("OK") }], "", {value: false}, []); + }, + + alertCheck: function alertCheck(aTitle, aText, aCheckMsg, aCheckState) { + let data = this.commonPrompt(aTitle, aText, [{ label: PromptUtils.getLocaleString("OK") }], aCheckMsg, aCheckState, []); + if (aCheckMsg) + aCheckState.value = data.checkbox == "true"; + }, + + confirm: function confirm(aTitle, aText) { + let data = this.commonPrompt(aTitle, aText, null, "", {value: false}, []); + return (data.button == 0); + }, + + confirmCheck: function confirmCheck(aTitle, aText, aCheckMsg, aCheckState) { + let data = this.commonPrompt(aTitle, aText, null, aCheckMsg, aCheckState, []); + if (aCheckMsg) + aCheckState.value = data.checkbox == "true"; + return (data.button == 0); + }, + + confirmEx: function confirmEx(aTitle, aText, aButtonFlags, aButton0, + aButton1, aButton2, aCheckMsg, aCheckState) { + let buttons = []; + let titles = [aButton0, aButton1, aButton2]; + for (let i = 0; i < 3; i++) { + let bTitle = null; + switch (aButtonFlags & 0xff) { + case Ci.nsIPromptService.BUTTON_TITLE_OK : + bTitle = PromptUtils.getLocaleString("OK"); + break; + case Ci.nsIPromptService.BUTTON_TITLE_CANCEL : + bTitle = PromptUtils.getLocaleString("Cancel"); + break; + case Ci.nsIPromptService.BUTTON_TITLE_YES : + bTitle = PromptUtils.getLocaleString("Yes"); + break; + case Ci.nsIPromptService.BUTTON_TITLE_NO : + bTitle = PromptUtils.getLocaleString("No"); + break; + case Ci.nsIPromptService.BUTTON_TITLE_SAVE : + bTitle = PromptUtils.getLocaleString("Save"); + break; + case Ci.nsIPromptService.BUTTON_TITLE_DONT_SAVE : + bTitle = PromptUtils.getLocaleString("DontSave"); + break; + case Ci.nsIPromptService.BUTTON_TITLE_REVERT : + bTitle = PromptUtils.getLocaleString("Revert"); + break; + case Ci.nsIPromptService.BUTTON_TITLE_IS_STRING : + bTitle = titles[i]; + break; + } + + if (bTitle) + buttons.push({label:bTitle}); + + aButtonFlags >>= 8; + } + + let data = this.commonPrompt(aTitle, aText, buttons, aCheckMsg, aCheckState, []); + aCheckState.value = data.checkbox == "true"; + return data.button; + }, + + nsIPrompt_prompt: function nsIPrompt_prompt(aTitle, aText, aValue, aCheckMsg, aCheckState) { + let inputs = [{ type: "textbox", value: aValue.value }]; + let data = this.commonPrompt(aTitle, aText, null, aCheckMsg, aCheckState, inputs); + + if (aCheckMsg) + aCheckState.value = data.checkbox == "true"; + if (data.textbox) + aValue.value = data.textbox; + return (data.button == 0); + }, + + nsIPrompt_promptPassword: function nsIPrompt_promptPassword( + aTitle, aText, aPassword, aCheckMsg, aCheckState) { + let inputs = [{ type: "password", hint: "Password", value: aPassword.value }]; + let data = this.commonPrompt(aTitle, aText, null, aCheckMsg, aCheckState, inputs); + + if (aCheckMsg) + aCheckState.value = data.checkbox == "true"; + if (data.password) + aPassword.value = data.password; + return (data.button == 0); + }, + + nsIPrompt_promptUsernameAndPassword: function nsIPrompt_promptUsernameAndPassword( + aTitle, aText, aUsername, aPassword, aCheckMsg, aCheckState) { + let inputs = [{ type: "textbox", hint: PromptUtils.getLocaleString("username", "passwdmgr"), value: aUsername.value }, + { type: "password", hint: PromptUtils.getLocaleString("password", "passwdmgr"), value: aPassword.value }]; + let data = this.commonPrompt(aTitle, aText, null, aCheckMsg, aCheckState, inputs); + if (aCheckMsg) + aCheckState.value = data.checkbox == "true"; + if (data.textbox) + aUsername.value = data.textbox; + if (data.password) + aPassword.value = data.password; + return (data.button == 0); + }, + + select: function select(aTitle, aText, aCount, aSelectList, aOutSelection) { + let data = this.commonPrompt(aTitle, aText, [ + { label: PromptUtils.getLocaleString("OK") } + ], "", {value: false}, [ + { type: "menulist", values: aSelectList }, + ]); + if (data.menulist) + aOutSelection.value = data.menulist; + return (data.button == 0); + }, + + /* ---------- nsIAuthPrompt ---------- */ + + nsIAuthPrompt_prompt : function (title, text, passwordRealm, savePassword, defaultText, result) { + // TODO: Port functions from nsLoginManagerPrompter.js to here + if (defaultText) + result.value = defaultText; + return this.nsIPrompt_prompt(title, text, result, null, {}); + }, + + nsIAuthPrompt_promptUsernameAndPassword : function (aTitle, aText, aPasswordRealm, aSavePassword, aUser, aPass) { + return nsIAuthPrompt_loginPrompt(aTitle, aText, aPasswordRealm, aSavePassword, aUser, aPass); + }, + + nsIAuthPrompt_promptPassword : function (aTitle, aText, aPasswordRealm, aSavePassword, aPass) { + return nsIAuthPrompt_loginPrompt(aTitle, aText, aPasswordRealm, aSavePassword, null, aPass); + }, + + nsIAuthPrompt_loginPrompt: function(aTitle, aPasswordRealm, aSavePassword, aUser, aPass) { + let checkMsg = null; + let check = { value: false }; + let [hostname, realm, aUser] = PromptUtils.getHostnameAndRealm(aPasswordRealm); + + let canSave = PromptUtils.canSaveLogin(hostname, aSavePassword); + if (canSave) { + // Look for existing logins. + let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, realm); + [checkMsg, check] = PromptUtils.getUsernameAndPassword(foundLogins, aUser, aPass); + } + + let ok = false; + if (aUser) + ok = this.nsIPrompt_promptUsernameAndPassword(aTitle, aText, aUser, aPass, checkMsg, check); + else + ok = this.nsIPrompt_promptPassword(aTitle, aText, aPass, checkMsg, check); + + if (ok && canSave && check.value) + PromptUtils.savePassword(hostname, realm, aUser, aPass); + + return ok; }, + + /* ---------- nsIAuthPrompt2 ---------- */ + + promptAuth: function promptAuth(aChannel, aLevel, aAuthInfo) { + let checkMsg = null; + let check = { value: false }; + let message = PromptUtils.makeDialogText(aChannel, aAuthInfo); + let [username, password] = PromptUtils.getAuthInfo(aAuthInfo); + let [hostname, httpRealm] = PromptUtils.getAuthTarget(aChannel, aAuthInfo); + let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, httpRealm); + + let canSave = PromptUtils.canSaveLogin(hostname, null); + if (canSave) + [checkMsg, check] = PromptUtils.getUsernameAndPassword(foundLogins, username, password); + + if (username.value && password.value) { + PromptUtils.setAuthInfo(aAuthInfo, username.value, password.value); + } + + let canAutologin = false; + if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY && + !(aAuthInfo.flags & Ci.nsIAuthInformation.PREVIOUS_FAILED) && + Services.prefs.getBoolPref("signon.autologin.proxy")) + canAutologin = true; + + let ok = canAutologin; + if (!ok && aAuthInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD) + ok = this.nsIPrompt_promptPassword(null, message, password, checkMsg, check); + else if (!ok) + ok = this.nsIPrompt_promptUsernameAndPassword(null, message, username, password, checkMsg, check); + + PromptUtils.setAuthInfo(aAuthInfo, username.value, password.value); + + if (ok && canSave && check.value) + PromptUtils.savePassword(foundLogins, username, password, hostname, httpRealm); + + return ok; + }, + + _asyncPrompts: {}, + _asyncPromptInProgress: false, + + _doAsyncPrompt : function() { + if (this._asyncPromptInProgress) + return; + + // Find the first prompt key we have in the queue + let hashKey = null; + for (hashKey in this._asyncPrompts) + break; + + if (!hashKey) + return; + + // If login manger has logins for this host, defer prompting if we're + // already waiting on a master password entry. + let prompt = this._asyncPrompts[hashKey]; + let prompter = prompt.prompter; + let [hostname, httpRealm] = PromptUtils.getAuthTarget(prompt.channel, prompt.authInfo); + let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, httpRealm); + if (foundLogins.length > 0 && PromptUtils.pwmgr.uiBusy) + return; + + this._asyncPromptInProgress = true; + prompt.inProgress = true; + + let self = this; + + let runnable = { + run: function() { + let ok = false; + try { + ok = prompter.promptAuth(prompt.channel, prompt.level, prompt.authInfo); + } catch (e) { + Cu.reportError("_doAsyncPrompt:run: " + e + "\n"); + } + + delete self._asyncPrompts[hashKey]; + prompt.inProgress = false; + self._asyncPromptInProgress = false; + + for each (let consumer in prompt.consumers) { + if (!consumer.callback) + // Not having a callback means that consumer didn't provide it + // or canceled the notification + continue; + + try { + if (ok) + consumer.callback.onAuthAvailable(consumer.context, prompt.authInfo); + else + consumer.callback.onAuthCancelled(consumer.context, true); + } catch (e) { /* Throw away exceptions caused by callback */ } + } + self._doAsyncPrompt(); + } + } + + Services.tm.mainThread.dispatch(runnable, Ci.nsIThread.DISPATCH_NORMAL); + }, + + asyncPromptAuth: function asyncPromptAuth(aChannel, aCallback, aContext, aLevel, aAuthInfo) { + let cancelable = null; + try { + // If the user submits a login but it fails, we need to remove the + // notification bar that was displayed. Conveniently, the user will + // be prompted for authentication again, which brings us here. + //this._removeLoginNotifications(); + + cancelable = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), + callback: aCallback, + context: aContext, + cancel: function() { + this.callback.onAuthCancelled(this.context, false); + this.callback = null; + this.context = null; + } + }; + let [hostname, httpRealm] = PromptUtils.getAuthTarget(aChannel, aAuthInfo); + let hashKey = aLevel + "|" + hostname + "|" + httpRealm; + let asyncPrompt = this._asyncPrompts[hashKey]; + if (asyncPrompt) { + asyncPrompt.consumers.push(cancelable); + return cancelable; + } + + asyncPrompt = { + consumers: [cancelable], + channel: aChannel, + authInfo: aAuthInfo, + level: aLevel, + inProgress : false, + prompter: this + } + + this._asyncPrompts[hashKey] = asyncPrompt; + this._doAsyncPrompt(); + } catch (e) { + Cu.reportError("PromptService: " + e + "\n"); + throw e; + } + return cancelable; + } +}; + +let PromptUtils = { + getLocaleString: function pu_getLocaleString(aKey, aService) { + if (aService == "passwdmgr") + return this.passwdBundle.GetStringFromName(aKey).replace(/&/g, ""); + + return this.bundle.GetStringFromName(aKey).replace(/&/g, ""); + }, + + get pwmgr() { + delete this.pwmgr; + return this.pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager); + }, + + getHostnameAndRealm: function pu_getHostnameAndRealm(aRealmString) { + let httpRealm = /^.+ \(.+\)$/; + if (httpRealm.test(aRealmString)) + return [null, null, null]; + + let uri = Services.io.newURI(aRealmString, null, null); + let pathname = ""; + + if (uri.path != "/") + pathname = uri.path; + + let formattedHostname = this._getFormattedHostname(uri); + return [formattedHostname, formattedHostname + pathname, uri.username]; + }, + + canSaveLogin: function pu_canSaveLogin(aHostname, aSavePassword) { + let canSave = !this._inPrivateBrowsing && this.pwmgr.getLoginSavingEnabled(aHostname) + if (aSavePassword) + canSave = canSave && (aSavePassword == Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY) + return canSave; + }, + + getUsernameAndPassword: function pu_getUsernameAndPassword(aFoundLogins, aUser, aPass) { + let checkLabel = null; + let check = { value: false }; + let selectedLogin; + + checkLabel = this.getLocaleString("rememberPassword", "passwdmgr"); + + // XXX Like the original code, we can't deal with multiple + // account selection. (bug 227632) + if (aFoundLogins.length > 0) { + selectedLogin = aFoundLogins[0]; + + // If the caller provided a username, try to use it. If they + // provided only a password, this will try to find a password-only + // login (or return null if none exists). + if (aUser.value) + selectedLogin = this.findLogin(aFoundLogins, "username", aUser.value); + + if (selectedLogin) { + check.value = true; + aUser.value = selectedLogin.username; + // If the caller provided a password, prefer it. + if (!aPass.value) + aPass.value = selectedLogin.password; + } + } + + return [checkLabel, check]; + }, + + findLogin: function pu_findLogin(aLogins, aName, aValue) { + for (let i = 0; i < aLogins.length; i++) + if (aLogins[i][aName] == aValue) + return aLogins[i]; + return null; + }, + + savePassword: function pu_savePassword(aLogins, aUser, aPass, aHostname, aRealm) { + let selectedLogin = this.findLogin(aLogins, "username", aUser.value); + + // If we didn't find an existing login, or if the username + // changed, save as a new login. + if (!selectedLogin) { + // add as new + var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo); + newLogin.init(aHostname, null, aRealm, aUser.value, aPass.value, "", ""); + this.pwmgr.addLogin(newLogin); + } else if (aPass.value != selectedLogin.password) { + // update password + this.updateLogin(selectedLogin, aPass.value); + } else { + this.updateLogin(selectedLogin); + } + }, + + updateLogin: function pu_updateLogin(aLogin, aPassword) { + let now = Date.now(); + let propBag = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag); + if (aPassword) { + propBag.setProperty("password", aPassword); + // Explicitly set the password change time here (even though it would + // be changed automatically), to ensure that it's exactly the same + // value as timeLastUsed. + propBag.setProperty("timePasswordChanged", now); + } + propBag.setProperty("timeLastUsed", now); + propBag.setProperty("timesUsedIncrement", 1); + + this.pwmgr.modifyLogin(aLogin, propBag); + }, + + // JS port of http://mxr.mozilla.org/mozilla-central/source/embedding/components/windowwatcher/src/nsPrompt.cpp#388 + makeDialogText: function pu_makeDialogText(aChannel, aAuthInfo) { + let isProxy = (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY); + let isPassOnly = (aAuthInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD); + + let username = aAuthInfo.username; + let [displayHost, realm] = this.getAuthTarget(aChannel, aAuthInfo); + + // Suppress "the site says: $realm" when we synthesized a missing realm. + if (!aAuthInfo.realm && !isProxy) + realm = ""; + + // Trim obnoxiously long realms. + if (realm.length > 150) { + realm = realm.substring(0, 150); + // Append "..." (or localized equivalent). + realm += this.ellipsis; + } + + let text; + if (isProxy) + text = this.bundle.formatStringFromName("EnterLoginForProxy", [realm, displayHost], 2); + else if (isPassOnly) + text = this.bundle.formatStringFromName("EnterPasswordFor", [username, displayHost], 2); + else if (!realm) + text = this.bundle.formatStringFromName("EnterUserPasswordFor", [displayHost], 1); + else + text = this.bundle.formatStringFromName("EnterLoginForRealm", [realm, displayHost], 2); + + return text; + }, + + // JS port of http://mxr.mozilla.org/mozilla-central/source/embedding/components/windowwatcher/public/nsPromptUtils.h#89 + getAuthHostPort: function pu_getAuthHostPort(aChannel, aAuthInfo) { + let uri = aChannel.URI; + let res = { host: null, port: -1 }; + if (aAuthInfo.flags & aAuthInfo.AUTH_PROXY) { + let proxy = aChannel.QueryInterface(Ci.nsIProxiedChannel); + res.host = proxy.proxyInfo.host; + res.port = proxy.proxyInfo.port; + } else { + res.host = uri.host; + res.port = uri.port; + } + return res; + }, + + getAuthTarget : function pu_getAuthTarget(aChannel, aAuthInfo) { + let hostname, realm; + // If our proxy is demanding authentication, don't use the + // channel's actual destination. + if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) { + if (!(aChannel instanceof Ci.nsIProxiedChannel)) + throw "proxy auth needs nsIProxiedChannel"; + + let info = aChannel.proxyInfo; + if (!info) + throw "proxy auth needs nsIProxyInfo"; + + // Proxies don't have a scheme, but we'll use "moz-proxy://" + // so that it's more obvious what the login is for. + let idnService = Cc["@mozilla.org/network/idn-service;1"].getService(Ci.nsIIDNService); + hostname = "moz-proxy://" + idnService.convertUTF8toACE(info.host) + ":" + info.port; + realm = aAuthInfo.realm; + if (!realm) + realm = hostname; + + return [hostname, realm]; + } + hostname = this.getFormattedHostname(aChannel.URI); + + // If a HTTP WWW-Authenticate header specified a realm, that value + // will be available here. If it wasn't set or wasn't HTTP, we'll use + // the formatted hostname instead. + realm = aAuthInfo.realm; + if (!realm) + realm = hostname; + + return [hostname, realm]; + }, + + getAuthInfo : function pu_getAuthInfo(aAuthInfo) { + let flags = aAuthInfo.flags; + let username = {value: ""}; + let password = {value: ""}; + + if (flags & Ci.nsIAuthInformation.NEED_DOMAIN && aAuthInfo.domain) + username.value = aAuthInfo.domain + "\\" + aAuthInfo.username; + else + username.value = aAuthInfo.username; + + password.value = aAuthInfo.password + + return [username, password]; + }, + + setAuthInfo : function (aAuthInfo, username, password) { + var flags = aAuthInfo.flags; + if (flags & Ci.nsIAuthInformation.NEED_DOMAIN) { + // Domain is separated from username by a backslash + var idx = username.indexOf("\\"); + if (idx == -1) { + aAuthInfo.username = username; + } else { + aAuthInfo.domain = username.substring(0, idx); + aAuthInfo.username = username.substring(idx+1); + } + } else { + aAuthInfo.username = username; + } + aAuthInfo.password = password; + }, + + getFormattedHostname : function pu_getFormattedHostname(uri) { + let scheme = uri.scheme; + let hostname = scheme + "://" + uri.host; + + // If the URI explicitly specified a port, only include it when + // it's not the default. (We never want "http://foo.com:80") + port = uri.port; + if (port != -1) { + let handler = Services.io.getProtocolHandler(scheme); + if (port != handler.defaultPort) + hostname += ":" + port; + } + return hostname; + }, + sendMessageToJava: function(aMsg) { + let data = Cc["@mozilla.org/android/bridge;1"].getService(Ci.nsIAndroidBridge).handleGeckoMessage(JSON.stringify({ gecko: aMsg })); + return JSON.parse(data); + } +}; + +XPCOMUtils.defineLazyGetter(PromptUtils, "passwdBundle", function () { + return Services.strings.createBundle("chrome://passwordmgr/locale/passwordmgr.properties"); +}); + +XPCOMUtils.defineLazyGetter(PromptUtils, "bundle", function () { + return Services.strings.createBundle("chrome://global/locale/commonDialogs.properties"); +}); + +const NSGetFactory = XPCOMUtils.generateNSGetFactory([PromptService]); diff --git a/mobile/android/components/SafeBrowsing.js b/mobile/android/components/SafeBrowsing.js new file mode 100644 index 000000000000..10dec7017441 --- /dev/null +++ b/mobile/android/components/SafeBrowsing.js @@ -0,0 +1,200 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Safe Browsing. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; +const Cr = Components.results; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +const kPhishWardenEnabledPref = "browser.safebrowsing.enabled"; +const kMalwareWardenEnabledPref = "browser.safebrowsing.malware.enabled"; + +// This XPCOM object doesn't have a public interface. It just works quietly in the background +function SafeBrowsing() { + this.listManager = null; + + // Once we register tables, their respective names will be listed here. + this.phishing = { + pref: kPhishWardenEnabledPref, + blackTables: [], + whiteTables: [] + }; + this.malware = { + pref: kMalwareWardenEnabledPref, + blackTables: [], + whiteTables: [] + }; + + // Get notifications when the phishing or malware warden enabled pref changes + Services.prefs.addObserver(kPhishWardenEnabledPref, this, true); + Services.prefs.addObserver(kMalwareWardenEnabledPref, this, true); +} + +SafeBrowsing.prototype = { + classID: Components.ID("{aadaed90-6c03-42d0-924a-fc61198ff283}"), + + QueryInterface: XPCOMUtils.generateQI([Ci.nsISessionStore, + Ci.nsIDOMEventListener, + Ci.nsIObserver, + Ci.nsISupportsWeakReference]), + + observe: function sb_observe(aSubject, aTopic, aData) { + switch (aTopic) { + case "app-startup": + Services.obs.addObserver(this, "final-ui-startup", true); + Services.obs.addObserver(this, "xpcom-shutdown", true); + break; + case "final-ui-startup": + Services.obs.removeObserver(this, "final-ui-startup"); + this._startup(); + break; + case "xpcom-shutdown": + Services.obs.removeObserver(this, "xpcom-shutdown"); + this._shutdown(); + break; + case "nsPref:changed": + if (aData == kPhishWardenEnabledPref) + this.maybeToggleUpdateChecking(this.phishing); + else if (aData == kMalwareWardenEnabledPref) + this.maybeToggleUpdateChecking(this.malware); + break; + } + }, + + _startup: function sb_startup() { + this.listManager = Cc["@mozilla.org/url-classifier/listmanager;1"].getService(Ci.nsIUrlListManager); + + // Add a test chunk to the database + let testData = "mozilla.org/firefox/its-an-attack.html"; + let testUpdate = + "n:1000\ni:test-malware-simple\nad:1\n" + + "a:1:32:" + testData.length + "\n" + + testData; + + testData = "mozilla.org/firefox/its-a-trap.html"; + testUpdate += + "n:1000\ni:test-phish-simple\nad:1\n" + + "a:1:32:" + testData.length + "\n" + + testData; + + let dbService = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(Ci.nsIUrlClassifierDBService); + + let listener = { + QueryInterface: function(aIID) { + if (aIID.equals(Ci.nsISupports) || aIID.equals(Ci.nsIUrlClassifierUpdateObserver)) + return this; + throw Cr.NS_ERROR_NO_INTERFACE; + }, + + updateUrlRequested: function(aURL) { }, + streamFinished: function(aStatus) { }, + updateError: function(aErrorCode) { }, + updateSuccess: function(aRequestedTimeout) { } + }; + + try { + dbService.beginUpdate(listener, "test-malware-simple,test-phish-simple", ""); + dbService.beginStream("", ""); + dbService.updateStream(testUpdate); + dbService.finishStream(); + dbService.finishUpdate(); + } catch(ex) {} + + this.registerBlackTable("goog-malware-shavar", this.malware); + this.maybeToggleUpdateChecking(this.malware); + + this.registerBlackTable("goog-phish-shavar", this.phishing); + this.maybeToggleUpdateChecking(this.phishing); + }, + + _shutdown: function sb_shutdown() { + Services.prefs.removeObserver(kPhishWardenEnabledPref, this); + Services.prefs.removeObserver(kMalwareWardenEnabledPref, this); + + this.listManager = null; + }, + + enableBlacklistTableUpdates: function sb_enableBlacklistTableUpdates(aWarden) { + for (let i = 0; i < aWarden.blackTables.length; ++i) { + this.listManager.enableUpdate(aWarden.blackTables[i]); + } + }, + + disableBlacklistTableUpdates: function sb_disableBlacklistTableUpdates(aWarden) { + for (let i = 0; i < aWarden.blackTables.length; ++i) { + this.listManager.disableUpdate(aWarden.blackTables[i]); + } + }, + + enableWhitelistTableUpdates: function sb_enableWhitelistTableUpdates(aWarden) { + for (let i = 0; i < this.whiteTables.length; ++i) { + this.listManager.enableUpdate(this.whiteTables[i]); + } + }, + + disableWhitelistTableUpdates: function sb_disableWhitelistTableUpdates(aWarden) { + for (let i = 0; i < aWarden.whiteTables.length; ++i) { + this.listManager.disableUpdate(aWarden.whiteTables[i]); + } + }, + + registerBlackTable: function sb_registerBlackTable(aTableName, aWarden) { + let result = this.listManager.registerTable(aTableName, false); + if (result) + aWarden.blackTables.push(aTableName); + return result; + }, + + registerWhiteTable: function sb_registerWhiteTable(aTableName, aWarden) { + let result = this.listManager.registerTable(aTableName, false); + if (result) + aWarden.whiteTables.push(aTableName); + return result; + }, + + maybeToggleUpdateChecking: function sb_maybeToggleUpdateChecking(aWarden) { + let enabled = Services.prefs.getBoolPref(aWarden.pref); + if (enabled) + this.enableBlacklistTableUpdates(aWarden); + else + this.disableBlacklistTableUpdates(aWarden); + } +} + +const NSGetFactory = XPCOMUtils.generateNSGetFactory([SafeBrowsing]); diff --git a/mobile/android/components/SessionStore.idl b/mobile/android/components/SessionStore.idl new file mode 100644 index 000000000000..1fc61b2227c2 --- /dev/null +++ b/mobile/android/components/SessionStore.idl @@ -0,0 +1,116 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Session Store component. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsISupports.idl" + +interface nsIDOMWindow; +interface nsIDOMNode; + +/** + * nsISessionStore keeps track of the current browsing state. + * + * The nsISessionStore API operates mostly on browser windows and the browser + * tabs contained in them. + */ + +[scriptable, uuid(766a09c1-d21b-4bf8-9fe3-8b34b716251a)] +interface nsISessionStore : nsISupports +{ + /** + * Get the current browsing state. + * @returns a JSON string representing the session state. + */ + AString getBrowserState(); + + /** + * Get the number of restore-able tabs for a browser window + */ + unsigned long getClosedTabCount(in nsIDOMWindow aWindow); + + /** + * Get closed tab data + * + * @param aWindow is the browser window for which to get closed tab data + * @returns a JSON string representing the list of closed tabs. + */ + AString getClosedTabData(in nsIDOMWindow aWindow); + + /** + * @param aWindow is the browser window to reopen a closed tab in. + * @param aIndex is the index of the tab to be restored (FIFO ordered). + * @returns a reference to the reopened tab. + */ + nsIDOMNode undoCloseTab(in nsIDOMWindow aWindow, in unsigned long aIndex); + + /** + * @param aWindow is the browser window associated with the closed tab. + * @param aIndex is the index of the closed tab to be removed (FIFO ordered). + */ + nsIDOMNode forgetClosedTab(in nsIDOMWindow aWindow, in unsigned long aIndex); + + /** + * @param aTab is the browser tab to get the value for. + * @param aKey is the value's name. + * + * @returns A string value or an empty string if none is set. + */ + AString getTabValue(in nsIDOMNode aTab, in AString aKey); + + /** + * @param aTab is the browser tab to set the value for. + * @param aKey is the value's name. + * @param aStringValue is the value itself (use JSON.stringify/parse before setting JS objects). + */ + void setTabValue(in nsIDOMNode aTab, in AString aKey, in AString aStringValue); + + /** + * @param aTab is the browser tab to get the value for. + * @param aKey is the value's name. + */ + void deleteTabValue(in nsIDOMNode aTab, in AString aKey); + + /** + * @returns A boolean indicating we should restore previous browser session + */ + boolean shouldRestore(); + + /** + * Restores the previous browser session using a fast, lightweight strategy + * @param aBringToFront should a restored tab be brought to the foreground? + */ + void restoreLastSession(in boolean aBringToFront); +}; diff --git a/mobile/android/components/SessionStore.js b/mobile/android/components/SessionStore.js new file mode 100644 index 000000000000..0d9c74386667 --- /dev/null +++ b/mobile/android/components/SessionStore.js @@ -0,0 +1,813 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Session Store. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark 'evil' Finkle + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; +const Cr = Components.results; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +#ifdef MOZ_CRASHREPORTER +XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter", + "@mozilla.org/xre/app-info;1", "nsICrashReporter"); +#endif + +XPCOMUtils.defineLazyGetter(this, "NetUtil", function() { + Cu.import("resource://gre/modules/NetUtil.jsm"); + return NetUtil; +}); + +// ----------------------------------------------------------------------- +// Session Store +// ----------------------------------------------------------------------- + +const STATE_STOPPED = 0; +const STATE_RUNNING = 1; +const STATE_QUITTING = -1; + +function SessionStore() { } + +SessionStore.prototype = { + classID: Components.ID("{8c1f07d6-cba3-4226-a315-8bd43d67d032}"), + + QueryInterface: XPCOMUtils.generateQI([Ci.nsISessionStore, + Ci.nsIDOMEventListener, + Ci.nsIObserver, + Ci.nsISupportsWeakReference]), + + _windows: {}, + _lastSaveTime: 0, + _lastSessionTime: 0, + _interval: 10000, + _maxTabsUndo: 1, + _shouldRestore: false, + + init: function ss_init() { + // Get file references + this._sessionFile = Services.dirsvc.get("ProfD", Ci.nsILocalFile); + this._sessionFileBackup = this._sessionFile.clone(); + this._sessionCache = this._sessionFile.clone(); + this._sessionFile.append("sessionstore.js"); + this._sessionFileBackup.append("sessionstore.bak"); + this._sessionCache.append("sessionstoreCache"); + + this._loadState = STATE_STOPPED; + + try { + if (this._sessionFileBackup.exists()) { + this._shouldRestore = true; + this._sessionFileBackup.remove(false); + } + + if (this._sessionFile.exists()) { + // Disable crash recovery if we have exceeded the timeout + this._lastSessionTime = this._sessionFile.lastModifiedTime; + let delta = Date.now() - this._lastSessionTime; + let timeout = Services.prefs.getIntPref("browser.sessionstore.resume_from_crash_timeout"); + if (delta > (timeout * 60000)) + this._shouldRestore = false; + + this._sessionFile.copyTo(null, this._sessionFileBackup.leafName); + } + + if (!this._sessionCache.exists() || !this._sessionCache.isDirectory()) + this._sessionCache.create(Ci.nsIFile.DIRECTORY_TYPE, 0700); + } catch (ex) { + Cu.reportError(ex); // file was write-locked? + } + + this._interval = Services.prefs.getIntPref("browser.sessionstore.interval"); + this._maxTabsUndo = Services.prefs.getIntPref("browser.sessionstore.max_tabs_undo"); + + // Disable crash recovery if it has been turned off + if (!Services.prefs.getBoolPref("browser.sessionstore.resume_from_crash")) + this._shouldRestore = false; + + // Do we need to restore session just this once, in case of a restart? + if (Services.prefs.getBoolPref("browser.sessionstore.resume_session_once")) { + Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", false); + this._shouldRestore = true; + } + }, + + _clearDisk: function ss_clearDisk() { + if (this._sessionFile.exists()) { + try { + this._sessionFile.remove(false); + } catch (ex) { dump(ex + '\n'); } // couldn't remove the file - what now? + } + if (this._sessionFileBackup.exists()) { + try { + this._sessionFileBackup.remove(false); + } catch (ex) { dump(ex + '\n'); } // couldn't remove the file - what now? + } + + this._clearCache(); + }, + + _clearCache: function ss_clearCache() { + // First, let's get a list of files we think should be active + let activeFiles = []; + this._forEachBrowserWindow(function(aWindow) { + let tabs = aWindow.Browser.tabs; + for (let i = 0; i < tabs.length; i++) { + let browser = tabs[i].browser; + if (browser.__SS_extdata && "thumbnail" in browser.__SS_extdata) + activeFiles.push(browser.__SS_extdata.thumbnail); + } + }); + + // Now, let's find the stale files in the cache folder + let staleFiles = []; + let cacheFiles = this._sessionCache.directoryEntries; + while (cacheFiles.hasMoreElements()) { + let file = cacheFiles.getNext().QueryInterface(Ci.nsILocalFile); + let fileURI = Services.io.newFileURI(file); + if (activeFiles.indexOf(fileURI) == -1) + staleFiles.push(file); + } + + // Remove the stale files in a separate step to keep the enumerator from + // messing up if we remove the files as we collect them. + staleFiles.forEach(function(aFile) { + aFile.remove(false); + }) + }, + + observe: function ss_observe(aSubject, aTopic, aData) { + let self = this; + let observerService = Services.obs; + switch (aTopic) { + case "app-startup": + observerService.addObserver(this, "final-ui-startup", true); + observerService.addObserver(this, "domwindowopened", true); + observerService.addObserver(this, "domwindowclosed", true); + observerService.addObserver(this, "browser-lastwindow-close-granted", true); + observerService.addObserver(this, "browser:purge-session-history", true); + observerService.addObserver(this, "quit-application-requested", true); + observerService.addObserver(this, "quit-application-granted", true); + observerService.addObserver(this, "quit-application", true); + break; + case "final-ui-startup": + observerService.removeObserver(this, "final-ui-startup"); + this.init(); + break; + case "domwindowopened": + let window = aSubject; + window.addEventListener("load", function() { + self.onWindowOpen(window); + window.removeEventListener("load", arguments.callee, false); + }, false); + break; + case "domwindowclosed": // catch closed windows + this.onWindowClose(aSubject); + break; + case "browser-lastwindow-close-granted": + // If a save has been queued, kill the timer and save state now + if (this._saveTimer) { + this._saveTimer.cancel(); + this._saveTimer = null; + this.saveState(); + } + + // Freeze the data at what we've got (ignoring closing windows) + this._loadState = STATE_QUITTING; + break; + case "quit-application-requested": + // Get a current snapshot of all windows + this._forEachBrowserWindow(function(aWindow) { + self._collectWindowData(aWindow); + }); + break; + case "quit-application-granted": + // Get a current snapshot of all windows + this._forEachBrowserWindow(function(aWindow) { + self._collectWindowData(aWindow); + }); + + // Freeze the data at what we've got (ignoring closing windows) + this._loadState = STATE_QUITTING; + break; + case "quit-application": + // If we are restarting, lets restore the tabs + if (aData == "restart") { + Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", true); + + // Ignore purges when restarting. The notification is fired after "quit-application". + Services.obs.removeObserver(this, "browser:purge-session-history"); + } + + // Freeze the data at what we've got (ignoring closing windows) + this._loadState = STATE_QUITTING; + + // No need for this back up, we are shutting down just fine + if (this._sessionFileBackup.exists()) + this._sessionFileBackup.remove(false); + + observerService.removeObserver(this, "domwindowopened"); + observerService.removeObserver(this, "domwindowclosed"); + observerService.removeObserver(this, "browser-lastwindow-close-granted"); + observerService.removeObserver(this, "quit-application-requested"); + observerService.removeObserver(this, "quit-application-granted"); + observerService.removeObserver(this, "quit-application"); + + // If a save has been queued, kill the timer and save state now + if (this._saveTimer) { + this._saveTimer.cancel(); + this._saveTimer = null; + this.saveState(); + } + break; + case "browser:purge-session-history": // catch sanitization + this._clearDisk(); + + // If the browser is shutting down, simply return after clearing the + // session data on disk as this notification fires after the + // quit-application notification so the browser is about to exit. + if (this._loadState == STATE_QUITTING) + return; + + // Clear all data about closed tabs + for (let [ssid, win] in Iterator(this._windows)) + win.closedTabs = []; + + if (this._loadState == STATE_RUNNING) { + // Save the purged state immediately + this.saveStateNow(); + } + break; + case "timer-callback": + // Timer call back for delayed saving + this._saveTimer = null; + this.saveState(); + break; + } + }, + + handleEvent: function ss_handleEvent(aEvent) { + let window = aEvent.currentTarget.ownerDocument.defaultView; + switch (aEvent.type) { + case "TabOpen": + case "TabClose": { + let browser = aEvent.originalTarget.linkedBrowser; + if (aEvent.type == "TabOpen") { + this.onTabAdd(window, browser); + } + else { + this.onTabClose(window, browser); + this.onTabRemove(window, browser); + } + break; + } + case "TabSelect": { + let browser = aEvent.originalTarget.linkedBrowser; + this.onTabSelect(window, browser); + break; + } + } + }, + + receiveMessage: function ss_receiveMessage(aMessage) { + let window = aMessage.target.ownerDocument.defaultView; + this.onTabLoad(window, aMessage.target, aMessage); + }, + + onWindowOpen: function ss_onWindowOpen(aWindow) { + // Return if window has already been initialized + if (aWindow && aWindow.__SSID && this._windows[aWindow.__SSID]) + return; + + // Ignore non-browser windows and windows opened while shutting down + if (aWindow.document.documentElement.getAttribute("windowtype") != "navigator:browser" || this._loadState == STATE_QUITTING) + return; + + // Assign it a unique identifier (timestamp) and create its data object + aWindow.__SSID = "window" + Date.now(); + this._windows[aWindow.__SSID] = { tabs: [], selected: 0, closedTabs: [] }; + + // Perform additional initialization when the first window is loading + if (this._loadState == STATE_STOPPED) { + this._loadState = STATE_RUNNING; + this._lastSaveTime = Date.now(); + + // Nothing to restore, notify observers things are complete + if (!this._shouldRestore) { + this._clearCache(); + Services.obs.notifyObservers(null, "sessionstore-windows-restored", ""); + } + } + + // Add tab change listeners to all already existing tabs + let tabs = aWindow.Browser.tabs; + for (let i = 0; i < tabs.length; i++) + this.onTabAdd(aWindow, tabs[i].browser, true); + + // Notification of tab add/remove/selection + let tabContainer = aWindow.document.getElementById("tabs"); + tabContainer.addEventListener("TabOpen", this, true); + tabContainer.addEventListener("TabClose", this, true); + tabContainer.addEventListener("TabSelect", this, true); + }, + + onWindowClose: function ss_onWindowClose(aWindow) { + // Ignore windows not tracked by SessionStore + if (!aWindow.__SSID || !this._windows[aWindow.__SSID]) + return; + + let tabContainer = aWindow.document.getElementById("tabs"); + tabContainer.removeEventListener("TabOpen", this, true); + tabContainer.removeEventListener("TabClose", this, true); + tabContainer.removeEventListener("TabSelect", this, true); + + if (this._loadState == STATE_RUNNING) { + // Update all window data for a last time + this._collectWindowData(aWindow); + + // Clear this window from the list + delete this._windows[aWindow.__SSID]; + + // Save the state without this window to disk + this.saveStateDelayed(); + } + + let tabs = aWindow.Browser.tabs; + for (let i = 0; i < tabs.length; i++) + this.onTabRemove(aWindow, tabs[i].browser, true); + + delete aWindow.__SSID; + }, + + onTabAdd: function ss_onTabAdd(aWindow, aBrowser, aNoNotification) { + aBrowser.messageManager.addMessageListener("pageshow", this); + aBrowser.messageManager.addMessageListener("Content:SessionHistory", this); + + if (!aNoNotification) + this.saveStateDelayed(); + this._updateCrashReportURL(aWindow); + }, + + onTabRemove: function ss_onTabRemove(aWindow, aBrowser, aNoNotification) { + aBrowser.messageManager.removeMessageListener("pageshow", this); + aBrowser.messageManager.removeMessageListener("Content:SessionHistory", this); + + // If this browser is being restored, skip any session save activity + if (aBrowser.__SS_restore) + return; + + delete aBrowser.__SS_data; + + if (!aNoNotification) + this.saveStateDelayed(); + }, + + onTabClose: function ss_onTabClose(aWindow, aBrowser) { + if (this._maxTabsUndo == 0) + return; + + if (aWindow.Browser.tabs.length > 0) { + // Bundle this browser's data and extra data and save in the closedTabs + // window property + let data = aBrowser.__SS_data; + data.extData = aBrowser.__SS_extdata; + + this._windows[aWindow.__SSID].closedTabs.unshift(data); + let length = this._windows[aWindow.__SSID].closedTabs.length; + if (length > this._maxTabsUndo) + this._windows[aWindow.__SSID].closedTabs.splice(this._maxTabsUndo, length - this._maxTabsUndo); + } + }, + + onTabLoad: function ss_onTabLoad(aWindow, aBrowser, aMessage) { + // If this browser is being restored, skip any session save activity + if (aBrowser.__SS_restore) + return; + + // Ignore a transient "about:blank" + if (!aBrowser.canGoBack && aBrowser.currentURI.spec == "about:blank") + return; + + if (aMessage.name == "Content:SessionHistory") { + delete aBrowser.__SS_data; + this._collectTabData(aBrowser, aMessage.json); + } + + // Save out the state as quickly as possible + if (aMessage.name == "pageshow") + this.saveStateNow(); + + this._updateCrashReportURL(aWindow); + }, + + onTabSelect: function ss_onTabSelect(aWindow, aBrowser) { + if (this._loadState != STATE_RUNNING) + return; + + let index = aWindow.Elements.browsers.selectedIndex; + this._windows[aWindow.__SSID].selected = parseInt(index) + 1; // 1-based + + // Restore the resurrected browser + if (aBrowser.__SS_restore) { + let data = aBrowser.__SS_data; + if (data.entries.length > 0) { + let json = { + uri: data.entries[data.index - 1].url, + flags: null, + entries: data.entries, + index: data.index + }; + aBrowser.messageManager.sendAsyncMessage("WebNavigation:LoadURI", json); + } + + delete aBrowser.__SS_restore; + } + + this._updateCrashReportURL(aWindow); + }, + + saveStateDelayed: function ss_saveStateDelayed() { + if (!this._saveTimer) { + // Interval until the next disk operation is allowed + let minimalDelay = this._lastSaveTime + this._interval - Date.now(); + + // If we have to wait, set a timer, otherwise saveState directly + let delay = Math.max(minimalDelay, 2000); + if (delay > 0) { + this._saveTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + this._saveTimer.init(this, delay, Ci.nsITimer.TYPE_ONE_SHOT); + } else { + this.saveState(); + } + } + }, + + saveStateNow: function ss_saveStateNow() { + // Kill any queued timer and save immediately + if (this._saveTimer) { + this._saveTimer.cancel(); + this._saveTimer = null; + } + this.saveState(); + }, + + saveState: function ss_saveState() { + let data = this._getCurrentState(); + this._writeFile(this._sessionFile, JSON.stringify(data)); + + this._lastSaveTime = Date.now(); + }, + + _getCurrentState: function ss_getCurrentState() { + let self = this; + this._forEachBrowserWindow(function(aWindow) { + self._collectWindowData(aWindow); + }); + + let data = { windows: [] }; + let index; + for (index in this._windows) + data.windows.push(this._windows[index]); + return data; + }, + + _collectTabData: function ss__collectTabData(aBrowser, aHistory) { + // If this browser is being restored, skip any session save activity + if (aBrowser.__SS_restore) + return; + + let aHistory = aHistory || { entries: [{ url: aBrowser.currentURI.spec, title: aBrowser.contentTitle }], index: 1 }; + + let tabData = {}; + tabData.entries = aHistory.entries; + tabData.index = aHistory.index; + tabData.attributes = { image: aBrowser.mIconURL }; + + aBrowser.__SS_data = tabData; + }, + + _collectWindowData: function ss__collectWindowData(aWindow) { + // Ignore windows not tracked by SessionStore + if (!aWindow.__SSID || !this._windows[aWindow.__SSID]) + return; + + let winData = this._windows[aWindow.__SSID]; + winData.tabs = []; + + let index = aWindow.Elements.browsers.selectedIndex; + winData.selected = parseInt(index) + 1; // 1-based + + let tabs = aWindow.Browser.tabs; + for (let i = 0; i < tabs.length; i++) { + let browser = tabs[i].browser; + if (browser.__SS_data) { + let tabData = browser.__SS_data; + if (browser.__SS_extdata) + tabData.extData = browser.__SS_extdata; + winData.tabs.push(tabData); + } + } + }, + + _forEachBrowserWindow: function ss_forEachBrowserWindow(aFunc) { + let windowsEnum = Services.wm.getEnumerator("navigator:browser"); + while (windowsEnum.hasMoreElements()) { + let window = windowsEnum.getNext(); + if (window.__SSID && !window.closed) + aFunc.call(this, window); + } + }, + + _writeFile: function ss_writeFile(aFile, aData) { + let stateString = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString); + stateString.data = aData; + Services.obs.notifyObservers(stateString, "sessionstore-state-write", ""); + + // Don't touch the file if an observer has deleted all state data + if (!stateString.data) + return; + + // Initialize the file output stream. + let ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"].createInstance(Ci.nsIFileOutputStream); + ostream.init(aFile, 0x02 | 0x08 | 0x20, 0600, ostream.DEFER_OPEN); + + // Obtain a converter to convert our data to a UTF-8 encoded input stream. + let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter); + converter.charset = "UTF-8"; + + // Asynchronously copy the data to the file. + let istream = converter.convertToInputStream(aData); + NetUtil.asyncCopy(istream, ostream, function(rc) { + if (Components.isSuccessCode(rc)) { + Services.obs.notifyObservers(null, "sessionstore-state-write-complete", ""); + } + }); + }, + + _updateCrashReportURL: function ss_updateCrashReportURL(aWindow) { +#ifdef MOZ_CRASHREPORTER + try { + let currentURI = aWindow.Browser.selectedBrowser.currentURI.clone(); + // if the current URI contains a username/password, remove it + try { + currentURI.userPass = ""; + } + catch (ex) { } // ignore failures on about: URIs + + CrashReporter.annotateCrashReport("URL", currentURI.spec); + } + catch (ex) { + // don't make noise when crashreporter is built but not enabled + if (ex.result != Components.results.NS_ERROR_NOT_INITIALIZED) + Components.utils.reportError("SessionStore:" + ex); + } +#endif + }, + + getBrowserState: function ss_getBrowserState() { + let data = this._getCurrentState(); + return JSON.stringify(data); + }, + + getClosedTabCount: function ss_getClosedTabCount(aWindow) { + if (!aWindow || !aWindow.__SSID) + return 0; // not a browser window, or not otherwise tracked by SS. + + return this._windows[aWindow.__SSID].closedTabs.length; + }, + + getClosedTabData: function ss_getClosedTabData(aWindow) { + if (!aWindow.__SSID) + throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); + + return JSON.stringify(this._windows[aWindow.__SSID].closedTabs); + }, + + undoCloseTab: function ss_undoCloseTab(aWindow, aIndex) { + if (!aWindow.__SSID) + throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); + + let closedTabs = this._windows[aWindow.__SSID].closedTabs; + if (!closedTabs) + return null; + + // default to the most-recently closed tab + aIndex = aIndex || 0; + if (!(aIndex in closedTabs)) + throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); + + // fetch the data of closed tab, while removing it from the array + let closedTab = closedTabs.splice(aIndex, 1).shift(); + + // create a new tab and bring to front + let tab = aWindow.Browser.addTab(closedTab.entries[closedTab.index - 1].url, true); + + tab.browser.messageManager.sendAsyncMessage("WebNavigation:LoadURI", { + uri: closedTab.entries[closedTab.index - 1].url, + flags: null, + entries: closedTab.entries, + index: closedTab.index + }); + + // Put back the extra data + tab.browser.__SS_extdata = closedTab.extData; + + return tab.chromeTab; + }, + + forgetClosedTab: function ss_forgetClosedTab(aWindow, aIndex) { + if (!aWindow.__SSID) + throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); + + let closedTabs = this._windows[aWindow.__SSID].closedTabs; + + // default to the most-recently closed tab + aIndex = aIndex || 0; + if (!(aIndex in closedTabs)) + throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); + + // remove closed tab from the array + closedTabs.splice(aIndex, 1); + }, + + getTabValue: function ss_getTabValue(aTab, aKey) { + let browser = aTab.linkedBrowser; + let data = browser.__SS_extdata || {}; + return data[aKey] || ""; + }, + + setTabValue: function ss_setTabValue(aTab, aKey, aStringValue) { + let browser = aTab.linkedBrowser; + + // Thumbnails are actually stored in the cache, so do the save and update the URI + if (aKey == "thumbnail") { + let file = this._sessionCache.clone(); + file.append("thumbnail-" + browser.contentWindowId); + file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600); + + let source = Services.io.newURI(aStringValue, "UTF8", null); + let target = Services.io.newFileURI(file) + + let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(Ci.nsIWebBrowserPersist); + persist.persistFlags = Ci.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES | Ci.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION; + persist.saveURI(source, null, null, null, null, file); + + aStringValue = target.spec; + } + + if (!browser.__SS_extdata) + browser.__SS_extdata = {}; + browser.__SS_extdata[aKey] = aStringValue; + this.saveStateDelayed(); + }, + + deleteTabValue: function ss_deleteTabValue(aTab, aKey) { + let browser = aTab.linkedBrowser; + if (browser.__SS_extdata && browser.__SS_extdata[aKey]) + delete browser.__SS_extdata[aKey]; + else + throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); + }, + + shouldRestore: function ss_shouldRestore() { + return this._shouldRestore; + }, + + restoreLastSession: function ss_restoreLastSession(aBringToFront) { + let self = this; + function notifyObservers(aMessage) { + self._clearCache(); + Services.obs.notifyObservers(null, "sessionstore-windows-restored", aMessage || ""); + } + + // The previous session data has already been renamed to the backup file + if (!this._sessionFileBackup.exists()) { + notifyObservers("fail") + return; + } + + try { + let channel = NetUtil.newChannel(this._sessionFileBackup); + channel.contentType = "application/json"; + NetUtil.asyncFetch(channel, function(aStream, aResult) { + if (!Components.isSuccessCode(aResult)) { + Cu.reportError("SessionStore: Could not read from sessionstore.bak file"); + notifyObservers("fail"); + return; + } + + // Read session state file into a string and let observers modify the state before it's being used + let state = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString); + state.data = NetUtil.readInputStreamToString(aStream, aStream.available()) || ""; + aStream.close(); + + Services.obs.notifyObservers(state, "sessionstore-state-read", ""); + + let data = null; + try { + data = JSON.parse(state.data); + } catch (ex) { + Cu.reportError("SessionStore: Could not parse JSON: " + ex); + } + + if (!data || data.windows.length == 0) { + notifyObservers("fail"); + return; + } + + let window = Services.wm.getMostRecentWindow("navigator:browser"); + + let tabs = data.windows[0].tabs; + let selected = data.windows[0].selected; + if (selected > tabs.length) // Clamp the selected index if it's bogus + selected = 1; + + for (let i=0; i +# Robert John Churchill +# David Hyatt +# Christopher A. Aillon +# Myk Melez +# Pamela Greene +# Gavin Sharp +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +const Ci = Components.interfaces; +const Cc = Components.classes; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +const SIDEBAR_CID = Components.ID("{22117140-9c6e-11d3-aaf1-00805f8a4905}"); +const SIDEBAR_CONTRACTID = "@mozilla.org/sidebar;1"; + +function Sidebar() { + // Depending on if we are in the parent or child, prepare to remote + // certain calls + var appInfo = Cc["@mozilla.org/xre/app-info;1"]; + if (!appInfo || appInfo.getService(Ci.nsIXULRuntime).processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) { + // Parent process + + this.inContentProcess = false; + + // Used for wakeups service. FIXME: clean up with bug 593407 + this.wrappedJSObject = this; + + // Setup listener for child messages. We don't need to call + // addMessageListener as the wakeup service will do that for us. + this.receiveMessage = function(aMessage) { + switch (aMessage.name) { + case "Sidebar:AddSearchProvider": + this.AddSearchProvider(aMessage.json.descriptionURL); + } + }; + } else { + // Child process + + this.inContentProcess = true; + this.messageManager = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsISyncMessageSender); + } +} + +Sidebar.prototype = { + // =========================== utility code =========================== + _validateSearchEngine: function validateSearchEngine(engineURL, iconURL) { + try { + // Make sure we're using HTTP, HTTPS, or FTP. + if (! /^(https?|ftp):\/\//i.test(engineURL)) + throw "Unsupported search engine URL"; + + // Make sure we're using HTTP, HTTPS, or FTP and refering to a + // .gif/.jpg/.jpeg/.png/.ico file for the icon. + if (iconURL && + ! /^(https?|ftp):\/\/.+\.(gif|jpg|jpeg|png|ico)$/i.test(iconURL)) + throw "Unsupported search icon URL."; + } catch(ex) { + Cu.reportError("Invalid argument passed to window.sidebar.addSearchEngine: " + ex); + + var searchBundle = Services.strings.createBundle("chrome://global/locale/search/search.properties"); + var brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties"); + var brandName = brandBundle.GetStringFromName("brandShortName"); + var title = searchBundle.GetStringFromName("error_invalid_engine_title"); + var msg = searchBundle.formatStringFromName("error_invalid_engine_msg", + [brandName], 1); + Services.prompt.alert(null, title, msg); + return false; + } + + return true; + }, + + // =========================== nsISidebar =========================== + addPanel: function addPanel(aTitle, aContentURL, aCustomizeURL) { + // not supported + }, + + addPersistentPanel: function addPersistentPanel(aTitle, aContentURL, aCustomizeURL) { + // not supported + }, + + // The suggestedTitle and suggestedCategory parameters are ignored, but remain + // for backward compatibility. + addSearchEngine: function addSearchEngine(engineURL, iconURL, suggestedTitle, + suggestedCategory) { + if (!this._validateSearchEngine(engineURL, iconURL)) + return; + + // File extension for Sherlock search plugin description files + const SHERLOCK_FILE_EXT_REGEXP = /\.src$/i; + + // OpenSearch files will likely be far more common than Sherlock files, and + // have less consistent suffixes, so we assume that ".src" is a Sherlock + // (text) file, and anything else is OpenSearch (XML). + var dataType; + if (SHERLOCK_FILE_EXT_REGEXP.test(engineURL)) + dataType = Ci.nsISearchEngine.DATA_TEXT; + else + dataType = Ci.nsISearchEngine.DATA_XML; + + Services.search.addEngine(engineURL, dataType, iconURL, true); + }, + + // =========================== nsISidebarExternal =========================== + // This function exists to implement window.external.AddSearchProvider(), + // to match other browsers' APIs. The capitalization, although nonstandard here, + // is therefore important. + AddSearchProvider: function AddSearchProvider(aDescriptionURL) { + if (!this._validateSearchEngine(aDescriptionURL, "")) + return; + + if (this.inContentProcess) { + this.messageManager.sendAsyncMessage("Sidebar:AddSearchProvider", + { descriptionURL: aDescriptionURL }); + return; + } + + const typeXML = Ci.nsISearchEngine.DATA_XML; + Services.search.addEngine(aDescriptionURL, typeXML, "", true); + }, + + // This function exists to implement window.external.IsSearchProviderInstalled(), + // for compatibility with other browsers. It will return an integer value + // indicating whether the given engine is installed for the current user. + // However, it is currently stubbed out due to security/privacy concerns + // stemming from difficulties in determining what domain issued the request. + // See bug 340604 and + // http://msdn.microsoft.com/en-us/library/aa342526%28VS.85%29.aspx . + // XXX Implement this! + IsSearchProviderInstalled: function IsSearchProviderInstalled(aSearchURL) { + return 0; + }, + + // =========================== nsIClassInfo =========================== + classInfo: XPCOMUtils.generateCI({classID: SIDEBAR_CID, + contractID: SIDEBAR_CONTRACTID, + interfaces: [Ci.nsISidebar, + Ci.nsISidebarExternal], + flags: Ci.nsIClassInfo.DOM_OBJECT, + classDescription: "Sidebar"}), + + // =========================== nsISupports =========================== + QueryInterface: XPCOMUtils.generateQI([Ci.nsISidebar, + Ci.nsISidebarExternal]), + + // XPCOMUtils stuff + classID: SIDEBAR_CID, +}; + +const NSGetFactory = XPCOMUtils.generateNSGetFactory([Sidebar]); diff --git a/mobile/android/components/UpdatePrompt.js b/mobile/android/components/UpdatePrompt.js new file mode 100644 index 000000000000..262744bcb4e9 --- /dev/null +++ b/mobile/android/components/UpdatePrompt.js @@ -0,0 +1,341 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Update Prompt. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * Alex Pakhotin + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +const UPDATE_NOTIFICATION_NAME = "update-app"; +const UPDATE_NOTIFICATION_ICON = "drawable://alert_download_progress"; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + + +XPCOMUtils.defineLazyGetter(this, "gUpdateBundle", function aus_gUpdateBundle() { + return Services.strings.createBundle("chrome://mozapps/locale/update/updates.properties"); +}); + +XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function aus_gBrandBundle() { + return Services.strings.createBundle("chrome://branding/locale/brand.properties"); +}); + +XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function aus_gBrowserBundle() { + return Services.strings.createBundle("chrome://browser/locale/browser.properties"); +}); + +XPCOMUtils.defineLazyGetter(this, "AddonManager", function() { + Cu.import("resource://gre/modules/AddonManager.jsm"); + return AddonManager; +}); + +XPCOMUtils.defineLazyGetter(this, "LocaleRepository", function() { + Cu.import("resource://gre/modules/LocaleRepository.jsm"); + return LocaleRepository; +}); + +function getPref(func, preference, defaultValue) { + try { + return Services.prefs[func](preference); + } catch (e) {} + return defaultValue; +} + +// ----------------------------------------------------------------------- +// Update Prompt +// ----------------------------------------------------------------------- + +function UpdatePrompt() { } + +UpdatePrompt.prototype = { + classID: Components.ID("{88b3eb21-d072-4e3b-886d-f89d8c49fe59}"), + QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdatePrompt, Ci.nsIRequestObserver, Ci.nsIProgressEventSink]), + + get _enabled() { + return !getPref("getBoolPref", "app.update.silent", false); + }, + + _showNotification: function UP__showNotif(aUpdate, aTitle, aText, aImageUrl, aMode) { + let observer = { + updatePrompt: this, + observe: function (aSubject, aTopic, aData) { + switch (aTopic) { + case "alertclickcallback": + this.updatePrompt._handleUpdate(aUpdate, aMode); + break; + } + } + }; + + let notifier = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService); + notifier.showAlertNotification(aImageUrl, aTitle, aText, true, "", observer, UPDATE_NOTIFICATION_NAME); + }, + + _handleUpdate: function UP__handleUpdate(aUpdate, aMode) { + if (aMode == "available") { + let window = Services.wm.getMostRecentWindow("navigator:browser"); + let title = gUpdateBundle.GetStringFromName("updatesfound_" + aUpdate.type + ".title"); + let brandName = gBrandBundle.GetStringFromName("brandShortName"); + + // Unconditionally use the "major" type here as for now it is always a new version + // without additional description required for a minor update message + let message = gUpdateBundle.formatStringFromName("intro_major", [brandName, aUpdate.displayVersion], 2); + let button0 = gUpdateBundle.GetStringFromName("okButton"); + let button1 = gUpdateBundle.GetStringFromName("askLaterButton"); + let prompt = Services.prompt; + let flags = prompt.BUTTON_POS_0 * prompt.BUTTON_TITLE_IS_STRING + prompt.BUTTON_POS_1 * prompt.BUTTON_TITLE_IS_STRING; + + let download = (prompt.confirmEx(window, title, message, flags, button0, button1, null, null, {value: false}) == 0); + if (download) { + // Start downloading the update in the background + let aus = Cc["@mozilla.org/updates/update-service;1"].getService(Ci.nsIApplicationUpdateService); + if (aus.downloadUpdate(aUpdate, true) != "failed") { + let title = gBrowserBundle.formatStringFromName("alertDownloadsStart", [aUpdate.name], 1); + this._showNotification(aUpdate, title, "", UPDATE_NOTIFICATION_ICON, "download"); + + // Add this UI as a listener for active downloads + aus.addDownloadListener(this); + } + } + } else if(aMode == "downloaded") { + // Notify all windows that an application quit has been requested + let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool); + Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart"); + + // If nothing aborted, restart the app + if (cancelQuit.data == false) { + let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup); + appStartup.quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit); + } + } + }, + + _updateDownloadProgress: function UP__updateDownloadProgress(aProgress, aTotal) { + let alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService); + let progressListener = alertsService.QueryInterface(Ci.nsIAlertsProgressListener); + if (progressListener) + progressListener.onProgress(UPDATE_NOTIFICATION_NAME, aProgress, aTotal); + }, + + // ------------------------- + // nsIUpdatePrompt interface + // ------------------------- + + // Right now this is used only to check for updates in progress + checkForUpdates: function UP_checkForUpdates() { + if (!this._enabled) + return; + + let aus = Cc["@mozilla.org/updates/update-service;1"].getService(Ci.nsIApplicationUpdateService); + if (!aus.isDownloading) + return; + + let updateManager = Cc["@mozilla.org/updates/update-manager;1"].getService(Ci.nsIUpdateManager); + + let updateName = updateManager.activeUpdate ? updateManager.activeUpdate.name : gBrandBundle.GetStringFromName("brandShortName"); + let title = gBrowserBundle.formatStringFromName("alertDownloadsStart", [updateName], 1); + + this._showNotification(updateManager.activeUpdate, title, "", UPDATE_NOTIFICATION_ICON, "downloading"); + + aus.removeDownloadListener(this); // just in case it's already added + aus.addDownloadListener(this); + }, + + showUpdateAvailable: function UP_showUpdateAvailable(aUpdate) { + if (!this._enabled) + return; + + const PREF_APP_UPDATE_SKIPNOTIFICATION = "app.update.skipNotification"; + + if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_SKIPNOTIFICATION) && + Services.prefs.getBoolPref(PREF_APP_UPDATE_SKIPNOTIFICATION)) { + Services.prefs.setBoolPref(PREF_APP_UPDATE_SKIPNOTIFICATION, false); + + // Notification was already displayed and clicked, so jump to the next step: + // ask the user about downloading update + this._handleUpdate(aUpdate, "available"); + return; + } + + let stringsPrefix = "updateAvailable_" + aUpdate.type + "."; + let title = gUpdateBundle.formatStringFromName(stringsPrefix + "title", [aUpdate.name], 1); + let text = gUpdateBundle.GetStringFromName(stringsPrefix + "text"); + let imageUrl = ""; + this._showNotification(aUpdate, title, text, imageUrl, "available"); + }, + + showUpdateDownloaded: function UP_showUpdateDownloaded(aUpdate, aBackground) { + if (!this._enabled) + return; + + // uninstall all installed locales + AddonManager.getAddonsByTypes(["locale"], (function (aAddons) { + if (aAddons.length > 0) { + let listener = this.getAddonListener(aUpdate, this); + AddonManager.addAddonListener(listener); + aAddons.forEach(function(aAddon) { + listener._uninstalling.push(aAddon.id); + aAddon.uninstall(); + }, this); + } else { + this._showDownloadedNotification(aUpdate); + } + }).bind(this)); + }, + + _showDownloadedNotification: function UP_showDlNotification(aUpdate) { + let stringsPrefix = "updateDownloaded_" + aUpdate.type + "."; + let title = gUpdateBundle.formatStringFromName(stringsPrefix + "title", [aUpdate.name], 1); + let text = gUpdateBundle.GetStringFromName(stringsPrefix + "text"); + let imageUrl = ""; + this._showNotification(aUpdate, title, text, imageUrl, "downloaded"); + }, + + _uninstalling: [], + _installing: [], + _currentUpdate: null, + + _reinstallLocales: function UP_reinstallLocales(aUpdate, aListener, aPending) { + LocaleRepository.getLocales((function(aLocales) { + aLocales.forEach(function(aLocale, aIndex, aArray) { + let index = aPending.indexOf(aLocale.addon.id); + if (index > -1) { + aListener._installing.push(aLocale.addon.id); + aLocale.addon.install.install(); + } + }, this); + // store the buildid of these locales so that we can disable locales when the + // user updates through a non-updater channel + Services.prefs.setCharPref("extensions.compatability.locales.buildid", aUpdate.buildID); + }).bind(this), { buildID: aUpdate.buildID }); + }, + + showUpdateInstalled: function UP_showUpdateInstalled() { + if (!this._enabled || !getPref("getBoolPref", "app.update.showInstalledUI", false)) + return; + + let title = gBrandBundle.GetStringFromName("brandShortName"); + let text = gUpdateBundle.GetStringFromName("installSuccess"); + let imageUrl = ""; + this._showNotification(aUpdate, title, text, imageUrl, "installed"); + }, + + showUpdateError: function UP_showUpdateError(aUpdate) { + if (!this._enabled) + return; + + if (aUpdate.state == "failed") { + var title = gBrandBundle.GetStringFromName("brandShortName"); + let text = gUpdateBundle.GetStringFromName("updaterIOErrorTitle"); + let imageUrl = ""; + this._showNotification(aUpdate, title, text, imageUrl, "error"); + } + }, + + showUpdateHistory: function UP_showUpdateHistory(aParent) { + // NOT IMPL + }, + + // ---------------------------- + // nsIRequestObserver interface + // ---------------------------- + + // When the data transfer begins + onStartRequest: function(request, context) { + // NOT IMPL + }, + + // When the data transfer ends + onStopRequest: function(request, context, status) { + let alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService); + let progressListener = alertsService.QueryInterface(Ci.nsIAlertsProgressListener); + if (progressListener) + progressListener.onCancel(UPDATE_NOTIFICATION_NAME); + + + let aus = Cc["@mozilla.org/updates/update-service;1"].getService(Ci.nsIApplicationUpdateService); + aus.removeDownloadListener(this); + }, + + // ------------------------------ + // nsIProgressEventSink interface + // ------------------------------ + + // When new data has been downloaded + onProgress: function(request, context, progress, maxProgress) { + this._updateDownloadProgress(progress, maxProgress); + }, + + // When we have new status text + onStatus: function(request, context, status, statusText) { + // NOT IMPL + }, + + // ------------------------------- + // AddonListener + // ------------------------------- + getAddonListener: function(aUpdate, aUpdatePrompt) { + return { + _installing: [], + _uninstalling: [], + onInstalling: function(aAddon, aNeedsRestart) { + let index = this._installing.indexOf(aAddon.id); + if (index > -1) + this._installing.splice(index, 1); + + if (this._installing.length == 0) { + aUpdatePrompt._showDownloadedNotification(aUpdate); + AddonManager.removeAddonListener(this); + } + }, + + onUninstalling: function(aAddon, aNeedsRestart) { + let pending = []; + let index = this._uninstalling.indexOf(aAddon.id); + if (index > -1) { + pending.push(aAddon.id); + this._uninstalling.splice(index, 1); + } + if (this._uninstalling.length == 0) + aUpdatePrompt._reinstallLocales(aUpdate, this, pending); + } + } + } + +}; + +const NSGetFactory = XPCOMUtils.generateNSGetFactory([UpdatePrompt]); diff --git a/mobile/android/components/XPIDialogService.js b/mobile/android/components/XPIDialogService.js new file mode 100644 index 000000000000..796abad325f9 --- /dev/null +++ b/mobile/android/components/XPIDialogService.js @@ -0,0 +1,71 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is XPI Dialog Service. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +// ----------------------------------------------------------------------- +// Web Install Prompt service +// ----------------------------------------------------------------------- + +function WebInstallPrompt() { } + +WebInstallPrompt.prototype = { + classID: Components.ID("{c1242012-27d8-477e-a0f1-0b098ffc329b}"), + QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallPrompt]), + + confirm: function(aWindow, aURL, aInstalls) { + let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); + + let prompt = Services.prompt; + let flags = prompt.BUTTON_POS_0 * prompt.BUTTON_TITLE_IS_STRING + prompt.BUTTON_POS_1 * prompt.BUTTON_TITLE_CANCEL; + let title = bundle.GetStringFromName("addonsConfirmInstall.title"); + let button = bundle.GetStringFromName("addonsConfirmInstall.install"); + + aInstalls.forEach(function(install) { + let result = (prompt.confirmEx(aWindow, title, install.name, flags, button, null, null, null, {value: false}) == 0); + if (result) + install.install(); + else + install.cancel(); + }); + } +}; + +const NSGetFactory = XPCOMUtils.generateNSGetFactory([WebInstallPrompt]); diff --git a/mobile/android/components/build/Makefile.in b/mobile/android/components/build/Makefile.in new file mode 100644 index 000000000000..d1b510e7759e --- /dev/null +++ b/mobile/android/components/build/Makefile.in @@ -0,0 +1,80 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Minimo Tel Protocol Handler. +# +# The Initial Developer of the Original Code is +# the Mozilla Foundation . +# Portions created by the Initial Developer are Copyright (C) 2008 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Doug Turner . +# Nino D'Aversa +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE_NAME = nsBrowserCompsModule +MODULE = browsercomps +LIBRARY_NAME = browsercomps +LIBXUL_LIBRARY = 1 + +IS_COMPONENT = 1 +EXPORT_LIBRARY = 1 + +EXPORTS = nsBrowserComponents.h + +REQUIRES = \ + xpcom \ + string \ + pref \ + $(NULL) + +XPIDL_MODULE = browsercomps + +XPIDLSRCS = nsIShellService.idl + +CPPSRCS = \ + nsBrowserModule.cpp \ + nsShellService.cpp \ + $(NULL) + +ifeq ($(MOZ_PLATFORM_MAEMO),5) +LOCAL_INCLUDES += $(MOZ_DBUS_GLIB_CFLAGS) +endif + +ifneq (,$(filter $(MOZ_WIDGET_TOOLKIT),qt)) +LOCAL_INCLUDES += $(MOZ_QT_CFLAGS) +endif + +include $(topsrcdir)/config/rules.mk + diff --git a/mobile/android/components/build/nsBrowserComponents.h b/mobile/android/components/build/nsBrowserComponents.h new file mode 100644 index 000000000000..1a85c7e7d89b --- /dev/null +++ b/mobile/android/components/build/nsBrowserComponents.h @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mobile Browser. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation . + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +// Needed for building our components as part of libxul +#define APP_COMPONENT_MODULES MODULE(nsBrowserCompsModule) diff --git a/mobile/android/components/build/nsBrowserModule.cpp b/mobile/android/components/build/nsBrowserModule.cpp new file mode 100644 index 000000000000..d986efa20c41 --- /dev/null +++ b/mobile/android/components/build/nsBrowserModule.cpp @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mobile Browser. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation . + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "mozilla/ModuleUtils.h" + +#include "nsShellService.h" + +NS_GENERIC_FACTORY_CONSTRUCTOR(nsShellService) +NS_DEFINE_NAMED_CID(nsShellService_CID); + +static const mozilla::Module::CIDEntry kBrowserCIDs[] = { + { &knsShellService_CID, false, NULL, nsShellServiceConstructor }, + { NULL } +}; + +static const mozilla::Module::ContractIDEntry kBrowserContracts[] = { + { nsShellService_ContractID, &knsShellService_CID }, + { NULL } +}; + +static const mozilla::Module kBrowserModule = { + mozilla::Module::kVersion, + kBrowserCIDs, + kBrowserContracts +}; + +NSMODULE_DEFN(nsBrowserCompsModule) = &kBrowserModule; diff --git a/mobile/android/components/build/nsIShellService.idl b/mobile/android/components/build/nsIShellService.idl new file mode 100644 index 000000000000..fa5059997fba --- /dev/null +++ b/mobile/android/components/build/nsIShellService.idl @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Minimo Tel Protocol Handler. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation . + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Doug Turner + * Nino D'Aversa + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsISupports.idl" + +[scriptable, uuid(fd2450a3-966b-44a9-a8eb-316256bb80b4)] +interface nsIShellService : nsISupports +{ + /** + * This method displays a UI to switch to (or launch) a different task + */ + void switchTask(); + + /** + * This method creates a shortcut on a desktop or homescreen that opens in + * the our application. + * + * @param aTitle the user-friendly name of the shortcut. + * @param aURI the URI to open. + * @param aIconData a base64 encoded representation of the shortcut's icon. + * @param aIntent how the URI should be opened. Examples: "default", "bookmark" and "webapp" + */ + void createShortcut(in AString aTitle, in AString aURI, in AString aIconData, in AString aIntent); +}; diff --git a/mobile/android/components/build/nsShellService.cpp b/mobile/android/components/build/nsShellService.cpp new file mode 100644 index 000000000000..477976477d48 --- /dev/null +++ b/mobile/android/components/build/nsShellService.cpp @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Minimo Tel Protocol Handler. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation . + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Doug Turner + * Nino D'Aversa + * Alex Pakhotin + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#if (MOZ_PLATFORM_MAEMO == 5) +#include +#endif + +#ifdef MOZ_WIDGET_QT +#include +#include +#endif + +#ifdef MOZ_WIDGET_ANDROID +#include "AndroidBridge.h" +#endif + +#include "nsShellService.h" +#include "nsString.h" + +NS_IMPL_ISUPPORTS1(nsShellService, nsIShellService) + +NS_IMETHODIMP +nsShellService::SwitchTask() +{ +#if (MOZ_PLATFORM_MAEMO == 5) + DBusError error; + dbus_error_init(&error); + + DBusConnection *conn = dbus_bus_get(DBUS_BUS_SESSION, &error); + + DBusMessage *msg = dbus_message_new_signal("/com/nokia/hildon_desktop", + "com.nokia.hildon_desktop", + "exit_app_view"); + + if (msg) { + dbus_connection_send(conn, msg, NULL); + dbus_message_unref(msg); + dbus_connection_flush(conn); + } + return NS_OK; +#elif MOZ_WIDGET_QT + QWidget * window = QApplication::activeWindow(); + if (window) + window->showMinimized(); + return NS_OK; +#else + return NS_ERROR_NOT_IMPLEMENTED; +#endif +} + +NS_IMETHODIMP +nsShellService::CreateShortcut(const nsAString& aTitle, const nsAString& aURI, const nsAString& aIconData, const nsAString& aIntent) +{ + if (!aTitle.Length() || !aURI.Length() || !aIconData.Length()) + return NS_ERROR_FAILURE; + +#if MOZ_WIDGET_ANDROID + mozilla::AndroidBridge::Bridge()->CreateShortcut(aTitle, aURI, aIconData, aIntent); + return NS_OK; +#else + return NS_ERROR_NOT_IMPLEMENTED; +#endif +} diff --git a/mobile/android/components/build/nsShellService.h b/mobile/android/components/build/nsShellService.h new file mode 100644 index 000000000000..a77b9f78d1ce --- /dev/null +++ b/mobile/android/components/build/nsShellService.h @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Minimo Tel Protocol Handler. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation . + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Doug Turner + * Nino D'Aversa + * Alex Pakhotin + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __NS_SHELLSERVICE_H__ +#define __NS_SHELLSERVICE_H__ + +#include "nsIShellService.h" + +class nsShellService : public nsIShellService +{ +public: + + NS_DECL_ISUPPORTS + NS_DECL_NSISHELLSERVICE + + nsShellService() {}; + ~nsShellService() {}; + +}; + +#define nsShellService_CID \ +{0xae9ebe1c, 0x61e9, 0x45fa, {0x8f, 0x34, 0xc1, 0x07, 0x80, 0x3a, 0x5b, 0x44}} + +#define nsShellService_ContractID "@mozilla.org/browser/shell-service;1" + +#endif diff --git a/mobile/android/config/mozconfigs/android/debug b/mobile/android/config/mozconfigs/android/debug new file mode 100644 index 000000000000..09933855668f --- /dev/null +++ b/mobile/android/config/mozconfigs/android/debug @@ -0,0 +1,21 @@ +# Global options +mk_add_options MOZ_MAKE_FLAGS=-j4 +ac_add_options --enable-debug + +# Build Fennec +ac_add_options --enable-application=mobile/android + +# Android +ac_add_options --target=arm-linux-androideabi +ac_add_options --with-endian=little +ac_add_options --with-android-ndk="/tools/android-ndk-r5c" +ac_add_options --with-android-sdk="/tools/android-sdk-r13/platforms/android-13" +ac_add_options --with-android-tools="/tools/android-sdk-r13/tools" +ac_add_options --with-android-toolchain=/tools/android-ndk-r5c/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86 +ac_add_options --with-android-platform=/tools/android-ndk-r5c/platforms/android-5/arch-arm +ac_add_options --with-system-zlib + +export JAVA_HOME=/tools/jdk6 +export MOZILLA_OFFICIAL=1 + +ac_add_options --with-branding=mobile/android/branding/nightly diff --git a/mobile/android/config/mozconfigs/android/nightly b/mobile/android/config/mozconfigs/android/nightly new file mode 100644 index 000000000000..b766f04ba11a --- /dev/null +++ b/mobile/android/config/mozconfigs/android/nightly @@ -0,0 +1,21 @@ +# Global options +mk_add_options MOZ_MAKE_FLAGS=-j4 + +# Build Fennec +ac_add_options --enable-application=mobile/android + +# Android +ac_add_options --target=arm-linux-androideabi +ac_add_options --with-endian=little +ac_add_options --with-android-ndk="/tools/android-ndk-r5c" +ac_add_options --with-android-sdk="/tools/android-sdk-r13/platforms/android-13" +ac_add_options --with-android-tools="/tools/android-sdk-r13/tools" +ac_add_options --with-android-toolchain=/tools/android-ndk-r5c/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86 +ac_add_options --with-android-platform=/tools/android-ndk-r5c/platforms/android-5/arch-arm +ac_add_options --with-system-zlib +ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} + +export JAVA_HOME=/tools/jdk6 +export MOZILLA_OFFICIAL=1 + +ac_add_options --with-branding=mobile/android/branding/nightly diff --git a/mobile/android/config/mozconfigs/linux-desktop/l10n-mozconfig b/mobile/android/config/mozconfigs/linux-desktop/l10n-mozconfig new file mode 100644 index 000000000000..ae620448594e --- /dev/null +++ b/mobile/android/config/mozconfigs/linux-desktop/l10n-mozconfig @@ -0,0 +1,5 @@ +mk_add_options MOZ_PKG_FORMAT=BZ2 + +ac_add_options --with-l10n-base=../../l10n-central +ac_add_options --enable-application=mobile/android +ac_add_options --disable-compile-environment diff --git a/mobile/android/config/mozconfigs/linux-desktop/nightly b/mobile/android/config/mozconfigs/linux-desktop/nightly new file mode 100644 index 000000000000..ef68f21ae1c2 --- /dev/null +++ b/mobile/android/config/mozconfigs/linux-desktop/nightly @@ -0,0 +1,12 @@ +mk_add_options MOZ_PKG_FORMAT=BZ2 +ac_add_options --enable-application=mobile/android + +# Nightlies only since this has a cost in performance +ac_add_options --enable-js-diagnostics + +CC=/tools/gcc-4.5/bin/gcc +CXX=/tools/gcc-4.5/bin/g++ +# Avoid dependency on libstdc++ 4.5 +ac_add_options --enable-stdcxx-compat + +export MOZILLA_OFFICIAL=1 diff --git a/mobile/android/config/mozconfigs/macosx-desktop/l10n-mozconfig b/mobile/android/config/mozconfigs/macosx-desktop/l10n-mozconfig new file mode 100644 index 000000000000..b8eb22b07fc1 --- /dev/null +++ b/mobile/android/config/mozconfigs/macosx-desktop/l10n-mozconfig @@ -0,0 +1,3 @@ +ac_add_options --with-l10n-base=../../l10n-central +ac_add_options --enable-application=mobile/android +ac_add_options --disable-compile-environment diff --git a/mobile/android/config/mozconfigs/macosx-desktop/nightly b/mobile/android/config/mozconfigs/macosx-desktop/nightly new file mode 100644 index 000000000000..2cf0bfc42db3 --- /dev/null +++ b/mobile/android/config/mozconfigs/macosx-desktop/nightly @@ -0,0 +1,15 @@ +# Options for client.mk. +mk_add_options AUTOCONF=autoconf213 + +# Global options +. $topsrcdir/build/macosx/mozconfig.leopard +ac_cv_visibility_pragma=no + +ac_add_options --disable-install-strip +ac_add_options --disable-installer +ac_add_options --enable-application=mobile/android + +# Nightlies only since this has a cost in performance +ac_add_options --enable-js-diagnostics + +export MOZILLA_OFFICIAL=1 diff --git a/mobile/android/config/mozconfigs/win32-desktop/l10n-mozconfig b/mobile/android/config/mozconfigs/win32-desktop/l10n-mozconfig new file mode 100644 index 000000000000..905c633852a2 --- /dev/null +++ b/mobile/android/config/mozconfigs/win32-desktop/l10n-mozconfig @@ -0,0 +1,6 @@ +mk_add_options MOZ_PKG_FORMAT=ZIP + +ac_add_options --with-l10n-base=../../l10n-central +ac_add_options --enable-application=mobile/android +ac_add_options --disable-compile-environment +ac_add_options --disable-angle diff --git a/mobile/android/config/mozconfigs/win32-desktop/nightly b/mobile/android/config/mozconfigs/win32-desktop/nightly new file mode 100644 index 000000000000..f37480434cca --- /dev/null +++ b/mobile/android/config/mozconfigs/win32-desktop/nightly @@ -0,0 +1,13 @@ +# Options for client.mk. +mk_add_options MOZ_PKG_FORMAT=ZIP + +export WIN32_REDIST_DIR=/d/msvs8/VC/redist/x86/Microsoft.VC80.CRT + +ac_add_options --enable-jemalloc +ac_add_options --enable-application=mobile/android +ac_add_options --disable-webm + +# Nightlies only since this has a cost in performance +ac_add_options --enable-js-diagnostics + +export MOZILLA_OFFICIAL=1 diff --git a/mobile/android/confvars.sh b/mobile/android/confvars.sh new file mode 100644 index 000000000000..8737693ba495 --- /dev/null +++ b/mobile/android/confvars.sh @@ -0,0 +1,76 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla. +# +# The Initial Developer of the Original Code is +# the Mozilla Foundation . +# Portions created by the Initial Developer are Copyright (C) 2007 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Finkle +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +MOZ_APP_BASENAME=Fennec +MOZ_APP_VENDOR=Mozilla + +MOZ_APP_VERSION=11.0a1 + +MOZ_BRANDING_DIRECTORY=mobile/android/branding/unofficial +MOZ_OFFICIAL_BRANDING_DIRECTORY=mobile/android/branding/official +# MOZ_APP_DISPLAYNAME is set by branding/configure.sh + +MOZ_SAFE_BROWSING= +MOZ_SERVICES_SYNC=1 + +MOZ_DISABLE_DOMCRYPTO=1 + +if test "$LIBXUL_SDK"; then +MOZ_XULRUNNER=1 +else +MOZ_XULRUNNER= +MOZ_PLACES=1 +fi + +if test "$OS_TARGET" = "Android"; then +MOZ_CAPTURE=1 +MOZ_RAW=1 +fi + +# Needed for building our components as part of libxul +MOZ_APP_COMPONENT_LIBS="browsercomps" +MOZ_APP_COMPONENT_INCLUDE=nsBrowserComponents.h + +# use custom widget for html:select +MOZ_USE_NATIVE_POPUP_WINDOWS=1 + +MOZ_APP_ID={aa3c5121-dab2-40e2-81ca-7ea25febc110} + +MOZ_JAVA_COMPOSITOR=1 +MOZ_EXTENSION_MANAGER=1 +MOZ_APP_STATIC_INI=1 + diff --git a/mobile/android/installer/Makefile.in b/mobile/android/installer/Makefile.in new file mode 100644 index 000000000000..9c14db85320c --- /dev/null +++ b/mobile/android/installer/Makefile.in @@ -0,0 +1,257 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla. +# +# The Initial Developer of the Original Code is +# the Mozilla Foundation . +# Portions created by the Initial Developer are Copyright (C) 2007 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Finkle +# Ben Combee +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +# overwrite mobile-l10n.js with a matchOS=true one for multi-locale builds +ifeq ($(AB_CD),multi) +PREF_JS_EXPORTS = $(srcdir)/mobile-l10n.js +endif + +include $(topsrcdir)/config/rules.mk + +MOZ_PKG_REMOVALS = $(srcdir)/removed-files.in + +MOZ_PKG_MANIFEST_P = $(srcdir)/package-manifest.in + +MOZ_NONLOCALIZED_PKG_LIST = \ + xpcom \ + browser \ + mobile \ + $(NULL) + +MOZ_LOCALIZED_PKG_LIST = $(AB_CD) multilocale + +DEFINES += \ + -DAB_CD=$(AB_CD) \ + -DMOZ_APP_NAME=$(MOZ_APP_NAME) \ + -DPREF_DIR=$(PREF_DIR) \ + $(NULL) + +ifeq ($(MOZ_CHROME_FILE_FORMAT),jar) +JAREXT=.jar +else +JAREXT= +endif +DEFINES += -DJAREXT=$(JAREXT) + +include $(topsrcdir)/ipc/app/defs.mk +DEFINES += -DMOZ_CHILD_PROCESS_NAME=$(MOZ_CHILD_PROCESS_NAME) + +ifdef MOZ_PKG_MANIFEST_P +MOZ_PKG_MANIFEST = package-manifest +endif + +MOZ_POST_STAGING_CMD = find chrome -type f -name *.properties -exec sed -i '/^\#/d' {} \; + +include $(topsrcdir)/toolkit/mozapps/installer/packager.mk + +ifeq (bundle, $(MOZ_FS_LAYOUT)) +BINPATH = $(_BINPATH) +DEFINES += -DAPPNAME=$(_APPNAME) +else +# Every other platform just winds up in dist/bin +BINPATH = bin +endif +DEFINES += -DBINPATH=$(BINPATH) + +ifdef MOZ_PKG_MANIFEST_P +$(MOZ_PKG_MANIFEST): $(MOZ_PKG_MANIFEST_P) FORCE + $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $< > $@ +ifdef MOZ_CHROME_MULTILOCALE + printf "\n[multilocale]\n" >> $@ + for LOCALE in en-US $(MOZ_CHROME_MULTILOCALE) ;\ + do \ + printf "$(BINPATH)/chrome/$$LOCALE$(JAREXT)\n" >> $@; \ + printf "$(BINPATH)/chrome/$$LOCALE.manifest\n" >> $@; \ + done +endif + +GARBAGE += $(MOZ_PKG_MANIFEST) +endif + +ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT))) +PACKAGE_XULRUNNER = +UNPACKAGE = +else +PACKAGE_XULRUNNER = package-xulrunner +UNPACKAGE = $(LIBXUL_DIST)/xulrunner*$(PKG_SUFFIX) +endif + +ifdef LIBXUL_SDK +MOZ_GRE_PKG_DIR=$(MOZ_PKG_DIR)/xulrunner +else +MOZ_GRE_PKG_DIR=$(MOZ_PKG_DIR) +endif + +package-xulrunner: +ifdef LIBXUL_SDK +ifndef SYSTEM_LIBXUL + @echo "Packaging xulrunner..." + @rm -rf $(LIBXUL_DIST)/xulrunner* + @$(MAKE) -C $(LIBXUL_DIST)/.. package || echo "Perhaps you're trying to package a prebuilt SDK. See 'https://wiki.mozilla.org/Mobile/Build/Fennec#Build' for more information." + @cd $(DIST)/$(MOZ_PKG_DIR); $(UNMAKE_PACKAGE) + @echo "Removing unpackaged files... (the ones xulrunner/installer keeps)" + @cd $(DIST)/$(MOZ_PKG_DIR)/xulrunner; rm -rf $(NO_PKG_FILES) +else + @echo "Using system xulrunner..." +endif +endif + +ifeq ($(OS_TARGET),Linux) +GRE_MILESTONE = $(shell $(PYTHON) $(topsrcdir)/config/printconfigsetting.py $(LIBXUL_DIST)/bin/platform.ini Build Milestone) +GRE_BUILDID = $(shell $(PYTHON) $(topsrcdir)/config/printconfigsetting.py $(LIBXUL_DIST)/bin/platform.ini Build BuildID) +ABS_OBJDIR=`cd $(DEPTH); pwd` +ABS_TOPSRCDIR=$(shell cd $(topsrcdir); pwd) +BASE64_ICON = dist/branding/fennec_maemo_icon26.txt +MOZ_DEB_TIMESTAMP = "$(shell date +"%a, %d %b %Y %T %z" )" + +DEB_PKG_VERSION = $(shell echo $(MOZ_APP_VERSION) | $(PERL) -pe 's/pre/~$(GRE_BUILDID)/; s/^([0-9.]+)([a-z][0-9]+)/$$1~$$2/') + +DEB_BUILD_ARCH = $(shell dpkg-architecture -qDEB_BUILD_ARCH) +# package name should match mobile/installer/debian/changelog.in +DEB_PKG_NAME = $(MOZ_PKG_APPNAME)_$(DEB_PKG_VERSION)_$(DEB_BUILD_ARCH).deb + +DEFINES += \ + -DGRE_MILESTONE=$(GRE_MILESTONE) \ + -DGRE_BUILDID=$(GRE_BUILDID) \ + -Dinstalldir=$(installdir) \ + -DMOZ_APP_DISPLAYNAME="$(MOZ_APP_DISPLAYNAME)" \ + -DMOZ_APP_VERSION=$(MOZ_APP_VERSION) \ + -DABS_OBJDIR=$(ABS_OBJDIR) \ + -DBASE64_ICON=$(BASE64_ICON) \ + -DMOZ_DEB_TIMESTAMP=$(MOZ_DEB_TIMESTAMP) \ + -DDEB_PKG_VERSION=$(DEB_PKG_VERSION) \ + $(NULL) + +DEBDESTDIR=debian/$(MOZ_APP_NAME) + +PP_DEB_FILES = debian/control \ + debian/changelog \ + debian/$(MOZ_APP_NAME).desktop \ + debian/$(MOZ_APP_NAME).links \ + debian/$(MOZ_APP_NAME).service \ + debian/compat \ + debian/files \ + debian/menu \ + debian/fennec.preinst \ + debian/fennec.prerm \ + debian/fennec.postinst \ + $(NULL) + +ifeq ($(MOZ_PLATFORM_MAEMO),6) +PP_DEB_FILES += debian/fennec.aegis \ + debian/backup \ + debian/restore \ + debian/fennec.conf \ + debian/fennec-cud.sh \ + debian/fennec-rfs.sh \ + debian/fennec.policy \ + $(NULL) +endif + +$(PP_DEB_FILES): + @$(EXIT_ON_ERROR) \ + for f in $(PP_DEB_FILES); do \ + src=$(srcdir)/debian/`basename $$f`.in; \ + echo $$src ">" $$f ;\ + $(RM) -f $$f; \ + mkdir -p debian; \ + $(PYTHON) $(topsrcdir)/config/Preprocessor.py \ + $(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $$src > $$f; \ + done + +deb: $(PP_DEB_FILES) $(DIST)/branding/$(MOZ_APP_NAME)_scalable.png \ + $(DIST)/branding/$(MOZ_APP_NAME)_26x26.png \ + $(DIST)/branding/$(MOZ_APP_NAME)_40x40.png + rm -rf $(DEBDESTDIR)/$(installdir)/* + $(NSINSTALL) -D $(DEBDESTDIR)/$(installdir) + cp -pRL $(DIST)/$(MOZ_APP_NAME)/* $(DEBDESTDIR)/$(installdir) +ifeq ($(MOZ_PLATFORM_MAEMO),6) + $(NSINSTALL) debian/$(MOZ_APP_NAME).desktop $(DEBDESTDIR)/usr/share/applications/ + $(NSINSTALL) -D $(DEBDESTDIR)/usr/share/dbus-1/services/ + cp debian/$(MOZ_APP_NAME).service $(DEBDESTDIR)/usr/share/dbus-1/services/org.mozilla.$(MOZ_APP_NAME).service + $(NSINSTALL) -D $(DEBDESTDIR)/usr/share/themes/blanco/meegotouch/icons/ + cp $(DIST)/branding/$(MOZ_APP_NAME)_scalable.png $(DEBDESTDIR)/usr/share/themes/blanco/meegotouch/icons/$(MOZ_APP_NAME).png + $(NSINSTALL) -D $(DEBDESTDIR)/usr/share/backup-framework/applications + $(NSINSTALL) -D $(DEBDESTDIR)/usr/share/$(MOZ_APP_NAME) + $(NSINSTALL) -D $(DEBDESTDIR)/etc/osso-cud-scripts + $(NSINSTALL) -D $(DEBDESTDIR)/etc/osso-rfs-scripts + $(NSINSTALL) -m 755 debian/backup $(DEBDESTDIR)/usr/share/$(MOZ_APP_NAME)/ + $(NSINSTALL) -m 755 debian/restore $(DEBDESTDIR)/usr/share/$(MOZ_APP_NAME)/ + cp debian/$(MOZ_APP_NAME).conf $(DEBDESTDIR)/usr/share/backup-framework/applications/$(MOZ_APP_NAME).conf + cp debian/$(MOZ_APP_NAME)-cud.sh $(DEBDESTDIR)/etc/osso-cud-scripts/$(MOZ_APP_NAME)-cud.sh + cp debian/$(MOZ_APP_NAME)-rfs.sh $(DEBDESTDIR)/etc/osso-rfs-scripts/$(MOZ_APP_NAME)-rfs.sh + $(NSINSTALL) -D $(DEBDESTDIR)/usr/share/policy/etc/syspart.conf.d + cp debian/$(MOZ_APP_NAME).policy $(DEBDESTDIR)/usr/share/policy/etc/syspart.conf.d/$(MOZ_APP_NAME) +else + $(NSINSTALL) debian/$(MOZ_APP_NAME).desktop $(DEBDESTDIR)/usr/share/applications/hildon/ + $(NSINSTALL) -D $(DEBDESTDIR)/usr/share/dbus-1/services/ + cp debian/$(MOZ_APP_NAME).service $(DEBDESTDIR)/usr/share/dbus-1/services/org.mozilla.$(MOZ_APP_NAME).service + $(NSINSTALL) -D $(DEBDESTDIR)/usr/share/icons/hicolor/scalable/hildon/ + cp $(DIST)/branding/$(MOZ_APP_NAME)_scalable.png $(DEBDESTDIR)/usr/share/icons/hicolor/scalable/hildon/$(MOZ_APP_NAME).png + $(NSINSTALL) -D $(DEBDESTDIR)/usr/share/icons/hicolor/26x26/hildon/ + cp $(DIST)/branding/$(MOZ_APP_NAME)_26x26.png $(DEBDESTDIR)/usr/share/icons/hicolor/26x26/hildon/$(MOZ_APP_NAME).png + $(NSINSTALL) -D $(DEBDESTDIR)/usr/share/icons/hicolor/40x40/hildon/ + cp $(DIST)/branding/$(MOZ_APP_NAME)_40x40.png $(DEBDESTDIR)/usr/share/icons/hicolor/40x40/hildon/$(MOZ_APP_NAME).png +endif + fakeroot dh_link; fakeroot dh_fixperms; fakeroot dh_installdeb; fakeroot dh_shlibdeps; fakeroot dh_gencontrol; fakeroot dh_md5sums; fakeroot dh_builddeb; + +# a defined CONTENTMANAGER implicitly means MOZ_PLATFORM_MAEMO is equals 6 +# in case you use CONTENTMANGER you need to sign your package to gain tracker access. +ifeq ($(MOZ_PLATFORM_MAEMO),6) + if test -e "/usr/bin/aegis-deb-add"; then \ + fakeroot aegis-deb-add -control $(DEBDESTDIR)/DEBIAN/control .. debian/fennec.aegis=_aegis; \ + else \ + echo aegis-builder not found, security signing failed!; \ + fi +endif + + echo $(DEB_PKG_NAME) > $(DIST)/deb_name.txt + +installer: deb + @echo Installer DEB created! + +# relative to $(DIST) +UPLOAD_EXTRA_FILES += ../mobile/$(DEB_PKG_NAME) deb_name.txt +endif diff --git a/mobile/android/installer/debian/backup.in b/mobile/android/installer/debian/backup.in new file mode 100644 index 000000000000..520820e14f2a --- /dev/null +++ b/mobile/android/installer/debian/backup.in @@ -0,0 +1,20 @@ +#filter substitution +#!/bin/sh + +# We do not care about parameters yet + +FENNEC_HOME=$HOME/.mozilla/@MOZ_APP_NAME@ +BACKUP=$HOME/.mozilla/backup + +rm -rf $BACKUP || exit 2 + +# do the backup only if there already is a profile +if [ -d $FENNEC_HOME ]; then + cp -a $FENNEC_HOME $BACKUP || exit 2 + find $BACKUP -name "*.dat" \ + -o -name "*.mfasl" \ + -o -name "Cache" \ + -o -name "OfflineCache" | xargs rm -rf || exit 2 +fi + +exit 0 diff --git a/mobile/android/installer/debian/changelog.in b/mobile/android/installer/debian/changelog.in new file mode 100644 index 000000000000..25f1a359130f --- /dev/null +++ b/mobile/android/installer/debian/changelog.in @@ -0,0 +1,10 @@ +#filter substitution +#ifdef MOZ_OFFICIAL_BRANDING +fennec (@DEB_PKG_VERSION@) stable; urgency=low +#else +fennec (@DEB_PKG_VERSION@) unstable; urgency=low +#endif + + * Mozilla Nightly (Closes: #nnnn) + + -- Mobile Feedback @MOZ_DEB_TIMESTAMP@ diff --git a/mobile/android/installer/debian/compat.in b/mobile/android/installer/debian/compat.in new file mode 100644 index 000000000000..b8626c4cff28 --- /dev/null +++ b/mobile/android/installer/debian/compat.in @@ -0,0 +1 @@ +4 diff --git a/mobile/android/installer/debian/control.in b/mobile/android/installer/debian/control.in new file mode 100644 index 000000000000..bdb40ef8bfb3 --- /dev/null +++ b/mobile/android/installer/debian/control.in @@ -0,0 +1,19 @@ +#filter substitution +Source: fennec +Section: user/network +Priority: extra +Maintainer: Mobile Feedback +Build-Depends: debhelper (>= 4) +Standards-Version: 3.7.2 + +Package: fennec +Architecture: any +Depends: ${shlibs:Depends} +Description: Web browser built by the Mozilla community + The Firefox Web browser puts you in control of your Web experience with easy navigation, support for add-ons and synchronizes with Firefox on your PC. +XB-Maemo-Display-Name: @MOZ_APP_DISPLAYNAME@ +#if MOZ_PLATFORM_MAEMO == 6 +XB-Maemo-Flags: visible +#endif +XB-Maemo-Icon-26: +#includesubst @ABS_OBJDIR@/@BASE64_ICON@ diff --git a/mobile/android/installer/debian/fennec-cud.sh.in b/mobile/android/installer/debian/fennec-cud.sh.in new file mode 100644 index 000000000000..533d73bc7ef5 --- /dev/null +++ b/mobile/android/installer/debian/fennec-cud.sh.in @@ -0,0 +1,3 @@ +#filter substitution +#!/bin/sh +rm -rf $HOME/.mozilla/@MOZ_APP_NAME@ diff --git a/mobile/android/installer/debian/fennec-rfs.sh.in b/mobile/android/installer/debian/fennec-rfs.sh.in new file mode 100644 index 000000000000..4e732e6f3ea4 --- /dev/null +++ b/mobile/android/installer/debian/fennec-rfs.sh.in @@ -0,0 +1,4 @@ +#filter substitution +#!/bin/sh +rm -rf $HOME/.mozilla/@MOZ_APP_NAME@ +gconftool-2 --recursive-unset /apps/@MOZ_APP_NAME@ diff --git a/mobile/android/installer/debian/fennec.aegis.in b/mobile/android/installer/debian/fennec.aegis.in new file mode 100644 index 000000000000..370cd385efec --- /dev/null +++ b/mobile/android/installer/debian/fennec.aegis.in @@ -0,0 +1,11 @@ +#filter substitution + + + + + + + + + + diff --git a/mobile/android/installer/debian/fennec.conf.in b/mobile/android/installer/debian/fennec.conf.in new file mode 100644 index 000000000000..a03dbc8ceaf3 --- /dev/null +++ b/mobile/android/installer/debian/fennec.conf.in @@ -0,0 +1,21 @@ +#filter substitution + + nokia + @MOZ_APP_NAME@ + backup-scripts + + + + /usr/share/@MOZ_APP_NAME@/backup + + + /usr/share/@MOZ_APP_NAME@/restore + + + + + + $HOME/.mozilla/backup + + + diff --git a/mobile/android/installer/debian/fennec.desktop.in b/mobile/android/installer/debian/fennec.desktop.in new file mode 100644 index 000000000000..ff7e9d677540 --- /dev/null +++ b/mobile/android/installer/debian/fennec.desktop.in @@ -0,0 +1,38 @@ +#filter substitution +[Desktop Entry] +Version=0.1 +Encoding=UTF-8 +Name=@MOZ_APP_DISPLAYNAME@ +Comment=@MOZ_APP_DISPLAYNAME@ mobile browser +StartupWMClass=Navigator +StartupNotify=true +Terminal=false +Type=Application +Categories=Application;Network; +#if MOZ_PLATFORM_MAEMO == 6 +Exec=@installdir@/fennec %U +Icon=/usr/share/themes/blanco/meegotouch/icons/@MOZ_APP_NAME@.png +Categories=X-MeeGo; +OnlyShownIn=X-MeeGo; +MimeType=x-maemo-urischeme/http;x-maemo-urischeme/https;x-maemo-urischeme/ftp;x-maemo-urischeme/file;text/html;x-maemo-highlight/ftp-url;x-maemo-highlight/http-url; +#else +Exec=@installdir@/fennec +Icon=@MOZ_APP_NAME@ +X-Icon-Path=/usr/share/icons +X-Window-Icon=@MOZ_APP_NAME@ +X-Window-Icon-dimmed=@MOZ_APP_NAME@ +X-Osso-Service=mozilla.@MOZ_APP_NAME@ +MimeType=text/html;text/xml;application/xhtml+xml;application/xml;application/vnd.mozilla.xul+xml;application/rss+xml;application/rdf+xml;image/gif;image/jpeg;image/png; +#endif +X-Osso-Type=application/x-executable + +[X-Osso-URI-Actions] +http=X-Osso-URI-Action-Open +https=X-Osso-URI-Action-Open +ftp=X-Osso-URI-Action-Open +file=X-Osso-URI-Action-Open + +[X-Osso-URI-Action-Open] +Method=load_url +Name=uri_link_open_link +TranslationDomain=osso-uri diff --git a/mobile/android/installer/debian/fennec.links.in b/mobile/android/installer/debian/fennec.links.in new file mode 100644 index 000000000000..3b91bd5fc3d7 --- /dev/null +++ b/mobile/android/installer/debian/fennec.links.in @@ -0,0 +1,5 @@ +#filter substitution +@installdir@/fennec /usr/bin/fennec +/usr/share/applications/hildon/fennec.desktop etc/others-menu/0112_fennec.desktop + + diff --git a/mobile/android/installer/debian/fennec.policy.in b/mobile/android/installer/debian/fennec.policy.in new file mode 100644 index 000000000000..98912d518812 --- /dev/null +++ b/mobile/android/installer/debian/fennec.policy.in @@ -0,0 +1,4 @@ +#filter substitution +[classify browser] +@installdir@/@MOZ_APP_NAME@ +@installdir@/plugin-container diff --git a/mobile/android/installer/debian/fennec.postinst.in b/mobile/android/installer/debian/fennec.postinst.in new file mode 100644 index 000000000000..99e301719b34 --- /dev/null +++ b/mobile/android/installer/debian/fennec.postinst.in @@ -0,0 +1,37 @@ +#filter substitution +#literal #! /bin/sh +# postinst script for fennec +# +# see: dh_installdeb(1) + +set -e + +case "$1" in + configure) + # for faster initial startup +#ifdef MOZ_PLATFORM_MAEMO + /bin/su -l user -c "/usr/bin/fennec -silent" > /dev/null 2>&1 || echo -n "" +#endif +#if MOZ_PLATFORM_MAEMO == 5 + # for fennec icons + gtk-update-icon-cache -f /usr/share/icons/hicolor +#endif + # select where to install fennec menu + if [ "$2" == "" ]; then + + if [ -x /usr/bin/update-desktop-database ]; then + update-desktop-database /usr/share/applications + fi + fi + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/mobile/android/installer/debian/fennec.preinst.in b/mobile/android/installer/debian/fennec.preinst.in new file mode 100644 index 000000000000..446c6b5eef59 --- /dev/null +++ b/mobile/android/installer/debian/fennec.preinst.in @@ -0,0 +1,26 @@ +#literal #! /bin/sh + +set -e + +RUNNING=`ps | grep "fennec.*/fennec" | grep -v grep | wc -l` + +if [ $RUNNING -gt 0 ]; +then + dbus-send --system --type=method_call --dest=mozilla.fennec /mozilla/fennec/request mozilla.fennec.quit + x=0 + while [ $x -lt 10 ] + do + sleep 1 + RUNNING=`ps | grep "fennec.*/fennec" | grep -v grep | wc -l` + + if [ $RUNNING -eq 0 ]; + then + break + fi + + x=`expr $x + 1` + done + + pkill -9 fennec + sleep 1 +fi diff --git a/mobile/android/installer/debian/fennec.prerm.in b/mobile/android/installer/debian/fennec.prerm.in new file mode 100644 index 000000000000..446c6b5eef59 --- /dev/null +++ b/mobile/android/installer/debian/fennec.prerm.in @@ -0,0 +1,26 @@ +#literal #! /bin/sh + +set -e + +RUNNING=`ps | grep "fennec.*/fennec" | grep -v grep | wc -l` + +if [ $RUNNING -gt 0 ]; +then + dbus-send --system --type=method_call --dest=mozilla.fennec /mozilla/fennec/request mozilla.fennec.quit + x=0 + while [ $x -lt 10 ] + do + sleep 1 + RUNNING=`ps | grep "fennec.*/fennec" | grep -v grep | wc -l` + + if [ $RUNNING -eq 0 ]; + then + break + fi + + x=`expr $x + 1` + done + + pkill -9 fennec + sleep 1 +fi diff --git a/mobile/android/installer/debian/fennec.service.in b/mobile/android/installer/debian/fennec.service.in new file mode 100644 index 000000000000..ca6bb3df2dab --- /dev/null +++ b/mobile/android/installer/debian/fennec.service.in @@ -0,0 +1,5 @@ +#filter substitution +[D-BUS Service] +Name=mozilla.@MOZ_APP_NAME@ +Exec=@installdir@/fennec + diff --git a/mobile/android/installer/debian/files.in b/mobile/android/installer/debian/files.in new file mode 100644 index 000000000000..fa287b5b43dc --- /dev/null +++ b/mobile/android/installer/debian/files.in @@ -0,0 +1,2 @@ +#filter substitution +@MOZ_APP_NAME@_@DEB_PKG_VERSION@_armel.deb user/network extra diff --git a/mobile/android/installer/debian/menu.in b/mobile/android/installer/debian/menu.in new file mode 100644 index 000000000000..391ecc69904d --- /dev/null +++ b/mobile/android/installer/debian/menu.in @@ -0,0 +1,2 @@ +?package(fennec):needs="X11|text|vc|wm" section="Apps/Internet"\ + title="fennec" command="/usr/bin/fennec" diff --git a/mobile/android/installer/debian/restore.in b/mobile/android/installer/debian/restore.in new file mode 100644 index 000000000000..baf0a03faa4c --- /dev/null +++ b/mobile/android/installer/debian/restore.in @@ -0,0 +1,15 @@ +#filter substitution +#!/bin/sh + +# We do not care about parameters yet + +FENNEC_HOME=$HOME/.mozilla/@MOZ_APP_NAME@ +BACKUP=$HOME/.mozilla/backup + +rm -rf $FENNEC_HOME || exit 2 + +# Fennec profile +cp -a $BACKUP $FENNEC_HOME || exit 2 + +exit 0 + diff --git a/mobile/android/installer/mobile-l10n.js b/mobile/android/installer/mobile-l10n.js new file mode 100644 index 000000000000..1835f1ded11d --- /dev/null +++ b/mobile/android/installer/mobile-l10n.js @@ -0,0 +1,2 @@ +// Inherit locale from the OS, used for multi-locale builds +pref("intl.locale.matchOS", true); diff --git a/mobile/android/installer/package-manifest.in b/mobile/android/installer/package-manifest.in new file mode 100644 index 000000000000..54e0dc88d0ea --- /dev/null +++ b/mobile/android/installer/package-manifest.in @@ -0,0 +1,621 @@ +; Package file for the Fennec build. +; +; File format: +; +; [] designates a toplevel component. Example: [xpcom] +; - in front of a file specifies it to be removed from the destination +; * wildcard support to recursively copy the entire directory +; ; file comment +; + +#filter substitution + +#ifdef XP_MACOSX +; Mac bundle stuff +@APPNAME@/Contents/Info.plist +@APPNAME@/Contents/PkgInfo +@APPNAME@/Contents/Plug-Ins/ +@APPNAME@/Contents/Resources/ +#endif + +[@AB_CD@] +@BINPATH@/chrome/@AB_CD@@JAREXT@ +@BINPATH@/chrome/@AB_CD@.manifest +@BINPATH@/@PREF_DIR@/mobile-l10n.js +@BINPATH@/searchplugins/* +@BINPATH@/defaults/profile/bookmarks.html +@BINPATH@/defaults/profile/localstore.rdf +@BINPATH@/defaults/profile/mimeTypes.rdf +@BINPATH@/defaults/profile/chrome/* +#ifdef MOZ_UPDATER +@BINPATH@/update.locale +@BINPATH@/updater.ini +#endif +@BINPATH@/dictionaries/* +@BINPATH@/hyphenation/* +#ifdef XP_WIN32 +@BINPATH@/uninstall/helper.exe +#endif + +[xpcom] +@BINPATH@/dependentlibs.list +#ifndef MOZ_STATIC_JS +@BINPATH@/@DLL_PREFIX@mozjs@DLL_SUFFIX@ +#endif +@BINPATH@/@DLL_PREFIX@plc4@DLL_SUFFIX@ +@BINPATH@/@DLL_PREFIX@plds4@DLL_SUFFIX@ +@BINPATH@/@DLL_PREFIX@xpcom@DLL_SUFFIX@ +@BINPATH@/@DLL_PREFIX@nspr4@DLL_SUFFIX@ +@BINPATH@/@DLL_PREFIX@mozalloc@DLL_SUFFIX@ +@BINPATH@/@DLL_PREFIX@mozutils@DLL_SUFFIX@ +#ifdef XP_MACOSX +@BINPATH@/XUL +#else +@BINPATH@/@DLL_PREFIX@xul@DLL_SUFFIX@ +#endif +#ifdef XP_MACOSX +@BINPATH@/@MOZ_CHILD_PROCESS_NAME@.app/ +#else +@BINPATH@/@MOZ_CHILD_PROCESS_NAME@ +#endif +#ifdef XP_WIN32 +#if _MSC_VER == 1400 +@BINPATH@/Microsoft.VC80.CRT.manifest +@BINPATH@/msvcm80.dll +@BINPATH@/msvcp80.dll +@BINPATH@/msvcr80.dll +#elif _MSC_VER == 1500 +@BINPATH@/Microsoft.VC90.CRT.manifest +@BINPATH@/msvcm90.dll +@BINPATH@/msvcp90.dll +@BINPATH@/msvcr90.dll +#elif _MSC_VER == 1600 +@BINPATH@/msvcp100.dll +@BINPATH@/msvcr100.dll +#elif _MSC_VER == 1700 +@BINPATH@/msvcp110.dll +@BINPATH@/msvcr110.dll +#endif + +#endif + +#ifdef ANDROID +@BINPATH@/AndroidManifest.xml +@BINPATH@/resources.arsc +@BINPATH@/package-name.txt +@BINPATH@/classes.dex +@BINPATH@/@DLL_PREFIX@mozutils@DLL_SUFFIX@ +@BINPATH@/res/drawable +@BINPATH@/res/drawable-hdpi +@BINPATH@/res/layout +@BINPATH@/recommended-addons.json +#endif + +#ifdef MOZ_PLATFORM_MAEMO +@BINPATH@/res/drawable +#endif + +[browser] +; [Base Browser Files] +#ifndef XP_UNIX +@BINPATH@/@MOZ_APP_NAME@.exe +#else +@BINPATH@/@MOZ_APP_NAME@-bin +@BINPATH@/@MOZ_APP_NAME@ +#endif +@BINPATH@/application.ini +@BINPATH@/platform.ini +#ifndef XP_OS2 +@BINPATH@/@DLL_PREFIX@mozsqlite3@DLL_SUFFIX@ +#else +@BINPATH@/mozsqlt3@DLL_SUFFIX@ +#endif +@BINPATH@/blocklist.xml +#ifdef XP_UNIX +@BINPATH@/run-mozilla.sh +#ifndef XP_MACOSX +@BINPATH@/mozilla-xremote-client +#endif +#endif + +; [Components] +@BINPATH@/components/components.manifest +@BINPATH@/components/alerts.xpt +#ifdef ACCESSIBILITY +#ifdef XP_WIN32 +@BINPATH@/AccessibleMarshal.dll +@BINPATH@/components/accessibility-msaa.xpt +#endif +@BINPATH@/components/accessibility.xpt +#endif +@BINPATH@/components/appshell.xpt +@BINPATH@/components/appstartup.xpt +@BINPATH@/components/autocomplete.xpt +@BINPATH@/components/autoconfig.xpt +@BINPATH@/components/browsercompsbase.xpt +@BINPATH@/components/browser-feeds.xpt +@BINPATH@/components/caps.xpt +@BINPATH@/components/chardet.xpt +@BINPATH@/components/chrome.xpt +@BINPATH@/components/commandhandler.xpt +@BINPATH@/components/commandlines.xpt +@BINPATH@/components/composer.xpt +@BINPATH@/components/content_base.xpt +@BINPATH@/components/content_events.xpt +@BINPATH@/components/content_canvas.xpt +@BINPATH@/components/content_htmldoc.xpt +@BINPATH@/components/content_html.xpt +@BINPATH@/components/content_xslt.xpt +@BINPATH@/components/content_xtf.xpt +@BINPATH@/components/cookie.xpt +@BINPATH@/components/directory.xpt +@BINPATH@/components/docshell.xpt +@BINPATH@/components/dom.xpt +@BINPATH@/components/dom_base.xpt +@BINPATH@/components/dom_battery.xpt +@BINPATH@/components/dom_canvas.xpt +@BINPATH@/components/dom_core.xpt +@BINPATH@/components/dom_css.xpt +@BINPATH@/components/dom_events.xpt +@BINPATH@/components/dom_geolocation.xpt +@BINPATH@/components/dom_notification.xpt +@BINPATH@/components/dom_html.xpt +@BINPATH@/components/dom_indexeddb.xpt +@BINPATH@/components/dom_offline.xpt +@BINPATH@/components/dom_json.xpt +@BINPATH@/components/dom_range.xpt +@BINPATH@/components/dom_sidebar.xpt +@BINPATH@/components/dom_storage.xpt +@BINPATH@/components/dom_stylesheets.xpt +@BINPATH@/components/dom_threads.xpt +@BINPATH@/components/dom_traversal.xpt +@BINPATH@/components/dom_views.xpt +@BINPATH@/components/dom_xbl.xpt +@BINPATH@/components/dom_xpath.xpt +@BINPATH@/components/dom_xul.xpt +@BINPATH@/components/dom_loadsave.xpt +@BINPATH@/components/downloads.xpt +@BINPATH@/components/editor.xpt +@BINPATH@/components/embed_base.xpt +@BINPATH@/components/extensions.xpt +@BINPATH@/components/exthandler.xpt +@BINPATH@/components/exthelper.xpt +@BINPATH@/components/fastfind.xpt +@BINPATH@/components/feeds.xpt +#ifdef MOZ_GTK2 +@BINPATH@/components/filepicker.xpt +#endif +@BINPATH@/components/find.xpt +@BINPATH@/components/fuel.xpt +@BINPATH@/components/gfx.xpt +@BINPATH@/components/htmlparser.xpt +@BINPATH@/components/imglib2.xpt +@BINPATH@/components/imgicon.xpt +@BINPATH@/components/inspector.xpt +@BINPATH@/components/intl.xpt +@BINPATH@/components/jar.xpt +@BINPATH@/components/jetpack.xpt +@BINPATH@/components/jsdservice.xpt +@BINPATH@/components/layout_base.xpt +@BINPATH@/components/layout_forms.xpt +#ifdef NS_PRINTING +@BINPATH@/components/layout_printing.xpt +#endif +@BINPATH@/components/layout_xul_tree.xpt +@BINPATH@/components/layout_xul.xpt +@BINPATH@/components/locale.xpt +@BINPATH@/components/lwbrk.xpt +@BINPATH@/components/migration.xpt +@BINPATH@/components/mimetype.xpt +@BINPATH@/components/mozfind.xpt +@BINPATH@/components/necko_about.xpt +@BINPATH@/components/necko_cache.xpt +@BINPATH@/components/necko_cookie.xpt +@BINPATH@/components/necko_dns.xpt +@BINPATH@/components/necko_file.xpt +@BINPATH@/components/necko_ftp.xpt +@BINPATH@/components/necko_http.xpt +@BINPATH@/components/necko_res.xpt +@BINPATH@/components/necko_socket.xpt +@BINPATH@/components/necko_strconv.xpt +@BINPATH@/components/necko_viewsource.xpt +@BINPATH@/components/necko_wifi.xpt +@BINPATH@/components/necko_wyciwyg.xpt +@BINPATH@/components/necko.xpt +@BINPATH@/components/loginmgr.xpt +@BINPATH@/components/parentalcontrols.xpt +@BINPATH@/components/places.xpt +@BINPATH@/components/plugin.xpt +@BINPATH@/components/pref.xpt +@BINPATH@/components/prefetch.xpt +@BINPATH@/components/profile.xpt +@BINPATH@/components/proxyObject.xpt +@BINPATH@/components/rdf.xpt +@BINPATH@/components/satchel.xpt +@BINPATH@/components/saxparser.xpt +@BINPATH@/components/sessionstore.xpt +#ifdef MOZ_SERVICES_SYNC +@BINPATH@/components/services-crypto.xpt +#endif +@BINPATH@/components/services-crypto-component.xpt +@BINPATH@/components/shellservice.xpt +@BINPATH@/components/shistory.xpt +@BINPATH@/components/spellchecker.xpt +@BINPATH@/components/storage.xpt +@BINPATH@/components/telemetry.xpt +@BINPATH@/components/toolkitprofile.xpt +#ifdef MOZ_ENABLE_XREMOTE +@BINPATH@/components/toolkitremote.xpt +#endif +@BINPATH@/components/txtsvc.xpt +@BINPATH@/components/txmgr.xpt +@BINPATH@/components/uconv.xpt +@BINPATH@/components/unicharutil.xpt +#ifdef MOZ_UPDATER +@BINPATH@/components/update.xpt +#endif +@BINPATH@/components/uriloader.xpt +@BINPATH@/components/urlformatter.xpt +@BINPATH@/components/webBrowser_core.xpt +@BINPATH@/components/webbrowserpersist.xpt +@BINPATH@/components/webshell_idls.xpt +@BINPATH@/components/widget.xpt +#ifdef XP_MACOSX +@BINPATH@/components/widget_cocoa.xpt +#endif +#ifdef ANDROID +@BINPATH@/components/widget_android.xpt +#endif +@BINPATH@/components/windowds.xpt +@BINPATH@/components/windowwatcher.xpt +@BINPATH@/components/xpcom_base.xpt +@BINPATH@/components/xpcom_system.xpt +@BINPATH@/components/xpcom_components.xpt +@BINPATH@/components/xpcom_ds.xpt +@BINPATH@/components/xpcom_io.xpt +@BINPATH@/components/xpcom_threads.xpt +@BINPATH@/components/xpcom_xpti.xpt +@BINPATH@/components/xpconnect.xpt +@BINPATH@/components/xulapp.xpt +@BINPATH@/components/xul.xpt +@BINPATH@/components/xuldoc.xpt +@BINPATH@/components/xultmpl.xpt +@BINPATH@/components/zipwriter.xpt +@BINPATH@/components/openwebapps.xpt + +; JavaScript components +@BINPATH@/components/ConsoleAPI.manifest +@BINPATH@/components/ConsoleAPI.js +@BINPATH@/components/FeedProcessor.manifest +@BINPATH@/components/FeedProcessor.js +@BINPATH@/components/BrowserFeeds.manifest +@BINPATH@/components/FeedConverter.js +@BINPATH@/components/FeedWriter.js +@BINPATH@/components/fuelApplication.manifest +@BINPATH@/components/fuelApplication.js +@BINPATH@/components/WebContentConverter.js +@BINPATH@/components/BrowserComponents.manifest +@BINPATH@/components/nsBrowserContentHandler.js +@BINPATH@/components/nsBrowserGlue.js +@BINPATH@/components/nsSetDefaultBrowser.manifest +@BINPATH@/components/nsSetDefaultBrowser.js +@BINPATH@/components/BrowserPlaces.manifest +@BINPATH@/components/nsPrivateBrowsingService.manifest +@BINPATH@/components/nsPrivateBrowsingService.js +@BINPATH@/components/toolkitsearch.manifest +@BINPATH@/components/nsSearchService.js +@BINPATH@/components/nsSearchSuggestions.js +@BINPATH@/components/passwordmgr.manifest +@BINPATH@/components/nsLoginInfo.js +@BINPATH@/components/nsLoginManager.js +@BINPATH@/components/nsLoginManagerPrompter.js +@BINPATH@/components/storage-Legacy.js +@BINPATH@/components/storage-mozStorage.js +@BINPATH@/components/crypto-SDR.js +@BINPATH@/components/jsconsole-clhandler.manifest +@BINPATH@/components/jsconsole-clhandler.js +#ifdef MOZ_GTK2 +@BINPATH@/components/nsFilePicker.manifest +@BINPATH@/components/nsFilePicker.js +#endif +@BINPATH@/components/nsHelperAppDlg.manifest +@BINPATH@/components/nsHelperAppDlg.js +@BINPATH@/components/nsDownloadManagerUI.manifest +@BINPATH@/components/nsDownloadManagerUI.js +@BINPATH@/components/nsProxyAutoConfig.manifest +@BINPATH@/components/nsProxyAutoConfig.js +@BINPATH@/components/NetworkGeolocationProvider.manifest +@BINPATH@/components/NetworkGeolocationProvider.js +@BINPATH@/components/GPSDGeolocationProvider.manifest +@BINPATH@/components/GPSDGeolocationProvider.js +@BINPATH@/components/nsSidebar.manifest +@BINPATH@/components/nsSidebar.js +@BINPATH@/components/extensions.manifest +@BINPATH@/components/addonManager.js +@BINPATH@/components/amContentHandler.js +@BINPATH@/components/amWebInstallListener.js +@BINPATH@/components/nsBlocklistService.js +@BINPATH@/components/OpenWebapps.manifest + +#ifdef MOZ_UPDATER +@BINPATH@/components/nsUpdateService.manifest +@BINPATH@/components/nsUpdateService.js +@BINPATH@/components/nsUpdateServiceStub.js +#endif +@BINPATH@/components/nsUpdateTimerManager.manifest +@BINPATH@/components/nsUpdateTimerManager.js +@BINPATH@/components/pluginGlue.manifest +@BINPATH@/components/nsSessionStore.manifest +@BINPATH@/components/nsSessionStartup.js +@BINPATH@/components/nsSessionStore.js +@BINPATH@/components/nsURLFormatter.manifest +@BINPATH@/components/nsURLFormatter.js +#ifndef XP_OS2 +@BINPATH@/components/@DLL_PREFIX@browsercomps@DLL_SUFFIX@ +#else +@BINPATH@/components/brwsrcmp@DLL_SUFFIX@ +#endif +@BINPATH@/components/txEXSLTRegExFunctions.manifest +@BINPATH@/components/txEXSLTRegExFunctions.js +@BINPATH@/components/toolkitplaces.manifest +@BINPATH@/components/nsLivemarkService.js +@BINPATH@/components/nsTaggingService.js +@BINPATH@/components/nsPlacesDBFlush.js +@BINPATH@/components/nsPlacesAutoComplete.manifest +@BINPATH@/components/nsPlacesAutoComplete.js +@BINPATH@/components/nsPlacesExpiration.js +@BINPATH@/components/PlacesProtocolHandler.js +@BINPATH@/components/PlacesCategoriesStarter.js +@BINPATH@/components/nsDefaultCLH.manifest +@BINPATH@/components/nsDefaultCLH.js +@BINPATH@/components/nsContentPrefService.manifest +@BINPATH@/components/nsContentPrefService.js +@BINPATH@/components/nsContentDispatchChooser.manifest +@BINPATH@/components/nsContentDispatchChooser.js +@BINPATH@/components/nsHandlerService.manifest +@BINPATH@/components/nsHandlerService.js +@BINPATH@/components/nsWebHandlerApp.manifest +@BINPATH@/components/nsWebHandlerApp.js +@BINPATH@/components/nsBadCertHandler.manifest +@BINPATH@/components/nsBadCertHandler.js +@BINPATH@/components/satchel.manifest +@BINPATH@/components/nsFormAutoComplete.js +@BINPATH@/components/nsFormHistory.js +@BINPATH@/components/nsInputListAutoComplete.js +@BINPATH@/components/contentSecurityPolicy.manifest +@BINPATH@/components/contentSecurityPolicy.js +@BINPATH@/components/contentAreaDropListener.manifest +@BINPATH@/components/contentAreaDropListener.js +@BINPATH@/components/messageWakeupService.js +@BINPATH@/components/messageWakeupService.manifest +@BINPATH@/components/nsFilePicker.js +@BINPATH@/components/nsFilePicker.manifest +#ifdef XP_MACOSX +@BINPATH@/components/libalerts_s.dylib +#endif +#ifdef MOZ_ENABLE_DBUS +@BINPATH@/components/@DLL_PREFIX@dbusservice@DLL_SUFFIX@ +#endif +@BINPATH@/components/nsINIProcessor.manifest +@BINPATH@/components/nsINIProcessor.js +@BINPATH@/components/nsPrompter.manifest +@BINPATH@/components/nsPrompter.js +#ifdef MOZ_SERVICES_SYNC +@BINPATH@/components/SyncComponents.manifest +@BINPATH@/components/Weave.js +@BINPATH@/components/WeaveCrypto.manifest +@BINPATH@/components/WeaveCrypto.js +#endif +@BINPATH@/components/TelemetryPing.js +@BINPATH@/components/TelemetryPing.manifest + +; Modules +@BINPATH@/modules/* + +; Safe Browsing +@BINPATH@/components/nsURLClassifier.manifest +@BINPATH@/components/nsUrlClassifierHashCompleter.js +@BINPATH@/components/nsUrlClassifierListManager.js +@BINPATH@/components/nsUrlClassifierLib.js +@BINPATH@/components/url-classifier.xpt + +; GNOME hooks +#ifdef MOZ_ENABLE_GNOME_COMPONENT +@BINPATH@/components/@DLL_PREFIX@mozgnome@DLL_SUFFIX@ +#endif + +; ANGLE on Win32 +#ifdef XP_WIN32 +#ifndef HAVE_64BIT_OS +@BINPATH@/libEGL.dll +@BINPATH@/libGLESv2.dll +#endif +#endif + +; [Browser Chrome Files] +@BINPATH@/chrome/browser@JAREXT@ +@BINPATH@/chrome/browser.manifest +@BINPATH@/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/install.rdf +@BINPATH@/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/icon.png +@BINPATH@/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/preview.png +#if MOZ_UPDATE_CHANNEL == beta +@BINPATH@/extensions/testpilot@labs.mozilla.com/* +#endif +@BINPATH@/chrome/toolkit@JAREXT@ +@BINPATH@/chrome/toolkit.manifest +#ifdef XP_UNIX +#ifndef XP_MACOSX +@BINPATH@/chrome/icons/default/default16.png +@BINPATH@/chrome/icons/default/default32.png +@BINPATH@/chrome/icons/default/default48.png +#endif +#endif + + +; shell icons +#ifdef XP_UNIX +#ifndef XP_MACOSX +@BINPATH@/icons/*.xpm +@BINPATH@/icons/*.png +#endif +#endif + +; [Default Preferences] +; All the pref files must be part of base to prevent migration bugs +@BINPATH@/@PREF_DIR@/mobile.js +@BINPATH@/@PREF_DIR@/mobile-branding.js +@BINPATH@/@PREF_DIR@/channel-prefs.js +#ifdef MOZ_SERVICES_SYNC +@BINPATH@/@PREF_DIR@/services-sync.js +#endif +@BINPATH@/greprefs.js +@BINPATH@/defaults/autoconfig/platform.js +@BINPATH@/defaults/autoconfig/prefcalls.js +@BINPATH@/defaults/profile/prefs.js + +; [Layout Engine Resources] +; Style Sheets, Graphics and other Resources used by the layout engine. +@BINPATH@/res/EditorOverride.css +@BINPATH@/res/contenteditable.css +@BINPATH@/res/designmode.css +@BINPATH@/res/table-add-column-after-active.gif +@BINPATH@/res/table-add-column-after-hover.gif +@BINPATH@/res/table-add-column-after.gif +@BINPATH@/res/table-add-column-before-active.gif +@BINPATH@/res/table-add-column-before-hover.gif +@BINPATH@/res/table-add-column-before.gif +@BINPATH@/res/table-add-row-after-active.gif +@BINPATH@/res/table-add-row-after-hover.gif +@BINPATH@/res/table-add-row-after.gif +@BINPATH@/res/table-add-row-before-active.gif +@BINPATH@/res/table-add-row-before-hover.gif +@BINPATH@/res/table-add-row-before.gif +@BINPATH@/res/table-remove-column-active.gif +@BINPATH@/res/table-remove-column-hover.gif +@BINPATH@/res/table-remove-column.gif +@BINPATH@/res/table-remove-row-active.gif +@BINPATH@/res/table-remove-row-hover.gif +@BINPATH@/res/table-remove-row.gif +@BINPATH@/res/grabber.gif +#ifdef XP_MACOSX +@BINPATH@/res/cursors/* +#endif +@BINPATH@/res/fonts/* +@BINPATH@/res/dtd/* +@BINPATH@/res/html/* +@BINPATH@/res/langGroups.properties +@BINPATH@/res/language.properties +@BINPATH@/res/entityTables/* +#ifdef XP_MACOSX +@BINPATH@/res/MainMenu.nib/ +#endif + +; svg +@BINPATH@/res/svg.css +@BINPATH@/components/dom_svg.xpt +@BINPATH@/components/dom_smil.xpt + +; [Personal Security Manager] +; +@BINPATH@/@DLL_PREFIX@nssckbi@DLL_SUFFIX@ +@BINPATH@/components/pipboot.xpt +@BINPATH@/components/pipnss.xpt +@BINPATH@/components/pippki.xpt +@BINPATH@/@DLL_PREFIX@nss3@DLL_SUFFIX@ +@BINPATH@/@DLL_PREFIX@nssutil3@DLL_SUFFIX@ +@BINPATH@/@DLL_PREFIX@smime3@DLL_SUFFIX@ +@BINPATH@/@DLL_PREFIX@softokn3@DLL_SUFFIX@ +@BINPATH@/@DLL_PREFIX@freebl3@DLL_SUFFIX@ +@BINPATH@/@DLL_PREFIX@ssl3@DLL_SUFFIX@ +#ifndef CROSS_COMPILE +@BINPATH@/@DLL_PREFIX@freebl3.chk +@BINPATH@/@DLL_PREFIX@softokn3.chk +#endif +#ifndef NSS_DISABLE_DBM +@BINPATH@/@DLL_PREFIX@nssdbm3@DLL_SUFFIX@ +#ifndef CROSS_COMPILE +@BINPATH@/@DLL_PREFIX@nssdbm3.chk +#endif +#endif +@BINPATH@/chrome/pippki@JAREXT@ +@BINPATH@/chrome/pippki.manifest + +; for Solaris SPARC +#ifdef SOLARIS +bin/libfreebl_32fpu_3.chk +bin/libfreebl_32fpu_3.so +bin/libfreebl_32int_3.chk +bin/libfreebl_32int_3.so +bin/libfreebl_32int64_3.chk +bin/libfreebl_32int64_3.so +#endif + +; [Updater] +; +#ifdef MOZ_UPDATER +#ifdef XP_MACOSX +@BINPATH@/updater.app/ +#else +@BINPATH@/updater@BIN_SUFFIX@ +#endif +#endif + +; [Crash Reporter] +; +#ifdef MOZ_CRASHREPORTER +#ifdef XP_MACOSX +@BINPATH@/crashreporter.app/ +#else +@BINPATH@/crashreporter@BIN_SUFFIX@ +@BINPATH@/crashreporter.crt +@BINPATH@/crashreporter.ini +#ifdef XP_UNIX +@BINPATH@/Throbber-small.gif +#endif +#endif +@BINPATH@/crashreporter-override.ini +#endif + +; [Extensions] +; +#ifdef MOZ_ENABLE_GNOMEVFS +bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@ +#endif + +; [OS/2] +#ifdef XP_OS2 +@BINPATH@/MozSounds.cmd +#endif + +[mobile] +@BINPATH@/chrome/icons/ +@BINPATH@/chrome/chrome@JAREXT@ +@BINPATH@/chrome/chrome.manifest +@BINPATH@/components/AboutRedirector.js +@BINPATH@/components/AddonUpdateService.js +@BINPATH@/components/AlertsService.js +@BINPATH@/components/BlocklistPrompt.js +@BINPATH@/components/BrowserCLH.js +@BINPATH@/components/ContentDispatchChooser.js +@BINPATH@/components/ContentPermissionPrompt.js +@BINPATH@/components/DirectoryProvider.js +@BINPATH@/components/DownloadManagerUI.js +@BINPATH@/components/FormAutoComplete.js +@BINPATH@/components/HelperAppDialog.js +@BINPATH@/components/LoginManagerPrompter.js +@BINPATH@/components/MobileComponents.manifest +@BINPATH@/components/MobileComponents.xpt +@BINPATH@/components/PromptService.js +; disabled (bug 696203) @BINPATH@/components/SessionStore.js +@BINPATH@/components/Sidebar.js +@BINPATH@/components/OpenWebapps.js +#ifdef MOZ_SAFE_BROWSING +@BINPATH@/components/SafeBrowsing.js +#endif +#ifdef MOZ_UPDATER +@BINPATH@/components/UpdatePrompt.js +#endif +@BINPATH@/components/XPIDialogService.js +@BINPATH@/components/CapturePicker.js +@BINPATH@/components/browsercomps.xpt +@BINPATH@/extensions/feedback@mobile.mozilla.org.xpi diff --git a/mobile/android/installer/removed-files.in b/mobile/android/installer/removed-files.in new file mode 100644 index 000000000000..a6a3226c069f --- /dev/null +++ b/mobile/android/installer/removed-files.in @@ -0,0 +1,30 @@ +update.locale +README.txt +components/nsTryToClose.js +#if MOZ_UPDATE_CHANNEL != beta +extensions/feedback@mobile.mozilla.org.xpi +#endif +#ifdef XP_WIN + #if _MSC_VER != 1400 + @BINPATH@/Microsoft.VC80.CRT.manifest + @BINPATH@/msvcm80.dll + @BINPATH@/msvcp80.dll + @BINPATH@/msvcr80.dll + #endif + #if _MSC_VER != 1500 + @BINPATH@/Microsoft.VC90.CRT.manifest + @BINPATH@/msvcm90.dll + @BINPATH@/msvcp90.dll + @BINPATH@/msvcr90.dll + #endif + #if _MSC_VER != 1600 + @BINPATH@/msvcp100.dll + @BINPATH@/msvcr100.dll + #endif + #if _MSC_VER != 1700 + @BINPATH@/msvcp110.dll + @BINPATH@/msvcr110.dll + #endif + mozcrt19.dll + mozcpp19.dll +#endif diff --git a/mobile/android/locales/Makefile.in b/mobile/android/locales/Makefile.in new file mode 100644 index 000000000000..a8f1d0d8a38a --- /dev/null +++ b/mobile/android/locales/Makefile.in @@ -0,0 +1,249 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla. +# +# The Initial Developer of the Original Code is +# the Mozilla Foundation . +# Portions created by the Initial Developer are Copyright (C) 2007 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Finkle +# Axel Hecht +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +relativesrcdir = mobile/android/locales + +include $(DEPTH)/config/autoconf.mk +include $(topsrcdir)/config/config.mk + +ifdef LOCALE_MERGEDIR +vpath book%.inc $(LOCALE_MERGEDIR)/mobile/android/profile +endif +vpath book%.inc $(LOCALE_SRCDIR)/profile +ifdef LOCALE_MERGEDIR +vpath book%.inc @srcdir@/en-US/profile +endif + +ifdef LOCALE_MERGEDIR +vpath crashreporter%.ini $(LOCALE_MERGEDIR)/mobile/android/crashreporter +endif +vpath crashreporter%.ini $(LOCALE_SRCDIR)/crashreporter +ifdef LOCALE_MERGEDIR +vpath crashreporter%.ini @srcdir@/en-US/crashreporter +endif + +SUBMAKEFILES += \ + $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/Makefile \ + $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales/Makefile \ + $(NULL) + +MOZ_LANGPACK_EID=langpack-$(AB_CD)@firefox-mobile.mozilla.org +PREF_JS_EXPORTS = $(firstword $(wildcard $(LOCALE_SRCDIR)/mobile-l10n.js) \ + @srcdir@/en-US/mobile-l10n.js ) + +# Shouldn't := DEB_BUILD_ARCH despite the $(shell ) as deb isn't everywhere +DEB_BUILD_ARCH = $(shell dpkg-architecture -qDEB_BUILD_ARCH) +DATASTAGE = $(CURDIR)/data-stage + +SEARCH_PLUGINS = $(shell cat \ + $(firstword $(wildcard $(LOCALE_SRCDIR)/searchplugins/list.txt) \ + @srcdir@/en-US/searchplugins/list.txt ) ) + +tmp-search.jar.mn:: + printf "$(AB_CD).jar:" > $@ + printf "$(foreach plugin,$(SEARCH_PLUGINS),$(subst __PLUGIN_SUBST__,$(plugin), \n locale/$(AB_CD)/browser/searchplugins/__PLUGIN_SUBST__.xml (__PLUGIN_SUBST__.xml)))" >> $@ + @echo >> $@ + +searchplugins: tmp-search.jar.mn + $(PYTHON) $(MOZILLA_DIR)/config/JarMaker.py \ + $(QUIET) -j $(FINAL_TARGET)/chrome \ + -s $(topsrcdir)/$(relativesrcdir)/en-US/searchplugins \ + -s $(LOCALE_SRCDIR)/searchplugins \ + $(MAKE_JARS_FLAGS) tmp-search.jar.mn + +export:: searchplugins + +GARBAGE += tmp-search.jar.mn + +include $(topsrcdir)/config/rules.mk + +include $(topsrcdir)/toolkit/locales/l10n.mk + +clobber-zip: + $(RM) $(STAGEDIST)/chrome/$(AB_CD).jar \ + $(STAGEDIST)/chrome/$(AB_CD).manifest \ + $(STAGEDIST)/defaults/preferences/mobile-l10n.js + $(RM) -r $(STAGEDIST)/dictionaries \ + $(STAGEDIST)/hyphenation \ + $(STAGEDIST)/defaults/profile \ + $(STAGEDIST)/chrome/$(AB_CD) + +libs-%: + $(NSINSTALL) -D $(DIST)/install + @$(MAKE) -C ../../toolkit/locales libs-$* BOTH_MANIFESTS=1 + @$(MAKE) -C ../../services/sync/locales AB_CD=$* XPI_NAME=locale-$* BOTH_MANIFESTS=1 + @$(MAKE) -C ../../intl/locales AB_CD=$* XPI_NAME=locale-$* BOTH_MANIFESTS=1 + @$(MAKE) -B bookmarks.json AB_CD=$* + @$(MAKE) -B searchplugins AB_CD=$* XPI_NAME=locale-$* + @$(MAKE) libs AB_CD=$* XPI_NAME=locale-$* PREF_DIR=defaults/preferences BOTH_MANIFESTS=1 + @$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales AB_CD=$* XPI_NAME=locale-$* BOTH_MANIFESTS=1 + +# Tailored target to just add the chrome processing for multi-locale builds +chrome-%: + @$(MAKE) -C $(DEPTH)/toolkit/locales chrome-$* + @$(MAKE) -C $(DEPTH)/services/sync/locales chrome AB_CD=$* + @$(MAKE) -B bookmarks.json AB_CD=$* + @$(MAKE) -B searchplugins AB_CD=$* + @$(MAKE) chrome AB_CD=$* + @$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales chrome AB_CD=$* +ifeq ($(OS_TARGET),Android) + @$(MAKE) -C $(DEPTH)/mobile/android/base chrome AB_CD=$* +endif + +# This is a generic target that will make a langpack and repack tarball +# builds. It is called from the tinderbox scripts. Alter it with caution. + +installers-%: clobber-% langpack-% repackage-zip-% + @echo "repackaging done" + +NO_JA_JP_MAC_AB_CD := $(if $(filter ja-JP-mac, $(AB_CD)),ja,$(AB_CD)) + +bookmarks.json: bookmarks.inc generic/profile/bookmarks.json.in + $(PYTHON) $(topsrcdir)/config/Preprocessor.py \ + -I $< \ + -DAB_CD=$(NO_JA_JP_MAC_AB_CD) \ + $(srcdir)/generic/profile/bookmarks.json.in \ + > $@ + +export:: bookmarks.json + +ifdef MOZ_UPDATER +ifdef LOCALE_MERGEDIR +UPDATER_INI := $(firstword $(wildcard $(LOCALE_MERGEDIR)/updater/updater.ini) \ + $(wildcard $(LOCALE_SRCDIR)/updater/updater.ini) \ + $(srcdir)/en-US/updater/updater.ini ) +else +UPDATER_INI := $(addprefix $(LOCALE_SRCDIR)/,updater/updater.ini) +endif +libs:: $(UPDATER_INI) + cat $< | \ + sed -e "s/^InfoText=/Info=/" -e "s/^TitleText=/Title=/" | \ + sed -e "s/%MOZ_APP_DISPLAYNAME%/$(MOZ_APP_DISPLAYNAME)/" > \ + $(FINAL_TARGET)/updater.ini +endif + +ifdef MOZ_CRASHREPORTER +libs:: crashreporter-override.ini + $(SYSINSTALL) $(IFLAGS1) $^ $(FINAL_TARGET) +endif + +# When we unpack fennec on MacOS X the platform.ini and application.ini are in slightly +# different locations that on all other platforms +ifeq (Darwin, $(OS_ARCH)) +ifdef LIBXUL_SDK +GECKO_PLATFORM_INI_PATH="$(STAGEDIST)/../Frameworks/XUL.framework/Versions/$(MOZILLA_VERSION)/platform.ini" +else +GECKO_PLATFORM_INI_PATH="$(STAGEDIST)/platform.ini" +endif +FENNEC_APPLICATION_INI_PATH="$(STAGEDIST)/application.ini" +else +ifdef LIBXUL_SDK +GECKO_PLATFORM_INI_PATH="$(STAGEDIST)/xulrunner/platform.ini" +else +GECKO_PLATFORM_INI_PATH="$(STAGEDIST)/platform.ini" +endif +FENNEC_APPLICATION_INI_PATH="$(STAGEDIST)/application.ini" +endif + +ident: + @printf "gecko_revision " + @$(PYTHON) $(topsrcdir)/config/printconfigsetting.py $(GECKO_PLATFORM_INI_PATH) Build SourceStamp + @printf "fennec_revision " + @$(PYTHON) $(topsrcdir)/config/printconfigsetting.py $(FENNEC_APPLICATION_INI_PATH) App SourceStamp + @printf "buildid " + @$(PYTHON) $(topsrcdir)/config/printconfigsetting.py $(FENNEC_APPLICATION_INI_PATH) App BuildID + +# special targets just to do the debian single locale packages +wget-DEB_PKG_NAME: +ifndef WGET + $(error Wget not installed) +endif +ifndef EN_US_BINARY_URL + $(error EN_US_BINARY_URL not defined) +endif + @$(WGET) -q -O - $(EN_US_BINARY_URL)/deb_name.txt + +wget-deb: +ifndef WGET + $(error Wget not installed) +endif +ifndef EN_US_BINARY_URL + $(error EN_US_BINARY_URL not defined) +endif +ifndef DEB_PKG_NAME + $(error DEB_PKG_NAME not defined) +endif + $(WGET) -nv -N $(EN_US_BINARY_URL)/$(DEB_PKG_NAME) + +$(DATASTAGE): $(DEB_PKG_NAME) + $(RM) -rf $(DATASTAGE) + $(NSINSTALL) -D $(DATASTAGE)/DEBIAN + ar -p $(DEB_PKG_NAME) data.tar.gz | $(TAR) -zx -C $(DATASTAGE) + $(MAKE) clobber-zip AB_CD=en-US STAGEDIST=$(DATASTAGE)/$(installdir) + ar -p $(DEB_PKG_NAME) control.tar.gz | $(TAR) -zx -C $(DATASTAGE)/DEBIAN +# XXX hack around multi-locale deb right now + $(RM) $(DATASTAGE)/$(installdir)/chrome/??.* + $(RM) $(DATASTAGE)/$(installdir)/chrome/??-??.* + +repackage-deb: $(DATASTAGE) + $(RM) -rf $(AB_CD) + $(NSINSTALL) -D $(AB_CD)/tmp + cd $(DIST)/xpi-stage/locale-$(AB_CD) && \ + $(TAR) --exclude=install.rdf --exclude=chrome.manifest --exclude=crashreporter.app $(TAR_CREATE_FLAGS) - * | ( cd $(DATASTAGE)/$(installdir) && $(TAR) -xf - ) + cd $(DATASTAGE) && $(TAR) $(TAR_CREATE_FLAGS) - * | (cd $(CURDIR)/$(AB_CD)/tmp && $(TAR) -xf - ) + $(MAKE) clobber-zip STAGEDIST=$(DATASTAGE)/$(installdir) + cd $(AB_CD) && dpkg-deb -b tmp $(DEB_PKG_NAME) + $(RM) -rf $(AB_CD)/tmp + +deb-%: AB_CD=$* +deb-%: clobber-% langpack-% +ifndef DEB_PKG_NAME + $(error DEB_PKG_NAME not defined) +endif + @$(MAKE) repackage-deb AB_CD=$(AB_CD) DEB_PKG_NAME=$(DEB_PKG_NAME) + +merge-%: +ifdef LOCALE_MERGEDIR + $(RM) -rf $(LOCALE_MERGEDIR) + MACOSX_DEPLOYMENT_TARGET= compare-locales -m $(LOCALE_MERGEDIR) $(srcdir)/l10n.ini $(L10NBASEDIR) $* +endif + @echo diff --git a/mobile/android/locales/all-locales b/mobile/android/locales/all-locales new file mode 100644 index 000000000000..0b604a57353e --- /dev/null +++ b/mobile/android/locales/all-locales @@ -0,0 +1,45 @@ +ar +be +ca +cs +da +de +el +es-AR +es-ES +et +eu +fa +fi +fr +fy-NL +ga-IE +gd +gl +he +hu +id +it +ja +ja-JP-mac +ko +lt +nb-NO +nl +nn-NO +pa-IN +pl +pt-BR +pt-PT +ro +ru +sk +sl +sq +sr +th +tr +uk +vi +zh-CN +zh-TW diff --git a/mobile/android/locales/en-US/chrome/about.dtd b/mobile/android/locales/en-US/chrome/about.dtd new file mode 100644 index 000000000000..02518d19f338 --- /dev/null +++ b/mobile/android/locales/en-US/chrome/about.dtd @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + diff --git a/mobile/android/locales/en-US/chrome/aboutAddons.dtd b/mobile/android/locales/en-US/chrome/aboutAddons.dtd new file mode 100644 index 000000000000..e018f7499bf2 --- /dev/null +++ b/mobile/android/locales/en-US/chrome/aboutAddons.dtd @@ -0,0 +1,2 @@ + + diff --git a/mobile/android/locales/en-US/chrome/aboutAddons.properties b/mobile/android/locales/en-US/chrome/aboutAddons.properties new file mode 100644 index 000000000000..3c4d435c0b6a --- /dev/null +++ b/mobile/android/locales/en-US/chrome/aboutAddons.properties @@ -0,0 +1,6 @@ +addonAction.enable=Enable +addonAction.disable=Disable +addonAction.uninstall=Uninstall +addonAction.cancel=Cancel +addonAction.options=Options +addonsSearchEngine.description=Integrated Search diff --git a/mobile/android/locales/en-US/chrome/aboutCertError.dtd b/mobile/android/locales/en-US/chrome/aboutCertError.dtd new file mode 100644 index 000000000000..9f5e09922dab --- /dev/null +++ b/mobile/android/locales/en-US/chrome/aboutCertError.dtd @@ -0,0 +1,34 @@ + + %brandDTD; + + + + + + + +#1, but we can't confirm that your connection is secure."> + + + + + + +Even if you trust the site, this error could mean that someone is +tampering with your connection."> + + + + + diff --git a/mobile/android/locales/en-US/chrome/browser.dtd b/mobile/android/locales/en-US/chrome/browser.dtd new file mode 100644 index 000000000000..3bbc6e170ad9 --- /dev/null +++ b/mobile/android/locales/en-US/chrome/browser.dtd @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/locales/en-US/chrome/browser.properties b/mobile/android/locales/en-US/chrome/browser.properties new file mode 100644 index 000000000000..abd2adf97c84 --- /dev/null +++ b/mobile/android/locales/en-US/chrome/browser.properties @@ -0,0 +1,275 @@ +# Add-on Manager +addonsLocalNone.label=No add-ons installed +addonsSearchStart.label=Searching for add-ons… +addonsSearchStart.button=Cancel +addonsSearchNone.search=No matches found +addonsSearchNone.recommended=No recommended add-ons +addonsSearchNone.button=Try Again +addonsSearchFail.label=%S couldn't retrieve add-ons +addonsSearchFail.retryButton=Retry +addonsSearchSuccess2.button=Clear Search +addonsBrowseAll.label=Browse all add-ons +addonsBrowseAll.description=addons.mozilla.org has many to explore +addonsBrowseAll.seeMore=See More Add-ons +addonsBrowseAll.browseSite=Browse Site + +# LOCALIZATION NOTE (addonsSearchMore.label): Semi-colon list of plural forms. +# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals +# #1 total number of add-ons that match the search terms +addonsSearchMore.label=Show result;Show all #1 results + +# LOCALIZATION NOTE (addonsSearchMore.description): Semi-colon list of plural forms. +# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals +# #1 number of search results currently visible +addonsSearchMore.description=If this result isn't what you're looking for, try this;If these #1 results aren't what you're looking for, try this + +addonsWhatAre.label=What are Add-ons? +addonsWhatAre.button=Learn More + +# LOCALIZATION NOTE (addonsWhatAre.description): +# #1 is the application name +addonsWhatAre.description=Add-ons are applications that let you personalize #1 with extra functionality or style. You can make #1 your own. + +addonsSearchEngine.description=Integrated Search + +addonsConfirmInstall.title=Installing Add-on +addonsConfirmInstall.install=Install + +addonType.2=Extension +addonType.4=Theme +addonType.8=Locale +addonType.1024=Search + +addonUpdate.checking=Checking for updates… +addonUpdate.updating=Updating to %S +addonUpdate.updated=Updated to %S +addonUpdate.compatibility=A compatibility update has been applied +addonUpdate.noupdate=No updates were found +addonUpdate.notsupported=Updates not supported +addonUpdate.disabled=Updates are disabled +addonUpdate.error=An error occurred + +addonBlocked.blocked=Blocked +addonBlocked.softBlocked=Known to cause security or stability issues +addonBlocked.outdated=Out of date + +# LOCALIZATION NOTE (addonError-1, addonError-2, addonError-3, addonError-4): +# #1 is the add-on name, #2 is the add-on host, #3 is the application name +addonError-1=The add-on could not be downloaded because of a connection failure on #2. +addonError-2=The add-on from #2 could not be installed because it does not match the add-on #3 expected. +addonError-3=The add-on downloaded from #2 could not be installed because it appears to be corrupt. +addonError-4=#1 could not be installed because #3 cannot modify the needed file. + +# LOCALIZATION NOTE (addonLocalError-1, addonLocalError-2, addonLocalError-3, addonLocalError-4, addonErrorIncompatible, addonErrorBlocklisted): +# #1 is the add-on name, #3 is the application name, #4 is the application version +addonLocalError-1=This add-on could not be installed because of a filesystem error. +addonLocalError-2=This add-on could not be installed because it does not match the add-on #3 expected. +addonLocalError-3=This add-on could not be installed because it appears to be corrupt. +addonLocalError-4=#1 could not be installed because #3 cannot modify the needed file. +addonErrorIncompatible=#1 could not be installed because it is not compatible with #3 #4. +addonErrorBlocklisted=#1 could not be installed because it has a high risk of causing stability or security problems. + +# Download Manager +# LOCALIZATION NOTE (Status): — is the "em dash" (long dash) +# #1 download size for FINISHED or download state; #2 host (e.g., eTLD + 1, IP) +downloadsStatus=#1 — #2 +# LOCALIZATION NOTE (Time): #1 left time for UNFINISHED, total time for FINISHED +downloadsTime= — #1 +downloadsUnknownSize=Unknown size +# LOCALIZATION NOTE (KnownSize): #1 size number; #2 size unit +downloadsKnownSize=#1 #2 +downloadsYesterday=Yesterday +# LOCALIZATION NOTE (MonthDate): #1 month name; #2 date number; e.g., January 22 +downloadsMonthDate=#1 #2 +downloadsEmpty=No downloads +downloadsDeleteTitle=Delete File + +# Alerts +alertAddons=Add-ons +alertAddonsDownloading=Downloading add-on +alertAddonsInstalling=Installing add-on +alertAddonsInstalled=Installation complete. Restart required. +alertAddonsInstalledNoRestart=Installation complete +alertAddonsFail=Installation failed +alertLinkBookmarked=Bookmark added +alertLockScreen=Screen Orientation +alertLockScreen.locked=Locked +alertLockScreen.unlocked=Unlocked + +# LOCALIZATION NOTE (alertAddonsDisabled): Semi-colon list of plural forms. +# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals +# #1 number of add-ons +alertAddonsDisabled=#1 incompatible add-on was disabled;#1 incompatible add-ons were disabled + +alertDownloads=Downloads +alertDownloadsStart=Downloading: %S +alertDownloadsDone=%S has finished downloading +alertCantOpenDownload=Can't open file. Tap to save it. +alertDownloadsSize=Download too big +alertDownloadsNoSpace=Not enough storage space +alertDownloadsToast=Download started… + +alertFullScreenToast=Press BACK to leave full-screen mode + +downloadCancelPromptTitle=Cancel Download +downloadCancelPromptMessage=Do you want to cancel this download? + +# Notifications +notificationRestart.normal=Restart to complete changes. +notificationRestart.update=Add-ons updated. Restart to complete changes. +notificationRestart.blocked=Unsafe add-ons installed. Restart to disable. +notificationRestart.button=Restart + +# Popup Blocker +popupWarning=%S prevented this site from opening a pop-up window. +popupWarningMultiple=%S prevented this site from opening %S pop-up windows. +popupButtonAllowOnce=Show +popupButtonAlwaysAllow2=Always Show +popupButtonNeverWarn2=Never Show + +# Telemetry +telemetry.optin.message=Help improve %S by sending anonymous usage information to Mozilla? +telemetry.optin.yes=Yes +telemetry.optin.no=No + +# XPInstall +xpinstallPromptWarning2=%S prevented this site (%S) from asking you to install software on your device. +xpinstallPromptAllowButton=Allow +xpinstallDisabledMessageLocked=Software installation has been disabled by your system administrator. +xpinstallDisabledMessage2=Software installation is currently disabled. Press Enable and try again. +xpinstallDisabledButton=Enable + +# Site Identity +identity.identified.verifier=Verified by: %S +identity.identified.verified_by_you=You have added a security exception for this site +identity.identified.state_and_country=%S, %S +identity.identified.title_with_country=%S (%S) +identity.encrypted2=Encrypted +identity.unencrypted2=Not encrypted +identity.unknown.tooltip=This website does not supply identity information. +identity.ownerUnknown2=(unknown) + +# Geolocation UI +geolocation.allow=Share +geolocation.dontAllow=Don't share +geolocation.wantsTo=%S wants your location. + +# Desktop notification UI +desktopNotification.allow=Allow +desktopNotification.dontAllow=Don't allow +desktopNotification.wantsTo=%S wants to use notifications. + +# New Tab Popup +# LOCALIZATION NOTE (newtabpopup): Semi-colon list of plural forms. +# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals +# #1 number of tabs +newtabpopup.opened=New tab opened;#1 new tabs opened + +# Error Console +typeError=Error: +typeWarning=Warning: + +# Offline web applications +offlineApps.available2=%S wants to store data on your device for offline use. +offlineApps.allow=Allow +offlineApps.never=Don't Allow +offlineApps.notNow=Not Now + +# New-style ContentPermissionPrompt values +offlineApps.dontAllow=Don't Allow +offlineApps.wantsTo=%S wants to store data on your device for offline use. + +# IndexedDB Quota increases +indexedDBQuota.allow=Allow +indexedDBQuota.dontAllow=Don't Allow +indexedDBQuota.wantsTo=%S wants to store a lot of data on your device for offline use. + +# Open Web Apps management API +openWebappsManage.allow=Allow +openWebappsManage.dontAllow=Don't Allow +openWebappsManage.wantsTo=%S wants to manage applications on your device. + +# Bookmark List +bookmarkList.desktop=Desktop Bookmarks + +# Closing Tabs +tabs.closeWarningTitle=Confirm close + +# LOCALIZATION NOTE (tabs.closeWarning): Semi-colon list of plural forms. +# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals +# #1 number of tabs (must be greater than 1) +tabs.closeWarning=NOT USED;You are about to close #1 tabs. Continue? + +tabs.closeButton=Close tabs +tabs.closeWarningPromptMe=Warn me when I attempt to close multiple tabs + +tabs.crashWarningTitle=Sorry! +tabs.crashWarningMsg=Something went wrong while displaying a web page. +tabs.crashSubmitReport=Send Mozilla a crash report +tabs.crashClose=Close tab +tabs.crashReload=Reload tab + +# Homepage +# LOCALIZATION NOTE: homepage.custom2 is the text displayed on the selector button if +# the user selects a webpage to be the startpage. We can't display the entire URL +# or webpage title on the menulist +homepage.custom2=Custom Page + +# Page Actions +pageactions.saveas.pdf=Save As PDF +pageactions.geolocation=Location +pageactions.popup=Popups +pageactions.offline-app=Offline Storage +pageactions.password=Password +pageactions.desktop-notification=Web Notifications +pageactions.openWebappsManage=Manage Web Apps + +# Open Search +opensearch.searchWith=Search With: +opensearch.searchFor=Search for "%S" + +# Open in Another App +# LOCALIZATION NOTE: openinapp.specific is the text displayed if there is a single external app +# %S is the name of the app, like "YouTube" or "Picassa" +openinapp.specific=Open in %S App +openinapp.general=Open in Another App + +# Clear Private Data +clearPrivateData.title=Clear Private Data +clearPrivateData.message=Delete your browsing history and settings, including passwords and cookies? + +# LOCALIZATION NOTE (browser.menu.showCharacterEncoding): Set to the string +# "true" (spelled and capitalized exactly that way) to show the "Character +# Encoding" menu in the site menu. Any other value will hide it. Without this +# setting, the "Character Encoding" menu must be enabled via Preferences. +# This is not a string to translate. If users frequently use the "Character Encoding" +# menu, set this to "true". Otherwise, you can leave it as "false". +browser.menu.showCharacterEncoding=false + +# LOCALIZATION NOTE (intl.charsetmenu.browser.static): Set to a series of comma separated +# values for charsets that the user can select from in the Character Encoding menu. +intl.charsetmenu.browser.static=iso-8859-1,utf-8,x-gbk,big5,iso-2022-jp,shift_jis,euc-jp + +# Application Menu +appMenu.more=More + +# Text Selection +selectionHelper.textCopied=Text copied to clipboard + +# Context menu +contextmenu.openInNewTab=Open Link in New Tab +contextmenu.changeInputMethod=Select Input Method +contextmenu.fullScreen=Full Screen + +# Select UI +selectHelper.closeMultipleSelectDialog=Done + +# Web Console API +stacktrace.anonymousFunction= +stacktrace.outputMessage=Stack trace from %S, function %S, line %S. +timer.start=%S: timer started + +# LOCALIZATION NOTE (timer.end): +# This string is used to display the result of the console.timeEnd() call. +# %1$S=name of timer, %2$S=number of milliseconds +timer.end=%1$S: %2$Sms diff --git a/mobile/android/locales/en-US/chrome/checkbox.dtd b/mobile/android/locales/en-US/chrome/checkbox.dtd new file mode 100644 index 000000000000..68c2883c5168 --- /dev/null +++ b/mobile/android/locales/en-US/chrome/checkbox.dtd @@ -0,0 +1,2 @@ + + diff --git a/mobile/android/locales/en-US/chrome/config.dtd b/mobile/android/locales/en-US/chrome/config.dtd new file mode 100644 index 000000000000..c6402f532269 --- /dev/null +++ b/mobile/android/locales/en-US/chrome/config.dtd @@ -0,0 +1,3 @@ + + + diff --git a/mobile/android/locales/en-US/chrome/config.properties b/mobile/android/locales/en-US/chrome/config.properties new file mode 100644 index 000000000000..89c8590cbc68 --- /dev/null +++ b/mobile/android/locales/en-US/chrome/config.properties @@ -0,0 +1,15 @@ +addPref.title=Add +addPref.selectType=Select type: +addPref.type.string=String +addPref.type.integer=Integer +addPref.type.boolean=Boolean +addPref.enterName=Enter name: + +togglePref.label=Toggle +modifyPref.label=Modify +modifyPref.selectText=Select value for %1$S: +modifyPref.promptText=Enter value for %1$S: +modifyPref.numberErrorTitle=Invalid Value +modifyPref.numberErrorText=The text you entered was not a number + +resetPref.label=Reset diff --git a/mobile/android/locales/en-US/chrome/feedback.dtd b/mobile/android/locales/en-US/chrome/feedback.dtd new file mode 100644 index 000000000000..4cb8b90600ca --- /dev/null +++ b/mobile/android/locales/en-US/chrome/feedback.dtd @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/mobile/android/locales/en-US/chrome/localepicker.properties b/mobile/android/locales/en-US/chrome/localepicker.properties new file mode 100644 index 000000000000..232e3656d97f --- /dev/null +++ b/mobile/android/locales/en-US/chrome/localepicker.properties @@ -0,0 +1,13 @@ +title=Select a language +continueIn=Continue in %S + +# LOCALIZATION NOTE (name): The localized name of this locale. +# Do not just translate the word 'English' +name=English +choose=Choose a different language +chooseLanguage=Choose a Language +cancel=Cancel +continue=Continue +installing=Installing %S +installerror=Error installing language +loading=Loading… diff --git a/mobile/android/locales/en-US/chrome/notification.dtd b/mobile/android/locales/en-US/chrome/notification.dtd new file mode 100644 index 000000000000..0539d6c4cede --- /dev/null +++ b/mobile/android/locales/en-US/chrome/notification.dtd @@ -0,0 +1,8 @@ + + + diff --git a/mobile/android/locales/en-US/chrome/overrides/appstrings.properties b/mobile/android/locales/en-US/chrome/overrides/appstrings.properties new file mode 100644 index 000000000000..87e9538f7a69 --- /dev/null +++ b/mobile/android/locales/en-US/chrome/overrides/appstrings.properties @@ -0,0 +1,68 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +malformedURI=The URL is not valid and cannot be loaded. +fileNotFound=Firefox can't find the file at %S. +dnsNotFound=Firefox can't find the server at %S. +protocolNotFound=Firefox doesn't know how to open this address, because the protocol (%S) isn't associated with any program. +connectionFailure=Firefox can't establish a connection to the server at %S. +netInterrupt=The connection to %S was interrupted while the page was loading. +netTimeout=The server at %S is taking too long to respond. +redirectLoop=Firefox has detected that the server is redirecting the request for this address in a way that will never complete. +## LOCALIZATION NOTE (confirmRepostPrompt): In this item, don't translate "%S" +confirmRepostPrompt=To display this page, %S must send information that will repeat any action (such as a search or order confirmation) that was performed earlier. +resendButton.label=Resend +unknownSocketType=Firefox doesn't know how to communicate with the server. +netReset=The connection to the server was reset while the page was loading. +notCached=This document is no longer available. +netOffline=Firefox is currently in offline mode and can't browse the Web. +isprinting=The document cannot change while Printing or in Print Preview. +deniedPortAccess=This address uses a network port which is normally used for purposes other than Web browsing. Firefox has canceled the request for your protection. +proxyResolveFailure=Firefox is configured to use a proxy server that can't be found. +proxyConnectFailure=Firefox is configured to use a proxy server that is refusing connections. +contentEncodingError=The page you are trying to view cannot be shown because it uses an invalid or unsupported form of compression. +unsafeContentType=The page you are trying to view cannot be shown because it is contained in a file type that may not be safe to open. Please contact the website owners to inform them of this problem. +externalProtocolTitle=External Protocol Request +externalProtocolPrompt=An external application must be launched to handle %1$S: links.\n\n\nRequested link:\n\n%2$S\n\nApplication: %3$S\n\n\nIf you were not expecting this request it may be an attempt to exploit a weakness in that other program. Cancel this request unless you are sure it is not malicious.\n +#LOCALIZATION NOTE (externalProtocolUnknown): The following string is shown if the application name can't be determined +externalProtocolUnknown= +externalProtocolChkMsg=Remember my choice for all links of this type. +externalProtocolLaunchBtn=Launch application +malwareBlocked=The site at %S has been reported as an attack site and has been blocked based on your security preferences. +phishingBlocked=The website at %S has been reported as a web forgery designed to trick users into sharing personal or financial information. +cspFrameAncestorBlocked=This page has a content security policy that prevents it from being embedded in this way. +corruptedContentError=The page you are trying to view cannot be shown because an error in the data transmission was detected. +remoteXUL=This page uses an unsupported technology that is no longer available by default in Firefox. diff --git a/mobile/android/locales/en-US/chrome/overrides/netError.dtd b/mobile/android/locales/en-US/chrome/overrides/netError.dtd new file mode 100644 index 000000000000..10687d275592 --- /dev/null +++ b/mobile/android/locales/en-US/chrome/overrides/netError.dtd @@ -0,0 +1,172 @@ + +%brandDTD; + + + + + + + + + + + + + + +
  • Check the address for typing errors such as + ww.example.com instead of + www.example.com
  • +
  • If you are unable to load any pages, check your device's data or Wi-Fi connection.
  • + +"> + + + +
  • Check the file name for capitalization or other typing errors.
  • +
  • Check to see if the file was moved, renamed or deleted.
  • + +"> + + + +&brandShortName; can't load this page for some reason.

    +"> + + + +
  • Web addresses are usually written like + http://www.example.com/
  • +
  • Make sure that you're using forward slashes (i.e. + /).
  • + +"> + + + + + +The requested document is not available in &brandShortName;'s cache.

    • As a security precaution, &brandShortName; does not automatically re-request sensitive documents.
    • Click Try Again to re-request the document from the website.
    "> + + + +
  • Try again. &brandShortName; will attempt to open a connection and reload the page.
  • + +"> + + + +
  • Please contact the website owners to inform them of this problem.
  • + +"> + + + +
  • Please contact the website owners to inform them of this problem.
  • + +"> + + + + + + + + + +
  • You might need to install other software to open this address.
  • + +"> + + + +
  • Check the proxy settings to make sure that they are correct.
  • +
  • Contact your network administrator to make sure the proxy server is + working.
  • + +"> + + + +
  • Check the proxy settings to make sure that they are correct.
  • +
  • Check to make sure your device has a working data or Wi-Fi connection.
  • + +"> + + + +
  • This problem can sometimes be caused by disabling or refusing to accept + cookies.
  • + +"> + + + +
  • Check to make sure your system has the Personal Security Manager + installed.
  • +
  • This might be due to a non-standard configuration on the server.
  • + +"> + + + +
  • The page you are trying to view can not be shown because the authenticity of the received data could not be verified.
  • +
  • Please contact the website owners to inform them of this problem. Alternatively, use the command found in the help menu to report this broken site.
  • + +"> + + + +
  • This could be a problem with the server's configuration, or it could be +someone trying to impersonate the server.
  • +
  • If you have connected to this server successfully in the past, the error may +be temporary, and you can try again later.
  • + +"> + + +
  • The site could be temporarily unavailable or too busy. Try again in a few moments.
  • +
  • If you are unable to load any pages, check your mobile device's data or Wi-Fi connection.
  • + +"> + + +&brandShortName; prevented this page from loading in this way because the page has a content security policy that disallows it.

    "> + + +The page you are trying to view cannot be shown because an error in the data transmission was detected.

    • Please contact the website owners to inform them of this problem.
    "> + + + + + + + +You should not add an exception if you are using an internet connection that you do not trust completely or if you are not used to seeing a warning for this server.

    + + + +"> + + +
    • Please contact the website owners to inform them of this problem.

    "> + diff --git a/mobile/android/locales/en-US/chrome/overrides/passwordmgr.properties b/mobile/android/locales/en-US/chrome/overrides/passwordmgr.properties new file mode 100644 index 000000000000..f907d4437f53 --- /dev/null +++ b/mobile/android/locales/en-US/chrome/overrides/passwordmgr.properties @@ -0,0 +1,74 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Password Manager. +# +# The Initial Developer of the Original Code is +# Brian Ryner. +# Portions created by the Initial Developer are Copyright (C) 2003 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Brian Ryner +# Ehsan Akhgari +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +rememberValue = Use Password Manager to remember this value. +rememberPassword = Use Password Manager to remember this password. +savePasswordTitle = Confirm +# 1st string is product name, 2nd is the username for the login, 3rd is the +# login's hostname. Note that long usernames may be truncated. +saveLoginText = Do you want %1$S to remember the password for "%2$S" on %3$S? +# 1st string is product name, 2nd is the login's hostname +saveLoginTextNoUsername = Do you want %1$S to remember this password on %2$S? +promptNotNowButtonText = Not Now +notifyBarNotNowButtonText = Not Now +notifyBarNotNowButtonAccessKey = +promptNeverForSiteButtonText = Never +notifyBarNeverForSiteButtonText = Never +notifyBarNeverForSiteButtonAccessKey = +promptRememberButtonText = Remember +notifyBarRememberButtonText = Remember +notifyBarRememberButtonAccessKey = +passwordChangeTitle = Confirm Password Change +passwordChangeText = Would you like to change the stored password for %S? +passwordChangeTextNoUser = Would you like to change the stored password for this login? +notifyBarChangeButtonText = Change +notifyBarChangeButtonAccessKey = +notifyBarDontChangeButtonText = Don't Change +notifyBarDontChangeButtonAccessKey = +userSelectText = Please confirm which user you are changing the password for +hidePasswords=Hide Passwords +hidePasswordsAccessKey=P +showPasswords=Show Passwords +showPasswordsAccessKey=P +noMasterPasswordPrompt=Are you sure you wish to show your passwords? +removeAllPasswordsPrompt=Are you sure you wish to remove all passwords? +removeAllPasswordsTitle=Remove all passwords +loginsSpielAll=Passwords for the following sites are stored on your computer: +loginsSpielFiltered=The following passwords match your search: +username=Username +password=Password diff --git a/mobile/android/locales/en-US/chrome/phishing.dtd b/mobile/android/locales/en-US/chrome/phishing.dtd new file mode 100644 index 000000000000..5299348f440a --- /dev/null +++ b/mobile/android/locales/en-US/chrome/phishing.dtd @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + has been reported as an attack page and has been blocked based on your security preferences."> +Attack pages try to install programs that steal private information, use your computer to attack others, or damage your system.

    Some attack pages intentionally distribute harmful software, but many are compromised without the knowledge or permission of their owners.

    "> + + + +These types of web forgeries are used in scams known as phishing attacks, in which fraudulent web pages and emails are used to imitate sources you may trust.

    "> diff --git a/mobile/android/locales/en-US/chrome/preferences.dtd b/mobile/android/locales/en-US/chrome/preferences.dtd new file mode 100644 index 000000000000..04b2e5fe0e7e --- /dev/null +++ b/mobile/android/locales/en-US/chrome/preferences.dtd @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/locales/en-US/chrome/prompt.dtd b/mobile/android/locales/en-US/chrome/prompt.dtd new file mode 100644 index 000000000000..c1251f1c4989 --- /dev/null +++ b/mobile/android/locales/en-US/chrome/prompt.dtd @@ -0,0 +1,2 @@ + + diff --git a/mobile/android/locales/en-US/chrome/region.properties b/mobile/android/locales/en-US/chrome/region.properties new file mode 100644 index 000000000000..2a7a85234bc3 --- /dev/null +++ b/mobile/android/locales/en-US/chrome/region.properties @@ -0,0 +1,32 @@ +# Default search engine +browser.search.defaultenginename=Google + +# Search engine order (order displayed in the search bar dropdown)s +browser.search.order.1=Google + +# This is the default set of web based feed handlers shown in the reader +# selection UI +browser.contentHandlers.types.0.title=My Yahoo +browser.contentHandlers.types.0.uri=http://add.my.yahoo.com/rss?url=%s +browser.contentHandlers.types.1.title=Google +browser.contentHandlers.types.1.uri=http://fusion.google.com/add?feedurl=%s + +# Keyword URL (for location bar searches) +keyword.URL=http://www.google.com/search?ie=UTF-8&oe=UTF-8&sourceid=navclient&gfns=1&q= + +# increment this number when anything gets changed in the list below. This will +# cause Firefox to re-read these prefs and inject any new handlers into the +# profile database. Note that "new" is defined as "has a different URL"; this +# means that it's not possible to update the name of existing handler, so +# don't make any spelling errors here. +gecko.handlerService.defaultHandlersVersion=2 + +# The default set of protocol handlers for webcal: +gecko.handlerService.schemes.webcal.0.name=30 Boxes +gecko.handlerService.schemes.webcal.0.uriTemplate=http://30boxes.com/external/widget?refer=ff&url=%s + +# The default set of protocol handlers for mailto: +gecko.handlerService.schemes.mailto.0.name=Yahoo! Mail +gecko.handlerService.schemes.mailto.0.uriTemplate=http://compose.mail.yahoo.com/?To=%s +gecko.handlerService.schemes.mailto.1.name=Gmail +gecko.handlerService.schemes.mailto.1.uriTemplate=https://mail.google.com/mail/?extsrc=mailto&url=%s diff --git a/mobile/android/locales/en-US/chrome/sync.dtd b/mobile/android/locales/en-US/chrome/sync.dtd new file mode 100644 index 000000000000..a532c6b10fd4 --- /dev/null +++ b/mobile/android/locales/en-US/chrome/sync.dtd @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/locales/en-US/chrome/sync.properties b/mobile/android/locales/en-US/chrome/sync.properties new file mode 100644 index 000000000000..541ee1144653 --- /dev/null +++ b/mobile/android/locales/en-US/chrome/sync.properties @@ -0,0 +1,29 @@ +# Mobile Sync + +# %S is the date and time at which the last sync successfully completed +lastSync2.label=Last sync: %S +lastSyncInProgress2.label=Last sync: in progress… + +# %S is the username logged in +account.label=Account: %S +notconnected.label=Not connected +connecting.label=Connecting… + +notificationDisconnect.label=Your Firefox Sync account has been removed +notificationDisconnect.button=Undo + +# LOCALIZATION NOTE (sync.clientUpdate, sync.remoteUpdate): +# #1 is the "application name" +# #2 is the "version" +sync.update.client=#1 #2 is not compatible with the latest version of Firefox Sync. Please update to the latest version. +sync.update.remote=#1 #2 is not compatible with older versions of Firefox Sync. Please update Firefox on your other computer(s). +sync.update.title=Firefox Sync +sync.update.button=Learn More +sync.update.close=Close +sync.setup.error.title=Cannot Setup Sync +sync.setup.error.network=No internet connection available +sync.setup.error.nodata=%S could not connect to Sync. Would you like to try again? +sync.setup.tryagain=Try again +sync.setup.manual=Manual setup + +sync.message.notabs=No tabs from your other computers. diff --git a/mobile/android/locales/en-US/chrome/webapps.dtd b/mobile/android/locales/en-US/chrome/webapps.dtd new file mode 100644 index 000000000000..f69f1c7a66b0 --- /dev/null +++ b/mobile/android/locales/en-US/chrome/webapps.dtd @@ -0,0 +1,7 @@ + + + + + + + diff --git a/mobile/android/locales/en-US/crashreporter/crashreporter-override.ini b/mobile/android/locales/en-US/crashreporter/crashreporter-override.ini new file mode 100644 index 000000000000..2ee9649d5fd2 --- /dev/null +++ b/mobile/android/locales/en-US/crashreporter/crashreporter-override.ini @@ -0,0 +1,10 @@ +# This file is in the UTF-8 encoding +[Strings] +# LOCALIZATION NOTE (CrashReporterProductErrorText2): %s is replaced with another string containing detailed information. +CrashReporterProductErrorText2=Firefox has crashed. Unfortunately the crash reporter is unable to submit a crash report.\n\nDetails: %s +CrashReporterDescriptionText2=Firefox has crashed. Your tabs will be listed on the Firefox Start page when you restart.\n\nPlease help us fix the problem! +# LOCALIZATION NOTE (CheckSendReport): The %s is replaced with the vendor name. +CheckSendReport=Send %s a crash report +CheckIncludeURL=Include the page address +Quit2=Quit Firefox +Restart=Restart Firefox diff --git a/mobile/android/locales/en-US/defines.inc b/mobile/android/locales/en-US/defines.inc new file mode 100644 index 000000000000..24f45813a28a --- /dev/null +++ b/mobile/android/locales/en-US/defines.inc @@ -0,0 +1,9 @@ +#filter emptyLines + +#define MOZ_LANGPACK_CREATOR mozilla.org + +# If non-English locales wish to credit multiple contributors, uncomment this +# variable definition and use the format specified. +# #define MOZ_LANGPACK_CONTRIBUTORS Joe Solon Suzy Solon + +#unfilter emptyLines diff --git a/mobile/android/locales/en-US/installer/setup.ini b/mobile/android/locales/en-US/installer/setup.ini new file mode 100644 index 000000000000..1ed7de333fd8 --- /dev/null +++ b/mobile/android/locales/en-US/installer/setup.ini @@ -0,0 +1,17 @@ +; This file is in the UTF-8 encoding +[Strings] +AppShortName=%MOZ_APP_DISPLAYNAME% +AppLongName=Mozilla %MOZ_APP_DISPLAYNAME% +WindowCaption=Mozilla %MOZ_APP_DISPLAYNAME% Setup +InstallTo=Install %MOZ_APP_DISPLAYNAME% to +Install=Install +Cancel=Cancel +InstalledSuccessfully=Mozilla %MOZ_APP_DISPLAYNAME% has been installed successfully. +ExtractionError=Archive extraction error: +ThereWereErrors=There were errors during installation: +CreatingUserProfile=Creating user profile. Please wait... +UninstallCaption=Mozilla %MOZ_APP_DISPLAYNAME% Uninstall +FilesWillBeRemoved=All files will be removed from +AreYouSure=Are you sure? +InstallationNotFound=Mozilla %MOZ_APP_DISPLAYNAME% installation not found. +UninstalledSuccessfully=Mozilla %MOZ_APP_DISPLAYNAME% has been uninstalled successfully. diff --git a/mobile/android/locales/en-US/mobile-l10n.js b/mobile/android/locales/en-US/mobile-l10n.js new file mode 100644 index 000000000000..e9027aaeb93d --- /dev/null +++ b/mobile/android/locales/en-US/mobile-l10n.js @@ -0,0 +1,39 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is the Firefox browser. +# +# The Initial Developer of the Original Code is +# Benjamin Smedberg +# Portions created by the Initial Developer are Copyright (C) 2004 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +#filter substitution + +pref("general.useragent.locale", "@AB_CD@"); diff --git a/mobile/android/locales/en-US/profile/bookmarks.inc b/mobile/android/locales/en-US/profile/bookmarks.inc new file mode 100644 index 000000000000..85091c79c041 --- /dev/null +++ b/mobile/android/locales/en-US/profile/bookmarks.inc @@ -0,0 +1,30 @@ +#filter emptyLines + +# LOCALIZATION NOTE: The 'en-US' strings in the URLs will be replaced with +# your locale code, and link to your translated pages as soon as they're +# live. + +# LOCALIZATION NOTE: Some of these URLs are currently 404s, but should be coming +# online shortly. + +# LOCALIZATION NOTE (bookmarks_title): +# title for the folder that will contains the default bookmarks +#define bookmarks_title Mobile + +# LOCALIZATION NOTE (bookmarks_aboutBrowser): +# link title for about:fennec +#define bookmarks_aboutBrowser Firefox: About your browser + +# LOCALIZATION NOTE (bookmarks_addons): +# link title for https://addons.mozilla.org/en-US/mobile +#define bookmarks_addons Firefox: Customize with add-ons + +# LOCALIZATION NOTE (bookmarks_support): +# link title for https://support.mozilla.com/mobile +#define bookmarks_support Firefox: Support + +# LOCALIZATION NOTE (bookmarks_aboutHome): +# link title for about:home +#define bookmarks_aboutHome Firefox Start + +#unfilter emptyLines diff --git a/mobile/android/locales/en-US/searchplugins/amazondotcom.xml b/mobile/android/locales/en-US/searchplugins/amazondotcom.xml new file mode 100644 index 000000000000..a58e4c46e1ee --- /dev/null +++ b/mobile/android/locales/en-US/searchplugins/amazondotcom.xml @@ -0,0 +1,12 @@ + +Amazon.com +ISO-8859-1 +data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2ZJREFUeNrEV01IFGEYfndXNzq0QdalJOoUbQiBUuopD2k/GFSG/Vxyq0OHbCUjwn7AEooUzUNRkdkhS1o9JBVaUDctKqhoRQi0WjtlxG6XVtfpfb6Z2caZ2ZnZXZdeeJxvx+973+f9+775XJIkkUaWMcoYG5TxfMpvxlvGMGM8+RYEFOxn/JJyLzOMiwwv7LqUCNQwHjpxIxKJ0Gg4LABZUVhIfr+f1jLSlNuMwyCwEHoZS6xmvxoZoc6Oq+JpJoVMpOncWdpcWZkOiRIQ2MKDp1az+kIhOtV40pHG6zdvpEOixc1/VtqFvKX5gmOX0pkL2yDgsZpxt+sORaPROe8Q6ncf3tPAk8eG3Ks14lA8brsZYZ2yukBAwOfzCeMHA3WGNXrCVpLnJKeqRyhAfX5RfNmILQF4urG0VIzxBBkU5aQI9agIeU4JqNLZ0UH9ob6sDWZE4MDefSn7P1txO/FcbxypQG18nhinew/u5zYC3dyG+qLL1qjjCKDg9C21q2a3oe9zRsCsn2PR2JzfKEy9PB96Nj8E0IJm54IaGZwPZsWJOU4jY1kD2OlAQhsJjKu3bSe7yPUzifpgMPsuOBY8brtR1evmyFt0IL0IzH4fJtcCH7kK1hn2/hh71G1yKKEdz/DBBIOTkUkRemzVl1uvCGKzE4OMIaFbiv1LSX51L7mXl4kxvgeO8vMaJk0PHiHvjl4DCTWs2lMOX0L6cwD/Bxlp6hNNv2gUT9MjsLiB8koaMOxJRgCMgPijWsqvaCP3qqqUZ4JVzUASYyFyL/WTqyggPxWH4qGtBlJzagCGIYjEzJv2zHe38vOUt6mNPGv20OyPMMV7yuVwL5IjBlLmRej1UX7VLdmLt+2CMVKTiSDn0wO1NPPyBOtdLL+MyzWkTbGhC5AGsBdKkEtWAjglgjUwCq/FGjhV0ZosdOhXI5FyH0DoIIK9slB0CS8UCgr8AlpvpamwqHpttcO4WtSqA57ioKENE05IqIYSY46uD4Is0qmGG2s9RYeS7adI3Paz3K6lTMmzIdFmXp/d1Gb1YvLF7i4IzxMfu1ITYWOe1VWix7U5tlKpXkzwYycONqfVLcW+cU7lQ0jePf360DqRS4zT/+Ny+ofRxPBoL6fa6zmu5uvtbkwZyE/lev6a8VV9+VeAAQADg36zc4GRNAAAAABJRU5ErkJggg== + + + + + + +http://www.amazon.com/ + diff --git a/mobile/android/locales/en-US/searchplugins/google.xml b/mobile/android/locales/en-US/searchplugins/google.xml new file mode 100644 index 000000000000..721939748da3 --- /dev/null +++ b/mobile/android/locales/en-US/searchplugins/google.xml @@ -0,0 +1,15 @@ + +Google +UTF-8 +data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABvxJREFUeNqUV1tsFVUUXWdm7qMPaKEUChJekgBioFEehhJBEkUjQQkGP5QPSSDCD4nhYZAPYiQaQyTyowJRCBoxEhOx+IMi5aFG5VEIELGFUiilFFrae3sf8zjHfc7M3Dtz70VkyOYMc4ez1l57n733MCEE6FJ/yevg+T5s+qETlgOYpg0zbWJpfRV2vP4oCq/uFSsh9uxBmeCIgsOgZ8wzf1Pahn4BbM8sMtP7fZQQTAuCn76RxruH70KPx6HFYmCe7f+rF5/+fLOIQO3nu6DNfwYOy8OyAhLBNWiSyDXGhBbcsPFiEiISAQwyfyWTz94/1IFS1+Dt22gznbxkeU+CXj3gWYjA9Xs2HMFINgYuoDblJJJgOnqzAl+e7CraIDp9GlBfD5tU8Encz1Di3yECtsNh2VyttiPgSCMmihC9+v3puyVVMObPIxU0FeuHMUnCCBGgzDNNh7wWlIQCtu0QCe6SoLePXrxXkgCqq3JJVpiE/0UAhQRGVOowsxlwTVcK2FINIiFXqUZvximJL6W34YZOBkELyMwLToETsKIQzBpTBjNjEQkLFh1BZZbjKsHFfdIIyLRdow2JsDKEzCkALvwtRGDh1CqUMcclIWsAgVsUEhka0FlnojSBRNNx8lKEzrpVcG8VPPctFIKqMh0rG2rw3o+36GxrsMlrLoPPCZw7ePmJoUXgt/d+BbPtKjJ1MVxpGIqeEXG0T6iASbqb9H+NhI1RrUmMupLEpAt9iA7YOSKqPggRdqsvZWPGlrO40mOraMqYSvChMY6f3p6OaWMH5ZP2Xh++WTUfF5dUIjk6RpsKZIl0loBlumTkartrWj0TmHOkC0sOtKP6TpaqJxHoHbBEdXlICDS3J7Fi12Wcuz6gZH+JPN/22kSMHRbPvZOykvjwxAbctVtQVkZKaZzAPVAf3LtX4LYk4ZGh+zlNXXhrdwtY7apjYvfKyVg8oxb/92rrbcXWY+upCiUUeJQKpjy6pkcgGyCQtgP3niJp716dgj6ivWzHeSzb3oxz1xIPBL/S04rNR9YhpSUgDEo8jYCFC2559cIR7ioPjlB/vHvhHc9AOTR0TajS0XjmDg6d6sLYmhjG1USx6MnhWPNicQfc17wPSUGeK3B3Z9vblJRVJKhs5Ej4xj0SXBQUooig1GHugaTmhI6eFDq7+zFvSvV9io573i3h7iY7IfO840I+lySgSNi+InLlyKnjE1EhiPEsYtxEVJiQZKKUyxGyS22l6/7qmWswJFYHqlXI2kwllzQ/8+XRM7kfEo+MKFQkf6/FnDR8i6s1Q4QyOPJ7Ky5eLSZRVzkCnyz8DJMG1yOdoYSi6UKRcFwSMgGl5Um4IbF8RXwluGv68Mde2GI4FgzHVNYwpQYNk4fh+ZmjEY8wTBg9pLgF61E8N34hOvq78DclpSqxKgeE67UXBkkitxY8k8RUNxxXHcHyRdMwa+ooTB4/DA9zbZy9AdNqp2Prnx+A0VGU441qPCLvteWTcERRbpSshKH2nOmGlaZJiELDjEGIVz9e8r2dzXux88IXNDmRV0y4igS8thw3HLJKmo67ilIEuNWPVHcTMj1nkLl3DproA7M7ILI3qUMyDJq4CTVT15ck8crBlbiUuKxqg+yMjggrYAXAg401RyDdfRI951bDEB2IxsqhGzp0MUCsknDooJtZA6lUBJGRyzHyqY+LCBxuO4E3f3lHqeAw7qkALxThQhXs6qoJpG7/iq4Ti1FZYSIep2NIVU4NWHLaJdM0akoGVYAox0D71+iwdTwy96MQgWfHzUWGToQKgeb1fy8MPgleYmpV88Ct39YiTgBRXdB8S4WFE7CjQ5Y6oXYjAlStDNpcvpO6egADnX8UqSBrg+p+Xm3IqCMpVALyoimVKdPM5A3wgesEDAUsCJSTh45FZhveqqtn1PJIHY3IGki0HCyejEJd0K0L0vvQp49qDCxXjw0rQVkudPJUggj1I3eVd5sHsRSSGIEzHiGicVIiCid5u1gBb4DhKJhMEfQ6MCzKHKgYOZs2LicCpLRFkmuO6gn5WMkoUWCEQUYfKYJmAlGGyjELQuDf/3NCtWSBEsAoAc5ZPgcGjX+VwCtJ/gpiUe4aL1PENEFGq7wHryCViCxqUTF2fojAjlPfEleRl9sHksAByd2PxUAI5PvDZm1EJw0ZZuICfZHZ0Gm6Ycx3QHNDRAo4tmxTNRg8Yy2MyroceFP7WRztOAM1YyHvXdGnkAgQ8d7J1QFuJpFsaUSqtRFO32VoJCdjzJOOEtCoQnT00xhc/0YIvLmrBQv2r0Uv71dFSPVmVvg9xvKfQkE1PAJy7ZXfN6GM7jzjJRIdlWglokMnFiVdU9tZLP1uswfOVQhccFbsPWd5QtwNvlh3nP0rwACsnu1i3ddmfAAAAABJRU5ErkJggg== + + + + + + + + + +http://www.google.com/m + diff --git a/mobile/android/locales/en-US/searchplugins/list.txt b/mobile/android/locales/en-US/searchplugins/list.txt new file mode 100644 index 000000000000..f241bfebff64 --- /dev/null +++ b/mobile/android/locales/en-US/searchplugins/list.txt @@ -0,0 +1,4 @@ +amazondotcom +google +twitter +wikipedia diff --git a/mobile/android/locales/en-US/searchplugins/twitter.xml b/mobile/android/locales/en-US/searchplugins/twitter.xml new file mode 100644 index 000000000000..2609568cb3c5 --- /dev/null +++ b/mobile/android/locales/en-US/searchplugins/twitter.xml @@ -0,0 +1,9 @@ + + Twitter + data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAADTppVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+Cjx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDQuMi4yLWMwNjMgNTMuMzUyNjI0LCAyMDA4LzA3LzMwLTE4OjA1OjQxICAgICAgICAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgeG1sbnM6eG1wUmlnaHRzPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIKICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgIHhtbG5zOklwdGM0eG1wQ29yZT0iaHR0cDovL2lwdGMub3JnL3N0ZC9JcHRjNHhtcENvcmUvMS4wL3htbG5zLyIKICAgeG1wUmlnaHRzOldlYlN0YXRlbWVudD0iIgogICBwaG90b3Nob3A6QXV0aG9yc1Bvc2l0aW9uPSIiPgogICA8ZGM6cmlnaHRzPgogICAgPHJkZjpBbHQ+CiAgICAgPHJkZjpsaSB4bWw6bGFuZz0ieC1kZWZhdWx0Ii8+CiAgICA8L3JkZjpBbHQ+CiAgIDwvZGM6cmlnaHRzPgogICA8ZGM6Y3JlYXRvcj4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkvPgogICAgPC9yZGY6U2VxPgogICA8L2RjOmNyZWF0b3I+CiAgIDxkYzp0aXRsZT4KICAgIDxyZGY6QWx0PgogICAgIDxyZGY6bGkgeG1sOmxhbmc9IngtZGVmYXVsdCI+dHdpdHRlci10LXNvbG88L3JkZjpsaT4KICAgIDwvcmRmOkFsdD4KICAgPC9kYzp0aXRsZT4KICAgPHhtcFJpZ2h0czpVc2FnZVRlcm1zPgogICAgPHJkZjpBbHQ+CiAgICAgPHJkZjpsaSB4bWw6bGFuZz0ieC1kZWZhdWx0Ii8+CiAgICA8L3JkZjpBbHQ+CiAgIDwveG1wUmlnaHRzOlVzYWdlVGVybXM+CiAgIDxJcHRjNHhtcENvcmU6Q3JlYXRvckNvbnRhY3RJbmZvCiAgICBJcHRjNHhtcENvcmU6Q2lBZHJFeHRhZHI9IiIKICAgIElwdGM0eG1wQ29yZTpDaUFkckNpdHk9IiIKICAgIElwdGM0eG1wQ29yZTpDaUFkclJlZ2lvbj0iIgogICAgSXB0YzR4bXBDb3JlOkNpQWRyUGNvZGU9IiIKICAgIElwdGM0eG1wQ29yZTpDaUFkckN0cnk9IiIKICAgIElwdGM0eG1wQ29yZTpDaVRlbFdvcms9IiIKICAgIElwdGM0eG1wQ29yZTpDaUVtYWlsV29yaz0iIgogICAgSXB0YzR4bXBDb3JlOkNpVXJsV29yaz0iIi8+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz6sMuh4AAADGElEQVR42tRXT2jTYBT/0tR0Sek2RIvr/NMq66RTOj3sMD1UKuxg9SQep8gugruoh6nbaRfBo6CgF93NHqUgooIgKpYhLWKZ3aTVTZG6dd26Nm3WNn6vS/QjJjVtuooPHs3H95L36/v/KFEUkRoFnmXs+MeD2YmZIa7imGOh450p1ASi1ABg5QPeDrN/uIcN7LGZ+ttoipPvPmbKr6dm+VB0pfQcgwgbRgAASD7xdHngZiQ3yZcqOVGD4C6ULNwHWeX79bJSuf1aOHu1lnISBADF7+w0AsCsMIgHzC6bfEUQ0Y1IDr1fLVUvD7ab0Vi/FXUwFAKZ03stgRc/hJf4aqFRDygBOMHn8oFUDgTP49Nr6NagbUPYRoOsE8eMQ+XbizhGhHoBMGTAkcplSuTL6Guugrqtpur5iofzz62U3Uq58NL6AgaWwY/JWlljbsRsKf43AJ+DOeNT+f8jiEWFspj/nK1EIGswGNWsMaFNJLBmbyc9OHHYOu7bzpyE9G4pABLIxQPsJagtUoEz5oKJyJouOWXWQIZdns6+AS+2xAIQxJBJMkkZ5mypC8hMkjKMaXkM1KJNBwBx8M8AyEFIdlKpnRvLgvP7WOSy0TVl7KzpV7ECgqIEBQmqomEAoPzQNv2vgvJHyeIDaYZIGQZQDyWz5ci9GT6oNcD8n1kQ+lLULQstG3rBsKvtnFovULpAAH/JLdnF0dX2q6S36XU0+S6HArstuoIQvnfKaTkbTZfmMYgkGQdKAN++5ytxadBAx7oYlPjEqyoAEMDN7gXJD+lSVD74uxlkpamm9QJozfjHrRkD2DTxO7P8k6wgLsEZkI/u55rWC/QGYezxfDEoH452bUGjvVzrsgBbITqVKASlslmloV0MGuuzNuQOshdAgEOg60nDGA6W6yQIsMTdI+0bZZijG+oFMB9KQ6r2ZkQuKZh9M8ulV2ITCBYZWHrgu5qbkRaIh3P87dViZdGIcmmL+mOVo7S2Y8Wy6oWt6UIPO9S31ezdwZnc5P5QqwnJY7lWL9AFgADilmY6h3K00iDhb4vJTwEGANC+26MCs7qQAAAAAElFTkSuQmCC + + + + UTF-8 + http://mobile.twitter.com/searches + diff --git a/mobile/android/locales/en-US/searchplugins/wikipedia.xml b/mobile/android/locales/en-US/searchplugins/wikipedia.xml new file mode 100644 index 000000000000..171c0f501aef --- /dev/null +++ b/mobile/android/locales/en-US/searchplugins/wikipedia.xml @@ -0,0 +1,14 @@ + +Wikipedia +UTF-8 +data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAACXBIWXMAAAsTAAALEwEAmpwYAAADGGlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjaY2BgnuDo4uTKJMDAUFBUUuQe5BgZERmlwH6egY2BmYGBgYGBITG5uMAxIMCHgYGBIS8/L5UBFTAyMHy7xsDIwMDAcFnX0cXJlYE0wJpcUFTCwMBwgIGBwSgltTiZgYHhCwMDQ3p5SUEJAwNjDAMDg0hSdkEJAwNjAQMDg0h2SJAzAwNjCwMDE09JakUJAwMDg3N+QWVRZnpGiYKhpaWlgmNKflKqQnBlcUlqbrGCZ15yflFBflFiSWoKAwMD1A4GBgYGXpf8EgX3xMw8BSMDVQYqg4jIKAUICxE+CDEESC4tKoMHJQODAIMCgwGDA0MAQyJDPcMChqMMbxjFGV0YSxlXMN5jEmMKYprAdIFZmDmSeSHzGxZLlg6WW6x6rK2s99gs2aaxfWMPZ9/NocTRxfGFM5HzApcj1xZuTe4FPFI8U3mFeCfxCfNN45fhXyygI7BD0FXwilCq0A/hXhEVkb2i4aJfxCaJG4lfkaiQlJM8JpUvLS19QqZMVl32llyfvIv8H4WtioVKekpvldeqFKiaqP5UO6jepRGqqaT5QeuA9iSdVF0rPUG9V/pHDBYY1hrFGNuayJsym740u2C+02KJ5QSrOutcmzjbQDtXe2sHY0cdJzVnJRcFV3k3BXdlD3VPXS8Tbxsfd99gvwT//ID6wIlBS4N3hVwMfRnOFCEXaRUVEV0RMzN2T9yDBLZE3aSw5IaUNak30zkyLDIzs+ZmX8xlz7PPryjYVPiuWLskq3RV2ZsK/cqSql01jLVedVPrHzbqNdU0n22VaytsP9op3VXUfbpXta+x/+5Em0mzJ/+dGj/t8AyNmf2zvs9JmHt6vvmCpYtEFrcu+bYsc/m9lSGrTq9xWbtvveWGbZtMNm/ZarJt+w6rnft3u+45uy9s/4ODOYd+Hmk/Jn58xUnrU+fOJJ/9dX7SRe1LR68kXv13fc5Nm1t379TfU75/4mHeY7En+59lvhB5efB1/lv5dxc+NH0y/fzq64Lv4T8Ffp360/rP8f9/AA0ADzT6lvFdAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAHqSURBVHjapJUxb9NQFIW/JAip2xNS1/J+goeqUqeajdGVmFgwI1OcmYEgITFG7EgJQ+b2HzgbElJF+QU2iBl7YAFRnQ5+fnFcB0J8Jp/r53vPu+f6vYEA4JBTTjhkN/zkio9kAAihpyr1v/ijN7ovEHqiffFeDHTAdx6wL46HnPX4HM6HHNEHR0NGvRKMhvRE7wT3ACYssBggpyThIRMCAFaEzHhOiQWumfGFBQGQY0kB9EKSCsVCyKpQxUMhdOEMj4Vi91wIGX2uyNJtwTDHAgbjOUBJ/T50ETDAzCn0CiRpLkSd2VW1rqZR6uOpzHoWl40EkmkIlTIhNJc0bUSlSNNtCaZCKGtpyDZimXB92uhBhTEAHzx/BuQ8Isb62Dti16c7PahrNnbovFhXLGQaau4oqDSULDx/BcCl55cEDTUdCqqagWcXbjpqWD8ZWxRUGq5ZOTYhAXLHV0DU/qCtQLJCkZuM0PHQGThvL152JJh5M61SP2CpMpmmgdsTFDJCiaaubq0haQ7Q3xLUZq4NqzS0DNzWxNq8ksgbFmGgwf95oFgijJuB6v9L3Fx2HSg3XeExZxv1xuSEXQt/o8fqg9cDHfB15zuxjRuOETrfu/5b9bhcf+mlRmLgr/cTgp1vqR9c8YlvALcDAPr5jIx+4LKcAAAAAElFTkSuQmCC + + + + + + + + +http://en.wikipedia.org/wiki/Special:Search + diff --git a/mobile/android/locales/en-US/searchplugins/yahoo.xml b/mobile/android/locales/en-US/searchplugins/yahoo.xml new file mode 100644 index 000000000000..ed3a4c3085c3 --- /dev/null +++ b/mobile/android/locales/en-US/searchplugins/yahoo.xml @@ -0,0 +1,13 @@ + +Yahoo +UTF-8 +data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAA7xJREFUeNrs10tslFUUB/DfN9NCaUsir2JAQAQKBgQXiFEeC6JdKAQCoiERXKiJrmRhNMZH3LjQjaILIiqKCWp8BgL4IGpQWkASCY+qWHm0CFjKyz5oO9PO56LfDNNhpmXHwp7ky9w5c+49//ud//nfO0EYhq6nxVxnGwAwAOC6Ayh6IVjfX8wEzMBMlCDdt2fwM+rR1s8a03AIR3AAv+EYGov6mLQKq3FPP4s34AN8gt8LxAxBEaZHT9rq85XgiWh3H15DchiPl6JdfRQly7XLBebuyAXwBdaFwhtTQt1SmSclFOpXNVeiCVNy/Mk8sQfweTaAT7GsZxhkEnfp1qVbiFAQffZpZVEpKrJ8+absQU2aAw9hRUpK+YhSyzbOVzFjpNbGNsnWpGRnShjS3NBq85PVOiV6vY1AICZQrEhMAHFsxZwoJJEHwHdoKcJ9eDG9UKIlac+6WsMm3mDJW/My0V89+pOTv5zVrsOtd08wbu5o7Rc7BAFDRpQoLok7uOmoprpLYj3dfQfmYRdO5QGwTcTMx9LMDASSiS4Htx3XptPx7aetOfogqFw81jcb9goQL467ac4otz0wCbQ2tavffcb+9+tyebIiApBr9ehMC9G07F8CgVKDDVeu7thJG6u2g+lLJ1n+zAKdkqp31jq1r6lnG0/XeLniPeuXbHG+vkVcPHu5+QV4sjUjREjll8iYcqV27zhs5FPD3L/2LlWv3qnx4L9mrp5o1sop1s/d7EDNX8qUKjeop/q9bWgBAN9nA8grRgHiYsqV+fbNPW5eONr0JbdY9XWVjpaE1yo2aWg6a6gy8TT1rr3/f80+C+oK9VMgUCQmqcsPz+3P+Lc8vsuRpgalSsQKJ4fdeXx/RxzIAHgHfxR6CyG6hVKJ7oy//VynpJSUVF/J4bM8vkO9DiNswbBIz3tZKFQSG2TS+DEql4/L+CcvGisRT2qua3PheHME9So7nF1rjI5a88/soPgCi9OyOBuVvfUzZczM4R7+ssrY2aOc3n/OxRPNSkeWmHrveJdOtvin9mKhIsxGc9Z4MKpxIYugYfC8t3PPgmW9uiGIKR5SpP1yQjKS9Jj4Fc272lJYiJ1ZvjpMjsYfoxaz8G5uByzHGryeWS1M6bjcGdUrniFnDzuCfOr2CM5n+QZlJYdGvNLXjeiNaOVnsRfJtNbHxcQj3gdXkp/AhkhNF+UkF11ifkRr9L06l4SFbC32YWmk6eOyRCtAC2qiW9HW6A6Rz5qjs+Z2TM3tuGDgn9EAgP89gP8GAF3DLhLnfG5QAAAAAElFTkSuQmCC + + + + + + +http://search.yahoo.com/ + diff --git a/mobile/android/locales/en-US/updater/updater.ini b/mobile/android/locales/en-US/updater/updater.ini new file mode 100644 index 000000000000..dfa025730656 --- /dev/null +++ b/mobile/android/locales/en-US/updater/updater.ini @@ -0,0 +1,4 @@ +; This file is in the UTF-8 encoding +[Strings] +TitleText=%MOZ_APP_DISPLAYNAME% Update +InfoText=%MOZ_APP_DISPLAYNAME% is installing your updates and will start in a few moments… diff --git a/mobile/android/locales/filter.py b/mobile/android/locales/filter.py new file mode 100644 index 000000000000..a6db1b06230a --- /dev/null +++ b/mobile/android/locales/filter.py @@ -0,0 +1,69 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla. +# +# The Initial Developer of the Original Code is +# Mozilla Foundation. +# Portions created by the Initial Developer are Copyright (C) 2009 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Axel Hecht +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + + +def test(mod, path, entity = None): + import re + # ignore anything but mobile, which is our local repo checkout name + if mod not in ("netwerk", "dom", "toolkit", "security/manager", + "services/sync", "mobile/android/base", + "mobile"): + return False + + # Ignore Lorentz strings, at least temporarily + if mod == "toolkit" and path == "chrome/mozapps/plugins/plugins.dtd": + if entity.startswith('reloadPlugin.'): return False + if entity.startswith('report.'): return False + + if mod != "mobile": + # we only have exceptions for mobile + return True + if not entity: + return not (re.match(r"searchplugins\/.+\.xml", path) or + re.match(r"mobile-l10n.js", path) or + re.match(r"defines.inc", path)) + if path == "defines.inc": + return entity != "MOZ_LANGPACK_CONTRIBUTORS" + + if path != "chrome/region.properties": + # only region.properties exceptions remain, compare all others + return True + + return not (re.match(r"browser\.search\.order\.[1-9]", entity) or + re.match(r"browser\.contentHandlers\.types\.[0-5]", entity) or + re.match(r"gecko\.handlerService\.schemes\.", entity) or + re.match(r"gecko\.handlerService\.defaultHandlersVersion", entity)) diff --git a/mobile/android/locales/generic/install.rdf b/mobile/android/locales/generic/install.rdf new file mode 100644 index 000000000000..3f7091d8d3bc --- /dev/null +++ b/mobile/android/locales/generic/install.rdf @@ -0,0 +1,62 @@ + + + + + +#ifdef MOZ_LANGPACK_CONTRIBUTORS + @MOZ_LANGPACK_CONTRIBUTORS@ +#endif + + + + {a23983c0-fd0e-11dc-95ff-0800200c9a66} + @MOZ_APP_VERSION@ + @MOZ_APP_VERSION@ + + + + diff --git a/mobile/android/locales/generic/profile/bookmarks.json.in b/mobile/android/locales/generic/profile/bookmarks.json.in new file mode 100644 index 000000000000..c188e2285b31 --- /dev/null +++ b/mobile/android/locales/generic/profile/bookmarks.json.in @@ -0,0 +1,20 @@ +#filter substitution +{"type":"text/x-moz-place-container","root":"placesRoot","children": + [{"type":"text/x-moz-place-container","title":"@bookmarks_title@","annos":[{"name":"mobile/bookmarksRoot","expires":4,"type":1,"value":1}], + "children": + [ + {"index":1,"title":"@bookmarks_aboutBrowser@", "type":"text/x-moz-place", "uri":"about:firefox", + "iconUri":"chrome://branding/content/favicon32.png" + }, + {"index":2,"title":"@bookmarks_addons@", "type":"text/x-moz-place", "uri":"https://addons.mozilla.org/@AB_CD@/mobile/", + "icon":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAcXSURBVFjDxZdrjF1VFcd/a59z7r1z585M5+10oK19mDbSAoGEUprS0ZgISX3URqNpI5iYGJv4Av1gooYvBASF9JuKNiYmamIwGm3SNgj0AW1KhY6dPoAOnU6ZlnnPfd9zzt7LD/fMdIaWh0jDSXbOvTln7/1f//Vf/72OqCof5WX4iC///5nc96B0hOrvFKP3o9Kt4CM6CewD79HDj9VOvtca8kFTsOn70kMQvHLP+h2tK3o+GXS09AKOC2NneXW437109rlKqVbYevjn0b7rwoD1/d985e5vt/et2+ZVoiLVqIR1luXda2nKtppVN6xr/O2eh/8iIi36LlF+YAAibFjWs9o7NrgXYzx8E1CNyhSrU1TCImt676C9qVvWf290NXD6QwegihdGVapxkXKtgIqi6uoj+Y04CTzkulSBwu/633gxbs/14CRGsSgWh6Ul28pkaZSJmdGLBx6vnbo+ZajmV0cH9sdN2TaMCI4YJxYVS2fzjfS//kLFqjz5oZThpu9Ip017jwqyDQgAjECxUki9NTNEOpUmrJVRFAXSqQynLxxXT/TJjT8MnliAW4hE9a9xZH905El9630BcBnv4RW9a7bfe+f2IBM0IElWxRhiW2O0PIgjBgFFma5c5ltf/Gk2jKo4Z7HqsC7COUctrGQOnNjzteGRcwrc9/5EqPLlz67/ajBeukC+No6IYERAoGYrdUVIXRkKvDHVT2BSKODU4ZxFbR1IY9DCXTd/xv/TyODW9w8AUqkgxVT1EqGtzAMgdTYEVBVBcVIv+ZqLUNU6A85SCfOUagViG7Gy/XaA1P9Uhs5FOGJUYhCDE64AADD16FElchHlME8lzFMOC1SiEuoc6pTAa0CNRbVeAP7bxaYN3i5VPg0UBfl1JPEffHys1gE4YkAp1iaJXIgjwmqMaox1MbGLiF3IrPepQmtjN9255bSkO6nGZaSesYUANvxA2iTtvbT5li2965av9xDpHDh/9KEjA8/+pFQqpJxRnESoWN4snCG05TkGZtMwW9hGJGEDmtNdrGq7gxMnX66MXD5im5tz0tHZ0eikPmMOgOf531y+ZE3X3es+701WLmFEuG31ptTKZatTpdo0NZunagtYDYm0gvHqIhSTrCRXOSUofLx9HcdPHC1fGB56WmLvkbHKxJ1j4xPfMKLdCwCocH93V0/m+PBeanEBRMiHY6iJaQiaiPNVVCxWQ8SrR4khYWC2Lq/YZP2voSHI8ebwxcBW7c4ju+I8MAA8dZURqdK7KNfKaHGIYjiBMYLxPTwEG9Uwnkkcz2KM1COfAwBX1HhlwVzQSr48hYobP7JL8+/qhB6mIZ1qoJKfwWlcT6ZaYmtRF+PiCCcxjhDjJZubhRoQqUevCaWZVCPF8jSIXHpPK3aiMxOly21iDIXqBBgHxtXp9qTOiFffdPZeZ+AKE2/Pv6VGc3MGtbpGHhKjP1P3LmeBnrg8dnFzW0ezjJVifOPT3tRLZ9ONZFNNTFYvMlI6884gZMFJCQpVnaEp20JDY9puKMrX+x6SP1aL/kbf0x1glhx8JOyba8k2PhD0pTKZf9x2y23Z1tY2nFompycYeXOkVCjn+VRfX+PJ8WfAuCsgDLQ19NKRXULab0REqNkSU9URJqrDOOvoyqzEVJs4dvjfpZmZYraxOVNYfGNn7rWBYTn8eGQW9IQbH/A/hydPiZgWVBXDiy7i94ju/sKXtnBm8iBqIowneEZYuuhWCH1eGzxrR86Pl2zszMeWtWVWrVzpZ3NpBmeO46yjLbWERanFqBWcKnEYse+fz9lDj0X+Aic89Iv470DXHT+W9kUR1b2PaAngrgeD3U5jEJ2juyu3gmre8uz+Q2XU7lYnf7ZIaej02Nah06M712++taWna5WMFM4yUbvAWHkIcT7qDDc03jSn1WueBUcf1omrm0C3QO3N6Q4OPH+sqKG97/Av7dPz3nx5w3eDv71yZOBf92y7OydGEKeIgCPGSICqRZKjw7xz0ykiIkZEDMmxOms2Yjx8STE9VUhNXLYvAOlkZIDMmT3R2Uq51uCcJfDSdYOas2vBqQXByezi19jcJJ1PGmgQwcZxSEOQw4iPwSPUCm2duVo6MFuATqArGd09N3v3ZltS5dnVA5PCkwAjPmkvSxTHIMSA718r8mTzXBKRp04OnBw4tfmmtWvE8w3igbOOxUs7m946X9gB7vUkPg8g08L27qWLcrW4Snf6E2igWFWIIY4sp/7zqotr+jyQvpYGJNFGFmgC/PMH3RNxZbz94rkDa+fecICgzspi4Pak/F3dh+SGc/2jOtg/qjhkwUElaG1aTp/bb58BWq76NEsYSAMtCYgg0Yo3L9d+AsMkQ4EYiBIQJPOCec/n+1QMTAHD1/w2FBEvaZmCZGOZx4z/DuKNATtvM3/e3KsaLKAKFP8LDkBq6lXFt0cAAAAASUVORK5CYII=" + }, + {"index":3,"title":"@bookmarks_support@", "type":"text/x-moz-place", "uri":"http://support.mozilla.com/@AB_CD@/mobile", + "icon":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAIYElEQVRYw5VXCVBUVxb9NZUqzWQWlVVl07hk3KKTmLikNMYoIAgiiGyuIBBHDSqOM2piYhCFGI0KLuNCEElRarMKDQ0NguiAiEJAIqKARjQ4fFmCC9Bw5r73f3fTIQTsqst7/fn97rnnrk8AcJdE7I9oNBrx1q1a8dz5HPHLr86IW7YeF4O3HBP/vf2kePCQQszNKxXrnzSK/T2P6RboTwv6+Dx61IAD316Arf1WWI/0hLGZC4aYLuKrsdliGNF+iMkimFsswcTJfghcewAEBhpNZ19Htwgykt/8NDe3YueX32HcxFWkxAXmw91gNcITNm96yeIti7S3HukFC+ulHBgD4+i0DVeulv8eALFXANeLK/GxbTAGGTvToR7dlHl3A+AF6257g+cExnToYg4odE8sOjo0/QegyiyCzShvOsC1h8LhVkthYr4Y5iSWxIi1pRsshrvCjKxm71vYeBi8b0mMDTJyxirfcPzyy/O+AeTmlmAEKR9m6W5A7zBSbEYKZk8PxOYNB3EsWoXvLl7D6YybiEouROTJNKz1C8PUyathQnFhyYCM0oP/q5ET/Pz3obOzq3cAVVUPMXbcSgwl/3W33JisnTHVHzHRSpyuaoT3HQ3Gl2pgXtQOo2ttGHq9E1PKgNVVwPcVTThyUIFxY31gOsyNG6M9iwVqyO6Y3wbAItZ7WSgPoO7I2fdl7juRVvkz3O4BQnYrBFUzhEySLBJ1G+0fQ1DmQEhNw2vqYvjWdiKpoBoO8zbChFjTgmABbEZuy8oq7gmAcpsj7B7hTPl6v71Ie/ISRv99CSGjiRQ26yWbPbsCIdEHQvxHsnwMQeGPsdcroKxpxYK5QZwJGxmEGe3nzN2EtrZ2PYD29g6K+C08zbR0mQ9fAnv6cc7jVlJOlmd2kEIS9XNZ+Qt6dpMU2pPMhZCwQC8MyPmFBKIM6tJHeHvCCp4N2rNNh7lCmV6oB1BQWEFB5s6LjDafLS2XIFFVjEnkWyGN0RtJaywpLSflbZL1qlKyfgkp/FBW7kBiK60M1AUfON97htgYNQ9gLbMsi7x8dpPqLgnAZzujeDXT0s/SKWB5CI49IeWpqdJh8bNIZkuHK89IDDAQWQ8gpOyi/82X/pe4VAbiIL2fdhxn7ndi3qz1lMLu/HwWC6PfWo779+tFgQqEaO/wL065jn6iKCWlEDPKyecKOjB+nv5QZiGzOEMtMaFmQNohJK+n/y0khm4TCE96x1Z6V+EGh8pGhO2Jo/R0MQju8xdyRaGurkF8a/xKKW9Z4aB18sSVUFY24fVLBd3862Do4+QgKSPULXIwXoWQriR2FPI79tKqmAOLghx8ryynguWmcwNj/PMvokSh/FaNyCLUaqSnXOncYTt7PU5Ud0hWMmU6AHb0fY4kCY6StdksKImpbHJXukqOB3s9YMWHGJj1HyRXNHLDLG08dXGwdt1BUSguviNa2CzlgccAsCLkYh+MwzUaAlAs079AojRxBSlJJp/vlGIiI192Q7PkhoshpHC6DKAbW2m7oPypDTPfD5SzwYvqw2JWnkXh5s0q0WqEhy4DhlIJdrLdjKO1z6VCw3zLg5AkZatkKQvA9HRioIL2z2QAz6XvzDU8AOdIbChm47X0fciofYFp7/rr0tHE3BW+a74WhduVD8SRo310bZa9MGtaIM5VN0K41EVWZtOB86UCk+RHyui5ulWymK26wtQk14hWKVXTL5LlUfQ7BwzJPY/kojqMGeWlczWbIzYERYjC06ct4pR3/OWWK6XIKFrzS6thRTVeyKgiAM4S5amH9JT3Ki1yZnRKgBIC8P4PVTgbdxmm5i66ls6qLrVpqQ6sWh3OBw4tCLY/+u05fFpHdCvv0SFO5H9XsqxGXwn7FAZEpLhQIuxRFzasCZfbuxRrvCeoiyUAioTLvM//fWoAr9WsJsyeuRZ5dS0YkEsKE4n6lO1yKe6H8kxZyB2TbgGFP1KXHWPo5klT1qChoVkC0Nr6AumqaygoqMCJkxf5UDHgj3bYHxqD0AZiIYkqX8pe2e+/o1hFcXCpFeNLNAi804bD5Q1QldTgk4B9vCtqCx1j+NONEYbdMCw8jveDU1FKZOfc4LX6HYqNtOR8bHpIilOpKGW19GJxE/6Q+wzuVZ2Iud2A4zGZ8PX5Ch/QDDGaAq/7cMNYYAyUlNw1BKBIyJOGSfLN1/vikJFxDersGzjwTRxuFP2I4LsaDGDKWEtWaUX6PqboJWIq/4eI/XH44D1//XhGin49xDLrl6/a23MeYOPSIBqbWD1gVYqBmfJuAE6cSsXVK2VsZEE5TVO+NYBjZRfsSRwqOxFQC2RX1cNu1joMpgGWxZLh1KzfMyb+NmElqmse9wTw08MnsF2wFUZmLrofssMYqMORCXjwoB5pSXk4EBaL8NCzCKdJd09INLZtjsC8j4J48HafpAwBeNNw6sFZUSjyep8J6+ufwo4642B5MmJV0dF5O5/tWYke+IY9D86Bb9jxVbv/y+CFPHOseyj35qBYg2PN59Dh+L6nYkoNCsAQXihYUL43/R/Izy/DN/vPIYQsp2sYtvzzGL7YFY09e2Oxm5jY/tkpeHqH4M0xy+SeorecxRSj/sjRpP7fC9glgu59PBaYX6fNWIeAT/aD7oF0U4qWgGw7gY2bIhFEsuPz04g5q8L0met0zYatg03ot/SMCs6r34zYJ01ZAKdFOziFA/9kj9dJ/jzIkbPDwGnFWBf1HjzfWZ2f8LYvdhAzDQ1NfV7N+ryc3qH7QuSRRM7C3PnBumLC/M7ugEZmEpCx41bQaL8bZ2JUoFsy+vHhl9O7r3CdZhO0+ENZtUhuEB0WbhMpaMUNGyPE+ITLYu39n1/las6v5/8HR4lI9EcypCoAAAAASUVORK5CYII=" + }, + {"index":4,"title":"@bookmarks_aboutHome@", "type":"text/x-moz-place", "uri":"about:home", + "iconUri":"chrome://branding/content/favicon32.png" + } + ] + }] +} diff --git a/mobile/android/locales/jar.mn b/mobile/android/locales/jar.mn new file mode 100644 index 000000000000..3fd7029d0fc1 --- /dev/null +++ b/mobile/android/locales/jar.mn @@ -0,0 +1,33 @@ +#filter substitution + +@AB_CD@.jar: +% locale browser @AB_CD@ %locale/@AB_CD@/browser/ + locale/@AB_CD@/browser/about.dtd (%chrome/about.dtd) + locale/@AB_CD@/browser/aboutAddons.dtd (%chrome/aboutAddons.dtd) + locale/@AB_CD@/browser/aboutAddons.properties (%chrome/aboutAddons.properties) + locale/@AB_CD@/browser/aboutCertError.dtd (%chrome/aboutCertError.dtd) + locale/@AB_CD@/browser/browser.dtd (%chrome/browser.dtd) + locale/@AB_CD@/browser/browser.properties (%chrome/browser.properties) + locale/@AB_CD@/browser/config.dtd (%chrome/config.dtd) + locale/@AB_CD@/browser/config.properties (%chrome/config.properties) + locale/@AB_CD@/browser/localepicker.properties (%chrome/localepicker.properties) + locale/@AB_CD@/browser/region.properties (%chrome/region.properties) + locale/@AB_CD@/browser/preferences.dtd (%chrome/preferences.dtd) + locale/@AB_CD@/browser/checkbox.dtd (%chrome/checkbox.dtd) + locale/@AB_CD@/browser/notification.dtd (%chrome/notification.dtd) + locale/@AB_CD@/browser/sync.dtd (%chrome/sync.dtd) + locale/@AB_CD@/browser/sync.properties (%chrome/sync.properties) + locale/@AB_CD@/browser/prompt.dtd (%chrome/prompt.dtd) + locale/@AB_CD@/browser/webapps.dtd (%chrome/webapps.dtd) + locale/@AB_CD@/browser/feedback.dtd (%chrome/feedback.dtd) + locale/@AB_CD@/browser/phishing.dtd (%chrome/phishing.dtd) + locale/@AB_CD@/browser/bookmarks.json (bookmarks.json) + locale/@AB_CD@/browser/searchplugins/list.txt (%searchplugins/list.txt) + +# Fennec-specific overrides of generic strings +* locale/@AB_CD@/browser/netError.dtd (%chrome/overrides/netError.dtd) +% override chrome://global/locale/netError.dtd chrome://browser/locale/netError.dtd +* locale/@AB_CD@/browser/appstrings.properties (%chrome/overrides/appstrings.properties) +% override chrome://global/locale/appstrings.properties chrome://browser/locale/appstrings.properties +* locale/@AB_CD@/browser/passwordmgr.properties (%chrome/overrides/passwordmgr.properties) +% override chrome://passwordmgr/locale/passwordmgr.properties chrome://browser/locale/passwordmgr.properties diff --git a/mobile/android/locales/l10n-central.ini b/mobile/android/locales/l10n-central.ini new file mode 100644 index 000000000000..c89778472fe9 --- /dev/null +++ b/mobile/android/locales/l10n-central.ini @@ -0,0 +1,31 @@ ++[general] +depth = ../.. +source-depth = .. +all = locales/all-locales + +[compare] +dirs = mobile/android +tld = mobile/android + +[includes] +toolkit = toolkit/locales/l10n.ini +services_sync = services/sync/locales/l10n.ini +embedding_android = mobile/android/base/locales/l10n.ini + +[include_toolkit] +type = hg +mozilla = mozilla-central +repo = http://hg.mozilla.org/ +l10n.ini = toolkit/locales/l10n.ini + +[include_services_sync] +type = hg +mozilla = mozilla-central +repo = http://hg.mozilla.org/ +l10n.ini = services/sync/locales/l10n.ini + +[include_embedding_android] +type = hg +mozilla = mozilla-central +repo = http://hg.mozilla.org/ +l10n.ini = mobile/android/base/locales/l10n.ini diff --git a/mobile/android/locales/l10n-mozilla-1.9.2.ini b/mobile/android/locales/l10n-mozilla-1.9.2.ini new file mode 100644 index 000000000000..6943a4cac3dc --- /dev/null +++ b/mobile/android/locales/l10n-mozilla-1.9.2.ini @@ -0,0 +1,17 @@ +[general] +depth = ../.. +source-depth = .. +all = locales/all-locales + +[compare] +dirs = mobile/android +tld = mobile/android + +[includes] +toolkit = toolkit/locales/l10n.ini + +[include_toolkit] +type = hg +mozilla = releases/mozilla-1.9.2 +repo = http://hg.mozilla.org/ +l10n.ini = toolkit/locales/l10n.ini diff --git a/mobile/android/locales/l10n-mozilla-2.0.ini b/mobile/android/locales/l10n-mozilla-2.0.ini new file mode 100644 index 000000000000..b9cfcdb331aa --- /dev/null +++ b/mobile/android/locales/l10n-mozilla-2.0.ini @@ -0,0 +1,31 @@ +[general] +depth = ../.. +source-depth = .. +all = locales/all-locales + +[compare] +dirs = mobile/android +tld = mobile/android + +[includes] +toolkit = toolkit/locales/l10n.ini +services_sync = services/sync/locales/l10n.ini +embedding_android = mobile/android/base/locales/l10n.ini + +[include_toolkit] +type = hg +mozilla = releases/mozilla-2.0 +repo = http://hg.mozilla.org/ +l10n.ini = toolkit/locales/l10n.ini + +[include_services_sync] +type = hg +mozilla = releases/mozilla-2.0 +repo = http://hg.mozilla.org/ +l10n.ini = services/sync/locales/l10n.ini + +[include_embedding_android] +type = hg +mozilla = releases/mozilla-2.0 +repo = http://hg.mozilla.org/ +l10n.ini = mobile/android/base/locales/l10n.ini diff --git a/mobile/android/locales/l10n.ini b/mobile/android/locales/l10n.ini new file mode 100644 index 000000000000..b4fbbbab1b21 --- /dev/null +++ b/mobile/android/locales/l10n.ini @@ -0,0 +1,11 @@ +[general] +depth = ../.. +all = mobile/android/locales/all-locales + +[compare] +dirs = mobile/android + +[includes] +toolkit = toolkit/locales/l10n.ini +services_sync = services/sync/locales/l10n.ini +embedding_android = mobile/android/base/locales/l10n.ini diff --git a/mobile/android/locales/maemo-locales b/mobile/android/locales/maemo-locales new file mode 100644 index 000000000000..5609e3a7f81d --- /dev/null +++ b/mobile/android/locales/maemo-locales @@ -0,0 +1,13 @@ +cs +da +de +es-ES +fi +fr +ja +it +nb-NO +nl +pl +pt-PT +ru diff --git a/mobile/android/makefiles.sh b/mobile/android/makefiles.sh new file mode 100644 index 000000000000..117a33af64b9 --- /dev/null +++ b/mobile/android/makefiles.sh @@ -0,0 +1,61 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla. +# +# The Initial Developer of the Original Code is +# the Mozilla Foundation . +# Portions created by the Initial Developer are Copyright (C) 2007 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Finkle +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +add_makefiles " +netwerk/locales/Makefile +dom/locales/Makefile +toolkit/locales/Makefile +security/manager/locales/Makefile +mobile/android/app/Makefile +mobile/android/app/profile/extensions/Makefile +$MOZ_BRANDING_DIRECTORY/Makefile +$MOZ_BRANDING_DIRECTORY/locales/Makefile +mobile/android/chrome/Makefile +mobile/android/chrome/tests/Makefile +mobile/android/components/Makefile +mobile/android/components/build/Makefile +mobile/android/modules/Makefile +mobile/android/installer/Makefile +mobile/android/locales/Makefile +mobile/android/Makefile +mobile/android/themes/core/Makefile" + +if test -n "$MOZ_UPDATE_PACKAGING"; then + add_makefiles " + tools/update-packaging/Makefile + " +fi diff --git a/mobile/android/modules/LocaleRepository.jsm b/mobile/android/modules/LocaleRepository.jsm new file mode 100644 index 000000000000..dae7098b2666 --- /dev/null +++ b/mobile/android/modules/LocaleRepository.jsm @@ -0,0 +1,351 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Mobile Browser. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * Wes Johnston + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +let EXPORTED_SYMBOLS = ["LocaleRepository"]; + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/AddonManager.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); + +// A map between XML keys to LocaleSearchResult keys for string values +// that require no extra parsing from XML +const STRING_KEY_MAP = { + name: "name", + target_locale: "targetLocale", + version: "version", + icon: "iconURL", + homepage: "homepageURL", + support: "supportURL", + strings: "strings" +}; + +var LocaleRepository = { + loggingEnabled: false, + + log: function(aMessage) { + if (this.loggingEnabled) + dump(aMessage + "\n"); + }, + + _getUniqueDescendant: function _getUniqueDescendant(aElement, aTagName) { + let elementsList = aElement.getElementsByTagName(aTagName); + return (elementsList.length == 1) ? elementsList[0] : null; + }, + + _getTextContent: function _getTextContent(aElement) { + let textContent = aElement.textContent.trim(); + return (textContent.length > 0) ? textContent : null; + }, + + _getDescendantTextContent: function _getDescendantTextContent(aElement, aTagName) { + let descendant = this._getUniqueDescendant(aElement, aTagName); + return (descendant != null) ? this._getTextContent(descendant) : null; + }, + + getLocales: function getLocales(aCallback, aFilters) { + let url = Services.prefs.getCharPref("extensions.getLocales.get.url"); + + if (!url) { + aCallback([]); + return; + } + + let buildID = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).QueryInterface(Ci.nsIXULRuntime).appBuildID; + if (aFilters) { + if (aFilters.buildID) + buildID = aFilters.buildID; + } + buildID = buildID.substring(0,4) + "-" + buildID.substring(4).replace(/\d{2}(?=\d)/g, "$&-"); + url = url.replace(/%BUILDID_EXPANDED%/g, buildID); + url = Services.urlFormatter.formatURL(url); + + let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest); + request.mozBackgroundRequest = true; + request.open("GET", url, true); + request.overrideMimeType("text/xml"); + + let self = this; + request.addEventListener("readystatechange", function () { + if (request.readyState == 4) { + if (request.status == 200) { + self.log("---- got response") + let documentElement = request.responseXML.documentElement; + let elements = documentElement.getElementsByTagName("addon"); + let totalResults = elements.length; + let parsedTotalResults = parseInt(documentElement.getAttribute("total_results")); + if (parsedTotalResults >= totalResults) + totalResults = parsedTotalResults; + + // TODO: Create a real Skip object from installed locales + self._parseLocales(elements, totalResults, { ids: [], sourceURIs: [] }, aCallback); + } else { + Cu.reportError("Locale Repository: Error getting locale from AMO [" + request.status + "]"); + } + } + }, false); + + request.send(null); + }, + + _parseLocale: function _parseLocale(aElement, aSkip) { + let skipIDs = (aSkip && aSkip.ids) ? aSkip.ids : []; + let skipSourceURIs = (aSkip && aSkip.sourceURIs) ? aSkip.sourceURIs : []; + + let guid = this._getDescendantTextContent(aElement, "guid"); + if (guid == null || skipIDs.indexOf(guid) != -1) + return null; + + let addon = new LocaleSearchResult(guid); + let result = { + addon: addon, + xpiURL: null, + xpiHash: null + }; + + let self = this; + for (let node = aElement.firstChild; node; node = node.nextSibling) { + if (!(node instanceof Ci.nsIDOMElement)) + continue; + + let localName = node.localName; + + // Handle case where the wanted string value is located in text content + // but only if the content is not empty + if (localName in STRING_KEY_MAP) { + addon[STRING_KEY_MAP[localName]] = this._getTextContent(node) || addon[STRING_KEY_MAP[localName]]; + continue; + } + + // Handle cases that aren't as simple as grabbing the text content + switch (localName) { + case "type": + // Map AMO's type id to corresponding string + let id = parseInt(node.getAttribute("id")); + switch (id) { + case 5: + addon.type = "language"; + break; + default: + this.log("Unknown type id when parsing addon: " + id); + } + break; + case "authors": + let authorNodes = node.getElementsByTagName("author"); + Array.forEach(authorNodes, function(aAuthorNode) { + let name = self._getDescendantTextContent(aAuthorNode, "name"); + if (name == null) + name = self._getTextContent(aAuthorNode); + let link = self._getDescendantTextContent(aAuthorNode, "link"); + if (name == null && link == null) + return; + + let author = { name: name, link: link }; + if (addon.creator == null) { + addon.creator = author; + } else { + if (addon.developers == null) + addon.developers = []; + + addon.developers.push(author); + } + }); + break; + case "status": + let repositoryStatus = parseInt(node.getAttribute("id")); + if (!isNaN(repositoryStatus)) + addon.repositoryStatus = repositoryStatus; + break; + case "all_compatible_os": + let nodes = node.getElementsByTagName("os"); + addon.isPlatformCompatible = Array.some(nodes, function(aNode) { + let text = aNode.textContent.toLowerCase().trim(); + return text == "all" || text == Services.appinfo.OS.toLowerCase(); + }); + break; + case "install": + // No os attribute means the xpi is compatible with any os + if (node.hasAttribute("os") && node.getAttribute("os")) { + let os = node.getAttribute("os").trim().toLowerCase(); + // If the os is not ALL and not the current OS then ignore this xpi + if (os != "all" && os != Services.appinfo.OS.toLowerCase()) + break; + } + + let xpiURL = this._getTextContent(node); + if (xpiURL == null) + break; + + if (skipSourceURIs.indexOf(xpiURL) != -1) + return null; + + result.xpiURL = xpiURL; + try { + addon.sourceURI = NetUtil.newURI(xpiURL); + } catch(ex) { + this.log("Addon has invalid uri: " + addon.sourceURI); + addon.sourceURI = null; + } + + let size = parseInt(node.getAttribute("size")); + addon.size = (size >= 0) ? size : null; + + let xpiHash = node.getAttribute("hash"); + if (xpiHash != null) + xpiHash = xpiHash.trim(); + result.xpiHash = xpiHash ? xpiHash : null; + break; + } + } + + return result; + }, + + _parseLocales: function _parseLocales(aElements, aTotalResults, aSkip, aCallback) { + let self = this; + let results = []; + for (let i = 0; i < aElements.length; i++) { + let element = aElements[i]; + + // Ignore add-ons not compatible with this Application + let tags = this._getUniqueDescendant(element, "compatible_applications"); + if (tags == null) + continue; + + let applications = tags.getElementsByTagName("appID"); + let compatible = Array.some(applications, function(aAppNode) { + if (self._getTextContent(aAppNode) != Services.appinfo.ID) + return false; + + let parent = aAppNode.parentNode; + let minVersion = self._getDescendantTextContent(parent, "min_version"); + let maxVersion = self._getDescendantTextContent(parent, "max_version"); + if (minVersion == null || maxVersion == null) + return false; + + let currentVersion = Services.appinfo.version; + return (Services.vc.compare(minVersion, currentVersion) <= 0 && Services.vc.compare(currentVersion, maxVersion) <= 0); + }); + + if (!compatible) + continue; + + // Add-on meets all requirements, so parse out data + let result = this._parseLocale(element, aSkip); + if (result == null) + continue; + + // Ignore add-on missing a required attribute + let requiredAttributes = ["id", "name", "version", "type", "targetLocale", "sourceURI"]; + if (requiredAttributes.some(function(aAttribute) !result.addon[aAttribute])) + continue; + + // Add only if the add-on is compatible with the platform + if (!result.addon.isPlatformCompatible) + continue; + + // Add only if there was an xpi compatible with this OS + if (!result.xpiURL) + continue; + + results.push(result); + + // Ignore this add-on from now on by adding it to the skip array + aSkip.ids.push(result.addon.id); + } + + // Immediately report success if no AddonInstall instances to create + let pendingResults = results.length; + if (pendingResults == 0) { + aCallback([]); + return; + } + + // Create an AddonInstall for each result + let self = this; + results.forEach(function(aResult) { + let addon = aResult.addon; + let callback = function(aInstall) { + aResult.addon.install = aInstall; + pendingResults--; + if (pendingResults == 0) + aCallback(results); + } + + if (aResult.xpiURL) { + AddonManager.getInstallForURL(aResult.xpiURL, callback, + "application/x-xpinstall", aResult.xpiHash, + addon.name, addon.iconURL, addon.version); + } else { + callback(null); + } + }); + } +}; + +function LocaleSearchResult(aId) { + this.id = aId; +} + +LocaleSearchResult.prototype = { + id: null, + type: null, + targetLocale: null, + name: null, + addon: null, + version: null, + iconURL: null, + install: null, + sourceURI: null, + repositoryStatus: null, + size: null, + strings: "", + updateDate: null, + isCompatible: true, + isPlatformCompatible: true, + providesUpdatesSecurely: true, + blocklistState: Ci.nsIBlocklistService.STATE_NOT_BLOCKED, + appDisabled: false, + userDisabled: false, + scope: AddonManager.SCOPE_PROFILE, + isActive: true, + pendingOperations: AddonManager.PENDING_NONE, + permissions: 0 +}; diff --git a/mobile/android/modules/Makefile.in b/mobile/android/modules/Makefile.in new file mode 100644 index 000000000000..98472a4182b1 --- /dev/null +++ b/mobile/android/modules/Makefile.in @@ -0,0 +1,55 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Mobile Browser. +# +# The Initial Developer of the Original Code is +# the Mozilla Foundation . +# Portions created by the Initial Developer are Copyright (C) 2010 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Finkle +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +EXTRA_JS_MODULES = \ + LocaleRepository.jsm \ + linuxTypes.jsm \ + video.jsm \ + $(NULL) + +EXTRA_PP_JS_MODULES = \ + contacts.jsm \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/mobile/android/modules/contacts.jsm b/mobile/android/modules/contacts.jsm new file mode 100644 index 000000000000..56846125628c --- /dev/null +++ b/mobile/android/modules/contacts.jsm @@ -0,0 +1,158 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Mobile Browser. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +let EXPORTED_SYMBOLS = ["Contacts"]; + +const Cu = Components.utils; + +let Contacts = { + _providers: [], + _contacts: [], + + _load: function _load() { + this._contacts = []; + + this._providers.forEach(function(provider) { + this._contacts = this._contacts.concat(provider.getContacts()); + }, this) + }, + + init: function init() { + // Not much to do for now + this._load(); + }, + + refresh: function refresh() { + // Pretty simple for now + this._load(); + }, + + addProvider: function(aProvider) { + this._providers.push(aProvider); + this.refresh(); + }, + + find: function find(aMatch) { + let results = []; + + if (!this._contacts.length) + return results; + + for (let field in aMatch) { + // Simple string-only partial matching + let match = aMatch[field]; + this._contacts.forEach(function(aContact) { + if (field in aContact && aContact[field].indexOf(match) != -1) + results.push(aContact); + }); + } + return results; + } +}; + +#ifndef ANDROID +#ifndef XP_MACOSX +#ifdef XP_UNIX +Cu.import("resource://gre/modules/ctypes.jsm"); +Cu.import("resource:///modules/linuxTypes.jsm"); + +function EBookProvider() { + EBook.init(); +} + +EBookProvider.prototype = { + getContacts: function() { + if (!EBook.lib) { + Cu.reportError("EBook not loaded") + return []; + } + + let gError = new GLib.GError.ptr; + let book = EBook.openSystem(gError.address()); + if (!book) { + Cu.reportError("EBook.openSystem: " + gError.contents.message.readString()) + return []; + } + + if (!EBook.openBook(book, false, gError.address())) { + Cu.reportError("EBook.openBook: " + gError.contents.message.readString()) + return []; + } + + let query = EBook.queryAnyFieldContains(""); + if (query) { + let gList = new GLib.GList.ptr(); + if (!EBook.getContacts(book, query, gList.address(), gError.address())) { + Cu.reportError("EBook.getContacts: " + gError.contents.message.readString()) + return []; + } + + let contacts = []; + while (gList && !gList.isNull()) { + let fullName = EBook.getContactField(gList.contents.data, EBook.E_CONTACT_FULL_NAME); + if (!fullName.isNull()) { + let contact = {}; + contact.fullName = fullName.readString(); + contact.emails = []; + contact.phoneNumbers = []; + + for (let emailIndex=EBook.E_CONTACT_EMAIL_FIRST; emailIndex<=EBook.E_CONTACT_EMAIL_LAST; emailIndex++) { + let email = EBook.getContactField(gList.contents.data, emailIndex); + if (!email.isNull()) + contact.emails.push(email.readString()); + } + + for (let phoneIndex=EBook.E_CONTACT_PHONE_FIRST; phoneIndex<=EBook.E_CONTACT_PHONE_LAST; phoneIndex++) { + let phone = EBook.getContactField(gList.contents.data, phoneIndex); + if (!phone.isNull()) + contact.phoneNumbers.push(phone.readString()); + } + + contacts.push(contact); + } + gList = ctypes.cast(gList.contents.next, GLib.GList.ptr); + } + return contacts; + } + return []; + } +}; + +Contacts.addProvider(new EBookProvider); +# XP_UNIX +#endif +#endif +#endif diff --git a/mobile/android/modules/linuxTypes.jsm b/mobile/android/modules/linuxTypes.jsm new file mode 100644 index 000000000000..288670adeeca --- /dev/null +++ b/mobile/android/modules/linuxTypes.jsm @@ -0,0 +1,116 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Mobile Browser. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +let EXPORTED_SYMBOLS = ["GLib", "EBook"]; + +const Cu = Components.utils; + +Cu.import("resource://gre/modules/ctypes.jsm"); + +let GLib = { + lib: null, + + init: function glib_init() { + if (this.lib) + return; + + this.lib = true; // TODO hook up the real glib + + this.GError = new ctypes.StructType("GError", [ + {"domain": ctypes.int32_t}, + {"code": ctypes.int32_t}, + {"message": ctypes.char.ptr} + ]); + + this.GList = new ctypes.StructType("GList", [ + {"data": ctypes.voidptr_t}, + {"next": ctypes.voidptr_t}, + {"prev": ctypes.voidptr_t} + ]); + } +}; + +let EBook = { + lib: null, + + E_CONTACT_FULL_NAME: 4, + E_CONTACT_EMAIL_1: 8, + E_CONTACT_EMAIL_2: 9, + E_CONTACT_EMAIL_3: 10, + E_CONTACT_EMAIL_4: 11, + E_CONTACT_PHONE_BUSINESS: 17, + E_CONTACT_PHONE_BUSINESS_2: 18, + E_CONTACT_PHONE_HOME: 23, + E_CONTACT_PHONE_HOME_2: 24, + E_CONTACT_PHONE_MOBILE: 27, + + E_CONTACT_EMAIL_FIRST: 8, + E_CONTACT_EMAIL_LAST: 11, + E_CONTACT_PHONE_FIRST: 16, + E_CONTACT_PHONE_LAST: 34, + + init: function ebook_init() { + if (this.lib) + return; + + GLib.init(); + + try { + // Shipping on N900 + this.lib = ctypes.open("libebook-1.2.so.5"); + } catch (e) { + try { + // Shipping on Ubuntu + this.lib = ctypes.open("libebook-1.2.so.9"); + } catch (e) { + Cu.reportError("EBook: couldn't load libebook:\n" + e) + this.lib = null; + return; + } + } + + this.EBook = new ctypes.StructType("EBook"); + this.EQuery = new ctypes.StructType("EQuery"); + + this.openSystem = this.lib.declare("e_book_new_system_addressbook", ctypes.default_abi, EBook.EBook.ptr, GLib.GError.ptr.ptr); + this.openBook = this.lib.declare("e_book_open", ctypes.default_abi, ctypes.bool, EBook.EBook.ptr, ctypes.bool, GLib.GError.ptr.ptr); + + this.queryAnyFieldContains = this.lib.declare("e_book_query_any_field_contains", ctypes.default_abi, EBook.EQuery.ptr, ctypes.char.ptr); + + this.getContacts = this.lib.declare("e_book_get_contacts", ctypes.default_abi, ctypes.bool, EBook.EBook.ptr, EBook.EQuery.ptr, GLib.GList.ptr.ptr, GLib.GError.ptr.ptr); + this.getContactField = this.lib.declare("e_contact_get_const", ctypes.default_abi, ctypes.char.ptr, ctypes.voidptr_t, ctypes.uint32_t); + } +}; diff --git a/mobile/android/modules/video.jsm b/mobile/android/modules/video.jsm new file mode 100644 index 000000000000..df208be487f8 --- /dev/null +++ b/mobile/android/modules/video.jsm @@ -0,0 +1,5 @@ +var EXPORTED_SYMBOLS = ["Video"]; + +var Video = { + fullScreenSourceElement: null +}; diff --git a/mobile/android/themes/core/Makefile.in b/mobile/android/themes/core/Makefile.in new file mode 100644 index 000000000000..b692a414a54f --- /dev/null +++ b/mobile/android/themes/core/Makefile.in @@ -0,0 +1,46 @@ +# ***** BEGIN LICENSE BLOCK ***** +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Mobile Browser. +# +# The Initial Developer of the Original Code is +# the Mozilla Foundation . +# Portions created by the Initial Developer are Copyright (C) 2007 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Finkle +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/rules.mk diff --git a/mobile/android/themes/core/about.css b/mobile/android/themes/core/about.css new file mode 100644 index 000000000000..8855a475ec89 --- /dev/null +++ b/mobile/android/themes/core/about.css @@ -0,0 +1,46 @@ +html { + background: #f0f0f0; + padding: 0 1em; + font-family: "Nokia Sans", Tahoma, sans-serif !important; + font-size: 100% !important; +} + +body { + color: black; + position: relative; + min-width: 330px; + max-width: 50em; + margin: 1em auto; + border: 1px solid gray; + -moz-border-radius: 10px; + padding: 3em; + -moz-padding-start: 30px; + background: white; +} + +.aboutPageWideContainer { + max-width: 80%; +} + +#aboutLogoContainer { + border: 1px solid lightgray; + width: 300px; + margin-bottom: 2em; +} + +img { + border: 0; +} + +#version { + font-weight: bold; + color: #909090; + margin: -24px 0 9px 17px; +} + +ul { + margin: 0; + -moz-margin-start: 1.5em; + padding: 0; + list-style: square; +} diff --git a/mobile/android/themes/core/aboutAddons.css b/mobile/android/themes/core/aboutAddons.css new file mode 100644 index 000000000000..e2eef93056c4 --- /dev/null +++ b/mobile/android/themes/core/aboutAddons.css @@ -0,0 +1,107 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Mobile Browser. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +html { + font-size: 24px; +} + +.addons-header { + border-bottom: 3px solid black; +} + +.addon-item { + border-bottom: 1px solid black; + padding: 8px; + position: relative; +} + +.addon-item:last-child { + border-bottom: 0; +} + +.addon-item:not([optionsURL]) .options-btn { + visibility: hidden; +} + +/* Make room for the image */ +.inner { + -moz-margin-start: 48px; +} + +.title { + color: black; +} + +.version { + /* The addon title is not localized, so keep the margin on the left side */ + margin-left: 12px; + font-size: 18px; + color: gray; +} + +.description { + width: 100%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.buttons { + padding-top: 8px; +} + +body[dir="ltr"] .favicon { + left: 12px; +} + +body[dir="ltr"] .favicon { + right: 12px; +} + +.favicon { + border: none; + top: 8; + width: 32px; + height: 32px; + position: absolute; +} + +button { + color: black; + font-size: 28px !important; + padding: 5px; +} diff --git a/mobile/android/themes/core/aboutPage.css b/mobile/android/themes/core/aboutPage.css new file mode 100644 index 000000000000..aacc3c48292a --- /dev/null +++ b/mobile/android/themes/core/aboutPage.css @@ -0,0 +1,105 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Mobile Browser. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Gavin Sharp + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#aboutLogoContainer { + width: 300px; +} + +#version { + font-weight: bold; + color: #909090; + margin: -24px 20px 0 118px; +} + +#update { + float: right; + padding: 8px; + margin-top: -32px; +} + +body[dir="rtl"] #update { + float: left; +} + +#update-message-checking, +#update-message-none, +#update-message-found { + display: none; + float: right; + padding: 8px; + margin-top: -32px; +} + +body[dir="rtl"] #update-message-checking, +body[dir="rtl"] #update-message-none, +body[dir="rtl"] #update-message-found { + float: left; +} + +#aboutLinks { + background-color: white; + padding: 5px; + border: 2px solid #e6e5e3; + font-size: 24px; +} + +#aboutLinks > li { + clear: both; + border-bottom: 2px solid #e6e5e3; + list-style: none; + -moz-padding-end: 16px; +} + +#aboutLinks > li:last-child { + border-bottom: 0; +} + +#aboutLinks > li > a { + padding: 16px; + display: block; + color: #3a3834; + background: url("images/arrowright-16.png") right center no-repeat; +} + +body[dir="rtl"] #aboutLinks > li > a { + background: url("images/arrowleft-16.png") left center no-repeat; +} + +#aboutDetails { + margin-top: 15px; + font-size: 18px; +} diff --git a/mobile/android/themes/core/browser.css b/mobile/android/themes/core/browser.css new file mode 100644 index 000000000000..c173187c6fdb --- /dev/null +++ b/mobile/android/themes/core/browser.css @@ -0,0 +1,67 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Mobile Browser. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +%filter substitution +%include defines.inc + +/* content scrollbars */ +.scroller { + opacity: 0; + background-color: rgba(0, 0, 0, 0.4) !important; + -moz-border-top-colors: none !important; + -moz-border-bottom-colors: none !important; + -moz-border-right-colors: none !important; + -moz-border-left-colors: none !important; + -moz-border-radius: @border_radius_tiny@; + border: @border_width_tiny@ solid rgba(255, 255, 255, 0.4) !important; +} + +.scroller[panning="true"] { + opacity: 1; +} + +.scroller[orient="vertical"] { + min-width: @scroller_thickness@; + width: @scroller_thickness@; + min-height: @scroller_minimum@; +} + +.scroller[orient="horizontal"] { + min-height: @scroller_thickness@; + height: @scroller_thickness@; + min-width: @scroller_minimum@; +} diff --git a/mobile/android/themes/core/config.css b/mobile/android/themes/core/config.css new file mode 100644 index 000000000000..beb9a62dcbdc --- /dev/null +++ b/mobile/android/themes/core/config.css @@ -0,0 +1,71 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Mobile Browser. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +html { + font-size: 24px; +} + +input, +button { + font-size: 28px; + padding: 5px; +} + +#filter-container { + display: -moz-box; + -moz-box-align: center; +} + +#new-pref-container { + margin: 10px 0; +} + +.pref-item { + padding-top: 10px; + border-top: 1px solid #ccc; +} + +.pref-item[default="false"] { + font-weight: bold; +} + +.pref-item > * { + margin-bottom: 10px; +} + +.modify-pref-button { + margin-right: 10px; +} diff --git a/mobile/android/themes/core/content.css b/mobile/android/themes/core/content.css new file mode 100644 index 000000000000..590822727c8f --- /dev/null +++ b/mobile/android/themes/core/content.css @@ -0,0 +1,370 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Mobile Browser. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * Doug Turner + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +%filter substitution +%include defines.inc + +@namespace url("http://www.w3.org/1999/xhtml"); +@namespace xul url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); + +/* make clicking on links stand out a bit (bug 532206) */ +* > *:not(embed):focus, * > *:focus > font { + outline: 2px solid #8db8d8 !important; + /* + XXX How do I preserve mac focusring without blowing focus color on other platforms? + outline-color: -moz-mac-focusring !important; + */ +} + +*:-moz-any-link:focus { + outline-offset: -2px; +} + +/* Style the scrollbars */ +html xul|scrollbar { + display: none; +} + +xul|window xul|scrollbar { + display: block; +} + +xul|scrollbar[orient="vertical"] { + -moz-appearance: none !important; + opacity: 0; + position: relative; + margin-left: -8px; + min-width: 8px; + background-color: transparent !important; + background-image: none !important; + border: 0px solid transparent !important; +} + +xul|scrollbar[orient="vertical"]:-moz-locale-dir(rtl) { + margin-left: 2px; + margin-right: -10px; +} + +xul|scrollbar[orient="vertical"] xul|thumb { + max-width: 6px !important; + min-width: 6px !important; +} + +xul|scrollbar[orient="horizontal"] { + -moz-appearance: none !important; + opacity: 0; + position: relative; + min-height: 8px; + margin-top: -8px; + background-color: transparent !important; + background-image: none !important; + border: 0px solid transparent !important; +} + +xul|scrollbar[orient="horizontal"] xul|thumb { + max-height: 6px !important; + min-height: 6px !important; +} + +xul|*[panning="true"] xul|scrollbar { + opacity: 1; +} + +xul|scrollbox { + overflow-y: scroll; + overflow-x: scroll; +} + +xul|scrollbarbutton { + min-height: 8px !important; + min-width: 8px !important; + -moz-appearance: none !important; + visibility: hidden; +} + +xul|scrollbarbutton[sbattr="scrollbar-up-top"], +xul|scrollbarbutton[sbattr="scrollbar-bottom-top"] { + display: none; +} + +xul|thumb { + background-color: rgba(0, 0, 0, 0.4) !important; + -moz-border-top-colors: none !important; + -moz-border-bottom-colors: none !important; + -moz-border-right-colors: none !important; + -moz-border-left-colors: none !important; + border: 1px solid rgba(255, 255, 255, 0.4) !important; + -moz-border-radius: 3px; +} + +select:not([size]):not([multiple]) > xul|scrollbar, +select[size="1"] > xul|scrollbar, +select:not([size]):not([multiple]) xul|scrollbarbutton, +select[size="1"] xul|scrollbarbutton { + display: block; + margin-left: 0; + min-width: 16px; +} + +/* Override inverse OS themes */ +select, +textarea, +button, +xul|button, +* > input:not([type="image"]) { + -moz-appearance: none !important; /* See bug 598421 for fixing the platform */ + -moz-border-radius: 3px; +} + +select[size], +select[multiple], +select[size][multiple], +textarea, +* > input:not([type="image"]) { + border-style: solid; + border-color: #7d7d7d; + color: #414141; + background: white -moz-linear-gradient(top, rgba(115,115,115,0.5) 0, rgba(215,215,215,0.5) 3px, rgba(255,255,255,0.2) 16px); +} + +/* Selects are handled by the form helper, see bug 685197 */ +select option, select optgroup { + pointer-events: none; +} + +input:-moz-placeholder, +textarea:-moz-placeholder { + color: GrayText; +} + +select:not([size]):not([multiple]), +select[size="0"], +select[size="1"], +* > input[type="button"], +* > input[type="submit"], +* > input[type="reset"], +button { + border-style: solid; + border-color: #7d7d7d; + color: #414141; + background: white -moz-linear-gradient(top, rgba(255,255,255,0.2) 0, rgba(215,215,215,0.5) 18px, rgba(115,115,115,0.5) 100%); +} + +input[type="checkbox"] { + background: white -moz-linear-gradient(top, rgba(115,115,115,0.5) 0, rgba(215,215,215,0.5) 2px, rgba(255,255,255,0.2) 6px); +} + +input[type="radio"] { + background: -moz-radial-gradient(6px 6px, cover, rgba(255,255,255,0.2) 3px, rgba(195,195,195,0.5) 5px, rgba(115,115,115,0.5) 100%); +} + +select { + border-width: 1px; + padding: 1px; +} + +select:not([size]):not([multiple]), +select[size="0"], +select[size="1"] { + padding: 0 1px 0 1px; +} + +* > input:not([type="image"]) { + border-width: 1px; + padding: 1px; +} + +textarea { + resize: none; + border-width: 1px; + padding: 2px 1px 2px 1px; +} + +input[type="button"], +input[type="submit"], +input[type="reset"], +button { + border-width: 1px; + padding: 0 7px 0 7px; +} + +input[type="radio"], +input[type="checkbox"] { + max-width: 14px; + max-height: 14px; + border: 1px solid #a7a7a7 !important; + padding: 2px 1px 2px 1px; +} + +select > input[type="button"] { + border-width: 0px !important; + margin: 0px !important; + padding: 0px !important; + -moz-border-radius: 0; + color: #414141; + + background-size: 100% 90%; + background-color: transparent; + background-image: -moz-radial-gradient(bottom left, #bbbbbb 40%, #f5f5f5) !important; + background-position: -15px center !important; + background-repeat: no-repeat !important; + + /* Use to position an svg arrow on element */ + -moz-binding: url("chrome://browser/content/bindings.xml#select-button"); + position: relative !important; + font-size: inherit; +} + +select[size]:focus, +select[multiple]:focus, +select[size][multiple]:focus, +textarea:focus, +input[type="file"]:focus > input[type="text"], +* > input:not([type="image"]):focus { + outline: 0px !important; + border-style: solid; + border-color: rgb(94,128,153); + background: white -moz-linear-gradient(top, rgba(27,113,177,0.5) 0, rgba(198,225,246,0.2) 3px, rgba(255,255,255,0.2) 16px); +} + +select:not([size]):not([multiple]):focus, +select[size="0"]:focus, +select[size="1"]:focus, +input[type="button"]:focus, +input[type="submit"]:focus, +input[type="reset"]:focus, +button:focus { + outline: 0px !important; + border-style: solid; + border-color: rgb(94,128,153); + background: white -moz-linear-gradient(top, rgba(255,255,255,0.2) 0, rgba(198,225,256,0.2) 18px, rgba(27,113,177,0.5) 100%); +} + +input[type="checkbox"]:focus, +input[type="radio"]:focus { + border-color: #99c6e0 !important; +} + +input[type="checkbox"]:focus { + background: white -moz-linear-gradient(top, rgba(27,113,177,0.5) 0, rgba(198,225,246,0.2) 2px, rgba(255,255,255,0.2) 6px); +} + +input[type="radio"]:focus { + background: -moz-radial-gradient(6px 6px, cover, rgba(255,255,255,0.2) 3px, rgba(198,225,246,0.2) 5px, rgba(27,113,177,0.5) 100%); +} + +/* we need to be specific for selects because the above rules are specific too */ +textarea[disabled], +select[size][disabled], +select[multiple][disabled], +select[size][multiple][disabled], +select:not([size]):not([multiple])[disabled], +select[size="0"][disabled], +select[size="1"][disabled], +button[disabled], +* > input:not([type="image"])[disabled] { + color: rgba(0,0,0,0.3); + border-color: rgba(125,125,125,0.4); + border-style: solid; + border-width: 1px; + background: transparent -moz-linear-gradient(top, rgba(185,185,185,0.4) 0, rgba(235,235,235,0.4) 3px, rgba(255,255,255,0.4) 100%); +} + +select:not([size]):not([multiple])[disabled], +select[size="0"][disabled], +select[size="1"][disabled] { + background: transparent -moz-linear-gradient(top, rgba(255,255,255,0.4) 0, rgba(235,235,235,0.4) 3px, rgba(185,185,185,0.4) 100%); +} + +input[type="button"][disabled], +input[type="submit"][disabled], +input[type="reset"][disabled], +button[disabled="true"] { + padding: 0 7px 0 7px; + background: transparent -moz-linear-gradient(top, rgba(255,255,255,0.4) 0, rgba(235,235,235,0.4) 3px, rgba(185,185,185,0.4) 100%); +} + +input[type="radio"][disabled], +input[type="radio"][disabled]:active, +input[type="radio"][disabled]:hover, +input[type="radio"][disabled]:hover:active, +input[type="checkbox"][disabled], +input[type="checkbox"][disabled]:active, +input[type="checkbox"][disabled]:hover, +input[type="checkbox"][disabled]:hover:active { + border:1px solid rgba(125,125,125,0.4) !important; +} + +select[disabled] > input[type="button"] { + opacity: 0.6; + padding: 1px 7px 1px 7px; +} + +/* -moz-touch-enabled? media elements */ +video > xul|videocontrols, +audio > xul|videocontrols { + -moz-binding: url("chrome://global/content/bindings/videocontrols.xml#touchControls"); +} + +*:-moz-any-link:active, +*[role=button]:active, +button:active, +input:active, +option:active, +select:active, +label:active, +textarea:active { + background-color: @color_background_highlight_overlay@ !important; +} + +/* + * Generate an additional space after the anonymous div to make it easier to + * to position the caret at the end of the text + */ +input > .anonymous-div:after { + content: ""; + margin: 16px; +} + +/* + * Enforce nearest scaling for video in order not to lose too much performance + * after fixing bug 598736 ("Use higher-quality imageinterpolation on mobile") + */ +video { + image-rendering: -moz-crisp-edges; +} diff --git a/mobile/android/themes/core/gingerbread/defines.inc b/mobile/android/themes/core/gingerbread/defines.inc new file mode 100644 index 000000000000..186aa0a2675b --- /dev/null +++ b/mobile/android/themes/core/gingerbread/defines.inc @@ -0,0 +1,128 @@ +%filter substitution + +%define color_background_active #525252 +%define color_background_default #000 +%define color_text_default #fff +%define color_divider_border #333333 +%define color_button_border #5a5a5a +%define color_dialog_border #5a5a5a +%define color_background_dlgbuttons #9a9a9a +%define color_background_panel #d6d6d6 +%define color_text_panel #000 +%define color_background_header #292929 +%define color_text_header #999999 +%define color_background_scroller #9a9a9a +%define color_background_inverse #fff +%define color_text_inverse #000 +%define color_text_button #000 +%define color_text_disabled #808080 +%define color_text_placeholder #808080 +%define color_text_as_link #febc2b + +%define color_background_highlight #febc2b +%define color_background_highlight_overlay rgba(254, 188, 43, 0.8) +%define color_text_highlight #000 + +%define color_subtext_default lightgray +%define color_subtext_inverse #414141 + +%define font_xlarge 5.08mozmm +%define font_xnormal 2.75mozmm +%define font_normal 2.54mozmm +%define font_snormal 2.33mozmm +%define font_small 1.91mozmm +%define font_xsmall 1.69mozmm +%define font_tiny 1.48mozmm +%define font_xtiny 1.27mozmm +%define font_xxtiny 1.08mozmm + +%define touch_row 7.41mozmm +%define touch_button_xlarge 7.62mozmm +%define touch_button_large 6.77mozmm +%define touch_button_small 5.93mozmm +%define touch_button_minwidth 11.86mozmm +%define touch_action_minwidth 21.17mozmm +%define touch_normal 6.77mozmm + +%define margin_context_popup 3.39mozmm +%define margin_large 2.54mozmm +%define margin_xxxnormal 1.69mozmm +%define margin_xnormal 1.06mozmm +%define margin_normal 0.85mozmm +%define margin_snormal 0.64mozmm +%define margin_small 0.42mozmm +%define margin_tiny 0.21mozmm +%define margin_xtiny 0.15mozmm + +%define padding_xlarge 3.39mozmm +%define padding_large 2.54mozmm +%define padding_xxxnormal 1.69mozmm +%define padding_xxnormal 1.27mozmm +%define padding_xnormal 1.06mozmm +%define padding_normal 0.85mozmm +%define padding_snormal 0.64mozmm +%define padding_small 0.42mozmm +%define padding_xsmall 0.21mozmm +%define padding_tiny 0.11mozmm + +%define border_width_xxlarge 0.64mozmm +%define border_width_xlarge 0.42mozmm +%define border_width_large 0.32mozmm +%define border_width_small 0.21mozmm +%define border_width_tiny 0.11mozmm + +%define border_radius_normal 0.85mozmm +%define border_radius_small 0.64mozmm +%define border_radius_xsmall 0.31mozmm +%define border_radius_tiny 0.21mozmm + +%define shadow_width_xlarge 1.06mozmm +%define shadow_width_large 0.64mozmm +%define shadow_width_small 0.21mozmm + +%define textbox_height 5.08mozmm + +%define dropmarker_padding 0.53mozmm + +%define progressmeter_height 3.39mozmm + +%define urlbar_edit_height 6.35mozmm +%define urlbar_edit_indent 0.85mozmm +%define identity_popup_tablet_width 72mozmm + +%define scroller_thickness 0.64mozmm +%define scroller_minimum 1.27mozmm + +%define sidebar_width_minimum 8.47mozmm +%define sidebar_button_height 7.41mozmm +%define documenttab_margin_bottom 0.85mozmm + +%define placelabel_padding 8.47mozmm +%define placeitem_padding 4.23mozmm + +%define autocomplete_item_container_image_padding 0.53mozmm +%define autocomplete_item_container_position 0.21mozmm +%define autocomplete_item_container_size 2.75mozmm +%define autocomplete_item_container_padding 5.08mozmm + +%define autocomplete_item_subtitle_margin 2.75mozmm +%define autocomplete_item_label_margin 3.18mozmm +%define autocomplete_item_tags_margin 3.39mozmm + +%define autocompleteresult_padding 0.53mozmm + +%define dialog_width 76.2mozmm + +%define appmenu_portrait_height 21.17mozmm +%define appmenu_button_height 10.48mozmm + +%define tablet_panel_controls 40mozmm +%define tablet_panel_minwidth 124mozmm + +%ifdef MOZ_PLATFORM_MAEMO +%define orientation -moz-device-orientation +%elifdef ANDROID +%define orientation -moz-device-orientation +%else +%define orientation orientation +%endif diff --git a/mobile/android/themes/core/gingerbread/images/aboutBackground.jpg b/mobile/android/themes/core/gingerbread/images/aboutBackground.jpg new file mode 100644 index 000000000000..5dba1c58eb5a Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/aboutBackground.jpg differ diff --git a/mobile/android/themes/core/gingerbread/images/addons-default-hdpi.png b/mobile/android/themes/core/gingerbread/images/addons-default-hdpi.png new file mode 100644 index 000000000000..e08a1a72739a Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/addons-default-hdpi.png differ diff --git a/mobile/android/themes/core/gingerbread/images/alert-addons-30.png b/mobile/android/themes/core/gingerbread/images/alert-addons-30.png new file mode 100644 index 000000000000..c8c1a8d3a0c2 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/alert-addons-30.png differ diff --git a/mobile/android/themes/core/gingerbread/images/alert-downloads-30.png b/mobile/android/themes/core/gingerbread/images/alert-downloads-30.png new file mode 100644 index 000000000000..8279c3c92c16 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/alert-downloads-30.png differ diff --git a/mobile/android/themes/core/gingerbread/images/arrowdown-16.png b/mobile/android/themes/core/gingerbread/images/arrowdown-16.png new file mode 100644 index 000000000000..c982426f2ade Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/arrowdown-16.png differ diff --git a/mobile/android/themes/core/gingerbread/images/arrowleft-16.png b/mobile/android/themes/core/gingerbread/images/arrowleft-16.png new file mode 100644 index 000000000000..464a4a866cd9 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/arrowleft-16.png differ diff --git a/mobile/android/themes/core/gingerbread/images/arrowright-16.png b/mobile/android/themes/core/gingerbread/images/arrowright-16.png new file mode 100644 index 000000000000..859e98ba64b6 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/arrowright-16.png differ diff --git a/mobile/android/themes/core/gingerbread/images/arrowup-16.png b/mobile/android/themes/core/gingerbread/images/arrowup-16.png new file mode 100644 index 000000000000..2a7e56485353 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/arrowup-16.png differ diff --git a/mobile/android/themes/core/gingerbread/images/back-default-hdpi.png b/mobile/android/themes/core/gingerbread/images/back-default-hdpi.png new file mode 100644 index 000000000000..4bae9afbca09 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/back-default-hdpi.png differ diff --git a/mobile/android/themes/core/gingerbread/images/browseaddons-bg.jpg b/mobile/android/themes/core/gingerbread/images/browseaddons-bg.jpg new file mode 100644 index 000000000000..dcd9a92b3522 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/browseaddons-bg.jpg differ diff --git a/mobile/android/themes/core/gingerbread/images/button-bg.png b/mobile/android/themes/core/gingerbread/images/button-bg.png new file mode 100644 index 000000000000..f57261d41a4b Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/button-bg.png differ diff --git a/mobile/android/themes/core/gingerbread/images/check-30.png b/mobile/android/themes/core/gingerbread/images/check-30.png new file mode 100644 index 000000000000..82cf8f415fa7 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/check-30.png differ diff --git a/mobile/android/themes/core/gingerbread/images/check-selected-hdpi.png b/mobile/android/themes/core/gingerbread/images/check-selected-hdpi.png new file mode 100644 index 000000000000..2fa34331d931 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/check-selected-hdpi.png differ diff --git a/mobile/android/themes/core/gingerbread/images/check-unselected-hdpi.png b/mobile/android/themes/core/gingerbread/images/check-unselected-hdpi.png new file mode 100644 index 000000000000..add20cb42c95 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/check-unselected-hdpi.png differ diff --git a/mobile/android/themes/core/gingerbread/images/dropmarker-hdpi.png b/mobile/android/themes/core/gingerbread/images/dropmarker-hdpi.png new file mode 100644 index 000000000000..a41c303c2a23 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/dropmarker-hdpi.png differ diff --git a/mobile/android/themes/core/gingerbread/images/errorpage-larry-black.png b/mobile/android/themes/core/gingerbread/images/errorpage-larry-black.png new file mode 100644 index 000000000000..9f2e4a6e7366 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/errorpage-larry-black.png differ diff --git a/mobile/android/themes/core/gingerbread/images/errorpage-larry-white.png b/mobile/android/themes/core/gingerbread/images/errorpage-larry-white.png new file mode 100644 index 000000000000..fc153c7314e8 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/errorpage-larry-white.png differ diff --git a/mobile/android/themes/core/gingerbread/images/errorpage-warning.png b/mobile/android/themes/core/gingerbread/images/errorpage-warning.png new file mode 100644 index 000000000000..8bf9d8e7decc Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/errorpage-warning.png differ diff --git a/mobile/android/themes/core/gingerbread/images/favicon-default-32.png b/mobile/android/themes/core/gingerbread/images/favicon-default-32.png new file mode 100644 index 000000000000..8e66e5ae57e0 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/favicon-default-32.png differ diff --git a/mobile/android/themes/core/gingerbread/images/forward-default-hdpi.png b/mobile/android/themes/core/gingerbread/images/forward-default-hdpi.png new file mode 100644 index 000000000000..1bfa8012f6d1 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/forward-default-hdpi.png differ diff --git a/mobile/android/themes/core/gingerbread/images/handle-end.png b/mobile/android/themes/core/gingerbread/images/handle-end.png new file mode 100644 index 000000000000..d2720e70ec28 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/handle-end.png differ diff --git a/mobile/android/themes/core/gingerbread/images/handle-start.png b/mobile/android/themes/core/gingerbread/images/handle-start.png new file mode 100644 index 000000000000..8abd627f0df7 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/handle-start.png differ diff --git a/mobile/android/themes/core/gingerbread/images/locked-hdpi.png b/mobile/android/themes/core/gingerbread/images/locked-hdpi.png new file mode 100644 index 000000000000..a21c3a158281 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/locked-hdpi.png differ diff --git a/mobile/android/themes/core/gingerbread/images/mute-hdpi.png b/mobile/android/themes/core/gingerbread/images/mute-hdpi.png new file mode 100644 index 000000000000..c716ef35cd50 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/mute-hdpi.png differ diff --git a/mobile/android/themes/core/gingerbread/images/navigation-magnifier-30.png b/mobile/android/themes/core/gingerbread/images/navigation-magnifier-30.png new file mode 100644 index 000000000000..2a36e16d21d3 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/navigation-magnifier-30.png differ diff --git a/mobile/android/themes/core/gingerbread/images/next-disabled-hdpi.png b/mobile/android/themes/core/gingerbread/images/next-disabled-hdpi.png new file mode 100644 index 000000000000..69cb6d38d6b0 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/next-disabled-hdpi.png differ diff --git a/mobile/android/themes/core/gingerbread/images/next-hdpi.png b/mobile/android/themes/core/gingerbread/images/next-hdpi.png new file mode 100644 index 000000000000..a370353e828e Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/next-hdpi.png differ diff --git a/mobile/android/themes/core/gingerbread/images/pause-hdpi.png b/mobile/android/themes/core/gingerbread/images/pause-hdpi.png new file mode 100644 index 000000000000..42b36576978b Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/pause-hdpi.png differ diff --git a/mobile/android/themes/core/gingerbread/images/play-hdpi.png b/mobile/android/themes/core/gingerbread/images/play-hdpi.png new file mode 100644 index 000000000000..7410c99cd5d4 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/play-hdpi.png differ diff --git a/mobile/android/themes/core/gingerbread/images/popup-selected-item-hdpi.png b/mobile/android/themes/core/gingerbread/images/popup-selected-item-hdpi.png new file mode 100644 index 000000000000..a11bd508f685 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/popup-selected-item-hdpi.png differ diff --git a/mobile/android/themes/core/gingerbread/images/previous-disabled-hdpi.png b/mobile/android/themes/core/gingerbread/images/previous-disabled-hdpi.png new file mode 100644 index 000000000000..fa7b069f597b Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/previous-disabled-hdpi.png differ diff --git a/mobile/android/themes/core/gingerbread/images/previous-hdpi.png b/mobile/android/themes/core/gingerbread/images/previous-hdpi.png new file mode 100644 index 000000000000..099ff2d574f7 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/previous-hdpi.png differ diff --git a/mobile/android/themes/core/gingerbread/images/radio-selected-hdpi.png b/mobile/android/themes/core/gingerbread/images/radio-selected-hdpi.png new file mode 100644 index 000000000000..9b3ad38a62a8 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/radio-selected-hdpi.png differ diff --git a/mobile/android/themes/core/gingerbread/images/radio-unselected-hdpi.png b/mobile/android/themes/core/gingerbread/images/radio-unselected-hdpi.png new file mode 100644 index 000000000000..17e346ae8efe Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/radio-unselected-hdpi.png differ diff --git a/mobile/android/themes/core/gingerbread/images/remotetabs-32.png b/mobile/android/themes/core/gingerbread/images/remotetabs-32.png new file mode 100644 index 000000000000..80f59a022c34 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/remotetabs-32.png differ diff --git a/mobile/android/themes/core/gingerbread/images/row-header-bg.png b/mobile/android/themes/core/gingerbread/images/row-header-bg.png new file mode 100644 index 000000000000..d0f3caa673c1 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/row-header-bg.png differ diff --git a/mobile/android/themes/core/gingerbread/images/scrubber-hdpi.png b/mobile/android/themes/core/gingerbread/images/scrubber-hdpi.png new file mode 100644 index 000000000000..0a2fdd3c7fe3 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/scrubber-hdpi.png differ diff --git a/mobile/android/themes/core/gingerbread/images/search-clear-30.png b/mobile/android/themes/core/gingerbread/images/search-clear-30.png new file mode 100644 index 000000000000..8bab39a08d35 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/search-clear-30.png differ diff --git a/mobile/android/themes/core/gingerbread/images/search-glass-30.png b/mobile/android/themes/core/gingerbread/images/search-glass-30.png new file mode 100644 index 000000000000..f8cb0ef2bc3d Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/search-glass-30.png differ diff --git a/mobile/android/themes/core/gingerbread/images/textbox-bg.png b/mobile/android/themes/core/gingerbread/images/textbox-bg.png new file mode 100644 index 000000000000..1f02bd73cbd2 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/textbox-bg.png differ diff --git a/mobile/android/themes/core/gingerbread/images/throbber.png b/mobile/android/themes/core/gingerbread/images/throbber.png new file mode 100644 index 000000000000..c601ec80ba7c Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/throbber.png differ diff --git a/mobile/android/themes/core/gingerbread/images/toggle-off.png b/mobile/android/themes/core/gingerbread/images/toggle-off.png new file mode 100644 index 000000000000..87a660c599e2 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/toggle-off.png differ diff --git a/mobile/android/themes/core/gingerbread/images/toggle-on.png b/mobile/android/themes/core/gingerbread/images/toggle-on.png new file mode 100644 index 000000000000..8d333d5d8619 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/toggle-on.png differ diff --git a/mobile/android/themes/core/gingerbread/images/unlocked-hdpi.png b/mobile/android/themes/core/gingerbread/images/unlocked-hdpi.png new file mode 100644 index 000000000000..e21b80fa3c6a Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/unlocked-hdpi.png differ diff --git a/mobile/android/themes/core/gingerbread/images/unmute-hdpi.png b/mobile/android/themes/core/gingerbread/images/unmute-hdpi.png new file mode 100644 index 000000000000..cf2fb7bfb666 Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/unmute-hdpi.png differ diff --git a/mobile/android/themes/core/gingerbread/images/urlbar-bg.png b/mobile/android/themes/core/gingerbread/images/urlbar-bg.png new file mode 100644 index 000000000000..9dddc7e7363a Binary files /dev/null and b/mobile/android/themes/core/gingerbread/images/urlbar-bg.png differ diff --git a/mobile/android/themes/core/gingerbread/localePicker.css b/mobile/android/themes/core/gingerbread/localePicker.css new file mode 100644 index 000000000000..2a6564944df1 --- /dev/null +++ b/mobile/android/themes/core/gingerbread/localePicker.css @@ -0,0 +1,96 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Mobile Browser. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Wes Johnston + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +%filter substitution +%include defines.inc + +.pane { + -moz-box-pack: center; + -moz-box-align: center; + -moz-box-flex: 1; +} + +#main-page { + background-image: url("chrome://branding/content/logo.png"); + background-repeat: no-repeat; + background-position: center center; +} + +#main-page:not([mode="loading"]) #loading-label, +#main-page[mode] #continue-in-button, +#main-page[mode] #change-language { + visibility: hidden; +} + +#picker-title { + font-weight: bold; + font-size: @font_normal@; +} + +.link { + padding: @padding_xlarge@ 0px; + font-weight: bold; +} + +richlistbox { + padding: 0px; + margin: 0px; + background-color: transparent; +} + +#installer-page { + background-color: black; + color: white; +} + +richlistitem { + height: @touch_row@; + font-size: @font_normal@; + border-bottom: @border_width_tiny@ solid gray; + padding: 0px @padding_normal@; + -moz-box-align: center; +} + +richlistitem .checkbox { + width: 46px; + height: 46px; + list-style-image: url("chrome://browser/skin/images/radio-unselected-hdpi.png"); +} + +richlistitem[selected] .checkbox { + list-style-image: url("chrome://browser/skin/images/radio-selected-hdpi.png"); +} diff --git a/mobile/android/themes/core/gingerbread/platform.css b/mobile/android/themes/core/gingerbread/platform.css new file mode 100644 index 000000000000..92af89111244 --- /dev/null +++ b/mobile/android/themes/core/gingerbread/platform.css @@ -0,0 +1,740 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Mobile Browser. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* global skin ------------------------------------------------------------- */ +@import url(chrome://global/skin/); + +%filter substitution +%include defines.inc + +/* general stuff ------------------------------------------------------------ */ +:root { + font-family: "Nokia Sans", Tahoma, sans-serif !important; + font-size: @font_normal@ !important; + background-color: @color_background_default@; /* force */ + color: @color_text_default@; /* force */ +} + +::-moz-selection { + background-color: @color_background_highlight@; + color: @color_text_highlight@; +} + +menu, +menuitem { + padding: 0 !important; + margin: 0 !important; +} + +description, +label { + /* force mac to use the same margins as windows and linux */ + -moz-margin-start: @margin_snormal@; + -moz-margin-end: @margin_snormal@; +} + +/* Override any OS inverse themes */ +textbox { + color: @color_text_inverse@; + background-color: @color_background_inverse@; +} + +/* textboxes --------------------------------------------------------------- */ +textbox:not([type="number"]) { + min-height: @textbox_height@; + border: @border_width_small@ solid @color_button_border@; + -moz-border-top-colors: -moz-initial; + -moz-border-right-colors: -moz-initial; + -moz-border-bottom-colors: -moz-initial; + -moz-border-left-colors: -moz-initial; +} + +textbox[isempty="true"] { + color: @color_text_placeholder@; +} + +textbox.search-bar { + border: @border_width_small@ solid rgba(0,0,0,0.4); + background-color: #f9f9f9; + background: url("chrome://browser/skin/images/textbox-bg.png") top left repeat-x; + background-size: 100% 100%; +} + +textbox[disabled="true"] { + background-color: lightgray; +} + +.link { + color: @color_text_as_link@; + text-decoration: underline; +} + +/* sidebars spacer --------------------------------------------------------- */ +.sidebar-spacer { + background-color: #767973; +} + +/* prompt dialogs ---------------------------------------------------------- */ +.context-block, +.modal-block, +.perm-modal-block { + -moz-box-align: center; + -moz-box-pack: center; + background-color: rgba(0,0,0,.6); +} + +.context-block { + padding: @margin_context_popup@; +} + +.dialog-dark, +.panel-arrowcontent { + background-color: @color_background_inverse@; + box-shadow: black 0 @border_radius_tiny@ @border_radius_tiny@, black 0 -@border_radius_tiny@ @border_radius_tiny@; + padding: 0; +} + +@media (max-width: 499px) { + .context-block { + padding: @padding_xlarge@; + } +} + +dialog > .prompt-header > .prompt-message { + white-space: pre-wrap; +} + +dialog > .prompt-header > .button-checkbox { + margin-left: @margin_large@; +} + +/* buttons ----------------------------------------------------------------- */ +.button-text, +.toolbarbutton-text { + font-weight: normal; + font-size: @font_snormal@ !important; +} + +button { + -moz-appearance: none; + min-width: @touch_button_minwidth@ !important; + min-height: @touch_button_small@ !important; /* button size */ + color: @color_text_button@; + margin: @margin_normal@; + padding: @padding_xnormal@; + background-image: url("chrome://browser/skin/images/button-bg.png"); + background-size: auto 100%; + border: @border_width_tiny@ solid @color_button_border@; +} + +button[disabled="true"] { + color: @color_text_disabled@ !important; + border: @border_width_tiny@ solid @color_button_border@ !important; +} + +button:focus > .button-box { + border: @border_width_tiny@ solid transparent; +} + +button:not([disabled]):hover:active, +button:not([disabled])[checked="true"] { + background-image: url("chrome://browser/skin/images/toggle-off.png"); +} + +/* Override GTK2 system setting */ +.button-icon { + display: -moz-initial !important; +} + +/* spinbuttons ------------------------------------------------------------- */ +spinbuttons { + border: none !important; +} + +.numberbox-input-box { + border: @border_width_small@ solid @color_button_border@; + border-right: 0 solid transparent; + -moz-border-top-colors: -moz-initial; + -moz-border-bottom-colors: -moz-initial; + -moz-border-left-colors: -moz-initial; +} + +.numberbox-input-box:-moz-locale-dir(rtl) { + border-right: @border_width_small@ solid @color_button_border@; + border-left: 0 solid transparent; +} + +.spinbuttons-box { + border: none !important; + -moz-box-orient: horizontal !important; + -moz-box-direction: reverse !important; +} + +.spinbuttons-up .button-icon, +.spinbuttons-down .button-icon { + display: block; +} + +.spinbuttons-up, +.spinbuttons-down { + -moz-appearance: none !important; + min-width: @touch_button_small@ !important; /* button size */ + min-height: @touch_button_small@ !important; /* button size */ + color: @color_text_button@; + margin: @margin_normal@; + padding: @padding_xnormal@; + background-image: url("chrome://browser/skin/images/button-bg.png"); + background-size: auto 100%; + border: @border_width_tiny@ solid @color_button_border@; + list-style-image: url("chrome://browser/skin/images/arrowdown-16.png"); +} + +.spinbuttons-up:hover:active:not([disabled=true]), +.spinbuttons-down:hover:active:not([disabled=true]) { + background-image: url("chrome://browser/skin/images/toggle-on.png"); +} + +.spinbuttons-up { + list-style-image: url("chrome://browser/skin/images/arrowup-16.png"); +} + +/* toolbar buttons --------------------------------------------------------- */ +toolbarbutton { + min-width: @touch_button_large@ !important; /* primary button size */ + min-height: @touch_button_large@ !important; /* primary button size */ + -moz-appearance: none !important; + margin: 0; + padding: @padding_xsmall@; +} + +toolbarbutton:not(.show-text) .toolbarbutton-text { + display: none; +} + +.toolbarbutton-icon[label]:not([label=""]), +.toolbarbutton-icon[type="menu"] { + -moz-margin-end: @margin_tiny@; +} + +toolbarbutton:not(.show-text) .toolbarbutton-icon, +toolbarbutton:not([image]) .toolbarbutton-icon, +toolbarbutton[image=''] .toolbarbutton-icon { + -moz-margin-end: 0; +} + +toolbarbutton:hover, +toolbarbutton:hover:active, +toolbarbutton[open="true"] { + border-color: transparent; +} + +/* checkbox buttons ----------------------------------------------------------- */ +.button-checkbox { + padding: 0 !important; + background: none !important; + border: none !important; + -moz-border-image: none !important; + color: @color_text_default@; + -moz-box-align: center; + font-size: @font_small@; + -moz-box-align: center; +} + +.prompt-checkbox-label { + text-align: left; +} + +.button-checkbox > .button-image-icon { + -moz-margin-end: @margin_normal@; + list-style-image: url("chrome://browser/skin/images/check-unselected-hdpi.png"); +} + +.button-checkbox[checked="true"] > .button-image-icon { + list-style-image: url("chrome://browser/skin/images/check-selected-hdpi.png"); +} + +.button-checkbox > .button-box, +.button-checkbox:hover:active > .button-box, +.button-checkbox[checked="true"] > .button-box { + padding-top: @padding_tiny@; + padding-bottom: @padding_xsmall@; + -moz-padding-start: @padding_small@; + -moz-padding-end: @padding_small@; +} + +/* radio buttons ----------------------------------------------------------- */ +radiogroup { + -moz-box-orient: horizontal; +} + +.radio-label { + font-weight: normal; + font-size: @font_snormal@ !important; +} + +radio { + -moz-appearance: none; + min-width: @touch_button_small@ !important; /* button size */ + min-height: @touch_button_small@ !important; /* button size */ + color: @color_text_button@; + padding: @padding_xnormal@; + margin: 0; + background-image: url("chrome://browser/skin/images/button-bg.png"); + background-size: auto 100%; + border-top: @border_width_tiny@ solid @color_button_border@; + border-bottom: @border_width_tiny@ solid @color_button_border@; +} + +radio .radio-icon, radio .radio-check { + display: none; +} + +radio:not([disabled=true]):hover:active, +radio[selected] { + color: white; + background-image: url("chrome://browser/skin/images/toggle-on.png"); +} + +radio:first-child { + border-left: @border_width_tiny@ solid @color_button_border@; +} + +radio:first-child:-moz-locale-dir(rtl) { + border-left: none; + border-right: @border_width_tiny@ solid @color_button_border@; +} + +radio:last-child { + border-right: @border_width_tiny@ solid @color_button_border@; +} + +radio:last-child:-moz-locale-dir(rtl) { + border-right: none; + border-left: @border_width_tiny@ solid @color_button_border@; +} + +radio[focused="true"] > .radio-label-box { + border: @border_width_tiny@ solid transparent; +} + +/* checkbox --------------------------------------------------------------- */ +checkbox { + margin: @margin_tiny@ @margin_small@ @margin_tiny@ @margin_small@; /* match platform style for buttons */ + -moz-binding: url("chrome://global/content/bindings/checkbox.xml#checkbox-with-spacing") !important; +} + +/* stop the focus from moving/showing the border around the label, which we don't use */ +checkbox:focus > .checkbox-label-center-box > .checkbox-label-box { + border: 1px solid transparent; +} + +.checkbox-check { + border: 2px transparent; + -moz-border-top-colors: -moz-initial; + -moz-border-right-colors: -moz-initial; + -moz-border-bottom-colors: -moz-initial; + -moz-border-left-colors: -moz-initial; + width: 46px; + height: 46px; + background: url("chrome://browser/skin/images/check-unselected-hdpi.png") no-repeat 50% 50%; +} + +checkbox[checked="true"] > .checkbox-spacer-box > .checkbox-check { + background-image: url("chrome://browser/skin/images/check-selected-hdpi.png"); +} + +checkbox[checked="true"][disabled="true"] > .checkbox-spacer-box > .checkbox-check { + background-image: url("chrome://browser/skin/images/check-selected-hdpi.png"); +} + +/* richlistbox ------------------------------------------------------------- */ +richlistbox { + color: @color_text_default@; + background-color: @color_background_default@; + -moz-user-focus: ignore; + margin: 0; +} + +richlistitem { + -moz-user-focus: ignore; + min-height: @touch_row@; /* row size */ + padding: @padding_small@; + border-bottom: @border_width_tiny@ solid @color_divider_border@; +} + +richlistitem label.title, +richlistitem description.title { + font-size: @font_normal@ !important; +} + +richlistitem label.normal, +richlistitem description.normal { + color: @color_subtext_default@; + font-size: @font_small@ !important; + white-space: pre-wrap; + word-wrap: break-word; +} + +richlistitem label.normal-black, +richlistitem description.normal-black { + font-size: @font_small@ !important; + white-space: pre-wrap; + word-wrap: break-word; +} + +richlistitem label.normal-bold, +richlistitem description.normal-bold { + font-weight: bold; + font-size: @font_small@ !important; + white-space: pre-wrap; + word-wrap: break-word; +} + +richlistitem[selected="true"] { + color: @color_text_default@; + background-color: @color_background_default@; +} + +richlistitem:hover:active:not([selected="true"]):not([nohighlight="true"]), +richlistitem:hover:active:not([selected="true"]):not([nohighlight="true"]) label.normal, +richlistitem:hover:active:not([selected="true"]):not([nohighlight="true"]) description.normal { + background-color: @color_background_highlight@; + color: @color_text_highlight@; +} + +richlistitem.section-header, +richlistitem[selected="true"].section-header { + font-weight: bold; + color: @color_text_header@; + background-color: @color_background_header@; +} + +richlistitem .show-on-select { + visibility: collapse; +} + +richlistitem[selected="true"] .show-on-select { + visibility: visible; +} + +richlistitem .hide-on-select { + visibility: visible; +} + +richlistitem[selected="true"] .hide-on-select { + visibility: collapse; +} + +richlistitem[typeName="message"] { + border-bottom: 0; +} + +/* colorpicker ------------------------------------------------------------- */ +colorpicker > panel { + background-color: #767973; +} + +colorpicker > vbox { + background-color: #767973; +} + +/* textbox ----------------------------------------------------------------- */ +.textbox-search-icon { + list-style-image: url("chrome://browser/skin/images/search-glass-30.png"); + -moz-image-region: auto; +} + +.textbox-search-clear { + list-style-image: url("chrome://browser/skin/images/search-clear-30.png"); + -moz-image-region: auto; +} + +/* menulist ---------------------------------------------------------------- */ +.menulist-label { + font-family: "Nokia Sans", Tahoma, sans-serif !important; + font-weight: normal; + font-size: @font_snormal@ !important; + background-color: transparent !important; +} + +menulist { + -moz-appearance: none !important; + -moz-user-focus: ignore; + min-width: @touch_button_minwidth@ !important; + min-height: @touch_button_small@ !important; /* button size */ + color: @color_text_button@ !important; + margin: @margin_normal@; + padding: @padding_small@ @padding_xnormal@; + background-image: url("chrome://browser/skin/images/button-bg.png"); + background-size: auto 100%; + border: @border_width_tiny@ solid @color_button_border@; +} + +menulist[disabled="true"] { + color: @color_text_disabled@ !important; + border: @border_width_tiny@ solid @color_button_border@ !important; +} + +menulist:not([disabled="true"]):hover:active { + background-image: url("chrome://browser/skin/images/toggle-off.png"); +} + +menulist > dropmarker { + height: 32px; + width: 32px; + margin-left: @margin_snormal@; + background-color: transparent; /* for windows */ + border: none; /* for windows */ + -moz-box-align: center; + -moz-box-pack: center; + list-style-image: url("chrome://browser/skin/images/dropmarker-hdpi.png"); + -moz-image-region: auto; + display: block; +} + +menulist[disabled="true"] > dropmarker { + opacity: 0.5; +} + +/* progressmeter ----------------------------------------------------------- */ +progressmeter { + background-color: #fff; + padding: @padding_small@; + height: @textbox_height@; + border: @border_width_large@ solid #aaa; + -moz-border-top-colors: -moz-initial; + -moz-border-right-colors: -moz-initial; + -moz-border-bottom-colors: -moz-initial; + -moz-border-left-colors: -moz-initial; +} + +.progress-bar { + background-color: @color_background_highlight@; +} + +/* panels / arrowboxes------------------------------------------------------ */ +arrowbox { + -moz-appearance: none; + background: transparent !important; + border: none; +} + +.arrowbox-dark .panel-arrowcontent { + padding: @padding_normal@; /* core spacing */ +} + +dialog, +.arrowbox-dark .panel-arrowcontent, +.panel-dark { + color: @color_text_default@; + background: @color_background_default@; +} + +dialog, +.arrowbox-dark .panel-arrowcontent { + border: @border_width_small@ solid @color_dialog_border@; + box-shadow: black 0 @shadow_width_small@ @shadow_width_small@; +} + +dialog { + margin: @margin_xxxnormal@ !important; + max-width: @dialog_width@; +} + +.prompt-message { + -moz-box-pack: center; + font-size: @font_snormal@; + margin: @padding_normal@; +} + +.prompt-title { + font-size: @font_xnormal@; + padding-top: @padding_xnormal@; + padding-left: @padding_normal@; +} + +/* Authentication dialogs do not have a title */ +.prompt-title:empty, +.prompt-title:empty + .prompt-line { + display: none; +} + +.prompt-line { + border-bottom: @border_width_tiny@ solid @color_divider_border@; + margin: @margin_small@ 0 0 0; + height: @padding_normal@ !important; +} + +.prompt-buttons { + font-size: @font_snormal@; + background-color: @color_background_dlgbuttons@; + display: inline-block; + text-align: center; +} + +.prompt-edit { + margin: @margin_xnormal@; + font-size: @font_normal@; + text-align: start; +} + +/*.panel-row-header ------------------------------------------------------------ */ +.panel-row-header { + border-bottom: @border_width_xxlarge@ solid @color_background_active@; + background-color: @color_background_default@ !important; + padding: 0 !important; +} + +.panel-row-button { + -moz-appearance: none; + background: @color_background_default@; + background-size: 100% 100%; + color: white; + border: 0 solid transparent !important; + -moz-border-start: @border_width_tiny@ solid rgba(255,255,255,0.2) !important; + -moz-border-end: @border_width_tiny@ solid rgba(0,0,0,0.2) !important; + padding-top: @padding_xsmall@ !important; + padding-bottom: @padding_xsmall@ !important; + -moz-padding-start: @padding_xsmall@ !important; + -moz-padding-end: @padding_xsmall@ !important; + -moz-box-flex: 1; + -moz-user-focus: ignore; + -moz-user-select: none; +} + +.panel-row-button:hover:active { + background: @color_background_active@; + background-size: 100% 100%; +} + +.panel-row-button:first-child { + -moz-border-start-width: 0 !important; +} + +.panel-row-button:last-child { + -moz-border-end-width: 0 !important; +} + +@media (@orientation@: portrait) { + .panel-row-button { + -moz-box-orient: vertical; + } + + .panel-row-button .toolbarbutton-text { + font-size: @font_xsmall@ !important; + } +} + +.panel-row-button .toolbarbutton-text { + text-align: left; + text-shadow: rgba(0,0,0,0.3) 0 @shadow_width_small@; +} + +.panel-row-button .toolbarbutton-text:-moz-locale-dir(rtl) { + text-align: right; +} + +.panel-row-button[disabled="true"] { + pointer-events: none; +} + +.panel-row-button[disabled="true"] .toolbarbutton-icon { + opacity: 0.5; +} + +.panel-row-button[disabled="true"] .toolbarbutton-text { + color: @color_text_disabled@; +} + +.panel-row-button[checked="true"] { + color: white !important; + background: @color_background_active@ !important; + background-size: 100% 100% !important; +} + +.panel-row-button[checked="true"], +.panel-row-button[disabled="true"] { + pointer-events: none; +} + +#panel-container-inner { + -moz-box-orient: vertical; +} + +#panel-controls { + -moz-box-orient: horizontal; +} + +@media (min-width: @tablet_panel_minwidth@) { + #panel-container-inner { + -moz-box-orient: horizontal; + -moz-box-pack: center; + } + + #panel-items { + max-width: @tablet_panel_minwidth@; + min-width: 0px !important; + } + + /* This will affect the prefs screen, but not the awesome screen */ + #panel-controls { + -moz-box-orient: vertical !important; + -moz-box-align: start; + } + + #panel-controls > .panel-row-button { + -moz-box-orient: horizontal; + -moz-box-flex: 0; + min-width: @tablet_panel_controls@ !important; + } + + #panel-controls .toolbarbutton-text { + display: -moz-box !important; + -moz-box-flex: 1; + } + + #panel-container { + -moz-box-pack: center; + padding: @padding_xlarge@ 0px; + } +} + +/* because the buttons can wrap, we need to use the margin to create inter-button + spacing and a bottom margin for the notification */ +notification > button { + margin-bottom: @margin_normal@; +} diff --git a/mobile/android/themes/core/header.css b/mobile/android/themes/core/header.css new file mode 100644 index 000000000000..3c221233a80e --- /dev/null +++ b/mobile/android/themes/core/header.css @@ -0,0 +1,47 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Mobile Browser. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Gavin Sharp + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +body { + font-family: helvetica,arial,clean,sans-serif; + color: #69645c; + background: white url("images/aboutBackground.jpg") top center repeat-x; +} + +#wrapper { + max-width: 600px; + margin: 0 auto; +} diff --git a/mobile/android/themes/core/honeycomb/browser.css b/mobile/android/themes/core/honeycomb/browser.css new file mode 100644 index 000000000000..811a152b8b03 --- /dev/null +++ b/mobile/android/themes/core/honeycomb/browser.css @@ -0,0 +1,68 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Mobile Browser. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +%filter substitution +%include defines.inc +%define honeycomb 1 + +/* content scrollbars */ +.scroller { + opacity: 0; + background-color: rgba(0, 0, 0, 0.4) !important; + -moz-border-top-colors: none !important; + -moz-border-bottom-colors: none !important; + -moz-border-right-colors: none !important; + -moz-border-left-colors: none !important; + -moz-border-radius: @border_radius_tiny@; + border: @border_width_tiny@ solid rgba(255, 255, 255, 0.4) !important; +} + +.scroller[panning="true"] { + opacity: 1; +} + +.scroller[orient="vertical"] { + min-width: @scroller_thickness@; + width: @scroller_thickness@; + min-height: @scroller_minimum@; +} + +.scroller[orient="horizontal"] { + min-height: @scroller_thickness@; + height: @scroller_thickness@; + min-width: @scroller_minimum@; +} diff --git a/mobile/android/themes/core/honeycomb/defines.inc b/mobile/android/themes/core/honeycomb/defines.inc new file mode 100644 index 000000000000..309a6c824804 --- /dev/null +++ b/mobile/android/themes/core/honeycomb/defines.inc @@ -0,0 +1,165 @@ +%filter substitution + +%define color_background_active #fff +%define color_text_active #222222 +%define color_background_default_window #fff +%define color_background_default rgba(255,255,255,0.95) +%define color_text_default #222222 +%define color_toolbar_background #eaeaea +%define color_toolbar_border #d9d9d9 +%define color_divider_border #6699ff +%define color_url_border #737373 +%define color_background_button #d9d9d9 +%define color_background_button_disabled #e3e3e3 +%define color_background_button_overlay rgba(217, 217, 217, 0.8) +%define color_button_border rgb(207,207,207) +%define color_background_dialog #fff +%define color_text_dialog #000 +%define color_dialog_border #5a5a5a +%define color_background_dlgbuttons #9a9a9a +%define color_background_panel #d6d6d6 +%define color_text_panel #000 +%define color_background_header #6699ff +%define color_text_header #fff +%define color_background_scroller #9a9a9a +%define color_background_textbox #fff +%define color_text_textbox #000 +%define color_text_disabled #808080 +%define color_text_button #222 +%define color_text_button_disabled #999 +%define color_text_placeholder #808080 +%define color_text_as_link #69f +%define color_background_panelrow #c8c8c8 +%define color_text_panelrow #222222 +%define color_text_toolbutton_inverse #666666 +%define color_background_settings #efefef +%define color_text_panel_header #999 +%define color_text_panel_subheader #333 +%define color_background_toaster #000 + +%define color_background_highlight #a7c5f4 +%define color_background_highlight_overlay rgba(167, 197, 244, 0.8) +%define color_text_highlight #000 +%define color_selection #c0e49a +%define color_shadow #6699ff +%define color_shadow_light rgba(102,153,255, 0.2) +%define color_shadow_green rgba(137,251,21, 0.2) +%define color_shadow_grey rgba(200,200,200, 0.5) + +%define color_subtext_default #aaaaaa +%define color_subtext_settings #666666 + +%define font_xlarge 6.08mozmm +%define font_xnormal 3.75mozmm +%define font_normal 3.54mozmm +%define font_snormal 3mozmm +%define font_small 2.91mozmm +%define font_xsmall 2.69mozmm +%define font_tiny 2.48mozmm +%define font_xtiny 2.27mozmm +%define font_xxtiny 1.93mozmm + +%define touch_row 10.478mozmm +%define touch_button_xlarge 10.62mozmm +%define touch_button_large 9.77mozmm +%define touch_button_small 8.93mozmm +%define touch_button_minwidth 11.86mozmm +%define touch_action_minwidth 21.17mozmm +%define touch_button_tiny 3.75mozmm +%define touch_button_minwidth 16.30mozmm +%define touch_button_minwidth 11.86mozmm +%define touch_normal 6.77mozmm + +%define margin_toaster 15.24mozmm +%define margin_context_popup 3.39mozmm +%define margin_xlarge 3.39mozmm +%define margin_large 2.54mozmm +%define margin_xxxnormal 1.69mozmm +%define margin_xxnormal 1.27mozmm +%define margin_xnormal 1.06mozmm +%define margin_normal 0.85mozmm +%define margin_snormal 0.64mozmm +%define margin_small 0.42mozmm +%define margin_tiny 0.21mozmm +%define margin_xtiny 0.15mozmm + +%define padding_xxxlarge 13.35mozmm +%define padding_xxlarge 7.938mozmm +%define padding_xlarge 3.39mozmm +%define padding_large 2.54mozmm +%define padding_xxxnormal 1.69mozmm +%define padding_xxnormal 1.27mozmm +%define padding_xnormal 1.06mozmm +%define padding_normal 0.85mozmm +%define padding_snormal 0.64mozmm +%define padding_small 0.42mozmm +%define padding_xsmall 0.21mozmm +%define padding_tiny 0.11mozmm + +%define border_width_xxlarge 0.64mozmm +%define border_width_xlarge 0.42mozmm +%define border_width_large 0.32mozmm +%define border_width_small 0.21mozmm +%define border_width_tiny 0.11mozmm + +%define border_radius_normal 0.85mozmm +%define border_radius_small 0.64mozmm +%define border_radius_xsmall 0.31mozmm +%define border_radius_tiny 0.21mozmm + +%define shadow_width_xlarge 3.06mozmm +%define shadow_width_large 2.64mozmm +%define shadow_width_medium 0.75mozmm +%define shadow_width_small 0.21mozmm +%define shadow_width_tiny 0.1mozmm + +%define textbox_height 5.08mozmm + +%define dropmarker_padding 0.53mozmm + +%define progressmeter_height 3.39mozmm + +%define urlbar_edit_height 5.25mozmm +%define urlbar_edit_indent 0.85mozmm +%define identity_popup_tablet_width 72mozmm + +%define scroller_thickness 0.64mozmm +%define scroller_minimum 1.27mozmm + +%define sidebar_width_minimum 8.47mozmm +%define sidebar_button_height 7.41mozmm +%define documenttab_margin_bottom 0.85mozmm + +%define placelabel_padding 8.47mozmm +%define placeitem_padding 4.23mozmm + +%define awesome_header_maxwidth 25mozmm +%define autocomplete_item_container_image_padding 0.53mozmm +%define autocomplete_item_container_position 0.21mozmm +%define autocomplete_item_container_size 4mozmm +%define autocomplete_item_container_padding 5.08mozmm + +%define autocomplete_item_subtitle_margin 2.75mozmm +%define autocomplete_item_label_margin 3.18mozmm +%define autocomplete_item_tags_margin 3.39mozmm + +%define autocompleteresult_padding 0.53mozmm + +%define dialog_width 76.2mozmm + +%define appmenu_portrait_height 21.17mozmm +%define appmenu_button_height 10.48mozmm + +%define tablet_panel_controls 40mozmm +%define tablet_panel_controls_wide 55mozmm +%define tablet_panel_minwidth 124mozmm +%define tablet_panel_item_width 69mozmm +%define tablet_panel_item_width_wide 90mozmm + +%ifdef MOZ_PLATFORM_MAEMO +%define orientation -moz-device-orientation +%elifdef ANDROID +%define orientation -moz-device-orientation +%else +%define orientation orientation +%endif diff --git a/mobile/android/themes/core/honeycomb/images/aboutBackground.jpg b/mobile/android/themes/core/honeycomb/images/aboutBackground.jpg new file mode 100644 index 000000000000..5dba1c58eb5a Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/aboutBackground.jpg differ diff --git a/mobile/android/themes/core/honeycomb/images/addons-default-hdpi.png b/mobile/android/themes/core/honeycomb/images/addons-default-hdpi.png new file mode 100644 index 000000000000..90ea938027a9 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/addons-default-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/alert-addons-30.png b/mobile/android/themes/core/honeycomb/images/alert-addons-30.png new file mode 100644 index 000000000000..18744ad85224 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/alert-addons-30.png differ diff --git a/mobile/android/themes/core/honeycomb/images/alert-downloads-30.png b/mobile/android/themes/core/honeycomb/images/alert-downloads-30.png new file mode 100644 index 000000000000..2546352d28f4 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/alert-downloads-30.png differ diff --git a/mobile/android/themes/core/honeycomb/images/arrowdown-16.png b/mobile/android/themes/core/honeycomb/images/arrowdown-16.png new file mode 100644 index 000000000000..c982426f2ade Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/arrowdown-16.png differ diff --git a/mobile/android/themes/core/honeycomb/images/arrowleft-16.png b/mobile/android/themes/core/honeycomb/images/arrowleft-16.png new file mode 100644 index 000000000000..464a4a866cd9 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/arrowleft-16.png differ diff --git a/mobile/android/themes/core/honeycomb/images/arrowright-16.png b/mobile/android/themes/core/honeycomb/images/arrowright-16.png new file mode 100644 index 000000000000..859e98ba64b6 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/arrowright-16.png differ diff --git a/mobile/android/themes/core/honeycomb/images/arrowup-16.png b/mobile/android/themes/core/honeycomb/images/arrowup-16.png new file mode 100644 index 000000000000..2a7e56485353 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/arrowup-16.png differ diff --git a/mobile/android/themes/core/honeycomb/images/back-default-hdpi.png b/mobile/android/themes/core/honeycomb/images/back-default-hdpi.png new file mode 100644 index 000000000000..0f868e75febb Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/back-default-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/browseaddons-bg.jpg b/mobile/android/themes/core/honeycomb/images/browseaddons-bg.jpg new file mode 100644 index 000000000000..dcd9a92b3522 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/browseaddons-bg.jpg differ diff --git a/mobile/android/themes/core/honeycomb/images/button-bg.png b/mobile/android/themes/core/honeycomb/images/button-bg.png new file mode 100644 index 000000000000..0dbc5329eaef Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/button-bg.png differ diff --git a/mobile/android/themes/core/honeycomb/images/check-selected-hdpi.png b/mobile/android/themes/core/honeycomb/images/check-selected-hdpi.png new file mode 100644 index 000000000000..10b067f39d5e Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/check-selected-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/check-selected-tap-hdpi.png b/mobile/android/themes/core/honeycomb/images/check-selected-tap-hdpi.png new file mode 100644 index 000000000000..85c7ff80ff6f Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/check-selected-tap-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/check-unselected-hdpi.png b/mobile/android/themes/core/honeycomb/images/check-unselected-hdpi.png new file mode 100644 index 000000000000..ca1e30df8b78 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/check-unselected-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/check-unselected-tap-hdpi.png b/mobile/android/themes/core/honeycomb/images/check-unselected-tap-hdpi.png new file mode 100644 index 000000000000..c478328b9ffd Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/check-unselected-tap-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/dropmarker-hdpi.png b/mobile/android/themes/core/honeycomb/images/dropmarker-hdpi.png new file mode 100644 index 000000000000..7bef658caa13 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/dropmarker-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/errorpage-larry-black.png b/mobile/android/themes/core/honeycomb/images/errorpage-larry-black.png new file mode 100644 index 000000000000..9f2e4a6e7366 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/errorpage-larry-black.png differ diff --git a/mobile/android/themes/core/honeycomb/images/errorpage-larry-white.png b/mobile/android/themes/core/honeycomb/images/errorpage-larry-white.png new file mode 100644 index 000000000000..fc153c7314e8 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/errorpage-larry-white.png differ diff --git a/mobile/android/themes/core/honeycomb/images/errorpage-warning.png b/mobile/android/themes/core/honeycomb/images/errorpage-warning.png new file mode 100644 index 000000000000..8bf9d8e7decc Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/errorpage-warning.png differ diff --git a/mobile/android/themes/core/honeycomb/images/favicon-default-32.png b/mobile/android/themes/core/honeycomb/images/favicon-default-32.png new file mode 100644 index 000000000000..8e66e5ae57e0 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/favicon-default-32.png differ diff --git a/mobile/android/themes/core/honeycomb/images/forward-default-hdpi.png b/mobile/android/themes/core/honeycomb/images/forward-default-hdpi.png new file mode 100644 index 000000000000..57611d4dc202 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/forward-default-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/locked-hdpi.png b/mobile/android/themes/core/honeycomb/images/locked-hdpi.png new file mode 100644 index 000000000000..766a56746feb Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/locked-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/menu-hdpi.png b/mobile/android/themes/core/honeycomb/images/menu-hdpi.png new file mode 100644 index 000000000000..96ba76a2a4a1 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/menu-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/menu-top-insideglow-green.png b/mobile/android/themes/core/honeycomb/images/menu-top-insideglow-green.png new file mode 100644 index 000000000000..61219f16108c Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/menu-top-insideglow-green.png differ diff --git a/mobile/android/themes/core/honeycomb/images/menu-top-insideglow-grey.png b/mobile/android/themes/core/honeycomb/images/menu-top-insideglow-grey.png new file mode 100644 index 000000000000..70d2d2ea76f7 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/menu-top-insideglow-grey.png differ diff --git a/mobile/android/themes/core/honeycomb/images/menu-top-insideglow.png b/mobile/android/themes/core/honeycomb/images/menu-top-insideglow.png new file mode 100644 index 000000000000..7bf59d5706fc Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/menu-top-insideglow.png differ diff --git a/mobile/android/themes/core/honeycomb/images/mute-hdpi.png b/mobile/android/themes/core/honeycomb/images/mute-hdpi.png new file mode 100644 index 000000000000..0bd1e60e6a3e Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/mute-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/navigation-magnifier-30.png b/mobile/android/themes/core/honeycomb/images/navigation-magnifier-30.png new file mode 100644 index 000000000000..5ca622b354cf Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/navigation-magnifier-30.png differ diff --git a/mobile/android/themes/core/honeycomb/images/next-disabled-hdpi.png b/mobile/android/themes/core/honeycomb/images/next-disabled-hdpi.png new file mode 100644 index 000000000000..81e68769e09f Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/next-disabled-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/next-hdpi.png b/mobile/android/themes/core/honeycomb/images/next-hdpi.png new file mode 100644 index 000000000000..c0115d491481 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/next-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/panelrow-active-hdpi.png b/mobile/android/themes/core/honeycomb/images/panelrow-active-hdpi.png new file mode 100644 index 000000000000..23676c11c477 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/panelrow-active-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/panelrow-default-hdpi.png b/mobile/android/themes/core/honeycomb/images/panelrow-default-hdpi.png new file mode 100644 index 000000000000..d8143e245c9a Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/panelrow-default-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/panelrow-selected-hdpi.png b/mobile/android/themes/core/honeycomb/images/panelrow-selected-hdpi.png new file mode 100644 index 000000000000..5e8dc6eb3a9f Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/panelrow-selected-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/pause-hdpi.png b/mobile/android/themes/core/honeycomb/images/pause-hdpi.png new file mode 100644 index 000000000000..e377d321ce03 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/pause-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/play-hdpi.png b/mobile/android/themes/core/honeycomb/images/play-hdpi.png new file mode 100644 index 000000000000..a8482eb4dd7e Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/play-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/popup-bg-hdpi.png b/mobile/android/themes/core/honeycomb/images/popup-bg-hdpi.png new file mode 100644 index 000000000000..df8e20c929f0 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/popup-bg-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/popup-selected-item-hdpi.png b/mobile/android/themes/core/honeycomb/images/popup-selected-item-hdpi.png new file mode 100644 index 000000000000..dd511023c1fa Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/popup-selected-item-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/previous-disabled-hdpi.png b/mobile/android/themes/core/honeycomb/images/previous-disabled-hdpi.png new file mode 100644 index 000000000000..459f5ab7de4c Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/previous-disabled-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/previous-hdpi.png b/mobile/android/themes/core/honeycomb/images/previous-hdpi.png new file mode 100644 index 000000000000..530206d58a54 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/previous-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/remotetabs-32.png b/mobile/android/themes/core/honeycomb/images/remotetabs-32.png new file mode 100644 index 000000000000..80f59a022c34 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/remotetabs-32.png differ diff --git a/mobile/android/themes/core/honeycomb/images/row-header-bg.png b/mobile/android/themes/core/honeycomb/images/row-header-bg.png new file mode 100644 index 000000000000..aaefd0f3d988 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/row-header-bg.png differ diff --git a/mobile/android/themes/core/honeycomb/images/scrubber-hdpi.png b/mobile/android/themes/core/honeycomb/images/scrubber-hdpi.png new file mode 100644 index 000000000000..49c60505f465 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/scrubber-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/search-clear-30.png b/mobile/android/themes/core/honeycomb/images/search-clear-30.png new file mode 100644 index 000000000000..8bab39a08d35 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/search-clear-30.png differ diff --git a/mobile/android/themes/core/honeycomb/images/search-glass-30.png b/mobile/android/themes/core/honeycomb/images/search-glass-30.png new file mode 100644 index 000000000000..71bf1f255ee1 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/search-glass-30.png differ diff --git a/mobile/android/themes/core/honeycomb/images/sidebarbutton-active-hdpi.png b/mobile/android/themes/core/honeycomb/images/sidebarbutton-active-hdpi.png new file mode 100644 index 000000000000..30f1d2017bc6 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/sidebarbutton-active-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/tabs-default-bg-rtl.png b/mobile/android/themes/core/honeycomb/images/tabs-default-bg-rtl.png new file mode 100644 index 000000000000..0700a979123b Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/tabs-default-bg-rtl.png differ diff --git a/mobile/android/themes/core/honeycomb/images/tabs-default-bg.png b/mobile/android/themes/core/honeycomb/images/tabs-default-bg.png new file mode 100644 index 000000000000..0762ade997d4 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/tabs-default-bg.png differ diff --git a/mobile/android/themes/core/honeycomb/images/tabs-hdpi.png b/mobile/android/themes/core/honeycomb/images/tabs-hdpi.png new file mode 100644 index 000000000000..95b43ce975aa Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/tabs-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/tabs-selected-bg-rtl.png b/mobile/android/themes/core/honeycomb/images/tabs-selected-bg-rtl.png new file mode 100644 index 000000000000..06d4647fd60f Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/tabs-selected-bg-rtl.png differ diff --git a/mobile/android/themes/core/honeycomb/images/tabs-selected-bg.png b/mobile/android/themes/core/honeycomb/images/tabs-selected-bg.png new file mode 100644 index 000000000000..3bf979e4cfc4 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/tabs-selected-bg.png differ diff --git a/mobile/android/themes/core/honeycomb/images/textbox-bg.png b/mobile/android/themes/core/honeycomb/images/textbox-bg.png new file mode 100644 index 000000000000..40edca065d63 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/textbox-bg.png differ diff --git a/mobile/android/themes/core/honeycomb/images/throbber.png b/mobile/android/themes/core/honeycomb/images/throbber.png new file mode 100644 index 000000000000..c601ec80ba7c Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/throbber.png differ diff --git a/mobile/android/themes/core/honeycomb/images/toggle-off.png b/mobile/android/themes/core/honeycomb/images/toggle-off.png new file mode 100644 index 000000000000..a27900e1fe49 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/toggle-off.png differ diff --git a/mobile/android/themes/core/honeycomb/images/toggle-on.png b/mobile/android/themes/core/honeycomb/images/toggle-on.png new file mode 100644 index 000000000000..a7dc009d5ea3 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/toggle-on.png differ diff --git a/mobile/android/themes/core/honeycomb/images/unlocked-hdpi.png b/mobile/android/themes/core/honeycomb/images/unlocked-hdpi.png new file mode 100644 index 000000000000..2b993352a4d8 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/unlocked-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/unmute-hdpi.png b/mobile/android/themes/core/honeycomb/images/unmute-hdpi.png new file mode 100644 index 000000000000..4dbb94f98f0a Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/unmute-hdpi.png differ diff --git a/mobile/android/themes/core/honeycomb/images/urlbar-border-bottom-active.png b/mobile/android/themes/core/honeycomb/images/urlbar-border-bottom-active.png new file mode 100644 index 000000000000..0c2e114032ef Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/urlbar-border-bottom-active.png differ diff --git a/mobile/android/themes/core/honeycomb/images/urlbar-border-bottom.png b/mobile/android/themes/core/honeycomb/images/urlbar-border-bottom.png new file mode 100644 index 000000000000..d527d50bbf56 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/urlbar-border-bottom.png differ diff --git a/mobile/android/themes/core/honeycomb/images/urlbar-border-side-active.png b/mobile/android/themes/core/honeycomb/images/urlbar-border-side-active.png new file mode 100644 index 000000000000..d422717f790b Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/urlbar-border-side-active.png differ diff --git a/mobile/android/themes/core/honeycomb/images/urlbar-border-side.png b/mobile/android/themes/core/honeycomb/images/urlbar-border-side.png new file mode 100644 index 000000000000..fe299ea362e3 Binary files /dev/null and b/mobile/android/themes/core/honeycomb/images/urlbar-border-side.png differ diff --git a/mobile/android/themes/core/honeycomb/platform.css b/mobile/android/themes/core/honeycomb/platform.css new file mode 100644 index 000000000000..b359b2c126b6 --- /dev/null +++ b/mobile/android/themes/core/honeycomb/platform.css @@ -0,0 +1,877 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Mobile Browser. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* global skin ------------------------------------------------------------- */ +@import url(chrome://global/skin/); + +%filter substitution +%include defines.inc + +/* general stuff ------------------------------------------------------------ */ +:root { + font-family: "Nokia Sans", Tahoma, sans-serif !important; + font-size: @font_normal@ !important; + background-color: @color_background_default_window@; /* force */ + color: @color_text_default@; /* force */ +} + +::-moz-selection { + background-color: @color_selection@; + color: @color_text_highlight@; +} + +menu, +menuitem { + padding: 0 !important; + margin: 0 !important; +} + +description, +label { + /* force mac to use the same margins as windows and linux */ + -moz-margin-start: @margin_snormal@; + -moz-margin-end: @margin_snormal@; +} + +/* Override any OS inverse themes */ +textbox { + color: @color_text_textbox@; + background-color: @color_background_textbox@; +} + +/* textboxes --------------------------------------------------------------- */ +textbox:not([type="number"]) { + min-height: @textbox_height@; + border: @border_width_small@ solid @color_button_border@; + -moz-border-top-colors: -moz-initial; + -moz-border-right-colors: -moz-initial; + -moz-border-bottom-colors: -moz-initial; + -moz-border-left-colors: -moz-initial; +} + +textbox[isempty="true"] { + color: @color_text_placeholder@; +} + +textbox.search-bar { + border: @border_width_small@ solid rgba(0,0,0,0.4); + background-color: #f9f9f9; + background: url("chrome://browser/skin/images/textbox-bg.png") top left repeat-x; + background-size: 100% 100%; +} + +textbox[disabled="true"] { + background-color: lightgray; +} + +.link { + color: @color_text_as_link@; + text-decoration: underline; +} + +/* sidebars spacer --------------------------------------------------------- */ +.sidebar-spacer { + background-color: #767973; +} + +/* prompt dialogs ---------------------------------------------------------- */ +.context-block, +.modal-block, +.perm-modal-block { + -moz-box-align: center; + -moz-box-pack: center; + background-color: rgba(0,0,0,.6); +} + +.dialog-dark, +.panel-arrowcontent { + background-color: @color_background_default@; + padding: 0; + box-shadow: 0 0 @shadow_width_tiny@ @shadow_width_medium@ @color_shadow_light@; +} + +.panel-arrowcontent { + background-image: url(chrome://browser/skin/images/menu-top-insideglow.png); + background-repeat: repeat-x; + background-position: top left; +} + +dialog > .prompt-header > .prompt-message { + white-space: pre-wrap; +} + +dialog > .prompt-header > .button-checkbox { + margin-left: @margin_large@; +} + +/* buttons ----------------------------------------------------------------- */ +.button-text, +.toolbarbutton-text { + font-weight: normal; + font-size: @font_xtiny@ !important; +} + +button { + -moz-appearance: none; + min-width: @touch_button_minwidth@ !important; + min-height: @touch_button_tiny@ !important; /* button size */ + color: @color_text_button@; + background-image: none; + background-color: @color_background_button@; + margin: @margin_normal@; + padding: @padding_xnormal@; + border-width: 0px; + border-radius: 0px; +} + +button[disabled="true"] { + color: @color_text_button_disabled@ !important; + background-color: @color_background_button_disabled@; +} + +button:not([disabled]):hover:active, +button:not([disabled])[checked="true"] { + background-color: @color_background_highlight@; +} + +/* Override GTK2 system setting */ +.button-icon { + display: -moz-initial !important; +} + +/* spinbuttons ------------------------------------------------------------- */ +spinbuttons { + border: none !important; +} + +.numberbox-input-box { + border: @border_width_small@ solid @color_button_border@; + border-right: 0 solid transparent; + -moz-border-top-colors: -moz-initial; + -moz-border-bottom-colors: -moz-initial; + -moz-border-left-colors: -moz-initial; +} + +.numberbox-input-box:-moz-locale-dir(rtl) { + border-right: @border_width_small@ solid @color_button_border@; + border-left: 0 solid transparent; +} + +.spinbuttons-box { + border: none !important; + -moz-box-orient: horizontal !important; + -moz-box-direction: reverse !important; +} + +.spinbuttons-up .button-icon, +.spinbuttons-down .button-icon { + display: block; +} + +.spinbuttons-up, +.spinbuttons-down { + -moz-appearance: none !important; + min-width: @touch_button_small@ !important; /* button size */ + min-height: @touch_button_small@ !important; /* button size */ + color: @color_text_button@; + margin: @margin_normal@; + padding: @padding_xnormal@; + background-image: url("chrome://browser/skin/images/button-bg.png"); + background-size: auto 100%; + border: @border_width_tiny@ solid @color_button_border@; + list-style-image: url("chrome://browser/skin/images/arrowdown-16.png"); +} + +.spinbuttons-up:hover:active:not([disabled=true]), +.spinbuttons-down:hover:active:not([disabled=true]) { + background-image: url("chrome://browser/skin/images/toggle-on.png"); +} + +.spinbuttons-up { + list-style-image: url("chrome://browser/skin/images/arrowup-16.png"); +} + +/* toolbar buttons --------------------------------------------------------- */ +toolbar { + -moz-appearance: none; + background-color: @color_background_default@; + max-height: @touch_button_small@; +} + +toolbarbutton { + min-width: @touch_button_small@ !important; /* primary button size */ + min-height: @touch_button_small@ !important; /* primary button size */ + -moz-appearance: none !important; + padding: @padding_xsmall@; +} + +toolbarbutton:not(.show-text) .toolbarbutton-text { + display: none; +} + +.toolbarbutton-icon[label]:not([label=""]), +.toolbarbutton-icon[type="menu"] { + -moz-margin-end: @margin_tiny@; +} + +toolbarbutton:not(.show-text) .toolbarbutton-icon, +toolbarbutton:not([image]) .toolbarbutton-icon, +toolbarbutton[image=''] .toolbarbutton-icon { + -moz-margin-end: 0; +} + +toolbarbutton:hover, +toolbarbutton:hover:active, +toolbarbutton[open="true"] { + border-color: transparent; +} + +toolbarbutton:not([disabled="true"]):active, +toolbarbutton:not([disabled="true"])[open="true"] { + background-color: @color_background_highlight@; +} + +/* checkbox buttons ----------------------------------------------------------- */ +.button-checkbox { + padding: 0 !important; + background: none !important; + border: none !important; + -moz-border-image: none !important; + color: @color_text_default@; + -moz-box-align: center; + font-size: @font_small@; + -moz-box-align: center; +} + +.prompt-checkbox-label { + text-align: left; +} + +.button-checkbox > .button-image-icon { + -moz-margin-end: @margin_normal@; + list-style-image: url("chrome://browser/skin/images/check-unselected-hdpi.png"); +} + +.button-checkbox[checked="true"] > .button-image-icon { + list-style-image: url("chrome://browser/skin/images/check-selected-hdpi.png"); +} + +.button-checkbox > .button-box, +.button-checkbox:hover:active > .button-box, +.button-checkbox[checked="true"] > .button-box { + padding-top: @padding_tiny@; + padding-bottom: @padding_xsmall@; + -moz-padding-start: @padding_small@; + -moz-padding-end: @padding_small@; +} + +/* radio buttons ----------------------------------------------------------- */ +radiogroup { + -moz-box-orient: horizontal; +} + +.radio-label { + font-weight: normal; + font-size: @font_snormal@ !important; +} + +radio { + -moz-appearance: none; + min-width: @touch_button_small@ !important; /* button size */ + min-height: @touch_button_small@ !important; /* button size */ + color: @color_text_button@; + padding: @padding_xnormal@; + margin: 0; + background-image: url("chrome://browser/skin/images/button-bg.png"); + background-size: auto 100%; + border-top: @border_width_tiny@ solid @color_button_border@; + border-bottom: @border_width_tiny@ solid @color_button_border@; +} + +radio .radio-icon, radio .radio-check { + display: none; +} + +radio:not([disabled=true]):hover:active, +radio[selected] { + color: white; + background-image: url("chrome://browser/skin/images/toggle-on.png"); +} + +radio:first-child { + border-left: @border_width_tiny@ solid @color_button_border@; +} + +radio:first-child:-moz-locale-dir(rtl) { + border-left: none; + border-right: @border_width_tiny@ solid @color_button_border@; +} + +radio:last-child { + border-right: @border_width_tiny@ solid @color_button_border@; +} + +radio:last-child:-moz-locale-dir(rtl) { + border-right: none; + border-left: @border_width_tiny@ solid @color_button_border@; +} + +radio[focused="true"] > .radio-label-box { + border: @border_width_tiny@ solid transparent; +} + +/* checkbox --------------------------------------------------------------- */ +checkbox { + margin: @margin_tiny@ @margin_small@ @margin_tiny@ @margin_small@; /* match platform style for buttons */ + -moz-binding: url("chrome://global/content/bindings/checkbox.xml#checkbox-with-spacing") !important; +} + +/* stop the focus from moving/showing the border around the label, which we don't use */ +checkbox:focus > .checkbox-label-center-box > .checkbox-label-box { + border: 1px solid transparent; +} + +.checkbox-check { + border: 2px transparent; + -moz-border-top-colors: -moz-initial; + -moz-border-right-colors: -moz-initial; + -moz-border-bottom-colors: -moz-initial; + -moz-border-left-colors: -moz-initial; + width: 46px; + height: 46px; + background: url("chrome://browser/skin/images/check-unselected-hdpi.png") no-repeat 50% 50%; +} + +checkbox[checked="true"] > .checkbox-spacer-box > .checkbox-check { + background-image: url("chrome://browser/skin/images/check-selected-hdpi.png"); +} + +checkbox[checked="true"][disabled="true"] > .checkbox-spacer-box > .checkbox-check { + background-image: url("chrome://browser/skin/images/check-selected-hdpi.png"); +} + +/* richlistbox ------------------------------------------------------------- */ +richlistbox { + color: @color_text_default@; + background-color: transparent; + -moz-user-focus: ignore; + margin: 0; +} + +richlistitem { + -moz-user-focus: ignore; + min-height: @touch_row@; /* row size */ + padding: @padding_small@; + border-bottom: @border_width_tiny@ solid @color_button_border@; +} + +richlistitem:last-child, +richlistitem[selector="last-child"] { + border-bottom: none; +} + +richlistitem label.title, +richlistitem description.title { + font-size: @font_snormal@ !important; +} + +richlistitem label.normal, +richlistitem description.normal { + color: @color_subtext_default@; + font-size: @font_xtiny@ !important; + white-space: pre-wrap; + word-wrap: break-word; +} + +richlistitem label.normal-black, +richlistitem description.normal-black { + font-size: @font_xtiny@ !important; + white-space: pre-wrap; + word-wrap: break-word; +} + +richlistitem label.normal-bold, +richlistitem description.normal-bold { + font-weight: bold; + font-size: @font_xtiny@ !important; + white-space: pre-wrap; + word-wrap: break-word; +} + +richlistitem[selected="true"] { + color: @color_text_default@; + background-color: @color_background_default@; +} + +richlistitem:hover:active:not([selected="true"]):not([nohighlight="true"]), +richlistitem:hover:active:not([selected="true"]):not([class="section-header"]), +richlistitem:hover:active:not([selected="true"]):not([nohighlight="true"]) label.normal, +richlistitem:hover:active:not([selected="true"]):not([nohighlight="true"]) description.normal { + background-color: @color_background_highlight@; + color: @color_text_highlight@; +} + +richlistitem.section-header, +richlistitem[selected="true"].section-header { + font-size: @font_xtiny@; + font-weight: bold; + color: @color_text_panel_subheader@; + background-color: @color_background_button@; + margin: 0; + padding: @padding_xxnormal@ @padding_large@; + border-top: @border_width_tiny@ solid @color_background_settings@; + border-bottom: @border_width_large@ solid; + -moz-border-bottom-colors: @color_background_button@ @color_background_settings@; +} + +richlistitem .show-on-select { + visibility: collapse; +} + +richlistitem[selected="true"] .show-on-select { + visibility: visible; +} + +richlistitem .hide-on-select { + visibility: visible; +} + +richlistitem[selected="true"] .hide-on-select { + visibility: collapse; +} + +richlistitem[typeName="message"] { + border-bottom: 0; +} + +richlistitem setting { + -moz-padding-start: 0; + -moz-padding-end: 0; +} + +richlistitem setting:first-child { + border-top: @border_width_tiny@ solid @color_background_button@; +} + +richlistitem[isDisabled="true"] .title, +richlistitem[isDisabled="true"] .normal { + color: @color_text_disabled@; +} + +richlistitem[isDisabled="true"] image { + opacity: 0.25; +} + +/* colorpicker ------------------------------------------------------------- */ +colorpicker > panel { + background-color: #767973; +} + +colorpicker > vbox { + background-color: #767973; +} + +/* textbox ----------------------------------------------------------------- */ +.textbox-search-icon { + list-style-image: url("chrome://browser/skin/images/search-glass-30.png"); + -moz-image-region: auto; +} + +.textbox-search-clear { + list-style-image: url("chrome://browser/skin/images/search-clear-30.png"); + -moz-image-region: auto; +} + +/* menulist ---------------------------------------------------------------- */ +.menulist-label { + font-family: "Nokia Sans", Tahoma, sans-serif !important; + font-weight: normal; + font-size: @font_xtiny@; + background-color: transparent !important; +} + +menulist { + -moz-appearance: none !important; + -moz-user-focus: ignore; + min-width: @touch_button_minwidth@ !important; + min-height: @touch_button_tiny@ !important; /* button size */ + color: @color_text_button@ !important; + background-color: @color_background_button@; + margin: @margin_normal@; + padding: @padding_xsmall@ @padding_xnormal@; + border-width: 0; + border-radius: 0; +} + +menulist[disabled="true"] { + color: @color_text_button_disabled@ !important; + background-color: @color_background_button_disabled@; +} + +menulist:not([disabled="true"]):hover:active { + background-color: @color_background_highlight@; +} + +menulist > dropmarker { + height: 32px; + width: 32px; + margin-left: @margin_snormal@; + background-color: transparent; /* for windows */ + border: none; /* for windows */ + -moz-box-align: center; + -moz-box-pack: center; + list-style-image: url("chrome://browser/skin/images/dropmarker-hdpi.png"); + -moz-image-region: auto; + display: block; +} + +menulist[disabled="true"] > dropmarker { + opacity: 0.5; +} + +/* progressmeter ----------------------------------------------------------- */ +progressmeter { + background-color: #fff; + padding: @padding_small@; + height: @textbox_height@; + border: @border_width_large@ solid #aaa; + -moz-border-top-colors: -moz-initial; + -moz-border-right-colors: -moz-initial; + -moz-border-bottom-colors: -moz-initial; + -moz-border-left-colors: -moz-initial; +} + +.progress-bar { + background-color: @color_background_highlight@; +} + +/* panels / arrowboxes------------------------------------------------------ */ +arrowbox { + -moz-appearance: none; + background: transparent !important; + border: none; +} + +.panel-arrow { + min-height: 18px; +} + +.arrowbox-dark .panel-arrowcontent { + padding: @padding_normal@; /* core spacing */ +} + +dialog, +.arrowbox-dark .panel-arrowcontent, +.panel-dark, +.dialog-dark { + color: @color_text_default@; + background: @color_background_default@; +} + +dialog { + border: @border_width_small@ solid @color_dialog_border@; + box-shadow: 0 0 @shadow_width_tiny@ @shadow_width_medium@ @color_shadow_light@; +} + +dialog, +.dialog-dark { + margin: @margin_xxxnormal@ !important; +} + +#context-popup, +#menulist-popup, +dialog { + margin: @margin_xxxnormal@ !important; + max-width: @dialog_width@; + -moz-box-flex: 1; +} + +.prompt-message { + -moz-box-pack: center; + font-size: @font_snormal@; + padding: @padding_normal@ @padding_large@; + min-height: @touch_row@; +} + +.prompt-header { + border-bottom: @border_width_tiny@ solid @color_button_border@; + margin: 0px @margin_large@ !important; +} + +.prompt-title { + font-size: @font_snormal@; + min-height: @touch_row@; + padding: @padding_xlarge@ @padding_large@; + margin: 0px; +} + +/* Authentication dialogs do not have a title */ +.prompt-title:empty, +.prompt-title:empty + .prompt-line { + display: none; +} + +.prompt-line { + background-color: transparent; + border-bottom: @border_width_xxlarge@ solid @color_divider_border@; + height: @border_width_xxlarge@ !important; +} + +.prompt-buttons { + font-size: @font_snormal@; + background-color: transparent; + text-align: center; +} + +.prompt-edit { + margin: @margin_xnormal@; + font-size: @font_normal@; + text-align: start; +} + +/*.panel-row-header ------------------------------------------------------------ */ +.panel-row-header { + background-color: @color_background_panelrow@ !important; + color: @color_text_panelrow@; + padding: 0 !important; +} + +.prompt-button, +.panel-row-button { + color: @color_text_panelrow@; + -moz-appearance: none; + border: @border_width_tiny@ solid transparent; + -moz-user-focus: ignore; + -moz-user-select: none; + margin: @margin_xtiny@; + -moz-margin-end: -moz-calc(2 * @margin_xtiny@); + padding: @padding_normal@ 0px !important; + -moz-box-flex: 1; + background-image: none; + background-color: transparent; + border-radius: 0px !important; +} + +.prompt-button[disabled="true"] { + border: none !important; +} + +.prompt-button > .button-box { + -moz-border-end: @border_width_tiny@ solid @color_button_border@; + -moz-border-right-colors: transparent @color_button_border@; + -moz-margin-end: -moz-calc(-3 * @border_width_tiny@); + padding: @padding_xxxnormal@ 0; +} + +.prompt-button:last-of-type { + -moz-margin-end: @margin_xtiny@; +} + +.prompt-button:last-of-type > .button-box { + -moz-border-end: none; +} + +.prompt-button:not([disabled]):hover:active, +.panel-row-button:hover:active { + background-image: none; + background-color: @color_background_highlight@; + color: @color_text_highlight@; +} + +.prompt-button { + min-width: 33%; + min-height: @touch_button_small@; +} + +.prompt-button .button-text { + font-size: @font_snormal@ !important; +} + +.panel-row-button:first-child { + -moz-border-start-width: 0 !important; +} + +.panel-row-button:last-child { + -moz-border-end-width: 0 !important; +} + +@media (@orientation@: portrait) { + .panel-row-button { + -moz-box-orient: vertical; + } +} + +@media (@orientation@: portrait) and (min-width: @tablet_panel_minwidth@) { + #panel-controls > .panel-row-button { + min-width: @tablet_panel_controls@ !important; + } + + #panel-items { + max-width: @tablet_panel_item_width@ !important; + } +} + +@media (@orientation@: landscape) and (min-width: @tablet_panel_minwidth@) { + #panel-controls > .panel-row-button { + min-width: @tablet_panel_controls_wide@ !important; + } + + #panel-items { + max-width: @tablet_panel_item_width_wide@ !important; + } +} + +.panel-row-button .toolbarbutton-text { + text-align: left; + font-size: @font_xtiny@ !important; +} + +.panel-row-button .toolbarbutton-text:-moz-locale-dir(rtl) { + text-align: right; +} + +.panel-row-button[disabled="true"] { + pointer-events: none; +} + +.panel-row-button[disabled="true"] .toolbarbutton-icon { + opacity: 0.5; +} + +.panel-row-button[disabled="true"] .toolbarbutton-text { + color: @color_text_disabled@; +} + +.panel-row-button[checked="true"] { + background: @color_background_default_window@; + color: @color_text_default@; +} + +.panel-row-button[checked="true"], +.panel-row-button[disabled="true"] { + pointer-events: none; +} + +#panel-container { + background-color: @color_background_settings@ !important; +} + +#panel-container-inner { + -moz-box-orient: vertical; + background-color: transparent !important; +} + +#panel-container-inner > vbox { + border: @border_width_large@ solid @color_background_active@; + box-shadow: 0 0 @shadow_width_tiny@ @shadow_width_medium@ @color_background_button_overlay@; +} + +#panel-controls { + -moz-box-orient: horizontal; + background-color: transparent !important; + padding: @padding_xxxnormal@ 0 !important; +} + +#panel-controls .toolbarbutton-text { + font-size: @font_snormal@ !important; +} + +#panel-controls .toolbarbutton-icon[label]:not([label=""]), +#panel-controls .toolbarbutton-icon[type="menu"] { + -moz-margin-start: @margin_xxxnormal@; + -moz-margin-end: @margin_xnormal@; +} + +#panel-controls > .panel-row-button { + border-top: @border_width_tiny@ solid @color_background_settings@ !important; + border-bottom: @border_width_large@ solid !important; + -moz-border-bottom-colors: @color_background_button@ @color_background_settings@ !important; + -moz-border-start: 0 solid; + -moz-border-end: 0 solid; +} + +#panel-controls > .panel-row-button[checked="true"] { + background-color: @color_background_highlight@; + color: @color_text_default@; +} + +.panel-list { + background: transparent; +} + +@media (min-width: @tablet_panel_minwidth@) { + #panel-container-inner { + -moz-box-orient: horizontal; + -moz-box-pack: center; + } + + #panel-items { + min-width: 0px !important; + } + + /* This will affect the prefs screen, but not the awesome screen */ + #panel-controls { + -moz-box-orient: vertical !important; + -moz-box-align: start; + } + + #panel-controls > .panel-row-button { + margin: 0; + -moz-box-orient: horizontal; + -moz-box-flex: 0; + } + + #panel-controls .toolbarbutton-text { + display: -moz-box !important; + -moz-box-flex: 1; + } + + #panel-container { + -moz-box-pack: center; + padding: @padding_xxxlarge@ 0 @padding_xlarge@ 0; + } + + #panel-container-inner > vbox { + padding: @padding_xlarge@ @padding_xxlarge@; + } +} + +/* because the buttons can wrap, we need to use the margin to create inter-button + spacing and a bottom margin for the notification */ +notification > button { + margin-bottom: @margin_normal@; +} diff --git a/mobile/android/themes/core/images/aboutBackground.jpg b/mobile/android/themes/core/images/aboutBackground.jpg new file mode 100644 index 000000000000..5dba1c58eb5a Binary files /dev/null and b/mobile/android/themes/core/images/aboutBackground.jpg differ diff --git a/mobile/android/themes/core/images/addons-32.png b/mobile/android/themes/core/images/addons-32.png new file mode 100644 index 000000000000..9679afd823c3 Binary files /dev/null and b/mobile/android/themes/core/images/addons-32.png differ diff --git a/mobile/android/themes/core/images/addons-default-hdpi.png b/mobile/android/themes/core/images/addons-default-hdpi.png new file mode 100644 index 000000000000..25f09906e011 Binary files /dev/null and b/mobile/android/themes/core/images/addons-default-hdpi.png differ diff --git a/mobile/android/themes/core/images/alert-addons-30.png b/mobile/android/themes/core/images/alert-addons-30.png new file mode 100644 index 000000000000..18744ad85224 Binary files /dev/null and b/mobile/android/themes/core/images/alert-addons-30.png differ diff --git a/mobile/android/themes/core/images/alert-downloads-30.png b/mobile/android/themes/core/images/alert-downloads-30.png new file mode 100644 index 000000000000..2546352d28f4 Binary files /dev/null and b/mobile/android/themes/core/images/alert-downloads-30.png differ diff --git a/mobile/android/themes/core/images/arrowdown-16.png b/mobile/android/themes/core/images/arrowdown-16.png new file mode 100644 index 000000000000..c982426f2ade Binary files /dev/null and b/mobile/android/themes/core/images/arrowdown-16.png differ diff --git a/mobile/android/themes/core/images/arrowleft-16.png b/mobile/android/themes/core/images/arrowleft-16.png new file mode 100644 index 000000000000..464a4a866cd9 Binary files /dev/null and b/mobile/android/themes/core/images/arrowleft-16.png differ diff --git a/mobile/android/themes/core/images/arrowright-16.png b/mobile/android/themes/core/images/arrowright-16.png new file mode 100644 index 000000000000..859e98ba64b6 Binary files /dev/null and b/mobile/android/themes/core/images/arrowright-16.png differ diff --git a/mobile/android/themes/core/images/arrowup-16.png b/mobile/android/themes/core/images/arrowup-16.png new file mode 100644 index 000000000000..2a7e56485353 Binary files /dev/null and b/mobile/android/themes/core/images/arrowup-16.png differ diff --git a/mobile/android/themes/core/images/aurora-lightbox-bg.jpg b/mobile/android/themes/core/images/aurora-lightbox-bg.jpg new file mode 100644 index 000000000000..e25ca85aa8a3 Binary files /dev/null and b/mobile/android/themes/core/images/aurora-lightbox-bg.jpg differ diff --git a/mobile/android/themes/core/images/aurora-lightbox-close.png b/mobile/android/themes/core/images/aurora-lightbox-close.png new file mode 100644 index 000000000000..394e7c4d354a Binary files /dev/null and b/mobile/android/themes/core/images/aurora-lightbox-close.png differ diff --git a/mobile/android/themes/core/images/aurora-lightbox-logo.png b/mobile/android/themes/core/images/aurora-lightbox-logo.png new file mode 100644 index 000000000000..12cb9d7e22b5 Binary files /dev/null and b/mobile/android/themes/core/images/aurora-lightbox-logo.png differ diff --git a/mobile/android/themes/core/images/back-default-hdpi.png b/mobile/android/themes/core/images/back-default-hdpi.png new file mode 100644 index 000000000000..8c2387cfcbec Binary files /dev/null and b/mobile/android/themes/core/images/back-default-hdpi.png differ diff --git a/mobile/android/themes/core/images/browseaddons-bg.jpg b/mobile/android/themes/core/images/browseaddons-bg.jpg new file mode 100644 index 000000000000..dcd9a92b3522 Binary files /dev/null and b/mobile/android/themes/core/images/browseaddons-bg.jpg differ diff --git a/mobile/android/themes/core/images/button-bg.png b/mobile/android/themes/core/images/button-bg.png new file mode 100644 index 000000000000..0dbc5329eaef Binary files /dev/null and b/mobile/android/themes/core/images/button-bg.png differ diff --git a/mobile/android/themes/core/images/check-30.png b/mobile/android/themes/core/images/check-30.png new file mode 100644 index 000000000000..82cf8f415fa7 Binary files /dev/null and b/mobile/android/themes/core/images/check-30.png differ diff --git a/mobile/android/themes/core/images/check-selected-30.png b/mobile/android/themes/core/images/check-selected-30.png new file mode 100644 index 000000000000..a29f65655ef5 Binary files /dev/null and b/mobile/android/themes/core/images/check-selected-30.png differ diff --git a/mobile/android/themes/core/images/check-unselected-30.png b/mobile/android/themes/core/images/check-unselected-30.png new file mode 100644 index 000000000000..054c7373b405 Binary files /dev/null and b/mobile/android/themes/core/images/check-unselected-30.png differ diff --git a/mobile/android/themes/core/images/checkmark-hdpi.png b/mobile/android/themes/core/images/checkmark-hdpi.png new file mode 100644 index 000000000000..affc84c69603 Binary files /dev/null and b/mobile/android/themes/core/images/checkmark-hdpi.png differ diff --git a/mobile/android/themes/core/images/dropmarker-hdpi.png b/mobile/android/themes/core/images/dropmarker-hdpi.png new file mode 100644 index 000000000000..960fbd6ab247 Binary files /dev/null and b/mobile/android/themes/core/images/dropmarker-hdpi.png differ diff --git a/mobile/android/themes/core/images/errorpage-larry-black.png b/mobile/android/themes/core/images/errorpage-larry-black.png new file mode 100644 index 000000000000..9f2e4a6e7366 Binary files /dev/null and b/mobile/android/themes/core/images/errorpage-larry-black.png differ diff --git a/mobile/android/themes/core/images/errorpage-larry-white.png b/mobile/android/themes/core/images/errorpage-larry-white.png new file mode 100644 index 000000000000..fc153c7314e8 Binary files /dev/null and b/mobile/android/themes/core/images/errorpage-larry-white.png differ diff --git a/mobile/android/themes/core/images/errorpage-warning.png b/mobile/android/themes/core/images/errorpage-warning.png new file mode 100644 index 000000000000..8bf9d8e7decc Binary files /dev/null and b/mobile/android/themes/core/images/errorpage-warning.png differ diff --git a/mobile/android/themes/core/images/favicon-default-32.png b/mobile/android/themes/core/images/favicon-default-32.png new file mode 100644 index 000000000000..8e66e5ae57e0 Binary files /dev/null and b/mobile/android/themes/core/images/favicon-default-32.png differ diff --git a/mobile/android/themes/core/images/forward-default-hdpi.png b/mobile/android/themes/core/images/forward-default-hdpi.png new file mode 100644 index 000000000000..4bcf5010976b Binary files /dev/null and b/mobile/android/themes/core/images/forward-default-hdpi.png differ diff --git a/mobile/android/themes/core/images/handle-end.png b/mobile/android/themes/core/images/handle-end.png new file mode 100644 index 000000000000..d2720e70ec28 Binary files /dev/null and b/mobile/android/themes/core/images/handle-end.png differ diff --git a/mobile/android/themes/core/images/handle-start.png b/mobile/android/themes/core/images/handle-start.png new file mode 100644 index 000000000000..8abd627f0df7 Binary files /dev/null and b/mobile/android/themes/core/images/handle-start.png differ diff --git a/mobile/android/themes/core/images/homescreen-blank-hdpi.png b/mobile/android/themes/core/images/homescreen-blank-hdpi.png new file mode 100644 index 000000000000..066864cf79ef Binary files /dev/null and b/mobile/android/themes/core/images/homescreen-blank-hdpi.png differ diff --git a/mobile/android/themes/core/images/homescreen-default-hdpi.png b/mobile/android/themes/core/images/homescreen-default-hdpi.png new file mode 100644 index 000000000000..963b5770fd95 Binary files /dev/null and b/mobile/android/themes/core/images/homescreen-default-hdpi.png differ diff --git a/mobile/android/themes/core/images/locked-hdpi.png b/mobile/android/themes/core/images/locked-hdpi.png new file mode 100644 index 000000000000..e50bc4f1dd28 Binary files /dev/null and b/mobile/android/themes/core/images/locked-hdpi.png differ diff --git a/mobile/android/themes/core/images/menu-hdpi.png b/mobile/android/themes/core/images/menu-hdpi.png new file mode 100644 index 000000000000..8662b9319c44 Binary files /dev/null and b/mobile/android/themes/core/images/menu-hdpi.png differ diff --git a/mobile/android/themes/core/images/mozilla-32.png b/mobile/android/themes/core/images/mozilla-32.png new file mode 100644 index 000000000000..98d8789e274a Binary files /dev/null and b/mobile/android/themes/core/images/mozilla-32.png differ diff --git a/mobile/android/themes/core/images/mute-hdpi.png b/mobile/android/themes/core/images/mute-hdpi.png new file mode 100644 index 000000000000..0bd1e60e6a3e Binary files /dev/null and b/mobile/android/themes/core/images/mute-hdpi.png differ diff --git a/mobile/android/themes/core/images/navigation-magnifier-30.png b/mobile/android/themes/core/images/navigation-magnifier-30.png new file mode 100644 index 000000000000..48001657c465 Binary files /dev/null and b/mobile/android/themes/core/images/navigation-magnifier-30.png differ diff --git a/mobile/android/themes/core/images/next-disabled-hdpi.png b/mobile/android/themes/core/images/next-disabled-hdpi.png new file mode 100644 index 000000000000..81e68769e09f Binary files /dev/null and b/mobile/android/themes/core/images/next-disabled-hdpi.png differ diff --git a/mobile/android/themes/core/images/next-hdpi.png b/mobile/android/themes/core/images/next-hdpi.png new file mode 100644 index 000000000000..c0115d491481 Binary files /dev/null and b/mobile/android/themes/core/images/next-hdpi.png differ diff --git a/mobile/android/themes/core/images/panelrow-active-hdpi.png b/mobile/android/themes/core/images/panelrow-active-hdpi.png new file mode 100644 index 000000000000..23676c11c477 Binary files /dev/null and b/mobile/android/themes/core/images/panelrow-active-hdpi.png differ diff --git a/mobile/android/themes/core/images/panelrow-default-hdpi.png b/mobile/android/themes/core/images/panelrow-default-hdpi.png new file mode 100644 index 000000000000..d8143e245c9a Binary files /dev/null and b/mobile/android/themes/core/images/panelrow-default-hdpi.png differ diff --git a/mobile/android/themes/core/images/panelrow-selected-hdpi.png b/mobile/android/themes/core/images/panelrow-selected-hdpi.png new file mode 100644 index 000000000000..5e8dc6eb3a9f Binary files /dev/null and b/mobile/android/themes/core/images/panelrow-selected-hdpi.png differ diff --git a/mobile/android/themes/core/images/pause-hdpi.png b/mobile/android/themes/core/images/pause-hdpi.png new file mode 100644 index 000000000000..e377d321ce03 Binary files /dev/null and b/mobile/android/themes/core/images/pause-hdpi.png differ diff --git a/mobile/android/themes/core/images/play-hdpi.png b/mobile/android/themes/core/images/play-hdpi.png new file mode 100644 index 000000000000..a8482eb4dd7e Binary files /dev/null and b/mobile/android/themes/core/images/play-hdpi.png differ diff --git a/mobile/android/themes/core/images/popup-bg-hdpi.png b/mobile/android/themes/core/images/popup-bg-hdpi.png new file mode 100644 index 000000000000..df8e20c929f0 Binary files /dev/null and b/mobile/android/themes/core/images/popup-bg-hdpi.png differ diff --git a/mobile/android/themes/core/images/popup-selected-item-hdpi.png b/mobile/android/themes/core/images/popup-selected-item-hdpi.png new file mode 100644 index 000000000000..dd511023c1fa Binary files /dev/null and b/mobile/android/themes/core/images/popup-selected-item-hdpi.png differ diff --git a/mobile/android/themes/core/images/previous-disabled-hdpi.png b/mobile/android/themes/core/images/previous-disabled-hdpi.png new file mode 100644 index 000000000000..459f5ab7de4c Binary files /dev/null and b/mobile/android/themes/core/images/previous-disabled-hdpi.png differ diff --git a/mobile/android/themes/core/images/previous-hdpi.png b/mobile/android/themes/core/images/previous-hdpi.png new file mode 100644 index 000000000000..530206d58a54 Binary files /dev/null and b/mobile/android/themes/core/images/previous-hdpi.png differ diff --git a/mobile/android/themes/core/images/ratings-18.png b/mobile/android/themes/core/images/ratings-18.png new file mode 100644 index 000000000000..b264e6d01681 Binary files /dev/null and b/mobile/android/themes/core/images/ratings-18.png differ diff --git a/mobile/android/themes/core/images/remotetabs-32.png b/mobile/android/themes/core/images/remotetabs-32.png new file mode 100644 index 000000000000..80f59a022c34 Binary files /dev/null and b/mobile/android/themes/core/images/remotetabs-32.png differ diff --git a/mobile/android/themes/core/images/row-header-bg.png b/mobile/android/themes/core/images/row-header-bg.png new file mode 100644 index 000000000000..aaefd0f3d988 Binary files /dev/null and b/mobile/android/themes/core/images/row-header-bg.png differ diff --git a/mobile/android/themes/core/images/scrubber-hdpi.png b/mobile/android/themes/core/images/scrubber-hdpi.png new file mode 100644 index 000000000000..49c60505f465 Binary files /dev/null and b/mobile/android/themes/core/images/scrubber-hdpi.png differ diff --git a/mobile/android/themes/core/images/search-clear-30.png b/mobile/android/themes/core/images/search-clear-30.png new file mode 100644 index 000000000000..8bab39a08d35 Binary files /dev/null and b/mobile/android/themes/core/images/search-clear-30.png differ diff --git a/mobile/android/themes/core/images/search-glass-30.png b/mobile/android/themes/core/images/search-glass-30.png new file mode 100644 index 000000000000..71bf1f255ee1 Binary files /dev/null and b/mobile/android/themes/core/images/search-glass-30.png differ diff --git a/mobile/android/themes/core/images/section-collapsed-16.png b/mobile/android/themes/core/images/section-collapsed-16.png new file mode 100644 index 000000000000..c9805f654c79 Binary files /dev/null and b/mobile/android/themes/core/images/section-collapsed-16.png differ diff --git a/mobile/android/themes/core/images/section-expanded-16.png b/mobile/android/themes/core/images/section-expanded-16.png new file mode 100644 index 000000000000..128cef90af66 Binary files /dev/null and b/mobile/android/themes/core/images/section-expanded-16.png differ diff --git a/mobile/android/themes/core/images/sidebarbutton-active-hdpi.png b/mobile/android/themes/core/images/sidebarbutton-active-hdpi.png new file mode 100644 index 000000000000..30f1d2017bc6 Binary files /dev/null and b/mobile/android/themes/core/images/sidebarbutton-active-hdpi.png differ diff --git a/mobile/android/themes/core/images/tabs-hdpi.png b/mobile/android/themes/core/images/tabs-hdpi.png new file mode 100644 index 000000000000..ba656e12dc26 Binary files /dev/null and b/mobile/android/themes/core/images/tabs-hdpi.png differ diff --git a/mobile/android/themes/core/images/textbox-bg.png b/mobile/android/themes/core/images/textbox-bg.png new file mode 100644 index 000000000000..40edca065d63 Binary files /dev/null and b/mobile/android/themes/core/images/textbox-bg.png differ diff --git a/mobile/android/themes/core/images/throbber.png b/mobile/android/themes/core/images/throbber.png new file mode 100644 index 000000000000..c601ec80ba7c Binary files /dev/null and b/mobile/android/themes/core/images/throbber.png differ diff --git a/mobile/android/themes/core/images/toggle-off.png b/mobile/android/themes/core/images/toggle-off.png new file mode 100644 index 000000000000..a27900e1fe49 Binary files /dev/null and b/mobile/android/themes/core/images/toggle-off.png differ diff --git a/mobile/android/themes/core/images/toggle-on.png b/mobile/android/themes/core/images/toggle-on.png new file mode 100644 index 000000000000..a7dc009d5ea3 Binary files /dev/null and b/mobile/android/themes/core/images/toggle-on.png differ diff --git a/mobile/android/themes/core/images/unlocked-hdpi.png b/mobile/android/themes/core/images/unlocked-hdpi.png new file mode 100644 index 000000000000..359923dc6cc4 Binary files /dev/null and b/mobile/android/themes/core/images/unlocked-hdpi.png differ diff --git a/mobile/android/themes/core/images/unmute-hdpi.png b/mobile/android/themes/core/images/unmute-hdpi.png new file mode 100644 index 000000000000..4dbb94f98f0a Binary files /dev/null and b/mobile/android/themes/core/images/unmute-hdpi.png differ diff --git a/mobile/android/themes/core/jar.mn b/mobile/android/themes/core/jar.mn new file mode 100644 index 000000000000..7b96c87866d9 --- /dev/null +++ b/mobile/android/themes/core/jar.mn @@ -0,0 +1,256 @@ +#filter substitution + +chrome.jar: +% skin browser classic/1.0 %skin/ os!=Android +% skin browser classic/1.0 %skin/ os=Android osversion<2.3 +% skin browser froyo/1.0 %skin/ +# NOTE: If you add a new file here, you'll need to add it to the gingerbread +# and honeycomb sections at the bottom of this file + skin/aboutPage.css (aboutPage.css) + skin/about.css (about.css) + skin/aboutAddons.css (aboutAddons.css) +* skin/browser.css (browser.css) +* skin/content.css (content.css) + skin/config.css (config.css) + skin/header.css (header.css) +* skin/platform.css (platform.css) + skin/touchcontrols.css (touchcontrols.css) + skin/netError.css (netError.css) +% override chrome://global/skin/about.css chrome://browser/skin/about.css +% override chrome://global/skin/media/videocontrols.css chrome://browser/skin/touchcontrols.css +% override chrome://global/skin/netError.css chrome://browser/skin/netError.css + + skin/images/aboutBackground.jpg (images/aboutBackground.jpg) + skin/images/button-bg.png (images/button-bg.png) + skin/images/textbox-bg.png (images/textbox-bg.png) + skin/images/browseaddons-bg.jpg (images/browseaddons-bg.jpg) + skin/images/addons-32.png (images/addons-32.png) + skin/images/arrowleft-16.png (images/arrowleft-16.png) + skin/images/arrowright-16.png (images/arrowright-16.png) + skin/images/arrowup-16.png (images/arrowup-16.png) + skin/images/arrowdown-16.png (images/arrowdown-16.png) + skin/images/popup-bg-hdpi.png (images/popup-bg-hdpi.png) + skin/images/popup-selected-item-hdpi.png (images/popup-selected-item-hdpi.png) + skin/images/checkmark-hdpi.png (images/checkmark-hdpi.png) + skin/images/check-selected-30.png (images/check-selected-30.png) + skin/images/check-unselected-30.png (images/check-unselected-30.png) + skin/images/dropmarker-hdpi.png (images/dropmarker-hdpi.png) + skin/images/ratings-18.png (images/ratings-18.png) + skin/images/favicon-default-32.png (images/favicon-default-32.png) + skin/images/errorpage-warning.png (images/errorpage-warning.png) + skin/images/errorpage-warning.png (images/errorpage-warning.png) + skin/images/errorpage-larry-white.png (images/errorpage-larry-white.png) + skin/images/errorpage-larry-black.png (images/errorpage-larry-black.png) + skin/images/throbber.png (images/throbber.png) + skin/images/navigation-magnifier-30.png (images/navigation-magnifier-30.png) + skin/images/alert-addons-30.png (images/alert-addons-30.png) + skin/images/alert-downloads-30.png (images/alert-downloads-30.png) + skin/images/addons-default-hdpi.png (images/addons-default-hdpi.png) + skin/images/back-default-hdpi.png (images/back-default-hdpi.png) + skin/images/tabs-hdpi.png (images/tabs-hdpi.png) + skin/images/menu-hdpi.png (images/menu-hdpi.png) + skin/images/panelrow-active-hdpi.png (images/panelrow-active-hdpi.png) + skin/images/panelrow-default-hdpi.png (images/panelrow-default-hdpi.png) + skin/images/panelrow-selected-hdpi.png (images/panelrow-selected-hdpi.png) + skin/images/forward-default-hdpi.png (images/forward-default-hdpi.png) + skin/images/row-header-bg.png (images/row-header-bg.png) + skin/images/remotetabs-32.png (images/remotetabs-32.png) + skin/images/mozilla-32.png (images/mozilla-32.png) + skin/images/toggle-on.png (images/toggle-on.png) + skin/images/toggle-off.png (images/toggle-off.png) + skin/images/sidebarbutton-active-hdpi.png (images/sidebarbutton-active-hdpi.png) + skin/images/previous-hdpi.png (images/previous-hdpi.png) + skin/images/previous-disabled-hdpi.png (images/previous-disabled-hdpi.png) + skin/images/next-hdpi.png (images/next-hdpi.png) + skin/images/next-disabled-hdpi.png (images/next-disabled-hdpi.png) + skin/images/unlocked-hdpi.png (images/unlocked-hdpi.png) + skin/images/locked-hdpi.png (images/locked-hdpi.png) + skin/images/check-30.png (images/check-30.png) + skin/images/search-glass-30.png (images/search-glass-30.png) + skin/images/search-clear-30.png (images/search-clear-30.png) + skin/images/section-expanded-16.png (images/section-expanded-16.png) + skin/images/section-collapsed-16.png (images/section-collapsed-16.png) + skin/images/play-hdpi.png (images/play-hdpi.png) + skin/images/pause-hdpi.png (images/pause-hdpi.png) + skin/images/mute-hdpi.png (images/mute-hdpi.png) + skin/images/unmute-hdpi.png (images/unmute-hdpi.png) + skin/images/scrubber-hdpi.png (images/scrubber-hdpi.png) + skin/images/handle-start.png (images/handle-start.png) + skin/images/handle-end.png (images/handle-end.png) + skin/images/homescreen-blank-hdpi.png (images/homescreen-blank-hdpi.png) + skin/images/homescreen-default-hdpi.png (images/homescreen-default-hdpi.png) + skin/images/aurora-lightbox-bg.jpg (images/aurora-lightbox-bg.jpg) + skin/images/aurora-lightbox-logo.png (images/aurora-lightbox-logo.png) + skin/images/aurora-lightbox-close.png (images/aurora-lightbox-close.png) + +chrome.jar: +% skin browser classic/1.0 %skin/gingerbread/ os=Android osversion=2.3 osversion=2.3.3 osversion=2.3.4 osversion=2.3.5 osversion=2.3.6 osversion=2.3.7 +% skin browser gingerbread/1.0 %skin/gingerbread/ + skin/gingerbread/aboutPage.css (aboutPage.css) + skin/gingerbread/about.css (about.css) + skin/gingerbread/aboutAddons.css (aboutAddons.css) +* skin/gingerbread/browser.css (gingerbread/browser.css) +* skin/gingerbread/content.css (gingerbread/content.css) + skin/gingerbread/config.css (config.css) + skin/gingerbread/header.css (header.css) +* skin/gingerbread/platform.css (gingerbread/platform.css) + skin/gingerbread/touchcontrols.css (touchcontrols.css) + skin/gingerbread/netError.css (netError.css) +% override chrome://global/skin/about.css chrome://browser/skin/about.css +% override chrome://global/skin/media/videocontrols.css chrome://browser/skin/touchcontrols.css +% override chrome://global/skin/netError.css chrome://browser/skin/netError.css + + skin/gingerbread/images/aboutBackground.jpg (gingerbread/images/aboutBackground.jpg) + skin/gingerbread/images/button-bg.png (gingerbread/images/button-bg.png) + skin/gingerbread/images/textbox-bg.png (gingerbread/images/textbox-bg.png) + skin/gingerbread/images/urlbar-bg.png (gingerbread/images/urlbar-bg.png) + skin/gingerbread/images/browseaddons-bg.jpg (gingerbread/images/browseaddons-bg.jpg) + skin/gingerbread/images/addons-32.png (images/addons-32.png) + skin/gingerbread/images/arrowleft-16.png (gingerbread/images/arrowleft-16.png) + skin/gingerbread/images/arrowright-16.png (gingerbread/images/arrowright-16.png) + skin/gingerbread/images/arrowup-16.png (gingerbread/images/arrowup-16.png) + skin/gingerbread/images/arrowdown-16.png (gingerbread/images/arrowdown-16.png) + skin/gingerbread/images/popup-selected-item-hdpi.png (gingerbread/images/popup-selected-item-hdpi.png) + skin/gingerbread/images/checkmark-hdpi.png (images/checkmark-hdpi.png) + skin/gingerbread/images/check-selected-hdpi.png (gingerbread/images/check-selected-hdpi.png) + skin/gingerbread/images/check-unselected-hdpi.png (gingerbread/images/check-unselected-hdpi.png) + skin/gingerbread/images/radio-selected-hdpi.png (gingerbread/images/radio-selected-hdpi.png) + skin/gingerbread/images/radio-unselected-hdpi.png (gingerbread/images/radio-unselected-hdpi.png) + skin/gingerbread/images/dropmarker-hdpi.png (gingerbread/images/dropmarker-hdpi.png) + skin/gingerbread/images/ratings-18.png (images/ratings-18.png) + skin/gingerbread/images/favicon-default-32.png (gingerbread/images/favicon-default-32.png) + skin/gingerbread/images/throbber.png (gingerbread/images/throbber.png) + skin/gingerbread/images/navigation-magnifier-30.png (gingerbread/images/navigation-magnifier-30.png) + skin/gingerbread/images/alert-addons-30.png (gingerbread/images/alert-addons-30.png) + skin/gingerbread/images/alert-downloads-30.png (gingerbread/images/alert-downloads-30.png) + skin/gingerbread/images/addons-default-hdpi.png (gingerbread/images/addons-default-hdpi.png) + skin/gingerbread/images/back-default-hdpi.png (gingerbread/images/back-default-hdpi.png) + skin/gingerbread/images/forward-default-hdpi.png (gingerbread/images/forward-default-hdpi.png) + skin/gingerbread/images/row-header-bg.png (gingerbread/images/row-header-bg.png) + skin/gingerbread/images/remotetabs-32.png (gingerbread/images/remotetabs-32.png) + skin/gingerbread/images/mozilla-32.png (images/mozilla-32.png) + skin/gingerbread/images/toggle-on.png (gingerbread/images/toggle-on.png) + skin/gingerbread/images/toggle-off.png (gingerbread/images/toggle-off.png) + skin/gingerbread/images/previous-hdpi.png (gingerbread/images/previous-hdpi.png) + skin/gingerbread/images/previous-disabled-hdpi.png (gingerbread/images/previous-disabled-hdpi.png) + skin/gingerbread/images/next-hdpi.png (gingerbread/images/next-hdpi.png) + skin/gingerbread/images/next-disabled-hdpi.png (gingerbread/images/next-disabled-hdpi.png) + skin/gingerbread/images/unlocked-hdpi.png (gingerbread/images/unlocked-hdpi.png) + skin/gingerbread/images/locked-hdpi.png (gingerbread/images/locked-hdpi.png) + skin/gingerbread/images/check-30.png (gingerbread/images/check-30.png) + skin/gingerbread/images/search-glass-30.png (gingerbread/images/search-glass-30.png) + skin/gingerbread/images/search-clear-30.png (gingerbread/images/search-clear-30.png) + skin/gingerbread/images/section-expanded-16.png (images/section-expanded-16.png) + skin/gingerbread/images/section-collapsed-16.png (images/section-collapsed-16.png) + skin/gingerbread/images/play-hdpi.png (gingerbread/images/play-hdpi.png) + skin/gingerbread/images/pause-hdpi.png (gingerbread/images/pause-hdpi.png) + skin/gingerbread/images/mute-hdpi.png (gingerbread/images/mute-hdpi.png) + skin/gingerbread/images/unmute-hdpi.png (gingerbread/images/unmute-hdpi.png) + skin/gingerbread/images/scrubber-hdpi.png (gingerbread/images/scrubber-hdpi.png) + skin/gingerbread/images/handle-start.png (gingerbread/images/handle-start.png) + skin/gingerbread/images/handle-end.png (gingerbread/images/handle-end.png) + skin/gingerbread/images/tabs-hdpi.png (images/tabs-hdpi.png) + skin/gingerbread/images/menu-hdpi.png (images/menu-hdpi.png) + skin/gingerbread/images/errorpage-warning.png (images/errorpage-warning.png) + skin/gingerbread/images/errorpage-larry-white.png (images/errorpage-larry-white.png) + skin/gingerbread/images/errorpage-larry-black.png (images/errorpage-larry-black.png) + skin/gingerbread/images/homescreen-blank-hdpi.png (images/homescreen-blank-hdpi.png) + skin/gingerbread/images/homescreen-default-hdpi.png (images/homescreen-default-hdpi.png) + skin/gingerbread/images/aurora-lightbox-bg.jpg (images/aurora-lightbox-bg.jpg) + skin/gingerbread/images/aurora-lightbox-logo.png (images/aurora-lightbox-logo.png) + skin/gingerbread/images/aurora-lightbox-close.png (images/aurora-lightbox-close.png) + +chrome.jar: +% skin browser classic/1.0 %skin/honeycomb/ os=Android osversion>=3.0 +% skin browser honeycomb/1.0 %skin/honeycomb/ + skin/honeycomb/aboutPage.css (aboutPage.css) + skin/honeycomb/about.css (about.css) + skin/honeycomb/aboutAddons.css (aboutAddons.css) +* skin/honeycomb/browser.css (honeycomb/browser.css) +* skin/honeycomb/content.css (content.css) + skin/honeycomb/config.css (config.css) + skin/honeycomb/header.css (header.css) +* skin/honeycomb/platform.css (honeycomb/platform.css) + skin/honeycomb/touchcontrols.css (touchcontrols.css) + skin/honeycomb/netError.css (netError.css) +% override chrome://global/skin/about.css chrome://browser/skin/about.css +% override chrome://global/skin/media/videocontrols.css chrome://browser/skin/touchcontrols.css +% override chrome://global/skin/netError.css chrome://browser/skin/netError.css + + skin/honeycomb/images/aboutBackground.jpg (honeycomb/images/aboutBackground.jpg) + skin/honeycomb/images/button-bg.png (honeycomb/images/button-bg.png) + skin/honeycomb/images/textbox-bg.png (honeycomb/images/textbox-bg.png) + skin/honeycomb/images/browseaddons-bg.jpg (honeycomb/images/browseaddons-bg.jpg) + skin/honeycomb/images/addons-32.png (images/addons-32.png) + skin/honeycomb/images/arrowleft-16.png (honeycomb/images/arrowleft-16.png) + skin/honeycomb/images/arrowright-16.png (honeycomb/images/arrowright-16.png) + skin/honeycomb/images/arrowup-16.png (honeycomb/images/arrowup-16.png) + skin/honeycomb/images/arrowdown-16.png (honeycomb/images/arrowdown-16.png) + skin/honeycomb/images/popup-bg-hdpi.png (honeycomb/images/popup-bg-hdpi.png) + skin/honeycomb/images/popup-selected-item-hdpi.png (honeycomb/images/popup-selected-item-hdpi.png) + skin/honeycomb/images/dropmarker-hdpi.png (honeycomb/images/dropmarker-hdpi.png) + skin/honeycomb/images/ratings-18.png (images/ratings-18.png) + skin/honeycomb/images/favicon-default-32.png (honeycomb/images/favicon-default-32.png) + skin/honeycomb/images/throbber.png (honeycomb/images/throbber.png) + skin/honeycomb/images/navigation-magnifier-30.png (honeycomb/images/navigation-magnifier-30.png) + skin/honeycomb/images/alert-addons-30.png (honeycomb/images/alert-addons-30.png) + skin/honeycomb/images/alert-downloads-30.png (honeycomb/images/alert-downloads-30.png) + skin/honeycomb/images/addons-default-hdpi.png (honeycomb/images/addons-default-hdpi.png) + skin/honeycomb/images/back-default-hdpi.png (honeycomb/images/back-default-hdpi.png) + skin/honeycomb/images/panelrow-active-hdpi.png (honeycomb/images/panelrow-active-hdpi.png) + skin/honeycomb/images/panelrow-default-hdpi.png (honeycomb/images/panelrow-default-hdpi.png) + skin/honeycomb/images/panelrow-selected-hdpi.png (honeycomb/images/panelrow-selected-hdpi.png) + skin/honeycomb/images/radio-selected-hdpi.png (gingerbread/images/radio-selected-hdpi.png) + skin/honeycomb/images/radio-unselected-hdpi.png (gingerbread/images/radio-unselected-hdpi.png) + skin/honeycomb/images/forward-default-hdpi.png (honeycomb/images/forward-default-hdpi.png) + skin/honeycomb/images/row-header-bg.png (honeycomb/images/row-header-bg.png) + skin/honeycomb/images/remotetabs-32.png (honeycomb/images/remotetabs-32.png) + skin/honeycomb/images/mozilla-32.png (images/mozilla-32.png) + skin/honeycomb/images/toggle-on.png (honeycomb/images/toggle-on.png) + skin/honeycomb/images/toggle-off.png (honeycomb/images/toggle-off.png) + skin/honeycomb/images/sidebarbutton-active-hdpi.png (honeycomb/images/sidebarbutton-active-hdpi.png) + skin/honeycomb/images/previous-hdpi.png (honeycomb/images/previous-hdpi.png) + skin/honeycomb/images/previous-disabled-hdpi.png (honeycomb/images/previous-disabled-hdpi.png) + skin/honeycomb/images/next-hdpi.png (honeycomb/images/next-hdpi.png) + skin/honeycomb/images/next-disabled-hdpi.png (honeycomb/images/next-disabled-hdpi.png) + skin/honeycomb/images/unlocked-hdpi.png (honeycomb/images/unlocked-hdpi.png) + skin/honeycomb/images/locked-hdpi.png (honeycomb/images/locked-hdpi.png) + skin/honeycomb/images/checkmark-hdpi.png (images/checkmark-hdpi.png) + skin/honeycomb/images/check-30.png (images/check-30.png) + skin/honeycomb/images/check-selected-hdpi.png (honeycomb/images/check-selected-hdpi.png) + skin/honeycomb/images/check-unselected-hdpi.png (honeycomb/images/check-unselected-hdpi.png) + skin/honeycomb/images/check-selected-tap-hdpi.png (honeycomb/images/check-selected-tap-hdpi.png) + skin/honeycomb/images/check-unselected-tap-hdpi.png (honeycomb/images/check-unselected-tap-hdpi.png) + skin/honeycomb/images/search-glass-30.png (honeycomb/images/search-glass-30.png) + skin/honeycomb/images/search-clear-30.png (honeycomb/images/search-clear-30.png) + skin/honeycomb/images/section-expanded-16.png (images/section-expanded-16.png) + skin/honeycomb/images/section-collapsed-16.png (images/section-collapsed-16.png) + skin/honeycomb/images/play-hdpi.png (honeycomb/images/play-hdpi.png) + skin/honeycomb/images/pause-hdpi.png (honeycomb/images/pause-hdpi.png) + skin/honeycomb/images/mute-hdpi.png (honeycomb/images/mute-hdpi.png) + skin/honeycomb/images/unmute-hdpi.png (honeycomb/images/unmute-hdpi.png) + skin/honeycomb/images/scrubber-hdpi.png (honeycomb/images/scrubber-hdpi.png) + skin/honeycomb/images/handle-start.png (images/handle-start.png) + skin/honeycomb/images/handle-end.png (images/handle-end.png) + skin/honeycomb/images/tabs-hdpi.png (honeycomb/images/tabs-hdpi.png) + skin/honeycomb/images/tabs-default-bg.png (honeycomb/images/tabs-default-bg.png) + skin/honeycomb/images/tabs-selected-bg.png (honeycomb/images/tabs-selected-bg.png) + skin/honeycomb/images/tabs-default-bg-rtl.png (honeycomb/images/tabs-default-bg-rtl.png) + skin/honeycomb/images/tabs-selected-bg-rtl.png (honeycomb/images/tabs-selected-bg-rtl.png) + skin/honeycomb/images/menu-hdpi.png (honeycomb/images/menu-hdpi.png) + skin/honeycomb/images/errorpage-warning.png (images/errorpage-warning.png) + skin/honeycomb/images/errorpage-larry-white.png (images/errorpage-larry-white.png) + skin/honeycomb/images/errorpage-larry-black.png (images/errorpage-larry-black.png) + skin/honeycomb/images/homescreen-blank-hdpi.png (images/homescreen-blank-hdpi.png) + skin/honeycomb/images/homescreen-default-hdpi.png (images/homescreen-default-hdpi.png) + skin/honeycomb/images/urlbar-border-side.png (honeycomb/images/urlbar-border-side.png) + skin/honeycomb/images/urlbar-border-bottom.png (honeycomb/images/urlbar-border-bottom.png) + skin/honeycomb/images/urlbar-border-side-active.png (honeycomb/images/urlbar-border-side-active.png) + skin/honeycomb/images/urlbar-border-bottom-active.png (honeycomb/images/urlbar-border-bottom-active.png) + skin/honeycomb/images/aurora-lightbox-bg.jpg (images/aurora-lightbox-bg.jpg) + skin/honeycomb/images/aurora-lightbox-logo.png (images/aurora-lightbox-logo.png) + skin/honeycomb/images/aurora-lightbox-close.png (images/aurora-lightbox-close.png) + + skin/honeycomb/images/menu-top-insideglow.png (honeycomb/images/menu-top-insideglow.png) + skin/honeycomb/images/menu-top-insideglow-green.png (honeycomb/images/menu-top-insideglow-green.png) + skin/honeycomb/images/menu-top-insideglow-grey.png (honeycomb/images/menu-top-insideglow-grey.png) diff --git a/mobile/android/themes/core/netError.css b/mobile/android/themes/core/netError.css new file mode 100644 index 000000000000..b69468f2494a --- /dev/null +++ b/mobile/android/themes/core/netError.css @@ -0,0 +1,127 @@ +/* + * This defines the look-and-feel styling of the error pages. + * (see: netError.xhtml) + * + * Original styling by William Price + * Updated for mobile by: Wes Johnston + */ + +body { + margin: 0; + padding: 0 8px 8px; + font-family: "Nokia Sans", Tahoma, sans-serif !important; +} + +h1 { + font-size: 22px; +} + +h2 { + font-size: 16px; +} + +ul { + margin: 0px; + padding: 0px 0px 0px 1em; +} + +li { + margin: 0px; + padding: 8px 0px; +} + +#errorPage { + background-color: #CEE6F4; +} + +#errorPage.certerror { + background-color: #EFD400; +} + +#errorPage.blockedsite { + background-color: #BF0000; +} + +#errorTitle { + background: url("chrome://browser/skin/images/errorpage-warning.png") left center no-repeat; + /* Scaled by .666 of their actual size */ + background-size: 40px 40px; + background-origin: content-box; + min-height: 60px; + margin-left: auto; + margin-right: auto; + max-width: 500px; + margin-left: auto; + margin-right: auto; +} + +#errorPage.certerror #errorTitle { + background-image: url("chrome://browser/skin/images/errorpage-larry-black.png"); +} + +#errorPage.blockedsite #errorTitle { + background-image: url("chrome://browser/skin/images/errorpage-larry-white.png"); + color: white; +} + +.errorTitleText { + padding: 0px 0px 0px 50px; + display: inline-block; + vertical-align: middle +} + +#errorPageContainer { + background-color: white; + border: 1px solid #999999; + border-radius: 6px; + padding: 6px 20px 20px; + font-size: 14px; + max-width: 500px; + margin-left: auto; + margin-right: auto; +} + +#errorShortDesc > p:empty { + display: none; +} + +#errorShortDesc > p { + overflow: auto; + border-bottom: 1px solid #999999; + padding-bottom: 1em; +} + +#errorPage.blockedsite #errorShortDesc > p { + font-weight: bold; + border-bottom: none; + padding-bottom: 0px; +} + +#securityOverrideDiv { + padding-top: 10px; +} + +div[collapsed] { + padding-left: 15px; + background-image: url("chrome://browser/skin/images/arrowright-16.png"); + background-size: 11px 11px; + background-repeat: no-repeat; + background-position: left 0.3em; +} + +div[collapsed="true"] { + background-image: url("chrome://browser/skin/images/arrowright-16.png"); +} + +div[collapsed="false"] { + background-image: url("chrome://browser/skin/images/arrowdown-16.png"); +} + +div[collapsed="true"] > p, +div[collapsed="true"] > div { + display: none; +} + +button { + padding: 0.3em !important; +} diff --git a/mobile/android/themes/core/platform.css b/mobile/android/themes/core/platform.css new file mode 100644 index 000000000000..61986be7166a --- /dev/null +++ b/mobile/android/themes/core/platform.css @@ -0,0 +1,753 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Mobile Browser. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* global skin ------------------------------------------------------------- */ +@import url(chrome://global/skin/); + +%filter substitution +%include defines.inc + +/* general stuff ------------------------------------------------------------ */ +:root { + font-family: "Nokia Sans", Tahoma, sans-serif !important; + font-size: @font_normal@ !important; + background-color: white; /* force */ + color: black; /* force */ +} + +::-moz-selection { + background-color: #8db8d8; + color: black; +} + +menu, +menuitem { + padding: 0 !important; + margin: 0 !important; +} + +description, +label { + /* force mac to use the same margins as windows and linux */ + -moz-margin-start: @margin_snormal@; + -moz-margin-end: @margin_snormal@; +} + +/* Override any OS inverse themes */ +richlistbox, +textbox { + color: black; + background-color: white; +} + +/* textboxes --------------------------------------------------------------- */ +textbox:not([type="number"]) { + min-height: @textbox_height@; + border: @border_width_small@ solid #cacdd5; + -moz-border-radius: @border_radius_normal@; + -moz-border-top-colors: -moz-initial; + -moz-border-right-colors: -moz-initial; + -moz-border-bottom-colors: -moz-initial; + -moz-border-left-colors: -moz-initial; +} + +textbox[isempty="true"] { + color: gray; +} + +textbox.search-bar { + border: @border_width_small@ solid rgba(0,0,0,0.4); + background-color: #f9f9f9; + background: url("chrome://browser/skin/images/textbox-bg.png") top left repeat-x; + background-size: 100% 100%; +} + +textbox[disabled="true"] { + background-color: lightgray; +} + +.link { + color: blue; + text-decoration: underline; +} + +/* sidebars spacer --------------------------------------------------------- */ +.sidebar-spacer { + background-color: #767973; +} + +/* prompt dialogs ---------------------------------------------------------- */ +.context-block, +.modal-block, +.perm-modal-block { + -moz-box-align: center; + -moz-box-pack: center; + background-color: rgba(0,0,0,.6); +} + +.context-block { + padding: @margin_context_popup@; +} + +.dialog-dark, +.panel-arrowcontent { + background: url("chrome://browser/skin/images/popup-bg-hdpi.png") left bottom repeat-x; + background-color: white; + border-radius: @border_radius_normal@; + box-shadow: black 0 @border_radius_tiny@ @border_radius_tiny@, black 0 -@border_radius_tiny@ @border_radius_tiny@; + padding: @padding_normal@ 0; /* core spacing on top/bottom */ +} + +@media (max-width: 499px) { + .context-block { + padding: @padding_xlarge@; + } +} + +dialog > .prompt-header > .prompt-message { + white-space: pre-wrap; +} + +dialog > .prompt-header > .button-checkbox { + margin-left: @margin_large@; +} + +/* buttons ----------------------------------------------------------------- */ +.button-text, +.toolbarbutton-text { + font-weight: normal; + font-size: @font_normal@ !important; +} + +button { + -moz-appearance: none; + min-width: @touch_button_small@ !important; /* button size */ + min-height: @touch_button_small@ !important; /* button size */ + color: #000; + border-radius: @border_radius_normal@; + margin: @margin_normal@; + padding: @padding_xnormal@; + background-image: url("chrome://browser/skin/images/button-bg.png"); + background-size: auto 100%; + border: @border_width_tiny@ solid #cacdd5; +} + +button[disabled="true"] { + color: #aaa !important; + border: @border_width_tiny@ solid #cacdd5 !important; +} + +button:focus > .button-box { + border: @border_width_tiny@ solid transparent; +} + +button:not([disabled]):hover:active, +button:not([disabled])[checked="true"] { + background-image: url("chrome://browser/skin/images/toggle-off.png"); +} + +/* Override GTK2 system setting */ +.button-icon { + display: -moz-initial !important; +} + +/* spinbuttons ------------------------------------------------------------- */ +spinbuttons { + border: none !important; +} + +.numberbox-input-box { + border: @border_width_small@ solid #cacdd5; + border-right: 0 solid transparent; + border-radius: @border_radius_normal@ 0 0 @border_radius_normal@; + -moz-border-top-colors: -moz-initial; + -moz-border-bottom-colors: -moz-initial; + -moz-border-left-colors: -moz-initial; +} + +.numberbox-input-box:-moz-locale-dir(rtl) { + border-radius: 0 @border_radius_normal@ @border_radius_normal@ 0; + border-right: @border_width_small@ solid #cacdd5; + border-left: 0 solid transparent; +} + +.spinbuttons-box { + border: none !important; + -moz-box-orient: horizontal !important; + -moz-box-direction: reverse !important; +} + +.spinbuttons-up .button-icon, +.spinbuttons-down .button-icon { + display: block; +} + +.spinbuttons-up, +.spinbuttons-down { + -moz-appearance: none !important; + min-width: @touch_button_small@ !important; /* button size */ + min-height: @touch_button_small@ !important; /* button size */ + color: #000; + margin: @margin_normal@; + padding: @padding_xnormal@; + border-radius: 0; + background-image: url("chrome://browser/skin/images/button-bg.png"); + background-size: auto 100%; + border: @border_width_tiny@ solid #cacdd5; + list-style-image: url("chrome://browser/skin/images/arrowdown-16.png"); +} + +.spinbuttons-up:hover:active:not([disabled=true]), +.spinbuttons-down:hover:active:not([disabled=true]) { + background-image: url("chrome://browser/skin/images/toggle-on.png"); +} + +.spinbuttons-up { + border-radius: 0 @border_radius_normal@ @border_radius_normal@ 0; + list-style-image: url("chrome://browser/skin/images/arrowup-16.png"); +} + +.spinbuttons-up:-moz-locale-dir(rtl) { + border-radius: @border_radius_normal@ 0 0 @border_radius_normal@; +} + +/* toolbar buttons --------------------------------------------------------- */ +toolbarbutton { + min-width: @touch_button_large@ !important; /* primary button size */ + min-height: @touch_button_large@ !important; /* primary button size */ + -moz-appearance: none !important; + margin: 0; + padding: @padding_xsmall@; +} + +toolbarbutton:not(.show-text) .toolbarbutton-text { + display: none; +} + +.toolbarbutton-icon[label]:not([label=""]), +.toolbarbutton-icon[type="menu"] { + -moz-margin-end: @margin_tiny@; +} + +toolbarbutton:not(.show-text) .toolbarbutton-icon, +toolbarbutton:not([image]) .toolbarbutton-icon, +toolbarbutton[image=''] .toolbarbutton-icon { + -moz-margin-end: 0; +} + +toolbarbutton:hover, +toolbarbutton:hover:active, +toolbarbutton[open="true"] { + border-color: transparent; +} + +/* checkbox buttons ----------------------------------------------------------- */ +.button-checkbox { + padding: 0 !important; + background: none !important; + border: none !important; + -moz-border-image: none !important; + color: white; + -moz-box-align: center; + font-size: @font_small@; + -moz-box-align: center; +} + +.prompt-checkbox-label { + text-align: left; +} + +.button-checkbox > .button-image-icon { + -moz-margin-end: @margin_normal@; + list-style-image: url("chrome://browser/skin/images/check-unselected-30.png"); +} + +.button-checkbox[checked="true"] > .button-image-icon { + list-style-image: url("chrome://browser/skin/images/check-selected-30.png"); +} + +.button-checkbox:hover:active > .button-box, +.button-checkbox[checked="true"] > .button-box { + padding-top: @padding_tiny@; + padding-bottom: @padding_xsmall@; + -moz-padding-start: @margin_small@; + -moz-padding-end: @margin_small@; +} + +/* radio buttons ----------------------------------------------------------- */ +radiogroup { + -moz-box-orient: horizontal; +} + +.radio-label { + font-weight: normal; + font-size: @font_normal@ !important; +} + +radio { + -moz-appearance: none; + min-width: @touch_button_small@ !important; /* button size */ + min-height: @touch_button_small@ !important; /* button size */ + color: #000; + padding: @padding_xnormal@; + margin: 0; + background-image: url("chrome://browser/skin/images/button-bg.png"); + background-size: auto 100%; + border-top: @border_width_tiny@ solid #cacdd5; + border-bottom: @border_width_tiny@ solid #cacdd5; +} + +radio .radio-icon, radio .radio-check { + display: none; +} + +radio:not([disabled=true]):hover:active, +radio[selected] { + color: white; + background-image: url("chrome://browser/skin/images/toggle-on.png"); +} + +radio:first-child { + border-left: @border_width_tiny@ solid #cacdd5; + border-radius: @border_radius_normal@ 0 0 @border_radius_normal@; +} + +radio:first-child:-moz-locale-dir(rtl) { + border-left: none; + border-right: @border_width_tiny@ solid #cacdd5; + border-radius: 0 @border_radius_normal@ @border_radius_normal@ 0; +} + +radio:last-child { + border-right: @border_width_tiny@ solid #cacdd5; + border-radius: 0 @border_radius_normal@ @border_radius_normal@ 0; +} + +radio:last-child:-moz-locale-dir(rtl) { + border-right: none; + border-left: @border_width_tiny@ solid #cacdd5; + border-radius: @border_radius_normal@ 0 0 @border_radius_normal@; +} + +radio[focused="true"] > .radio-label-box { + border: @border_width_tiny@ solid transparent; +} + +/* checkbox radios --------------------------------------------------------- */ +checkbox { + margin: @margin_tiny@ @margin_small@ @margin_tiny@ @margin_small@; /* match platform style for buttons */ +} + +radio.checkbox-radio-on:not([selected]) { + border-right: @border_width_tiny@ solid #cacdd5; +} + +radio.checkbox-radio-on:not([selected]):-moz-locale-dir(rtl) { + border-left: none; + border-left: @border_width_tiny@ solid #cacdd5; +} + +radio.checkbox-radio-off[selected], +radio.checkbox-radio-off:hover:active { + background-image: url("chrome://browser/skin/images/toggle-off.png"); + color: black; + background-repeat: repeat-x; +} + +radio.checkbox-radio-on:not([selected]) .radio-label-box, +radio.checkbox-radio-off:not([selected]) .radio-label-box { + visibility: hidden; +} + +/* richlistbox ------------------------------------------------------------- */ +richlistbox { + -moz-user-focus: ignore; + margin: 0; +} + +richlistitem { + -moz-user-focus: ignore; + min-height: @touch_row@; /* row size */ + padding: @padding_small@; + border-bottom: @border_width_tiny@ solid rgb(207,207,207); +} + +richlistitem label.title, +richlistitem description.title { + font-size: @font_normal@ !important; +} + +richlistitem label.normal, +richlistitem description.normal { + color: gray; + font-size: @font_small@ !important; + white-space: pre-wrap; + word-wrap: break-word; +} + +richlistitem label.normal-black, +richlistitem description.normal-black { + font-size: @font_small@ !important; + white-space: pre-wrap; + word-wrap: break-word; +} + +richlistitem label.normal-bold, +richlistitem description.normal-bold { + font-weight: bold; + font-size: @font_small@ !important; + white-space: pre-wrap; + word-wrap: break-word; +} + +richlistitem[selected="true"] { + color: black; + background-color: white; +} + +richlistitem:hover:active:not([selected="true"]):not([nohighlight="true"]) { + background-color: #8db8d8; +} + +richlistitem.section-header, +richlistitem[selected="true"].section-header { + font-weight: bold; + color: #000; + background-color: lightgray; +} + +richlistitem .show-on-select { + visibility: collapse; +} + +richlistitem[selected="true"] .show-on-select { + visibility: visible; +} + +richlistitem .hide-on-select { + visibility: visible; +} + +richlistitem[selected="true"] .hide-on-select { + visibility: collapse; +} + +richlistitem[typeName="message"] { + border-bottom: 0; +} + +/* colorpicker ------------------------------------------------------------- */ +colorpicker > panel { + background-color: #767973; +} + +colorpicker > vbox { + background-color: #767973; +} + +/* textbox ----------------------------------------------------------------- */ +.textbox-search-icon { + list-style-image: url("chrome://browser/skin/images/search-glass-30.png"); + -moz-image-region: auto; +} + +.textbox-search-clear { + list-style-image: url("chrome://browser/skin/images/search-clear-30.png"); + -moz-image-region: auto; +} + +/* menulist ---------------------------------------------------------------- */ +.menulist-label { + font-family: "Nokia Sans", Tahoma, sans-serif !important; + font-weight: normal; + font-size: @font_normal@ !important; + background-color: transparent !important; +} + +menulist { + -moz-appearance: none !important; + -moz-user-focus: ignore; + min-width: @touch_button_small@ !important; /* button size */ + min-height: @touch_button_small@ !important; /* button size */ + color: #000 !important; + border-radius: @border_radius_normal@; + margin: @margin_normal@; + padding: @padding_small@ @padding_xnormal@; + background-image: url("chrome://browser/skin/images/button-bg.png"); + background-size: auto 100%; + border: @border_width_tiny@ solid #cacdd5; +} + +menulist[disabled="true"] { + color: #aaa !important; + border: @border_width_tiny@ solid #cacdd5 !important; +} + +menulist:not([disabled="true"]):hover:active { + background-image: url("chrome://browser/skin/images/toggle-off.png"); +} + +menulist > dropmarker { + height: 32px; + width: 32px; + margin-left: @margin_snormal@; + background-color: transparent; /* for windows */ + border: none; /* for windows */ + -moz-box-align: center; + -moz-box-pack: center; + list-style-image: url("chrome://browser/skin/images/dropmarker-hdpi.png"); + -moz-image-region: auto; + display: block; +} + +menulist[disabled="true"] > dropmarker { + opacity: 0.5; +} + +/* progressmeter ----------------------------------------------------------- */ +progressmeter { + background-color: #fff; + padding: @padding_small@; + height: @textbox_height@; + border: @border_width_large@ solid #aaa; + -moz-border-top-colors: -moz-initial; + -moz-border-right-colors: -moz-initial; + -moz-border-bottom-colors: -moz-initial; + -moz-border-left-colors: -moz-initial; + -moz-border-radius: @border_radius_normal@; +} + +.progress-bar { + background-color: #8db8d8; +} + +/* panels / arrowboxes------------------------------------------------------ */ +arrowbox { + -moz-appearance: none; + background: transparent !important; + border: none; +} + +dialog, +.arrowbox-dark .panel-arrowcontent, +.panel-dark { + color: white; + background: rgb(94,97,102); +} + +dialog, +.arrowbox-dark .panel-arrowcontent { + border: @border_width_large@ solid white; + border-radius: @border_radius_normal@; + box-shadow: black 0 @shadow_width_small@ @shadow_width_small@; +} + +dialog { + margin: @margin_xxxnormal@ !important; + max-width: @dialog_width@; +} + +.prompt-message { + text-align: center; + -moz-box-pack: center; + font-size: @font_snormal@; + margin: @padding_normal@; +} + +.prompt-message .link { + color: white; +} + +.prompt-title { + text-align: center; + font-size: @font_xnormal@; + -moz-box-align: center; + -moz-box-pack: center; + padding-top: @padding_xnormal@; +} + +/* Authentication dialogs do not have a title */ +.prompt-title:empty, +.prompt-title:empty + .prompt-line { + display: none; +} + +.prompt-line { + border-bottom: @border_width_small@ solid white; + margin: @margin_small@ 3em 0 3em; + height: @padding_normal@ !important; +} + +.prompt-buttons { + font-size: @font_snormal@; + background-color: lightgray; + display: inline-block; + text-align: center; +} + +.prompt-edit { + margin: @margin_xnormal@; + font-size: @font_normal@; + text-align: start; +} + +/*.panel-row-header ------------------------------------------------------------ */ +.panel-row-header { + border-bottom: @border_width_xlarge@ solid rgb(101,121,227); + background-color: rgb(94,97,102); + padding: 0 !important; +} + +.panel-row-button { + -moz-appearance: none; + background: rgb(94,97,102) url(images/panelrow-default-hdpi.png) no-repeat; + background-size: 100% 100%; + color: white; + border: 0 solid transparent !important; + -moz-border-start: @border_width_tiny@ solid rgba(255,255,255,0.2) !important; + -moz-border-end: @border_width_tiny@ solid rgba(0,0,0,0.2) !important; + padding-top: @padding_xsmall@ !important; + padding-bottom: @padding_xsmall@ !important; + -moz-padding-start: @padding_xsmall@ !important; + -moz-padding-end: @padding_xsmall@ !important; + -moz-box-flex: 1; + -moz-user-focus: ignore; + -moz-user-select: none; +} + +.panel-row-button:hover:active { + background: rgb(94,97,102) url(images/panelrow-active-hdpi.png) no-repeat; + background-size: 100% 100%; +} + +.panel-row-button:first-child { + -moz-border-start-width: 0 !important; +} + +.panel-row-button:last-child { + -moz-border-end-width: 0 !important; +} + +@media (@orientation@: portrait) { + .panel-row-button { + -moz-box-orient: vertical; + } + + .panel-row-button .toolbarbutton-text { + font-size: @font_xsmall@ !important; + } +} + +.panel-row-button .toolbarbutton-text { + text-align: left; + text-shadow: rgba(0,0,0,0.3) 0 @shadow_width_small@; +} + +.panel-row-button .toolbarbutton-text:-moz-locale-dir(rtl) { + text-align: right; +} + +.panel-row-button[disabled="true"] { + pointer-events: none; +} + +.panel-row-button[disabled="true"] .toolbarbutton-icon { + opacity: 0.5; +} + +.panel-row-button[disabled="true"] .toolbarbutton-text { + color: #aaa; +} + +.panel-row-button[checked="true"] { + color: white !important; + background: rgb(94, 97, 102) url(images/panelrow-selected-hdpi.png) no-repeat !important; + background-size: 100% 100% !important; +} + +.panel-row-button[checked="true"], +.panel-row-button[disabled="true"] { + pointer-events: none; +} + +#panel-container-inner { + -moz-box-orient: vertical; +} + +#panel-controls { + -moz-box-orient: horizontal; +} + +@media (min-width: @tablet_panel_minwidth@) { + #panel-container-inner { + -moz-box-orient: horizontal; + -moz-box-pack: center; + } + + #panel-items { + max-width: @tablet_panel_minwidth@; + min-width: 0px !important; + } + + /* This will affect the prefs screen, but not the awesome screen */ + #panel-controls { + -moz-box-orient: vertical !important; + -moz-box-align: start; + } + + #panel-controls > .panel-row-button { + -moz-box-orient: horizontal; + -moz-box-flex: 0; + min-width: @tablet_panel_controls@ !important; + } + + #panel-controls .toolbarbutton-text { + display: -moz-box !important; + -moz-box-flex: 1; + } + + #panel-container { + -moz-box-pack: center; + padding: @padding_xlarge@ 0px; + } +} + +/* because the buttons can wrap, we need to use the margin to create inter-button + spacing and a bottom margin for the notification */ +notification > button { + margin-bottom: @margin_normal@; +} diff --git a/mobile/android/themes/core/touchcontrols.css b/mobile/android/themes/core/touchcontrols.css new file mode 100644 index 000000000000..022476b4cc8a --- /dev/null +++ b/mobile/android/themes/core/touchcontrols.css @@ -0,0 +1,169 @@ +@namespace url(http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul); + +/* video controls */ +.controlsOverlay { + -moz-box-pack: center; + -moz-box-align: end; + padding: 20px; + -moz-box-flex: 1; + -moz-box-orient: horizontal; +} + +.controlBar { + -moz-box-flex: 1; + font-size: 16pt; + padding: 10px; + background-color: #34353a; + border-radius: 8px; + width: 100%; +} + +.controlsSpacer { + display: none; + -moz-box-flex: 0; +} + +.playButton, +.muteButton { + -moz-appearance: none; + min-height: 42px; + min-width: 42px; + border: none !important; +} + +.playButton { + -moz-transform: translateX(21px); + background: url("chrome://browser/skin/images/pause-hdpi.png") no-repeat center; +} + +.playButton[paused="true"] { + background: url("chrome://browser/skin/images/play-hdpi.png") no-repeat center; +} + +.muteButton { + background: url("chrome://browser/skin/images/mute-hdpi.png") no-repeat center; +} + +.muteButton[muted="true"] { + background: url("chrome://browser/skin/images/unmute-hdpi.png") no-repeat center; +} + +/* bars */ +.scrubberStack { + width: 100%; + min-height: 32px; + max-height: 32px; + padding: 0px 8px; + margin: 0px; +} + +.bufferBar, +.bufferBar .progress-bar, +.progressBar, +.progressBar .progress-bar, +.scrubber, +.scrubber .scale-slider, +.scrubber .scale-thumb { + -moz-appearance: none; + border: none; + padding: 0px; + margin: 0px; + background-color: transparent; +} + +.bufferBar { + border: 1px solid #5e6166; +} + +.bufferBar, +.progressBar { + margin: 9px 0px 11px 0px; + height: 8px +} + +.bufferBar .progress-bar { + background-color: #5e6166; +} + +.progressBar .progress-bar { + background-color: white; +} + +.scrubber { + margin-left: -16px; + margin-right: -16px; +} + +.scrubber .scale-thumb { + display: -moz-box; + background: url("chrome://browser/skin/images/scrubber-hdpi.png") no-repeat; + height: 32px; + width: 32px; +} + +.durationBox { + -moz-box-orient: horizontal; + -moz-box-pack: start; + -moz-box-align: center; + color: white; + font-weight: bold; + padding: 0px 8px; + margin-top: -6px; +} + +.positionLabel { + -moz-box-flex: 1; +} + +.statusOverlay { + -moz-box-align: center; + -moz-box-pack: center; + background-color: rgb(50,50,50); +} + +.statusIcon { + margin-bottom: 28px; + width: 36px; + height: 36px; +} + +.statusIcon[type="throbber"] { + background: url(chrome://global/skin/media/throbber.png) no-repeat center; +} + +.statusIcon[type="error"] { + background: url(chrome://global/skin/media/error.png) no-repeat center; +} + +/* CSS Transitions */ +.controlBar:not([immediate]) { + -moz-transition-property: opacity; + -moz-transition-duration: 200ms; +} + +.controlBar[fadeout] { + opacity: 0; +} + +.statusOverlay:not([immediate]) { + -moz-transition-property: opacity; + -moz-transition-duration: 300ms; + -moz-transition-delay: 750ms; +} + +.statusOverlay[fadeout] { + opacity: 0; +} + +.volumeStack, +.controlBar[firstshow="true"] .muteButton, +.controlBar[firstshow="true"] .scrubberStack, +.controlBar[firstshow="true"] .durationBox, +.timeLabel { + display: none; +} + +.controlBar[firstshow="true"] .playButton { + -moz-transform: none; +} + diff --git a/mobile/xul/installer/package-manifest.in b/mobile/xul/installer/package-manifest.in index 2a4e12f672a5..87c4a52554dd 100644 --- a/mobile/xul/installer/package-manifest.in +++ b/mobile/xul/installer/package-manifest.in @@ -82,6 +82,7 @@ #ifdef ANDROID @BINPATH@/AndroidManifest.xml @BINPATH@/resources.arsc +@BINPATH@/package-name.txt @BINPATH@/classes.dex @BINPATH@/@DLL_PREFIX@mozutils@DLL_SUFFIX@ @BINPATH@/res/drawable diff --git a/other-licenses/android/nsGeckoUtils.cpp b/other-licenses/android/nsGeckoUtils.cpp index 6f7879e3ab51..59487842c7d6 100644 --- a/other-licenses/android/nsGeckoUtils.cpp +++ b/other-licenses/android/nsGeckoUtils.cpp @@ -53,3 +53,19 @@ Java_org_mozilla_gecko_GeckoAppShell_putenv(JNIEnv *jenv, jclass, jstring map) jenv->ReleaseStringUTFChars(map, str); } +extern "C" +__attribute__ ((visibility("default"))) +jobject JNICALL +Java_org_mozilla_gecko_GeckoAppShell_allocateDirectBuffer(JNIEnv *jenv, jclass, jlong size) +{ + return jenv->NewDirectByteBuffer(malloc(size), size); +} + +extern "C" +__attribute__ ((visibility("default"))) +void JNICALL +Java_org_mozilla_gecko_GeckoAppShell_freeDirectBuffer(JNIEnv *jenv, jclass, jobject buf) +{ + free(jenv->GetDirectBufferAddress(buf)); +} + diff --git a/toolkit/mozapps/installer/packager.mk b/toolkit/mozapps/installer/packager.mk index c8672031dc55..77a5eee4bc0f 100644 --- a/toolkit/mozapps/installer/packager.mk +++ b/toolkit/mozapps/installer/packager.mk @@ -290,6 +290,7 @@ DIST_FILES = \ libsoftokn3.so \ extensions \ application.ini \ + package-name.txt \ platform.ini \ greprefs.js \ browserconfig.properties \ @@ -297,6 +298,7 @@ DIST_FILES = \ chrome.manifest \ update.locale \ removed-files \ + recommended-addons.json \ $(NULL) NON_DIST_FILES = \ diff --git a/toolkit/xre/Makefile.in b/toolkit/xre/Makefile.in index f15e0f9bcb93..5a3a2f964995 100644 --- a/toolkit/xre/Makefile.in +++ b/toolkit/xre/Makefile.in @@ -256,8 +256,20 @@ GRE_BUILDID := $(shell cat $(DEPTH)/config/buildid) DEFINES += -DGRE_MILESTONE=$(GRE_MILESTONE) -DGRE_BUILDID=$(GRE_BUILDID) +ifdef MOZILLA_OFFICIAL +DEFINES += -DMOZILLA_OFFICIAL +endif + +DEFINES += -DAPP_VERSION=$(MOZ_APP_VERSION) + +DEFINES += -DAPP_ID=$(MOZ_APP_ID) + $(srcdir)/nsAppRunner.cpp: $(DEPTH)/config/buildid $(topsrcdir)/config/milestone.txt +ifeq ($(OS_TARGET),Android) +nsAndroidStartup.o: $(DEPTH)/config/buildid $(topsrcdir)/config/milestone.txt +endif + platform.ini: FORCE $(PYTHON) $(srcdir)/make-platformini.py --buildid=$(shell cat $(DEPTH)/config/buildid) $(INIARGS) $(topsrcdir)/config/milestone.txt > $@ diff --git a/widget/src/android/AndroidBridge.cpp b/widget/src/android/AndroidBridge.cpp index cf8c171c17d7..3dcac11a3913 100644 --- a/widget/src/android/AndroidBridge.cpp +++ b/widget/src/android/AndroidBridge.cpp @@ -125,7 +125,6 @@ AndroidBridge::Init(JNIEnv *jEnv, jEnableLocation = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableLocation", "(Z)V"); jReturnIMEQueryResult = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "returnIMEQueryResult", "(Ljava/lang/String;II)V"); jScheduleRestart = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "scheduleRestart", "()V"); - jNotifyAppShellReady = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "onAppShellReady", "()V"); jNotifyXreExit = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "onXreExit", "()V"); jGetHandlersForMimeType = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getHandlersForMimeType", "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"); jGetHandlersForURL = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getHandlersForURL", "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"); @@ -365,15 +364,6 @@ AndroidBridge::ReturnIMEQueryResult(const PRUnichar *aResult, PRUint32 aLen, jReturnIMEQueryResult, args); } -void -AndroidBridge::NotifyAppShellReady() -{ - ALOG_BRIDGE("AndroidBridge::NotifyAppShellReady"); - mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jNotifyAppShellReady); - - mURIFixup = do_GetService(NS_URIFIXUP_CONTRACTID); -} - void AndroidBridge::ScheduleRestart() { @@ -593,8 +583,10 @@ AndroidBridge::CanCreateFixupURI(const nsACString& aURIText) { ALOG_BRIDGE("AndroidBridge::CanCreateFixupURI"); - if (!mURIFixup) - return false; + if (!mURIFixup) { + mURIFixup = do_GetService(NS_URIFIXUP_CONTRACTID); + if (!mURIFixup) return false; + } nsCOMPtr targetURI; diff --git a/widget/src/android/AndroidBridge.h b/widget/src/android/AndroidBridge.h index c3d7ecfbd2f5..2fee187fbb3e 100644 --- a/widget/src/android/AndroidBridge.h +++ b/widget/src/android/AndroidBridge.h @@ -145,8 +145,6 @@ public: void ReturnIMEQueryResult(const PRUnichar *aResult, PRUint32 aLen, int aSelStart, int aSelLen); - void NotifyAppShellReady(); - void NotifyXreExit(); void ScheduleRestart(); diff --git a/widget/src/android/AndroidJavaWrappers.cpp b/widget/src/android/AndroidJavaWrappers.cpp index 5f1e9297aa0c..21bd063a1d27 100644 --- a/widget/src/android/AndroidJavaWrappers.cpp +++ b/widget/src/android/AndroidJavaWrappers.cpp @@ -491,11 +491,6 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj) break; } - case SAVE_STATE: { - ReadCharactersField(jenv); - break; - } - default: break; } diff --git a/widget/src/android/AndroidJavaWrappers.h b/widget/src/android/AndroidJavaWrappers.h index 7241ca66c38e..e0fe0c8a0b7c 100644 --- a/widget/src/android/AndroidJavaWrappers.h +++ b/widget/src/android/AndroidJavaWrappers.h @@ -531,7 +531,6 @@ public: GECKO_EVENT_SYNC = 15, FORCED_RESIZE = 16, ACTIVITY_START = 17, - SAVE_STATE = 18, BROADCAST = 19, dummy_java_enum_list_end }; diff --git a/widget/src/android/nsAppShell.cpp b/widget/src/android/nsAppShell.cpp index 53164a1a34d7..506ca3822adf 100644 --- a/widget/src/android/nsAppShell.cpp +++ b/widget/src/android/nsAppShell.cpp @@ -121,8 +121,6 @@ nsAppShell::Init() nsresult rv = nsBaseAppShell::Init(); AndroidBridge* bridge = AndroidBridge::Bridge(); - if (bridge) - bridge->NotifyAppShellReady(); nsCOMPtr obsServ = mozilla::services::GetObserverService(); diff --git a/widget/src/android/nsWindow.cpp b/widget/src/android/nsWindow.cpp index b250fca0e8c7..c928fa1593d3 100644 --- a/widget/src/android/nsWindow.cpp +++ b/widget/src/android/nsWindow.cpp @@ -79,7 +79,7 @@ using mozilla::unused; #include "nsStringGlue.h" -// NB: Keep these in sync with LayerController.java in embedding/android/. +// NB: Keep these in sync with LayerController.java in mobile/android/base and embedding/android/. #define TILE_WIDTH 1024 #define TILE_HEIGHT 2048 @@ -796,82 +796,6 @@ nsWindow::GetThebesSurface() return new gfxImageSurface(gfxIntSize(5,5), gfxImageSurface::ImageFormatRGB24); } - -class DrawToFileRunnable : public nsRunnable { -public: - DrawToFileRunnable(nsWindow* win, const nsAString &path) { - mPath = path; - mWindow = win; - } - NS_IMETHOD Run() { - mWindow->DrawToFile(mPath); - return NS_OK; - } -private: - nsString mPath; - nsRefPtr mWindow; -}; - -bool -nsWindow::DrawToFile(const nsAString &path) -{ - if (!IsTopLevel() || !mIsVisible) { - ALOG("### DrawToFile works only for a visible toplevel window!"); - return PR_FALSE; - } - - if (GetLayerManager(nsnull)->GetBackendType() != LayerManager::LAYERS_BASIC) { - ALOG("### DrawToFile works only for a basic layers!"); - return PR_FALSE; - } - - nsRefPtr imgSurface = - new gfxImageSurface(gfxIntSize(mBounds.width, mBounds.height), - gfxImageSurface::ImageFormatARGB32); - - if (imgSurface->CairoStatus()) { - ALOG("### Failed to create a valid surface"); - return PR_FALSE; - } - - nsIntRect boundsRect(0, 0, mBounds.width, mBounds.height); - bool result = DrawTo(imgSurface, boundsRect); - NS_ENSURE_TRUE(result, PR_FALSE); - - nsCOMPtr encoder = do_CreateInstance("@mozilla.org/image/encoder;2?type=image/png"); - NS_ENSURE_TRUE(encoder, PR_FALSE); - - encoder->InitFromData(imgSurface->Data(), - imgSurface->Stride() * mBounds.height, - mBounds.width, - mBounds.height, - imgSurface->Stride(), - imgIEncoder::INPUT_FORMAT_HOSTARGB, - EmptyString()); - - nsCOMPtr file; - NS_NewLocalFile(path, true, getter_AddRefs(file)); - NS_ENSURE_TRUE(file, PR_FALSE); - - PRUint32 length; - encoder->Available(&length); - - nsCOMPtr outputStream; - NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file); - NS_ENSURE_TRUE(outputStream, PR_FALSE); - - nsCOMPtr bufferedOutputStream; - NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), - outputStream, length); - NS_ENSURE_TRUE(bufferedOutputStream, PR_FALSE); - - PRUint32 numWritten; - bufferedOutputStream->WriteFrom(encoder, length, &numWritten); - NS_ENSURE_SUCCESS(length == numWritten, PR_FALSE); - - return PR_TRUE; -} - void nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae) { @@ -1024,14 +948,6 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae) AndroidBridge::Bridge()->AcknowledgeEventSync(); break; - case AndroidGeckoEvent::SAVE_STATE: - { - nsCOMPtr thread; - nsRefPtr runnable = new DrawToFileRunnable(win, ae->Characters()); - NS_NewThread(getter_AddRefs(thread), runnable); - } - break; - default: break; } diff --git a/widget/src/android/nsWindow.h b/widget/src/android/nsWindow.h index bc85d8a9f371..fb557ac63a25 100644 --- a/widget/src/android/nsWindow.h +++ b/widget/src/android/nsWindow.h @@ -177,8 +177,6 @@ public: static bool sAccessibilityEnabled; #endif - bool DrawToFile(const nsAString &path); - protected: void BringToFront(); nsWindow *FindTopLevel();