Merge fx-team to m-c

This commit is contained in:
Wes Kocher 2014-03-07 17:04:34 -08:00
commit b9d79021f5
11 changed files with 189 additions and 56 deletions

View File

@ -9,6 +9,7 @@ support-files =
code_blackboxing_one.js
code_blackboxing_three.js
code_blackboxing_two.js
code_breakpoints-break-on-last-line-of-script-on-reload.js
code_function-search-01.js
code_function-search-02.js
code_function-search-03.js
@ -34,6 +35,7 @@ support-files =
doc_auto-pretty-print-02.html
doc_binary_search.html
doc_blackboxing.html
doc_breakpoints-break-on-last-line-of-script-on-reload.html
doc_closures.html
doc_cmd-break.html
doc_cmd-dbg.html
@ -97,6 +99,7 @@ support-files =
[browser_dbg_break-on-dom-07.js]
[browser_dbg_break-on-dom-08.js]
[browser_dbg_breakpoints-actual-location.js]
[browser_dbg_breakpoints-break-on-last-line-of-script-on-reload.js]
[browser_dbg_breakpoints-button-01.js]
[browser_dbg_breakpoints-button-02.js]
[browser_dbg_breakpoints-contextmenu.js]

View File

@ -0,0 +1,79 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Bug 978019: Setting a breakpoint on the last line of a Debugger.Script and
* reloading should still hit the breakpoint.
*/
const TAB_URL = EXAMPLE_URL + "doc_breakpoints-break-on-last-line-of-script-on-reload.html";
const CODE_URL = EXAMPLE_URL + "code_breakpoints-break-on-last-line-of-script-on-reload.js";
const { promiseInvoke } = require("devtools/async-utils");
function test() {
let gPanel, gDebugger, gThreadClient, gEvents;
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gThreadClient = gDebugger.gThreadClient;
gEvents = gDebugger.EVENTS;
Task.spawn(function* () {
yield waitForSourceShown(gPanel, CODE_URL);
// Pause and set our breakpoints.
yield doInterrupt();
yield promise.all([
gPanel.addBreakpoint({
url: CODE_URL,
line: 2
}),
gPanel.addBreakpoint({
url: CODE_URL,
line: 3
}),
gPanel.addBreakpoint({
url: CODE_URL,
line: 4
})
]);
// Should hit the first breakpoint on reload.
yield promise.all([
reloadActiveTab(gPanel, gEvents.SOURCE_SHOWN),
waitForCaretUpdated(gPanel, 2)
]);
// And should hit the other breakpoints as we resume.
yield promise.all([
doResume(),
waitForCaretUpdated(gPanel, 3)
]);
yield promise.all([
doResume(),
waitForCaretUpdated(gPanel, 4)
]);
yield resumeDebuggerThenCloseAndFinish(gPanel);
});
});
function rdpInvoke(obj, method) {
return promiseInvoke(obj, method)
.then(({error, message }) => {
if (error) {
throw new Error(error + ": " + message);
}
});
}
function doResume() {
return rdpInvoke(gThreadClient, gThreadClient.resume);
}
function doInterrupt() {
return rdpInvoke(gThreadClient, gThreadClient.interrupt);
}
}

View File

@ -0,0 +1,5 @@
var a = (function(){
var b = 9;
console.log("x", b);
return b;
})();

View File

@ -0,0 +1,8 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!DOCTYPE html>
<head>
<meta charset="utf-8"/>
<title>Debugger Break on Last Line of Script on Reload Test Page</title>
</head>
<script src="code_breakpoints-break-on-last-line-of-script-on-reload.js"></script>

View File

@ -16,6 +16,7 @@ let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
let { Promise: promise } = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
let { require } = devtools;
let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
let { BrowserToolboxProcess } = Cu.import("resource:///modules/devtools/ToolboxProcess.jsm", {});
let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});

View File

@ -132,7 +132,7 @@ abstract public class BrowserApp extends GeckoApp
private BrowserToolbar mBrowserToolbar;
private HomePager mHomePager;
private TabsPanel mTabsPanel;
private View mHomePagerContainer;
private ViewGroup mHomePagerContainer;
protected Telemetry.Timer mAboutHomeStartupTimer = null;
private ActionModeCompat mActionMode;
private boolean mShowActionModeEndAnimation = false;
@ -480,7 +480,7 @@ abstract public class BrowserApp extends GeckoApp
}
});
mHomePagerContainer = findViewById(R.id.home_pager_container);
mHomePagerContainer = (ViewGroup) findViewById(R.id.home_pager_container);
mBrowserSearchContainer = findViewById(R.id.search_container);
mBrowserSearch = (BrowserSearch) getSupportFragmentManager().findFragmentByTag(BROWSER_SEARCH_TAG);
@ -1693,8 +1693,17 @@ abstract public class BrowserApp extends GeckoApp
final ViewStub homePagerStub = (ViewStub) findViewById(R.id.home_pager_stub);
mHomePager = (HomePager) homePagerStub.inflate();
HomeBanner homeBanner = (HomeBanner) findViewById(R.id.home_banner);
final HomeBanner homeBanner = (HomeBanner) findViewById(R.id.home_banner);
mHomePager.setBanner(homeBanner);
// Remove the banner from the view hierarchy if it is dismissed.
homeBanner.setOnDismissListener(new HomeBanner.OnDismissListener() {
@Override
public void onDismiss() {
mHomePager.setBanner(null);
mHomePagerContainer.removeView(homeBanner);
}
});
}
mHomePagerContainer.setVisibility(View.VISIBLE);

View File

@ -56,6 +56,13 @@ public class HomeBanner extends LinearLayout
private final TextView mTextView;
private final ImageView mIconView;
// Listener that gets called when the banner is dismissed from the close button.
private OnDismissListener mOnDismissListener;
public interface OnDismissListener {
public void onDismiss();
}
public HomeBanner(Context context) {
this(context, null);
}
@ -84,21 +91,29 @@ public class HomeBanner extends LinearLayout
@Override
public void onClick(View view) {
HomeBanner.this.setVisibility(View.GONE);
// Send the current message id back to JS.
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("HomeBanner:Dismiss", (String) getTag()));
if (mOnDismissListener != null) {
mOnDismissListener.onDismiss();
}
}
});
setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Hide the banner. This does not remove the message from the rotation, so it may appear
// again if the JS onclick handler doesn't choose to remove it.
HomeBanner.this.setVisibility(View.GONE);
// Send the current message id back to JS.
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("HomeBanner:Click", (String) getTag()));
}
});
GeckoAppShell.getEventDispatcher().registerEventListener("HomeBanner:Data", this);
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("HomeBanner:Get", null));
}
@Override
@ -123,44 +138,55 @@ public class HomeBanner extends LinearLayout
mScrollingPages = scrollingPages;
}
public void setOnDismissListener(OnDismissListener listener) {
mOnDismissListener = listener;
}
/**
* Sends a message to gecko to request a new banner message. UI is updated in handleMessage.
*/
public void update() {
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("HomeBanner:Get", null));
}
@Override
public void handleMessage(String event, JSONObject message) {
try {
// Store the current message id to pass back to JS in the view's OnClickListener.
setTag(message.getString("id"));
// Display styled text from an HTML string.
final Spanned text = Html.fromHtml(message.getString("text"));
// Update the banner message on the UI thread.
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
mTextView.setText(text);
setVisibility(VISIBLE);
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("HomeBanner:Shown", (String) getTag()));
// Animate the banner if it is currently active.
if (mActive) {
animateUp();
}
}
});
} catch (JSONException e) {
Log.e(LOGTAG, "Exception handling " + event + " message", e);
return;
}
final String id = message.optString("id");
final String text = message.optString("text");
final String iconURI = message.optString("iconURI");
BitmapUtils.getDrawable(getContext(), iconURI, new BitmapUtils.BitmapLoader() {
// Update the banner message on the UI thread.
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void onBitmapFound(final Drawable d) {
// Hide the image view if we don't have an icon to show.
if (d == null) {
mIconView.setVisibility(View.GONE);
} else {
mIconView.setImageDrawable(d);
public void run() {
// Hide the banner if the message doesn't have valid id and text.
if (TextUtils.isEmpty(id) || TextUtils.isEmpty(text)) {
setVisibility(View.GONE);
return;
}
// Store the current message id to pass back to JS in the view's OnClickListener.
setTag(id);
mTextView.setText(Html.fromHtml(text));
BitmapUtils.getDrawable(getContext(), iconURI, new BitmapUtils.BitmapLoader() {
@Override
public void onBitmapFound(final Drawable d) {
// Hide the image view if we don't have an icon to show.
if (d == null) {
mIconView.setVisibility(View.GONE);
} else {
mIconView.setImageDrawable(d);
}
}
});
setVisibility(View.VISIBLE);
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("HomeBanner:Shown", id));
// Animate the banner if it is currently active.
if (mActive) {
animateUp();
}
}
});

View File

@ -155,6 +155,11 @@ public class HomePager extends ViewPager {
mLoaded = true;
mInitialPanelId = panelId;
// Update the home banner message each time the HomePager is loaded.
if (mHomeBanner != null) {
mHomeBanner.update();
}
// Only animate on post-HC devices, when a non-null animator is given
final boolean shouldAnimate = (animator != null && Build.VERSION.SDK_INT >= 11);

View File

@ -351,6 +351,8 @@ function loadSyncPromoBanner() {
text: text + "<a href=\"#\">" + link + "</a>",
icon: "drawable://sync_promo",
onclick: function() {
// Remove the message, so that it won't show again for the rest of the app lifetime.
Home.banner.remove(id);
Accounts.launchSetup();
},
ondismiss: function() {

View File

@ -52,17 +52,13 @@ let HomeBanner = (function () {
// Holds the messages that will rotate through the banner.
let _messages = {};
// A queue used to keep track of which message to show next.
let _queue = [];
let _handleGet = function() {
// Get the message from the front of the queue, then add it back
// to the end of the queue to show it again later.
let id = _queue.shift();
_queue.push(id);
// Choose a message at random.
let keys = Object.keys(_messages);
let randomId = keys[Math.floor(Math.random() * keys.length)];
let message = _messages[randomId];
let message = _messages[id];
sendMessageToJava({
type: "HomeBanner:Data",
id: message.id,
@ -119,9 +115,6 @@ let HomeBanner = (function () {
let message = new BannerMessage(options);
_messages[message.id] = message;
// Add the new message to the end of the queue.
_queue.push(message.id);
// If this is the first message we're adding, add
// observers to listen for requests from the Java UI.
if (Object.keys(_messages).length == 1) {
@ -150,10 +143,6 @@ let HomeBanner = (function () {
delete _messages[id];
// Remove the message from the queue.
let index = _queue.indexOf(id);
_queue.splice(index, 1);
// If there are no more messages, remove the observers.
if (Object.keys(_messages).length == 0) {
Services.obs.removeObserver(this, "HomeBanner:Get");

View File

@ -1892,8 +1892,7 @@ ThreadActor.prototype = {
* @return The EnvironmentActor for aEnvironment or undefined for host
* functions or functions scoped to a non-debuggee global.
*/
createEnvironmentActor:
function (aEnvironment, aPool) {
createEnvironmentActor: function (aEnvironment, aPool) {
if (!aEnvironment) {
return undefined;
}
@ -1954,8 +1953,7 @@ ThreadActor.prototype = {
* Return a protocol completion value representing the given
* Debugger-provided completion value.
*/
createProtocolCompletionValue:
function (aCompletion) {
createProtocolCompletionValue: function (aCompletion) {
let protoValue = {};
if ("return" in aCompletion) {
protoValue.return = this.createValueGrip(aCompletion.return);
@ -2194,6 +2192,14 @@ ThreadActor.prototype = {
*/
onNewScript: function (aScript, aGlobal) {
this._addScript(aScript);
// |onNewScript| is only fired for top level scripts (AKA staticLevel == 0),
// so we have to make sure to call |_addScript| on every child script as
// well to restore breakpoints in those scripts.
for (let s of aScript.getChildScripts()) {
this._addScript(s);
}
this.sources.sourcesForScript(aScript);
},