From 89402898f0d67a2ac99ccb94c6782aff152173ec Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Mon, 15 Jun 2015 10:13:03 -0700 Subject: [PATCH 001/134] Bug 1136055 - 'Save link as ...' should be treated as toplevel load. r=Gijs --- browser/base/content/nsContextMenu.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js index f5b752a32415..9361c329b8cc 100644 --- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -1298,7 +1298,9 @@ nsContextMenu.prototype = { } } - // set up a channel to do the saving + // setting up a new channel for 'right click - save link as ...' + // which should be treated the same way as a toplevel load, hence + // we use TYPE_DOCUMENT, see also bug: 1136055 var ioService = Cc["@mozilla.org/network/io-service;1"]. getService(Ci.nsIIOService); var channel = ioService.newChannelFromURI2(makeURI(linkURL), @@ -1306,7 +1308,7 @@ nsContextMenu.prototype = { this.principal, // aLoadingPrincipal null, // aTriggeringPrincipal Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + Ci.nsIContentPolicy.TYPE_DOCUMENT); if (linkDownload) channel.contentDispositionFilename = linkDownload; if (channel instanceof Ci.nsIPrivateBrowsingChannel) { From 2d88648a97bc75dbcdbe8a9df7274ff8fff8c86e Mon Sep 17 00:00:00 2001 From: dominique vincent Date: Sat, 13 Jun 2015 22:42:20 +0200 Subject: [PATCH 002/134] Bug 1165127 - Animations for displaying/closing the zoomed view. r=mcomella --- mobile/android/base/ZoomedView.java | 152 +++++++++++++++++++++++----- 1 file changed, 129 insertions(+), 23 deletions(-) diff --git a/mobile/android/base/ZoomedView.java b/mobile/android/base/ZoomedView.java index f8809e86c0ad..da4ecf5db527 100644 --- a/mobile/android/base/ZoomedView.java +++ b/mobile/android/base/ZoomedView.java @@ -35,6 +35,10 @@ import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewTreeObserver; +import android.view.animation.Animation; +import android.view.animation.Animation.AnimationListener; +import android.view.animation.OvershootInterpolator; +import android.view.animation.ScaleAnimation; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.RelativeLayout; @@ -52,6 +56,9 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange private static final int H_CAPTURED_VIEW_IN_PERCENT = 50; private static final int MINIMUM_DELAY_BETWEEN_TWO_RENDER_CALLS_NS = 1000000; private static final int DELAY_BEFORE_NEXT_RENDER_REQUEST_MS = 2000; + private static final int OPENING_ANIMATION_DURATION_MS = 250; + private static final int CLOSING_ANIMATION_DURATION_MS = 150; + private static final float OVERSHOOT_INTERPOLATOR_TENSION = 1.5f; private float zoomFactor; private int currentZoomFactorIndex; @@ -65,6 +72,7 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange private Point lastPosition; private boolean shouldSetVisibleOnUpdate; private PointF returnValue; + private final PointF animationStart; private ImageView closeButton; private TextView changeZoomFactorButton; private boolean toolbarOnTop; @@ -81,6 +89,12 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange private long startTimeReRender; private long lastStartTimeReRender; + private ZoomedViewTouchListener touchListener; + + private enum StartPointUpdate { + GECKO_POSITION, CENTER, NO_CHANGE + } + private class RoundedBitmapDrawable extends BitmapDrawable { private Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG); final float cornerRadius; @@ -182,7 +196,7 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange float newLeftMargin = params.leftMargin + event.getRawX() - originRawX; float newTopMargin = params.topMargin + event.getRawY() - originRawY; ImmutableViewportMetrics metrics = layerView.getViewportMetrics(); - ZoomedView.this.moveZoomedView(metrics, newLeftMargin, newTopMargin); + ZoomedView.this.moveZoomedView(metrics, newLeftMargin, newTopMargin, StartPointUpdate.CENTER); originRawX = event.getRawX(); originRawY = event.getRawY(); return true; @@ -200,6 +214,7 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange public ZoomedView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); returnValue = new PointF(); + animationStart = new PointF(); currentZoomFactorIndex = 0; zoomFactor = ZOOM_FACTORS_LIST[currentZoomFactorIndex]; requestRenderRunnable = new Runnable() { @@ -208,6 +223,7 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange requestZoomedViewRender(); } }; + touchListener = new ZoomedViewTouchListener(); EventDispatcher.getInstance().registerGeckoThreadListener(this, "Gesture:clusteredLinksClicked", "Window:Resize", "Content:LocationChange"); } @@ -225,22 +241,10 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange protected void onFinishInflate() { super.onFinishInflate(); closeButton = (ImageView) findViewById(R.id.dialog_close); - closeButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View view) { - stopZoomDisplay(); - } - }); - changeZoomFactorButton = (TextView) findViewById(R.id.change_zoom_factor); - changeZoomFactorButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View view) { - changeZoomFactor(); - } - }); - setTextInZoomFactorButton(ZOOM_FACTORS_LIST[0]); - zoomedImageView = (ImageView) findViewById(R.id.zoomed_image_view); - this.setOnTouchListener(new ZoomedViewTouchListener()); + + setTextInZoomFactorButton(ZOOM_FACTORS_LIST[0]); toolbarHeight = getResources().getDimensionPixelSize(R.dimen.zoomed_view_toolbar_height); containterSize = getResources().getDimensionPixelSize(R.dimen.drawable_dropshadow_size); @@ -249,6 +253,29 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange moveToolbar(true); } + private void setListeners() { + closeButton.setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + stopZoomDisplay(true); + } + }); + + changeZoomFactorButton.setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + changeZoomFactor(); + } + }); + + setOnTouchListener(touchListener); + } + + private void removeListeners() { + closeButton.setOnClickListener(null); + + changeZoomFactorButton.setOnClickListener(null); + + setOnTouchListener(null); + } /* * Convert a click from ZoomedView. Return the position of the click in the * LayerView @@ -323,7 +350,8 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange return returnValue; } - private void moveZoomedView(ImmutableViewportMetrics metrics, float newLeftMargin, float newTopMargin) { + private void moveZoomedView(ImmutableViewportMetrics metrics, float newLeftMargin, float newTopMargin, + StartPointUpdate animateStartPoint) { final float parentWidth = metrics.getWidth(); final float parentHeight = metrics.getHeight(); RelativeLayout.LayoutParams newLayoutParams = (RelativeLayout.LayoutParams) getLayoutParams(); @@ -353,6 +381,22 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange moveToolbar(true); } + if (animateStartPoint == StartPointUpdate.GECKO_POSITION) { + // Before this point, the animationStart point is relative to the layerView. + // The value is initialized in startZoomDisplay using the click point position coming from Gecko. + // The position of the zoomed view is now calculated, so the position of the animation + // can now be correctly set relative to the zoomed view + animationStart.x = animationStart.x - newLayoutParams.leftMargin; + animationStart.y = animationStart.y - newLayoutParams.topMargin; + } else if (animateStartPoint == StartPointUpdate.CENTER) { + // At this point, the animationStart point is no more valid probably because + // the zoomed view has been moved by the user. + // In this case, the animationStart point is set to the center point of the zoomed view. + PointF convertedPosition = getUnzoomedPositionFromPointInZoomedView(viewContainerWidth / 2, viewContainerHeight / 2); + animationStart.x = convertedPosition.x - newLayoutParams.leftMargin; + animationStart.y = convertedPosition.y - newLayoutParams.topMargin; + } + setLayoutParams(newLayoutParams); PointF convertedPosition = getUnzoomedPositionFromPointInZoomedView(0, 0); lastPosition = PointUtils.round(convertedPosition); @@ -409,7 +453,7 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) getLayoutParams(); setCapturedSize(viewport); - moveZoomedView(viewport, params.leftMargin, params.topMargin); + moveZoomedView(viewport, params.leftMargin, params.topMargin, StartPointUpdate.NO_CHANGE); } private void setCapturedSize(ImmutableViewportMetrics metrics) { @@ -443,12 +487,21 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange } startTimeReRender = 0; shouldSetVisibleOnUpdate = true; + + ImmutableViewportMetrics metrics = layerView.getViewportMetrics(); + PointF offset = metrics.getMarginOffset(); + // At this point, the start point is relative to the layerView. + // Later, it will be converted relative to the zoomed view as soon as + // the position of the zoomed view will be calculated. + animationStart.x = (float) leftFromGecko * metrics.zoomFactor + offset.x; + animationStart.y = (float) topFromGecko * metrics.zoomFactor + offset.y; + moveUsingGeckoPosition(leftFromGecko, topFromGecko); } - private void stopZoomDisplay() { + private void stopZoomDisplay(boolean withAnimation) { shouldSetVisibleOnUpdate = false; - this.setVisibility(View.GONE); + hideZoomedView(withAnimation); ThreadUtils.removeCallbacksFromUiThread(requestRenderRunnable); if (layerView != null) { layerView.setOnMetricsChangedZoomedViewportListener(null); @@ -494,7 +547,7 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange ImmutableViewportMetrics metrics = layerView.getViewportMetrics(); refreshZoomedViewSize(metrics); } else if (event.equals("Content:LocationChange")) { - stopZoomDisplay(); + stopZoomDisplay(false); } } catch (JSONException e) { Log.e(LOGTAG, "JSON exception", e); @@ -511,7 +564,7 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange moveToolbar((topFromGecko * metrics.zoomFactor > parentHeight / 2)); PointF convertedPosition = getZoomedViewTopLeftPositionFromTouchPosition((leftFromGecko * metrics.zoomFactor), (topFromGecko * metrics.zoomFactor)); - moveZoomedView(metrics, convertedPosition.x, convertedPosition.y); + moveZoomedView(metrics, convertedPosition.x, convertedPosition.y, StartPointUpdate.GECKO_POSITION); } @Override @@ -548,13 +601,66 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange } } if (shouldSetVisibleOnUpdate) { - this.setVisibility(View.VISIBLE); - shouldSetVisibleOnUpdate = false; + this.showZoomedView(); } lastStartTimeReRender = startTimeReRender; startTimeReRender = 0; } + private void showZoomedView() { + // no animation if the zoomed view is already visible + if (getVisibility() != View.VISIBLE) { + final Animation anim = new ScaleAnimation( + 0f, 1f, // Start and end values for the X axis scaling + 0f, 1f, // Start and end values for the Y axis scaling + Animation.ABSOLUTE, animationStart.x, // Pivot point of X scaling + Animation.ABSOLUTE, animationStart.y); // Pivot point of Y scaling + anim.setFillAfter(true); // Needed to keep the result of the animation + anim.setDuration(OPENING_ANIMATION_DURATION_MS); + anim.setInterpolator(new OvershootInterpolator(OVERSHOOT_INTERPOLATOR_TENSION)); + anim.setAnimationListener(new AnimationListener() { + public void onAnimationEnd(Animation animation) { + setListeners(); + } + public void onAnimationRepeat(Animation animation) { + } + public void onAnimationStart(Animation animation) { + removeListeners(); + } + }); + setAnimation(anim); + } + setVisibility(View.VISIBLE); + shouldSetVisibleOnUpdate = false; + } + + private void hideZoomedView(boolean withAnimation) { + if (withAnimation) { + final Animation anim = new ScaleAnimation( + 1f, 0f, // Start and end values for the X axis scaling + 1f, 0f, // Start and end values for the Y axis scaling + Animation.ABSOLUTE, animationStart.x, // Pivot point of X scaling + Animation.ABSOLUTE, animationStart.y); // Pivot point of Y scaling + anim.setFillAfter(true); // Needed to keep the result of the animation + anim.setDuration(CLOSING_ANIMATION_DURATION_MS); + anim.setAnimationListener(new AnimationListener() { + public void onAnimationEnd(Animation animation) { + } + public void onAnimationRepeat(Animation animation) { + } + public void onAnimationStart(Animation animation) { + removeListeners(); + } + }); + setAnimation(anim); + } else { + removeListeners(); + setAnimation(null); + } + setVisibility(View.GONE); + shouldSetVisibleOnUpdate = false; + } + private void updateBufferSize() { int pixelSize = (GeckoAppShell.getScreenDepth() == 24) ? 4 : 2; int capacity = viewWidth * viewHeight * pixelSize; From a67b660b655ecbf1d2cbc88f4c250ca349b297a7 Mon Sep 17 00:00:00 2001 From: dominique vincent Date: Fri, 12 Jun 2015 22:05:21 +0200 Subject: [PATCH 003/134] Bug 1172488 - Small clickable text nodes are wrongly detected in cluster detection process. r=kats --- layout/base/PositionedEventTargeting.cpp | 49 +++++++++++++++++++----- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/layout/base/PositionedEventTargeting.cpp b/layout/base/PositionedEventTargeting.cpp index 32ee30775c06..9cefdddb2d6b 100644 --- a/layout/base/PositionedEventTargeting.cpp +++ b/layout/base/PositionedEventTargeting.cpp @@ -387,11 +387,14 @@ GetClosest(nsIFrame* aRoot, const nsPoint& aPointRelativeToRootFrame, /* * Return always true when touch cluster detection is OFF. - * When cluster detection is ON, return true if the text inside - * the frame is readable (by human eyes): - * in this case, the frame is really clickable. - * Frames with a too small size will return false: - * in this case, the frame is considered not clickable. + * When cluster detection is ON, return true: + * if the text inside the frame is readable (by human eyes) + * or + * if the structure is too complex to determine the size. + * In both cases, the frame is considered as clickable. + * + * Frames with a too small size will return false. + * In this case, the frame is considered not clickable. */ static bool IsElementClickableAndReadable(nsIFrame* aFrame, WidgetGUIEvent* aEvent, const EventRadiusPrefs* aPrefs) @@ -413,11 +416,37 @@ IsElementClickableAndReadable(nsIFrame* aFrame, WidgetGUIEvent* aEvent, const Ev (pc->AppUnitsToGfxUnits(frameSize.width) * cumulativeResolution) < limitReadableSize) { return false; } - nsRefPtr fm; - nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm), - nsLayoutUtils::FontSizeInflationFor(aFrame)); - if (fm) { - if ((fm->EmHeight() > 0) && // See bug 1171731 + // We want to detect small clickable text elements using the font size. + // Two common cases are supported for now: + // 1. text node + // 2. any element with only one child of type text node + // All the other cases are currently ignored. + nsIContent *content = aFrame->GetContent(); + bool testFontSize = false; + if (content) { + nsINodeList* childNodes = content->ChildNodes(); + uint32_t childNodeCount = childNodes->Length(); + if ((content->IsNodeOfType(nsINode::eTEXT)) || + // click occurs on the text inside or other clickable tags with text inside + + (childNodeCount == 1 && childNodes->Item(0) && + childNodes->Item(0)->IsNodeOfType(nsINode::eTEXT))) { + // The click occurs on an element with only one text node child. In this case, the font size + // can be tested. + // The number of child nodes is tested to avoid the following cases (See bug 1172488): + // Some jscript libraries transform text elements into Canvas elements but keep the text nodes + // with a very small size (1px) to handle the selection of text. + // With such libraries, the font size of the text elements is not relevant to detect small elements. + + testFontSize = true; + } + } + + if (testFontSize) { + nsRefPtr fm; + nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm), + nsLayoutUtils::FontSizeInflationFor(aFrame)); + if (fm && fm->EmHeight() > 0 && // See bug 1171731 (pc->AppUnitsToGfxUnits(fm->EmHeight()) * cumulativeResolution) < limitReadableSize) { return false; } From 08ceb34ed578cb1bde4ddcfd910b829d3cad1fad Mon Sep 17 00:00:00 2001 From: Allison Naaktgeboren Date: Mon, 15 Jun 2015 13:21:38 -0700 Subject: [PATCH 004/134] Bug 1144413 - Remove details page from about:passwords.r=liuche --- mobile/android/chrome/content/aboutLogins.js | 86 ------------------- .../android/chrome/content/aboutLogins.xhtml | 17 ---- .../locales/en-US/chrome/aboutLogins.dtd | 3 - .../en-US/chrome/aboutLogins.properties | 1 - mobile/android/themes/core/aboutLogins.css | 8 -- 5 files changed, 115 deletions(-) diff --git a/mobile/android/chrome/content/aboutLogins.js b/mobile/android/chrome/content/aboutLogins.js index eeb378c5fe99..329deea25c0e 100644 --- a/mobile/android/chrome/content/aboutLogins.js +++ b/mobile/android/chrome/content/aboutLogins.js @@ -63,10 +63,6 @@ let Logins = { this._loadList(this._getLogins()); - document.getElementById("copyusername-btn").addEventListener("click", this._copyUsername.bind(this), false); - document.getElementById("copypassword-btn").addEventListener("click", this._copyPassword.bind(this), false); - document.getElementById("details-header").addEventListener("click", this._openLink.bind(this), false); - let filterInput = document.getElementById("filter-input"); let filterContainer = document.getElementById("filter-input-container"); @@ -125,26 +121,10 @@ let Logins = { }, _showList: function () { - // Hide the detail page and show the list - let details = document.getElementById("login-details"); - details.setAttribute("hidden", "true"); let list = document.getElementById("logins-list"); list.removeAttribute("hidden"); }, - _onPopState: function (event) { - // Called when back/forward is used to change the state of the page - if (event.state) { - // Show the detail page for an addon - this._showDetails(this._getElementForLogin(event.state.id)); - } else { - // Clear any previous detail addon - let detailItem = document.querySelector("#login-details > .login-item"); - detailItem.login = null; - this._showList(); - } - }, - _onLoginClick: function (event) { let loginItem = event.currentTarget; let login = loginItem.login; @@ -160,7 +140,6 @@ let Logins = { { label: gStringBundle.GetStringFromName("loginsMenu.showPassword") }, { label: gStringBundle.GetStringFromName("loginsMenu.copyPassword") }, { label: gStringBundle.GetStringFromName("loginsMenu.copyUsername") }, - { label: gStringBundle.GetStringFromName("loginsMenu.details") }, { label: gStringBundle.GetStringFromName("loginsMenu.delete") } ]; @@ -190,10 +169,6 @@ let Logins = { copyStringAndToast(login.username, gStringBundle.GetStringFromName("loginsDetails.usernameCopied")); break; case 3: - this._showDetails(loginItem); - history.pushState({ id: login.guid }, document.title); - break; - case 4: let confirmPrompt = new Prompt({ window: window, message: gStringBundle.GetStringFromName("loginsDialog.confirmDelete"), @@ -265,12 +240,6 @@ let Logins = { return loginItem; }, - _getElementForLogin: function (login) { - let list = document.getElementById("logins-list"); - let element = list.querySelector("div[loginID=" + login.quote() + "]"); - return element; - }, - handleEvent: function (event) { switch (event.type) { case "popstate": { @@ -294,61 +263,6 @@ let Logins = { } }, - _showDetails: function (listItem) { - let detailItem = document.querySelector("#login-details > .login-item"); - let login = detailItem.login = listItem.login; - let favicon = detailItem.querySelector(".icon"); - favicon.style["background-image"] = listItem.querySelector(".icon").style["background-image"]; - favicon.style.visibility = "visible"; - document.getElementById("details-header").setAttribute("link", login.hostname); - - document.getElementById("detail-hostname").textContent = login.hostname; - document.getElementById("detail-realm").textContent = login.httpRealm; - document.getElementById("detail-username").textContent = login.username; - - // Borrowed from desktop Firefox: http://mxr.mozilla.org/mozilla-central/source/browser/base/content/urlbarBindings.xml#204 - let matchedURL = login.hostname.match(/^((?:[a-z]+:\/\/)?(?:[^\/]+@)?)(.+?)(?::\d+)?(?:\/|$)/); - - let userInputs = []; - if (matchedURL) { - let [, , domain] = matchedURL; - userInputs = domain.split(".").filter(part => part.length > 3); - } - - let lastChanged = new Date(login.QueryInterface(Ci.nsILoginMetaInfo).timePasswordChanged); - let days = Math.round((Date.now() - lastChanged) / 1000 / 60 / 60/ 24); - document.getElementById("detail-age").textContent = gStringBundle.formatStringFromName("loginsDetails.age", [days], 1); - - let list = document.getElementById("logins-list"); - list.setAttribute("hidden", "true"); - - let loginDetails = document.getElementById("login-details"); - loginDetails.removeAttribute("hidden"); - - // Password details page is loaded. - let loadEvent = document.createEvent("Events"); - loadEvent.initEvent("PasswordsDetailsLoad", true, false); - window.dispatchEvent(loadEvent); - }, - - _copyUsername: function() { - let detailItem = document.querySelector("#login-details > .login-item"); - let login = detailItem.login; - copyStringAndToast(login.username, gStringBundle.GetStringFromName("loginsDetails.usernameCopied")); - }, - - _copyPassword: function() { - let detailItem = document.querySelector("#login-details > .login-item"); - let login = detailItem.login; - copyStringAndToast(login.password, gStringBundle.GetStringFromName("loginsDetails.passwordCopied")); - }, - - _openLink: function (clickEvent) { - let url = clickEvent.currentTarget.getAttribute("link"); - let BrowserApp = gChromeWin.BrowserApp; - BrowserApp.addTab(url, { selected: true, parentId: BrowserApp.selectedTab.id }); - }, - _filter: function(event) { let value = event.target.value.toLowerCase(); let logins = this._logins.filter((login) => { diff --git a/mobile/android/chrome/content/aboutLogins.xhtml b/mobile/android/chrome/content/aboutLogins.xhtml index 20622b8e1460..1c9f2ab53452 100644 --- a/mobile/android/chrome/content/aboutLogins.xhtml +++ b/mobile/android/chrome/content/aboutLogins.xhtml @@ -29,23 +29,6 @@ -