author | Jim Chen <nchen@mozilla.com> |
Fri, 09 Dec 2016 12:32:45 -0500 | |
changeset 325565 | 1cce3b69bfc76377f5a0c015372d790b81f7b2bd |
parent 325564 | 6ed38cf05aaf57389f9a8b6680a454d247916c50 |
child 325566 | c9d3669dc5133a29944acb1353101c7bf905e914 |
push id | 31061 |
push user | philringnalda@gmail.com |
push date | Sat, 10 Dec 2016 16:28:08 +0000 |
treeherder | mozilla-central@c51e7406d7b2 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | snorp, sebastian, gbrown |
bugs | 1321418 |
milestone | 53.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/accessible/jsat/AccessFu.jsm +++ b/accessible/jsat/AccessFu.jsm @@ -28,18 +28,17 @@ this.AccessFu = { // jshint ignore:line * Initialize chrome-layer accessibility functionality. * If accessibility is enabled on the platform, then a special accessibility * mode is started. */ attach: function attach(aWindow) { Utils.init(aWindow); try { - Services.androidBridge.handleGeckoMessage( - { type: 'Accessibility:Ready' }); + Services.androidBridge.dispatch('Accessibility:Ready'); Services.obs.addObserver(this, 'Accessibility:Settings', false); } catch (x) { // Not on Android if (aWindow.navigator.mozSettings) { let lock = aWindow.navigator.mozSettings.createLock(); let req = lock.get(SCREENREADER_SETTING); req.addEventListener('success', () => { this._systemPref = req.result[SCREENREADER_SETTING]; @@ -597,17 +596,16 @@ var Output = { const ANDROID_VIEW_TEXT_CHANGED = 0x10; const ANDROID_VIEW_TEXT_SELECTION_CHANGED = 0x2000; if (!this.androidBridge) { return; } for (let androidEvent of aDetails) { - androidEvent.type = 'Accessibility:Event'; if (androidEvent.bounds) { androidEvent.bounds = AccessFu.adjustContentBounds( androidEvent.bounds, aBrowser); } switch(androidEvent.eventType) { case ANDROID_VIEW_TEXT_CHANGED: androidEvent.brailleOutput = this.brailleState.adjustText( @@ -617,17 +615,19 @@ var Output = { androidEvent.brailleOutput = this.brailleState.adjustSelection( androidEvent.brailleOutput); break; default: androidEvent.brailleOutput = this.brailleState.init( androidEvent.brailleOutput); break; } - this.androidBridge.handleGeckoMessage(androidEvent); + let win = Utils.win; + let view = win && win.QueryInterface(Ci.nsIAndroidView); + view.dispatch('Accessibility:Event', androidEvent); } }, Braille: function Braille(aDetails) { Logger.debug('Braille output: ' + aDetails.output); } }; @@ -813,18 +813,19 @@ var Input = { return; } else { target.blur(); } } if (Utils.MozBuildApp == 'mobile/android') { // Return focus to native Android browser chrome. - Services.androidBridge.handleGeckoMessage( - { type: 'ToggleChrome:Focus' }); + let win = Utils.win; + let view = win && win.QueryInterface(Ci.nsIAndroidView); + view.dispatch('ToggleChrome:Focus'); } break; case aEvent.DOM_VK_RETURN: if (this.editState.editing) { return; } this.activateCurrent(); break;
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java +++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java @@ -98,22 +98,19 @@ import org.mozilla.gecko.updater.PostUpd import org.mozilla.gecko.updater.UpdateServiceHelper; import org.mozilla.gecko.util.ActivityUtils; import org.mozilla.gecko.util.Clipboard; import org.mozilla.gecko.util.BundleEventListener; import org.mozilla.gecko.util.EventCallback; import org.mozilla.gecko.util.FloatUtils; import org.mozilla.gecko.util.GamepadUtils; import org.mozilla.gecko.util.GeckoBundle; -import org.mozilla.gecko.util.GeckoEventListener; import org.mozilla.gecko.util.HardwareUtils; import org.mozilla.gecko.util.IntentUtils; import org.mozilla.gecko.util.MenuUtils; -import org.mozilla.gecko.util.NativeEventListener; -import org.mozilla.gecko.util.NativeJSObject; import org.mozilla.gecko.util.PrefUtils; import org.mozilla.gecko.util.StringUtils; import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.gecko.widget.AnchoredPopup; import org.mozilla.gecko.widget.GeckoActionProvider; import android.app.Activity; @@ -197,18 +194,17 @@ public class BrowserApp extends GeckoApp View.OnKeyListener, LayerView.DynamicToolbarListener, BrowserSearch.OnSearchListener, BrowserSearch.OnEditSuggestionListener, OnUrlOpenListener, OnUrlOpenInBackgroundListener, AnchoredPopup.OnVisibilityChangeListener, ActionModeCompat.Presenter, - LayoutInflater.Factory, - BundleEventListener { + LayoutInflater.Factory { private static final String LOGTAG = "GeckoBrowserApp"; private static final int TABS_ANIMATION_DURATION = 450; // Intent String extras used to specify custom Switchboard configurations. private static final String INTENT_KEY_SWITCHBOARD_SERVER = "switchboard-server"; // TODO: Replace with kinto endpoint. @@ -721,42 +717,46 @@ public class BrowserApp extends GeckoApp setBrowserToolbarListeners(); mFindInPageBar = (FindInPageBar) findViewById(R.id.find_in_page); mMediaCastingBar = (MediaCastingBar) findViewById(R.id.media_casting); mDoorhangerOverlay = findViewById(R.id.doorhanger_overlay); - EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener)this, - "Gecko:DelayedStartup", + EventDispatcher.getInstance().registerGeckoThreadListener(this, + "Search:Keyword", + "Favicon:CacheLoad", + null); + + EventDispatcher.getInstance().registerUiThreadListener(this, "Menu:Open", "Menu:Update", + "Menu:Add", + "Menu:Remove", "LightweightTheme:Update", - "Search:Keyword", "Tab:Added", - "Video:Play"); - - EventDispatcher.getInstance().registerGeckoThreadListener((NativeEventListener)this, + "Video:Play", "CharEncoding:Data", "CharEncoding:State", - "Download:AndroidDownloadManager", + "Settings:Show", + "Updater:Launch", + null); + + EventDispatcher.getInstance().registerBackgroundThreadListener(this, "Experiments:GetActive", "Experiments:SetOverride", "Experiments:ClearOverride", - "Favicon:CacheLoad", "Feedback:MaybeLater", - "Menu:Add", - "Menu:Remove", "Sanitize:ClearHistory", "Sanitize:ClearSyncedTabs", - "Settings:Show", "Telemetry:Gather", - "Updater:Launch", - "Website:Metadata"); + "Download:AndroidDownloadManager", + "Website:Metadata", + null); getAppEventDispatcher().registerUiThreadListener(this, "Prompt:ShowTop"); final GeckoProfile profile = getProfile(); // We want to upload the telemetry core ping as soon after startup as possible. It relies on the // Distribution being initialized. If you move this initialization, ensure it plays well with telemetry. final Distribution distribution = Distribution.init(getApplicationContext()); @@ -1425,42 +1425,46 @@ public class BrowserApp extends GeckoApp } if (mZoomedView != null) { mZoomedView.destroy(); } mSearchEngineManager.unregisterListeners(); - EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener) this, - "Gecko:DelayedStartup", + EventDispatcher.getInstance().unregisterGeckoThreadListener(this, + "Search:Keyword", + "Favicon:CacheLoad", + null); + + EventDispatcher.getInstance().unregisterUiThreadListener(this, "Menu:Open", "Menu:Update", + "Menu:Add", + "Menu:Remove", "LightweightTheme:Update", - "Search:Keyword", "Tab:Added", - "Video:Play"); - - EventDispatcher.getInstance().unregisterGeckoThreadListener((NativeEventListener) this, + "Video:Play", "CharEncoding:Data", "CharEncoding:State", - "Download:AndroidDownloadManager", + "Settings:Show", + "Updater:Launch", + null); + + EventDispatcher.getInstance().unregisterBackgroundThreadListener(this, "Experiments:GetActive", "Experiments:SetOverride", "Experiments:ClearOverride", - "Favicon:CacheLoad", "Feedback:MaybeLater", - "Menu:Add", - "Menu:Remove", "Sanitize:ClearHistory", "Sanitize:ClearSyncedTabs", - "Settings:Show", "Telemetry:Gather", - "Updater:Launch", - "Website:Metadata"); + "Download:AndroidDownloadManager", + "Website:Metadata", + null); getAppEventDispatcher().unregisterUiThreadListener(this, "Prompt:ShowTop"); if (AppConstants.MOZ_ANDROID_BEAM) { NfcAdapter nfc = NfcAdapter.getDefaultAdapter(this); if (nfc != null) { // null this out even though the docs say it's not needed, // because the source code looks like it will only do this @@ -1543,35 +1547,16 @@ public class BrowserApp extends GeckoApp @Override public void onDoorHangerHide() { final Animator alphaAnimator = ObjectAnimator.ofFloat(mDoorhangerOverlay, "alpha", 0); alphaAnimator.setDuration(200); alphaAnimator.start(); } - private void handleClearHistory(final boolean clearSearchHistory) { - final BrowserDB db = BrowserDB.from(getProfile()); - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - db.clearHistory(getContentResolver(), clearSearchHistory); - } - }); - } - - private void handleClearSyncedTabs() { - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - FennecTabsRepository.deleteNonLocalClientsAndTabs(getContext()); - } - }); - } - private void setToolbarMargin(int margin) { ((RelativeLayout.LayoutParams) mGeckoLayout.getLayoutParams()).topMargin = margin; mGeckoLayout.requestLayout(); } @Override public void onTranslationChanged(float aToolbarTranslation, float aLayerViewTranslation) { if (mBrowserChrome == null) { @@ -1658,39 +1643,31 @@ public class BrowserApp extends GeckoApp mToolbarHeight = height; mLayerView.setMaxTranslation(height); mDynamicToolbar.setVisible(true, VisibilityTransition.IMMEDIATE); } } @Override void toggleChrome(final boolean aShow) { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - if (aShow) { - mBrowserChrome.setVisibility(View.VISIBLE); - } else { - mBrowserChrome.setVisibility(View.GONE); - } - } - }); + if (aShow) { + mBrowserChrome.setVisibility(View.VISIBLE); + } else { + mBrowserChrome.setVisibility(View.GONE); + } super.toggleChrome(aShow); } @Override void focusChrome() { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - mBrowserChrome.setVisibility(View.VISIBLE); - mActionBarFlipper.requestFocusFromTouch(); - } - }); + mBrowserChrome.setVisibility(View.VISIBLE); + mActionBarFlipper.requestFocusFromTouch(); + + super.focusChrome(); } @Override public void refreshChrome() { invalidateOptionsMenu(); if (mTabsPanel != null) { mTabsPanel.refresh(); @@ -1702,39 +1679,168 @@ public class BrowserApp extends GeckoApp mBrowserToolbar.refresh(); } @Override // BundleEventListener public void handleMessage(final String event, final GeckoBundle message, final EventCallback callback) { switch (event) { + case "Gecko:Ready": + EventDispatcher.getInstance().registerUiThreadListener(this, "Gecko:DelayedStartup"); + + // Handle this message in GeckoApp, but also enable the Settings + // menuitem, which is specific to BrowserApp. + super.handleMessage(event, message, callback); + + final Menu menu = mMenu; + ThreadUtils.postToUiThread(new Runnable() { + @Override + public void run() { + if (menu != null) { + menu.findItem(R.id.settings).setEnabled(true); + menu.findItem(R.id.help).setEnabled(true); + } + } + }); + + // Display notification for Mozilla data reporting, if data should be collected. + if (AppConstants.MOZ_DATA_REPORTING && + Restrictions.isAllowed(this, Restrictable.DATA_CHOICES)) { + DataReportingNotification.checkAndNotifyPolicy(GeckoAppShell.getContext()); + } + break; + + case "Gecko:DelayedStartup": + EventDispatcher.getInstance().unregisterUiThreadListener(this, "Gecko:DelayedStartup"); + + // Force tabs panel inflation once the initial pageload is finished. + ensureTabsPanelExists(); + + if (AppConstants.NIGHTLY_BUILD && mZoomedView == null) { + ViewStub stub = (ViewStub) findViewById(R.id.zoomed_view_stub); + mZoomedView = (ZoomedView) stub.inflate(); + } + + if (AppConstants.MOZ_MEDIA_PLAYER) { + // Check if the fragment is already added. This should never be true + // here, but this is a nice safety check. If casting is disabled, + // these classes aren't built. We use reflection to initialize them. + final Class<?> mediaManagerClass = getMediaPlayerManager(); + + if (mediaManagerClass != null) { + try { + final String tag = ""; + mediaManagerClass.getDeclaredField("MEDIA_PLAYER_TAG").get(tag); + Log.i(LOGTAG, "Found tag " + tag); + final Fragment frag = getSupportFragmentManager().findFragmentByTag(tag); + if (frag == null) { + final Method getInstance = mediaManagerClass.getMethod( + "getInstance", (Class[]) null); + final Fragment mpm = (Fragment) getInstance.invoke(null); + getSupportFragmentManager().beginTransaction() + .disallowAddToBackStack().add(mpm, tag).commit(); + } + } catch (Exception ex) { + Log.e(LOGTAG, "Error initializing media manager", ex); + } + } + } + + if (AppConstants.MOZ_STUMBLER_BUILD_TIME_ENABLED && + Restrictions.isAllowed(this, Restrictable.DATA_CHOICES)) { + // Start (this acts as ping if started already) the stumbler lib; if + // the stumbler has queued data it will upload it. Stumbler operates + // on its own thread, and startup impact is further minimized by + // delaying work (such as upload) a few seconds. Avoid any potential + // startup CPU/thread contention by delaying the pref broadcast. + GeckoPreferences.broadcastStumblerPref(BrowserApp.this); + } + + if (AppConstants.MOZ_ANDROID_DOWNLOAD_CONTENT_SERVICE) { + // TODO: Better scheduling of sync action (Bug 1257492) + DownloadContentService.startSync(this); + DownloadContentService.startVerification(this); + } + + FeedService.setup(this); + break; + + case "Menu:Open": + if (mBrowserToolbar.isEditing()) { + mBrowserToolbar.cancelEdit(); + } + openOptionsMenu(); + break; + + case "Menu:Update": + updateAddonMenuItem(message.getInt("id"), message.getBundle("options")); + break; + + case "Menu:Add": + final MenuItemInfo info = new MenuItemInfo(); + info.label = message.getString("name"); + info.id = message.getInt("id") + ADDON_MENU_OFFSET; + info.checked = message.getBoolean("checked", false); + info.enabled = message.getBoolean("enabled", true); + info.visible = message.getBoolean("visible", true); + info.checkable = message.getBoolean("checkable", false); + final int parent = message.getInt("parent", 0); + info.parent = parent <= 0 ? parent : parent + ADDON_MENU_OFFSET; + addAddonMenuItem(info); + break; + + case "Menu:Remove": + removeAddonMenuItem(message.getInt("id") + ADDON_MENU_OFFSET); + break; + + case "LightweightTheme:Update": + mDynamicToolbar.setVisible(true, VisibilityTransition.ANIMATE); + break; + + case "Search:Keyword": + storeSearchQuery(message.getString("query")); + recordSearch(GeckoSharedPrefs.forProfile(this), message.getString("identifier"), + TelemetryContract.Method.ACTIONBAR); + break; + case "Prompt:ShowTop": // Bring this activity to front so the prompt is visible.. Intent bringToFrontIntent = new Intent(); bringToFrontIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS); bringToFrontIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); startActivity(bringToFrontIntent); break; - } - } - - @Override - public void handleMessage(final String event, final NativeJSObject message, - final EventCallback callback) { - switch (event) { + + case "Tab:Added": + if (message.getBoolean("cancelEditMode")) { + // Set the target tab to null so it does not get selected (on editing + // mode exit) in lieu of the tab that we're going to open and select. + mTargetTabForEditingMode = null; + mBrowserToolbar.cancelEdit(); + } + break; + + case "Video:Play": + if (SwitchBoard.isInExperiment(this, Experiments.HLS_VIDEO_PLAYBACK)) { + mVideoPlayer.start(Uri.parse(message.getString("uri"))); + Telemetry.sendUIEvent(TelemetryContract.Event.SHOW, + TelemetryContract.Method.CONTENT, "playhls"); + } + break; + case "CharEncoding:Data": - final NativeJSObject[] charsets = message.getObjectArray("charsets"); + final GeckoBundle[] charsets = message.getBundleArray("charsets"); final int selected = message.getInt("selected"); final String[] titleArray = new String[charsets.length]; final String[] codeArray = new String[charsets.length]; for (int i = 0; i < charsets.length; i++) { - final NativeJSObject charset = charsets[i]; + final GeckoBundle charset = charsets[i]; titleArray[i] = charset.getString("title"); codeArray[i] = charset.getString("code"); } final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); dialogBuilder.setSingleChoiceItems(titleArray, selected, new AlertDialog.OnClickListener() { @Override @@ -1745,197 +1851,183 @@ public class BrowserApp extends GeckoApp }); dialogBuilder.setNegativeButton(R.string.button_cancel, new AlertDialog.OnClickListener() { @Override public void onClick(final DialogInterface dialog, final int which) { dialog.dismiss(); } }); - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - dialogBuilder.show(); - } - }); + dialogBuilder.show(); break; case "CharEncoding:State": - final boolean visible = message.getString("visible").equals("true"); + final boolean visible = "true".equals(message.getString("visible")); GeckoPreferences.setCharEncodingState(visible); - final Menu menu = mMenu; - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - if (menu != null) { - menu.findItem(R.id.char_encoding).setVisible(visible); - } - } - }); + if (mMenu != null) { + mMenu.findItem(R.id.char_encoding).setVisible(visible); + } break; case "Experiments:GetActive": final List<String> experiments = SwitchBoard.getActiveExperiments(this); final JSONArray json = new JSONArray(experiments); callback.sendSuccess(json.toString()); break; case "Experiments:SetOverride": - Experiments.setOverride(getContext(), message.getString("name"), message.getBoolean("isEnabled")); + Experiments.setOverride(getContext(), message.getString("name"), + message.getBoolean("isEnabled")); break; case "Experiments:ClearOverride": Experiments.clearOverride(getContext(), message.getString("name")); break; case "Favicon:CacheLoad": final String url = message.getString("url"); getFaviconFromCache(callback, url); break; case "Feedback:MaybeLater": - resetFeedbackLaunchCount(); - break; - - case "Menu:Add": - final MenuItemInfo info = new MenuItemInfo(); - info.label = message.getString("name"); - info.id = message.getInt("id") + ADDON_MENU_OFFSET; - info.checked = message.optBoolean("checked", false); - info.enabled = message.optBoolean("enabled", true); - info.visible = message.optBoolean("visible", true); - info.checkable = message.optBoolean("checkable", false); - final int parent = message.optInt("parent", 0); - info.parent = parent <= 0 ? parent : parent + ADDON_MENU_OFFSET; - final MenuItemInfo menuItemInfo = info; - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - addAddonMenuItem(menuItemInfo); - } - }); - break; - - case "Menu:Remove": - final int id = message.getInt("id") + ADDON_MENU_OFFSET; - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - removeAddonMenuItem(id); - } - }); + SharedPreferences settings = getPreferences(Activity.MODE_PRIVATE); + settings.edit().putInt(getPackageName() + ".feedback_launch_count", 0).apply(); break; case "Sanitize:ClearHistory": - handleClearHistory(message.optBoolean("clearSearchHistory", false)); - callback.sendSuccess(true); + BrowserDB.from(getProfile()).clearHistory( + getContentResolver(), message.getBoolean("clearSearchHistory", false)); + callback.sendSuccess(null); break; case "Sanitize:ClearSyncedTabs": - handleClearSyncedTabs(); - callback.sendSuccess(true); + FennecTabsRepository.deleteNonLocalClientsAndTabs(getContext()); + callback.sendSuccess(null); break; case "Settings:Show": - final String resource = - message.optString(GeckoPreferences.INTENT_EXTRA_RESOURCES, null); final Intent settingsIntent = new Intent(this, GeckoPreferences.class); + final String resource = message.getString(GeckoPreferences.INTENT_EXTRA_RESOURCES); + GeckoPreferences.setResourceToOpen(settingsIntent, resource); startActivityForResult(settingsIntent, ACTIVITY_REQUEST_PREFERENCES); // Don't use a transition to settings if we're on a device where that // would look bad. if (HardwareUtils.IS_KINDLE_DEVICE) { overridePendingTransition(0, 0); } break; case "Telemetry:Gather": final BrowserDB db = BrowserDB.from(getProfile()); final ContentResolver cr = getContentResolver(); + Telemetry.addToHistogram("PLACES_PAGES_COUNT", db.getCount(cr, "history")); Telemetry.addToHistogram("FENNEC_BOOKMARKS_COUNT", db.getCount(cr, "bookmarks")); - Telemetry.addToHistogram("BROWSER_IS_USER_DEFAULT", (isDefaultBrowser(Intent.ACTION_VIEW) ? 1 : 0)); - Telemetry.addToHistogram("FENNEC_CUSTOM_HOMEPAGE", (TextUtils.isEmpty(getHomepage()) ? 0 : 1)); + Telemetry.addToHistogram("BROWSER_IS_USER_DEFAULT", + (isDefaultBrowser(Intent.ACTION_VIEW) ? 1 : 0)); + Telemetry.addToHistogram("FENNEC_CUSTOM_HOMEPAGE", + (TextUtils.isEmpty(getHomepage()) ? 0 : 1)); + final SharedPreferences prefs = GeckoSharedPrefs.forProfile(getContext()); final boolean hasCustomHomepanels = - prefs.contains(HomeConfigPrefsBackend.PREFS_CONFIG_KEY) || prefs.contains(HomeConfigPrefsBackend.PREFS_CONFIG_KEY_OLD); + prefs.contains(HomeConfigPrefsBackend.PREFS_CONFIG_KEY) || + prefs.contains(HomeConfigPrefsBackend.PREFS_CONFIG_KEY_OLD); + Telemetry.addToHistogram("FENNEC_HOMEPANELS_CUSTOM", hasCustomHomepanels ? 1 : 0); Telemetry.addToHistogram("FENNEC_READER_VIEW_CACHE_SIZE", - SavedReaderViewHelper.getSavedReaderViewHelper(getContext()).getDiskSpacedUsedKB()); + SavedReaderViewHelper.getSavedReaderViewHelper(getContext()) + .getDiskSpacedUsedKB()); if (Versions.feature16Plus) { - Telemetry.addToHistogram("BROWSER_IS_ASSIST_DEFAULT", (isDefaultBrowser(Intent.ACTION_ASSIST) ? 1 : 0)); + Telemetry.addToHistogram("BROWSER_IS_ASSIST_DEFAULT", + (isDefaultBrowser(Intent.ACTION_ASSIST) ? 1 : 0)); } break; case "Updater:Launch": - handleUpdaterLaunch(); + /** + * Launch UI that lets the user update Firefox. + * + * This depends on the current channel: Release and Beta both direct to + * the Google Play Store. If updating is enabled, Aurora, Nightly, and + * custom builds open about:, which provides an update interface. + * + * If updating is not enabled, this simply logs an error. + */ + if (AppConstants.RELEASE_OR_BETA) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse("market://details?id=" + getPackageName())); + startActivity(intent); + break; + } + + if (AppConstants.MOZ_UPDATER) { + Tabs.getInstance().loadUrlInTab(AboutPages.UPDATER); + break; + } + + Log.w(LOGTAG, "No candidate updater found; ignoring launch request."); break; case "Download:AndroidDownloadManager": // Downloading via Android's download manager - final String uri = message.getString("uri"); final String filename = message.getString("filename"); final String mimeType = message.getString("mimeType"); final DownloadManager.Request request = new DownloadManager.Request(Uri.parse(uri)); request.setMimeType(mimeType); try { - request.setDestinationInExternalFilesDir(this, Environment.DIRECTORY_DOWNLOADS, filename); + request.setDestinationInExternalFilesDir( + this, Environment.DIRECTORY_DOWNLOADS, filename); } catch (IllegalStateException e) { Log.e(LOGTAG, "Cannot create download directory"); - return; + break; } request.allowScanningByMediaScanner(); - request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); + request.setNotificationVisibility( + DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); request.addRequestHeader("User-Agent", HardwareUtils.isTablet() ? AppConstants.USER_AGENT_FENNEC_TABLET : AppConstants.USER_AGENT_FENNEC_MOBILE); try { - DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE); + DownloadManager manager = (DownloadManager) + getSystemService(Context.DOWNLOAD_SERVICE); manager.enqueue(request); - - Log.d(LOGTAG, "Enqueued download (Download Manager)"); } catch (RuntimeException e) { Log.e(LOGTAG, "Download failed: " + e); } break; case "Website:Metadata": - final NativeJSObject metadata = message.getObject("metadata"); final String location = message.getString("location"); - - final boolean hasImage = !TextUtils.isEmpty(metadata.optString("image_url", null)); - final String metadataJSON = metadata.toString(); - - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - final ContentProviderClient contentProviderClient = getContentResolver() - .acquireContentProviderClient(BrowserContract.PageMetadata.CONTENT_URI); - if (contentProviderClient == null) { - Log.w(LOGTAG, "Failed to obtain content provider client for: " + BrowserContract.PageMetadata.CONTENT_URI); - return; - } - try { - GlobalPageMetadata.getInstance().add( - BrowserDB.from(getProfile()), - contentProviderClient, - location, hasImage, metadataJSON); - } finally { - contentProviderClient.release(); - } - } - }); + final boolean hasImage = message.getBoolean("hasImage"); + final String metadata = message.getString("metadata"); + + final ContentProviderClient contentProviderClient = getContentResolver() + .acquireContentProviderClient(BrowserContract.PageMetadata.CONTENT_URI); + if (contentProviderClient == null) { + Log.w(LOGTAG, "Failed to obtain content provider client for: " + + BrowserContract.PageMetadata.CONTENT_URI); + return; + } + try { + GlobalPageMetadata.getInstance().add( + BrowserDB.from(getProfile()), + contentProviderClient, + location, hasImage, metadata); + } finally { + contentProviderClient.release(); + } break; default: super.handleMessage(event, message, callback); break; } } @@ -1990,174 +2082,16 @@ public class BrowserApp extends GeckoApp return false; } final String packageName = info.activityInfo.packageName; return (TextUtils.equals(packageName, getPackageName())); } @Override - public void handleMessage(String event, JSONObject message) { - try { - switch (event) { - case "Menu:Open": - if (mBrowserToolbar.isEditing()) { - mBrowserToolbar.cancelEdit(); - } - - openOptionsMenu(); - break; - - case "Menu:Update": - final int id = message.getInt("id") + ADDON_MENU_OFFSET; - final JSONObject options = message.getJSONObject("options"); - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - updateAddonMenuItem(id, options); - } - }); - break; - - case "Gecko:DelayedStartup": - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - // Force tabs panel inflation once the initial - // pageload is finished. - ensureTabsPanelExists(); - if (AppConstants.NIGHTLY_BUILD && mZoomedView == null) { - ViewStub stub = (ViewStub) findViewById(R.id.zoomed_view_stub); - mZoomedView = (ZoomedView) stub.inflate(); - } - } - }); - - if (AppConstants.MOZ_MEDIA_PLAYER) { - // Check if the fragment is already added. This should never be true here, but this is - // a nice safety check. - // If casting is disabled, these classes aren't built. We use reflection to initialize them. - final Class<?> mediaManagerClass = getMediaPlayerManager(); - - if (mediaManagerClass != null) { - try { - final String tag = ""; - mediaManagerClass.getDeclaredField("MEDIA_PLAYER_TAG").get(tag); - Log.i(LOGTAG, "Found tag " + tag); - final Fragment frag = getSupportFragmentManager().findFragmentByTag(tag); - if (frag == null) { - final Method getInstance = mediaManagerClass.getMethod("getInstance", (Class[]) null); - final Fragment mpm = (Fragment) getInstance.invoke(null); - getSupportFragmentManager().beginTransaction().disallowAddToBackStack().add(mpm, tag).commit(); - } - } catch (Exception ex) { - Log.e(LOGTAG, "Error initializing media manager", ex); - } - } - } - - if (AppConstants.MOZ_STUMBLER_BUILD_TIME_ENABLED && Restrictions.isAllowed(this, Restrictable.DATA_CHOICES)) { - // Start (this acts as ping if started already) the stumbler lib; if the stumbler has queued data it will upload it. - // Stumbler operates on its own thread, and startup impact is further minimized by delaying work (such as upload) a few seconds. - // Avoid any potential startup CPU/thread contention by delaying the pref broadcast. - final long oneSecondInMillis = 1000; - ThreadUtils.getBackgroundHandler().postDelayed(new Runnable() { - @Override - public void run() { - GeckoPreferences.broadcastStumblerPref(BrowserApp.this); - } - }, oneSecondInMillis); - } - - if (AppConstants.MOZ_ANDROID_DOWNLOAD_CONTENT_SERVICE) { - // TODO: Better scheduling of sync action (Bug 1257492) - DownloadContentService.startSync(this); - - DownloadContentService.startVerification(this); - } - - FeedService.setup(this); - - super.handleMessage(event, message); - break; - - case "Gecko:Ready": - // Handle this message in GeckoApp, but also enable the Settings - // menuitem, which is specific to BrowserApp. - super.handleMessage(event, message); - final Menu menu = mMenu; - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - if (menu != null) { - menu.findItem(R.id.settings).setEnabled(true); - menu.findItem(R.id.help).setEnabled(true); - } - } - }); - - // Display notification for Mozilla data reporting, if data should be collected. - if (AppConstants.MOZ_DATA_REPORTING && Restrictions.isAllowed(this, Restrictable.DATA_CHOICES)) { - DataReportingNotification.checkAndNotifyPolicy(GeckoAppShell.getContext()); - } - break; - - case "Search:Keyword": - storeSearchQuery(message.getString("query")); - recordSearch(GeckoSharedPrefs.forProfile(this), message.getString("identifier"), - TelemetryContract.Method.ACTIONBAR); - break; - - case "LightweightTheme:Update": - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - mDynamicToolbar.setVisible(true, VisibilityTransition.ANIMATE); - } - }); - break; - - case "Video:Play": - if (SwitchBoard.isInExperiment(this, Experiments.HLS_VIDEO_PLAYBACK)) { - final String uri = message.getString("uri"); - final String uuid = message.getString("uuid"); - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - mVideoPlayer.start(Uri.parse(uri)); - Telemetry.sendUIEvent(TelemetryContract.Event.SHOW, TelemetryContract.Method.CONTENT, "playhls"); - } - }); - } - break; - - case "Tab:Added": - if (message.getBoolean("cancelEditMode")) { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - // Set the target tab to null so it does not get selected (on editing - // mode exit) in lieu of the tab that we're going to open and select. - mTargetTabForEditingMode = null; - mBrowserToolbar.cancelEdit(); - } - }); - } - break; - - default: - super.handleMessage(event, message); - break; - } - } catch (Exception e) { - Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e); - } - } - - @Override public void addTab() { Tabs.getInstance().addTab(); } @Override public void addPrivateTab() { Tabs.getInstance().addPrivateTab(); } @@ -3243,43 +3177,43 @@ public class BrowserApp extends GeckoApp if (mMenu == null) return; final MenuItem menuItem = mMenu.findItem(id); if (menuItem != null) mMenu.removeItem(id); } - private void updateAddonMenuItem(int id, JSONObject options) { + private void updateAddonMenuItem(int id, final GeckoBundle options) { // Set attribute for the menu item in cache, if available if (mAddonMenuItemsCache != null && !mAddonMenuItemsCache.isEmpty()) { for (MenuItemInfo item : mAddonMenuItemsCache) { if (item.id == id) { - item.label = options.optString("name", item.label); - item.checkable = options.optBoolean("checkable", item.checkable); - item.checked = options.optBoolean("checked", item.checked); - item.enabled = options.optBoolean("enabled", item.enabled); - item.visible = options.optBoolean("visible", item.visible); + item.label = options.getString("name", item.label); + item.checkable = options.getBoolean("checkable", item.checkable); + item.checked = options.getBoolean("checked", item.checked); + item.enabled = options.getBoolean("enabled", item.enabled); + item.visible = options.getBoolean("visible", item.visible); item.added = (mMenu != null); break; } } } if (mMenu == null) { return; } final MenuItem menuItem = mMenu.findItem(id); if (menuItem != null) { - menuItem.setTitle(options.optString("name", menuItem.getTitle().toString())); - menuItem.setCheckable(options.optBoolean("checkable", menuItem.isCheckable())); - menuItem.setChecked(options.optBoolean("checked", menuItem.isChecked())); - menuItem.setEnabled(options.optBoolean("enabled", menuItem.isEnabled())); - menuItem.setVisible(options.optBoolean("visible", menuItem.isVisible())); + menuItem.setTitle(options.getString("name", menuItem.getTitle().toString())); + menuItem.setCheckable(options.getBoolean("checkable", menuItem.isCheckable())); + menuItem.setChecked(options.getBoolean("checked", menuItem.isChecked())); + menuItem.setEnabled(options.getBoolean("enabled", menuItem.isEnabled())); + menuItem.setVisible(options.getBoolean("visible", menuItem.isVisible())); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Sets mMenu = menu. super.onCreateOptionsMenu(menu); @@ -4024,21 +3958,16 @@ public class BrowserApp extends GeckoApp && TabQueueHelper.shouldShowTabQueuePrompt(BrowserApp.this)) { Intent promptIntent = new Intent(BrowserApp.this, TabQueuePrompt.class); startActivityForResult(promptIntent, ACTIVITY_REQUEST_TAB_QUEUE); } } }); } - private void resetFeedbackLaunchCount() { - SharedPreferences settings = getPreferences(Activity.MODE_PRIVATE); - settings.edit().putInt(getPackageName() + ".feedback_launch_count", 0).apply(); - } - // HomePager.OnUrlOpenListener @Override public void onUrlOpen(String url, EnumSet<OnUrlOpenListener.Flags> flags) { if (flags.contains(OnUrlOpenListener.Flags.OPEN_WITH_INTENT)) { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(url)); startActivity(intent); } else { @@ -4142,44 +4071,16 @@ public class BrowserApp extends GeckoApp } // For use from tests only. @RobocopTarget public ReadingListHelper getReadingListHelper() { return mReadingListHelper; } - /** - * Launch UI that lets the user update Firefox. - * - * This depends on the current channel: Release and Beta both direct to the - * Google Play Store. If updating is enabled, Aurora, Nightly, and custom - * builds open about:, which provides an update interface. - * - * If updating is not enabled, this simply logs an error. - * - * @return true if update UI was launched. - */ - protected boolean handleUpdaterLaunch() { - if (AppConstants.RELEASE_OR_BETA) { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse("market://details?id=" + getPackageName())); - startActivity(intent); - return true; - } - - if (AppConstants.MOZ_UPDATER) { - Tabs.getInstance().loadUrlInTab(AboutPages.UPDATER); - return true; - } - - Log.w(LOGTAG, "No candidate updater found; ignoring launch request."); - return false; - } - /* Implementing ActionModeCompat.Presenter */ @Override public void startActionModeCompat(final ActionModeCompat.Callback callback) { // If actionMode is null, we're not currently showing one. Flip to the action mode view if (mActionMode == null) { mActionBarFlipper.showNext(); DynamicToolbarAnimator toolbar = mLayerView.getDynamicToolbarAnimator();
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java @@ -34,22 +34,22 @@ import org.mozilla.gecko.preferences.Gec import org.mozilla.gecko.prompts.PromptService; import org.mozilla.gecko.restrictions.Restrictions; import org.mozilla.gecko.tabqueue.TabQueueHelper; import org.mozilla.gecko.text.FloatingToolbarTextSelection; import org.mozilla.gecko.text.TextSelection; import org.mozilla.gecko.updater.UpdateServiceHelper; import org.mozilla.gecko.util.ActivityResultHandler; import org.mozilla.gecko.util.ActivityUtils; +import org.mozilla.gecko.util.BundleEventListener; import org.mozilla.gecko.util.EventCallback; import org.mozilla.gecko.util.FileUtils; -import org.mozilla.gecko.util.GeckoEventListener; +import org.mozilla.gecko.util.GeckoBundle; import org.mozilla.gecko.util.GeckoRequest; import org.mozilla.gecko.util.HardwareUtils; -import org.mozilla.gecko.util.NativeEventListener; import org.mozilla.gecko.util.NativeJSObject; import org.mozilla.gecko.util.PrefUtils; import org.mozilla.gecko.util.ThreadUtils; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; import android.app.AlertDialog; @@ -125,22 +125,21 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; public abstract class GeckoApp extends GeckoActivity implements + BundleEventListener, ContextGetter, GeckoAppShell.GeckoInterface, - GeckoEventListener, GeckoMenu.Callback, GeckoMenu.MenuPresenter, - NativeEventListener, Tabs.OnTabsChangedListener, ViewTreeObserver.OnGlobalLayoutListener { private static final String LOGTAG = "GeckoApp"; private static final long ONE_DAY_MS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS); public static final String ACTION_ALERT_CALLBACK = "org.mozilla.gecko.ALERT_CALLBACK"; public static final String ACTION_HOMESCREEN_SHORTCUT = "org.mozilla.gecko.BOOKMARK"; @@ -611,46 +610,62 @@ public abstract class GeckoApp * @return True if the tab UI was hidden. */ public boolean autoHideTabs() { return false; } @Override public boolean areTabsShown() { return false; } @Override - public void handleMessage(final String event, final NativeJSObject message, + public void handleMessage(final String event, final GeckoBundle message, final EventCallback callback) { - if ("Accessibility:Ready".equals(event)) { + if (event.equals("Gecko:Ready")) { + mGeckoReadyStartupTimer.stop(); + geckoConnected(); + + // This method is already running on the background thread, so we + // know that mHealthRecorder will exist. That doesn't stop us being + // paranoid. + // This method is cheap, so don't spawn a new runnable. + final HealthRecorder rec = mHealthRecorder; + if (rec != null) { + rec.recordGeckoStartupTime(mGeckoReadyStartupTimer.getElapsed()); + } + + GeckoApplication.get().onDelayedStartup(); + + } else if (event.equals("Gecko:Exited")) { + // Gecko thread exited first; let GeckoApp die too. + doShutdown(); + + } else if ("Accessibility:Ready".equals(event)) { GeckoAccessibility.updateAccessibilitySettings(this); + } else if ("Accessibility:Event".equals(event)) { + GeckoAccessibility.sendAccessibilityEvent(message); + } else if ("Bookmark:Insert".equals(event)) { - final String url = message.getString("url"); - final String title = message.getString("title"); - final Context context = this; final BrowserDB db = BrowserDB.from(getProfile()); - ThreadUtils.postToBackgroundThread(new Runnable() { + final boolean bookmarkAdded = db.addBookmark( + getContentResolver(), message.getString("title"), message.getString("url")); + final int resId = bookmarkAdded ? R.string.bookmark_added + : R.string.bookmark_already_added; + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { - final boolean bookmarkAdded = db.addBookmark(getContentResolver(), title, url); - final int resId = bookmarkAdded ? R.string.bookmark_added : R.string.bookmark_already_added; - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - SnackbarBuilder.builder(GeckoApp.this) - .message(resId) - .duration(Snackbar.LENGTH_LONG) - .buildAndShow(); - } - }); + SnackbarBuilder.builder(GeckoApp.this) + .message(resId) + .duration(Snackbar.LENGTH_LONG) + .buildAndShow(); } }); } else if ("Contact:Add".equals(event)) { - final String email = message.optString("email", null); - final String phone = message.optString("phone", null); + final String email = message.getString("email", null); + final String phone = message.getString("phone", null); if (email != null) { Uri contactUri = Uri.parse(email); Intent i = new Intent(ContactsContract.Intents.SHOW_OR_CREATE_CONTACT, contactUri); startActivity(i); } else if (phone != null) { Uri contactUri = Uri.parse(phone); Intent i = new Intent(ContactsContract.Intents.SHOW_OR_CREATE_CONTACT, contactUri); startActivity(i); @@ -680,24 +695,42 @@ public abstract class GeckoApp } else if ("Image:SetAs".equals(event)) { String src = message.getString("url"); setImageAs(src); } else if ("Locale:Set".equals(event)) { setLocale(message.getString("locale")); } else if ("Permissions:Data".equals(event)) { - final NativeJSObject[] permissions = message.getObjectArray("permissions"); + final GeckoBundle[] permissions = message.getBundleArray("permissions"); showSiteSettingsDialog(permissions); } else if ("PrivateBrowsing:Data".equals(event)) { - mPrivateBrowsingSession = message.optString("session", null); - - } else if ("Session:StatePurged".equals(event)) { - onStatePurged(); + mPrivateBrowsingSession = message.getString("session", null); + + } else if ("RuntimePermissions:Prompt".equals(event)) { + String[] permissions = message.getStringArray("permissions"); + if (callback == null || permissions == null) { + return; + } + + Permissions.from(this) + .withPermissions(permissions) + .andFallback(new Runnable() { + @Override + public void run() { + callback.sendSuccess(false); + } + }) + .run(new Runnable() { + @Override + public void run() { + callback.sendSuccess(true); + } + }); } else if ("Share:Text".equals(event)) { final String text = message.getString("text"); final Tab tab = Tabs.getInstance().getSelectedTab(); String title = ""; if (tab != null) { title = tab.getDisplayTitle(); } @@ -708,101 +741,54 @@ public abstract class GeckoApp } else if ("Snackbar:Show".equals(event)) { SnackbarBuilder.builder(this) .fromEvent(message) .callback(callback) .buildAndShow(); } else if ("SystemUI:Visibility".equals(event)) { - setSystemUiVisible(message.getBoolean("visible")); + if (message.getBoolean("visible", true)) { + mMainLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); + } else { + mMainLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE); + } } else if ("ToggleChrome:Focus".equals(event)) { focusChrome(); } else if ("ToggleChrome:Hide".equals(event)) { toggleChrome(false); } else if ("ToggleChrome:Show".equals(event)) { toggleChrome(true); } else if ("Update:Check".equals(event)) { UpdateServiceHelper.checkForUpdate(this); + } else if ("Update:Download".equals(event)) { UpdateServiceHelper.downloadUpdate(this); + } else if ("Update:Install".equals(event)) { UpdateServiceHelper.applyUpdate(this); - } else if ("RuntimePermissions:Prompt".equals(event)) { - String[] permissions = message.getStringArray("permissions"); - if (callback == null || permissions == null) { - return; - } - - Permissions.from(this) - .withPermissions(permissions) - .andFallback(new Runnable() { - @Override - public void run() { - callback.sendSuccess(false); - } - }) - .run(new Runnable() { - @Override - public void run() { - callback.sendSuccess(true); - } - }); } } - @Override - public void handleMessage(String event, JSONObject message) { - try { - if (event.equals("Gecko:Ready")) { - mGeckoReadyStartupTimer.stop(); - geckoConnected(); - - // This method is already running on the background thread, so we - // know that mHealthRecorder will exist. That doesn't stop us being - // paranoid. - // This method is cheap, so don't spawn a new runnable. - final HealthRecorder rec = mHealthRecorder; - if (rec != null) { - rec.recordGeckoStartupTime(mGeckoReadyStartupTimer.getElapsed()); - } - - GeckoApplication.get().onDelayedStartup(); - - } else if (event.equals("Gecko:Exited")) { - // Gecko thread exited first; let GeckoApp die too. - doShutdown(); - return; - - } else if (event.equals("Accessibility:Event")) { - GeckoAccessibility.sendAccessibilityEvent(message); - } - } catch (Exception e) { - Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e); - } - } - - void onStatePurged() { } - /** * @param permissions * Array of JSON objects to represent site permissions. * Example: { type: "offline-app", setting: "Store Offline Data", value: "Allow" } */ - private void showSiteSettingsDialog(final NativeJSObject[] permissions) { + private void showSiteSettingsDialog(final GeckoBundle[] permissions) { final AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.site_settings_title); final ArrayList<HashMap<String, String>> itemList = new ArrayList<HashMap<String, String>>(); - for (final NativeJSObject permObj : permissions) { + for (final GeckoBundle permObj : permissions) { final HashMap<String, String> map = new HashMap<String, String>(); map.put("setting", permObj.getString("setting")); map.put("value", permObj.getString("value")); itemList.add(map); } // setMultiChoiceItems doesn't support using an adapter, so we're creating a hack with // setSingleChoiceItems and changing the choiceMode below when we create the dialog @@ -835,46 +821,39 @@ public abstract class GeckoApp builder.setNegativeButton(R.string.site_settings_cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }); - ThreadUtils.postToUiThread(new Runnable() { + AlertDialog dialog = builder.create(); + dialog.show(); + + final ListView listView = dialog.getListView(); + if (listView != null) { + listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); + } + + final Button clearButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE); + clearButton.setEnabled(false); + + dialog.getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override - public void run() { - AlertDialog dialog = builder.create(); - dialog.show(); - - final ListView listView = dialog.getListView(); - if (listView != null) { - listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); + public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { + if (listView.getCheckedItemCount() == 0) { + clearButton.setEnabled(false); + } else { + clearButton.setEnabled(true); } - - final Button clearButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE); - clearButton.setEnabled(false); - - dialog.getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { - if (listView.getCheckedItemCount() == 0) { - clearButton.setEnabled(false); - } else { - clearButton.setEnabled(true); - } - } - }); } }); } - - /* package */ void addFullScreenPluginView(View view) { if (mFullScreenPluginView != null) { Log.w(LOGTAG, "Already have a fullscreen plugin view"); return; } setFullScreen(true); @@ -953,16 +932,45 @@ public abstract class GeckoApp @Override public void run() { removeFullScreenPluginView(view); } }); } } + private void showSetImageResult(final boolean success, final int message, final String path) { + ThreadUtils.postToUiThread(new Runnable() { + @Override + public void run() { + if (!success) { + SnackbarBuilder.builder(GeckoApp.this) + .message(message) + .duration(Snackbar.LENGTH_LONG) + .buildAndShow(); + return; + } + + final Intent intent = new Intent(Intent.ACTION_ATTACH_DATA); + intent.addCategory(Intent.CATEGORY_DEFAULT); + intent.setData(Uri.parse(path)); + + // Removes the image from storage once the chooser activity ends. + Intent chooser = Intent.createChooser(intent, getString(message)); + ActivityResultHandler handler = new ActivityResultHandler() { + @Override + public void onActivityResult (int resultCode, Intent data) { + getContentResolver().delete(intent.getData(), null, null); + } + }; + ActivityHandlerHelper.startIntentForActivity(GeckoApp.this, chooser, handler); + } + }); + } + // This method starts downloading an image synchronously and displays the Chooser activity to set the image as wallpaper. private void setImageAs(final String aSrc) { boolean isDataURI = aSrc.startsWith("data:"); Bitmap image = null; InputStream is = null; ByteArrayOutputStream os = null; try { if (isDataURI) { @@ -985,48 +993,27 @@ public abstract class GeckoApp byte[] imgBuffer = os.toByteArray(); image = BitmapUtils.decodeByteArray(imgBuffer); } if (image != null) { // Some devices don't have a DCIM folder and the Media.insertImage call will fail. File dcimDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); if (!dcimDir.mkdirs() && !dcimDir.isDirectory()) { - SnackbarBuilder.builder(this) - .message(R.string.set_image_path_fail) - .duration(Snackbar.LENGTH_LONG) - .buildAndShow(); + showSetImageResult(/* success */ false, R.string.set_image_path_fail, null); return; } String path = Media.insertImage(getContentResolver(), image, null, null); if (path == null) { - SnackbarBuilder.builder(this) - .message(R.string.set_image_path_fail) - .duration(Snackbar.LENGTH_LONG) - .buildAndShow(); + showSetImageResult(/* success */ false, R.string.set_image_path_fail, null); return; } - final Intent intent = new Intent(Intent.ACTION_ATTACH_DATA); - intent.addCategory(Intent.CATEGORY_DEFAULT); - intent.setData(Uri.parse(path)); - - // Removes the image from storage once the chooser activity ends. - Intent chooser = Intent.createChooser(intent, getString(R.string.set_image_chooser_title)); - ActivityResultHandler handler = new ActivityResultHandler() { - @Override - public void onActivityResult (int resultCode, Intent data) { - getContentResolver().delete(intent.getData(), null, null); - } - }; - ActivityHandlerHelper.startIntentForActivity(this, chooser, handler); + showSetImageResult(/* success */ true, R.string.set_image_chooser_title, path); } else { - SnackbarBuilder.builder(this) - .message(R.string.set_image_fail) - .duration(Snackbar.LENGTH_LONG) - .buildAndShow(); + showSetImageResult(/* success */ false, R.string.set_image_fail, null); } } catch (OutOfMemoryError ome) { Log.e(LOGTAG, "Out of Memory when converting to byte array", ome); } catch (IOException ioe) { Log.e(LOGTAG, "I/O Exception while setting wallpaper", ioe); } finally { if (is != null) { try { @@ -1188,25 +1175,22 @@ public abstract class GeckoApp final String uri = getURIFromIntent(intent); if (!TextUtils.isEmpty(uri)) { // Start a speculative connection as soon as Gecko loads. GeckoThread.speculativeConnect(uri); } } - // GeckoThread has to register for "Gecko:Ready" first, so GeckoApp registers - // for events after initializing GeckoThread but before launching it. - - EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener)this, + // To prevent races, register startup events before launching the Gecko thread. + EventDispatcher.getInstance().registerGeckoThreadListener(this, + "Accessibility:Ready", + "Gecko:Exited", "Gecko:Ready", - "Gecko:Exited"); - - EventDispatcher.getInstance().registerGeckoThreadListener((NativeEventListener)this, - "Accessibility:Ready"); + null); GeckoThread.launch(); Bundle stateBundle = IntentUtils.getBundleExtraSafe(getIntent(), EXTRA_STATE_BUNDLE); if (stateBundle != null) { // Use the state bundle if it was given as an intent extra. This is // only intended to be used internally via Robocop, so a boolean // is read from a private shared pref to prevent other apps from @@ -1228,40 +1212,43 @@ public abstract class GeckoApp setContentView(getLayout()); // Set up Gecko layout. mRootLayout = (RelativeLayout) findViewById(R.id.root_layout); mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout); mMainLayout = (RelativeLayout) findViewById(R.id.main_layout); mLayerView = (GeckoView) findViewById(R.id.layer_view); - getAppEventDispatcher().registerGeckoThreadListener((GeckoEventListener)this, - "Accessibility:Event"); - - getAppEventDispatcher().registerGeckoThreadListener((NativeEventListener)this, + getAppEventDispatcher().registerGeckoThreadListener(this, + "Accessibility:Event", + "Locale:Set", + null); + + getAppEventDispatcher().registerBackgroundThreadListener(this, "Bookmark:Insert", + "Image:SetAs", + null); + + getAppEventDispatcher().registerUiThreadListener(this, "Contact:Add", "DevToolsAuth:Scan", "DOMFullScreen:Start", "DOMFullScreen:Stop", - "Image:SetAs", - "Locale:Set", "Permissions:Data", "PrivateBrowsing:Data", "RuntimePermissions:Prompt", - "Session:StatePurged", "Share:Text", - "Snackbar:Show", "SystemUI:Visibility", "ToggleChrome:Focus", "ToggleChrome:Hide", "ToggleChrome:Show", "Update:Check", "Update:Download", - "Update:Install"); + "Update:Install", + null); Tabs.getInstance().attachToContext(this, mLayerView); // Use global layout state change to kick off additional initialization mMainLayout.getViewTreeObserver().addOnGlobalLayoutListener(this); if (Versions.preMarshmallow) { mTextSelection = new ActionBarTextSelection(this); @@ -1678,19 +1665,19 @@ public abstract class GeckoApp // site loaded from the intent is on top (last loaded) and selected with all other tabs // being opened behind it. We process the tab queue first and request a callback from the JS - the // listener will open the url from the intent as normal when the tab queue has been processed. ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { if (TabQueueHelper.TAB_QUEUE_ENABLED && TabQueueHelper.shouldOpenTabQueueUrls(GeckoApp.this)) { - getAppEventDispatcher().registerGeckoThreadListener(new NativeEventListener() { + getAppEventDispatcher().registerGeckoThreadListener(new BundleEventListener() { @Override - public void handleMessage(String event, NativeJSObject message, EventCallback callback) { + public void handleMessage(String event, GeckoBundle message, EventCallback callback) { if ("Tabs:TabsOpened".equals(event)) { getAppEventDispatcher().unregisterGeckoThreadListener(this, "Tabs:TabsOpened"); openTabsRunnable.run(); } } }, "Tabs:TabsOpened"); TabQueueHelper.openQueuedUrls(GeckoApp.this, getProfile(), TabQueueHelper.FILE_NAME, true); } else { @@ -2078,16 +2065,19 @@ public abstract class GeckoApp } @Override public void onResume() { // After an onPause, the activity is back in the foreground. // Undo whatever we did in onPause. super.onResume(); + + EventDispatcher.getInstance().registerUiThreadListener(this, "Snackbar:Show"); + if (mIsAbortingAppLaunch) { return; } foregrounded = true; GeckoAppShell.setGeckoInterface(this); @@ -2159,16 +2149,18 @@ public abstract class GeckoApp mLayerView.setFocusableInTouchMode(true); getWindow().setBackgroundDrawable(null); } } @Override public void onPause() { + EventDispatcher.getInstance().unregisterUiThreadListener(this, "Snackbar:Show"); + if (mIsAbortingAppLaunch) { super.onPause(); return; } foregrounded = false; final Tab selectedTab = Tabs.getInstance().getSelectedTab(); @@ -2242,47 +2234,49 @@ public abstract class GeckoApp public void onDestroy() { if (mIsAbortingAppLaunch) { // This build does not support the Android version of the device: // We did not initialize anything, so skip cleaning up. super.onDestroy(); return; } - EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener)this, + EventDispatcher.getInstance().unregisterGeckoThreadListener(this, + "Accessibility:Ready", + "Gecko:Exited", "Gecko:Ready", - "Gecko:Exited"); - - EventDispatcher.getInstance().unregisterGeckoThreadListener((NativeEventListener)this, - "Accessibility:Ready"); - - getAppEventDispatcher().unregisterGeckoThreadListener((GeckoEventListener)this, - "Accessibility:Event"); - - getAppEventDispatcher().unregisterGeckoThreadListener((NativeEventListener)this, + null); + + getAppEventDispatcher().unregisterGeckoThreadListener(this, + "Accessibility:Event", + "Locale:Set", + null); + + getAppEventDispatcher().unregisterBackgroundThreadListener(this, "Bookmark:Insert", + "Image:SetAs", + null); + + getAppEventDispatcher().unregisterUiThreadListener(this, "Contact:Add", "DevToolsAuth:Scan", "DOMFullScreen:Start", "DOMFullScreen:Stop", - "Image:SetAs", - "Locale:Set", "Permissions:Data", "PrivateBrowsing:Data", "RuntimePermissions:Prompt", - "Session:StatePurged", "Share:Text", - "Snackbar:Show", "SystemUI:Visibility", "ToggleChrome:Focus", "ToggleChrome:Hide", "ToggleChrome:Show", "Update:Check", "Update:Download", - "Update:Install"); + "Update:Install", + null); if (mPromptService != null) mPromptService.destroy(); final HealthRecorder rec = mHealthRecorder; mHealthRecorder = null; if (rec != null && rec.isEnabled()) { // Closing a HealthRecorder could incur a write. @@ -2794,29 +2788,16 @@ public abstract class GeckoApp final String resultant = BrowserLocaleManager.getInstance().setSelectedLocale(this, locale); if (resultant == null) { return; } onLocaleChanged(resultant); } - private void setSystemUiVisible(final boolean visible) { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - if (visible) { - mMainLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); - } else { - mMainLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE); - } - } - }); - } - protected HealthRecorder createHealthRecorder(final Context context, final String profilePath, final EventDispatcher dispatcher, final String osLocale, final String appLocale, final SessionInformation previousSession) { // GeckoApp does not need to record any health information - return a stub. return new StubbedHealthRecorder();
--- a/mobile/android/base/java/org/mozilla/gecko/SnackbarBuilder.java +++ b/mobile/android/base/java/org/mozilla/gecko/SnackbarBuilder.java @@ -1,17 +1,17 @@ /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko; import org.mozilla.gecko.util.EventCallback; -import org.mozilla.gecko.util.NativeJSObject; +import org.mozilla.gecko.util.GeckoBundle; import android.app.Activity; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.graphics.drawable.InsetDrawable; import android.support.annotation.StringRes; import android.support.design.widget.Snackbar; import android.support.v4.content.ContextCompat; @@ -168,32 +168,32 @@ public class SnackbarBuilder { public SnackbarBuilder actionColor(final Integer actionColor) { this.actionColor = actionColor; return this; } /** * @param object Populate the builder with data from a Gecko Snackbar:Show event. */ - public SnackbarBuilder fromEvent(final NativeJSObject object) { + public SnackbarBuilder fromEvent(final GeckoBundle object) { message = object.getString("message"); duration = object.getInt("duration"); - if (object.has("backgroundColor")) { + if (object.containsKey("backgroundColor")) { final String providedColor = object.getString("backgroundColor"); try { backgroundColor = Color.parseColor(providedColor); } catch (IllegalArgumentException e) { Log.w(LOGTAG, "Failed to parse color string: " + providedColor); } } - NativeJSObject actionObject = object.optObject("action", null); + GeckoBundle actionObject = object.getBundle("action"); if (actionObject != null) { - action = actionObject.optString("label", null); + action = actionObject.getString("label", null); } return this; } public void buildAndShow() { final View parentView = findBestParentView(activity); final Snackbar snackbar = Snackbar.make(parentView, message, duration);
--- a/mobile/android/base/java/org/mozilla/gecko/Tabs.java +++ b/mobile/android/base/java/org/mozilla/gecko/Tabs.java @@ -18,16 +18,19 @@ import org.json.JSONObject; import org.mozilla.gecko.annotation.JNITarget; import org.mozilla.gecko.annotation.RobocopTarget; import org.mozilla.gecko.AppConstants.Versions; import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.gfx.LayerView; import org.mozilla.gecko.mozglue.SafeIntent; import org.mozilla.gecko.notifications.WhatsNewReceiver; import org.mozilla.gecko.reader.ReaderModeUtils; +import org.mozilla.gecko.util.BundleEventListener; +import org.mozilla.gecko.util.EventCallback; +import org.mozilla.gecko.util.GeckoBundle; import org.mozilla.gecko.util.GeckoEventListener; import org.mozilla.gecko.util.ThreadUtils; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.OnAccountsUpdateListener; import android.content.ContentResolver; import android.content.Context; @@ -35,17 +38,17 @@ import android.database.ContentObserver; import android.database.sqlite.SQLiteException; import android.graphics.Color; import android.net.Uri; import android.os.Handler; import android.provider.Browser; import android.support.v4.content.ContextCompat; import android.util.Log; -public class Tabs implements GeckoEventListener { +public class Tabs implements BundleEventListener, GeckoEventListener { private static final String LOGTAG = "GeckoTabs"; // mOrder and mTabs are always of the same cardinality, and contain the same values. private final CopyOnWriteArrayList<Tab> mOrder = new CopyOnWriteArrayList<Tab>(); // All writes to mSelectedTab must be synchronized on the Tabs instance. // In general, it's preferred to always use selectTab()). private volatile Tab mSelectedTab; @@ -99,18 +102,21 @@ public class Tabs implements GeckoEventL db.getTabsAccessor().persistLocalTabs(context.getContentResolver(), tabs); } catch (SQLiteException e) { Log.w(LOGTAG, "Error persisting local tabs", e); } } }; private Tabs() { - EventDispatcher.getInstance().registerGeckoThreadListener(this, + EventDispatcher.getInstance().registerUiThreadListener(this, "Tab:Added", + null); + + EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener) this, "Tab:Close", "Tab:Select", "Tab:SelectAndForeground", "Content:LocationChange", "Content:SecurityChange", "Content:StateChange", "Content:LoadError", "Content:PageShow", @@ -468,60 +474,66 @@ public class Tabs implements GeckoEventL private static final Tabs INSTANCE = new Tabs(); } @RobocopTarget public static Tabs getInstance() { return Tabs.TabsInstanceHolder.INSTANCE; } + @Override // BundleEventListener + public synchronized void handleMessage(final String event, final GeckoBundle message, + final EventCallback callback) { + // "Tab:Added" is a special case because tab will be null if the tab was just added + if ("Tab:Added".equals(event)) { + int id = message.getInt("tabID"); + Tab tab = getTab(id); + + String url = message.getString("uri"); + + if (message.getBoolean("cancelEditMode")) { + final Tab oldTab = getSelectedTab(); + if (oldTab != null) { + oldTab.setIsEditing(false); + } + } + + if (message.getBoolean("stub")) { + if (tab == null) { + // Tab was already closed; abort + return; + } + } else { + tab = addTab(id, url, message.getBoolean("external"), + message.getInt("parentId"), + message.getString("title"), + message.getBoolean("isPrivate"), + message.getInt("tabIndex")); + // If we added the tab as a stub, we should have already + // selected it, so ignore this flag for stubbed tabs. + if (message.getBoolean("selected")) + selectTab(id); + } + + if (message.getBoolean("delayLoad")) + tab.setState(Tab.STATE_DELAYED); + if (message.getBoolean("desktopMode")) + tab.setDesktopMode(true); + } + } + // GeckoEventListener implementation @Override - public void handleMessage(String event, JSONObject message) { + public synchronized void handleMessage(String event, JSONObject message) { Log.d(LOGTAG, "handleMessage: " + event); try { // All other events handled below should contain a tabID property int id = message.getInt("tabID"); Tab tab = getTab(id); - // "Tab:Added" is a special case because tab will be null if the tab was just added - if (event.equals("Tab:Added")) { - String url = message.isNull("uri") ? null : message.getString("uri"); - - if (message.getBoolean("cancelEditMode")) { - final Tab oldTab = getSelectedTab(); - if (oldTab != null) { - oldTab.setIsEditing(false); - } - } - - if (message.getBoolean("stub")) { - if (tab == null) { - // Tab was already closed; abort - return; - } - } else { - tab = addTab(id, url, message.getBoolean("external"), - message.getInt("parentId"), - message.getString("title"), - message.getBoolean("isPrivate"), - message.getInt("tabIndex")); - // If we added the tab as a stub, we should have already - // selected it, so ignore this flag for stubbed tabs. - if (message.getBoolean("selected")) - selectTab(id); - } - - if (message.getBoolean("delayLoad")) - tab.setState(Tab.STATE_DELAYED); - if (message.getBoolean("desktopMode")) - tab.setDesktopMode(true); - return; - } - // Tab was already closed; abort if (tab == null) return; if (event.equals("Tab:Close")) { closeTab(tab); } else if (event.equals("Tab:Select")) { selectTab(tab.getId());
--- a/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryPanel.java +++ b/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryPanel.java @@ -533,24 +533,17 @@ public class CombinedHistoryPanel extend final SpannableStringBuilder ssb = new SpannableStringBuilder(text); // Set clickable text. final ClickableSpan clickableSpan = new ClickableSpan() { @Override public void onClick(View widget) { Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.BUTTON, "hint_private_browsing"); - try { - final JSONObject json = new JSONObject(); - json.put("type", "Menu:Open"); - GeckoApp.getEventDispatcher().dispatchEvent(json, null); - EventDispatcher.getInstance().dispatchEvent(json, null); - } catch (JSONException e) { - Log.e(LOGTAG, "Error forming JSON for Private Browsing contextual hint", e); - } + EventDispatcher.getInstance().dispatch("Menu:Open", null); } }; ssb.setSpan(clickableSpan, 0, text.length(), 0); // Remove underlining set by ClickableSpan. final UnderlineSpan noUnderlineSpan = new UnderlineSpan() { @Override
--- a/mobile/android/base/java/org/mozilla/gecko/lwt/LightweightTheme.java +++ b/mobile/android/base/java/org/mozilla/gecko/lwt/LightweightTheme.java @@ -10,17 +10,19 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.List; import org.json.JSONObject; import org.mozilla.gecko.AppConstants.Versions; import org.mozilla.gecko.EventDispatcher; import org.mozilla.gecko.GeckoSharedPrefs; import org.mozilla.gecko.gfx.BitmapUtils; -import org.mozilla.gecko.util.GeckoEventListener; +import org.mozilla.gecko.util.BundleEventListener; +import org.mozilla.gecko.util.EventCallback; +import org.mozilla.gecko.util.GeckoBundle; import org.mozilla.gecko.util.WindowUtils; import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.gecko.util.ThreadUtils.AssertBehavior; import android.app.Application; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -32,17 +34,17 @@ import android.graphics.Shader; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.text.TextUtils; import android.util.Log; import android.view.Gravity; import android.view.View; import android.view.ViewParent; -public class LightweightTheme implements GeckoEventListener { +public class LightweightTheme implements BundleEventListener { private static final String LOGTAG = "GeckoLightweightTheme"; private static final String PREFS_URL = "lightweightTheme.headerURL"; private static final String PREFS_COLOR = "lightweightTheme.color"; private static final String ASSETS_PREFIX = "resource://android/assets/"; private final Application mApplication; @@ -158,17 +160,17 @@ public class LightweightTheme implements } } public LightweightTheme(Application application) { mApplication = application; mListeners = new ArrayList<OnChangeListener>(); // unregister isn't needed as the lifetime is same as the application. - EventDispatcher.getInstance().registerGeckoThreadListener(this, + EventDispatcher.getInstance().registerUiThreadListener(this, "LightweightTheme:Update", "LightweightTheme:Disable"); ThreadUtils.postToBackgroundThread(new LightweightThemeRunnable()); } public void addListener(final OnChangeListener listener) { // Don't inform the listeners that attached late. @@ -176,38 +178,29 @@ public class LightweightTheme implements mListeners.add(listener); } public void removeListener(OnChangeListener listener) { mListeners.remove(listener); } @Override - public void handleMessage(String event, JSONObject message) { - try { - if (event.equals("LightweightTheme:Update")) { - JSONObject lightweightTheme = message.getJSONObject("data"); - final String headerURL = lightweightTheme.getString("headerURL"); - final String color = lightweightTheme.optString("accentcolor"); + public void handleMessage(String event, GeckoBundle message, EventCallback callback) { + if (event.equals("LightweightTheme:Update")) { + GeckoBundle lightweightTheme = message.getBundle("data"); + final String headerURL = lightweightTheme.getString("headerURL"); + final String color = lightweightTheme.getString("accentcolor"); - ThreadUtils.postToBackgroundThread(new LightweightThemeRunnable(headerURL, color)); - } else if (event.equals("LightweightTheme:Disable")) { - // Clear the saved data when a theme is disabled. - // Called on the Gecko thread, but should be very lightweight. - clearPrefs(); + ThreadUtils.postToBackgroundThread(new LightweightThemeRunnable(headerURL, color)); - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - resetLightweightTheme(); - } - }); - } - } catch (Exception e) { - Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e); + } else if (event.equals("LightweightTheme:Disable")) { + // Clear the saved data when a theme is disabled. + // Called on the Gecko thread, but should be very lightweight. + clearPrefs(); + resetLightweightTheme(); } } /** * Clear the data stored in preferences for fast path loading during startup */ private void clearPrefs() { GeckoSharedPrefs.forProfile(mApplication)
--- a/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java +++ b/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java @@ -36,18 +36,20 @@ import org.mozilla.gecko.feeds.FeedServi import org.mozilla.gecko.feeds.action.CheckForUpdatesAction; import org.mozilla.gecko.permissions.Permissions; import org.mozilla.gecko.restrictions.Restrictable; import org.mozilla.gecko.restrictions.Restrictions; import org.mozilla.gecko.tabqueue.TabQueueHelper; import org.mozilla.gecko.tabqueue.TabQueuePrompt; import org.mozilla.gecko.updater.UpdateService; import org.mozilla.gecko.updater.UpdateServiceHelper; +import org.mozilla.gecko.util.BundleEventListener; import org.mozilla.gecko.util.ContextUtils; import org.mozilla.gecko.util.EventCallback; +import org.mozilla.gecko.util.GeckoBundle; import org.mozilla.gecko.util.HardwareUtils; import org.mozilla.gecko.util.InputOptionsUtils; import org.mozilla.gecko.util.NativeEventListener; import org.mozilla.gecko.util.NativeJSObject; import org.mozilla.gecko.util.ThreadUtils; import android.annotation.TargetApi; import android.app.AlertDialog; @@ -96,22 +98,22 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; public class GeckoPreferences -extends AppCompatPreferenceActivity -implements -GeckoActivityStatus, -NativeEventListener, -OnPreferenceChangeListener, -OnSharedPreferenceChangeListener + extends AppCompatPreferenceActivity + implements BundleEventListener, + GeckoActivityStatus, + NativeEventListener, + OnPreferenceChangeListener, + OnSharedPreferenceChangeListener { private static final String LOGTAG = "GeckoPreferences"; // We have a white background, which makes transitions on // some devices look bad. Don't use transitions on those // devices. private static final boolean NO_TRANSITIONS = HardwareUtils.IS_KINDLE_DEVICE; private static final int NO_SUCH_ID = 0; @@ -364,19 +366,18 @@ OnSharedPreferenceChangeListener localeSwitchingIsEnabled = false; throw new IllegalStateException("foobar"); } } // Use setResourceToOpen to specify these extras. Bundle intentExtras = getIntent().getExtras(); - EventDispatcher.getInstance().registerGeckoThreadListener(this, - "Sanitize:Finished", - "Snackbar:Show"); + EventDispatcher.getInstance().registerGeckoThreadListener((NativeEventListener) this, + "Sanitize:Finished"); // Add handling for long-press click. // This is only for Android 3.0 and below (which use the long-press-context-menu paradigm). final ListView mListView = getListView(); mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { // Call long-click handler if it the item implements it. @@ -501,28 +502,29 @@ OnSharedPreferenceChangeListener Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, Method.BACK, "settings"); } @Override protected void onDestroy() { super.onDestroy(); - EventDispatcher.getInstance().unregisterGeckoThreadListener(this, - "Sanitize:Finished", - "Snackbar:Show"); + EventDispatcher.getInstance().unregisterGeckoThreadListener((NativeEventListener) this, + "Sanitize:Finished"); if (mPrefsRequest != null) { PrefsHelper.removeObserver(mPrefsRequest); mPrefsRequest = null; } } @Override public void onPause() { + EventDispatcher.getInstance().unregisterUiThreadListener(this, "Snackbar:Show"); + // Symmetric with onResume. if (isMultiPane()) { SharedPreferences prefs = GeckoSharedPrefs.forApp(this); prefs.unregisterOnSharedPreferenceChangeListener(this); } super.onPause(); @@ -530,16 +532,18 @@ OnSharedPreferenceChangeListener ((GeckoApplication) getApplication()).onActivityPause(this); } } @Override public void onResume() { super.onResume(); + EventDispatcher.getInstance().registerUiThreadListener(this, "Snackbar:Show"); + if (getApplication() instanceof GeckoApplication) { ((GeckoApplication) getApplication()).onActivityResume(this); } // Watch prefs, otherwise we don't reliably get told when they change. // See documentation for onSharedPreferenceChange for more. // Inexplicably only needed on tablet. if (isMultiPane()) { @@ -603,34 +607,39 @@ OnSharedPreferenceChangeListener } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { Permissions.onRequestPermissionsResult(this, permissions, grantResults); } @Override + public void handleMessage(final String event, final GeckoBundle message, + final EventCallback callback) { + if ("Snackbar:Show".equals(event)) { + SnackbarBuilder.builder(this) + .fromEvent(message) + .callback(callback) + .buildAndShow(); + } + } + + @Override public void handleMessage(final String event, final NativeJSObject message, final EventCallback callback) { try { switch (event) { case "Sanitize:Finished": boolean success = message.getBoolean("success"); final int stringRes = success ? R.string.private_data_success : R.string.private_data_fail; SnackbarBuilder.builder(GeckoPreferences.this) .message(stringRes) .duration(Snackbar.LENGTH_LONG) .buildAndShow(); break; - case "Snackbar:Show": - SnackbarBuilder.builder(this) - .fromEvent(message) - .callback(callback) - .buildAndShow(); - break; } } catch (Exception e) { Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e); } } /** * Initialize all of the preferences (native of Gecko ones) for this screen.
--- a/mobile/android/chrome/content/ActionBarHandler.js +++ b/mobile/android/chrome/content/ActionBarHandler.js @@ -586,17 +586,17 @@ var ActionBarHandler = { return false; } // Allow if selected text exists. return (ActionBarHandler._getSelectedText().length > 0); }, }, action: function(element, win) { - Messaging.sendRequest({ + WindowEventDispatcher.sendRequest({ type: "Share:Text", text: ActionBarHandler._getSelectedText(), }); ActionBarHandler._uninit(); UITelemetry.addEvent("action.1", "actionbar", null, "share"); }, },
--- a/mobile/android/chrome/content/Feedback.js +++ b/mobile/android/chrome/content/Feedback.js @@ -43,17 +43,17 @@ var Feedback = { } switch (event.type) { case "FeedbackClose": // Do nothing. break; case "FeedbackMaybeLater": - Messaging.sendRequest({ type: "Feedback:MaybeLater" }); + GlobalEventDispatcher.sendRequest({ type: "Feedback:MaybeLater" }); break; } let win = event.target.ownerDocument.defaultView.top; BrowserApp.closeTab(BrowserApp.getTabForWindow(win)); }, _isAllowed: function(node) {
--- a/mobile/android/chrome/content/PermissionsHelper.js +++ b/mobile/android/chrome/content/PermissionsHelper.js @@ -101,17 +101,17 @@ var PermissionsHelper = { hasPermissions: false }); return; } // Keep track of permissions, so we know which ones to clear this._currentPermissions = permissions; - Messaging.sendRequest({ + WindowEventDispatcher.sendRequest({ type: "Permissions:Data", permissions: permissions }); break; case "Permissions:Clear": // An array of the indices of the permissions we want to clear let permissionsToClear = JSON.parse(aData);
--- a/mobile/android/chrome/content/Reader.js +++ b/mobile/android/chrome/content/Reader.js @@ -121,17 +121,17 @@ var Reader = { url: message.data.url }).then(data => { message.target.messageManager.sendAsyncMessage("Reader:FaviconReturn", JSON.parse(data)); }); break; } case "Reader:SystemUIVisibility": - Messaging.sendRequest({ + WindowEventDispatcher.sendRequest({ type: "SystemUI:Visibility", visible: message.data.visible }); break; case "Reader:ToolbarHidden": if (!this._hasUsedToolbar) { Snackbars.show(Strings.browser.GetStringFromName("readerMode.toolbarTip"), Snackbars.LENGTH_LONG);
--- a/mobile/android/chrome/content/RemoteDebugger.js +++ b/mobile/android/chrome/content/RemoteDebugger.js @@ -146,17 +146,17 @@ var RemoteDebugger = { * * k : K(random 128-bit number) * A promise that will be resolved to the above is also allowed. */ receiveOOB() { if (this._receivingOOB) { return this._receivingOOB; } - this._receivingOOB = Messaging.sendRequestForResult({ + this._receivingOOB = WindowEventDispatcher.sendRequestForResult({ type: "DevToolsAuth:Scan" }).then(data => { return JSON.parse(data); }, () => { let title = Strings.browser.GetStringFromName("remoteQRScanFailedPromptTitle"); let msg = Strings.browser.GetStringFromName("remoteQRScanFailedPromptMessage"); let ok = Strings.browser.GetStringFromName("remoteQRScanFailedPromptOK"); let prompt = new Prompt({
--- a/mobile/android/chrome/content/about.js +++ b/mobile/android/chrome/content/about.js @@ -73,27 +73,30 @@ function init() { }, }; Updater.init(); function checkForUpdates() { showCheckingMessage(); - Services.androidBridge.handleGeckoMessage({ type: "Update:Check" }); + let window = Services.wm.getMostRecentWindow("navigator:browser"); + window.WindowEventDispatcher.sendRequest({ type: "Update:Check" }); } function downloadUpdate() { - Services.androidBridge.handleGeckoMessage({ type: "Update:Download" }); + let window = Services.wm.getMostRecentWindow("navigator:browser"); + window.WindowEventDispatcher.sendRequest({ type: "Update:Download" }); } function installUpdate() { showCheckAction(); - Services.androidBridge.handleGeckoMessage({ type: "Update:Install" }); + let window = Services.wm.getMostRecentWindow("navigator:browser"); + window.WindowEventDispatcher.sendRequest({ type: "Update:Install" }); } let updateLink = document.getElementById("updateLink"); let checkingSpan = document.getElementById("update-message-checking"); let noneSpan = document.getElementById("update-message-none"); let foundSpan = document.getElementById("update-message-found"); let downloadingSpan = document.getElementById("update-message-downloading"); let downloadedSpan = document.getElementById("update-message-downloaded");
--- a/mobile/android/chrome/content/aboutHealthReport.js +++ b/mobile/android/chrome/content/aboutHealthReport.js @@ -3,18 +3,21 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; var { classes: Cc, interfaces: Ci, utils: Cu } = Components; Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/Messaging.jsm"); Cu.import("resource://gre/modules/SharedPreferences.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher", + "resource://gre/modules/Messaging.jsm"); // Name of Android SharedPreference controlling whether to upload // health reports. const PREF_UPLOAD_ENABLED = "android.not_a_preference.healthreport.uploadEnabled"; // Name of Gecko Pref specifying report content location. const PREF_REPORTURL = "datareporting.healthreport.about.reportUrl"; @@ -114,25 +117,25 @@ var healthReportWrapper = { }; let iframe = document.getElementById("remote-report"); iframe.contentWindow.postMessage(data, reportUrl); }, showSettings: function () { console.log("AboutHealthReport: showing settings."); - Messaging.sendRequest({ + EventDispatcher.instance.sendRequest({ type: "Settings:Show", resource: "preferences_vendor", }); }, launchUpdater: function () { console.log("AboutHealthReport: launching updater."); - Messaging.sendRequest({ + EventDispatcher.instance.sendRequest({ type: "Updater:Launch", }); }, handleRemoteCommand: function (evt) { switch (evt.detail.command) { case "DisableDataSubmission": this.onOptOut();
--- a/mobile/android/chrome/content/aboutLogins.js +++ b/mobile/android/chrome/content/aboutLogins.js @@ -1,29 +1,30 @@ /* Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ var Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils; Cu.import("resource://services-common/utils.js"); /*global: CommonUtils */ -Cu.import("resource://gre/modules/Messaging.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/TelemetryStopwatch.jsm"); XPCOMUtils.defineLazyGetter(window, "gChromeWin", () => window.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIWebNavigation) .QueryInterface(Ci.nsIDocShellTreeItem) .rootTreeItem .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindow) .QueryInterface(Ci.nsIDOMChromeWindow)); +XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher", + "resource://gre/modules/Messaging.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Snackbars", "resource://gre/modules/Snackbars.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Prompt", "resource://gre/modules/Prompt.jsm"); var debug = Cu.import("resource://gre/modules/AndroidLog.jsm", {}).AndroidLog.d.bind(null, "AboutLogins"); var gStringBundle = Services.strings.createBundle("chrome://browser/locale/aboutLogins.properties"); @@ -411,17 +412,17 @@ var Logins = { } }); } }); }, _loadFavicon: function (aImg, aHostname) { // Load favicon from cache. - Messaging.sendRequestForResult({ + EventDispatcher.instance.sendRequestForResult({ type: "Favicon:CacheLoad", url: aHostname, }).then(function(faviconUrl) { aImg.style.backgroundImage= "url('" + faviconUrl + "')"; aImg.style.visibility = "visible"; }, function(data) { debug("Favicon cache failure : " + data); aImg.style.visibility = "visible";
--- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -385,36 +385,35 @@ var BrowserApp = { Services.obs.addObserver(this, "Sanitize:ClearData", false); Services.obs.addObserver(this, "FullScreen:Exit", false); Services.obs.addObserver(this, "Passwords:Init", false); Services.obs.addObserver(this, "FormHistory:Init", false); Services.obs.addObserver(this, "android-get-pref", false); Services.obs.addObserver(this, "android-set-pref", false); Services.obs.addObserver(this, "gather-telemetry", false); Services.obs.addObserver(this, "keyword-search", false); - Services.obs.addObserver(this, "sessionstore-state-purge-complete", false); Services.obs.addObserver(this, "Fonts:Reload", false); Services.obs.addObserver(this, "Vibration:Request", false); Messaging.addListener(this.getHistory.bind(this), "Session:GetHistory"); window.addEventListener("fullscreen", function() { - Messaging.sendRequest({ + WindowEventDispatcher.sendRequest({ type: window.fullScreen ? "ToggleChrome:Hide" : "ToggleChrome:Show" }); }, false); window.addEventListener("fullscreenchange", (e) => { // This event gets fired on the document and its entire ancestor chain // of documents. When enabling fullscreen, it is fired on the top-level // document first and goes down; when disabling the order is reversed // (per spec). This means the last event on enabling will be for the innermost // document, which will have fullscreenElement set correctly. let doc = e.target; - Messaging.sendRequest({ + WindowEventDispatcher.sendRequest({ type: doc.fullscreenElement ? "DOMFullScreen:Start" : "DOMFullScreen:Stop", rootElement: doc.fullscreenElement == doc.documentElement }); if (this.fullscreenTransitionTab) { // Tab selection has changed during a fullscreen transition, handle it now. let tab = this.fullscreenTransitionTab; this.fullscreenTransitionTab = null; @@ -503,26 +502,26 @@ var BrowserApp = { console.log("browser.js: loading Firefox Accounts WebChannel"); Cu.import("resource://gre/modules/FxAccountsWebChannel.jsm"); EnsureFxAccountsWebChannel(); } else { console.log("browser.js: not loading Firefox Accounts WebChannel; this profile cannot connect to Firefox Accounts."); } // Notify Java that Gecko has loaded. - Messaging.sendRequest({ type: "Gecko:Ready" }); + GlobalEventDispatcher.sendRequest({ type: "Gecko:Ready" }); this.deck.addEventListener("DOMContentLoaded", function BrowserApp_delayedStartup() { BrowserApp.deck.removeEventListener("DOMContentLoaded", BrowserApp_delayedStartup, false); InitLater(() => Cu.import("resource://gre/modules/NotificationDB.jsm")); InitLater(() => Cu.import("resource://gre/modules/PresentationDeviceInfoManager.jsm")); InitLater(() => Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "")); - InitLater(() => Messaging.sendRequest({ type: "Gecko:DelayedStartup" })); + InitLater(() => GlobalEventDispatcher.sendRequest({ type: "Gecko:DelayedStartup" })); if (!AppConstants.RELEASE_OR_BETA) { InitLater(() => WebcompatReporter.init()); } // Collect telemetry data. // We do this at startup because we want to move away from "gather-telemetry" (bug 1127907) InitLater(() => { @@ -568,17 +567,17 @@ var BrowserApp = { return this._startupStatus; }, /** * Pass this a locale string, such as "fr" or "es_ES". */ setLocale: function (locale) { console.log("browser.js: requesting locale set: " + locale); - Messaging.sendRequest({ type: "Locale:Set", locale: locale }); + WindowEventDispatcher.sendRequest({ type: "Locale:Set", locale: locale }); }, initContextMenu: function () { // We pass a thunk in place of a raw label string. This allows the // context menu to automatically accommodate locale changes without // having to be rebuilt. let stringGetter = name => () => Strings.browser.GetStringFromName(name); @@ -721,43 +720,43 @@ var BrowserApp = { }); NativeWindow.contextmenus.add(stringGetter("contextmenu.addToContacts"), NativeWindow.contextmenus._disableRestricted("ADD_CONTACT", NativeWindow.contextmenus.emailLinkContext), function(aTarget) { UITelemetry.addEvent("action.1", "contextmenu", null, "web_contact_email"); let url = NativeWindow.contextmenus._getLinkURL(aTarget); - Messaging.sendRequest({ + WindowEventDispatcher.sendRequest({ type: "Contact:Add", email: url }); }); NativeWindow.contextmenus.add(stringGetter("contextmenu.addToContacts"), NativeWindow.contextmenus._disableRestricted("ADD_CONTACT", NativeWindow.contextmenus.phoneNumberLinkContext), function(aTarget) { UITelemetry.addEvent("action.1", "contextmenu", null, "web_contact_phone"); let url = NativeWindow.contextmenus._getLinkURL(aTarget); - Messaging.sendRequest({ + WindowEventDispatcher.sendRequest({ type: "Contact:Add", phone: url }); }); NativeWindow.contextmenus.add(stringGetter("contextmenu.bookmarkLink"), NativeWindow.contextmenus._disableRestricted("BOOKMARK", NativeWindow.contextmenus.linkBookmarkableContext), function(aTarget) { UITelemetry.addEvent("action.1", "contextmenu", null, "web_bookmark"); UITelemetry.addEvent("save.1", "contextmenu", null, "bookmark"); let url = NativeWindow.contextmenus._getLinkURL(aTarget); let title = aTarget.textContent || aTarget.title || url; - Messaging.sendRequest({ + WindowEventDispatcher.sendRequest({ type: "Bookmark:Insert", url: url, title: title }); }); NativeWindow.contextmenus.add(stringGetter("contextmenu.playMedia"), NativeWindow.contextmenus.mediaContext("media-paused"), @@ -884,17 +883,17 @@ var BrowserApp = { }); NativeWindow.contextmenus.add(stringGetter("contextmenu.setImageAs"), NativeWindow.contextmenus._disableRestricted("SET_IMAGE", NativeWindow.contextmenus.imageSaveableContext), function(aTarget) { UITelemetry.addEvent("action.1", "contextmenu", null, "web_background_image"); let src = aTarget.src; - Messaging.sendRequest({ + WindowEventDispatcher.sendRequest({ type: "Image:SetAs", url: src }); }); NativeWindow.contextmenus.add( function(aTarget) { if (aTarget instanceof HTMLVideoElement) { @@ -1751,17 +1750,17 @@ var BrowserApp = { // perform a keyword search on the selected tab. this.selectedTab.isSearch = true; // Don't store queries in private browsing mode. let isPrivate = PrivateBrowsingUtils.isBrowserPrivate(this.selectedTab.browser); let query = isPrivate ? "" : aData; let engine = aSubject.QueryInterface(Ci.nsISearchEngine); - Messaging.sendRequest({ + GlobalEventDispatcher.sendRequest({ type: "Search:Keyword", identifier: engine.identifier, name: engine.name, query: query }); break; case "Browser:Quit": @@ -1897,22 +1896,18 @@ var BrowserApp = { Services.appinfo.submitReports = value; aSubject.setAsEmpty(); } break; } break; } - case "sessionstore-state-purge-complete": - Messaging.sendRequest({ type: "Session:StatePurged" }); - break; - case "gather-telemetry": - Messaging.sendRequest({ type: "Telemetry:Gather" }); + GlobalEventDispatcher.sendRequest({ type: "Telemetry:Gather" }); break; case "Locale:OS": // We know the system locale. We use this for generating Accept-Language headers. console.log("Locale:OS: " + aData); let currentOSLocale = this.getOSLocalePref(); if (currentOSLocale == aData) { break; @@ -2174,31 +2169,31 @@ var NativeWindow = { }; } else { throw "Incorrect number of parameters"; } options.type = "Menu:Add"; options.id = this._menuId; - Messaging.sendRequest(options); + GlobalEventDispatcher.sendRequest(options); this._callbacks[this._menuId] = options.callback; this._menuId++; return this._menuId - 1; }, remove: function(aId) { - Messaging.sendRequest({ type: "Menu:Remove", id: aId }); + GlobalEventDispatcher.sendRequest({ type: "Menu:Remove", id: aId }); }, update: function(aId, aOptions) { if (!aOptions) return; - Messaging.sendRequest({ + GlobalEventDispatcher.sendRequest({ type: "Menu:Update", id: aId, options: aOptions }); } }, doorhanger: { @@ -3499,17 +3494,17 @@ Tab.prototype = { ? aParams.selected !== false || aParams.cancelEditMode === true : true, cancelEditMode: aParams.cancelEditMode === true, title: truncate(title, MAX_TITLE_LENGTH), delayLoad: aParams.delayLoad || false, desktopMode: this.desktopMode, isPrivate: isPrivate, stub: stub }; - Messaging.sendRequest(message); + GlobalEventDispatcher.sendRequest(message); } let flags = Ci.nsIWebProgress.NOTIFY_STATE_ALL | Ci.nsIWebProgress.NOTIFY_LOCATION | Ci.nsIWebProgress.NOTIFY_SECURITY; this.filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"].createInstance(Ci.nsIWebProgress); this.filter.addProgressListener(this, flags) this.browser.addProgressListener(this.filter, flags); @@ -4530,17 +4525,17 @@ var BrowserEventHandler = { } break; case 'MozMouseHittest': this._handleRetargetedTouchStart(aEvent); break; case 'OpenMediaWithExternalApp': { let mediaSrc = aEvent.target.currentSrc || aEvent.target.src; let uuid = uuidgen.generateUUID().toString(); - Services.androidBridge.handleGeckoMessage({ + GlobalEventDispatcher.sendRequest({ type: "Video:Play", uri: mediaSrc, uuid: uuid }); break; } } }, @@ -5627,17 +5622,17 @@ var CharacterEncoding = { }, sendState: function sendState() { let showCharEncoding = "false"; try { showCharEncoding = Services.prefs.getComplexValue("browser.menu.showCharacterEncoding", Ci.nsIPrefLocalizedString).data; } catch (e) { /* Optional */ } - Messaging.sendRequest({ + GlobalEventDispatcher.sendRequest({ type: "CharEncoding:State", visible: showCharEncoding }); }, getEncoding: function getEncoding() { function infoToCharset(info) { return { code: info.value, title: info.label }; @@ -5661,17 +5656,17 @@ var CharacterEncoding = { for (let i = 0; i < charsetCount; i++) { if (this._charsets[i].code === docCharset) { selected = i; break; } } - Messaging.sendRequest({ + GlobalEventDispatcher.sendRequest({ type: "CharEncoding:Data", charsets: this._charsets, selected: selected }); }, setEncoding: function setEncoding(aEncoding) { let browser = BrowserApp.selectedBrowser; @@ -6329,17 +6324,17 @@ var Telemetry = { var Experiments = { // Enable malware download protection (bug 936041) MALWARE_DOWNLOAD_PROTECTION: "malware-download-protection", // Try to load pages from disk cache when network is offline (bug 935190) OFFLINE_CACHE: "offline-cache", init() { - Messaging.sendRequestForResult({ + GlobalEventDispatcher.sendRequestForResult({ type: "Experiments:GetActive" }).then(experiments => { let names = JSON.parse(experiments); for (let name of names) { switch (name) { case this.MALWARE_DOWNLOAD_PROTECTION: { // Apply experiment preferences on the default branch. This allows // us to avoid migrating user prefs when experiments are enabled/disabled, @@ -6356,25 +6351,25 @@ var Experiments = { continue; } } } }); }, setOverride(name, isEnabled) { - Messaging.sendRequest({ + GlobalEventDispatcher.sendRequest({ type: "Experiments:SetOverride", name: name, isEnabled: isEnabled }); }, clearOverride(name) { - Messaging.sendRequest({ + GlobalEventDispatcher.sendRequest({ type: "Experiments:ClearOverride", name: name }); } }; var ExternalApps = { _contextMenuId: null,
--- a/mobile/android/chrome/content/geckoview.js +++ b/mobile/android/chrome/content/geckoview.js @@ -9,24 +9,18 @@ var Cu = Components.utils; var Cr = Components.results; Cu.import("resource://gre/modules/AppConstants.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Log", "resource://gre/modules/AndroidLog.jsm", "AndroidLog"); -XPCOMUtils.defineLazyModuleGetter(this, "Messaging", - "resource://gre/modules/Messaging.jsm", "Messaging"); - XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm", "Services"); function dump(msg) { Log.d("View", msg); } function startup() { dump("zerdatime " + Date.now() + " - geckoivew chrome startup finished."); - - // Notify Java that Gecko has loaded. - Messaging.sendRequest({ type: "Gecko:Ready" }); }
--- a/mobile/android/components/HelperAppDialog.js +++ b/mobile/android/components/HelperAppDialog.js @@ -21,17 +21,17 @@ Cu.import("resource://gre/modules/Downlo Cu.import("resource://gre/modules/FileUtils.jsm"); Cu.import("resource://gre/modules/HelperApps.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/NetUtil.jsm"); Cu.import("resource://gre/modules/Task.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "RuntimePermissions", "resource://gre/modules/RuntimePermissions.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Messaging", "resource://gre/modules/Messaging.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher", "resource://gre/modules/Messaging.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Snackbars", "resource://gre/modules/Snackbars.jsm"); // ----------------------------------------------------------------------- // HelperApp Launcher Dialog // ----------------------------------------------------------------------- XPCOMUtils.defineLazyGetter(this, "ContentAreaUtils", function() { let ContentAreaUtils = {}; @@ -234,17 +234,17 @@ HelperAppLauncherDialog.prototype = { }, _downloadWithAndroidDownloadManager(aLauncher) { let mimeType = aLauncher.MIMEInfo.MIMEType; if (!mimeType) { mimeType = ContentAreaUtils.getMIMETypeForURI(aLauncher.source) || ""; } - Messaging.sendRequest({ + EventDispatcher.instance.sendRequest({ 'type': 'Download:AndroidDownloadManager', 'uri': aLauncher.source.spec, 'mimeType': mimeType, 'filename': aLauncher.suggestedFileName }); }, _getPrefName: function getPrefName(mimetype) {
--- a/mobile/android/components/SessionStore.js +++ b/mobile/android/components/SessionStore.js @@ -306,17 +306,18 @@ SessionStore.prototype = { break; } case "Tabs:OpenMultiple": { let data = JSON.parse(aData); this._openTabs(data); if (data.shouldNotifyTabsOpenedToJava) { - Messaging.sendRequest({ + let window = Services.wm.getMostRecentWindow("navigator:browser"); + window.WindowEventDispatcher.sendRequest({ type: "Tabs:TabsOpened" }); } break; } case "Tab:KeepZombified": { if (aData >= 0) { this._keepAsZombieTabId = aData; @@ -974,17 +975,18 @@ SessionStore.prototype = { normalData.windows[0].tabs.length + " tabs in window[0]"); } else { log("_saveState() writing empty normal data"); } this._writeFile(this._sessionFile, this._sessionFileTemp, normalData, aAsync); // If we have private data, send it to Java; otherwise, send null to // indicate that there is no private data - Messaging.sendRequest({ + let window = Services.wm.getMostRecentWindow("navigator:browser"); + window.WindowEventDispatcher.sendRequest({ type: "PrivateBrowsing:Data", session: (privateData.windows.length > 0 && privateData.windows[0].tabs.length > 0) ? JSON.stringify(privateData) : null }); this._lastSaveTime = Date.now(); }, _getCurrentState: function ss_getCurrentState() {
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java @@ -101,16 +101,19 @@ public final class EventDispatcher exten private <T> void registerListener(final Class<?> listType, final Map<String, List<T>> listenersMap, final T listener, final String[] events) { try { synchronized (listenersMap) { for (final String event : events) { + if (event == null) { + continue; + } List<T> listeners = listenersMap.get(event); if (listeners == null) { // Java doesn't let us put Class<? extends List<T>> as the type for listType. @SuppressWarnings("unchecked") final Class<? extends List<T>> type = (Class) listType; listeners = type.newInstance(); listenersMap.put(event, listeners); } @@ -151,16 +154,19 @@ public final class EventDispatcher exten } } private <T> void unregisterListener(final Map<String, List<T>> listenersMap, final T listener, final String[] events) { synchronized (listenersMap) { for (final String event : events) { + if (event == null) { + continue; + } List<T> listeners = listenersMap.get(event); if ((listeners == null || !listeners.remove(listener)) && !AppConstants.RELEASE_OR_BETA) { throw new IllegalArgumentException(event + " was not registered"); } } } }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAccessibility.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAccessibility.java @@ -1,19 +1,19 @@ /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko; -import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.mozilla.gecko.AppConstants.Versions; +import org.mozilla.gecko.util.GeckoBundle; import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.gecko.util.UIAsyncTask; import android.content.Context; import android.graphics.Rect; import android.os.Bundle; import android.util.Log; import android.view.View; @@ -29,17 +29,17 @@ import com.googlecode.eyesfree.braille.s public class GeckoAccessibility { private static final String LOGTAG = "GeckoAccessibility"; private static final int VIRTUAL_ENTRY_POINT_BEFORE = 1; private static final int VIRTUAL_CURSOR_POSITION = 2; private static final int VIRTUAL_ENTRY_POINT_AFTER = 3; private static boolean sEnabled; // Used to store the JSON message and populate the event later in the code path. - private static JSONObject sHoverEnter; + private static GeckoBundle sHoverEnter; private static AccessibilityNodeInfo sVirtualCursorNode; private static int sCurrentNode; // This is the number Brailleback uses to start indexing routing keys. private static final int BRAILLE_CLICK_BASE_INDEX = -275000000; private static SelfBrailleClient sSelfBrailleClient; public static void updateAccessibilitySettings (final Context context) { @@ -71,42 +71,42 @@ public class GeckoAccessibility { if (geckoInterface == null) { return; } geckoInterface.setAccessibilityEnabled(sEnabled); } }.execute(); } - private static void populateEventFromJSON (AccessibilityEvent event, JSONObject message) { - final JSONArray textArray = message.optJSONArray("text"); + private static void populateEventFromJSON (AccessibilityEvent event, GeckoBundle message) { + final String[] textArray = message.getStringArray("text"); if (textArray != null) { - for (int i = 0; i < textArray.length(); i++) - event.getText().add(textArray.optString(i)); + for (int i = 0; i < textArray.length; i++) + event.getText().add(textArray[i]); } - event.setContentDescription(message.optString("description")); - event.setEnabled(message.optBoolean("enabled", true)); - event.setChecked(message.optBoolean("checked")); - event.setPassword(message.optBoolean("password")); - event.setAddedCount(message.optInt("addedCount", -1)); - event.setRemovedCount(message.optInt("removedCount", -1)); - event.setFromIndex(message.optInt("fromIndex", -1)); - event.setItemCount(message.optInt("itemCount", -1)); - event.setCurrentItemIndex(message.optInt("currentItemIndex", -1)); - event.setBeforeText(message.optString("beforeText")); - event.setToIndex(message.optInt("toIndex", -1)); - event.setScrollable(message.optBoolean("scrollable")); - event.setScrollX(message.optInt("scrollX", -1)); - event.setScrollY(message.optInt("scrollY", -1)); - event.setMaxScrollX(message.optInt("maxScrollX", -1)); - event.setMaxScrollY(message.optInt("maxScrollY", -1)); + event.setContentDescription(message.getString("description")); + event.setEnabled(message.getBoolean("enabled", true)); + event.setChecked(message.getBoolean("checked")); + event.setPassword(message.getBoolean("password")); + event.setAddedCount(message.getInt("addedCount", -1)); + event.setRemovedCount(message.getInt("removedCount", -1)); + event.setFromIndex(message.getInt("fromIndex", -1)); + event.setItemCount(message.getInt("itemCount", -1)); + event.setCurrentItemIndex(message.getInt("currentItemIndex", -1)); + event.setBeforeText(message.getString("beforeText")); + event.setToIndex(message.getInt("toIndex", -1)); + event.setScrollable(message.getBoolean("scrollable")); + event.setScrollX(message.getInt("scrollX", -1)); + event.setScrollY(message.getInt("scrollY", -1)); + event.setMaxScrollX(message.getInt("maxScrollX", -1)); + event.setMaxScrollY(message.getInt("maxScrollY", -1)); } - private static void sendDirectAccessibilityEvent(int eventType, JSONObject message) { + private static void sendDirectAccessibilityEvent(int eventType, GeckoBundle message) { final Context context = GeckoAppShell.getApplicationContext(); final AccessibilityEvent accEvent = AccessibilityEvent.obtain(eventType); accEvent.setClassName(GeckoAccessibility.class.getName()); accEvent.setPackageName(context.getPackageName()); populateEventFromJSON(accEvent, message); AccessibilityManager accessibilityManager = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE); try { @@ -115,34 +115,34 @@ public class GeckoAccessibility { // Accessibility is off. } } public static boolean isEnabled() { return sEnabled; } - public static void sendAccessibilityEvent (final JSONObject message) { + public static void sendAccessibilityEvent(final GeckoBundle message) { if (!sEnabled) return; - final int eventType = message.optInt("eventType", -1); + final int eventType = message.getInt("eventType", -1); if (eventType < 0) { Log.e(LOGTAG, "No accessibility event type provided"); return; } sendAccessibilityEvent(message, eventType); } - public static void sendAccessibilityEvent (final JSONObject message, final int eventType) { + public static void sendAccessibilityEvent(final GeckoBundle message, final int eventType) { if (!sEnabled) return; - final String exitView = message.optString("exitView"); + final String exitView = message.getString("exitView"); if (exitView.equals("moveNext")) { sCurrentNode = VIRTUAL_ENTRY_POINT_AFTER; } else if (exitView.equals("movePrevious")) { sCurrentNode = VIRTUAL_ENTRY_POINT_BEFORE; } else { sCurrentNode = VIRTUAL_CURSOR_POSITION; } @@ -157,51 +157,52 @@ public class GeckoAccessibility { }); } else { // In Jelly Bean we populate an AccessibilityNodeInfo with the minimal amount of data to have // it work with TalkBack. final View view = GeckoAppShell.getLayerView(); if (view == null) return; - if (sVirtualCursorNode == null) + if (sVirtualCursorNode == null) { sVirtualCursorNode = AccessibilityNodeInfo.obtain(view, VIRTUAL_CURSOR_POSITION); - sVirtualCursorNode.setEnabled(message.optBoolean("enabled", true)); - sVirtualCursorNode.setClickable(message.optBoolean("clickable")); - sVirtualCursorNode.setCheckable(message.optBoolean("checkable")); - sVirtualCursorNode.setChecked(message.optBoolean("checked")); - sVirtualCursorNode.setPassword(message.optBoolean("password")); + } + sVirtualCursorNode.setEnabled(message.getBoolean("enabled", true)); + sVirtualCursorNode.setClickable(message.getBoolean("clickable")); + sVirtualCursorNode.setCheckable(message.getBoolean("checkable")); + sVirtualCursorNode.setChecked(message.getBoolean("checked")); + sVirtualCursorNode.setPassword(message.getBoolean("password")); - final JSONArray textArray = message.optJSONArray("text"); + final String[] textArray = message.getStringArray("text"); StringBuilder sb = new StringBuilder(); - if (textArray != null && textArray.length() > 0) { - sb.append(textArray.optString(0)); - for (int i = 1; i < textArray.length(); i++) { - sb.append(" ").append(textArray.optString(i)); + if (textArray != null && textArray.length > 0) { + sb.append(textArray[0]); + for (int i = 1; i < textArray.length; i++) { + sb.append(" ").append(textArray[i]); } sVirtualCursorNode.setText(sb.toString()); } - sVirtualCursorNode.setContentDescription(message.optString("description")); + sVirtualCursorNode.setContentDescription(message.getString("description")); - JSONObject bounds = message.optJSONObject("bounds"); + final GeckoBundle bounds = message.getBundle("bounds"); if (bounds != null) { - Rect relativeBounds = new Rect(bounds.optInt("left"), bounds.optInt("top"), - bounds.optInt("right"), bounds.optInt("bottom")); + Rect relativeBounds = new Rect(bounds.getInt("left"), bounds.getInt("top"), + bounds.getInt("right"), bounds.getInt("bottom")); sVirtualCursorNode.setBoundsInParent(relativeBounds); int[] locationOnScreen = new int[2]; view.getLocationOnScreen(locationOnScreen); Rect screenBounds = new Rect(relativeBounds); screenBounds.offset(locationOnScreen[0], locationOnScreen[1]); sVirtualCursorNode.setBoundsInScreen(screenBounds); } - final JSONObject braille = message.optJSONObject("brailleOutput"); + final GeckoBundle braille = message.getBundle("brailleOutput"); if (braille != null) { - sendBrailleText(view, braille.optString("text"), - braille.optInt("selectionStart"), braille.optInt("selectionEnd")); + sendBrailleText(view, braille.getString("text"), + braille.getInt("selectionStart"), braille.getInt("selectionEnd")); } if (eventType == AccessibilityEvent.TYPE_VIEW_HOVER_ENTER) { sHoverEnter = message; } ThreadUtils.postToUiThread(new Runnable() { @Override
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java @@ -546,23 +546,17 @@ public class GeckoThread extends Thread } // And go. GeckoLoader.nativeRun(args, mCrashFileDescriptor, mIPCFileDescriptor); // And... we're done. setState(State.EXITED); - try { - final JSONObject msg = new JSONObject(); - msg.put("type", "Gecko:Exited"); - EventDispatcher.getInstance().dispatchEvent(msg, null); - } catch (final JSONException e) { - Log.e(LOGTAG, "unable to dispatch event", e); - } + EventDispatcher.getInstance().dispatch("Gecko:Exited", null); // Remove pumpMessageLoop() idle handler Looper.myQueue().removeIdleHandler(idleHandler); } @WrapForJNI(calledFrom = "gecko") private static boolean pumpMessageLoop(final Message msg) { final Handler geckoHandler = ThreadUtils.sGeckoHandler;
--- a/mobile/android/modules/LightweightThemeConsumer.jsm +++ b/mobile/android/modules/LightweightThemeConsumer.jsm @@ -3,16 +3,20 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var EXPORTED_SYMBOLS = ["LightweightThemeConsumer"]; var Cc = Components.classes; var Ci = Components.interfaces; Components.utils.import("resource://gre/modules/Services.jsm"); Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm"); +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher", + "resource://gre/modules/Messaging.jsm"); function LightweightThemeConsumer(aDocument) { this._doc = aDocument; Services.obs.addObserver(this, "lightweight-theme-styling-update", false); Services.obs.addObserver(this, "lightweight-theme-apply", false); this._update(LightweightThemeManager.currentThemeForDisplay); } @@ -34,11 +38,11 @@ LightweightThemeConsumer.prototype = { _update: function (aData) { if (!aData) aData = { headerURL: "", footerURL: "", textcolor: "", accentcolor: "" }; let active = !!aData.headerURL; let msg = active ? { type: "LightweightTheme:Update", data: aData } : { type: "LightweightTheme:Disable" }; - Services.androidBridge.handleGeckoMessage(msg); + EventDispatcher.instance.sendRequest(msg); } }
--- a/mobile/android/modules/RuntimePermissions.jsm +++ b/mobile/android/modules/RuntimePermissions.jsm @@ -3,16 +3,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; const { classes: Cc, interfaces: Ci, utils: Cu } = Components; this.EXPORTED_SYMBOLS = ["RuntimePermissions"]; +Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Messaging", "resource://gre/modules/Messaging.jsm"); // See: http://developer.android.com/reference/android/Manifest.permission.html const CAMERA = "android.permission.CAMERA"; const WRITE_EXTERNAL_STORAGE = "android.permission.WRITE_EXTERNAL_STORAGE"; const RECORD_AUDIO = "android.permission.RECORD_AUDIO"; @@ -31,11 +32,12 @@ var RuntimePermissions = { waitForPermissions: function(permission) { let permissions = [].concat(permission); let msg = { type: 'RuntimePermissions:Prompt', permissions: permissions }; - return Messaging.sendRequestForResult(msg); + let window = Services.wm.getMostRecentWindow("navigator:browser"); + return window.WindowEventDispatcher.sendRequestForResult(msg); } -}; \ No newline at end of file +};
--- a/mobile/android/modules/Sanitizer.jsm +++ b/mobile/android/modules/Sanitizer.jsm @@ -8,24 +8,25 @@ var Cc = Components.classes; var Ci = Components.interfaces; var Cu = Components.utils; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/LoadContextInfo.jsm"); Cu.import("resource://gre/modules/FormHistory.jsm"); -Cu.import("resource://gre/modules/Messaging.jsm"); Cu.import("resource://gre/modules/Task.jsm"); Cu.import("resource://gre/modules/Downloads.jsm"); Cu.import("resource://gre/modules/osfile.jsm"); Cu.import("resource://gre/modules/Accounts.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "DownloadIntegration", "resource://gre/modules/DownloadIntegration.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher", + "resource://gre/modules/Messaging.jsm"); function dump(a) { Services.console.logStringMessage(a); } this.EXPORTED_SYMBOLS = ["Sanitizer"]; function Sanitizer() {} @@ -139,17 +140,17 @@ Sanitizer.prototype = { { return true; } }, history: { clear: function () { - return Messaging.sendRequestForResult({ type: "Sanitize:ClearHistory" }) + return EventDispatcher.instance.sendRequestForResult({ type: "Sanitize:ClearHistory" }) .catch(e => Cu.reportError("Java-side history clearing failed: " + e)) .then(function() { try { Services.obs.notifyObservers(null, "browser:purge-session-history", ""); } catch (e) { } try { @@ -165,17 +166,17 @@ Sanitizer.prototype = { // the browser:purge-session-history notification. (like error console) return true; } }, searchHistory: { clear: function () { - return Messaging.sendRequestForResult({ type: "Sanitize:ClearHistory", clearSearchHistory: true }) + return EventDispatcher.instance.sendRequestForResult({ type: "Sanitize:ClearHistory", clearSearchHistory: true }) .catch(e => Cu.reportError("Java-side search history clearing failed: " + e)) }, get canClear() { return true; } }, @@ -278,17 +279,17 @@ Sanitizer.prototype = { { return true; } }, syncedTabs: { clear: function () { - return Messaging.sendRequestForResult({ type: "Sanitize:ClearSyncedTabs" }) + return EventDispatcher.instance.sendRequestForResult({ type: "Sanitize:ClearSyncedTabs" }) .catch(e => Cu.reportError("Java-side synced tabs clearing failed: " + e)); }, canClear: function(aCallback) { Accounts.anySyncAccountsExist().then(aCallback) .catch(function(err) { Cu.reportError("Java-side synced tabs clearing failed: " + err)
--- a/mobile/android/modules/Snackbars.jsm +++ b/mobile/android/modules/Snackbars.jsm @@ -5,17 +5,17 @@ "use strict"; const { classes: Cc, interfaces: Ci, utils: Cu } = Components; this.EXPORTED_SYMBOLS = ["Snackbars"]; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Messaging", "resource://gre/modules/Messaging.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher", "resource://gre/modules/Messaging.jsm"); const LENGTH_INDEFINITE = -2; const LENGTH_LONG = 0; const LENGTH_SHORT = -1; var Snackbars = { LENGTH_INDEFINITE: LENGTH_INDEFINITE, LENGTH_LONG: LENGTH_LONG, @@ -40,19 +40,19 @@ var Snackbars = { if (aOptions && aOptions.action) { msg.action = {}; if (aOptions.action.label) { msg.action.label = aOptions.action.label; } - Messaging.sendRequestForResult(msg).then(result => aOptions.action.callback()); + EventDispatcher.instance.sendRequestForResult(msg).then(result => aOptions.action.callback()); } else { - Messaging.sendRequest(msg); + EventDispatcher.instance.sendRequest(msg); } } }; function migrateToastIfNeeded(aDuration, aOptions) { let duration; if (aDuration === "long") { duration = LENGTH_LONG; @@ -64,9 +64,9 @@ function migrateToastIfNeeded(aDuration, let options = {}; if (aOptions && aOptions.button) { options.action = { label: aOptions.button.label, callback: () => aOptions.button.callback(), }; } return [duration, options]; -} \ No newline at end of file +}
--- a/mobile/android/modules/WebsiteMetadata.jsm +++ b/mobile/android/modules/WebsiteMetadata.jsm @@ -5,17 +5,17 @@ 'use strict'; const { classes: Cc, interfaces: Ci, utils: Cu } = Components; this.EXPORTED_SYMBOLS = ["WebsiteMetadata"]; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Messaging", "resource://gre/modules/Messaging.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher", "resource://gre/modules/Messaging.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm"); var WebsiteMetadata = { /** * Asynchronously parse the document extract metadata. A 'Website:Metadata' event with the metadata * will be sent. */ parseAsynchronously: function(doc) { @@ -27,20 +27,21 @@ var WebsiteMetadata = { // No metadata was extracted, so don't bother sending it. if (Object.keys(metadata).length === 0) { return; } let msg = { type: 'Website:Metadata', location: doc.location.href, - metadata: metadata, + hasImage: metadata.image_url && metadata.image_url !== "", + metadata: JSON.stringify(metadata), }; - Messaging.sendRequest(msg); + EventDispatcher.instance.sendRequest(msg); }); } }; // ################################################################################################# // # Modified version of makeUrlAbsolute() to not import url parser library (and dependencies) // #################################################################################################
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/BaseTest.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/BaseTest.java @@ -63,27 +63,29 @@ abstract class BaseTest extends BaseRobo public Device mDevice; protected DatabaseHelper mDatabaseHelper; protected int mScreenMidWidth; protected int mScreenMidHeight; private final HashSet<Integer> mKnownTabIDs = new HashSet<Integer>(); protected void blockForDelayedStartup() { try { - Actions.EventExpecter delayedStartupExpector = mActions.expectGeckoEvent("Gecko:DelayedStartup"); + Actions.EventExpecter delayedStartupExpector = + mActions.expectGlobalEvent(Actions.EventType.UI, "Gecko:DelayedStartup"); delayedStartupExpector.blockForEvent(GECKO_READY_WAIT_MS, true); delayedStartupExpector.unregisterListener(); } catch (Exception e) { mAsserter.dumpLog("Exception in blockForDelayedStartup", e); } } protected void blockForGeckoReady() { try { - Actions.EventExpecter geckoReadyExpector = mActions.expectGeckoEvent("Gecko:Ready"); + Actions.EventExpecter geckoReadyExpector = + mActions.expectGlobalEvent(Actions.EventType.GECKO, "Gecko:Ready"); if (!GeckoThread.isRunning()) { geckoReadyExpector.blockForEvent(GECKO_READY_WAIT_MS, true); } geckoReadyExpector.unregisterListener(); } catch (Exception e) { mAsserter.dumpLog("Exception in blockForGeckoReady", e); } }
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/ContentContextMenuTest.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/ContentContextMenuTest.java @@ -43,17 +43,17 @@ abstract class ContentContextMenuTest ex mAsserter.ok(mSolo.searchText(option), "Checking that the option: " + option + " is available", "The option is available"); } } protected void openTabFromContextMenu(String contextMenuOption, int expectedTabCount) { if (!mSolo.searchText(contextMenuOption)) { openWebContentContextMenu(contextMenuOption); // Open the context menu if it is not already } - Actions.EventExpecter tabEventExpecter = mActions.expectGeckoEvent("Tab:Added"); + Actions.EventExpecter tabEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added"); mSolo.clickOnText(contextMenuOption); tabEventExpecter.blockForEvent(); tabEventExpecter.unregisterListener(); verifyTabCount(expectedTabCount); } protected void verifyTabs(String[] items) { if (!mSolo.searchText(items[0])) {
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/helpers/GeckoHelper.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/helpers/GeckoHelper.java @@ -21,29 +21,29 @@ public final class GeckoHelper { private GeckoHelper() { /* To disallow instantiation. */ } protected static void init(final UITestContext context) { sActivity = context.getActivity(); sActions = context.getActions(); } public static void blockForReady() { - blockForEvent("Gecko:Ready"); + blockForEvent(Actions.EventType.GECKO, "Gecko:Ready"); } /** * Blocks for the "Gecko:DelayedStartup" event, which occurs after "Gecko:Ready" and the * first page load. */ public static void blockForDelayedStartup() { - blockForEvent("Gecko:DelayedStartup"); + blockForEvent(Actions.EventType.UI, "Gecko:DelayedStartup"); } - private static void blockForEvent(final String eventName) { - final EventExpecter eventExpecter = sActions.expectGeckoEvent(eventName); + private static void blockForEvent(final Actions.EventType type, final String eventName) { + final EventExpecter eventExpecter = sActions.expectGlobalEvent(type, eventName); if (!GeckoThread.isRunning()) { eventExpecter.blockForEvent(); } eventExpecter.unregisterListener(); } }
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAboutPage.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAboutPage.java @@ -24,17 +24,17 @@ public class testAboutPage extends Pixel // Open a new page to remove the about: page from the current tab. url = getAbsoluteUrl(mStringHelper.ROBOCOP_BLANK_PAGE_01_URL); loadUrlAndWait(url); // At this point the page title should have been set. verifyUrlInContentDescription(url); // Set up listeners to catch the page load we're about to do. - Actions.EventExpecter tabEventExpecter = mActions.expectGeckoEvent("Tab:Added"); + Actions.EventExpecter tabEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added"); Actions.EventExpecter contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded"); selectSettingsItem(mStringHelper.MOZILLA_SECTION_LABEL, mStringHelper.ABOUT_LABEL); // Wait for the new tab and page to load tabEventExpecter.blockForEvent(); contentEventExpecter.blockForEvent();
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAddonManager.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAddonManager.java @@ -24,17 +24,17 @@ public class testAddonManager extends Pi final String aboutAddonsURL = mStringHelper.ABOUT_ADDONS_URL; blockForGeckoReady(); // Use the menu to open the Addon Manger selectMenuItem(mStringHelper.ADDONS_LABEL); // Set up listeners to catch the page load we're about to do - tabEventExpecter = mActions.expectGeckoEvent("Tab:Added"); + tabEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added"); contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded"); // Wait for the new tab and page to load tabEventExpecter.blockForEvent(); contentEventExpecter.blockForEvent(); tabEventExpecter.unregisterListener(); contentEventExpecter.unregisterListener(); @@ -45,17 +45,17 @@ public class testAddonManager extends Pi // Close the Add-on Manager mSolo.goBack(); // Load the about:addons page and verify it was loaded loadAndPaint(aboutAddonsURL); verifyUrlBarTitle(aboutAddonsURL); // Setup wait for tab to spawn and load - tabEventExpecter = mActions.expectGeckoEvent("Tab:Added"); + tabEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added"); contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded"); // Open a new tab final String blankURL = getAbsoluteUrl(mStringHelper.ROBOCOP_BLANK_PAGE_01_URL); addTab(blankURL); // Wait for the new tab and page to load tabEventExpecter.blockForEvent();
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testBookmarksPanel.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testBookmarksPanel.java @@ -1,25 +1,24 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.tests; -import org.json.JSONException; -import org.json.JSONObject; import org.mozilla.gecko.Actions; import org.mozilla.gecko.Element; import org.mozilla.gecko.R; +import org.mozilla.gecko.util.GeckoBundle; import org.mozilla.gecko.util.StringUtils; public class testBookmarksPanel extends AboutHomeTest { public void testBookmarksPanel() { final String BOOKMARK_URL = getAbsoluteUrl(mStringHelper.ROBOCOP_BLANK_PAGE_01_URL); - JSONObject data = null; + GeckoBundle data = null; // Make sure our default bookmarks are loaded. // Technically this will race with the check below. initializeProfile(); // Add a mobile bookmark. mDatabaseHelper.addMobileBookmark(mStringHelper.ROBOCOP_BLANK_PAGE_01_TITLE, BOOKMARK_URL); @@ -35,53 +34,37 @@ public class testBookmarksPanel extends assertAllContextMenuOptionsArePresent(mStringHelper.DEFAULT_BOOKMARKS_URLS[1], mStringHelper.DEFAULT_BOOKMARKS_URLS[0]); openBookmarkContextMenu(mStringHelper.DEFAULT_BOOKMARKS_URLS[0]); // Test that "Open in New Tab" works final Element tabCount = mDriver.findElement(getActivity(), R.id.tabs_counter); final int tabCountInt = Integer.parseInt(tabCount.getText()); - Actions.EventExpecter tabEventExpecter = mActions.expectGeckoEvent("Tab:Added"); + Actions.EventExpecter tabEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added"); mSolo.clickOnText(mStringHelper.BOOKMARK_CONTEXT_MENU_ITEMS[0]); - try { - data = new JSONObject(tabEventExpecter.blockForEventData()); - } catch (JSONException e) { - mAsserter.ok(false, "exception getting event data", e.toString()); - } + + data = tabEventExpecter.blockForBundle(); tabEventExpecter.unregisterListener(); mAsserter.ok(mSolo.searchText(mStringHelper.TITLE_PLACE_HOLDER), "Checking that the tab is not changed", "The tab was not changed"); // extra check here on the Tab:Added message to be sure the right tab opened - int tabID = 0; - try { - mAsserter.is(mStringHelper.ABOUT_FIREFOX_URL, data.getString("uri"), "Checking tab uri"); - tabID = data.getInt("tabID"); - } catch (JSONException e) { - mAsserter.ok(false, "exception accessing event data", e.toString()); - } + mAsserter.is(mStringHelper.ABOUT_FIREFOX_URL, data.getString("uri"), "Checking tab uri"); // close tab so about:firefox can be selected again - closeTab(tabID); + closeTab(data.getInt("tabID")); // Test that "Open in Private Tab" works openBookmarkContextMenu(mStringHelper.DEFAULT_BOOKMARKS_URLS[0]); - tabEventExpecter = mActions.expectGeckoEvent("Tab:Added"); + tabEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added"); mSolo.clickOnText(mStringHelper.BOOKMARK_CONTEXT_MENU_ITEMS[1]); - try { - data = new JSONObject(tabEventExpecter.blockForEventData()); - } catch (JSONException e) { - mAsserter.ok(false, "exception getting event data", e.toString()); - } + + data = tabEventExpecter.blockForBundle(); tabEventExpecter.unregisterListener(); mAsserter.ok(mSolo.searchText(mStringHelper.TITLE_PLACE_HOLDER), "Checking that the tab is not changed", "The tab was not changed"); // extra check here on the Tab:Added message to be sure the right tab opened, again - try { - mAsserter.is(mStringHelper.ABOUT_FIREFOX_URL, data.getString("uri"), "Checking tab uri"); - } catch (JSONException e) { - mAsserter.ok(false, "exception accessing event data", e.toString()); - } + mAsserter.is(mStringHelper.ABOUT_FIREFOX_URL, data.getString("uri"), "Checking tab uri"); // Test that "Edit" works String[] editedBookmarkValues = new String[] { "New bookmark title", "www.NewBookmark.url", "newBookmarkKeyword" }; editBookmark(BOOKMARK_URL, editedBookmarkValues); checkBookmarkEdit(editedBookmarkValues[1], editedBookmarkValues); // Test that "Remove" works openBookmarkContextMenu(editedBookmarkValues[1]);
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testDoorHanger.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testDoorHanger.java @@ -2,20 +2,18 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.tests; import android.widget.CheckBox; import android.view.View; import com.robotium.solo.Condition; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; import org.mozilla.gecko.Actions; +import org.mozilla.gecko.util.GeckoBundle; /* This test will test if doorhangers are displayed and dismissed The test will test: * geolocation doorhangers - sharing and not sharing the location dismisses the doorhanger * opening a new tab hides the doorhanger * offline storage permission doorhangers - allowing and not allowing offline storage dismisses the doorhanger * Password Manager doorhangers - Remember and Not Now options dismiss the doorhanger */ @@ -128,36 +126,32 @@ public class testDoorHanger extends Base setPreferenceAndWaitForChange("dom.disable_open_during_load", true); // Load page with popup loadUrlAndWait(POPUP_URL); waitForText(mStringHelper.POPUP_MESSAGE); mAsserter.is(mSolo.searchText(mStringHelper.POPUP_MESSAGE), true, "Popup blocker is displayed"); // Wait for the popup to be shown. - Actions.EventExpecter tabEventExpecter = mActions.expectGeckoEvent("Tab:Added"); + Actions.EventExpecter tabEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added"); waitForCheckBox(); mSolo.clickOnCheckBox(0); mSolo.clickOnButton(mStringHelper.POPUP_ALLOW); waitForTextDismissed(mStringHelper.POPUP_MESSAGE); mAsserter.is(mSolo.searchText(mStringHelper.POPUP_MESSAGE), false, "Popup blocker is hidden when popup allowed"); - try { - final JSONObject data = new JSONObject(tabEventExpecter.blockForEventData()); - - // Check to make sure the popup window was opened. - mAsserter.is("data:text/plain;charset=utf-8,a", data.getString("uri"), "Checking popup URL"); + final GeckoBundle data = tabEventExpecter.blockForBundle(); - // Close the popup window. - closeTab(data.getInt("tabID")); + // Check to make sure the popup window was opened. + mAsserter.is("data:text/plain;charset=utf-8,a", data.getString("uri"), "Checking popup URL"); - } catch (JSONException e) { - mAsserter.ok(false, "exception getting event data", e.toString()); - } + // Close the popup window. + closeTab(data.getInt("tabID")); + tabEventExpecter.unregisterListener(); // Load page with popup loadUrlAndWait(POPUP_URL); waitForText(mStringHelper.POPUP_MESSAGE); mAsserter.is(mSolo.searchText(mStringHelper.POPUP_MESSAGE), true, "Popup blocker is displayed"); waitForCheckBox();
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPrivateBrowsing.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPrivateBrowsing.java @@ -1,20 +1,19 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.tests; import java.util.ArrayList; -import org.json.JSONException; -import org.json.JSONObject; import org.mozilla.gecko.Actions; import org.mozilla.gecko.Tabs; +import org.mozilla.gecko.util.GeckoBundle; /** * The test loads a new private tab and loads a page with a big link on it * Opens the link in a new private tab and checks that it is private * Adds a new normal tab and loads a 3rd URL * Checks that the bigLinkUrl loaded in the normal tab is present in the browsing history but the 2 urls opened in private tabs are not */ public class testPrivateBrowsing extends ContentContextMenuTest { @@ -22,17 +21,17 @@ public class testPrivateBrowsing extends public void testPrivateBrowsing() { String bigLinkUrl = getAbsoluteUrl(mStringHelper.ROBOCOP_BIG_LINK_URL); String blank1Url = getAbsoluteUrl(mStringHelper.ROBOCOP_BLANK_PAGE_01_URL); String blank2Url = getAbsoluteUrl(mStringHelper.ROBOCOP_BLANK_PAGE_02_URL); Tabs tabs = Tabs.getInstance(); blockForGeckoReady(); - Actions.EventExpecter tabExpecter = mActions.expectGeckoEvent("Tab:Added"); + Actions.EventExpecter tabExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added"); Actions.EventExpecter contentExpecter = mActions.expectGeckoEvent("Content:PageShow"); tabs.loadUrl(bigLinkUrl, Tabs.LOADURL_NEW_TAB | Tabs.LOADURL_PRIVATE); tabExpecter.blockForEvent(); tabExpecter.unregisterListener(); contentExpecter.blockForEvent(); contentExpecter.unregisterListener(); verifyTabCount(1); @@ -41,28 +40,29 @@ public class testPrivateBrowsing extends // Open the link context menu and verify the options verifyContextMenuItems(mStringHelper.CONTEXT_MENU_ITEMS_IN_PRIVATE_TAB); // Check that "Open Link in New Tab" is not in the menu mAsserter.ok(!mSolo.searchText(mStringHelper.CONTEXT_MENU_ITEMS_IN_NORMAL_TAB[0]), "Checking that 'Open Link in New Tab' is not displayed in the context menu", "'Open Link in New Tab' is not displayed in the context menu"); // Open the link in a new private tab and check that it is private - tabExpecter = mActions.expectGeckoEvent("Tab:Added"); + tabExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added"); contentExpecter = mActions.expectGeckoEvent("Content:PageShow"); mSolo.clickOnText(mStringHelper.CONTEXT_MENU_ITEMS_IN_PRIVATE_TAB[0]); - String eventData = tabExpecter.blockForEventData(); + + final GeckoBundle eventData = tabExpecter.blockForBundle(); tabExpecter.unregisterListener(); contentExpecter.blockForEvent(); contentExpecter.unregisterListener(); - mAsserter.ok(isTabPrivate(eventData), "Checking if the new tab opened from the context menu was a private tab", "The tab was a private tab"); + mAsserter.ok(eventData.getBoolean("isPrivate"), "Checking if the new tab opened from the context menu was a private tab", "The tab was a private tab"); verifyTabCount(2); // Open a normal tab to check later that it was registered in the Firefox Browser History - tabExpecter = mActions.expectGeckoEvent("Tab:Added"); + tabExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added"); contentExpecter = mActions.expectGeckoEvent("Content:PageShow"); tabs.loadUrl(blank2Url, Tabs.LOADURL_NEW_TAB); tabExpecter.blockForEvent(); tabExpecter.unregisterListener(); contentExpecter.blockForEvent(); contentExpecter.unregisterListener(); verifyTabCount(2); @@ -71,19 +71,9 @@ public class testPrivateBrowsing extends // Get the history list and check that the links open in private browsing are not saved final ArrayList<String> firefoxHistory = mDatabaseHelper.getBrowserDBUrls(DatabaseHelper.BrowserDataType.HISTORY); mAsserter.ok(!firefoxHistory.contains(bigLinkUrl), "Check that the link opened in the first private tab was not saved", bigLinkUrl + " was not added to history"); mAsserter.ok(!firefoxHistory.contains(blank1Url), "Check that the link opened in the private tab from the context menu was not saved", blank1Url + " was not added to history"); mAsserter.ok(firefoxHistory.contains(blank2Url), "Check that the link opened in the normal tab was saved", blank2Url + " was added to history"); } - - private boolean isTabPrivate(String eventData) { - try { - JSONObject data = new JSONObject(eventData); - return data.getBoolean("isPrivate"); - } catch (JSONException e) { - mAsserter.ok(false, "Error parsing the event data", e.toString()); - return false; - } - } }
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testRuntimePermissionsAPI.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testRuntimePermissionsAPI.java @@ -1,42 +1,43 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.tests; import org.mozilla.gecko.EventDispatcher; +import org.mozilla.gecko.GeckoApp; +import org.mozilla.gecko.util.BundleEventListener; import org.mozilla.gecko.util.EventCallback; -import org.mozilla.gecko.util.NativeEventListener; -import org.mozilla.gecko.util.NativeJSObject; +import org.mozilla.gecko.util.GeckoBundle; import static org.mozilla.gecko.tests.helpers.AssertionHelper.fFail; -public class testRuntimePermissionsAPI extends JavascriptTest implements NativeEventListener { +public class testRuntimePermissionsAPI extends JavascriptTest implements BundleEventListener { public testRuntimePermissionsAPI() { super("testRuntimePermissionsAPI.js"); } @Override public void setUp() throws Exception { super.setUp(); - EventDispatcher.getInstance().registerGeckoThreadListener(this, "RuntimePermissions:Prompt"); + GeckoApp.getEventDispatcher().registerUiThreadListener(this, "RuntimePermissions:Prompt"); } @Override public void tearDown() throws Exception { super.tearDown(); - EventDispatcher.getInstance().unregisterGeckoThreadListener(this, "RuntimePermissions:Prompt"); + GeckoApp.getEventDispatcher().unregisterUiThreadListener(this, "RuntimePermissions:Prompt"); } @Override - public void handleMessage(String event, NativeJSObject message, EventCallback callback) { + public void handleMessage(String event, GeckoBundle message, EventCallback callback) { mAsserter.is(event, "RuntimePermissions:Prompt", "Received RuntimePermissions:Prompt event"); try { String[] permissions = message.getStringArray("permissions"); mAsserter.is(3, permissions.length, "Received three permissions"); mAsserter.is("android.permission.CAMERA", permissions[0], "Received CAMERA permission"); mAsserter.is("android.permission.WRITE_EXTERNAL_STORAGE", permissions[1], "Received WRITE_EXTERNAL_STORAGE permission");
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testSnackbarAPI.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testSnackbarAPI.java @@ -2,51 +2,58 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.tests; import static org.mozilla.gecko.tests.helpers.AssertionHelper.fFail; import org.mozilla.gecko.EventDispatcher; +import org.mozilla.gecko.util.BundleEventListener; import org.mozilla.gecko.util.EventCallback; -import org.mozilla.gecko.util.NativeEventListener; -import org.mozilla.gecko.util.NativeJSObject; +import org.mozilla.gecko.util.GeckoBundle; -public class testSnackbarAPI extends JavascriptTest implements NativeEventListener { +public class testSnackbarAPI extends JavascriptTest implements BundleEventListener { // Snackbar.LENGTH_INDEFINITE: To avoid tests depending on the android design support library private static final int SNACKBAR_LENGTH_INDEFINITE = -2; public testSnackbarAPI() { super("testSnackbarAPI.js"); } @Override - public void handleMessage(String event, NativeJSObject message, EventCallback callback) { + public void handleMessage(String event, GeckoBundle message, EventCallback callback) { + if ("Robocop:WaitOnUI".equals(event)) { + callback.sendSuccess(null); + return; + } + mAsserter.is(event, "Snackbar:Show", "Received Snackbar:Show event"); try { mAsserter.is(message.getString("message"), "This is a Snackbar", "Snackbar message"); mAsserter.is(message.getInt("duration"), SNACKBAR_LENGTH_INDEFINITE, "Snackbar duration"); - NativeJSObject action = message.getObject("action"); + GeckoBundle action = message.getBundle("action"); mAsserter.is(action.getString("label"), "Click me", "Snackbar action label"); } catch (Exception e) { fFail("Event does not contain expected data: " + e.getMessage()); } } @Override public void setUp() throws Exception { super.setUp(); - EventDispatcher.getInstance().registerGeckoThreadListener(this, "Snackbar:Show"); + EventDispatcher.getInstance().registerUiThreadListener(this, "Snackbar:Show"); + EventDispatcher.getInstance().registerUiThreadListener(this, "Robocop:WaitOnUI"); } @Override public void tearDown() throws Exception { super.tearDown(); - EventDispatcher.getInstance().unregisterGeckoThreadListener(this, "Snackbar:Show"); + EventDispatcher.getInstance().unregisterUiThreadListener(this, "Snackbar:Show"); + EventDispatcher.getInstance().unregisterUiThreadListener(this, "Robocop:WaitOnUI"); } }
--- a/mobile/android/tests/browser/robocop/testSnackbarAPI.js +++ b/mobile/android/tests/browser/robocop/testSnackbarAPI.js @@ -3,19 +3,24 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ const { classes: Cc, interfaces: Ci, utils: Cu } = Components; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Snackbars", "resource://gre/modules/Snackbars.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher", "resource://gre/modules/Messaging.jsm"); add_task(function* test_snackbar_api() { Snackbars.show("This is a Snackbar", Snackbars.LENGTH_INDEFINITE, { action: { label: "Click me", callback: function () {} } }); + + yield EventDispatcher.instance.sendRequestForResult({ + type: "Robocop:WaitOnUI" + }); }); run_next_test();
--- a/toolkit/content/aboutTelemetry.js +++ b/toolkit/content/aboutTelemetry.js @@ -229,17 +229,17 @@ var Settings = { Preferences.observe(setting.pref, this.render, this); } let elements = document.getElementsByClassName("change-data-choices-link"); for (let el of elements) { el.addEventListener("click", function() { if (AppConstants.platform == "android") { Cu.import("resource://gre/modules/Messaging.jsm"); - Messaging.sendRequest({ + EventDispatcher.instance.sendRequest({ type: "Settings:Show", resource: "preferences_privacy", }); } else { // Show the data choices preferences on desktop. let mainWindow = getMainWindowWithPreferencesPane(); mainWindow.openAdvancedPreferences("dataChoicesTab"); }