author | Ryan VanderMeulen <ryanvm@gmail.com> |
Thu, 03 Jul 2014 23:24:35 -0400 | |
changeset 192328 | e8df6826a571509fdd08d5538f8158671c4fd58b |
parent 192306 | 02363e5245811abedbff3fdf522a26c37639ff92 (current diff) |
parent 192327 | 00309d102efc5647b90c70ff7cca8dc99c46882d (diff) |
child 192329 | ae49277dc7ea2e243945c0a002c862cb2bd47252 |
child 192386 | 4fd9df60e73c999c4ce72b1e8e0ca0c7440447b7 |
child 192394 | 215d323a555b73b0899b20ee162315a0f558b3c4 |
push id | 45799 |
push user | ryanvm@gmail.com |
push date | Fri, 04 Jul 2014 03:25:44 +0000 |
treeherder | mozilla-inbound@ae49277dc7ea [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 33.0a1 |
first release with | nightly linux32
e8df6826a571
/
33.0a1
/
20140704030208
/
files
nightly linux64
e8df6826a571
/
33.0a1
/
20140704030208
/
files
nightly mac
e8df6826a571
/
33.0a1
/
20140704030208
/
files
nightly win32
e8df6826a571
/
33.0a1
/
20140704030208
/
files
nightly win64
e8df6826a571
/
33.0a1
/
20140704030208
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
33.0a1
/
20140704030208
/
pushlog to previous
nightly linux64
33.0a1
/
20140704030208
/
pushlog to previous
nightly mac
33.0a1
/
20140704030208
/
pushlog to previous
nightly win32
33.0a1
/
20140704030208
/
pushlog to previous
nightly win64
33.0a1
/
20140704030208
/
pushlog to previous
|
--- a/browser/base/content/browser-social.js +++ b/browser/base/content/browser-social.js @@ -546,24 +546,25 @@ SocialShare = { return false; if (!aURI || !(aURI.schemeIs('http') || aURI.schemeIs('https'))) return false; return true; }, update: function() { - let shareButton = this.shareButton; - if (!shareButton) + let widget = CustomizableUI.getWidget("social-share-button"); + if (!widget) return; - // if we got here, the button is in the window somewhere, update it's hidden - // state based on available providers. - shareButton.hidden = !SocialUI.enabled || - [p for (p of Social.providers) if (p.shareURL)].length == 0; - let disabled = shareButton.hidden || !this.canSharePage(gBrowser.currentURI); + let shareButton = widget.forWindow(window).node; + // hidden state is based on available share providers and location of + // button. It's always visible and disabled in the customization palette. + shareButton.hidden = !SocialUI.enabled || (widget.areaType && + [p for (p of Social.providers) if (p.shareURL)].length == 0); + let disabled = !widget.areaType || shareButton.hidden || !this.canSharePage(gBrowser.currentURI); // 1. update the relevent command's disabled state so the keyboard // shortcut only works when available. // 2. If the button has been relocated to a place that is not visible by // default (e.g. menu panel) then the disabled attribute will not update // correctly based on the command, so we update the attribute directly as. let cmd = document.getElementById("Social:SharePage"); if (disabled) {
--- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -934,16 +934,17 @@ <toolbarbutton id="social-share-button" class="toolbarbutton-1 chromeclass-toolbar-additional" label="&sharePageCmd.label;" tooltiptext="&sharePageCmd.label;" cui-areatype="toolbar" removable="true" + hidden="true" command="Social:SharePage"/> </hbox> <toolbarbutton id="nav-bar-overflow-button" class="toolbarbutton-1 chromeclass-toolbar-additional overflow-button" cui-areatype="toolbar" skipintoolbarset="true" tooltiptext="&navbarOverflow.label;"/>
--- a/browser/base/content/newtab/newTab.js +++ b/browser/base/content/newtab/newTab.js @@ -8,17 +8,16 @@ let Cu = Components.utils; let Ci = Components.interfaces; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/PageThumbs.jsm"); Cu.import("resource://gre/modules/BackgroundPageThumbs.jsm"); Cu.import("resource://gre/modules/DirectoryLinksProvider.jsm"); Cu.import("resource://gre/modules/NewTabUtils.jsm"); -Cu.import("resource://gre/modules/Promise.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Rect", "resource://gre/modules/Geometry.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel", "resource://gre/modules/UpdateChannel.jsm");
--- a/browser/base/content/newtab/transformations.js +++ b/browser/base/content/newtab/transformations.js @@ -174,29 +174,28 @@ let gTransformation = { let callback = aOptions && aOptions.callback; let unfreeze = aOptions && aOptions.unfreeze; aSites.forEach(function (aSite, aIndex) { // Do not re-arrange empty cells or the dragged site. if (!aSite || aSite == gDrag.draggedSite) return; - let deferred = Promise.defer(); - batch.push(deferred.promise); - let cb = deferred.resolve; - - if (!cells[aIndex]) - // The site disappeared from the grid, hide it. - this.hideSite(aSite, cb); - else if (this._getNodeOpacity(aSite.node) != 1) - // The site disappeared before but is now back, show it. - this.showSite(aSite, cb); - else - // The site's position has changed, move it around. - this._moveSite(aSite, aIndex, {unfreeze: unfreeze, callback: cb}); + batch.push(new Promise(resolve => { + if (!cells[aIndex]) { + // The site disappeared from the grid, hide it. + this.hideSite(aSite, resolve); + } else if (this._getNodeOpacity(aSite.node) != 1) { + // The site disappeared before but is now back, show it. + this.showSite(aSite, resolve); + } else { + // The site's position has changed, move it around. + this._moveSite(aSite, aIndex, {unfreeze: unfreeze, callback: resolve}); + } + })); }, this); if (callback) { Promise.all(batch).then(callback); } }, /**
--- a/browser/base/content/newtab/updater.js +++ b/browser/base/content/newtab/updater.js @@ -129,27 +129,26 @@ let gUpdater = { let batch = []; // Delete sites that were removed from the grid. gGrid.sites.forEach(function (aSite) { // The site must be valid and not in the current grid. if (!aSite || aSites.indexOf(aSite) != -1) return; - let deferred = Promise.defer(); - batch.push(deferred.promise); + batch.push(new Promise(resolve => { + // Fade out the to-be-removed site. + gTransformation.hideSite(aSite, function () { + let node = aSite.node; - // Fade out the to-be-removed site. - gTransformation.hideSite(aSite, function () { - let node = aSite.node; - - // Remove the site from the DOM. - node.parentNode.removeChild(node); - deferred.resolve(); - }); + // Remove the site from the DOM. + node.parentNode.removeChild(node); + resolve(); + }); + })); }); Promise.all(batch).then(aCallback); }, /** * Tries to fill empty cells with new links if available. * @param aLinks The array of links. @@ -159,26 +158,25 @@ let gUpdater = { let {cells, sites} = gGrid; let batch = []; // Find empty cells and fill them. sites.forEach(function (aSite, aIndex) { if (aSite || !aLinks[aIndex]) return; - let deferred = Promise.defer(); - batch.push(deferred.promise); - - // Create the new site and fade it in. - let site = gGrid.createSite(aLinks[aIndex], cells[aIndex]); + batch.push(new Promise(resolve => { + // Create the new site and fade it in. + let site = gGrid.createSite(aLinks[aIndex], cells[aIndex]); - // Set the site's initial opacity to zero. - site.node.style.opacity = 0; + // Set the site's initial opacity to zero. + site.node.style.opacity = 0; - // Flush all style changes for the dynamically inserted site to make - // the fade-in transition work. - window.getComputedStyle(site.node).opacity; - gTransformation.showSite(site, function () deferred.resolve()); + // Flush all style changes for the dynamically inserted site to make + // the fade-in transition work. + window.getComputedStyle(site.node).opacity; + gTransformation.showSite(site, resolve); + })); }); Promise.all(batch).then(aCallback); } };
--- a/browser/base/content/test/newtab/browser_newtab_reportLinkAction.js +++ b/browser/base/content/test/newtab/browser_newtab_reportLinkAction.js @@ -42,16 +42,17 @@ function runTests() { expected.tile = 1; expected.pinned = false; yield EventUtils.synthesizeMouseAtCenter(pinButton, {}, getContentWindow()); // Unpin that link expected.action = "unpin"; expected.pinned = true; yield EventUtils.synthesizeMouseAtCenter(pinButton, {}, getContentWindow()); + yield whenPagesUpdated(); // Block the site in the 0th tile spot let blockedSite = getCell(0).node.querySelector(".newtab-site"); let blockButton = blockedSite.querySelector(".newtab-control-block"); expected.type = "organic"; expected.link = 0; expected.action = "block"; expected.tile = 0;
--- a/browser/branding/aurora/pref/firefox-branding.js +++ b/browser/branding/aurora/pref/firefox-branding.js @@ -6,17 +6,18 @@ // We don't have pages ready for this, so leave these prefs empty for now (bug 648330) pref("startup.homepage_override_url",""); pref("startup.homepage_welcome_url",""); // The time interval between checks for a new version (in seconds) pref("app.update.interval", 28800); // 8 hours // The time interval between the downloading of mar file chunks in the // background (in seconds) -pref("app.update.download.backgroundInterval", 60); +// 0 means "download everything at once" +pref("app.update.download.backgroundInterval", 0); // Give the user x seconds to react before showing the big UI. default=168 hours pref("app.update.promptWaitTime", 604800); // URL user can browse to manually if for some reason all update installation // attempts fail. pref("app.update.url.manual", "https://www.mozilla.org/firefox/aurora/"); // A default value for the "More information about this update" link // supplied in the "An update is available" page of the update wizard. pref("app.update.url.details", "https://www.mozilla.org/firefox/aurora/");
--- a/browser/branding/nightly/pref/firefox-branding.js +++ b/browser/branding/nightly/pref/firefox-branding.js @@ -3,17 +3,18 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ pref("startup.homepage_override_url", "https://www.mozilla.org/projects/firefox/%VERSION%/whatsnew/?oldversion=%OLD_VERSION%"); pref("startup.homepage_welcome_url", "https://www.mozilla.org/projects/firefox/%VERSION%/firstrun/"); // The time interval between checks for a new version (in seconds) pref("app.update.interval", 7200); // 2 hours // The time interval between the downloading of mar file chunks in the // background (in seconds) -pref("app.update.download.backgroundInterval", 60); +// 0 means "download everything at once" +pref("app.update.download.backgroundInterval", 0); // Give the user x seconds to react before showing the big UI. default=12 hours pref("app.update.promptWaitTime", 43200); // URL user can browse to manually if for some reason all update installation // attempts fail. pref("app.update.url.manual", "https://nightly.mozilla.org"); // A default value for the "More information about this update" link // supplied in the "An update is available" page of the update wizard. pref("app.update.url.details", "https://nightly.mozilla.org");
--- a/browser/components/preferences/in-content/privacy.xul +++ b/browser/components/preferences/in-content/privacy.xul @@ -73,17 +73,17 @@ data-category="panePrivacy"> <image class="header-icon"/> <label class="header-name">&panePrivacy.title;</label> </hbox> <!-- Tracking --> <groupbox id="trackingGroup" data-category="panePrivacy" hidden="true" align="start"> <caption><label>&tracking.label;</label></caption> - <radiogroup id="doNotTrackSelection" orient="vertical" + <radiogroup id="doNotTrackSelection" orient="vertical" align="start" preference="privacy.donottrackheader.value" onsynctopreference="return gPrivacyPane.setTrackingPrefs()" onsyncfrompreference="return gPrivacyPane.getTrackingPrefs()"> <radio id="dntnotrack" value="1" label="&dntTrackingNotOkay.label2;" accesskey="&dntTrackingNotOkay.accesskey;" /> <radio id="dntdotrack" value="0" label="&dntTrackingOkay.label2;" accesskey="&dntTrackingOkay.accesskey;" /> <radio id="dntnopref" value="-1" label="&dntTrackingNopref.label2;" @@ -146,47 +146,41 @@ >&dontrememberActions.clearHistory.label;</html:a>&dontrememberActions.post.label;</description> </vbox> <spacer flex="1" class="indent"/> </hbox> </vbox> <vbox id="historyCustomPane"> <separator class="thin"/> <vbox class="indent"> - <hbox> + <vbox align="start"> <checkbox id="privateBrowsingAutoStart" label="&privateBrowsingPermanent2.label;" accesskey="&privateBrowsingPermanent2.accesskey;" preference="browser.privatebrowsing.autostart" oncommand="gPrivacyPane.updateAutostart()"/> - <spacer flex="1"/> - </hbox> + </vbox> <vbox class="indent"> - <hbox> + <vbox align="start"> <checkbox id="rememberHistory" label="&rememberHistory2.label;" accesskey="&rememberHistory2.accesskey;" preference="places.history.enabled"/> - <spacer flex="1"/> - </hbox> - <hbox> <checkbox id="rememberForms" label="&rememberSearchForm.label;" accesskey="&rememberSearchForm.accesskey;" preference="browser.formfill.enable"/> - <spacer flex="1"/> - </hbox> - + </vbox> <hbox id="cookiesBox"> <checkbox id="acceptCookies" label="&acceptCookies.label;" preference="network.cookie.cookieBehavior" accesskey="&acceptCookies.accesskey;" onsyncfrompreference="return gPrivacyPane.readAcceptCookies();" onsynctopreference="return gPrivacyPane.writeAcceptCookies();"/> - <spacer flex="1"/> + <spacer flex="1" /> <button id="cookieExceptions" oncommand="gPrivacyPane.showCookieExceptions();" label="&cookieExceptions.label;" accesskey="&cookieExceptions.accesskey;" preference="pref.privacy.disable_button.cookie_exceptions"/> </hbox> <hbox id="acceptThirdPartyRow" class="indent" align="center"> <label id="acceptThirdPartyLabel" control="acceptThirdPartyMenu"
--- a/browser/themes/osx/browser.css +++ b/browser/themes/osx/browser.css @@ -3613,16 +3613,17 @@ toolbarbutton.chevron > .toolbarbutton-m notification[value="translation"] { color: #484848; background-color: #EFEFEF; background-image: none; border-top: none; border-bottom: 1px solid #c4c4c4; padding-top: 1px; padding-bottom: 1px; + min-height: 35px; } .translate-infobar-element { margin-top: 0 !important; margin-bottom: 0 !important; } button.translate-infobar-element {
--- a/mobile/android/base/PrivateTab.java +++ b/mobile/android/base/PrivateTab.java @@ -5,16 +5,21 @@ package org.mozilla.gecko; import android.content.Context; public class PrivateTab extends Tab { public PrivateTab(Context context, int id, String url, boolean external, int parentId, String title) { super(context, id, url, external, parentId, title); + + // Init background to background_private to ensure flicker-free + // private tab creation. Page loads will reset it to white as expected. + final int bgColor = context.getResources().getColor(R.color.background_private); + setBackgroundColor(bgColor); } @Override protected void saveThumbnailToDB() {} @Override public boolean isPrivate() { return true;
--- a/mobile/android/base/gfx/LayerRenderer.java +++ b/mobile/android/base/gfx/LayerRenderer.java @@ -131,20 +131,18 @@ public class LayerRenderer implements Ta "varying vec2 vTexCoord;\n" + "uniform sampler2D sTexture;\n" + "void main() {\n" + " gl_FragColor = texture2D(sTexture, vTexCoord);\n" + "}\n"; public LayerRenderer(LayerView view) { mView = view; - try { - mOverscrollColor = view.getContext().getResources().getColor(R.color.background_normal); - } catch (Resources.NotFoundException nfe) { mOverscrollColor = Color.BLACK; } - + setOverscrollColor(R.color.background_normal); + Bitmap scrollbarImage = view.getScrollbarImage(); IntSize size = new IntSize(scrollbarImage.getWidth(), scrollbarImage.getHeight()); scrollbarImage = expandCanvasToPowerOfTwo(scrollbarImage, size); mTasks = new CopyOnWriteArrayList<RenderTask>(); mLastFrameTime = System.nanoTime(); mVertScrollLayer = new ScrollbarLayer(this, scrollbarImage, size, true); @@ -194,16 +192,22 @@ public class LayerRenderer implements Ta } void onSurfaceCreated(EGLConfig config) { checkMonitoringEnabled(); createDefaultProgram(); activateDefaultProgram(); } + void setOverscrollColor(int colorId) { + try { + mOverscrollColor = mView.getContext().getResources().getColor(colorId); + } catch (Resources.NotFoundException nfe) { mOverscrollColor = Color.BLACK; } + } + public void createDefaultProgram() { int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, DEFAULT_VERTEX_SHADER); int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, DEFAULT_FRAGMENT_SHADER); mProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program GLES20.glLinkProgram(mProgram); // creates OpenGL program executables @@ -712,16 +716,20 @@ public class LayerRenderer implements Ta @Override public void onTabChanged(final Tab tab, Tabs.TabEvents msg, Object data) { // Sets the background of the newly selected tab. This background color // gets cleared in endDrawing(). This function runs on the UI thread, // but other code that touches the paint state is run on the compositor // thread, so this may need to be changed if any problems appear. if (msg == Tabs.TabEvents.SELECTED) { if (mView != null) { + final int overscrollColor = + (tab.isPrivate() ? R.color.background_private : R.color.background_normal); + setOverscrollColor(overscrollColor); + if (mView.getChildAt(0) != null) { mView.getChildAt(0).setBackgroundColor(tab.getBackgroundColor()); } mView.setPaintState(LayerView.PAINT_START); } } } }
--- a/mobile/android/base/resources/layout/remote_tabs_setup_panel.xml +++ b/mobile/android/base/resources/layout/remote_tabs_setup_panel.xml @@ -1,18 +1,14 @@ <?xml version="1.0" encoding="utf-8"?> <!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> -<org.mozilla.gecko.tabspanel.RemoteTabsSetupPanel - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:visibility="gone"> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> <LinearLayout android:id="@+id/remote_tabs_setup_containing_layout" style="@style/TabsPanelFrame" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout style="@style/TabsPanelSection" android:layout_width="match_parent" @@ -51,9 +47,9 @@ android:layout_height="wrap_content" style="@style/TabsPanelItem.TextAppearance.Linkified" android:text="@string/fxaccount_getting_started_old_firefox"/> </LinearLayout> </LinearLayout> -</org.mozilla.gecko.tabspanel.RemoteTabsSetupPanel> +</merge>
--- a/mobile/android/base/resources/layout/remote_tabs_verification_panel.xml +++ b/mobile/android/base/resources/layout/remote_tabs_verification_panel.xml @@ -1,18 +1,14 @@ <?xml version="1.0" encoding="utf-8"?> <!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> -<org.mozilla.gecko.tabspanel.RemoteTabsVerificationPanel - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:visibility="gone"> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> <LinearLayout android:id="@+id/remote_tabs_verification_containing_layout" style="@style/TabsPanelFrame" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout style="@style/TabsPanelSection" android:layout_width="match_parent" @@ -46,9 +42,9 @@ style="@style/TabsPanelItem.TextAppearance.Linkified.Resend" android:layout_width="match_parent" android:text="@string/fxaccount_confirm_account_resend_email"/> </LinearLayout> </LinearLayout> -</org.mozilla.gecko.tabspanel.RemoteTabsVerificationPanel> +</merge>
--- a/mobile/android/base/tabspanel/RemoteTabsPanel.java +++ b/mobile/android/base/tabspanel/RemoteTabsPanel.java @@ -94,30 +94,30 @@ class RemoteTabsPanel extends FrameLayou if (accountState.getNeededAction() == State.Action.NeedsVerification) { return RemotePanelType.VERIFICATION; } return RemotePanelType.CONTAINER; } private PanelView inflatePanel(final RemotePanelType panelType) { - final LayoutInflater inflater = LayoutInflater.from(getContext()); - final View inflatedView; + final PanelView view; switch (panelType) { case SETUP: - inflatedView = inflater.inflate(R.layout.remote_tabs_setup_panel, null); + view = new RemoteTabsSetupPanel(getContext()); break; case VERIFICATION: - inflatedView = inflater.inflate(R.layout.remote_tabs_verification_panel, null); + view = new RemoteTabsVerificationPanel(getContext()); break; case CONTAINER: - inflatedView = inflater.inflate(R.layout.remote_tabs_container_panel, null); + final LayoutInflater inflater = LayoutInflater.from(getContext()); + view = (PanelView) inflater.inflate(R.layout.remote_tabs_container_panel, null); break; default: throw new IllegalArgumentException("Unknown panelType, " + panelType); } - return (PanelView) inflatedView; + return view; } }
--- a/mobile/android/base/tabspanel/RemoteTabsSetupPanel.java +++ b/mobile/android/base/tabspanel/RemoteTabsSetupPanel.java @@ -9,39 +9,35 @@ import java.util.Locale; import org.mozilla.gecko.R; import org.mozilla.gecko.Tabs; import org.mozilla.gecko.fxa.FirefoxAccounts; import org.mozilla.gecko.fxa.activities.FxAccountCreateAccountActivity; import org.mozilla.gecko.tabspanel.TabsPanel.PanelView; import android.content.Context; import android.content.Intent; -import android.util.AttributeSet; +import android.view.LayoutInflater; import android.view.View; import android.widget.LinearLayout; import android.widget.ScrollView; /** * A tabs panel which allows a user to get started setting up a Firefox * Accounts account. Currently used as one sub-panel in a sequence * contained by the {@link RemoteTabsPanel}. */ class RemoteTabsSetupPanel extends ScrollView implements PanelView { - private LinearLayout containingLayout; + private final LinearLayout containingLayout; private TabsPanel tabsPanel; - public RemoteTabsSetupPanel(Context context, AttributeSet attrs) { - super(context, attrs); - } + public RemoteTabsSetupPanel(Context context) { + super(context); - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - + LayoutInflater.from(context).inflate(R.layout.remote_tabs_setup_panel, this); containingLayout = (LinearLayout) findViewById(R.id.remote_tabs_setup_containing_layout); final View setupGetStartedButton = containingLayout.findViewById(R.id.remote_tabs_setup_get_started); setupGetStartedButton.setOnClickListener(new OnClickListener() { @Override public void onClick(final View v) { final Context context = getContext();
--- a/mobile/android/base/tabspanel/RemoteTabsVerificationPanel.java +++ b/mobile/android/base/tabspanel/RemoteTabsVerificationPanel.java @@ -5,43 +5,39 @@ package org.mozilla.gecko.tabspanel; import org.mozilla.gecko.R; import org.mozilla.gecko.fxa.FirefoxAccounts; import org.mozilla.gecko.fxa.login.State; import org.mozilla.gecko.tabspanel.TabsPanel.PanelView; import android.content.Context; -import android.util.AttributeSet; import android.util.Log; +import android.view.LayoutInflater; import android.view.View; import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; /** * A tabs panel which allows a user to get resend the verification email * to confirm a Firefox Account. Currently used as one sub-panel in a sequence * contained by the {@link RemoteTabsPanel}. */ class RemoteTabsVerificationPanel extends ScrollView implements PanelView { private static final String LOG_TAG = RemoteTabsVerificationPanel.class.getSimpleName(); - private LinearLayout containingLayout; + private final LinearLayout containingLayout; private TabsPanel tabsPanel; - public RemoteTabsVerificationPanel(Context context, AttributeSet attrs) { - super(context, attrs); - } + public RemoteTabsVerificationPanel(Context context) { + super(context); - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - + LayoutInflater.from(context).inflate(R.layout.remote_tabs_verification_panel, this); containingLayout = (LinearLayout) findViewById(R.id.remote_tabs_verification_containing_layout); final View resendLink = containingLayout.findViewById(R.id.remote_tabs_confirm_resend); resendLink.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { final State accountState = FirefoxAccounts.getFirefoxAccountState(getContext()); final State.Action neededAction = accountState.getNeededAction();
--- a/mobile/android/base/tabspanel/TabsPanel.java +++ b/mobile/android/base/tabspanel/TabsPanel.java @@ -167,19 +167,25 @@ public class TabsPanel extends LinearLay mTabWidget.setTabSelectionListener(this); mMenuButton = (ImageButton) findViewById(R.id.menu); mMenuButton.setOnClickListener(new Button.OnClickListener() { @Override public void onClick(View view) { final Menu menu = mPopupMenu.getMenu(); + + // Each panel has a "+" shortcut button, so don't show it for that panel. + menu.findItem(R.id.new_tab).setVisible(mCurrentPanel != Panel.NORMAL_TABS); + menu.findItem(R.id.new_private_tab).setVisible(mCurrentPanel != Panel.PRIVATE_TABS); + + // Only show "Clear * tabs" for current panel. menu.findItem(R.id.close_all_tabs).setVisible(mCurrentPanel == Panel.NORMAL_TABS); menu.findItem(R.id.close_private_tabs).setVisible(mCurrentPanel == Panel.PRIVATE_TABS); - + mPopupMenu.show(); } }); mPopupMenu.setAnchor(mMenuButton); } private void addTab() { if (mCurrentPanel == Panel.NORMAL_TABS) {
--- a/services/healthreport/docs/dataformat.rst +++ b/services/healthreport/docs/dataformat.rst @@ -1068,16 +1068,45 @@ Example ] } org.mozilla.crashes.crashes --------------------------- This measurement contains a historical record of application crashes. +Version 4 +^^^^^^^^^ + +This version follows up from version 3, adding submissions which are now +tracked by the :ref:`crashes_crashmanager`. + +This measurement will be reported on each day there was a crash or crash +submission. Records may contain the following fields, whose values indicate +the number of crashes, hangs, or submissions that occurred on the given day: + +* main-crash +* main-crash-submission-succeeded +* main-crash-submission-failed +* main-hang +* main-hang-submission-succeeded +* main-hang-submission-failed +* content-crash +* content-crash-submission-succeeded +* content-crash-submission-failed +* content-hang +* content-hang-submission-succeeded +* content-hang-submission-failed +* plugin-crash +* plugin-crash-submission-succeeded +* plugin-crash-submission-failed +* plugin-hang +* plugin-hang-submission-succeeded +* plugin-hang-submission-failed + Version 3 ^^^^^^^^^ This version follows up from version 2, building on improvements to the :ref:`crashes_crashmanager`. This measurement will be reported on each day there was a crash. Records may contain the following fields, whose values indicate @@ -1147,16 +1176,24 @@ Example "_v": 1, "pending": 1, "submitted": 2 }, "org.mozilla.crashes.crashes": { "_v": 2, "mainCrash": 2 } + "org.mozilla.crashes.crashes": { + "_v": 4, + "main-crash": 2, + "main-crash-submission-succeeded": 1, + "main-crash-submission-failed": 1, + "main-hang": 1, + "plugin-crash": 2 + } org.mozilla.healthreport.submissions ------------------------------------ This measurement contains a history of FHR's own data submission activity. It was added in Firefox 23 in early May 2013. Version 2
--- a/services/healthreport/providers.jsm +++ b/services/healthreport/providers.jsm @@ -1042,46 +1042,79 @@ DailyCrashesMeasurement3.prototype = Obj "main-hang": DAILY_LAST_NUMERIC_FIELD, "content-crash": DAILY_LAST_NUMERIC_FIELD, "content-hang": DAILY_LAST_NUMERIC_FIELD, "plugin-crash": DAILY_LAST_NUMERIC_FIELD, "plugin-hang": DAILY_LAST_NUMERIC_FIELD, }, }); +function DailyCrashesMeasurement4() { + Metrics.Measurement.call(this); +} + +DailyCrashesMeasurement4.prototype = Object.freeze({ + __proto__: Metrics.Measurement.prototype, + + name: "crashes", + version: 4, + + fields: { + "main-crash": DAILY_LAST_NUMERIC_FIELD, + "main-crash-submission-succeeded": DAILY_LAST_NUMERIC_FIELD, + "main-crash-submission-failed": DAILY_LAST_NUMERIC_FIELD, + "main-hang": DAILY_LAST_NUMERIC_FIELD, + "main-hang-submission-succeeded": DAILY_LAST_NUMERIC_FIELD, + "main-hang-submission-failed": DAILY_LAST_NUMERIC_FIELD, + "content-crash": DAILY_LAST_NUMERIC_FIELD, + "content-crash-submission-succeeded": DAILY_LAST_NUMERIC_FIELD, + "content-crash-submission-failed": DAILY_LAST_NUMERIC_FIELD, + "content-hang": DAILY_LAST_NUMERIC_FIELD, + "content-hang-submission-succeeded": DAILY_LAST_NUMERIC_FIELD, + "content-hang-submission-failed": DAILY_LAST_NUMERIC_FIELD, + "plugin-crash": DAILY_LAST_NUMERIC_FIELD, + "plugin-crash-submission-succeeded": DAILY_LAST_NUMERIC_FIELD, + "plugin-crash-submission-failed": DAILY_LAST_NUMERIC_FIELD, + "plugin-hang": DAILY_LAST_NUMERIC_FIELD, + "plugin-hang-submission-succeeded": DAILY_LAST_NUMERIC_FIELD, + "plugin-hang-submission-failed": DAILY_LAST_NUMERIC_FIELD, + }, +}); + this.CrashesProvider = function () { Metrics.Provider.call(this); // So we can unit test. this._manager = Services.crashmanager; }; CrashesProvider.prototype = Object.freeze({ __proto__: Metrics.Provider.prototype, name: "org.mozilla.crashes", measurementTypes: [ DailyCrashesMeasurement1, DailyCrashesMeasurement2, DailyCrashesMeasurement3, + DailyCrashesMeasurement4, ], pullOnly: true, collectDailyData: function () { return this.storage.enqueueTransaction(this._populateCrashCounts.bind(this)); }, _populateCrashCounts: function () { this._log.info("Grabbing crash counts from crash manager."); let crashCounts = yield this._manager.getCrashCountsByDay(); - let m = this.getMeasurement("crashes", 3); - let fields = DailyCrashesMeasurement3.prototype.fields; + let m = this.getMeasurement("crashes", 4); + let fields = DailyCrashesMeasurement4.prototype.fields; for (let [day, types] of crashCounts) { let date = Metrics.daysToDate(day); for (let [type, count] of types) { if (!(type in fields)) { this._log.warn("Unknown crash type encountered: " + type); continue; }
--- a/services/healthreport/tests/xpcshell/test_provider_crashes.js +++ b/services/healthreport/tests/xpcshell/test_provider_crashes.js @@ -45,57 +45,75 @@ add_task(function* test_collect() { provider._manager = manager; let day1 = new Date(2014, 0, 1, 0, 0, 0); let day2 = new Date(2014, 0, 3, 0, 0, 0); yield manager.addCrash(manager.PROCESS_TYPE_MAIN, manager.CRASH_TYPE_CRASH, "mc1", day1); + yield manager.addSubmission(manager.PROCESS_TYPE_MAIN, + manager.CRASH_TYPE_CRASH, + true, + "mc1", day1) yield manager.addCrash(manager.PROCESS_TYPE_MAIN, manager.CRASH_TYPE_CRASH, "mc2", day1); + yield manager.addSubmission(manager.PROCESS_TYPE_MAIN, + manager.CRASH_TYPE_CRASH, + false, + "mc2", day1) yield manager.addCrash(manager.PROCESS_TYPE_CONTENT, manager.CRASH_TYPE_HANG, "ch", day1); yield manager.addCrash(manager.PROCESS_TYPE_PLUGIN, manager.CRASH_TYPE_CRASH, "pc", day1); yield manager.addCrash(manager.PROCESS_TYPE_MAIN, manager.CRASH_TYPE_HANG, "mh", day2); yield manager.addCrash(manager.PROCESS_TYPE_CONTENT, manager.CRASH_TYPE_CRASH, "cc", day2); + yield manager.addSubmission(manager.PROCESS_TYPE_CONTENT, + manager.CRASH_TYPE_CRASH, + true, + "cc", day2) yield manager.addCrash(manager.PROCESS_TYPE_PLUGIN, manager.CRASH_TYPE_HANG, "ph", day2); yield provider.collectDailyData(); - let m = provider.getMeasurement("crashes", 3); + let m = provider.getMeasurement("crashes", 4); let values = yield m.getValues(); do_check_eq(values.days.size, 2); do_check_true(values.days.hasDay(day1)); do_check_true(values.days.hasDay(day2)); let value = values.days.getDay(day1); do_check_true(value.has("main-crash")); do_check_eq(value.get("main-crash"), 2); + do_check_true(value.has("main-crash-submission-succeeded")); + do_check_eq(value.get("main-crash-submission-succeeded"), 1); + do_check_true(value.has("main-crash-submission-failed")); + do_check_eq(value.get("main-crash-submission-failed"), 1); do_check_true(value.has("content-hang")); do_check_eq(value.get("content-hang"), 1); do_check_true(value.has("plugin-crash")); do_check_eq(value.get("plugin-crash"), 1); value = values.days.getDay(day2); do_check_true(value.has("main-hang")); do_check_eq(value.get("main-hang"), 1); do_check_true(value.has("content-crash")); do_check_eq(value.get("content-crash"), 1); + do_check_true(value.has("content-crash-submission-succeeded")); + do_check_eq(value.get("content-crash-submission-succeeded"), 1); do_check_true(value.has("plugin-hang")); do_check_eq(value.get("plugin-hang"), 1); // Check that adding a new crash increments counter on next collect. yield manager.addCrash(manager.PROCESS_TYPE_MAIN, manager.CRASH_TYPE_HANG, "mc3", day2);
--- a/toolkit/components/crashes/CrashManager.jsm +++ b/toolkit/components/crashes/CrashManager.jsm @@ -125,22 +125,31 @@ this.CrashManager.prototype = Object.fre PROCESS_TYPE_MAIN: "main", // A crash in a content process. PROCESS_TYPE_CONTENT: "content", // A crash in a plugin process. PROCESS_TYPE_PLUGIN: "plugin", + // A submission of a crash. + PROCESS_TYPE_SUBMISSION: "submission", + // A real crash. CRASH_TYPE_CRASH: "crash", // A hang. CRASH_TYPE_HANG: "hang", + // A successful submission. + SUBMISSION_TYPE_SUCCEEDED: "succeeded", + + // A failed submission. + SUBMISSION_TYPE_FAILED: "failed", + DUMP_REGEX: /^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.dmp$/i, SUBMITTED_REGEX: /^bp-(?:hr-)?([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.txt$/i, ALL_REGEX: /^(.*)$/, // How long the store object should persist in memory before being // automatically garbage collected. STORE_EXPIRATION_MS: 60 * 1000, @@ -356,16 +365,48 @@ this.CrashManager.prototype = Object.fre let store = yield this._getStore(); if (store.addCrash(processType, crashType, id, date)) { yield store.save(); } }.bind(this)); }, /** + * Record the occurrence of a crash submission. + * + * @param processType (string) One of the PROCESS_TYPE constants. + * @param crashType (string) One of the CRASH_TYPE constants. + * @param succeeded (boolean) Whether the submission succeeded. + * @param id (string) Crash ID. Likely a UUID. + * @param date (Date) When the crash occurred. + * + * @return boolean True if the crash submission was recorded and false if not. + */ + addSubmission: function (processType, crashType, succeeded, id, date) { + return Task.spawn(function* () { + let store = yield this._getStore(); + if (this._addSubmissionAsCrash(store, processType, crashType, succeeded, + id, date)) { + yield store.save(); + } + }.bind(this)); + }, + + _addSubmissionAsCrash: function (store, processType, crashType, succeeded, + id, date) { + let id = id + "-" + this.PROCESS_TYPE_SUBMISSION; + let process = processType + "-" + crashType + "-" + + this.PROCESS_TYPE_SUBMISSION; + let submission_type = ( + succeeded ? this.SUBMISSION_TYPE_SUCCEEDED : this.SUBMISSION_TYPE_FAILED); + + return store.addCrash(process, submission_type, id, date); + }, + + /** * Obtain the paths of all unprocessed events files. * * The promise-resolved array is sorted by file mtime, oldest to newest. */ _getUnprocessedEventsFiles: function () { return Task.spawn(function* () { let entries = []; @@ -420,37 +461,45 @@ this.CrashManager.prototype = Object.fre return this._handleEventFilePayload(store, entry, type, date, payload); }.bind(this)); }, _handleEventFilePayload: function (store, entry, type, date, payload) { // The payload types and formats are documented in docs/crash-events.rst. // Do not change the format of an existing type. Instead, invent a new // type. - - // type in event file => [processType, crashType] - let eventMap = { - "crash.main.1": ["main", "crash"], - }; + // DO NOT ADD NEW TYPES WITHOUT DOCUMENTING! + let lines = payload.split("\n"); - if (type in eventMap) { - let lines = payload.split("\n"); - if (lines.length > 1) { - this._log.warn("Multiple lines unexpected in payload for " + - entry.path); - return this.EVENT_FILE_ERROR_MALFORMED; - } + switch (type) { + case "crash.main.1": + if (lines.length > 1) { + this._log.warn("Multiple lines unexpected in payload for " + + entry.path); + return this.EVENT_FILE_ERROR_MALFORMED; + } + store.addCrash(this.PROCESS_TYPE_MAIN, this.CRASH_TYPE_CRASH, + payload, date); + break; - store.addCrash(...eventMap[type], payload, date); - return this.EVENT_FILE_SUCCESS; + case "crash.submission.1": + if (lines.length == 3) { + this._addSubmissionAsCrash(store, this.PROCESS_TYPE_MAIN, + this.CRASH_TYPE_CRASH, + lines[1] === "true", lines[0], date); + } else { + return this.EVENT_FILE_ERROR_MALFORMED; + } + break; + + default: + return this.EVENT_FILE_ERROR_UNKNOWN_EVENT; } - // DO NOT ADD NEW TYPES WITHOUT DOCUMENTING! - - return this.EVENT_FILE_ERROR_UNKNOWN_EVENT; + return this.EVENT_FILE_SUCCESS; }, /** * The resolved promise is an array of objects with the properties: * * path -- String filename * id -- regexp.match()[1] (likely the crash ID) * date -- Date mtime of the file
--- a/toolkit/components/crashes/docs/crash-events.rst +++ b/toolkit/components/crashes/docs/crash-events.rst @@ -69,16 +69,28 @@ crash.main.1 ^^^^^^^^^^^^ This event is produced when the main process crashes. The payload of this event is the string crash ID, very likely a UUID. There should be ``UUID.dmp`` and ``UUID.extra`` files on disk, saved by Breakpad. +crash.submission.1 +^^^^^^^^^^^^ + +This event is produced when a crash is submitted. + +The payload of this event is delimited by UNIX newlines (*\n*) and contains the +following fields: + +* The crash ID string +* "true" if the submission succeeded or "false" otherwise +* The remote crash ID string if the submission succeeded + Aggregated Event Log ==================== Crash events are aggregated together into a unified event *log*. Currently, this *log* is really a JSON file. However, this is an implementation detail and it could change at any time. The interface to crash data provided by the JavaScript API is the only supported interface.
--- a/toolkit/components/crashes/tests/xpcshell/test_crash_manager.js +++ b/toolkit/components/crashes/tests/xpcshell/test_crash_manager.js @@ -299,8 +299,47 @@ add_task(function* test_addCrash() { Assert.ok(crash.isOfType(m.PROCESS_TYPE_PLUGIN, m.CRASH_TYPE_CRASH)); crash = map.get("plugin-hang"); Assert.ok(!!crash); Assert.equal(crash.crashDate, DUMMY_DATE); Assert.equal(crash.type, m.PROCESS_TYPE_PLUGIN + "-" + m.CRASH_TYPE_HANG); Assert.ok(crash.isOfType(m.PROCESS_TYPE_PLUGIN, m.CRASH_TYPE_HANG)); }); + +add_task(function* test_addSubmission() { + let m = yield getManager(); + + let crashes = yield m.getCrashes(); + Assert.equal(crashes.length, 0); + + yield m.addSubmission(m.PROCESS_TYPE_MAIN, m.CRASH_TYPE_CRASH, true, + "success", DUMMY_DATE); + yield m.addSubmission(m.PROCESS_TYPE_MAIN, m.CRASH_TYPE_CRASH, false, + "failure", DUMMY_DATE); + + crashes = yield m.getCrashes(); + Assert.equal(crashes.length, 2); + + let map = new Map(crashes.map(crash => [crash.id, crash])); + + let crash = map.get("success-submission"); + Assert.ok(!!crash); + Assert.equal(crash.crashDate, DUMMY_DATE); + Assert.equal(crash.type, + m.PROCESS_TYPE_MAIN + "-" + m.CRASH_TYPE_CRASH + "-" + + m.PROCESS_TYPE_SUBMISSION + "-" + m.SUBMISSION_TYPE_SUCCEEDED); + Assert.ok( + crash.isOfType(m.PROCESS_TYPE_MAIN + "-" + m.CRASH_TYPE_CRASH + "-" + + m.PROCESS_TYPE_SUBMISSION, m.SUBMISSION_TYPE_SUCCEEDED)); + + let crash = map.get("failure-submission"); + Assert.ok(!!crash); + Assert.equal(crash.crashDate, DUMMY_DATE); + Assert.equal(crash.type, + m.PROCESS_TYPE_MAIN + "-" + m.CRASH_TYPE_CRASH + "-" + + m.PROCESS_TYPE_SUBMISSION + "-" + m.SUBMISSION_TYPE_FAILED); + Assert.ok( + crash.isOfType(m.PROCESS_TYPE_MAIN + "-" + m.CRASH_TYPE_CRASH + "-" + + m.PROCESS_TYPE_SUBMISSION, m.SUBMISSION_TYPE_FAILED)); + +}); +
--- a/toolkit/components/crashes/tests/xpcshell/test_crash_store.js +++ b/toolkit/components/crashes/tests/xpcshell/test_crash_store.js @@ -12,18 +12,21 @@ const {classes: Cc, interfaces: Ci, util let bsp = Cu.import("resource://gre/modules/CrashManager.jsm", this); Cu.import("resource://gre/modules/osfile.jsm", this); Cu.import("resource://gre/modules/Task.jsm", this); const { PROCESS_TYPE_MAIN, PROCESS_TYPE_CONTENT, PROCESS_TYPE_PLUGIN, + PROCESS_TYPE_SUBMISSION, CRASH_TYPE_CRASH, CRASH_TYPE_HANG, + SUBMISSION_TYPE_SUCCEEDED, + SUBMISSION_TYPE_FAILED, } = CrashManager.prototype; const CrashStore = bsp.CrashStore; let STORE_DIR_COUNT = 0; function getStore() { return Task.spawn(function* () { @@ -268,16 +271,54 @@ add_task(function* test_add_plugin_hang( s.addCrash(PROCESS_TYPE_PLUGIN, CRASH_TYPE_HANG, "id1", new Date()) ); Assert.equal(s.crashesCount, 2); let crashes = s.getCrashesOfType(PROCESS_TYPE_PLUGIN, CRASH_TYPE_HANG); Assert.equal(crashes.length, 2); }); +add_task(function* test_add_submission() { + let s = yield getStore(); + + Assert.ok( + s.addCrash(PROCESS_TYPE_MAIN + "-" + CRASH_TYPE_CRASH + "-" + + PROCESS_TYPE_SUBMISSION, SUBMISSION_TYPE_SUCCEEDED, + "id1", new Date()) + ); + Assert.equal(s.crashesCount, 1); + + let c = s.crashes[0]; + Assert.ok(c.crashDate); + Assert.equal(c.type, PROCESS_TYPE_MAIN + "-" + CRASH_TYPE_CRASH + "-" + + PROCESS_TYPE_SUBMISSION + "-" + SUBMISSION_TYPE_SUCCEEDED); + Assert.ok(c.isOfType(PROCESS_TYPE_MAIN + "-" + CRASH_TYPE_CRASH + "-" + + PROCESS_TYPE_SUBMISSION, SUBMISSION_TYPE_SUCCEEDED)); + + Assert.ok( + s.addCrash(PROCESS_TYPE_MAIN + "-" + CRASH_TYPE_CRASH + "-" + + PROCESS_TYPE_SUBMISSION, SUBMISSION_TYPE_FAILED, + "id2", new Date()) + ); + Assert.equal(s.crashesCount, 2); + + // Duplicate. + Assert.ok( + s.addCrash(PROCESS_TYPE_MAIN + "-" + CRASH_TYPE_CRASH + "-" + + PROCESS_TYPE_SUBMISSION, SUBMISSION_TYPE_SUCCEEDED, + "id1", new Date()) + ); + Assert.equal(s.crashesCount, 2); + + let crashes = s.getCrashesOfType(PROCESS_TYPE_MAIN + "-" + CRASH_TYPE_CRASH + + "-" + PROCESS_TYPE_SUBMISSION, + SUBMISSION_TYPE_SUCCEEDED); + Assert.equal(crashes.length, 1); +}); + add_task(function* test_add_mixed_types() { let s = yield getStore(); Assert.ok( s.addCrash(PROCESS_TYPE_MAIN, CRASH_TYPE_CRASH, "mcrash", new Date()) && s.addCrash(PROCESS_TYPE_MAIN, CRASH_TYPE_HANG, "mhang", new Date()) && s.addCrash(PROCESS_TYPE_CONTENT, CRASH_TYPE_CRASH, "ccrash", new Date()) && s.addCrash(PROCESS_TYPE_CONTENT, CRASH_TYPE_HANG, "chang", new Date()) &&
--- a/toolkit/crashreporter/client/crashreporter.cpp +++ b/toolkit/crashreporter/client/crashreporter.cpp @@ -27,19 +27,22 @@ using std::ostream; using std::ofstream; using std::vector; using std::auto_ptr; namespace CrashReporter { StringTable gStrings; string gSettingsPath; +string gEventsPath; int gArgc; char** gArgv; +enum SubmissionResult {Succeeded, Failed}; + static auto_ptr<ofstream> gLogStream(nullptr); static string gReporterDumpFile; static string gExtraFile; static string kExtraDataExtension = ".extra"; void UIError(const string& message) { @@ -167,16 +170,63 @@ bool WriteStringsToFile(const string& pa success = WriteStrings(*f, header, strings, escape); f->close(); } delete f; return success; } +static string Basename(const string& file) +{ + string::size_type slashIndex = file.rfind(UI_DIR_SEPARATOR); + if (slashIndex != string::npos) + return file.substr(slashIndex + 1); + else + return file; +} + +static string GetDumpLocalID() +{ + string localId = Basename(gReporterDumpFile); + string::size_type dot = localId.rfind('.'); + + if (dot == string::npos) + return ""; + + return localId.substr(0, dot); +} + +static void WriteSubmissionEvent(SubmissionResult result, + const string& remoteId) +{ + if (gEventsPath.empty()) { + // If there is no path for writing the submission event, skip it. + return; + } + + string localId = GetDumpLocalID(); + string fpath = gEventsPath + UI_DIR_SEPARATOR + localId + "-submission"; + ofstream* f = UIOpenWrite(fpath.c_str()); + time_t tm; + time(&tm); + + if (f->is_open()) { + *f << "crash.submission.1\n"; + *f << tm << "\n"; + *f << localId << "\n"; + *f << (result == Succeeded ? "true" : "false") << "\n"; + *f << remoteId; + + f->close(); + } + + delete f; +} + void LogMessage(const std::string& message) { if (gLogStream.get()) { char date[64]; time_t tm; time(&tm); if (strftime(date, sizeof(date) - 1, "%c", localtime(&tm)) == 0) date[0] = '\0'; @@ -213,25 +263,16 @@ static string GetExtraDataFilename(const int dot = filename.rfind('.'); if (dot < 0) return ""; filename.replace(dot, filename.length() - dot, kExtraDataExtension); return filename; } -static string Basename(const string& file) -{ - int slashIndex = file.rfind(UI_DIR_SEPARATOR); - if (slashIndex >= 0) - return file.substr(slashIndex + 1); - else - return file; -} - static bool MoveCrashData(const string& toDir, string& dumpfile, string& extrafile) { if (!UIEnsurePathExists(toDir)) { UIError(gStrings[ST_ERROR_CREATEDUMPDIR]); return false; } @@ -311,16 +352,17 @@ static bool AddSubmittedReport(const str gStrings["CrashDetailsURL"].c_str(), responseItems["ViewURL"].c_str()); *file << buf << "\n"; } file->close(); delete file; + WriteSubmissionEvent(Succeeded, responseItems["CrashID"]); return true; } void DeleteDump() { const char* noDelete = getenv("MOZ_CRASHREPORTER_NO_DELETE_DUMP"); if (!noDelete || *noDelete == '\0') { if (!gReporterDumpFile.empty()) @@ -338,17 +380,20 @@ void SendCompleted(bool success, const s } else { string directory = gReporterDumpFile; int slashpos = directory.find_last_of("/\\"); if (slashpos < 2) return; directory.resize(slashpos); UIPruneSavedDumps(directory); + WriteSubmissionEvent(Failed, ""); } + } else { + WriteSubmissionEvent(Failed, ""); } } bool ShouldEnableSending() { srand(time(0)); return ((rand() % 100) < MOZ_CRASHREPORTER_ENABLE_PERCENT); } @@ -509,16 +554,33 @@ int main(int argc, char** argv) if (gSettingsPath.empty() || !UIEnsurePathExists(gSettingsPath)) { UIError(gStrings[ST_ERROR_NOSETTINGSPATH]); return 0; } OpenLogFile(); +#ifdef XP_WIN32 + static const wchar_t kEventsDirKey[] = L"MOZ_CRASHREPORTER_EVENTS_DIRECTORY"; + const wchar_t *eventsPath = _wgetenv(kEventsDirKey); + if (eventsPath && *eventsPath) { + gEventsPath = WideToUTF8(eventsPath); + } +#else + static const char kEventsDirKey[] = "MOZ_CRASHREPORTER_EVENTS_DIRECTORY"; + const char *eventsPath = getenv(kEventsDirKey); + if (eventsPath && *eventsPath) { + gEventsPath = eventsPath; + } +#endif + else { + gEventsPath.clear(); + } + if (!UIFileExists(gReporterDumpFile)) { UIError(gStrings[ST_ERROR_DUMPFILEEXISTS]); return 0; } string pendingDir = gSettingsPath + UI_DIR_SEPARATOR + "pending"; if (!MoveCrashData(pendingDir, gReporterDumpFile, gExtraFile)) { return 0;
--- a/toolkit/crashreporter/client/crashreporter.h +++ b/toolkit/crashreporter/client/crashreporter.h @@ -77,16 +77,17 @@ typedef std::map<std::string, std::strin //============================================================================= // implemented in crashreporter.cpp //============================================================================= namespace CrashReporter { extern StringTable gStrings; extern std::string gSettingsPath; + extern std::string gEventsPath; extern int gArgc; extern char** gArgv; void UIError(const std::string& message); // The UI finished sending the report void SendCompleted(bool success, const std::string& serverResponse);
--- a/toolkit/crashreporter/nsExceptionHandler.cpp +++ b/toolkit/crashreporter/nsExceptionHandler.cpp @@ -158,16 +158,17 @@ static const char kCrashMainID[] = "cras static google_breakpad::ExceptionHandler* gExceptionHandler = nullptr; static XP_CHAR* pendingDirectory; static XP_CHAR* crashReporterPath; // Where crash events should go. static XP_CHAR* eventsDirectory; +static char* eventsEnv = nullptr; // If this is false, we don't launch the crash reporter static bool doReport = true; // If this is true, we don't have a crash reporter static bool headlessClient = false; // if this is true, we pass the exception on to the OS crash reporter @@ -2102,20 +2103,39 @@ SetCrashEventsDir(nsIFile* aDir) if (eventsDirectory) { NS_Free(eventsDirectory); } #ifdef XP_WIN nsString path; eventsDir->GetPath(path); eventsDirectory = reinterpret_cast<wchar_t*>(ToNewUnicode(path)); + + // Save the path in the environment for the crash reporter application. + nsAutoString eventsDirEnv(NS_LITERAL_STRING("MOZ_CRASHREPORTER_EVENTS_DIRECTORY=")); + eventsDirEnv.Append(path); + _wputenv(eventsDirEnv.get()); #else nsCString path; eventsDir->GetNativePath(path); eventsDirectory = ToNewCString(path); + + // Save the path in the environment for the crash reporter application. + nsAutoCString eventsDirEnv("MOZ_CRASHREPORTER_EVENTS_DIRECTORY="); + eventsDirEnv.Append(path); + + // PR_SetEnv() wants the string to be available for the lifetime + // of the app, so dup it here. + char* oldEventsEnv = eventsEnv; + eventsEnv = ToNewCString(eventsDirEnv); + PR_SetEnv(eventsEnv); + + if (oldEventsEnv) { + NS_Free(oldEventsEnv); + } #endif } void SetProfileDirectory(nsIFile* aDir) { nsCOMPtr<nsIFile> dir; aDir->Clone(getter_AddRefs(dir));
--- a/toolkit/mozapps/update/tests/chrome/test_0084_error_patchApplyFailure_verify_failed.xul +++ b/toolkit/mozapps/update/tests/chrome/test_0084_error_patchApplyFailure_verify_failed.xul @@ -11,17 +11,16 @@ <window title="Update Wizard pages: error patching, download, and errors (partial failed and download complete verification failure)" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" onload="runTestDefault();"> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> <script type="application/javascript" src="utils.js"/> -<script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"></script> <script type="application/javascript"> <![CDATA[ const TESTS = [ { pageid: PAGEID_ERROR_PATCHING, buttonClick: "next" }, { pageid: PAGEID_DOWNLOADING,
--- a/toolkit/themes/osx/global/global.css +++ b/toolkit/themes/osx/global/global.css @@ -348,16 +348,17 @@ notification > button > .button-box > .b } .close-icon:hover:active { -moz-image-region: rect(0, 48px, 16px, 32px); } @media (min-resolution: 2dppx) { .close-icon > .button-icon, + .close-icon > .button-box > .button-icon, .close-icon > .toolbarbutton-icon { width: 16px; } .close-icon { list-style-image: url("chrome://global/skin/icons/close@2x.png"); -moz-image-region: rect(0, 32px, 32px, 0); }
--- a/webapprt/content/mochitest-shared.js +++ b/webapprt/content/mochitest-shared.js @@ -1,16 +1,17 @@ /* 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/. */ /* Note: this script is loaded by both mochitest.js and head.js, so make sure * the code you put here can be evaluated by both! */ Cu.import("resource://webapprt/modules/WebappRT.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); // When WebappsHandler opens an install confirmation dialog for apps we install, // close it, which will be seen as the equivalent of cancelling the install. // This doesn't prevent us from installing those apps, as we listen for the same // notification as WebappsHandler and do the install ourselves. It just // prevents the modal installation confirmation dialogs from hanging tests. Services.ww.registerNotification({ observe: function(win, topic) { @@ -37,53 +38,53 @@ Services.ww.registerNotification({ * @param {Object} parameters * The value to pass as the "parameters" argument to * mozIDOMApplicationRegistry.install, e.g., { receipts: ... }. * Use undefined to pass nothing. * @param {Function} onBecome * The callback to call once the transmogrification is complete. */ function becomeWebapp(manifestURL, parameters, onBecome) { - function observeInstall(subj, topic, data) { + let observeInstall = Task.async(function*(subj, topic, data) { Services.obs.removeObserver(observeInstall, "webapps-ask-install"); // Step 2: Configure the runtime session to represent the app. // We load DOMApplicationRegistry into a local scope to avoid appearing // to leak it. let scope = {}; Cu.import("resource://gre/modules/Webapps.jsm", scope); Cu.import("resource://webapprt/modules/Startup.jsm", scope); - scope.DOMApplicationRegistry.confirmInstall(JSON.parse(data)); + yield scope.DOMApplicationRegistry.confirmInstall(JSON.parse(data)); let installRecord = JSON.parse(data); installRecord.mm = subj; installRecord.registryDir = Services.dirsvc.get("ProfD", Ci.nsIFile).path; WebappRT.config = installRecord; let win = Services.wm.getMostRecentWindow("webapprt:webapp"); if (!win) { win = Services.ww.openWindow(null, "chrome://webapprt/content/webapp.xul", "_blank", "chrome,dialog=no,resizable,scrollbars,centerscreen", null); } - let promise = scope.startup(win); - // During chrome tests, we use the same window to load all the tests. We // need to change the buildID so that the permissions for the currently // tested application get installed. Services.prefs.setCharPref("webapprt.buildID", WebappRT.config.app.manifestURL); - // During tests, the webapps registry is already loaded. - // The Startup module needs to be notified when the webapps registry - // gets loaded, so we do that now. + // During tests, the webapps registry is already loaded, + // but SystemMessageInternal expects to be notified when the registry + // start and then when it's ready, so we do that now. Services.obs.notifyObservers(this, "webapps-registry-start", null); + Services.obs.notifyObservers(this, "webapps-registry-ready", null); - promise.then(onBecome); - } + yield scope.startup(win); + onBecome(); + }); Services.obs.addObserver(observeInstall, "webapps-ask-install", false); // Step 1: Install the app at the URL specified by the manifest. navigator.mozApps.install(manifestURL, parameters); }
--- a/webapprt/content/mochitest.xul +++ b/webapprt/content/mochitest.xul @@ -1,16 +1,17 @@ <?xml version="1.0"?> <!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this file, - You can obtain one at http://mozilla.org/MPL/2.0/. --> <?xml-stylesheet href="chrome://global/skin/" type="text/css"?> -<window windowtype="webapprt:mochitest" +<window id="browserTestHarness" + windowtype="webapprt:mochitest" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <script type="application/javascript" src="chrome://webapprt/content/mochitest.js"/> <description value="WebappRT Test Shim"/> </window>