зеркало из https://github.com/mozilla/gecko-dev.git
Merge MC -> JM
This commit is contained in:
Коммит
3d85556f58
|
@ -13,11 +13,11 @@ ID
|
|||
.*.sw[a-z]
|
||||
|
||||
# User files that may appear at the root
|
||||
.mozconfig
|
||||
mozconfig
|
||||
configure
|
||||
config.cache
|
||||
config.log
|
||||
/.mozconfig*
|
||||
/mozconfig
|
||||
/configure
|
||||
/config.cache
|
||||
/config.log
|
||||
|
||||
# Empty marker file that's generated when we check out NSS
|
||||
security/manager/.nss.checkout
|
||||
|
|
|
@ -166,7 +166,11 @@ SYMBOL_INDEX_NAME = \
|
|||
buildsymbols:
|
||||
ifdef MOZ_CRASHREPORTER
|
||||
ifdef USE_ELF_HACK
|
||||
$(MAKE) -C $(MOZ_BUILD_APP)/installer elfhack
|
||||
ifeq (mobile,$(MOZ_BUILD_APP))
|
||||
$(MAKE) -C mobile/xul/installer elfhack
|
||||
else
|
||||
$(MAKE) -C $(MOZ_BUILD_APP)/installer elfhack
|
||||
endif
|
||||
endif
|
||||
echo building symbol store
|
||||
$(RM) -r $(DIST)/crashreporter-symbols
|
||||
|
|
|
@ -50,6 +50,12 @@ AccGroupInfo::AccGroupInfo(nsAccessible* aItem, PRUint32 aRole) :
|
|||
return;
|
||||
|
||||
PRInt32 indexInParent = aItem->IndexInParent();
|
||||
PRInt32 siblingCount = parent->GetChildCount();
|
||||
if (siblingCount < indexInParent) {
|
||||
NS_ERROR("Wrong index in parent! Tree invalidation problem.");
|
||||
return;
|
||||
}
|
||||
|
||||
PRInt32 level = nsAccUtils::GetARIAOrDefaultLevel(aItem);
|
||||
|
||||
// Compute position in set.
|
||||
|
@ -95,7 +101,6 @@ AccGroupInfo::AccGroupInfo(nsAccessible* aItem, PRUint32 aRole) :
|
|||
// Compute set size.
|
||||
mSetSize = mPosInSet;
|
||||
|
||||
PRInt32 siblingCount = parent->GetChildCount();
|
||||
for (PRInt32 idx = indexInParent + 1; idx < siblingCount; idx++) {
|
||||
nsAccessible* sibling = parent->GetChildAt(idx);
|
||||
|
||||
|
|
|
@ -440,7 +440,8 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
|
|||
eNoValue,
|
||||
eNoAction,
|
||||
eNoLiveAttr,
|
||||
kNoReqStates
|
||||
kNoReqStates,
|
||||
eARIAOrientation
|
||||
},
|
||||
{
|
||||
"slider",
|
||||
|
@ -450,6 +451,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
|
|||
eNoAction,
|
||||
eNoLiveAttr,
|
||||
kNoReqStates,
|
||||
eARIAOrientation,
|
||||
eARIAReadonly
|
||||
},
|
||||
{
|
||||
|
@ -649,8 +651,8 @@ nsStateMapEntry nsARIAMap::gWAIStateMap[] = {
|
|||
|
||||
// eARIAOrientation
|
||||
nsStateMapEntry(&nsGkAtoms::aria_orientation, eUseFirstState,
|
||||
"vertical", states::VERTICAL,
|
||||
"horizontal", states::HORIZONTAL),
|
||||
"horizontal", states::HORIZONTAL,
|
||||
"vertical", states::VERTICAL),
|
||||
|
||||
// eARIAPressed
|
||||
nsStateMapEntry(&nsGkAtoms::aria_pressed, kMixedType,
|
||||
|
|
|
@ -191,7 +191,7 @@ __try {
|
|||
if (aTargetIndex < 0 || aTargetIndex >= mTargets.Length() || !aTarget)
|
||||
return E_INVALIDARG;
|
||||
|
||||
mTargets[aTargetIndex]->QueryInterface((const nsID&) IID_IUnknown, (void**) aTarget);
|
||||
mTargets[aTargetIndex]->QueryNativeInterface(IID_IUnknown, (void**) aTarget);
|
||||
return S_OK;
|
||||
|
||||
} __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
|
||||
|
|
|
@ -289,15 +289,12 @@ __try {
|
|||
nsresult rv = xpAccessible->GetName(name);
|
||||
if (NS_FAILED(rv))
|
||||
return GetHRESULT(rv);
|
||||
|
||||
if (name.IsVoid()) {
|
||||
// Valid return value for the name:
|
||||
// The name was not provided, e.g. no alt attribute for an image.
|
||||
// A screen reader may choose to invent its own accessible name, e.g. from
|
||||
// an image src attribute.
|
||||
// See nsHTMLImageAccessible::GetName()
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// The name was not provided, e.g. no alt attribute for an image. A screen
|
||||
// reader may choose to invent its own accessible name, e.g. from an image src
|
||||
// attribute. Refer to NS_OK_EMPTY_NAME return value.
|
||||
if (name.IsVoid())
|
||||
return S_FALSE;
|
||||
|
||||
*pszName = ::SysAllocStringLen(name.get(), name.Length());
|
||||
if (!*pszName)
|
||||
|
|
|
@ -124,10 +124,16 @@
|
|||
testStates("aria_main_anchor", STATE_SELECTABLE);
|
||||
testStates("aria_navigation_anchor", STATE_SELECTABLE);
|
||||
|
||||
// scrollbar
|
||||
testStates("aria_scrollbar", 0, EXT_STATE_VERTICAL);
|
||||
// aria-orientation (applied to scrollbar, separator, slider)
|
||||
testStates("aria_scrollbar", 0, EXT_STATE_HORIZONTAL);
|
||||
testStates("aria_hscrollbar", 0, EXT_STATE_HORIZONTAL);
|
||||
testStates("aria_vscrollbar", 0, EXT_STATE_VERTICAL);
|
||||
testStates("aria_separator", 0, EXT_STATE_HORIZONTAL);
|
||||
testStates("aria_hseparator", 0, EXT_STATE_HORIZONTAL);
|
||||
testStates("aria_vseparator", 0, EXT_STATE_VERTICAL);
|
||||
testStates("aria_slider", 0, EXT_STATE_HORIZONTAL);
|
||||
testStates("aria_hslider", 0, EXT_STATE_HORIZONTAL);
|
||||
testStates("aria_vslider", 0, EXT_STATE_VERTICAL);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
@ -160,6 +166,11 @@
|
|||
title="aria-autocomplete not supported on standard form text input controls">
|
||||
Mozilla Bug 681674
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=681674"
|
||||
title="aria-orientation should be applied to separator and slider roles">
|
||||
Mozilla Bug 681674
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
|
@ -227,9 +238,15 @@
|
|||
<a id="aria_main_anchor" role="main" name="main_anchor">main</a>
|
||||
<a id="aria_navigation_anchor" role="navigation" name="nav_anchor">nav</a>
|
||||
|
||||
<!-- scrollbar -->
|
||||
<div id="aria_scrollbar" role="scrollbar">scrollbar</a>
|
||||
<div id="aria_hscrollbar" role="scrollbar" aria-orientation="horizontal">horizontal scrollbar</a>
|
||||
<div id="aria_vscrollbar" role="scrollbar" aria-orientation="vertical">vertical scrollbar</a>
|
||||
<!-- aria-orientation -->
|
||||
<div id="aria_scrollbar" role="scrollbar">scrollbar</div>
|
||||
<div id="aria_hscrollbar" role="scrollbar" aria-orientation="horizontal">horizontal scrollbar</div>
|
||||
<div id="aria_vscrollbar" role="scrollbar" aria-orientation="vertical">vertical scrollbar</div>
|
||||
<div id="aria_separator" role="separator">separator</div>
|
||||
<div id="aria_hseparator" role="separator" aria-orientation="horizontal">horizontal separator</div>
|
||||
<div id="aria_vseparator" role="separator" aria-orientation="vertical">vertical separator</div>
|
||||
<div id="aria_slider" role="slider">slider</div>
|
||||
<div id="aria_hslider" role="slider" aria-orientation="horizontal">horizontal slider</div>
|
||||
<div id="aria_vslider" role="slider" aria-orientation="vertical">vertical slider</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -54,14 +54,17 @@ pref("browser.hiddenWindowChromeURL", "chrome://browser/content/hiddenWindow.xul
|
|||
// Enables some extra Extension System Logging (can reduce performance)
|
||||
pref("extensions.logging.enabled", false);
|
||||
|
||||
// Enables strict compatibility. To be toggled in bug 698653, to make addons
|
||||
// compatibile by default.
|
||||
pref("extensions.strictCompatibility", true);
|
||||
// Disables strict compatibility, making addons compatible-by-default.
|
||||
pref("extensions.strictCompatibility", false);
|
||||
|
||||
// Specifies a minimum maxVersion an addon needs to say it's compatible with
|
||||
// for it to be compatible by default.
|
||||
pref("extensions.minCompatibleAppVersion", "4.0");
|
||||
|
||||
// Preferences for AMO integration
|
||||
pref("extensions.getAddons.cache.enabled", true);
|
||||
pref("extensions.getAddons.maxResults", 15);
|
||||
pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/guid:%IDS%?src=firefox&appOS=%OS%&appVersion=%VERSION%&tMain=%TIME_MAIN%&tFirstPaint=%TIME_FIRST_PAINT%&tSessionRestored=%TIME_SESSION_RESTORED%");
|
||||
pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/guid:%IDS%?src=firefox&appOS=%OS%&appVersion=%VERSION%");
|
||||
pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/firefox/search?q=%TERMS%");
|
||||
pref("extensions.getAddons.search.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/%TERMS%/all/%MAX_RESULTS%/%OS%/%VERSION%?src=firefox");
|
||||
pref("extensions.webservice.discoverURL", "https://services.addons.mozilla.org/%LOCALE%/firefox/discovery/pane/%VERSION%/%OS%");
|
||||
|
@ -1015,6 +1018,9 @@ pref("devtools.ruleview.enabled", true);
|
|||
// Enable the Scratchpad tool.
|
||||
pref("devtools.scratchpad.enabled", true);
|
||||
|
||||
// Enable the Style Editor.
|
||||
pref("devtools.styleeditor.enabled", true);
|
||||
|
||||
// Enable tools for Chrome development.
|
||||
pref("devtools.chrome.enabled", false);
|
||||
|
||||
|
|
|
@ -137,10 +137,9 @@ let RemoteTabViewer = {
|
|||
, title: title
|
||||
, hiddenRows: [ "description"
|
||||
, "location"
|
||||
, "folderPicker"
|
||||
, "loadInSidebar"
|
||||
, "keyword" ]
|
||||
});
|
||||
}, window.top);
|
||||
},
|
||||
|
||||
bookmarkSelectedTabs: function() {
|
||||
|
@ -160,7 +159,7 @@ let RemoteTabViewer = {
|
|||
, type: "folder"
|
||||
, URIList: URIs
|
||||
, hiddenRows: [ "description" ]
|
||||
});
|
||||
}, window.top);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -192,6 +192,11 @@
|
|||
label="&scratchpad.label;"
|
||||
key="key_scratchpad"
|
||||
command="Tools:Scratchpad"/>
|
||||
<menuitem id="appmenu_styleeditor"
|
||||
hidden="true"
|
||||
label="&styleeditor.label;"
|
||||
key="key_styleeditor"
|
||||
command="Tools:StyleEditor"/>
|
||||
<menuitem id="appmenu_pageSource"
|
||||
label="&viewPageSourceCmd.label;"
|
||||
command="View:PageSource"
|
||||
|
|
|
@ -550,6 +550,12 @@
|
|||
accesskey="&scratchpad.accesskey;"
|
||||
key="key_scratchpad"
|
||||
command="Tools:Scratchpad"/>
|
||||
<menuitem id="menu_styleeditor"
|
||||
hidden="true"
|
||||
label="&styleeditor.label;"
|
||||
accesskey="&styleeditor.accesskey;"
|
||||
key="key_styleeditor"
|
||||
command="Tools:StyleEditor"/>
|
||||
<menuitem id="menu_pageSource"
|
||||
accesskey="&pageSourceCmd.accesskey;"
|
||||
label="&pageSourceCmd.label;"
|
||||
|
|
|
@ -386,15 +386,14 @@ var PlacesCommandHook = {
|
|||
, hiddenRows: [ "description"
|
||||
, "location"
|
||||
, "loadInSidebar"
|
||||
, "folderPicker"
|
||||
, "keyword" ]
|
||||
});
|
||||
}, window);
|
||||
}
|
||||
else {
|
||||
PlacesUIUtils.showBookmarkDialog({ action: "edit"
|
||||
, type: "bookmark"
|
||||
, itemId: itemId
|
||||
});
|
||||
}, window);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -427,7 +426,7 @@ var PlacesCommandHook = {
|
|||
, type: "folder"
|
||||
, URIList: pages
|
||||
, hiddenRows: [ "description" ]
|
||||
});
|
||||
}, window);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -478,7 +477,7 @@ var PlacesCommandHook = {
|
|||
, hiddenRows: [ "feedLocation"
|
||||
, "siteLocation"
|
||||
, "description" ]
|
||||
});
|
||||
}, window);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -126,6 +126,7 @@
|
|||
<command id="Tools:Downloads" oncommand="BrowserDownloadsUI();"/>
|
||||
<command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();" disabled="true"/>
|
||||
<command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();" disabled="true"/>
|
||||
<command id="Tools:StyleEditor" oncommand="StyleEditor.openChrome();" disabled="true"/>
|
||||
<command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
|
||||
<command id="Tools:Sanitize"
|
||||
oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
|
||||
|
@ -244,6 +245,8 @@
|
|||
<key id="key_inspect" key="&inspectMenu.commandkey;" command="Tools:Inspect" modifiers="accel,shift"/>
|
||||
<key id="key_scratchpad" keycode="&scratchpad.keycode;" modifiers="shift"
|
||||
keytext="&scratchpad.keytext;" command="Tools:Scratchpad"/>
|
||||
<key id="key_styleeditor" keycode="&styleeditor.keycode;" modifiers="shift"
|
||||
keytext="&styleeditor.keytext;" command="Tools:StyleEditor"/>
|
||||
<key id="openFileKb" key="&openFileCmd.commandkey;" command="Browser:OpenFile" modifiers="accel"/>
|
||||
<key id="key_savePage" key="&savePageCmd.commandkey;" command="Browser:SavePage" modifiers="accel"/>
|
||||
<key id="printKb" key="&printCmd.commandkey;" command="cmd_print" modifiers="accel"/>
|
||||
|
|
|
@ -362,8 +362,8 @@ window[chromehidden~="toolbar"] toolbar:not(.toolbar-primary):not(.chromeclass-m
|
|||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#full-screen-warning-container[fade-warning-out] {
|
||||
|
|
|
@ -1721,6 +1721,16 @@ function delayedStartup(isLoadingBlank, mustLoadSidebar) {
|
|||
#endif
|
||||
}
|
||||
|
||||
// Enable Style Editor?
|
||||
let styleEditorEnabled = gPrefService.getBoolPref(StyleEditor.prefEnabledName);
|
||||
if (styleEditorEnabled) {
|
||||
document.getElementById("menu_styleeditor").hidden = false;
|
||||
document.getElementById("Tools:StyleEditor").removeAttribute("disabled");
|
||||
#ifdef MENUBAR_CAN_AUTOHIDE
|
||||
document.getElementById("appmenu_styleeditor").hidden = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MENUBAR_CAN_AUTOHIDE
|
||||
// If the user (or the locale) hasn't enabled the top-level "Character
|
||||
// Encoding" menu via the "browser.menu.showCharacterEncoding" preference,
|
||||
|
@ -3163,9 +3173,8 @@ var bookmarksButtonObserver = {
|
|||
, hiddenRows: [ "description"
|
||||
, "location"
|
||||
, "loadInSidebar"
|
||||
, "folderPicker"
|
||||
, "keyword" ]
|
||||
});
|
||||
}, window);
|
||||
} catch(ex) { }
|
||||
},
|
||||
|
||||
|
@ -5792,9 +5801,8 @@ function contentAreaClick(event, isPanelClick)
|
|||
, loadBookmarkInSidebar: true
|
||||
, hiddenRows: [ "description"
|
||||
, "location"
|
||||
, "folderPicker"
|
||||
, "keyword" ]
|
||||
});
|
||||
}, window);
|
||||
event.preventDefault();
|
||||
return true;
|
||||
}
|
||||
|
@ -6839,9 +6847,10 @@ function AddKeywordForSearchField() {
|
|||
, postData: postData
|
||||
, charSet: charset
|
||||
, hiddenRows: [ "location"
|
||||
, "loadInSidebar"
|
||||
, "folderPicker" ]
|
||||
});
|
||||
, "description"
|
||||
, "tags"
|
||||
, "loadInSidebar" ]
|
||||
}, window);
|
||||
}
|
||||
|
||||
function SwitchDocumentDirection(aWindow) {
|
||||
|
@ -8970,6 +8979,34 @@ XPCOMUtils.defineLazyGetter(Scratchpad, "ScratchpadManager", function() {
|
|||
return tmp.ScratchpadManager;
|
||||
});
|
||||
|
||||
var StyleEditor = {
|
||||
prefEnabledName: "devtools.styleeditor.enabled",
|
||||
openChrome: function SE_openChrome()
|
||||
{
|
||||
const CHROME_URL = "chrome://browser/content/styleeditor.xul";
|
||||
const CHROME_WINDOW_TYPE = "Tools:StyleEditor";
|
||||
const CHROME_WINDOW_FLAGS = "chrome,centerscreen,resizable,dialog=no";
|
||||
|
||||
// focus currently open Style Editor window for this document, if any
|
||||
let contentWindow = gBrowser.selectedBrowser.contentWindow;
|
||||
let contentWindowID = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
|
||||
let enumerator = Services.wm.getEnumerator(CHROME_WINDOW_TYPE);
|
||||
while (enumerator.hasMoreElements()) {
|
||||
var win = enumerator.getNext();
|
||||
if (win.styleEditorChrome.contentWindowID == contentWindowID) {
|
||||
win.focus();
|
||||
return win;
|
||||
}
|
||||
}
|
||||
|
||||
let chromeWindow = Services.ww.openWindow(null, CHROME_URL, "_blank",
|
||||
CHROME_WINDOW_FLAGS,
|
||||
contentWindow);
|
||||
chromeWindow.focus();
|
||||
return chromeWindow;
|
||||
}
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyGetter(window, "gShowPageResizers", function () {
|
||||
#ifdef XP_WIN
|
||||
|
|
|
@ -976,7 +976,7 @@
|
|||
</hbox>
|
||||
|
||||
<hbox id="full-screen-warning-container" hidden="true" fadeout="true">
|
||||
<hbox style="min-width: 100%;" pack="center"> <!-- Inner hbox needed due to bug 579776. -->
|
||||
<hbox style="width: 100%;" pack="center"> <!-- Inner hbox needed due to bug 579776. -->
|
||||
<hbox id="full-screen-warning-message">
|
||||
<description id="full-screen-warning-text" value="&domFullScreenWarning.label;"></description>
|
||||
</hbox>
|
||||
|
|
|
@ -36,9 +36,9 @@
|
|||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
let Cc = Components.classes;
|
||||
let Ci = Components.interfaces;
|
||||
let Cu = Components.utils;
|
||||
|
||||
// Bug 671101 - directly using webNavigation in this context
|
||||
// causes docshells to leak
|
||||
|
|
|
@ -1406,15 +1406,14 @@ nsContextMenu.prototype = {
|
|||
, hiddenRows: [ "description"
|
||||
, "location"
|
||||
, "loadInSidebar"
|
||||
, "folderPicker"
|
||||
, "keyword" ]
|
||||
});
|
||||
}, window.top);
|
||||
}
|
||||
else {
|
||||
PlacesUIUtils.showBookmarkDialog({ action: "edit"
|
||||
, type: "bookmark"
|
||||
, itemId: itemId
|
||||
});
|
||||
}, window.top);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
function test() {
|
||||
waitForExplicitFinish();
|
||||
ignoreAllUncaughtExceptions();
|
||||
|
||||
// test the main (normal) browser window
|
||||
testCustomize(window, testChromeless);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
function test() {
|
||||
waitForExplicitFinish();
|
||||
ignoreAllUncaughtExceptions();
|
||||
|
||||
testCustomize(window, finish);
|
||||
}
|
||||
|
|
|
@ -264,8 +264,6 @@ var BookmarkPropertiesPanel = {
|
|||
NS_ASSERT("itemId" in dialogInfo);
|
||||
this._itemId = dialogInfo.itemId;
|
||||
this._title = PlacesUtils.bookmarks.getItemTitle(this._itemId);
|
||||
// Don't show folderPicker when editing
|
||||
this._hiddenRows.push("folderPicker");
|
||||
this._readOnly = !!dialogInfo.readOnly;
|
||||
|
||||
switch (dialogInfo.type) {
|
||||
|
|
|
@ -312,7 +312,7 @@ PlacesController.prototype = {
|
|||
, "loadInSidebar" ]
|
||||
, uri: NetUtil.newURI(node.uri)
|
||||
, title: node.title
|
||||
}, window.top, true);
|
||||
}, window.top);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
@ -720,6 +720,7 @@ PlacesController.prototype = {
|
|||
, type: itemType
|
||||
, itemId: itemId
|
||||
, readOnly: isRootItem
|
||||
, hiddenRows: [ "folderPicker" ]
|
||||
}, window.top);
|
||||
},
|
||||
|
||||
|
@ -768,7 +769,7 @@ PlacesController.prototype = {
|
|||
, type: aType
|
||||
, defaultInsertionPoint: ip
|
||||
, hiddenRows: [ "folderPicker" ]
|
||||
}, window);
|
||||
}, window.top);
|
||||
if (performed) {
|
||||
// Select the new item.
|
||||
let insertedNodeId = PlacesUtils.bookmarks
|
||||
|
|
|
@ -340,29 +340,36 @@ var PlacesUIUtils = {
|
|||
* See documentation at the top of bookmarkProperties.js
|
||||
* @param aWindow
|
||||
* Owner window for the new dialog.
|
||||
* @param aMinimalUI [optional]
|
||||
* Whether to open the dialog in "minimal ui" mode. Do not pass this
|
||||
* for new callers. It'll be removed in a future release.
|
||||
* @param aResizable [optional]
|
||||
* Whether the dialog is allowed to resize. Do not pass this for new
|
||||
* callers since it's deprecated. It'll be removed in future releases.
|
||||
*
|
||||
* @see documentation at the top of bookmarkProperties.js
|
||||
* @return true if any transaction has been performed, false otherwise.
|
||||
*/
|
||||
showBookmarkDialog:
|
||||
function PUIU_showBookmarkDialog(aInfo, aParentWindow, aMinimalUI) {
|
||||
function PUIU_showBookmarkDialog(aInfo, aParentWindow, aResizable) {
|
||||
// This is a compatibility shim for add-ons. It will warn in the Error
|
||||
// Console when used.
|
||||
if (!aParentWindow) {
|
||||
aParentWindow = this._getWindow(null);
|
||||
}
|
||||
|
||||
// Preserve size attributes differently based on the fact the dialog has
|
||||
// a folder picker or not.
|
||||
let minimalUI = "hiddenRows" in aInfo &&
|
||||
aInfo.hiddenRows.indexOf("folderPicker") != -1;
|
||||
let dialogURL = aMinimalUI ?
|
||||
// a folder picker or not. If the picker is visible, the dialog should
|
||||
// be resizable since it may not show enough content for the folders
|
||||
// hierarchy.
|
||||
let hasFolderPicker = !("hiddenRows" in aInfo) ||
|
||||
aInfo.hiddenRows.indexOf("folderPicker") == -1;
|
||||
let resizable = aResizable !== undefined ? aResizable : hasFolderPicker;
|
||||
// Use a different chrome url, since this allows to persist different sizes,
|
||||
// based on resizability of the dialog.
|
||||
let dialogURL = resizable ?
|
||||
"chrome://browser/content/places/bookmarkProperties2.xul" :
|
||||
"chrome://browser/content/places/bookmarkProperties.xul";
|
||||
|
||||
let features =
|
||||
"centerscreen,chrome,modal,resizable=" + (aMinimalUI ? "yes" : "no");
|
||||
"centerscreen,chrome,modal,resizable=" + (resizable ? "yes" : "no");
|
||||
|
||||
aParentWindow.openDialog(dialogURL, "", features, aInfo);
|
||||
return ("performed" in aInfo && aInfo.performed);
|
||||
|
|
|
@ -260,10 +260,10 @@ var gTests = [
|
|||
function nextTest() {
|
||||
if (gTests.length) {
|
||||
var test = gTests.shift();
|
||||
info("Start of test: " + test.desc);
|
||||
test.run();
|
||||
|
||||
waitForFocus(nextTest);
|
||||
waitForFocus(function() {
|
||||
info("Start of test: " + test.desc);
|
||||
test.run();
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Collapse the personal toolbar if needed.
|
||||
|
@ -283,6 +283,6 @@ function test() {
|
|||
if (wasCollapsed)
|
||||
setToolbarVisibility(toolbar, true);
|
||||
|
||||
waitForFocus(nextTest);
|
||||
nextTest();
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
ignoreAllUncaughtExceptions();
|
||||
|
||||
const BOOKMARKS_SIDEBAR_ID = "viewBookmarksSidebar";
|
||||
const BOOKMARKS_SIDEBAR_TREE_ID = "bookmarks-view";
|
||||
|
|
|
@ -38,16 +38,9 @@
|
|||
|
||||
#include "nsIShellService.idl"
|
||||
|
||||
[scriptable, uuid(16e7e8da-8bef-4f41-be5f-045b2e9918e1)]
|
||||
[scriptable, uuid(89b0a761-d9a0-4c39-ab83-d81566459a31)]
|
||||
interface nsIWindowsShellService : nsIShellService
|
||||
{
|
||||
/**
|
||||
* The number of unread mail messages for the current user.
|
||||
*
|
||||
* @return The number of unread (new) mail messages for the current user.
|
||||
*/
|
||||
readonly attribute unsigned long unreadMailCount;
|
||||
|
||||
/**
|
||||
* Provides the shell service an opportunity to do some Win7+ shortcut
|
||||
* maintenance needed on initial startup of the browser.
|
||||
|
|
|
@ -803,66 +803,6 @@ nsWindowsShellService::SetDesktopBackgroundColor(PRUint32 aColor)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowsShellService::GetUnreadMailCount(PRUint32* aCount)
|
||||
{
|
||||
*aCount = 0;
|
||||
|
||||
HKEY accountKey;
|
||||
if (GetMailAccountKey(&accountKey)) {
|
||||
DWORD type, unreadCount;
|
||||
DWORD len = sizeof unreadCount;
|
||||
DWORD res = ::RegQueryValueExW(accountKey, L"MessageCount", 0,
|
||||
&type, (LPBYTE)&unreadCount, &len);
|
||||
if (REG_SUCCEEDED(res))
|
||||
*aCount = unreadCount;
|
||||
|
||||
// Close the key we opened.
|
||||
::RegCloseKey(accountKey);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsWindowsShellService::GetMailAccountKey(HKEY* aResult)
|
||||
{
|
||||
NS_NAMED_LITERAL_STRING(unread,
|
||||
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\UnreadMail\\");
|
||||
|
||||
HKEY mailKey;
|
||||
DWORD res = ::RegOpenKeyExW(HKEY_CURRENT_USER, unread.get(), 0,
|
||||
KEY_ENUMERATE_SUB_KEYS, &mailKey);
|
||||
|
||||
PRInt32 i = 0;
|
||||
do {
|
||||
PRUnichar subkeyName[MAX_BUF];
|
||||
DWORD len = sizeof subkeyName;
|
||||
res = ::RegEnumKeyExW(mailKey, i++, subkeyName, &len, NULL, NULL,
|
||||
NULL, NULL);
|
||||
if (REG_SUCCEEDED(res)) {
|
||||
HKEY accountKey;
|
||||
res = ::RegOpenKeyExW(mailKey, PromiseFlatString(subkeyName).get(),
|
||||
0, KEY_READ, &accountKey);
|
||||
if (REG_SUCCEEDED(res)) {
|
||||
*aResult = accountKey;
|
||||
|
||||
// Close the key we opened.
|
||||
::RegCloseKey(mailKey);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
while (1);
|
||||
|
||||
// Close the key we opened.
|
||||
::RegCloseKey(mailKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowsShellService::OpenApplicationWithURI(nsILocalFile* aApplication,
|
||||
const nsACString& aURI)
|
||||
|
|
|
@ -59,8 +59,6 @@ public:
|
|||
protected:
|
||||
bool IsDefaultBrowserVista(bool* aIsDefaultBrowser);
|
||||
|
||||
bool GetMailAccountKey(HKEY* aResult);
|
||||
|
||||
private:
|
||||
bool mCheckedThisSession;
|
||||
};
|
||||
|
|
|
@ -110,12 +110,11 @@ function (aTitle, aContentURL, aCustomizeURL, aPersist)
|
|||
, type: "bookmark"
|
||||
, hiddenRows: [ "description"
|
||||
, "keyword"
|
||||
, "location"
|
||||
, "loadInSidebar" ]
|
||||
, "location" ]
|
||||
, uri: uri
|
||||
, title: aTitle
|
||||
, loadBookmarkInSidebar: true
|
||||
}, win, true);
|
||||
}, win);
|
||||
}
|
||||
|
||||
nsSidebar.prototype.validateSearchEngine =
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const Cu = Components.utils;
|
||||
let Cu = Components.utils;
|
||||
|
||||
Cu.import("resource:///modules/tabview/utils.jsm");
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ DIRS = \
|
|||
highlighter \
|
||||
webconsole \
|
||||
sourceeditor \
|
||||
styleeditor \
|
||||
styleinspector \
|
||||
scratchpad \
|
||||
shared \
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
ignoreAllUncaughtExceptions();
|
||||
|
||||
let nodes = [
|
||||
{nodeId: "i1111", result: "i1 i11 i111 i1111"},
|
||||
|
|
|
@ -3,9 +3,13 @@ browser.jar:
|
|||
content/browser/NetworkPanel.xhtml (webconsole/NetworkPanel.xhtml)
|
||||
* content/browser/scratchpad.xul (scratchpad/scratchpad.xul)
|
||||
* content/browser/scratchpad.js (scratchpad/scratchpad.js)
|
||||
* content/browser/styleeditor.xul (styleeditor/styleeditor.xul)
|
||||
content/browser/splitview.css (styleeditor/splitview.css)
|
||||
content/browser/styleeditor.css (styleeditor/styleeditor.css)
|
||||
content/browser/devtools/csshtmltree.xul (styleinspector/csshtmltree.xul)
|
||||
content/browser/devtools/cssruleview.xul (styleinspector/cssruleview.xul)
|
||||
content/browser/devtools/styleinspector.css (styleinspector/styleinspector.css)
|
||||
content/browser/orion.js (sourceeditor/orion/orion.js)
|
||||
content/browser/orion.css (sourceeditor/orion/orion.css)
|
||||
content/browser/orion-mozilla.css (sourceeditor/orion/mozilla.css)
|
||||
|
||||
|
|
|
@ -156,7 +156,8 @@ var Scratchpad = {
|
|||
return {
|
||||
filename: this.filename,
|
||||
text: this.getText(),
|
||||
executionContext: this.executionContext
|
||||
executionContext: this.executionContext,
|
||||
saved: this.saved
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -172,6 +173,7 @@ var Scratchpad = {
|
|||
if (aState.filename) {
|
||||
this.setFilename(aState.filename);
|
||||
}
|
||||
this.saved = aState.saved;
|
||||
|
||||
if (aState.executionContext == SCRATCHPAD_CONTEXT_BROWSER) {
|
||||
this.setBrowserContext();
|
||||
|
@ -489,10 +491,12 @@ var Scratchpad = {
|
|||
|
||||
/**
|
||||
* Open a new Scratchpad window.
|
||||
*
|
||||
* @return nsIWindow
|
||||
*/
|
||||
openScratchpad: function SP_openScratchpad()
|
||||
{
|
||||
ScratchpadManager.openScratchpad();
|
||||
return ScratchpadManager.openScratchpad();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -591,7 +595,7 @@ var Scratchpad = {
|
|||
fp.defaultString = "";
|
||||
if (fp.show() != Ci.nsIFilePicker.returnCancel) {
|
||||
this.setFilename(fp.file.path);
|
||||
this.importFromFile(fp.file);
|
||||
this.importFromFile(fp.file, false, this.onTextSaved.bind(this));
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -606,7 +610,7 @@ var Scratchpad = {
|
|||
|
||||
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
|
||||
file.initWithPath(this.filename);
|
||||
this.exportToFile(file, true);
|
||||
this.exportToFile(file, true, false, this.onTextSaved.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -619,8 +623,8 @@ var Scratchpad = {
|
|||
Ci.nsIFilePicker.modeSave);
|
||||
fp.defaultString = "scratchpad.js";
|
||||
if (fp.show() != Ci.nsIFilePicker.returnCancel) {
|
||||
document.title = this.filename = fp.file.path;
|
||||
this.exportToFile(fp.file, true);
|
||||
this.setFilename(fp.file.path);
|
||||
this.exportToFile(fp.file, true, false, this.onTextSaved.bind(this));
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -759,6 +763,15 @@ var Scratchpad = {
|
|||
this.onContextMenu);
|
||||
this.editor.focus();
|
||||
this.editor.setCaretOffset(this.editor.getCharCount());
|
||||
|
||||
if (this.filename && !this.saved) {
|
||||
this.onTextChanged();
|
||||
}
|
||||
else if (this.filename && this.saved) {
|
||||
this.onTextSaved();
|
||||
}
|
||||
|
||||
this._triggerObservers("Ready");
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -823,6 +836,33 @@ var Scratchpad = {
|
|||
this.editor.redo();
|
||||
},
|
||||
|
||||
/**
|
||||
* This method adds a listener to the editor for text changes. Called when
|
||||
* a scratchpad is saved, opened from file, or restored from a saved file.
|
||||
*/
|
||||
onTextSaved: function SP_onTextSaved(aStatus)
|
||||
{
|
||||
if (aStatus && !Components.isSuccessCode(aStatus)) {
|
||||
return;
|
||||
}
|
||||
document.title = document.title.replace(/^\*/, "");
|
||||
this.saved = true;
|
||||
this.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
|
||||
this.onTextChanged);
|
||||
},
|
||||
|
||||
/**
|
||||
* The scratchpad handler for editor text change events. This handler
|
||||
* indicates that there are unsaved changes in the UI.
|
||||
*/
|
||||
onTextChanged: function SP_onTextChanged()
|
||||
{
|
||||
document.title = "*" + document.title;
|
||||
Scratchpad.saved = false;
|
||||
Scratchpad.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
|
||||
Scratchpad.onTextChanged);
|
||||
},
|
||||
|
||||
/**
|
||||
* The Scratchpad window unload event handler. This method unloads/destroys
|
||||
* the source editor.
|
||||
|
@ -841,6 +881,68 @@ var Scratchpad = {
|
|||
this.editor.destroy();
|
||||
this.editor = null;
|
||||
},
|
||||
|
||||
_observers: [],
|
||||
|
||||
/**
|
||||
* Add an observer for Scratchpad events.
|
||||
*
|
||||
* The observer implements IScratchpadObserver := {
|
||||
* onReady: Called when the Scratchpad and its SourceEditor are ready.
|
||||
* Arguments: (Scratchpad aScratchpad)
|
||||
* }
|
||||
*
|
||||
* All observer handlers are optional.
|
||||
*
|
||||
* @param IScratchpadObserver aObserver
|
||||
* @see removeObserver
|
||||
*/
|
||||
addObserver: function SP_addObserver(aObserver)
|
||||
{
|
||||
this._observers.push(aObserver);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove an observer for Scratchpad events.
|
||||
*
|
||||
* @param IScratchpadObserver aObserver
|
||||
* @see addObserver
|
||||
*/
|
||||
removeObserver: function SP_removeObserver(aObserver)
|
||||
{
|
||||
let index = this._observers.indexOf(aObserver);
|
||||
if (index != -1) {
|
||||
this._observers.splice(index, 1);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Trigger named handlers in Scratchpad observers.
|
||||
*
|
||||
* @param string aName
|
||||
* Name of the handler to trigger.
|
||||
* @param Array aArgs
|
||||
* Optional array of arguments to pass to the observer(s).
|
||||
* @see addObserver
|
||||
*/
|
||||
_triggerObservers: function SP_triggerObservers(aName, aArgs)
|
||||
{
|
||||
// insert this Scratchpad instance as the first argument
|
||||
if (!aArgs) {
|
||||
aArgs = [this];
|
||||
} else {
|
||||
aArgs.unshift(this);
|
||||
}
|
||||
|
||||
// trigger all observers that implement this named handler
|
||||
for (let i = 0; i < this._observers.length; ++i) {
|
||||
let observer = this._observers[i];
|
||||
let handler = observer["on" + aName];
|
||||
if (handler) {
|
||||
handler.apply(observer, aArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyGetter(Scratchpad, "strings", function () {
|
||||
|
|
|
@ -57,6 +57,8 @@ _BROWSER_TEST_FILES = \
|
|||
browser_scratchpad_restore.js \
|
||||
browser_scratchpad_bug_679467_falsy.js \
|
||||
browser_scratchpad_bug_699130_edit_ui_updates.js \
|
||||
browser_scratchpad_bug_669612_unsaved.js \
|
||||
head.js \
|
||||
|
||||
libs:: $(_BROWSER_TEST_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Reference to the Scratchpad chrome window object.
|
||||
let gScratchpadWindow;
|
||||
|
||||
let gOldPref;
|
||||
let DEVTOOLS_CHROME_ENABLED = "devtools.chrome.enabled";
|
||||
|
||||
|
@ -55,8 +52,5 @@ function runTests()
|
|||
|
||||
Services.prefs.setBoolPref(DEVTOOLS_CHROME_ENABLED, gOldPref);
|
||||
|
||||
gScratchpadWindow.close();
|
||||
gScratchpadWindow = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
|
|
@ -2,23 +2,28 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Reference to the Scratchpad chrome window object.
|
||||
let gScratchpadWindow;
|
||||
var ScratchpadManager = Scratchpad.ScratchpadManager;
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onTabLoad() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onTabLoad, true);
|
||||
|
||||
ok(window.Scratchpad, "Scratchpad variable exists");
|
||||
|
||||
Services.prefs.setIntPref("devtools.editor.tabsize", 5);
|
||||
|
||||
gScratchpadWindow = Scratchpad.openScratchpad();
|
||||
gScratchpadWindow.addEventListener("load", runTests, false);
|
||||
gScratchpadWindow.addEventListener("load", function onScratchpadLoad() {
|
||||
gScratchpadWindow.removeEventListener("load", onScratchpadLoad, false);
|
||||
|
||||
gScratchpadWindow.Scratchpad.addObserver({
|
||||
onReady: runTests
|
||||
});
|
||||
}, false);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,Scratchpad test for the Tab key, bug 660560";
|
||||
|
@ -26,11 +31,12 @@ function test()
|
|||
|
||||
function runTests()
|
||||
{
|
||||
gScratchpadWindow.removeEventListener("load", arguments.callee, false);
|
||||
|
||||
let sp = gScratchpadWindow.Scratchpad;
|
||||
ok(sp, "Scratchpad object exists in new window");
|
||||
|
||||
is(this.onReady, runTests, "the handler runs in the context of the observer");
|
||||
sp.removeObserver(this);
|
||||
|
||||
ok(sp.editor.hasFocus(), "the editor has focus");
|
||||
|
||||
sp.setText("window.foo;");
|
||||
|
@ -63,15 +69,20 @@ function runTests()
|
|||
|
||||
Services.prefs.setIntPref("devtools.editor.tabsize", 6);
|
||||
Services.prefs.setBoolPref("devtools.editor.expandtab", false);
|
||||
|
||||
gScratchpadWindow = Scratchpad.openScratchpad();
|
||||
gScratchpadWindow.addEventListener("load", runTests2, false);
|
||||
gScratchpadWindow.addEventListener("load", function onScratchpadLoad() {
|
||||
gScratchpadWindow.removeEventListener("load", onScratchpadLoad, false);
|
||||
gScratchpadWindow.Scratchpad.addObserver({
|
||||
onReady: runTests2
|
||||
});
|
||||
}, false);
|
||||
}
|
||||
|
||||
function runTests2()
|
||||
{
|
||||
gScratchpadWindow.removeEventListener("load", arguments.callee, false);
|
||||
|
||||
let sp = gScratchpadWindow.Scratchpad;
|
||||
sp.removeObserver(this);
|
||||
|
||||
sp.setText("window.foo;");
|
||||
sp.editor.setCaretOffset(0);
|
||||
|
@ -85,9 +96,5 @@ function runTests2()
|
|||
Services.prefs.clearUserPref("devtools.editor.tabsize");
|
||||
Services.prefs.clearUserPref("devtools.editor.expandtab");
|
||||
|
||||
gScratchpadWindow.close();
|
||||
gScratchpadWindow = null;
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,170 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// only finish() when correct number of tests are done
|
||||
const expected = 5;
|
||||
var count = 0;
|
||||
function done()
|
||||
{
|
||||
if (++count == expected) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
var ScratchpadManager = Scratchpad.ScratchpadManager;
|
||||
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
testListeners();
|
||||
testErrorStatus();
|
||||
testRestoreNotFromFile();
|
||||
testRestoreFromFileSaved();
|
||||
testRestoreFromFileUnsaved();
|
||||
|
||||
content.location = "data:text/html,<p>test star* UI for unsaved file changes";
|
||||
}
|
||||
|
||||
function testListeners()
|
||||
{
|
||||
let win = ScratchpadManager.openScratchpad();
|
||||
|
||||
win.addEventListener("load", function onScratchpadLoad() {
|
||||
win.removeEventListener("load", onScratchpadLoad, false);
|
||||
|
||||
win.Scratchpad.addObserver({
|
||||
onReady: function (aScratchpad) {
|
||||
aScratchpad.removeObserver(this);
|
||||
|
||||
aScratchpad.setText("new text");
|
||||
ok(!isStar(win), "no star if scratchpad isn't from a file");
|
||||
|
||||
aScratchpad.onTextSaved();
|
||||
ok(!isStar(win), "no star before changing text");
|
||||
|
||||
aScratchpad.setText("new text2");
|
||||
ok(isStar(win), "shows star if scratchpad text changes");
|
||||
|
||||
aScratchpad.onTextSaved();
|
||||
ok(!isStar(win), "no star if scratchpad was just saved");
|
||||
|
||||
aScratchpad.undo();
|
||||
ok(isStar(win), "star if scratchpad undo");
|
||||
|
||||
win.close();
|
||||
done();
|
||||
}
|
||||
});
|
||||
}, false);
|
||||
}
|
||||
|
||||
function testErrorStatus()
|
||||
{
|
||||
let win = ScratchpadManager.openScratchpad();
|
||||
|
||||
win.addEventListener("load", function onScratchpadLoad() {
|
||||
win.removeEventListener("load", onScratchpadLoad, false);
|
||||
|
||||
win.Scratchpad.addObserver({
|
||||
onReady: function (aScratchpad) {
|
||||
aScratchpad.removeObserver(this);
|
||||
|
||||
aScratchpad.onTextSaved(Components.results.NS_ERROR_FAILURE);
|
||||
aScratchpad.setText("new text");
|
||||
ok(!isStar(win), "no star if file save failed");
|
||||
|
||||
win.close();
|
||||
done();
|
||||
}
|
||||
});
|
||||
}, false);
|
||||
}
|
||||
|
||||
|
||||
function testRestoreNotFromFile()
|
||||
{
|
||||
let session = [{
|
||||
text: "test1",
|
||||
executionContext: 1
|
||||
}];
|
||||
|
||||
let [win] = ScratchpadManager.restoreSession(session);
|
||||
win.addEventListener("load", function onScratchpadLoad() {
|
||||
win.removeEventListener("load", onScratchpadLoad, false);
|
||||
|
||||
win.Scratchpad.addObserver({
|
||||
onReady: function (aScratchpad) {
|
||||
aScratchpad.removeObserver(this);
|
||||
|
||||
aScratchpad.setText("new text");
|
||||
ok(!isStar(win), "no star if restored scratchpad isn't from a file");
|
||||
|
||||
win.close();
|
||||
done();
|
||||
}
|
||||
});
|
||||
}, false);
|
||||
}
|
||||
|
||||
function testRestoreFromFileSaved()
|
||||
{
|
||||
let session = [{
|
||||
filename: "test.js",
|
||||
text: "test1",
|
||||
executionContext: 1,
|
||||
saved: true
|
||||
}];
|
||||
|
||||
let [win] = ScratchpadManager.restoreSession(session);
|
||||
win.addEventListener("load", function onScratchpadLoad() {
|
||||
win.removeEventListener("load", onScratchpadLoad, false);
|
||||
|
||||
win.Scratchpad.addObserver({
|
||||
onReady: function (aScratchpad) {
|
||||
aScratchpad.removeObserver(this);
|
||||
|
||||
ok(!isStar(win), "no star before changing text in scratchpad restored from file");
|
||||
|
||||
aScratchpad.setText("new text");
|
||||
ok(isStar(win), "star when text changed from scratchpad restored from file");
|
||||
|
||||
win.close();
|
||||
done();
|
||||
}
|
||||
});
|
||||
}, false);
|
||||
}
|
||||
|
||||
function testRestoreFromFileUnsaved()
|
||||
{
|
||||
let session = [{
|
||||
filename: "test.js",
|
||||
text: "test1",
|
||||
executionContext: 1,
|
||||
saved: false
|
||||
}];
|
||||
|
||||
let [win] = ScratchpadManager.restoreSession(session);
|
||||
win.addEventListener("load", function onScratchpadLoad() {
|
||||
win.removeEventListener("load", onScratchpadLoad, false);
|
||||
|
||||
win.Scratchpad.addObserver({
|
||||
onReady: function (aScratchpad) {
|
||||
aScratchpad.removeObserver(this);
|
||||
|
||||
ok(isStar(win), "star with scratchpad restored with unsaved text");
|
||||
|
||||
win.close();
|
||||
done();
|
||||
}
|
||||
});
|
||||
}, false);
|
||||
}
|
||||
|
||||
function isStar(win)
|
||||
{
|
||||
return win.document.title.match(/^\*[^\*]/);
|
||||
}
|
|
@ -30,9 +30,6 @@ function testFalsy(sp)
|
|||
sp.setBrowserContext();
|
||||
verifyFalsies(sp);
|
||||
|
||||
gScratchpadWindow.close();
|
||||
gScratchpadWindow = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,6 @@
|
|||
|
||||
Cu.import("resource:///modules/source-editor.jsm");
|
||||
|
||||
// Reference to the Scratchpad chrome window object.
|
||||
let gScratchpadWindow;
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
@ -18,10 +15,7 @@ function test()
|
|||
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
|
||||
|
||||
gScratchpadWindow = Scratchpad.openScratchpad();
|
||||
gScratchpadWindow.addEventListener("load", function onScratchpadLoad() {
|
||||
gScratchpadWindow.removeEventListener("load", onScratchpadLoad, false);
|
||||
waitForFocus(runTests, gScratchpadWindow);
|
||||
}, false);
|
||||
gScratchpadWindow.addEventListener("load", runTests, false);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,test Edit menu updates Scratchpad - bug 699130";
|
||||
|
@ -29,6 +23,8 @@ function test()
|
|||
|
||||
function runTests()
|
||||
{
|
||||
gScratchpadWindow.removeEventListener("load", runTests, false);
|
||||
|
||||
let sp = gScratchpadWindow.Scratchpad;
|
||||
let doc = gScratchpadWindow.document;
|
||||
let winUtils = gScratchpadWindow.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
|
@ -126,7 +122,12 @@ function runTests()
|
|||
|
||||
let hideAfterSelect = function() {
|
||||
sp.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, onCut);
|
||||
EventUtils.synthesizeKey("x", {accelKey: true}, gScratchpadWindow);
|
||||
waitForFocus(function () {
|
||||
let selectedText = sp.editor.getSelectedText();
|
||||
ok(selectedText.length > 0, "non-empty selected text will be cut");
|
||||
|
||||
EventUtils.synthesizeKey("x", {accelKey: true}, gScratchpadWindow);
|
||||
}, gScratchpadWindow);
|
||||
};
|
||||
|
||||
let onCut = function() {
|
||||
|
@ -142,7 +143,9 @@ function runTests()
|
|||
|
||||
let hideAfterCut = function() {
|
||||
sp.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, onPaste);
|
||||
EventUtils.synthesizeKey("v", {accelKey: true}, gScratchpadWindow);
|
||||
waitForFocus(function () {
|
||||
EventUtils.synthesizeKey("v", {accelKey: true}, gScratchpadWindow);
|
||||
}, gScratchpadWindow);
|
||||
};
|
||||
|
||||
let onPaste = function() {
|
||||
|
@ -161,7 +164,7 @@ function runTests()
|
|||
pass++;
|
||||
testContextMenu();
|
||||
} else {
|
||||
finishTest();
|
||||
finish();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -182,12 +185,5 @@ function runTests()
|
|||
openMenu(10, 10, firstShow);
|
||||
};
|
||||
|
||||
let finishTest = function() {
|
||||
gScratchpadWindow.close();
|
||||
gScratchpadWindow = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
};
|
||||
|
||||
openMenu(10, 10, firstShow);
|
||||
}
|
||||
|
|
|
@ -123,8 +123,5 @@ function runTests()
|
|||
is(sp.run()[2], "undefined",
|
||||
"global variable no longer exists after changing the context");
|
||||
|
||||
gScratchpadWindow.close();
|
||||
gScratchpadWindow = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Reference to the Scratchpad chrome window object.
|
||||
let gScratchpadWindow;
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
@ -129,8 +126,5 @@ function runTests()
|
|||
sp.redo();
|
||||
is(sp.getText(), "foo2", "redo() works");
|
||||
|
||||
gScratchpadWindow.close();
|
||||
gScratchpadWindow = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
|
|
@ -138,8 +138,5 @@ function fileRead(aInputStream, aStatus)
|
|||
gFile.remove(false);
|
||||
gFile = null;
|
||||
gScratchpad = null;
|
||||
gScratchpadWindow.close();
|
||||
gScratchpadWindow = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
|
|
@ -50,8 +50,5 @@ function runTests()
|
|||
is(chromeContextCommand.getAttribute("disabled"), "true",
|
||||
"Chrome context command is disabled");
|
||||
|
||||
gScratchpadWindow.close();
|
||||
gScratchpadWindow = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
|
|
@ -55,9 +55,6 @@ function runTests()
|
|||
executeSoon(function() {
|
||||
propPanel.hidePopup();
|
||||
|
||||
gScratchpadWindow.close();
|
||||
gScratchpadWindow = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
});
|
||||
}, false);
|
||||
|
|
|
@ -28,7 +28,9 @@ function testOpen()
|
|||
{
|
||||
let win = ScratchpadManager.openScratchpad();
|
||||
|
||||
win.addEventListener("load", function() {
|
||||
win.addEventListener("load", function onScratchpadLoad() {
|
||||
win.removeEventListener("load", onScratchpadLoad, false);
|
||||
|
||||
is(win.Scratchpad.filename, undefined, "Default filename is undefined");
|
||||
is(win.Scratchpad.getText(),
|
||||
win.Scratchpad.strings.GetStringFromName("scratchpadIntro"),
|
||||
|
@ -38,7 +40,7 @@ function testOpen()
|
|||
|
||||
win.close();
|
||||
done();
|
||||
});
|
||||
}, false);
|
||||
}
|
||||
|
||||
function testOpenWithState()
|
||||
|
@ -51,14 +53,16 @@ function testOpenWithState()
|
|||
|
||||
let win = ScratchpadManager.openScratchpad(state);
|
||||
|
||||
win.addEventListener("load", function() {
|
||||
win.addEventListener("load", function onScratchpadLoad() {
|
||||
win.removeEventListener("load", onScratchpadLoad, false);
|
||||
|
||||
is(win.Scratchpad.filename, state.filename, "Filename loaded from state");
|
||||
is(win.Scratchpad.executionContext, state.executionContext, "Execution context loaded from state");
|
||||
is(win.Scratchpad.getText(), state.text, "Content loaded from state");
|
||||
|
||||
win.close();
|
||||
done();
|
||||
});
|
||||
}, false);
|
||||
}
|
||||
|
||||
function testOpenInvalidState()
|
||||
|
|
|
@ -49,9 +49,10 @@ function testRestore()
|
|||
asyncMap(states, function(state, done) {
|
||||
// Open some scratchpad windows
|
||||
let win = ScratchpadManager.openScratchpad(state);
|
||||
win.addEventListener("load", function() {
|
||||
win.addEventListener("load", function onScratchpadLoad() {
|
||||
removeEventListener("load", onScratchpadLoad, false);
|
||||
done(win);
|
||||
})
|
||||
}, false)
|
||||
}, function(wins) {
|
||||
// Then save the windows to session store
|
||||
ScratchpadManager.saveOpenWindows();
|
||||
|
@ -73,11 +74,12 @@ function testRestore()
|
|||
is(restoredWins.length, 3, "Three scratchad windows restored");
|
||||
|
||||
asyncMap(restoredWins, function(restoredWin, done) {
|
||||
restoredWin.addEventListener("load", function() {
|
||||
restoredWin.addEventListener("load", function onScratchpadLoad() {
|
||||
restoredWin.removeEventListener("load", onScratchpadLoad, false);
|
||||
let state = restoredWin.Scratchpad.getState();
|
||||
restoredWin.close();
|
||||
done(state);
|
||||
});
|
||||
}, false);
|
||||
}, function(restoredStates) {
|
||||
// Then make sure they were restored with the right states
|
||||
ok(statesMatch(restoredStates, states),
|
||||
|
|
|
@ -99,12 +99,8 @@ function runTests3() {
|
|||
sp.setText("typeof foosbug653108;");
|
||||
is(sp.run()[2], "undefined", "global variable does not exist");
|
||||
|
||||
gScratchpadWindow.close();
|
||||
gScratchpadWindow = null;
|
||||
tab1 = null;
|
||||
tab2 = null;
|
||||
sp = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
|
|
@ -75,8 +75,5 @@ function runTests()
|
|||
|
||||
delete sp.__noSuchMethod__;
|
||||
|
||||
gScratchpadWindow.close();
|
||||
gScratchpadWindow = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
let gScratchpadWindow; // Reference to the Scratchpad chrome window object
|
||||
|
||||
function cleanup()
|
||||
{
|
||||
if (gScratchpadWindow) {
|
||||
gScratchpadWindow.close();
|
||||
gScratchpadWindow = null;
|
||||
}
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
gBrowser.removeCurrentTab();
|
||||
}
|
||||
}
|
||||
|
||||
registerCleanupFunction(cleanup);
|
|
@ -25,6 +25,8 @@ Orion version: git clone from 2011-10-26
|
|||
+ patch for Eclipse Bug 362835 - Pasted HTML shows twice:
|
||||
https://github.com/mihaisucan/orion.client/tree/bug-362835
|
||||
see https://bugs.eclipse.org/bugs/show_bug.cgi?id=362835
|
||||
+ patch for Eclipse Bug 363508 - Selection is broken after TextView hide/unhide
|
||||
see https://bugs.eclipse.org/bugs/show_bug.cgi?id=363508
|
||||
|
||||
# License
|
||||
|
||||
|
|
|
@ -1952,7 +1952,7 @@ if (typeof window !== "undefined" && typeof window.define !== "undefined") {
|
|||
* Contributors:
|
||||
* Felipe Heidrich (IBM Corporation) - initial API and implementation
|
||||
* Silenio Quarti (IBM Corporation) - initial API and implementation
|
||||
* Mihai Sucan (Mozilla Foundation) - fix for Bug#334583 Bug#348471 Bug#349485 Bug#350595 Bug#360726 Bug#361180 Bug#358623 Bug#362286 Bug#362107 Bug#362428 Bug#362835
|
||||
* Mihai Sucan (Mozilla Foundation) - fix for Bug#334583 Bug#348471 Bug#349485 Bug#350595 Bug#360726 Bug#361180 Bug#358623 Bug#362286 Bug#362107 Bug#362428 Bug#362835 Bug#363508
|
||||
******************************************************************************/
|
||||
|
||||
/*global window document navigator setTimeout clearTimeout XMLHttpRequest define */
|
||||
|
@ -3864,6 +3864,8 @@ orion.textview.TextView = (function() {
|
|||
if (!this._hasFocus) {
|
||||
this.focus();
|
||||
}
|
||||
}
|
||||
if (e.preventDefault) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -222,7 +222,17 @@ SourceEditor.prototype = {
|
|||
}, this);
|
||||
|
||||
if (aCallback) {
|
||||
aCallback(this);
|
||||
let iframe = this._view._frame;
|
||||
let document = iframe.contentDocument;
|
||||
if (!document || document.readyState != "complete") {
|
||||
let onIframeLoad = function () {
|
||||
iframe.contentWindow.removeEventListener("load", onIframeLoad, false);
|
||||
aCallback(this);
|
||||
}.bind(this);
|
||||
iframe.contentWindow.addEventListener("load", onIframeLoad, false);
|
||||
} else {
|
||||
aCallback(this);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -547,6 +557,28 @@ SourceEditor.prototype = {
|
|||
this._view.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the first visible line number.
|
||||
*
|
||||
* @return number
|
||||
* The line number, counting from 0.
|
||||
*/
|
||||
getTopIndex: function SE_getTopIndex()
|
||||
{
|
||||
return this._view.getTopIndex();
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the first visible line number.
|
||||
*
|
||||
* @param number aTopIndex
|
||||
* The line number, counting from 0.
|
||||
*/
|
||||
setTopIndex: function SE_setTopIndex(aTopIndex)
|
||||
{
|
||||
this._view.setTopIndex(aTopIndex);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the editor has focus.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Style Editor code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
#
|
||||
# Portions created by the Initial Developer are Copyright (C) 2010
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Cedric Vivier <cedricv@neonux.com> (Original author)
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
ifdef ENABLE_TESTS
|
||||
ifneq (mobile,$(MOZ_BUILD_APP))
|
||||
DIRS += test
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
libs::
|
||||
$(NSINSTALL) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/devtools
|
|
@ -0,0 +1,489 @@
|
|||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Style Editor code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Cedric Vivier <cedricv@neonux.com> (original author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
"use strict";
|
||||
|
||||
const EXPORTED_SYMBOLS = ["SplitView"];
|
||||
|
||||
/* this must be kept in sync with CSS (ie. splitview.css) */
|
||||
const LANDSCAPE_MEDIA_QUERY = "(min-aspect-ratio: 5/3)";
|
||||
|
||||
const BINDING_USERDATA = "splitview-binding";
|
||||
|
||||
|
||||
/**
|
||||
* SplitView constructor
|
||||
*
|
||||
* Initialize the split view UI on an existing DOM element.
|
||||
*
|
||||
* A split view contains items, each of those having one summary and one details
|
||||
* elements.
|
||||
* It is adaptive as it behaves similarly to a richlistbox when there the aspect
|
||||
* ratio is narrow or as a pair listbox-box otherwise.
|
||||
*
|
||||
* @param DOMElement aRoot
|
||||
* @see appendItem
|
||||
*/
|
||||
function SplitView(aRoot)
|
||||
{
|
||||
this._root = aRoot;
|
||||
this._controller = aRoot.querySelector(".splitview-controller");
|
||||
this._nav = aRoot.querySelector(".splitview-nav");
|
||||
this._side = aRoot.querySelector(".splitview-side-details");
|
||||
this._activeSummary = null
|
||||
|
||||
this._mql = aRoot.ownerDocument.defaultView.matchMedia(LANDSCAPE_MEDIA_QUERY);
|
||||
|
||||
this._filter = aRoot.querySelector(".splitview-filter");
|
||||
if (this._filter) {
|
||||
this._setupFilterBox();
|
||||
}
|
||||
|
||||
// items list focus and search-on-type handling
|
||||
this._nav.addEventListener("keydown", function onKeyCatchAll(aEvent) {
|
||||
function getFocusedItemWithin(nav) {
|
||||
let node = nav.ownerDocument.activeElement;
|
||||
while (node && node.parentNode != nav) {
|
||||
node = node.parentNode;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
// do not steal focus from inside iframes or textboxes
|
||||
if (aEvent.target.ownerDocument != this._nav.ownerDocument ||
|
||||
aEvent.target.tagName == "input" ||
|
||||
aEvent.target.tagName == "textbox" ||
|
||||
aEvent.target.tagName == "textarea" ||
|
||||
aEvent.target.classList.contains("textbox")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// handle keyboard navigation within the items list
|
||||
let newFocusOrdinal;
|
||||
if (aEvent.keyCode == aEvent.DOM_VK_PAGE_UP ||
|
||||
aEvent.keyCode == aEvent.DOM_VK_HOME) {
|
||||
newFocusOrdinal = 0;
|
||||
} else if (aEvent.keyCode == aEvent.DOM_VK_PAGE_DOWN ||
|
||||
aEvent.keyCode == aEvent.DOM_VK_END) {
|
||||
newFocusOrdinal = this._nav.childNodes.length - 1;
|
||||
} else if (aEvent.keyCode == aEvent.DOM_VK_UP) {
|
||||
newFocusOrdinal = getFocusedItemWithin(this._nav).getAttribute("data-ordinal");
|
||||
newFocusOrdinal--;
|
||||
} else if (aEvent.keyCode == aEvent.DOM_VK_DOWN) {
|
||||
newFocusOrdinal = getFocusedItemWithin(this._nav).getAttribute("data-ordinal");
|
||||
newFocusOrdinal++;
|
||||
}
|
||||
if (newFocusOrdinal !== undefined) {
|
||||
aEvent.stopPropagation();
|
||||
let el = this.getSummaryElementByOrdinal(newFocusOrdinal);
|
||||
if (el) {
|
||||
el.focus();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// search-on-type when any non-whitespace character is pressed while list
|
||||
// has the focus
|
||||
if (this._filter &&
|
||||
!/\s/.test(String.fromCharCode(aEvent.which))) {
|
||||
this._filter.focus();
|
||||
}
|
||||
}.bind(this), false);
|
||||
}
|
||||
|
||||
SplitView.prototype = {
|
||||
/**
|
||||
* Retrieve whether the UI currently has a landscape orientation.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
get isLandscape() this._mql.matches,
|
||||
|
||||
/**
|
||||
* Retrieve the root element.
|
||||
*
|
||||
* @return DOMElement
|
||||
*/
|
||||
get rootElement() this._root,
|
||||
|
||||
/**
|
||||
* Retrieve the active item's summary element or null if there is none.
|
||||
*
|
||||
* @return DOMElement
|
||||
*/
|
||||
get activeSummary() this._activeSummary,
|
||||
|
||||
/**
|
||||
* Set the active item's summary element.
|
||||
*
|
||||
* @param DOMElement aSummary
|
||||
*/
|
||||
set activeSummary(aSummary)
|
||||
{
|
||||
if (aSummary == this._activeSummary) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._activeSummary) {
|
||||
let binding = this._activeSummary.getUserData(BINDING_USERDATA);
|
||||
|
||||
if (binding.onHide) {
|
||||
binding.onHide(this._activeSummary, binding._details, binding.data);
|
||||
}
|
||||
|
||||
this._activeSummary.classList.remove("splitview-active");
|
||||
binding._details.classList.remove("splitview-active");
|
||||
}
|
||||
|
||||
if (!aSummary) {
|
||||
return;
|
||||
}
|
||||
|
||||
let binding = aSummary.getUserData(BINDING_USERDATA);
|
||||
aSummary.classList.add("splitview-active");
|
||||
binding._details.classList.add("splitview-active");
|
||||
|
||||
this._activeSummary = aSummary;
|
||||
|
||||
if (binding.onShow) {
|
||||
binding.onShow(aSummary, binding._details, binding.data);
|
||||
}
|
||||
aSummary.scrollIntoView();
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve the active item's details element or null if there is none.
|
||||
* @return DOMElement
|
||||
*/
|
||||
get activeDetails()
|
||||
{
|
||||
let summary = this.activeSummary;
|
||||
return summary ? summary.getUserData(BINDING_USERDATA)._details : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve the summary element for a given ordinal.
|
||||
*
|
||||
* @param number aOrdinal
|
||||
* @return DOMElement
|
||||
* Summary element with given ordinal or null if not found.
|
||||
* @see appendItem
|
||||
*/
|
||||
getSummaryElementByOrdinal: function SEC_getSummaryElementByOrdinal(aOrdinal)
|
||||
{
|
||||
return this._nav.querySelector("* > li[data-ordinal='" + aOrdinal + "']");
|
||||
},
|
||||
|
||||
/**
|
||||
* Append an item to the split view.
|
||||
*
|
||||
* @param DOMElement aSummary
|
||||
* The summary element for the item.
|
||||
* @param DOMElement aDetails
|
||||
* The details element for the item.
|
||||
* @param object aOptions
|
||||
* Optional object that defines custom behavior and data for the item.
|
||||
* All properties are optional :
|
||||
* - function(DOMElement summary, DOMElement details, object data) onCreate
|
||||
* Called when the item has been added.
|
||||
* - function(summary, details, data) onShow
|
||||
* Called when the item is shown/active.
|
||||
* - function(summary, details, data) onHide
|
||||
* Called when the item is hidden/inactive.
|
||||
* - function(summary, details, data) onDestroy
|
||||
* Called when the item has been removed.
|
||||
* - function(summary, details, data, query) onFilterBy
|
||||
* Called when the user performs a filtering search.
|
||||
* If the function returns false, the item does not match query
|
||||
* string and will be hidden.
|
||||
* - object data
|
||||
* Object to pass to the callbacks above.
|
||||
* - boolean disableAnimations
|
||||
* If true there is no animation or scrolling when this item is
|
||||
* appended. Set this when batch appending (eg. initial population).
|
||||
* - number ordinal
|
||||
* Items with a lower ordinal are displayed before those with a
|
||||
* higher ordinal.
|
||||
*/
|
||||
appendItem: function ASV_appendItem(aSummary, aDetails, aOptions)
|
||||
{
|
||||
let binding = aOptions || {};
|
||||
|
||||
binding._summary = aSummary;
|
||||
binding._details = aDetails;
|
||||
aSummary.setUserData(BINDING_USERDATA, binding, null);
|
||||
|
||||
if (!binding.disableAnimations) {
|
||||
aSummary.classList.add("splitview-slide");
|
||||
aSummary.classList.add("splitview-flash");
|
||||
}
|
||||
this._nav.appendChild(aSummary);
|
||||
|
||||
aSummary.addEventListener("click", function onSummaryClick(aEvent) {
|
||||
aEvent.stopPropagation();
|
||||
this.activeSummary = aSummary;
|
||||
}.bind(this), false);
|
||||
|
||||
this._side.appendChild(aDetails);
|
||||
|
||||
if (binding.onCreate) {
|
||||
// queue onCreate handler
|
||||
this._root.ownerDocument.defaultView.setTimeout(function () {
|
||||
binding.onCreate(aSummary, aDetails, binding.data);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
if (!binding.disableAnimations) {
|
||||
scheduleAnimation(aSummary, "splitview-slide", "splitview-flash");
|
||||
aSummary.scrollIntoView();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Append an item to the split view according to two template elements
|
||||
* (one for the item's summary and the other for the item's details).
|
||||
*
|
||||
* @param string aName
|
||||
* Name of the template elements to instantiate.
|
||||
* Requires two (hidden) DOM elements with id "splitview-tpl-summary-"
|
||||
* and "splitview-tpl-details-" suffixed with aName.
|
||||
* @param object aOptions
|
||||
* Optional object that defines custom behavior and data for the item.
|
||||
* See appendItem for full description.
|
||||
* @return object{summary:,details:}
|
||||
* Object with the new DOM elements created for summary and details.
|
||||
* @see appendItem
|
||||
*/
|
||||
appendTemplatedItem: function ASV_appendTemplatedItem(aName, aOptions)
|
||||
{
|
||||
aOptions = aOptions || {};
|
||||
let summary = this._root.querySelector("#splitview-tpl-summary-" + aName);
|
||||
let details = this._root.querySelector("#splitview-tpl-details-" + aName);
|
||||
|
||||
summary = summary.cloneNode(true);
|
||||
summary.id = "";
|
||||
if (aOptions.ordinal !== undefined) { // can be zero
|
||||
summary.style.MozBoxOrdinalGroup = aOptions.ordinal;
|
||||
summary.setAttribute("data-ordinal", aOptions.ordinal);
|
||||
}
|
||||
details = details.cloneNode(true);
|
||||
details.id = "";
|
||||
|
||||
this.appendItem(summary, details, aOptions);
|
||||
return {summary: summary, details: details};
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove an item from the split view.
|
||||
*
|
||||
* @param DOMElement aSummary
|
||||
* Summary element of the item to remove.
|
||||
*/
|
||||
removeItem: function ASV_removeItem(aSummary)
|
||||
{
|
||||
if (aSummary == this._activeSummary) {
|
||||
this.activeSummary = null;
|
||||
}
|
||||
|
||||
let binding = aSummary.getUserData(BINDING_USERDATA);
|
||||
aSummary.parentNode.removeChild(aSummary);
|
||||
binding._details.parentNode.removeChild(binding._details);
|
||||
|
||||
if (binding.onDestroy) {
|
||||
binding.onDestroy(aSummary, binding._details, binding.data);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove all items from the split view.
|
||||
*/
|
||||
removeAll: function ASV_removeAll()
|
||||
{
|
||||
while (this._nav.hasChildNodes()) {
|
||||
this.removeItem(this._nav.firstChild);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Filter items by given string.
|
||||
* Matching is performed on every item by calling onFilterBy when defined
|
||||
* and then by searching aQuery in the summary element's text item.
|
||||
* Non-matching item is hidden.
|
||||
*
|
||||
* If no item matches, 'splitview-all-filtered' class is set on the filter
|
||||
* input element and the splitview-nav element.
|
||||
*
|
||||
* @param string aQuery
|
||||
* The query string. Use null to reset (no filter).
|
||||
* @return number
|
||||
* The number of filtered (non-matching) item.
|
||||
*/
|
||||
filterItemsBy: function ASV_filterItemsBy(aQuery)
|
||||
{
|
||||
if (!this._nav.hasChildNodes()) {
|
||||
return 0;
|
||||
}
|
||||
if (aQuery) {
|
||||
aQuery = aQuery.trim();
|
||||
}
|
||||
if (!aQuery) {
|
||||
for (let i = 0; i < this._nav.childNodes.length; ++i) {
|
||||
this._nav.childNodes[i].classList.remove("splitview-filtered");
|
||||
}
|
||||
this._filter.classList.remove("splitview-all-filtered");
|
||||
this._nav.classList.remove("splitview-all-filtered");
|
||||
return 0;
|
||||
}
|
||||
|
||||
let count = 0;
|
||||
let filteredCount = 0;
|
||||
for (let i = 0; i < this._nav.childNodes.length; ++i) {
|
||||
let summary = this._nav.childNodes[i];
|
||||
|
||||
let matches = false;
|
||||
let binding = summary.getUserData(BINDING_USERDATA);
|
||||
if (binding.onFilterBy) {
|
||||
matches = binding.onFilterBy(summary, binding._details, binding.data, aQuery);
|
||||
}
|
||||
if (!matches) { // try text content
|
||||
let content = summary.textContent.toUpperCase();
|
||||
matches = (content.indexOf(aQuery.toUpperCase()) > -1);
|
||||
}
|
||||
|
||||
count++;
|
||||
if (!matches) {
|
||||
summary.classList.add("splitview-filtered");
|
||||
filteredCount++;
|
||||
} else {
|
||||
summary.classList.remove("splitview-filtered");
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0 && filteredCount == count) {
|
||||
this._filter.classList.add("splitview-all-filtered");
|
||||
this._nav.classList.add("splitview-all-filtered");
|
||||
} else {
|
||||
this._filter.classList.remove("splitview-all-filtered");
|
||||
this._nav.classList.remove("splitview-all-filtered");
|
||||
}
|
||||
return filteredCount;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the item's CSS class name.
|
||||
* This sets the class on both the summary and details elements, retaining
|
||||
* any SplitView-specific classes.
|
||||
*
|
||||
* @param DOMElement aSummary
|
||||
* Summary element of the item to set.
|
||||
* @param string aClassName
|
||||
* One or more space-separated CSS classes.
|
||||
*/
|
||||
setItemClassName: function ASV_setItemClassName(aSummary, aClassName)
|
||||
{
|
||||
let binding = aSummary.getUserData(BINDING_USERDATA);
|
||||
let viewSpecific;
|
||||
|
||||
viewSpecific = aSummary.className.match(/(splitview\-[\w-]+)/g);
|
||||
viewSpecific = viewSpecific ? viewSpecific.join(" ") : "";
|
||||
aSummary.className = viewSpecific + " " + aClassName;
|
||||
|
||||
viewSpecific = binding._details.className.match(/(splitview\-[\w-]+)/g);
|
||||
viewSpecific = viewSpecific ? viewSpecific.join(" ") : "";
|
||||
binding._details.className = viewSpecific + " " + aClassName;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set up filter search box.
|
||||
*/
|
||||
_setupFilterBox: function ASV__setupFilterBox()
|
||||
{
|
||||
let clearFilter = function clearFilter(aEvent) {
|
||||
this._filter.value = "";
|
||||
this.filterItemsBy("");
|
||||
return false;
|
||||
}.bind(this);
|
||||
|
||||
this._filter.addEventListener("command", function onFilterInput(aEvent) {
|
||||
this.filterItemsBy(this._filter.value);
|
||||
}.bind(this), false);
|
||||
|
||||
this._filter.addEventListener("keyup", function onFilterKeyUp(aEvent) {
|
||||
if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) {
|
||||
clearFilter();
|
||||
}
|
||||
if (aEvent.keyCode == aEvent.DOM_VK_ENTER ||
|
||||
aEvent.keyCode == aEvent.DOM_VK_RETURN) {
|
||||
// autofocus matching item if there is only one
|
||||
let matches = this._nav.querySelectorAll("* > li:not(.splitview-filtered)");
|
||||
if (matches.length == 1) {
|
||||
this.activeSummary = matches[0];
|
||||
}
|
||||
}
|
||||
}.bind(this), false);
|
||||
|
||||
let clearButtons = this._root.querySelectorAll(".splitview-filter-clearButton");
|
||||
for (let i = 0; i < clearButtons.length; ++i) {
|
||||
clearButtons[i].addEventListener("click", clearFilter, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// private helpers
|
||||
|
||||
/**
|
||||
* Schedule one or multiple CSS animation(s) on an element.
|
||||
*
|
||||
* @param DOMElement aElement
|
||||
* @param string ...
|
||||
* One or multiple animation class name(s).
|
||||
*/
|
||||
function scheduleAnimation(aElement)
|
||||
{
|
||||
let classes = Array.prototype.slice.call(arguments, 1);
|
||||
for each (let klass in classes) {
|
||||
aElement.classList.add(klass);
|
||||
}
|
||||
|
||||
let window = aElement.ownerDocument.defaultView;
|
||||
window.mozRequestAnimationFrame(function triggerAnimation() {
|
||||
for each (let klass in classes) {
|
||||
aElement.classList.remove(klass);
|
||||
}
|
||||
});
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,491 @@
|
|||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Style Editor code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Cedric Vivier <cedricv@neonux.com> (original author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
"use strict";
|
||||
|
||||
const EXPORTED_SYMBOLS = ["StyleEditorChrome"];
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/PluralForm.jsm");
|
||||
Cu.import("resource:///modules/devtools/StyleEditor.jsm");
|
||||
Cu.import("resource:///modules/devtools/StyleEditorUtil.jsm");
|
||||
Cu.import("resource:///modules/devtools/SplitView.jsm");
|
||||
|
||||
const STYLE_EDITOR_TEMPLATE = "stylesheet";
|
||||
|
||||
|
||||
/**
|
||||
* StyleEditorChrome constructor.
|
||||
*
|
||||
* The 'chrome' of the Style Editor is all the around the actual editor (textbox).
|
||||
* Manages the sheet selector, history, and opened editor(s) for the attached
|
||||
* content window.
|
||||
*
|
||||
* @param DOMElement aRoot
|
||||
* Element that owns the chrome UI.
|
||||
* @param DOMWindow aContentWindow
|
||||
* Content DOMWindow to attach to this chrome.
|
||||
*/
|
||||
function StyleEditorChrome(aRoot, aContentWindow)
|
||||
{
|
||||
assert(aRoot, "Argument 'aRoot' is required to initialize StyleEditorChrome.");
|
||||
|
||||
this._root = aRoot;
|
||||
this._document = this._root.ownerDocument;
|
||||
this._window = this._document.defaultView;
|
||||
|
||||
this._editors = [];
|
||||
this._listeners = []; // @see addChromeListener
|
||||
|
||||
this._contentWindow = null;
|
||||
this._isContentAttached = false;
|
||||
|
||||
let initializeUI = function (aEvent) {
|
||||
if (aEvent) {
|
||||
this._window.removeEventListener("load", initializeUI, false);
|
||||
}
|
||||
|
||||
let viewRoot = this._root.parentNode.querySelector(".splitview-root");
|
||||
this._view = new SplitView(viewRoot);
|
||||
|
||||
this._setupChrome();
|
||||
|
||||
// attach to the content window
|
||||
this.contentWindow = aContentWindow;
|
||||
this._contentWindowID = null;
|
||||
}.bind(this);
|
||||
|
||||
if (this._document.readyState == "complete") {
|
||||
initializeUI();
|
||||
} else {
|
||||
this._window.addEventListener("load", initializeUI, false);
|
||||
}
|
||||
}
|
||||
|
||||
StyleEditorChrome.prototype = {
|
||||
/**
|
||||
* Retrieve the content window attached to this chrome.
|
||||
*
|
||||
* @return DOMWindow
|
||||
* Content window or null if no content window is attached.
|
||||
*/
|
||||
get contentWindow() this._contentWindow,
|
||||
|
||||
/**
|
||||
* Retrieve the ID of the content window attached to this chrome.
|
||||
*
|
||||
* @return number
|
||||
* Window ID or -1 if no content window is attached.
|
||||
*/
|
||||
get contentWindowID()
|
||||
{
|
||||
try {
|
||||
return this._contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
|
||||
} catch (ex) {
|
||||
return -1;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the content window attached to this chrome.
|
||||
* Content attach or detach events/notifications are triggered after the
|
||||
* operation is complete (possibly asynchronous if the content is not fully
|
||||
* loaded yet).
|
||||
*
|
||||
* @param DOMWindow aContentWindow
|
||||
* @see addChromeListener
|
||||
*/
|
||||
set contentWindow(aContentWindow)
|
||||
{
|
||||
if (this._contentWindow == aContentWindow) {
|
||||
return; // no change
|
||||
}
|
||||
|
||||
this._contentWindow = aContentWindow;
|
||||
|
||||
if (!aContentWindow) {
|
||||
this._disableChrome();
|
||||
return;
|
||||
}
|
||||
|
||||
let onContentUnload = function () {
|
||||
aContentWindow.removeEventListener("unload", onContentUnload, false);
|
||||
if (this.contentWindow == aContentWindow) {
|
||||
this.contentWindow = null; // detach
|
||||
}
|
||||
}.bind(this);
|
||||
aContentWindow.addEventListener("unload", onContentUnload, false);
|
||||
|
||||
if (aContentWindow.document.readyState == "complete") {
|
||||
this._populateChrome();
|
||||
return;
|
||||
} else {
|
||||
let onContentReady = function () {
|
||||
aContentWindow.removeEventListener("load", onContentReady, false);
|
||||
this._populateChrome();
|
||||
}.bind(this);
|
||||
aContentWindow.addEventListener("load", onContentReady, false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve the content document attached to this chrome.
|
||||
*
|
||||
* @return DOMDocument
|
||||
*/
|
||||
get contentDocument()
|
||||
{
|
||||
return this._contentWindow ? this._contentWindow.document : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve whether the content has been attached and StyleEditor instances
|
||||
* exist for all of its stylesheets.
|
||||
*
|
||||
* @return boolean
|
||||
* @see addChromeListener
|
||||
*/
|
||||
get isContentAttached() this._isContentAttached,
|
||||
|
||||
/**
|
||||
* Retrieve an array with the StyleEditor instance for each live style sheet,
|
||||
* ordered by style sheet index.
|
||||
*
|
||||
* @return Array<StyleEditor>
|
||||
*/
|
||||
get editors()
|
||||
{
|
||||
let editors = [];
|
||||
this._editors.forEach(function (aEditor) {
|
||||
if (aEditor.styleSheetIndex >= 0) {
|
||||
editors[aEditor.styleSheetIndex] = aEditor;
|
||||
}
|
||||
});
|
||||
return editors;
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a listener for StyleEditorChrome events.
|
||||
*
|
||||
* The listener implements IStyleEditorChromeListener := {
|
||||
* onContentAttach: Called when a content window has been attached.
|
||||
* All editors are instantiated, though they might
|
||||
* not be loaded yet.
|
||||
* Arguments: (StyleEditorChrome aChrome)
|
||||
* @see contentWindow
|
||||
* @see StyleEditor.isLoaded
|
||||
* @see StyleEditor.addActionListener
|
||||
*
|
||||
* onContentDetach: Called when the content window has been detached.
|
||||
* Arguments: (StyleEditorChrome aChrome)
|
||||
* @see contentWindow
|
||||
*
|
||||
* onEditorAdded: Called when a stylesheet (therefore a StyleEditor
|
||||
* instance) has been added to the UI.
|
||||
* Arguments (StyleEditorChrome aChrome,
|
||||
* StyleEditor aEditor)
|
||||
* }
|
||||
*
|
||||
* All listener methods are optional.
|
||||
*
|
||||
* @param IStyleEditorChromeListener aListener
|
||||
* @see removeChromeListener
|
||||
*/
|
||||
addChromeListener: function SEC_addChromeListener(aListener)
|
||||
{
|
||||
this._listeners.push(aListener);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a listener for Chrome events from the current list of listeners.
|
||||
*
|
||||
* @param IStyleEditorChromeListener aListener
|
||||
* @see addChromeListener
|
||||
*/
|
||||
removeChromeListener: function SEC_removeChromeListener(aListener)
|
||||
{
|
||||
let index = this._listeners.indexOf(aListener);
|
||||
if (index != -1) {
|
||||
this._listeners.splice(index, 1);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Trigger named handlers in StyleEditorChrome listeners.
|
||||
*
|
||||
* @param string aName
|
||||
* Name of the event to trigger.
|
||||
* @param Array aArgs
|
||||
* Optional array of arguments to pass to the listener(s).
|
||||
* @see addActionListener
|
||||
*/
|
||||
_triggerChromeListeners: function SE__triggerChromeListeners(aName, aArgs)
|
||||
{
|
||||
// insert the origin Chrome instance as first argument
|
||||
if (!aArgs) {
|
||||
aArgs = [this];
|
||||
} else {
|
||||
aArgs.unshift(this);
|
||||
}
|
||||
|
||||
// trigger all listeners that have this named handler
|
||||
for (let i = 0; i < this._listeners.length; ++i) {
|
||||
let listener = this._listeners[i];
|
||||
let handler = listener["on" + aName];
|
||||
if (handler) {
|
||||
handler.apply(listener, aArgs);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set up the chrome UI. Install event listeners and so on.
|
||||
*/
|
||||
_setupChrome: function SEC__setupChrome()
|
||||
{
|
||||
// wire up UI elements
|
||||
wire(this._view.rootElement, ".style-editor-newButton", function onNewButton() {
|
||||
let editor = new StyleEditor(this.contentDocument);
|
||||
this._editors.push(editor);
|
||||
editor.addActionListener(this);
|
||||
editor.load();
|
||||
}.bind(this));
|
||||
|
||||
wire(this._view.rootElement, ".style-editor-importButton", function onImportButton() {
|
||||
let editor = new StyleEditor(this.contentDocument);
|
||||
this._editors.push(editor);
|
||||
editor.addActionListener(this);
|
||||
editor.importFromFile(this._mockImportFile || null, this._window);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset the chrome UI to an empty state.
|
||||
*/
|
||||
_resetChrome: function SEC__resetChrome()
|
||||
{
|
||||
this._editors.forEach(function (aEditor) {
|
||||
aEditor.removeActionListener(this);
|
||||
}.bind(this));
|
||||
this._editors = [];
|
||||
|
||||
this._view.removeAll();
|
||||
},
|
||||
|
||||
/**
|
||||
* Populate the chrome UI according to the content document.
|
||||
*
|
||||
* @see StyleEditor._setupShadowStyleSheet
|
||||
*/
|
||||
_populateChrome: function SEC__populateChrome()
|
||||
{
|
||||
this._resetChrome();
|
||||
|
||||
this._document.title = _("chromeWindowTitle",
|
||||
this.contentDocument.title || this.contentDocument.location.href);
|
||||
|
||||
let document = this.contentDocument;
|
||||
for (let i = 0; i < document.styleSheets.length; ++i) {
|
||||
let styleSheet = document.styleSheets[i];
|
||||
|
||||
let editor = new StyleEditor(document, styleSheet);
|
||||
editor.addActionListener(this);
|
||||
this._editors.push(editor);
|
||||
}
|
||||
|
||||
this._triggerChromeListeners("ContentAttach");
|
||||
|
||||
// Queue editors loading so that ContentAttach is consistently triggered
|
||||
// right after all editor instances are available (this.editors) but are
|
||||
// NOT loaded/ready yet. This also helps responsivity during loading when
|
||||
// there are many heavy stylesheets.
|
||||
this._editors.forEach(function (aEditor) {
|
||||
this._window.setTimeout(aEditor.load.bind(aEditor), 0);
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable all UI, effectively making editors read-only.
|
||||
* This is automatically called when no content window is attached.
|
||||
*
|
||||
* @see contentWindow
|
||||
*/
|
||||
_disableChrome: function SEC__disableChrome()
|
||||
{
|
||||
let matches = this._root.querySelectorAll("button,input,select");
|
||||
for (let i = 0; i < matches.length; ++i) {
|
||||
matches[i].setAttribute("disabled", "disabled");
|
||||
}
|
||||
|
||||
this.editors.forEach(function onEnterReadOnlyMode(aEditor) {
|
||||
aEditor.readOnly = true;
|
||||
});
|
||||
|
||||
this._view.rootElement.setAttribute("disabled", "disabled");
|
||||
|
||||
this._triggerChromeListeners("ContentDetach");
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve the summary element for a given editor.
|
||||
*
|
||||
* @param StyleEditor aEditor
|
||||
* @return DOMElement
|
||||
* Item's summary element or null if not found.
|
||||
* @see SplitView
|
||||
*/
|
||||
getSummaryElementForEditor: function SEC_getSummaryElementForEditor(aEditor)
|
||||
{
|
||||
return this._view.getSummaryElementByOrdinal(aEditor.styleSheetIndex);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update split view summary of given StyleEditor instance.
|
||||
*
|
||||
* @param StyleEditor aEditor
|
||||
* @param DOMElement aSummary
|
||||
* Optional item's summary element to update. If none, item corresponding
|
||||
* to passed aEditor is used.
|
||||
*/
|
||||
_updateSummaryForEditor: function SEC__updateSummaryForEditor(aEditor, aSummary)
|
||||
{
|
||||
let summary = aSummary || this.getSummaryElementForEditor(aEditor);
|
||||
let ruleCount = aEditor.styleSheet.cssRules.length;
|
||||
|
||||
this._view.setItemClassName(summary, aEditor.flags);
|
||||
|
||||
text(summary, ".stylesheet-name", aEditor.getFriendlyName());
|
||||
text(summary, ".stylesheet-title", aEditor.styleSheet.title || "");
|
||||
text(summary, ".stylesheet-rule-count",
|
||||
PluralForm.get(ruleCount, _("ruleCount.label")).replace("#1", ruleCount));
|
||||
text(summary, ".stylesheet-error-message", aEditor.errorMessage);
|
||||
},
|
||||
|
||||
/**
|
||||
* IStyleEditorActionListener implementation
|
||||
* @See StyleEditor.addActionListener.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Called when source has been loaded and editor is ready for some action.
|
||||
*
|
||||
* @param StyleEditor aEditor
|
||||
*/
|
||||
onLoad: function SEAL_onLoad(aEditor)
|
||||
{
|
||||
let item = this._view.appendTemplatedItem(STYLE_EDITOR_TEMPLATE, {
|
||||
data: {
|
||||
editor: aEditor
|
||||
},
|
||||
disableAnimations: this._alwaysDisableAnimations,
|
||||
ordinal: aEditor.styleSheetIndex,
|
||||
onCreate: function ASV_onItemCreate(aSummary, aDetails, aData) {
|
||||
let editor = aData.editor;
|
||||
|
||||
wire(aSummary, ".stylesheet-enabled", function onToggleEnabled(aEvent) {
|
||||
aEvent.stopPropagation();
|
||||
aEvent.target.blur();
|
||||
|
||||
editor.enableStyleSheet(editor.styleSheet.disabled);
|
||||
});
|
||||
|
||||
wire(aSummary, ".stylesheet-saveButton", function onSaveButton(aEvent) {
|
||||
aEvent.stopPropagation();
|
||||
aEvent.target.blur();
|
||||
|
||||
editor.saveToFile(editor.savedFile);
|
||||
});
|
||||
|
||||
this._updateSummaryForEditor(editor, aSummary);
|
||||
|
||||
aSummary.addEventListener("focus", function onSummaryFocus(aEvent) {
|
||||
if (aEvent.target == aSummary) {
|
||||
// autofocus the stylesheet name
|
||||
aSummary.querySelector(".stylesheet-name").focus();
|
||||
}
|
||||
}, false);
|
||||
|
||||
// autofocus the first or new stylesheet
|
||||
if (editor.styleSheetIndex == 0 ||
|
||||
editor.hasFlag(StyleEditorFlags.NEW)) {
|
||||
this._view.activeSummary = aSummary;
|
||||
}
|
||||
|
||||
this._triggerChromeListeners("EditorAdded", [editor]);
|
||||
}.bind(this),
|
||||
onHide: function ASV_onItemShow(aSummary, aDetails, aData) {
|
||||
aData.editor.onHide();
|
||||
},
|
||||
onShow: function ASV_onItemShow(aSummary, aDetails, aData) {
|
||||
let editor = aData.editor;
|
||||
if (!editor.inputElement) {
|
||||
// attach editor to input element the first time it is shown
|
||||
editor.inputElement = aDetails.querySelector(".stylesheet-editor-input");
|
||||
}
|
||||
editor.onShow();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when an editor flag changed.
|
||||
*
|
||||
* @param StyleEditor aEditor
|
||||
* @param string aFlagName
|
||||
* @see StyleEditor.flags
|
||||
*/
|
||||
onFlagChange: function SEAL_onFlagChange(aEditor, aFlagName)
|
||||
{
|
||||
this._updateSummaryForEditor(aEditor);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when when changes have been committed/applied to the live DOM
|
||||
* stylesheet.
|
||||
*
|
||||
* @param StyleEditor aEditor
|
||||
*/
|
||||
onCommit: function SEAL_onCommit(aEditor)
|
||||
{
|
||||
this._updateSummaryForEditor(aEditor);
|
||||
},
|
||||
};
|
|
@ -0,0 +1,196 @@
|
|||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Style Editor code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Cedric Vivier <cedricv@neonux.com> (original author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
"use strict";
|
||||
|
||||
const EXPORTED_SYMBOLS = [
|
||||
"_",
|
||||
"assert",
|
||||
"attr",
|
||||
"getCurrentBrowserTabContentWindow",
|
||||
"log",
|
||||
"text",
|
||||
"wire"
|
||||
];
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const PROPERTIES_URL = "chrome://browser/locale/devtools/styleeditor.properties";
|
||||
|
||||
const console = Services.console;
|
||||
const gStringBundle = Services.strings.createBundle(PROPERTIES_URL);
|
||||
|
||||
|
||||
/**
|
||||
* Returns a localized string with the given key name from the string bundle.
|
||||
*
|
||||
* @param aName
|
||||
* @param ...rest
|
||||
* Optional arguments to format in the string.
|
||||
* @return string
|
||||
*/
|
||||
function _(aName)
|
||||
{
|
||||
|
||||
if (arguments.length == 1) {
|
||||
return gStringBundle.GetStringFromName(aName);
|
||||
}
|
||||
let rest = Array.prototype.slice.call(arguments, 1);
|
||||
return gStringBundle.formatStringFromName(aName, rest, rest.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert an expression is true or throw if false.
|
||||
*
|
||||
* @param aExpression
|
||||
* @param aMessage
|
||||
* Optional message.
|
||||
* @return aExpression
|
||||
*/
|
||||
function assert(aExpression, aMessage)
|
||||
{
|
||||
if (!!!(aExpression)) {
|
||||
let msg = aMessage ? "ASSERTION FAILURE:" + aMessage : "ASSERTION FAILURE";
|
||||
log(msg);
|
||||
throw new Error(msg);
|
||||
}
|
||||
return aExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve or set the text content of an element.
|
||||
*
|
||||
* @param DOMElement aRoot
|
||||
* The element to use for querySelector.
|
||||
* @param string aSelector
|
||||
* Selector string for the element to get/set the text content.
|
||||
* @param string aText
|
||||
* Optional text to set.
|
||||
* @return string
|
||||
* Text content of matching element or null if there were no element
|
||||
* matching aSelector.
|
||||
*/
|
||||
function text(aRoot, aSelector, aText)
|
||||
{
|
||||
let element = aRoot.querySelector(aSelector);
|
||||
if (!element) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (aText === undefined) {
|
||||
return element.textContent;
|
||||
}
|
||||
element.textContent = aText;
|
||||
return aText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates _own_ properties of an object.
|
||||
*
|
||||
* @param aObject
|
||||
* The object to iterate.
|
||||
* @param function aCallback(aKey, aValue)
|
||||
*/
|
||||
function forEach(aObject, aCallback)
|
||||
{
|
||||
for (let key in aObject) {
|
||||
if (aObject.hasOwnProperty(key)) {
|
||||
aCallback(key, aObject[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message to the console.
|
||||
*
|
||||
* @param ...rest
|
||||
* One or multiple arguments to log.
|
||||
* If multiple arguments are given, they will be joined by " " in the log.
|
||||
*/
|
||||
function log()
|
||||
{
|
||||
console.logStringMessage(Array.prototype.slice.call(arguments).join(" "));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wire up element(s) matching selector with attributes, event listeners, etc.
|
||||
*
|
||||
* @param DOMElement aRoot
|
||||
* The element to use for querySelectorAll.
|
||||
* Can be null if aSelector is a DOMElement.
|
||||
* @param string|DOMElement aSelectorOrElement
|
||||
* Selector string or DOMElement for the element(s) to wire up.
|
||||
* @param object aDescriptor
|
||||
* An object describing how to wire matching selector, supported properties
|
||||
* are "events", "attributes" and "userData" taking objects themselves.
|
||||
* Each key of properties above represents the name of the event, attribute
|
||||
* or userData, with the value being a function used as an event handler,
|
||||
* string to use as attribute value, or object to use as named userData
|
||||
* respectively.
|
||||
* If aDescriptor is a function, the argument is equivalent to :
|
||||
* {events: {'click': aDescriptor}}
|
||||
*/
|
||||
function wire(aRoot, aSelectorOrElement, aDescriptor)
|
||||
{
|
||||
let matches;
|
||||
if (typeof(aSelectorOrElement) == "string") { // selector
|
||||
matches = aRoot.querySelectorAll(aSelectorOrElement);
|
||||
if (!matches.length) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
matches = [aSelectorOrElement]; // element
|
||||
}
|
||||
|
||||
if (typeof(aDescriptor) == "function") {
|
||||
aDescriptor = {events: {click: aDescriptor}};
|
||||
}
|
||||
|
||||
for (let i = 0; i < matches.length; ++i) {
|
||||
let element = matches[i];
|
||||
forEach(aDescriptor.events, function (aName, aHandler) {
|
||||
element.addEventListener(aName, aHandler, false);
|
||||
});
|
||||
forEach(aDescriptor.attributes, element.setAttribute);
|
||||
forEach(aDescriptor.userData, element.setUserData);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Style Editor code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Cedric Vivier <cedricv@neonux.com> (original author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
box,
|
||||
.splitview-nav {
|
||||
-moz-box-flex: 1;
|
||||
-moz-box-orient: vertical;
|
||||
}
|
||||
|
||||
.splitview-main {
|
||||
-moz-box-flex: 0;
|
||||
}
|
||||
|
||||
.splitview-controller {
|
||||
min-height: 8em;
|
||||
max-height: 14em;
|
||||
}
|
||||
|
||||
.splitview-nav {
|
||||
display: -moz-box;
|
||||
}
|
||||
|
||||
/* only the active details pane is shown */
|
||||
.splitview-side-details > * {
|
||||
display: none;
|
||||
}
|
||||
.splitview-side-details > .splitview-active {
|
||||
display: -moz-box;
|
||||
}
|
||||
|
||||
/* this is to keep in sync with SplitView.jsm's LANDSCAPE_MEDIA_QUERY */
|
||||
@media (min-aspect-ratio: 5/3) {
|
||||
.splitview-root {
|
||||
-moz-box-orient: horizontal;
|
||||
}
|
||||
.splitview-controller {
|
||||
-moz-box-flex: 0;
|
||||
max-height: none;
|
||||
}
|
||||
.splitview-details {
|
||||
display: none;
|
||||
}
|
||||
.splitview-details.splitview-active {
|
||||
display: -moz-box;
|
||||
}
|
||||
}
|
||||
|
||||
/* filtered items are hidden */
|
||||
ol.splitview-nav > li.splitview-filtered {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* "empty list" and "all filtered" placeholders are hidden */
|
||||
.splitview-nav:empty,
|
||||
.splitview-nav.splitview-all-filtered,
|
||||
.splitview-nav + .splitview-nav.placeholder {
|
||||
display: none;
|
||||
}
|
||||
.splitview-nav.splitview-all-filtered ~ .splitview-nav.placeholder.all-filtered,
|
||||
.splitview-nav:empty ~ .splitview-nav.placeholder.empty {
|
||||
display: -moz-box;
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Style Editor code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Cedric Vivier <cedricv@neonux.com> (original author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
.splitview-nav > li hgroup > .stylesheet-error-message {
|
||||
display: none;
|
||||
}
|
||||
.splitview-nav > li.error hgroup > .stylesheet-error-message {
|
||||
display: block;
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- ***** BEGIN LICENSE BLOCK *****
|
||||
- Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
-
|
||||
- The contents of this file are subject to the Mozilla Public License Version
|
||||
- 1.1 (the "License"); you may not use this file except in compliance with
|
||||
- the License. You may obtain a copy of the License at
|
||||
- http://www.mozilla.org/MPL/
|
||||
-
|
||||
- Software distributed under the License is distributed on an "AS IS" basis,
|
||||
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
- for the specific language governing rights and limitations under the
|
||||
- License.
|
||||
-
|
||||
- The Original Code is Style Editor code.
|
||||
-
|
||||
- The Initial Developer of the Original Code is The Mozilla Foundation.
|
||||
- Portions created by the Initial Developer are Copyright (C) 2011
|
||||
- the Initial Developer. All Rights Reserved.
|
||||
-
|
||||
- Contributor(s):
|
||||
- Cedric Vivier <cedricv@neonux.com> (original author)
|
||||
-
|
||||
- Alternatively, the contents of this file may be used under the terms of
|
||||
- either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
- the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
- in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
- of those above. If you wish to allow use of your version of this file only
|
||||
- under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
- use your version of this file under the terms of the MPL, indicate your
|
||||
- decision by deleting the provisions above and replace them with the notice
|
||||
- and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
- the provisions above, a recipient may use your version of this file under
|
||||
- the terms of any one of the MPL, the GPL or the LGPL.
|
||||
-
|
||||
- ***** END LICENSE BLOCK ***** -->
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % styleEditorDTD SYSTEM "chrome://browser/locale/devtools/styleeditor.dtd" >
|
||||
%styleEditorDTD;
|
||||
]>
|
||||
<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/content/splitview.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/devtools/splitview.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/content/styleeditor.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/devtools/styleeditor.css" type="text/css"?>
|
||||
<xul:window xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
id="style-editor-chrome-window"
|
||||
title="&window.title;"
|
||||
windowtype="Tools:StyleEditor"
|
||||
width="800" height="280"
|
||||
persist="screenX screenY width height sizemode">
|
||||
<xul:script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
|
||||
|
||||
<xul:box id="style-editor-chrome" class="splitview-root">
|
||||
<xul:box class="splitview-side-details"></xul:box>
|
||||
<xul:box class="splitview-controller">
|
||||
<xul:box class="splitview-main">
|
||||
<xul:box class="toolbar">
|
||||
<xul:button class="style-editor-newButton"
|
||||
accesskey="&newButton.accesskey;"
|
||||
tooltiptext="&newButton.tooltip;"
|
||||
label="&newButton.label;"></xul:button>
|
||||
<xul:button class="style-editor-importButton"
|
||||
accesskey="&importButton.accesskey;"
|
||||
tooltiptext="&importButton.tooltip;"
|
||||
label="&importButton.label;"></xul:button>
|
||||
<xul:box class="spacer" flex="1"></xul:box>
|
||||
<xul:textbox class="splitview-filter"
|
||||
type="search"
|
||||
tooltiptext="&searchInput.tooltip;"
|
||||
placeholder="&searchInput.placeholder;"/>
|
||||
</xul:box>
|
||||
</xul:box>
|
||||
<xul:box class="splitview-nav-container">
|
||||
<ol class="splitview-nav" tabindex="0"></ol>
|
||||
<div class="splitview-nav placeholder empty">
|
||||
<p><strong>&noStyleSheet.label;</strong></p>
|
||||
<p>&noStyleSheet-tip-start.label;
|
||||
<a href="#"
|
||||
class="style-editor-newButton">&noStyleSheet-tip-action.label;</a>
|
||||
&noStyleSheet-tip-end.label;</p>
|
||||
</div>
|
||||
<div class="splitview-nav placeholder all-filtered">
|
||||
<p><strong>&searchNoResults.label;</strong></p>
|
||||
<p>
|
||||
<a href="#"
|
||||
class="splitview-filter-clearButton">&searchClearButton.label;</a>
|
||||
</p>
|
||||
</div>
|
||||
</xul:box> <!-- .splitview-nav-container -->
|
||||
</xul:box> <!-- .splitview-controller -->
|
||||
|
||||
<div id="splitview-templates" hidden="true">
|
||||
<li id="splitview-tpl-summary-stylesheet" tabindex="0">
|
||||
<a class="stylesheet-enabled" tabindex="0" href="#"
|
||||
title="&visibilityToggle.tooltip;"
|
||||
accesskey="&saveButton.accesskey;"></a>
|
||||
<hgroup class="stylesheet-info">
|
||||
<h1><a class="stylesheet-name" href="#"></a></h1>
|
||||
<h2 class="stylesheet-title"></h2>
|
||||
<h3 class="stylesheet-error-message"></h3>
|
||||
</hgroup>
|
||||
<div class="stylesheet-more">
|
||||
<hgroup class="stylesheet-stats">
|
||||
<h3 class="stylesheet-rule-count"></h3>
|
||||
</hgroup>
|
||||
<hgroup class="stylesheet-actions">
|
||||
<h1><a class="stylesheet-saveButton" href="#"
|
||||
title="&saveButton.tooltip;"
|
||||
accesskey="&saveButton.accesskey;">&saveButton.label;</a></h1>
|
||||
</hgroup>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<xul:box id="splitview-tpl-details-stylesheet" class="splitview-details">
|
||||
<div class="stylesheet-editor-input textbox"
|
||||
data-placeholder="&editorTextbox.placeholder;"></div>
|
||||
</xul:box>
|
||||
</div> <!-- #splitview-templates -->
|
||||
</xul:box> <!-- .splitview-root -->
|
||||
|
||||
<xul:script type="application/javascript"><![CDATA[
|
||||
Components.utils.import("resource:///modules/devtools/StyleEditorChrome.jsm");
|
||||
let chromeRoot = document.getElementById("style-editor-chrome");
|
||||
let contentWindow = window.arguments[0];
|
||||
let chrome = new StyleEditorChrome(chromeRoot, contentWindow);
|
||||
window.styleEditorChrome = chrome;
|
||||
]]></xul:script>
|
||||
</xul:window>
|
|
@ -0,0 +1,72 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Style Editor code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
#
|
||||
# Portions created by the Initial Developer are Copyright (C) 2007
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Cedric Vivier <cedricv@neonux.com> (original author)
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
relativesrcdir = browser/devtools/styleeditor/test
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_BROWSER_TEST_FILES = \
|
||||
browser_styleeditor_enabled.js \
|
||||
browser_styleeditor_import.js \
|
||||
browser_styleeditor_init.js \
|
||||
browser_styleeditor_loading.js \
|
||||
browser_styleeditor_new.js \
|
||||
browser_styleeditor_pretty.js \
|
||||
browser_styleeditor_readonly.js \
|
||||
browser_styleeditor_reopen.js \
|
||||
browser_styleeditor_sv_filter.js \
|
||||
browser_styleeditor_sv_keynav.js \
|
||||
browser_styleeditor_sv_resize.js \
|
||||
four.html \
|
||||
head.js \
|
||||
media.html \
|
||||
media-small.css \
|
||||
minified.html \
|
||||
simple.css \
|
||||
simple.css.gz \
|
||||
simple.css.gz^headers^ \
|
||||
simple.gz.html \
|
||||
simple.html \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_BROWSER_TEST_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
|
|
@ -0,0 +1,101 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// https rather than chrome to improve coverage
|
||||
const TESTCASE_URI = TEST_BASE_HTTPS + "simple.html";
|
||||
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
let count = 0;
|
||||
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
|
||||
aChrome.addChromeListener({
|
||||
onEditorAdded: function (aChrome, aEditor) {
|
||||
count++;
|
||||
if (count == 2) {
|
||||
// we test against first stylesheet after all are ready
|
||||
let editor = aChrome.editors[0];
|
||||
if (!editor.sourceEditor) {
|
||||
editor.addActionListener({
|
||||
onAttach: function (aEditor) {
|
||||
run(aChrome, aEditor);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
run(aChrome, editor);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
content.location = TESTCASE_URI;
|
||||
}
|
||||
|
||||
function run(aChrome, aEditor)
|
||||
{
|
||||
testEnabledToggle(aChrome, aEditor);
|
||||
}
|
||||
|
||||
function testEnabledToggle(aChrome, aEditor)
|
||||
{
|
||||
is(aEditor, aChrome.editors[0],
|
||||
"stylesheet with index 0 is the first stylesheet listed in the UI");
|
||||
|
||||
let firstStyleSheetEditor = aEditor;
|
||||
let firstStyleSheetUI = aChrome.getSummaryElementForEditor(aEditor);
|
||||
let enabledToggle = firstStyleSheetUI.querySelector(".stylesheet-enabled");
|
||||
|
||||
is(firstStyleSheetEditor.contentDocument.styleSheets[0].disabled, false,
|
||||
"first stylesheet is initially enabled");
|
||||
is(firstStyleSheetEditor.hasFlag("disabled"), false,
|
||||
"first stylesheet is initially enabled, it does not have DISABLED flag");
|
||||
is(firstStyleSheetUI.classList.contains("disabled"), false,
|
||||
"first stylesheet is initially enabled, UI does not have DISABLED class");
|
||||
|
||||
let disabledToggleCount = 0;
|
||||
firstStyleSheetEditor.addActionListener({
|
||||
onFlagChange: function (aEditor, aFlagName) {
|
||||
if (aFlagName != "disabled") {
|
||||
return;
|
||||
}
|
||||
disabledToggleCount++;
|
||||
|
||||
if (disabledToggleCount == 1) {
|
||||
is(firstStyleSheetEditor, aEditor,
|
||||
"FlagChange handler triggered for DISABLED flag on the first editor");
|
||||
is(firstStyleSheetEditor.styleSheet.disabled, true,
|
||||
"first stylesheet is now disabled");
|
||||
is(firstStyleSheetEditor.hasFlag("disabled"), true,
|
||||
"first stylesheet is now disabled, it has DISABLED flag");
|
||||
is(firstStyleSheetUI.classList.contains("disabled"), true,
|
||||
"first stylesheet is now disabled, UI has DISABLED class");
|
||||
|
||||
// now toggle it back to enabled
|
||||
waitForFocus(function () {
|
||||
EventUtils.synthesizeMouseAtCenter(enabledToggle, {}, gChromeWindow);
|
||||
}, gChromeWindow);
|
||||
return;
|
||||
}
|
||||
|
||||
// disabledToggleCount == 2
|
||||
is(firstStyleSheetEditor, aEditor,
|
||||
"FlagChange handler triggered for DISABLED flag on the first editor (2)");
|
||||
is(firstStyleSheetEditor.styleSheet.disabled, false,
|
||||
"first stylesheet is now enabled again");
|
||||
is(firstStyleSheetEditor.hasFlag("disabled"), false,
|
||||
"first stylesheet is now enabled again, it does not have DISABLED flag");
|
||||
is(firstStyleSheetUI.classList.contains("disabled"), false,
|
||||
"first stylesheet is now enabled again, UI does not have DISABLED class");
|
||||
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
waitForFocus(function () {
|
||||
EventUtils.synthesizeMouseAtCenter(enabledToggle, {}, gChromeWindow);
|
||||
}, gChromeWindow);
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// http rather than chrome to improve coverage
|
||||
const TESTCASE_URI = TEST_BASE_HTTP + "simple.html";
|
||||
|
||||
Components.utils.import("resource://gre/modules/FileUtils.jsm");
|
||||
const FILENAME = "styleeditor-import-test.css";
|
||||
const SOURCE = "body{background:red;}";
|
||||
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
|
||||
aChrome.addChromeListener({
|
||||
onContentAttach: run,
|
||||
onEditorAdded: testEditorAdded
|
||||
});
|
||||
if (aChrome.isContentAttached) {
|
||||
run(aChrome);
|
||||
}
|
||||
});
|
||||
|
||||
content.location = TESTCASE_URI;
|
||||
}
|
||||
|
||||
function run(aChrome)
|
||||
{
|
||||
is(aChrome.editors.length, 2,
|
||||
"there is 2 stylesheets initially");
|
||||
}
|
||||
|
||||
function testImport(aChrome, aEditor)
|
||||
{
|
||||
// create file to import first
|
||||
let file = FileUtils.getFile("ProfD", [FILENAME]);
|
||||
let ostream = FileUtils.openSafeFileOutputStream(file);
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
let istream = converter.convertToInputStream(SOURCE);
|
||||
NetUtil.asyncCopy(istream, ostream, function (status) {
|
||||
FileUtils.closeSafeFileOutputStream(ostream);
|
||||
|
||||
// click the import button now that the file to import is ready
|
||||
aChrome._mockImportFile = file;
|
||||
|
||||
waitForFocus(function () {
|
||||
let document = gChromeWindow.document
|
||||
let importButton = document.querySelector(".style-editor-importButton");
|
||||
EventUtils.synthesizeMouseAtCenter(importButton, {}, gChromeWindow);
|
||||
}, gChromeWindow);
|
||||
});
|
||||
}
|
||||
|
||||
let gAddedCount = 0;
|
||||
function testEditorAdded(aChrome, aEditor)
|
||||
{
|
||||
if (++gAddedCount == 2) {
|
||||
// test import after the 2 initial stylesheets have been loaded
|
||||
if (!aChrome.editors[0].sourceEditor) {
|
||||
aChrome.editors[0].addActionListener({
|
||||
onAttach: function () {
|
||||
testImport(aChrome);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
testImport(aChrome);
|
||||
}
|
||||
}
|
||||
|
||||
if (!aEditor.hasFlag("imported")) {
|
||||
return;
|
||||
}
|
||||
|
||||
ok(!aEditor.hasFlag("inline"),
|
||||
"imported stylesheet does not have INLINE flag");
|
||||
ok(aEditor.savedFile,
|
||||
"imported stylesheet will be saved directly into the same file");
|
||||
is(aEditor.getFriendlyName(), FILENAME,
|
||||
"imported stylesheet has the same name as the filename");
|
||||
|
||||
finish();
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const TESTCASE_URI = TEST_BASE + "simple.html";
|
||||
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
|
||||
aChrome.addChromeListener({
|
||||
onContentAttach: run,
|
||||
onEditorAdded: testEditorAdded
|
||||
});
|
||||
if (aChrome.isContentAttached) {
|
||||
run(aChrome);
|
||||
}
|
||||
});
|
||||
|
||||
content.location = TESTCASE_URI;
|
||||
}
|
||||
|
||||
let gContentAttachHandled = false;
|
||||
function run(aChrome)
|
||||
{
|
||||
gContentAttachHandled = true;
|
||||
is(aChrome.contentWindow.document.readyState, "complete",
|
||||
"content document is complete");
|
||||
|
||||
let SEC = gChromeWindow.styleEditorChrome;
|
||||
is(SEC, aChrome, "StyleEditorChrome object exists as new window property");
|
||||
|
||||
ok(gChromeWindow.document.title.indexOf("simple testcase") >= 0,
|
||||
"the Style Editor window title contains the document's title");
|
||||
|
||||
// check editors are instantiated
|
||||
is(SEC.editors.length, 2,
|
||||
"there is two StyleEditor instances managed");
|
||||
ok(SEC.editors[0].styleSheetIndex < SEC.editors[1].styleSheetIndex,
|
||||
"editors are ordered by styleSheetIndex");
|
||||
|
||||
// check StyleEditorChrome is a singleton wrt to the same DOMWindow
|
||||
let chromeWindow = StyleEditor.openChrome();
|
||||
is(chromeWindow, gChromeWindow,
|
||||
"attempt to edit the same document returns the same Style Editor window");
|
||||
}
|
||||
|
||||
let gEditorAddedCount = 0;
|
||||
function testEditorAdded(aChrome, aEditor)
|
||||
{
|
||||
if (!gEditorAddedCount) {
|
||||
is(gContentAttachHandled, true,
|
||||
"ContentAttach event triggered before EditorAdded");
|
||||
}
|
||||
|
||||
if (aEditor.styleSheetIndex == 0) {
|
||||
gEditorAddedCount++;
|
||||
testFirstStyleSheetEditor(aChrome, aEditor);
|
||||
}
|
||||
if (aEditor.styleSheetIndex == 1) {
|
||||
gEditorAddedCount++;
|
||||
testSecondStyleSheetEditor(aChrome, aEditor);
|
||||
}
|
||||
|
||||
if (gEditorAddedCount == 2) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
function testFirstStyleSheetEditor(aChrome, aEditor)
|
||||
{
|
||||
//testing TESTCASE's simple.css stylesheet
|
||||
is(aEditor.styleSheetIndex, 0,
|
||||
"first stylesheet is at index 0");
|
||||
|
||||
is(aEditor, aChrome.editors[0],
|
||||
"first stylesheet corresponds to StyleEditorChrome.editors[0]");
|
||||
|
||||
ok(!aEditor.hasFlag("inline"),
|
||||
"first stylesheet does not have INLINE flag");
|
||||
|
||||
let summary = aChrome.getSummaryElementForEditor(aEditor);
|
||||
ok(!summary.classList.contains("inline"),
|
||||
"first stylesheet UI does not have INLINE class");
|
||||
|
||||
let name = summary.querySelector(".stylesheet-name").textContent;
|
||||
is(name, "simple.css",
|
||||
"first stylesheet's name is `simple.css`");
|
||||
|
||||
let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
|
||||
is(parseInt(ruleCount), 1,
|
||||
"first stylesheet UI shows rule count as 1");
|
||||
|
||||
ok(summary.classList.contains("splitview-active"),
|
||||
"first stylesheet UI is focused/active");
|
||||
}
|
||||
|
||||
function testSecondStyleSheetEditor(aChrome, aEditor)
|
||||
{
|
||||
//testing TESTCASE's inline stylesheet
|
||||
is(aEditor.styleSheetIndex, 1,
|
||||
"second stylesheet is at index 1");
|
||||
|
||||
is(aEditor, aChrome.editors[1],
|
||||
"second stylesheet corresponds to StyleEditorChrome.editors[1]");
|
||||
|
||||
ok(aEditor.hasFlag("inline"),
|
||||
"second stylesheet has INLINE flag");
|
||||
|
||||
let summary = aChrome.getSummaryElementForEditor(aEditor);
|
||||
ok(summary.classList.contains("inline"),
|
||||
"second stylesheet UI has INLINE class");
|
||||
|
||||
let name = summary.querySelector(".stylesheet-name").textContent;
|
||||
ok(/^<.*>$/.test(name),
|
||||
"second stylesheet's name is surrounded by `<>`");
|
||||
|
||||
let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
|
||||
is(parseInt(ruleCount), 3,
|
||||
"second stylesheet UI shows rule count as 3");
|
||||
|
||||
ok(!summary.classList.contains("splitview-active"),
|
||||
"second stylesheet UI is NOT focused/active");
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const TESTCASE_URI = TEST_BASE + "simple.html";
|
||||
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
|
||||
// launch Style Editor right when the tab is created (before load)
|
||||
// this checks that the Style Editor still launches correctly when it is opened
|
||||
// *while* the page is still loading
|
||||
launchStyleEditorChrome(function (aChrome) {
|
||||
isnot(gBrowser.selectedBrowser.contentWindow.document.readyState, "complete",
|
||||
"content document is still loading");
|
||||
|
||||
if (!aChrome.isContentAttached) {
|
||||
aChrome.addChromeListener({
|
||||
onContentAttach: run
|
||||
});
|
||||
} else {
|
||||
run(aChrome);
|
||||
}
|
||||
});
|
||||
|
||||
content.location = TESTCASE_URI;
|
||||
}
|
||||
|
||||
function run(aChrome)
|
||||
{
|
||||
is(aChrome.contentWindow.document.readyState, "complete",
|
||||
"content document is complete");
|
||||
|
||||
finish();
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const TESTCASE_URI = TEST_BASE + "simple.html";
|
||||
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
|
||||
aChrome.addChromeListener({
|
||||
onContentAttach: run,
|
||||
onEditorAdded: testEditorAdded
|
||||
});
|
||||
if (aChrome.isContentAttached) {
|
||||
run(aChrome);
|
||||
}
|
||||
});
|
||||
|
||||
content.location = TESTCASE_URI;
|
||||
}
|
||||
|
||||
function run(aChrome)
|
||||
{
|
||||
is(aChrome.editors.length, 2,
|
||||
"there is 2 stylesheets initially");
|
||||
}
|
||||
|
||||
let gAddedCount = 0; // to add new stylesheet after the 2 initial stylesheets
|
||||
let gNewEditor; // to make sure only one new stylesheet got created
|
||||
let gCommitCount = 0; // to make sure only one Commit event is triggered
|
||||
|
||||
function testEditorAdded(aChrome, aEditor)
|
||||
{
|
||||
gAddedCount++;
|
||||
if (gAddedCount == 2) {
|
||||
waitForFocus(function () { // create a new style sheet
|
||||
let newButton = gChromeWindow.document.querySelector(".style-editor-newButton");
|
||||
EventUtils.synthesizeMouseAtCenter(newButton, {}, gChromeWindow);
|
||||
}, gChromeWindow);
|
||||
}
|
||||
if (gAddedCount != 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
ok(!gNewEditor, "creating a new stylesheet triggers one EditorAdded event");
|
||||
gNewEditor = aEditor; // above test will fail if we get a duplicate event
|
||||
|
||||
is(aChrome.editors.length, 3,
|
||||
"creating a new stylesheet added a new StyleEditor instance");
|
||||
|
||||
let listener = {
|
||||
onAttach: function (aEditor) {
|
||||
waitForFocus(function () {
|
||||
ok(aEditor.isLoaded,
|
||||
"new editor is loaded when attached");
|
||||
ok(aEditor.hasFlag("new"),
|
||||
"new editor has NEW flag");
|
||||
ok(!aEditor.hasFlag("unsaved"),
|
||||
"new editor does not have UNSAVED flag");
|
||||
|
||||
ok(aEditor.inputElement,
|
||||
"new editor has an input element attached");
|
||||
|
||||
ok(aEditor.sourceEditor.hasFocus(),
|
||||
"new editor has focus");
|
||||
|
||||
let summary = aChrome.getSummaryElementForEditor(aEditor);
|
||||
let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
|
||||
is(parseInt(ruleCount), 0,
|
||||
"new editor initially shows 0 rules");
|
||||
|
||||
let computedStyle = content.getComputedStyle(content.document.body, null);
|
||||
is(computedStyle.backgroundColor, "rgb(255, 255, 255)",
|
||||
"content's background color is initially white");
|
||||
|
||||
for each (let c in "body{background-color:red;}") {
|
||||
EventUtils.synthesizeKey(c, {}, gChromeWindow);
|
||||
}
|
||||
}, gChromeWindow) ;
|
||||
},
|
||||
|
||||
onCommit: function (aEditor) {
|
||||
gCommitCount++;
|
||||
|
||||
ok(aEditor.hasFlag("new"),
|
||||
"new editor still has NEW flag");
|
||||
ok(aEditor.hasFlag("unsaved"),
|
||||
"new editor has UNSAVED flag after modification");
|
||||
|
||||
let summary = aChrome.getSummaryElementForEditor(aEditor);
|
||||
let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
|
||||
is(parseInt(ruleCount), 1,
|
||||
"new editor shows 1 rule after modification");
|
||||
|
||||
let computedStyle = content.getComputedStyle(content.document.body, null);
|
||||
is(computedStyle.backgroundColor, "rgb(255, 0, 0)",
|
||||
"content's background color has been updated to red");
|
||||
|
||||
executeSoon(function () {
|
||||
is(gCommitCount, 1, "received only one Commit event (throttle)");
|
||||
|
||||
aEditor.removeActionListener(listener);
|
||||
|
||||
gNewEditor = null;
|
||||
finish();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
aEditor.addActionListener(listener);
|
||||
if (aEditor.sourceEditor) {
|
||||
listener.onAttach(aEditor);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const TESTCASE_URI = TEST_BASE + "minified.html";
|
||||
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
|
||||
aChrome.addChromeListener({
|
||||
onEditorAdded: function (aChrome, aEditor) {
|
||||
if (aEditor.sourceEditor) {
|
||||
run(aEditor); // already attached to input element
|
||||
} else {
|
||||
aEditor.addActionListener({
|
||||
onAttach: run
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
content.location = TESTCASE_URI;
|
||||
}
|
||||
|
||||
let editorTestedCount = 0;
|
||||
function run(aEditor)
|
||||
{
|
||||
if (aEditor.styleSheetIndex == 0) {
|
||||
let prettifiedSource = "body\{\r?\n\tbackground\:white;\r?\n\}\r?\n\r?\ndiv\{\r?\n\tfont\-size\:4em;\r?\n\tcolor\:red\r?\n\}\r?\n";
|
||||
let prettifiedSourceRE = new RegExp(prettifiedSource);
|
||||
|
||||
ok(prettifiedSourceRE.test(aEditor.sourceEditor.getText()),
|
||||
"minified source has been prettified automatically");
|
||||
editorTestedCount++;
|
||||
let chrome = gChromeWindow.styleEditorChrome;
|
||||
let summary = chrome.getSummaryElementForEditor(chrome.editors[1]);
|
||||
EventUtils.synthesizeMouseAtCenter(summary, {}, gChromeWindow);
|
||||
}
|
||||
|
||||
if (aEditor.styleSheetIndex == 1) {
|
||||
let originalSource = "body \{ background\: red; \}\r?\ndiv \{\r?\nfont\-size\: 5em;\r?\ncolor\: red\r?\n\}";
|
||||
let originalSourceRE = new RegExp(originalSource);
|
||||
|
||||
ok(originalSourceRE.test(aEditor.sourceEditor.getText()),
|
||||
"non-minified source has been left untouched");
|
||||
editorTestedCount++;
|
||||
}
|
||||
|
||||
if (editorTestedCount == 2) {
|
||||
finish();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const TESTCASE_URI = TEST_BASE + "simple.html";
|
||||
|
||||
|
||||
let gEditorAddedCount = 0;
|
||||
let gEditorReadOnlyCount = 0;
|
||||
let gChromeListener = {
|
||||
onEditorAdded: function (aChrome, aEditor) {
|
||||
gEditorAddedCount++;
|
||||
if (aEditor.readOnly) {
|
||||
gEditorReadOnlyCount++;
|
||||
}
|
||||
|
||||
if (gEditorAddedCount == aChrome.editors.length) {
|
||||
// continue testing after all editors are ready
|
||||
|
||||
is(gEditorReadOnlyCount, 0,
|
||||
"all editors are NOT read-only initially");
|
||||
|
||||
// all editors have been loaded, queue closing the content tab
|
||||
executeSoon(function () {
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
}
|
||||
},
|
||||
onContentDetach: function (aChrome) {
|
||||
// check that the UI has switched to read-only
|
||||
run(aChrome);
|
||||
}
|
||||
};
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.addTab(); // because we'll close the next one
|
||||
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
|
||||
aChrome.addChromeListener(gChromeListener);
|
||||
});
|
||||
|
||||
content.location = TESTCASE_URI;
|
||||
}
|
||||
|
||||
function run(aChrome)
|
||||
{
|
||||
let document = gChromeWindow.document;
|
||||
let disabledCount;
|
||||
let elements;
|
||||
|
||||
disabledCount = 0;
|
||||
elements = document.querySelectorAll("button,input,select");
|
||||
for (let i = 0; i < elements.length; ++i) {
|
||||
if (elements[i].hasAttribute("disabled")) {
|
||||
disabledCount++;
|
||||
}
|
||||
}
|
||||
ok(elements.length && disabledCount == elements.length,
|
||||
"all buttons, input and select elements are disabled");
|
||||
|
||||
disabledCount = 0;
|
||||
aChrome.editors.forEach(function (aEditor) {
|
||||
if (aEditor.readOnly) {
|
||||
disabledCount++;
|
||||
}
|
||||
});
|
||||
ok(aChrome.editors.length && disabledCount == aChrome.editors.length,
|
||||
"all editors are read-only");
|
||||
|
||||
aChrome.removeChromeListener(gChromeListener);
|
||||
finish();
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// http rather than chrome to improve coverage
|
||||
const TESTCASE_URI = TEST_BASE_HTTP + "simple.gz.html";
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
Components.utils.import("resource://gre/modules/FileUtils.jsm");
|
||||
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
|
||||
aChrome.addChromeListener({
|
||||
onEditorAdded: function (aChrome, aEditor) {
|
||||
if (aEditor.styleSheetIndex != 0) {
|
||||
return; // we want to test against the first stylesheet
|
||||
}
|
||||
|
||||
if (aEditor.sourceEditor) {
|
||||
run(aEditor); // already attached to input element
|
||||
} else {
|
||||
aEditor.addActionListener({
|
||||
onAttach: run
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
gChromeWindow.addEventListener("unload", function onClose() {
|
||||
gChromeWindow.removeEventListener("unload", onClose, true);
|
||||
gChromeWindow = null;
|
||||
executeSoon(function () {
|
||||
waitForFocus(function () {
|
||||
// wait that browser has focus again
|
||||
// open StyleEditorChrome again (a new one since we closed the previous one)
|
||||
launchStyleEditorChrome(function (aChrome) {
|
||||
is(gChromeWindow.document.documentElement.hasAttribute("data-marker"),
|
||||
false,
|
||||
"opened a completely new StyleEditorChrome window");
|
||||
|
||||
aChrome.addChromeListener({
|
||||
onEditorAdded: function (aChrome, aEditor) {
|
||||
if (aEditor.styleSheetIndex != 0) {
|
||||
return; // we want to test against the first stylesheet
|
||||
}
|
||||
|
||||
if (aEditor.sourceEditor) {
|
||||
testNewChrome(aEditor); // already attached to input element
|
||||
} else {
|
||||
aEditor.addActionListener({
|
||||
onAttach: testNewChrome
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}, true);
|
||||
});
|
||||
|
||||
content.location = TESTCASE_URI;
|
||||
}
|
||||
|
||||
let gFilename;
|
||||
|
||||
function run(aEditor)
|
||||
{
|
||||
gFilename = FileUtils.getFile("ProfD", ["styleeditor-test.css"])
|
||||
|
||||
aEditor.saveToFile(gFilename, function (aFile) {
|
||||
ok(aFile, "file got saved successfully");
|
||||
|
||||
aEditor.addActionListener({
|
||||
onFlagChange: function (aEditor, aFlag) {
|
||||
if (aFlag != "unsaved") {
|
||||
return;
|
||||
}
|
||||
|
||||
ok(aEditor.hasFlag("unsaved"),
|
||||
"first stylesheet has UNSAVED flag after making a change");
|
||||
|
||||
// marker used to check it does not exist when we reopen
|
||||
// ie. the window we opened is indeed a new one
|
||||
gChromeWindow.document.documentElement.setAttribute("data-marker", "true");
|
||||
gChromeWindow.close();
|
||||
}
|
||||
});
|
||||
|
||||
waitForFocus(function () {
|
||||
// insert char so that this stylesheet has the UNSAVED flag
|
||||
EventUtils.synthesizeKey("x", {}, gChromeWindow);
|
||||
}, gChromeWindow);
|
||||
});
|
||||
}
|
||||
|
||||
function testNewChrome(aEditor)
|
||||
{
|
||||
ok(aEditor.savedFile,
|
||||
"first stylesheet editor will save directly into the same file");
|
||||
|
||||
is(aEditor.getFriendlyName(), gFilename.leafName,
|
||||
"first stylesheet still has the filename as it was saved");
|
||||
gFilename = null;
|
||||
|
||||
ok(aEditor.hasFlag("unsaved"),
|
||||
"first stylesheet still has UNSAVED flag at reopening");
|
||||
|
||||
ok(!aEditor.hasFlag("inline"),
|
||||
"first stylesheet does not have INLINE flag");
|
||||
|
||||
ok(!aEditor.hasFlag("error"),
|
||||
"editor does not have error flag initially");
|
||||
let hadError = false;
|
||||
|
||||
let onSaveCallback = function (aFile) {
|
||||
aEditor.addActionListener({
|
||||
onFlagChange: function (aEditor, aFlag) {
|
||||
if (!hadError && aFlag == "error") {
|
||||
ok(aEditor.hasFlag("error"),
|
||||
"editor has ERROR flag after attempting to save with invalid path");
|
||||
hadError = true;
|
||||
|
||||
// save using source editor key binding (previous successful path)
|
||||
waitForFocus(function () {
|
||||
EventUtils.synthesizeKey("S", {accelKey: true}, gChromeWindow);
|
||||
}, gChromeWindow);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hadError && aFlag == "unsaved") {
|
||||
executeSoon(function () {
|
||||
ok(!aEditor.hasFlag("unsaved"),
|
||||
"first stylesheet has no UNSAVED flag after successful save");
|
||||
ok(!aEditor.hasFlag("error"),
|
||||
"ERROR flag has been removed since last operation succeeded");
|
||||
finish();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let os = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
|
||||
if (os == "WINNT") {
|
||||
aEditor.saveToFile("C:\\I_DO_NOT_EXIST_42\\bogus.css", onSaveCallback);
|
||||
} else {
|
||||
aEditor.saveToFile("/I_DO_NOT_EXIST_42/bogos.css", onSaveCallback);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const TESTCASE_URI = TEST_BASE + "simple.html";
|
||||
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
|
||||
aChrome.addChromeListener({
|
||||
onContentAttach: run
|
||||
});
|
||||
if (aChrome.isContentAttached) {
|
||||
run(aChrome);
|
||||
}
|
||||
});
|
||||
|
||||
content.location = TESTCASE_URI;
|
||||
}
|
||||
|
||||
function getFilteredItemsCount(nav)
|
||||
{
|
||||
let matches = nav.querySelectorAll("*.splitview-filtered");
|
||||
return matches ? matches.length : 0;
|
||||
}
|
||||
|
||||
function run(aChrome)
|
||||
{
|
||||
aChrome.editors[0].addActionListener({onAttach: onFirstEditorAttach});
|
||||
aChrome.editors[1].addActionListener({onAttach: onSecondEditorAttach});
|
||||
}
|
||||
|
||||
function onFirstEditorAttach(aEditor)
|
||||
{
|
||||
let filter = gChromeWindow.document.querySelector(".splitview-filter");
|
||||
// force the command event on input since it is not possible to disable
|
||||
// the search textbox's timeout.
|
||||
let forceCommandEvent = function forceCommandEvent() {
|
||||
let evt = gChromeWindow.document.createEvent("XULCommandEvent");
|
||||
evt.initCommandEvent("command", true, true, gChromeWindow, 0, false, false,
|
||||
false, false, null);
|
||||
filter.dispatchEvent(evt);
|
||||
}
|
||||
filter.addEventListener("input", forceCommandEvent, false);
|
||||
|
||||
let nav = gChromeWindow.document.querySelector(".splitview-nav");
|
||||
nav.focus();
|
||||
|
||||
is(getFilteredItemsCount(nav), 0,
|
||||
"there is 0 filtered item initially");
|
||||
|
||||
waitForFocus(function () {
|
||||
// Search [s] (type-on-search since we focused nav above - not filter directly)
|
||||
EventUtils.synthesizeKey("s", {}, gChromeWindow);
|
||||
|
||||
// the search space is "simple.css" and "inline stylesheet #1" (2 sheets)
|
||||
is(getFilteredItemsCount(nav), 0,
|
||||
"there is 0 filtered item if searching for 's'");
|
||||
|
||||
EventUtils.synthesizeKey("i", {}, gChromeWindow); // Search [si]
|
||||
|
||||
is(getFilteredItemsCount(nav), 1, // inline stylesheet is filtered
|
||||
"there is 1 filtered item if searching for 's'");
|
||||
|
||||
// use uppercase to check that filtering is case-insensitive
|
||||
EventUtils.synthesizeKey("X", {}, gChromeWindow); // Search [siX]
|
||||
is(getFilteredItemsCount(nav), 2,
|
||||
"there is 2 filtered items if searching for 's'"); // no match
|
||||
|
||||
// clear the search
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {}, gChromeWindow);
|
||||
|
||||
is(filter.value, "",
|
||||
"filter is back to empty");
|
||||
is(getFilteredItemsCount(nav), 0,
|
||||
"there is 0 filtered item when filter is empty again");
|
||||
|
||||
for each (let c in "inline") {
|
||||
EventUtils.synthesizeKey(c, {}, gChromeWindow);
|
||||
}
|
||||
|
||||
is(getFilteredItemsCount(nav), 1, // simple.css is filtered
|
||||
"there is 1 filtered item if searching for 'inline'");
|
||||
|
||||
// auto-select the only result (enter the editor)
|
||||
EventUtils.synthesizeKey("VK_ENTER", {}, gChromeWindow);
|
||||
|
||||
filter.removeEventListener("input", forceCommandEvent, false);
|
||||
}, gChromeWindow);
|
||||
}
|
||||
|
||||
function onSecondEditorAttach(aEditor)
|
||||
{
|
||||
ok(aEditor.sourceEditor.hasFocus(),
|
||||
"second editor has been selected and focused automatically.");
|
||||
|
||||
finish();
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const TESTCASE_URI = TEST_BASE + "four.html";
|
||||
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
|
||||
aChrome.addChromeListener({
|
||||
onContentAttach: run
|
||||
});
|
||||
if (aChrome.isContentAttached) {
|
||||
run(aChrome);
|
||||
}
|
||||
});
|
||||
|
||||
content.location = TESTCASE_URI;
|
||||
}
|
||||
|
||||
let gChrome;
|
||||
|
||||
function run(aChrome)
|
||||
{
|
||||
gChrome = aChrome;
|
||||
aChrome.editors[0].addActionListener({onAttach: onEditor0Attach});
|
||||
aChrome.editors[2].addActionListener({onAttach: onEditor2Attach});
|
||||
}
|
||||
|
||||
function getStylesheetNameLinkFor(aEditor)
|
||||
{
|
||||
return gChrome.getSummaryElementForEditor(aEditor).querySelector(".stylesheet-name");
|
||||
}
|
||||
|
||||
function onEditor0Attach(aEditor)
|
||||
{
|
||||
waitForFocus(function () {
|
||||
let summary = gChrome.getSummaryElementForEditor(aEditor);
|
||||
EventUtils.synthesizeMouseAtCenter(summary, {}, gChromeWindow);
|
||||
|
||||
let item = getStylesheetNameLinkFor(gChrome.editors[0]);
|
||||
is(gChromeWindow.document.activeElement, item,
|
||||
"editor 0 item is the active element");
|
||||
|
||||
EventUtils.synthesizeKey("VK_DOWN", {}, gChromeWindow);
|
||||
item = getStylesheetNameLinkFor(gChrome.editors[1]);
|
||||
is(gChromeWindow.document.activeElement, item,
|
||||
"editor 1 item is the active element");
|
||||
|
||||
EventUtils.synthesizeKey("VK_HOME", {}, gChromeWindow);
|
||||
item = getStylesheetNameLinkFor(gChrome.editors[0]);
|
||||
is(gChromeWindow.document.activeElement, item,
|
||||
"fist editor item is the active element");
|
||||
|
||||
EventUtils.synthesizeKey("VK_END", {}, gChromeWindow);
|
||||
item = getStylesheetNameLinkFor(gChrome.editors[3]);
|
||||
is(gChromeWindow.document.activeElement, item,
|
||||
"last editor item is the active element");
|
||||
|
||||
EventUtils.synthesizeKey("VK_UP", {}, gChromeWindow);
|
||||
item = getStylesheetNameLinkFor(gChrome.editors[2]);
|
||||
is(gChromeWindow.document.activeElement, item,
|
||||
"editor 2 item is the active element");
|
||||
|
||||
EventUtils.synthesizeKey("VK_RETURN", {}, gChromeWindow);
|
||||
// this will attach and give focus editor 2
|
||||
}, gChromeWindow);
|
||||
}
|
||||
|
||||
function onEditor2Attach(aEditor)
|
||||
{
|
||||
ok(aEditor.sourceEditor.hasFocus(),
|
||||
"editor 2 has focus");
|
||||
|
||||
gChrome = null;
|
||||
finish();
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const TESTCASE_URI = TEST_BASE + "simple.html";
|
||||
|
||||
let gOriginalWidth; // these are set by run() when gChromeWindow is ready
|
||||
let gOriginalHeight;
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
|
||||
if (aChrome.isContentAttached) {
|
||||
run(aChrome);
|
||||
} else {
|
||||
aChrome.addChromeListener({
|
||||
onContentAttach: run
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
content.location = TESTCASE_URI;
|
||||
}
|
||||
|
||||
function run(aChrome)
|
||||
{
|
||||
is(aChrome.editors.length, 2,
|
||||
"there is 2 stylesheets initially");
|
||||
|
||||
aChrome.editors[0].addActionListener({
|
||||
onAttach: function onEditorAttached(aEditor) {
|
||||
let originalSourceEditor = aEditor.sourceEditor;
|
||||
aEditor.sourceEditor.setCaretOffset(4); // to check the caret is preserved
|
||||
|
||||
// queue a resize to inverse aspect ratio
|
||||
// this will trigger a detach and reattach (to workaround bug 254144)
|
||||
executeSoon(function () {
|
||||
waitForFocus(function () {
|
||||
gOriginalWidth = gChromeWindow.outerWidth;
|
||||
gOriginalHeight = gChromeWindow.outerHeight;
|
||||
gChromeWindow.resizeTo(120, 480);
|
||||
|
||||
executeSoon(function () {
|
||||
is(aEditor.sourceEditor, originalSourceEditor,
|
||||
"the editor still references the same SourceEditor instance");
|
||||
is(aEditor.sourceEditor.getCaretOffset(), 4,
|
||||
"the caret position has been preserved");
|
||||
|
||||
// queue a resize to original aspect ratio
|
||||
waitForFocus(function () {
|
||||
gChromeWindow.resizeTo(gOriginalWidth, gOriginalHeight);
|
||||
executeSoon(function () {
|
||||
finish();
|
||||
});
|
||||
}, gChromeWindow);
|
||||
});
|
||||
}, gChromeWindow);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>four stylesheets</title>
|
||||
<link rel="stylesheet" type="text/css" media="scren" href="simple.css"/>
|
||||
<style type="text/css">
|
||||
div {
|
||||
font-size: 2em;
|
||||
}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
span {
|
||||
font-size: 3em;
|
||||
}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
p {
|
||||
font-size: 4em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>four <span>stylesheets</span></div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,46 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const TEST_BASE = "chrome://mochitests/content/browser/browser/devtools/styleeditor/test/";
|
||||
const TEST_BASE_HTTP = "http://example.com/browser/browser/devtools/styleeditor/test/";
|
||||
const TEST_BASE_HTTPS = "https://example.com/browser/browser/devtools/styleeditor/test/";
|
||||
|
||||
let gChromeWindow; //StyleEditorChrome window
|
||||
|
||||
function cleanup()
|
||||
{
|
||||
if (gChromeWindow) {
|
||||
gChromeWindow.close();
|
||||
gChromeWindow = null;
|
||||
}
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
gBrowser.removeCurrentTab();
|
||||
}
|
||||
}
|
||||
|
||||
function launchStyleEditorChrome(aCallback)
|
||||
{
|
||||
gChromeWindow = StyleEditor.openChrome();
|
||||
if (gChromeWindow.document.readyState != "complete") {
|
||||
gChromeWindow.addEventListener("load", function onChromeLoad() {
|
||||
gChromeWindow.removeEventListener("load", onChromeLoad, true);
|
||||
gChromeWindow.styleEditorChrome._alwaysDisableAnimations = true;
|
||||
aCallback(gChromeWindow.styleEditorChrome);
|
||||
}, true);
|
||||
} else {
|
||||
gChromeWindow.styleEditorChrome._alwaysDisableAnimations = true;
|
||||
aCallback(gChromeWindow.styleEditorChrome);
|
||||
}
|
||||
}
|
||||
|
||||
function addTabAndLaunchStyleEditorChromeWhenLoaded(aCallback)
|
||||
{
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
|
||||
launchStyleEditorChrome(aCallback);
|
||||
}, true);
|
||||
}
|
||||
|
||||
registerCleanupFunction(cleanup);
|
|
@ -0,0 +1,5 @@
|
|||
/* this stylesheet applies when min-width<400px */
|
||||
body {
|
||||
background: red;
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="simple.css" media="screen,print"/>
|
||||
<link rel="stylesheet" type="text/css" href="media-small.css" media="screen and (min-width: 200px)"/>
|
||||
</head>
|
||||
<body>
|
||||
<div>test for media labels</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>minified testcase</title>
|
||||
<style type="text/css"><!--
|
||||
body{background:white;}div{font-size:4em;color:red}
|
||||
--></style>
|
||||
<style type="text/css">body { background: red; }
|
||||
div {
|
||||
font-size: 5em;
|
||||
color: red
|
||||
}</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>minified <span>testcase</span></div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,8 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
Двоичный файл не отображается.
|
@ -0,0 +1,4 @@
|
|||
Vary: Accept-Encoding
|
||||
Content-Encoding: gzip
|
||||
Content-Type: text/css
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>simple testcase</title>
|
||||
<link rel="stylesheet" type="text/css" media="scren" href="simple.css.gz"/>
|
||||
<style type="text/css">
|
||||
body {
|
||||
background: white;
|
||||
}
|
||||
|
||||
div {
|
||||
font-size: 4em;
|
||||
}
|
||||
|
||||
div > span {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>simple <span>testcase</span></div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,23 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>simple testcase</title>
|
||||
<link rel="stylesheet" type="text/css" media="scren" href="simple.css"/>
|
||||
<style type="text/css">
|
||||
body {
|
||||
background: white;
|
||||
}
|
||||
|
||||
div {
|
||||
font-size: 4em;
|
||||
}
|
||||
|
||||
div > span {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>simple <span>testcase</span></div>
|
||||
</body>
|
||||
</html>
|
|
@ -655,13 +655,10 @@ function CssRuleView(aDoc, aStore)
|
|||
this.doc = aDoc;
|
||||
this.store = aStore;
|
||||
|
||||
this.element = this.doc.createElementNS(HTML_NS, "div");
|
||||
this.element = this.doc.createElementNS(XUL_NS, "vbox");
|
||||
this.element.setAttribute("tabindex", "0");
|
||||
this.element.classList.add("ruleview");
|
||||
|
||||
// Give a relative position for the inplace editor's measurement
|
||||
// span to be placed absolutely against.
|
||||
this.element.style.position = "relative";
|
||||
this.element.flex = 1;
|
||||
}
|
||||
|
||||
CssRuleView.prototype = {
|
||||
|
@ -761,6 +758,10 @@ RuleEditor.prototype = {
|
|||
this.element = this.doc.createElementNS(HTML_NS, "div");
|
||||
this.element._ruleEditor = this;
|
||||
|
||||
// Give a relative position for the inplace editor's measurement
|
||||
// span to be placed absolutely against.
|
||||
this.element.style.position = "relative";
|
||||
|
||||
// Add the source link.
|
||||
let source = createChild(this.element, "div", {
|
||||
class: "ruleview-rule-source",
|
||||
|
|
|
@ -35,6 +35,10 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
.ruleview {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.ruleview-computedlist:not(.styleinspector-open) {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -47,7 +47,6 @@ include $(topsrcdir)/config/rules.mk
|
|||
|
||||
_BROWSER_TEST_FILES = \
|
||||
browser_styleinspector.js \
|
||||
browser_styleinspector_webconsole.js \
|
||||
browser_bug683672.js \
|
||||
browser_styleinspector_bug_672746_default_styles.js \
|
||||
browser_styleinspector_bug_672744_search_filter.js \
|
||||
|
@ -63,7 +62,6 @@ _BROWSER_TEST_FILES = \
|
|||
$(NULL)
|
||||
|
||||
_BROWSER_TEST_PAGES = \
|
||||
browser_styleinspector_webconsole.htm \
|
||||
browser_bug683672.html \
|
||||
$(NULL)
|
||||
|
||||
|
|
|
@ -1,185 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html dir="ltr" xml:lang="en-US" lang="en-US"><head>
|
||||
<title>Style inspector test</title>
|
||||
<style>
|
||||
.text {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.container > .text {
|
||||
font-family: serif;
|
||||
}
|
||||
|
||||
.text2 {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.text3 {
|
||||
font-size: 100px;
|
||||
}
|
||||
|
||||
.text[dir=rtl] {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.container .text {
|
||||
font-family: fantasy;
|
||||
}
|
||||
|
||||
#container .text {
|
||||
font-family: fantasy;
|
||||
}
|
||||
|
||||
.container > .text {
|
||||
font-family: cursive;
|
||||
}
|
||||
|
||||
#container > .text {
|
||||
font-family: cursive;
|
||||
}
|
||||
|
||||
#container > .dummy, #container > .dummy2 {
|
||||
font-family: cursive;
|
||||
}
|
||||
|
||||
div {
|
||||
font-family: fantasy;
|
||||
}
|
||||
|
||||
#container {
|
||||
font-family: fantasy;
|
||||
}
|
||||
|
||||
#container > span {
|
||||
font-family: cursive;
|
||||
}
|
||||
|
||||
#container .dummy {
|
||||
font-family: fantasy;
|
||||
}
|
||||
|
||||
html + .dummy {
|
||||
font-family: fantasy;
|
||||
}
|
||||
|
||||
span + span {
|
||||
font-family: fantasy;
|
||||
}
|
||||
|
||||
span[id=text] {
|
||||
font-family: cursive;
|
||||
}
|
||||
|
||||
span[att=glue] {
|
||||
font-family: cursive;
|
||||
}
|
||||
|
||||
span::before {
|
||||
font-family: cursive;
|
||||
content: "START ";
|
||||
}
|
||||
|
||||
span::after {
|
||||
font-family: cursive;
|
||||
content: " END";
|
||||
}
|
||||
|
||||
spawn::before {
|
||||
font-family: cursive;
|
||||
content: "START ";
|
||||
}
|
||||
|
||||
spawn::after {
|
||||
font-family: cursive;
|
||||
content: " END";
|
||||
}
|
||||
|
||||
a:link {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.link:link {
|
||||
font-family: fantasy;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.link:visited {
|
||||
font-family: fantasy;
|
||||
}
|
||||
|
||||
a:active {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.link:active {
|
||||
font-family: fantasy;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
font-family: fantasy;
|
||||
}
|
||||
|
||||
a:focus {
|
||||
font-family: sans-serif;
|
||||
outline: 5px solid #0f0;
|
||||
}
|
||||
|
||||
.link:focus {
|
||||
font-family: fantasy;
|
||||
}
|
||||
|
||||
span::first-letter {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.text::first-letter {
|
||||
font-family: fantasy;
|
||||
}
|
||||
|
||||
span::first-line {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.text::first-line {
|
||||
font-family: fantasy;
|
||||
}
|
||||
|
||||
#container:first-child {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
div:first-child {
|
||||
font-family: fantasy;
|
||||
}
|
||||
|
||||
span:lang(en) {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
span:lang(it) {
|
||||
font-family: fantasy;
|
||||
}
|
||||
|
||||
html::-moz-selection {
|
||||
background-color: #f00;
|
||||
font-family: fantasy;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>font-size</h2>
|
||||
<div id="container">
|
||||
<span id="text" lang="en" class="text">Use inspectstyle($('text')) to inspect me</span><br />
|
||||
<span id="text2" class="text2">Use inspectstyle($('text2'))</span><br />
|
||||
<a class="link" href="#">Some Link</a>
|
||||
<h2>font-family has a single unmatched rule</h2>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,194 +0,0 @@
|
|||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is DevTools test code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Michael Ratcliffe <mratcliffe@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
// Tests that inspectstyle(node) works properly
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/styleinspector/test/browser/browser_styleinspector_webconsole.htm";
|
||||
|
||||
let doc;
|
||||
let jsterm;
|
||||
let hudBox;
|
||||
let stylePanels = [];
|
||||
|
||||
function test() {
|
||||
addTab(TEST_URI);
|
||||
browser.addEventListener("DOMContentLoaded", prepConsole, false);
|
||||
}
|
||||
|
||||
function prepConsole() {
|
||||
browser.removeEventListener("DOMContentLoaded", prepConsole, false);
|
||||
doc = content.document;
|
||||
openConsole();
|
||||
|
||||
ok(window.StyleInspector, "StyleInspector exists");
|
||||
|
||||
let hud = HUDService.getHudByWindow(content);
|
||||
ok(hud, "we have a console");
|
||||
|
||||
hudBox = hud.HUDBox;
|
||||
ok(hudBox, "we have the console display");
|
||||
|
||||
jsterm = hud.jsterm;
|
||||
ok(jsterm, "we have a jsterm");
|
||||
|
||||
openStyleInspector1();
|
||||
}
|
||||
|
||||
function openStyleInspector1() {
|
||||
info("opening style inspector instance 1");
|
||||
Services.obs.addObserver(openStyleInspector2, "StyleInspector-opened", false);
|
||||
jsterm.execute("inspectstyle($('text'))");
|
||||
}
|
||||
|
||||
function openStyleInspector2() {
|
||||
Services.obs.removeObserver(openStyleInspector2, "StyleInspector-opened", false);
|
||||
info("opening style inspector instance 2");
|
||||
Services.obs.addObserver(openStyleInspector3, "StyleInspector-opened", false);
|
||||
jsterm.execute("inspectstyle($('text2'))");
|
||||
}
|
||||
|
||||
function openStyleInspector3() {
|
||||
Services.obs.removeObserver(openStyleInspector3, "StyleInspector-opened", false);
|
||||
info("opening style inspector instance 3");
|
||||
Services.obs.addObserver(teststylePanels, "StyleInspector-opened", false);
|
||||
jsterm.execute("inspectstyle($('container'))");
|
||||
}
|
||||
|
||||
function teststylePanels() {
|
||||
Services.obs.removeObserver(teststylePanels, "StyleInspector-opened", false);
|
||||
|
||||
info("adding style inspector instances to stylePanels array");
|
||||
let popupSet = document.getElementById("mainPopupSet");
|
||||
let len = popupSet.childNodes.length - 3;
|
||||
stylePanels.push(popupSet.childNodes[len++]);
|
||||
stylePanels.push(popupSet.childNodes[len++]);
|
||||
stylePanels.push(popupSet.childNodes[len++]);
|
||||
|
||||
let eltArray = [
|
||||
doc.getElementById("text"),
|
||||
doc.getElementById("text2"),
|
||||
doc.getElementById("container")
|
||||
];
|
||||
|
||||
// We have 3 style inspector instances, each with an element selected:
|
||||
// 1. #text
|
||||
// 2. #text2
|
||||
// 3. #container
|
||||
//
|
||||
// We will loop through each instance and check that the correct node is
|
||||
// selected and that the correct css selector has been selected as active
|
||||
info("looping through array to check initialization");
|
||||
for (let i = 0, max = stylePanels.length; i < max; i++) {
|
||||
ok(stylePanels[i], "style inspector instance " + i +
|
||||
" correctly initialized");
|
||||
is(stylePanels[i].state, "open", "style inspector " + i + " is open");
|
||||
|
||||
/* // the following should be tested elsewhere
|
||||
// TODO bug 696166
|
||||
let htmlTree = stylePanels[i].cssHtmlTree;
|
||||
let cssLogic = htmlTree.cssLogic;
|
||||
let elt = eltArray[i];
|
||||
let eltId = elt.id;
|
||||
|
||||
// Check that the correct node is selected
|
||||
is(elt, htmlTree.viewedElement,
|
||||
"style inspector node matches the selected node (id=" + eltId + ")");
|
||||
is(htmlTree.viewedElement, stylePanels[i].cssLogic.viewedElement,
|
||||
"cssLogic node matches the cssHtmlTree node (id=" + eltId + ")");
|
||||
|
||||
// Check that the correct css selector has been selected as active
|
||||
let matchedSelectors = cssLogic.getPropertyInfo("font-family").matchedSelectors;
|
||||
let sel = matchedSelectors[0];
|
||||
let selector = sel.selector.text;
|
||||
let value = sel.value;
|
||||
|
||||
// Because we know which selectors should be the best match and what their
|
||||
// values should be we can check them
|
||||
switch(eltId) {
|
||||
case "text":
|
||||
is(selector, "#container > .text", "correct best match for #text");
|
||||
is(value, "cursive", "correct css property value for #" + eltId);
|
||||
break;
|
||||
case "text2":
|
||||
is(selector, "#container > span", "correct best match for #text2");
|
||||
is(value, "cursive", "correct css property value for #" + eltId);
|
||||
break;
|
||||
case "container":
|
||||
is(selector, "#container", "correct best match for #container");
|
||||
is(value, "fantasy", "correct css property value for #" + eltId);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
info("hiding stylePanels[1]");
|
||||
Services.obs.addObserver(styleInspectorClosedByHide,
|
||||
"StyleInspector-closed", false);
|
||||
stylePanels[1].hidePopup();
|
||||
}
|
||||
|
||||
function styleInspectorClosedByHide()
|
||||
{
|
||||
Services.obs.removeObserver(styleInspectorClosedByHide, "StyleInspector-closed", false);
|
||||
is(stylePanels[0].state, "open", "instance stylePanels[0] is still open");
|
||||
isnot(stylePanels[1].state, "open", "instance stylePanels[1] is not open");
|
||||
is(stylePanels[2].state, "open", "instance stylePanels[2] is still open");
|
||||
|
||||
info("closing web console");
|
||||
Services.obs.addObserver(styleInspectorClosedFromConsole1,
|
||||
"StyleInspector-closed", false);
|
||||
closeConsole();
|
||||
}
|
||||
|
||||
function styleInspectorClosedFromConsole1()
|
||||
{
|
||||
Services.obs.removeObserver(styleInspectorClosedFromConsole1,
|
||||
"StyleInspector-closed", false);
|
||||
info("Style Inspector 1 and 2 closed");
|
||||
executeSoon(cleanUp);
|
||||
}
|
||||
|
||||
function cleanUp()
|
||||
{
|
||||
let panels = document.querySelector("panel[hudToolId]");
|
||||
ok(!panels,
|
||||
"all style inspector panels are now detached and ready for garbage collection");
|
||||
|
||||
info("cleaning up");
|
||||
|
||||
doc = hudBox = stylePanels = jsterm = null;
|
||||
finishTest();
|
||||
}
|
|
@ -79,12 +79,6 @@ XPCOMUtils.defineLazyGetter(this, "gcli", function () {
|
|||
return obj.gcli;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "StyleInspector", function () {
|
||||
var obj = {};
|
||||
Cu.import("resource:///modules/devtools/StyleInspector.jsm", obj);
|
||||
return obj.StyleInspector;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "CssRuleView", function() {
|
||||
let tmp = {};
|
||||
Cu.import("resource:///modules/devtools/CssRuleView.jsm", tmp);
|
||||
|
@ -1860,10 +1854,6 @@ HUD_SERVICE.prototype =
|
|||
for (let i = 0; i < panels.length; i++) {
|
||||
panels[i].hidePopup();
|
||||
}
|
||||
panels = popupset.querySelectorAll("panel[hudToolId=" + aHUDId + "]");
|
||||
for (let i = 0; i < panels.length; i++) {
|
||||
panels[i].hidePopup();
|
||||
}
|
||||
|
||||
let id = ConsoleUtils.supString(aHUDId);
|
||||
Services.obs.notifyObservers(id, "web-console-destroyed", null);
|
||||
|
@ -4598,40 +4588,6 @@ function JSTermHelper(aJSTerm)
|
|||
propPanel.panel.setAttribute("hudId", aJSTerm.hudId);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inspects the passed aNode in the style inspector.
|
||||
*
|
||||
* @param object aNode
|
||||
* aNode to inspect.
|
||||
* @returns void
|
||||
*/
|
||||
aJSTerm.sandbox.inspectstyle = function JSTH_inspectstyle(aNode)
|
||||
{
|
||||
let errstr = null;
|
||||
aJSTerm.helperEvaluated = true;
|
||||
|
||||
if (!Services.prefs.getBoolPref("devtools.styleinspector.enabled")) {
|
||||
errstr = HUDService.getStr("inspectStyle.styleInspectorNotEnabled");
|
||||
} else if (!aNode) {
|
||||
errstr = HUDService.getStr("inspectStyle.nullObjectPassed");
|
||||
} else if (!(aNode instanceof Ci.nsIDOMNode)) {
|
||||
errstr = HUDService.getStr("inspectStyle.mustBeDomNode");
|
||||
} else if (!(aNode.style instanceof Ci.nsIDOMCSSStyleDeclaration)) {
|
||||
errstr = HUDService.getStr("inspectStyle.nodeHasNoStyleProps");
|
||||
}
|
||||
|
||||
if (!errstr) {
|
||||
let chromeWin = HUDService.getHudReferenceById(aJSTerm.hudId).chromeWindow;
|
||||
let styleInspector = new StyleInspector(chromeWin);
|
||||
styleInspector.createPanel(false, function() {
|
||||
styleInspector.panel.setAttribute("hudToolId", aJSTerm.hudId);
|
||||
styleInspector.open(aNode);
|
||||
});
|
||||
} else {
|
||||
aJSTerm.writeOutput(errstr + "\n", CATEGORY_OUTPUT, SEVERITY_ERROR);
|
||||
}
|
||||
};
|
||||
|
||||
aJSTerm.sandbox.inspectrules = function JSTH_inspectrules(aNode)
|
||||
{
|
||||
aJSTerm.helperEvaluated = true;
|
||||
|
@ -5173,6 +5129,7 @@ JSTerm.prototype = {
|
|||
}
|
||||
|
||||
hud.HUDBox.lastTimestamp = 0;
|
||||
hud.groupDepth = 0;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -43,6 +43,14 @@ function onLoad(aEvent) {
|
|||
is(msg.length, 4, "four message nodes displayed");
|
||||
is(msg[3].style.marginLeft, GROUP_INDENT + "px", "correct group indent found");
|
||||
|
||||
// Test that clearing the console removes the indentation.
|
||||
hud.jsterm.clearOutput();
|
||||
content.console.log("cleared");
|
||||
findLogEntry("cleared");
|
||||
let msg = outputNode.querySelectorAll(".webconsole-msg-icon-container");
|
||||
is(msg.length, 1, "one message node displayed");
|
||||
is(msg[0].style.marginLeft, "0px", "correct group indent found");
|
||||
|
||||
finishTest();
|
||||
}
|
||||
|
||||
|
|
|
@ -444,6 +444,8 @@
|
|||
@BINPATH@/res/EditorOverride.css
|
||||
@BINPATH@/res/contenteditable.css
|
||||
@BINPATH@/res/designmode.css
|
||||
@BINPATH@/res/TopLevelImageDocument.css
|
||||
@BINPATH@/res/TopLevelVideoDocument.css
|
||||
@BINPATH@/res/table-add-column-after-active.gif
|
||||
@BINPATH@/res/table-add-column-after-hover.gif
|
||||
@BINPATH@/res/table-add-column-after.gif
|
||||
|
|
|
@ -684,6 +684,7 @@ uninstall/uninst.exe
|
|||
uninstall/uninstall.exe
|
||||
xpicleanup@BIN_SUFFIX@
|
||||
#ifdef MOZ_OMNIJAR
|
||||
omni.jar
|
||||
chrome/af.jar
|
||||
chrome/af.manifest
|
||||
chrome/ar.jar
|
||||
|
@ -1087,6 +1088,7 @@ xpicleanup@BIN_SUFFIX@
|
|||
#else
|
||||
components/binary.manifest
|
||||
omni.jar
|
||||
omni.ja
|
||||
#endif
|
||||
#ifdef XP_MACOSX
|
||||
../Plug-Ins/PrintPDE.plugin/Contents/Info.plist
|
||||
|
|
|
@ -220,6 +220,13 @@ can reach it easily. -->
|
|||
<!ENTITY inspectStyleButton.label "Style">
|
||||
<!ENTITY inspectStyleButton.accesskey "S">
|
||||
|
||||
<!-- LOCALIZATION NOTE (styleeditor.label): This menu item label appears
|
||||
- in the Tools menu. -->
|
||||
<!ENTITY styleeditor.label "Style Editor">
|
||||
<!ENTITY styleeditor.accesskey "y">
|
||||
<!ENTITY styleeditor.keycode "VK_F7">
|
||||
<!ENTITY styleeditor.keytext "F7">
|
||||
|
||||
<!ENTITY getMoreDevtoolsCmd.label "Get More Tools">
|
||||
<!ENTITY getMoreDevtoolsCmd.accesskey "M">
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
<!-- LOCALIZATION NOTE : FILE This file contains the Style Editor window strings -->
|
||||
<!-- LOCALIZATION NOTE : FILE Do not translate commandkeys -->
|
||||
<!-- LOCALIZATION NOTE : The correct localization of this file might be to keep
|
||||
it in English, or another language commonly spoken among web developers.
|
||||
You want to make that choice consistent across the developer tools.
|
||||
A good criteria is the language in which you'd find the best documentation
|
||||
on web development on the web. -->
|
||||
|
||||
<!-- LOCALIZATION NOTE (window.title): This is the default title for Style Editor
|
||||
main window. NB: the property chromeWindowTitle in styleeditor.properties
|
||||
is used to dynamically update the Style Editor window title with the title
|
||||
of the web page it is editing. -->
|
||||
<!ENTITY window.title "Style Editor">
|
||||
|
||||
<!ENTITY newButton.label "New">
|
||||
<!ENTITY newButton.tooltip "Create and append a new style sheet to the document">
|
||||
<!ENTITY newButton.accesskey "N">
|
||||
<!ENTITY newButton.commandkey "n">
|
||||
|
||||
<!ENTITY importButton.label "Import…">
|
||||
<!ENTITY importButton.tooltip "Import and append an existing style sheet to the document">
|
||||
<!ENTITY importButton.accesskey "I">
|
||||
<!ENTITY importButton.commandkey "i">
|
||||
|
||||
<!ENTITY searchInput.tooltip "Filter style sheets by name">
|
||||
<!ENTITY searchInput.placeholder "Find style sheet">
|
||||
|
||||
<!-- LOCALIZATION NOTE (searchNoResults): This is shown when searching a term
|
||||
that is not found in any stylesheet or stylesheet name. -->
|
||||
<!ENTITY searchNoResults.label "No matching style sheet has been found.">
|
||||
|
||||
<!-- LOCALIZATION NOTE (searchClearButton): This button clears the search input
|
||||
box and is visible only when a search term has been typed. -->
|
||||
<!ENTITY searchClearButton.label "Clear">
|
||||
|
||||
<!ENTITY visibilityToggle.tooltip "Toggle style sheet visibility">
|
||||
<!ENTITY visibilityToggle.accesskey "V">
|
||||
|
||||
<!ENTITY saveButton.label "Save">
|
||||
<!ENTITY saveButton.tooltip "Save this style sheet to a file">
|
||||
<!ENTITY saveButton.accesskey "S">
|
||||
<!ENTITY saveButton.commandkey "s">
|
||||
|
||||
<!-- LOCALICATION NOTE (scoped.label): This is shown in a stylesheets list item
|
||||
when the stylesheet uses the scoped attribute on the <style> element. -->
|
||||
<!ENTITY scoped.label "Scoped.">
|
||||
|
||||
<!ENTITY editorTextbox.placeholder "Type CSS here.">
|
||||
|
||||
<!-- LOCALICATION NOTE (noStyleSheet.label): This is shown when a page has no
|
||||
stylesheet. -->
|
||||
<!ENTITY noStyleSheet.label "This page has no style sheet.">
|
||||
|
||||
<!-- LOCALICATION NOTE (noStyleSheet-tip-start.label): This is the start of a
|
||||
tip sentence shown when there is no stylesheet. It suggests to create a new
|
||||
stylesheet and provides an action link to do so. -->
|
||||
<!ENTITY noStyleSheet-tip-start.label "Perhaps you'd like to ">
|
||||
<!-- LOCALICATION NOTE (noStyleSheet-tip-action.label): This is text for the
|
||||
link that triggers creation of a new stylesheet. -->
|
||||
<!ENTITY noStyleSheet-tip-action.label "append a new style sheet">
|
||||
<!-- LOCALICATION NOTE (noStyleSheet-tip-end.label): End of the tip sentence -->
|
||||
<!ENTITY noStyleSheet-tip-end.label "?">
|
|
@ -0,0 +1,64 @@
|
|||
# LOCALIZATION NOTE These strings are used inside the Style Editor.
|
||||
# LOCALIZATION NOTE The correct localization of this file might be to keep it
|
||||
# in English, or another language commonly spoken among web developers.
|
||||
# You want to make that choice consistent across the developer tools.
|
||||
# A good criteria is the language in which you'd find the best documentation
|
||||
# on web development on the web.
|
||||
|
||||
# LOCALIZATION NOTE (chromeWindowTitle): This is the title of the Style Editor
|
||||
# 'chrome' window. That is, the main window with the stylesheets list.
|
||||
# The argument is either the content document's title or its href if no title
|
||||
# is available.
|
||||
chromeWindowTitle=Style Editor [%S]
|
||||
|
||||
# LOCALIZATION NOTE (inlineStyleSheet): This is the name used for an style sheet
|
||||
# that is declared inline in the <style> element. Shown in the stylesheets list.
|
||||
# the argument is the index (order) of the containing <style> element in the
|
||||
# document.
|
||||
inlineStyleSheet=<inline style sheet #%S>
|
||||
|
||||
# LOCALIZATION NOTE (newStyleSheet): This is the default name for a new
|
||||
# user-created style sheet.
|
||||
newStyleSheet=New style sheet #%S
|
||||
|
||||
# LOCALIZATION NOTE (ruleCount.label): Semi-colon list of plural forms.
|
||||
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
|
||||
# This is shown in the style sheets list.
|
||||
# #1 rule.
|
||||
# example: 111 rules.
|
||||
ruleCount.label=#1 rule.;#1 rules.
|
||||
|
||||
# LOCALIZATION NOTE (error-load.label): This is shown when loading fails.
|
||||
error-load=Style sheet could not be loaded.
|
||||
|
||||
# LOCALIZATION NOTE (error-save.label): This is shown when saving fails.
|
||||
error-save=Style sheet could not be saved.
|
||||
|
||||
# LOCALIZATION NOTE (importStyleSheet.title): This is the file picker title,
|
||||
# when you import a style sheet into the Style Editor.
|
||||
importStyleSheet.title=Import style sheet
|
||||
|
||||
# LOCALIZATION NOTE (importStyleSheet.title): This is the *.css filter title
|
||||
importStyleSheet.filter=CSS files
|
||||
|
||||
# LOCALIZATION NOTE (saveStyleSheet.title): This is the file picker title,
|
||||
# when you save a style sheet from the Style Editor.
|
||||
saveStyleSheet.title=Save style sheet
|
||||
|
||||
# LOCALIZATION NOTE (saveStyleSheet.title): This is the *.css filter title
|
||||
saveStyleSheet.filter=CSS files
|
||||
|
||||
# LOCALIZATION NOTE (saveStyleSheet.commandkey): This the key to use in
|
||||
# conjunction with accel (Command on Mac or Ctrl on other platforms) to Save
|
||||
saveStyleSheet.commandkey=S
|
||||
|
||||
# LOCALIZATION NOTE (undo.commandkey): This the key to use in
|
||||
# conjunction with accel (Command on Mac or Ctrl on other platforms) to Undo a
|
||||
# change in the editor.
|
||||
undo.commandkey=Z
|
||||
|
||||
# LOCALIZATION NOTE (redo.commandkey): This the key to use in
|
||||
# conjunction with accel+shift (accel is Command on Mac or Ctrl on other
|
||||
# platforms) to Redo a change in the editor.
|
||||
redo.commandkey=Z
|
||||
|
|
@ -115,23 +115,6 @@ NetworkPanel.imageSizeDeltaDurationMS=%Sx%Spx, Δ%Sms
|
|||
NetworkPanel.responseBodyUnableToDisplay.content=Unable to display responses of type "%S"
|
||||
ConsoleAPIDisabled=The Web Console logging API (console.log, console.info, console.warn, console.error) has been disabled by a script on this page.
|
||||
|
||||
# LOCALIZATION NOTE (inspectStyle.nullObjectPassed):
|
||||
# This message is returned when a null object is passed in to inspectstyle()
|
||||
inspectStyle.nullObjectPassed=Object is null
|
||||
|
||||
# LOCALIZATION NOTE (inspectStyle.mustBeDomNode):
|
||||
# This message is returned when a non-DOM node is passed in to inspectstyle()
|
||||
inspectStyle.mustBeDomNode=Object must be a valid DOM node
|
||||
|
||||
# LOCALIZATION NOTE (inspectStyle.nodeHasNoStyleProps):
|
||||
# This message is returned when an unstyleable object is passed in to inspectstyle()
|
||||
inspectStyle.nodeHasNoStyleProps=Object cannot be styled
|
||||
|
||||
# LOCALIZATION NOTE (inspectStyle.styleInspectorNotEnabled):
|
||||
# This message is returned when devtools.styleinspector.enabled is not set to
|
||||
# true
|
||||
inspectStyle.styleInspectorNotEnabled=The style inspector is not enabled. Please set the option devtools.styleinspector.enabled to true in about:config to use this command.
|
||||
|
||||
# LOCALIZATION NOTE (webConsolePosition): The label shown for the menu which
|
||||
# allows the user to toggle between the Web Console positioning types.
|
||||
webConsolePosition=Position
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
locale/browser/devtools/inspector.properties (%chrome/browser/devtools/inspector.properties)
|
||||
locale/browser/devtools/scratchpad.properties (%chrome/browser/devtools/scratchpad.properties)
|
||||
locale/browser/devtools/scratchpad.dtd (%chrome/browser/devtools/scratchpad.dtd)
|
||||
locale/browser/devtools/styleeditor.properties (%chrome/browser/devtools/styleeditor.properties)
|
||||
locale/browser/devtools/styleeditor.dtd (%chrome/browser/devtools/styleeditor.dtd)
|
||||
locale/browser/devtools/styleinspector.properties (%chrome/browser/devtools/styleinspector.properties)
|
||||
locale/browser/devtools/styleinspector.dtd (%chrome/browser/devtools/styleinspector.dtd)
|
||||
locale/browser/devtools/webConsole.dtd (%chrome/browser/devtools/webConsole.dtd)
|
||||
|
|
|
@ -2030,6 +2030,7 @@ panel[dimmed="true"] {
|
|||
border-radius: 3px;
|
||||
background: -moz-linear-gradient(hsla(212,7%,57%,.35), hsla(212,7%,57%,.1)) padding-box;
|
||||
box-shadow: 0 1px 0 hsla(210,16%,76%,.15) inset, 0 0 0 1px hsla(210,16%,76%,.15) inset, 0 1px 0 hsla(210,16%,76%,.15);
|
||||
margin: 0 3px;
|
||||
}
|
||||
|
||||
#inspector-inspect-toolbutton:not([checked]):hover:active,
|
||||
|
@ -2169,7 +2170,7 @@ panel[dimmed="true"] {
|
|||
/* Highlighter toolbar - breadcrumbs */
|
||||
|
||||
#inspector-breadcrumbs {
|
||||
padding: 0 6px;
|
||||
padding: 0 3px;
|
||||
/* A fake 1px-shadow is included in the border-images of the
|
||||
inspector-breadcrumbs-buttons, to match toolbar-buttons style.
|
||||
This negative margin compensate the extra row of pixels created
|
||||
|
|
|
@ -222,11 +222,11 @@
|
|||
|
||||
.ruleview-rule-source {
|
||||
background-color: -moz-dialog;
|
||||
padding: 2px;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
.ruleview-code {
|
||||
padding: 2px;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
.ruleview-propertylist {
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 784 B |
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче