This commit is contained in:
Richard Newman 2013-04-10 17:57:37 -07:00
Родитель ef5fbc29f7 512a13dbf8
Коммит f6dfd7b2d3
1080 изменённых файлов: 38487 добавлений и 22518 удалений

Просмотреть файл

@ -17,4 +17,4 @@
# #
# Modifying this file will now automatically clobber the buildbot machines \o/ # Modifying this file will now automatically clobber the buildbot machines \o/
# #
Bug 856349 broke Windows b2g builds. Bug 859894: Hopefully the final WebIDL clobber.

Просмотреть файл

@ -102,7 +102,6 @@ pref("dom.disable_open_during_load", true);
pref("privacy.popups.showBrowserMessage", true); pref("privacy.popups.showBrowserMessage", true);
pref("keyword.enabled", true); pref("keyword.enabled", true);
pref("keyword.URL", "https://www.google.com/m?ie=UTF-8&oe=UTF-8&sourceid=navclient&gfns=1&q=");
pref("accessibility.typeaheadfind", false); pref("accessibility.typeaheadfind", false);
pref("accessibility.typeaheadfind.timeout", 5000); pref("accessibility.typeaheadfind.timeout", 5000);

Просмотреть файл

@ -102,7 +102,7 @@ textarea,
border-style: solid; border-style: solid;
border-color: #7d7d7d; border-color: #7d7d7d;
color: #414141; color: #414141;
background: white -moz-linear-gradient(top, rgba(115,115,115,0.5) 0, rgba(215,215,215,0.5) 3px, rgba(255,255,255,0.2) 16px); background: white linear-gradient(rgba(115,115,115,0.5) 0, rgba(215,215,215,0.5) 3px, rgba(255,255,255,0.2) 16px);
} }
/* Selects are handled by the form helper, see bug 685197 */ /* Selects are handled by the form helper, see bug 685197 */
@ -120,15 +120,15 @@ button {
border-style: solid; border-style: solid;
border-color: #7d7d7d; border-color: #7d7d7d;
color: #414141; color: #414141;
background: white -moz-linear-gradient(top, rgba(255,255,255,0.2) 0, rgba(215,215,215,0.5) 18px, rgba(115,115,115,0.5) 100%); background: white linear-gradient(rgba(255,255,255,0.2) 0, rgba(215,215,215,0.5) 18px, rgba(115,115,115,0.5) 100%);
} }
input[type="checkbox"] { input[type="checkbox"] {
background: white -moz-linear-gradient(top, rgba(115,115,115,0.5) 0, rgba(215,215,215,0.5) 2px, rgba(255,255,255,0.2) 6px); background: white linear-gradient(rgba(115,115,115,0.5) 0, rgba(215,215,215,0.5) 2px, rgba(255,255,255,0.2) 6px);
} }
input[type="radio"] { input[type="radio"] {
background: -moz-radial-gradient(6px 6px, cover, rgba(255,255,255,0.2) 3px, rgba(195,195,195,0.5) 5px, rgba(115,115,115,0.5) 100%); background: radial-gradient(at 6px 6px, rgba(255,255,255,0.2) 3px, rgba(195,195,195,0.5) 5px, rgba(115,115,115,0.5) 100%);
} }
select { select {
@ -176,7 +176,7 @@ select > button {
border-radius: 0; border-radius: 0;
color: #414141; color: #414141;
background-image: -moz-radial-gradient(bottom left, #bbbbbb 40%, #f5f5f5), url(arrow.svg) !important; background-image: radial-gradient(at bottom left, #bbbbbb 40%, #f5f5f5), url(arrow.svg) !important;
background-color: transparent; background-color: transparent;
background-position: -15px center, 4px center !important; background-position: -15px center, 4px center !important;
background-repeat: no-repeat, no-repeat !important; background-repeat: no-repeat, no-repeat !important;
@ -196,7 +196,7 @@ input[type="file"]:focus > input[type="text"],
outline: 0px !important; outline: 0px !important;
border-style: solid; border-style: solid;
border-color: rgb(94,128,153); border-color: rgb(94,128,153);
background: white -moz-linear-gradient(top, rgba(27,113,177,0.5) 0, rgba(198,225,246,0.2) 3px, rgba(255,255,255,0.2) 16px); background: white linear-gradient(rgba(27,113,177,0.5) 0, rgba(198,225,246,0.2) 3px, rgba(255,255,255,0.2) 16px);
} }
select:not([size]):not([multiple]):focus, select:not([size]):not([multiple]):focus,
@ -209,7 +209,7 @@ button:focus {
outline: 0px !important; outline: 0px !important;
border-style: solid; border-style: solid;
border-color: rgb(94,128,153); border-color: rgb(94,128,153);
background: white -moz-linear-gradient(top, rgba(255,255,255,0.2) 0, rgba(198,225,256,0.2) 18px, rgba(27,113,177,0.5) 100%); background: white linear-gradient(rgba(255,255,255,0.2) 0, rgba(198,225,256,0.2) 18px, rgba(27,113,177,0.5) 100%);
} }
input[type="checkbox"]:focus, input[type="checkbox"]:focus,
@ -218,11 +218,11 @@ input[type="radio"]:focus {
} }
input[type="checkbox"]:focus { input[type="checkbox"]:focus {
background: white -moz-linear-gradient(top, rgba(27,113,177,0.5) 0, rgba(198,225,246,0.2) 2px, rgba(255,255,255,0.2) 6px); background: white linear-gradient(rgba(27,113,177,0.5) 0, rgba(198,225,246,0.2) 2px, rgba(255,255,255,0.2) 6px);
} }
input[type="radio"]:focus { input[type="radio"]:focus {
background: -moz-radial-gradient(6px 6px, cover, rgba(255,255,255,0.2) 3px, rgba(198,225,246,0.2) 5px, rgba(27,113,177,0.5) 100%); background: radial-gradient(at 6px 6px, rgba(255,255,255,0.2) 3px, rgba(198,225,246,0.2) 5px, rgba(27,113,177,0.5) 100%);
} }
/* we need to be specific for selects because the above rules are specific too */ /* we need to be specific for selects because the above rules are specific too */
@ -239,13 +239,13 @@ button[disabled],
border-color: rgba(125,125,125,0.4); border-color: rgba(125,125,125,0.4);
border-style: solid; border-style: solid;
border-width: 1px; border-width: 1px;
background: transparent -moz-linear-gradient(top, rgba(185,185,185,0.4) 0, rgba(235,235,235,0.4) 3px, rgba(255,255,255,0.4) 100%); background: transparent linear-gradient(rgba(185,185,185,0.4) 0, rgba(235,235,235,0.4) 3px, rgba(255,255,255,0.4) 100%);
} }
select:not([size]):not([multiple])[disabled], select:not([size]):not([multiple])[disabled],
select[size="0"][disabled], select[size="0"][disabled],
select[size="1"][disabled] { select[size="1"][disabled] {
background: transparent -moz-linear-gradient(top, rgba(255,255,255,0.4) 0, rgba(235,235,235,0.4) 3px, rgba(185,185,185,0.4) 100%); background: transparent linear-gradient(rgba(255,255,255,0.4) 0, rgba(235,235,235,0.4) 3px, rgba(185,185,185,0.4) 100%);
} }
input[type="button"][disabled], input[type="button"][disabled],
@ -253,7 +253,7 @@ input[type="submit"][disabled],
input[type="reset"][disabled], input[type="reset"][disabled],
button[disabled="true"] { button[disabled="true"] {
padding: 0 7px 0 7px; padding: 0 7px 0 7px;
background: transparent -moz-linear-gradient(top, rgba(255,255,255,0.4) 0, rgba(235,235,235,0.4) 3px, rgba(185,185,185,0.4) 100%); background: transparent linear-gradient(rgba(255,255,255,0.4) 0, rgba(235,235,235,0.4) 3px, rgba(185,185,185,0.4) 100%);
} }
input[type="radio"][disabled], input[type="radio"][disabled],

Просмотреть файл

@ -66,10 +66,6 @@ ifdef MOZ_LINKER
LIBS += $(MOZ_ZLIB_LIBS) LIBS += $(MOZ_ZLIB_LIBS)
endif endif
ifdef HAVE_CLOCK_MONOTONIC
LIBS += $(REALTIME_LIBS)
endif
ifndef MOZ_WINCONSOLE ifndef MOZ_WINCONSOLE
ifdef MOZ_DEBUG ifdef MOZ_DEBUG
MOZ_WINCONSOLE = 1 MOZ_WINCONSOLE = 1

Просмотреть файл

@ -13,13 +13,12 @@
#include <io.h> #include <io.h>
#include <fcntl.h> #include <fcntl.h>
#elif defined(XP_UNIX) #elif defined(XP_UNIX)
#include <sys/time.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <time.h>
#include <unistd.h> #include <unistd.h>
#endif #endif
#ifdef XP_MACOSX #ifdef XP_MACOSX
#include <mach/mach_time.h>
#include "MacQuirks.h" #include "MacQuirks.h"
#endif #endif
@ -389,80 +388,25 @@ static int do_main(int argc, char* argv[], nsIFile *xreDirectory)
return 255; return 255;
} }
#ifdef XP_WIN /* Local implementation of PR_Now, since the executable can't depend on NSPR */
static PRTime _PR_Now()
/**
* Used only when GetTickCount64 is not available on the platform.
* Last result of GetTickCount call. Kept in [ms].
*/
static DWORD sLastGTCResult = 0;
/**
* Higher part of the 64-bit value of MozGetTickCount64,
* incremented atomically.
*/
static DWORD sLastGTCRollover = 0;
/**
* Function protecting GetTickCount result from rolling over. The original
* code comes from the Windows implementation of the TimeStamp class minus the
* locking harness which isn't needed here.
*
* @returns The current time in milliseconds
*/
static ULONGLONG WINAPI
MozGetTickCount64()
{ {
DWORD GTC = ::GetTickCount(); #ifdef XP_WIN
MOZ_STATIC_ASSERT(sizeof(PRTime) == sizeof(FILETIME), "PRTime must have the same size as FILETIME");
/* Pull the rollover counter forward only if new value of GTC goes way FILETIME ft;
* down under the last saved result */ GetSystemTimeAsFileTime(&ft);
if ((sLastGTCResult > GTC) && ((sLastGTCResult - GTC) > (1UL << 30))) PRTime now;
++sLastGTCRollover; CopyMemory(&now, &ft, sizeof(PRTime));
#ifdef __GNUC__
sLastGTCResult = GTC; return (now - 116444736000000000LL) / 10LL;
return (ULONGLONG)sLastGTCRollover << 32 | sLastGTCResult; #else
} return (now - 116444736000000000i64) / 10i64;
typedef ULONGLONG (WINAPI* GetTickCount64_t)();
static GetTickCount64_t sGetTickCount64 = nullptr;
#endif #endif
/** #else
* Local TimeStamp::Now()-compatible implementation used to record timestamps struct timeval tm;
* which will be passed to XRE_StartupTimelineRecord(). gettimeofday(&tm, 0);
*/ return (((PRTime)tm.tv_sec * 1000000LL) + (PRTime)tm.tv_usec);
static uint64_t
TimeStamp_Now()
{
#ifdef XP_WIN
LARGE_INTEGER freq;
::QueryPerformanceFrequency(&freq);
HMODULE kernelDLL = GetModuleHandleW(L"kernel32.dll");
sGetTickCount64 = reinterpret_cast<GetTickCount64_t>
(GetProcAddress(kernelDLL, "GetTickCount64"));
if (!sGetTickCount64) {
/* If the platform does not support the GetTickCount64 (Windows XP doesn't),
* then use our fallback implementation based on GetTickCount. */
sGetTickCount64 = MozGetTickCount64;
}
return sGetTickCount64() * freq.QuadPart;
#elif defined(XP_MACOSX)
return mach_absolute_time();
#elif defined(HAVE_CLOCK_MONOTONIC)
struct timespec ts;
int rv = clock_gettime(CLOCK_MONOTONIC, &ts);
if (rv != 0) {
return 0;
}
uint64_t baseNs = (uint64_t)ts.tv_sec * 1000000000;
return baseNs + (uint64_t)ts.tv_nsec;
#endif #endif
} }
@ -584,7 +528,7 @@ int main(int argc, char* argv[])
#ifdef DEBUG_delay_start_metro #ifdef DEBUG_delay_start_metro
Sleep(5000); Sleep(5000);
#endif #endif
uint64_t start = TimeStamp_Now(); PRTime start = _PR_Now();
#ifdef XP_MACOSX #ifdef XP_MACOSX
TriggerQuirks(); TriggerQuirks();

Просмотреть файл

@ -210,9 +210,6 @@ pref("xpinstall.whitelist.add.180", "marketplace.firefox.com");
pref("lightweightThemes.update.enabled", true); pref("lightweightThemes.update.enabled", true);
pref("keyword.enabled", true); pref("keyword.enabled", true);
// Override the default keyword.URL. Empty value means
// "use the search service's default engine"
pref("keyword.URL", "");
pref("general.useragent.locale", "@AB_CD@"); pref("general.useragent.locale", "@AB_CD@");
pref("general.skins.selectedSkin", "classic/1.0"); pref("general.skins.selectedSkin", "classic/1.0");
@ -540,6 +537,8 @@ pref("browser.gesture.twist.left", "cmd_gestureRotateLeft");
pref("browser.gesture.twist.end", "cmd_gestureRotateEnd"); pref("browser.gesture.twist.end", "cmd_gestureRotateEnd");
pref("browser.gesture.tap", "cmd_fullZoomReset"); pref("browser.gesture.tap", "cmd_fullZoomReset");
pref("browser.snapshots.limit", 0);
// 0: Nothing happens // 0: Nothing happens
// 1: Scrolling contents // 1: Scrolling contents
// 2: Go back or go forward, in your history // 2: Go back or go forward, in your history

Просмотреть файл

@ -256,19 +256,6 @@ function onSearchSubmit(aEvent)
function setupSearchEngine() function setupSearchEngine()
{ {
let searchEngineName = document.documentElement.getAttribute("searchEngineName");
let searchEngineInfo = SEARCH_ENGINES[searchEngineName];
if (!searchEngineInfo) {
return;
}
// Add search engine logo.
if (searchEngineInfo.image) {
let logoElt = document.getElementById("searchEngineLogo");
logoElt.src = searchEngineInfo.image;
logoElt.alt = searchEngineName;
}
// The "autofocus" attribute doesn't focus the form element // The "autofocus" attribute doesn't focus the form element
// immediately when the element is first drawn, so the // immediately when the element is first drawn, so the
// attribute is also used for styling when the page first loads. // attribute is also used for styling when the page first loads.
@ -278,6 +265,26 @@ function setupSearchEngine()
searchText.removeAttribute("autofocus"); searchText.removeAttribute("autofocus");
}); });
let searchEngineName = document.documentElement.getAttribute("searchEngineName");
let searchEngineInfo = SEARCH_ENGINES[searchEngineName];
let logoElt = document.getElementById("searchEngineLogo");
// Add search engine logo.
if (searchEngineInfo && searchEngineInfo.image) {
logoElt.parentNode.hidden = false;
logoElt.src = searchEngineInfo.image;
#ifdef XP_MACOSX
if (searchEngineInfo.imageHD && !window.matchMedia("(max-resolution: 1dppx)").matches)
logoElt.src = searchEngineInfo.imageHD;
#endif
logoElt.alt = searchEngineName;
searchText.placeholder = "";
}
else {
logoElt.parentNode.hidden = true;
searchText.placeholder = searchEngineName;
}
} }
/** /**

Просмотреть файл

@ -149,6 +149,8 @@
observes="devtoolsMenuBroadcaster_DevToolbar"/> observes="devtoolsMenuBroadcaster_DevToolbar"/>
<menuitem id="appmenu_chromeDebugger" <menuitem id="appmenu_chromeDebugger"
observes="devtoolsMenuBroadcaster_ChromeDebugger"/> observes="devtoolsMenuBroadcaster_ChromeDebugger"/>
<menuitem id="appmenu_browserConsole"
observes="devtoolsMenuBroadcaster_BrowserConsole"/>
<menuitem id="appmenu_responsiveUI" <menuitem id="appmenu_responsiveUI"
observes="devtoolsMenuBroadcaster_ResponsiveUI"/> observes="devtoolsMenuBroadcaster_ResponsiveUI"/>
<menuitem id="appmenu_scratchpad" <menuitem id="appmenu_scratchpad"

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -576,6 +576,8 @@
accesskey="&devToolbarMenu.accesskey;"/> accesskey="&devToolbarMenu.accesskey;"/>
<menuitem id="menu_chromeDebugger" <menuitem id="menu_chromeDebugger"
observes="devtoolsMenuBroadcaster_ChromeDebugger"/> observes="devtoolsMenuBroadcaster_ChromeDebugger"/>
<menuitem id="menu_browserConsole"
observes="devtoolsMenuBroadcaster_BrowserConsole"/>
<menuitem id="menu_responsiveUI" <menuitem id="menu_responsiveUI"
observes="devtoolsMenuBroadcaster_ResponsiveUI" observes="devtoolsMenuBroadcaster_ResponsiveUI"
accesskey="&responsiveDesignTool.accesskey;"/> accesskey="&responsiveDesignTool.accesskey;"/>

Просмотреть файл

@ -96,6 +96,7 @@
<command id="Tools:DevToolbar" oncommand="DeveloperToolbar.toggle();" disabled="true" hidden="true"/> <command id="Tools:DevToolbar" oncommand="DeveloperToolbar.toggle();" disabled="true" hidden="true"/>
<command id="Tools:DevToolbarFocus" oncommand="DeveloperToolbar.focusToggle();" disabled="true"/> <command id="Tools:DevToolbarFocus" oncommand="DeveloperToolbar.focusToggle();" disabled="true"/>
<command id="Tools:ChromeDebugger" oncommand="DebuggerUI.toggleChromeDebugger();" disabled="true" hidden="true"/> <command id="Tools:ChromeDebugger" oncommand="DebuggerUI.toggleChromeDebugger();" disabled="true" hidden="true"/>
<command id="Tools:BrowserConsole" oncommand="HUDConsoleUI.toggleBrowserConsole();" disabled="true" hidden="true"/>
<command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();" disabled="true" hidden="true"/> <command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();" disabled="true" hidden="true"/>
<command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();" disabled="true" hidden="true"/> <command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();" disabled="true" hidden="true"/>
<command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/> <command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
@ -184,6 +185,9 @@
<broadcaster id="devtoolsMenuBroadcaster_ChromeDebugger" <broadcaster id="devtoolsMenuBroadcaster_ChromeDebugger"
label="&chromeDebuggerMenu.label;" label="&chromeDebuggerMenu.label;"
command="Tools:ChromeDebugger"/> command="Tools:ChromeDebugger"/>
<broadcaster id="devtoolsMenuBroadcaster_BrowserConsole"
label="&browserConsoleCmd.label;"
command="Tools:BrowserConsole"/>
<broadcaster id="devtoolsMenuBroadcaster_Scratchpad" <broadcaster id="devtoolsMenuBroadcaster_Scratchpad"
label="&scratchpad.label;" label="&scratchpad.label;"
command="Tools:Scratchpad" command="Tools:Scratchpad"

Просмотреть файл

@ -358,6 +358,30 @@ window[chromehidden~="toolbar"] toolbar:not(.toolbar-primary):not(.chromeclass-m
} }
%endif %endif
/* History Swipe Animation */
#historySwipeAnimationContainer {
overflow: hidden;
}
#historySwipeAnimationPreviousPage,
#historySwipeAnimationCurrentPage,
#historySwipeAnimationNextPage {
background: none top left no-repeat white;
}
#historySwipeAnimationPreviousPage {
background-image: -moz-element(#historySwipeAnimationPreviousPageSnapshot);
}
#historySwipeAnimationCurrentPage {
background-image: -moz-element(#historySwipeAnimationCurrentPageSnapshot);
}
#historySwipeAnimationNextPage {
background-image: -moz-element(#historySwipeAnimationNextPageSnapshot);
}
/* Identity UI */ /* Identity UI */
#identity-popup-content-box.unknownIdentity > #identity-popup-connectedToLabel , #identity-popup-content-box.unknownIdentity > #identity-popup-connectedToLabel ,
#identity-popup-content-box.unknownIdentity > #identity-popup-runByLabel , #identity-popup-content-box.unknownIdentity > #identity-popup-runByLabel ,

Просмотреть файл

@ -157,6 +157,7 @@ let gInitialPages = [
#include browser-tabview.js #include browser-tabview.js
#include browser-thumbnails.js #include browser-thumbnails.js
#include browser-webrtcUI.js #include browser-webrtcUI.js
#include browser-gestureSupport.js
#ifdef MOZ_DATA_REPORTING #ifdef MOZ_DATA_REPORTING
#include browser-data-submission-info-bar.js #include browser-data-submission-info-bar.js
@ -724,396 +725,6 @@ const gFormSubmitObserver = {
} }
}; };
// Simple gestures support
//
// As per bug #412486, web content must not be allowed to receive any
// simple gesture events. Multi-touch gesture APIs are in their
// infancy and we do NOT want to be forced into supporting an API that
// will probably have to change in the future. (The current Mac OS X
// API is undocumented and was reverse-engineered.) Until support is
// implemented in the event dispatcher to keep these events as
// chrome-only, we must listen for the simple gesture events during
// the capturing phase and call stopPropagation on every event.
let gGestureSupport = {
_currentRotation: 0,
_lastRotateDelta: 0,
_rotateMomentumThreshold: .75,
/**
* Add or remove mouse gesture event listeners
*
* @param aAddListener
* True to add/init listeners and false to remove/uninit
*/
init: function GS_init(aAddListener) {
const gestureEvents = ["SwipeGesture",
"MagnifyGestureStart", "MagnifyGestureUpdate", "MagnifyGesture",
"RotateGestureStart", "RotateGestureUpdate", "RotateGesture",
"TapGesture", "PressTapGesture"];
let addRemove = aAddListener ? window.addEventListener :
window.removeEventListener;
gestureEvents.forEach(function (event) addRemove("Moz" + event, this, true),
this);
},
/**
* Dispatch events based on the type of mouse gesture event. For now, make
* sure to stop propagation of every gesture event so that web content cannot
* receive gesture events.
*
* @param aEvent
* The gesture event to handle
*/
handleEvent: function GS_handleEvent(aEvent) {
if (!Services.prefs.getBoolPref(
"dom.debug.propagate_gesture_events_through_content")) {
aEvent.stopPropagation();
}
// Create a preference object with some defaults
let def = function(aThreshold, aLatched)
({ threshold: aThreshold, latched: !!aLatched });
switch (aEvent.type) {
case "MozSwipeGesture":
aEvent.preventDefault();
this.onSwipe(aEvent);
break;
case "MozMagnifyGestureStart":
aEvent.preventDefault();
#ifdef XP_WIN
this._setupGesture(aEvent, "pinch", def(25, 0), "out", "in");
#else
this._setupGesture(aEvent, "pinch", def(150, 1), "out", "in");
#endif
break;
case "MozRotateGestureStart":
aEvent.preventDefault();
this._setupGesture(aEvent, "twist", def(25, 0), "right", "left");
break;
case "MozMagnifyGestureUpdate":
case "MozRotateGestureUpdate":
aEvent.preventDefault();
this._doUpdate(aEvent);
break;
case "MozTapGesture":
aEvent.preventDefault();
this._doAction(aEvent, ["tap"]);
break;
case "MozRotateGesture":
aEvent.preventDefault();
this._doAction(aEvent, ["twist", "end"]);
break;
/* case "MozPressTapGesture":
break; */
}
},
/**
* Called at the start of "pinch" and "twist" gestures to setup all of the
* information needed to process the gesture
*
* @param aEvent
* The continual motion start event to handle
* @param aGesture
* Name of the gesture to handle
* @param aPref
* Preference object with the names of preferences and defaults
* @param aInc
* Command to trigger for increasing motion (without gesture name)
* @param aDec
* Command to trigger for decreasing motion (without gesture name)
*/
_setupGesture: function GS__setupGesture(aEvent, aGesture, aPref, aInc, aDec) {
// Try to load user-set values from preferences
for (let [pref, def] in Iterator(aPref))
aPref[pref] = this._getPref(aGesture + "." + pref, def);
// Keep track of the total deltas and latching behavior
let offset = 0;
let latchDir = aEvent.delta > 0 ? 1 : -1;
let isLatched = false;
// Create the update function here to capture closure state
this._doUpdate = function GS__doUpdate(aEvent) {
// Update the offset with new event data
offset += aEvent.delta;
// Check if the cumulative deltas exceed the threshold
if (Math.abs(offset) > aPref["threshold"]) {
// Trigger the action if we don't care about latching; otherwise, make
// sure either we're not latched and going the same direction of the
// initial motion; or we're latched and going the opposite way
let sameDir = (latchDir ^ offset) >= 0;
if (!aPref["latched"] || (isLatched ^ sameDir)) {
this._doAction(aEvent, [aGesture, offset > 0 ? aInc : aDec]);
// We must be getting latched or leaving it, so just toggle
isLatched = !isLatched;
}
// Reset motion counter to prepare for more of the same gesture
offset = 0;
}
};
// The start event also contains deltas, so handle an update right away
this._doUpdate(aEvent);
},
/**
* Generator producing the powerset of the input array where the first result
* is the complete set and the last result (before StopIteration) is empty.
*
* @param aArray
* Source array containing any number of elements
* @yield Array that is a subset of the input array from full set to empty
*/
_power: function GS__power(aArray) {
// Create a bitmask based on the length of the array
let num = 1 << aArray.length;
while (--num >= 0) {
// Only select array elements where the current bit is set
yield aArray.reduce(function (aPrev, aCurr, aIndex) {
if (num & 1 << aIndex)
aPrev.push(aCurr);
return aPrev;
}, []);
}
},
/**
* Determine what action to do for the gesture based on which keys are
* pressed and which commands are set
*
* @param aEvent
* The original gesture event to convert into a fake click event
* @param aGesture
* Array of gesture name parts (to be joined by periods)
*/
_doAction: function GS__doAction(aEvent, aGesture) {
// Create an array of pressed keys in a fixed order so that a command for
// "meta" is preferred over "ctrl" when both buttons are pressed (and a
// command for both don't exist)
let keyCombos = [];
["shift", "alt", "ctrl", "meta"].forEach(function (key) {
if (aEvent[key + "Key"])
keyCombos.push(key);
});
// Try each combination of key presses in decreasing order for commands
for (let subCombo of this._power(keyCombos)) {
// Convert a gesture and pressed keys into the corresponding command
// action where the preference has the gesture before "shift" before
// "alt" before "ctrl" before "meta" all separated by periods
let command;
try {
command = this._getPref(aGesture.concat(subCombo).join("."));
} catch (e) {}
if (!command)
continue;
let node = document.getElementById(command);
if (node) {
if (node.getAttribute("disabled") != "true") {
let cmdEvent = document.createEvent("xulcommandevent");
cmdEvent.initCommandEvent("command", true, true, window, 0,
aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKey,
aEvent.metaKey, aEvent);
node.dispatchEvent(cmdEvent);
}
} else {
goDoCommand(command);
}
break;
}
},
/**
* Convert continual motion events into an action if it exceeds a threshold
* in a given direction. This function will be set by _setupGesture to
* capture state that needs to be shared across multiple gesture updates.
*
* @param aEvent
* The continual motion update event to handle
*/
_doUpdate: function(aEvent) {},
/**
* Convert the swipe gesture into a browser action based on the direction
*
* @param aEvent
* The swipe event to handle
*/
onSwipe: function GS_onSwipe(aEvent) {
// Figure out which one (and only one) direction was triggered
for (let dir of ["UP", "RIGHT", "DOWN", "LEFT"]) {
if (aEvent.direction == aEvent["DIRECTION_" + dir]) {
this._doAction(aEvent, ["swipe", dir.toLowerCase()]);
break;
}
}
},
/**
* Get a gesture preference or use a default if it doesn't exist
*
* @param aPref
* Name of the preference to load under the gesture branch
* @param aDef
* Default value if the preference doesn't exist
*/
_getPref: function GS__getPref(aPref, aDef) {
// Preferences branch under which all gestures preferences are stored
const branch = "browser.gesture.";
try {
// Determine what type of data to load based on default value's type
let type = typeof aDef;
let getFunc = "get" + (type == "boolean" ? "Bool" :
type == "number" ? "Int" : "Char") + "Pref";
return gPrefService[getFunc](branch + aPref);
}
catch (e) {
return aDef;
}
},
/**
* Perform rotation for ImageDocuments
*
* @param aEvent
* The MozRotateGestureUpdate event triggering this call
*/
rotate: function(aEvent) {
if (!(content.document instanceof ImageDocument))
return;
let contentElement = content.document.body.firstElementChild;
if (!contentElement)
return;
// If we're currently snapping, cancel that snap
if (contentElement.classList.contains("completeRotation"))
this._clearCompleteRotation();
this.rotation = Math.round(this.rotation + aEvent.delta);
contentElement.style.transform = "rotate(" + this.rotation + "deg)";
this._lastRotateDelta = aEvent.delta;
},
/**
* Perform a rotation end for ImageDocuments
*/
rotateEnd: function() {
if (!(content.document instanceof ImageDocument))
return;
let contentElement = content.document.body.firstElementChild;
if (!contentElement)
return;
let transitionRotation = 0;
// The reason that 360 is allowed here is because when rotating between
// 315 and 360, setting rotate(0deg) will cause it to rotate the wrong
// direction around--spinning wildly.
if (this.rotation <= 45)
transitionRotation = 0;
else if (this.rotation > 45 && this.rotation <= 135)
transitionRotation = 90;
else if (this.rotation > 135 && this.rotation <= 225)
transitionRotation = 180;
else if (this.rotation > 225 && this.rotation <= 315)
transitionRotation = 270;
else
transitionRotation = 360;
// If we're going fast enough, and we didn't already snap ahead of rotation,
// then snap ahead of rotation to simulate momentum
if (this._lastRotateDelta > this._rotateMomentumThreshold &&
this.rotation > transitionRotation)
transitionRotation += 90;
else if (this._lastRotateDelta < -1 * this._rotateMomentumThreshold &&
this.rotation < transitionRotation)
transitionRotation -= 90;
// Only add the completeRotation class if it is is necessary
if (transitionRotation != this.rotation) {
contentElement.classList.add("completeRotation");
contentElement.addEventListener("transitionend", this._clearCompleteRotation);
}
contentElement.style.transform = "rotate(" + transitionRotation + "deg)";
this.rotation = transitionRotation;
},
/**
* Gets the current rotation for the ImageDocument
*/
get rotation() {
return this._currentRotation;
},
/**
* Sets the current rotation for the ImageDocument
*
* @param aVal
* The new value to take. Can be any value, but it will be bounded to
* 0 inclusive to 360 exclusive.
*/
set rotation(aVal) {
this._currentRotation = aVal % 360;
if (this._currentRotation < 0)
this._currentRotation += 360;
return this._currentRotation;
},
/**
* When the location/tab changes, need to reload the current rotation for the
* image
*/
restoreRotationState: function() {
if (!(content.document instanceof ImageDocument))
return;
let contentElement = content.document.body.firstElementChild;
let transformValue = content.window.getComputedStyle(contentElement, null)
.transform;
if (transformValue == "none") {
this.rotation = 0;
return;
}
// transformValue is a rotation matrix--split it and do mathemagic to
// obtain the real rotation value
transformValue = transformValue.split("(")[1]
.split(")")[0]
.split(",");
this.rotation = Math.round(Math.atan2(transformValue[1], transformValue[0]) *
(180 / Math.PI));
},
/**
* Removes the transition rule by removing the completeRotation class
*/
_clearCompleteRotation: function() {
let contentElement = content.document &&
content.document instanceof ImageDocument &&
content.document.body &&
content.document.body.firstElementChild;
if (!contentElement)
return;
contentElement.classList.remove("completeRotation");
contentElement.removeEventListener("transitionend", this._clearCompleteRotation);
},
};
var gBrowserInit = { var gBrowserInit = {
onLoad: function() { onLoad: function() {
// window.arguments[0]: URI to load (string), or an nsISupportsArray of // window.arguments[0]: URI to load (string), or an nsISupportsArray of
@ -1212,6 +823,9 @@ var gBrowserInit = {
// setup simple gestures support // setup simple gestures support
gGestureSupport.init(true); gGestureSupport.init(true);
// setup history swipe animation
gHistorySwipeAnimation.init();
if (window.opener && !window.opener.closed) { if (window.opener && !window.opener.closed) {
let openerSidebarBox = window.opener.document.getElementById("sidebar-box"); let openerSidebarBox = window.opener.document.getElementById("sidebar-box");
// If the opener had a sidebar, open the same sidebar in our window. // If the opener had a sidebar, open the same sidebar in our window.
@ -1571,19 +1185,27 @@ var gBrowserInit = {
} }
// Enable Chrome Debugger? // Enable Chrome Debugger?
let enabled = gPrefService.getBoolPref("devtools.chrome.enabled") && let chromeEnabled = gPrefService.getBoolPref("devtools.chrome.enabled");
gPrefService.getBoolPref("devtools.debugger.chrome-enabled") && let remoteEnabled = chromeEnabled &&
gPrefService.getBoolPref("devtools.debugger.remote-enabled"); gPrefService.getBoolPref("devtools.debugger.chrome-enabled") &&
if (enabled) { gPrefService.getBoolPref("devtools.debugger.remote-enabled");
if (remoteEnabled) {
let cmd = document.getElementById("Tools:ChromeDebugger"); let cmd = document.getElementById("Tools:ChromeDebugger");
cmd.removeAttribute("disabled"); cmd.removeAttribute("disabled");
cmd.removeAttribute("hidden"); cmd.removeAttribute("hidden");
} }
// Enable the Browser Console?
if (chromeEnabled) {
let cmd = document.getElementById("Tools:BrowserConsole");
cmd.removeAttribute("disabled");
cmd.removeAttribute("hidden");
}
// Enable Error Console? // Enable Error Console?
// Temporarily enabled. See bug 798925. // Temporarily enabled. See bug 798925.
let consoleEnabled = true || gPrefService.getBoolPref("devtools.errorconsole.enabled") || let consoleEnabled = true || gPrefService.getBoolPref("devtools.errorconsole.enabled") ||
gPrefService.getBoolPref("devtools.chrome.enabled"); chromeEnabled;
if (consoleEnabled) { if (consoleEnabled) {
let cmd = document.getElementById("Tools:ErrorConsole"); let cmd = document.getElementById("Tools:ErrorConsole");
cmd.removeAttribute("disabled"); cmd.removeAttribute("disabled");
@ -1681,6 +1303,8 @@ var gBrowserInit = {
gGestureSupport.init(false); gGestureSupport.init(false);
gHistorySwipeAnimation.uninit();
FullScreen.cleanup(); FullScreen.cleanup();
Services.obs.removeObserver(gPluginHandler.pluginCrashed, "plugin-crashed"); Services.obs.removeObserver(gPluginHandler.pluginCrashed, "plugin-crashed");
@ -1807,6 +1431,10 @@ var gBrowserInit = {
SocialUI.nonBrowserWindowInit(); SocialUI.nonBrowserWindowInit();
if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
document.getElementById("macDockMenuNewWindow").hidden = true;
}
this._delayedStartupTimeoutId = setTimeout(this.nonBrowserWindowDelayedStartup.bind(this), 0); this._delayedStartupTimeoutId = setTimeout(this.nonBrowserWindowDelayedStartup.bind(this), 0);
}, },
@ -1896,7 +1524,6 @@ var nonBrowserWindowDelayedStartup = gBrowserInit.nonBrowserWindowDelayedStartup
var nonBrowserWindowShutdown = gBrowserInit.nonBrowserWindowShutdown.bind(gBrowserInit); var nonBrowserWindowShutdown = gBrowserInit.nonBrowserWindowShutdown.bind(gBrowserInit);
#endif #endif
function HandleAppCommandEvent(evt) { function HandleAppCommandEvent(evt) {
switch (evt.command) { switch (evt.command) {
case "Back": case "Back":

Просмотреть файл

@ -191,8 +191,8 @@
<hbox pack="start" align="center" class="popup-notification-button-container"> <hbox pack="start" align="center" class="popup-notification-button-container">
<label id="social-undoactivation-button" <label id="social-undoactivation-button"
class="text-link" class="text-link"
value="&social.activated.undobutton.label;" value="&social.activated.undo.label;"
accesskey="&social.activated.undobutton.accesskey;" accesskey="&social.activated.undo.accesskey;"
onclick="SocialUI.undoActivation(this);"/> onclick="SocialUI.undoActivation(this);"/>
<spacer flex="1"/> <spacer flex="1"/>
<button id="social-activation-button" <button id="social-activation-button"

Просмотреть файл

@ -55,7 +55,8 @@
<menupopup id="menu_mac_dockmenu"> <menupopup id="menu_mac_dockmenu">
<!-- The command cannot be cmd_newNavigator because we need to activate <!-- The command cannot be cmd_newNavigator because we need to activate
the application. --> the application. -->
<menuitem label="&newNavigatorCmd.label;" oncommand="OpenBrowserWindowFromDockMenu();" /> <menuitem label="&newNavigatorCmd.label;" oncommand="OpenBrowserWindowFromDockMenu();"
id="macDockMenuNewWindow" />
<menuitem label="&newPrivateWindow.label;" oncommand="OpenBrowserWindowFromDockMenu({private: true});" /> <menuitem label="&newPrivateWindow.label;" oncommand="OpenBrowserWindowFromDockMenu({private: true});" />
</menupopup> </menupopup>
</popupset> </popupset>

Просмотреть файл

@ -310,10 +310,8 @@ function onLoadPageInfo()
gStrings.mediaEmbed = gBundle.getString("mediaEmbed"); gStrings.mediaEmbed = gBundle.getString("mediaEmbed");
gStrings.mediaLink = gBundle.getString("mediaLink"); gStrings.mediaLink = gBundle.getString("mediaLink");
gStrings.mediaInput = gBundle.getString("mediaInput"); gStrings.mediaInput = gBundle.getString("mediaInput");
#ifdef MOZ_MEDIA
gStrings.mediaVideo = gBundle.getString("mediaVideo"); gStrings.mediaVideo = gBundle.getString("mediaVideo");
gStrings.mediaAudio = gBundle.getString("mediaAudio"); gStrings.mediaAudio = gBundle.getString("mediaAudio");
#endif
var args = "arguments" in window && var args = "arguments" in window &&
window.arguments.length >= 1 && window.arguments.length >= 1 &&
@ -677,14 +675,12 @@ function grabAll(elem)
addImage(href, gStrings.mediaImg, "", elem, false); addImage(href, gStrings.mediaImg, "", elem, false);
} catch (e) { } } catch (e) { }
} }
#ifdef MOZ_MEDIA
else if (elem instanceof HTMLVideoElement) { else if (elem instanceof HTMLVideoElement) {
addImage(elem.currentSrc, gStrings.mediaVideo, "", elem, false); addImage(elem.currentSrc, gStrings.mediaVideo, "", elem, false);
} }
else if (elem instanceof HTMLAudioElement) { else if (elem instanceof HTMLAudioElement) {
addImage(elem.currentSrc, gStrings.mediaAudio, "", elem, false); addImage(elem.currentSrc, gStrings.mediaAudio, "", elem, false);
} }
#endif
else if (elem instanceof HTMLLinkElement) { else if (elem instanceof HTMLLinkElement) {
if (elem.rel && /\bicon\b/i.test(elem.rel)) if (elem.rel && /\bicon\b/i.test(elem.rel))
addImage(elem.href, gStrings.mediaLink, "", elem, false); addImage(elem.href, gStrings.mediaLink, "", elem, false);
@ -1015,7 +1011,6 @@ function makePreview(row)
document.getElementById("theimagecontainer").collapsed = false document.getElementById("theimagecontainer").collapsed = false
document.getElementById("brokenimagecontainer").collapsed = true; document.getElementById("brokenimagecontainer").collapsed = true;
} }
#ifdef MOZ_MEDIA
else if (item instanceof HTMLVideoElement && isProtocolAllowed) { else if (item instanceof HTMLVideoElement && isProtocolAllowed) {
newImage = document.createElementNS("http://www.w3.org/1999/xhtml", "video"); newImage = document.createElementNS("http://www.w3.org/1999/xhtml", "video");
newImage.id = "thepreviewimage"; newImage.id = "thepreviewimage";
@ -1037,7 +1032,6 @@ function makePreview(row)
document.getElementById("theimagecontainer").collapsed = false; document.getElementById("theimagecontainer").collapsed = false;
document.getElementById("brokenimagecontainer").collapsed = true; document.getElementById("brokenimagecontainer").collapsed = true;
} }
#endif
else { else {
// fallback image for protocols not allowed (e.g., javascript:) // fallback image for protocols not allowed (e.g., javascript:)
// or elements not [yet] handled (e.g., object, embed). // or elements not [yet] handled (e.g., object, embed).

Просмотреть файл

@ -145,6 +145,9 @@ _BROWSER_FILES = \
browser_bug647886.js \ browser_bug647886.js \
browser_bug655584.js \ browser_bug655584.js \
browser_bug664672.js \ browser_bug664672.js \
browser_bug678392.js \
browser_bug678392-1.html \
browser_bug678392-2.html \
browser_bug710878.js \ browser_bug710878.js \
browser_bug719271.js \ browser_bug719271.js \
browser_bug724239.js \ browser_bug724239.js \

Просмотреть файл

@ -0,0 +1,12 @@
<!DOCTYPE html PUBLIC"-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<title>bug678392 - 1</title>
</head>
<body>
bug 678392 test page 1
</body>
</html>

Просмотреть файл

@ -0,0 +1,12 @@
<!DOCTYPE html PUBLIC"-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<title>bug678392 - 2</title>
</head>
<body>
bug 678392 test page 2
</body>
</html>

Просмотреть файл

@ -0,0 +1,192 @@
/* 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/. */
let HTTPROOT = "http://example.com/browser/browser/base/content/test/";
function maxSnapshotOverride() {
return 5;
}
function test() {
waitForExplicitFinish();
BrowserOpenTab();
let tab = gBrowser.selectedTab;
registerCleanupFunction(function () { gBrowser.removeTab(tab); });
ok(gHistorySwipeAnimation, "gHistorySwipeAnimation exists.");
if (!gHistorySwipeAnimation._isSupported()) {
is(gHistorySwipeAnimation.active, false, "History swipe animation is not " +
"active when not supported by the platform.");
finish();
return;
}
gHistorySwipeAnimation._getMaxSnapshots = maxSnapshotOverride;
gHistorySwipeAnimation.init();
is(gHistorySwipeAnimation.active, true, "History swipe animation support " +
"was successfully initialized when supported.");
cleanupArray();
load(gBrowser.selectedTab, HTTPROOT + "browser_bug678392-2.html", test0);
}
function load(aTab, aUrl, aCallback) {
aTab.linkedBrowser.addEventListener("pageshow", function onpageshow(aEvent) {
aEvent.currentTarget.removeEventListener("pageshow", onpageshow, false);
waitForFocus(aCallback, content);
}, false);
aTab.linkedBrowser.loadURI(aUrl);
}
function cleanupArray() {
let arr = gHistorySwipeAnimation._trackedSnapshots;
while (arr.length > 0) {
delete arr[0].browser.snapshots[arr[0].index]; // delete actual snapshot
arr.splice(0, 1);
}
}
function testArrayCleanup() {
// Test cleanup of array of tracked snapshots.
let arr = gHistorySwipeAnimation._trackedSnapshots;
is(arr.length, 0, "Snapshots were removed correctly from the array of " +
"tracked snapshots.");
}
function test0() {
// Test growing of array of tracked snapshots.
let tab = gBrowser.selectedTab;
load(tab, HTTPROOT + "browser_bug678392-1.html", function() {
ok(gHistorySwipeAnimation._trackedSnapshots, "Array for snapshot " +
"tracking is initialized.");
is(gHistorySwipeAnimation._trackedSnapshots.length, 1, "Snapshot array " +
"has correct length of 1 after loading one page.");
load(tab, HTTPROOT + "browser_bug678392-2.html", function() {
is(gHistorySwipeAnimation._trackedSnapshots.length, 2, "Snapshot array " +
" has correct length of 2 after loading two pages.");
load(tab, HTTPROOT + "browser_bug678392-1.html", function() {
is(gHistorySwipeAnimation._trackedSnapshots.length, 3, "Snapshot " +
"array has correct length of 3 after loading three pages.");
load(tab, HTTPROOT + "browser_bug678392-2.html", function() {
is(gHistorySwipeAnimation._trackedSnapshots.length, 4, "Snapshot " +
"array has correct length of 4 after loading four pages.");
cleanupArray();
testArrayCleanup();
test1();
});
});
});
});
}
function verifyRefRemoved(aIndex, aBrowser) {
let wasFound = false;
let arr = gHistorySwipeAnimation._trackedSnapshots;
for (let i = 0; i < arr.length; i++) {
if (arr[i].index == aIndex && arr[i].browser == aBrowser)
wasFound = true;
}
is(wasFound, false, "The reference that was previously removed was " +
"still found in the array of tracked snapshots.");
}
function test1() {
// Test presence of snpashots in per-tab array of snapshots and removal of
// individual snapshots (and corresponding references in the array of
// tracked snapshots).
let tab = gBrowser.selectedTab;
load(tab, HTTPROOT + "browser_bug678392-1.html", function() {
var historyIndex = gBrowser.webNavigation.sessionHistory.index - 1;
load(tab, HTTPROOT + "browser_bug678392-2.html", function() {
load(tab, HTTPROOT + "browser_bug678392-1.html", function() {
load(tab, HTTPROOT + "browser_bug678392-2.html", function() {
let browser = gBrowser.selectedBrowser;
ok(browser.snapshots, "Array of snapshots exists in browser.");
ok(browser.snapshots[historyIndex], "First page exists in snapshot " +
"array.");
ok(browser.snapshots[historyIndex + 1], "Second page exists in " +
"snapshot array.");
ok(browser.snapshots[historyIndex + 2], "Third page exists in " +
"snapshot array.");
ok(browser.snapshots[historyIndex + 3], "Fourth page exists in " +
"snapshot array.");
is(gHistorySwipeAnimation._trackedSnapshots.length, 4, "Length of " +
"array of tracked snapshots is equal to 4 after loading four " +
"pages.");
// Test removal of reference in the middle of the array.
gHistorySwipeAnimation._removeTrackedSnapshot(historyIndex + 1,
browser);
verifyRefRemoved(historyIndex + 1, browser);
is(gHistorySwipeAnimation._trackedSnapshots.length, 3, "Length of " +
"array of tracked snapshots is equal to 3 after removing one" +
"reference from the array with length 4.");
// Test removal of reference at end of array.
gHistorySwipeAnimation._removeTrackedSnapshot(historyIndex + 3,
browser);
verifyRefRemoved(historyIndex + 3, browser);
is(gHistorySwipeAnimation._trackedSnapshots.length, 2, "Length of " +
"array of tracked snapshots is equal to 2 after removing two" +
"references from the array with length 4.");
// Test removal of reference at head of array.
gHistorySwipeAnimation._removeTrackedSnapshot(historyIndex,
browser);
verifyRefRemoved(historyIndex, browser);
is(gHistorySwipeAnimation._trackedSnapshots.length, 1, "Length of " +
"array of tracked snapshots is equal to 1 after removing three" +
"references from the array with length 4.");
cleanupArray();
test2();
});
});
});
});
}
function test2() {
// Test growing of snapshot array across tabs.
let tab = gBrowser.selectedTab;
load(tab, HTTPROOT + "browser_bug678392-1.html", function() {
var historyIndex = gBrowser.webNavigation.sessionHistory.index - 1;
load(tab, HTTPROOT + "browser_bug678392-2.html", function() {
is(gHistorySwipeAnimation._trackedSnapshots.length, 2, "Length of " +
"snapshot array is equal to 2 after loading two pages");
let prevTab = tab;
tab = gBrowser.addTab("about:newtab");
gBrowser.selectedTab = tab;
load(tab, HTTPROOT + "browser_bug678392-2.html" /* initial page */,
function() {
load(tab, HTTPROOT + "browser_bug678392-1.html", function() {
load(tab, HTTPROOT + "browser_bug678392-2.html", function() {
is(gHistorySwipeAnimation._trackedSnapshots.length, 4, "Length " +
"of snapshot array is equal to 4 after loading two pages in " +
"two tabs each.");
gBrowser.removeCurrentTab();
gBrowser.selectedTab = prevTab;
cleanupArray();
test3();
});
});
});
});
});
}
function test3() {
// Test uninit of gHistorySwipeAnimation.
// This test MUST be the last one to execute.
gHistorySwipeAnimation.uninit();
is(gHistorySwipeAnimation.active, false, "History swipe animation support " +
"was successfully uninitialized");
finish();
}

Просмотреть файл

@ -17,7 +17,7 @@ var pairs = [
["1.1.1.1", "http://1.1.1.1/"], ["1.1.1.1", "http://1.1.1.1/"],
["ftp://example", "ftp://example/"], ["ftp://example", "ftp://example/"],
["ftp.example.bar", "ftp://ftp.example.bar/"], ["ftp.example.bar", "ftp://ftp.example.bar/"],
["ex ample", Services.search.originalDefaultEngine.getSubmission("ex ample", null, "keyword").uri.spec], ["ex ample", Services.search.defaultEngine.getSubmission("ex ample", null, "keyword").uri.spec],
]; ];
function testNext() { function testNext() {

Просмотреть файл

@ -122,6 +122,14 @@ function test_TestEventListeners()
{ {
let e = test_helper1; // easier to type this name let e = test_helper1; // easier to type this name
// Swipe gesture animation events
e("MozSwipeGestureStart", 0, -0.7, 0);
e("MozSwipeGestureUpdate", 0, -0.4, 0);
e("MozSwipeGestureEnd", 0, 0, 0);
e("MozSwipeGestureStart", 0, 0.6, 0);
e("MozSwipeGestureUpdate", 0, 0.3, 0);
e("MozSwipeGestureEnd", 0, 1, 0);
// Swipe gesture event // Swipe gesture event
e("MozSwipeGesture", SimpleGestureEvent.DIRECTION_LEFT, 0.0, 0); e("MozSwipeGesture", SimpleGestureEvent.DIRECTION_LEFT, 0.0, 0);
e("MozSwipeGesture", SimpleGestureEvent.DIRECTION_RIGHT, 0.0, 0); e("MozSwipeGesture", SimpleGestureEvent.DIRECTION_RIGHT, 0.0, 0);
@ -199,7 +207,7 @@ function test_helper2(type, direction, delta, altKey, ctrlKey, shiftKey, metaKey
10, 10, 10, 10, 10, 10, 10, 10,
ctrlKey, altKey, shiftKey, metaKey, ctrlKey, altKey, shiftKey, metaKey,
1, window, 1, window,
direction, delta, 0); 0, direction, delta, 0);
successful = true; successful = true;
} }
catch (ex) { catch (ex) {

Просмотреть файл

@ -7,30 +7,12 @@ var gTests = [
{ {
name: "normal search (search service)", name: "normal search (search service)",
testText: "test search", testText: "test search",
searchURL: Services.search.originalDefaultEngine.getSubmission("test search", null, "keyword").uri.spec searchURL: Services.search.defaultEngine.getSubmission("test search", null, "keyword").uri.spec
}, },
{ {
name: "?-prefixed search (search service)", name: "?-prefixed search (search service)",
testText: "? foo ", testText: "? foo ",
searchURL: Services.search.originalDefaultEngine.getSubmission("foo", null, "keyword").uri.spec searchURL: Services.search.defaultEngine.getSubmission("foo", null, "keyword").uri.spec
},
{
name: "normal search (keyword.url)",
testText: "test search",
keywordURLPref: "http://example.com/?q=",
searchURL: "http://example.com/?q=test+search"
},
{
name: "?-prefixed search (keyword.url)",
testText: "? foo ",
keywordURLPref: "http://example.com/?q=",
searchURL: "http://example.com/?q=foo"
},
{
name: "encoding test (keyword.url)",
testText: "test encoded+%/",
keywordURLPref: "http://example.com/?q=",
searchURL: "http://example.com/?q=test+encoded%2B%25%2F"
} }
]; ];

Просмотреть файл

@ -1,3 +1,5 @@
/* This Source Code Form is subject to the terms of the Mozilla Public /* 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 * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -17,11 +19,11 @@ pref("app.update.promptWaitTime", 86400);
pref("app.update.url.manual", "http://www.mozilla.com/firefox/channel/"); pref("app.update.url.manual", "http://www.mozilla.com/firefox/channel/");
// A default value for the "More information about this update" link // A default value for the "More information about this update" link
// supplied in the "An update is available" page of the update wizard. // supplied in the "An update is available" page of the update wizard.
pref("app.update.url.details", "http://www.mozilla.org/projects/%APP%/"); pref("app.update.url.details", "http://www.mozilla.org/projects/firefox/");
// Release notes and vendor URLs // Release notes and vendor URLs
pref("app.releaseNotesURL", "http://www.mozilla.org/projects/%APP%/%VERSION%/releasenotes/"); pref("app.releaseNotesURL", "http://www.mozilla.org/projects/firefox/%VERSION%/releasenotes/");
pref("app.vendorURL", "http://www.mozilla.org/projects/%APP%/"); pref("app.vendorURL", "http://www.mozilla.org/projects/firefox/");
// Search codes belong only in builds with official branding // Search codes belong only in builds with official branding
pref("browser.search.param.yahoo-fr", ""); pref("browser.search.param.yahoo-fr", "");

Просмотреть файл

@ -2,8 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
pref("startup.homepage_override_url","http://www.mozilla.org/projects/%APP%/%VERSION%/whatsnew/?oldversion=%OLD_VERSION%"); pref("startup.homepage_override_url","http://www.mozilla.org/projects/firefox/%VERSION%/whatsnew/?oldversion=%OLD_VERSION%");
pref("startup.homepage_welcome_url","http://www.mozilla.org/projects/%APP%/%VERSION%/firstrun/"); pref("startup.homepage_welcome_url","http://www.mozilla.org/projects/firefox/%VERSION%/firstrun/");
// The time interval between checks for a new version (in seconds) // The time interval between checks for a new version (in seconds)
pref("app.update.interval", 7200); // 2 hours pref("app.update.interval", 7200); // 2 hours
// The time interval between the downloading of mar file chunks in the // The time interval between the downloading of mar file chunks in the
@ -16,11 +16,11 @@ pref("app.update.promptWaitTime", 43200);
pref("app.update.url.manual", "http://nightly.mozilla.org/"); pref("app.update.url.manual", "http://nightly.mozilla.org/");
// A default value for the "More information about this update" link // A default value for the "More information about this update" link
// supplied in the "An update is available" page of the update wizard. // supplied in the "An update is available" page of the update wizard.
pref("app.update.url.details", "http://www.mozilla.org/projects/%APP%/"); pref("app.update.url.details", "http://www.mozilla.org/projects/firefox/");
// Release notes and vendor URLs // Release notes and vendor URLs
pref("app.releaseNotesURL", "http://www.mozilla.org/projects/%APP%/%VERSION%/releasenotes/"); pref("app.releaseNotesURL", "http://www.mozilla.org/projects/firefox/%VERSION%/releasenotes/");
pref("app.vendorURL", "http://www.mozilla.org/projects/%APP%/"); pref("app.vendorURL", "http://www.mozilla.org/projects/firefox/");
// Search codes belong only in builds with official branding // Search codes belong only in builds with official branding
pref("browser.search.param.yahoo-fr", ""); pref("browser.search.param.yahoo-fr", "");

Просмотреть файл

@ -2,8 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
pref("startup.homepage_override_url","http://www.mozilla.com/%LOCALE%/%APP%/%VERSION%/whatsnew/?oldversion=%OLD_VERSION%"); pref("startup.homepage_override_url","http://www.mozilla.com/%LOCALE%/firefox/%VERSION%/whatsnew/?oldversion=%OLD_VERSION%");
pref("startup.homepage_welcome_url","http://www.mozilla.com/%LOCALE%/%APP%/%VERSION%/firstrun/"); pref("startup.homepage_welcome_url","http://www.mozilla.com/%LOCALE%/firefox/%VERSION%/firstrun/");
// Interval: Time between checks for a new version (in seconds) // Interval: Time between checks for a new version (in seconds)
pref("app.update.interval", 43200); // 12 hours pref("app.update.interval", 43200); // 12 hours
// The time interval between the downloading of mar file chunks in the // The time interval between the downloading of mar file chunks in the
@ -16,11 +16,11 @@ pref("app.update.promptWaitTime", 86400);
pref("app.update.url.manual", "http://www.firefox.com"); pref("app.update.url.manual", "http://www.firefox.com");
// A default value for the "More information about this update" link // A default value for the "More information about this update" link
// supplied in the "An update is available" page of the update wizard. // supplied in the "An update is available" page of the update wizard.
pref("app.update.url.details", "http://www.mozilla.com/%LOCALE%/%APP%/releases/"); pref("app.update.url.details", "http://www.mozilla.com/%LOCALE%/firefox/releases/");
// Release notes and vendor URLs // Release notes and vendor URLs
pref("app.releaseNotesURL", "http://www.mozilla.com/%LOCALE%/%APP%/%VERSION%/releasenotes/"); pref("app.releaseNotesURL", "http://www.mozilla.com/%LOCALE%/firefox/%VERSION%/releasenotes/");
pref("app.vendorURL", "http://www.mozilla.com/%LOCALE%/%APP%/"); pref("app.vendorURL", "http://www.mozilla.com/%LOCALE%/firefox/");
pref("browser.search.param.ms-pc", "MOZI"); pref("browser.search.param.ms-pc", "MOZI");
pref("browser.search.param.yahoo-fr", "moz35"); pref("browser.search.param.yahoo-fr", "moz35");

Просмотреть файл

@ -2,8 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
pref("startup.homepage_override_url","http://www.mozilla.org/projects/%APP%/%VERSION%/whatsnew/"); pref("startup.homepage_override_url","http://www.mozilla.org/projects/firefox/%VERSION%/whatsnew/");
pref("startup.homepage_welcome_url","http://www.mozilla.org/projects/%APP%/%VERSION%/firstrun/"); pref("startup.homepage_welcome_url","http://www.mozilla.org/projects/firefox/%VERSION%/firstrun/");
// The time interval between checks for a new version (in seconds) // The time interval between checks for a new version (in seconds)
pref("app.update.interval", 86400); // 24 hours pref("app.update.interval", 86400); // 24 hours
// The time interval between the downloading of mar file chunks in the // The time interval between the downloading of mar file chunks in the
@ -13,14 +13,14 @@ pref("app.update.download.backgroundInterval", 60);
pref("app.update.promptWaitTime", 86400); pref("app.update.promptWaitTime", 86400);
// URL user can browse to manually if for some reason all update installation // URL user can browse to manually if for some reason all update installation
// attempts fail. // attempts fail.
pref("app.update.url.manual", "http://www.mozilla.org/products/%APP%/"); pref("app.update.url.manual", "http://www.mozilla.org/products/firefox/");
// A default value for the "More information about this update" link // A default value for the "More information about this update" link
// supplied in the "An update is available" page of the update wizard. // supplied in the "An update is available" page of the update wizard.
pref("app.update.url.details", "http://www.mozilla.org/projects/%APP%/"); pref("app.update.url.details", "http://www.mozilla.org/projects/firefox/");
// Release notes and vendor URLs // Release notes and vendor URLs
pref("app.releaseNotesURL", "http://www.mozilla.org/projects/%APP%/%VERSION%/releasenotes/"); pref("app.releaseNotesURL", "http://www.mozilla.org/projects/firefox/%VERSION%/releasenotes/");
pref("app.vendorURL", "http://www.mozilla.org/projects/%APP%/"); pref("app.vendorURL", "http://www.mozilla.org/projects/firefox/");
// Search codes belong only in builds with official branding // Search codes belong only in builds with official branding
pref("browser.search.param.yahoo-fr", ""); pref("browser.search.param.yahoo-fr", "");

Просмотреть файл

@ -77,9 +77,10 @@ FirefoxProfileMigrator.prototype.getResources = function() {
let formData = getFileResource(types.FORMDATA, ["formhistory.sqlite"]); let formData = getFileResource(types.FORMDATA, ["formhistory.sqlite"]);
let bookmarksBackups = getFileResource(types.OTHERDATA, let bookmarksBackups = getFileResource(types.OTHERDATA,
[PlacesUtils.backups.profileRelativeFolderPath]); [PlacesUtils.backups.profileRelativeFolderPath]);
let dictionary = getFileResource(types.OTHERDATA, ["persdict.dat"]);
return [r for each (r in [places, cookies, passwords, formData, return [r for each (r in [places, cookies, passwords, formData,
bookmarksBackups]) if (r)]; dictionary, bookmarksBackups]) if (r)];
} }
Object.defineProperty(FirefoxProfileMigrator.prototype, "startupOnlyMigrator", { Object.defineProperty(FirefoxProfileMigrator.prototype, "startupOnlyMigrator", {

Просмотреть файл

@ -405,7 +405,8 @@ this.MigrationUtils = Object.freeze({
*/ */
getLocalizedString: function MU_getLocalizedString(aKey, aReplacements) { getLocalizedString: function MU_getLocalizedString(aKey, aReplacements) {
const OVERRIDES = { const OVERRIDES = {
"4_firefox": "4_firefox_history_and_bookmarks" "4_firefox": "4_firefox_history_and_bookmarks",
"64_firefox": "64_firefox_other"
}; };
aKey = OVERRIDES[aKey] || aKey; aKey = OVERRIDES[aKey] || aKey;

Просмотреть файл

@ -53,9 +53,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "webrtcUI",
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm"); "resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "KeywordURLResetPrompter",
"resource:///modules/KeywordURLResetPrompter.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow", XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
"resource:///modules/RecentWindow.jsm"); "resource:///modules/RecentWindow.jsm");
@ -264,13 +261,6 @@ BrowserGlue.prototype = {
this._initPlaces(false); this._initPlaces(false);
} }
break; break;
case "defaultURIFixup-using-keyword-pref":
if (KeywordURLResetPrompter.shouldPrompt) {
let keywordURI = subject.QueryInterface(Ci.nsIURI);
KeywordURLResetPrompter.prompt(this.getMostRecentBrowserWindow(),
keywordURI);
}
break;
case "initial-migration-will-import-default-bookmarks": case "initial-migration-will-import-default-bookmarks":
this._migrationImportsDefaultBookmarks = true; this._migrationImportsDefaultBookmarks = true;
break; break;
@ -344,7 +334,6 @@ BrowserGlue.prototype = {
os.addObserver(this, "distribution-customization-complete", false); os.addObserver(this, "distribution-customization-complete", false);
os.addObserver(this, "places-shutdown", false); os.addObserver(this, "places-shutdown", false);
this._isPlacesShutdownObserver = true; this._isPlacesShutdownObserver = true;
os.addObserver(this, "defaultURIFixup-using-keyword-pref", false);
os.addObserver(this, "handle-xul-text-link", false); os.addObserver(this, "handle-xul-text-link", false);
os.addObserver(this, "profile-before-change", false); os.addObserver(this, "profile-before-change", false);
#ifdef MOZ_SERVICES_HEALTHREPORT #ifdef MOZ_SERVICES_HEALTHREPORT
@ -378,7 +367,6 @@ BrowserGlue.prototype = {
os.removeObserver(this, "places-database-locked"); os.removeObserver(this, "places-database-locked");
if (this._isPlacesShutdownObserver) if (this._isPlacesShutdownObserver)
os.removeObserver(this, "places-shutdown"); os.removeObserver(this, "places-shutdown");
os.removeObserver(this, "defaultURIFixup-using-keyword-pref");
os.removeObserver(this, "handle-xul-text-link"); os.removeObserver(this, "handle-xul-text-link");
os.removeObserver(this, "profile-before-change"); os.removeObserver(this, "profile-before-change");
#ifdef MOZ_SERVICES_HEALTHREPORT #ifdef MOZ_SERVICES_HEALTHREPORT
@ -564,9 +552,6 @@ BrowserGlue.prototype = {
}); });
} }
let keywordURLUserSet = Services.prefs.prefHasUserValue("keyword.URL");
Services.telemetry.getHistogramById("FX_KEYWORD_URL_USERSET").add(keywordURLUserSet);
// Perform default browser checking. // Perform default browser checking.
var shell; var shell;
try { try {
@ -1223,7 +1208,7 @@ BrowserGlue.prototype = {
}, },
_migrateUI: function BG__migrateUI() { _migrateUI: function BG__migrateUI() {
const UI_VERSION = 9; const UI_VERSION = 11;
const BROWSER_DOCURL = "chrome://browser/content/browser.xul#"; const BROWSER_DOCURL = "chrome://browser/content/browser.xul#";
let currentUIVersion = 0; let currentUIVersion = 0;
try { try {
@ -1366,6 +1351,29 @@ BrowserGlue.prototype = {
Services.prefs.clearUserPref("browser.library.useNewDownloadsView"); Services.prefs.clearUserPref("browser.library.useNewDownloadsView");
} }
#ifdef XP_WIN
if (currentUIVersion < 10) {
// For Windows systems with display set to > 96dpi (i.e. systemDefaultScale
// will return a value > 1.0), we want to discard any saved full-zoom settings,
// as we'll now be scaling the content according to the system resolution
// scale factor (Windows "logical DPI" setting)
let sm = Cc["@mozilla.org/gfx/screenmanager;1"].getService(Ci.nsIScreenManager);
if (sm.systemDefaultScale > 1.0) {
let cps2 = Cc["@mozilla.org/content-pref/service;1"].
getService(Ci.nsIContentPrefService2);
cps2.removeByName("browser.content.full-zoom", null);
}
}
#endif
if (currentUIVersion < 11) {
Services.prefs.clearUserPref("dom.disable_window_move_resize");
Services.prefs.clearUserPref("dom.disable_window_flip");
Services.prefs.clearUserPref("dom.event.contextmenu.enabled");
Services.prefs.clearUserPref("javascript.enabled");
Services.prefs.clearUserPref("permissions.default.image");
}
if (this._dirty) if (this._dirty)
this._dataSource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush(); this._dataSource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();

Просмотреть файл

@ -1,46 +0,0 @@
<?xml version="1.0"?>
<!-- -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -->
<!-- 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/. -->
<!DOCTYPE prefwindow SYSTEM "chrome://browser/locale/preferences/advanced-scripts.dtd">
<?xml-stylesheet href="chrome://global/skin/"?>
<prefwindow id="AdvancedJSDialog" type="child"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="&advancedJSDialog.title;"
dlgbuttons="accept,cancel,help"
ondialoghelp="openPrefsHelp()">
<script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
<prefpane id="AdvancedJSDialogPane"
helpTopic="prefs-advanced-javascript">
<preferences>
<preference id="dom.event.contextmenu.enabled" name="dom.event.contextmenu.enabled" type="bool"/>
<preference id="dom.disable_window_move_resize" name="dom.disable_window_move_resize" type="bool" inverted="true"/>
<preference id="dom.disable_window_flip" name="dom.disable_window_flip" type="bool" inverted="true"/>
</preferences>
<script type="application/javascript" src="chrome://browser/content/preferences/advanced-scripts.js"/>
<stringbundle id="preferencesBundle" src="chrome://browser/locale/preferences/preferences.properties"/>
<description value="&allowScripts.label;"/>
<checkbox id="moveResizePopupWindows" label="&moveResizePopupWindows.label;"
accesskey="&moveResizePopupWindows.accesskey;"
preference="dom.disable_window_move_resize"/>
<checkbox id="raiseLowerWindows" label="&raiseLowerWindows.label;"
accesskey="&raiseLowerWindows.accesskey;"
preference="dom.disable_window_flip"/>
<checkbox id="disableContextMenus" label="&disableContextMenus.label;"
accesskey="&disableContextMenus.accesskey;"
preference="dom.event.contextmenu.enabled"/>
</prefpane>
</prefwindow>

Просмотреть файл

@ -36,8 +36,7 @@ var gContentPane = {
* The exceptions types which may be passed to this._showExceptions(). * The exceptions types which may be passed to this._showExceptions().
*/ */
_exceptionsParams: { _exceptionsParams: {
popup: { blockVisible: false, sessionVisible: false, allowVisible: true, prefilledHost: "", permissionType: "popup" }, popup: { blockVisible: false, sessionVisible: false, allowVisible: true, prefilledHost: "", permissionType: "popup" }
image: { blockVisible: true, sessionVisible: false, allowVisible: true, prefilledHost: "", permissionType: "image" }
}, },
/** /**
@ -62,15 +61,6 @@ var gContentPane = {
* *
* dom.disable_open_during_load * dom.disable_open_during_load
* - true if popups are blocked by default, false otherwise * - true if popups are blocked by default, false otherwise
* permissions.default.image
* - an integer:
* 1 all images should be loaded,
* 2 no images should be loaded,
* 3 load only images from the site on which the current page resides
* (i.e., if viewing foo.example.com, foo.example.com/foo.jpg and
* bar.foo.example.com/bar.jpg load but example.com/quux.jpg does not)
* javascript.enabled
* - true if JavaScript is enabled, false otherwise
*/ */
// POP-UPS // POP-UPS
@ -84,48 +74,6 @@ var gContentPane = {
this._showExceptions("popup"); this._showExceptions("popup");
}, },
// IMAGES
/**
* Converts the value of the permissions.default.image preference into a
* Boolean value for use in determining the state of the "load images"
* checkbox, returning true if images should be loaded and false otherwise.
*/
readLoadImages: function ()
{
var pref = document.getElementById("permissions.default.image");
return (pref.value == 1 || pref.value == 3);
},
/**
* Returns the "load images" preference value which maps to the state of the
* preferences UI.
*/
writeLoadImages: function ()
{
return (document.getElementById("loadImages").checked) ? 1 : 2;
},
/**
* Displays image exception preferences for which websites can and cannot
* load images.
*/
showImageExceptions: function ()
{
this._showExceptions("image");
},
// JAVASCRIPT
/**
* Displays the advanced JavaScript preferences for enabling or disabling
* various annoying behaviors.
*/
showAdvancedJS: function ()
{
document.documentElement.openSubDialog("chrome://browser/content/preferences/advanced-scripts.xul",
"", null);
},
// FONTS // FONTS

Просмотреть файл

@ -22,17 +22,8 @@
<preferences id="contentPreferences"> <preferences id="contentPreferences">
<!--XXX buttons prefs --> <!--XXX buttons prefs -->
<!-- POPUPS, IMAGES, JAVASCRIPT --> <!-- POPUPS -->
<preference id="dom.disable_open_during_load" name="dom.disable_open_during_load" type="bool"/> <preference id="dom.disable_open_during_load" name="dom.disable_open_during_load" type="bool"/>
<preference id="permissions.default.image" name="permissions.default.image" type="int"/>
<preference id="javascript.enabled" name="javascript.enabled" type="bool"/>
<preference id="pref.advanced.images.disable_button.view_image"
name="pref.advanced.images.disable_button.view_image"
type="bool"/>
<preference id="pref.advanced.javascript.disable_button.advanced"
name="pref.advanced.javascript.disable_button.advanced"
type="bool"/>
<!-- FONTS --> <!-- FONTS -->
<preference id="font.language.group" <preference id="font.language.group"
@ -65,36 +56,6 @@
oncommand="gContentPane.showPopupExceptions();" oncommand="gContentPane.showPopupExceptions();"
accesskey="&popupExceptions.accesskey;"/> accesskey="&popupExceptions.accesskey;"/>
</row> </row>
<row id="enableImagesRow">
<vbox align="start">
<checkbox id="loadImages"
label="&loadImages.label;"
accesskey="&loadImages.accesskey;"
preference="permissions.default.image"
onsyncfrompreference="return gContentPane.readLoadImages();"
onsynctopreference="return gContentPane.writeLoadImages();"/>
</vbox>
<vbox>
<button label="&exceptions.label;"
accesskey="&exceptions.accesskey;"
oncommand="gContentPane.showImageExceptions();"
preference="pref.advanced.images.disable_button.view_image"/>
</vbox>
</row>
<row id="enableJavaScriptRow">
<vbox align="start">
<checkbox id="enableJavaScript" preference="javascript.enabled"
label="&enableJavaScript.label;" accesskey="&enableJavaScript.accesskey;"
onsyncfrompreference="return gContentPane.updateButtons('advancedJSButton',
'javascript.enabled');"/>
</vbox>
<vbox>
<button id="advancedJSButton" label="&advancedJS.label;"
accesskey="&advancedJS.accesskey;"
oncommand="gContentPane.showAdvancedJS();"
preference="pref.advanced.javascript.disable_button.advanced"/>
</vbox>
</row>
</rows> </rows>
</grid> </grid>
</groupbox> </groupbox>

Просмотреть файл

@ -36,9 +36,7 @@ var gContentPane = {
*/ */
_exceptionsParams: { _exceptionsParams: {
popup: { blockVisible: false, sessionVisible: false, allowVisible: true, popup: { blockVisible: false, sessionVisible: false, allowVisible: true,
prefilledHost: "", permissionType: "popup" }, prefilledHost: "", permissionType: "popup" }
image: { blockVisible: true, sessionVisible: false, allowVisible: true,
prefilledHost: "", permissionType: "image" }
}, },
/** /**
@ -63,15 +61,6 @@ var gContentPane = {
* *
* dom.disable_open_during_load * dom.disable_open_during_load
* - true if popups are blocked by default, false otherwise * - true if popups are blocked by default, false otherwise
* permissions.default.image
* - an integer:
* 1 all images should be loaded,
* 2 no images should be loaded,
* 3 load only images from the site on which the current page resides
* (i.e., if viewing foo.example.com, foo.example.com/foo.jpg and
* bar.foo.example.com/bar.jpg load but example.com/quux.jpg does not)
* javascript.enabled
* - true if JavaScript is enabled, false otherwise
*/ */
// POP-UPS // POP-UPS
@ -85,49 +74,6 @@ var gContentPane = {
this._showExceptions("popup"); this._showExceptions("popup");
}, },
// IMAGES
/**
* Converts the value of the permissions.default.image preference into a
* Boolean value for use in determining the state of the "load images"
* checkbox, returning true if images should be loaded and false otherwise.
*/
readLoadImages: function ()
{
var pref = document.getElementById("permissions.default.image");
return (pref.value == 1 || pref.value == 3);
},
/**
* Returns the "load images" preference value which maps to the state of the
* preferences UI.
*/
writeLoadImages: function ()
{
return (document.getElementById("loadImages").checked) ? 1 : 2;
},
/**
* Displays image exception preferences for which websites can and cannot
* load images.
*/
showImageExceptions: function ()
{
this._showExceptions("image");
},
// JAVASCRIPT
/**
* Displays the advanced JavaScript preferences for enabling or disabling
* various annoying behaviors.
*/
showAdvancedJS: function ()
{
openDialog("chrome://browser/content/preferences/advanced-scripts.xul",
"Browser:AdvancedScripts", null);
},
// FONTS // FONTS
/** /**

Просмотреть файл

@ -4,22 +4,10 @@
<preferences id="contentPreferences"> <preferences id="contentPreferences">
<!-- Popups, images, and JavaScript --> <!-- Popups -->
<preference id="dom.disable_open_during_load" <preference id="dom.disable_open_during_load"
name="dom.disable_open_during_load" name="dom.disable_open_during_load"
type="bool"/> type="bool"/>
<preference id="permissions.default.image"
name="permissions.default.image"
type="int"/>
<preference id="javascript.enabled"
name="javascript.enabled"
type="bool"/>
<preference id="pref.advanced.images.disable_button.view_image"
name="pref.advanced.images.disable_button.view_image"
type="bool"/>
<preference id="pref.advanced.javascript.disable_button.advanced"
name="pref.advanced.javascript.disable_button.advanced"
type="bool"/>
<!-- Fonts --> <!-- Fonts -->
<preference id="font.language.group" <preference id="font.language.group"
@ -56,36 +44,6 @@
oncommand="gContentPane.showPopupExceptions();" oncommand="gContentPane.showPopupExceptions();"
accesskey="&popupExceptions.accesskey;"/> accesskey="&popupExceptions.accesskey;"/>
</row> </row>
<row id="enableImagesRow">
<vbox align="start">
<checkbox id="loadImages"
label="&loadImages.label;"
accesskey="&loadImages.accesskey;"
preference="permissions.default.image"
onsyncfrompreference="return gContentPane.readLoadImages();"
onsynctopreference="return gContentPane.writeLoadImages();"/>
</vbox>
<vbox>
<button label="&exceptions.label;"
accesskey="&exceptions.accesskey;"
oncommand="gContentPane.showImageExceptions();"
preference="pref.advanced.images.disable_button.view_image"/>
</vbox>
</row>
<row id="enableJavaScriptRow">
<vbox align="start">
<checkbox id="enableJavaScript" preference="javascript.enabled"
label="&enableJavaScript.label;" accesskey="&enableJavaScript.accesskey;"
onsyncfrompreference="return gContentPane.updateButtons('advancedJSButton',
'javascript.enabled');"/>
</vbox>
<vbox>
<button id="advancedJSButton" label="&advancedJS.label;"
accesskey="&advancedJS.accesskey;"
oncommand="gContentPane.showAdvancedJS();"
preference="pref.advanced.javascript.disable_button.advanced"/>
</vbox>
</row>
</rows> </rows>
</grid> </grid>
</groupbox> </groupbox>

Просмотреть файл

@ -146,13 +146,14 @@ var gPrivacyPane = {
*/ */
getTrackingPrefs: function PPP_getTrackingPrefs() getTrackingPrefs: function PPP_getTrackingPrefs()
{ {
let dntValuePref = document.getElementById("privacy.donottrackheader.value"), // XXX avoid using bindings that might not be attached, see bug 859982
dntEnabledPref = document.getElementById("privacy.donottrackheader.enabled"); let dntValue = Services.prefs.getBoolPref("privacy.donottrackheader.value"),
dntEnabled = Services.prefs.getBoolPref("privacy.donottrackheader.enabled");
// if DNT is enbaled, select the value from the selected radio // if DNT is enbaled, select the value from the selected radio
// button, otherwise choose the "no preference" radio button // button, otherwise choose the "no preference" radio button
if (dntEnabledPref.value) if (dntEnabled)
return dntValuePref.value; return dntValue;
return document.getElementById("dntnopref").value; return document.getElementById("dntnopref").value;
}, },

Просмотреть файл

@ -9,7 +9,6 @@ browser.jar:
content/browser/preferences/aboutPermissions.xml content/browser/preferences/aboutPermissions.xml
* content/browser/preferences/advanced.xul * content/browser/preferences/advanced.xul
* content/browser/preferences/advanced.js * content/browser/preferences/advanced.js
content/browser/preferences/advanced-scripts.xul
content/browser/preferences/applications.xul content/browser/preferences/applications.xul
* content/browser/preferences/applications.js * content/browser/preferences/applications.js
content/browser/preferences/applicationManager.xul content/browser/preferences/applicationManager.xul

Просмотреть файл

@ -45,6 +45,7 @@ var gEngineManagerDialog = {
break; break;
case "engine-removed": case "engine-removed":
case "engine-current": case "engine-current":
case "engine-default":
// Not relevant // Not relevant
break; break;
} }

Просмотреть файл

@ -121,8 +121,12 @@
<field name="searchButton">document.getAnonymousElementByAttribute(this, <field name="searchButton">document.getAnonymousElementByAttribute(this,
"anonid", "searchbar-engine-button");</field> "anonid", "searchbar-engine-button");</field>
<property name="currentEngine" <property name="currentEngine">
onset="this.searchService.currentEngine = val; return val;"> <setter><![CDATA[
let ss = this.searchService;
ss.defaultEngine = ss.currentEngine = val;
return val;
]]></setter>
<getter><![CDATA[ <getter><![CDATA[
var currentEngine = this.searchService.currentEngine; var currentEngine = this.searchService.currentEngine;
// Return a dummy engine if there is no currentEngine // Return a dummy engine if there is no currentEngine
@ -422,8 +426,9 @@
var newIndex = this.engines.indexOf(this.currentEngine); var newIndex = this.engines.indexOf(this.currentEngine);
newIndex += isNextEngine ? 1 : -1; newIndex += isNextEngine ? 1 : -1;
if (newIndex >= 0 && newIndex < this.engines.length) if (newIndex >= 0 && newIndex < this.engines.length) {
this.currentEngine = this.engines[newIndex]; this.currentEngine = this.engines[newIndex];
}
aEvent.preventDefault(); aEvent.preventDefault();
aEvent.stopPropagation(); aEvent.stopPropagation();

Просмотреть файл

@ -57,7 +57,7 @@ function test() {
let engine = Services.search.getEngineByName("Google"); let engine = Services.search.getEngineByName("Google");
ok(engine, "Google"); ok(engine, "Google");
is(Services.search.originalDefaultEngine, engine, "Check that Google is the default search engine"); is(Services.search.defaultEngine, engine, "Check that Google is the default search engine");
let distributionID; let distributionID;
try { try {

Просмотреть файл

@ -23,62 +23,6 @@ function nsSidebar()
nsSidebar.prototype.classID = SIDEBAR_CID; nsSidebar.prototype.classID = SIDEBAR_CID;
nsSidebar.prototype.nc = "http://home.netscape.com/NC-rdf#";
function sidebarURLSecurityCheck(url)
{
if (!/^(https?:|ftp:)/i.test(url)) {
Components.utils.reportError("Invalid argument passed to window.sidebar.addPanel: Unsupported panel URL." );
return false;
}
return true;
}
/* decorate prototype to provide ``class'' methods and property accessors */
nsSidebar.prototype.addPanel =
function (aTitle, aContentURL, aCustomizeURL)
{
debug("addPanel(" + aTitle + ", " + aContentURL + ", " +
aCustomizeURL + ")");
return this.addPanelInternal(aTitle, aContentURL, aCustomizeURL, false);
}
nsSidebar.prototype.addPersistentPanel =
function(aTitle, aContentURL, aCustomizeURL)
{
debug("addPersistentPanel(" + aTitle + ", " + aContentURL + ", " +
aCustomizeURL + ")\n");
return this.addPanelInternal(aTitle, aContentURL, aCustomizeURL, true);
}
nsSidebar.prototype.addPanelInternal =
function (aTitle, aContentURL, aCustomizeURL, aPersist)
{
// XXX Bug 620418: We shouldn't do this anymore. Instead, we should find the
// global object for our caller and use it.
var win = Services.wm.getMostRecentWindow("navigator:browser");
if (!sidebarURLSecurityCheck(aContentURL))
return;
var uri = null;
try {
uri = Services.io.newURI(aContentURL, null, null);
}
catch(ex) { return; }
win.PlacesUIUtils.showBookmarkDialog({ action: "add"
, type: "bookmark"
, hiddenRows: [ "description"
, "keyword"
, "location" ]
, uri: uri
, title: aTitle
, loadBookmarkInSidebar: true
}, win);
}
nsSidebar.prototype.validateSearchEngine = nsSidebar.prototype.validateSearchEngine =
function (engineURL, iconURL) function (engineURL, iconURL)
{ {

Просмотреть файл

@ -161,10 +161,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
// Create the promise that will be resolved when the add-on listing has // Create the promise that will be resolved when the add-on listing has
// been finished. // been finished.
let promise = context.createPromise(); let deferred = context.defer();
let types = aArgs.type == "all" ? null : [aArgs.type]; let types = aArgs.type == "all" ? null : [aArgs.type];
AddonManager.getAddonsByTypes(types, list.bind(promise, aArgs.type)); AddonManager.getAddonsByTypes(types, list.bind(deferred, aArgs.type));
return promise; return deferred.promise;
} }
}); });
@ -259,10 +259,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
this.resolve(message); this.resolve(message);
} }
let promise = context.createPromise(); let deferred = context.defer();
// List the installed add-ons, enable one when done listing. // List the installed add-ons, enable one when done listing.
AddonManager.getAllAddons(enable.bind(promise, aArgs.name)); AddonManager.getAllAddons(enable.bind(deferred, aArgs.name));
return promise; return deferred.promise;
} }
}); });
@ -301,10 +301,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
this.resolve(message); this.resolve(message);
} }
let promise = context.createPromise(); let deferred = context.defer();
// List the installed add-ons, disable one when done listing. // List the installed add-ons, disable one when done listing.
AddonManager.getAllAddons(disable.bind(promise, aArgs.name)); AddonManager.getAllAddons(disable.bind(deferred, aArgs.name));
return promise; return deferred.promise;
} }
}); });
module.CmdAddonFlags.addonsLoaded = true; module.CmdAddonFlags.addonsLoaded = true;
@ -1235,7 +1235,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
return gcli.lookup('jsbInvalidURL'); return gcli.lookup('jsbInvalidURL');
} }
let promise = context.createPromise(); let deferred = context.defer();
xhr.onreadystatechange = function(aEvt) { xhr.onreadystatechange = function(aEvt) {
if (xhr.readyState == 4) { if (xhr.readyState == 4) {
@ -1247,15 +1247,15 @@ XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
browserWindow.Scratchpad.ScratchpadManager.openScratchpad({text: result}); browserWindow.Scratchpad.ScratchpadManager.openScratchpad({text: result});
promise.resolve(); deferred.resolve();
} else { } else {
promise.resolve("Unable to load page to beautify: " + args.url + " " + deferred.resolve("Unable to load page to beautify: " + args.url + " " +
xhr.status + " " + xhr.statusText); xhr.status + " " + xhr.statusText);
} }
}; };
} }
xhr.send(null); xhr.send(null);
return promise; return deferred.promise;
} }
}); });
}(this)); }(this));
@ -1642,13 +1642,13 @@ XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
var document = args.chrome? context.environment.chromeDocument var document = args.chrome? context.environment.chromeDocument
: context.environment.contentDocument; : context.environment.contentDocument;
if (args.delay > 0) { if (args.delay > 0) {
var promise = context.createPromise(); var deferred = context.defer();
document.defaultView.setTimeout(function Command_screenshotDelay() { document.defaultView.setTimeout(function Command_screenshotDelay() {
let reply = this.grabScreen(document, args.filename, args.clipboard, let reply = this.grabScreen(document, args.filename, args.clipboard,
args.fullpage); args.fullpage);
promise.resolve(reply); deferred.resolve(reply);
}.bind(this), args.delay * 1000); }.bind(this), args.delay * 1000);
return promise; return deferred.promise;
} }
else { else {
return this.grabScreen(document, args.filename, args.clipboard, return this.grabScreen(document, args.filename, args.clipboard,

Просмотреть файл

@ -30,32 +30,93 @@ gcli.addCommand({
gcli.addCommand({ gcli.addCommand({
name: "break list", name: "break list",
description: gcli.lookup("breaklistDesc"), description: gcli.lookup("breaklistDesc"),
returnType: "html", returnType: "breakpoints",
exec: function(args, context) { exec: function(args, context) {
let dbg = getPanel(context, "jsdebugger"); let panel = getPanel(context, "jsdebugger", {ensure_opened: true});
if (!dbg) { return panel.then(function(dbg) {
return gcli.lookup("debuggerStopped"); let breakpoints = [];
} for (let source in dbg.panelWin.DebuggerView.Sources) {
for (let { attachment: breakpoint } in source) {
let breakpoints = dbg.getAllBreakpoints(); breakpoints.push({
url: source.value,
if (Object.keys(breakpoints).length === 0) { label: source.label,
return gcli.lookup("breaklistNone"); lineNumber: breakpoint.lineNumber,
} lineText: breakpoint.lineText
});
let reply = gcli.lookup("breaklistIntro"); }
reply += "<ol>"; }
for each (let breakpoint in breakpoints) { return breakpoints;
let text = gcli.lookupFormat("breaklistLineEntry", });
[breakpoint.location.url,
breakpoint.location.line]);
reply += "<li>" + text + "</li>";
};
reply += "</ol>";
return reply;
} }
}); });
gcli.addConverter({
from: "breakpoints",
to: "view",
exec: function(breakpoints, context) {
let dbg = getPanel(context, "jsdebugger");
if (dbg && breakpoints.length) {
let SourceUtils = dbg.panelWin.SourceUtils;
let index = 0;
return context.createView({
html: breakListHtml,
data: {
breakpoints: breakpoints.map(function(breakpoint) {
return {
index: index++,
url: breakpoint.url,
label: SourceUtils.trimUrlLength(
breakpoint.label + ":" + breakpoint.lineNumber,
MAX_LABEL_LENGTH,
"start"),
lineText: breakpoint.lineText,
truncatedLineText: SourceUtils.trimUrlLength(
breakpoint.lineText,
MAX_LINE_TEXT_LENGTH,
"end")
};
}),
onclick: createUpdateHandler(context),
ondblclick: createExecuteHandler(context)
}
});
} else {
return context.createView({
html: "<p>${message}</p>",
data: { message: gcli.lookup("breaklistNone") }
});
}
}
});
var breakListHtml = "" +
"<table>" +
" <thead>" +
" <th>Source</th>" +
" <th>Line</th>" +
" <th>Actions</th>" +
" </thead>" +
" <tbody>" +
" <tr foreach='breakpoint in ${breakpoints}'>" +
" <td class='gcli-breakpoint-label'>${breakpoint.label}</td>" +
" <td class='gcli-breakpoint-lineText'>" +
" ${breakpoint.truncatedLineText}" +
" </td>" +
" <td>" +
" <span class='gcli-out-shortcut'" +
" data-command='break del ${breakpoint.index}'" +
" onclick='${onclick}'" +
" ondblclick='${ondblclick}'" +
" >" + gcli.lookup("breaklistOutRemove") + "</span>" +
" </td>" +
" </tr>" +
" </tbody>" +
"</table>" +
"";
var MAX_LINE_TEXT_LENGTH = 30;
var MAX_LABEL_LENGTH = 20;
/** /**
* 'break add' command * 'break add' command
*/ */
@ -348,14 +409,71 @@ function createXHTMLElement(document, tagname) {
return document.createElementNS("http://www.w3.org/1999/xhtml", tagname); return document.createElementNS("http://www.w3.org/1999/xhtml", tagname);
} }
/**
* Helper to find the 'data-command' attribute and call some action on it.
* @see |updateCommand()| and |executeCommand()|
*/
function withCommand(element, action) {
var command = element.getAttribute("data-command");
if (!command) {
command = element.querySelector("*[data-command]")
.getAttribute("data-command");
}
if (command) {
action(command);
}
else {
console.warn("Missing data-command for " + util.findCssSelector(element));
}
}
/**
* Create a handler to update the requisition to contain the text held in the
* first matching data-command attribute under the currentTarget of the event.
* @param context Either a Requisition or an ExecutionContext or another object
* that contains an |update()| function that follows a similar contract.
*/
function createUpdateHandler(context) {
return function(ev) {
withCommand(ev.currentTarget, function(command) {
context.update(command);
});
}
}
/**
* Create a handler to execute the text held in the data-command attribute
* under the currentTarget of the event.
* @param context Either a Requisition or an ExecutionContext or another object
* that contains an |update()| function that follows a similar contract.
*/
function createExecuteHandler(context) {
return function(ev) {
withCommand(ev.currentTarget, function(command) {
context.exec({
visible: true,
typed: command
});
});
}
}
/** /**
* A helper to go from a command context to a debugger panel * A helper to go from a command context to a debugger panel
*/ */
function getPanel(context, id) { function getPanel(context, id, opts) {
if (context == null) { if (context == null) {
return undefined; return undefined;
} }
let toolbox = gDevTools.getToolbox(context.environment.target); let target = context.environment.target;
return toolbox == null ? undefined : toolbox.getPanel(id); if (opts && opts.ensure_opened) {
return gDevTools.showToolbox(target, id).then(function(toolbox) {
return toolbox.getPanel(id);
});
} else {
let toolbox = gDevTools.getToolbox(target);
return toolbox && toolbox.getPanel(id);
}
} }

Просмотреть файл

@ -86,10 +86,4 @@ DebuggerPanel.prototype = {
getAllBreakpoints: function() { getAllBreakpoints: function() {
return this._bkp.store; return this._bkp.store;
}, },
// Private
_ensureOnlyOneRunningDebugger: function() {
// FIXME
},
}; };

Просмотреть файл

@ -12,7 +12,6 @@ const Cu = Components.utils;
const DBG_XUL = "chrome://browser/content/debugger.xul"; const DBG_XUL = "chrome://browser/content/debugger.xul";
const DBG_STRINGS_URI = "chrome://browser/locale/devtools/debugger.properties"; const DBG_STRINGS_URI = "chrome://browser/locale/devtools/debugger.properties";
const CHROME_DEBUGGER_PROFILE_NAME = "-chrome-debugger"; const CHROME_DEBUGGER_PROFILE_NAME = "-chrome-debugger";
const TAB_SWITCH_NOTIFICATION = "debugger-tab-switch";
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -83,10 +82,6 @@ DebuggerUI.prototype = {
let selectedTab = this.chromeWindow.gBrowser.selectedTab; let selectedTab = this.chromeWindow.gBrowser.selectedTab;
if (scriptDebugger) { if (scriptDebugger) {
if (scriptDebugger.ownerTab !== selectedTab) {
this.showTabSwitchNotification();
return scriptDebugger;
}
scriptDebugger.close(); scriptDebugger.close();
return null; return null;
} }
@ -171,63 +166,6 @@ DebuggerUI.prototype = {
*/ */
getChromeDebugger: function DUI_getChromeDebugger() { getChromeDebugger: function DUI_getChromeDebugger() {
return '_chromeDebugger' in this ? this._chromeDebugger : null; return '_chromeDebugger' in this ? this._chromeDebugger : null;
},
/**
* Currently, there can only be one debugger per tab.
* Show an asynchronous notification which asks the user to switch the
* script debugger to the current tab if it's already open in another one.
*/
showTabSwitchNotification: function DUI_showTabSwitchNotification() {
let gBrowser = this.chromeWindow.gBrowser;
let selectedBrowser = gBrowser.selectedBrowser;
let nbox = gBrowser.getNotificationBox(selectedBrowser);
let notification = nbox.getNotificationWithValue(TAB_SWITCH_NOTIFICATION);
if (notification) {
nbox.removeNotification(notification);
return;
}
let self = this;
let buttons = [{
id: "debugger.confirmTabSwitch.buttonSwitch",
label: L10N.getStr("confirmTabSwitch.buttonSwitch"),
accessKey: L10N.getStr("confirmTabSwitch.buttonSwitch.accessKey"),
callback: function DUI_notificationButtonSwitch() {
let scriptDebugger = self.findDebugger();
let targetWindow = scriptDebugger.globalUI.chromeWindow;
targetWindow.gBrowser.selectedTab = scriptDebugger.ownerTab;
targetWindow.focus();
}
}, {
id: "debugger.confirmTabSwitch.buttonOpen",
label: L10N.getStr("confirmTabSwitch.buttonOpen"),
accessKey: L10N.getStr("confirmTabSwitch.buttonOpen.accessKey"),
callback: function DUI_notificationButtonOpen() {
let scriptDebugger = self.findDebugger();
let targetWindow = scriptDebugger.globalUI.chromeWindow;
scriptDebugger.close();
targetWindow.addEventListener("Debugger:Shutdown", function onShutdown() {
targetWindow.removeEventListener("Debugger:Shutdown", onShutdown, false);
Services.tm.currentThread.dispatch({ run: function() {
self.toggleDebugger();
}}, 0);
}, false);
}
}];
let message = L10N.getStr("confirmTabSwitch.message");
let imageURL = "chrome://browser/skin/Info.png";
notification = nbox.appendNotification(
message, TAB_SWITCH_NOTIFICATION,
imageURL, nbox.PRIORITY_WARNING_HIGH, buttons, null);
// Make sure this is not a transient notification, to avoid the automatic
// transient notification removal.
notification.persistence = -1;
} }
}; };

Просмотреть файл

@ -15,17 +15,6 @@ const NEW_SOURCE_DISPLAY_DELAY = 200; // ms
const FETCH_SOURCE_RESPONSE_DELAY = 50; // ms const FETCH_SOURCE_RESPONSE_DELAY = 50; // ms
const FRAME_STEP_CLEAR_DELAY = 100; // ms const FRAME_STEP_CLEAR_DELAY = 100; // ms
const CALL_STACK_PAGE_SIZE = 25; // frames const CALL_STACK_PAGE_SIZE = 25; // frames
const VARIABLES_VIEW_NON_SORTABLE = [
"Array",
"Int8Array",
"Uint8Array",
"Int16Array",
"Uint16Array",
"Int32Array",
"Uint32Array",
"Float32Array",
"Float64Array"
];
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -267,7 +256,7 @@ let DebuggerController = {
this.ThreadState.connect(); this.ThreadState.connect();
this.StackFrames.connect(); this.StackFrames.connect();
this.SourceScripts.connect(); this.SourceScripts.connect();
aThreadClient.resume(); aThreadClient.resume(this._ensureResumptionOrder);
if (aCallback) { if (aCallback) {
aCallback(); aCallback();
@ -276,6 +265,15 @@ let DebuggerController = {
}); });
}, },
/**
* Warn if resuming execution produced a wrongOrder error.
*/
_ensureResumptionOrder: function DC__ensureResumptionOrder(aResponse) {
if (aResponse.error == "wrongOrder") {
DebuggerView.Toolbar.showResumeWarning(aResponse.lastPausedUrl);
}
},
/** /**
* Sets up a chrome debugging session. * Sets up a chrome debugging session.
* *
@ -303,7 +301,7 @@ let DebuggerController = {
this.ThreadState.connect(); this.ThreadState.connect();
this.StackFrames.connect(); this.StackFrames.connect();
this.SourceScripts.connect(); this.SourceScripts.connect();
aThreadClient.resume(); aThreadClient.resume(this._ensureResumptionOrder);
if (aCallback) { if (aCallback) {
aCallback(); aCallback();
@ -528,7 +526,7 @@ StackFrames.prototype = {
// If the breakpoint's conditional expression evaluation is falsy, // If the breakpoint's conditional expression evaluation is falsy,
// automatically resume execution. // automatically resume execution.
if (VariablesView.isFalsy({ value: this.currentEvaluation.return })) { if (VariablesView.isFalsy({ value: this.currentEvaluation.return })) {
this.activeThread.resume(); this.activeThread.resume(DebuggerController._ensureResumptionOrder);
return; return;
} }
} }
@ -893,7 +891,7 @@ StackFrames.prototype = {
this.activeThread.pauseGrip(grip).getPrototypeAndProperties(function(aResponse) { this.activeThread.pauseGrip(grip).getPrototypeAndProperties(function(aResponse) {
let { ownProperties, prototype } = aResponse; let { ownProperties, prototype } = aResponse;
let sortable = VARIABLES_VIEW_NON_SORTABLE.indexOf(grip.class) == -1; let sortable = VariablesView.NON_SORTABLE_CLASSES.indexOf(grip.class) == -1;
// Add all the variable properties. // Add all the variable properties.
if (ownProperties) { if (ownProperties) {

Просмотреть файл

@ -27,6 +27,7 @@ ToolbarView.prototype = {
dumpn("Initializing the ToolbarView"); dumpn("Initializing the ToolbarView");
this._instrumentsPaneToggleButton = document.getElementById("instruments-pane-toggle"); this._instrumentsPaneToggleButton = document.getElementById("instruments-pane-toggle");
this._resumeOrderPanel = document.getElementById("resumption-order-panel");
this._resumeButton = document.getElementById("resume"); this._resumeButton = document.getElementById("resume");
this._stepOverButton = document.getElementById("step-over"); this._stepOverButton = document.getElementById("step-over");
this._stepInButton = document.getElementById("step-in"); this._stepInButton = document.getElementById("step-in");
@ -89,6 +90,19 @@ ToolbarView.prototype = {
} }
}, },
/**
* Display a warning when trying to resume a debuggee while another is paused.
* Debuggees must be unpaused in a Last-In-First-Out order.
*
* @param string aPausedUrl
* The URL of the last paused debuggee.
*/
showResumeWarning: function DVT_showResumeWarning(aPausedUrl) {
let label = L10N.getFormatStr("resumptionOrderPanelTitle", [aPausedUrl]);
document.getElementById("resumption-panel-desc").textContent = label;
this._resumeOrderPanel.openPopup(this._resumeButton);
},
/** /**
* Sets the chrome globals container hidden or visible. It's hidden by default. * Sets the chrome globals container hidden or visible. It's hidden by default.
* *
@ -115,7 +129,8 @@ ToolbarView.prototype = {
*/ */
_onResumePressed: function DVT__onResumePressed() { _onResumePressed: function DVT__onResumePressed() {
if (DebuggerController.activeThread.paused) { if (DebuggerController.activeThread.paused) {
DebuggerController.activeThread.resume(); let warn = DebuggerController._ensureResumptionOrder;
DebuggerController.activeThread.resume(warn);
} else { } else {
DebuggerController.activeThread.interrupt(); DebuggerController.activeThread.interrupt();
} }
@ -149,6 +164,7 @@ ToolbarView.prototype = {
}, },
_instrumentsPaneToggleButton: null, _instrumentsPaneToggleButton: null,
_resumeOrderPanel: null,
_resumeButton: null, _resumeButton: null,
_stepOverButton: null, _stepOverButton: null,
_stepInButton: null, _stepInButton: null,

Просмотреть файл

@ -340,4 +340,14 @@
</vbox> </vbox>
</panel> </panel>
<panel id="resumption-order-panel"
type="arrow"
noautofocus="true"
position="before_start">
<hbox align="start">
<image class="alert-icon"/>
<label id="resumption-panel-desc" class="description"/>
</hbox>
</panel>
</window> </window>

Просмотреть файл

@ -17,8 +17,6 @@ MOCHITEST_BROWSER_TESTS = \
$(browser_dbg_cmd_break.js disabled until bug 722727 is fixed) \ $(browser_dbg_cmd_break.js disabled until bug 722727 is fixed) \
browser_dbg_createChrome.js \ browser_dbg_createChrome.js \
$(browser_dbg_createRemote.js disabled for intermittent failures, bug 753225) \ $(browser_dbg_createRemote.js disabled for intermittent failures, bug 753225) \
$(browser_dbg_debugger-tab-switch.js disabled until issues 106, 40 are fixed) \
$(browser_dbg_debugger-tab-switch-window.js disabled until issues 106, 40 are fixed) \
browser_dbg_debuggerstatement.js \ browser_dbg_debuggerstatement.js \
browser_dbg_listtabs.js \ browser_dbg_listtabs.js \
browser_dbg_tabactor-01.js \ browser_dbg_tabactor-01.js \

Просмотреть файл

@ -1,244 +0,0 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
let gInitialTab, gTab1, gTab2, gTab3, gTab4;
let gInitialWindow, gSecondWindow;
let gPane1, gPane2;
let gNbox;
/**
* Tests that a debugger instance can't be opened in multiple windows at once,
* and that on such an attempt a notification is shown, which can either switch
* to the old debugger instance, or close the old instance to open a new one.
*/
function test() {
gInitialWindow = window;
gInitialTab = window.gBrowser.selectedTab;
gNbox = gInitialWindow.gBrowser.getNotificationBox(gInitialWindow.gBrowser.selectedBrowser);
testTab1_initialWindow(function() {
testTab2_secondWindow(function() {
testTab3_secondWindow(function() {
testTab4_secondWindow(function() {
lastTest(function() {
cleanup(function() {
finish();
});
});
});
});
});
});
}
function testTab1_initialWindow(callback) {
gTab1 = addTab(TAB1_URL, function() {
gInitialWindow.gBrowser.selectedTab = gTab1;
gNbox = gInitialWindow.gBrowser.getNotificationBox(gInitialWindow.gBrowser.selectedBrowser);
is(gNbox.getNotificationWithValue("debugger-tab-switch"), null,
"Shouldn't have a tab switch notification.");
ok(!gInitialWindow.DebuggerUI.getDebugger(),
"Shouldn't have a debugger pane for this tab yet.");
info("Toggling a debugger (1).");
gPane1 = gInitialWindow.DebuggerUI.toggleDebugger();
ok(gPane1, "toggleDebugger() should return a pane.");
is(gPane1.ownerTab, gTab1, "Incorrect tab owner.");
is(gInitialWindow.DebuggerUI.getDebugger(), gPane1,
"getDebugger() should return the same pane as toggleDebugger().");
wait_for_connect_and_resume(function dbgLoaded() {
info("First debugger has finished loading correctly.");
executeSoon(function() {
callback();
});
}, gInitialWindow);
}, gInitialWindow);
}
function testTab2_secondWindow(callback) {
gSecondWindow = addWindow();
gTab2 = addTab(TAB1_URL, function() {
gSecondWindow.gBrowser.selectedTab = gTab2;
gNbox = gSecondWindow.gBrowser.getNotificationBox(gSecondWindow.gBrowser.selectedBrowser);
is(gNbox.getNotificationWithValue("debugger-tab-switch"), null,
"Shouldn't have a tab switch notification yet.");
ok(gSecondWindow.DebuggerUI.findDebugger(),
"Should already have a debugger pane for another tab.");
gNbox.addEventListener("AlertActive", function active() {
gNbox.removeEventListener("AlertActive", active, true);
executeSoon(function() {
ok(gPane2, "toggleDebugger() should always return a pane.");
is(gPane2.ownerTab, gTab1, "Incorrect tab owner.");
is(gSecondWindow.DebuggerUI.findDebugger(), gPane1,
"findDebugger() should return the same pane as the first call to toggleDebugger().");
is(gSecondWindow.DebuggerUI.findDebugger(), gPane2,
"findDebugger() should return the same pane as the second call to toggleDebugger().");
info("Second debugger has not loaded.");
let notification = gNbox.getNotificationWithValue("debugger-tab-switch");
ok(gNbox.currentNotification, "Should have a tab switch notification.");
is(gNbox.currentNotification, notification, "Incorrect current notification.");
info("Notification will be simply closed.");
notification.close();
executeSoon(function() {
callback();
});
});
}, true);
info("Toggling a debugger (2).");
gPane2 = gSecondWindow.DebuggerUI.toggleDebugger();
}, gSecondWindow);
}
function testTab3_secondWindow(callback) {
gTab3 = addTab(TAB1_URL, function() {
gSecondWindow.gBrowser.selectedTab = gTab3;
gNbox = gSecondWindow.gBrowser.getNotificationBox(gSecondWindow.gBrowser.selectedBrowser);
is(gNbox.getNotificationWithValue("debugger-tab-switch"), null,
"Shouldn't have a tab switch notification.");
ok(gSecondWindow.DebuggerUI.findDebugger(),
"Should already have a debugger pane for another tab.");
gNbox.addEventListener("AlertActive", function active() {
gNbox.removeEventListener("AlertActive", active, true);
executeSoon(function() {
ok(gPane2, "toggleDebugger() should always return a pane.");
is(gPane2.ownerTab, gTab1, "Incorrect tab owner.");
is(gSecondWindow.DebuggerUI.findDebugger(), gPane1,
"findDebugger() should return the same pane as the first call to toggleDebugger().");
is(gSecondWindow.DebuggerUI.findDebugger(), gPane2,
"findDebugger() should return the same pane as the second call to toggleDebugger().");
info("Second debugger has not loaded.");
let notification = gNbox.getNotificationWithValue("debugger-tab-switch");
ok(gNbox.currentNotification, "Should have a tab switch notification.");
is(gNbox.currentNotification, notification, "Incorrect current notification.");
gInitialWindow.gBrowser.selectedTab = gInitialTab;
gInitialWindow.gBrowser.tabContainer.addEventListener("TabSelect", function tabSelect() {
gInitialWindow.gBrowser.tabContainer.removeEventListener("TabSelect", tabSelect, true);
executeSoon(function() {
callback();
});
}, true);
let buttonSwitch = notification.querySelectorAll("button")[0];
buttonSwitch.focus();
EventUtils.sendKey("SPACE", gSecondWindow);
info("The switch button on the notification was pressed.");
});
}, true);
info("Toggling a debugger (3).");
gPane2 = gSecondWindow.DebuggerUI.toggleDebugger();
}, gSecondWindow);
}
function testTab4_secondWindow(callback) {
is(gInitialWindow.gBrowser.selectedTab, gTab1,
"Should've switched to the first debugged tab.");
gTab4 = addTab(TAB1_URL, function() {
gSecondWindow.gBrowser.selectedTab = gTab4;
gNbox = gSecondWindow.gBrowser.getNotificationBox(gSecondWindow.gBrowser.selectedBrowser);
is(gNbox.getNotificationWithValue("debugger-tab-switch"), null,
"Shouldn't have a tab switch notification.");
ok(gSecondWindow.DebuggerUI.findDebugger(),
"Should already have a debugger pane for another tab.");
gNbox.addEventListener("AlertActive", function active() {
gNbox.removeEventListener("AlertActive", active, true);
executeSoon(function() {
ok(gPane2, "toggleDebugger() should always return a pane.");
is(gPane2.ownerTab, gTab1, "Incorrect tab owner.");
is(gSecondWindow.DebuggerUI.findDebugger(), gPane1,
"findDebugger() should return the same pane as the first call to toggleDebugger().");
is(gSecondWindow.DebuggerUI.findDebugger(), gPane2,
"findDebugger() should return the same pane as the second call to toggleDebugger().");
info("Second debugger has not loaded.");
let notification = gNbox.getNotificationWithValue("debugger-tab-switch");
ok(gNbox.currentNotification, "Should have a tab switch notification.");
is(gNbox.currentNotification, notification, "Incorrect current notification.");
let buttonOpen = notification.querySelectorAll("button")[1];
buttonOpen.focus();
EventUtils.sendKey("SPACE", gSecondWindow);
info("The open button on the notification was pressed.");
wait_for_connect_and_resume(function() {
callback();
}, gSecondWindow);
});
}, true);
info("Toggling a debugger (4).");
gPane2 = gSecondWindow.DebuggerUI.toggleDebugger();
}, gSecondWindow);
}
function lastTest(callback) {
is(gInitialWindow.gBrowser.selectedTab, gTab1,
"The initial window should continue having selected the first debugged tab.");
is(gSecondWindow.gBrowser.selectedTab, gTab4,
"Should currently be in the fourth tab.");
is(gSecondWindow.DebuggerUI.findDebugger().ownerTab, gTab4,
"The debugger should be open for the fourth tab.");
is(gNbox.getNotificationWithValue("debugger-tab-switch"), null,
"Shouldn't have a tab switch notification.");
info("Second debugger has loaded.");
executeSoon(function() {
callback();
});
}
function cleanup(callback)
{
gPane1 = null;
gPane2 = null;
gNbox = null;
closeDebuggerAndFinish(false, function() {
removeTab(gTab1, gInitialWindow);
removeTab(gTab2, gSecondWindow);
removeTab(gTab3, gSecondWindow);
removeTab(gTab4, gSecondWindow);
gSecondWindow.close();
gTab1 = null;
gTab2 = null;
gTab3 = null;
gTab4 = null;
gInitialWindow = null;
gSecondWindow = null;
callback();
}, gSecondWindow);
}

Просмотреть файл

@ -1,235 +0,0 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
let gTab1, gTab2, gTab3, gTab4;
let gPane1, gPane2;
let gNbox;
/**
* Tests that a debugger instance can't be opened in multiple tabs at once,
* and that on such an attempt a notification is shown, which can either switch
* to the old debugger instance, or close the old instance to open a new one.
*/
function test() {
gNbox = gBrowser.getNotificationBox(gBrowser.selectedBrowser);
testTab1(function() {
testTab2(function() {
testTab3(function() {
testTab4(function() {
lastTest(function() {
cleanup(function() {
finish();
});
});
});
});
});
});
}
function testTab1(callback) {
gTab1 = addTab(TAB1_URL, function() {
gBrowser.selectedTab = gTab1;
gNbox = gBrowser.getNotificationBox(gBrowser.selectedBrowser);
is(gNbox.getNotificationWithValue("debugger-tab-switch"), null,
"Shouldn't have a tab switch notification.");
ok(!DebuggerUI.getDebugger(),
"Shouldn't have a debugger pane for this tab yet.");
info("Toggling a debugger (1).");
gPane1 = DebuggerUI.toggleDebugger();
ok(gPane1, "toggleDebugger() should return a pane.");
is(gPane1.ownerTab, gTab1, "Incorrect tab owner.");
is(DebuggerUI.getDebugger(), gPane1,
"getDebugger() should return the same pane as toggleDebugger().");
wait_for_connect_and_resume(function dbgLoaded() {
info("First debugger has finished loading correctly.");
executeSoon(function() {
callback();
});
});
});
}
function testTab2(callback) {
gTab2 = addTab(TAB1_URL, function() {
gBrowser.selectedTab = gTab2;
gNbox = gBrowser.getNotificationBox(gBrowser.selectedBrowser);
is(gNbox.getNotificationWithValue("debugger-tab-switch"), null,
"Shouldn't have a tab switch notification yet.");
ok(DebuggerUI.getDebugger(),
"Should already have a debugger pane for another tab.");
gNbox.addEventListener("AlertActive", function active() {
gNbox.removeEventListener("AlertActive", active, true);
executeSoon(function() {
ok(gPane2, "toggleDebugger() should always return a pane.");
is(gPane2.ownerTab, gTab1, "Incorrect tab owner.");
is(DebuggerUI.getDebugger(), gPane1,
"getDebugger() should return the same pane as the first call to toggleDebugger().");
is(DebuggerUI.getDebugger(), gPane2,
"getDebugger() should return the same pane as the second call to toggleDebugger().");
info("Second debugger has not loaded.");
let notification = gNbox.getNotificationWithValue("debugger-tab-switch");
ok(gNbox.currentNotification, "Should have a tab switch notification.");
is(gNbox.currentNotification, notification, "Incorrect current notification.");
info("Notification will be simply closed.");
notification.close();
executeSoon(function() {
callback();
});
});
}, true);
info("Toggling a debugger (2).");
gPane2 = DebuggerUI.toggleDebugger();
});
}
function testTab3(callback) {
gTab3 = addTab(TAB1_URL, function() {
gBrowser.selectedTab = gTab3;
gNbox = gBrowser.getNotificationBox(gBrowser.selectedBrowser);
is(gNbox.getNotificationWithValue("debugger-tab-switch"), null,
"Shouldn't have a tab switch notification.");
ok(DebuggerUI.getDebugger(),
"Should already have a debugger pane for another tab.");
gNbox.addEventListener("AlertActive", function active() {
gNbox.removeEventListener("AlertActive", active, true);
executeSoon(function() {
ok(gPane2, "toggleDebugger() should always return a pane.");
is(gPane2.ownerTab, gTab1, "Incorrect tab owner.");
is(DebuggerUI.getDebugger(), gPane1,
"getDebugger() should return the same pane as the first call to toggleDebugger().");
is(DebuggerUI.getDebugger(), gPane2,
"getDebugger() should return the same pane as the second call to toggleDebugger().");
info("Second debugger has not loaded.");
let notification = gNbox.getNotificationWithValue("debugger-tab-switch");
ok(gNbox.currentNotification, "Should have a tab switch notification.");
is(gNbox.currentNotification, notification, "Incorrect current notification.");
gBrowser.tabContainer.addEventListener("TabSelect", function tabSelect() {
gBrowser.tabContainer.removeEventListener("TabSelect", tabSelect, true);
executeSoon(function() {
callback();
});
}, true);
let buttonSwitch = notification.querySelectorAll("button")[0];
buttonSwitch.focus();
EventUtils.sendKey("SPACE");
info("The switch button on the notification was pressed.");
});
}, true);
info("Toggling a debugger (3).");
gPane2 = DebuggerUI.toggleDebugger();
});
}
function testTab4(callback) {
is(gBrowser.selectedTab, gTab1,
"Should've switched to the first debugged tab.");
gTab4 = addTab(TAB1_URL, function() {
gBrowser.selectedTab = gTab4;
gNbox = gBrowser.getNotificationBox(gBrowser.selectedBrowser);
is(gNbox.getNotificationWithValue("debugger-tab-switch"), null,
"Shouldn't have a tab switch notification.");
ok(DebuggerUI.getDebugger(),
"Should already have a debugger pane for another tab.");
gNbox.addEventListener("AlertActive", function active() {
gNbox.removeEventListener("AlertActive", active, true);
executeSoon(function() {
ok(gPane2, "toggleDebugger() should always return a pane.");
is(gPane2.ownerTab, gTab1, "Incorrect tab owner.");
is(DebuggerUI.getDebugger(), gPane1,
"getDebugger() should return the same pane as the first call to toggleDebugger().");
is(DebuggerUI.getDebugger(), gPane2,
"getDebugger() should return the same pane as the second call to toggleDebugger().");
info("Second debugger has not loaded.");
let notification = gNbox.getNotificationWithValue("debugger-tab-switch");
ok(gNbox.currentNotification, "Should have a tab switch notification.");
is(gNbox.currentNotification, notification, "Incorrect current notification.");
let buttonOpen = notification.querySelectorAll("button")[1];
buttonOpen.focus();
EventUtils.sendKey("SPACE");
info("The open button on the notification was pressed.");
wait_for_connect_and_resume(function() {
callback();
});
});
}, true);
info("Toggling a debugger (4).");
gPane2 = DebuggerUI.toggleDebugger();
});
}
function lastTest(callback) {
isnot(gBrowser.selectedTab, gTab1,
"Shouldn't have switched to the first debugged tab.");
is(gBrowser.selectedTab, gTab4,
"Should currently be in the fourth tab.");
is(DebuggerUI.getDebugger().ownerTab, gTab4,
"The debugger should be open for the fourth tab.");
is(gNbox.getNotificationWithValue("debugger-tab-switch"), null,
"Shouldn't have a tab switch notification.");
info("Second debugger has loaded.");
executeSoon(function() {
callback();
});
}
function cleanup(callback)
{
gPane1 = null;
gPane2 = null;
gNbox = null;
closeDebuggerAndFinish(false, function() {
removeTab(gTab1);
removeTab(gTab2);
removeTab(gTab3);
removeTab(gTab4);
gTab1 = null;
gTab2 = null;
gTab3 = null;
gTab4 = null;
callback();
});
}

Просмотреть файл

@ -61,14 +61,14 @@ ToolSidebar.prototype = {
let onIFrameLoaded = function() { let onIFrameLoaded = function() {
tab.setAttribute("label", iframe.contentDocument.title); tab.setAttribute("label", iframe.contentDocument.title);
iframe.removeEventListener("DOMContentLoaded", onIFrameLoaded, true); iframe.removeEventListener("load", onIFrameLoaded, true);
if ("setPanel" in iframe.contentWindow) { if ("setPanel" in iframe.contentWindow) {
iframe.contentWindow.setPanel(this._toolPanel, iframe); iframe.contentWindow.setPanel(this._toolPanel, iframe);
} }
this.emit(id + "-ready"); this.emit(id + "-ready");
}.bind(this); }.bind(this);
iframe.addEventListener("DOMContentLoaded", onIFrameLoaded, true); iframe.addEventListener("load", onIFrameLoaded, true);
let tabpanel = this._panelDoc.createElementNS(XULNS, "tabpanel"); let tabpanel = this._panelDoc.createElementNS(XULNS, "tabpanel");
tabpanel.setAttribute("id", "sidebar-panel-" + id); tabpanel.setAttribute("id", "sidebar-panel-" + id);
@ -91,7 +91,7 @@ ToolSidebar.prototype = {
// the "selected" attribute set to true. // the "selected" attribute set to true.
this._panelDoc.defaultView.setTimeout(function() { this._panelDoc.defaultView.setTimeout(function() {
this.select(id); this.select(id);
}.bind(this), 0); }.bind(this), 10);
} }
this.emit("new-tab-registered", id); this.emit("new-tab-registered", id);

Просмотреть файл

@ -22,7 +22,7 @@ function test() {
const tab3URL = "data:text/html;charset=utf8,<title>3</title><p>3</p>"; const tab3URL = "data:text/html;charset=utf8,<title>3</title><p>3</p>";
let panelDoc; let panelDoc;
let tab1Selected = false;
let registeredTabs = {}; let registeredTabs = {};
let readyTabs = {}; let readyTabs = {};
@ -63,24 +63,27 @@ function test() {
}); });
panel.sidebar.once("tab1-ready", function(event) { panel.sidebar.once("tab1-ready", function(event) {
info(event);
readyTabs.tab1 = true; readyTabs.tab1 = true;
if (readyTabs.tab1 && readyTabs.tab2 && readyTabs.tab3) { allTabsReady(panel);
allTabsReady(panel);
}
}); });
panel.sidebar.once("tab2-ready", function(event) { panel.sidebar.once("tab2-ready", function(event) {
info(event);
readyTabs.tab2 = true; readyTabs.tab2 = true;
if (readyTabs.tab1 && readyTabs.tab2 && readyTabs.tab3) { allTabsReady(panel);
allTabsReady(panel);
}
}); });
panel.sidebar.once("tab3-ready", function(event) { panel.sidebar.once("tab3-ready", function(event) {
info(event);
readyTabs.tab3 = true; readyTabs.tab3 = true;
if (readyTabs.tab1 && readyTabs.tab2 && readyTabs.tab3) { allTabsReady(panel);
allTabsReady(panel); });
}
panel.sidebar.once("tab1-selected", function(event) {
info(event);
tab1Selected = true;
allTabsReady(panel);
}); });
panel.sidebar.addTab("tab1", tab1URL, true); panel.sidebar.addTab("tab1", tab1URL, true);
@ -92,6 +95,10 @@ function test() {
}); });
function allTabsReady(panel) { function allTabsReady(panel) {
if (!tab1Selected || !readyTabs.tab1 || !readyTabs.tab2 || !readyTabs.tab3) {
return;
}
ok(registeredTabs.tab1, "tab1 registered"); ok(registeredTabs.tab1, "tab1 registered");
ok(registeredTabs.tab2, "tab2 registered"); ok(registeredTabs.tab2, "tab2 registered");
ok(registeredTabs.tab3, "tab3 registered"); ok(registeredTabs.tab3, "tab3 registered");

Просмотреть файл

@ -4,6 +4,7 @@
browser.jar: browser.jar:
content/browser/devtools/widgets.css (shared/widgets/widgets.css) content/browser/devtools/widgets.css (shared/widgets/widgets.css)
content/browser/devtools/widgets/VariablesView.xul (shared/widgets/VariablesView.xul)
content/browser/devtools/markup-view.xhtml (markupview/markup-view.xhtml) content/browser/devtools/markup-view.xhtml (markupview/markup-view.xhtml)
content/browser/devtools/markup-view.css (markupview/markup-view.css) content/browser/devtools/markup-view.css (markupview/markup-view.css)
content/browser/devtools/netmonitor.xul (netmonitor/netmonitor.xul) content/browser/devtools/netmonitor.xul (netmonitor/netmonitor.xul)

Просмотреть файл

@ -259,7 +259,7 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
initialize: function NVRM_initialize() { initialize: function NVRM_initialize() {
dumpn("Initializing the RequestsMenuView"); dumpn("Initializing the RequestsMenuView");
this.node = new SideMenuWidget($("#requests-menu-contents")); this.node = new SideMenuWidget($("#requests-menu-contents"), false);
this.node.addEventListener("mousedown", this._onMouseDown, false); this.node.addEventListener("mousedown", this._onMouseDown, false);
this.node.addEventListener("select", this._onSelect, false); this.node.addEventListener("select", this._onSelect, false);
@ -403,6 +403,9 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
break; break;
case "statusText": case "statusText":
requestItem.attachment.statusText = value; requestItem.attachment.statusText = value;
this._updateMenuView(requestItem, key,
requestItem.attachment.status + " " +
requestItem.attachment.statusText);
break; break;
case "headersSize": case "headersSize":
requestItem.attachment.headersSize = value; requestItem.attachment.headersSize = value;
@ -458,18 +461,24 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
let hostPort = NetworkHelper.convertToUnicode(unescape(uri.hostPort)); let hostPort = NetworkHelper.convertToUnicode(unescape(uri.hostPort));
let template = $("#requests-menu-item-template"); let template = $("#requests-menu-item-template");
let requestsMenuItem = template.firstChild.cloneNode(true); let fragment = document.createDocumentFragment();
$(".requests-menu-method", requestsMenuItem) $(".requests-menu-method", template).setAttribute("value", aMethod);
.setAttribute("value", aMethod);
$(".requests-menu-file", requestsMenuItem) let file = $(".requests-menu-file", template);
.setAttribute("value", name + (query ? "?" + query : "")); file.setAttribute("value", name + (query ? "?" + query : ""));
file.setAttribute("tooltiptext", name + (query ? "?" + query : ""));
$(".requests-menu-domain", requestsMenuItem) let domain = $(".requests-menu-domain", template);
.setAttribute("value", hostPort); domain.setAttribute("value", hostPort);
domain.setAttribute("tooltiptext", hostPort);
return requestsMenuItem; // Flatten the DOM by removing one redundant box (the template container).
for (let node of template.childNodes) {
fragment.appendChild(node.cloneNode(true));
}
return fragment;
}, },
/** /**
@ -489,21 +498,32 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
node.setAttribute("code", aValue); node.setAttribute("code", aValue);
break; break;
} }
case "statusText": {
let node = $(".requests-menu-status-and-method", aItem.target);
node.setAttribute("tooltiptext", aValue);
break;
}
case "contentSize": { case "contentSize": {
let size = (aValue / 1024).toFixed(CONTENT_SIZE_DECIMALS); let size = (aValue / 1024).toFixed(CONTENT_SIZE_DECIMALS);
let node = $(".requests-menu-size", aItem.target); let node = $(".requests-menu-size", aItem.target);
node.setAttribute("value", L10N.getFormatStr("networkMenu.size", size)); let text = L10N.getFormatStr("networkMenu.sizeKB", size);
node.setAttribute("value", text);
node.setAttribute("tooltiptext", text);
break; break;
} }
case "mimeType": { case "mimeType": {
let type = aValue.split(";")[0].split("/")[1] || "?"; let type = aValue.split(";")[0].split("/")[1] || "?";
let node = $(".requests-menu-type", aItem.target); let node = $(".requests-menu-type", aItem.target);
node.setAttribute("value", CONTENT_MIME_TYPE_ABBREVIATIONS[type] || type); let text = CONTENT_MIME_TYPE_ABBREVIATIONS[type] || type;
node.setAttribute("value", text);
node.setAttribute("tooltiptext", aValue);
break; break;
} }
case "totalTime": { case "totalTime": {
let node = $(".requests-menu-timings-total", aItem.target); let node = $(".requests-menu-timings-total", aItem.target);
node.setAttribute("value", L10N.getFormatStr("networkMenu.total", aValue)); let text = L10N.getFormatStr("networkMenu.totalMS", aValue);
node.setAttribute("value", text);
node.setAttribute("tooltiptext", text);
break; break;
} }
} }
@ -795,6 +815,7 @@ create({ constructor: NetworkDetailsView, proto: MenuContainer.prototype }, {
if (aData.url) { if (aData.url) {
let unicodeUrl = NetworkHelper.convertToUnicode(unescape(aData.url)); let unicodeUrl = NetworkHelper.convertToUnicode(unescape(aData.url));
$("#headers-summary-url-value").setAttribute("value", unicodeUrl); $("#headers-summary-url-value").setAttribute("value", unicodeUrl);
$("#headers-summary-url-value").setAttribute("tooltiptext", unicodeUrl);
$("#headers-summary-url").removeAttribute("hidden"); $("#headers-summary-url").removeAttribute("hidden");
} else { } else {
$("#headers-summary-url").setAttribute("hidden", "true"); $("#headers-summary-url").setAttribute("hidden", "true");
@ -858,7 +879,7 @@ create({ constructor: NetworkDetailsView, proto: MenuContainer.prototype }, {
*/ */
_addHeaders: function NVND__addHeaders(aName, aResponse) { _addHeaders: function NVND__addHeaders(aName, aResponse) {
let kb = (aResponse.headersSize / 1024).toFixed(HEADERS_SIZE_DECIMALS); let kb = (aResponse.headersSize / 1024).toFixed(HEADERS_SIZE_DECIMALS);
let size = L10N.getFormatStr("networkMenu.size", kb); let size = L10N.getFormatStr("networkMenu.sizeKB", kb);
let headersScope = this._headers.addScope(aName + " (" + size + ")"); let headersScope = this._headers.addScope(aName + " (" + size + ")");
headersScope.expanded = true; headersScope.expanded = true;
@ -1083,32 +1104,32 @@ create({ constructor: NetworkDetailsView, proto: MenuContainer.prototype }, {
$("#timings-summary-blocked .requests-menu-timings-box") $("#timings-summary-blocked .requests-menu-timings-box")
.setAttribute("width", blocked * scale); .setAttribute("width", blocked * scale);
$("#timings-summary-blocked .requests-menu-timings-total") $("#timings-summary-blocked .requests-menu-timings-total")
.setAttribute("value", L10N.getFormatStr("networkMenu.total", blocked)); .setAttribute("value", L10N.getFormatStr("networkMenu.totalMS", blocked));
$("#timings-summary-dns .requests-menu-timings-box") $("#timings-summary-dns .requests-menu-timings-box")
.setAttribute("width", dns * scale); .setAttribute("width", dns * scale);
$("#timings-summary-dns .requests-menu-timings-total") $("#timings-summary-dns .requests-menu-timings-total")
.setAttribute("value", L10N.getFormatStr("networkMenu.total", dns)); .setAttribute("value", L10N.getFormatStr("networkMenu.totalMS", dns));
$("#timings-summary-connect .requests-menu-timings-box") $("#timings-summary-connect .requests-menu-timings-box")
.setAttribute("width", connect * scale); .setAttribute("width", connect * scale);
$("#timings-summary-connect .requests-menu-timings-total") $("#timings-summary-connect .requests-menu-timings-total")
.setAttribute("value", L10N.getFormatStr("networkMenu.total", connect)); .setAttribute("value", L10N.getFormatStr("networkMenu.totalMS", connect));
$("#timings-summary-send .requests-menu-timings-box") $("#timings-summary-send .requests-menu-timings-box")
.setAttribute("width", send * scale); .setAttribute("width", send * scale);
$("#timings-summary-send .requests-menu-timings-total") $("#timings-summary-send .requests-menu-timings-total")
.setAttribute("value", L10N.getFormatStr("networkMenu.total", send)); .setAttribute("value", L10N.getFormatStr("networkMenu.totalMS", send));
$("#timings-summary-wait .requests-menu-timings-box") $("#timings-summary-wait .requests-menu-timings-box")
.setAttribute("width", wait * scale); .setAttribute("width", wait * scale);
$("#timings-summary-wait .requests-menu-timings-total") $("#timings-summary-wait .requests-menu-timings-total")
.setAttribute("value", L10N.getFormatStr("networkMenu.total", wait)); .setAttribute("value", L10N.getFormatStr("networkMenu.totalMS", wait));
$("#timings-summary-receive .requests-menu-timings-box") $("#timings-summary-receive .requests-menu-timings-box")
.setAttribute("width", receive * scale); .setAttribute("width", receive * scale);
$("#timings-summary-receive .requests-menu-timings-total") $("#timings-summary-receive .requests-menu-timings-total")
.setAttribute("value", L10N.getFormatStr("networkMenu.total", receive)); .setAttribute("value", L10N.getFormatStr("networkMenu.totalMS", receive));
$("#timings-summary-dns .requests-menu-timings-box") $("#timings-summary-dns .requests-menu-timings-box")
.style.transform = "translateX(" + (scale * blocked) + "px)"; .style.transform = "translateX(" + (scale * blocked) + "px)";

Просмотреть файл

@ -55,35 +55,33 @@
<label class="plain requests-menu-empty-notice" <label class="plain requests-menu-empty-notice"
value="&netmonitorUI.emptyNotice;"/> value="&netmonitorUI.emptyNotice;"/>
<vbox id="requests-menu-contents" flex="1"> <vbox id="requests-menu-contents" flex="1">
<template id="requests-menu-item-template"> <hbox id="requests-menu-item-template" hidden="true">
<hbox class="requests-menu-item"> <hbox class="requests-menu-subitem requests-menu-status-and-method"
<hbox class="requests-menu-subitem requests-menu-status-and-method" align="center">
<hbox class="requests-menu-status"/>
<label class="plain requests-menu-method"
crop="end"
flex="1"/>
</hbox>
<label class="plain requests-menu-subitem requests-menu-file"
crop="end"/>
<label class="plain requests-menu-subitem requests-menu-domain"
crop="end"/>
<label class="plain requests-menu-subitem requests-menu-type"
crop="end"/>
<label class="plain requests-menu-subitem requests-menu-size"
crop="end"/>
<hbox class="requests-menu-subitem requests-menu-waterfall"
align="center"
flex="1">
<hbox class="requests-menu-timings"
align="center"> align="center">
<hbox class="requests-menu-status"/> <hbox class="start requests-menu-timings-cap" hidden="true"/>
<label class="plain requests-menu-method" <hbox class="end requests-menu-timings-cap" hidden="true"/>
crop="end" <label class="plain requests-menu-timings-total"/>
flex="1"/>
</hbox>
<label class="plain requests-menu-subitem requests-menu-file"
crop="end"/>
<label class="plain requests-menu-subitem requests-menu-domain"
crop="end"/>
<label class="plain requests-menu-subitem requests-menu-type"
crop="end"/>
<label class="plain requests-menu-subitem requests-menu-size"
crop="end"/>
<hbox class="requests-menu-subitem requests-menu-waterfall"
align="center"
flex="1">
<hbox class="requests-menu-timings"
align="center">
<hbox class="start requests-menu-timings-cap" hidden="true"/>
<hbox class="end requests-menu-timings-cap" hidden="true"/>
<label class="plain requests-menu-timings-total"/>
</hbox>
</hbox> </hbox>
</hbox> </hbox>
</template> </hbox>
</vbox> </vbox>
</vbox> </vbox>

Просмотреть файл

@ -9,7 +9,7 @@ function test() {
initNetMonitor(CONTENT_TYPE_URL).then(([aTab, aDebuggee, aMonitor]) => { initNetMonitor(CONTENT_TYPE_URL).then(([aTab, aDebuggee, aMonitor]) => {
info("Starting test... "); info("Starting test... ");
let { document, SourceEditor, NetMonitorView } = aMonitor.panelWin; let { document, L10N, SourceEditor, NetMonitorView } = aMonitor.panelWin;
let { RequestsMenu } = NetMonitorView; let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false; RequestsMenu.lazyUpdate = false;
@ -18,43 +18,55 @@ function test() {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
"GET", CONTENT_TYPE_SJS + "?fmt=xml", { "GET", CONTENT_TYPE_SJS + "?fmt=xml", {
status: 200, status: 200,
statusText: "OK",
type: "xml", type: "xml",
size: "0.04kb", fullMimeType: "text/xml; charset=utf-8",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.04),
time: true time: true
}); });
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(1), verifyRequestItemTarget(RequestsMenu.getItemAtIndex(1),
"GET", CONTENT_TYPE_SJS + "?fmt=css", { "GET", CONTENT_TYPE_SJS + "?fmt=css", {
status: 200, status: 200,
statusText: "OK",
type: "css", type: "css",
size: "0.03kb", fullMimeType: "text/css; charset=utf-8",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.03),
time: true time: true
}); });
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(2), verifyRequestItemTarget(RequestsMenu.getItemAtIndex(2),
"GET", CONTENT_TYPE_SJS + "?fmt=js", { "GET", CONTENT_TYPE_SJS + "?fmt=js", {
status: 200, status: 200,
statusText: "OK",
type: "js", type: "js",
size: "0.03kb", fullMimeType: "application/javascript; charset=utf-8",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.03),
time: true time: true
}); });
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(3), verifyRequestItemTarget(RequestsMenu.getItemAtIndex(3),
"GET", CONTENT_TYPE_SJS + "?fmt=json", { "GET", CONTENT_TYPE_SJS + "?fmt=json", {
status: 200, status: 200,
statusText: "OK",
type: "json", type: "json",
size: "0.03kb", fullMimeType: "application/json; charset=utf-8",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.03),
time: true time: true
}); });
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(4), verifyRequestItemTarget(RequestsMenu.getItemAtIndex(4),
"GET", CONTENT_TYPE_SJS + "?fmt=bogus", { "GET", CONTENT_TYPE_SJS + "?fmt=bogus", {
status: 404, status: 404,
statusText: "Not Found",
type: "html", type: "html",
size: "0.02kb", fullMimeType: "text/html; charset=utf-8",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.02),
time: true time: true
}); });
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(5), verifyRequestItemTarget(RequestsMenu.getItemAtIndex(5),
"GET", TEST_IMAGE, { "GET", TEST_IMAGE, {
status: 200, status: 200,
statusText: "OK",
type: "png", type: "png",
size: "0.76kb", fullMimeType: "image/png",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.76),
time: true time: true
}); });

Просмотреть файл

@ -19,15 +19,19 @@ function test() {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0), verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
"POST", SIMPLE_SJS + "?foo=bar&baz=42&type=urlencoded", { "POST", SIMPLE_SJS + "?foo=bar&baz=42&type=urlencoded", {
status: 200, status: 200,
statusText: "Och Aye",
type: "plain", type: "plain",
size: "0.01kb", fullMimeType: "text/plain; charset=utf-8",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.01),
time: true time: true
}); });
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(1), verifyRequestItemTarget(RequestsMenu.getItemAtIndex(1),
"POST", SIMPLE_SJS + "?foo=bar&baz=42&type=multipart", { "POST", SIMPLE_SJS + "?foo=bar&baz=42&type=multipart", {
status: 200, status: 200,
statusText: "Och Aye",
type: "plain", type: "plain",
size: "0.01kb", fullMimeType: "text/plain; charset=utf-8",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.01),
time: true time: true
}); });

Просмотреть файл

@ -27,8 +27,8 @@ function test() {
stringBundle.GetStringFromName("netmonitor.label"), stringBundle.GetStringFromName("netmonitor.label"),
"The getStr() method didn't return the expected string."); "The getStr() method didn't return the expected string.");
is(L10N.getFormatStr("networkMenu.total", "foo"), is(L10N.getFormatStr("networkMenu.totalMS", "foo"),
stringBundle.formatStringFromName("networkMenu.total", ["foo"], 1), stringBundle.formatStringFromName("networkMenu.totalMS", ["foo"], 1),
"The getFormatStr() method didn't return the expected string."); "The getFormatStr() method didn't return the expected string.");
} }

Просмотреть файл

@ -9,7 +9,7 @@ function test() {
initNetMonitor(SIMPLE_SJS).then(([aTab, aDebuggee, aMonitor]) => { initNetMonitor(SIMPLE_SJS).then(([aTab, aDebuggee, aMonitor]) => {
info("Starting test... "); info("Starting test... ");
let { NetMonitorView } = aMonitor.panelWin; let { L10N, NetMonitorView } = aMonitor.panelWin;
let { RequestsMenu } = NetMonitorView; let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false; RequestsMenu.lazyUpdate = false;
@ -148,7 +148,8 @@ function test() {
"The headersSize attachment has an incorrect value."); "The headersSize attachment has an incorrect value.");
verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, { verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, {
status: "200" status: "200",
statusText: "Och Aye"
}); });
}); });
@ -162,7 +163,8 @@ function test() {
verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, { verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, {
type: "plain", type: "plain",
size: "0.01kb" fullMimeType: "text/plain; charset=utf-8",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.01),
}); });
}); });
@ -180,7 +182,8 @@ function test() {
verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, { verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, {
type: "plain", type: "plain",
size: "0.01kb" fullMimeType: "text/plain; charset=utf-8",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.01),
}); });
}); });

Просмотреть файл

@ -52,6 +52,8 @@ function test() {
is(tabpanel.querySelector("#headers-summary-url-value").getAttribute("value"), is(tabpanel.querySelector("#headers-summary-url-value").getAttribute("value"),
SIMPLE_SJS, "The url summary value is incorrect."); SIMPLE_SJS, "The url summary value is incorrect.");
is(tabpanel.querySelector("#headers-summary-url-value").getAttribute("tooltiptext"),
SIMPLE_SJS, "The url summary tooltiptext is incorrect.");
is(tabpanel.querySelector("#headers-summary-method-value").getAttribute("value"), is(tabpanel.querySelector("#headers-summary-method-value").getAttribute("value"),
"GET", "The method summary value is incorrect."); "GET", "The method summary value is incorrect.");
is(tabpanel.querySelector("#headers-summary-status-circle").getAttribute("code"), is(tabpanel.querySelector("#headers-summary-status-circle").getAttribute("code"),
@ -71,7 +73,7 @@ function test() {
is(responseScope.querySelector(".name").getAttribute("value"), is(responseScope.querySelector(".name").getAttribute("value"),
L10N.getStr("responseHeaders") + " (" + L10N.getStr("responseHeaders") + " (" +
L10N.getFormatStr("networkMenu.size", "0.169") + ")", L10N.getFormatStr("networkMenu.sizeKB", "0.169") + ")",
"The response headers scope doesn't have the correct title."); "The response headers scope doesn't have the correct title.");
ok(requestScope.querySelector(".name").getAttribute("value").contains( ok(requestScope.querySelector(".name").getAttribute("value").contains(
@ -196,27 +198,27 @@ function test() {
"The timings tab in the network details pane should be selected."); "The timings tab in the network details pane should be selected.");
ok(tabpanel.querySelector("#timings-summary-blocked .requests-menu-timings-total") ok(tabpanel.querySelector("#timings-summary-blocked .requests-menu-timings-total")
.getAttribute("value").match(/[0-9]+ms$/), .getAttribute("value").match(/[0-9]+/),
"The blocked timing info does not appear to be correct."); "The blocked timing info does not appear to be correct.");
ok(tabpanel.querySelector("#timings-summary-dns .requests-menu-timings-total") ok(tabpanel.querySelector("#timings-summary-dns .requests-menu-timings-total")
.getAttribute("value").match(/[0-9]+ms$/), .getAttribute("value").match(/[0-9]+/),
"The dns timing info does not appear to be correct."); "The dns timing info does not appear to be correct.");
ok(tabpanel.querySelector("#timings-summary-connect .requests-menu-timings-total") ok(tabpanel.querySelector("#timings-summary-connect .requests-menu-timings-total")
.getAttribute("value").match(/[0-9]+ms$/), .getAttribute("value").match(/[0-9]+/),
"The connect timing info does not appear to be correct."); "The connect timing info does not appear to be correct.");
ok(tabpanel.querySelector("#timings-summary-send .requests-menu-timings-total") ok(tabpanel.querySelector("#timings-summary-send .requests-menu-timings-total")
.getAttribute("value").match(/[0-9]+ms$/), .getAttribute("value").match(/[0-9]+/),
"The send timing info does not appear to be correct."); "The send timing info does not appear to be correct.");
ok(tabpanel.querySelector("#timings-summary-wait .requests-menu-timings-total") ok(tabpanel.querySelector("#timings-summary-wait .requests-menu-timings-total")
.getAttribute("value").match(/[0-9]+ms$/), .getAttribute("value").match(/[0-9]+/),
"The wait timing info does not appear to be correct."); "The wait timing info does not appear to be correct.");
ok(tabpanel.querySelector("#timings-summary-receive .requests-menu-timings-total") ok(tabpanel.querySelector("#timings-summary-receive .requests-menu-timings-total")
.getAttribute("value").match(/[0-9]+ms$/), .getAttribute("value").match(/[0-9]+/),
"The receive timing info does not appear to be correct."); "The receive timing info does not appear to be correct.");
} }

Просмотреть файл

@ -21,36 +21,46 @@ function test() {
verifyRequestItemTarget(requestItems[0] = RequestsMenu.getItemAtIndex(0), verifyRequestItemTarget(requestItems[0] = RequestsMenu.getItemAtIndex(0),
"GET", STATUS_CODES_SJS + "?sts=100", { "GET", STATUS_CODES_SJS + "?sts=100", {
status: 101, status: 101,
statusText: "Switching Protocols",
type: "plain", type: "plain",
size: "0.00kb", fullMimeType: "text/plain; charset=utf-8",
size: L10N.getFormatStr("networkMenu.sizeKB", "0.00"),
time: true time: true
}); });
verifyRequestItemTarget(requestItems[1] = RequestsMenu.getItemAtIndex(1), verifyRequestItemTarget(requestItems[1] = RequestsMenu.getItemAtIndex(1),
"GET", STATUS_CODES_SJS + "?sts=200", { "GET", STATUS_CODES_SJS + "?sts=200", {
status: 202, status: 202,
statusText: "Created",
type: "plain", type: "plain",
size: "0.02kb", fullMimeType: "text/plain; charset=utf-8",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.02),
time: true time: true
}); });
verifyRequestItemTarget(requestItems[2] = RequestsMenu.getItemAtIndex(2), verifyRequestItemTarget(requestItems[2] = RequestsMenu.getItemAtIndex(2),
"GET", STATUS_CODES_SJS + "?sts=300", { "GET", STATUS_CODES_SJS + "?sts=300", {
status: 303, status: 303,
statusText: "See Other",
type: "plain", type: "plain",
size: "0.00kb", fullMimeType: "text/plain; charset=utf-8",
size: L10N.getFormatStr("networkMenu.sizeKB", "0.00"),
time: true time: true
}); });
verifyRequestItemTarget(requestItems[3] = RequestsMenu.getItemAtIndex(3), verifyRequestItemTarget(requestItems[3] = RequestsMenu.getItemAtIndex(3),
"GET", STATUS_CODES_SJS + "?sts=400", { "GET", STATUS_CODES_SJS + "?sts=400", {
status: 404, status: 404,
statusText: "Not Found",
type: "plain", type: "plain",
size: "0.02kb", fullMimeType: "text/plain; charset=utf-8",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.02),
time: true time: true
}); });
verifyRequestItemTarget(requestItems[4] = RequestsMenu.getItemAtIndex(4), verifyRequestItemTarget(requestItems[4] = RequestsMenu.getItemAtIndex(4),
"GET", STATUS_CODES_SJS + "?sts=500", { "GET", STATUS_CODES_SJS + "?sts=500", {
status: 501, status: 501,
statusText: "Not Implemented",
type: "plain", type: "plain",
size: "0.02kb", fullMimeType: "text/plain; charset=utf-8",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.02),
time: true time: true
}); });

Просмотреть файл

@ -178,7 +178,7 @@ function verifyRequestItemTarget(aRequestItem, aMethod, aUrl, aData = {}) {
info("> Verifying: " + aMethod + " " + aUrl + " " + aData.toSource()); info("> Verifying: " + aMethod + " " + aUrl + " " + aData.toSource());
info("> Request: " + aRequestItem.attachment.toSource()); info("> Request: " + aRequestItem.attachment.toSource());
let { status, type, size, time } = aData; let { status, statusText, type, fullMimeType, size, time } = aData;
let { attachment, target } = aRequestItem let { attachment, target } = aRequestItem
let uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL); let uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL);
@ -197,28 +197,44 @@ function verifyRequestItemTarget(aRequestItem, aMethod, aUrl, aData = {}) {
is(target.querySelector(".requests-menu-file").getAttribute("value"), is(target.querySelector(".requests-menu-file").getAttribute("value"),
name + (query ? "?" + query : ""), "The displayed file is incorrect."); name + (query ? "?" + query : ""), "The displayed file is incorrect.");
is(target.querySelector(".requests-menu-file").getAttribute("tooltiptext"),
name + (query ? "?" + query : ""), "The tooltip file is incorrect.");
is(target.querySelector(".requests-menu-domain").getAttribute("value"), is(target.querySelector(".requests-menu-domain").getAttribute("value"),
hostPort, "The displayed domain is incorrect."); hostPort, "The displayed domain is incorrect.");
is(target.querySelector(".requests-menu-domain").getAttribute("tooltiptext"),
hostPort, "The tooltip domain is incorrect.");
if (status !== undefined) { if (status !== undefined) {
let value = target.querySelector(".requests-menu-status").getAttribute("code"); let value = target.querySelector(".requests-menu-status").getAttribute("code");
let tooltip = target.querySelector(".requests-menu-status-and-method").getAttribute("tooltiptext");
info("Displayed status: " + value); info("Displayed status: " + value);
info("Tooltip status: " + tooltip);
is(value, status, "The displayed status is incorrect."); is(value, status, "The displayed status is incorrect.");
is(tooltip, status + " " + statusText, "The tooltip status is incorrect.");
} }
if (type !== undefined) { if (type !== undefined) {
let value = target.querySelector(".requests-menu-type").getAttribute("value"); let value = target.querySelector(".requests-menu-type").getAttribute("value");
let tooltip = target.querySelector(".requests-menu-type").getAttribute("tooltiptext");
info("Displayed type: " + value); info("Displayed type: " + value);
info("Tooltip type: " + tooltip);
is(value, type, "The displayed type is incorrect."); is(value, type, "The displayed type is incorrect.");
is(tooltip, fullMimeType, "The tooltip type is incorrect.");
} }
if (size !== undefined) { if (size !== undefined) {
let value = target.querySelector(".requests-menu-size").getAttribute("value"); let value = target.querySelector(".requests-menu-size").getAttribute("value");
let tooltip = target.querySelector(".requests-menu-size").getAttribute("tooltiptext");
info("Displayed size: " + value); info("Displayed size: " + value);
info("Tooltip size: " + tooltip);
is(value, size, "The displayed size is incorrect."); is(value, size, "The displayed size is incorrect.");
is(tooltip, size, "The tooltip size is incorrect.");
} }
if (time !== undefined) { if (time !== undefined) {
let value = target.querySelector(".requests-menu-timings-total").getAttribute("value"); let value = target.querySelector(".requests-menu-timings-total").getAttribute("value");
let tooltip = target.querySelector(".requests-menu-timings-total").getAttribute("tooltiptext");
info("Displayed time: " + value); info("Displayed time: " + value);
info("Tooltip time: " + tooltip);
ok(~~(value.match(/[0-9]+/)) >= 0, "The displayed time is incorrect."); ok(~~(value.match(/[0-9]+/)) >= 0, "The displayed time is incorrect.");
ok(~~(tooltip.match(/[0-9]+/)) >= 0, "The tooltip time is incorrect.");
} }
} }

Просмотреть файл

@ -18,6 +18,16 @@ XPCOMUtils.defineLazyGetter(this, "DebuggerServer", function () {
return DebuggerServer; return DebuggerServer;
}); });
/**
* Data structure that contains information that has
* to be shared between separate ProfilerController
* instances.
*/
const sharedData = {
startTime: 0,
data: new WeakMap(),
};
/** /**
* Makes a structure representing an individual profile. * Makes a structure representing an individual profile.
*/ */
@ -29,163 +39,51 @@ function makeProfile(name) {
}; };
} }
/** // Three functions below all operate with sharedData
* Object acting as a mediator between the ProfilerController and // structure defined above. They should be self-explanatory.
* DebuggerServer.
*/ function addTarget(target) {
function ProfilerConnection(client) { sharedData.data.set(target, new Map());
this.client = client;
this.startTime = 0;
} }
ProfilerConnection.prototype = { function getProfiles(target) {
actor: null, return sharedData.data.get(target);
startTime: null, }
/** function getCurrentTime() {
* Returns how many milliseconds have passed since the connection return (new Date()).getTime() - sharedData.startTime;
* was started (start time is specificed by the startTime property). }
*
* @return number
*/
get currentTime() {
return (new Date()).getTime() - this.startTime;
},
/** /**
* Connects to a debugee and executes a callback when ready. * Object to control the JavaScript Profiler over the remote
* * debugging protocol.
* @param function aCallback *
* Function to be called once we're connected to the client. * @param Target target
*/ * A target object as defined in Target.jsm
connect: function PCn_connect(aCallback) { */
this.client.listTabs(function (aResponse) { function ProfilerController(target) {
this.actor = aResponse.profilerActor; this.target = target;
aCallback(); this.client = target.client;
}.bind(this)); this.isConnected = false;
},
/** addTarget(target);
* Sends a message to check if the profiler is currently active.
*
* @param function aCallback
* Function to be called once we have a response from
* the client. It will be called with a single argument
* containing a response object.
*/
isActive: function PCn_isActive(aCallback) {
var message = { to: this.actor, type: "isActive" };
this.client.request(message, aCallback);
},
/** // Chrome debugging targets have already obtained a reference
* Sends a message to start a profiler. // to the profiler actor.
* if (target.chrome) {
* @param function aCallback this.isConnected = true;
* Function to be called once the profiler is running. this.actor = target.form.profilerActor;
* It will be called with a single argument containing
* a response object.
*/
startProfiler: function PCn_startProfiler(aCallback) {
var message = {
to: this.actor,
type: "startProfiler",
entries: 1000000,
interval: 1,
features: ["js"],
};
this.client.request(message, function () {
// Record the current time so we could split profiler data
// in chunks later.
this.startTime = (new Date()).getTime();
aCallback.apply(null, Array.slice(arguments));
}.bind(this));
},
/**
* Sends a message to stop a profiler.
*
* @param function aCallback
* Function to be called once the profiler is idle.
* It will be called with a single argument containing
* a response object.
*/
stopProfiler: function PCn_stopProfiler(aCallback) {
var message = { to: this.actor, type: "stopProfiler" };
this.client.request(message, aCallback);
},
/**
* Sends a message to get the generated profile data.
*
* @param function aCallback
* Function to be called once we have the data.
* It will be called with a single argument containing
* a response object.
*/
getProfileData: function PCn_getProfileData(aCallback) {
var message = { to: this.actor, type: "getProfile" };
this.client.request(message, aCallback);
},
/**
* Cleanup.
*/
destroy: function PCn_destroy() {
this.client = null;
} }
}; };
/**
* Object defining the profiler controller components.
*/
function ProfilerController(target) {
this.profiler = new ProfilerConnection(target.client);
this.profiles = new Map();
// Chrome debugging targets have already obtained a reference to the
// profiler actor.
this._connected = !!target.chrome;
if (target.chrome) {
this.profiler.actor = target.form.profilerActor;
}
}
ProfilerController.prototype = { ProfilerController.prototype = {
/** /**
* Connects to the client unless we're already connected. * Return a map of profile results for the current target.
* *
* @param function aCallback * @return Map
* Function to be called once we're connected. If
* the controller is already connected, this function
* will be called immediately (synchronously).
*/ */
connect: function (aCallback) { get profiles() {
if (this._connected) { return getProfiles(this.target);
return void aCallback();
}
this.profiler.connect(function onConnect() {
this._connected = true;
aCallback();
}.bind(this));
},
/**
* Checks whether the profiler is active.
*
* @param function aCallback
* Function to be called with a response from the
* client. It will be called with two arguments:
* an error object (may be null) and a boolean
* value indicating if the profiler is active or not.
*/
isActive: function PC_isActive(aCallback) {
this.profiler.isActive(function onActive(aResponse) {
aCallback(aResponse.error, aResponse.isActive);
});
}, },
/** /**
@ -199,6 +97,56 @@ ProfilerController.prototype = {
return profile.timeStarted !== null && profile.timeEnded === null; return profile.timeStarted !== null && profile.timeEnded === null;
}, },
/**
* Connects to the client unless we're already connected.
*
* @param function cb
* Function to be called once we're connected. If
* the controller is already connected, this function
* will be called immediately (synchronously).
*/
connect: function (cb) {
if (this.isConnected) {
return void cb();
}
this.client.listTabs((resp) => {
this.actor = resp.profilerActor;
this.isConnected = true;
cb();
})
},
/**
* Adds actor and type information to data and sends the request over
* the remote debugging protocol.
*
* @param string type
* Method to call on the other side
* @param object data
* Data to send with the request
* @param function cb
* A callback function
*/
request: function (type, data, cb) {
data.to = this.actor;
data.type = type;
this.client.request(data, cb);
},
/**
* Checks whether the profiler is active.
*
* @param function cb
* Function to be called with a response from the
* client. It will be called with two arguments:
* an error object (may be null) and a boolean
* value indicating if the profiler is active or not.
*/
isActive: function (cb) {
this.request("isActive", {}, (resp) => cb(resp.error, resp.isActive));
},
/** /**
* Creates a new profile and starts the profiler, if needed. * Creates a new profile and starts the profiler, if needed.
* *
@ -214,35 +162,42 @@ ProfilerController.prototype = {
return; return;
} }
let profiler = this.profiler;
let profile = makeProfile(name); let profile = makeProfile(name);
this.profiles.set(name, profile); this.profiles.set(name, profile);
// If profile is already running, no need to do anything. // If profile is already running, no need to do anything.
if (this.isProfileRecording(profile)) { if (this.isProfileRecording(profile)) {
return void cb(); return void cb();
} }
this.isActive(function (err, isActive) { this.isActive((err, isActive) => {
if (isActive) { if (isActive) {
profile.timeStarted = profiler.currentTime; profile.timeStarted = getCurrentTime();
return void cb(); return void cb();
} }
profiler.startProfiler(function onStart(aResponse) { let params = {
if (aResponse.error) { entries: 1000000,
return void cb(aResponse.error); interval: 1,
features: ["js"],
};
this.request("startProfiler", params, (resp) => {
if (resp.error) {
return void cb(resp.error);
} }
profile.timeStarted = profiler.currentTime; sharedData.startTime = (new Date()).getTime();
profile.timeStarted = getCurrentTime();
cb(); cb();
}); });
}); });
}, },
/** /**
* Stops the profiler. * Stops the profiler. NOTE, that we don't stop the actual
* SPS Profiler here. It will be stopped as soon as all
* clients disconnect from the profiler actor.
* *
* @param string name * @param string name
* Name of the profile that needs to be stopped. * Name of the profile that needs to be stopped.
@ -252,50 +207,36 @@ ProfilerController.prototype = {
* argument: an error object (may be null). * argument: an error object (may be null).
*/ */
stop: function PC_stop(name, cb) { stop: function PC_stop(name, cb) {
let profiler = this.profiler; if (!this.profiles.has(name)) {
let profile = this.profiles.get(name);
if (!profile || !this.isProfileRecording(profile)) {
return; return;
} }
let isRecording = function () { let profile = this.profiles.get(name);
for (let [ name, profile ] of this.profiles) { if (!this.isProfileRecording(profile)) {
if (this.isProfileRecording(profile)) { return;
return true; }
}
this.request("getProfile", {}, (resp) => {
if (resp.error) {
Cu.reportError("Failed to fetch profile data.");
return void cb(resp.error, null);
} }
return false; let data = resp.profile;
}.bind(this); profile.timeEnded = getCurrentTime();
let onStop = function (data) { // Filter out all samples that fall out of current
if (isRecording()) { // profile's range.
return void cb(null, data);
}
profiler.stopProfiler(function onStopProfiler(response) { data.threads = data.threads.map((thread) => {
cb(response.error, data); let samples = thread.samples.filter((sample) => {
});
}.bind(this);
profiler.getProfileData(function onData(aResponse) {
if (aResponse.error) {
Cu.reportError("Failed to fetch profile data before stopping the profiler.");
return void cb(aResponse.error, null);
}
let data = aResponse.profile;
profile.timeEnded = profiler.currentTime;
data.threads = data.threads.map(function (thread) {
let samples = thread.samples.filter(function (sample) {
return sample.time >= profile.timeStarted; return sample.time >= profile.timeStarted;
}); });
return { samples: samples }; return { samples: samples };
}); });
onStop(data); cb(null, data);
}); });
}, },
@ -303,7 +244,8 @@ ProfilerController.prototype = {
* Cleanup. * Cleanup.
*/ */
destroy: function PC_destroy() { destroy: function PC_destroy() {
this.profiler.destroy(); this.client = null;
this.profiler = null; this.target = null;
this.actor = null;
} }
}; };

Просмотреть файл

@ -18,6 +18,7 @@ MOCHITEST_BROWSER_TESTS = \
browser_profiler_run.js \ browser_profiler_run.js \
browser_profiler_controller.js \ browser_profiler_controller.js \
browser_profiler_bug_830664_multiple_profiles.js \ browser_profiler_bug_830664_multiple_profiles.js \
browser_profiler_bug_855244_multiple_tabs.js \
head.js \ head.js \
$(NULL) $(NULL)

Просмотреть файл

@ -0,0 +1,103 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const URL = "data:text/html;charset=utf8,<p>JavaScript Profiler test</p>";
let gTab1, gPanel1;
let gTab2, gPanel2;
// Tests that you can run the profiler in multiple tabs at the same
// time and that closing the debugger panel in one tab doesn't lock
// profilers in other tabs.
registerCleanupFunction(function () {
gTab1 = gTab2 = gPanel1 = gPanel2 = null;
});
function test() {
waitForExplicitFinish();
openTwoTabs()
.then(startTwoProfiles)
.then(stopFirstProfile)
.then(stopSecondProfile)
.then(closeTabs)
.then(openTwoTabs)
.then(startTwoProfiles)
.then(closeFirstPanel)
.then(stopSecondProfile)
.then(closeTabs)
.then(finish);
}
function openTwoTabs() {
let deferred = Promise.defer();
setUp(URL, (tab, browser, panel) => {
gTab1 = tab;
gPanel1 = panel;
loadTab(URL, (tab, browser) => {
gTab2 = tab;
openProfiler(tab, () => {
let target = TargetFactory.forTab(tab);
gPanel2 = gDevTools.getToolbox(target).getPanel("jsprofiler");
deferred.resolve();
});
});
});
return deferred.promise;
}
function startTwoProfiles() {
let deferred = Promise.defer();
gPanel1.controller.start("Profile 1", (err) => {
ok(!err, "Profile in tab 1 started without errors");
gPanel2.controller.start("Profile 1", (err) => {
ok(!err, "Profile in tab 2 started without errors");
gPanel1.controller.isActive((err, isActive) => {
ok(isActive, "Profiler is active");
deferred.resolve();
});
});
});
return deferred.promise;
}
function stopFirstProfile() {
let deferred = Promise.defer();
gPanel1.controller.stop("Profile 1", (err, data) => {
ok(!err, "Profile in tab 1 stopped without errors");
ok(data, "Profile in tab 1 returned some data");
deferred.resolve();
});
return deferred.promise;
}
function stopSecondProfile() {
let deferred = Promise.defer();
gPanel2.controller.stop("Profile 1", (err, data) => {
ok(!err, "Profile in tab 2 stopped without errors");
ok(data, "Profile in tab 2 returned some data");
deferred.resolve();
});
return deferred.promise;
}
function closeTabs() {
while (gBrowser.tabs.length > 1) {
gBrowser.removeCurrentTab();
}
}
function closeFirstPanel() {
let target = TargetFactory.forTab(gTab1);
let toolbox = gDevTools.getToolbox(target);
return toolbox.destroy;
}

Просмотреть файл

@ -13,26 +13,12 @@ function test() {
gPanel = panel; gPanel = panel;
panel.once("started", onStart); panel.once("started", onStart);
panel.once("stopped", onStop);
panel.once("parsed", onParsed); panel.once("parsed", onParsed);
testUI(); testUI();
}); });
} }
function attemptTearDown() {
gAttempts += 1;
if (gAttempts < 2) {
return;
}
tearDown(gTab, function onTearDown() {
gPanel = null;
gTab = null;
});
}
function testUI() { function testUI() {
ok(gPanel, "Profiler panel exists"); ok(gPanel, "Profiler panel exists");
ok(gPanel.activeProfile, "Active profile exists"); ok(gPanel.activeProfile, "Active profile exists");
@ -58,13 +44,6 @@ function onStart() {
}); });
} }
function onStop() {
gPanel.controller.isActive(function (err, isActive) {
ok(!isActive, "Profiler is idle");
attemptTearDown();
});
}
function onParsed() { function onParsed() {
function assertSample() { function assertSample() {
let [win,doc] = getProfileInternals(); let [win,doc] = getProfileInternals();
@ -76,7 +55,11 @@ function onParsed() {
ok(sample.length > 0, "We have some items displayed"); ok(sample.length > 0, "We have some items displayed");
is(sample[0].innerHTML, "100.0%", "First percentage is 100%"); is(sample[0].innerHTML, "100.0%", "First percentage is 100%");
attemptTearDown();
tearDown(gTab, function onTearDown() {
gPanel = null;
gTab = null;
});
} }
assertSample(); assertSample();

Просмотреть файл

@ -33,15 +33,19 @@ this.EXPORTED_SYMBOLS = ["SideMenuWidget"];
* *
* @param nsIDOMNode aNode * @param nsIDOMNode aNode
* The element associated with the widget. * The element associated with the widget.
* @param boolean aShowArrows
* Specifies if items in this container should display horizontal arrows.
*/ */
this.SideMenuWidget = function SideMenuWidget(aNode) { this.SideMenuWidget = function SideMenuWidget(aNode, aShowArrows = true) {
this._parent = aNode; this._parent = aNode;
this._showArrows = aShowArrows;
// Create an internal scrollbox container. // Create an internal scrollbox container.
this._list = this.document.createElement("scrollbox"); this._list = this.document.createElement("scrollbox");
this._list.className = "side-menu-widget-container"; this._list.className = "side-menu-widget-container";
this._list.setAttribute("flex", "1"); this._list.setAttribute("flex", "1");
this._list.setAttribute("orient", "vertical"); this._list.setAttribute("orient", "vertical");
this._list.setAttribute("with-arrow", aShowArrows);
this._parent.appendChild(this._list); this._parent.appendChild(this._list);
this._boxObject = this._list.boxObject.QueryInterface(Ci.nsIScrollBoxObject); this._boxObject = this._list.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
@ -83,7 +87,7 @@ SideMenuWidget.prototype = {
this.ensureSelectionIsVisible(true, true); // Don't worry, it's delayed. this.ensureSelectionIsVisible(true, true); // Don't worry, it's delayed.
let group = this._getGroupForName(aGroup); let group = this._getGroupForName(aGroup);
return group.insertItemAt(aIndex, aContents, aTooltip); return group.insertItemAt(aIndex, aContents, aTooltip, this._showArrows);
}, },
/** /**
@ -150,9 +154,11 @@ SideMenuWidget.prototype = {
} }
for (let node of menuElementsArray) { for (let node of menuElementsArray) {
if (node == aChild) { if (node == aChild) {
node.classList.add("selected");
node.parentNode.classList.add("selected"); node.parentNode.classList.add("selected");
this._selectedItem = node; this._selectedItem = node;
} else { } else {
node.classList.remove("selected");
node.parentNode.classList.remove("selected"); node.parentNode.classList.remove("selected");
} }
} }
@ -397,13 +403,15 @@ SideMenuGroup.prototype = {
* The string or node displayed in the container. * The string or node displayed in the container.
* @param string aTooltip [optional] * @param string aTooltip [optional]
* A tooltip attribute for the displayed item. * A tooltip attribute for the displayed item.
* @param boolean aArrowFlag
* True if a horizontal arrow should be shown.
* @return nsIDOMNode * @return nsIDOMNode
* The element associated with the displayed item. * The element associated with the displayed item.
*/ */
insertItemAt: function SMG_insertItemAt(aIndex, aContents, aTooltip) { insertItemAt: function SMG_insertItemAt(aIndex, aContents, aTooltip, aArrowFlag) {
let list = this._list; let list = this._list;
let menuArray = this._menuElementsArray; let menuArray = this._menuElementsArray;
let item = new SideMenuItem(this, aContents, aTooltip); let item = new SideMenuItem(this, aContents, aTooltip, aArrowFlag);
// Invalidate any notices set on the owner widget. // Invalidate any notices set on the owner widget.
this.ownerView.removeAttribute("notice"); this.ownerView.removeAttribute("notice");
@ -475,24 +483,39 @@ SideMenuGroup.prototype = {
* A tooltip attribute for the displayed item. * A tooltip attribute for the displayed item.
* @param string | nsIDOMNode aContents * @param string | nsIDOMNode aContents
* The string or node displayed in the container. * The string or node displayed in the container.
* @param boolean aArrowFlag
* True if a horizontal arrow should be shown.
*/ */
function SideMenuItem(aGroup, aContents, aTooltip = "") { function SideMenuItem(aGroup, aContents, aTooltip, aArrowFlag) {
this.ownerView = aGroup; this.ownerView = aGroup;
let document = this.document; let document = this.document;
let target = this._target = document.createElement("vbox");
target.className = "side-menu-widget-item-contents"; // Show a horizontal arrow towards the content.
target.setAttribute("flex", "1"); if (aArrowFlag) {
let target = this._target = document.createElement("vbox");
target.className = "side-menu-widget-item-contents";
let arrow = this._arrow = document.createElement("hbox");
arrow.className = "side-menu-widget-item-arrow";
let container = this._container = document.createElement("hbox");
container.className = "side-menu-widget-item side-menu-widget-item-or-group";
container.setAttribute("tooltiptext", aTooltip);
container.appendChild(target);
container.appendChild(arrow);
}
// Skip a few redundant nodes when no horizontal arrow is shown.
else {
let target = this._target = this._container = document.createElement("hbox");
target.className =
"side-menu-widget-item " +
"side-menu-widget-item-or-group " +
"side-menu-widget-item-contents";
}
this._target.setAttribute("flex", "1");
this.contents = aContents; this.contents = aContents;
let arrow = this._arrow = document.createElement("hbox");
arrow.className = "side-menu-widget-item-arrow";
let container = this._container = document.createElement("hbox");
container.className = "side-menu-widget-item side-menu-widget-item-or-group";
container.setAttribute("tooltiptext", aTooltip);
container.appendChild(target);
container.appendChild(arrow);
} }
SideMenuItem.prototype = { SideMenuItem.prototype = {

Просмотреть файл

@ -918,6 +918,18 @@ VariablesView.prototype = {
_emptyTextValue: "" _emptyTextValue: ""
}; };
VariablesView.NON_SORTABLE_CLASSES = [
"Array",
"Int8Array",
"Uint8Array",
"Int16Array",
"Uint16Array",
"Int32Array",
"Uint32Array",
"Float32Array",
"Float64Array"
];
/** /**
* Generates the string evaluated when performing simple value changes. * Generates the string evaluated when performing simple value changes.
* *
@ -983,7 +995,8 @@ VariablesView.getterOrSetterEvalMacro = function(aItem, aCurrentString) {
// morph it into a plain value. // morph it into a plain value.
if ((type == "set" && propertyObject.getter.type == "undefined") || if ((type == "set" && propertyObject.getter.type == "undefined") ||
(type == "get" && propertyObject.setter.type == "undefined")) { (type == "get" && propertyObject.setter.type == "undefined")) {
return VariablesView.overrideValueEvalMacro(propertyObject, "undefined"); // Make sure the right getter/setter to value override macro is applied to the target object.
return propertyObject.evaluationMacro(propertyObject, "undefined");
} }
// Construct and return the getter/setter removal evaluation string. // Construct and return the getter/setter removal evaluation string.
@ -1041,7 +1054,10 @@ VariablesView.getterOrSetterEvalMacro = function(aItem, aCurrentString) {
*/ */
VariablesView.getterOrSetterDeleteCallback = function(aItem) { VariablesView.getterOrSetterDeleteCallback = function(aItem) {
aItem._disable(); aItem._disable();
aItem.ownerView.eval(VariablesView.getterOrSetterEvalMacro(aItem, ""));
// Make sure the right getter/setter to value override macro is applied to the target object.
aItem.ownerView.eval(aItem.evaluationMacro(aItem, ""));
return true; // Don't hide the element. return true; // Don't hide the element.
}; };
@ -1416,6 +1432,13 @@ Scope.prototype = {
return true; return true;
}, },
/**
* Focus this scope.
*/
focus: function S_focus() {
this._variablesView._focusItem(this);
},
/** /**
* Adds an event listener for a certain event on this scope's title. * Adds an event listener for a certain event on this scope's title.
* @param string aName * @param string aName
@ -1448,6 +1471,18 @@ Scope.prototype = {
*/ */
get name() this._nameString, get name() this._nameString,
/**
* Gets the displayed value for this item.
* @return string
*/
get displayValue() this._valueString,
/**
* Gets the class names used for the displayed value.
* @return string
*/
get displayValueClassName() this._valueClassName,
/** /**
* Gets the element associated with this item. * Gets the element associated with this item.
* @return nsIDOMNode * @return nsIDOMNode
@ -1525,7 +1560,7 @@ Scope.prototype = {
return; return;
} }
this.toggle(); this.toggle();
this._variablesView._focusItem(this); this.focus();
}, },
/** /**
@ -1907,7 +1942,7 @@ ViewHelpers.create({ constructor: Variable, proto: Scope.prototype }, {
* someProp4: { value: { type: "null" } }, * someProp4: { value: { type: "null" } },
* someProp5: { value: { type: "object", class: "Object" } }, * someProp5: { value: { type: "object", class: "Object" } },
* someProp6: { get: { type: "object", class: "Function" }, * someProp6: { get: { type: "object", class: "Function" },
* set: { type: "undefined" } } * set: { type: "undefined" } } }
* @param object aOptions [optional] * @param object aOptions [optional]
* Additional options for adding the properties. Supported options: * Additional options for adding the properties. Supported options:
* - sorted: true to sort all the properties before adding them * - sorted: true to sort all the properties before adding them
@ -2190,6 +2225,7 @@ ViewHelpers.create({ constructor: Variable, proto: Scope.prototype }, {
// evaluation method is provided. // evaluation method is provided.
else { else {
this.delete = null; this.delete = null;
this.evaluationMacro = null;
} }
let getter = this.addProperty("get", { value: descriptor.get }); let getter = this.addProperty("get", { value: descriptor.get });
@ -2528,11 +2564,11 @@ ViewHelpers.create({ constructor: Variable, proto: Scope.prototype }, {
case e.DOM_VK_RETURN: case e.DOM_VK_RETURN:
case e.DOM_VK_ENTER: case e.DOM_VK_ENTER:
this._saveNameInput(e); this._saveNameInput(e);
this._variablesView._focusItem(this); this.focus();
return; return;
case e.DOM_VK_ESCAPE: case e.DOM_VK_ESCAPE:
this._deactivateNameInput(e); this._deactivateNameInput(e);
this._variablesView._focusItem(this); this.focus();
return; return;
} }
}, },
@ -2547,11 +2583,11 @@ ViewHelpers.create({ constructor: Variable, proto: Scope.prototype }, {
case e.DOM_VK_RETURN: case e.DOM_VK_RETURN:
case e.DOM_VK_ENTER: case e.DOM_VK_ENTER:
this._saveValueInput(e); this._saveValueInput(e);
this._variablesView._focusItem(this); this.focus();
return; return;
case e.DOM_VK_ESCAPE: case e.DOM_VK_ESCAPE:
this._deactivateValueInput(e); this._deactivateValueInput(e);
this._variablesView._focusItem(this); this.focus();
return; return;
} }
}, },

Просмотреть файл

@ -0,0 +1,16 @@
<?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/global.css"?>
<?xml-stylesheet href="chrome://browser/content/devtools/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/devtools/common.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/devtools/widgets.css" type="text/css"?>
<!DOCTYPE window [
<!ENTITY % viewDTD SYSTEM "chrome://browser/locale/devtools/VariablesView.dtd">
%viewDTD;
]>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="&PropertiesViewWindowTitle;">
<vbox id="variables" flex="1"/>
</window>

Просмотреть файл

@ -108,6 +108,14 @@ this.ViewHelpers = {
* The element representing the pane to toggle. * The element representing the pane to toggle.
*/ */
togglePane: function VH_togglePane(aFlags, aPane) { togglePane: function VH_togglePane(aFlags, aPane) {
// Hiding is always handled via margins, not the hidden attribute.
aPane.removeAttribute("hidden");
// Add a class to the pane to handle min-widths, margins and animations.
if (!aPane.classList.contains("generic-toggled-side-pane")) {
aPane.classList.add("generic-toggled-side-pane");
}
// Avoid useless toggles. // Avoid useless toggles.
if (aFlags.visible == !aPane.hasAttribute("pane-collapsed")) { if (aFlags.visible == !aPane.hasAttribute("pane-collapsed")) {
if (aFlags.callback) aFlags.callback(); if (aFlags.callback) aFlags.callback();
@ -140,14 +148,6 @@ this.ViewHelpers = {
} }
} }
// Add a class to the pane to handle min-widths, margins and animations.
if (!aPane.classList.contains("generic-toggled-side-pane")) {
aPane.classList.add("generic-toggled-side-pane");
}
// Hiding is always handled via margins, not the hidden attribute.
aPane.removeAttribute("hidden");
// The "animated" attributes enables animated toggles (slide in-out). // The "animated" attributes enables animated toggles (slide in-out).
if (aFlags.animated) { if (aFlags.animated) {
aPane.setAttribute("animated", ""); aPane.setAttribute("animated", "");

Просмотреть файл

@ -74,12 +74,8 @@ function ElementStyle(aElement, aStore)
this.store.userProperties = new UserProperties(); this.store.userProperties = new UserProperties();
} }
if (this.store.disabled) { if (!("disabled" in this.store)) {
this.store.disabled = aStore.disabled; this.store.disabled = new WeakMap();
} else {
// FIXME: This should be a WeakMap once bug 753517 is fixed.
// See Bug 777373 for details.
this.store.disabled = new Map();
} }
let doc = aElement.ownerDocument; let doc = aElement.ownerDocument;
@ -1666,9 +1662,7 @@ TextPropertyEditor.prototype = {
*/ */
function UserProperties() function UserProperties()
{ {
// FIXME: This should be a WeakMap once bug 753517 is fixed. this.weakMap = new WeakMap();
// See Bug 777373 for details.
this.map = new Map();
} }
UserProperties.prototype = { UserProperties.prototype = {
@ -1688,7 +1682,7 @@ UserProperties.prototype = {
* otherwise. * otherwise.
*/ */
getProperty: function UP_getProperty(aStyle, aName, aComputedValue) { getProperty: function UP_getProperty(aStyle, aName, aComputedValue) {
let entry = this.map.get(aStyle, null); let entry = this.weakMap.get(aStyle, null);
if (entry && aName in entry) { if (entry && aName in entry) {
let item = entry[aName]; let item = entry[aName];
@ -1717,13 +1711,13 @@ UserProperties.prototype = {
* The value of the property to set. * The value of the property to set.
*/ */
setProperty: function UP_setProperty(aStyle, aName, aComputedValue, aUserValue) { setProperty: function UP_setProperty(aStyle, aName, aComputedValue, aUserValue) {
let entry = this.map.get(aStyle, null); let entry = this.weakMap.get(aStyle, null);
if (entry) { if (entry) {
entry[aName] = { computed: aComputedValue, user: aUserValue }; entry[aName] = { computed: aComputedValue, user: aUserValue };
} else { } else {
let props = {}; let props = {};
props[aName] = { computed: aComputedValue, user: aUserValue }; props[aName] = { computed: aComputedValue, user: aUserValue };
this.map.set(aStyle, props); this.weakMap.set(aStyle, props);
} }
}, },
@ -1736,7 +1730,7 @@ UserProperties.prototype = {
* The name of the property to check. * The name of the property to check.
*/ */
contains: function UP_contains(aStyle, aName) { contains: function UP_contains(aStyle, aName) {
let entry = this.map.get(aStyle, null); let entry = this.weakMap.get(aStyle, null);
return !!entry && aName in entry; return !!entry && aName in entry;
}, },
}; };

Просмотреть файл

@ -19,9 +19,9 @@ function openRuleView()
// Highlight a node. // Highlight a node.
let node = content.document.getElementsByTagName("h1")[0]; let node = content.document.getElementsByTagName("h1")[0];
inspector.selection.once("new-node", testFocus); inspector.selection.once("new-node", testFocus);
executeSoon(function() {
inspector.selection.setNode(doc.body); inspector.sidebar.once("ruleview-ready",
}); () => inspector.selection.setNode(doc.body));
}); });
} }

Просмотреть файл

@ -9,6 +9,8 @@ const Cu = Components.utils;
// Tilt notifications dispatched through the nsIObserverService. // Tilt notifications dispatched through the nsIObserverService.
const TILT_NOTIFICATIONS = { const TILT_NOTIFICATIONS = {
// Called early in the startup of a new tilt instance
STARTUP: "tilt-startup",
// Fires when Tilt starts the initialization. // Fires when Tilt starts the initialization.
INITIALIZING: "tilt-initializing", INITIALIZING: "tilt-initializing",
@ -118,12 +120,15 @@ Tilt.prototype = {
// create a visualizer instance for the current tab // create a visualizer instance for the current tab
this.visualizers[id] = new TiltVisualizer({ this.visualizers[id] = new TiltVisualizer({
chromeWindow: this.chromeWindow, chromeWindow: this.chromeWindow,
contentWindow: this.chromeWindow.gBrowser.selectedBrowser.contentWindow, contentWindow: contentWindow,
parentNode: this.chromeWindow.gBrowser.selectedBrowser.parentNode, parentNode: this.chromeWindow.gBrowser.selectedBrowser.parentNode,
notifications: this.NOTIFICATIONS, notifications: this.NOTIFICATIONS,
tab: this.chromeWindow.gBrowser.selectedTab tab: this.chromeWindow.gBrowser.selectedTab
}); });
Services.obs.notifyObservers(contentWindow, TILT_NOTIFICATIONS.STARTUP, null);
this.visualizers[id].init();
// make sure the visualizer object was initialized properly // make sure the visualizer object was initialized properly
if (!this.visualizers[id].isInitialized()) { if (!this.visualizers[id].isInitialized()) {
this.destroy(id); this.destroy(id);
@ -131,8 +136,9 @@ Tilt.prototype = {
return; return;
} }
this.lastInstanceId = id;
this.emit("change", this.chromeWindow.gBrowser.selectedTab); this.emit("change", this.chromeWindow.gBrowser.selectedTab);
Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.INITIALIZING, null); Services.obs.notifyObservers(contentWindow, TILT_NOTIFICATIONS.INITIALIZING, null);
}, },
/** /**
@ -166,7 +172,7 @@ Tilt.prototype = {
} }
// otherwise, trigger the outro animation and notify necessary observers // otherwise, trigger the outro animation and notify necessary observers
Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.DESTROYING, null); Services.obs.notifyObservers(content, TILT_NOTIFICATIONS.DESTROYING, null);
controller.removeEventListeners(); controller.removeEventListeners();
controller.arcball.reset([-pageXOffset, -pageYOffset]); controller.arcball.reset([-pageXOffset, -pageYOffset]);
@ -181,6 +187,7 @@ Tilt.prototype = {
*/ */
_finish: function T__finish(aId) _finish: function T__finish(aId)
{ {
let contentWindow = this.visualizers[aId].presenter.contentWindow;
this.visualizers[aId].removeOverlay(); this.visualizers[aId].removeOverlay();
this.visualizers[aId].cleanup(); this.visualizers[aId].cleanup();
this.visualizers[aId] = null; this.visualizers[aId] = null;
@ -188,7 +195,7 @@ Tilt.prototype = {
this._isDestroying = false; this._isDestroying = false;
this.chromeWindow.gBrowser.selectedBrowser.focus(); this.chromeWindow.gBrowser.selectedBrowser.focus();
this.emit("change", this.chromeWindow.gBrowser.selectedTab); this.emit("change", this.chromeWindow.gBrowser.selectedTab);
Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.DESTROYED, null); Services.obs.notifyObservers(contentWindow, TILT_NOTIFICATIONS.DESTROYED, null);
}, },
/** /**
@ -196,11 +203,17 @@ Tilt.prototype = {
*/ */
_onTabSelect: function T__onTabSelect() _onTabSelect: function T__onTabSelect()
{ {
if (this.currentInstance) { if (this.visualizers[this.lastInstanceId]) {
Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.SHOWN, null); let contentWindow = this.visualizers[this.lastInstanceId].presenter.contentWindow;
} else { Services.obs.notifyObservers(contentWindow, TILT_NOTIFICATIONS.HIDDEN, null);
Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.HIDDEN, null);
} }
if (this.currentInstance) {
let contentWindow = this.currentInstance.presenter.contentWindow;
Services.obs.notifyObservers(contentWindow, TILT_NOTIFICATIONS.SHOWN, null);
}
this.lastInstanceId = this.currentWindowId;
}, },
/** /**

Просмотреть файл

@ -427,6 +427,9 @@ TiltUtils.DOM = {
* the window content holding the document * the window content holding the document
* @param {Object} aProperties * @param {Object} aProperties
* optional, an object containing the following properties: * optional, an object containing the following properties:
* {Function} nodeCallback
* a function to call instead of TiltUtils.DOM.getNodePosition
* to get the position and depth to display nodes
* {Object} invisibleElements * {Object} invisibleElements
* elements which should be ignored * elements which should be ignored
* {Number} minSize * {Number} minSize
@ -448,6 +451,8 @@ TiltUtils.DOM = {
let aMaxX = aProperties.maxX || Number.MAX_VALUE; let aMaxX = aProperties.maxX || Number.MAX_VALUE;
let aMaxY = aProperties.maxY || Number.MAX_VALUE; let aMaxY = aProperties.maxY || Number.MAX_VALUE;
let nodeCallback = aProperties.nodeCallback || this.getNodePosition.bind(this);
let nodes = aContentWindow.document.childNodes; let nodes = aContentWindow.document.childNodes;
let store = { info: [], nodes: [] }; let store = { info: [], nodes: [] };
let depth = 0; let depth = 0;
@ -466,7 +471,7 @@ TiltUtils.DOM = {
continue; continue;
} }
let coord = this.getNodePosition(aContentWindow, node, parentPosition); let coord = nodeCallback(aContentWindow, node, parentPosition);
if (!coord) { if (!coord) {
continue; continue;
} }

Просмотреть файл

@ -109,6 +109,14 @@ this.TiltVisualizer = function TiltVisualizer(aProperties)
TiltVisualizer.prototype = { TiltVisualizer.prototype = {
/**
* Initializes the visualizer
*/
init: function TV_init()
{
this.presenter.init();
},
/** /**
* Checks if this object was initialized properly. * Checks if this object was initialized properly.
* *
@ -309,6 +317,11 @@ TiltVisualizer.Presenter = function TV_Presenter(
*/ */
this.NOTIFICATIONS = aNotifications; this.NOTIFICATIONS = aNotifications;
/**
* Use the default node callback function
*/
this.nodeCallback = null;
/** /**
* Create the renderer, containing useful functions for easy drawing. * Create the renderer, containing useful functions for easy drawing.
*/ */
@ -375,14 +388,19 @@ TiltVisualizer.Presenter = function TV_Presenter(
this._delta = 0; this._delta = 0;
this._prevFrameTime = 0; this._prevFrameTime = 0;
this._currFrameTime = 0; this._currFrameTime = 0;
this._setup();
this._loop();
}; };
TiltVisualizer.Presenter.prototype = { TiltVisualizer.Presenter.prototype = {
/**
* Initializes the presenter and starts the animation loop
*/
init: function TVP_init()
{
this._setup();
this._loop();
},
/** /**
* The initialization logic. * The initialization logic.
*/ */
@ -718,6 +736,7 @@ TiltVisualizer.Presenter.prototype = {
// traverse the document and get the depths, coordinates and local names // traverse the document and get the depths, coordinates and local names
this._traverseData = TiltUtils.DOM.traverse(this.contentWindow, { this._traverseData = TiltUtils.DOM.traverse(this.contentWindow, {
nodeCallback: this.nodeCallback,
invisibleElements: INVISIBLE_ELEMENTS, invisibleElements: INVISIBLE_ELEMENTS,
minSize: ELEMENT_MIN_SIZE, minSize: ELEMENT_MIN_SIZE,
maxX: this._texture.width, maxX: this._texture.width,
@ -851,7 +870,7 @@ TiltVisualizer.Presenter.prototype = {
this._currentSelection = -1; this._currentSelection = -1;
this._highlight.disabled = true; this._highlight.disabled = true;
Services.obs.notifyObservers(null, this.NOTIFICATIONS.UNHIGHLIGHTING, null); Services.obs.notifyObservers(this.contentWindow, this.NOTIFICATIONS.UNHIGHLIGHTING, null);
return; return;
} }
@ -888,7 +907,7 @@ TiltVisualizer.Presenter.prototype = {
vec3.scale(this._highlight.v1, this.transforms.zoom, []), 0.5)); vec3.scale(this._highlight.v1, this.transforms.zoom, []), 0.5));
} }
Services.obs.notifyObservers(null, this.NOTIFICATIONS.HIGHLIGHTING, null); Services.obs.notifyObservers(this.contentWindow, this.NOTIFICATIONS.HIGHLIGHTING, null);
}, },
/** /**
@ -920,7 +939,7 @@ TiltVisualizer.Presenter.prototype = {
this._highlight.disabled = true; this._highlight.disabled = true;
this._redraw = true; this._redraw = true;
Services.obs.notifyObservers(null, this.NOTIFICATIONS.NODE_REMOVED, null); Services.obs.notifyObservers(this.contentWindow, this.NOTIFICATIONS.NODE_REMOVED, null);
}, },
/** /**
@ -1044,7 +1063,7 @@ TiltVisualizer.Presenter.prototype = {
!this._isExecutingDestruction) { !this._isExecutingDestruction) {
this._isInitializationFinished = true; this._isInitializationFinished = true;
Services.obs.notifyObservers(null, this.NOTIFICATIONS.INITIALIZED, null); Services.obs.notifyObservers(this.contentWindow, this.NOTIFICATIONS.INITIALIZED, null);
if ("function" === typeof this._onInitializationFinished) { if ("function" === typeof this._onInitializationFinished) {
this._onInitializationFinished(); this._onInitializationFinished();
@ -1056,7 +1075,7 @@ TiltVisualizer.Presenter.prototype = {
this._isExecutingDestruction) { this._isExecutingDestruction) {
this._isDestructionFinished = true; this._isDestructionFinished = true;
Services.obs.notifyObservers(null, this.NOTIFICATIONS.BEFORE_DESTROYED, null); Services.obs.notifyObservers(this.contentWindow, this.NOTIFICATIONS.BEFORE_DESTROYED, null);
if ("function" === typeof this._onDestructionFinished) { if ("function" === typeof this._onDestructionFinished) {
this._onDestructionFinished(); this._onDestructionFinished();

Просмотреть файл

@ -18,7 +18,7 @@ let rgba = TiltMath.hex2rgba;
this.TiltVisualizerStyle = { this.TiltVisualizerStyle = {
canvas: { canvas: {
background: "-moz-linear-gradient(top, #454545 0%, #000 100%)", background: "linear-gradient(#454545 0%, #000 100%)",
}, },
nodes: { nodes: {

Просмотреть файл

@ -15,6 +15,7 @@ MOCHITEST_BROWSER_FILES = \
browser_tilt_01_lazy_getter.js \ browser_tilt_01_lazy_getter.js \
browser_tilt_02_notifications-seq.js \ browser_tilt_02_notifications-seq.js \
browser_tilt_02_notifications.js \ browser_tilt_02_notifications.js \
browser_tilt_02_notifications-tabs.js \
browser_tilt_03_tab_switch.js \ browser_tilt_03_tab_switch.js \
browser_tilt_04_initialization.js \ browser_tilt_04_initialization.js \
browser_tilt_05_destruction-esc.js \ browser_tilt_05_destruction-esc.js \
@ -53,6 +54,7 @@ MOCHITEST_BROWSER_FILES = \
browser_tilt_utils05.js \ browser_tilt_utils05.js \
browser_tilt_utils06.js \ browser_tilt_utils06.js \
browser_tilt_utils07.js \ browser_tilt_utils07.js \
browser_tilt_utils08.js \
browser_tilt_visualizer.js \ browser_tilt_visualizer.js \
browser_tilt_zoom.js \ browser_tilt_zoom.js \
$(NULL) $(NULL)

Просмотреть файл

@ -19,6 +19,7 @@ function test() {
createTab(function() { createTab(function() {
Services.obs.addObserver(finalize, DESTROYED, false); Services.obs.addObserver(finalize, DESTROYED, false);
Services.obs.addObserver(obs_STARTUP, STARTUP, false);
Services.obs.addObserver(obs_INITIALIZING, INITIALIZING, false); Services.obs.addObserver(obs_INITIALIZING, INITIALIZING, false);
Services.obs.addObserver(obs_INITIALIZED, INITIALIZED, false); Services.obs.addObserver(obs_INITIALIZED, INITIALIZED, false);
Services.obs.addObserver(obs_DESTROYING, DESTROYING, false); Services.obs.addObserver(obs_DESTROYING, DESTROYING, false);
@ -34,39 +35,51 @@ function test() {
}); });
} }
function obs_INITIALIZING() { function obs_STARTUP(win) {
info("Handling the STARTUP notification.");
is(win, gBrowser.selectedBrowser.contentWindow, "Saw the correct window");
tabEvents += "STARTUP;";
}
function obs_INITIALIZING(win) {
info("Handling the INITIALIZING notification."); info("Handling the INITIALIZING notification.");
is(win, gBrowser.selectedBrowser.contentWindow, "Saw the correct window");
tabEvents += "INITIALIZING;"; tabEvents += "INITIALIZING;";
} }
function obs_INITIALIZED() { function obs_INITIALIZED(win) {
info("Handling the INITIALIZED notification."); info("Handling the INITIALIZED notification.");
is(win, gBrowser.selectedBrowser.contentWindow, "Saw the correct window");
tabEvents += "INITIALIZED;"; tabEvents += "INITIALIZED;";
Tilt.destroy(Tilt.currentWindowId, true); Tilt.destroy(Tilt.currentWindowId, true);
} }
function obs_DESTROYING() { function obs_DESTROYING(win) {
info("Handling the DESTROYING( notification."); info("Handling the DESTROYING( notification.");
is(win, gBrowser.selectedBrowser.contentWindow, "Saw the correct window");
tabEvents += "DESTROYING;"; tabEvents += "DESTROYING;";
} }
function obs_BEFORE_DESTROYED() { function obs_BEFORE_DESTROYED(win) {
info("Handling the BEFORE_DESTROYED notification."); info("Handling the BEFORE_DESTROYED notification.");
is(win, gBrowser.selectedBrowser.contentWindow, "Saw the correct window");
tabEvents += "BEFORE_DESTROYED;"; tabEvents += "BEFORE_DESTROYED;";
} }
function obs_DESTROYED() { function obs_DESTROYED(win) {
info("Handling the DESTROYED notification."); info("Handling the DESTROYED notification.");
is(win, gBrowser.selectedBrowser.contentWindow, "Saw the correct window");
tabEvents += "DESTROYED;"; tabEvents += "DESTROYED;";
} }
function finalize() { function finalize(win) {
if (!tabEvents) { if (!tabEvents) {
return; return;
} }
is(tabEvents, "INITIALIZING;INITIALIZED;DESTROYING;BEFORE_DESTROYED;DESTROYED;", is(win, gBrowser.selectedBrowser.contentWindow, "Saw the correct window");
is(tabEvents, "STARTUP;INITIALIZING;INITIALIZED;DESTROYING;BEFORE_DESTROYED;DESTROYED;",
"The notifications weren't fired in the correct order."); "The notifications weren't fired in the correct order.");
cleanup(); cleanup();

Просмотреть файл

@ -0,0 +1,173 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
let tab0, tab1, tab2;
let testStep = -1;
let expected = [];
function expect(notification, win) {
expected.push({ notification: notification, window: win });
}
function notification(win, topic) {
if (expected.length == 0) {
is(topic, null, "Shouldn't see a notification");
return;
}
let { notification, window } = expected.shift();
is(topic, notification, "Saw the expected notification");
is(win, window, "Saw the expected window");
}
function after(notification, callback) {
function observer() {
Services.obs.removeObserver(observer, notification);
executeSoon(callback);
}
Services.obs.addObserver(observer, notification, false);
}
function test() {
if (!isTiltEnabled()) {
info("Skipping tab switch test because Tilt isn't enabled.");
return;
}
if (!isWebGLSupported()) {
info("Skipping tab switch test because WebGL isn't supported.");
return;
}
Services.obs.addObserver(notification, STARTUP, false);
Services.obs.addObserver(notification, INITIALIZING, false);
Services.obs.addObserver(notification, INITIALIZED, false);
Services.obs.addObserver(notification, DESTROYING, false);
Services.obs.addObserver(notification, BEFORE_DESTROYED, false);
Services.obs.addObserver(notification, DESTROYED, false);
Services.obs.addObserver(notification, SHOWN, false);
Services.obs.addObserver(notification, HIDDEN, false);
waitForExplicitFinish();
tab0 = gBrowser.selectedTab;
nextStep();
}
function createTab2() {
}
let testSteps = [
function step0() {
tab1 = createTab(function() {
expect(STARTUP, tab1.linkedBrowser.contentWindow);
expect(INITIALIZING, tab1.linkedBrowser.contentWindow);
expect(INITIALIZED, tab1.linkedBrowser.contentWindow);
after(INITIALIZED, nextStep);
createTilt({}, false, function suddenDeath()
{
info("Tilt could not be initialized properly.");
cleanup();
});
});
},
function step1() {
expect(HIDDEN, tab1.linkedBrowser.contentWindow);
tab2 = createTab(function() {
expect(STARTUP, tab2.linkedBrowser.contentWindow);
expect(INITIALIZING, tab2.linkedBrowser.contentWindow);
expect(INITIALIZED, tab2.linkedBrowser.contentWindow);
after(INITIALIZED, nextStep);
createTilt({}, false, function suddenDeath()
{
info("Tilt could not be initialized properly.");
cleanup();
});
});
},
function step2() {
expect(HIDDEN, tab2.linkedBrowser.contentWindow);
after(HIDDEN, nextStep);
gBrowser.selectedTab = tab0;
},
function step3() {
expect(SHOWN, tab2.linkedBrowser.contentWindow);
after(SHOWN, nextStep);
gBrowser.selectedTab = tab2;
},
function step4() {
expect(HIDDEN, tab2.linkedBrowser.contentWindow);
expect(SHOWN, tab1.linkedBrowser.contentWindow);
after(SHOWN, nextStep);
gBrowser.selectedTab = tab1;
},
function step5() {
expect(HIDDEN, tab1.linkedBrowser.contentWindow);
expect(SHOWN, tab2.linkedBrowser.contentWindow);
after(SHOWN, nextStep);
gBrowser.selectedTab = tab2;
},
function step6() {
expect(DESTROYING, tab2.linkedBrowser.contentWindow);
expect(BEFORE_DESTROYED, tab2.linkedBrowser.contentWindow);
expect(DESTROYED, tab2.linkedBrowser.contentWindow);
after(DESTROYED, nextStep);
Tilt.destroy(Tilt.currentWindowId, true);
},
function step7() {
expect(SHOWN, tab1.linkedBrowser.contentWindow);
gBrowser.removeCurrentTab();
tab2 = null;
expect(DESTROYING, tab1.linkedBrowser.contentWindow);
expect(HIDDEN, tab1.linkedBrowser.contentWindow);
expect(BEFORE_DESTROYED, tab1.linkedBrowser.contentWindow);
expect(DESTROYED, tab1.linkedBrowser.contentWindow);
after(DESTROYED, nextStep);
gBrowser.removeCurrentTab();
tab1 = null;
},
function step8_cleanup() {
is(gBrowser.selectedTab, tab0, "Should be back to the first tab");
cleanup();
}
];
function cleanup() {
if (tab1) {
gBrowser.removeTab(tab1);
tab1 = null;
}
if (tab2) {
gBrowser.removeTab(tab2);
tab2 = null;
}
Services.obs.removeObserver(notification, STARTUP);
Services.obs.removeObserver(notification, INITIALIZING);
Services.obs.removeObserver(notification, INITIALIZED);
Services.obs.removeObserver(notification, DESTROYING);
Services.obs.removeObserver(notification, BEFORE_DESTROYED);
Services.obs.removeObserver(notification, DESTROYED);
Services.obs.removeObserver(notification, SHOWN);
Services.obs.removeObserver(notification, HIDDEN);
finish();
}
function nextStep() {
let step = testSteps.shift();
info("Executing " + step.name);
step();
}

Просмотреть файл

@ -28,6 +28,7 @@ function createNewTab() {
tab1 = createTab(function() { tab1 = createTab(function() {
Services.obs.addObserver(finalize, DESTROYED, false); Services.obs.addObserver(finalize, DESTROYED, false);
Services.obs.addObserver(tab_STARTUP, STARTUP, false);
Services.obs.addObserver(tab_INITIALIZING, INITIALIZING, false); Services.obs.addObserver(tab_INITIALIZING, INITIALIZING, false);
Services.obs.addObserver(tab_DESTROYING, DESTROYING, false); Services.obs.addObserver(tab_DESTROYING, DESTROYING, false);
Services.obs.addObserver(tab_SHOWN, SHOWN, false); Services.obs.addObserver(tab_SHOWN, SHOWN, false);
@ -48,23 +49,33 @@ function createNewTab() {
}); });
} }
function tab_INITIALIZING() { function tab_STARTUP(win) {
info("Handling the STARTUP notification.");
is(win, tab1.linkedBrowser.contentWindow, "Saw the correct window");
tabEvents += "STARTUP;";
}
function tab_INITIALIZING(win) {
info("Handling the INITIALIZING notification."); info("Handling the INITIALIZING notification.");
is(win, tab1.linkedBrowser.contentWindow, "Saw the correct window");
tabEvents += "INITIALIZING;"; tabEvents += "INITIALIZING;";
} }
function tab_DESTROYING() { function tab_DESTROYING(win) {
info("Handling the DESTROYING notification."); info("Handling the DESTROYING notification.");
is(win, tab1.linkedBrowser.contentWindow, "Saw the correct window");
tabEvents += "DESTROYING;"; tabEvents += "DESTROYING;";
} }
function tab_SHOWN() { function tab_SHOWN(win) {
info("Handling the SHOWN notification."); info("Handling the SHOWN notification.");
is(win, tab1.linkedBrowser.contentWindow, "Saw the correct window");
tabEvents += "SHOWN;"; tabEvents += "SHOWN;";
} }
function tab_HIDDEN() { function tab_HIDDEN(win) {
info("Handling the HIDDEN notification."); info("Handling the HIDDEN notification.");
is(win, tab1.linkedBrowser.contentWindow, "Saw the correct window");
tabEvents += "HIDDEN;"; tabEvents += "HIDDEN;";
} }
@ -83,12 +94,14 @@ let testSteps = [
} }
]; ];
function finalize() { function finalize(win) {
if (!tabEvents) { if (!tabEvents) {
return; return;
} }
is(tabEvents, "INITIALIZING;HIDDEN;SHOWN;DESTROYING;", is(win, tab1.linkedBrowser.contentWindow, "Saw the correct window");
is(tabEvents, "STARTUP;INITIALIZING;HIDDEN;SHOWN;DESTROYING;",
"The notifications weren't fired in the correct order."); "The notifications weren't fired in the correct order.");
cleanup(); cleanup();

Просмотреть файл

@ -74,13 +74,13 @@ function test() {
let store = dom.traverse(iframe.contentWindow); let store = dom.traverse(iframe.contentWindow);
let expected = [ let expected = [
{ name: "html", depth: 0 * STACK_THICKNESS }, { name: "html", depth: 0 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "head", depth: 1 * STACK_THICKNESS }, { name: "head", depth: 1 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "body", depth: 1 * STACK_THICKNESS }, { name: "body", depth: 1 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "style", depth: 2 * STACK_THICKNESS }, { name: "style", depth: 2 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "script", depth: 2 * STACK_THICKNESS }, { name: "script", depth: 2 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "div", depth: 2 * STACK_THICKNESS }, { name: "div", depth: 2 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "span", depth: 3 * STACK_THICKNESS }, { name: "span", depth: 3 * STACK_THICKNESS, thickness: STACK_THICKNESS },
]; ];
is(store.nodes.length, expected.length, is(store.nodes.length, expected.length,
@ -93,6 +93,8 @@ function test() {
"traversed node " + (i + 1) + " isn't the expected one."); "traversed node " + (i + 1) + " isn't the expected one.");
is(store.info[i].coord.depth, expected[i].depth, is(store.info[i].coord.depth, expected[i].depth,
"traversed node " + (i + 1) + " doesn't have the expected depth."); "traversed node " + (i + 1) + " doesn't have the expected depth.");
is(store.info[i].coord.thickness, expected[i].thickness,
"traversed node " + (i + 1) + " doesn't have the expected thickness.");
} }
}); });
} }

Просмотреть файл

@ -123,22 +123,22 @@ function test() {
let store = dom.traverse(iframe.contentWindow); let store = dom.traverse(iframe.contentWindow);
let expected = [ let expected = [
{ name: "html", depth: 0 * STACK_THICKNESS }, { name: "html", depth: 0 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "head", depth: 1 * STACK_THICKNESS }, { name: "head", depth: 1 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "body", depth: 1 * STACK_THICKNESS }, { name: "body", depth: 1 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "div", depth: 2 * STACK_THICKNESS }, { name: "div", depth: 2 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "span", depth: 2 * STACK_THICKNESS }, { name: "span", depth: 2 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "iframe", depth: 2 * STACK_THICKNESS }, { name: "iframe", depth: 2 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "span", depth: 2 * STACK_THICKNESS }, { name: "span", depth: 2 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "iframe", depth: 2 * STACK_THICKNESS }, { name: "iframe", depth: 2 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "html", depth: 3 * STACK_THICKNESS }, { name: "html", depth: 3 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "html", depth: 3 * STACK_THICKNESS }, { name: "html", depth: 3 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "head", depth: 4 * STACK_THICKNESS }, { name: "head", depth: 4 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "body", depth: 4 * STACK_THICKNESS }, { name: "body", depth: 4 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "head", depth: 4 * STACK_THICKNESS }, { name: "head", depth: 4 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "body", depth: 4 * STACK_THICKNESS }, { name: "body", depth: 4 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "span", depth: 5 * STACK_THICKNESS }, { name: "span", depth: 5 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "div", depth: 5 * STACK_THICKNESS }, { name: "div", depth: 5 * STACK_THICKNESS, thickness: STACK_THICKNESS },
]; ];
is(store.nodes.length, expected.length, is(store.nodes.length, expected.length,
@ -151,6 +151,8 @@ function test() {
"traversed node " + (i + 1) + " isn't the expected one."); "traversed node " + (i + 1) + " isn't the expected one.");
is(store.info[i].coord.depth, expected[i].depth, is(store.info[i].coord.depth, expected[i].depth,
"traversed node " + (i + 1) + " doesn't have the expected depth."); "traversed node " + (i + 1) + " doesn't have the expected depth.");
is(store.info[i].coord.thickness, expected[i].thickness,
"traversed node " + (i + 1) + " doesn't have the expected thickness.");
} }
}); });
} }

Просмотреть файл

@ -0,0 +1,85 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const STACK_THICKNESS = 15;
function init(callback) {
let iframe = gBrowser.ownerDocument.createElement("iframe");
iframe.addEventListener("load", function onLoad() {
iframe.removeEventListener("load", onLoad, true);
callback(iframe);
gBrowser.parentNode.removeChild(iframe);
finish();
}, true);
iframe.setAttribute("src", ["data:text/html,",
"<!DOCTYPE html>",
"<html>",
"<body style='margin: 0;'>",
"<div>",
"<p>Foo</p>",
"<div>",
"<span>Bar</span>",
"</div>",
"<div></div>",
"</div>",
"</body>",
"</html>"
].join(""));
gBrowser.parentNode.appendChild(iframe);
}
function nodeCallback(aContentWindow, aNode, aParentPosition) {
let coord = TiltUtils.DOM.getNodePosition(aContentWindow, aNode, aParentPosition);
if (aNode.localName != "div")
coord.thickness = 0;
if (aNode.localName == "span")
coord.depth += STACK_THICKNESS;
return coord;
}
function test() {
waitForExplicitFinish();
ok(TiltUtils, "The TiltUtils object doesn't exist.");
let dom = TiltUtils.DOM;
ok(dom, "The TiltUtils.DOM wasn't found.");
init(function(iframe) {
let store = dom.traverse(iframe.contentWindow, {
nodeCallback: nodeCallback
});
let expected = [
{ name: "html", depth: 0 * STACK_THICKNESS, thickness: 0 },
{ name: "head", depth: 0 * STACK_THICKNESS, thickness: 0 },
{ name: "body", depth: 0 * STACK_THICKNESS, thickness: 0 },
{ name: "div", depth: 0 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "p", depth: 1 * STACK_THICKNESS, thickness: 0 },
{ name: "div", depth: 1 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "div", depth: 1 * STACK_THICKNESS, thickness: STACK_THICKNESS },
{ name: "span", depth: 3 * STACK_THICKNESS, thickness: 0 },
];
is(store.nodes.length, expected.length,
"The traverse() function didn't walk the correct number of nodes.");
is(store.info.length, expected.length,
"The traverse() function didn't examine the correct number of nodes.");
for (let i = 0; i < expected.length; i++) {
is(store.info[i].name, expected[i].name,
"traversed node " + (i + 1) + " isn't the expected one.");
is(store.info[i].coord.depth, expected[i].depth,
"traversed node " + (i + 1) + " doesn't have the expected depth.");
is(store.info[i].coord.thickness, expected[i].thickness,
"traversed node " + (i + 1) + " doesn't have the expected thickness.");
}
});
}

Просмотреть файл

@ -32,6 +32,7 @@ function test() {
webGLLoad = true; webGLLoad = true;
} }
}); });
visualizer.init();
ok(webGLError ^ webGLLoad, ok(webGLError ^ webGLLoad,
"The WebGL context should either be created or not."); "The WebGL context should either be created or not.");

Просмотреть файл

@ -50,6 +50,7 @@ const DEFAULT_HTML = "data:text/html," +
let Tilt = TiltManager.getTiltForBrowser(window); let Tilt = TiltManager.getTiltForBrowser(window);
const STARTUP = Tilt.NOTIFICATIONS.STARTUP;
const INITIALIZING = Tilt.NOTIFICATIONS.INITIALIZING; const INITIALIZING = Tilt.NOTIFICATIONS.INITIALIZING;
const INITIALIZED = Tilt.NOTIFICATIONS.INITIALIZED; const INITIALIZED = Tilt.NOTIFICATIONS.INITIALIZED;
const DESTROYING = Tilt.NOTIFICATIONS.DESTROYING; const DESTROYING = Tilt.NOTIFICATIONS.DESTROYING;

Просмотреть файл

@ -10,7 +10,6 @@ const Cc = Components.classes;
const Ci = Components.interfaces; const Ci = Components.interfaces;
const Cu = Components.utils; const Cu = Components.utils;
const CONSOLEAPI_CLASS_ID = "{b49c18f8-3379-4fc0-8c90-d7772c1a9ff3}";
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -23,15 +22,29 @@ XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
XPCOMUtils.defineLazyModuleGetter(this, "Services", XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm"); "resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DebuggerServer",
"resource://gre/modules/devtools/dbg-server.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DebuggerClient",
"resource://gre/modules/devtools/dbg-client.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "WebConsoleUtils", XPCOMUtils.defineLazyModuleGetter(this, "WebConsoleUtils",
"resource://gre/modules/devtools/WebConsoleUtils.jsm"); "resource://gre/modules/devtools/WebConsoleUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "webConsoleDefinition",
"resource:///modules/devtools/ToolDefinitions.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise", XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/commonjs/sdk/core/promise.js"); "resource://gre/modules/commonjs/sdk/core/promise.js");
XPCOMUtils.defineLazyModuleGetter(this, "ViewHelpers",
"resource:///modules/devtools/ViewHelpers.jsm");
const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties"; const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
let l10n = new WebConsoleUtils.l10n(STRINGS_URI); let l10n = new WebConsoleUtils.l10n(STRINGS_URI);
const BROWSER_CONSOLE_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
this.EXPORTED_SYMBOLS = ["HUDService"]; this.EXPORTED_SYMBOLS = ["HUDService"];
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -75,14 +88,39 @@ HUD_SERVICE.prototype =
* *
* @param object aTarget * @param object aTarget
* The target that the web console will connect to. * The target that the web console will connect to.
* @param nsIDOMElement aIframe * @param nsIDOMWindow aIframeWindow
* The iframe element into which to place the web console. * The window where the web console UI is already loaded.
* @param nsIDOMWindow aChromeWindow
* The window of the web console owner.
* @return object * @return object
* A Promise object for the opening of the new WebConsole instance. * A Promise object for the opening of the new WebConsole instance.
*/ */
openWebConsole: function HS_openWebConsole(aTarget, aIframe) openWebConsole:
function HS_openWebConsole(aTarget, aIframeWindow, aChromeWindow)
{ {
let hud = new WebConsole(aTarget, aIframe); let hud = new WebConsole(aTarget, aIframeWindow, aChromeWindow);
this.hudReferences[hud.hudId] = hud;
return hud.init();
},
/**
* Open a Browser Console for the given target.
*
* @see devtools/framework/Target.jsm for details about targets.
*
* @param object aTarget
* The target that the browser console will connect to.
* @param nsIDOMWindow aIframeWindow
* The window where the browser console UI is already loaded.
* @param nsIDOMWindow aChromeWindow
* The window of the browser console owner.
* @return object
* A Promise object for the opening of the new BrowserConsole instance.
*/
openBrowserConsole:
function HS_openBrowserConsole(aTarget, aIframeWindow, aChromeWindow)
{
let hud = new BrowserConsole(aTarget, aIframeWindow, aChromeWindow);
this.hudReferences[hud.hudId] = hud; this.hudReferences[hud.hudId] = hud;
return hud.init(); return hud.init();
}, },
@ -144,24 +182,27 @@ HUD_SERVICE.prototype =
* that displays console log data as well as provides an interactive terminal to * that displays console log data as well as provides an interactive terminal to
* manipulate the target's document content. * manipulate the target's document content.
* *
* This object only wraps the iframe that holds the Web Console UI. * This object only wraps the iframe that holds the Web Console UI. This is
* meant to be an integration point between the Firefox UI and the Web Console
* UI and features.
* *
* @constructor * @constructor
* @param object aTarget * @param object aTarget
* The target that the web console will connect to. * The target that the web console will connect to.
* @param nsIDOMElement aIframe * @param nsIDOMWindow aIframeWindow
* iframe into which we should create the WebConsole UI. * The window where the web console UI is already loaded.
* @param nsIDOMWindow aChromeWindow
* The window of the web console owner.
*/ */
function WebConsole(aTarget, aIframe) function WebConsole(aTarget, aIframeWindow, aChromeWindow)
{ {
this.iframe = aIframe; this.iframeWindow = aIframeWindow;
this.iframe.className = "web-console-frame"; this.chromeWindow = aChromeWindow;
this.chromeDocument = this.iframe.ownerDocument;
this.chromeWindow = this.chromeDocument.defaultView;
this.hudId = "hud_" + Date.now(); this.hudId = "hud_" + Date.now();
this.target = aTarget; this.target = aTarget;
this.browserWindow = this.chromeWindow.top; this.browserWindow = this.chromeWindow.top;
let element = this.browserWindow.document.documentElement; let element = this.browserWindow.document.documentElement;
if (element.getAttribute("windowtype") != "navigator:browser") { if (element.getAttribute("windowtype") != "navigator:browser") {
this.browserWindow = HUDService.currentContext(); this.browserWindow = HUDService.currentContext();
@ -169,11 +210,12 @@ function WebConsole(aTarget, aIframe)
} }
WebConsole.prototype = { WebConsole.prototype = {
iframeWindow: null,
chromeWindow: null, chromeWindow: null,
chromeDocument: null, browserWindow: null,
hudId: null, hudId: null,
target: null, target: null,
iframe: null, _browserConsole: false,
_destroyer: null, _destroyer: null,
/** /**
@ -212,37 +254,8 @@ WebConsole.prototype = {
*/ */
init: function WC_init() init: function WC_init()
{ {
let deferred = Promise.defer(); this.ui = new this.iframeWindow.WebConsoleFrame(this);
return this.ui.init().then(() => this);
let onIframeLoad = function() {
this.iframe.removeEventListener("load", onIframeLoad, true);
initUI();
}.bind(this);
let initUI = function() {
this.iframeWindow = this.iframe.contentWindow.wrappedJSObject;
this.ui = new this.iframeWindow.WebConsoleFrame(this);
this.ui.init().then(onSuccess, onFailure);
}.bind(this);
let onSuccess = function() {
deferred.resolve(this);
}.bind(this);
let onFailure = function(aReason) {
deferred.reject(aReason);
};
let win, doc;
if ((win = this.iframe.contentWindow) &&
(doc = win.document) &&
doc.readyState == "complete") {
initUI();
}
else {
this.iframe.addEventListener("load", onIframeLoad, true);
}
return deferred.promise;
}, },
/** /**
@ -360,6 +373,10 @@ WebConsole.prototype = {
let panelWin = null; let panelWin = null;
let debuggerWasOpen = true; let debuggerWasOpen = true;
let toolbox = gDevTools.getToolbox(this.target); let toolbox = gDevTools.getToolbox(this.target);
if (!toolbox) {
self.viewSource(aSourceURL, aSourceLine);
return;
}
if (!toolbox.getPanel("jsdebugger")) { if (!toolbox.getPanel("jsdebugger")) {
debuggerWasOpen = false; debuggerWasOpen = false;
@ -404,6 +421,40 @@ WebConsole.prototype = {
} }
}, },
/**
* Retrieve information about the JavaScript debugger's stackframes list. This
* is used to allow the Web Console to evaluate code in the selected
* stackframe.
*
* @return object|null
* An object which holds:
* - frames: the active ThreadClient.cachedFrames array.
* - selected: depth/index of the selected stackframe in the debugger
* UI.
* If the debugger is not open or if it's not paused, then |null| is
* returned.
*/
getDebuggerFrames: function WC_getDebuggerFrames()
{
let toolbox = gDevTools.getToolbox(this.target);
if (!toolbox) {
return null;
}
let panel = toolbox.getPanel("jsdebugger");
if (!panel) {
return null;
}
let framesController = panel.panelWin.gStackFrames;
let thread = framesController.activeThread;
if (thread && thread.paused) {
return {
frames: thread.cachedFrames,
selected: framesController.currentFrame,
};
}
return null;
},
/** /**
* Destroy the object. Call this method to avoid memory leaks when the Web * Destroy the object. Call this method to avoid memory leaks when the Web
* Console is closed. * Console is closed.
@ -452,11 +503,102 @@ WebConsole.prototype = {
}, },
}; };
/**
* A BrowserConsole instance is an interactive console initialized *per target*
* that displays console log data as well as provides an interactive terminal to
* manipulate the target's document content.
*
* This object only wraps the iframe that holds the Browser Console UI. This is
* meant to be an integration point between the Firefox UI and the Browser Console
* UI and features.
*
* @constructor
* @param object aTarget
* The target that the browser console will connect to.
* @param nsIDOMWindow aIframeWindow
* The window where the browser console UI is already loaded.
* @param nsIDOMWindow aChromeWindow
* The window of the browser console owner.
*/
function BrowserConsole()
{
WebConsole.apply(this, arguments);
}
ViewHelpers.create({ constructor: BrowserConsole, proto: WebConsole.prototype },
{
_browserConsole: true,
_bc_init: null,
_bc_destroyer: null,
$init: WebConsole.prototype.init,
/**
* Initialize the Browser Console instance.
*
* @return object
* A Promise for the initialization.
*/
init: function BC_init()
{
if (this._bc_init) {
return this._bc_init;
}
let window = this.iframeWindow;
let onClose = () => {
window.removeEventListener("unload", onClose);
this.destroy();
};
window.addEventListener("unload", onClose);
this._bc_init = this.$init().then((aReason) => {
let title = this.ui.rootElement.getAttribute("browserConsoleTitle");
this.ui.rootElement.setAttribute("title", title);
return aReason;
});
return this._bc_init;
},
$destroy: WebConsole.prototype.destroy,
/**
* Destroy the object.
*
* @return object
* A Promise object that is resolved once the Browser Console is closed.
*/
destroy: function BC_destroy()
{
if (this._bc_destroyer) {
return this._bc_destroyer.promise;
}
this._bc_destroyer = Promise.defer();
let chromeWindow = this.chromeWindow;
this.$destroy().then(() =>
this.target.client.close(() => {
HeadsUpDisplayUICommands._browserConsoleID = null;
chromeWindow.close();
this._bc_destroyer.resolve(null);
}));
return this._bc_destroyer.promise;
},
});
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// HeadsUpDisplayUICommands // HeadsUpDisplayUICommands
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
var HeadsUpDisplayUICommands = { var HeadsUpDisplayUICommands = {
_browserConsoleID: null,
_browserConsoleDefer: null,
/** /**
* Toggle the Web Console for the current tab. * Toggle the Web Console for the current tab.
* *
@ -493,6 +635,83 @@ var HeadsUpDisplayUICommands = {
let panel = toolbox ? toolbox.getPanel("webconsole") : null; let panel = toolbox ? toolbox.getPanel("webconsole") : null;
return panel ? panel.hud : null; return panel ? panel.hud : null;
}, },
/**
* Toggle the Browser Console.
*/
toggleBrowserConsole: function UIC_toggleBrowserConsole()
{
if (this._browserConsoleID) {
let hud = HUDService.getHudReferenceById(this._browserConsoleID);
return hud.destroy();
}
if (this._browserConsoleDefer) {
return this._browserConsoleDefer.promise;
}
this._browserConsoleDefer = Promise.defer();
function connect()
{
let deferred = Promise.defer();
if (!DebuggerServer.initialized) {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
let client = new DebuggerClient(DebuggerServer.connectPipe());
client.connect(() =>
client.listTabs((aResponse) =>
deferred.resolve({ form: aResponse, client: client })
));
return deferred.promise;
}
let target;
function getTarget(aConnection)
{
let options = {
form: aConnection.form,
client: aConnection.client,
chrome: true,
};
return TargetFactory.forRemoteTab(options);
}
function openWindow(aTarget)
{
target = aTarget;
let deferred = Promise.defer();
let win = Services.ww.openWindow(null, webConsoleDefinition.url, "_blank",
BROWSER_CONSOLE_WINDOW_FEATURES, null);
win.addEventListener("load", function onLoad() {
win.removeEventListener("load", onLoad);
deferred.resolve(win);
});
return deferred.promise;
}
connect().then(getTarget).then(openWindow).then((aWindow) =>
HUDService.openBrowserConsole(target, aWindow, aWindow)
.then((aBrowserConsole) => {
this._browserConsoleID = aBrowserConsole.hudId;
this._browserConsoleDefer.resolve(aBrowserConsole);
this._browserConsoleDefer = null;
}));
return this._browserConsoleDefer.promise;
},
get browserConsole() {
return HUDService.getHudReferenceById(this._browserConsoleID);
},
}; };
const HUDService = new HUD_SERVICE(); const HUDService = new HUD_SERVICE();

Просмотреть файл

@ -41,26 +41,52 @@ WebConsolePanel.prototype = {
{ {
let parentDoc = this._toolbox.doc; let parentDoc = this._toolbox.doc;
let iframe = parentDoc.getElementById("toolbox-panel-iframe-webconsole"); let iframe = parentDoc.getElementById("toolbox-panel-iframe-webconsole");
let promise; iframe.className = "web-console-frame";
// Local debugging needs to make the target remote. // Make sure the iframe content window is ready.
if (!this.target.isRemote) { let deferredIframe = Promise.defer();
promise = this.target.makeRemote(); let win, doc;
} else { if ((win = iframe.contentWindow) &&
promise = Promise.resolve(this.target); (doc = win.document) &&
doc.readyState == "complete") {
deferredIframe.resolve(null);
}
else {
iframe.addEventListener("load", function onIframeLoad() {
iframe.removeEventListener("load", onIframeLoad, true);
deferredIframe.resolve(null);
}, true);
} }
return promise // Local debugging needs to make the target remote.
.then(function(aTarget) { let promiseTarget;
if (!this.target.isRemote) {
promiseTarget = this.target.makeRemote();
}
else {
promiseTarget = Promise.resolve(this.target);
}
// 1. Wait for the iframe to load.
// 2. Wait for the remote target.
// 3. Open the Web Console.
return deferredIframe.promise
.then(() => promiseTarget)
.then((aTarget) => {
this._frameWindow._remoteTarget = aTarget; this._frameWindow._remoteTarget = aTarget;
return HUDService.openWebConsole(this.target, iframe);
}.bind(this)) let webConsoleUIWindow = iframe.contentWindow.wrappedJSObject;
.then(function onSuccess(aWebConsole) { let chromeWindow = iframe.ownerDocument.defaultView;
return HUDService.openWebConsole(this.target, webConsoleUIWindow,
chromeWindow);
})
.then((aWebConsole) => {
this.hud = aWebConsole; this.hud = aWebConsole;
this._isReady = true; this._isReady = true;
this.emit("ready"); this.emit("ready");
return this; return this;
}.bind(this), function onError(aReason) { }, (aReason) => {
let msg = "WebConsolePanel open failed. " + let msg = "WebConsolePanel open failed. " +
aReason.error + ": " + aReason.message; aReason.error + ": " + aReason.message;
dump(msg + "\n"); dump(msg + "\n");
@ -80,9 +106,7 @@ WebConsolePanel.prototype = {
} }
this._destroyer = this.hud.destroy(); this._destroyer = this.hud.destroy();
this._destroyer.then(function() { this._destroyer.then(() => this.emit("destroyed"));
this.emit("destroyed");
}.bind(this));
return this._destroyer; return this._destroyer;
}, },

Просмотреть файл

@ -42,7 +42,6 @@ MOCHITEST_BROWSER_FILES = \
browser_webconsole_jsterm.js \ browser_webconsole_jsterm.js \
browser_webconsole_null_and_undefined_output.js \ browser_webconsole_null_and_undefined_output.js \
browser_webconsole_output_order.js \ browser_webconsole_output_order.js \
browser_webconsole_property_panel.js \
browser_webconsole_property_provider.js \ browser_webconsole_property_provider.js \
browser_webconsole_bug_587617_output_copy.js \ browser_webconsole_bug_587617_output_copy.js \
browser_webconsole_bug_585237_line_limit.js \ browser_webconsole_bug_585237_line_limit.js \
@ -116,10 +115,12 @@ MOCHITEST_BROWSER_FILES = \
browser_output_longstring_expand.js \ browser_output_longstring_expand.js \
browser_netpanel_longstring_expand.js \ browser_netpanel_longstring_expand.js \
browser_repeated_messages_accuracy.js \ browser_repeated_messages_accuracy.js \
head.js \
browser_webconsole_bug_821877_csp_errors.js \ browser_webconsole_bug_821877_csp_errors.js \
test-bug-821877-csperrors.html \ browser_eval_in_debugger_stackframe.js \
test-bug-821877-csperrors.html^headers^ \ browser_console_variables_view.js \
browser_console_variables_view_while_debugging.js \
browser_console.js \
head.js \
$(NULL) $(NULL)
ifeq ($(OS_ARCH), Darwin) ifeq ($(OS_ARCH), Darwin)
@ -217,6 +218,9 @@ MOCHITEST_BROWSER_FILES += \
test-bug-766001-console-log.js \ test-bug-766001-console-log.js \
test-bug-766001-js-console-links.html \ test-bug-766001-js-console-links.html \
test-bug-766001-js-errors.js \ test-bug-766001-js-errors.js \
test-bug-821877-csperrors.html \
test-bug-821877-csperrors.html^headers^ \
test-eval-in-stackframe.html \
$(NULL) $(NULL)
include $(topsrcdir)/config/rules.mk include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -52,7 +52,7 @@ function test()
gBrowser.selectedBrowser.addEventListener("load", onPageLoad2, true); gBrowser.selectedBrowser.addEventListener("load", onPageLoad2, true);
content.location = TEST_URI2; content.location = TEST_URI2;
}, },
failureFn: finishTest, failureFn: finishTestWithError,
}; };
function onPageLoad2() { function onPageLoad2() {
@ -82,7 +82,7 @@ function test()
gBrowser.goBack(); gBrowser.goBack();
waitForSuccess(waitForBack); waitForSuccess(waitForBack);
}, },
failureFn: finishTest, failureFn: finishTestWithError,
}; };
let waitForBack = { let waitForBack = {
@ -98,7 +98,7 @@ function test()
waitForSuccess(waitForLocation3); waitForSuccess(waitForLocation3);
}, },
failureFn: finishTest, failureFn: finishTestWithError,
}; };
let waitForLocation3 = { let waitForLocation3 = {
@ -118,6 +118,12 @@ function test()
executeSoon(finishTest); executeSoon(finishTest);
}, },
failureFn: finishTest, failureFn: finishTestWithError,
}; };
function finishTestWithError()
{
info("output content: " + hud.outputNode.textContent);
finishTest();
}
} }

Просмотреть файл

@ -0,0 +1,93 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Test the basic features of the Browser Console, bug 587757.
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-eval-in-stackframe.html";
function test()
{
HUDConsoleUI.toggleBrowserConsole().then(consoleOpened);
}
function consoleOpened(hud)
{
hud.jsterm.clearOutput(true);
expectUncaughtException();
executeSoon(() => {
foobarExceptionBug587757();
});
// Add a message from a chrome window.
hud.iframeWindow.console.log("bug587757a");
// Add a message from a content window.
content.console.log("bug587757b");
// Test eval.
hud.jsterm.execute("document.location.href");
// Check for network requests.
let xhr = new XMLHttpRequest();
xhr.onload = () => info("xhr loaded, status is: " + xhr.status);
xhr.open("get", TEST_URI, true);
xhr.send();
let chromeConsole = -1;
let contentConsole = -1;
let execValue = -1;
let exception = -1;
let xhrRequest = false;
let output = hud.outputNode;
function performChecks()
{
let text = output.textContent;
chromeConsole = text.indexOf("bug587757a");
contentConsole = text.indexOf("bug587757b");
execValue = text.indexOf("browser.xul");
exception = text.indexOf("foobarExceptionBug587757");
xhrRequest = false;
let urls = output.querySelectorAll(".webconsole-msg-url");
for (let url of urls) {
if (url.value.indexOf(TEST_URI) > -1) {
xhrRequest = true;
break;
}
}
}
function showResults()
{
isnot(chromeConsole, -1, "chrome window console.log() is displayed");
isnot(contentConsole, -1, "content window console.log() is displayed");
isnot(execValue, -1, "jsterm eval result is displayed");
isnot(exception, -1, "exception is displayed");
ok(xhrRequest, "xhr request is displayed");
}
waitForSuccess({
name: "messages displayed",
validatorFn: () => {
performChecks();
return chromeConsole > -1 &&
contentConsole > -1 &&
execValue > -1 &&
exception > -1 &&
xhrRequest;
},
successFn: () => {
showResults();
executeSoon(finishTest);
},
failureFn: () => {
showResults();
info("output: " + output.textContent);
executeSoon(finishTest);
},
});
}

Просмотреть файл

@ -36,17 +36,18 @@ function performTest(hud)
let clickable = hud.outputNode.querySelector(".hud-clickable"); let clickable = hud.outputNode.querySelector(".hud-clickable");
ok(clickable, "the console.log() object .hud-clickable was found"); ok(clickable, "the console.log() object .hud-clickable was found");
isnot(clickable.textContent.indexOf("omgBug676722"), -1, isnot(clickable.textContent.indexOf("Object"), -1,
"clickable node content is correct"); "clickable node content is correct");
document.addEventListener("popupshown", function _onPopupShown(aEvent) { hud.jsterm.once("variablesview-fetched",
document.removeEventListener("popupshown", _onPopupShown); (aEvent, aVar) => {
ok(aVar, "object inspector opened on click");
isnot(aEvent.target.label.indexOf("omgBug676722"), -1, findVariableViewProperties(aVar, [{
"object inspector opened on click"); name: "abba",
value: "omgBug676722",
executeSoon(finishTest); }], { webconsole: hud }).then(finishTest);
}); });
executeSoon(function() { executeSoon(function() {
EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow); EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow);

Просмотреть файл

@ -0,0 +1,178 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Test that makes sure web console eval happens in the user-selected stackframe
// from the js debugger.
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-eval-in-stackframe.html";
let gWebConsole, gJSTerm, gVariablesView;
function test()
{
addTab(TEST_URI);
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
openConsole(null, consoleOpened);
}, true);
}
function consoleOpened(hud)
{
gWebConsole = hud;
gJSTerm = hud.jsterm;
gJSTerm.execute("fooObj", onExecuteFooObj);
}
function onExecuteFooObj()
{
let msg = gWebConsole.outputNode.querySelector(".webconsole-msg-output");
ok(msg, "output message found");
isnot(msg.textContent.indexOf("[object Object]"), -1, "message text check");
gJSTerm.once("variablesview-fetched", onFooObjFetch);
executeSoon(() =>
EventUtils.synthesizeMouse(msg, 2, 2, {}, gWebConsole.iframeWindow)
);
}
function onFooObjFetch(aEvent, aVar)
{
gVariablesView = aVar._variablesView;
ok(gVariablesView, "variables view object");
findVariableViewProperties(aVar, [
{ name: "testProp", value: "testValue" },
], { webconsole: gWebConsole }).then(onTestPropFound);
}
function onTestPropFound(aResults)
{
let prop = aResults[0].matchedProp;
ok(prop, "matched the |testProp| property in the variables view");
is(content.wrappedJSObject.fooObj.testProp, aResults[0].value,
"|fooObj.testProp| value is correct");
// Check that property value updates work and that jsterm functions can be
// used.
updateVariablesViewProperty({
property: prop,
field: "value",
string: "document.title + window.location + $('p')",
webconsole: gWebConsole,
callback: onFooObjFetchAfterUpdate,
});
}
function onFooObjFetchAfterUpdate(aEvent, aVar)
{
info("onFooObjFetchAfterUpdate");
let para = content.document.querySelector("p");
let expectedValue = content.document.title + content.location + para;
findVariableViewProperties(aVar, [
{ name: "testProp", value: expectedValue },
], { webconsole: gWebConsole }).then(onUpdatedTestPropFound);
}
function onUpdatedTestPropFound(aResults)
{
let prop = aResults[0].matchedProp;
ok(prop, "matched the updated |testProp| property value");
is(content.wrappedJSObject.fooObj.testProp, aResults[0].value,
"|fooObj.testProp| value has been updated");
// Check that property name updates work.
updateVariablesViewProperty({
property: prop,
field: "name",
string: "testUpdatedProp",
webconsole: gWebConsole,
callback: onFooObjFetchAfterPropRename,
});
}
function onFooObjFetchAfterPropRename(aEvent, aVar)
{
info("onFooObjFetchAfterPropRename");
let para = content.document.querySelector("p");
let expectedValue = content.document.title + content.location + para;
// Check that the new value is in the variables view.
findVariableViewProperties(aVar, [
{ name: "testUpdatedProp", value: expectedValue },
], { webconsole: gWebConsole }).then(onRenamedTestPropFound);
}
function onRenamedTestPropFound(aResults)
{
let prop = aResults[0].matchedProp;
ok(prop, "matched the renamed |testProp| property");
ok(!content.wrappedJSObject.fooObj.testProp,
"|fooObj.testProp| has been deleted");
is(content.wrappedJSObject.fooObj.testUpdatedProp, aResults[0].value,
"|fooObj.testUpdatedProp| is correct");
// Check that property value updates that cause exceptions are reported in
// the web console output.
updateVariablesViewProperty({
property: prop,
field: "value",
string: "foobarzFailure()",
webconsole: gWebConsole,
callback: onPropUpdateError,
});
}
function onPropUpdateError(aEvent, aVar)
{
info("onPropUpdateError");
let para = content.document.querySelector("p");
let expectedValue = content.document.title + content.location + para;
// Make sure the property did not change.
findVariableViewProperties(aVar, [
{ name: "testUpdatedProp", value: expectedValue },
], { webconsole: gWebConsole }).then(onRenamedTestPropFoundAgain);
}
function onRenamedTestPropFoundAgain(aResults)
{
let prop = aResults[0].matchedProp;
ok(prop, "matched the renamed |testProp| property again");
let outputNode = gWebConsole.outputNode;
waitForSuccess({
name: "exception in property update reported in the web console output",
validatorFn: () => outputNode.textContent.indexOf("foobarzFailure") != -1,
successFn: testPropDelete.bind(null, prop),
failureFn: testPropDelete.bind(null, prop),
});
}
function testPropDelete(aProp)
{
gVariablesView.window.focus();
aProp.focus();
executeSoon(() => {
EventUtils.synthesizeKey("VK_DELETE", {}, gVariablesView.window);
gWebConsole = gJSTerm = gVariablesView = null;
});
waitForSuccess({
name: "property deleted",
validatorFn: () => !("testUpdatedProp" in content.wrappedJSObject.fooObj),
successFn: finishTest,
failureFn: finishTest,
});
}

Просмотреть файл

@ -0,0 +1,132 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Test that makes sure web console eval happens in the user-selected stackframe
// from the js debugger, when changing the value of a property in the variables
// view.
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-eval-in-stackframe.html";
let gWebConsole, gJSTerm, gDebuggerWin, gThread, gDebuggerController,
gStackframes, gVariablesView;
function test()
{
addTab(TEST_URI);
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
openConsole(null, consoleOpened);
}, true);
}
function consoleOpened(hud)
{
gWebConsole = hud;
gJSTerm = hud.jsterm;
executeSoon(() => {
info("openDebugger");
openDebugger().then(debuggerOpened);
});
}
function debuggerOpened(aResult)
{
gDebuggerWin = aResult.panelWin;
gDebuggerController = gDebuggerWin.DebuggerController;
gThread = gDebuggerController.activeThread;
gStackframes = gDebuggerController.StackFrames;
executeSoon(() => {
gThread.addOneTimeListener("framesadded", onFramesAdded);
info("firstCall()");
content.wrappedJSObject.firstCall();
});
}
function onFramesAdded()
{
info("onFramesAdded");
executeSoon(() =>
openConsole(null, () =>
gJSTerm.execute("fooObj", onExecuteFooObj)
)
);
}
function onExecuteFooObj()
{
let msg = gWebConsole.outputNode.querySelector(".webconsole-msg-output");
ok(msg, "output message found");
isnot(msg.textContent.indexOf("[object Object]"), -1, "message text check");
gJSTerm.once("variablesview-fetched", onFooObjFetch);
executeSoon(() => EventUtils.synthesizeMouse(msg, 2, 2, {},
gWebConsole.iframeWindow));
}
function onFooObjFetch(aEvent, aVar)
{
gVariablesView = aVar._variablesView;
ok(gVariablesView, "variables view object");
findVariableViewProperties(aVar, [
{ name: "testProp2", value: "testValue2" },
{ name: "testProp", value: "testValue", dontMatch: true },
], { webconsole: gWebConsole }).then(onTestPropFound);
}
function onTestPropFound(aResults)
{
let prop = aResults[0].matchedProp;
ok(prop, "matched the |testProp2| property in the variables view");
// Check that property value updates work and that jsterm functions can be
// used.
updateVariablesViewProperty({
property: prop,
field: "value",
string: "document.title + foo2 + $('p')",
webconsole: gWebConsole,
callback: onFooObjFetchAfterUpdate,
});
}
function onFooObjFetchAfterUpdate(aEvent, aVar)
{
info("onFooObjFetchAfterUpdate");
let para = content.document.querySelector("p");
let expectedValue = content.document.title + "foo2SecondCall" + para;
findVariableViewProperties(aVar, [
{ name: "testProp2", value: expectedValue },
], { webconsole: gWebConsole }).then(onUpdatedTestPropFound);
}
function onUpdatedTestPropFound(aResults)
{
let prop = aResults[0].matchedProp;
ok(prop, "matched the updated |testProp2| property value");
// Check that testProp2 was updated.
executeSoon(() => gJSTerm.execute("fooObj.testProp2", onExecuteFooObjTestProp2));
}
function onExecuteFooObjTestProp2()
{
let para = content.document.querySelector("p");
let expected = content.document.title + "foo2SecondCall" + para;
isnot(gWebConsole.outputNode.textContent.indexOf(expected), -1,
"fooObj.testProp2 is correct");
gWebConsole = gJSTerm = gDebuggerWin = gThread = gDebuggerController =
gStackframes = gVariablesView = null;
executeSoon(finishTest);
}

Просмотреть файл

@ -0,0 +1,150 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Test that makes sure web console eval happens in the user-selected stackframe
// from the js debugger.
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-eval-in-stackframe.html";
let gWebConsole, gJSTerm, gDebuggerWin, gThread, gDebuggerController, gStackframes;
function test()
{
addTab(TEST_URI);
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
openConsole(null, consoleOpened);
}, true);
}
function consoleOpened(hud)
{
gWebConsole = hud;
gJSTerm = hud.jsterm;
gJSTerm.execute("foo", onExecuteFoo);
}
function onExecuteFoo()
{
isnot(gWebConsole.outputNode.textContent.indexOf("globalFooBug783499"), -1,
"|foo| value is correct");
gJSTerm.clearOutput();
// Test for Bug 690529 - Web Console and Scratchpad should evaluate
// expressions in the scope of the content window, not in a sandbox.
executeSoon(() => gJSTerm.execute("foo2 = 'newFoo'; window.foo2", onNewFoo2));
}
function onNewFoo2()
{
is(gWebConsole.outputNode.textContent.indexOf("undefined"), -1,
"|undefined| is not displayed after adding |foo2|");
let msg = gWebConsole.outputNode.querySelector(".webconsole-msg-output");
ok(msg, "output result found");
isnot(msg.textContent.indexOf("newFoo"), -1,
"'newFoo' is displayed after adding |foo2|");
gJSTerm.clearOutput();
info("openDebugger");
executeSoon(() => openDebugger().then(debuggerOpened));
}
function debuggerOpened(aResult)
{
gDebuggerWin = aResult.panelWin;
gDebuggerController = gDebuggerWin.DebuggerController;
gThread = gDebuggerController.activeThread;
gStackframes = gDebuggerController.StackFrames;
info("openConsole");
executeSoon(() =>
openConsole(null, () =>
gJSTerm.execute("foo + foo2", onExecuteFooAndFoo2)
)
);
}
function onExecuteFooAndFoo2()
{
let expected = "globalFooBug783499newFoo";
isnot(gWebConsole.outputNode.textContent.indexOf(expected), -1,
"|foo + foo2| is displayed after starting the debugger");
executeSoon(() => {
gJSTerm.clearOutput();
info("openDebugger");
openDebugger().then(() => {
gThread.addOneTimeListener("framesadded", onFramesAdded);
info("firstCall()");
content.wrappedJSObject.firstCall();
});
});
}
function onFramesAdded()
{
info("onFramesAdded, openConsole() now");
executeSoon(() =>
openConsole(null, () =>
gJSTerm.execute("foo + foo2", onExecuteFooAndFoo2InSecondCall)
)
);
}
function onExecuteFooAndFoo2InSecondCall()
{
let expected = "globalFooBug783499foo2SecondCall";
isnot(gWebConsole.outputNode.textContent.indexOf(expected), -1,
"|foo + foo2| from |secondCall()|");
executeSoon(() => {
gJSTerm.clearOutput();
info("openDebugger and selectFrame(1)");
openDebugger().then(() => {
gStackframes.selectFrame(1);
info("openConsole");
executeSoon(() =>
openConsole(null, () =>
gJSTerm.execute("foo + foo2 + foo3", onExecuteFoo23InFirstCall)
)
);
});
});
}
function onExecuteFoo23InFirstCall()
{
let expected = "fooFirstCallnewFoofoo3FirstCall";
isnot(gWebConsole.outputNode.textContent.indexOf(expected), -1,
"|foo + foo2 + foo3| from |firstCall()|");
executeSoon(() =>
gJSTerm.execute("foo = 'abba'; foo3 = 'bug783499'; foo + foo3",
onExecuteFooAndFoo3ChangesInFirstCall));
}
function onExecuteFooAndFoo3ChangesInFirstCall()
{
let expected = "abbabug783499";
isnot(gWebConsole.outputNode.textContent.indexOf(expected), -1,
"|foo + foo3| updated in |firstCall()|");
is(content.wrappedJSObject.foo, "globalFooBug783499", "|foo| in content window");
is(content.wrappedJSObject.foo2, "newFoo", "|foo2| in content window");
ok(!content.wrappedJSObject.foo3, "|foo3| was not added to the content window");
gWebConsole = gJSTerm = gDebuggerWin = gThread = gDebuggerController =
gStackframes = null;
executeSoon(finishTest);
}

Просмотреть файл

@ -21,13 +21,14 @@ function performTest(hud)
{ {
hud.jsterm.clearOutput(true); hud.jsterm.clearOutput(true);
content.console.log("fooBug773466a"); hud.jsterm.execute("console.log('fooBug773466a')");
content.console.dir(function funBug773466(){}); hud.jsterm.execute("myObj = Object.create(null)");
hud.jsterm.execute("console.dir(myObj)");
waitForSuccess({ waitForSuccess({
name: "eval results are shown", name: "eval results are shown",
validatorFn: function() validatorFn: function()
{ {
return hud.outputNode.textContent.indexOf("funBug773466") > -1; return hud.outputNode.querySelector(".webconsole-msg-inspector");
}, },
successFn: function() successFn: function()
{ {

Просмотреть файл

@ -37,8 +37,8 @@ function performTest(hud)
let msg = hud.outputNode.querySelector(".webconsole-msg-output"); let msg = hud.outputNode.querySelector(".webconsole-msg-output");
ok(msg, "eval output node found"); ok(msg, "eval output node found");
isnot(msg.textContent.indexOf("HTMLDivElement"), -1, is(msg.textContent.indexOf("HTMLDivElement"), -1,
"HTMLDivElement string found"); "HTMLDivElement string not displayed");
EventUtils.synthesizeMouseAtCenter(msg, {type: "mousemove"}); EventUtils.synthesizeMouseAtCenter(msg, {type: "mousemove"});
ok(!gBrowser._bug772506, "no content variable"); ok(!gBrowser._bug772506, "no content variable");

Просмотреть файл

@ -49,6 +49,8 @@ function tabLoad2(aEvent) {
function networkPanelShown(aEvent) { function networkPanelShown(aEvent) {
document.removeEventListener(aEvent.type, networkPanelShown, false); document.removeEventListener(aEvent.type, networkPanelShown, false);
info("networkPanelShown");
document.addEventListener("popupshown", networkPanelShowFailure, false); document.addEventListener("popupshown", networkPanelShowFailure, false);
// The network panel should not open for the second time. // The network panel should not open for the second time.
@ -70,6 +72,8 @@ function networkPanelShowFailure(aEvent) {
function networkPanelHidden(aEvent) { function networkPanelHidden(aEvent) {
this.removeEventListener(aEvent.type, networkPanelHidden, false); this.removeEventListener(aEvent.type, networkPanelHidden, false);
info("networkPanelHidden");
// The network panel should not show because this is a mouse event that starts // The network panel should not show because this is a mouse event that starts
// in a position and ends in another. // in a position and ends in another.
EventUtils.sendMouseEvent({type: "mousedown", clientX: 3, clientY: 4}, EventUtils.sendMouseEvent({type: "mousedown", clientX: 3, clientY: 4},
@ -94,73 +98,25 @@ function networkPanelHidden(aEvent) {
// Done with the network output. Now test the jsterm output and the property // Done with the network output. Now test the jsterm output and the property
// panel. // panel.
HUD.jsterm.setInputValue("document"); HUD.jsterm.execute("document", () => {
HUD.jsterm.execute(); info("jsterm execute 'document' callback");
waitForSuccess({ HUD.jsterm.once("variablesview-open", onVariablesViewOpen);
name: "jsterm output message", let outputItem = outputNode
validatorFn: function() .querySelector(".webconsole-msg-output .hud-clickable");
{ ok(outputItem, "jsterm output message found");
return outputNode.querySelector(".webconsole-msg-output .hud-clickable");
},
successFn: function()
{
document.addEventListener("popupshown", propertyPanelShown, false);
// Send the mousedown and click events such that the property panel opens. // Send the mousedown and click events such that the property panel opens.
EventUtils.sendMouseEvent({type: "mousedown"}, outputItem); EventUtils.sendMouseEvent({type: "mousedown"}, outputItem);
EventUtils.sendMouseEvent({type: "click"}, outputItem); EventUtils.sendMouseEvent({type: "click"}, outputItem);
},
failureFn: finishTest,
}); });
}); });
} }
function propertyPanelShown(aEvent) { function onVariablesViewOpen() {
document.removeEventListener(aEvent.type, propertyPanelShown, false); info("onVariablesViewOpen");
document.addEventListener("popupshown", propertyPanelShowFailure, false);
// The property panel should not open for the second time.
EventUtils.sendMouseEvent({type: "mousedown"}, outputItem);
EventUtils.sendMouseEvent({type: "click"}, outputItem);
executeSoon(function() { executeSoon(function() {
aEvent.target.addEventListener("popuphidden", propertyPanelHidden, false);
aEvent.target.hidePopup();
});
}
function propertyPanelShowFailure(aEvent) {
document.removeEventListener(aEvent.type, propertyPanelShowFailure, false);
ok(false, "the property panel should not show");
}
function propertyPanelHidden(aEvent) {
this.removeEventListener(aEvent.type, propertyPanelHidden, false);
// The property panel should not show because this is a mouse event that
// starts in a position and ends in another.
EventUtils.sendMouseEvent({type: "mousedown", clientX: 3, clientY: 4},
outputItem);
EventUtils.sendMouseEvent({type: "click", clientX: 5, clientY: 6},
outputItem);
// The property panel should not show because this is a middle-click.
EventUtils.sendMouseEvent({type: "mousedown", button: 1},
outputItem);
EventUtils.sendMouseEvent({type: "click", button: 1},
outputItem);
// The property panel should not show because this is a right-click.
EventUtils.sendMouseEvent({type: "mousedown", button: 2},
outputItem);
EventUtils.sendMouseEvent({type: "click", button: 2},
outputItem);
executeSoon(function() {
document.removeEventListener("popupshown", propertyPanelShowFailure, false);
HUD = outputItem = null; HUD = outputItem = null;
executeSoon(finishTest); executeSoon(finishTest);
}); });

Просмотреть файл

@ -19,6 +19,8 @@ let openTabs = [];
let loadedTabCount = 0; let loadedTabCount = 0;
function test() { function test() {
requestLongerTimeout(2);
// Add two tabs in the main window. // Add two tabs in the main window.
addTabs(win1); addTabs(win1);
@ -64,7 +66,7 @@ function openConsoles() {
} }
waitForSuccess({ waitForSuccess({
timeout: 10000, timeout: 15000,
name: "4 web consoles opened", name: "4 web consoles opened",
validatorFn: function() validatorFn: function()
{ {

Просмотреть файл

@ -24,11 +24,11 @@ let initialString = longString.substring(0,
let inputValues = [ let inputValues = [
// [showsPropertyPanel?, input value, expected output format, // [showsPropertyPanel?, input value, expected output format,
// print() output, console output, optional console API test] // print() output, console API output, optional console API test]
// 0 // 0
[false, "'hello \\nfrom \\rthe \\\"string world!'", [false, "'hello \\nfrom \\rthe \\\"string world!'",
'"hello \\nfrom \\rthe \\"string world!"', '"hello \nfrom \rthe "string world!"',
"hello \nfrom \rthe \"string world!"], "hello \nfrom \rthe \"string world!"],
// 1 // 1
@ -52,7 +52,7 @@ let inputValues = [
[false, "'42'", '"42"', "42"], [false, "'42'", '"42"', "42"],
// 7 // 7
[false, "/foobar/", "/foobar/"], [true, "/foobar/", "[object RegExp]", '"/foobar/"', "[object RegExp]"],
// 8 // 8
[false, "null", "null"], [false, "null", "null"],
@ -64,36 +64,30 @@ let inputValues = [
[false, "true", "true"], [false, "true", "true"],
// 11 // 11
[false, "document.getElementById", "function getElementById() {\n [native code]\n}", [true, "document.getElementById", "[object Function]",
"function getElementById() {\n [native code]\n}", "function getElementById() {\n [native code]\n}",
"function getElementById() {\n [native code]\n}", "[object Function]"],
"document.wrappedJSObject.getElementById"],
// 12 // 12
[false, "(function() { return 42; })", "function () { return 42; }", [true, "(function() { return 42; })", "[object Function]",
"function () { return 42; }", "function () { return 42; }", "[object Function]"],
"(function () { return 42; })"],
// 13 // 13
[false, "new Date(" + dateNow + ")", (new Date(dateNow)).toString()], [true, "new Date(" + dateNow + ")", "[object Date]", (new Date(dateNow)).toString(), "[object Date]"],
// 14 // 14
[true, "document.body", "[object HTMLBodyElement", "[object HTMLBodyElement", [true, "document.body", "[object HTMLBodyElement]"],
"[object HTMLBodyElement",
"document.wrappedJSObject.body"],
// 15 // 15
[true, "window.location", TEST_URI], [true, "window.location", "[object Location]", TEST_URI, "[object Location]"],
// 16 // 16
[true, "[1,2,3,'a','b','c','4','5']", '[1, 2, 3, "a", "b", "c", "4", "5"]', [true, "[1,2,3,'a','b','c','4','5']", '[object Array]',
'1,2,3,a,b,c,4,5', '1,2,3,a,b,c,4,5',
'[1, 2, 3, "a", "b", "c", "4", "5"]'], "[object Array]"],
// 17 // 17
[true, "({a:'b', c:'d', e:1, f:'2'})", '({a:"b", c:"d", e:1, f:"2"})', [true, "({a:'b', c:'d', e:1, f:'2'})", "[object Object]"],
"[object Object",
'({a:"b", c:"d", e:1, f:"2"})'],
// 18 // 18
[false, "'" + longString + "'", [false, "'" + longString + "'",
@ -154,10 +148,7 @@ function testGen() {
// Test the console.log() output. // Test the console.log() output.
// Ugly but it does the job. HUD.jsterm.execute("console.log(" + consoleTest + ")");
with (content) {
eval("content.console.log(" + consoleTest + ")");
}
waitForSuccess({ waitForSuccess({
name: "console.log message for test #" + cpos, name: "console.log message for test #" + cpos,
@ -233,28 +224,28 @@ function testGen() {
// Test click on output. // Test click on output.
let eventHandlerID = eventHandlers.length + 1; let eventHandlerID = eventHandlers.length + 1;
let propertyPanelShown = function(aEvent) { let propertyPanelShown = function(aEvent, aView, aOptions) {
let label = aEvent.target.getAttribute("label"); if (aOptions.label.indexOf(expectedOutput) == -1) {
if (!label || label.indexOf(inputValue) == -1) {
return; return;
} }
document.removeEventListener(aEvent.type, propertyPanelShown, false); HUD.jsterm.off("variablesview-open", propertyPanelShown);
eventHandlers[eventHandlerID] = null; eventHandlers[eventHandlerID] = null;
ok(showsPropertyPanel, ok(showsPropertyPanel,
"the property panel shown for inputValues[" + cpos + "]"); "the property panel shown for inputValues[" + cpos + "]");
aEvent.target.hidePopup(); HUD.jsterm._splitter.state = "collapsed";
popupShown[cpos] = true; popupShown[cpos] = true;
if (showsPropertyPanel) { if (showsPropertyPanel) {
subtestNext(); executeSoon(subtestNext);
} }
}; };
document.addEventListener("popupshown", propertyPanelShown, false); HUD.jsterm.on("variablesview-open", propertyPanelShown);
eventHandlers.push(propertyPanelShown); eventHandlers.push(propertyPanelShown);
@ -281,7 +272,7 @@ function testEnd() {
for (let i = 0; i < eventHandlers.length; i++) { for (let i = 0; i < eventHandlers.length; i++) {
if (eventHandlers[i]) { if (eventHandlers[i]) {
document.removeEventListener("popupshown", eventHandlers[i], false); HUD.jsterm.off("variablesview-open", eventHandlers[i]);
} }
} }
@ -296,6 +287,7 @@ function testEnd() {
} }
function test() { function test() {
requestLongerTimeout(2);
addTab(TEST_URI); addTab(TEST_URI);
browser.addEventListener("load", tabLoad, true); browser.addEventListener("load", tabLoad, true);
} }

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше